Add -reqin_new_tid option to apps/cmp.c and OSSL_CMP_MSG_update_transactionID()
[oweals/openssl.git] / crypto / cmp / cmp_protect.c
1 /*
2  * Copyright 2007-2020 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
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
21 DEFINE_STACK_OF(X509)
22
23 /*
24  * This function is also used for verification from cmp_vfy.
25  *
26  * Calculate protection for given PKImessage utilizing the given credentials
27  * and the algorithm parameters set inside the message header's protectionAlg.
28  *
29  * Either secret or pkey must be set, the other must be NULL. Attempts doing
30  * PBMAC in case 'secret' is set and signature if 'pkey' is set - but will only
31  * do the protection already marked in msg->header->protectionAlg.
32  *
33  * returns ptr to ASN1_BIT_STRING containing protection on success, else NULL
34  */
35 ASN1_BIT_STRING *ossl_cmp_calc_protection(const OSSL_CMP_MSG *msg,
36                                           const ASN1_OCTET_STRING *secret,
37                                           EVP_PKEY *pkey)
38 {
39     ASN1_BIT_STRING *prot = NULL;
40     OSSL_CMP_PROTECTEDPART prot_part;
41     const ASN1_OBJECT *algorOID = NULL;
42     int len;
43     size_t prot_part_der_len;
44     unsigned char *prot_part_der = NULL;
45     size_t sig_len;
46     unsigned char *protection = NULL;
47     const void *ppval = NULL;
48     int pptype = 0;
49     OSSL_CRMF_PBMPARAMETER *pbm = NULL;
50     ASN1_STRING *pbm_str = NULL;
51     const unsigned char *pbm_str_uc = NULL;
52     EVP_MD_CTX *evp_ctx = NULL;
53     int md_NID;
54     const EVP_MD *md = NULL;
55
56     if (!ossl_assert(msg != NULL))
57         return NULL;
58
59     /* construct data to be signed */
60     prot_part.header = msg->header;
61     prot_part.body = msg->body;
62
63     len = i2d_OSSL_CMP_PROTECTEDPART(&prot_part, &prot_part_der);
64     if (len < 0 || prot_part_der == NULL) {
65         CMPerr(0, CMP_R_ERROR_CALCULATING_PROTECTION);
66         goto end;
67     }
68     prot_part_der_len = (size_t) len;
69
70     if (msg->header->protectionAlg == NULL) {
71         CMPerr(0, CMP_R_UNKNOWN_ALGORITHM_ID);
72         goto end;
73     }
74     X509_ALGOR_get0(&algorOID, &pptype, &ppval, msg->header->protectionAlg);
75
76     if (secret != NULL && pkey == NULL) {
77         if (ppval == NULL) {
78             CMPerr(0, CMP_R_ERROR_CALCULATING_PROTECTION);
79             goto end;
80         }
81         if (NID_id_PasswordBasedMAC != OBJ_obj2nid(algorOID)) {
82             CMPerr(0, CMP_R_WRONG_ALGORITHM_OID);
83             goto end;
84         }
85         pbm_str = (ASN1_STRING *)ppval;
86         pbm_str_uc = pbm_str->data;
87         pbm = d2i_OSSL_CRMF_PBMPARAMETER(NULL, &pbm_str_uc, pbm_str->length);
88         if (pbm == NULL) {
89             CMPerr(0, CMP_R_WRONG_ALGORITHM_OID);
90             goto end;
91         }
92
93         if (!OSSL_CRMF_pbm_new(pbm, prot_part_der, prot_part_der_len,
94                                secret->data, secret->length,
95                                &protection, &sig_len))
96             goto end;
97     } else if (secret == NULL && pkey != NULL) {
98         /* TODO combine this with large parts of CRMF_poposigningkey_init() */
99         /* EVP_DigestSignInit() checks that pkey type is correct for the alg */
100
101         if (!OBJ_find_sigid_algs(OBJ_obj2nid(algorOID), &md_NID, NULL)
102                 || (md = EVP_get_digestbynid(md_NID)) == NULL
103                 || (evp_ctx = EVP_MD_CTX_new()) == NULL) {
104             CMPerr(0, CMP_R_UNKNOWN_ALGORITHM_ID);
105             goto end;
106         }
107         if (EVP_DigestSignInit(evp_ctx, NULL, md, NULL, pkey) <= 0
108                 || EVP_DigestSignUpdate(evp_ctx, prot_part_der,
109                                         prot_part_der_len) <= 0
110                 || EVP_DigestSignFinal(evp_ctx, NULL, &sig_len) <= 0
111                 || (protection = OPENSSL_malloc(sig_len)) == NULL
112                 || EVP_DigestSignFinal(evp_ctx, protection, &sig_len) <= 0) {
113             CMPerr(0, CMP_R_ERROR_CALCULATING_PROTECTION);
114             goto end;
115         }
116     } else {
117         CMPerr(0, CMP_R_INVALID_ARGS);
118         goto end;
119     }
120
121     if ((prot = ASN1_BIT_STRING_new()) == NULL)
122         goto end;
123     /* OpenSSL defaults all bit strings to be encoded as ASN.1 NamedBitList */
124     prot->flags &= ~(ASN1_STRING_FLAG_BITS_LEFT | 0x07);
125     prot->flags |= ASN1_STRING_FLAG_BITS_LEFT;
126     if (!ASN1_BIT_STRING_set(prot, protection, sig_len)) {
127         ASN1_BIT_STRING_free(prot);
128         prot = NULL;
129     }
130
131  end:
132     OSSL_CRMF_PBMPARAMETER_free(pbm);
133     EVP_MD_CTX_free(evp_ctx);
134     OPENSSL_free(protection);
135     OPENSSL_free(prot_part_der);
136     return prot;
137 }
138
139 int ossl_cmp_msg_add_extraCerts(OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg)
140 {
141     if (!ossl_assert(ctx != NULL && msg != NULL))
142         return 0;
143
144     if (msg->extraCerts == NULL
145             && (msg->extraCerts = sk_X509_new_null()) == NULL)
146         return 0;
147
148     if (ctx->clCert != NULL && ctx->pkey != NULL) {
149         /* make sure that our own cert is included in the first position */
150         if (!ossl_cmp_sk_X509_add1_cert(msg->extraCerts, ctx->clCert, 1, 1))
151             return 0;
152         /* if we have untrusted certs, try to add intermediate certs */
153         if (ctx->untrusted_certs != NULL) {
154             STACK_OF(X509) *chain =
155                 ossl_cmp_build_cert_chain(ctx->untrusted_certs, ctx->clCert);
156             int res = ossl_cmp_sk_X509_add1_certs(msg->extraCerts, chain,
157                                                   1 /* no self-issued */,
158                                                   1 /* no duplicates */, 0);
159
160             sk_X509_pop_free(chain, X509_free);
161             if (res == 0)
162                 return 0;
163         }
164     }
165
166     /* add any additional certificates from ctx->extraCertsOut */
167     if (!ossl_cmp_sk_X509_add1_certs(msg->extraCerts, ctx->extraCertsOut, 0,
168                                      1 /* no duplicates */, 0))
169         return 0;
170
171     /* if none was found avoid empty ASN.1 sequence */
172     if (sk_X509_num(msg->extraCerts) == 0) {
173         sk_X509_free(msg->extraCerts);
174         msg->extraCerts = NULL;
175     }
176     return 1;
177 }
178
179 /*
180  * Create an X509_ALGOR structure for PasswordBasedMAC protection based on
181  * the pbm settings in the context
182  * returns pointer to X509_ALGOR on success, NULL on error
183  */
184 static X509_ALGOR *create_pbmac_algor(OSSL_CMP_CTX *ctx)
185 {
186     X509_ALGOR *alg = NULL;
187     OSSL_CRMF_PBMPARAMETER *pbm = NULL;
188     unsigned char *pbm_der = NULL;
189     int pbm_der_len;
190     ASN1_STRING *pbm_str = NULL;
191
192     if (!ossl_assert(ctx != NULL))
193         return NULL;
194
195     alg = X509_ALGOR_new();
196     pbm = OSSL_CRMF_pbmp_new(ctx->pbm_slen, ctx->pbm_owf, ctx->pbm_itercnt,
197                              ctx->pbm_mac);
198     pbm_str = ASN1_STRING_new();
199     if (alg == NULL || pbm == NULL || pbm_str == NULL)
200         goto err;
201
202     if ((pbm_der_len = i2d_OSSL_CRMF_PBMPARAMETER(pbm, &pbm_der)) < 0)
203         goto err;
204
205     if (!ASN1_STRING_set(pbm_str, pbm_der, pbm_der_len))
206         goto err;
207     OPENSSL_free(pbm_der);
208
209     X509_ALGOR_set0(alg, OBJ_nid2obj(NID_id_PasswordBasedMAC),
210                     V_ASN1_SEQUENCE, pbm_str);
211     OSSL_CRMF_PBMPARAMETER_free(pbm);
212     return alg;
213
214  err:
215     ASN1_STRING_free(pbm_str);
216     X509_ALGOR_free(alg);
217     OPENSSL_free(pbm_der);
218     OSSL_CRMF_PBMPARAMETER_free(pbm);
219     return NULL;
220 }
221
222 int ossl_cmp_msg_protect(OSSL_CMP_CTX *ctx, OSSL_CMP_MSG *msg)
223 {
224     if (!ossl_assert(ctx != NULL && msg != NULL))
225         return 0;
226
227     /*
228      * For the case of re-protection remove pre-existing protection.
229      * TODO: Consider also removing any pre-existing extraCerts.
230      */
231     X509_ALGOR_free(msg->header->protectionAlg);
232     msg->header->protectionAlg = NULL;
233     ASN1_BIT_STRING_free(msg->protection);
234     msg->protection = NULL;
235
236     if (ctx->unprotectedSend)
237         return 1;
238
239     /* use PasswordBasedMac according to 5.1.3.1 if secretValue is given */
240     if (ctx->secretValue != NULL) {
241         if ((msg->header->protectionAlg = create_pbmac_algor(ctx)) == NULL)
242             goto err;
243         if (ctx->referenceValue != NULL
244                 && !ossl_cmp_hdr_set1_senderKID(msg->header,
245                                                 ctx->referenceValue))
246             goto err;
247     } else if (ctx->clCert != NULL && ctx->pkey != NULL) {
248         /*
249          * use MSG_SIG_ALG according to 5.1.3.3 if client Certificate and
250          * private key is given
251          */
252         const ASN1_OCTET_STRING *subjKeyIDStr = NULL;
253         int algNID = 0;
254         ASN1_OBJECT *alg = NULL;
255
256         /* make sure that key and certificate match */
257         if (!X509_check_private_key(ctx->clCert, ctx->pkey)) {
258             CMPerr(0, CMP_R_CERT_AND_KEY_DO_NOT_MATCH);
259             goto err;
260         }
261
262         if (msg->header->protectionAlg == NULL)
263             if ((msg->header->protectionAlg = X509_ALGOR_new()) == NULL)
264                 goto err;
265
266         if (!OBJ_find_sigid_by_algs(&algNID, ctx->digest,
267                                     EVP_PKEY_id(ctx->pkey))) {
268             CMPerr(0, CMP_R_UNSUPPORTED_KEY_TYPE);
269             goto err;
270         }
271         if ((alg = OBJ_nid2obj(algNID)) == NULL)
272             goto err;
273         if (!X509_ALGOR_set0(msg->header->protectionAlg, alg,
274                              V_ASN1_UNDEF, NULL)) {
275             ASN1_OBJECT_free(alg);
276             goto err;
277         }
278
279         /*
280          * set senderKID to keyIdentifier of the used certificate according
281          * to section 5.1.1
282          */
283         subjKeyIDStr = X509_get0_subject_key_id(ctx->clCert);
284         if (subjKeyIDStr == NULL)
285             subjKeyIDStr = ctx->referenceValue; /* fallback */
286         if (subjKeyIDStr != NULL
287                 && !ossl_cmp_hdr_set1_senderKID(msg->header, subjKeyIDStr))
288             goto err;
289     } else {
290         CMPerr(0, CMP_R_MISSING_KEY_INPUT_FOR_CREATING_PROTECTION);
291         goto err;
292     }
293     if ((msg->protection =
294          ossl_cmp_calc_protection(msg, ctx->secretValue, ctx->pkey)) == NULL)
295         goto err;
296
297     /*
298      * If present, add ctx->clCert followed by its chain as far as possible.
299      * Finally add any additional certificates from ctx->extraCertsOut;
300      * even if not needed to validate the protection
301      * the option to do this might be handy for certain use cases.
302      */
303     if (!ossl_cmp_msg_add_extraCerts(ctx, msg))
304         goto err;
305
306     /*
307      * As required by RFC 4210 section 5.1.1., if the sender name is not known
308      * to the client it set to NULL-DN. In this case for identification at least
309      * the senderKID must be set, where we took the referenceValue as fallback.
310      */
311     if (ossl_cmp_general_name_is_NULL_DN(msg->header->sender)
312             && msg->header->senderKID == NULL)
313         CMPerr(0, CMP_R_MISSING_SENDER_IDENTIFICATION);
314     else
315         return 1;
316
317  err:
318     CMPerr(0, CMP_R_ERROR_PROTECTING_MESSAGE);
319     return 0;
320 }