xref: /freebsd/crypto/openssl/test/algorithmid_test.c (revision a689bfa4e25af8307709dc12f75b0e02a65abf18)
1 /*
2  * Copyright 2021 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9 
10 #include <openssl/asn1.h>
11 #include <openssl/pem.h>
12 #include "internal/sizes.h"
13 #include "crypto/evp.h"
14 #include "testutil.h"
15 
16 /* Collected arguments */
17 static const char *eecert_filename = NULL; /* For test_x509_file() */
18 static const char *cacert_filename = NULL; /* For test_x509_file() */
19 static const char *pubkey_filename = NULL; /* For test_spki_file() */
20 
21 #define ALGORITHMID_NAME "algorithm-id"
22 
23 static int test_spki_aid(X509_PUBKEY *pubkey, const char *filename)
24 {
25     const ASN1_OBJECT *oid;
26     X509_ALGOR *alg = NULL;
27     EVP_PKEY *pkey = NULL;
28     EVP_KEYMGMT *keymgmt = NULL;
29     void *keydata = NULL;
30     char name[OSSL_MAX_NAME_SIZE] = "";
31     unsigned char *algid_legacy = NULL;
32     int algid_legacy_len = 0;
33     static unsigned char algid_prov[OSSL_MAX_ALGORITHM_ID_SIZE];
34     size_t algid_prov_len = 0;
35     const OSSL_PARAM *gettable_params = NULL;
36     OSSL_PARAM params[] = {
37         OSSL_PARAM_octet_string(ALGORITHMID_NAME,
38             &algid_prov, sizeof(algid_prov)),
39         OSSL_PARAM_END
40     };
41     int ret = 0;
42 
43     if (!TEST_true(X509_PUBKEY_get0_param(NULL, NULL, NULL, &alg, pubkey))
44         || !TEST_ptr(pkey = X509_PUBKEY_get0(pubkey)))
45         goto end;
46 
47     if (!TEST_int_ge(algid_legacy_len = i2d_X509_ALGOR(alg, &algid_legacy), 0))
48         goto end;
49 
50     X509_ALGOR_get0(&oid, NULL, NULL, alg);
51     if (!TEST_int_gt(OBJ_obj2txt(name, sizeof(name), oid, 0), 0))
52         goto end;
53 
54     /*
55      * We use an internal functions to ensure we have a provided key.
56      * Note that |keydata| should not be freed, as it's cached in |pkey|.
57      * The |keymgmt|, however, should, as its reference count is incremented
58      * in this function.
59      */
60     if ((keydata = evp_pkey_export_to_provider(pkey, NULL,
61              &keymgmt, NULL))
62         == NULL) {
63         TEST_info("The public key found in '%s' doesn't have provider support."
64                   "  Skipping...",
65             filename);
66         ret = 1;
67         goto end;
68     }
69 
70     if (!TEST_true(EVP_KEYMGMT_is_a(keymgmt, name))) {
71         TEST_info("The AlgorithmID key type (%s) for the public key found in"
72                   " '%s' doesn't match the key type of the extracted public"
73                   " key.",
74             name, filename);
75         ret = 1;
76         goto end;
77     }
78 
79     if (!TEST_ptr(gettable_params = EVP_KEYMGMT_gettable_params(keymgmt))
80         || !TEST_ptr(OSSL_PARAM_locate_const(gettable_params, ALGORITHMID_NAME))) {
81         TEST_info("The %s provider keymgmt appears to lack support for algorithm-id."
82                   "  Skipping...",
83             name);
84         ret = 1;
85         goto end;
86     }
87 
88     algid_prov[0] = '\0';
89     if (!TEST_true(evp_keymgmt_get_params(keymgmt, keydata, params)))
90         goto end;
91     algid_prov_len = params[0].return_size;
92 
93     /* We now have all the algorithm IDs we need, let's compare them */
94     if (TEST_mem_eq(algid_legacy, algid_legacy_len,
95             algid_prov, algid_prov_len))
96         ret = 1;
97 
98 end:
99     EVP_KEYMGMT_free(keymgmt);
100     OPENSSL_free(algid_legacy);
101     return ret;
102 }
103 
104 static int test_x509_spki_aid(X509 *cert, const char *filename)
105 {
106     X509_PUBKEY *pubkey = X509_get_X509_PUBKEY(cert);
107 
108     return test_spki_aid(pubkey, filename);
109 }
110 
111 static int test_x509_sig_aid(X509 *eecert, const char *ee_filename,
112     X509 *cacert, const char *ca_filename)
113 {
114     const ASN1_OBJECT *sig_oid = NULL;
115     const X509_ALGOR *alg = NULL;
116     int sig_nid = NID_undef, dig_nid = NID_undef, pkey_nid = NID_undef;
117     EVP_MD_CTX *mdctx = NULL;
118     EVP_PKEY_CTX *pctx = NULL;
119     EVP_PKEY *pkey = NULL;
120     unsigned char *algid_legacy = NULL;
121     int algid_legacy_len = 0;
122     static unsigned char algid_prov[OSSL_MAX_ALGORITHM_ID_SIZE];
123     size_t algid_prov_len = 0;
124     const OSSL_PARAM *gettable_params = NULL;
125     OSSL_PARAM params[] = {
126         OSSL_PARAM_octet_string("algorithm-id",
127             &algid_prov, sizeof(algid_prov)),
128         OSSL_PARAM_END
129     };
130     int ret = 0;
131 
132     X509_get0_signature(NULL, &alg, eecert);
133     X509_ALGOR_get0(&sig_oid, NULL, NULL, alg);
134     if (!TEST_int_eq(X509_ALGOR_cmp(alg, X509_get0_tbs_sigalg(eecert)), 0))
135         goto end;
136     if (!TEST_int_ne(sig_nid = OBJ_obj2nid(sig_oid), NID_undef)
137         || !TEST_true(OBJ_find_sigid_algs(sig_nid, &dig_nid, &pkey_nid))
138         || !TEST_ptr(pkey = X509_get0_pubkey(cacert)))
139         goto end;
140 
141     if (!TEST_true(EVP_PKEY_is_a(pkey, OBJ_nid2sn(pkey_nid)))) {
142         TEST_info("The '%s' pubkey can't be used to verify the '%s' signature",
143             ca_filename, ee_filename);
144         TEST_info("Signature algorithm is %s (pkey type %s, hash type %s)",
145             OBJ_nid2sn(sig_nid), OBJ_nid2sn(pkey_nid), OBJ_nid2sn(dig_nid));
146         TEST_info("Pkey key type is %s", EVP_PKEY_get0_type_name(pkey));
147         goto end;
148     }
149 
150     if (!TEST_int_ge(algid_legacy_len = i2d_X509_ALGOR(alg, &algid_legacy), 0))
151         goto end;
152 
153     if (!TEST_ptr(mdctx = EVP_MD_CTX_new())
154         || !TEST_true(EVP_DigestVerifyInit_ex(mdctx, &pctx,
155             OBJ_nid2sn(dig_nid),
156             NULL, NULL, pkey, NULL))) {
157         TEST_info("Couldn't initialize a DigestVerify operation with "
158                   "pkey type %s and hash type %s",
159             OBJ_nid2sn(pkey_nid), OBJ_nid2sn(dig_nid));
160         goto end;
161     }
162 
163     if (!TEST_ptr(gettable_params = EVP_PKEY_CTX_gettable_params(pctx))
164         || !TEST_ptr(OSSL_PARAM_locate_const(gettable_params, ALGORITHMID_NAME))) {
165         TEST_info("The %s provider keymgmt appears to lack support for algorithm-id"
166                   "  Skipping...",
167             OBJ_nid2sn(pkey_nid));
168         ret = 1;
169         goto end;
170     }
171 
172     algid_prov[0] = '\0';
173     if (!TEST_true(EVP_PKEY_CTX_get_params(pctx, params)))
174         goto end;
175     algid_prov_len = params[0].return_size;
176 
177     /* We now have all the algorithm IDs we need, let's compare them */
178     if (TEST_mem_eq(algid_legacy, algid_legacy_len,
179             algid_prov, algid_prov_len))
180         ret = 1;
181 
182 end:
183     EVP_MD_CTX_free(mdctx);
184     /* pctx is free by EVP_MD_CTX_free() */
185     OPENSSL_free(algid_legacy);
186     return ret;
187 }
188 
189 static int test_spki_file(void)
190 {
191     X509_PUBKEY *pubkey = NULL;
192     BIO *b = BIO_new_file(pubkey_filename, "r");
193     int ret = 0;
194 
195     if (b == NULL) {
196         TEST_error("Couldn't open '%s' for reading\n", pubkey_filename);
197         TEST_openssl_errors();
198         goto end;
199     }
200 
201     if ((pubkey = PEM_read_bio_X509_PUBKEY(b, NULL, NULL, NULL)) == NULL) {
202         TEST_error("'%s' doesn't appear to be a SubjectPublicKeyInfo in PEM format\n",
203             pubkey_filename);
204         TEST_openssl_errors();
205         goto end;
206     }
207 
208     ret = test_spki_aid(pubkey, pubkey_filename);
209 end:
210     BIO_free(b);
211     X509_PUBKEY_free(pubkey);
212     return ret;
213 }
214 
215 static int test_x509_files(void)
216 {
217     X509 *eecert = NULL, *cacert = NULL;
218     BIO *bee = NULL, *bca = NULL;
219     int ret = 0;
220 
221     if ((bee = BIO_new_file(eecert_filename, "r")) == NULL) {
222         TEST_error("Couldn't open '%s' for reading\n", eecert_filename);
223         TEST_openssl_errors();
224         goto end;
225     }
226     if ((bca = BIO_new_file(cacert_filename, "r")) == NULL) {
227         TEST_error("Couldn't open '%s' for reading\n", cacert_filename);
228         TEST_openssl_errors();
229         goto end;
230     }
231 
232     if ((eecert = PEM_read_bio_X509(bee, NULL, NULL, NULL)) == NULL) {
233         TEST_error("'%s' doesn't appear to be a X.509 certificate in PEM format\n",
234             eecert_filename);
235         TEST_openssl_errors();
236         goto end;
237     }
238     if ((cacert = PEM_read_bio_X509(bca, NULL, NULL, NULL)) == NULL) {
239         TEST_error("'%s' doesn't appear to be a X.509 certificate in PEM format\n",
240             cacert_filename);
241         TEST_openssl_errors();
242         goto end;
243     }
244 
245     ret = test_x509_sig_aid(eecert, eecert_filename, cacert, cacert_filename)
246         & test_x509_spki_aid(eecert, eecert_filename)
247         & test_x509_spki_aid(cacert, cacert_filename);
248 end:
249     BIO_free(bee);
250     BIO_free(bca);
251     X509_free(eecert);
252     X509_free(cacert);
253     return ret;
254 }
255 
256 typedef enum OPTION_choice {
257     OPT_ERR = -1,
258     OPT_EOF = 0,
259     OPT_X509,
260     OPT_SPKI,
261     OPT_TEST_ENUM
262 } OPTION_CHOICE;
263 
264 const OPTIONS *test_get_options(void)
265 {
266     static const OPTIONS test_options[] = {
267         OPT_TEST_OPTIONS_WITH_EXTRA_USAGE("file...\n"),
268         { "x509", OPT_X509, '-', "Test X.509 certificates.  Requires two files" },
269         { "spki", OPT_SPKI, '-', "Test public keys in SubjectPublicKeyInfo form.  Requires one file" },
270         { OPT_HELP_STR, 1, '-',
271             "file...\tFile(s) to run tests on.  All files must be PEM encoded.\n" },
272         { NULL }
273     };
274     return test_options;
275 }
276 
277 int setup_tests(void)
278 {
279     OPTION_CHOICE o;
280     int n, x509 = 0, spki = 0, testcount = 0;
281 
282     while ((o = opt_next()) != OPT_EOF) {
283         switch (o) {
284         case OPT_X509:
285             x509 = 1;
286             break;
287         case OPT_SPKI:
288             spki = 1;
289             break;
290         case OPT_TEST_CASES:
291             break;
292         default:
293         case OPT_ERR:
294             return 0;
295         }
296     }
297 
298     /* |testcount| adds all the given test types together */
299     testcount = x509 + spki;
300 
301     if (testcount < 1)
302         BIO_printf(bio_err, "No test type given\n");
303     else if (testcount > 1)
304         BIO_printf(bio_err, "Only one test type may be given\n");
305     if (testcount != 1)
306         return 0;
307 
308     n = test_get_argument_count();
309     if (spki && n == 1) {
310         pubkey_filename = test_get_argument(0);
311     } else if (x509 && n == 2) {
312         eecert_filename = test_get_argument(0);
313         cacert_filename = test_get_argument(1);
314     }
315 
316     if (spki && pubkey_filename == NULL) {
317         BIO_printf(bio_err, "Missing -spki argument\n");
318         return 0;
319     } else if (x509 && (eecert_filename == NULL || cacert_filename == NULL)) {
320         BIO_printf(bio_err, "Missing -x509 argument(s)\n");
321         return 0;
322     }
323 
324     if (x509)
325         ADD_TEST(test_x509_files);
326     if (spki)
327         ADD_TEST(test_spki_file);
328     return 1;
329 }
330