Annotation of src/usr.bin/ssh/ssh-sk.c, Revision 1.3
1.3 ! markus 1: /* $OpenBSD: ssh-sk.c,v 1.2 2019/11/12 19:29:54 markus Exp $ */
1.1 djm 2: /*
3: * Copyright (c) 2019 Google LLC
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: /* #define DEBUG_SK 1 */
19:
20: #include <dlfcn.h>
21: #include <stddef.h>
22: #include <stdint.h>
23: #include <string.h>
24: #include <stdio.h>
25:
26: #include <openssl/objects.h>
27: #include <openssl/ec.h>
28:
29: #include "log.h"
30: #include "misc.h"
31: #include "sshbuf.h"
32: #include "sshkey.h"
33: #include "ssherr.h"
34: #include "digest.h"
35:
36: #include "ssh-sk.h"
37: #include "sk-api.h"
38:
39: struct sshsk_provider {
40: char *path;
41: void *dlhandle;
42:
43: /* Return the version of the middleware API */
44: uint32_t (*sk_api_version)(void);
45:
46: /* Enroll a U2F key (private key generation) */
47: int (*sk_enroll)(const uint8_t *challenge, size_t challenge_len,
48: const char *application, uint8_t flags,
49: struct sk_enroll_response **enroll_response);
50:
51: /* Sign a challenge */
52: int (*sk_sign)(const uint8_t *message, size_t message_len,
53: const char *application,
54: const uint8_t *key_handle, size_t key_handle_len,
55: uint8_t flags, struct sk_sign_response **sign_response);
56: };
57:
58: static void
59: sshsk_free(struct sshsk_provider *p)
60: {
61: if (p == NULL)
62: return;
63: free(p->path);
64: if (p->dlhandle != NULL)
65: dlclose(p->dlhandle);
66: free(p);
67: }
68:
69: static struct sshsk_provider *
70: sshsk_open(const char *path)
71: {
72: struct sshsk_provider *ret = NULL;
73: uint32_t version;
74:
75: if ((ret = calloc(1, sizeof(*ret))) == NULL) {
76: error("%s: calloc failed", __func__);
77: return NULL;
78: }
79: if ((ret->path = strdup(path)) == NULL) {
80: error("%s: strdup failed", __func__);
81: goto fail;
82: }
83: if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) {
84: error("Security key provider %s dlopen failed: %s",
85: path, dlerror());
86: goto fail;
87: }
88: if ((ret->sk_api_version = dlsym(ret->dlhandle,
89: "sk_api_version")) == NULL) {
90: error("Security key provider %s dlsym(sk_api_version) "
91: "failed: %s", path, dlerror());
92: goto fail;
93: }
94: version = ret->sk_api_version();
95: debug("%s: provider %s implements version 0x%08lx", __func__,
96: ret->path, (u_long)version);
97: if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) {
98: error("Security key provider %s implements unsupported version "
99: "0x%08lx (supported: 0x%08lx)", path, (u_long)version,
100: (u_long)SSH_SK_VERSION_MAJOR);
101: goto fail;
102: }
103: if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) {
104: error("Security key provider %s dlsym(sk_enroll) "
105: "failed: %s", path, dlerror());
106: goto fail;
107: }
108: if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) {
109: error("Security key provider %s dlsym(sk_sign) failed: %s",
110: path, dlerror());
111: goto fail;
112: }
113: /* success */
114: return ret;
115: fail:
116: sshsk_free(ret);
117: return NULL;
118: }
119:
120: static void
121: sshsk_free_enroll_response(struct sk_enroll_response *r)
122: {
123: if (r == NULL)
124: return;
125: freezero(r->key_handle, r->key_handle_len);
126: freezero(r->public_key, r->public_key_len);
127: freezero(r->signature, r->signature_len);
128: freezero(r->attestation_cert, r->attestation_cert_len);
129: freezero(r, sizeof(*r));
130: };
131:
132: static void
133: sshsk_free_sign_response(struct sk_sign_response *r)
134: {
135: if (r == NULL)
136: return;
137: freezero(r->sig_r, r->sig_r_len);
138: freezero(r->sig_s, r->sig_s_len);
139: freezero(r, sizeof(*r));
140: };
141:
1.2 markus 142: /* Assemble key from response */
143: static int
144: sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
145: {
146: struct sshkey *key = NULL;
147: struct sshbuf *b = NULL;
148: EC_POINT *q = NULL;
149: int r;
150:
151: *keyp = NULL;
152: if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) {
153: error("%s: sshkey_new failed", __func__);
154: r = SSH_ERR_ALLOC_FAIL;
155: goto out;
156: }
157: key->ecdsa_nid = NID_X9_62_prime256v1;
158: if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
159: (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL ||
160: (b = sshbuf_new()) == NULL) {
161: error("%s: allocation failed", __func__);
162: r = SSH_ERR_ALLOC_FAIL;
163: goto out;
164: }
165: if ((r = sshbuf_put_string(b,
166: resp->public_key, resp->public_key_len)) != 0) {
167: error("%s: buffer error: %s", __func__, ssh_err(r));
168: goto out;
169: }
170: if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) {
171: error("%s: parse key: %s", __func__, ssh_err(r));
172: r = SSH_ERR_INVALID_FORMAT;
173: goto out;
174: }
175: if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) {
176: error("Security key returned invalid ECDSA key");
177: r = SSH_ERR_KEY_INVALID_EC_VALUE;
178: goto out;
179: }
180: if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
181: /* XXX assume it is a allocation error */
182: error("%s: allocation failed", __func__);
183: r = SSH_ERR_ALLOC_FAIL;
184: goto out;
185: }
186: /* success */
187: *keyp = key;
188: key = NULL; /* transferred */
189: r = 0;
190: out:
191: EC_POINT_free(q);
192: sshkey_free(key);
193: sshbuf_free(b);
194: return r;
195: }
196:
1.1 djm 197: int
198: sshsk_enroll(const char *provider_path, const char *application,
199: uint8_t flags, struct sshbuf *challenge_buf, struct sshkey **keyp,
200: struct sshbuf *attest)
201: {
202: struct sshsk_provider *skp = NULL;
203: struct sshkey *key = NULL;
204: u_char randchall[32];
205: const u_char *challenge;
206: size_t challenge_len;
207: struct sk_enroll_response *resp = NULL;
208: int r = SSH_ERR_INTERNAL_ERROR;
209:
210: *keyp = NULL;
211: if (attest)
212: sshbuf_reset(attest);
213: if (provider_path == NULL) {
214: error("%s: missing provider", __func__);
215: r = SSH_ERR_INVALID_ARGUMENT;
216: goto out;
217: }
218: if (application == NULL || *application == '\0') {
219: error("%s: missing application", __func__);
220: r = SSH_ERR_INVALID_ARGUMENT;
221: goto out;
222: }
223: if (challenge_buf == NULL) {
224: debug("%s: using random challenge", __func__);
225: arc4random_buf(randchall, sizeof(randchall));
226: challenge = randchall;
227: challenge_len = sizeof(randchall);
228: } else if (sshbuf_len(challenge_buf) == 0) {
229: error("Missing enrollment challenge");
230: r = SSH_ERR_INVALID_ARGUMENT;
231: goto out;
232: } else {
233: challenge = sshbuf_ptr(challenge_buf);
234: challenge_len = sshbuf_len(challenge_buf);
235: debug3("%s: using explicit challenge len=%zd",
236: __func__, challenge_len);
237: }
238: if ((skp = sshsk_open(provider_path)) == NULL) {
239: r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
240: goto out;
241: }
242: /* XXX validate flags? */
243: /* enroll key */
244: if ((r = skp->sk_enroll(challenge, challenge_len, application,
245: flags, &resp)) != 0) {
246: error("Security key provider %s returned failure %d",
247: provider_path, r);
248: r = SSH_ERR_INVALID_FORMAT; /* XXX error codes in API? */
249: goto out;
250: }
251: /* Check response validity */
252: if (resp->public_key == NULL || resp->key_handle == NULL ||
253: resp->signature == NULL || resp->attestation_cert == NULL) {
254: error("%s: sk_enroll response invalid", __func__);
255: r = SSH_ERR_INVALID_FORMAT;
256: goto out;
257: }
1.2 markus 258: if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0)
1.1 djm 259: goto out;
260: key->sk_flags = flags;
1.2 markus 261: if ((key->sk_key_handle = sshbuf_new()) == NULL ||
262: (key->sk_reserved = sshbuf_new()) == NULL) {
1.1 djm 263: error("%s: allocation failed", __func__);
264: r = SSH_ERR_ALLOC_FAIL;
265: goto out;
266: }
267: if ((key->sk_application = strdup(application)) == NULL) {
268: error("%s: strdup application failed", __func__);
269: r = SSH_ERR_ALLOC_FAIL;
270: goto out;
271: }
272: if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle,
273: resp->key_handle_len)) != 0) {
274: error("%s: buffer error: %s", __func__, ssh_err(r));
275: goto out;
276: }
277: /* Optionally fill in the attestation information */
278: if (attest != NULL) {
279: if ((r = sshbuf_put_cstring(attest, "sk-attest-v00")) != 0 ||
280: (r = sshbuf_put_u32(attest, 1)) != 0 || /* XXX U2F ver */
281: (r = sshbuf_put_string(attest,
282: resp->attestation_cert, resp->attestation_cert_len)) != 0 ||
283: (r = sshbuf_put_string(attest,
284: resp->signature, resp->signature_len)) != 0 ||
285: (r = sshbuf_put_u32(attest, flags)) != 0 || /* XXX right? */
286: (r = sshbuf_put_string(attest, NULL, 0)) != 0) {
287: error("%s: buffer error: %s", __func__, ssh_err(r));
288: goto out;
289: }
290: }
291: /* success */
292: *keyp = key;
293: key = NULL; /* transferred */
294: r = 0;
295: out:
296: sshsk_free(skp);
297: sshkey_free(key);
298: sshsk_free_enroll_response(resp);
299: explicit_bzero(randchall, sizeof(randchall));
300: return r;
301: }
302:
1.3 ! markus 303: static int
! 304: sshsk_ecdsa_inner_sig(struct sk_sign_response *resp, struct sshbuf **retp)
! 305: {
! 306: struct sshbuf *inner_sig = NULL;
! 307: int r = SSH_ERR_INTERNAL_ERROR;
! 308:
! 309: *retp = NULL;
! 310: if ((inner_sig = sshbuf_new()) == NULL) {
! 311: r = SSH_ERR_ALLOC_FAIL;
! 312: goto out;
! 313: }
! 314: /* Prepare inner signature object */
! 315: if ((r = sshbuf_put_bignum2_bytes(inner_sig,
! 316: resp->sig_r, resp->sig_r_len)) != 0 ||
! 317: (r = sshbuf_put_bignum2_bytes(inner_sig,
! 318: resp->sig_s, resp->sig_s_len)) != 0 ||
! 319: (r = sshbuf_put_u8(inner_sig, resp->flags)) != 0 ||
! 320: (r = sshbuf_put_u32(inner_sig, resp->counter)) != 0) {
! 321: debug("%s: buffer error: %s", __func__, ssh_err(r));
! 322: goto out;
! 323: }
! 324: #ifdef DEBUG_SK
! 325: fprintf(stderr, "%s: sig_r:\n", __func__);
! 326: sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
! 327: fprintf(stderr, "%s: sig_s:\n", __func__);
! 328: sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr);
! 329: fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
! 330: __func__, resp->flags, resp->counter);
! 331: #endif
! 332: *retp = inner_sig;
! 333: inner_sig = NULL;
! 334: r = 0;
! 335: out:
! 336: sshbuf_free(inner_sig);
! 337: return r;
! 338: }
! 339:
1.1 djm 340: int
341: sshsk_ecdsa_sign(const char *provider_path, const struct sshkey *key,
342: u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
343: u_int compat)
344: {
345: struct sshsk_provider *skp = NULL;
346: int r = SSH_ERR_INTERNAL_ERROR;
347: struct sk_sign_response *resp = NULL;
348: struct sshbuf *inner_sig = NULL, *sig = NULL;
349: uint8_t message[32];
350:
351: if (sigp != NULL)
352: *sigp = NULL;
353: if (lenp != NULL)
354: *lenp = 0;
355: if (provider_path == NULL ||
356: sshkey_type_plain(key->type) != KEY_ECDSA_SK ||
357: key->sk_key_handle == NULL ||
358: key->sk_application == NULL || *key->sk_application == '\0') {
359: r = SSH_ERR_INVALID_ARGUMENT;
360: goto out;
361: }
362: if ((skp = sshsk_open(provider_path)) == NULL) {
363: r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
364: goto out;
365: }
366:
367: /* hash data to be signed before it goes to the security key */
368: if ((r = ssh_digest_memory(SSH_DIGEST_SHA256, data, datalen,
369: message, sizeof(message))) != 0) {
370: error("%s: hash application failed: %s", __func__, ssh_err(r));
371: r = SSH_ERR_INTERNAL_ERROR;
372: goto out;
373: }
374: if ((r = skp->sk_sign(message, sizeof(message),
375: key->sk_application,
376: sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
377: key->sk_flags, &resp)) != 0) {
378: debug("%s: sk_sign failed with code %d", __func__, r);
379: goto out;
380: }
1.3 ! markus 381: /* Prepare inner signature object */
! 382: if ((r = sshsk_ecdsa_inner_sig(resp, &inner_sig)) != 0)
! 383: goto out;
! 384: /* Assemble outer signature */
! 385: if ((sig = sshbuf_new()) == NULL) {
1.1 djm 386: r = SSH_ERR_ALLOC_FAIL;
387: goto out;
388: }
389: if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0 ||
390: (r = sshbuf_put_stringb(sig, inner_sig)) != 0) {
391: debug("%s: buffer error (outer): %s", __func__, ssh_err(r));
392: goto out;
393: }
394: #ifdef DEBUG_SK
395: fprintf(stderr, "%s: hashed message:\n", __func__);
396: sshbuf_dump_data(message, sizeof(message), stderr);
397: fprintf(stderr, "%s: inner:\n", __func__);
398: sshbuf_dump(inner_sig, stderr);
399: fprintf(stderr, "%s: sigbuf:\n", __func__);
400: sshbuf_dump(sig, stderr);
401: #endif
402: if (sigp != NULL) {
403: if ((*sigp = malloc(sshbuf_len(sig))) == NULL) {
404: r = SSH_ERR_ALLOC_FAIL;
405: goto out;
406: }
407: memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig));
408: }
409: if (lenp != NULL)
410: *lenp = sshbuf_len(sig);
411: /* success */
412: r = 0;
413: out:
414: explicit_bzero(message, sizeof(message));
415: sshsk_free(skp);
416: sshsk_free_sign_response(resp);
417: sshbuf_free(sig);
418: sshbuf_free(inner_sig);
419: return r;
420: }