blob: 3f8ea041364082ba0cf504a8c9bd14f48b1dc445 [file] [log] [blame]
#include <stdlib.h>
#include <memory.h>
#include "cose.h"
#include "cose_int.h"
#include "configure.h"
#include "crypto.h"
extern bool BuildContextBytes(COSE * pcose, int algID, size_t cbitKey, byte ** ppbContext, size_t * pcbContext, CBOR_CONTEXT_COMMA cose_errback * perr);
COSE* RecipientRoot = NULL;
/*! \private
* @brief Test if a HCOSE_RECIPIENT handle is valid
*
* Internal function to test if a recipient handle is valid.
* This will start returning invalid results and cause the code to
* crash if handles are not released before the memory that underlies them
* is deallocated. This is an issue of a block allocator is used since
* in that case it is common to allocate memory but never to de-allocate it
* and just do that in a single big block.
*
* @param h handle to be validated
* @returns result of check
*/
bool IsValidRecipientHandle(HCOSE_RECIPIENT h)
{
COSE_RecipientInfo * p = (COSE_RecipientInfo *)h;
if (p == NULL) return false;
return _COSE_IsInList(RecipientRoot, &p->m_encrypt.m_message);
}
HCOSE_RECIPIENT COSE_Recipient_Init(COSE_INIT_FLAGS flags, CBOR_CONTEXT_COMMA cose_errback * perr)
{
CHECK_CONDITION(flags == COSE_INIT_FLAGS_NONE, COSE_ERR_INVALID_PARAMETER);
COSE_RecipientInfo * pobj = (COSE_RecipientInfo *)COSE_CALLOC(1, sizeof(COSE_RecipientInfo), context);
CHECK_CONDITION(pobj != NULL, COSE_ERR_OUT_OF_MEMORY);
if (!_COSE_Init(flags, &pobj->m_encrypt.m_message, COSE_recipient_object, CBOR_CONTEXT_PARAM_COMMA perr)) {
_COSE_Recipient_Free(pobj);
return NULL;
}
_COSE_InsertInList(&RecipientRoot, &pobj->m_encrypt.m_message);
return (HCOSE_RECIPIENT)pobj;
errorReturn:
return NULL;
}
bool COSE_Recipient_Free(HCOSE_RECIPIENT hRecipient)
{
if (IsValidRecipientHandle(hRecipient)) {
COSE_RecipientInfo * p = (COSE_RecipientInfo *)hRecipient;
_COSE_RemoveFromList(&RecipientRoot, &p->m_encrypt.m_message);
_COSE_Recipient_Free(p);
return true;
}
return false;
}
HCOSE_RECIPIENT COSE_Recipient_from_shared_secret(byte * rgbKey, int cbKey, byte * rgbKid, int cbKid, CBOR_CONTEXT_COMMA cose_errback * perr)
{
HCOSE_RECIPIENT hRecipient = NULL;
hRecipient = COSE_Recipient_Init(0, CBOR_CONTEXT_PARAM_COMMA perr);
if (hRecipient == NULL) goto errorReturn;
if (!COSE_Recipient_SetKey_secret(hRecipient, rgbKey, cbKey, rgbKid, cbKid, perr)) goto errorReturn;
return hRecipient;
errorReturn:
if (hRecipient != NULL) COSE_Recipient_Free(hRecipient);
return NULL;
}
COSE_RecipientInfo * _COSE_Recipient_Init_From_Object(cn_cbor * cbor, CBOR_CONTEXT_COMMA cose_errback * perr)
{
COSE_RecipientInfo * pRecipient = NULL;
pRecipient = (COSE_RecipientInfo *)COSE_CALLOC(1, sizeof(COSE_RecipientInfo), context);
CHECK_CONDITION(pRecipient != NULL, COSE_ERR_OUT_OF_MEMORY);
#ifdef USE_ARRAY
CHECK_CONDITION(cbor->type == CN_CBOR_ARRAY, COSE_ERR_INVALID_PARAMETER);
#else
if (cbor->type != CN_CBOR_MAP) {
if (errp != NULL) errp->err = COSE_ERR_INVALID_PARAMETER;
COSE_FREE(pRecipient, context);
return NULL;
}
#endif
if (_COSE_Enveloped_Init_From_Object(cbor, &pRecipient->m_encrypt, CBOR_CONTEXT_PARAM_COMMA perr) == NULL) {
goto errorReturn;
}
_COSE_InsertInList(&RecipientRoot, &pRecipient->m_encrypt.m_message);
return pRecipient;
errorReturn:
if (pRecipient != NULL) _COSE_Recipient_Free(pRecipient);
return NULL;
}
void _COSE_Recipient_Free(COSE_RecipientInfo * pRecipient)
{
if (pRecipient->m_encrypt.m_message.m_refCount > 1) {
pRecipient->m_encrypt.m_message.m_refCount--;
return;
}
COSE_FREE(pRecipient, &pRecipient->m_encrypt.m_message.m_allocContext);
return;
}
/**
* Perform an AES-CCM Decryption operation
*
* @param[in] COSE * Pointer to COSE Encryption context object
* @param[in] int Alorithm key is being generated for
* @param[in] cn_cbor * Key used for operation
* @param[out] byte * Buffer to return new key in
* @param[in] size_t Size of key to create in bits
* @param[in] size_t Size of digest function
* @param[in] cbor_context * Allocation context
* @param[out] cose_errback * Returned error information
* @return Did the function succeed?
*/
static bool HKDF_X(COSE * pCose, bool fHMAC, bool fECDH, bool fStatic, bool fSend, int algResult, const cn_cbor * pKey, byte * pbKey, size_t cbitKey, size_t cbitHash, CBOR_CONTEXT_COMMA cose_errback * perr)
{
byte * pbContext = NULL;
size_t cbContext;
bool fRet = false;
const cn_cbor * cn;
byte rgbDigest[512 / 8];
size_t cbDigest;
byte * pbSecret = NULL;
size_t cbSecret = 0;
if (!BuildContextBytes(pCose, algResult, cbitKey, &pbContext, &cbContext, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
cn = cn_cbor_mapget_int(pKey, COSE_Key_Type);
CHECK_CONDITION((cn != NULL) && (cn->type == CN_CBOR_UINT), COSE_ERR_INVALID_PARAMETER);
if (fECDH) {
cn_cbor * pkeyMessage;
pkeyMessage = _COSE_map_get_int(pCose, fStatic ? COSE_Header_ECDH_STATIC : COSE_Header_ECDH_EPHEMERAL, COSE_BOTH, perr);
CHECK_CONDITION(pKey != NULL, COSE_ERR_INVALID_PARAMETER);
if (fSend) {
if (!ECDH_ComputeSecret(pCose, &pkeyMessage, pKey, &pbSecret, &cbSecret, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (pkeyMessage->parent == NULL) {
if (!_COSE_map_put(pCose, COSE_Header_ECDH_EPHEMERAL, pkeyMessage, COSE_UNPROTECT_ONLY, perr)) goto errorReturn;
}
}
else {
CHECK_CONDITION(pkeyMessage != NULL, COSE_ERR_INVALID_PARAMETER);
if (!ECDH_ComputeSecret(pCose, (cn_cbor **)&pKey, pkeyMessage, &pbSecret, &cbSecret, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
}
}
else {
CHECK_CONDITION(cn->v.sint == 4, COSE_ERR_INVALID_PARAMETER);
cn = cn_cbor_mapget_int(pKey, -1);
CHECK_CONDITION((cn != NULL) && (cn->type == CN_CBOR_BYTES), COSE_ERR_INVALID_PARAMETER);
pbSecret = (byte *) cn->v.bytes;
cbSecret = cn->length;
}
if (fHMAC) {
if (!HKDF_Extract(pCose, pbSecret, cbSecret, cbitHash, rgbDigest, &cbDigest, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!HKDF_Expand(pCose, cbitHash, rgbDigest, cbDigest, pbContext, cbContext, pbKey, cbitKey / 8, perr)) goto errorReturn;
}
else {
if (!HKDF_AES_Expand(pCose, cbitHash, pbSecret, cbSecret, pbContext, cbContext, pbKey, cbitKey / 8, perr)) goto errorReturn;
}
fRet = true;
errorReturn:
if (fECDH && pbSecret != NULL) {
memset(pbSecret, 0, cbSecret);
COSE_FREE(pbSecret, context);
}
memset(rgbDigest, 0, sizeof(rgbDigest));
if (pbContext != NULL) COSE_FREE(pbContext, context);
return fRet;
}
bool _COSE_Recipient_decrypt(COSE_RecipientInfo * pRecip, int algIn, int cbitKey, byte * pbKeyIn, cose_errback * perr)
{
int alg;
const cn_cbor * cn = NULL;
COSE_RecipientInfo * pRecip2;
byte * pbKey = pbKeyIn;
#ifdef USE_CBOR_CONTEXT
cn_cbor_context * context;
#endif
byte * pbAuthData = NULL;
byte * pbProtected = NULL;
COSE_Enveloped * pcose = &pRecip->m_encrypt;
cn_cbor * cnBody = NULL;
byte * pbContext = NULL;
byte rgbKey[256 / 8];
byte * pbSecret = NULL;
int cbKey2;
#ifdef USE_CBOR_CONTEXT
context = &pcose->m_message.m_allocContext;
#endif
cn = _COSE_map_get_int(&pRecip->m_encrypt.m_message, COSE_Header_Algorithm, COSE_BOTH, perr);
if (cn == NULL) {
errorReturn:
if (pbContext != NULL) COSE_FREE(pbContext, context);
if (pbProtected != NULL) COSE_FREE(pbProtected, context);
if (pbAuthData != NULL) COSE_FREE(pbAuthData, context);
if (pbSecret != NULL) COSE_FREE(pbSecret, context);
return false;
}
CHECK_CONDITION((cn->type == CN_CBOR_UINT) || (cn->type == CN_CBOR_INT), COSE_ERR_INVALID_PARAMETER);
alg = (int)cn->v.uint;
CHECK_CONDITION(pbKey != NULL, COSE_ERR_INVALID_PARAMETER);
switch (alg) {
case COSE_Algorithm_Direct:
CHECK_CONDITION(pRecip->m_pkey != NULL, COSE_ERR_INVALID_PARAMETER);
cn = cn_cbor_mapget_int(pRecip->m_pkey, -1);
CHECK_CONDITION((cn != NULL) && (cn->type == CN_CBOR_BYTES), COSE_ERR_INVALID_PARAMETER);
CHECK_CONDITION((cn->length == (unsigned int)cbitKey / 8), COSE_ERR_INVALID_PARAMETER);
memcpy(pbKey, cn->v.bytes, cn->length);
return true;
case COSE_Algorithm_AES_KW_128:
case COSE_Algorithm_AES_KW_192:
case COSE_Algorithm_AES_KW_256:
break;
case COSE_Algorithm_Direct_HKDF_AES_128:
case COSE_Algorithm_Direct_HKDF_AES_256:
case COSE_Algorithm_Direct_HKDF_HMAC_SHA_256:
case COSE_Algorithm_Direct_HKDF_HMAC_SHA_512:
break;
case COSE_Algorithm_ECDH_ES_HKDF_256:
case COSE_Algorithm_ECDH_ES_HKDF_512:
case COSE_Algorithm_ECDH_SS_HKDF_256:
case COSE_Algorithm_ECDH_SS_HKDF_512:
break;
case COSE_Algorithm_ECDH_ES_A128KW:
case COSE_Algorithm_ECDH_ES_A192KW:
case COSE_Algorithm_ECDH_ES_A256KW:
break;
case COSE_Algorithm_ECDH_SS_A128KW:
case COSE_Algorithm_ECDH_SS_A192KW:
case COSE_Algorithm_ECDH_SS_A256KW:
break;
default:
FAIL_CONDITION(COSE_ERR_UNKNOWN_ALGORITHM);
break;
}
// If there is a recipient - ask it for the key
for (pRecip2 = pcose->m_recipientFirst; pRecip2 != NULL; pRecip2 = pRecip->m_recipientNext) {
if (_COSE_Recipient_decrypt(pRecip2, alg, cbitKey, pbKey, perr)) break;
}
cnBody = _COSE_arrayget_int(&pcose->m_message, INDEX_BODY);
CHECK_CONDITION(cnBody != NULL, COSE_ERR_INVALID_PARAMETER);
switch (alg) {
case COSE_Algorithm_AES_KW_128:
case COSE_Algorithm_AES_KW_192:
case COSE_Algorithm_AES_KW_256:
CHECK_CONDITION(pRecip->m_pkey != NULL, COSE_ERR_INVALID_PARAMETER);
int x = cbitKey / 8;
cn = cn_cbor_mapget_int(pRecip->m_pkey, -1);
CHECK_CONDITION((cn != NULL) && (cn->type == CN_CBOR_BYTES), COSE_ERR_INVALID_PARAMETER);
if (!AES_KW_Decrypt((COSE_Enveloped *)pcose, cn->v.bytes, cn->length * 8, cnBody->v.bytes, cnBody->length, pbKey, &x, perr)) goto errorReturn;
break;
case COSE_Algorithm_Direct_HKDF_HMAC_SHA_256:
if (!HKDF_X(&pcose->m_message, true, false, false, false, algIn, pRecip->m_pkey, pbKey, cbitKey, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_Direct_HKDF_HMAC_SHA_512:
if (!HKDF_X(&pcose->m_message, true, false, false, false, algIn, pRecip->m_pkey, pbKey, cbitKey, 512, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_Direct_HKDF_AES_128:
if (!HKDF_X(&pcose->m_message, false, false, false, false, algIn, pRecip->m_pkey, pbKey, cbitKey, 128, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_Direct_HKDF_AES_256:
if (!HKDF_X(&pcose->m_message, false, false, false, false, algIn, pRecip->m_pkey, pbKey, cbitKey, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_ES_HKDF_256:
if (!HKDF_X(&pcose->m_message, true, true, false, false, algIn, pRecip->m_pkey, pbKey, cbitKey, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_ES_HKDF_512:
if (!HKDF_X(&pcose->m_message, true, true, false, false, algIn, pRecip->m_pkey, pbKey, cbitKey, 512, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_SS_HKDF_256:
if (!HKDF_X(&pcose->m_message, true, true, true, false, algIn, pRecip->m_pkey, pbKey, cbitKey, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_SS_HKDF_512:
if (!HKDF_X(&pcose->m_message, true, true, true, false, algIn, pRecip->m_pkey, pbKey, cbitKey, 512, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_ES_A128KW:
if (!HKDF_X(&pcose->m_message, true, true, false, false, COSE_Algorithm_AES_KW_128, pRecip->m_pkey, rgbKey, 128, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!AES_KW_Decrypt((COSE_Enveloped *)pcose, rgbKey, 128, cnBody->v.bytes, cnBody->length, pbKey, &cbKey2, perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_ES_A192KW:
if (!HKDF_X(&pcose->m_message, true, true, false, false, COSE_Algorithm_AES_KW_192, pRecip->m_pkey, rgbKey, 192, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!AES_KW_Decrypt((COSE_Enveloped *)pcose, rgbKey, 192, cnBody->v.bytes, cnBody->length, pbKey, &cbKey2, perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_ES_A256KW:
if (!HKDF_X(&pcose->m_message, true, true, false, false, COSE_Algorithm_AES_KW_256, pRecip->m_pkey, rgbKey, 256, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!AES_KW_Decrypt((COSE_Enveloped *)pcose, rgbKey, 256, cnBody->v.bytes, cnBody->length, pbKey, &cbKey2, perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_SS_A128KW:
if (!HKDF_X(&pcose->m_message, true, true, true, false, COSE_Algorithm_AES_KW_128, pRecip->m_pkey, rgbKey, 128, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!AES_KW_Decrypt((COSE_Enveloped *)pcose, rgbKey, 128, cnBody->v.bytes, cnBody->length, pbKey, &cbKey2, perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_SS_A192KW:
if (!HKDF_X(&pcose->m_message, true, true, true, false, COSE_Algorithm_AES_KW_192, pRecip->m_pkey, rgbKey, 192, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!AES_KW_Decrypt((COSE_Enveloped *)pcose, rgbKey, 192, cnBody->v.bytes, cnBody->length, pbKey, &cbKey2, perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_SS_A256KW:
if (!HKDF_X(&pcose->m_message, true, true, true, false, COSE_Algorithm_AES_KW_256, pRecip->m_pkey, rgbKey, 256, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!AES_KW_Decrypt((COSE_Enveloped *)pcose, rgbKey, 256, cnBody->v.bytes, cnBody->length, pbKey, &cbKey2, perr)) goto errorReturn;
break;
default:
FAIL_CONDITION(COSE_ERR_UNKNOWN_ALGORITHM);
break;
}
return true;
}
bool _COSE_Recipient_encrypt(COSE_RecipientInfo * pRecipient, const byte * pbContent, size_t cbContent, cose_errback * perr)
{
int alg;
int t;
COSE_RecipientInfo * pri;
const cn_cbor * cn_Alg = NULL;
byte * pbAuthData = NULL;
cn_cbor * ptmp = NULL;
size_t cbitKey;
#ifdef USE_CBOR_CONTEXT
cn_cbor_context * context = NULL;
#endif
cn_cbor_errback cbor_error;
bool fRet = false;
byte * pbContext = NULL;
byte rgbKey[256 / 8];
byte * pbSecret = NULL;
byte * pbKey = NULL;
size_t cbKey = 0;
#ifdef USE_CBOR_CONTEXT
context = &pRecipient->m_encrypt.m_message.m_allocContext;
#endif // USE_CBOR_CONTEXT
cn_Alg = _COSE_map_get_int(&pRecipient->m_encrypt.m_message, COSE_Header_Algorithm, COSE_BOTH, perr);
if (cn_Alg == NULL) goto errorReturn;
CHECK_CONDITION((cn_Alg->type == CN_CBOR_UINT) || (cn_Alg->type == CN_CBOR_INT), COSE_ERR_INVALID_PARAMETER);
alg = (int)cn_Alg->v.uint;
// Get the key size
switch (alg) {
case COSE_Algorithm_Direct:
case COSE_Algorithm_Direct_HKDF_HMAC_SHA_256:
case COSE_Algorithm_Direct_HKDF_HMAC_SHA_512:
case COSE_Algorithm_Direct_HKDF_AES_128:
case COSE_Algorithm_Direct_HKDF_AES_256:
case COSE_Algorithm_ECDH_ES_HKDF_256:
case COSE_Algorithm_ECDH_ES_HKDF_512:
case COSE_Algorithm_ECDH_SS_HKDF_256:
case COSE_Algorithm_ECDH_SS_HKDF_512:
// This is a NOOP
cbitKey = 0;
CHECK_CONDITION(pRecipient->m_encrypt.m_recipientFirst == NULL, COSE_ERR_INVALID_PARAMETER);
break;
case COSE_Algorithm_AES_KW_128:
case COSE_Algorithm_ECDH_ES_A128KW:
case COSE_Algorithm_ECDH_SS_A128KW:
cbitKey = 128;
break;
case COSE_Algorithm_AES_KW_192:
case COSE_Algorithm_ECDH_ES_A192KW:
case COSE_Algorithm_ECDH_SS_A192KW:
cbitKey = 192;
break;
case COSE_Algorithm_AES_KW_256:
case COSE_Algorithm_ECDH_ES_A256KW:
case COSE_Algorithm_ECDH_SS_A256KW:
cbitKey = 256;
break;
default:
FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER);
}
// If we are doing direct encryption - then recipient generates the key
if (pRecipient->m_encrypt.m_recipientFirst != NULL) {
t = 0;
for (pri = pRecipient->m_encrypt.m_recipientFirst; pri != NULL; pri = pri->m_recipientNext) {
if (pri->m_encrypt.m_message.m_flags & 1) {
t |= 1;
pbKey = _COSE_RecipientInfo_generateKey(pri, alg, cbitKey, perr);
if (pbKey == NULL) goto errorReturn;
cbKey = cbitKey / 8;
}
else {
t |= 2;
}
}
CHECK_CONDITION(t != 3, COSE_ERR_INVALID_PARAMETER);
}
// Do we need to generate a random key at this point -
// This is only true if we both haven't done it and and we have a recipient to encrypt it.
if ((pRecipient->m_pkey!= NULL)) {
pbKey = (byte *)COSE_CALLOC(cbitKey / 8, 1, context);
CHECK_CONDITION(pbKey != NULL, COSE_ERR_OUT_OF_MEMORY);
cbKey = cbitKey / 8;
rand_bytes(pbKey, cbKey);
}
// Build protected headers
const cn_cbor * cbProtected = _COSE_encode_protected(&pRecipient->m_encrypt.m_message, perr);
if (cbProtected == NULL) goto errorReturn;
// Build authenticated data
size_t cbAuthData = 0;
if (!_COSE_Encrypt_Build_AAD(&pRecipient->m_encrypt.m_message, &pbAuthData, &cbAuthData, "Recipient", perr)) goto errorReturn;
switch (alg) {
case COSE_Algorithm_Direct:
case COSE_Algorithm_Direct_HKDF_HMAC_SHA_256:
case COSE_Algorithm_Direct_HKDF_HMAC_SHA_512:
case COSE_Algorithm_Direct_HKDF_AES_128:
case COSE_Algorithm_Direct_HKDF_AES_256:
case COSE_Algorithm_ECDH_ES_HKDF_256:
case COSE_Algorithm_ECDH_ES_HKDF_512:
case COSE_Algorithm_ECDH_SS_HKDF_256:
case COSE_Algorithm_ECDH_SS_HKDF_512:
ptmp = cn_cbor_data_create(NULL, 0, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(ptmp != NULL, cbor_error);
CHECK_CONDITION_CBOR(_COSE_array_replace(&pRecipient->m_encrypt.m_message, ptmp, INDEX_BODY, CBOR_CONTEXT_PARAM_COMMA &cbor_error), cbor_error);
ptmp = NULL;
break;
case COSE_Algorithm_AES_KW_128:
case COSE_Algorithm_AES_KW_192:
case COSE_Algorithm_AES_KW_256:
if (pRecipient->m_pkey != NULL) {
cn_cbor * pK = cn_cbor_mapget_int(pRecipient->m_pkey, -1);
CHECK_CONDITION(pK != NULL, COSE_ERR_INVALID_PARAMETER);
if (!AES_KW_Encrypt(pRecipient, pK->v.bytes, (int) pK->length*8, pbContent, (int) cbContent, perr)) goto errorReturn;
}
else {
if (!AES_KW_Encrypt(pRecipient, NULL, 0, pbContent, (int) cbContent, perr)) goto errorReturn;
}
break;
case COSE_Algorithm_ECDH_ES_A128KW:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, true, true, false, true, COSE_Algorithm_AES_KW_128, pRecipient->m_pkey, rgbKey, 128, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!AES_KW_Encrypt(pRecipient, rgbKey, 128, pbContent, (int)cbContent, perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_ES_A192KW:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, true, true, false, true, COSE_Algorithm_AES_KW_192, pRecipient->m_pkey, rgbKey, 192, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!AES_KW_Encrypt(pRecipient, rgbKey, 192, pbContent, (int)cbContent, perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_ES_A256KW:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, true, true, false, true, COSE_Algorithm_AES_KW_256, pRecipient->m_pkey, rgbKey, 256, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!AES_KW_Encrypt(pRecipient, rgbKey, 256, pbContent, (int)cbContent, perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_SS_A128KW:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, true, true, true, true, COSE_Algorithm_AES_KW_128, pRecipient->m_pkey, rgbKey, 128, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!AES_KW_Encrypt(pRecipient, rgbKey, 128, pbContent, (int)cbContent, perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_SS_A192KW:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, true, true, true, true, COSE_Algorithm_AES_KW_192, pRecipient->m_pkey, rgbKey, 192, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!AES_KW_Encrypt(pRecipient, rgbKey, 192, pbContent, (int)cbContent, perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_SS_A256KW:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, true, true, true, true, COSE_Algorithm_AES_KW_256, pRecipient->m_pkey, rgbKey, 256, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
if (!AES_KW_Encrypt(pRecipient, rgbKey, 256, pbContent, (int)cbContent, perr)) goto errorReturn;
break;
default:
FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER);
}
for (pri = pRecipient->m_encrypt.m_recipientFirst; pri != NULL; pri = pri->m_recipientNext) {
if (!_COSE_Recipient_encrypt(pri, pbKey, cbKey, perr)) goto errorReturn;
}
// Figure out the clean up
fRet = true;
errorReturn:
memset(rgbKey, 0, sizeof(rgbKey));
if (pbKey != NULL) {
memset(pbKey, 0, cbKey);
COSE_FREE(pbKey, context);
}
if (pbSecret != NULL) COSE_FREE(pbSecret, context);
if (pbContext != NULL) COSE_FREE(pbContext, context);
if (pbAuthData != NULL) COSE_FREE(pbAuthData, context);
if (ptmp != NULL) cn_cbor_free(ptmp CBOR_CONTEXT_PARAM);
return fRet;
}
byte * _COSE_RecipientInfo_generateKey(COSE_RecipientInfo * pRecipient, int algIn, size_t cbitKeySize, cose_errback * perr)
{
int alg;
const cn_cbor * cn_Alg = _COSE_map_get_int(&pRecipient->m_encrypt.m_message, COSE_Header_Algorithm, COSE_BOTH, perr);
byte * pbContext = NULL;
byte * pb = NULL;
#ifdef USE_CBOR_CONTEXT
cn_cbor_context * context = &pRecipient->m_encrypt.m_message.m_allocContext;
#endif
const cn_cbor * pK;
byte *pbSecret = NULL;
CHECK_CONDITION(cn_Alg != NULL, COSE_ERR_INVALID_PARAMETER);
CHECK_CONDITION((cn_Alg->type == CN_CBOR_UINT) || (cn_Alg->type == CN_CBOR_INT), COSE_ERR_INVALID_PARAMETER);
alg = (int)cn_Alg->v.uint;
_COSE_encode_protected(&pRecipient->m_encrypt.m_message, perr);
pb = COSE_CALLOC(cbitKeySize / 8, 1, context);
CHECK_CONDITION(pb != NULL, COSE_ERR_OUT_OF_MEMORY);
switch (alg) {
case COSE_Algorithm_Direct:
CHECK_CONDITION(pRecipient->m_pkey != NULL, COSE_ERR_INVALID_PARAMETER);
pK = cn_cbor_mapget_int(pRecipient->m_pkey, -1);
CHECK_CONDITION((pK != NULL) && (pK->type == CN_CBOR_BYTES), COSE_ERR_INVALID_PARAMETER);
CHECK_CONDITION(pK->length == cbitKeySize / 8, COSE_ERR_INVALID_PARAMETER);
memcpy(pb, pK->v.bytes, cbitKeySize / 8);
break;
case COSE_Algorithm_Direct_HKDF_HMAC_SHA_256:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, true, false, false, true, algIn, pRecipient->m_pkey, pb, cbitKeySize, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_Direct_HKDF_HMAC_SHA_512:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, true, false, false, true, algIn, pRecipient->m_pkey, pb, cbitKeySize, 512, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_Direct_HKDF_AES_128:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, false, false, false, true, algIn, pRecipient->m_pkey, pb, cbitKeySize, 128, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_Direct_HKDF_AES_256:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, false, false, false, true, algIn, pRecipient->m_pkey, pb, cbitKeySize, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_ES_HKDF_256:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, true, true, false, true, algIn, pRecipient->m_pkey, pb, cbitKeySize, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_ES_HKDF_512:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, true, true, false, true, algIn, pRecipient->m_pkey, pb, cbitKeySize, 512, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_SS_HKDF_256:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, true, true, true, true, algIn, pRecipient->m_pkey, pb, cbitKeySize, 256, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
case COSE_Algorithm_ECDH_SS_HKDF_512:
if (!HKDF_X(&pRecipient->m_encrypt.m_message, true, true, true, true, algIn, pRecipient->m_pkey, pb, cbitKeySize, 512, CBOR_CONTEXT_PARAM_COMMA perr)) goto errorReturn;
break;
default:
FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER);
}
if (pbSecret != NULL) COSE_FREE(pbSecret, context);
if (pbContext != NULL) COSE_FREE(pbContext, context);
return pb;
errorReturn:
if (pbSecret != NULL) COSE_FREE(pbSecret, context);
if (pbContext != NULL) COSE_FREE(pbContext, context);
if (pb != NULL) COSE_FREE(pb, context);
return NULL;
}
bool COSE_Recipient_SetKey_secret(HCOSE_RECIPIENT hRecipient, const byte * rgbKey, int cbKey, const byte * rgbKid, int cbKid, cose_errback * perr)
{
COSE_RecipientInfo * p;
cn_cbor * cn_Temp = NULL;
cn_cbor * cnTemp = NULL;
cn_cbor_errback cbor_error;
byte * pbTemp = NULL;
byte * pbKey = NULL;
#ifdef USE_CBOR_CONTEXT
cn_cbor_context * context;
#endif
if (!IsValidRecipientHandle(hRecipient) || (rgbKey == NULL)) {
if (perr != NULL) perr->err = COSE_ERR_CBOR;
return false;
}
p = (COSE_RecipientInfo *)hRecipient;
#ifdef USE_CBOR_CONTEXT
context = &p->m_encrypt.m_message.m_allocContext;
#endif
cn_Temp = cn_cbor_int_create(COSE_Algorithm_Direct, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cn_Temp != NULL, cbor_error);
if (!COSE_Recipient_map_put(hRecipient, COSE_Header_Algorithm, cn_Temp, COSE_UNPROTECT_ONLY, perr)) goto errorReturn;
cn_Temp = NULL;
if (cbKid > 0) {
pbTemp = (byte *)COSE_CALLOC(cbKid, 1, context);
CHECK_CONDITION(pbTemp != NULL, COSE_ERR_OUT_OF_MEMORY);
memcpy(pbTemp, rgbKid, cbKid);
cnTemp = cn_cbor_data_create(pbTemp, cbKid, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnTemp != NULL, cbor_error);
pbTemp = NULL;
if (!COSE_Recipient_map_put(hRecipient, COSE_Header_KID, cnTemp, COSE_UNPROTECT_ONLY, perr)) goto errorReturn;
}
pbKey = (byte *)COSE_CALLOC(cbKey, 1, context);
CHECK_CONDITION(pbKey != NULL, COSE_ERR_OUT_OF_MEMORY);
memcpy(pbKey, rgbKey, cbKey);
cn_Temp = cn_cbor_map_create(CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cn_Temp != NULL, cbor_error);
cnTemp = cn_cbor_int_create(4, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnTemp != NULL, cbor_error);
CHECK_CONDITION_CBOR(cn_cbor_mapput_int(cn_Temp, COSE_Key_Type, cnTemp, CBOR_CONTEXT_PARAM_COMMA &cbor_error), cbor_error);
cnTemp = NULL;
cnTemp = cn_cbor_data_create(pbKey, cbKey, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnTemp != NULL, cbor_error);
pbKey = NULL;
CHECK_CONDITION_CBOR(cn_cbor_mapput_int(cn_Temp, -1, cnTemp, CBOR_CONTEXT_PARAM_COMMA &cbor_error), cbor_error);
cnTemp = NULL;
if (!COSE_Recipient_SetKey(hRecipient, cn_Temp, perr)) goto errorReturn;
cn_Temp = NULL;
return true;
errorReturn:
if (cn_Temp != NULL) CN_CBOR_FREE(cn_Temp, context);
if (cnTemp != NULL) CN_CBOR_FREE(cnTemp, context);
if (pbTemp != NULL) COSE_FREE(pbTemp, context);
if (pbKey != NULL) COSE_FREE(pbKey, context);
return false;
}
bool COSE_Recipient_SetKey(HCOSE_RECIPIENT h, const cn_cbor * pKey, cose_errback * perror)
{
COSE_RecipientInfo * p;
if (!IsValidRecipientHandle(h) || (pKey == NULL)) {
if (perror != NULL) perror->err = COSE_ERR_INVALID_PARAMETER;
return false;
}
p = (COSE_RecipientInfo *)h;
p->m_pkey = pKey;
return true;
}
/*!
* @brief Set the application external data for authentication
*
* Recipient data objects support the authentication of external application
* supplied data. This function is provided to supply that data to the library.
*
* The external data is not copied, nor will be it freed when the handle is released.
*
* @param hcose Handle for the COSE recipient data object
* @param pbEternalData point to the external data
* @param cbExternalData size of the external data
* @param perr location to return errors
* @return result of the operation.
*/
bool COSE_Recipient_SetExternal(HCOSE_RECIPIENT hcose, const byte * pbExternalData, size_t cbExternalData, cose_errback * perr)
{
if (!IsValidRecipientHandle(hcose)) {
if (perr != NULL) perr->err = COSE_ERR_INVALID_PARAMETER;
return false;
}
return _COSE_SetExternal(&((COSE_RecipientInfo *)hcose)->m_encrypt.m_message, pbExternalData, cbExternalData, perr);
}
bool COSE_Recipient_map_put(HCOSE_RECIPIENT h, int key, cn_cbor * value, int flags, cose_errback * perror)
{
if (!IsValidRecipientHandle(h) || (value == NULL)) {
if (perror != NULL) perror->err = COSE_ERR_INVALID_PARAMETER;
return false;
}
if (!_COSE_map_put(&((COSE_RecipientInfo *)h)->m_encrypt.m_message, key, value, flags, perror)) return false;
if (key == COSE_Header_Algorithm) {
if (value->type == CN_CBOR_INT) {
switch (value->v.uint) {
case COSE_Algorithm_Direct:
case COSE_Algorithm_Direct_HKDF_AES_128:
case COSE_Algorithm_Direct_HKDF_AES_256:
case COSE_Algorithm_Direct_HKDF_HMAC_SHA_256:
case COSE_Algorithm_Direct_HKDF_HMAC_SHA_512:
case COSE_Algorithm_ECDH_ES_HKDF_256:
case COSE_Algorithm_ECDH_ES_HKDF_512:
case COSE_Algorithm_ECDH_SS_HKDF_256:
case COSE_Algorithm_ECDH_SS_HKDF_512:
((COSE_RecipientInfo *)h)->m_encrypt.m_message.m_flags |= 1;
break;
default:
((COSE_RecipientInfo *)h)->m_encrypt.m_message.m_flags &= ~1;
break;
}
}
else {
((COSE_RecipientInfo *)h)->m_encrypt.m_message.m_flags &= ~1;
}
}
return true;
}
byte RgbDontUse4[8 * 1024];
bool BuildContextBytes(COSE * pcose, int algID, size_t cbitKey, byte ** ppbContext, size_t * pcbContext, CBOR_CONTEXT_COMMA cose_errback * perr)
{
cn_cbor * pArray;
cn_cbor_errback cbor_error;
bool fReturn = false;
cn_cbor * cnT = NULL;
cn_cbor * cnArrayT = NULL;
cn_cbor * cnParam;
byte * pbContext = NULL;
size_t cbContext;
pArray = cn_cbor_array_create(CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(pArray != NULL, cbor_error);
cnT = cn_cbor_int_create(algID, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnT != NULL, cbor_error);
CHECK_CONDITION_CBOR(cn_cbor_array_append(pArray, cnT, &cbor_error), cbor_error);
cnT = NULL;
cnArrayT = cn_cbor_array_create(CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnArrayT != NULL, cbor_error);
cnParam = _COSE_map_get_int(pcose, COSE_Header_KDF_U_nonce, COSE_BOTH, perr);
if (cnParam != NULL) {
cnT = cn_cbor_clone(cnParam, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnT != NULL, cbor_error);
CHECK_CONDITION_CBOR(cn_cbor_array_append(cnArrayT, cnT, &cbor_error), cbor_error);
cnT = NULL;
cnParam = NULL;
}
cnParam = _COSE_map_get_int(pcose, COSE_Header_KDF_U_name, COSE_BOTH, perr);
if (cnParam != NULL) {
cnT = cn_cbor_clone(cnParam, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnT != NULL, cbor_error);
CHECK_CONDITION_CBOR(cn_cbor_array_append(cnArrayT, cnT, &cbor_error), cbor_error);
cnT = NULL;
cnParam = NULL;
}
cnParam = _COSE_map_get_int(pcose, COSE_Header_KDF_U_other, COSE_BOTH, perr);
if (cnParam != NULL) {
cnT = cn_cbor_clone(cnParam, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnT != NULL, cbor_error);
CHECK_CONDITION_CBOR(cn_cbor_array_append(cnArrayT, cnT, &cbor_error), cbor_error);
cnT = NULL;
cnParam = NULL;
}
CHECK_CONDITION_CBOR(cn_cbor_array_append(pArray, cnArrayT, &cbor_error), cbor_error);
cnArrayT = NULL;
cnArrayT = cn_cbor_array_create(CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnArrayT != NULL, cbor_error);
cnParam = _COSE_map_get_int(pcose, COSE_Header_KDF_V_nonce, COSE_BOTH, perr);
if (cnParam != NULL) {
cnT = cn_cbor_clone(cnParam, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnT != NULL, cbor_error);
CHECK_CONDITION_CBOR(cn_cbor_array_append(cnArrayT, cnT, &cbor_error), cbor_error);
cnT = NULL;
cnParam = NULL;
}
cnParam = _COSE_map_get_int(pcose, COSE_Header_KDF_V_name, COSE_BOTH, perr);
if (cnParam != NULL) {
cnT = cn_cbor_clone(cnParam, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnT != NULL, cbor_error);
CHECK_CONDITION_CBOR(cn_cbor_array_append(cnArrayT, cnT, &cbor_error), cbor_error);
cnT = NULL;
cnParam = NULL;
}
cnParam = _COSE_map_get_int(pcose, COSE_Header_KDF_V_other, COSE_BOTH, perr);
if (cnParam != NULL) {
cnT = cn_cbor_clone(cnParam, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnT != NULL, cbor_error);
CHECK_CONDITION_CBOR(cn_cbor_array_append(cnArrayT, cnT, &cbor_error), cbor_error);
cnT = NULL;
cnParam = NULL;
}
CHECK_CONDITION_CBOR(cn_cbor_array_append(pArray, cnArrayT, &cbor_error), cbor_error);
cnArrayT = NULL;
cnArrayT = cn_cbor_array_create(CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnArrayT != NULL, cbor_error);
cnT = cn_cbor_int_create(cbitKey, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnT != NULL, cbor_error);
CHECK_CONDITION_CBOR(cn_cbor_array_append(cnArrayT, cnT, &cbor_error), cbor_error);
cnT = NULL;
cnParam = _COSE_arrayget_int(pcose, INDEX_PROTECTED);
if (cnParam != NULL) {
cnT = cn_cbor_clone(cnParam, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnT != NULL, cbor_error);
CHECK_CONDITION_CBOR(cn_cbor_array_append(cnArrayT, cnT, &cbor_error), cbor_error);
cnT = NULL;
cnParam = NULL;
}
cnParam = _COSE_map_get_int(pcose, COSE_Header_KDF_PUB_other, COSE_BOTH, perr);
if (cnParam != NULL) {
cnT = cn_cbor_clone(cnParam, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnT != NULL, cbor_error);
CHECK_CONDITION_CBOR(cn_cbor_array_append(cnArrayT, cnT, &cbor_error), cbor_error);
cnT = NULL;
cnParam = NULL;
}
CHECK_CONDITION_CBOR(cn_cbor_array_append(pArray, cnArrayT, &cbor_error), cbor_error);
cnArrayT = NULL;
cnParam = _COSE_map_get_int(pcose, COSE_Header_KDF_PRIV, COSE_BOTH, perr);
if (cnParam != NULL) {
cnT = cn_cbor_clone(cnParam, CBOR_CONTEXT_PARAM_COMMA &cbor_error);
CHECK_CONDITION_CBOR(cnT != NULL, cbor_error);
CHECK_CONDITION_CBOR(cn_cbor_array_append(pArray, cnT, &cbor_error), cbor_error);
cnT = NULL;
cnParam = NULL;
}
cbContext = cn_cbor_encoder_write(RgbDontUse4, 0, sizeof(RgbDontUse4), pArray);
CHECK_CONDITION(cbContext > 0, COSE_ERR_CBOR);
pbContext = (byte *)COSE_CALLOC(cbContext, 1, context);
CHECK_CONDITION(pbContext != NULL, COSE_ERR_OUT_OF_MEMORY);
CHECK_CONDITION(cn_cbor_encoder_write(pbContext, 0, cbContext, pArray), COSE_ERR_CBOR);
*ppbContext = pbContext;
*pcbContext = cbContext;
pbContext = NULL;
fReturn = true;
returnHere:
if (pbContext != NULL) COSE_FREE(pbContext, context);
if (pArray != NULL) CN_CBOR_FREE(pArray, context);
if (cnArrayT != NULL) CN_CBOR_FREE(cnArrayT, context);
if (cnT != NULL) CN_CBOR_FREE(cnT, context);
return fReturn;
errorReturn:
fReturn = false;
goto returnHere;
}