]>
Commit | Line | Data |
---|---|---|
8c6afbc5 | 1 | /* |
33388b44 | 2 | * Copyright 2016-2020 The OpenSSL Project Authors. All Rights Reserved. |
8c6afbc5 | 3 | * |
5477e842 | 4 | * Licensed under the Apache License 2.0 (the "License"). You may not use |
d2e9e320 RS |
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 | |
8c6afbc5 RP |
8 | */ |
9 | ||
10 | #ifdef OPENSSL_NO_CT | |
11 | # error "CT is disabled" | |
12 | #endif | |
13 | ||
14 | #include <stddef.h> | |
15 | #include <string.h> | |
16 | ||
17 | #include <openssl/err.h> | |
18 | #include <openssl/obj_mac.h> | |
19 | #include <openssl/x509.h> | |
20 | ||
706457b7 | 21 | #include "ct_local.h" |
8c6afbc5 | 22 | |
b4250010 | 23 | SCT_CTX *SCT_CTX_new(OSSL_LIB_CTX *libctx, const char *propq) |
8c6afbc5 | 24 | { |
0dfd6cf9 RP |
25 | SCT_CTX *sctx = OPENSSL_zalloc(sizeof(*sctx)); |
26 | ||
bd5f2800 | 27 | if (sctx == NULL) { |
9311d0c4 | 28 | ERR_raise(ERR_LIB_CT, ERR_R_MALLOC_FAILURE); |
bd5f2800 P |
29 | return NULL; |
30 | } | |
0dfd6cf9 | 31 | |
d4b2bfba MC |
32 | sctx->libctx = libctx; |
33 | if (propq != NULL) { | |
34 | sctx->propq = OPENSSL_strdup(propq); | |
35 | if (sctx->propq == NULL) { | |
9311d0c4 | 36 | ERR_raise(ERR_LIB_CT, ERR_R_MALLOC_FAILURE); |
d4b2bfba MC |
37 | OPENSSL_free(sctx); |
38 | return NULL; | |
39 | } | |
40 | } | |
41 | ||
8c6afbc5 RP |
42 | return sctx; |
43 | } | |
44 | ||
45 | void SCT_CTX_free(SCT_CTX *sctx) | |
46 | { | |
47 | if (sctx == NULL) | |
48 | return; | |
49 | EVP_PKEY_free(sctx->pkey); | |
50 | OPENSSL_free(sctx->pkeyhash); | |
51 | OPENSSL_free(sctx->ihash); | |
52 | OPENSSL_free(sctx->certder); | |
53 | OPENSSL_free(sctx->preder); | |
d4b2bfba | 54 | OPENSSL_free(sctx->propq); |
8c6afbc5 RP |
55 | OPENSSL_free(sctx); |
56 | } | |
57 | ||
0dfd6cf9 RP |
58 | /* |
59 | * Finds the index of the first extension with the given NID in cert. | |
60 | * If there is more than one extension with that NID, *is_duplicated is set to | |
61 | * 1, otherwise 0 (unless it is NULL). | |
62 | */ | |
63 | static int ct_x509_get_ext(X509 *cert, int nid, int *is_duplicated) | |
8c6afbc5 | 64 | { |
0dfd6cf9 RP |
65 | int ret = X509_get_ext_by_NID(cert, nid, -1); |
66 | ||
67 | if (is_duplicated != NULL) | |
68 | *is_duplicated = ret >= 0 && X509_get_ext_by_NID(cert, nid, ret) >= 0; | |
69 | ||
70 | return ret; | |
8c6afbc5 RP |
71 | } |
72 | ||
73 | /* | |
0dfd6cf9 RP |
74 | * Modifies a certificate by deleting extensions and copying the issuer and |
75 | * AKID from the presigner certificate, if necessary. | |
76 | * Returns 1 on success, 0 otherwise. | |
8c6afbc5 | 77 | */ |
5da65ef2 | 78 | __owur static int ct_x509_cert_fixup(X509 *cert, X509 *presigner) |
8c6afbc5 RP |
79 | { |
80 | int preidx, certidx; | |
0dfd6cf9 RP |
81 | int pre_akid_ext_is_dup, cert_akid_ext_is_dup; |
82 | ||
8c6afbc5 RP |
83 | if (presigner == NULL) |
84 | return 1; | |
0dfd6cf9 RP |
85 | |
86 | preidx = ct_x509_get_ext(presigner, NID_authority_key_identifier, | |
87 | &pre_akid_ext_is_dup); | |
88 | certidx = ct_x509_get_ext(cert, NID_authority_key_identifier, | |
89 | &cert_akid_ext_is_dup); | |
90 | ||
91 | /* An error occurred whilst searching for the extension */ | |
92 | if (preidx < -1 || certidx < -1) | |
93 | return 0; | |
94 | /* Invalid certificate if they contain duplicate extensions */ | |
95 | if (pre_akid_ext_is_dup || cert_akid_ext_is_dup) | |
8c6afbc5 RP |
96 | return 0; |
97 | /* AKID must be present in both certificate or absent in both */ | |
98 | if (preidx >= 0 && certidx == -1) | |
99 | return 0; | |
100 | if (preidx == -1 && certidx >= 0) | |
101 | return 0; | |
102 | /* Copy issuer name */ | |
103 | if (!X509_set_issuer_name(cert, X509_get_issuer_name(presigner))) | |
104 | return 0; | |
105 | if (preidx != -1) { | |
106 | /* Retrieve and copy AKID encoding */ | |
107 | X509_EXTENSION *preext = X509_get_ext(presigner, preidx); | |
108 | X509_EXTENSION *certext = X509_get_ext(cert, certidx); | |
109 | ASN1_OCTET_STRING *preextdata; | |
0dfd6cf9 | 110 | |
8c6afbc5 RP |
111 | /* Should never happen */ |
112 | if (preext == NULL || certext == NULL) | |
113 | return 0; | |
114 | preextdata = X509_EXTENSION_get_data(preext); | |
0dfd6cf9 RP |
115 | if (preextdata == NULL || |
116 | !X509_EXTENSION_set_data(certext, preextdata)) | |
8c6afbc5 RP |
117 | return 0; |
118 | } | |
119 | return 1; | |
120 | } | |
121 | ||
122 | int SCT_CTX_set1_cert(SCT_CTX *sctx, X509 *cert, X509 *presigner) | |
123 | { | |
124 | unsigned char *certder = NULL, *preder = NULL; | |
125 | X509 *pretmp = NULL; | |
126 | int certderlen = 0, prederlen = 0; | |
0dfd6cf9 RP |
127 | int idx = -1; |
128 | int poison_ext_is_dup, sct_ext_is_dup; | |
129 | int poison_idx = ct_x509_get_ext(cert, NID_ct_precert_poison, &poison_ext_is_dup); | |
130 | ||
98af7310 | 131 | /* Duplicate poison extensions are present - error */ |
0dfd6cf9 | 132 | if (poison_ext_is_dup) |
8c6afbc5 | 133 | goto err; |
0dfd6cf9 | 134 | |
98af7310 | 135 | /* If *cert doesn't have a poison extension, it isn't a precert */ |
0dfd6cf9 | 136 | if (poison_idx == -1) { |
98af7310 | 137 | /* cert isn't a precert, so we shouldn't have a presigner */ |
0dfd6cf9 | 138 | if (presigner != NULL) |
8c6afbc5 | 139 | goto err; |
0dfd6cf9 | 140 | |
8c6afbc5 RP |
141 | certderlen = i2d_X509(cert, &certder); |
142 | if (certderlen < 0) | |
143 | goto err; | |
144 | } | |
0dfd6cf9 | 145 | |
98af7310 | 146 | /* See if cert has a precert SCTs extension */ |
0dfd6cf9 | 147 | idx = ct_x509_get_ext(cert, NID_ct_precert_scts, &sct_ext_is_dup); |
98af7310 | 148 | /* Duplicate SCT extensions are present - error */ |
0dfd6cf9 | 149 | if (sct_ext_is_dup) |
8c6afbc5 | 150 | goto err; |
0dfd6cf9 | 151 | |
98af7310 RP |
152 | if (idx >= 0 && poison_idx >= 0) { |
153 | /* | |
154 | * cert can't both contain SCTs (i.e. have an SCT extension) and be a | |
155 | * precert (i.e. have a poison extension). | |
156 | */ | |
157 | goto err; | |
158 | } | |
159 | ||
160 | if (idx == -1) { | |
0dfd6cf9 RP |
161 | idx = poison_idx; |
162 | } | |
163 | ||
98af7310 RP |
164 | /* |
165 | * If either a poison or SCT extension is present, remove it before encoding | |
166 | * cert. This, along with ct_x509_cert_fixup(), gets a TBSCertificate (see | |
167 | * RFC5280) from cert, which is what the CT log signed when it produced the | |
168 | * SCT. | |
169 | */ | |
8c6afbc5 RP |
170 | if (idx >= 0) { |
171 | X509_EXTENSION *ext; | |
0dfd6cf9 RP |
172 | |
173 | /* Take a copy of certificate so we don't modify passed version */ | |
8c6afbc5 RP |
174 | pretmp = X509_dup(cert); |
175 | if (pretmp == NULL) | |
176 | goto err; | |
0dfd6cf9 | 177 | |
8c6afbc5 RP |
178 | ext = X509_delete_ext(pretmp, idx); |
179 | X509_EXTENSION_free(ext); | |
0dfd6cf9 RP |
180 | |
181 | if (!ct_x509_cert_fixup(pretmp, presigner)) | |
8c6afbc5 RP |
182 | goto err; |
183 | ||
184 | prederlen = i2d_re_X509_tbs(pretmp, &preder); | |
185 | if (prederlen <= 0) | |
186 | goto err; | |
187 | } | |
188 | ||
189 | X509_free(pretmp); | |
190 | ||
191 | OPENSSL_free(sctx->certder); | |
192 | sctx->certder = certder; | |
193 | sctx->certderlen = certderlen; | |
194 | ||
195 | OPENSSL_free(sctx->preder); | |
196 | sctx->preder = preder; | |
197 | sctx->prederlen = prederlen; | |
198 | ||
199 | return 1; | |
0dfd6cf9 | 200 | err: |
8c6afbc5 RP |
201 | OPENSSL_free(certder); |
202 | OPENSSL_free(preder); | |
203 | X509_free(pretmp); | |
204 | return 0; | |
205 | } | |
206 | ||
d4b2bfba MC |
207 | __owur static int ct_public_key_hash(SCT_CTX *sctx, X509_PUBKEY *pkey, |
208 | unsigned char **hash, size_t *hash_len) | |
8c6afbc5 | 209 | { |
5da65ef2 | 210 | int ret = 0; |
8c6afbc5 RP |
211 | unsigned char *md = NULL, *der = NULL; |
212 | int der_len; | |
213 | unsigned int md_len; | |
d4b2bfba MC |
214 | EVP_MD *sha256 = EVP_MD_fetch(sctx->libctx, "SHA2-256", sctx->propq); |
215 | ||
216 | if (sha256 == NULL) | |
217 | goto err; | |
0dfd6cf9 | 218 | |
8c6afbc5 RP |
219 | /* Reuse buffer if possible */ |
220 | if (*hash != NULL && *hash_len >= SHA256_DIGEST_LENGTH) { | |
221 | md = *hash; | |
222 | } else { | |
223 | md = OPENSSL_malloc(SHA256_DIGEST_LENGTH); | |
224 | if (md == NULL) | |
0dfd6cf9 | 225 | goto err; |
8c6afbc5 RP |
226 | } |
227 | ||
228 | /* Calculate key hash */ | |
229 | der_len = i2d_X509_PUBKEY(pkey, &der); | |
230 | if (der_len <= 0) | |
231 | goto err; | |
0dfd6cf9 | 232 | |
d4b2bfba | 233 | if (!EVP_Digest(der, der_len, md, &md_len, sha256, NULL)) |
8c6afbc5 | 234 | goto err; |
0dfd6cf9 | 235 | |
8c6afbc5 RP |
236 | if (md != *hash) { |
237 | OPENSSL_free(*hash); | |
238 | *hash = md; | |
239 | *hash_len = SHA256_DIGEST_LENGTH; | |
240 | } | |
0dfd6cf9 | 241 | |
8c6afbc5 RP |
242 | md = NULL; |
243 | ret = 1; | |
244 | err: | |
d4b2bfba | 245 | EVP_MD_free(sha256); |
8c6afbc5 RP |
246 | OPENSSL_free(md); |
247 | OPENSSL_free(der); | |
248 | return ret; | |
249 | } | |
250 | ||
251 | int SCT_CTX_set1_issuer(SCT_CTX *sctx, const X509 *issuer) | |
252 | { | |
5da65ef2 | 253 | return SCT_CTX_set1_issuer_pubkey(sctx, X509_get_X509_PUBKEY(issuer)); |
8c6afbc5 RP |
254 | } |
255 | ||
256 | int SCT_CTX_set1_issuer_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey) | |
257 | { | |
d4b2bfba | 258 | return ct_public_key_hash(sctx, pubkey, &sctx->ihash, &sctx->ihashlen); |
8c6afbc5 RP |
259 | } |
260 | ||
261 | int SCT_CTX_set1_pubkey(SCT_CTX *sctx, X509_PUBKEY *pubkey) | |
262 | { | |
263 | EVP_PKEY *pkey = X509_PUBKEY_get(pubkey); | |
0dfd6cf9 | 264 | |
8c6afbc5 RP |
265 | if (pkey == NULL) |
266 | return 0; | |
267 | ||
d4b2bfba | 268 | if (!ct_public_key_hash(sctx, pubkey, &sctx->pkeyhash, &sctx->pkeyhashlen)) { |
8c6afbc5 RP |
269 | EVP_PKEY_free(pkey); |
270 | return 0; | |
271 | } | |
272 | ||
273 | EVP_PKEY_free(sctx->pkey); | |
274 | sctx->pkey = pkey; | |
275 | return 1; | |
276 | } | |
1fa9ffd9 RP |
277 | |
278 | void SCT_CTX_set_time(SCT_CTX *sctx, uint64_t time_in_ms) | |
279 | { | |
280 | sctx->epoch_time_in_ms = time_in_ms; | |
281 | } |