Branch data Line data Source code
1 : : /*
2 : : * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License").
5 : : * You may not use this file except in compliance with the License.
6 : : * A copy of the License is located at
7 : : *
8 : : * http://aws.amazon.com/apache2.0
9 : : *
10 : : * or in the "license" file accompanying this file. This file is distributed
11 : : * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12 : : * express or implied. See the License for the specific language governing
13 : : * permissions and limitations under the License.
14 : : */
15 : :
16 : : #include "crypto/s2n_pkey.h"
17 : :
18 : : #include <openssl/evp.h>
19 : :
20 : : #include "crypto/s2n_mldsa.h"
21 : : #include "crypto/s2n_openssl_evp.h"
22 : : #include "crypto/s2n_openssl_x509.h"
23 : : #include "crypto/s2n_pkey_evp.h"
24 : : #include "crypto/s2n_rsa_pss.h"
25 : : #include "error/s2n_errno.h"
26 : : #include "utils/s2n_mem.h"
27 : : #include "utils/s2n_result.h"
28 : : #include "utils/s2n_safety.h"
29 : :
30 : : #ifndef EVP_PKEY_RSA_PSS
31 : : #define EVP_PKEY_RSA_PSS EVP_PKEY_NONE
32 : : #endif
33 : :
34 : : int s2n_pkey_zero_init(struct s2n_pkey *pkey)
35 : 7103114 : {
36 : 7103114 : pkey->pkey = NULL;
37 : 7103114 : pkey->size = NULL;
38 : 7103114 : pkey->sign = NULL;
39 : 7103114 : pkey->verify = NULL;
40 : 7103114 : pkey->encrypt = NULL;
41 : 7103114 : pkey->decrypt = NULL;
42 : 7103114 : return 0;
43 : 7103114 : }
44 : :
45 : : S2N_RESULT s2n_pkey_setup_for_type(struct s2n_pkey *pkey, s2n_pkey_type pkey_type)
46 : 12242 : {
47 [ - + ]: 12242 : switch (pkey_type) {
48 [ + + ]: 9373 : case S2N_PKEY_TYPE_RSA:
49 [ + + ]: 12060 : case S2N_PKEY_TYPE_ECDSA:
50 [ + + ]: 12242 : case S2N_PKEY_TYPE_RSA_PSS:
51 [ - + ]: 12242 : case S2N_PKEY_TYPE_MLDSA:
52 : 12242 : return s2n_pkey_evp_init(pkey);
53 [ - + ]: 0 : case S2N_PKEY_TYPE_SENTINEL:
54 [ - + ]: 0 : case S2N_PKEY_TYPE_UNKNOWN:
55 [ # # ]: 0 : RESULT_BAIL(S2N_ERR_CERT_TYPE_UNSUPPORTED);
56 : 12242 : }
57 [ # # ]: 0 : RESULT_BAIL(S2N_ERR_CERT_TYPE_UNSUPPORTED);
58 : 0 : }
59 : :
60 : : int s2n_pkey_check_key_exists(const struct s2n_pkey *pkey)
61 : 2314 : {
62 [ # # ][ - + ]: 2314 : POSIX_ENSURE_REF(pkey);
63 [ + + ][ + - ]: 2314 : POSIX_ENSURE_REF(pkey->pkey);
64 : 2226 : return S2N_SUCCESS;
65 : 2314 : }
66 : :
67 : : S2N_RESULT s2n_pkey_size(const struct s2n_pkey *pkey, uint32_t *size_out)
68 : 8203 : {
69 [ - + ][ # # ]: 8203 : RESULT_ENSURE_REF(pkey);
70 [ - + ][ # # ]: 8203 : RESULT_ENSURE_REF(pkey->size);
71 [ - + ][ # # ]: 8203 : RESULT_ENSURE_REF(size_out);
72 : :
73 [ - + ]: 8203 : RESULT_GUARD(pkey->size(pkey, size_out));
74 : :
75 : 8203 : return S2N_RESULT_OK;
76 : 8203 : }
77 : :
78 : : int s2n_pkey_sign(const struct s2n_pkey *pkey, s2n_signature_algorithm sig_alg,
79 : : struct s2n_hash_state *digest, struct s2n_blob *signature)
80 : 5054 : {
81 [ # # ][ - + ]: 5054 : POSIX_ENSURE_REF(pkey->sign);
82 : :
83 : 5054 : return pkey->sign(pkey, sig_alg, digest, signature);
84 : 5054 : }
85 : :
86 : : int s2n_pkey_verify(const struct s2n_pkey *pkey, s2n_signature_algorithm sig_alg,
87 : : struct s2n_hash_state *digest, struct s2n_blob *signature)
88 : 4241 : {
89 [ - + ][ # # ]: 4241 : POSIX_ENSURE_REF(pkey);
90 [ - + ][ # # ]: 4241 : POSIX_ENSURE_REF(pkey->verify);
91 : :
92 : 4241 : return pkey->verify(pkey, sig_alg, digest, signature);
93 : 4241 : }
94 : :
95 : : int s2n_pkey_encrypt(const struct s2n_pkey *pkey, struct s2n_blob *in, struct s2n_blob *out)
96 : 248 : {
97 [ - + ][ # # ]: 248 : POSIX_ENSURE_REF(pkey->encrypt);
98 : :
99 : 248 : return pkey->encrypt(pkey, in, out);
100 : 248 : }
101 : :
102 : : int s2n_pkey_decrypt(const struct s2n_pkey *pkey, struct s2n_blob *in, struct s2n_blob *out)
103 : 1710 : {
104 [ - + ][ # # ]: 1710 : POSIX_ENSURE_REF(pkey->decrypt);
105 : :
106 : 1710 : return pkey->decrypt(pkey, in, out);
107 : 1710 : }
108 : :
109 : : int s2n_pkey_match(const struct s2n_pkey *pub_key, const struct s2n_pkey *priv_key)
110 : 801 : {
111 [ - + ][ # # ]: 801 : POSIX_ENSURE_REF(pub_key);
112 : :
113 : : /* Minimally, both keys must be of the same type */
114 : 801 : s2n_pkey_type priv_type = 0, pub_type = 0;
115 [ - + ]: 801 : POSIX_GUARD_RESULT(s2n_pkey_get_type(priv_key->pkey, &priv_type));
116 [ - + ]: 801 : POSIX_GUARD_RESULT(s2n_pkey_get_type(pub_key->pkey, &pub_type));
117 [ + - ][ + + ]: 801 : POSIX_ENSURE(priv_type == pub_type, S2N_ERR_KEY_MISMATCH);
118 : :
119 : : /* If both keys are of the same type, check that the public key
120 : : * can verify a test signature from the private key.
121 : : */
122 : :
123 : 777 : uint8_t input[] = "key check";
124 : 777 : DEFER_CLEANUP(struct s2n_blob signature = { 0 }, s2n_free);
125 : :
126 : : /* Choose one signature algorithm to test each type of pkey.
127 : : * For example, RSA certs can be used for either S2N_SIGNATURE_RSA (PKCS1)
128 : : * or S2N_SIGNATURE_RSA_PSS_RSAE, but we only test with S2N_SIGNATURE_RSA.
129 : : */
130 : 777 : s2n_signature_algorithm check_alg = S2N_SIGNATURE_ANONYMOUS;
131 : 777 : s2n_hash_algorithm hash_alg = S2N_HASH_SHA256;
132 : 777 : switch (priv_type) {
133 [ + + ]: 283 : case S2N_PKEY_TYPE_ECDSA:
134 : 283 : check_alg = S2N_SIGNATURE_ECDSA;
135 : 283 : break;
136 [ + + ]: 459 : case S2N_PKEY_TYPE_RSA:
137 : 459 : check_alg = S2N_SIGNATURE_RSA;
138 : 459 : break;
139 [ + + ]: 35 : case S2N_PKEY_TYPE_RSA_PSS:
140 : 35 : check_alg = S2N_SIGNATURE_RSA_PSS_PSS;
141 : 35 : break;
142 [ - + ]: 0 : case S2N_PKEY_TYPE_MLDSA:
143 : 0 : check_alg = S2N_SIGNATURE_MLDSA;
144 : 0 : hash_alg = S2N_HASH_SHAKE256_64;
145 : 0 : break;
146 [ - + ]: 0 : default:
147 [ # # ]: 0 : POSIX_BAIL(S2N_ERR_CERT_TYPE_UNSUPPORTED);
148 : 777 : }
149 : :
150 : 777 : DEFER_CLEANUP(struct s2n_hash_state state_in = { 0 }, s2n_hash_free);
151 [ - + ]: 777 : POSIX_GUARD(s2n_hash_new(&state_in));
152 [ - + ]: 777 : POSIX_GUARD(s2n_hash_init(&state_in, hash_alg));
153 [ - + ]: 777 : POSIX_GUARD_RESULT(s2n_pkey_init_hash(pub_key, check_alg, &state_in));
154 [ - + ]: 777 : POSIX_GUARD(s2n_hash_update(&state_in, input, sizeof(input)));
155 : :
156 : 777 : DEFER_CLEANUP(struct s2n_hash_state state_out = { 0 }, s2n_hash_free);
157 [ - + ]: 777 : POSIX_GUARD(s2n_hash_new(&state_out));
158 [ - + ]: 777 : POSIX_GUARD(s2n_hash_copy(&state_out, &state_in));
159 : :
160 : 777 : uint32_t size = 0;
161 [ - + ]: 777 : POSIX_GUARD_RESULT(s2n_pkey_size(priv_key, &size));
162 [ - + ]: 777 : POSIX_GUARD(s2n_alloc(&signature, size));
163 : :
164 : : /* Note: The Libcrypto RSA EVP_PKEY will cache certain computations used for
165 : : * RSA signing.
166 : : *
167 : : * This means that the first RSA sign with an EVP_PKEY is ~300 us slower
168 : : * than subsequent sign operations. The effect is much smaller for ECDSA signatures.
169 : : *
170 : : * If this pkey_sign operation is moved out of config creation, then the
171 : : * 300 us penalty will be paid by the first handshake done on the config.
172 : : */
173 [ - + ]: 777 : POSIX_GUARD(s2n_pkey_sign(priv_key, check_alg, &state_in, &signature));
174 [ + + ][ + - ]: 777 : POSIX_ENSURE(s2n_pkey_verify(pub_key, check_alg, &state_out, &signature) == S2N_SUCCESS,
175 : 769 : S2N_ERR_KEY_MISMATCH);
176 : :
177 : 769 : return S2N_SUCCESS;
178 : 777 : }
179 : :
180 : : int s2n_pkey_free(struct s2n_pkey *key)
181 : 7106458 : {
182 [ - + ]: 7106458 : if (key == NULL) {
183 : 0 : return S2N_SUCCESS;
184 : 0 : }
185 [ + + ]: 7106458 : if (key->pkey != NULL) {
186 : 6586 : EVP_PKEY_free(key->pkey);
187 : 6586 : key->pkey = NULL;
188 : 6586 : }
189 : 7106458 : return S2N_SUCCESS;
190 : 7106458 : }
191 : :
192 : : S2N_RESULT s2n_asn1der_to_private_key(struct s2n_pkey *priv_key, struct s2n_blob *asn1der, int type_hint)
193 : 768 : {
194 : 768 : const unsigned char *key_to_parse = asn1der->data;
195 : :
196 : : /* We use "d2i_AutoPrivateKey" instead of "PEM_read_bio_PrivateKey" because
197 : : * s2n-tls prefers to perform its own custom PEM parsing. Historically,
198 : : * openssl's PEM parsing tended to ignore invalid certificates rather than
199 : : * error on them. We prefer to fail early rather than continue without
200 : : * the full and correct chain intended by the application.
201 : : */
202 : 768 : DEFER_CLEANUP(EVP_PKEY *evp_private_key = d2i_AutoPrivateKey(NULL, &key_to_parse, asn1der->size),
203 : 768 : EVP_PKEY_free_pointer);
204 : :
205 : : /* We have found cases where d2i_AutoPrivateKey fails to detect the type of
206 : : * the key. For example, openssl fails to identify an EC key without the
207 : : * optional publicKey field.
208 : : *
209 : : * If d2i_AutoPrivateKey fails, try once more with the type we parsed from the PEM.
210 : : */
211 [ + + ]: 768 : if (evp_private_key == NULL) {
212 : : /* libcrypto implementations leave key_to_parse unchanged on failure,
213 : : * but OpenSSL only documents the successful-advance behavior.
214 : : * Reset key_to_parse defensively so the retry always starts from the
215 : : * beginning of the buffer, regardless of underlying libcrypto behavior.
216 : : */
217 : 1 : key_to_parse = asn1der->data;
218 : 1 : evp_private_key = d2i_PrivateKey(type_hint, NULL, &key_to_parse, asn1der->size);
219 : 1 : }
220 [ + + ][ + - ]: 768 : RESULT_ENSURE(evp_private_key, S2N_ERR_DECODE_PRIVATE_KEY);
221 : :
222 : : /* If key parsing is successful, d2i_AutoPrivateKey increments *key_to_parse to the byte following the parsed data */
223 : 767 : uint32_t parsed_len = key_to_parse - asn1der->data;
224 [ - + ][ # # ]: 767 : RESULT_ENSURE(parsed_len == asn1der->size, S2N_ERR_DECODE_PRIVATE_KEY);
225 : :
226 : : /* Initialize s2n_pkey according to key type */
227 : 767 : s2n_pkey_type type = 0;
228 [ - + ]: 767 : RESULT_GUARD(s2n_pkey_get_type(evp_private_key, &type));
229 [ - + ]: 767 : RESULT_GUARD(s2n_pkey_setup_for_type(priv_key, type));
230 : :
231 : 767 : priv_key->pkey = evp_private_key;
232 : 767 : ZERO_TO_DISABLE_DEFER_CLEANUP(evp_private_key);
233 : :
234 : 767 : return S2N_RESULT_OK;
235 : 767 : }
236 : :
237 : : S2N_RESULT s2n_asn1der_to_public_key_and_type(struct s2n_pkey *pub_key,
238 : : s2n_pkey_type *pkey_type_out, struct s2n_blob *asn1der)
239 : 115 : {
240 : 115 : DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer);
241 [ - + ]: 115 : RESULT_GUARD(s2n_openssl_x509_parse(asn1der, &cert));
242 [ - + ]: 115 : RESULT_GUARD(s2n_pkey_from_x509(cert, pub_key, pkey_type_out));
243 : :
244 : 115 : return S2N_RESULT_OK;
245 : 115 : }
246 : :
247 : : S2N_RESULT s2n_pkey_get_type(EVP_PKEY *evp_pkey, s2n_pkey_type *pkey_type)
248 : 17767 : {
249 [ - + ][ # # ]: 17767 : RESULT_ENSURE_REF(evp_pkey);
250 [ - + ][ # # ]: 17767 : RESULT_ENSURE_REF(pkey_type);
251 : 17767 : *pkey_type = S2N_PKEY_TYPE_UNKNOWN;
252 : :
253 : 17767 : int type = EVP_PKEY_base_id(evp_pkey);
254 : 17767 : switch (type) {
255 [ + + ]: 13192 : case EVP_PKEY_RSA:
256 : 13192 : *pkey_type = S2N_PKEY_TYPE_RSA;
257 : 13192 : break;
258 [ + + ]: 352 : case EVP_PKEY_RSA_PSS:
259 : 352 : *pkey_type = S2N_PKEY_TYPE_RSA_PSS;
260 : 352 : break;
261 [ + + ]: 4223 : case EVP_PKEY_EC:
262 : 4223 : *pkey_type = S2N_PKEY_TYPE_ECDSA;
263 : 4223 : break;
264 : : #if S2N_LIBCRYPTO_SUPPORTS_MLDSA
265 : : case EVP_PKEY_PQDSA:
266 : : *pkey_type = S2N_PKEY_TYPE_MLDSA;
267 : : break;
268 : : #endif
269 [ - + ]: 0 : default:
270 [ # # ]: 0 : RESULT_BAIL(S2N_ERR_DECODE_CERTIFICATE);
271 : 17767 : }
272 : :
273 : 17767 : return S2N_RESULT_OK;
274 : 17767 : }
275 : :
276 : : S2N_RESULT s2n_pkey_from_x509(X509 *cert, struct s2n_pkey *pub_key_out,
277 : : s2n_pkey_type *pkey_type_out)
278 : 5819 : {
279 [ # # ][ - + ]: 5819 : RESULT_ENSURE_REF(cert);
280 [ - + ][ # # ]: 5819 : RESULT_ENSURE_REF(pub_key_out);
281 [ # # ][ - + ]: 5819 : RESULT_ENSURE_REF(pkey_type_out);
282 : :
283 : 5819 : DEFER_CLEANUP(EVP_PKEY *evp_public_key = X509_get_pubkey(cert), EVP_PKEY_free_pointer);
284 [ - + ][ # # ]: 5819 : RESULT_ENSURE(evp_public_key != NULL, S2N_ERR_DECODE_CERTIFICATE);
285 : :
286 [ - + ]: 5819 : RESULT_GUARD(s2n_pkey_get_type(evp_public_key, pkey_type_out));
287 [ - + ]: 5819 : RESULT_GUARD(s2n_pkey_setup_for_type(pub_key_out, *pkey_type_out));
288 : :
289 : 5819 : pub_key_out->pkey = evp_public_key;
290 : 5819 : ZERO_TO_DISABLE_DEFER_CLEANUP(evp_public_key);
291 : :
292 : 5819 : return S2N_RESULT_OK;
293 : 5819 : }
294 : :
295 : : S2N_RESULT s2n_pkey_init_hash(const struct s2n_pkey *pkey,
296 : : s2n_signature_algorithm sig_alg, struct s2n_hash_state *hash)
297 : 6553 : {
298 [ - + ]: 6553 : if (sig_alg == S2N_SIGNATURE_MLDSA) {
299 [ # # ]: 0 : RESULT_GUARD(s2n_mldsa_init_mu_hash(hash, pkey));
300 : 0 : }
301 : 6553 : return S2N_RESULT_OK;
302 : 6553 : }
|