| #include "cose/cose.h" |
| #include "cose/cose_configure.h" |
| #include "cose_int.h" |
| #include "crypto.h" |
| |
| #include <assert.h> |
| #include <memory.h> |
| #include <stdbool.h> |
| |
| #ifdef USE_OPEN_SSL |
| |
| #include <openssl/evp.h> |
| #include <openssl/aes.h> |
| #include <openssl/cmac.h> |
| #include <openssl/hmac.h> |
| #include <openssl/ecdsa.h> |
| #include <openssl/ecdh.h> |
| #include <openssl/rand.h> |
| |
| static bool FUseCompressed = true; |
| |
| #define MIN(A, B) ((A) < (B) ? (A) : (B)) |
| |
| #if (OPENSSL_VERSION_NUMBER < 0x10100000) |
| |
| HMAC_CTX *HMAC_CTX_new() |
| { |
| HMAC_CTX *foo = malloc(sizeof(HMAC_CTX)); |
| if (foo != NULL) { |
| HMAC_CTX_init(foo); |
| } |
| return foo; |
| } |
| |
| void HMAC_CTX_free(HMAC_CTX *foo) |
| { |
| if (foo != NULL) |
| free(foo); |
| } |
| |
| void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps) |
| { |
| if (pr != NULL) |
| *pr = sig->r; |
| if (ps != NULL) |
| *ps = sig->s; |
| } |
| |
| int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s) |
| { |
| if (r == NULL || s == NULL) |
| return 0; |
| BN_clear_free(sig->r); |
| BN_clear_free(sig->s); |
| sig->r = r; |
| sig->s = s; |
| return 1; |
| } |
| #endif |
| |
| bool AES_CCM_Decrypt(COSE_Enveloped *pcose, |
| int TSize, |
| int LSize, |
| const byte *pbKey, |
| size_t cbKey, |
| const byte *pbCrypto, |
| size_t cbCrypto, |
| const byte *pbAuthData, |
| size_t cbAuthData, |
| cose_errback *perr) |
| { |
| EVP_CIPHER_CTX *ctx; |
| int cbOut; |
| byte *rgbOut = NULL; |
| int NSize = 15 - (LSize / 8); |
| int outl = 0; |
| byte rgbIV[15] = {0}; |
| const cn_cbor *pIV = NULL; |
| const EVP_CIPHER *cipher; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pcose->m_message.m_allocContext; |
| #endif |
| |
| ctx = EVP_CIPHER_CTX_new(); |
| CHECK_CONDITION(ctx != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| // Setup the IV/Nonce and put it into the message |
| |
| pIV = _COSE_map_get_int(&pcose->m_message, COSE_Header_IV, COSE_BOTH, NULL); |
| if ((pIV == NULL) || (pIV->type != CN_CBOR_BYTES)) { |
| if (perr != NULL) { |
| perr->err = COSE_ERR_INVALID_PARAMETER; |
| } |
| |
| errorReturn: |
| if (rgbOut != NULL) { |
| COSE_FREE(rgbOut, context); |
| } |
| EVP_CIPHER_CTX_free(ctx); |
| return false; |
| } |
| |
| CHECK_CONDITION(pIV->length == NSize, COSE_ERR_INVALID_PARAMETER); |
| memcpy(rgbIV, pIV->v.str, pIV->length); |
| |
| // Setup and run the OpenSSL code |
| |
| switch (cbKey) { |
| case 128 / 8: |
| cipher = EVP_aes_128_ccm(); |
| break; |
| |
| case 192 / 8: |
| cipher = EVP_aes_192_ccm(); |
| break; |
| |
| case 256 / 8: |
| cipher = EVP_aes_256_ccm(); |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| break; |
| } |
| CHECK_CONDITION(EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL), |
| COSE_ERR_DECRYPT_FAILED); |
| |
| TSize /= 8; // Comes in in bits not bytes. |
| CHECK_CONDITION( |
| EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_L, (LSize / 8), 0), |
| COSE_ERR_DECRYPT_FAILED); |
| // CHECK_CONDITION(EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_CCM_SET_IVLEN, NSize, |
| // 0), COSE_ERR_DECRYPT_FAILED); |
| CHECK_CONDITION(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, TSize, |
| (void *)&pbCrypto[cbCrypto - TSize]), |
| COSE_ERR_DECRYPT_FAILED); |
| |
| CHECK_CONDITION(EVP_DecryptInit_ex(ctx, 0, NULL, pbKey, rgbIV), |
| COSE_ERR_DECRYPT_FAILED); |
| |
| CHECK_CONDITION( |
| EVP_DecryptUpdate(ctx, NULL, &cbOut, NULL, (int)cbCrypto - TSize), |
| COSE_ERR_DECRYPT_FAILED); |
| |
| cbOut = (int)cbCrypto - TSize; |
| rgbOut = (byte *)COSE_CALLOC(cbOut, 1, context); |
| CHECK_CONDITION(rgbOut != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| CHECK_CONDITION( |
| EVP_DecryptUpdate(ctx, NULL, &outl, pbAuthData, (int)cbAuthData), |
| COSE_ERR_DECRYPT_FAILED); |
| |
| CHECK_CONDITION( |
| EVP_DecryptUpdate(ctx, rgbOut, &cbOut, pbCrypto, (int)cbCrypto - TSize), |
| COSE_ERR_DECRYPT_FAILED); |
| |
| EVP_CIPHER_CTX_free(ctx); |
| |
| pcose->pbContent = rgbOut; |
| pcose->cbContent = cbOut; |
| |
| return true; |
| } |
| |
| bool AES_CCM_Encrypt(COSE_Enveloped *pcose, |
| int TSize, |
| int LSize, |
| const byte *pbKey, |
| size_t cbKey, |
| const byte *pbAuthData, |
| size_t cbAuthData, |
| cose_errback *perr) |
| { |
| EVP_CIPHER_CTX *ctx; |
| int cbOut; |
| byte *rgbOut = NULL; |
| int NSize = 15 - (LSize / 8); |
| int outl = 0; |
| const cn_cbor *cbor_iv = NULL; |
| cn_cbor *cbor_iv_t = NULL; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pcose->m_message.m_allocContext; |
| #endif |
| cn_cbor *cnTmp = NULL; |
| const EVP_CIPHER *cipher; |
| byte rgbIV[16]; |
| byte *pbIV = NULL; |
| cn_cbor_errback cbor_error; |
| |
| ctx = EVP_CIPHER_CTX_new(); |
| CHECK_CONDITION(NULL != ctx, COSE_ERR_OUT_OF_MEMORY); |
| |
| switch (cbKey * 8) { |
| case 128: |
| cipher = EVP_aes_128_ccm(); |
| break; |
| |
| case 192: |
| cipher = EVP_aes_192_ccm(); |
| break; |
| |
| case 256: |
| cipher = EVP_aes_256_ccm(); |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| } |
| |
| // Setup the IV/Nonce and put it into the message |
| |
| cbor_iv = |
| _COSE_map_get_int(&pcose->m_message, COSE_Header_IV, COSE_BOTH, perr); |
| if (cbor_iv == NULL) { |
| pbIV = COSE_CALLOC(NSize, 1, context); |
| CHECK_CONDITION(pbIV != NULL, COSE_ERR_OUT_OF_MEMORY); |
| rand_bytes(pbIV, NSize); |
| memcpy(rgbIV, pbIV, NSize); |
| cbor_iv_t = cn_cbor_data_create( |
| pbIV, NSize, CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(cbor_iv_t != NULL, cbor_error); |
| pbIV = NULL; |
| |
| if (!_COSE_map_put(&pcose->m_message, COSE_Header_IV, cbor_iv_t, |
| COSE_UNPROTECT_ONLY, perr)) { |
| goto errorReturn; |
| } |
| cbor_iv_t = NULL; |
| } |
| else { |
| CHECK_CONDITION( |
| cbor_iv->type == CN_CBOR_BYTES, COSE_ERR_INVALID_PARAMETER); |
| CHECK_CONDITION(cbor_iv->length == NSize, COSE_ERR_INVALID_PARAMETER); |
| memcpy(rgbIV, cbor_iv->v.str, cbor_iv->length); |
| } |
| |
| // Setup and run the OpenSSL code |
| |
| CHECK_CONDITION(EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| TSize /= 8; // Comes in in bits not bytes. |
| CHECK_CONDITION( |
| EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_L, (LSize / 8), 0), |
| COSE_ERR_CRYPTO_FAIL); |
| // CHECK_CONDITION(EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_CCM_SET_IVLEN, NSize, |
| // 0), COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, TSize, NULL), |
| COSE_ERR_CRYPTO_FAIL); // Say we are doing an 8 byte tag |
| |
| CHECK_CONDITION( |
| EVP_EncryptInit_ex(ctx, 0, NULL, pbKey, rgbIV), COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION(EVP_EncryptUpdate(ctx, 0, &cbOut, 0, (int)pcose->cbContent), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION( |
| EVP_EncryptUpdate(ctx, NULL, &outl, pbAuthData, (int)cbAuthData), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| rgbOut = (byte *)COSE_CALLOC(cbOut + TSize, 1, context); |
| CHECK_CONDITION(rgbOut != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| CHECK_CONDITION(EVP_EncryptUpdate(ctx, rgbOut, &cbOut, pcose->pbContent, |
| (int)pcose->cbContent), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION( |
| EVP_EncryptFinal_ex(ctx, &rgbOut[cbOut], &cbOut), COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, TSize, |
| &rgbOut[pcose->cbContent]), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| cnTmp = cn_cbor_data_create( |
| rgbOut, (int)pcose->cbContent + TSize, CBOR_CONTEXT_PARAM_COMMA NULL); |
| CHECK_CONDITION(cnTmp != NULL, COSE_ERR_CBOR); |
| rgbOut = NULL; |
| |
| CHECK_CONDITION(_COSE_array_replace(&pcose->m_message, cnTmp, INDEX_BODY, |
| CBOR_CONTEXT_PARAM_COMMA NULL), |
| COSE_ERR_CBOR); |
| cnTmp = NULL; |
| EVP_CIPHER_CTX_free(ctx); |
| return true; |
| |
| errorReturn: |
| if (pbIV != NULL) { |
| COSE_FREE(pbIV, context); |
| } |
| if (cbor_iv_t != NULL) { |
| COSE_FREE(cbor_iv_t, context); |
| } |
| if (rgbOut != NULL) { |
| COSE_FREE(rgbOut, context); |
| } |
| if (cnTmp != NULL) { |
| COSE_FREE(cnTmp, context); |
| } |
| EVP_CIPHER_CTX_free(ctx); |
| return false; |
| } |
| |
| bool AES_GCM_Decrypt(COSE_Enveloped *pcose, |
| const byte *pbKey, |
| size_t cbKey, |
| const byte *pbCrypto, |
| size_t cbCrypto, |
| const byte *pbAuthData, |
| size_t cbAuthData, |
| cose_errback *perr) |
| { |
| EVP_CIPHER_CTX *ctx; |
| int cbOut; |
| byte *rgbOut = NULL; |
| int outl = 0; |
| byte rgbIV[15] = {0}; |
| const cn_cbor *pIV = NULL; |
| const EVP_CIPHER *cipher; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pcose->m_message.m_allocContext; |
| #endif |
| int TSize = 128 / 8; |
| |
| ctx = EVP_CIPHER_CTX_new(); |
| CHECK_CONDITION(NULL != ctx, COSE_ERR_OUT_OF_MEMORY); |
| |
| // Setup the IV/Nonce and put it into the message |
| |
| pIV = _COSE_map_get_int(&pcose->m_message, COSE_Header_IV, COSE_BOTH, NULL); |
| if ((pIV == NULL) || (pIV->type != CN_CBOR_BYTES)) { |
| if (perr != NULL) { |
| perr->err = COSE_ERR_INVALID_PARAMETER; |
| } |
| |
| errorReturn: |
| if (rgbOut != NULL) { |
| COSE_FREE(rgbOut, context); |
| } |
| EVP_CIPHER_CTX_free(ctx); |
| return false; |
| } |
| |
| CHECK_CONDITION(pIV->length == 96 / 8, COSE_ERR_INVALID_PARAMETER); |
| memcpy(rgbIV, pIV->v.str, pIV->length); |
| |
| // Setup and run the OpenSSL code |
| |
| switch (cbKey) { |
| case 128 / 8: |
| cipher = EVP_aes_128_gcm(); |
| break; |
| |
| case 192 / 8: |
| cipher = EVP_aes_192_gcm(); |
| break; |
| |
| case 256 / 8: |
| cipher = EVP_aes_256_gcm(); |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| break; |
| } |
| |
| // Do the setup for OpenSSL |
| |
| CHECK_CONDITION(EVP_DecryptInit_ex(ctx, cipher, NULL, NULL, NULL), |
| COSE_ERR_DECRYPT_FAILED); |
| |
| CHECK_CONDITION(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, TSize, |
| (void *)&pbCrypto[cbCrypto - TSize]), |
| COSE_ERR_DECRYPT_FAILED); |
| |
| CHECK_CONDITION(EVP_DecryptInit_ex(ctx, 0, NULL, pbKey, rgbIV), |
| COSE_ERR_DECRYPT_FAILED); |
| |
| // Pus in the AAD |
| |
| CHECK_CONDITION( |
| EVP_DecryptUpdate(ctx, NULL, &outl, pbAuthData, (int)cbAuthData), |
| COSE_ERR_DECRYPT_FAILED); |
| |
| // |
| |
| cbOut = (int)cbCrypto - TSize; |
| rgbOut = (byte *)COSE_CALLOC(cbOut, 1, context); |
| CHECK_CONDITION(rgbOut != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| // Process content |
| |
| CHECK_CONDITION( |
| EVP_DecryptUpdate(ctx, rgbOut, &cbOut, pbCrypto, (int)cbCrypto - TSize), |
| COSE_ERR_DECRYPT_FAILED); |
| |
| // Process Tag |
| |
| CHECK_CONDITION(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, TSize, |
| (byte *)pbCrypto + cbCrypto - TSize), |
| COSE_ERR_DECRYPT_FAILED); |
| |
| // Check the result |
| |
| CHECK_CONDITION( |
| EVP_DecryptFinal(ctx, rgbOut + cbOut, &cbOut), COSE_ERR_DECRYPT_FAILED); |
| |
| EVP_CIPHER_CTX_free(ctx); |
| |
| pcose->pbContent = rgbOut; |
| pcose->cbContent = cbOut; |
| |
| return true; |
| } |
| |
| bool AES_GCM_Encrypt(COSE_Enveloped *pcose, |
| const byte *pbKey, |
| size_t cbKey, |
| const byte *pbAuthData, |
| size_t cbAuthData, |
| cose_errback *perr) |
| { |
| EVP_CIPHER_CTX *ctx; |
| int cbOut; |
| byte *rgbOut = NULL; |
| int outl = 0; |
| byte rgbIV[16] = {0}; |
| byte *pbIV = NULL; |
| const cn_cbor *cbor_iv = NULL; |
| cn_cbor *cbor_iv_t = NULL; |
| const EVP_CIPHER *cipher; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pcose->m_message.m_allocContext; |
| #endif |
| cn_cbor_errback cbor_error; |
| |
| // Make it first so we can clean it up |
| ctx = EVP_CIPHER_CTX_new(); |
| CHECK_CONDITION(NULL != ctx, COSE_ERR_OUT_OF_MEMORY); |
| |
| // Setup the IV/Nonce and put it into the message |
| |
| cbor_iv = |
| _COSE_map_get_int(&pcose->m_message, COSE_Header_IV, COSE_BOTH, perr); |
| if (cbor_iv == NULL) { |
| pbIV = COSE_CALLOC(96, 1, context); |
| CHECK_CONDITION(pbIV != NULL, COSE_ERR_OUT_OF_MEMORY); |
| rand_bytes(pbIV, 96 / 8); |
| memcpy(rgbIV, pbIV, 96 / 8); |
| cbor_iv_t = cn_cbor_data_create( |
| pbIV, 96 / 8, CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(cbor_iv_t != NULL, cbor_error); |
| pbIV = NULL; |
| |
| if (!_COSE_map_put(&pcose->m_message, COSE_Header_IV, cbor_iv_t, |
| COSE_UNPROTECT_ONLY, perr)) { |
| goto errorReturn; |
| } |
| cbor_iv_t = NULL; |
| } |
| else { |
| CHECK_CONDITION( |
| cbor_iv->type == CN_CBOR_BYTES, COSE_ERR_INVALID_PARAMETER); |
| CHECK_CONDITION(cbor_iv->length == 96 / 8, COSE_ERR_INVALID_PARAMETER); |
| memcpy(rgbIV, cbor_iv->v.str, cbor_iv->length); |
| } |
| |
| switch (cbKey * 8) { |
| case 128: |
| cipher = EVP_aes_128_gcm(); |
| break; |
| |
| case 192: |
| cipher = EVP_aes_192_gcm(); |
| break; |
| |
| case 256: |
| cipher = EVP_aes_256_gcm(); |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| break; |
| } |
| |
| // Setup and run the OpenSSL code |
| |
| CHECK_CONDITION(EVP_EncryptInit_ex(ctx, cipher, NULL, NULL, NULL), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION( |
| EVP_EncryptInit_ex(ctx, 0, NULL, pbKey, rgbIV), COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION( |
| EVP_EncryptUpdate(ctx, NULL, &outl, pbAuthData, (int)cbAuthData), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| rgbOut = (byte *)COSE_CALLOC(pcose->cbContent + 128 / 8, 1, context); |
| CHECK_CONDITION(rgbOut != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| CHECK_CONDITION(EVP_EncryptUpdate(ctx, rgbOut, &cbOut, pcose->pbContent, |
| (int)pcose->cbContent), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION( |
| EVP_EncryptFinal_ex(ctx, &rgbOut[cbOut], &cbOut), COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION(EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 128 / 8, |
| &rgbOut[pcose->cbContent]), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| cn_cbor *cnTmp = cn_cbor_data_create( |
| rgbOut, (int)pcose->cbContent + 128 / 8, CBOR_CONTEXT_PARAM_COMMA NULL); |
| CHECK_CONDITION(cnTmp != NULL, COSE_ERR_CBOR); |
| rgbOut = NULL; |
| CHECK_CONDITION(_COSE_array_replace(&pcose->m_message, cnTmp, INDEX_BODY, |
| CBOR_CONTEXT_PARAM_COMMA NULL), |
| COSE_ERR_CBOR); |
| |
| EVP_CIPHER_CTX_free(ctx); |
| return true; |
| |
| errorReturn: |
| if (pbIV != NULL) { |
| COSE_FREE(pbIV, context); |
| } |
| if (cbor_iv_t != NULL) { |
| COSE_FREE(cbor_iv_t, context); |
| } |
| if (rgbOut != NULL) { |
| COSE_FREE(rgbOut, context); |
| } |
| EVP_CIPHER_CTX_free(ctx); |
| return false; |
| } |
| |
| bool AES_CBC_MAC_Create(COSE_MacMessage *pcose, |
| int TSize, |
| const byte *pbKey, |
| size_t cbKey, |
| const byte *pbAuthData, |
| size_t cbAuthData, |
| cose_errback *perr) |
| { |
| const EVP_CIPHER *pcipher = NULL; |
| EVP_CIPHER_CTX *ctx; |
| int cbOut; |
| byte rgbIV[16] = {0}; |
| byte *rgbOut = NULL; |
| bool f = false; |
| unsigned int i; |
| cn_cbor *cn = NULL; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pcose->m_message.m_allocContext; |
| #endif |
| |
| ctx = EVP_CIPHER_CTX_new(); |
| CHECK_CONDITION(NULL != ctx, COSE_ERR_OUT_OF_MEMORY); |
| |
| rgbOut = COSE_CALLOC(16, 1, context); |
| CHECK_CONDITION(rgbOut != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| switch (cbKey * 8) { |
| case 128: |
| pcipher = EVP_aes_128_cbc(); |
| break; |
| |
| case 256: |
| pcipher = EVP_aes_256_cbc(); |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| } |
| |
| // Setup and run the OpenSSL code |
| |
| CHECK_CONDITION(EVP_EncryptInit_ex(ctx, pcipher, NULL, pbKey, rgbIV), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| for (i = 0; i < (unsigned int)cbAuthData / 16; i++) { |
| CHECK_CONDITION( |
| EVP_EncryptUpdate(ctx, rgbOut, &cbOut, pbAuthData + (i * 16), 16), |
| COSE_ERR_CRYPTO_FAIL); |
| } |
| if (cbAuthData % 16 != 0) { |
| CHECK_CONDITION(EVP_EncryptUpdate(ctx, rgbOut, &cbOut, |
| pbAuthData + (i * 16), cbAuthData % 16), |
| COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION(EVP_EncryptUpdate( |
| ctx, rgbOut, &cbOut, rgbIV, 16 - (cbAuthData % 16)), |
| COSE_ERR_CRYPTO_FAIL); |
| } |
| |
| cn = cn_cbor_data_create(rgbOut, TSize / 8, CBOR_CONTEXT_PARAM_COMMA NULL); |
| CHECK_CONDITION(cn != NULL, COSE_ERR_OUT_OF_MEMORY); |
| rgbOut = NULL; |
| |
| CHECK_CONDITION(_COSE_array_replace(&pcose->m_message, cn, INDEX_MAC_TAG, |
| CBOR_CONTEXT_PARAM_COMMA NULL), |
| COSE_ERR_CBOR); |
| cn = NULL; |
| |
| EVP_CIPHER_CTX_free(ctx); |
| return !f; |
| |
| errorReturn: |
| if (rgbOut != NULL) { |
| COSE_FREE(rgbOut, context); |
| } |
| if (cn != NULL) { |
| CN_CBOR_FREE(cn, context); |
| } |
| EVP_CIPHER_CTX_free(ctx); |
| return false; |
| } |
| |
| bool AES_CBC_MAC_Validate(COSE_MacMessage *pcose, |
| int TSize, |
| const byte *pbKey, |
| size_t cbKey, |
| const byte *pbAuthData, |
| size_t cbAuthData, |
| cose_errback *perr) |
| { |
| const EVP_CIPHER *pcipher = NULL; |
| EVP_CIPHER_CTX *ctx = NULL; |
| int cbOut; |
| byte rgbIV[16] = {0}; |
| byte rgbTag[16] = {0}; |
| bool f = false; |
| unsigned int i; |
| |
| switch (cbKey * 8) { |
| case 128: |
| pcipher = EVP_aes_128_cbc(); |
| break; |
| |
| case 256: |
| pcipher = EVP_aes_256_cbc(); |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| } |
| |
| // Setup and run the OpenSSL code |
| |
| ctx = EVP_CIPHER_CTX_new(); |
| CHECK_CONDITION(NULL != ctx, COSE_ERR_OUT_OF_MEMORY); |
| CHECK_CONDITION(EVP_EncryptInit_ex(ctx, pcipher, NULL, pbKey, rgbIV), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| TSize /= 8; |
| |
| for (i = 0; i < (unsigned int)cbAuthData / 16; i++) { |
| CHECK_CONDITION( |
| EVP_EncryptUpdate(ctx, rgbTag, &cbOut, pbAuthData + (i * 16), 16), |
| COSE_ERR_CRYPTO_FAIL); |
| } |
| if (cbAuthData % 16 != 0) { |
| CHECK_CONDITION(EVP_EncryptUpdate(ctx, rgbTag, &cbOut, |
| pbAuthData + (i * 16), cbAuthData % 16), |
| COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION(EVP_EncryptUpdate( |
| ctx, rgbTag, &cbOut, rgbIV, 16 - (cbAuthData % 16)), |
| COSE_ERR_CRYPTO_FAIL); |
| } |
| |
| cn_cbor *cn = _COSE_arrayget_int(&pcose->m_message, INDEX_MAC_TAG); |
| CHECK_CONDITION(cn != NULL, COSE_ERR_CBOR); |
| |
| for (i = 0; i < (unsigned int)TSize; i++) { |
| f |= (cn->v.bytes[i] != rgbTag[i]); |
| } |
| |
| EVP_CIPHER_CTX_free(ctx); |
| return !f; |
| |
| errorReturn: |
| EVP_CIPHER_CTX_free(ctx); |
| return false; |
| } |
| |
| #if 0 |
| // We are doing CBC-MAC not CMAC at this time |
| bool AES_CMAC_Validate(COSE_MacMessage * pcose, int KeySize, int TagSize, const byte * pbAuthData, int cbAuthData, cose_errback * perr) |
| { |
| CMAC_CTX * pctx = NULL; |
| const EVP_CIPHER * pcipher = NULL; |
| byte * rgbOut = NULL; |
| size_t cbOut; |
| bool f = false; |
| unsigned int i; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context * context = &pcose->m_message.m_allocContext; |
| #endif |
| |
| pctx = CMAC_CTX_new(); |
| |
| |
| switch (KeySize) { |
| case 128: pcipher = EVP_aes_128_cbc(); break; |
| case 256: pcipher = EVP_aes_256_cbc(); break; |
| default: FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); break; |
| } |
| |
| rgbOut = COSE_CALLOC(128/8, 1, context); |
| CHECK_CONDITION(rgbOut != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| CHECK_CONDITION(CMAC_Init(pctx, pcose->pbKey, pcose->cbKey, pcipher, NULL /*impl*/) == 1, COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION(CMAC_Update(pctx, pbAuthData, cbAuthData), COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION(CMAC_Final(pctx, rgbOut, &cbOut), COSE_ERR_CRYPTO_FAIL); |
| |
| cn_cbor * cn = _COSE_arrayget_int(&pcose->m_message, INDEX_MAC_TAG); |
| CHECK_CONDITION(cn != NULL, COSE_ERR_CBOR); |
| |
| for (i = 0; i < (unsigned int)TagSize / 8; i++) f |= (cn->v.bytes[i] != rgbOut[i]); |
| |
| COSE_FREE(rgbOut, context); |
| CMAC_CTX_cleanup(pctx); |
| CMAC_CTX_free(pctx); |
| return !f; |
| |
| errorReturn: |
| COSE_FREE(rgbOut, context); |
| CMAC_CTX_cleanup(pctx); |
| CMAC_CTX_free(pctx); |
| return false; |
| |
| } |
| #endif |
| |
| bool HKDF_AES_Expand(COSE *pcose, |
| size_t cbitKey, |
| const byte *pbPRK, |
| size_t cbPRK, |
| const byte *pbInfo, |
| size_t cbInfo, |
| byte *pbOutput, |
| size_t cbOutput, |
| cose_errback *perr) |
| { |
| const EVP_CIPHER *pcipher = NULL; |
| EVP_CIPHER_CTX *ctx; |
| int cbOut; |
| byte rgbIV[16] = {0}; |
| byte bCount = 1; |
| size_t ib; |
| byte rgbDigest[128 / 8]; |
| int cbDigest = 0; |
| byte rgbOut[16]; |
| |
| UNUSED(pcose); |
| |
| ctx = EVP_CIPHER_CTX_new(); |
| CHECK_CONDITION(NULL != ctx, COSE_ERR_OUT_OF_MEMORY); |
| |
| switch (cbitKey) { |
| case 128: |
| pcipher = EVP_aes_128_cbc(); |
| break; |
| |
| case 256: |
| pcipher = EVP_aes_256_cbc(); |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| } |
| CHECK_CONDITION(cbPRK == cbitKey / 8, COSE_ERR_INVALID_PARAMETER); |
| |
| // Setup and run the OpenSSL code |
| |
| for (ib = 0; ib < cbOutput; ib += 16, bCount += 1) { |
| size_t ib2; |
| |
| CHECK_CONDITION(EVP_EncryptInit_ex(ctx, pcipher, NULL, pbPRK, rgbIV), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION( |
| EVP_EncryptUpdate(ctx, rgbOut, &cbOut, rgbDigest, cbDigest), |
| COSE_ERR_CRYPTO_FAIL); |
| for (ib2 = 0; ib2 < cbInfo; ib2 += 16) { |
| CHECK_CONDITION(EVP_EncryptUpdate(ctx, rgbOut, &cbOut, pbInfo + ib2, |
| (int)MIN(16, cbInfo - ib2)), |
| COSE_ERR_CRYPTO_FAIL); |
| } |
| CHECK_CONDITION(EVP_EncryptUpdate(ctx, rgbOut, &cbOut, &bCount, 1), |
| COSE_ERR_CRYPTO_FAIL); |
| if ((cbInfo + 1) % 16 != 0) { |
| CHECK_CONDITION(EVP_EncryptUpdate(ctx, rgbOut, &cbOut, rgbIV, |
| (int)16 - (cbInfo + 1) % 16), |
| COSE_ERR_CRYPTO_FAIL); |
| } |
| memcpy(rgbDigest, rgbOut, cbOut); |
| cbDigest = cbOut; |
| memcpy(pbOutput + ib, rgbDigest, MIN(16, cbOutput - ib)); |
| } |
| |
| EVP_CIPHER_CTX_free(ctx); |
| return true; |
| |
| errorReturn: |
| EVP_CIPHER_CTX_free(ctx); |
| return false; |
| } |
| |
| bool HKDF_Extract(COSE *pcose, |
| const byte *pbKey, |
| size_t cbKey, |
| size_t cbitDigest, |
| byte *rgbDigest, |
| size_t *pcbDigest, |
| CBOR_CONTEXT_COMMA cose_errback *perr) |
| { |
| #ifdef USE_CBOR_CONTEXT |
| UNUSED(context); |
| #endif |
| byte rgbSalt[EVP_MAX_MD_SIZE] = {0}; |
| int cbSalt; |
| cn_cbor *cnSalt; |
| HMAC_CTX *ctx; |
| const EVP_MD *pmd = NULL; |
| unsigned int cbDigest; |
| |
| ctx = HMAC_CTX_new(); |
| CHECK_CONDITION(NULL != ctx, COSE_ERR_OUT_OF_MEMORY); |
| |
| if (0) { |
| errorReturn: |
| HMAC_CTX_free(ctx); |
| return false; |
| } |
| |
| switch (cbitDigest) { |
| case 256: |
| pmd = EVP_sha256(); |
| cbSalt = 256 / 8; |
| break; |
| case 384: |
| pmd = EVP_sha384(); |
| cbSalt = 384 / 8; |
| break; |
| case 512: |
| pmd = EVP_sha512(); |
| cbSalt = 512 / 8; |
| break; |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| break; |
| } |
| |
| cnSalt = _COSE_map_get_int(pcose, COSE_Header_HKDF_salt, COSE_BOTH, perr); |
| |
| if (cnSalt != NULL) { |
| CHECK_CONDITION( |
| HMAC_Init_ex(ctx, cnSalt->v.bytes, (int)cnSalt->length, pmd, NULL), |
| COSE_ERR_CRYPTO_FAIL); |
| } |
| else { |
| CHECK_CONDITION(HMAC_Init_ex(ctx, rgbSalt, cbSalt, pmd, NULL), |
| COSE_ERR_CRYPTO_FAIL); |
| } |
| CHECK_CONDITION(HMAC_Update(ctx, pbKey, (int)cbKey), COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION( |
| HMAC_Final(ctx, rgbDigest, &cbDigest), COSE_ERR_CRYPTO_FAIL); |
| *pcbDigest = cbDigest; |
| HMAC_CTX_free(ctx); |
| return true; |
| } |
| |
| bool HKDF_Expand(COSE *pcose, |
| size_t cbitDigest, |
| const byte *pbPRK, |
| size_t cbPRK, |
| const byte *pbInfo, |
| size_t cbInfo, |
| byte *pbOutput, |
| size_t cbOutput, |
| cose_errback *perr) |
| { |
| HMAC_CTX *ctx; |
| const EVP_MD *pmd = NULL; |
| size_t ib; |
| unsigned int cbDigest = 0; |
| byte rgbDigest[EVP_MAX_MD_SIZE]; |
| byte bCount = 1; |
| |
| UNUSED(pcose); |
| |
| ctx = HMAC_CTX_new(); |
| CHECK_CONDITION(ctx != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| if (0) { |
| errorReturn: |
| HMAC_CTX_free(ctx); |
| return false; |
| } |
| |
| switch (cbitDigest) { |
| case 256: |
| pmd = EVP_sha256(); |
| break; |
| case 384: |
| pmd = EVP_sha384(); |
| break; |
| case 512: |
| pmd = EVP_sha512(); |
| break; |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| break; |
| } |
| |
| for (ib = 0; ib < cbOutput; ib += cbDigest, bCount += 1) { |
| CHECK_CONDITION(HMAC_Init_ex(ctx, pbPRK, (int)cbPRK, pmd, NULL), |
| COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION( |
| HMAC_Update(ctx, rgbDigest, cbDigest), COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION(HMAC_Update(ctx, pbInfo, cbInfo), COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION(HMAC_Update(ctx, &bCount, 1), COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION( |
| HMAC_Final(ctx, rgbDigest, &cbDigest), COSE_ERR_CRYPTO_FAIL); |
| |
| memcpy(pbOutput + ib, rgbDigest, MIN(cbDigest, cbOutput - ib)); |
| } |
| |
| HMAC_CTX_free(ctx); |
| return true; |
| } |
| |
| bool HMAC_Create(COSE_MacMessage *pcose, |
| int HSize, |
| int TSize, |
| const byte *pbKey, |
| size_t cbKey, |
| const byte *pbAuthData, |
| size_t cbAuthData, |
| cose_errback *perr) |
| { |
| HMAC_CTX *ctx; |
| const EVP_MD *pmd = NULL; |
| byte *rgbOut = NULL; |
| unsigned int cbOut; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pcose->m_message.m_allocContext; |
| #endif |
| |
| ctx = HMAC_CTX_new(); |
| CHECK_CONDITION(NULL != ctx, COSE_ERR_OUT_OF_MEMORY); |
| |
| if (0) { |
| errorReturn: |
| COSE_FREE(rgbOut, context); |
| HMAC_CTX_free(ctx); |
| return false; |
| } |
| |
| switch (HSize) { |
| case 256: |
| pmd = EVP_sha256(); |
| break; |
| case 384: |
| pmd = EVP_sha384(); |
| break; |
| case 512: |
| pmd = EVP_sha512(); |
| break; |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| break; |
| } |
| |
| rgbOut = COSE_CALLOC(EVP_MAX_MD_SIZE, 1, context); |
| CHECK_CONDITION(rgbOut != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| CHECK_CONDITION( |
| HMAC_Init_ex(ctx, pbKey, (int)cbKey, pmd, NULL), COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION( |
| HMAC_Update(ctx, pbAuthData, cbAuthData), COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION(HMAC_Final(ctx, rgbOut, &cbOut), COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION(_COSE_array_replace(&pcose->m_message, |
| cn_cbor_data_create( |
| rgbOut, TSize / 8, CBOR_CONTEXT_PARAM_COMMA NULL), |
| INDEX_MAC_TAG, CBOR_CONTEXT_PARAM_COMMA NULL), |
| COSE_ERR_CBOR); |
| |
| HMAC_CTX_free(ctx); |
| return true; |
| } |
| |
| bool HMAC_Validate(COSE_MacMessage *pcose, |
| int HSize, |
| int TSize, |
| const byte *pbKey, |
| size_t cbKey, |
| const byte *pbAuthData, |
| size_t cbAuthData, |
| cose_errback *perr) |
| { |
| HMAC_CTX *ctx; |
| const EVP_MD *pmd = NULL; |
| byte *rgbOut = NULL; |
| unsigned int cbOut; |
| bool f = false; |
| unsigned int i; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pcose->m_message.m_allocContext; |
| #endif |
| |
| ctx = HMAC_CTX_new(); |
| CHECK_CONDITION(ctx != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| switch (HSize) { |
| case 256: |
| pmd = EVP_sha256(); |
| break; |
| case 384: |
| pmd = EVP_sha384(); |
| break; |
| case 512: |
| pmd = EVP_sha512(); |
| break; |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| break; |
| } |
| |
| rgbOut = COSE_CALLOC(EVP_MAX_MD_SIZE, 1, context); |
| CHECK_CONDITION(rgbOut != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| CHECK_CONDITION( |
| HMAC_Init_ex(ctx, pbKey, (int)cbKey, pmd, NULL), COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION( |
| HMAC_Update(ctx, pbAuthData, cbAuthData), COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION(HMAC_Final(ctx, rgbOut, &cbOut), COSE_ERR_CRYPTO_FAIL); |
| |
| cn_cbor *cn = _COSE_arrayget_int(&pcose->m_message, INDEX_MAC_TAG); |
| CHECK_CONDITION(cn != NULL, COSE_ERR_CBOR); |
| |
| if (cn->length > (int)cbOut) { |
| return false; |
| } |
| for (i = 0; i < (unsigned int)TSize / 8; i++) { |
| f |= (cn->v.bytes[i] != rgbOut[i]); |
| } |
| |
| HMAC_CTX_free(ctx); |
| return !f; |
| |
| errorReturn: |
| COSE_FREE(rgbOut, context); |
| HMAC_CTX_free(ctx); |
| return false; |
| } |
| |
| #define COSE_Key_EC_Curve -1 |
| #define COSE_Key_EC_X -2 |
| #define COSE_Key_EC_Y -3 |
| #define COSE_Key_EC_d -4 |
| |
| EC_KEY *ECKey_From(const cn_cbor *pKey, int *cbGroup, cose_errback *perr) |
| { |
| EC_KEY *pNewKey = EC_KEY_new(); |
| byte rgbKey[512 + 1]; |
| int cbKey; |
| const cn_cbor *p; |
| int nidGroup = -1; |
| EC_POINT *pPoint = NULL; |
| |
| p = cn_cbor_mapget_int(pKey, COSE_Key_EC_Curve); |
| CHECK_CONDITION(p != NULL, COSE_ERR_INVALID_PARAMETER); |
| |
| switch (p->v.sint) { |
| case 1: // P-256 |
| nidGroup = NID_X9_62_prime256v1; |
| *cbGroup = 256 / 8; |
| break; |
| |
| case 2: // P-384 |
| nidGroup = NID_secp384r1; |
| *cbGroup = 384 / 8; |
| break; |
| |
| case 3: // P-521 |
| nidGroup = NID_secp521r1; |
| *cbGroup = (521 + 7) / 8; |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| } |
| |
| EC_GROUP *ecgroup = EC_GROUP_new_by_curve_name(nidGroup); |
| CHECK_CONDITION(ecgroup != NULL, COSE_ERR_INVALID_PARAMETER); |
| CHECK_CONDITION( |
| EC_KEY_set_group(pNewKey, ecgroup) == 1, COSE_ERR_CRYPTO_FAIL); |
| |
| p = cn_cbor_mapget_int(pKey, COSE_Key_EC_X); |
| CHECK_CONDITION( |
| (p != NULL) && (p->type == CN_CBOR_BYTES), COSE_ERR_INVALID_PARAMETER); |
| CHECK_CONDITION(p->length == *cbGroup, COSE_ERR_INVALID_PARAMETER); |
| memcpy(rgbKey + 1, p->v.str, p->length); |
| |
| p = cn_cbor_mapget_int(pKey, COSE_Key_EC_Y); |
| CHECK_CONDITION((p != NULL), COSE_ERR_INVALID_PARAMETER); |
| if (p->type == CN_CBOR_BYTES) { |
| rgbKey[0] = POINT_CONVERSION_UNCOMPRESSED; |
| cbKey = (*cbGroup * 2) + 1; |
| CHECK_CONDITION(p->length == *cbGroup, COSE_ERR_INVALID_PARAMETER); |
| memcpy(rgbKey + p->length + 1, p->v.str, p->length); |
| } |
| else if (p->type == CN_CBOR_TRUE) { |
| cbKey = (*cbGroup) + 1; |
| rgbKey[0] = POINT_CONVERSION_COMPRESSED + 1; |
| } |
| else if (p->type == CN_CBOR_FALSE) { |
| cbKey = (*cbGroup) + 1; |
| rgbKey[0] = POINT_CONVERSION_COMPRESSED; |
| } |
| else |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| |
| pPoint = EC_POINT_new(ecgroup); |
| CHECK_CONDITION(pPoint != NULL, COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION( |
| EC_POINT_oct2point(ecgroup, pPoint, rgbKey, cbKey, NULL) == 1, |
| COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION( |
| EC_KEY_set_public_key(pNewKey, pPoint) == 1, COSE_ERR_CRYPTO_FAIL); |
| |
| p = cn_cbor_mapget_int(pKey, COSE_Key_EC_d); |
| if (p != NULL) { |
| BIGNUM *pbn; |
| |
| pbn = BN_bin2bn(p->v.bytes, (int)p->length, NULL); |
| CHECK_CONDITION(pbn != NULL, COSE_ERR_CRYPTO_FAIL); |
| CHECK_CONDITION( |
| EC_KEY_set_private_key(pNewKey, pbn) == 1, COSE_ERR_CRYPTO_FAIL); |
| } |
| |
| return pNewKey; |
| |
| errorReturn: |
| if (pNewKey != NULL) { |
| EC_KEY_free(pNewKey); |
| } |
| return NULL; |
| } |
| |
| cn_cbor *EC_FromKey(const EC_KEY *pKey, CBOR_CONTEXT_COMMA cose_errback *perr) |
| { |
| cn_cbor *pkey = NULL; |
| const EC_GROUP *pgroup; |
| int cose_group; |
| cn_cbor *p = NULL; |
| cn_cbor_errback cbor_error; |
| const EC_POINT *pPoint; |
| size_t cbSize; |
| byte *pbOut = NULL; |
| |
| pgroup = EC_KEY_get0_group(pKey); |
| CHECK_CONDITION(pgroup != NULL, COSE_ERR_INVALID_PARAMETER); |
| |
| switch (EC_GROUP_get_curve_name(pgroup)) { |
| case NID_X9_62_prime256v1: |
| cose_group = 1; |
| break; |
| case NID_secp384r1: |
| cose_group = 2; |
| break; |
| case NID_secp521r1: |
| cose_group = 3; |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| } |
| |
| pkey = cn_cbor_map_create(CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(pkey != NULL, cbor_error); |
| |
| p = cn_cbor_int_create(cose_group, CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(p != NULL, cbor_error); |
| CHECK_CONDITION_CBOR(cn_cbor_mapput_int(pkey, COSE_Key_EC_Curve, p, |
| CBOR_CONTEXT_PARAM_COMMA & cbor_error), |
| cbor_error); |
| p = NULL; |
| |
| pPoint = EC_KEY_get0_public_key(pKey); |
| CHECK_CONDITION(pPoint != NULL, COSE_ERR_INVALID_PARAMETER); |
| |
| if (FUseCompressed) { |
| cbSize = EC_POINT_point2oct( |
| pgroup, pPoint, POINT_CONVERSION_COMPRESSED, NULL, 0, NULL); |
| CHECK_CONDITION(cbSize > 0, COSE_ERR_CRYPTO_FAIL); |
| pbOut = COSE_CALLOC(cbSize, 1, context); |
| CHECK_CONDITION(pbOut != NULL, COSE_ERR_OUT_OF_MEMORY); |
| CHECK_CONDITION( |
| EC_POINT_point2oct(pgroup, pPoint, POINT_CONVERSION_COMPRESSED, |
| pbOut, cbSize, NULL) == cbSize, |
| COSE_ERR_CRYPTO_FAIL); |
| } |
| else { |
| cbSize = EC_POINT_point2oct( |
| pgroup, pPoint, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); |
| CHECK_CONDITION(cbSize > 0, COSE_ERR_CRYPTO_FAIL); |
| pbOut = COSE_CALLOC(cbSize, 1, context); |
| CHECK_CONDITION(pbOut != NULL, COSE_ERR_OUT_OF_MEMORY); |
| CHECK_CONDITION( |
| EC_POINT_point2oct(pgroup, pPoint, POINT_CONVERSION_UNCOMPRESSED, |
| pbOut, cbSize, NULL) == cbSize, |
| COSE_ERR_CRYPTO_FAIL); |
| } |
| p = cn_cbor_data_create( |
| pbOut + 1, (int)(cbSize / 2), CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(p != NULL, cbor_error); |
| CHECK_CONDITION_CBOR(cn_cbor_mapput_int(pkey, COSE_Key_EC_X, p, |
| CBOR_CONTEXT_PARAM_COMMA & cbor_error), |
| cbor_error); |
| p = NULL; |
| |
| if (FUseCompressed) { |
| p = cn_cbor_bool_create( |
| pbOut[0] & 1, CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(p != NULL, cbor_error); |
| CHECK_CONDITION_CBOR(cn_cbor_mapput_int(pkey, COSE_Key_EC_Y, p, |
| CBOR_CONTEXT_PARAM_COMMA & cbor_error), |
| cbor_error); |
| p = NULL; |
| } |
| else { |
| p = cn_cbor_data_create(pbOut + cbSize / 2 + 1, (int)(cbSize / 2), |
| CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| pbOut = NULL; // It is already part of the other one. |
| CHECK_CONDITION_CBOR(p != NULL, cbor_error); |
| CHECK_CONDITION_CBOR(cn_cbor_mapput_int(pkey, COSE_Key_EC_Y, p, |
| CBOR_CONTEXT_PARAM_COMMA & cbor_error), |
| cbor_error); |
| p = NULL; |
| } |
| |
| p = cn_cbor_int_create( |
| COSE_Key_Type_EC2, CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(p != NULL, cbor_error); |
| CHECK_CONDITION_CBOR(cn_cbor_mapput_int(pkey, COSE_Key_Type, p, |
| CBOR_CONTEXT_PARAM_COMMA & cbor_error), |
| cbor_error); |
| p = NULL; |
| |
| returnHere: |
| if (pbOut != NULL) { |
| COSE_FREE(pbOut, context); |
| } |
| if (p != NULL) { |
| CN_CBOR_FREE(p, context); |
| } |
| return pkey; |
| |
| errorReturn: |
| CN_CBOR_FREE(pkey, context); |
| pkey = NULL; |
| goto returnHere; |
| } |
| |
| /* |
| bool ECDSA_Sign(const cn_cbor * pKey) |
| { |
| byte * digest = NULL; |
| int digestLen = 0; |
| ECDSA_SIG * sig; |
| |
| EC_KEY * eckey = ECKey_From(pKey); |
| |
| sig = ECDSA_do_sign(digest, digestLen, eckey); |
| |
| return true; |
| } |
| */ |
| |
| bool ECDSA_Sign(COSE *pSigner, |
| int index, |
| const cn_cbor *pKey, |
| int cbitDigest, |
| const byte *rgbToSign, |
| size_t cbToSign, |
| cose_errback *perr) |
| { |
| EC_KEY *eckey = NULL; |
| byte rgbDigest[EVP_MAX_MD_SIZE]; |
| unsigned int cbDigest = sizeof(rgbDigest); |
| byte *pbSig = NULL; |
| const EVP_MD *digest; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pSigner->m_allocContext; |
| #endif |
| cn_cbor *p = NULL; |
| ECDSA_SIG *psig = NULL; |
| cn_cbor_errback cbor_error; |
| int cbR; |
| byte rgbSig[66]; |
| int cb; |
| |
| eckey = ECKey_From(pKey, &cbR, perr); |
| if (eckey == NULL) { |
| errorReturn: |
| if (pbSig != NULL) { |
| COSE_FREE(pbSig, context); |
| } |
| if (p != NULL) { |
| CN_CBOR_FREE(p, context); |
| } |
| if (eckey != NULL) { |
| EC_KEY_free(eckey); |
| } |
| return false; |
| } |
| |
| switch (cbitDigest) { |
| case 256: |
| digest = EVP_sha256(); |
| break; |
| case 512: |
| digest = EVP_sha512(); |
| break; |
| case 384: |
| digest = EVP_sha384(); |
| break; |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| } |
| |
| EVP_Digest(rgbToSign, cbToSign, rgbDigest, &cbDigest, digest, NULL); |
| |
| psig = ECDSA_do_sign(rgbDigest, cbDigest, eckey); |
| CHECK_CONDITION(psig != NULL, COSE_ERR_CRYPTO_FAIL); |
| |
| pbSig = COSE_CALLOC(cbR, 2, context); |
| CHECK_CONDITION(pbSig != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| const BIGNUM *r; |
| const BIGNUM *s; |
| ECDSA_SIG_get0(psig, &r, &s); |
| cb = BN_bn2bin(r, rgbSig); |
| CHECK_CONDITION(cb <= cbR, COSE_ERR_INVALID_PARAMETER); |
| memcpy(pbSig + cbR - cb, rgbSig, cb); |
| |
| cb = BN_bn2bin(s, rgbSig); |
| CHECK_CONDITION(cb <= cbR, COSE_ERR_INVALID_PARAMETER); |
| memcpy(pbSig + 2 * cbR - cb, rgbSig, cb); |
| |
| p = cn_cbor_data_create( |
| pbSig, cbR * 2, CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(p != NULL, cbor_error); |
| |
| CHECK_CONDITION( |
| _COSE_array_replace(pSigner, p, index, CBOR_CONTEXT_PARAM_COMMA NULL), |
| COSE_ERR_CBOR); |
| |
| pbSig = NULL; |
| |
| if (eckey != NULL) { |
| EC_KEY_free(eckey); |
| } |
| |
| return true; |
| } |
| |
| bool ECDSA_Verify(COSE *pSigner, |
| int index, |
| const cn_cbor *pKey, |
| int cbitDigest, |
| const byte *rgbToSign, |
| size_t cbToSign, |
| cose_errback *perr) |
| { |
| EC_KEY *eckey = NULL; |
| byte rgbDigest[EVP_MAX_MD_SIZE]; |
| unsigned int cbDigest = sizeof(rgbDigest); |
| const EVP_MD *digest; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pSigner->m_allocContext; |
| #endif |
| cn_cbor *p = NULL; |
| ECDSA_SIG *sig = NULL; |
| int cbR; |
| cn_cbor *pSig; |
| size_t cbSignature; |
| |
| BIGNUM *r, *s; |
| |
| eckey = ECKey_From(pKey, &cbR, perr); |
| if (eckey == NULL) { |
| errorReturn: |
| if (p != NULL) { |
| CN_CBOR_FREE(p, context); |
| } |
| if (eckey != NULL) { |
| EC_KEY_free(eckey); |
| } |
| if (sig != NULL) { |
| ECDSA_SIG_free(sig); |
| } |
| return false; |
| } |
| |
| switch (cbitDigest) { |
| case 256: |
| digest = EVP_sha256(); |
| break; |
| case 512: |
| digest = EVP_sha512(); |
| break; |
| case 384: |
| digest = EVP_sha384(); |
| break; |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| } |
| EVP_Digest(rgbToSign, cbToSign, rgbDigest, &cbDigest, digest, NULL); |
| |
| pSig = _COSE_arrayget_int(pSigner, index); |
| CHECK_CONDITION(pSig != NULL, COSE_ERR_INVALID_PARAMETER); |
| cbSignature = pSig->length; |
| |
| CHECK_CONDITION(cbSignature / 2 == (size_t)cbR, COSE_ERR_INVALID_PARAMETER); |
| r = BN_bin2bn(pSig->v.bytes, (int)cbSignature / 2, NULL); |
| CHECK_CONDITION(NULL != r, COSE_ERR_OUT_OF_MEMORY); |
| s = BN_bin2bn(pSig->v.bytes + cbSignature / 2, (int)cbSignature / 2, NULL); |
| CHECK_CONDITION(NULL != s, COSE_ERR_OUT_OF_MEMORY); |
| |
| sig = ECDSA_SIG_new(); |
| CHECK_CONDITION(sig != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| ECDSA_SIG_set0(sig, r, s); |
| |
| CHECK_CONDITION(ECDSA_do_verify(rgbDigest, cbDigest, sig, eckey) == 1, |
| COSE_ERR_CRYPTO_FAIL); |
| |
| if (eckey != NULL) { |
| EC_KEY_free(eckey); |
| } |
| if (sig != NULL) { |
| ECDSA_SIG_free(sig); |
| } |
| |
| return true; |
| } |
| |
| #ifdef USE_EDDSA |
| bool EdDSA_Sign(COSE *pSigner, |
| int index, |
| const cn_cbor *pKeyIn, |
| const byte *rgbToSign, |
| size_t cbToSign, |
| cose_errback *perr) |
| { |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pSigner->m_allocContext; |
| #endif |
| cn_cbor *p; |
| cn_cbor_errback cbor_error; |
| EVP_PKEY_CTX *keyCtx = NULL; |
| EVP_MD_CTX *mdCtx = NULL; |
| EVP_PKEY *pkey = NULL; |
| byte *pbSig = NULL; |
| int cbSig; |
| |
| p = cn_cbor_mapget_int(pKeyIn, COSE_Key_OPK_Curve); |
| if (p == NULL) { |
| errorReturn: |
| if (mdCtx != NULL) { |
| EVP_MD_CTX_free(mdCtx); |
| } |
| if (keyCtx != NULL) { |
| EVP_PKEY_CTX_free(keyCtx); |
| } |
| if (pkey != NULL) { |
| EVP_PKEY_free(pkey); |
| } |
| if (pbSig != NULL) { |
| COSE_FREE(pbSig, context); |
| } |
| return false; |
| } |
| |
| int type; |
| |
| switch (p->v.uint) { |
| case COSE_Curve_Ed25519: |
| type = EVP_PKEY_ED25519; |
| cbSig = 32 * 2; |
| break; |
| |
| case COSE_Curve_Ed448: |
| type = EVP_PKEY_ED448; |
| cbSig = 64 * 2; |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| } |
| |
| p = cn_cbor_mapget_int(pKeyIn, COSE_Key_EC_d); |
| CHECK_CONDITION(p != NULL, COSE_ERR_INVALID_PARAMETER); |
| |
| pkey = EVP_PKEY_new_raw_private_key(type, NULL, p->v.bytes, p->length); |
| CHECK_CONDITION(pkey != NULL, COSE_ERR_CRYPTO_FAIL); |
| |
| keyCtx = EVP_PKEY_CTX_new_id(type, NULL); |
| CHECK_CONDITION(keyCtx != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| mdCtx = EVP_MD_CTX_new(); |
| CHECK_CONDITION(mdCtx != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| CHECK_CONDITION(EVP_DigestSignInit(mdCtx, &keyCtx, NULL, NULL, pkey) == 1, |
| COSE_ERR_CRYPTO_FAIL); |
| keyCtx = NULL; |
| |
| pbSig = COSE_CALLOC(cbSig, 1, context); |
| CHECK_CONDITION(pbSig != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| size_t cb2 = cbSig; |
| CHECK_CONDITION( |
| EVP_DigestSign(mdCtx, pbSig, &cb2, rgbToSign, cbToSign) == 1, |
| COSE_ERR_CRYPTO_FAIL); |
| |
| p = cn_cbor_data_create( |
| pbSig, (int)cb2, CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION(p != NULL, COSE_ERR_OUT_OF_MEMORY); |
| pbSig = NULL; |
| |
| CHECK_CONDITION( |
| _COSE_array_replace(pSigner, p, index, CBOR_CONTEXT_PARAM_COMMA NULL), |
| COSE_ERR_CBOR); |
| |
| if (mdCtx != NULL) { |
| EVP_MD_CTX_free(mdCtx); |
| } |
| if (keyCtx != NULL) { |
| EVP_PKEY_CTX_free(keyCtx); |
| } |
| if (pkey != NULL) { |
| EVP_PKEY_free(pkey); |
| } |
| if (pbSig != NULL) { |
| COSE_FREE(pbSig, context); |
| } |
| |
| return true; |
| } |
| |
| bool EdDSA_Verify(COSE *pSigner, |
| int index, |
| const cn_cbor *pKey, |
| const byte *rgbToSign, |
| size_t cbToSign, |
| cose_errback *perr) |
| { |
| cn_cbor *pSig; |
| EVP_PKEY *pkey = NULL; |
| |
| cn_cbor *p = cn_cbor_mapget_int(pKey, COSE_Key_OPK_Curve); |
| if (p == NULL) { |
| errorReturn: |
| if (pkey != NULL) { |
| EVP_PKEY_free(pkey); |
| } |
| return false; |
| } |
| |
| int type; |
| |
| switch (p->v.uint) { |
| case COSE_Curve_Ed25519: |
| type = EVP_PKEY_ED25519; |
| break; |
| |
| case COSE_Curve_Ed448: |
| type = EVP_PKEY_ED448; |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| } |
| |
| p = cn_cbor_mapget_int(pKey, COSE_Key_OPK_X); |
| CHECK_CONDITION(p != NULL, COSE_ERR_INVALID_PARAMETER); |
| |
| pkey = EVP_PKEY_new_raw_public_key(type, NULL, p->v.bytes, p->length); |
| CHECK_CONDITION(pkey != NULL, COSE_ERR_CBOR); |
| |
| pSig = _COSE_arrayget_int(pSigner, index); |
| CHECK_CONDITION(pSig != NULL, COSE_ERR_INVALID_PARAMETER); |
| |
| EVP_MD_CTX *pmdCtx = EVP_MD_CTX_new(); |
| EVP_PKEY_CTX *keyCtx = EVP_PKEY_CTX_new_id(type, NULL); |
| |
| CHECK_CONDITION( |
| EVP_DigestVerifyInit(pmdCtx, &keyCtx, NULL, NULL, pkey) == 1, |
| COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION(EVP_DigestVerify(pmdCtx, pSig->v.bytes, pSig->length, |
| rgbToSign, cbToSign) == 1, |
| COSE_ERR_CRYPTO_FAIL); |
| |
| if (pmdCtx != NULL) { |
| EVP_MD_CTX_free(pmdCtx); |
| } |
| if (pkey != NULL) { |
| EVP_PKEY_free(pkey); |
| } |
| |
| return true; |
| } |
| #endif |
| |
| bool AES_KW_Decrypt(COSE_Enveloped *pcose, |
| const byte *pbKeyIn, |
| size_t cbitKey, |
| const byte *pbCipherText, |
| size_t cbCipherText, |
| byte *pbKeyOut, |
| int *pcbKeyOut, |
| cose_errback *perr) |
| { |
| byte rgbOut[512 / 8]; |
| AES_KEY key; |
| |
| UNUSED(pcose); |
| |
| CHECK_CONDITION(AES_set_decrypt_key(pbKeyIn, (int)cbitKey, &key) == 0, |
| COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION( |
| AES_unwrap_key(&key, NULL, rgbOut, pbCipherText, (int)cbCipherText), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| memcpy(pbKeyOut, rgbOut, cbCipherText - 8); |
| *pcbKeyOut = (int)(cbCipherText - 8); |
| |
| return true; |
| errorReturn: |
| return false; |
| } |
| |
| bool AES_KW_Encrypt(COSE_RecipientInfo *pcose, |
| const byte *pbKeyIn, |
| int cbitKey, |
| const byte *pbContent, |
| int cbContent, |
| cose_errback *perr) |
| { |
| byte *pbOut = NULL; |
| AES_KEY key; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pcose->m_encrypt.m_message.m_allocContext; |
| #endif |
| cn_cbor *cnTmp = NULL; |
| |
| pbOut = COSE_CALLOC(cbContent + 8, 1, context); |
| CHECK_CONDITION(pbOut != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| CHECK_CONDITION( |
| AES_set_encrypt_key(pbKeyIn, cbitKey, &key) == 0, COSE_ERR_CRYPTO_FAIL); |
| |
| CHECK_CONDITION(AES_wrap_key(&key, NULL, pbOut, pbContent, cbContent), |
| COSE_ERR_CRYPTO_FAIL); |
| |
| cnTmp = cn_cbor_data_create( |
| pbOut, (int)cbContent + 8, CBOR_CONTEXT_PARAM_COMMA NULL); |
| CHECK_CONDITION(cnTmp != NULL, COSE_ERR_CBOR); |
| pbOut = NULL; |
| CHECK_CONDITION(_COSE_array_replace(&pcose->m_encrypt.m_message, cnTmp, |
| INDEX_BODY, CBOR_CONTEXT_PARAM_COMMA NULL), |
| COSE_ERR_CBOR); |
| cnTmp = NULL; |
| |
| return true; |
| |
| errorReturn: |
| COSE_FREE(cnTmp, context); |
| if (pbOut != NULL) { |
| COSE_FREE(pbOut, context); |
| } |
| return false; |
| } |
| |
| void rand_bytes(byte *pb, size_t cb) |
| { |
| RAND_bytes(pb, (int)cb); |
| } |
| |
| /*! |
| * |
| * @param[in] pRecipent Pointer to the message object |
| * @param[in] ppKeyPrivate Address of key with private portion |
| * @param[in] pKeyPublic Address of the key w/o a private portion |
| * @param[in/out] ppbSecret pointer to buffer to hold the computed secret |
| * @param[in/out] pcbSecret size of the computed secret |
| * @param[in] context cbor allocation context structure |
| * @param[out] perr location to return error information |
| * @returns success of the function |
| */ |
| |
| bool ECDH_ComputeSecret(COSE *pRecipient, |
| cn_cbor **ppKeyPrivate, |
| const cn_cbor *pKeyPublic, |
| byte **ppbSecret, |
| size_t *pcbSecret, |
| CBOR_CONTEXT_COMMA cose_errback *perr) |
| { |
| EC_KEY *peckeyPrivate = NULL; |
| EC_KEY *peckeyPublic = NULL; |
| int cbGroup; |
| int cbsecret; |
| byte *pbsecret = NULL; |
| bool fRet = false; |
| |
| peckeyPublic = ECKey_From(pKeyPublic, &cbGroup, perr); |
| if (peckeyPublic == NULL) { |
| goto errorReturn; |
| } |
| |
| if (*ppKeyPrivate == NULL) { |
| { |
| cn_cbor *pCompress = _COSE_map_get_int( |
| pRecipient, COSE_Header_UseCompressedECDH, COSE_BOTH, perr); |
| if (pCompress == NULL) { |
| FUseCompressed = false; |
| } |
| else { |
| FUseCompressed = (pCompress->type == CN_CBOR_TRUE); |
| } |
| } |
| peckeyPrivate = EC_KEY_new(); |
| EC_KEY_set_group(peckeyPrivate, EC_KEY_get0_group(peckeyPublic)); |
| CHECK_CONDITION( |
| EC_KEY_generate_key(peckeyPrivate) == 1, COSE_ERR_CRYPTO_FAIL); |
| *ppKeyPrivate = |
| EC_FromKey(peckeyPrivate, CBOR_CONTEXT_PARAM_COMMA perr); |
| if (*ppKeyPrivate == NULL) { |
| goto errorReturn; |
| } |
| } |
| else { |
| peckeyPrivate = ECKey_From(*ppKeyPrivate, &cbGroup, perr); |
| if (peckeyPrivate == NULL) { |
| goto errorReturn; |
| } |
| } |
| |
| pbsecret = COSE_CALLOC(cbGroup, 1, context); |
| CHECK_CONDITION(pbsecret != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| cbsecret = ECDH_compute_key(pbsecret, cbGroup, |
| EC_KEY_get0_public_key(peckeyPublic), peckeyPrivate, NULL); |
| CHECK_CONDITION(cbsecret > 0, COSE_ERR_CRYPTO_FAIL); |
| |
| *ppbSecret = pbsecret; |
| *pcbSecret = cbsecret; |
| pbsecret = NULL; |
| |
| fRet = true; |
| |
| errorReturn: |
| if (pbsecret != NULL) { |
| COSE_FREE(pbsecret, context); |
| } |
| if (peckeyPublic != NULL) { |
| EC_KEY_free(peckeyPublic); |
| } |
| if (peckeyPrivate != NULL) { |
| EC_KEY_free(peckeyPrivate); |
| } |
| |
| return fRet; |
| } |
| |
| #endif // USE_OPEN_SSL |