blob: 2b25c42ff759b0c653b7a9d3cdbf41d23af4dbbb [file] [log] [blame]
djm@openbsd.orga70d92f2019-11-19 22:23:19 +00001/* $OpenBSD: ssh-sk.c,v 1.16 2019/11/19 22:23:19 djm Exp $ */
djm@openbsd.orged3467c2019-10-31 21:16:20 +00002/*
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 "includes.h"
21
Damien Miller764d51e2019-11-01 13:34:49 +110022#ifdef ENABLE_SK
23
djm@openbsd.orged3467c2019-10-31 21:16:20 +000024#include <dlfcn.h>
25#include <stddef.h>
26#include <stdint.h>
27#include <string.h>
28#include <stdio.h>
29
naddy@openbsd.org723a5362019-11-18 16:08:57 +000030#ifdef WITH_OPENSSL
djm@openbsd.orged3467c2019-10-31 21:16:20 +000031#include <openssl/objects.h>
32#include <openssl/ec.h>
naddy@openbsd.org723a5362019-11-18 16:08:57 +000033#endif /* WITH_OPENSSL */
djm@openbsd.orged3467c2019-10-31 21:16:20 +000034
35#include "log.h"
36#include "misc.h"
37#include "sshbuf.h"
38#include "sshkey.h"
39#include "ssherr.h"
40#include "digest.h"
41
42#include "ssh-sk.h"
43#include "sk-api.h"
markus@openbsd.org7c32b512019-11-12 19:31:45 +000044#include "crypto_api.h"
djm@openbsd.orged3467c2019-10-31 21:16:20 +000045
46struct sshsk_provider {
47 char *path;
48 void *dlhandle;
49
50 /* Return the version of the middleware API */
51 uint32_t (*sk_api_version)(void);
52
53 /* Enroll a U2F key (private key generation) */
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +000054 int (*sk_enroll)(int alg, const uint8_t *challenge,
55 size_t challenge_len, const char *application, uint8_t flags,
djm@openbsd.orged3467c2019-10-31 21:16:20 +000056 struct sk_enroll_response **enroll_response);
57
58 /* Sign a challenge */
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +000059 int (*sk_sign)(int alg, const uint8_t *message, size_t message_len,
djm@openbsd.orged3467c2019-10-31 21:16:20 +000060 const char *application,
61 const uint8_t *key_handle, size_t key_handle_len,
62 uint8_t flags, struct sk_sign_response **sign_response);
63};
64
djm@openbsd.org6bff9522019-11-14 21:27:29 +000065/* Built-in version */
66int ssh_sk_enroll(int alg, const uint8_t *challenge,
67 size_t challenge_len, const char *application, uint8_t flags,
68 struct sk_enroll_response **enroll_response);
69int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len,
70 const char *application,
71 const uint8_t *key_handle, size_t key_handle_len,
72 uint8_t flags, struct sk_sign_response **sign_response);
73
djm@openbsd.orged3467c2019-10-31 21:16:20 +000074static void
75sshsk_free(struct sshsk_provider *p)
76{
77 if (p == NULL)
78 return;
79 free(p->path);
80 if (p->dlhandle != NULL)
81 dlclose(p->dlhandle);
82 free(p);
83}
84
85static struct sshsk_provider *
86sshsk_open(const char *path)
87{
88 struct sshsk_provider *ret = NULL;
89 uint32_t version;
90
91 if ((ret = calloc(1, sizeof(*ret))) == NULL) {
92 error("%s: calloc failed", __func__);
93 return NULL;
94 }
95 if ((ret->path = strdup(path)) == NULL) {
96 error("%s: strdup failed", __func__);
97 goto fail;
98 }
djm@openbsd.org6bff9522019-11-14 21:27:29 +000099 /* Skip the rest if we're using the linked in middleware */
100 if (strcasecmp(ret->path, "internal") == 0) {
101#ifdef ENABLE_SK_INTERNAL
102 ret->sk_enroll = ssh_sk_enroll;
103 ret->sk_sign = ssh_sk_sign;
104#else
105 error("internal security key support not enabled");
106#endif
107 return ret;
108 }
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000109 if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) {
110 error("Security key provider %s dlopen failed: %s",
111 path, dlerror());
112 goto fail;
113 }
114 if ((ret->sk_api_version = dlsym(ret->dlhandle,
115 "sk_api_version")) == NULL) {
116 error("Security key provider %s dlsym(sk_api_version) "
117 "failed: %s", path, dlerror());
118 goto fail;
119 }
120 version = ret->sk_api_version();
121 debug("%s: provider %s implements version 0x%08lx", __func__,
122 ret->path, (u_long)version);
123 if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) {
124 error("Security key provider %s implements unsupported version "
125 "0x%08lx (supported: 0x%08lx)", path, (u_long)version,
126 (u_long)SSH_SK_VERSION_MAJOR);
127 goto fail;
128 }
129 if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) {
130 error("Security key provider %s dlsym(sk_enroll) "
131 "failed: %s", path, dlerror());
132 goto fail;
133 }
134 if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) {
135 error("Security key provider %s dlsym(sk_sign) failed: %s",
136 path, dlerror());
137 goto fail;
138 }
139 /* success */
140 return ret;
141fail:
142 sshsk_free(ret);
143 return NULL;
144}
145
146static void
147sshsk_free_enroll_response(struct sk_enroll_response *r)
148{
149 if (r == NULL)
150 return;
151 freezero(r->key_handle, r->key_handle_len);
152 freezero(r->public_key, r->public_key_len);
153 freezero(r->signature, r->signature_len);
154 freezero(r->attestation_cert, r->attestation_cert_len);
155 freezero(r, sizeof(*r));
156};
157
158static void
159sshsk_free_sign_response(struct sk_sign_response *r)
160{
161 if (r == NULL)
162 return;
163 freezero(r->sig_r, r->sig_r_len);
164 freezero(r->sig_s, r->sig_s_len);
165 freezero(r, sizeof(*r));
166};
167
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000168#ifdef WITH_OPENSSL
markus@openbsd.orgcef84a02019-11-12 19:29:54 +0000169/* Assemble key from response */
170static int
171sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
172{
173 struct sshkey *key = NULL;
174 struct sshbuf *b = NULL;
175 EC_POINT *q = NULL;
176 int r;
177
178 *keyp = NULL;
179 if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) {
180 error("%s: sshkey_new failed", __func__);
181 r = SSH_ERR_ALLOC_FAIL;
182 goto out;
183 }
184 key->ecdsa_nid = NID_X9_62_prime256v1;
185 if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
186 (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL ||
187 (b = sshbuf_new()) == NULL) {
188 error("%s: allocation failed", __func__);
189 r = SSH_ERR_ALLOC_FAIL;
190 goto out;
191 }
192 if ((r = sshbuf_put_string(b,
193 resp->public_key, resp->public_key_len)) != 0) {
194 error("%s: buffer error: %s", __func__, ssh_err(r));
195 goto out;
196 }
197 if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) {
198 error("%s: parse key: %s", __func__, ssh_err(r));
199 r = SSH_ERR_INVALID_FORMAT;
200 goto out;
201 }
202 if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) {
203 error("Security key returned invalid ECDSA key");
204 r = SSH_ERR_KEY_INVALID_EC_VALUE;
205 goto out;
206 }
207 if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
208 /* XXX assume it is a allocation error */
209 error("%s: allocation failed", __func__);
210 r = SSH_ERR_ALLOC_FAIL;
211 goto out;
212 }
213 /* success */
214 *keyp = key;
215 key = NULL; /* transferred */
216 r = 0;
217 out:
218 EC_POINT_free(q);
219 sshkey_free(key);
220 sshbuf_free(b);
221 return r;
222}
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000223#endif /* WITH_OPENSSL */
markus@openbsd.orgcef84a02019-11-12 19:29:54 +0000224
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000225static int
226sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
227{
228 struct sshkey *key = NULL;
229 int r;
230
231 *keyp = NULL;
232 if (resp->public_key_len != ED25519_PK_SZ) {
233 error("%s: invalid size: %zu", __func__, resp->public_key_len);
234 r = SSH_ERR_INVALID_FORMAT;
235 goto out;
236 }
237 if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) {
238 error("%s: sshkey_new failed", __func__);
239 r = SSH_ERR_ALLOC_FAIL;
240 goto out;
241 }
242 if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
243 error("%s: malloc failed", __func__);
244 r = SSH_ERR_ALLOC_FAIL;
245 goto out;
246 }
247 memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ);
248 /* success */
249 *keyp = key;
250 key = NULL; /* transferred */
251 r = 0;
252 out:
253 sshkey_free(key);
254 return r;
255}
256
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000257int
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000258sshsk_enroll(int type, const char *provider_path, const char *application,
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000259 uint8_t flags, struct sshbuf *challenge_buf, struct sshkey **keyp,
260 struct sshbuf *attest)
261{
262 struct sshsk_provider *skp = NULL;
263 struct sshkey *key = NULL;
264 u_char randchall[32];
265 const u_char *challenge;
266 size_t challenge_len;
267 struct sk_enroll_response *resp = NULL;
268 int r = SSH_ERR_INTERNAL_ERROR;
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000269 int alg;
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000270
djm@openbsd.org4103a3e2019-11-16 22:42:30 +0000271 debug("%s: provider \"%s\", application \"%s\", flags 0x%02x, "
272 "challenge len %zu", __func__, provider_path, application,
273 flags, challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf));
274
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000275 *keyp = NULL;
276 if (attest)
277 sshbuf_reset(attest);
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000278 switch (type) {
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000279#ifdef WITH_OPENSSL
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000280 case KEY_ECDSA_SK:
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000281 alg = SSH_SK_ECDSA;
282 break;
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000283#endif /* WITH_OPENSSL */
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000284 case KEY_ED25519_SK:
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000285 alg = SSH_SK_ED25519;
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000286 break;
287 default:
288 error("%s: unsupported key type", __func__);
289 r = SSH_ERR_INVALID_ARGUMENT;
290 goto out;
291 }
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000292 if (provider_path == NULL) {
293 error("%s: missing provider", __func__);
294 r = SSH_ERR_INVALID_ARGUMENT;
295 goto out;
296 }
297 if (application == NULL || *application == '\0') {
298 error("%s: missing application", __func__);
299 r = SSH_ERR_INVALID_ARGUMENT;
300 goto out;
301 }
302 if (challenge_buf == NULL) {
303 debug("%s: using random challenge", __func__);
304 arc4random_buf(randchall, sizeof(randchall));
305 challenge = randchall;
306 challenge_len = sizeof(randchall);
307 } else if (sshbuf_len(challenge_buf) == 0) {
308 error("Missing enrollment challenge");
309 r = SSH_ERR_INVALID_ARGUMENT;
310 goto out;
311 } else {
312 challenge = sshbuf_ptr(challenge_buf);
313 challenge_len = sshbuf_len(challenge_buf);
314 debug3("%s: using explicit challenge len=%zd",
315 __func__, challenge_len);
316 }
317 if ((skp = sshsk_open(provider_path)) == NULL) {
318 r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
319 goto out;
320 }
321 /* XXX validate flags? */
322 /* enroll key */
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000323 if ((r = skp->sk_enroll(alg, challenge, challenge_len, application,
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000324 flags, &resp)) != 0) {
325 error("Security key provider %s returned failure %d",
326 provider_path, r);
327 r = SSH_ERR_INVALID_FORMAT; /* XXX error codes in API? */
328 goto out;
329 }
330 /* Check response validity */
331 if (resp->public_key == NULL || resp->key_handle == NULL ||
djm@openbsd.orgfccff332019-11-12 22:38:19 +0000332 resp->signature == NULL ||
333 (resp->attestation_cert == NULL && resp->attestation_cert_len != 0)) {
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000334 error("%s: sk_enroll response invalid", __func__);
335 r = SSH_ERR_INVALID_FORMAT;
336 goto out;
337 }
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000338 switch (type) {
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000339#ifdef WITH_OPENSSL
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000340 case KEY_ECDSA_SK:
341 if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0)
342 goto out;
343 break;
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000344#endif /* WITH_OPENSSL */
markus@openbsd.org7c32b512019-11-12 19:31:45 +0000345 case KEY_ED25519_SK:
346 if ((r = sshsk_ed25519_assemble(resp, &key)) != 0)
347 goto out;
348 break;
349 }
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000350 key->sk_flags = flags;
markus@openbsd.orgcef84a02019-11-12 19:29:54 +0000351 if ((key->sk_key_handle = sshbuf_new()) == NULL ||
352 (key->sk_reserved = sshbuf_new()) == NULL) {
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000353 error("%s: allocation failed", __func__);
354 r = SSH_ERR_ALLOC_FAIL;
355 goto out;
356 }
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000357 if ((key->sk_application = strdup(application)) == NULL) {
358 error("%s: strdup application failed", __func__);
359 r = SSH_ERR_ALLOC_FAIL;
360 goto out;
361 }
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000362 if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle,
363 resp->key_handle_len)) != 0) {
364 error("%s: buffer error: %s", __func__, ssh_err(r));
365 goto out;
366 }
367 /* Optionally fill in the attestation information */
368 if (attest != NULL) {
369 if ((r = sshbuf_put_cstring(attest, "sk-attest-v00")) != 0 ||
370 (r = sshbuf_put_u32(attest, 1)) != 0 || /* XXX U2F ver */
371 (r = sshbuf_put_string(attest,
372 resp->attestation_cert, resp->attestation_cert_len)) != 0 ||
373 (r = sshbuf_put_string(attest,
374 resp->signature, resp->signature_len)) != 0 ||
375 (r = sshbuf_put_u32(attest, flags)) != 0 || /* XXX right? */
376 (r = sshbuf_put_string(attest, NULL, 0)) != 0) {
377 error("%s: buffer error: %s", __func__, ssh_err(r));
378 goto out;
379 }
380 }
381 /* success */
382 *keyp = key;
383 key = NULL; /* transferred */
384 r = 0;
385 out:
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000386 sshsk_free(skp);
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000387 sshkey_free(key);
388 sshsk_free_enroll_response(resp);
389 explicit_bzero(randchall, sizeof(randchall));
390 return r;
391}
392
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000393#ifdef WITH_OPENSSL
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000394static int
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000395sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig)
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000396{
397 struct sshbuf *inner_sig = NULL;
398 int r = SSH_ERR_INTERNAL_ERROR;
399
markus@openbsd.org3fcf69a2019-11-12 19:34:00 +0000400 /* Check response validity */
markus@openbsd.orgdffd02e2019-11-13 20:25:45 +0000401 if (resp->sig_r == NULL || resp->sig_s == NULL) {
markus@openbsd.org3fcf69a2019-11-12 19:34:00 +0000402 error("%s: sk_sign response invalid", __func__);
403 r = SSH_ERR_INVALID_FORMAT;
404 goto out;
405 }
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000406 if ((inner_sig = sshbuf_new()) == NULL) {
407 r = SSH_ERR_ALLOC_FAIL;
408 goto out;
409 }
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000410 /* Prepare and append inner signature object */
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000411 if ((r = sshbuf_put_bignum2_bytes(inner_sig,
412 resp->sig_r, resp->sig_r_len)) != 0 ||
413 (r = sshbuf_put_bignum2_bytes(inner_sig,
djm@openbsd.orga70d92f2019-11-19 22:23:19 +0000414 resp->sig_s, resp->sig_s_len)) != 0) {
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000415 debug("%s: buffer error: %s", __func__, ssh_err(r));
416 goto out;
417 }
djm@openbsd.orga70d92f2019-11-19 22:23:19 +0000418 if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 ||
419 (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
420 (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000421 debug("%s: buffer error: %s", __func__, ssh_err(r));
422 goto out;
423 }
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000424#ifdef DEBUG_SK
425 fprintf(stderr, "%s: sig_r:\n", __func__);
426 sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
427 fprintf(stderr, "%s: sig_s:\n", __func__);
428 sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr);
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000429 fprintf(stderr, "%s: inner:\n", __func__);
430 sshbuf_dump(inner_sig, stderr);
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000431#endif
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000432 r = 0;
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000433 out:
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000434 sshbuf_free(inner_sig);
435 return r;
436}
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000437#endif /* WITH_OPENSSL */
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000438
439static int
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000440sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig)
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000441{
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000442 int r = SSH_ERR_INTERNAL_ERROR;
443
markus@openbsd.org3fcf69a2019-11-12 19:34:00 +0000444 /* Check response validity */
445 if (resp->sig_r == NULL) {
446 error("%s: sk_sign response invalid", __func__);
447 r = SSH_ERR_INVALID_FORMAT;
448 goto out;
449 }
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000450 if ((r = sshbuf_put_string(sig,
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000451 resp->sig_r, resp->sig_r_len)) != 0 ||
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000452 (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
453 (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000454 debug("%s: buffer error: %s", __func__, ssh_err(r));
455 goto out;
456 }
457#ifdef DEBUG_SK
458 fprintf(stderr, "%s: sig_r:\n", __func__);
459 sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000460#endif
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000461 r = 0;
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000462 out:
463 return 0;
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000464}
465
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000466int
markus@openbsd.orge03a29e2019-11-12 19:30:50 +0000467sshsk_sign(const char *provider_path, const struct sshkey *key,
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000468 u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
469 u_int compat)
470{
471 struct sshsk_provider *skp = NULL;
472 int r = SSH_ERR_INTERNAL_ERROR;
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000473 int type, alg;
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000474 struct sk_sign_response *resp = NULL;
475 struct sshbuf *inner_sig = NULL, *sig = NULL;
476 uint8_t message[32];
477
djm@openbsd.org9a1225e2019-11-16 23:17:20 +0000478 debug("%s: provider \"%s\", key %s, flags 0x%02x", __func__,
djm@openbsd.org4103a3e2019-11-16 22:42:30 +0000479 provider_path, sshkey_type(key), key->sk_flags);
480
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000481 if (sigp != NULL)
482 *sigp = NULL;
483 if (lenp != NULL)
484 *lenp = 0;
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000485 type = sshkey_type_plain(key->type);
486 switch (type) {
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000487#ifdef WITH_OPENSSL
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000488 case KEY_ECDSA_SK:
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000489 alg = SSH_SK_ECDSA;
490 break;
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000491#endif /* WITH_OPENSSL */
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000492 case KEY_ED25519_SK:
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000493 alg = SSH_SK_ED25519;
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000494 break;
495 default:
496 return SSH_ERR_INVALID_ARGUMENT;
497 }
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000498 if (provider_path == NULL ||
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000499 key->sk_key_handle == NULL ||
500 key->sk_application == NULL || *key->sk_application == '\0') {
501 r = SSH_ERR_INVALID_ARGUMENT;
502 goto out;
503 }
504 if ((skp = sshsk_open(provider_path)) == NULL) {
505 r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
506 goto out;
507 }
508
509 /* hash data to be signed before it goes to the security key */
510 if ((r = ssh_digest_memory(SSH_DIGEST_SHA256, data, datalen,
511 message, sizeof(message))) != 0) {
512 error("%s: hash application failed: %s", __func__, ssh_err(r));
513 r = SSH_ERR_INTERNAL_ERROR;
514 goto out;
515 }
markus@openbsd.orgfd1a3b52019-11-12 19:32:30 +0000516 if ((r = skp->sk_sign(alg, message, sizeof(message),
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000517 key->sk_application,
518 sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
519 key->sk_flags, &resp)) != 0) {
520 debug("%s: sk_sign failed with code %d", __func__, r);
521 goto out;
522 }
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000523 /* Assemble signature */
markus@openbsd.orgbc7b5d62019-11-12 19:30:21 +0000524 if ((sig = sshbuf_new()) == NULL) {
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000525 r = SSH_ERR_ALLOC_FAIL;
526 goto out;
527 }
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000528 if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) {
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000529 debug("%s: buffer error (outer): %s", __func__, ssh_err(r));
530 goto out;
531 }
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000532 switch (type) {
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000533#ifdef WITH_OPENSSL
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000534 case KEY_ECDSA_SK:
535 if ((r = sshsk_ecdsa_sig(resp, sig)) != 0)
536 goto out;
537 break;
naddy@openbsd.org723a5362019-11-18 16:08:57 +0000538#endif /* WITH_OPENSSL */
markus@openbsd.orgb556cc32019-11-12 19:34:40 +0000539 case KEY_ED25519_SK:
540 if ((r = sshsk_ed25519_sig(resp, sig)) != 0)
541 goto out;
542 break;
543 }
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000544#ifdef DEBUG_SK
markus@openbsd.orgfe05a362019-11-12 19:31:18 +0000545 fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
546 __func__, resp->flags, resp->counter);
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000547 fprintf(stderr, "%s: hashed message:\n", __func__);
548 sshbuf_dump_data(message, sizeof(message), stderr);
djm@openbsd.orged3467c2019-10-31 21:16:20 +0000549 fprintf(stderr, "%s: sigbuf:\n", __func__);
550 sshbuf_dump(sig, stderr);
551#endif
552 if (sigp != NULL) {
553 if ((*sigp = malloc(sshbuf_len(sig))) == NULL) {
554 r = SSH_ERR_ALLOC_FAIL;
555 goto out;
556 }
557 memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig));
558 }
559 if (lenp != NULL)
560 *lenp = sshbuf_len(sig);
561 /* success */
562 r = 0;
563 out:
564 explicit_bzero(message, sizeof(message));
565 sshsk_free(skp);
566 sshsk_free_sign_response(resp);
567 sshbuf_free(sig);
568 sshbuf_free(inner_sig);
569 return r;
570}
Damien Miller764d51e2019-11-01 13:34:49 +1100571#endif /* ENABLE_SK */