/src/openssl41/crypto/crmf/crmf_pbm.c
Line | Count | Source |
1 | | /*- |
2 | | * Copyright 2007-2026 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 | | * CRMF implementation by Martin Peylo, Miikka Viljanen, and David von Oheimb. |
12 | | */ |
13 | | |
14 | | #include "crmf_local.h" |
15 | | #include <openssl/rand.h> /* for RAND_bytes_ex() */ |
16 | | #include "internal/sizes.h" /* for OSSL_MAX_NAME_SIZE */ |
17 | | #include <openssl/err.h> |
18 | | |
19 | | #include <crypto/asn1.h> |
20 | | |
21 | | /*- |
22 | | * creates and initializes OSSL_CRMF_PBMPARAMETER (section 4.4) |
23 | | * |slen| SHOULD be at least 8 (16 is common) |
24 | | * |owfnid| e.g., NID_sha256 |
25 | | * |itercnt| MUST be >= 100 (e.g., 500) and <= OSSL_CRMF_PBM_MAX_ITERATION_COUNT |
26 | | * |macnid| e.g., NID_hmac_sha1 |
27 | | * returns pointer to OSSL_CRMF_PBMPARAMETER on success, NULL on error |
28 | | */ |
29 | | OSSL_CRMF_PBMPARAMETER *OSSL_CRMF_pbmp_new(OSSL_LIB_CTX *libctx, size_t slen, |
30 | | int owfnid, size_t itercnt, |
31 | | int macnid) |
32 | 0 | { |
33 | 0 | OSSL_CRMF_PBMPARAMETER *pbm = NULL; |
34 | 0 | unsigned char *salt = NULL; |
35 | |
|
36 | 0 | if ((pbm = OSSL_CRMF_PBMPARAMETER_new()) == NULL) |
37 | 0 | goto err; |
38 | | |
39 | | /* |
40 | | * salt contains a randomly generated value used in computing the key |
41 | | * of the MAC process. The salt SHOULD be at least 8 octets (64 |
42 | | * bits) long. |
43 | | */ |
44 | 0 | if ((salt = OPENSSL_malloc(slen)) == NULL) |
45 | 0 | goto err; |
46 | 0 | if (RAND_bytes_ex(libctx, salt, slen, 0) <= 0) { |
47 | 0 | ERR_raise(ERR_LIB_CRMF, CRMF_R_FAILURE_OBTAINING_RANDOM); |
48 | 0 | goto err; |
49 | 0 | } |
50 | 0 | if (!ASN1_OCTET_STRING_set(pbm->salt, salt, (int)slen)) |
51 | 0 | goto err; |
52 | | |
53 | | /* |
54 | | * owf identifies the hash algorithm and associated parameters used to |
55 | | * compute the key used in the MAC process. All implementations MUST |
56 | | * support SHA-1. |
57 | | */ |
58 | 0 | if (!X509_ALGOR_set0(pbm->owf, OBJ_nid2obj(owfnid), V_ASN1_UNDEF, NULL)) { |
59 | 0 | ERR_raise(ERR_LIB_CRMF, CRMF_R_SETTING_OWF_ALGOR_FAILURE); |
60 | 0 | goto err; |
61 | 0 | } |
62 | | |
63 | | /* |
64 | | * iterationCount identifies the number of times the hash is applied |
65 | | * during the key computation process. The iterationCount MUST be a |
66 | | * minimum of 100. Many people suggest using values as high as 1000 |
67 | | * iterations as the minimum value. The trade off here is between |
68 | | * protection of the password from attacks and the time spent by the |
69 | | * server processing all of the different iterations in deriving |
70 | | * passwords. Hashing is generally considered a cheap operation but |
71 | | * this may not be true with all hash functions in the future. |
72 | | */ |
73 | 0 | if (itercnt < 100) { |
74 | 0 | ERR_raise(ERR_LIB_CRMF, CRMF_R_ITERATIONCOUNT_BELOW_100); |
75 | 0 | goto err; |
76 | 0 | } |
77 | 0 | if (itercnt > OSSL_CRMF_PBM_MAX_ITERATION_COUNT) { |
78 | 0 | ERR_raise(ERR_LIB_CRMF, CRMF_R_BAD_PBM_ITERATIONCOUNT); |
79 | 0 | goto err; |
80 | 0 | } |
81 | | |
82 | 0 | if (!ASN1_INTEGER_set(pbm->iterationCount, (long)itercnt)) { |
83 | 0 | ERR_raise(ERR_LIB_CRMF, CRMF_R_CRMFERROR); |
84 | 0 | goto err; |
85 | 0 | } |
86 | | |
87 | | /* |
88 | | * mac identifies the algorithm and associated parameters of the MAC |
89 | | * function to be used. All implementations MUST support HMAC-SHA1 [HMAC]. |
90 | | * All implementations SHOULD support DES-MAC and Triple-DES-MAC [PKCS11]. |
91 | | */ |
92 | 0 | if (!X509_ALGOR_set0(pbm->mac, OBJ_nid2obj(macnid), V_ASN1_UNDEF, NULL)) { |
93 | 0 | ERR_raise(ERR_LIB_CRMF, CRMF_R_SETTING_MAC_ALGOR_FAILURE); |
94 | 0 | goto err; |
95 | 0 | } |
96 | | |
97 | 0 | OPENSSL_free(salt); |
98 | 0 | return pbm; |
99 | 0 | err: |
100 | 0 | OPENSSL_free(salt); |
101 | 0 | OSSL_CRMF_PBMPARAMETER_free(pbm); |
102 | 0 | return NULL; |
103 | 0 | } |
104 | | |
105 | | /*- |
106 | | * calculates the PBM based on the settings of the given OSSL_CRMF_PBMPARAMETER |
107 | | * |pbmp| identifies the algorithms, salt to use |
108 | | * |msg| message to apply the PBM for |
109 | | * |msglen| length of the message |
110 | | * |sec| key to use |
111 | | * |seclen| length of the key |
112 | | * |out| pointer to the computed mac, will be set on success |
113 | | * |outlen| if not NULL, will set variable to the length of the mac on success |
114 | | * returns 1 on success, 0 on error |
115 | | */ |
116 | | /* could be combined with other MAC calculations in the library */ |
117 | | int OSSL_CRMF_pbm_new(OSSL_LIB_CTX *libctx, const char *propq, |
118 | | const OSSL_CRMF_PBMPARAMETER *pbmp, |
119 | | const unsigned char *msg, size_t msglen, |
120 | | const unsigned char *sec, size_t seclen, |
121 | | unsigned char **out, size_t *outlen) |
122 | 897 | { |
123 | 897 | int mac_nid, hmac_md_nid = NID_undef; |
124 | 897 | char mdname[OSSL_MAX_NAME_SIZE]; |
125 | 897 | char hmac_mdname[OSSL_MAX_NAME_SIZE]; |
126 | 897 | EVP_MD *owf = NULL; |
127 | 897 | EVP_MD_CTX *ctx = NULL; |
128 | 897 | unsigned char basekey[EVP_MAX_MD_SIZE]; |
129 | 897 | unsigned int bklen = EVP_MAX_MD_SIZE; |
130 | 897 | int64_t iterations; |
131 | 897 | unsigned char *mac_res = 0; |
132 | 897 | int ok = 0; |
133 | | |
134 | 897 | if (out == NULL || pbmp == NULL || pbmp->mac == NULL |
135 | 897 | || pbmp->mac->algorithm == NULL || msg == NULL || sec == NULL) { |
136 | 17 | ERR_raise(ERR_LIB_CRMF, CRMF_R_NULL_ARGUMENT); |
137 | 17 | goto err; |
138 | 17 | } |
139 | 880 | if ((mac_res = OPENSSL_malloc(EVP_MAX_MD_SIZE)) == NULL) |
140 | 0 | goto err; |
141 | | |
142 | | /* |
143 | | * owf identifies the hash algorithm and associated parameters used to |
144 | | * compute the key used in the MAC process. All implementations MUST |
145 | | * support SHA-1. |
146 | | */ |
147 | 880 | OBJ_obj2txt(mdname, sizeof(mdname), pbmp->owf->algorithm, 0); |
148 | 880 | if ((owf = EVP_MD_fetch(libctx, mdname, propq)) == NULL) { |
149 | 109 | ERR_raise(ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_ALGORITHM); |
150 | 109 | goto err; |
151 | 109 | } |
152 | | |
153 | 771 | if ((ctx = EVP_MD_CTX_new()) == NULL) |
154 | 0 | goto err; |
155 | | |
156 | | /* compute the basekey of the salted secret */ |
157 | 771 | if (!EVP_DigestInit_ex(ctx, owf, NULL)) |
158 | 0 | goto err; |
159 | | /* first the secret */ |
160 | 771 | if (!EVP_DigestUpdate(ctx, sec, seclen)) |
161 | 0 | goto err; |
162 | | /* then the salt */ |
163 | 771 | if (!EVP_DigestUpdate(ctx, pbmp->salt->data, pbmp->salt->length)) |
164 | 0 | goto err; |
165 | 771 | if (!EVP_DigestFinal_ex(ctx, basekey, &bklen)) |
166 | 19 | goto err; |
167 | 752 | if (!ASN1_INTEGER_get_int64(&iterations, pbmp->iterationCount) |
168 | 752 | || iterations < 100 /* min from RFC */ |
169 | 654 | || iterations > OSSL_CRMF_PBM_MAX_ITERATION_COUNT) { |
170 | 123 | ERR_raise(ERR_LIB_CRMF, CRMF_R_BAD_PBM_ITERATIONCOUNT); |
171 | 123 | goto err; |
172 | 123 | } |
173 | | |
174 | | /* the first iteration was already done above */ |
175 | 2.95M | while (--iterations > 0) { |
176 | 2.95M | if (!EVP_DigestInit_ex(ctx, owf, NULL)) |
177 | 0 | goto err; |
178 | 2.95M | if (!EVP_DigestUpdate(ctx, basekey, bklen)) |
179 | 0 | goto err; |
180 | 2.95M | if (!EVP_DigestFinal_ex(ctx, basekey, &bklen)) |
181 | 0 | goto err; |
182 | 2.95M | } |
183 | | |
184 | | /* |
185 | | * mac identifies the algorithm and associated parameters of the MAC |
186 | | * function to be used. All implementations MUST support HMAC-SHA1 [HMAC]. |
187 | | * All implementations SHOULD support DES-MAC and Triple-DES-MAC [PKCS11]. |
188 | | */ |
189 | 629 | mac_nid = OBJ_obj2nid(pbmp->mac->algorithm); |
190 | | |
191 | 629 | if (!EVP_PBE_find(EVP_PBE_TYPE_PRF, mac_nid, NULL, &hmac_md_nid, NULL) |
192 | 412 | || OBJ_obj2txt(hmac_mdname, sizeof(hmac_mdname), |
193 | 412 | OBJ_nid2obj(hmac_md_nid), 0) |
194 | 412 | <= 0) { |
195 | 217 | ERR_raise(ERR_LIB_CRMF, CRMF_R_UNSUPPORTED_ALGORITHM); |
196 | 217 | goto err; |
197 | 217 | } |
198 | | /* could be generalized to allow non-HMAC: */ |
199 | 412 | if (EVP_Q_mac(libctx, "HMAC", propq, hmac_mdname, NULL, basekey, bklen, |
200 | 412 | msg, msglen, mac_res, EVP_MAX_MD_SIZE, outlen) |
201 | 412 | == NULL) |
202 | 18 | goto err; |
203 | | |
204 | 394 | ok = 1; |
205 | | |
206 | 897 | err: |
207 | 897 | OPENSSL_cleanse(basekey, bklen); |
208 | 897 | EVP_MD_free(owf); |
209 | 897 | EVP_MD_CTX_free(ctx); |
210 | | |
211 | 897 | if (ok == 1) { |
212 | 394 | *out = mac_res; |
213 | 394 | return 1; |
214 | 394 | } |
215 | | |
216 | 503 | OPENSSL_free(mac_res); |
217 | | |
218 | 503 | if (pbmp != NULL && pbmp->mac != NULL) { |
219 | 503 | char buf[128]; |
220 | | |
221 | 503 | if (OBJ_obj2txt(buf, sizeof(buf), pbmp->mac->algorithm, 0)) |
222 | 503 | ERR_add_error_data(1, buf); |
223 | 503 | } |
224 | 503 | return 0; |
225 | 897 | } |