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