]>
Commit | Line | Data |
---|---|---|
3dbc5156 | 1 | /* |
33388b44 | 2 | * Copyright 2007-2020 The OpenSSL Project Authors. All Rights Reserved. |
3dbc5156 DDO |
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 | ||
14 | /* explicit #includes not strictly needed since implied by the above: */ | |
15 | #include <openssl/asn1t.h> | |
16 | #include <openssl/cmp.h> | |
17 | #include <openssl/crmf.h> | |
18 | #include <openssl/err.h> | |
19 | #include <openssl/x509.h> | |
20 | ||
852c2ed2 RS |
21 | DEFINE_STACK_OF(X509) |
22 | ||
3dbc5156 | 23 | /* |
6d1f50b5 | 24 | * This function is also used by the internal verify_PBMAC() in cmp_vfy.c. |
3dbc5156 | 25 | * |
6d1f50b5 DDO |
26 | * Calculate protection for given PKImessage according to |
27 | * the algorithm and parameters in the message header's protectionAlg | |
28 | * using the credentials, library context, and property criteria in the ctx. | |
3dbc5156 | 29 | * |
6d1f50b5 | 30 | * returns ASN1_BIT_STRING representing the protection on success, else NULL |
3dbc5156 | 31 | */ |
6d1f50b5 DDO |
32 | ASN1_BIT_STRING *ossl_cmp_calc_protection(const OSSL_CMP_CTX *ctx, |
33 | const OSSL_CMP_MSG *msg) | |
3dbc5156 DDO |
34 | { |
35 | ASN1_BIT_STRING *prot = NULL; | |
642f60d8 | 36 | OSSL_CMP_PROTECTEDPART prot_part; |
3dbc5156 | 37 | const ASN1_OBJECT *algorOID = NULL; |
3dbc5156 DDO |
38 | const void *ppval = NULL; |
39 | int pptype = 0; | |
3dbc5156 | 40 | |
6d1f50b5 | 41 | if (!ossl_assert(ctx != NULL && msg != NULL)) |
3dbc5156 DDO |
42 | return NULL; |
43 | ||
44 | /* construct data to be signed */ | |
45 | prot_part.header = msg->header; | |
46 | prot_part.body = msg->body; | |
47 | ||
3dbc5156 DDO |
48 | if (msg->header->protectionAlg == NULL) { |
49 | CMPerr(0, CMP_R_UNKNOWN_ALGORITHM_ID); | |
6d1f50b5 | 50 | return NULL; |
3dbc5156 DDO |
51 | } |
52 | X509_ALGOR_get0(&algorOID, &pptype, &ppval, msg->header->protectionAlg); | |
53 | ||
6d1f50b5 DDO |
54 | if (OBJ_obj2nid(algorOID) == NID_id_PasswordBasedMAC) { |
55 | int len; | |
56 | size_t prot_part_der_len; | |
57 | unsigned char *prot_part_der = NULL; | |
58 | size_t sig_len; | |
59 | unsigned char *protection = NULL; | |
60 | OSSL_CRMF_PBMPARAMETER *pbm = NULL; | |
61 | ASN1_STRING *pbm_str = NULL; | |
62 | const unsigned char *pbm_str_uc = NULL; | |
63 | ||
64 | if (ctx->secretValue == NULL) { | |
65 | CMPerr(0, CMP_R_MISSING_PBM_SECRET); | |
66 | return NULL; | |
67 | } | |
3dbc5156 DDO |
68 | if (ppval == NULL) { |
69 | CMPerr(0, CMP_R_ERROR_CALCULATING_PROTECTION); | |
6d1f50b5 | 70 | return NULL; |
3dbc5156 | 71 | } |
6d1f50b5 DDO |
72 | |
73 | len = i2d_OSSL_CMP_PROTECTEDPART(&prot_part, &prot_part_der); | |
74 | if (len < 0 || prot_part_der == NULL) { | |
75 | CMPerr(0, CMP_R_ERROR_CALCULATING_PROTECTION); | |
3dbc5156 DDO |
76 | goto end; |
77 | } | |
6d1f50b5 DDO |
78 | prot_part_der_len = (size_t)len; |
79 | ||
3dbc5156 DDO |
80 | pbm_str = (ASN1_STRING *)ppval; |
81 | pbm_str_uc = pbm_str->data; | |
82 | pbm = d2i_OSSL_CRMF_PBMPARAMETER(NULL, &pbm_str_uc, pbm_str->length); | |
83 | if (pbm == NULL) { | |
84 | CMPerr(0, CMP_R_WRONG_ALGORITHM_OID); | |
85 | goto end; | |
86 | } | |
87 | ||
6d1f50b5 DDO |
88 | if (!OSSL_CRMF_pbm_new(ctx->libctx, ctx->propq, |
89 | pbm, prot_part_der, prot_part_der_len, | |
90 | ctx->secretValue->data, ctx->secretValue->length, | |
3dbc5156 DDO |
91 | &protection, &sig_len)) |
92 | goto end; | |
3dbc5156 | 93 | |
6d1f50b5 DDO |
94 | if ((prot = ASN1_BIT_STRING_new()) == NULL) |
95 | return NULL; | |
96 | /* OpenSSL defaults all bit strings to be encoded as ASN.1 NamedBitList */ | |
97 | prot->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07); | |
98 | prot->flags |= ASN1_STRING_FLAG_BITS_LEFT; | |
99 | if (!ASN1_BIT_STRING_set(prot, protection, sig_len)) { | |
100 | ASN1_BIT_STRING_free(prot); | |
101 | prot = NULL; | |
3dbc5156 | 102 | } |
6d1f50b5 DDO |
103 | end: |
104 | OSSL_CRMF_PBMPARAMETER_free(pbm); | |
105 | OPENSSL_free(protection); | |
106 | OPENSSL_free(prot_part_der); | |
107 | return prot; | |
3dbc5156 | 108 | } else { |
6d1f50b5 DDO |
109 | int md_nid; |
110 | const EVP_MD *md = NULL; | |
3dbc5156 | 111 | |
6d1f50b5 DDO |
112 | if (ctx->pkey == NULL) { |
113 | CMPerr(0, CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION); | |
114 | return NULL; | |
115 | } | |
116 | if (!OBJ_find_sigid_algs(OBJ_obj2nid(algorOID), &md_nid, NULL) | |
117 | || (md = EVP_get_digestbynid(md_nid)) == NULL) { | |
118 | CMPerr(0, CMP_R_UNKNOWN_ALGORITHM_ID); | |
119 | return NULL; | |
120 | } | |
121 | ||
122 | if ((prot = ASN1_BIT_STRING_new()) == NULL) | |
123 | return NULL; | |
124 | if (ASN1_item_sign_with_libctx(ASN1_ITEM_rptr(OSSL_CMP_PROTECTEDPART), | |
125 | NULL, NULL, prot, &prot_part, NULL, | |
126 | ctx->pkey, md, ctx->libctx, ctx->propq)) | |
127 | return prot; | |
3dbc5156 | 128 | ASN1_BIT_STRING_free(prot); |
6d1f50b5 | 129 | return NULL; |
3dbc5156 | 130 | } |
3dbc5156 DDO |
131 | } |
132 | ||
133 | int ossl_cmp_msg_add_extraCerts(OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg) | |
134 | { | |
135 | if (!ossl_assert(ctx != NULL && msg != NULL)) | |
136 | return 0; | |
137 | ||
138 | if (msg->extraCerts == NULL | |
139 | && (msg->extraCerts = sk_X509_new_null()) == NULL) | |
140 | return 0; | |
141 | ||
63f1883d | 142 | if (ctx->cert != NULL && ctx->pkey != NULL) { |
143be474 | 143 | /* make sure that our own cert is included in the first position */ |
eeccc237 DDO |
144 | if (!X509_add_cert(msg->extraCerts, ctx->cert, |
145 | X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP | |
146 | | X509_ADD_FLAG_PREPEND)) | |
3dbc5156 | 147 | return 0; |
143be474 | 148 | /* if we have untrusted certs, try to add intermediate certs */ |
3dbc5156 | 149 | if (ctx->untrusted_certs != NULL) { |
1a5ae1da DDO |
150 | STACK_OF(X509) *chain; |
151 | int res; | |
152 | ||
153 | ossl_cmp_debug(ctx, | |
154 | "trying to build chain for own CMP signer cert"); | |
15076c26 | 155 | chain = ossl_cmp_build_cert_chain(ctx->libctx, ctx->propq, NULL, |
1a5ae1da DDO |
156 | ctx->untrusted_certs, ctx->cert); |
157 | res = X509_add_certs(msg->extraCerts, chain, | |
158 | X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP | |
159 | | X509_ADD_FLAG_NO_SS); | |
3dbc5156 | 160 | sk_X509_pop_free(chain, X509_free); |
1a5ae1da DDO |
161 | if (res == 0) { |
162 | ossl_cmp_err(ctx, | |
163 | "could not build chain for own CMP signer cert"); | |
3dbc5156 | 164 | return 0; |
1a5ae1da DDO |
165 | } |
166 | ossl_cmp_debug(ctx, | |
167 | "succeeded building chain for own CMP signer cert"); | |
3dbc5156 DDO |
168 | } |
169 | } | |
170 | ||
171 | /* add any additional certificates from ctx->extraCertsOut */ | |
eeccc237 DDO |
172 | if (!X509_add_certs(msg->extraCerts, ctx->extraCertsOut, |
173 | X509_ADD_FLAG_UP_REF | X509_ADD_FLAG_NO_DUP)) | |
3dbc5156 DDO |
174 | return 0; |
175 | ||
176 | /* if none was found avoid empty ASN.1 sequence */ | |
177 | if (sk_X509_num(msg->extraCerts) == 0) { | |
178 | sk_X509_free(msg->extraCerts); | |
179 | msg->extraCerts = NULL; | |
180 | } | |
181 | return 1; | |
182 | } | |
183 | ||
184 | /* | |
185 | * Create an X509_ALGOR structure for PasswordBasedMAC protection based on | |
186 | * the pbm settings in the context | |
3dbc5156 | 187 | */ |
6d1f50b5 | 188 | static int set_pbmac_algor(const OSSL_CMP_CTX *ctx, X509_ALGOR **alg) |
3dbc5156 | 189 | { |
3dbc5156 DDO |
190 | OSSL_CRMF_PBMPARAMETER *pbm = NULL; |
191 | unsigned char *pbm_der = NULL; | |
192 | int pbm_der_len; | |
193 | ASN1_STRING *pbm_str = NULL; | |
194 | ||
195 | if (!ossl_assert(ctx != NULL)) | |
6d1f50b5 | 196 | return 0; |
3dbc5156 | 197 | |
97e00da9 | 198 | pbm = OSSL_CRMF_pbmp_new(ctx->libctx, ctx->pbm_slen, |
6d1f50b5 DDO |
199 | EVP_MD_type(ctx->pbm_owf), ctx->pbm_itercnt, |
200 | ctx->pbm_mac); | |
3dbc5156 | 201 | pbm_str = ASN1_STRING_new(); |
6d1f50b5 | 202 | if (pbm == NULL || pbm_str == NULL) |
3dbc5156 DDO |
203 | goto err; |
204 | ||
205 | if ((pbm_der_len = i2d_OSSL_CRMF_PBMPARAMETER(pbm, &pbm_der)) < 0) | |
206 | goto err; | |
207 | ||
208 | if (!ASN1_STRING_set(pbm_str, pbm_der, pbm_der_len)) | |
209 | goto err; | |
6d1f50b5 DDO |
210 | if (*alg == NULL && (*alg = X509_ALGOR_new()) == NULL) |
211 | goto err; | |
3dbc5156 DDO |
212 | OPENSSL_free(pbm_der); |
213 | ||
6d1f50b5 | 214 | X509_ALGOR_set0(*alg, OBJ_nid2obj(NID_id_PasswordBasedMAC), |
3dbc5156 DDO |
215 | V_ASN1_SEQUENCE, pbm_str); |
216 | OSSL_CRMF_PBMPARAMETER_free(pbm); | |
6d1f50b5 | 217 | return 1; |
3dbc5156 DDO |
218 | |
219 | err: | |
220 | ASN1_STRING_free(pbm_str); | |
3dbc5156 DDO |
221 | OPENSSL_free(pbm_der); |
222 | OSSL_CRMF_PBMPARAMETER_free(pbm); | |
6d1f50b5 DDO |
223 | return 0; |
224 | } | |
225 | ||
226 | static int set_sig_algor(const OSSL_CMP_CTX *ctx, X509_ALGOR **alg) | |
227 | { | |
228 | int nid = 0; | |
229 | ASN1_OBJECT *algo = NULL; | |
230 | ||
231 | if (!OBJ_find_sigid_by_algs(&nid, EVP_MD_type(ctx->digest), | |
232 | EVP_PKEY_id(ctx->pkey))) { | |
233 | CMPerr(0, CMP_R_UNSUPPORTED_KEY_TYPE); | |
234 | return 0; | |
235 | } | |
236 | if ((algo = OBJ_nid2obj(nid)) == NULL) | |
237 | return 0; | |
238 | if (*alg == NULL && (*alg = X509_ALGOR_new()) == NULL) | |
239 | return 0; | |
240 | ||
241 | if (X509_ALGOR_set0(*alg, algo, V_ASN1_UNDEF, NULL)) | |
242 | return 1; | |
243 | ASN1_OBJECT_free(algo); | |
244 | return 0; | |
245 | } | |
246 | ||
247 | static int set_senderKID(const OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg, | |
248 | const ASN1_OCTET_STRING *id) | |
249 | { | |
250 | if (id == NULL) | |
251 | id = ctx->referenceValue; /* standard for PBM, fallback for sig-based */ | |
252 | return id == NULL || ossl_cmp_hdr_set1_senderKID(msg->header, id); | |
3dbc5156 DDO |
253 | } |
254 | ||
255 | int ossl_cmp_msg_protect(OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg) | |
256 | { | |
257 | if (!ossl_assert(ctx != NULL && msg != NULL)) | |
258 | return 0; | |
259 | ||
143be474 DDO |
260 | /* |
261 | * For the case of re-protection remove pre-existing protection. | |
262 | * TODO: Consider also removing any pre-existing extraCerts. | |
263 | */ | |
264 | X509_ALGOR_free(msg->header->protectionAlg); | |
265 | msg->header->protectionAlg = NULL; | |
266 | ASN1_BIT_STRING_free(msg->protection); | |
267 | msg->protection = NULL; | |
268 | ||
3dbc5156 DDO |
269 | if (ctx->unprotectedSend) |
270 | return 1; | |
271 | ||
272 | /* use PasswordBasedMac according to 5.1.3.1 if secretValue is given */ | |
273 | if (ctx->secretValue != NULL) { | |
6d1f50b5 | 274 | if (!set_pbmac_algor(ctx, &msg->header->protectionAlg)) |
3dbc5156 | 275 | goto err; |
6d1f50b5 | 276 | if (!set_senderKID(ctx, msg, NULL)) |
3dbc5156 | 277 | goto err; |
6d1f50b5 | 278 | |
3dbc5156 | 279 | /* |
6d1f50b5 DDO |
280 | * will add any additional certificates from ctx->extraCertsOut |
281 | * while not needed to validate the protection certificate, | |
282 | * the option to do this might be handy for certain use cases | |
3dbc5156 | 283 | */ |
6d1f50b5 DDO |
284 | } else if (ctx->cert != NULL && ctx->pkey != NULL) { |
285 | /* use MSG_SIG_ALG according to 5.1.3.3 if client cert and key given */ | |
3dbc5156 | 286 | |
143be474 | 287 | /* make sure that key and certificate match */ |
63f1883d | 288 | if (!X509_check_private_key(ctx->cert, ctx->pkey)) { |
143be474 DDO |
289 | CMPerr(0, CMP_R_CERT_AND_KEY_DO_NOT_MATCH); |
290 | goto err; | |
291 | } | |
3dbc5156 | 292 | |
6d1f50b5 | 293 | if (!set_sig_algor(ctx, &msg->header->protectionAlg)) |
143be474 | 294 | goto err; |
6d1f50b5 DDO |
295 | /* set senderKID to keyIdentifier of the cert according to 5.1.1 */ |
296 | if (!set_senderKID(ctx, msg, X509_get0_subject_key_id(ctx->cert))) | |
143be474 | 297 | goto err; |
143be474 DDO |
298 | |
299 | /* | |
6d1f50b5 DDO |
300 | * will add ctx->cert followed, if possible, by its chain built |
301 | * from ctx->untrusted_certs, and then ctx->extraCertsOut | |
143be474 | 302 | */ |
143be474 DDO |
303 | } else { |
304 | CMPerr(0, CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION); | |
305 | goto err; | |
3dbc5156 | 306 | } |
6d1f50b5 | 307 | if ((msg->protection = ossl_cmp_calc_protection(ctx, msg)) == NULL) |
143be474 DDO |
308 | goto err; |
309 | ||
310 | /* | |
63f1883d | 311 | * If present, add ctx->cert followed by its chain as far as possible. |
143be474 DDO |
312 | * Finally add any additional certificates from ctx->extraCertsOut; |
313 | * even if not needed to validate the protection | |
314 | * the option to do this might be handy for certain use cases. | |
315 | */ | |
316 | if (!ossl_cmp_msg_add_extraCerts(ctx, msg)) | |
317 | goto err; | |
3dbc5156 | 318 | |
cfca56df DDO |
319 | /* |
320 | * As required by RFC 4210 section 5.1.1., if the sender name is not known | |
321 | * to the client it set to NULL-DN. In this case for identification at least | |
322 | * the senderKID must be set, where we took the referenceValue as fallback. | |
323 | */ | |
cfca56df DDO |
324 | if (ossl_cmp_general_name_is_NULL_DN(msg->header->sender) |
325 | && msg->header->senderKID == NULL) | |
326 | CMPerr(0, CMP_R_MISSING_SENDER_IDENTIFICATION); | |
327 | else | |
328 | return 1; | |
329 | ||
3dbc5156 DDO |
330 | err: |
331 | CMPerr(0, CMP_R_ERROR_PROTECTING_MESSAGE); | |
332 | return 0; | |
333 | } |