diff options
author | eric <> | 2022-01-25 21:51:24 +0000 |
---|---|---|
committer | eric <> | 2022-01-25 21:51:24 +0000 |
commit | 5bc45eb57d3df492a992eb97f4f9efadef0b060c (patch) | |
tree | f7e1f8bcb82bc7a21b3720f212d7fbf3f1d02872 /src/lib/libtls/tls_signer.c | |
parent | c8578f33457bc1465ca08176ebca6e8aac53fcd3 (diff) | |
download | openbsd-5bc45eb57d3df492a992eb97f4f9efadef0b060c.tar.gz openbsd-5bc45eb57d3df492a992eb97f4f9efadef0b060c.tar.bz2 openbsd-5bc45eb57d3df492a992eb97f4f9efadef0b060c.zip |
Introduce a signer interface intented to make TLS privsep simpler
to implement.
Add a tls_config_set_sign_cb() function that allows to register
a callback for the signing operation on a tls_config. When used,
the context installs fake pivate keys internally, and the callback
receives the hash of the public key.
Add a tls_signer_*() set of functions to manage tls_signer objects.
A tls_signer is an opaque structure on which keys are added.
It is used to compute signatures with private keys identified by
their associated public key hash.
Discussed with and ok jsing@ tb@
Diffstat (limited to 'src/lib/libtls/tls_signer.c')
-rw-r--r-- | src/lib/libtls/tls_signer.c | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/src/lib/libtls/tls_signer.c b/src/lib/libtls/tls_signer.c new file mode 100644 index 0000000000..690cb51615 --- /dev/null +++ b/src/lib/libtls/tls_signer.c | |||
@@ -0,0 +1,376 @@ | |||
1 | /* $OpenBSD: tls_signer.c,v 1.1 2022/01/25 21:51:24 eric Exp $ */ | ||
2 | /* | ||
3 | * Copyright (c) 2021 Eric Faurot <eric@openbsd.org> | ||
4 | * | ||
5 | * Permission to use, copy, modify, and distribute this software for any | ||
6 | * purpose with or without fee is hereby granted, provided that the above | ||
7 | * copyright notice and this permission notice appear in all copies. | ||
8 | * | ||
9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | ||
10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | ||
11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | ||
12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||
13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | ||
14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | ||
15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | ||
16 | */ | ||
17 | |||
18 | #include <openssl/err.h> | ||
19 | |||
20 | #include "tls.h" | ||
21 | #include "tls_internal.h" | ||
22 | |||
23 | struct tls_signer_key { | ||
24 | char *hash; | ||
25 | RSA *rsa; | ||
26 | EC_KEY *ecdsa; | ||
27 | struct tls_signer_key *next; | ||
28 | }; | ||
29 | |||
30 | struct tls_signer { | ||
31 | struct tls_error error; | ||
32 | struct tls_signer_key *keys; | ||
33 | }; | ||
34 | |||
35 | static pthread_mutex_t signer_method_lock = PTHREAD_MUTEX_INITIALIZER; | ||
36 | |||
37 | struct tls_signer * | ||
38 | tls_signer_new(void) | ||
39 | { | ||
40 | struct tls_signer *signer; | ||
41 | |||
42 | if ((signer = calloc(1, sizeof(*signer))) == NULL) | ||
43 | return (NULL); | ||
44 | |||
45 | return (signer); | ||
46 | } | ||
47 | |||
48 | void | ||
49 | tls_signer_free(struct tls_signer *signer) | ||
50 | { | ||
51 | struct tls_signer_key *skey; | ||
52 | |||
53 | if (signer == NULL) | ||
54 | return; | ||
55 | |||
56 | tls_error_clear(&signer->error); | ||
57 | |||
58 | while (signer->keys) { | ||
59 | skey = signer->keys; | ||
60 | signer->keys = skey->next; | ||
61 | RSA_free(skey->rsa); | ||
62 | EC_KEY_free(skey->ecdsa); | ||
63 | free(skey->hash); | ||
64 | free(skey); | ||
65 | } | ||
66 | |||
67 | free(signer); | ||
68 | } | ||
69 | |||
70 | const char * | ||
71 | tls_signer_error(struct tls_signer *signer) | ||
72 | { | ||
73 | return (signer->error.msg); | ||
74 | } | ||
75 | |||
76 | int | ||
77 | tls_signer_add_keypair_mem(struct tls_signer *signer, const uint8_t *cert, | ||
78 | size_t cert_len, const uint8_t *key, size_t key_len) | ||
79 | { | ||
80 | struct tls_signer_key *skey = NULL; | ||
81 | char *errstr = "unknown"; | ||
82 | int ssl_err; | ||
83 | EVP_PKEY *pkey = NULL; | ||
84 | X509 *x509 = NULL; | ||
85 | BIO *bio = NULL; | ||
86 | char *hash = NULL; | ||
87 | |||
88 | /* Compute certificate hash */ | ||
89 | if ((bio = BIO_new_mem_buf(cert, cert_len)) == NULL) { | ||
90 | tls_error_setx(&signer->error, | ||
91 | "failed to create certificate bio"); | ||
92 | goto err; | ||
93 | } | ||
94 | if ((x509 = PEM_read_bio_X509(bio, NULL, tls_password_cb, | ||
95 | NULL)) == NULL) { | ||
96 | if ((ssl_err = ERR_peek_error()) != 0) | ||
97 | errstr = ERR_error_string(ssl_err, NULL); | ||
98 | tls_error_setx(&signer->error, "failed to load certificate: %s", | ||
99 | errstr); | ||
100 | goto err; | ||
101 | } | ||
102 | if (tls_cert_pubkey_hash(x509, &hash) == -1) { | ||
103 | tls_error_setx(&signer->error, | ||
104 | "failed to get certificate hash"); | ||
105 | goto err; | ||
106 | } | ||
107 | |||
108 | X509_free(x509); | ||
109 | x509 = NULL; | ||
110 | BIO_free(bio); | ||
111 | bio = NULL; | ||
112 | |||
113 | /* Read private key */ | ||
114 | if ((bio = BIO_new_mem_buf(key, key_len)) == NULL) { | ||
115 | tls_error_setx(&signer->error, "failed to create key bio"); | ||
116 | goto err; | ||
117 | } | ||
118 | if ((pkey = PEM_read_bio_PrivateKey(bio, NULL, tls_password_cb, | ||
119 | NULL)) == NULL) { | ||
120 | tls_error_setx(&signer->error, "failed to read private key"); | ||
121 | goto err; | ||
122 | } | ||
123 | |||
124 | if ((skey = calloc(1, sizeof(*skey))) == NULL) { | ||
125 | tls_error_set(&signer->error, "failed to create key entry"); | ||
126 | goto err; | ||
127 | } | ||
128 | skey->hash = hash; | ||
129 | if ((skey->rsa = EVP_PKEY_get1_RSA(pkey)) == NULL && | ||
130 | (skey->ecdsa = EVP_PKEY_get1_EC_KEY(pkey)) == NULL) { | ||
131 | tls_error_setx(&signer->error, "unknown key type"); | ||
132 | goto err; | ||
133 | } | ||
134 | |||
135 | skey->next = signer->keys; | ||
136 | signer->keys = skey; | ||
137 | EVP_PKEY_free(pkey); | ||
138 | BIO_free(bio); | ||
139 | |||
140 | return (0); | ||
141 | |||
142 | err: | ||
143 | EVP_PKEY_free(pkey); | ||
144 | X509_free(x509); | ||
145 | BIO_free(bio); | ||
146 | free(hash); | ||
147 | free(skey); | ||
148 | |||
149 | return (-1); | ||
150 | } | ||
151 | |||
152 | int | ||
153 | tls_signer_add_keypair_file(struct tls_signer *signer, const char *cert_file, | ||
154 | const char *key_file) | ||
155 | { | ||
156 | char *cert = NULL, *key = NULL; | ||
157 | size_t cert_len, key_len; | ||
158 | int rv = -1; | ||
159 | |||
160 | if (tls_config_load_file(&signer->error, "certificate", cert_file, | ||
161 | &cert, &cert_len) == -1) | ||
162 | goto err; | ||
163 | |||
164 | if (tls_config_load_file(&signer->error, "key", key_file, &key, | ||
165 | &key_len) == -1) | ||
166 | goto err; | ||
167 | |||
168 | rv = tls_signer_add_keypair_mem(signer, cert, cert_len, key, key_len); | ||
169 | |||
170 | err: | ||
171 | free(cert); | ||
172 | free(key); | ||
173 | |||
174 | return (rv); | ||
175 | } | ||
176 | |||
177 | static int | ||
178 | tls_sign_rsa(struct tls_signer *signer, struct tls_signer_key *skey, | ||
179 | const uint8_t *dgst, size_t dgstlen, uint8_t **psig, size_t *psiglen, | ||
180 | int padding) | ||
181 | { | ||
182 | |||
183 | char *buf; | ||
184 | int siglen, r; | ||
185 | |||
186 | *psig = NULL; | ||
187 | *psiglen = 0; | ||
188 | |||
189 | siglen = RSA_size(skey->rsa); | ||
190 | if (siglen <= 0) { | ||
191 | tls_error_setx(&signer->error, "incorrect RSA_size: %d", | ||
192 | siglen); | ||
193 | return (-1); | ||
194 | } | ||
195 | |||
196 | if ((buf = malloc(siglen)) == NULL) { | ||
197 | tls_error_set(&signer->error, "RSA sign"); | ||
198 | return (-1); | ||
199 | } | ||
200 | |||
201 | r = RSA_private_encrypt((int)dgstlen, dgst, buf, skey->rsa, padding); | ||
202 | if (r == -1) { | ||
203 | tls_error_setx(&signer->error, "RSA_private_encrypt failed"); | ||
204 | free(buf); | ||
205 | return (-1); | ||
206 | } | ||
207 | |||
208 | *psig = buf; | ||
209 | *psiglen = (size_t)r; | ||
210 | |||
211 | return (0); | ||
212 | } | ||
213 | |||
214 | static int | ||
215 | tls_sign_ecdsa(struct tls_signer *signer, struct tls_signer_key *skey, | ||
216 | const uint8_t *dgst, size_t dgstlen, uint8_t **psig, size_t *psiglen) | ||
217 | { | ||
218 | unsigned char *sig; | ||
219 | unsigned int siglen; | ||
220 | |||
221 | *psig = NULL; | ||
222 | *psiglen = 0; | ||
223 | |||
224 | siglen = ECDSA_size(skey->ecdsa); | ||
225 | if (siglen == 0) { | ||
226 | tls_error_setx(&signer->error, "incorrect ECDSA_size: %u", | ||
227 | siglen); | ||
228 | return (-1); | ||
229 | } | ||
230 | if ((sig = malloc(siglen)) == NULL) { | ||
231 | tls_error_set(&signer->error, "ECDSA sign"); | ||
232 | return (-1); | ||
233 | } | ||
234 | |||
235 | if (!ECDSA_sign(0, dgst, dgstlen, sig, &siglen, skey->ecdsa)) { | ||
236 | tls_error_setx(&signer->error, "ECDSA_sign failed"); | ||
237 | free(sig); | ||
238 | return (-1); | ||
239 | } | ||
240 | |||
241 | *psig = sig; | ||
242 | *psiglen = siglen; | ||
243 | |||
244 | return (0); | ||
245 | } | ||
246 | |||
247 | int | ||
248 | tls_signer_sign(struct tls_signer *signer, const char *hash, | ||
249 | const uint8_t *dgst, size_t dgstlen, uint8_t **psig, size_t *psiglen, | ||
250 | int padding) | ||
251 | { | ||
252 | struct tls_signer_key *skey; | ||
253 | |||
254 | for (skey = signer->keys; skey; skey = skey->next) | ||
255 | if (!strcmp(hash, skey->hash)) | ||
256 | break; | ||
257 | |||
258 | if (skey == NULL) { | ||
259 | tls_error_setx(&signer->error, "key not found"); | ||
260 | return (-1); | ||
261 | } | ||
262 | |||
263 | if (skey->rsa != NULL) | ||
264 | return tls_sign_rsa(signer, skey, dgst, dgstlen, psig, psiglen, | ||
265 | padding); | ||
266 | |||
267 | if (skey->ecdsa != NULL) | ||
268 | return tls_sign_ecdsa(signer, skey, dgst, dgstlen, psig, psiglen); | ||
269 | |||
270 | tls_error_setx(&signer->error, "unknown key type"); | ||
271 | return (-1); | ||
272 | } | ||
273 | |||
274 | static int | ||
275 | tls_rsa_priv_enc(int srclen, const unsigned char *src, unsigned char *to, | ||
276 | RSA *rsa, int padding) | ||
277 | { | ||
278 | struct tls_config *config; | ||
279 | const char *hash; | ||
280 | size_t tolen; | ||
281 | |||
282 | hash = RSA_get_ex_data(rsa, 0); | ||
283 | config = RSA_get_ex_data(rsa, 1); | ||
284 | |||
285 | if (hash == NULL || config == NULL) | ||
286 | return (-1); | ||
287 | |||
288 | if (config->sign_cb(config->sign_cb_arg, hash, (const uint8_t *)src, | ||
289 | srclen, (uint8_t *)to, &tolen, padding) == -1) | ||
290 | return (-1); | ||
291 | |||
292 | if (tolen > INT_MAX) | ||
293 | return (-1); | ||
294 | |||
295 | return ((int)tolen); | ||
296 | } | ||
297 | |||
298 | RSA_METHOD * | ||
299 | tls_signer_rsa_method(void) | ||
300 | { | ||
301 | static RSA_METHOD *rsa_method = NULL; | ||
302 | |||
303 | pthread_mutex_lock(&signer_method_lock); | ||
304 | |||
305 | if (rsa_method != NULL) | ||
306 | goto out; | ||
307 | |||
308 | rsa_method = RSA_meth_new("libtls RSA method", 0); | ||
309 | if (rsa_method == NULL) | ||
310 | goto out; | ||
311 | |||
312 | RSA_meth_set_priv_enc(rsa_method, tls_rsa_priv_enc); | ||
313 | |||
314 | out: | ||
315 | pthread_mutex_unlock(&signer_method_lock); | ||
316 | |||
317 | return (rsa_method); | ||
318 | } | ||
319 | |||
320 | static ECDSA_SIG * | ||
321 | tls_ecdsa_do_sign(const unsigned char *dgst, int dgst_len, const BIGNUM *inv, | ||
322 | const BIGNUM *rp, EC_KEY *eckey) | ||
323 | { | ||
324 | struct tls_config *config; | ||
325 | ECDSA_SIG *sig = NULL; | ||
326 | const unsigned char *tsigbuf; | ||
327 | const char *hash; | ||
328 | char *sigbuf; | ||
329 | size_t siglen; | ||
330 | |||
331 | hash = ECDSA_get_ex_data(eckey, 0); | ||
332 | config = ECDSA_get_ex_data(eckey, 1); | ||
333 | |||
334 | if (hash == NULL || config == NULL) | ||
335 | return (NULL); | ||
336 | |||
337 | siglen = ECDSA_size(eckey); | ||
338 | if ((sigbuf = malloc(siglen)) == NULL) | ||
339 | return (NULL); | ||
340 | |||
341 | if (config->sign_cb(config->sign_cb_arg, hash, dgst, dgst_len, sigbuf, | ||
342 | &siglen, 0) != -1) { | ||
343 | tsigbuf = sigbuf; | ||
344 | sig = d2i_ECDSA_SIG(NULL, &tsigbuf, siglen); | ||
345 | } | ||
346 | free(sigbuf); | ||
347 | |||
348 | return (sig); | ||
349 | } | ||
350 | |||
351 | ECDSA_METHOD * | ||
352 | tls_signer_ecdsa_method(void) | ||
353 | { | ||
354 | static ECDSA_METHOD *ecdsa_method = NULL; | ||
355 | |||
356 | pthread_mutex_lock(&signer_method_lock); | ||
357 | |||
358 | if (ecdsa_method != NULL) | ||
359 | goto out; | ||
360 | |||
361 | ecdsa_method = calloc(1, sizeof(*ecdsa_method)); | ||
362 | if (ecdsa_method == NULL) | ||
363 | goto out; | ||
364 | |||
365 | ecdsa_method->ecdsa_do_sign = tls_ecdsa_do_sign; | ||
366 | ecdsa_method->name = strdup("libtls ECDSA method"); | ||
367 | if (ecdsa_method->name == NULL) { | ||
368 | free(ecdsa_method); | ||
369 | ecdsa_method = NULL; | ||
370 | } | ||
371 | |||
372 | out: | ||
373 | pthread_mutex_unlock(&signer_method_lock); | ||
374 | |||
375 | return (ecdsa_method); | ||
376 | } | ||