| /** \file Sign.c |
| * Contains implementation of the functions related to HCOSE_SIGN handle |
| * objects. |
| */ |
| |
| #include <stdlib.h> |
| |
| #include "cose/cose.h" |
| #include "cose_int.h" |
| #include "cose/cose_configure.h" |
| |
| #if INCLUDE_SIGN |
| |
| COSE *SignRoot = NULL; |
| |
| /*! \private |
| * @brief Test if a HCOSE_SIGN handle is valid |
| * |
| * Internal function to test if a sign 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 IsValidSignHandle(HCOSE_SIGN h) |
| { |
| COSE_SignMessage *p = (COSE_SignMessage *)h; |
| |
| if (p == NULL) { |
| return false; |
| } |
| return _COSE_IsInList(SignRoot, (COSE *)p); |
| } |
| |
| /** Allocate a SIGN message structure. |
| * |
| * Allocate a new SIGN message structure for creation of a COSE_Sign object. |
| * @param context is a cn_cbor context object |
| * @param perr is a cose_errback return variable |
| * @return HCOSE_SIGN a handle for the newly allocated object |
| */ |
| HCOSE_SIGN COSE_Sign_Init(COSE_INIT_FLAGS flags, |
| CBOR_CONTEXT_COMMA cose_errback *perr) |
| { |
| CHECK_CONDITION(flags == COSE_INIT_FLAGS_NONE, COSE_ERR_INVALID_PARAMETER); |
| COSE_SignMessage *pobj = |
| (COSE_SignMessage *)COSE_CALLOC(1, sizeof(COSE_SignMessage), context); |
| CHECK_CONDITION(pobj != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| if (!_COSE_Init(flags, &pobj->m_message, COSE_sign_object, |
| CBOR_CONTEXT_PARAM_COMMA perr)) { |
| _COSE_Sign_Release(pobj); |
| COSE_FREE(pobj, context); |
| return NULL; |
| } |
| |
| _COSE_InsertInList(&SignRoot, &pobj->m_message); |
| |
| return (HCOSE_SIGN)pobj; |
| |
| errorReturn: |
| return NULL; |
| } |
| |
| HCOSE_SIGN _COSE_Sign_Init_From_Object(cn_cbor *cbor, |
| COSE_SignMessage *pIn, |
| CBOR_CONTEXT_COMMA cose_errback *perr) |
| { |
| COSE_SignMessage *pobj = pIn; |
| cn_cbor *pSigners = NULL; |
| // cn_cbor * tmp; |
| cose_errback error = {0}; |
| if (perr == NULL) { |
| perr = &error; |
| } |
| |
| if (pobj == NULL) { |
| pobj = (COSE_SignMessage *)COSE_CALLOC( |
| 1, sizeof(COSE_SignMessage), context); |
| } |
| CHECK_CONDITION(pobj != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| if (!_COSE_Init_From_Object( |
| &pobj->m_message, cbor, CBOR_CONTEXT_PARAM_COMMA perr)) { |
| goto errorReturn; |
| } |
| |
| pSigners = _COSE_arrayget_int(&pobj->m_message, INDEX_SIGNERS); |
| CHECK_CONDITION(pSigners != NULL, COSE_ERR_INVALID_PARAMETER); |
| CHECK_CONDITION( |
| pSigners->type == CN_CBOR_ARRAY, COSE_ERR_INVALID_PARAMETER); |
| CHECK_CONDITION(pSigners->length > 0, |
| COSE_ERR_INVALID_PARAMETER); // Must be at least one signer |
| |
| pSigners = pSigners->first_child; |
| do { |
| COSE_SignerInfo *pInfo = _COSE_SignerInfo_Init_From_Object( |
| pSigners, NULL, CBOR_CONTEXT_PARAM_COMMA perr); |
| if (pInfo == NULL) { |
| goto errorReturn; |
| } |
| |
| pInfo->m_signerNext = pobj->m_signerFirst; |
| pobj->m_signerFirst = pInfo; |
| pSigners = pSigners->next; |
| } while (pSigners != NULL); |
| |
| if (pIn == NULL) { |
| _COSE_InsertInList(&SignRoot, &pobj->m_message); |
| } |
| |
| return (HCOSE_SIGN)pobj; |
| |
| errorReturn: |
| if (pobj != NULL) { |
| _COSE_Sign_Release(pobj); |
| if (pIn == NULL) { |
| COSE_FREE(pobj, context); |
| } |
| } |
| return NULL; |
| } |
| |
| bool COSE_Sign_Free(HCOSE_SIGN h) |
| { |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context; |
| #endif |
| COSE_SignMessage *pMessage = (COSE_SignMessage *)h; |
| |
| if (!IsValidSignHandle(h)) { |
| return false; |
| } |
| |
| // Check reference counting |
| if (pMessage->m_message.m_refCount > 1) { |
| pMessage->m_message.m_refCount--; |
| return true; |
| } |
| |
| _COSE_RemoveFromList(&SignRoot, &pMessage->m_message); |
| |
| #ifdef USE_CBOR_CONTEXT |
| context = &pMessage->m_message.m_allocContext; |
| #endif |
| |
| _COSE_Sign_Release(pMessage); |
| |
| COSE_FREE(pMessage, context); |
| |
| return true; |
| } |
| |
| void _COSE_Sign_Release(COSE_SignMessage *p) |
| { |
| COSE_SignerInfo *pSigner; |
| COSE_SignerInfo *pSigner2; |
| |
| for (pSigner = p->m_signerFirst; pSigner != NULL; pSigner = pSigner2) { |
| pSigner2 = pSigner->m_signerNext; |
| COSE_Signer_Free((HCOSE_SIGNER)pSigner); |
| } |
| |
| _COSE_Release(&p->m_message); |
| } |
| |
| bool COSE_Sign_SetContent(HCOSE_SIGN h, |
| const byte *rgb, |
| size_t cb, |
| cose_errback *perr) |
| { |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = NULL; |
| #endif |
| cn_cbor *p = NULL; |
| COSE_SignMessage *pMessage = (COSE_SignMessage *)h; |
| bool f = false; |
| |
| CHECK_CONDITION(IsValidSignHandle(h), COSE_ERR_INVALID_HANDLE); |
| CHECK_CONDITION(rgb != NULL, COSE_ERR_INVALID_PARAMETER); |
| |
| #ifdef USE_CBOR_CONTEXT |
| context = &pMessage->m_message.m_allocContext; |
| #endif |
| |
| p = cn_cbor_data_create(rgb, (int)cb, CBOR_CONTEXT_PARAM_COMMA NULL); |
| CHECK_CONDITION(p != NULL, COSE_ERR_OUT_OF_MEMORY); |
| |
| CHECK_CONDITION(_COSE_array_replace(&pMessage->m_message, p, INDEX_BODY, |
| CBOR_CONTEXT_PARAM_COMMA NULL), |
| COSE_ERR_OUT_OF_MEMORY); |
| p = NULL; |
| |
| f = true; |
| errorReturn: |
| if (p != NULL) { |
| CN_CBOR_FREE(p, context); |
| } |
| |
| return f; |
| } |
| |
| HCOSE_SIGNER COSE_Sign_add_signer(HCOSE_SIGN hSign, |
| const cn_cbor *pkey, |
| int algId, |
| cose_errback *perr) |
| { |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = NULL; |
| COSE_SignMessage *pMessage = (COSE_SignMessage *)hSign; |
| #endif |
| const cn_cbor *cbor; |
| cn_cbor *cbor2 = NULL; |
| HCOSE_SIGNER hSigner = NULL; |
| cn_cbor_errback cbor_error; |
| |
| CHECK_CONDITION(IsValidSignHandle(hSign), COSE_ERR_INVALID_HANDLE); |
| CHECK_CONDITION(pkey != NULL, COSE_ERR_INVALID_PARAMETER); |
| |
| #ifdef USE_CBOR_CONTEXT |
| context = &pMessage->m_message.m_allocContext; |
| #endif |
| |
| hSigner = COSE_Signer_Init(CBOR_CONTEXT_PARAM_COMMA perr); |
| if (hSigner == NULL) { |
| goto errorReturn; |
| } |
| |
| cbor2 = cn_cbor_int_create(algId, CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(cbor2 != NULL, cbor_error); |
| if (!COSE_Signer_map_put_int( |
| hSigner, COSE_Header_Algorithm, cbor2, COSE_PROTECT_ONLY, perr)) { |
| goto errorReturn; |
| } |
| cbor2 = NULL; |
| |
| cbor = cn_cbor_mapget_int(pkey, COSE_Key_ID); |
| if (cbor != NULL) { |
| CHECK_CONDITION( |
| cbor->type == CN_CBOR_BYTES, COSE_ERR_INVALID_PARAMETER); |
| cbor2 = cn_cbor_data_create(cbor->v.bytes, (int)cbor->length, |
| CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(cbor2 != NULL, cbor_error); |
| if (!COSE_Signer_map_put_int( |
| hSigner, COSE_Header_KID, cbor2, COSE_UNPROTECT_ONLY, perr)) { |
| goto errorReturn; |
| } |
| cbor2 = NULL; |
| } |
| |
| if (!COSE_Signer_SetKey(hSigner, pkey, perr)) { |
| goto errorReturn; |
| } |
| |
| if (!COSE_Sign_AddSigner(hSign, hSigner, perr)) { |
| goto errorReturn; |
| } |
| |
| return hSigner; |
| |
| errorReturn: |
| if (cbor2 != NULL) { |
| CN_CBOR_FREE((void *)cbor2, context); |
| } |
| if (hSigner != NULL) { |
| COSE_Signer_Free(hSigner); |
| } |
| return NULL; |
| } |
| |
| bool COSE_Sign_Sign(HCOSE_SIGN h, cose_errback *perr) |
| { |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = NULL; |
| #endif |
| COSE_SignMessage *pMessage = (COSE_SignMessage *)h; |
| COSE_SignerInfo *pSigner; |
| const cn_cbor *pcborBody; |
| const cn_cbor *pcborProtected; |
| |
| if (!IsValidSignHandle(h)) { |
| CHECK_CONDITION(false, COSE_ERR_INVALID_HANDLE); |
| errorReturn: |
| return false; |
| } |
| #ifdef USE_CBOR_CONTEXT |
| context = &pMessage->m_message.m_allocContext; |
| #endif |
| |
| pcborBody = _COSE_arrayget_int(&pMessage->m_message, INDEX_BODY); |
| CHECK_CONDITION((pcborBody != NULL) && (pcborBody->type == CN_CBOR_BYTES), |
| COSE_ERR_INVALID_PARAMETER); |
| |
| pcborProtected = _COSE_encode_protected(&pMessage->m_message, perr); |
| if (pcborProtected == NULL) { |
| goto errorReturn; |
| } |
| |
| for (pSigner = pMessage->m_signerFirst; pSigner != NULL; |
| pSigner = pSigner->m_signerNext) { |
| if (!_COSE_Signer_sign( |
| pSigner, pcborBody, pcborProtected, "Signature", perr)) { |
| goto errorReturn; |
| } |
| } |
| |
| #if INCLUDE_COUNTERSIGNATURE |
| if (pMessage->m_message.m_counterSigners != NULL) { |
| if (!_COSE_CounterSign_Sign( |
| &pMessage->m_message, CBOR_CONTEXT_PARAM_COMMA perr)) { |
| goto errorReturn; |
| } |
| } |
| #endif |
| |
| return true; |
| } |
| |
| bool COSE_Sign_validate(HCOSE_SIGN hSign, |
| HCOSE_SIGNER hSigner, |
| cose_errback *perr) |
| { |
| bool f; |
| COSE_SignMessage *pSign; |
| COSE_SignerInfo *pSigner; |
| const cn_cbor *cnContent; |
| const cn_cbor *cnProtected; |
| |
| CHECK_CONDITION(IsValidSignHandle(hSign), COSE_ERR_INVALID_HANDLE); |
| CHECK_CONDITION(IsValidSignerHandle(hSigner), COSE_ERR_INVALID_HANDLE); |
| |
| pSign = (COSE_SignMessage *)hSign; |
| pSigner = (COSE_SignerInfo *)hSigner; |
| |
| cnContent = _COSE_arrayget_int(&pSign->m_message, INDEX_BODY); |
| CHECK_CONDITION(cnContent != NULL && cnContent->type == CN_CBOR_BYTES, |
| COSE_ERR_INVALID_PARAMETER); |
| |
| cnProtected = _COSE_arrayget_int(&pSign->m_message, INDEX_PROTECTED); |
| CHECK_CONDITION(cnProtected != NULL && cnProtected->type == CN_CBOR_BYTES, |
| COSE_ERR_INVALID_PARAMETER); |
| |
| f = _COSE_Signer_validate( |
| pSigner, cnContent, cnProtected, "Signature", perr); |
| |
| return f; |
| |
| errorReturn: |
| return false; |
| } |
| |
| bool COSE_Sign_AddSigner(HCOSE_SIGN hSign, |
| HCOSE_SIGNER hSigner, |
| cose_errback *perr) |
| { |
| COSE_SignerInfo *pSigner; |
| COSE_SignMessage *pSign; |
| cn_cbor *pSigners = NULL; |
| cn_cbor *pSignersT = NULL; |
| #ifdef USE_CBOR_CONTEXT |
| cn_cbor_context *context = NULL; |
| #endif |
| cn_cbor_errback cbor_error; |
| |
| CHECK_CONDITION(IsValidSignHandle(hSign), COSE_ERR_INVALID_HANDLE); |
| CHECK_CONDITION(IsValidSignerHandle(hSigner), COSE_ERR_INVALID_HANDLE); |
| |
| pSign = (COSE_SignMessage *)hSign; |
| pSigner = (COSE_SignerInfo *)hSigner; |
| |
| pSigner->m_signerNext = pSign->m_signerFirst; |
| pSign->m_signerFirst = pSigner; |
| |
| #ifdef USE_CBOR_CONTEXT |
| context = &pSign->m_message.m_allocContext; |
| #endif // USE_CBOR_CONTEXT |
| |
| pSigners = _COSE_arrayget_int(&pSign->m_message, INDEX_SIGNERS); |
| if (pSigners == NULL) { |
| pSignersT = cn_cbor_array_create(CBOR_CONTEXT_PARAM_COMMA & cbor_error); |
| CHECK_CONDITION_CBOR(pSignersT != NULL, cbor_error); |
| |
| CHECK_CONDITION_CBOR( |
| _COSE_array_replace(&pSign->m_message, pSignersT, INDEX_SIGNERS, |
| CBOR_CONTEXT_PARAM_COMMA & cbor_error), |
| cbor_error); |
| pSigners = pSignersT; |
| pSignersT = NULL; |
| } |
| |
| CHECK_CONDITION_CBOR( |
| cn_cbor_array_append(pSigners, pSigner->m_message.m_cbor, &cbor_error), |
| cbor_error); |
| pSigner->m_message.m_refCount++; |
| |
| return true; |
| |
| errorReturn: |
| if (pSignersT == NULL) { |
| CN_CBOR_FREE(pSignersT, context); |
| } |
| return false; |
| } |
| |
| cn_cbor *COSE_Sign_map_get_int(HCOSE_SIGN h, |
| int key, |
| int flags, |
| cose_errback *perror) |
| { |
| if (!IsValidSignHandle(h)) { |
| if (perror != NULL) { |
| perror->err = COSE_ERR_INVALID_HANDLE; |
| } |
| return NULL; |
| } |
| |
| return _COSE_map_get_int( |
| &((COSE_SignMessage *)h)->m_message, key, flags, perror); |
| } |
| |
| bool COSE_Sign_map_put_int(HCOSE_SIGN h, |
| int key, |
| cn_cbor *value, |
| int flags, |
| cose_errback *perror) |
| { |
| if (!IsValidSignHandle(h)) { |
| if (perror != NULL) { |
| perror->err = COSE_ERR_INVALID_HANDLE; |
| } |
| return false; |
| } |
| |
| return _COSE_map_put( |
| &((COSE_SignMessage *)h)->m_message, key, value, flags, perror); |
| } |
| |
| HCOSE_SIGNER COSE_Sign_GetSigner(HCOSE_SIGN cose, |
| int iSigner, |
| cose_errback *perr) |
| { |
| int i; |
| COSE_SignerInfo *p; |
| |
| if (!IsValidSignHandle(cose)) { |
| if (perr != NULL) { |
| perr->err = COSE_ERR_INVALID_HANDLE; |
| } |
| return NULL; |
| } |
| |
| p = ((COSE_SignMessage *)cose)->m_signerFirst; |
| for (i = 0; i < iSigner; i++) { |
| if (p == NULL) { |
| if (perr != NULL) { |
| perr->err = COSE_ERR_INVALID_PARAMETER; |
| } |
| return NULL; |
| } |
| p = p->m_signerNext; |
| } |
| p->m_message.m_refCount++; |
| |
| return (HCOSE_SIGNER)p; |
| } |
| |
| #endif |