xref: /openssh-portable/sk-usbhid.c (revision c1c44eee)
1 /* $OpenBSD: sk-usbhid.c,v 1.25 2020/08/31 00:17:41 djm Exp $ */
2 /*
3  * Copyright (c) 2019 Markus Friedl
4  * Copyright (c) 2020 Pedro Martelletto
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include "includes.h"
20 
21 #ifdef ENABLE_SK_INTERNAL
22 
23 #include <stdint.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <stddef.h>
28 #include <stdarg.h>
29 #ifdef HAVE_SHA2_H
30 #include <sha2.h>
31 #endif
32 
33 #ifdef WITH_OPENSSL
34 #include <openssl/opensslv.h>
35 #include <openssl/crypto.h>
36 #include <openssl/bn.h>
37 #include <openssl/ec.h>
38 #include <openssl/ecdsa.h>
39 #include <openssl/evp.h>
40 #endif /* WITH_OPENSSL */
41 
42 #include <fido.h>
43 #include <fido/credman.h>
44 
45 /* backwards compat for libfido2 */
46 #ifndef HAVE_FIDO_CRED_PROT
47 #define fido_cred_prot(x) (0)
48 #endif
49 #ifndef HAVE_FIDO_CRED_SET_PROT
50 #define fido_cred_set_prot(x, y) (FIDO_ERR_UNSUPPORTED_OPTION)
51 #endif
52 #ifndef HAVE_FIDO_DEV_SUPPORTS_CRED_PROT
53 #define fido_dev_supports_cred_prot(x) (0)
54 #endif
55 #ifndef HAVE_FIDO_DEV_GET_TOUCH_BEGIN
56 #define fido_dev_get_touch_begin(x) (FIDO_ERR_UNSUPPORTED_OPTION)
57 #endif
58 #ifndef HAVE_FIDO_DEV_GET_TOUCH_STATUS
59 #define fido_dev_get_touch_status(x, y, z) (FIDO_ERR_UNSUPPORTED_OPTION)
60 #endif
61 #ifndef FIDO_CRED_PROT_UV_REQUIRED
62 #define FIDO_CRED_PROT_UV_REQUIRED 0
63 #endif
64 #ifndef FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID
65 #define FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID 0
66 #endif
67 
68 #ifndef SK_STANDALONE
69 # include "log.h"
70 # include "xmalloc.h"
71 # include "misc.h"
72 /*
73  * If building as part of OpenSSH, then rename exported functions.
74  * This must be done before including sk-api.h.
75  */
76 # define sk_api_version		ssh_sk_api_version
77 # define sk_enroll		ssh_sk_enroll
78 # define sk_sign		ssh_sk_sign
79 # define sk_load_resident_keys	ssh_sk_load_resident_keys
80 #endif /* !SK_STANDALONE */
81 
82 #include "sk-api.h"
83 
84 /* #define SK_DEBUG 1 */
85 
86 #ifdef SK_DEBUG
87 #define SSH_FIDO_INIT_ARG	FIDO_DEBUG
88 #else
89 #define SSH_FIDO_INIT_ARG	0
90 #endif
91 
92 #define MAX_FIDO_DEVICES	8
93 #define FIDO_POLL_MS		50
94 #define SELECT_MS		15000
95 #define POLL_SLEEP_NS		200000000
96 
97 /* Compatibility with OpenSSH 1.0.x */
98 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
99 #define ECDSA_SIG_get0(sig, pr, ps) \
100 	do { \
101 		(*pr) = sig->r; \
102 		(*ps) = sig->s; \
103 	} while (0)
104 #endif
105 
106 struct sk_usbhid {
107 	fido_dev_t *dev;
108 	char *path;
109 };
110 
111 /* Return the version of the middleware API */
112 uint32_t sk_api_version(void);
113 
114 /* Enroll a U2F key (private key generation) */
115 int sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
116     const char *application, uint8_t flags, const char *pin,
117     struct sk_option **options, struct sk_enroll_response **enroll_response);
118 
119 /* Sign a challenge */
120 int sk_sign(uint32_t alg, const uint8_t *message, size_t message_len,
121     const char *application, const uint8_t *key_handle, size_t key_handle_len,
122     uint8_t flags, const char *pin, struct sk_option **options,
123     struct sk_sign_response **sign_response);
124 
125 /* Load resident keys */
126 int sk_load_resident_keys(const char *pin, struct sk_option **options,
127     struct sk_resident_key ***rks, size_t *nrks);
128 
129 static void skdebug(const char *func, const char *fmt, ...)
130     __attribute__((__format__ (printf, 2, 3)));
131 
132 static void
133 skdebug(const char *func, const char *fmt, ...)
134 {
135 #if !defined(SK_STANDALONE)
136 	char *msg;
137 	va_list ap;
138 
139 	va_start(ap, fmt);
140 	xvasprintf(&msg, fmt, ap);
141 	va_end(ap);
142 	debug("%s: %s", func, msg);
143 	free(msg);
144 #elif defined(SK_DEBUG)
145 	va_list ap;
146 
147 	va_start(ap, fmt);
148 	fprintf(stderr, "%s: ", func);
149 	vfprintf(stderr, fmt, ap);
150 	fputc('\n', stderr);
151 	va_end(ap);
152 #else
153 	(void)func; /* XXX */
154 	(void)fmt; /* XXX */
155 #endif
156 }
157 
158 uint32_t
159 sk_api_version(void)
160 {
161 	return SSH_SK_VERSION_MAJOR;
162 }
163 
164 static struct sk_usbhid *
165 sk_open(const char *path)
166 {
167 	struct sk_usbhid *sk;
168 	int r;
169 
170 	if (path == NULL) {
171 		skdebug(__func__, "path == NULL");
172 		return NULL;
173 	}
174 	if ((sk = calloc(1, sizeof(*sk))) == NULL) {
175 		skdebug(__func__, "calloc sk failed");
176 		return NULL;
177 	}
178 	if ((sk->path = strdup(path)) == NULL) {
179 		skdebug(__func__, "strdup path failed");
180 		free(sk);
181 		return NULL;
182 	}
183 	if ((sk->dev = fido_dev_new()) == NULL) {
184 		skdebug(__func__, "fido_dev_new failed");
185 		free(sk->path);
186 		free(sk);
187 		return NULL;
188 	}
189 	if ((r = fido_dev_open(sk->dev, sk->path)) != FIDO_OK) {
190 		skdebug(__func__, "fido_dev_open %s failed: %s", sk->path,
191 		    fido_strerr(r));
192 		fido_dev_free(&sk->dev);
193 		free(sk->path);
194 		free(sk);
195 		return NULL;
196 	}
197 	return sk;
198 }
199 
200 static void
201 sk_close(struct sk_usbhid *sk)
202 {
203 	if (sk == NULL)
204 		return;
205 	fido_dev_cancel(sk->dev); /* cancel any pending operation */
206 	fido_dev_close(sk->dev);
207 	fido_dev_free(&sk->dev);
208 	free(sk->path);
209 	free(sk);
210 }
211 
212 static struct sk_usbhid **
213 sk_openv(const fido_dev_info_t *devlist, size_t ndevs, size_t *nopen)
214 {
215 	const fido_dev_info_t *di;
216 	struct sk_usbhid **skv;
217 	size_t i;
218 
219 	*nopen = 0;
220 	if ((skv = calloc(ndevs, sizeof(*skv))) == NULL) {
221 		skdebug(__func__, "calloc skv failed");
222 		return NULL;
223 	}
224 	for (i = 0; i < ndevs; i++) {
225 		if ((di = fido_dev_info_ptr(devlist, i)) == NULL)
226 			skdebug(__func__, "fido_dev_info_ptr failed");
227 		else if ((skv[*nopen] = sk_open(fido_dev_info_path(di))) == NULL)
228 			skdebug(__func__, "sk_open failed");
229 		else
230 			(*nopen)++;
231 	}
232 	if (*nopen == 0) {
233 		for (i = 0; i < ndevs; i++)
234 			sk_close(skv[i]);
235 		free(skv);
236 		skv = NULL;
237 	}
238 
239 	return skv;
240 }
241 
242 static void
243 sk_closev(struct sk_usbhid **skv, size_t nsk)
244 {
245 	size_t i;
246 
247 	for (i = 0; i < nsk; i++)
248 		sk_close(skv[i]);
249 	free(skv);
250 }
251 
252 static int
253 sk_touch_begin(struct sk_usbhid **skv, size_t nsk)
254 {
255 	size_t i, ok = 0;
256 	int r;
257 
258 	for (i = 0; i < nsk; i++)
259 		if ((r = fido_dev_get_touch_begin(skv[i]->dev)) != FIDO_OK)
260 			skdebug(__func__, "fido_dev_get_touch_begin %s failed:"
261 			    " %s", skv[i]->path, fido_strerr(r));
262 		else
263 			ok++;
264 
265 	return ok ? 0 : -1;
266 }
267 
268 static int
269 sk_touch_poll(struct sk_usbhid **skv, size_t nsk, int *touch, size_t *idx)
270 {
271 	struct timespec ts_pause;
272 	size_t npoll, i;
273 	int r;
274 
275 	ts_pause.tv_sec = 0;
276 	ts_pause.tv_nsec = POLL_SLEEP_NS;
277 	nanosleep(&ts_pause, NULL);
278 	npoll = nsk;
279 	for (i = 0; i < nsk; i++) {
280 		if (skv[i] == NULL)
281 			continue; /* device discarded */
282 		skdebug(__func__, "polling %s", skv[i]->path);
283 		if ((r = fido_dev_get_touch_status(skv[i]->dev, touch,
284 		    FIDO_POLL_MS)) != FIDO_OK) {
285 			skdebug(__func__, "fido_dev_get_touch_status %s: %s",
286 			    skv[i]->path, fido_strerr(r));
287 			sk_close(skv[i]); /* discard device */
288 			skv[i] = NULL;
289 			if (--npoll == 0) {
290 				skdebug(__func__, "no device left to poll");
291 				return -1;
292 			}
293 		} else if (*touch) {
294 			*idx = i;
295 			return 0;
296 		}
297 	}
298 	*touch = 0;
299 	return 0;
300 }
301 
302 /* Calculate SHA256(m) */
303 static int
304 sha256_mem(const void *m, size_t mlen, u_char *d, size_t dlen)
305 {
306 #ifdef WITH_OPENSSL
307 	u_int mdlen;
308 #endif
309 
310 	if (dlen != 32)
311 		return -1;
312 #ifdef WITH_OPENSSL
313 	mdlen = dlen;
314 	if (!EVP_Digest(m, mlen, d, &mdlen, EVP_sha256(), NULL))
315 		return -1;
316 #else
317 	SHA256Data(m, mlen, d);
318 #endif
319 	return 0;
320 }
321 
322 /* Check if the specified key handle exists on a given sk. */
323 static int
324 sk_try(const struct sk_usbhid *sk, const char *application,
325     const uint8_t *key_handle, size_t key_handle_len)
326 {
327 	fido_assert_t *assert = NULL;
328 	/* generate an invalid signature on FIDO2 tokens */
329 	const char *data = "";
330 	uint8_t message[32];
331 	int r = FIDO_ERR_INTERNAL;
332 
333 	if (sha256_mem(data, strlen(data), message, sizeof(message)) != 0) {
334 		skdebug(__func__, "hash message failed");
335 		goto out;
336 	}
337 	if ((assert = fido_assert_new()) == NULL) {
338 		skdebug(__func__, "fido_assert_new failed");
339 		goto out;
340 	}
341 	if ((r = fido_assert_set_clientdata_hash(assert, message,
342 	    sizeof(message))) != FIDO_OK) {
343 		skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
344 		    fido_strerr(r));
345 		goto out;
346 	}
347 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
348 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
349 		goto out;
350 	}
351 	if ((r = fido_assert_allow_cred(assert, key_handle,
352 	    key_handle_len)) != FIDO_OK) {
353 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
354 		goto out;
355 	}
356 	if ((r = fido_assert_set_up(assert, FIDO_OPT_FALSE)) != FIDO_OK) {
357 		skdebug(__func__, "fido_assert_up: %s", fido_strerr(r));
358 		goto out;
359 	}
360 	r = fido_dev_get_assert(sk->dev, assert, NULL);
361 	skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
362 	if (r == FIDO_ERR_USER_PRESENCE_REQUIRED) {
363 		/* U2F tokens may return this */
364 		r = FIDO_OK;
365 	}
366  out:
367 	fido_assert_free(&assert);
368 
369 	return r != FIDO_OK ? -1 : 0;
370 }
371 
372 static struct sk_usbhid *
373 sk_select_by_cred(const fido_dev_info_t *devlist, size_t ndevs,
374     const char *application, const uint8_t *key_handle, size_t key_handle_len)
375 {
376 	struct sk_usbhid **skv, *sk;
377 	size_t skvcnt, i;
378 
379 	if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
380 		skdebug(__func__, "sk_openv failed");
381 		return NULL;
382 	}
383 	if (skvcnt == 1) {
384 		sk = skv[0];
385 		skv[0] = NULL;
386 		goto out;
387 	}
388 	sk = NULL;
389 	for (i = 0; i < skvcnt; i++) {
390 		if (sk_try(skv[i], application, key_handle,
391 		    key_handle_len) == 0) {
392 			sk = skv[i];
393 			skv[i] = NULL;
394 			skdebug(__func__, "found key in %s", sk->path);
395 			break;
396 		}
397 	}
398  out:
399 	sk_closev(skv, skvcnt);
400 	return sk;
401 }
402 
403 static struct sk_usbhid *
404 sk_select_by_touch(const fido_dev_info_t *devlist, size_t ndevs)
405 {
406 	struct sk_usbhid **skv, *sk;
407 	struct timeval tv_start, tv_now, tv_delta;
408 	size_t skvcnt, idx;
409 	int touch, ms_remain;
410 
411 	if ((skv = sk_openv(devlist, ndevs, &skvcnt)) == NULL) {
412 		skdebug(__func__, "sk_openv failed");
413 		return NULL;
414 	}
415 	sk = NULL;
416 	if (skvcnt < 2) {
417 		if (skvcnt == 1) {
418 			/* single candidate */
419 			sk = skv[0];
420 			skv[0] = NULL;
421 		}
422 		goto out;
423 	}
424 #ifndef HAVE_FIDO_DEV_GET_TOUCH_STATUS
425 	skdebug(__func__, "libfido2 version does not support a feature needed for multiple tokens. Please upgrade to >=1.5.0");
426 	goto out;
427 #endif
428 
429 	if (sk_touch_begin(skv, skvcnt) == -1) {
430 		skdebug(__func__, "sk_touch_begin failed");
431 		goto out;
432 	}
433 	monotime_tv(&tv_start);
434 	do {
435 		if (sk_touch_poll(skv, skvcnt, &touch, &idx) == -1) {
436 			skdebug(__func__, "sk_touch_poll failed");
437 			goto out;
438 		}
439 		if (touch) {
440 			sk = skv[idx];
441 			skv[idx] = NULL;
442 			goto out;
443 		}
444 		monotime_tv(&tv_now);
445 		timersub(&tv_now, &tv_start, &tv_delta);
446 		ms_remain = SELECT_MS - tv_delta.tv_sec * 1000 -
447 		    tv_delta.tv_usec / 1000;
448 	} while (ms_remain >= FIDO_POLL_MS);
449 	skdebug(__func__, "timeout");
450 out:
451 	sk_closev(skv, skvcnt);
452 	return sk;
453 }
454 
455 static struct sk_usbhid *
456 sk_probe(const char *application, const uint8_t *key_handle,
457     size_t key_handle_len)
458 {
459 	struct sk_usbhid *sk;
460 	fido_dev_info_t *devlist;
461 	size_t ndevs;
462 	int r;
463 
464 	if ((devlist = fido_dev_info_new(MAX_FIDO_DEVICES)) == NULL) {
465 		skdebug(__func__, "fido_dev_info_new failed");
466 		return NULL;
467 	}
468 	if ((r = fido_dev_info_manifest(devlist, MAX_FIDO_DEVICES,
469 	    &ndevs)) != FIDO_OK) {
470 		skdebug(__func__, "fido_dev_info_manifest failed: %s",
471 		    fido_strerr(r));
472 		fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
473 		return NULL;
474 	}
475 	skdebug(__func__, "%zu device(s) detected", ndevs);
476 	if (ndevs == 0) {
477 		sk = NULL;
478 	} else if (application != NULL && key_handle != NULL) {
479 		skdebug(__func__, "selecting sk by cred");
480 		sk = sk_select_by_cred(devlist, ndevs, application, key_handle,
481 		    key_handle_len);
482 	} else {
483 		skdebug(__func__, "selecting sk by touch");
484 		sk = sk_select_by_touch(devlist, ndevs);
485 	}
486 	fido_dev_info_free(&devlist, MAX_FIDO_DEVICES);
487 	return sk;
488 }
489 
490 #ifdef WITH_OPENSSL
491 /*
492  * The key returned via fido_cred_pubkey_ptr() is in affine coordinates,
493  * but the API expects a SEC1 octet string.
494  */
495 static int
496 pack_public_key_ecdsa(const fido_cred_t *cred,
497     struct sk_enroll_response *response)
498 {
499 	const uint8_t *ptr;
500 	BIGNUM *x = NULL, *y = NULL;
501 	EC_POINT *q = NULL;
502 	EC_GROUP *g = NULL;
503 	int ret = -1;
504 
505 	response->public_key = NULL;
506 	response->public_key_len = 0;
507 
508 	if ((x = BN_new()) == NULL ||
509 	    (y = BN_new()) == NULL ||
510 	    (g = EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
511 	    (q = EC_POINT_new(g)) == NULL) {
512 		skdebug(__func__, "libcrypto setup failed");
513 		goto out;
514 	}
515 	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
516 		skdebug(__func__, "fido_cred_pubkey_ptr failed");
517 		goto out;
518 	}
519 	if (fido_cred_pubkey_len(cred) != 64) {
520 		skdebug(__func__, "bad fido_cred_pubkey_len %zu",
521 		    fido_cred_pubkey_len(cred));
522 		goto out;
523 	}
524 
525 	if (BN_bin2bn(ptr, 32, x) == NULL ||
526 	    BN_bin2bn(ptr + 32, 32, y) == NULL) {
527 		skdebug(__func__, "BN_bin2bn failed");
528 		goto out;
529 	}
530 	if (EC_POINT_set_affine_coordinates_GFp(g, q, x, y, NULL) != 1) {
531 		skdebug(__func__, "EC_POINT_set_affine_coordinates_GFp failed");
532 		goto out;
533 	}
534 	response->public_key_len = EC_POINT_point2oct(g, q,
535 	    POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL);
536 	if (response->public_key_len == 0 || response->public_key_len > 2048) {
537 		skdebug(__func__, "bad pubkey length %zu",
538 		    response->public_key_len);
539 		goto out;
540 	}
541 	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
542 		skdebug(__func__, "malloc pubkey failed");
543 		goto out;
544 	}
545 	if (EC_POINT_point2oct(g, q, POINT_CONVERSION_UNCOMPRESSED,
546 	    response->public_key, response->public_key_len, NULL) == 0) {
547 		skdebug(__func__, "EC_POINT_point2oct failed");
548 		goto out;
549 	}
550 	/* success */
551 	ret = 0;
552  out:
553 	if (ret != 0 && response->public_key != NULL) {
554 		memset(response->public_key, 0, response->public_key_len);
555 		free(response->public_key);
556 		response->public_key = NULL;
557 	}
558 	EC_POINT_free(q);
559 	EC_GROUP_free(g);
560 	BN_clear_free(x);
561 	BN_clear_free(y);
562 	return ret;
563 }
564 #endif /* WITH_OPENSSL */
565 
566 static int
567 pack_public_key_ed25519(const fido_cred_t *cred,
568     struct sk_enroll_response *response)
569 {
570 	const uint8_t *ptr;
571 	size_t len;
572 	int ret = -1;
573 
574 	response->public_key = NULL;
575 	response->public_key_len = 0;
576 
577 	if ((len = fido_cred_pubkey_len(cred)) != 32) {
578 		skdebug(__func__, "bad fido_cred_pubkey_len len %zu", len);
579 		goto out;
580 	}
581 	if ((ptr = fido_cred_pubkey_ptr(cred)) == NULL) {
582 		skdebug(__func__, "fido_cred_pubkey_ptr failed");
583 		goto out;
584 	}
585 	response->public_key_len = len;
586 	if ((response->public_key = malloc(response->public_key_len)) == NULL) {
587 		skdebug(__func__, "malloc pubkey failed");
588 		goto out;
589 	}
590 	memcpy(response->public_key, ptr, len);
591 	ret = 0;
592  out:
593 	if (ret != 0)
594 		free(response->public_key);
595 	return ret;
596 }
597 
598 static int
599 pack_public_key(uint32_t alg, const fido_cred_t *cred,
600     struct sk_enroll_response *response)
601 {
602 	switch(alg) {
603 #ifdef WITH_OPENSSL
604 	case SSH_SK_ECDSA:
605 		return pack_public_key_ecdsa(cred, response);
606 #endif /* WITH_OPENSSL */
607 	case SSH_SK_ED25519:
608 		return pack_public_key_ed25519(cred, response);
609 	default:
610 		return -1;
611 	}
612 }
613 
614 static int
615 fidoerr_to_skerr(int fidoerr)
616 {
617 	switch (fidoerr) {
618 	case FIDO_ERR_UNSUPPORTED_OPTION:
619 	case FIDO_ERR_UNSUPPORTED_ALGORITHM:
620 		return SSH_SK_ERR_UNSUPPORTED;
621 	case FIDO_ERR_PIN_REQUIRED:
622 	case FIDO_ERR_PIN_INVALID:
623 		return SSH_SK_ERR_PIN_REQUIRED;
624 	default:
625 		return -1;
626 	}
627 }
628 
629 static int
630 check_enroll_options(struct sk_option **options, char **devicep,
631     uint8_t *user_id, size_t user_id_len)
632 {
633 	size_t i;
634 
635 	if (options == NULL)
636 		return 0;
637 	for (i = 0; options[i] != NULL; i++) {
638 		if (strcmp(options[i]->name, "device") == 0) {
639 			if ((*devicep = strdup(options[i]->value)) == NULL) {
640 				skdebug(__func__, "strdup device failed");
641 				return -1;
642 			}
643 			skdebug(__func__, "requested device %s", *devicep);
644 		} else if (strcmp(options[i]->name, "user") == 0) {
645 			if (strlcpy(user_id, options[i]->value, user_id_len) >=
646 			    user_id_len) {
647 				skdebug(__func__, "user too long");
648 				return -1;
649 			}
650 			skdebug(__func__, "requested user %s",
651 			    (char *)user_id);
652 		} else {
653 			skdebug(__func__, "requested unsupported option %s",
654 			    options[i]->name);
655 			if (options[i]->required) {
656 				skdebug(__func__, "unknown required option");
657 				return -1;
658 			}
659 		}
660 	}
661 	return 0;
662 }
663 
664 int
665 sk_enroll(uint32_t alg, const uint8_t *challenge, size_t challenge_len,
666     const char *application, uint8_t flags, const char *pin,
667     struct sk_option **options, struct sk_enroll_response **enroll_response)
668 {
669 	fido_cred_t *cred = NULL;
670 	const uint8_t *ptr;
671 	uint8_t user_id[32];
672 	struct sk_usbhid *sk = NULL;
673 	struct sk_enroll_response *response = NULL;
674 	size_t len;
675 	int credprot;
676 	int cose_alg;
677 	int ret = SSH_SK_ERR_GENERAL;
678 	int r;
679 	char *device = NULL;
680 
681 	fido_init(SSH_FIDO_INIT_ARG);
682 
683 	if (enroll_response == NULL) {
684 		skdebug(__func__, "enroll_response == NULL");
685 		goto out;
686 	}
687 	*enroll_response = NULL;
688 	memset(user_id, 0, sizeof(user_id));
689 	if (check_enroll_options(options, &device, user_id,
690 	    sizeof(user_id)) != 0)
691 		goto out; /* error already logged */
692 
693 	switch(alg) {
694 #ifdef WITH_OPENSSL
695 	case SSH_SK_ECDSA:
696 		cose_alg = COSE_ES256;
697 		break;
698 #endif /* WITH_OPENSSL */
699 	case SSH_SK_ED25519:
700 		cose_alg = COSE_EDDSA;
701 		break;
702 	default:
703 		skdebug(__func__, "unsupported key type %d", alg);
704 		goto out;
705 	}
706 	if (device != NULL)
707 		sk = sk_open(device);
708 	else
709 		sk = sk_probe(NULL, NULL, 0);
710 	if (sk == NULL) {
711 		skdebug(__func__, "failed to find sk");
712 		goto out;
713 	}
714 	skdebug(__func__, "using device %s", sk->path);
715 	if ((cred = fido_cred_new()) == NULL) {
716 		skdebug(__func__, "fido_cred_new failed");
717 		goto out;
718 	}
719 	if ((r = fido_cred_set_type(cred, cose_alg)) != FIDO_OK) {
720 		skdebug(__func__, "fido_cred_set_type: %s", fido_strerr(r));
721 		goto out;
722 	}
723 	if ((r = fido_cred_set_clientdata_hash(cred, challenge,
724 	    challenge_len)) != FIDO_OK) {
725 		skdebug(__func__, "fido_cred_set_clientdata_hash: %s",
726 		    fido_strerr(r));
727 		goto out;
728 	}
729 	if ((r = fido_cred_set_rk(cred, (flags & SSH_SK_RESIDENT_KEY) != 0 ?
730 	    FIDO_OPT_TRUE : FIDO_OPT_OMIT)) != FIDO_OK) {
731 		skdebug(__func__, "fido_cred_set_rk: %s", fido_strerr(r));
732 		goto out;
733 	}
734 	if ((r = fido_cred_set_user(cred, user_id, sizeof(user_id),
735 	    "openssh", "openssh", NULL)) != FIDO_OK) {
736 		skdebug(__func__, "fido_cred_set_user: %s", fido_strerr(r));
737 		goto out;
738 	}
739 	if ((r = fido_cred_set_rp(cred, application, NULL)) != FIDO_OK) {
740 		skdebug(__func__, "fido_cred_set_rp: %s", fido_strerr(r));
741 		goto out;
742 	}
743 	if ((flags & (SSH_SK_RESIDENT_KEY|SSH_SK_USER_VERIFICATION_REQD)) != 0) {
744 #if !defined(HAVE_FIDO_DEV_SUPPORTS_CRED_PROT) || \
745     !defined(HAVE_FIDO_CRED_SET_PROT)
746 		skdebug(__func__, "libfido2 version does not support a feature required for this operation. Please upgrade to >=1.5.0");
747 		ret = SSH_SK_ERR_UNSUPPORTED;
748 		goto out;
749 		credprot = 0; (void)credprot; /* avoid warning */
750 #endif
751 		if (!fido_dev_supports_cred_prot(sk->dev)) {
752 			skdebug(__func__, "%s does not support credprot, "
753 			    "refusing to create unprotected "
754 			    "resident/verify-required key", sk->path);
755 			ret = SSH_SK_ERR_UNSUPPORTED;
756 			goto out;
757 		}
758 		if ((flags & SSH_SK_USER_VERIFICATION_REQD))
759 			credprot = FIDO_CRED_PROT_UV_REQUIRED;
760 		else
761 			credprot = FIDO_CRED_PROT_UV_OPTIONAL_WITH_ID;
762 
763 		if ((r = fido_cred_set_prot(cred, credprot)) != FIDO_OK) {
764 			skdebug(__func__, "fido_cred_set_prot: %s",
765 			    fido_strerr(r));
766 			ret = fidoerr_to_skerr(r);
767 			goto out;
768 		}
769 	}
770 	if ((r = fido_dev_make_cred(sk->dev, cred, pin)) != FIDO_OK) {
771 		skdebug(__func__, "fido_dev_make_cred: %s", fido_strerr(r));
772 		ret = fidoerr_to_skerr(r);
773 		goto out;
774 	}
775 	if (fido_cred_x5c_ptr(cred) != NULL) {
776 		if ((r = fido_cred_verify(cred)) != FIDO_OK) {
777 			skdebug(__func__, "fido_cred_verify: %s",
778 			    fido_strerr(r));
779 			goto out;
780 		}
781 	} else {
782 		skdebug(__func__, "self-attested credential");
783 		if ((r = fido_cred_verify_self(cred)) != FIDO_OK) {
784 			skdebug(__func__, "fido_cred_verify_self: %s",
785 			    fido_strerr(r));
786 			goto out;
787 		}
788 	}
789 	if ((response = calloc(1, sizeof(*response))) == NULL) {
790 		skdebug(__func__, "calloc response failed");
791 		goto out;
792 	}
793 	if (pack_public_key(alg, cred, response) != 0) {
794 		skdebug(__func__, "pack_public_key failed");
795 		goto out;
796 	}
797 	if ((ptr = fido_cred_id_ptr(cred)) != NULL) {
798 		len = fido_cred_id_len(cred);
799 		if ((response->key_handle = calloc(1, len)) == NULL) {
800 			skdebug(__func__, "calloc key handle failed");
801 			goto out;
802 		}
803 		memcpy(response->key_handle, ptr, len);
804 		response->key_handle_len = len;
805 	}
806 	if ((ptr = fido_cred_sig_ptr(cred)) != NULL) {
807 		len = fido_cred_sig_len(cred);
808 		if ((response->signature = calloc(1, len)) == NULL) {
809 			skdebug(__func__, "calloc signature failed");
810 			goto out;
811 		}
812 		memcpy(response->signature, ptr, len);
813 		response->signature_len = len;
814 	}
815 	if ((ptr = fido_cred_x5c_ptr(cred)) != NULL) {
816 		len = fido_cred_x5c_len(cred);
817 		debug3("%s: attestation cert len=%zu", __func__, len);
818 		if ((response->attestation_cert = calloc(1, len)) == NULL) {
819 			skdebug(__func__, "calloc attestation cert failed");
820 			goto out;
821 		}
822 		memcpy(response->attestation_cert, ptr, len);
823 		response->attestation_cert_len = len;
824 	}
825 	*enroll_response = response;
826 	response = NULL;
827 	ret = 0;
828  out:
829 	free(device);
830 	if (response != NULL) {
831 		free(response->public_key);
832 		free(response->key_handle);
833 		free(response->signature);
834 		free(response->attestation_cert);
835 		free(response);
836 	}
837 	sk_close(sk);
838 	fido_cred_free(&cred);
839 	return ret;
840 }
841 
842 #ifdef WITH_OPENSSL
843 static int
844 pack_sig_ecdsa(fido_assert_t *assert, struct sk_sign_response *response)
845 {
846 	ECDSA_SIG *sig = NULL;
847 	const BIGNUM *sig_r, *sig_s;
848 	const unsigned char *cp;
849 	size_t sig_len;
850 	int ret = -1;
851 
852 	cp = fido_assert_sig_ptr(assert, 0);
853 	sig_len = fido_assert_sig_len(assert, 0);
854 	if ((sig = d2i_ECDSA_SIG(NULL, &cp, sig_len)) == NULL) {
855 		skdebug(__func__, "d2i_ECDSA_SIG failed");
856 		goto out;
857 	}
858 	ECDSA_SIG_get0(sig, &sig_r, &sig_s);
859 	response->sig_r_len = BN_num_bytes(sig_r);
860 	response->sig_s_len = BN_num_bytes(sig_s);
861 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL ||
862 	    (response->sig_s = calloc(1, response->sig_s_len)) == NULL) {
863 		skdebug(__func__, "calloc signature failed");
864 		goto out;
865 	}
866 	BN_bn2bin(sig_r, response->sig_r);
867 	BN_bn2bin(sig_s, response->sig_s);
868 	ret = 0;
869  out:
870 	ECDSA_SIG_free(sig);
871 	if (ret != 0) {
872 		free(response->sig_r);
873 		free(response->sig_s);
874 		response->sig_r = NULL;
875 		response->sig_s = NULL;
876 	}
877 	return ret;
878 }
879 #endif /* WITH_OPENSSL */
880 
881 static int
882 pack_sig_ed25519(fido_assert_t *assert, struct sk_sign_response *response)
883 {
884 	const unsigned char *ptr;
885 	size_t len;
886 	int ret = -1;
887 
888 	ptr = fido_assert_sig_ptr(assert, 0);
889 	len = fido_assert_sig_len(assert, 0);
890 	if (len != 64) {
891 		skdebug(__func__, "bad length %zu", len);
892 		goto out;
893 	}
894 	response->sig_r_len = len;
895 	if ((response->sig_r = calloc(1, response->sig_r_len)) == NULL) {
896 		skdebug(__func__, "calloc signature failed");
897 		goto out;
898 	}
899 	memcpy(response->sig_r, ptr, len);
900 	ret = 0;
901  out:
902 	if (ret != 0) {
903 		free(response->sig_r);
904 		response->sig_r = NULL;
905 	}
906 	return ret;
907 }
908 
909 static int
910 pack_sig(uint32_t  alg, fido_assert_t *assert,
911     struct sk_sign_response *response)
912 {
913 	switch(alg) {
914 #ifdef WITH_OPENSSL
915 	case SSH_SK_ECDSA:
916 		return pack_sig_ecdsa(assert, response);
917 #endif /* WITH_OPENSSL */
918 	case SSH_SK_ED25519:
919 		return pack_sig_ed25519(assert, response);
920 	default:
921 		return -1;
922 	}
923 }
924 
925 /* Checks sk_options for sk_sign() and sk_load_resident_keys() */
926 static int
927 check_sign_load_resident_options(struct sk_option **options, char **devicep)
928 {
929 	size_t i;
930 
931 	if (options == NULL)
932 		return 0;
933 	for (i = 0; options[i] != NULL; i++) {
934 		if (strcmp(options[i]->name, "device") == 0) {
935 			if ((*devicep = strdup(options[i]->value)) == NULL) {
936 				skdebug(__func__, "strdup device failed");
937 				return -1;
938 			}
939 			skdebug(__func__, "requested device %s", *devicep);
940 		} else {
941 			skdebug(__func__, "requested unsupported option %s",
942 			    options[i]->name);
943 			if (options[i]->required) {
944 				skdebug(__func__, "unknown required option");
945 				return -1;
946 			}
947 		}
948 	}
949 	return 0;
950 }
951 
952 int
953 sk_sign(uint32_t alg, const uint8_t *data, size_t datalen,
954     const char *application,
955     const uint8_t *key_handle, size_t key_handle_len,
956     uint8_t flags, const char *pin, struct sk_option **options,
957     struct sk_sign_response **sign_response)
958 {
959 	fido_assert_t *assert = NULL;
960 	char *device = NULL;
961 	struct sk_usbhid *sk = NULL;
962 	struct sk_sign_response *response = NULL;
963 	uint8_t message[32];
964 	int ret = SSH_SK_ERR_GENERAL;
965 	int r;
966 
967 	fido_init(SSH_FIDO_INIT_ARG);
968 
969 	if (sign_response == NULL) {
970 		skdebug(__func__, "sign_response == NULL");
971 		goto out;
972 	}
973 	*sign_response = NULL;
974 	if (check_sign_load_resident_options(options, &device) != 0)
975 		goto out; /* error already logged */
976 	/* hash data to be signed before it goes to the security key */
977 	if ((r = sha256_mem(data, datalen, message, sizeof(message))) != 0) {
978 		skdebug(__func__, "hash message failed");
979 		goto out;
980 	}
981 	if (device != NULL)
982 		sk = sk_open(device);
983 	else if (pin != NULL || (flags & SSH_SK_USER_VERIFICATION_REQD))
984 		sk = sk_probe(NULL, NULL, 0);
985 	else
986 		sk = sk_probe(application, key_handle, key_handle_len);
987 	if (sk == NULL) {
988 		skdebug(__func__, "failed to find sk");
989 		goto out;
990 	}
991 	if ((assert = fido_assert_new()) == NULL) {
992 		skdebug(__func__, "fido_assert_new failed");
993 		goto out;
994 	}
995 	if ((r = fido_assert_set_clientdata_hash(assert, message,
996 	    sizeof(message))) != FIDO_OK) {
997 		skdebug(__func__, "fido_assert_set_clientdata_hash: %s",
998 		    fido_strerr(r));
999 		goto out;
1000 	}
1001 	if ((r = fido_assert_set_rp(assert, application)) != FIDO_OK) {
1002 		skdebug(__func__, "fido_assert_set_rp: %s", fido_strerr(r));
1003 		goto out;
1004 	}
1005 	if ((r = fido_assert_allow_cred(assert, key_handle,
1006 	    key_handle_len)) != FIDO_OK) {
1007 		skdebug(__func__, "fido_assert_allow_cred: %s", fido_strerr(r));
1008 		goto out;
1009 	}
1010 	if ((r = fido_assert_set_up(assert,
1011 	    (flags & SSH_SK_USER_PRESENCE_REQD) ?
1012 	    FIDO_OPT_TRUE : FIDO_OPT_FALSE)) != FIDO_OK) {
1013 		skdebug(__func__, "fido_assert_set_up: %s", fido_strerr(r));
1014 		goto out;
1015 	}
1016 	if (pin == NULL && (flags & SSH_SK_USER_VERIFICATION_REQD) &&
1017 	    (r = fido_assert_set_uv(assert, FIDO_OPT_TRUE)) != FIDO_OK) {
1018 		skdebug(__func__, "fido_assert_set_uv: %s", fido_strerr(r));
1019 		ret = FIDO_ERR_PIN_REQUIRED;
1020 		goto out;
1021 	}
1022 	if ((r = fido_dev_get_assert(sk->dev, assert, pin)) != FIDO_OK) {
1023 		skdebug(__func__, "fido_dev_get_assert: %s", fido_strerr(r));
1024 		ret = fidoerr_to_skerr(r);
1025 		goto out;
1026 	}
1027 	if ((response = calloc(1, sizeof(*response))) == NULL) {
1028 		skdebug(__func__, "calloc response failed");
1029 		goto out;
1030 	}
1031 	response->flags = fido_assert_flags(assert, 0);
1032 	response->counter = fido_assert_sigcount(assert, 0);
1033 	if (pack_sig(alg, assert, response) != 0) {
1034 		skdebug(__func__, "pack_sig failed");
1035 		goto out;
1036 	}
1037 	*sign_response = response;
1038 	response = NULL;
1039 	ret = 0;
1040  out:
1041 	explicit_bzero(message, sizeof(message));
1042 	free(device);
1043 	if (response != NULL) {
1044 		free(response->sig_r);
1045 		free(response->sig_s);
1046 		free(response);
1047 	}
1048 	sk_close(sk);
1049 	fido_assert_free(&assert);
1050 	return ret;
1051 }
1052 
1053 static int
1054 read_rks(struct sk_usbhid *sk, const char *pin,
1055     struct sk_resident_key ***rksp, size_t *nrksp)
1056 {
1057 	int ret = SSH_SK_ERR_GENERAL, r = -1;
1058 	fido_credman_metadata_t *metadata = NULL;
1059 	fido_credman_rp_t *rp = NULL;
1060 	fido_credman_rk_t *rk = NULL;
1061 	size_t i, j, nrp, nrk;
1062 	const fido_cred_t *cred;
1063 	struct sk_resident_key *srk = NULL, **tmp;
1064 
1065 	if (pin == NULL) {
1066 		skdebug(__func__, "no PIN specified");
1067 		ret = SSH_SK_ERR_PIN_REQUIRED;
1068 		goto out;
1069 	}
1070 	if ((metadata = fido_credman_metadata_new()) == NULL) {
1071 		skdebug(__func__, "alloc failed");
1072 		goto out;
1073 	}
1074 
1075 	if ((r = fido_credman_get_dev_metadata(sk->dev, metadata, pin)) != 0) {
1076 		if (r == FIDO_ERR_INVALID_COMMAND) {
1077 			skdebug(__func__, "device %s does not support "
1078 			    "resident keys", sk->path);
1079 			ret = 0;
1080 			goto out;
1081 		}
1082 		skdebug(__func__, "get metadata for %s failed: %s",
1083 		    sk->path, fido_strerr(r));
1084 		ret = fidoerr_to_skerr(r);
1085 		goto out;
1086 	}
1087 	skdebug(__func__, "existing %llu, remaining %llu",
1088 	    (unsigned long long)fido_credman_rk_existing(metadata),
1089 	    (unsigned long long)fido_credman_rk_remaining(metadata));
1090 	if ((rp = fido_credman_rp_new()) == NULL) {
1091 		skdebug(__func__, "alloc rp failed");
1092 		goto out;
1093 	}
1094 	if ((r = fido_credman_get_dev_rp(sk->dev, rp, pin)) != 0) {
1095 		skdebug(__func__, "get RPs for %s failed: %s",
1096 		    sk->path, fido_strerr(r));
1097 		goto out;
1098 	}
1099 	nrp = fido_credman_rp_count(rp);
1100 	skdebug(__func__, "Device %s has resident keys for %zu RPs",
1101 	    sk->path, nrp);
1102 
1103 	/* Iterate over RP IDs that have resident keys */
1104 	for (i = 0; i < nrp; i++) {
1105 		skdebug(__func__, "rp %zu: name=\"%s\" id=\"%s\" hashlen=%zu",
1106 		    i, fido_credman_rp_name(rp, i), fido_credman_rp_id(rp, i),
1107 		    fido_credman_rp_id_hash_len(rp, i));
1108 
1109 		/* Skip non-SSH RP IDs */
1110 		if (strncasecmp(fido_credman_rp_id(rp, i), "ssh:", 4) != 0)
1111 			continue;
1112 
1113 		fido_credman_rk_free(&rk);
1114 		if ((rk = fido_credman_rk_new()) == NULL) {
1115 			skdebug(__func__, "alloc rk failed");
1116 			goto out;
1117 		}
1118 		if ((r = fido_credman_get_dev_rk(sk->dev,
1119 		    fido_credman_rp_id(rp, i), rk, pin)) != 0) {
1120 			skdebug(__func__, "get RKs for %s slot %zu failed: %s",
1121 			    sk->path, i, fido_strerr(r));
1122 			goto out;
1123 		}
1124 		nrk = fido_credman_rk_count(rk);
1125 		skdebug(__func__, "RP \"%s\" has %zu resident keys",
1126 		    fido_credman_rp_id(rp, i), nrk);
1127 
1128 		/* Iterate over resident keys for this RP ID */
1129 		for (j = 0; j < nrk; j++) {
1130 			if ((cred = fido_credman_rk(rk, j)) == NULL) {
1131 				skdebug(__func__, "no RK in slot %zu", j);
1132 				continue;
1133 			}
1134 			skdebug(__func__, "Device %s RP \"%s\" slot %zu: "
1135 			    "type %d flags 0x%02x prot 0x%02x", sk->path,
1136 			    fido_credman_rp_id(rp, i), j, fido_cred_type(cred),
1137 			    fido_cred_flags(cred), fido_cred_prot(cred));
1138 
1139 			/* build response entry */
1140 			if ((srk = calloc(1, sizeof(*srk))) == NULL ||
1141 			    (srk->key.key_handle = calloc(1,
1142 			    fido_cred_id_len(cred))) == NULL ||
1143 			    (srk->application = strdup(fido_credman_rp_id(rp,
1144 			    i))) == NULL) {
1145 				skdebug(__func__, "alloc sk_resident_key");
1146 				goto out;
1147 			}
1148 
1149 			srk->key.key_handle_len = fido_cred_id_len(cred);
1150 			memcpy(srk->key.key_handle, fido_cred_id_ptr(cred),
1151 			    srk->key.key_handle_len);
1152 
1153 			switch (fido_cred_type(cred)) {
1154 			case COSE_ES256:
1155 				srk->alg = SSH_SK_ECDSA;
1156 				break;
1157 			case COSE_EDDSA:
1158 				srk->alg = SSH_SK_ED25519;
1159 				break;
1160 			default:
1161 				skdebug(__func__, "unsupported key type %d",
1162 				    fido_cred_type(cred));
1163 				goto out; /* XXX free rk and continue */
1164 			}
1165 
1166 			if (fido_cred_prot(cred) == FIDO_CRED_PROT_UV_REQUIRED)
1167 				srk->flags |=  SSH_SK_USER_VERIFICATION_REQD;
1168 
1169 			if ((r = pack_public_key(srk->alg, cred,
1170 			    &srk->key)) != 0) {
1171 				skdebug(__func__, "pack public key failed");
1172 				goto out;
1173 			}
1174 			/* append */
1175 			if ((tmp = recallocarray(*rksp, *nrksp, (*nrksp) + 1,
1176 			    sizeof(**rksp))) == NULL) {
1177 				skdebug(__func__, "alloc rksp");
1178 				goto out;
1179 			}
1180 			*rksp = tmp;
1181 			(*rksp)[(*nrksp)++] = srk;
1182 			srk = NULL;
1183 		}
1184 	}
1185 	/* Success */
1186 	ret = 0;
1187  out:
1188 	if (srk != NULL) {
1189 		free(srk->application);
1190 		freezero(srk->key.public_key, srk->key.public_key_len);
1191 		freezero(srk->key.key_handle, srk->key.key_handle_len);
1192 		freezero(srk, sizeof(*srk));
1193 	}
1194 	fido_credman_rp_free(&rp);
1195 	fido_credman_rk_free(&rk);
1196 	fido_credman_metadata_free(&metadata);
1197 	return ret;
1198 }
1199 
1200 int
1201 sk_load_resident_keys(const char *pin, struct sk_option **options,
1202     struct sk_resident_key ***rksp, size_t *nrksp)
1203 {
1204 	int ret = SSH_SK_ERR_GENERAL, r = -1;
1205 	size_t i, nrks = 0;
1206 	struct sk_resident_key **rks = NULL;
1207 	struct sk_usbhid *sk = NULL;
1208 	char *device = NULL;
1209 
1210 	*rksp = NULL;
1211 	*nrksp = 0;
1212 
1213 	fido_init(SSH_FIDO_INIT_ARG);
1214 
1215 	if (check_sign_load_resident_options(options, &device) != 0)
1216 		goto out; /* error already logged */
1217 	if (device != NULL)
1218 		sk = sk_open(device);
1219 	else
1220 		sk = sk_probe(NULL, NULL, 0);
1221 	if (sk == NULL) {
1222 		skdebug(__func__, "failed to find sk");
1223 		goto out;
1224 	}
1225 	skdebug(__func__, "trying %s", sk->path);
1226 	if ((r = read_rks(sk, pin, &rks, &nrks)) != 0) {
1227 		skdebug(__func__, "read_rks failed for %s", sk->path);
1228 		ret = r;
1229 		goto out;
1230 	}
1231 	/* success, unless we have no keys but a specific error */
1232 	if (nrks > 0 || ret == SSH_SK_ERR_GENERAL)
1233 		ret = 0;
1234 	*rksp = rks;
1235 	*nrksp = nrks;
1236 	rks = NULL;
1237 	nrks = 0;
1238  out:
1239 	sk_close(sk);
1240 	for (i = 0; i < nrks; i++) {
1241 		free(rks[i]->application);
1242 		freezero(rks[i]->key.public_key, rks[i]->key.public_key_len);
1243 		freezero(rks[i]->key.key_handle, rks[i]->key.key_handle_len);
1244 		freezero(rks[i], sizeof(*rks[i]));
1245 	}
1246 	free(rks);
1247 	return ret;
1248 }
1249 
1250 #endif /* ENABLE_SK_INTERNAL */
1251