| /* |
| * Copyright 2007-2022 The OpenSSL Project Authors. All Rights Reserved. |
| * Copyright Nokia 2007-2019 |
| * Copyright Siemens AG 2015-2019 |
| * |
| * Licensed under the Apache License 2.0 (the "License"). You may not use |
| * this file except in compliance with the License. You can obtain a copy |
| * in the file LICENSE in the source distribution or at |
| * https://www.openssl.org/source/license.html |
| */ |
| |
| /* CMP functions for PKIHeader handling */ |
| |
| #include "cmp_local.h" |
| |
| #include <openssl/rand.h> |
| |
| /* explicit #includes not strictly needed since implied by the above: */ |
| #include <openssl/asn1t.h> |
| #include <openssl/cmp.h> |
| #include <openssl/err.h> |
| |
| int ossl_cmp_hdr_set_pvno(OSSL_CMP_PKIHEADER *hdr, int pvno) |
| { |
| if (!ossl_assert(hdr != NULL)) |
| return 0; |
| return ASN1_INTEGER_set(hdr->pvno, pvno); |
| } |
| |
| int ossl_cmp_hdr_get_pvno(const OSSL_CMP_PKIHEADER *hdr) |
| { |
| int64_t pvno; |
| |
| if (!ossl_assert(hdr != NULL)) |
| return -1; |
| if (!ASN1_INTEGER_get_int64(&pvno, hdr->pvno) || pvno < 0 || pvno > INT_MAX) |
| return -1; |
| return (int)pvno; |
| } |
| |
| int ossl_cmp_hdr_get_protection_nid(const OSSL_CMP_PKIHEADER *hdr) |
| { |
| if (!ossl_assert(hdr != NULL) |
| || hdr->protectionAlg == NULL) |
| return NID_undef; |
| return OBJ_obj2nid(hdr->protectionAlg->algorithm); |
| } |
| |
| ASN1_OCTET_STRING *OSSL_CMP_HDR_get0_transactionID(const |
| OSSL_CMP_PKIHEADER *hdr) |
| { |
| if (hdr == NULL) { |
| ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT); |
| return NULL; |
| } |
| return hdr->transactionID; |
| } |
| |
| ASN1_OCTET_STRING *ossl_cmp_hdr_get0_senderNonce(const OSSL_CMP_PKIHEADER *hdr) |
| { |
| if (!ossl_assert(hdr != NULL)) |
| return NULL; |
| return hdr->senderNonce; |
| } |
| |
| ASN1_OCTET_STRING *OSSL_CMP_HDR_get0_recipNonce(const OSSL_CMP_PKIHEADER *hdr) |
| { |
| if (hdr == NULL) { |
| ERR_raise(ERR_LIB_CMP, CMP_R_NULL_ARGUMENT); |
| return NULL; |
| } |
| return hdr->recipNonce; |
| } |
| |
| /* a NULL-DN as an empty sequence of RDNs */ |
| int ossl_cmp_general_name_is_NULL_DN(GENERAL_NAME *name) |
| { |
| return name == NULL |
| || (name->type == GEN_DIRNAME && IS_NULL_DN(name->d.directoryName)); |
| } |
| |
| /* assign to *tgt a copy of src (which may be NULL to indicate an empty DN) */ |
| static int set1_general_name(GENERAL_NAME **tgt, const X509_NAME *src) |
| { |
| GENERAL_NAME *name; |
| |
| if (!ossl_assert(tgt != NULL)) |
| return 0; |
| if ((name = GENERAL_NAME_new()) == NULL) |
| goto err; |
| name->type = GEN_DIRNAME; |
| |
| if (src == NULL) { /* NULL-DN */ |
| if ((name->d.directoryName = X509_NAME_new()) == NULL) |
| goto err; |
| } else if (!X509_NAME_set(&name->d.directoryName, src)) { |
| goto err; |
| } |
| |
| GENERAL_NAME_free(*tgt); |
| *tgt = name; |
| |
| return 1; |
| |
| err: |
| GENERAL_NAME_free(name); |
| return 0; |
| } |
| |
| /* |
| * Set the sender name in PKIHeader. |
| * when nm is NULL, sender is set to an empty string |
| * returns 1 on success, 0 on error |
| */ |
| int ossl_cmp_hdr_set1_sender(OSSL_CMP_PKIHEADER *hdr, const X509_NAME *nm) |
| { |
| if (!ossl_assert(hdr != NULL)) |
| return 0; |
| return set1_general_name(&hdr->sender, nm); |
| } |
| |
| int ossl_cmp_hdr_set1_recipient(OSSL_CMP_PKIHEADER *hdr, const X509_NAME *nm) |
| { |
| if (!ossl_assert(hdr != NULL)) |
| return 0; |
| return set1_general_name(&hdr->recipient, nm); |
| } |
| |
| int ossl_cmp_hdr_update_messageTime(OSSL_CMP_PKIHEADER *hdr) |
| { |
| if (!ossl_assert(hdr != NULL)) |
| return 0; |
| if (hdr->messageTime == NULL |
| && (hdr->messageTime = ASN1_GENERALIZEDTIME_new()) == NULL) |
| return 0; |
| return ASN1_GENERALIZEDTIME_set(hdr->messageTime, time(NULL)) != NULL; |
| } |
| |
| /* assign to *tgt a random byte array of given length */ |
| static int set_random(ASN1_OCTET_STRING **tgt, OSSL_CMP_CTX *ctx, size_t len) |
| { |
| unsigned char *bytes = OPENSSL_malloc(len); |
| int res = 0; |
| |
| if (bytes == NULL || RAND_bytes_ex(ctx->libctx, bytes, len, 0) <= 0) |
| ERR_raise(ERR_LIB_CMP, CMP_R_FAILURE_OBTAINING_RANDOM); |
| else |
| res = ossl_cmp_asn1_octet_string_set1_bytes(tgt, bytes, len); |
| OPENSSL_free(bytes); |
| return res; |
| } |
| |
| int ossl_cmp_hdr_set1_senderKID(OSSL_CMP_PKIHEADER *hdr, |
| const ASN1_OCTET_STRING *senderKID) |
| { |
| if (!ossl_assert(hdr != NULL)) |
| return 0; |
| return ossl_cmp_asn1_octet_string_set1(&hdr->senderKID, senderKID); |
| } |
| |
| /* push the given text string to the given PKIFREETEXT ft */ |
| int ossl_cmp_hdr_push0_freeText(OSSL_CMP_PKIHEADER *hdr, ASN1_UTF8STRING *text) |
| { |
| if (!ossl_assert(hdr != NULL && text != NULL)) |
| return 0; |
| |
| if (hdr->freeText == NULL |
| && (hdr->freeText = sk_ASN1_UTF8STRING_new_null()) == NULL) |
| return 0; |
| |
| return sk_ASN1_UTF8STRING_push(hdr->freeText, text); |
| } |
| |
| int ossl_cmp_hdr_push1_freeText(OSSL_CMP_PKIHEADER *hdr, ASN1_UTF8STRING *text) |
| { |
| if (!ossl_assert(hdr != NULL && text != NULL)) |
| return 0; |
| |
| if (hdr->freeText == NULL |
| && (hdr->freeText = sk_ASN1_UTF8STRING_new_null()) == NULL) |
| return 0; |
| |
| return |
| ossl_cmp_sk_ASN1_UTF8STRING_push_str(hdr->freeText, (char *)text->data, |
| text->length); |
| } |
| |
| int ossl_cmp_hdr_generalInfo_push0_item(OSSL_CMP_PKIHEADER *hdr, |
| OSSL_CMP_ITAV *itav) |
| { |
| if (!ossl_assert(hdr != NULL && itav != NULL)) |
| return 0; |
| return OSSL_CMP_ITAV_push0_stack_item(&hdr->generalInfo, itav); |
| } |
| |
| int ossl_cmp_hdr_generalInfo_push1_items(OSSL_CMP_PKIHEADER *hdr, |
| const STACK_OF(OSSL_CMP_ITAV) *itavs) |
| { |
| int i; |
| OSSL_CMP_ITAV *itav; |
| |
| if (!ossl_assert(hdr != NULL)) |
| return 0; |
| |
| for (i = 0; i < sk_OSSL_CMP_ITAV_num(itavs); i++) { |
| itav = OSSL_CMP_ITAV_dup(sk_OSSL_CMP_ITAV_value(itavs, i)); |
| if (itav == NULL) |
| return 0; |
| |
| if (!ossl_cmp_hdr_generalInfo_push0_item(hdr, itav)) { |
| OSSL_CMP_ITAV_free(itav); |
| return 0; |
| } |
| } |
| return 1; |
| } |
| |
| int ossl_cmp_hdr_set_implicitConfirm(OSSL_CMP_PKIHEADER *hdr) |
| { |
| OSSL_CMP_ITAV *itav; |
| ASN1_TYPE *asn1null; |
| |
| if (!ossl_assert(hdr != NULL)) |
| return 0; |
| asn1null = (ASN1_TYPE *)ASN1_NULL_new(); |
| if (asn1null == NULL) |
| return 0; |
| if ((itav = OSSL_CMP_ITAV_create(OBJ_nid2obj(NID_id_it_implicitConfirm), |
| asn1null)) == NULL) |
| goto err; |
| if (!ossl_cmp_hdr_generalInfo_push0_item(hdr, itav)) |
| goto err; |
| return 1; |
| |
| err: |
| ASN1_TYPE_free(asn1null); |
| OSSL_CMP_ITAV_free(itav); |
| return 0; |
| } |
| |
| /* return 1 if implicitConfirm in the generalInfo field of the header is set */ |
| int ossl_cmp_hdr_has_implicitConfirm(const OSSL_CMP_PKIHEADER *hdr) |
| { |
| int itavCount; |
| int i; |
| OSSL_CMP_ITAV *itav; |
| |
| if (!ossl_assert(hdr != NULL)) |
| return 0; |
| |
| itavCount = sk_OSSL_CMP_ITAV_num(hdr->generalInfo); |
| for (i = 0; i < itavCount; i++) { |
| itav = sk_OSSL_CMP_ITAV_value(hdr->generalInfo, i); |
| if (itav != NULL |
| && OBJ_obj2nid(itav->infoType) == NID_id_it_implicitConfirm) |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * set ctx->transactionID in CMP header |
| * if ctx->transactionID is NULL, a random one is created with 128 bit |
| * according to section 5.1.1: |
| * |
| * It is RECOMMENDED that the clients fill the transactionID field with |
| * 128 bits of (pseudo-) random data for the start of a transaction to |
| * reduce the probability of having the transactionID in use at the server. |
| */ |
| int ossl_cmp_hdr_set_transactionID(OSSL_CMP_CTX *ctx, OSSL_CMP_PKIHEADER *hdr) |
| { |
| if (ctx->transactionID == NULL) { |
| char *tid; |
| |
| if (!set_random(&ctx->transactionID, ctx, |
| OSSL_CMP_TRANSACTIONID_LENGTH)) |
| return 0; |
| tid = i2s_ASN1_OCTET_STRING(NULL, ctx->transactionID); |
| if (tid != NULL) |
| ossl_cmp_log1(DEBUG, ctx, |
| "Starting new transaction with ID=%s", tid); |
| OPENSSL_free(tid); |
| } |
| |
| return ossl_cmp_asn1_octet_string_set1(&hdr->transactionID, |
| ctx->transactionID); |
| } |
| |
| /* fill in all fields of the hdr according to the info given in ctx */ |
| int ossl_cmp_hdr_init(OSSL_CMP_CTX *ctx, OSSL_CMP_PKIHEADER *hdr) |
| { |
| const X509_NAME *sender; |
| const X509_NAME *rcp = NULL; |
| |
| if (!ossl_assert(ctx != NULL && hdr != NULL)) |
| return 0; |
| |
| /* set the CMP version */ |
| if (!ossl_cmp_hdr_set_pvno(hdr, OSSL_CMP_PVNO)) |
| return 0; |
| |
| /* |
| * If no protection cert nor oldCert nor CSR nor subject is given, |
| * sender name is not known to the client and thus set to NULL-DN |
| */ |
| sender = ctx->cert != NULL ? X509_get_subject_name(ctx->cert) : |
| ctx->oldCert != NULL ? X509_get_subject_name(ctx->oldCert) : |
| ctx->p10CSR != NULL ? X509_REQ_get_subject_name(ctx->p10CSR) : |
| ctx->subjectName; |
| if (!ossl_cmp_hdr_set1_sender(hdr, sender)) |
| return 0; |
| |
| /* determine recipient entry in PKIHeader */ |
| if (ctx->recipient != NULL) |
| rcp = ctx->recipient; |
| else if (ctx->srvCert != NULL) |
| rcp = X509_get_subject_name(ctx->srvCert); |
| else if (ctx->issuer != NULL) |
| rcp = ctx->issuer; |
| else if (ctx->oldCert != NULL) |
| rcp = X509_get_issuer_name(ctx->oldCert); |
| else if (ctx->cert != NULL) |
| rcp = X509_get_issuer_name(ctx->cert); |
| if (!ossl_cmp_hdr_set1_recipient(hdr, rcp)) |
| return 0; |
| |
| /* set current time as message time */ |
| if (!ossl_cmp_hdr_update_messageTime(hdr)) |
| return 0; |
| |
| if (ctx->recipNonce != NULL |
| && !ossl_cmp_asn1_octet_string_set1(&hdr->recipNonce, |
| ctx->recipNonce)) |
| return 0; |
| |
| if (!ossl_cmp_hdr_set_transactionID(ctx, hdr)) |
| return 0; |
| |
| /*- |
| * set random senderNonce |
| * according to section 5.1.1: |
| * |
| * senderNonce present |
| * -- 128 (pseudo-)random bits |
| * The senderNonce and recipNonce fields protect the PKIMessage against |
| * replay attacks. The senderNonce will typically be 128 bits of |
| * (pseudo-) random data generated by the sender, whereas the recipNonce |
| * is copied from the senderNonce of the previous message in the |
| * transaction. |
| */ |
| if (!set_random(&hdr->senderNonce, ctx, OSSL_CMP_SENDERNONCE_LENGTH)) |
| return 0; |
| |
| /* store senderNonce - for cmp with recipNonce in next outgoing msg */ |
| if (!OSSL_CMP_CTX_set1_senderNonce(ctx, hdr->senderNonce)) |
| return 0; |
| |
| /*- |
| * freeText [7] PKIFreeText OPTIONAL, |
| * -- this may be used to indicate context-specific instructions |
| * -- (this field is intended for human consumption) |
| */ |
| if (ctx->freeText != NULL |
| && !ossl_cmp_hdr_push1_freeText(hdr, ctx->freeText)) |
| return 0; |
| |
| return 1; |
| } |