diff options
author | tb <> | 2025-01-04 18:16:37 +0000 |
---|---|---|
committer | tb <> | 2025-01-04 18:16:37 +0000 |
commit | 29b768008903d0bf17e31f30cd791a8153e2d1fb (patch) | |
tree | d45949fd45e507c58061c260d36f361bd777dd3a | |
parent | cd8752d60a836e131c86ae2883a1769e20419f3a (diff) | |
download | openbsd-29b768008903d0bf17e31f30cd791a8153e2d1fb.tar.gz openbsd-29b768008903d0bf17e31f30cd791a8153e2d1fb.tar.bz2 openbsd-29b768008903d0bf17e31f30cd791a8153e2d1fb.zip |
Add some regress coverage for custom RSA methods
This currently only covers sign and verify since other parts are already
known to work in practice. Prompted by a bug report by kn
-rw-r--r-- | src/regress/lib/libcrypto/rsa/Makefile | 3 | ||||
-rw-r--r-- | src/regress/lib/libcrypto/rsa/rsa_method_test.c | 278 |
2 files changed, 280 insertions, 1 deletions
diff --git a/src/regress/lib/libcrypto/rsa/Makefile b/src/regress/lib/libcrypto/rsa/Makefile index 6c33c39755..3950ce6813 100644 --- a/src/regress/lib/libcrypto/rsa/Makefile +++ b/src/regress/lib/libcrypto/rsa/Makefile | |||
@@ -1,6 +1,7 @@ | |||
1 | # $OpenBSD: Makefile,v 1.5 2025/01/04 18:13:58 tb Exp $ | 1 | # $OpenBSD: Makefile,v 1.6 2025/01/04 18:16:37 tb Exp $ |
2 | 2 | ||
3 | PROGS += rsa_test | 3 | PROGS += rsa_test |
4 | PROGS += rsa_method_test | ||
4 | PROGS += rsa_padding_test | 5 | PROGS += rsa_padding_test |
5 | 6 | ||
6 | LDADD = -lcrypto | 7 | LDADD = -lcrypto |
diff --git a/src/regress/lib/libcrypto/rsa/rsa_method_test.c b/src/regress/lib/libcrypto/rsa/rsa_method_test.c new file mode 100644 index 0000000000..9a5bac6513 --- /dev/null +++ b/src/regress/lib/libcrypto/rsa/rsa_method_test.c | |||
@@ -0,0 +1,278 @@ | |||
1 | /* $OpenBSD: rsa_method_test.c,v 1.1 2025/01/04 18:16:37 tb Exp $ */ | ||
2 | |||
3 | /* | ||
4 | * Copyright (c) 2025 Theo Buehler <tb@openbsd.org> | ||
5 | * | ||
6 | * Permission to use, copy, modify, and distribute this software for any | ||
7 | * purpose with or without fee is hereby granted, provided that the above | ||
8 | * copyright notice and this permission notice appear in all copies. | ||
9 | * | ||
10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
17 | */ | ||
18 | |||
19 | #include <assert.h> | ||
20 | #include <err.h> | ||
21 | #include <stdint.h> | ||
22 | #include <stdio.h> | ||
23 | |||
24 | #include <openssl/asn1.h> | ||
25 | #include <openssl/bn.h> | ||
26 | #include <openssl/err.h> | ||
27 | #include <openssl/evp.h> | ||
28 | #include <openssl/objects.h> | ||
29 | #include <openssl/rsa.h> | ||
30 | #include <openssl/x509.h> | ||
31 | |||
32 | /* | ||
33 | * XXX - This currently only covers sign and verify. | ||
34 | */ | ||
35 | |||
36 | /* sigh */ | ||
37 | static int ex_index; | ||
38 | |||
39 | /* Unsure if this applies to RSA, ASN.1, or the OpenSSL code base altogether. */ | ||
40 | static const uint8_t msg[] = { | ||
41 | 0x44, 0x69, 0x65, 0x2c, 0x20, 0x64, 0x69, 0x65, | ||
42 | 0x2c, 0x20, 0x64, 0x69, 0x65, 0x2c, 0x20, 0x6d, | ||
43 | 0x79, 0x20, 0x64, 0x61, 0x72, 0x6c, 0x69, 0x6e, | ||
44 | 0x67, 0x0a, 0x44, 0x6f, 0x6e, 0x27, 0x74, 0x20, | ||
45 | 0x75, 0x74, 0x74, 0x65, 0x72, 0x20, 0x61, 0x20, | ||
46 | 0x73, 0x69, 0x6e, 0x67, 0x6c, 0x65, 0x20, 0x77, | ||
47 | 0x6f, 0x72, 0x64, 0x0a, 0x44, 0x69, 0x65, 0x2c, | ||
48 | 0x20, 0x64, 0x69, 0x65, 0x2c, 0x20, 0x64, 0x69, | ||
49 | 0x65, 0x2c, 0x20, 0x6d, 0x79, 0x20, 0x64, 0x61, | ||
50 | 0x72, 0x6c, 0x69, 0x6e, 0x67, 0x0a, 0x53, 0x68, | ||
51 | 0x75, 0x74, 0x20, 0x79, 0x6f, 0x75, 0x72, 0x20, | ||
52 | 0x70, 0x72, 0x65, 0x74, 0x74, 0x79, 0x20, 0x65, | ||
53 | 0x79, 0x65, 0x73, 0x0a, 0x0a, 0x49, 0x27, 0x6c, | ||
54 | 0x6c, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x65, | ||
55 | 0x69, 0x6e, 0x67, 0x20, 0x79, 0x6f, 0x75, 0x20, | ||
56 | 0x61, 0x67, 0x61, 0x69, 0x6e, 0x0a, 0x49, 0x27, | ||
57 | 0x6c, 0x6c, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, | ||
58 | 0x65, 0x69, 0x6e, 0x67, 0x20, 0x79, 0x6f, 0x75, | ||
59 | 0x20, 0x69, 0x6e, 0x20, 0x68, 0x65, 0x6c, 0x6c, | ||
60 | 0x0a, 0x0a, 0x54, 0x68, 0x65, 0x20, 0x4d, 0x69, | ||
61 | 0x73, 0x66, 0x69, 0x74, 0x73, 0x20, 0x7e, 0x20, | ||
62 | 0x31, 0x39, 0x38, 0x32, | ||
63 | }; | ||
64 | |||
65 | static int | ||
66 | sign_and_verify(const char *descr, EVP_PKEY *priv, EVP_PKEY *pub) | ||
67 | { | ||
68 | ASN1_IA5STRING *message = NULL; | ||
69 | ASN1_BIT_STRING *signature = NULL; | ||
70 | X509_ALGOR *x509_alg = NULL; | ||
71 | const ASN1_OBJECT *oid; | ||
72 | int nid, ret; | ||
73 | int failed = 1; | ||
74 | |||
75 | if ((message = ASN1_IA5STRING_new()) == NULL) | ||
76 | errx(1, "ASN1_IA5STRING_new"); | ||
77 | if (!ASN1_STRING_set(message, msg, sizeof(msg))) | ||
78 | errx(1, "ASN1_STRING_set"); | ||
79 | |||
80 | if ((signature = ASN1_BIT_STRING_new()) == NULL) | ||
81 | errx(1, "ASN1_BIT_STRING_new"); | ||
82 | if ((x509_alg = X509_ALGOR_new()) == NULL) | ||
83 | errx(1, "X509_ALGOR_new"); | ||
84 | if ((ret = ASN1_item_sign(&ASN1_IA5STRING_it, x509_alg, NULL, signature, | ||
85 | message, priv, EVP_sha256())) <= 0) { | ||
86 | fprintf(stderr, "FAIL: %s (%s): ASN1_item_sign() returned %d\n", | ||
87 | __func__, descr, ret); | ||
88 | ERR_print_errors_fp(stderr); | ||
89 | goto err; | ||
90 | } | ||
91 | |||
92 | X509_ALGOR_get0(&oid, NULL, NULL, x509_alg); | ||
93 | if ((nid = OBJ_obj2nid(oid)) != NID_sha256WithRSAEncryption) { | ||
94 | fprintf(stderr, "FAIL: %s (%s): OBJ_obj2nid(): want %d, got %d\n", | ||
95 | __func__, descr, NID_sha256WithRSAEncryption, nid); | ||
96 | goto err; | ||
97 | } | ||
98 | |||
99 | if ((ret = ASN1_item_verify(&ASN1_IA5STRING_it, x509_alg, signature, | ||
100 | message, pub)) != 1) { | ||
101 | fprintf(stderr, "FAIL: %s (%s): ASN1_item_verify() returned %d\n", | ||
102 | __func__, descr, ret); | ||
103 | ERR_print_errors_fp(stderr); | ||
104 | goto err; | ||
105 | } | ||
106 | |||
107 | failed = 0; | ||
108 | |||
109 | err: | ||
110 | ASN1_IA5STRING_free(message); | ||
111 | ASN1_BIT_STRING_free(signature); | ||
112 | X509_ALGOR_free(x509_alg); | ||
113 | |||
114 | return failed; | ||
115 | } | ||
116 | |||
117 | static void | ||
118 | generate_rsa_keypair(int bits, int exponent, RSA **out_priv, RSA **out_pub) | ||
119 | { | ||
120 | BIGNUM *e; | ||
121 | RSA *rsa; | ||
122 | |||
123 | assert(out_priv == NULL || *out_priv == NULL); | ||
124 | assert(out_pub == NULL || *out_pub == NULL); | ||
125 | |||
126 | if ((e = BN_new()) == NULL) | ||
127 | errx(1, "%s: BN_new()", __func__); | ||
128 | if (!BN_set_word(e, exponent)) | ||
129 | errx(1, "%s: BN_set_word()", __func__); | ||
130 | |||
131 | if ((rsa = RSA_new()) == NULL) | ||
132 | errx(1, "%s: RSA_new()", __func__); | ||
133 | if (!RSA_generate_key_ex(rsa, bits, e, NULL)) | ||
134 | errx(1, "%s: RSA_generate_key_ex", __func__); | ||
135 | |||
136 | /* Take the opportunity to exercise these two functions. */ | ||
137 | if (out_priv != NULL) { | ||
138 | if ((*out_priv = RSAPrivateKey_dup(rsa)) == NULL) | ||
139 | errx(1, "%s: RSAPrivateKey_dup", __func__); | ||
140 | } | ||
141 | if (out_pub != NULL) { | ||
142 | if ((*out_pub = RSAPublicKey_dup(rsa)) == NULL) | ||
143 | errx(1, "%s: RSAPublicKey_dup", __func__); | ||
144 | } | ||
145 | |||
146 | RSA_free(rsa); | ||
147 | BN_free(e); | ||
148 | } | ||
149 | |||
150 | static void | ||
151 | rsa_to_evp(RSA *rsa, EVP_PKEY **out_evp) | ||
152 | { | ||
153 | assert(*out_evp == NULL); | ||
154 | |||
155 | if ((*out_evp = EVP_PKEY_new()) == NULL) | ||
156 | errx(1, "%s: EVP_PKEY_new", __func__); | ||
157 | if (!EVP_PKEY_set1_RSA(*out_evp, rsa)) | ||
158 | errx(1, "%s: EVP_PKEY_set1_RSA", __func__); | ||
159 | } | ||
160 | |||
161 | static void | ||
162 | clear_evp_keys(EVP_PKEY **evp_priv, EVP_PKEY **evp_pub) | ||
163 | { | ||
164 | EVP_PKEY_free(*evp_priv); | ||
165 | EVP_PKEY_free(*evp_pub); | ||
166 | *evp_priv = NULL; | ||
167 | *evp_pub = NULL; | ||
168 | } | ||
169 | |||
170 | static int | ||
171 | rsa_method_app_data_sign(int dtype, const unsigned char *m, unsigned int m_len, | ||
172 | unsigned char *sig, unsigned int *sig_len, const RSA *rsa) | ||
173 | { | ||
174 | const RSA_METHOD *method = RSA_get_method(rsa); | ||
175 | RSA *sign_rsa = RSA_meth_get0_app_data(method); | ||
176 | |||
177 | return RSA_sign(dtype, m, m_len, sig, sig_len, sign_rsa); | ||
178 | } | ||
179 | |||
180 | static int | ||
181 | rsa_ex_data_verify(int dtype, const unsigned char *m, unsigned int m_len, | ||
182 | const unsigned char *sig, unsigned int sig_len, const RSA *rsa) | ||
183 | { | ||
184 | RSA *verify_rsa; | ||
185 | |||
186 | assert(ex_index != 0); | ||
187 | |||
188 | if ((verify_rsa = RSA_get_ex_data(rsa, ex_index)) == NULL) | ||
189 | errx(1, "%s: RSA_get_ex_data", __func__); | ||
190 | |||
191 | return RSA_verify(dtype, m, m_len, sig, sig_len, verify_rsa); | ||
192 | } | ||
193 | |||
194 | static int | ||
195 | sign_and_verify_test(void) | ||
196 | { | ||
197 | RSA_METHOD *sign_verify_method = NULL; | ||
198 | RSA *rsa_priv = NULL, *rsa_pub = NULL, *rsa_bogus = NULL; | ||
199 | EVP_PKEY *evp_priv = NULL, *evp_pub = NULL; | ||
200 | int failed = 0; | ||
201 | |||
202 | assert(ex_index != 0); | ||
203 | |||
204 | /* | ||
205 | * XXX - Hilarity ensues if the public key sizes don't match. | ||
206 | * One reason is that EVP_PKEY_sign() uses EVP_PKEY_size() | ||
207 | * which ignores the RSA method. Awesome design is awesome and | ||
208 | * OpenSSL's abstractions are leakier than Manneken Pis. | ||
209 | */ | ||
210 | generate_rsa_keypair(2048, RSA_F4, &rsa_priv, &rsa_pub); | ||
211 | generate_rsa_keypair(2048, 3, NULL, &rsa_bogus); | ||
212 | |||
213 | rsa_to_evp(rsa_priv, &evp_priv); | ||
214 | rsa_to_evp(rsa_pub, &evp_pub); | ||
215 | |||
216 | failed |= sign_and_verify("default method", evp_priv, evp_pub); | ||
217 | |||
218 | clear_evp_keys(&evp_priv, &evp_pub); | ||
219 | |||
220 | |||
221 | if (!RSA_set_ex_data(rsa_bogus, ex_index, rsa_pub)) | ||
222 | errx(1, "%s: RSA_set_ex_data", __func__); | ||
223 | |||
224 | if ((sign_verify_method = RSA_meth_dup(RSA_get_default_method())) == NULL) | ||
225 | errx(1, "%s: RSA_get_default_method", __func__); | ||
226 | if (!RSA_meth_set0_app_data(sign_verify_method, rsa_priv)) | ||
227 | errx(1, "%s: RSA_meth_set0_app_data", __func__); | ||
228 | |||
229 | if (!RSA_meth_set_sign(sign_verify_method, rsa_method_app_data_sign)) | ||
230 | errx(1, "%s: RSA_meth_set_sign", __func__); | ||
231 | if (!RSA_meth_set_verify(sign_verify_method, rsa_ex_data_verify)) | ||
232 | errx(1, "%s: RSA_meth_set_verify", __func__); | ||
233 | |||
234 | RSA_set_flags(rsa_bogus, RSA_FLAG_SIGN_VER); | ||
235 | if (!RSA_set_method(rsa_bogus, sign_verify_method)) | ||
236 | errx(1, "%s: RSA_set_method", __func__); | ||
237 | |||
238 | rsa_to_evp(rsa_bogus, &evp_priv); | ||
239 | rsa_to_evp(rsa_pub, &evp_pub); | ||
240 | |||
241 | failed |= sign_and_verify("app data sign method", evp_priv, evp_pub); | ||
242 | |||
243 | clear_evp_keys(&evp_priv, &evp_pub); | ||
244 | |||
245 | rsa_to_evp(rsa_priv, &evp_priv); | ||
246 | rsa_to_evp(rsa_bogus, &evp_pub); | ||
247 | |||
248 | failed |= sign_and_verify("ex data verify method", evp_priv, evp_pub); | ||
249 | |||
250 | clear_evp_keys(&evp_priv, &evp_pub); | ||
251 | |||
252 | rsa_to_evp(rsa_bogus, &evp_priv); | ||
253 | rsa_to_evp(rsa_bogus, &evp_pub); | ||
254 | |||
255 | failed |= sign_and_verify("both sides bous", evp_priv, evp_pub); | ||
256 | |||
257 | RSA_free(rsa_priv); | ||
258 | RSA_free(rsa_pub); | ||
259 | RSA_free(rsa_bogus); | ||
260 | EVP_PKEY_free(evp_priv); | ||
261 | EVP_PKEY_free(evp_pub); | ||
262 | RSA_meth_free(sign_verify_method); | ||
263 | |||
264 | return failed; | ||
265 | } | ||
266 | |||
267 | int | ||
268 | main(void) | ||
269 | { | ||
270 | int failed = 0; | ||
271 | |||
272 | if ((ex_index = RSA_get_ex_new_index(0, NULL, NULL, NULL, NULL)) <= 0) | ||
273 | errx(1, "RSA_get_ex_new_index"); | ||
274 | |||
275 | failed |= sign_and_verify_test(); | ||
276 | |||
277 | return failed; | ||
278 | } | ||