1 /*
2  * Copyright 2007-2025 The OpenSSL Project Authors. All Rights Reserved.
3  * Copyright Nokia 2007-2019
4  * Copyright Siemens AG 2015-2019
5  *
6  * Licensed under the Apache License 2.0 (the "License").  You may not use
7  * this file except in compliance with the License.  You can obtain a copy
8  * in the file LICENSE in the source distribution or at
9  * https://www.openssl.org/source/license.html
10  */
11 
12 #include "cmp_local.h"
13 #include "crypto/asn1.h" /* for ossl_X509_ALGOR_from_nid() */
14 
15 /*
16  * This function is also used by the internal verify_PBMAC() in cmp_vfy.c.
17  *
18  * Calculate protection for |msg| according to |msg->header->protectionAlg|
19  * using the credentials, library context, and property criteria in the ctx.
20  * Unless |msg->header->protectionAlg| is PasswordBasedMAC,
21  * its value is completed according to |ctx->pkey| and |ctx->digest|,
22  * where the latter irrelevant in the case of Edwards curves.
23  *
24  * returns ASN1_BIT_STRING representing the protection on success, else NULL
25  */
ossl_cmp_calc_protection(const OSSL_CMP_CTX * ctx,const OSSL_CMP_MSG * msg)26 ASN1_BIT_STRING *ossl_cmp_calc_protection(const OSSL_CMP_CTX *ctx,
27                                           const OSSL_CMP_MSG *msg)
28 {
29     ASN1_BIT_STRING *prot = NULL;
30     OSSL_CMP_PROTECTEDPART prot_part;
31     const ASN1_OBJECT *algorOID = NULL;
32     const void *ppval = NULL;
33     int pptype = 0;
34 
35     if (!ossl_assert(ctx != NULL && msg != NULL))
36         return NULL;
37 
38     /* construct data to be signed */
39     prot_part.header = msg->header;
40     prot_part.body = msg->body;
41 
42     if (msg->header->protectionAlg == NULL) {
43         ERR_raise(ERR_LIB_CMP, CMP_R_UNKNOWN_ALGORITHM_ID);
44         return NULL;
45     }
46     X509_ALGOR_get0(&algorOID, &pptype, &ppval, msg->header->protectionAlg);
47 
48     if (OBJ_obj2nid(algorOID) == NID_id_PasswordBasedMAC) {
49         int len;
50         size_t prot_part_der_len;
51         unsigned char *prot_part_der = NULL;
52         size_t sig_len;
53         unsigned char *protection = NULL;
54         OSSL_CRMF_PBMPARAMETER *pbm = NULL;
55         ASN1_STRING *pbm_str = NULL;
56         const unsigned char *pbm_str_uc = NULL;
57 
58         if (ctx->secretValue == NULL) {
59             ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_PBM_SECRET);
60             return NULL;
61         }
62         if (ppval == NULL) {
63             ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_CALCULATING_PROTECTION);
64             return NULL;
65         }
66 
67         len = i2d_OSSL_CMP_PROTECTEDPART(&prot_part, &prot_part_der);
68         if (len < 0 || prot_part_der == NULL) {
69             ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_CALCULATING_PROTECTION);
70             goto end;
71         }
72         prot_part_der_len = (size_t)len;
73 
74         pbm_str = (ASN1_STRING *)ppval;
75         pbm_str_uc = pbm_str->data;
76         pbm = d2i_OSSL_CRMF_PBMPARAMETER(NULL, &pbm_str_uc, pbm_str->length);
77         if (pbm == NULL) {
78             ERR_raise(ERR_LIB_CMP, CMP_R_WRONG_ALGORITHM_OID);
79             goto end;
80         }
81 
82         if (!OSSL_CRMF_pbm_new(ctx->libctx, ctx->propq,
83                                pbm, prot_part_der, prot_part_der_len,
84                                ctx->secretValue->data, ctx->secretValue->length,
85                                &protection, &sig_len))
86             goto end;
87 
88         if (sig_len > INT_MAX || (prot = ASN1_BIT_STRING_new()) == NULL)
89             goto end;
90         /* OpenSSL by default encodes all bit strings as ASN.1 NamedBitList */
91         ossl_asn1_string_set_bits_left(prot, 0);
92         if (!ASN1_BIT_STRING_set(prot, protection, (int)sig_len)) {
93             ASN1_BIT_STRING_free(prot);
94             prot = NULL;
95         }
96     end:
97         OSSL_CRMF_PBMPARAMETER_free(pbm);
98         OPENSSL_free(protection);
99         OPENSSL_free(prot_part_der);
100         return prot;
101     } else {
102         const EVP_MD *md = ctx->digest;
103         char name[80] = "";
104 
105         if (ctx->pkey == NULL) {
106             ERR_raise(ERR_LIB_CMP,
107                       CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION);
108             return NULL;
109         }
110         if (EVP_PKEY_get_default_digest_name(ctx->pkey, name, sizeof(name)) > 0
111             && strcmp(name, "UNDEF") == 0) /* at least for Ed25519, Ed448 */
112             md = NULL;
113 
114         if ((prot = ASN1_BIT_STRING_new()) == NULL)
115             return NULL;
116         if (ASN1_item_sign_ex(ASN1_ITEM_rptr(OSSL_CMP_PROTECTEDPART),
117                               msg->header->protectionAlg, /* sets X509_ALGOR */
118                               NULL, prot, &prot_part, NULL, ctx->pkey, md,
119                               ctx->libctx, ctx->propq))
120             return prot;
121         ASN1_BIT_STRING_free(prot);
122         return NULL;
123     }
124 }
125 
ossl_cmp_set_own_chain(OSSL_CMP_CTX * ctx)126 void ossl_cmp_set_own_chain(OSSL_CMP_CTX *ctx)
127 {
128     if (!ossl_assert(ctx != NULL))
129         return;
130     /* if not yet done try to build chain using available untrusted certs */
131     if (ctx->chain == NULL) {
132         ossl_cmp_debug(ctx, "trying to build chain for own CMP signer cert");
133         ctx->chain = X509_build_chain(ctx->cert, ctx->untrusted, NULL, 0,
134                                       ctx->libctx, ctx->propq);
135         if (ctx->chain != NULL) {
136             ossl_cmp_debug(ctx, "success building chain for own CMP signer cert");
137         } else {
138             /* dump errors to avoid confusion when printing further ones */
139             OSSL_CMP_CTX_print_errors(ctx);
140             ossl_cmp_warn(ctx, "could not build chain for own CMP signer cert");
141         }
142     }
143 }
144 
145 /* ctx is not const just because ctx->chain may get adapted */
ossl_cmp_msg_add_extraCerts(OSSL_CMP_CTX * ctx,OSSL_CMP_MSG * msg)146 int ossl_cmp_msg_add_extraCerts(OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg)
147 {
148     if (!ossl_assert(ctx != NULL && msg != NULL))
149         return 0;
150 
151     /* Add first ctx->cert and its chain if using signature-based protection */
152     if (!ctx->unprotectedSend && ctx->secretValue == NULL
153             && ctx->cert != NULL && ctx->pkey != NULL) {
154         int prepend = X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP
155             | X509_ADD_FLAG_PREPEND | X509_ADD_FLAG_NO_SS;
156 
157         ossl_cmp_set_own_chain(ctx);
158         if (ctx->chain != NULL) {
159             if (!ossl_x509_add_certs_new(&msg->extraCerts, ctx->chain, prepend))
160                 return 0;
161         } else {
162             /* make sure that at least our own signer cert is included first */
163             if (!ossl_x509_add_cert_new(&msg->extraCerts, ctx->cert, prepend))
164                 return 0;
165             ossl_cmp_debug(ctx, "fallback: adding just own CMP signer cert");
166         }
167     }
168 
169     /* add any additional certificates from ctx->extraCertsOut */
170     if (!ossl_x509_add_certs_new(&msg->extraCerts, ctx->extraCertsOut,
171                                  X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP))
172         return 0;
173 
174     /* in case extraCerts are empty list avoid empty ASN.1 sequence */
175     if (sk_X509_num(msg->extraCerts) == 0) {
176         sk_X509_free(msg->extraCerts);
177         msg->extraCerts = NULL;
178     }
179     return 1;
180 }
181 
182 /*
183  * Create an X509_ALGOR structure for PasswordBasedMAC protection based on
184  * the pbm settings in the context
185  */
pbmac_algor(const OSSL_CMP_CTX * ctx)186 static X509_ALGOR *pbmac_algor(const OSSL_CMP_CTX *ctx)
187 {
188     OSSL_CRMF_PBMPARAMETER *pbm = NULL;
189     unsigned char *pbm_der = NULL;
190     int pbm_der_len;
191     ASN1_STRING *pbm_str = NULL;
192     X509_ALGOR *alg = NULL;
193 
194     if (!ossl_assert(ctx != NULL))
195         return NULL;
196 
197     pbm = OSSL_CRMF_pbmp_new(ctx->libctx, ctx->pbm_slen,
198                              EVP_MD_get_type(ctx->pbm_owf), ctx->pbm_itercnt,
199                              ctx->pbm_mac);
200     pbm_str = ASN1_STRING_new();
201     if (pbm == NULL || pbm_str == NULL)
202         goto err;
203     if ((pbm_der_len = i2d_OSSL_CRMF_PBMPARAMETER(pbm, &pbm_der)) < 0)
204         goto err;
205     if (!ASN1_STRING_set(pbm_str, pbm_der, pbm_der_len))
206         goto err;
207     alg = ossl_X509_ALGOR_from_nid(NID_id_PasswordBasedMAC,
208                                    V_ASN1_SEQUENCE, pbm_str);
209  err:
210     if (alg == NULL)
211         ASN1_STRING_free(pbm_str);
212     OPENSSL_free(pbm_der);
213     OSSL_CRMF_PBMPARAMETER_free(pbm);
214     return alg;
215 }
216 
set_senderKID(const OSSL_CMP_CTX * ctx,OSSL_CMP_MSG * msg,const ASN1_OCTET_STRING * id)217 static int set_senderKID(const OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg,
218                          const ASN1_OCTET_STRING *id)
219 {
220     if (id == NULL)
221         id = ctx->referenceValue; /* standard for PBM, fallback for sig-based */
222     return id == NULL || ossl_cmp_hdr_set1_senderKID(msg->header, id);
223 }
224 
225 /* ctx is not const just because ctx->chain may get adapted */
ossl_cmp_msg_protect(OSSL_CMP_CTX * ctx,OSSL_CMP_MSG * msg)226 int ossl_cmp_msg_protect(OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg)
227 {
228     if (!ossl_assert(ctx != NULL && msg != NULL))
229         return 0;
230 
231     /*
232      * For the case of re-protection remove pre-existing protection.
233      * Does not remove any pre-existing extraCerts.
234      */
235     X509_ALGOR_free(msg->header->protectionAlg);
236     msg->header->protectionAlg = NULL;
237     ASN1_BIT_STRING_free(msg->protection);
238     msg->protection = NULL;
239 
240     if (ctx->unprotectedSend) {
241         if (!set_senderKID(ctx, msg, NULL))
242             goto err;
243     } else if (ctx->secretValue != NULL) {
244         /* use PasswordBasedMac according to 5.1.3.1 if secretValue is given */
245         if ((msg->header->protectionAlg = pbmac_algor(ctx)) == NULL)
246             goto err;
247         if (!set_senderKID(ctx, msg, NULL))
248             goto err;
249 
250         /*
251          * will add any additional certificates from ctx->extraCertsOut
252          * while not needed to validate the protection certificate,
253          * the option to do this might be handy for certain use cases
254          */
255     } else if (ctx->cert != NULL && ctx->pkey != NULL) {
256         /* use MSG_SIG_ALG according to 5.1.3.3 if client cert and key given */
257 
258         /* make sure that key and certificate match */
259         if (!X509_check_private_key(ctx->cert, ctx->pkey)) {
260             ERR_raise(ERR_LIB_CMP, CMP_R_CERT_AND_KEY_DO_NOT_MATCH);
261             goto err;
262         }
263 
264         if ((msg->header->protectionAlg = X509_ALGOR_new()) == NULL)
265             goto err;
266         /* set senderKID to keyIdentifier of the cert according to 5.1.1 */
267         if (!set_senderKID(ctx, msg, X509_get0_subject_key_id(ctx->cert)))
268             goto err;
269 
270         /*
271          * will add ctx->cert followed, if possible, by its chain built
272          * from ctx->untrusted, and then ctx->extraCertsOut
273          */
274     } else {
275         ERR_raise(ERR_LIB_CMP,
276                   CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION);
277         goto err;
278     }
279     if (!ctx->unprotectedSend
280         /* protect according to msg->header->protectionAlg partly set above */
281             && ((msg->protection = ossl_cmp_calc_protection(ctx, msg)) == NULL))
282         goto err;
283 
284     /*
285      * For signature-based protection add ctx->cert followed by its chain.
286      * Finally add any additional certificates from ctx->extraCertsOut;
287      * even if not needed to validate the protection
288      * the option to do this might be handy for certain use cases.
289      */
290     if (!ossl_cmp_msg_add_extraCerts(ctx, msg))
291         goto err;
292 
293     /*
294      * As required by RFC 4210 section 5.1.1., if the sender name is not known
295      * to the client it set to NULL-DN. In this case for identification at least
296      * the senderKID must be set, where we took the referenceValue as fallback.
297      */
298     if (!(ossl_cmp_general_name_is_NULL_DN(msg->header->sender)
299           && msg->header->senderKID == NULL))
300         return 1;
301     ERR_raise(ERR_LIB_CMP, CMP_R_MISSING_SENDER_IDENTIFICATION);
302 
303  err:
304     ERR_raise(ERR_LIB_CMP, CMP_R_ERROR_PROTECTING_MESSAGE);
305     return 0;
306 }
307