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