| #include <stdlib.h> |
| #ifndef __MBED__ |
| #include <memory.h> |
| #endif |
| |
| #include "cose/cose.h" |
| #include "cose_int.h" |
| #include "cose/cose_configure.h" |
| #include "crypto.h" |
| |
| bool IsValidCOSEHandle(HCOSE h) |
| { |
| COSE_Encrypt *p = (COSE_Encrypt *)h; |
| if (p == NULL) |
| return false; |
| return true; |
| } |
| |
| bool _COSE_Init(COSE_INIT_FLAGS flags, |
| COSE *pobj, |
| int msgType, |
| CBOR_CONTEXT_COMMA cose_errback *perr) |
| { |
| cn_cbor_errback errState; |
| ; |
| |
| #ifdef USE_CBOR_CONTEXT |
| if (context != NULL) |
| pobj->m_allocContext = *context; |
| #endif |
| |
| CHECK_CONDITION((flags & ~(COSE_INIT_FLAGS_DETACHED_CONTENT | |
| COSE_INIT_FLAGS_NO_CBOR_TAG)) == 0, |
| COSE_ERR_INVALID_PARAMETER); |
| |
| pobj->m_flags = flags; |
| |
| pobj->m_protectedMap = |
| cn_cbor_map_create(CBOR_CONTEXT_PARAM_COMMA & errState); |
| CHECK_CONDITION_CBOR(pobj->m_protectedMap != NULL, errState); |
| |
| pobj->m_dontSendMap = |
| cn_cbor_map_create(CBOR_CONTEXT_PARAM_COMMA & errState); |
| CHECK_CONDITION_CBOR(pobj->m_dontSendMap != NULL, errState); |
| |
| pobj->m_cborRoot = pobj->m_cbor = |
| cn_cbor_array_create(CBOR_CONTEXT_PARAM_COMMA & errState); |
| CHECK_CONDITION_CBOR(pobj->m_cbor != NULL, errState); |
| pobj->m_ownMsg = 1; |
| |
| pobj->m_msgType = msgType; |
| |
| pobj->m_unprotectMap = |
| cn_cbor_map_create(CBOR_CONTEXT_PARAM_COMMA & errState); |
| CHECK_CONDITION_CBOR(pobj->m_unprotectMap != NULL, errState); |
| CHECK_CONDITION_CBOR( |
| _COSE_array_replace(pobj, pobj->m_unprotectMap, INDEX_UNPROTECTED, |
| CBOR_CONTEXT_PARAM_COMMA & errState), |
| errState); |
| pobj->m_ownUnprotectedMap = false; |
| |
| if (!(flags & COSE_INIT_FLAGS_NO_CBOR_TAG)) { |
| cn_cbor_errback cbor_error; |
| cn_cbor *cn = cn_cbor_tag_create( |
| msgType, pobj->m_cborRoot, CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(cn != NULL, cbor_error); |
| pobj->m_cborRoot = cn; |
| } |
| |
| pobj->m_refCount = 1; |
| |
| return true; |
| |
| errorReturn: |
| return false; |
| } |
| |
| bool _COSE_Init_From_Object(COSE *pobj, |
| cn_cbor *pcbor, |
| CBOR_CONTEXT_COMMA cose_errback *perr) |
| { |
| const cn_cbor *pmap = NULL; |
| cn_cbor_errback errState; // = { 0 }; |
| cn_cbor_errback cbor_error; |
| |
| #ifdef USE_CBOR_CONTEXT |
| if (context != NULL) |
| pobj->m_allocContext = *context; |
| #endif |
| pobj->m_cborRoot = pcbor; |
| pobj->m_cbor = pcbor; |
| |
| // Check if we have a tag |
| if (pcbor->type == CN_CBOR_TAG) { |
| pcbor = pobj->m_cbor = pcbor->first_child; |
| } |
| |
| pmap = _COSE_arrayget_int(pobj, INDEX_PROTECTED); |
| |
| CHECK_CONDITION(pmap != NULL, COSE_ERR_INVALID_PARAMETER); |
| if (pmap != NULL) { |
| CHECK_CONDITION( |
| pmap->type == CN_CBOR_BYTES, COSE_ERR_INVALID_PARAMETER); |
| |
| if (pmap->length == 0) { |
| pobj->m_protectedMap = |
| cn_cbor_map_create(CBOR_CONTEXT_PARAM_COMMA NULL); |
| CHECK_CONDITION(pobj->m_protectedMap, COSE_ERR_OUT_OF_MEMORY); |
| } else { |
| pobj->m_protectedMap = cn_cbor_decode((const byte *)pmap->v.str, |
| pmap->length, CBOR_CONTEXT_PARAM_COMMA & errState); |
| CHECK_CONDITION( |
| pobj->m_protectedMap != NULL, COSE_ERR_INVALID_PARAMETER); |
| } |
| } |
| |
| pobj->m_unprotectMap = _COSE_arrayget_int(pobj, INDEX_UNPROTECTED); |
| CHECK_CONDITION((pobj->m_unprotectMap != NULL) && |
| (pobj->m_unprotectMap->type == CN_CBOR_MAP), |
| COSE_ERR_INVALID_PARAMETER); |
| pobj->m_ownUnprotectedMap = false; |
| |
| pobj->m_dontSendMap = |
| cn_cbor_map_create(CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(pobj->m_dontSendMap != NULL, cbor_error); |
| |
| pobj->m_ownMsg = true; |
| pobj->m_refCount = 1; |
| |
| return true; |
| |
| errorReturn: |
| return false; |
| } |
| |
| void _COSE_Release(COSE *pobj) |
| { |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pobj->m_allocContext; |
| #endif |
| |
| if (pobj->m_protectedMap != NULL) |
| CN_CBOR_FREE(pobj->m_protectedMap, context); |
| if (pobj->m_ownUnprotectedMap && (pobj->m_unprotectMap != NULL)) |
| CN_CBOR_FREE(pobj->m_unprotectMap, context); |
| if (pobj->m_dontSendMap != NULL) |
| CN_CBOR_FREE(pobj->m_dontSendMap, context); |
| if (pobj->m_ownMsg && (pobj->m_cborRoot != NULL) && |
| (pobj->m_cborRoot->parent == NULL)) |
| CN_CBOR_FREE(pobj->m_cborRoot, context); |
| } |
| |
| HCOSE COSE_Decode(const byte *rgbData, |
| size_t cbData, |
| int *ptype, |
| COSE_object_type struct_type, |
| CBOR_CONTEXT_COMMA cose_errback *perr) |
| { |
| cn_cbor *cbor = NULL; |
| cn_cbor *cborRoot = NULL; |
| cn_cbor_errback cbor_err; |
| HCOSE h; |
| |
| CHECK_CONDITION( |
| (rgbData != NULL) && (ptype != NULL), COSE_ERR_INVALID_PARAMETER); |
| |
| cbor = cborRoot = |
| cn_cbor_decode(rgbData, cbData, CBOR_CONTEXT_PARAM_COMMA & cbor_err); |
| CHECK_CONDITION_CBOR(cbor != NULL, cbor_err); |
| |
| if (cbor->type == CN_CBOR_TAG) { |
| if (struct_type != 0) { |
| CHECK_CONDITION(struct_type == (COSE_object_type)cbor->v.sint, |
| COSE_ERR_INVALID_PARAMETER); |
| } else |
| struct_type = cbor->v.uint; |
| |
| *ptype = struct_type; |
| |
| cbor = cbor->first_child; |
| } else { |
| *ptype = struct_type; |
| } |
| |
| CHECK_CONDITION(cbor->type == CN_CBOR_ARRAY, COSE_ERR_INVALID_PARAMETER); |
| |
| switch (*ptype) { |
| case COSE_enveloped_object: |
| #if INCLUDE_ENCRYPT |
| h = (HCOSE)_COSE_Enveloped_Init_From_Object( |
| cbor, NULL, CBOR_CONTEXT_PARAM_COMMA perr); |
| if (h == NULL) { |
| goto errorReturn; |
| } |
| #else |
| FAIL_CONDITION(COSE_ERR_UNSUPPORTED_COSE_TYPE); |
| #endif |
| break; |
| |
| case COSE_sign_object: |
| #if INCLUDE_SIGN |
| h = (HCOSE)_COSE_Sign_Init_From_Object( |
| cborRoot, NULL, CBOR_CONTEXT_PARAM_COMMA perr); |
| if (h == NULL) { |
| goto errorReturn; |
| } |
| #else |
| FAIL_CONDITION(COSE_ERR_UNSUPPORTED_COSE_TYPE); |
| #endif |
| break; |
| |
| case COSE_sign1_object: |
| #if INCLUDE_SIGN1 |
| h = (HCOSE)_COSE_Sign1_Init_From_Object( |
| cborRoot, NULL, CBOR_CONTEXT_PARAM_COMMA perr); |
| if (h == NULL) { |
| goto errorReturn; |
| } |
| #else |
| FAIL_CONDITION(COSE_ERR_UNSUPPORTED_COSE_TYPE); |
| #endif |
| break; |
| |
| case COSE_mac_object: |
| #if INCLUDE_MAC |
| h = (HCOSE)_COSE_Mac_Init_From_Object( |
| cbor, NULL, CBOR_CONTEXT_PARAM_COMMA perr); |
| if (h == NULL) { |
| goto errorReturn; |
| } |
| #else |
| FAIL_CONDITION(COSE_ERR_UNSUPPORTED_COSE_TYPE); |
| #endif |
| break; |
| |
| case COSE_mac0_object: |
| #if INCLUDE_MAC0 |
| h = (HCOSE)_COSE_Mac0_Init_From_Object( |
| cbor, NULL, CBOR_CONTEXT_PARAM_COMMA perr); |
| if (h == NULL) { |
| goto errorReturn; |
| } |
| #else |
| FAIL_CONDITION(COSE_ERR_UNSUPPORTED_COSE_TYPE); |
| #endif |
| break; |
| |
| case COSE_encrypt_object: |
| #if INCLUDE_ENCRYPT0 |
| h = (HCOSE)_COSE_Encrypt_Init_From_Object( |
| cbor, NULL, CBOR_CONTEXT_PARAM_COMMA perr); |
| if (h == NULL) { |
| goto errorReturn; |
| } |
| #else |
| FAIL_CONDITION(COSE_ERR_UNSUPPORTED_COSE_TYPE); |
| #endif |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| } |
| |
| return h; |
| |
| errorReturn: |
| COSE_FREE(cbor, context); |
| return NULL; |
| } |
| |
| size_t COSE_Encode(HCOSE msg, byte *rgb, size_t ib, size_t cb) |
| { |
| if (rgb == NULL) |
| return cn_cbor_encode_size(((COSE *)msg)->m_cbor) + ib; |
| ssize_t size = cn_cbor_encoder_write(rgb, ib, cb, ((COSE *)msg)->m_cbor); |
| return size >= 0 ? size : 0; |
| } |
| |
| cn_cbor *COSE_get_cbor(HCOSE h) |
| { |
| COSE *msg = (COSE *)h; |
| if (!IsValidCOSEHandle(h)) |
| return NULL; |
| |
| return msg->m_cbor; |
| } |
| |
| bool _COSE_SetExternal(COSE *pcose, |
| const byte *pbExternalData, |
| size_t cbExternalData, |
| cose_errback *perr) |
| { |
| (void)perr; |
| pcose->m_pbExternal = pbExternalData; |
| pcose->m_cbExternal = cbExternalData; |
| |
| return true; |
| } |
| |
| cn_cbor *_COSE_map_get_int(COSE *pcose, |
| int key, |
| int flags, |
| cose_errback *perror) |
| { |
| cn_cbor *p = NULL; |
| |
| if (perror != NULL) |
| perror->err = COSE_ERR_NONE; |
| |
| if ((pcose->m_protectedMap != NULL) && ((flags & COSE_PROTECT_ONLY) != 0)) { |
| p = cn_cbor_mapget_int(pcose->m_protectedMap, key); |
| if (p != NULL) |
| return p; |
| } |
| |
| if ((pcose->m_unprotectMap != NULL) && |
| ((flags & COSE_UNPROTECT_ONLY) != 0)) { |
| p = cn_cbor_mapget_int(pcose->m_unprotectMap, key); |
| if (p != NULL) |
| return p; |
| } |
| |
| if ((pcose->m_dontSendMap != NULL) && ((flags & COSE_DONT_SEND) != 0)) { |
| p = cn_cbor_mapget_int(pcose->m_dontSendMap, key); |
| } |
| |
| if ((p == NULL) && (perror != NULL)) |
| perror->err = COSE_ERR_INVALID_PARAMETER; |
| |
| return p; |
| } |
| |
| cn_cbor *_COSE_map_get_str(COSE *pcose, |
| const char *key, |
| int flags, |
| cose_errback *perror) |
| { |
| cn_cbor *p = NULL; |
| |
| if (perror != NULL) |
| perror->err = COSE_ERR_NONE; |
| |
| if ((pcose->m_protectedMap != NULL) && ((flags & COSE_PROTECT_ONLY) != 0)) { |
| p = cn_cbor_mapget_string(pcose->m_protectedMap, key); |
| if (p != NULL) |
| return p; |
| } |
| |
| if ((pcose->m_unprotectMap != NULL) && |
| ((flags & COSE_UNPROTECT_ONLY) != 0)) { |
| p = cn_cbor_mapget_string(pcose->m_unprotectMap, key); |
| } |
| |
| if ((pcose->m_dontSendMap != NULL) && ((flags & COSE_DONT_SEND) != 0)) { |
| p = cn_cbor_mapget_string(pcose->m_dontSendMap, key); |
| } |
| |
| return p; |
| } |
| |
| bool _COSE_map_put(COSE *pCose, |
| int key, |
| cn_cbor *value, |
| int flags, |
| cose_errback *perr) |
| { |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pCose->m_allocContext; |
| #endif |
| cn_cbor_errback error; |
| bool f = false; |
| CHECK_CONDITION(value != NULL, COSE_ERR_INVALID_PARAMETER); |
| |
| CHECK_CONDITION(cn_cbor_mapget_int(pCose->m_protectedMap, key) == NULL, |
| COSE_ERR_INVALID_PARAMETER); |
| CHECK_CONDITION(cn_cbor_mapget_int(pCose->m_unprotectMap, key) == NULL, |
| COSE_ERR_INVALID_PARAMETER); |
| CHECK_CONDITION(cn_cbor_mapget_int(pCose->m_dontSendMap, key) == NULL, |
| COSE_ERR_INVALID_PARAMETER); |
| |
| switch (flags) { |
| case COSE_PROTECT_ONLY: |
| f = cn_cbor_mapput_int(pCose->m_protectedMap, key, value, |
| CBOR_CONTEXT_PARAM_COMMA & error); |
| break; |
| |
| case COSE_UNPROTECT_ONLY: |
| f = cn_cbor_mapput_int(pCose->m_unprotectMap, key, value, |
| CBOR_CONTEXT_PARAM_COMMA & error); |
| break; |
| |
| case COSE_DONT_SEND: |
| f = cn_cbor_mapput_int(pCose->m_dontSendMap, key, value, |
| CBOR_CONTEXT_PARAM_COMMA & error); |
| break; |
| |
| default: |
| FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER); |
| break; |
| } |
| |
| CHECK_CONDITION(f, _MapFromCBOR(error)); |
| |
| errorReturn: |
| return f; |
| } |
| |
| cn_cbor *_COSE_encode_protected(COSE *pMessage, cose_errback *perr) |
| { |
| cn_cbor *pProtected; |
| int cbProtected; |
| byte *pbProtected = NULL; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = &pMessage->m_allocContext; |
| #endif // USE_CBOR_CONTEXT |
| |
| pProtected = cn_cbor_index(pMessage->m_cbor, INDEX_PROTECTED); |
| if ((pProtected != NULL) && (pProtected->type != CN_CBOR_INVALID)) { |
| errorReturn: |
| if (pbProtected != NULL) |
| COSE_FREE(pbProtected, context); |
| return pProtected; |
| } |
| |
| if (pMessage->m_protectedMap->length > 0) { |
| cbProtected = cn_cbor_encode_size(pMessage->m_protectedMap); |
| pbProtected = (byte *)COSE_CALLOC(cbProtected, 1, context); |
| CHECK_CONDITION(pbProtected != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| CHECK_CONDITION(cn_cbor_encoder_write(pbProtected, 0, cbProtected, |
| pMessage->m_protectedMap) == cbProtected, |
| COSE_ERR_CBOR); |
| } else { |
| cbProtected = 0; |
| } |
| |
| pProtected = cn_cbor_data_create( |
| pbProtected, cbProtected, CBOR_CONTEXT_PARAM_COMMA NULL); |
| CHECK_CONDITION(pProtected != NULL, COSE_ERR_OUT_OF_MEMORY); |
| pbProtected = NULL; |
| |
| CHECK_CONDITION(_COSE_array_replace(pMessage, pProtected, INDEX_PROTECTED, |
| CBOR_CONTEXT_PARAM_COMMA NULL), |
| COSE_ERR_CBOR); |
| |
| return pProtected; |
| } |
| |
| #ifdef USE_COUNTER_SIGNATURES |
| bool _COSE_CounterSign_add(COSE *pMessage, |
| HCOSE_COUNTERSIGN hSigner, |
| cose_errback *perr) |
| { |
| COSE_CounterSign *pSigner = (COSE_CounterSign *)hSigner; |
| |
| CHECK_CONDITION(IsValidCounterSignHandle(hSigner), COSE_ERR_INVALID_HANDLE); |
| CHECK_CONDITION( |
| pSigner->m_signer.m_signerNext == NULL, COSE_ERR_INVALID_PARAMETER); |
| |
| pSigner = pMessage->m_counterSigners; |
| pMessage->m_counterSigners = pSigner; |
| return true; |
| |
| errorReturn: |
| return false; |
| } |
| |
| HCOSE_COUNTERSIGN _COSE_CounterSign_get(COSE *pMessage, |
| int iSigner, |
| cose_errback *perr) |
| { |
| COSE_CounterSign *pSigner = pMessage->m_counterSigners; |
| int i; |
| |
| for (i = 0; i < iSigner; i++, pSigner = pSigner->m_next) { |
| CHECK_CONDITION(pSigner != NULL, COSE_ERR_INVALID_PARAMETER); |
| } |
| |
| return (HCOSE_COUNTERSIGN)pSigner; |
| |
| errorReturn: |
| return false; |
| } |
| |
| bool _COSE_CountSign_create(COSE *pMessage, |
| cn_cbor *pcnBody, |
| CBOR_CONTEXT_COMMA cose_errback *perr) |
| { |
| cn_cbor *pArray = NULL; |
| cn_cbor_errback cbor_err; |
| COSE_CounterSign *pSigner = NULL; |
| cn_cbor *pcnProtected = NULL; |
| cn_cbor *pcn = NULL; |
| cn_cbor *pcn2 = NULL; |
| |
| if (pMessage->m_counterSigners == NULL) |
| return true; |
| |
| // One or more than one? |
| if (pMessage->m_counterSigners->m_signer.m_signerNext != NULL) { |
| pArray = cn_cbor_array_create(CBOR_CONTEXT_PARAM_COMMA & cbor_err); |
| CHECK_CONDITION_CBOR(pArray != NULL, cbor_err); |
| } |
| |
| pcnProtected = _COSE_arrayget_int(pMessage, INDEX_PROTECTED); |
| CHECK_CONDITION(pcnProtected != NULL, COSE_ERR_INTERNAL); |
| |
| for (pSigner = pMessage->m_counterSigners; pSigner != NULL; |
| pSigner = pSigner->m_next) { |
| CHECK_CONDITION( |
| pSigner->m_signer.m_signerNext == NULL, COSE_ERR_INTERNAL); |
| |
| pcn = cn_cbor_data_create(pcnProtected->v.bytes, pcnProtected->v.count, |
| CBOR_CONTEXT_PARAM_COMMA & cbor_err); |
| CHECK_CONDITION_CBOR(pcnProtected != NULL, cbor_err); |
| |
| pcn2 = cn_cbor_clone(pcnBody, CBOR_CONTEXT_PARAM_COMMA & cbor_err); |
| CHECK_CONDITION_CBOR(pcnBody != NULL, cbor_err); |
| |
| if (!_COSE_Signer_sign(&pSigner->m_signer, pcnBody, pcn2, perr)) |
| goto errorReturn; |
| pcn = NULL; |
| pcn2 = NULL; |
| |
| if (pArray != NULL) { |
| bool f = cn_cbor_array_append( |
| pArray, pSigner->m_signer.m_message.m_cborRoot, &cbor_err); |
| CHECK_CONDITION_CBOR(f, cbor_err); |
| } else { |
| pArray = pSigner->m_signer.m_message.m_cborRoot; |
| } |
| } |
| |
| if (!_COSE_map_put(pMessage, COSE_Header_CounterSign, pArray, |
| COSE_UNPROTECT_ONLY, perr)) |
| goto errorReturn; |
| |
| return true; |
| |
| errorReturn: |
| if (pArray != NULL) |
| CN_CBOR_FREE(pArray, context); |
| if ((pcn != NULL) && (pcn->parent != NULL)) |
| CN_CBOR_FREE(pcn, context); |
| if ((pcn2 != NULL) && (pcn2->parent != NULL)) |
| CN_CBOR_FREE(pcn2, context); |
| return false; |
| } |
| |
| #endif |
| |
| bool _COSE_array_replace(COSE *pMessage, |
| cn_cbor *cb_value, |
| int index, |
| CBOR_CONTEXT_COMMA cn_cbor_errback *errp) |
| { |
| return cn_cbor_array_replace( |
| pMessage->m_cbor, cb_value, index, CBOR_CONTEXT_PARAM_COMMA errp); |
| } |
| |
| cn_cbor *_COSE_arrayget_int(COSE *pMessage, int index) |
| { |
| return cn_cbor_index(pMessage->m_cbor, index); |
| } |
| |
| cose_error _MapFromCBOR(cn_cbor_errback err) |
| { |
| switch (err.err) { |
| case CN_CBOR_ERR_INVALID_PARAMETER: |
| return COSE_ERR_INVALID_PARAMETER; |
| |
| case CN_CBOR_ERR_OUT_OF_MEMORY: |
| return COSE_ERR_OUT_OF_MEMORY; |
| |
| default: |
| return COSE_ERR_CBOR; |
| } |
| } |
| |
| void _COSE_InsertInList(COSE **root, COSE *newMsg) |
| { |
| if (*root == NULL) { |
| *root = newMsg; |
| return; |
| } |
| |
| newMsg->m_handleList = *root; |
| *root = newMsg; |
| return; |
| } |
| |
| bool _COSE_IsInList(const COSE *const root, const COSE *const thisMsg) |
| { |
| if (root == NULL) { |
| return false; |
| } |
| if (thisMsg == NULL) { |
| return false; |
| } |
| |
| for (const COSE *walk = root; walk != NULL; walk = walk->m_handleList) { |
| if (walk == thisMsg) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| void _COSE_RemoveFromList(COSE **root, COSE *thisMsg) |
| { |
| COSE *walk; |
| |
| if (*root == thisMsg) { |
| *root = thisMsg->m_handleList; |
| thisMsg->m_handleList = NULL; |
| return; |
| } |
| |
| for (walk = *root; walk->m_handleList != NULL; walk = walk->m_handleList) { |
| if (walk->m_handleList == thisMsg) { |
| walk->m_handleList = thisMsg->m_handleList; |
| thisMsg->m_handleList = NULL; |
| return; |
| } |
| } |
| return; |
| } |