Annotation of src/usr.bin/ssh/ssh-sk.c, Revision 1.19
1.19 ! djm 1: /* $OpenBSD: ssh-sk.c,v 1.18 2019/12/13 19:09:10 djm 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:
1.15 naddy 26: #ifdef WITH_OPENSSL
1.1 djm 27: #include <openssl/objects.h>
28: #include <openssl/ec.h>
1.15 naddy 29: #endif /* WITH_OPENSSL */
1.1 djm 30:
31: #include "log.h"
32: #include "misc.h"
33: #include "sshbuf.h"
34: #include "sshkey.h"
35: #include "ssherr.h"
36: #include "digest.h"
37:
38: #include "ssh-sk.h"
39: #include "sk-api.h"
1.6 markus 40: #include "crypto_api.h"
1.1 djm 41:
42: struct sshsk_provider {
43: char *path;
44: void *dlhandle;
45:
46: /* Return the version of the middleware API */
47: uint32_t (*sk_api_version)(void);
48:
49: /* Enroll a U2F key (private key generation) */
1.7 markus 50: int (*sk_enroll)(int alg, const uint8_t *challenge,
51: size_t challenge_len, const char *application, uint8_t flags,
1.1 djm 52: struct sk_enroll_response **enroll_response);
53:
54: /* Sign a challenge */
1.7 markus 55: int (*sk_sign)(int alg, const uint8_t *message, size_t message_len,
1.1 djm 56: const char *application,
57: const uint8_t *key_handle, size_t key_handle_len,
58: uint8_t flags, struct sk_sign_response **sign_response);
59: };
60:
1.12 djm 61: /* Built-in version */
62: int ssh_sk_enroll(int alg, const uint8_t *challenge,
63: size_t challenge_len, const char *application, uint8_t flags,
64: struct sk_enroll_response **enroll_response);
65: int ssh_sk_sign(int alg, const uint8_t *message, size_t message_len,
66: const char *application,
67: const uint8_t *key_handle, size_t key_handle_len,
68: uint8_t flags, struct sk_sign_response **sign_response);
69:
1.1 djm 70: static void
71: sshsk_free(struct sshsk_provider *p)
72: {
73: if (p == NULL)
74: return;
75: free(p->path);
76: if (p->dlhandle != NULL)
77: dlclose(p->dlhandle);
78: free(p);
79: }
80:
81: static struct sshsk_provider *
82: sshsk_open(const char *path)
83: {
84: struct sshsk_provider *ret = NULL;
85: uint32_t version;
86:
87: if ((ret = calloc(1, sizeof(*ret))) == NULL) {
88: error("%s: calloc failed", __func__);
89: return NULL;
90: }
91: if ((ret->path = strdup(path)) == NULL) {
92: error("%s: strdup failed", __func__);
93: goto fail;
1.12 djm 94: }
95: /* Skip the rest if we're using the linked in middleware */
96: if (strcasecmp(ret->path, "internal") == 0) {
97: ret->sk_enroll = ssh_sk_enroll;
98: ret->sk_sign = ssh_sk_sign;
99: return ret;
1.1 djm 100: }
101: if ((ret->dlhandle = dlopen(path, RTLD_NOW)) == NULL) {
102: error("Security key provider %s dlopen failed: %s",
103: path, dlerror());
104: goto fail;
105: }
106: if ((ret->sk_api_version = dlsym(ret->dlhandle,
107: "sk_api_version")) == NULL) {
108: error("Security key provider %s dlsym(sk_api_version) "
109: "failed: %s", path, dlerror());
110: goto fail;
111: }
112: version = ret->sk_api_version();
113: debug("%s: provider %s implements version 0x%08lx", __func__,
114: ret->path, (u_long)version);
115: if ((version & SSH_SK_VERSION_MAJOR_MASK) != SSH_SK_VERSION_MAJOR) {
116: error("Security key provider %s implements unsupported version "
117: "0x%08lx (supported: 0x%08lx)", path, (u_long)version,
118: (u_long)SSH_SK_VERSION_MAJOR);
119: goto fail;
120: }
121: if ((ret->sk_enroll = dlsym(ret->dlhandle, "sk_enroll")) == NULL) {
122: error("Security key provider %s dlsym(sk_enroll) "
123: "failed: %s", path, dlerror());
124: goto fail;
125: }
126: if ((ret->sk_sign = dlsym(ret->dlhandle, "sk_sign")) == NULL) {
127: error("Security key provider %s dlsym(sk_sign) failed: %s",
128: path, dlerror());
129: goto fail;
130: }
131: /* success */
132: return ret;
133: fail:
134: sshsk_free(ret);
135: return NULL;
136: }
137:
138: static void
139: sshsk_free_enroll_response(struct sk_enroll_response *r)
140: {
141: if (r == NULL)
142: return;
143: freezero(r->key_handle, r->key_handle_len);
144: freezero(r->public_key, r->public_key_len);
145: freezero(r->signature, r->signature_len);
146: freezero(r->attestation_cert, r->attestation_cert_len);
147: freezero(r, sizeof(*r));
1.17 djm 148: }
1.1 djm 149:
150: static void
151: sshsk_free_sign_response(struct sk_sign_response *r)
152: {
153: if (r == NULL)
154: return;
155: freezero(r->sig_r, r->sig_r_len);
156: freezero(r->sig_s, r->sig_s_len);
157: freezero(r, sizeof(*r));
1.17 djm 158: }
1.1 djm 159:
1.15 naddy 160: #ifdef WITH_OPENSSL
1.2 markus 161: /* Assemble key from response */
162: static int
163: sshsk_ecdsa_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
164: {
165: struct sshkey *key = NULL;
166: struct sshbuf *b = NULL;
167: EC_POINT *q = NULL;
168: int r;
169:
170: *keyp = NULL;
171: if ((key = sshkey_new(KEY_ECDSA_SK)) == NULL) {
172: error("%s: sshkey_new failed", __func__);
173: r = SSH_ERR_ALLOC_FAIL;
174: goto out;
175: }
176: key->ecdsa_nid = NID_X9_62_prime256v1;
177: if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid)) == NULL ||
178: (q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL ||
179: (b = sshbuf_new()) == NULL) {
180: error("%s: allocation failed", __func__);
181: r = SSH_ERR_ALLOC_FAIL;
182: goto out;
183: }
184: if ((r = sshbuf_put_string(b,
185: resp->public_key, resp->public_key_len)) != 0) {
186: error("%s: buffer error: %s", __func__, ssh_err(r));
187: goto out;
188: }
189: if ((r = sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa))) != 0) {
190: error("%s: parse key: %s", __func__, ssh_err(r));
191: r = SSH_ERR_INVALID_FORMAT;
192: goto out;
193: }
194: if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa), q) != 0) {
195: error("Security key returned invalid ECDSA key");
196: r = SSH_ERR_KEY_INVALID_EC_VALUE;
197: goto out;
198: }
199: if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
200: /* XXX assume it is a allocation error */
201: error("%s: allocation failed", __func__);
202: r = SSH_ERR_ALLOC_FAIL;
203: goto out;
204: }
205: /* success */
206: *keyp = key;
207: key = NULL; /* transferred */
208: r = 0;
209: out:
210: EC_POINT_free(q);
211: sshkey_free(key);
212: sshbuf_free(b);
213: return r;
214: }
1.15 naddy 215: #endif /* WITH_OPENSSL */
1.2 markus 216:
1.6 markus 217: static int
218: sshsk_ed25519_assemble(struct sk_enroll_response *resp, struct sshkey **keyp)
219: {
220: struct sshkey *key = NULL;
221: int r;
222:
223: *keyp = NULL;
224: if (resp->public_key_len != ED25519_PK_SZ) {
225: error("%s: invalid size: %zu", __func__, resp->public_key_len);
226: r = SSH_ERR_INVALID_FORMAT;
227: goto out;
228: }
229: if ((key = sshkey_new(KEY_ED25519_SK)) == NULL) {
230: error("%s: sshkey_new failed", __func__);
231: r = SSH_ERR_ALLOC_FAIL;
232: goto out;
233: }
234: if ((key->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
235: error("%s: malloc failed", __func__);
236: r = SSH_ERR_ALLOC_FAIL;
237: goto out;
238: }
239: memcpy(key->ed25519_pk, resp->public_key, ED25519_PK_SZ);
240: /* success */
241: *keyp = key;
242: key = NULL; /* transferred */
243: r = 0;
244: out:
245: sshkey_free(key);
246: return r;
247: }
248:
1.19 ! djm 249: static int
! 250: sshsk_key_from_response(int alg, const char *application, uint8_t flags,
! 251: struct sk_enroll_response *resp, struct sshkey **keyp)
! 252: {
! 253: struct sshkey *key = NULL;
! 254: int r = SSH_ERR_INTERNAL_ERROR;
! 255:
! 256: *keyp = NULL;
! 257:
! 258: /* Check response validity */
! 259: if (resp->public_key == NULL || resp->key_handle == NULL ||
! 260: resp->signature == NULL ||
! 261: (resp->attestation_cert == NULL && resp->attestation_cert_len != 0)) {
! 262: error("%s: sk_enroll response invalid", __func__);
! 263: r = SSH_ERR_INVALID_FORMAT;
! 264: goto out;
! 265: }
! 266: switch (alg) {
! 267: #ifdef WITH_OPENSSL
! 268: case SSH_SK_ECDSA:
! 269: if ((r = sshsk_ecdsa_assemble(resp, &key)) != 0)
! 270: goto out;
! 271: break;
! 272: #endif /* WITH_OPENSSL */
! 273: case SSH_SK_ED25519:
! 274: if ((r = sshsk_ed25519_assemble(resp, &key)) != 0)
! 275: goto out;
! 276: break;
! 277: default:
! 278: error("%s: unsupported algorithm %d", __func__, alg);
! 279: r = SSH_ERR_INVALID_ARGUMENT;
! 280: goto out;
! 281: }
! 282: key->sk_flags = flags;
! 283: if ((key->sk_key_handle = sshbuf_new()) == NULL ||
! 284: (key->sk_reserved = sshbuf_new()) == NULL) {
! 285: error("%s: allocation failed", __func__);
! 286: r = SSH_ERR_ALLOC_FAIL;
! 287: goto out;
! 288: }
! 289: if ((key->sk_application = strdup(application)) == NULL) {
! 290: error("%s: strdup application failed", __func__);
! 291: r = SSH_ERR_ALLOC_FAIL;
! 292: goto out;
! 293: }
! 294: if ((r = sshbuf_put(key->sk_key_handle, resp->key_handle,
! 295: resp->key_handle_len)) != 0) {
! 296: error("%s: buffer error: %s", __func__, ssh_err(r));
! 297: goto out;
! 298: }
! 299: /* success */
! 300: r = 0;
! 301: *keyp = key;
! 302: key = NULL;
! 303: out:
! 304: sshkey_free(key);
! 305: return r;
! 306: }
! 307:
1.1 djm 308: int
1.6 markus 309: sshsk_enroll(int type, const char *provider_path, const char *application,
1.1 djm 310: uint8_t flags, struct sshbuf *challenge_buf, struct sshkey **keyp,
311: struct sshbuf *attest)
312: {
313: struct sshsk_provider *skp = NULL;
314: struct sshkey *key = NULL;
315: u_char randchall[32];
316: const u_char *challenge;
317: size_t challenge_len;
318: struct sk_enroll_response *resp = NULL;
319: int r = SSH_ERR_INTERNAL_ERROR;
1.7 markus 320: int alg;
1.1 djm 321:
1.13 djm 322: debug("%s: provider \"%s\", application \"%s\", flags 0x%02x, "
323: "challenge len %zu", __func__, provider_path, application,
324: flags, challenge_buf == NULL ? 0 : sshbuf_len(challenge_buf));
325:
1.1 djm 326: *keyp = NULL;
327: if (attest)
328: sshbuf_reset(attest);
1.6 markus 329: switch (type) {
1.15 naddy 330: #ifdef WITH_OPENSSL
1.6 markus 331: case KEY_ECDSA_SK:
1.7 markus 332: alg = SSH_SK_ECDSA;
333: break;
1.15 naddy 334: #endif /* WITH_OPENSSL */
1.6 markus 335: case KEY_ED25519_SK:
1.7 markus 336: alg = SSH_SK_ED25519;
1.6 markus 337: break;
338: default:
339: error("%s: unsupported key type", __func__);
340: r = SSH_ERR_INVALID_ARGUMENT;
341: goto out;
342: }
1.1 djm 343: if (provider_path == NULL) {
344: error("%s: missing provider", __func__);
345: r = SSH_ERR_INVALID_ARGUMENT;
346: goto out;
347: }
348: if (application == NULL || *application == '\0') {
349: error("%s: missing application", __func__);
350: r = SSH_ERR_INVALID_ARGUMENT;
351: goto out;
352: }
353: if (challenge_buf == NULL) {
354: debug("%s: using random challenge", __func__);
355: arc4random_buf(randchall, sizeof(randchall));
356: challenge = randchall;
357: challenge_len = sizeof(randchall);
358: } else if (sshbuf_len(challenge_buf) == 0) {
359: error("Missing enrollment challenge");
360: r = SSH_ERR_INVALID_ARGUMENT;
361: goto out;
362: } else {
363: challenge = sshbuf_ptr(challenge_buf);
364: challenge_len = sshbuf_len(challenge_buf);
365: debug3("%s: using explicit challenge len=%zd",
366: __func__, challenge_len);
367: }
368: if ((skp = sshsk_open(provider_path)) == NULL) {
369: r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
370: goto out;
371: }
372: /* XXX validate flags? */
373: /* enroll key */
1.7 markus 374: if ((r = skp->sk_enroll(alg, challenge, challenge_len, application,
1.1 djm 375: flags, &resp)) != 0) {
376: error("Security key provider %s returned failure %d",
377: provider_path, r);
378: r = SSH_ERR_INVALID_FORMAT; /* XXX error codes in API? */
379: goto out;
380: }
1.19 ! djm 381:
! 382: if ((r = sshsk_key_from_response(alg, application, flags,
! 383: resp, &key)) != 0)
1.1 djm 384: goto out;
1.19 ! djm 385:
1.1 djm 386: /* Optionally fill in the attestation information */
387: if (attest != NULL) {
388: if ((r = sshbuf_put_cstring(attest, "sk-attest-v00")) != 0 ||
389: (r = sshbuf_put_u32(attest, 1)) != 0 || /* XXX U2F ver */
390: (r = sshbuf_put_string(attest,
391: resp->attestation_cert, resp->attestation_cert_len)) != 0 ||
392: (r = sshbuf_put_string(attest,
393: resp->signature, resp->signature_len)) != 0 ||
394: (r = sshbuf_put_u32(attest, flags)) != 0 || /* XXX right? */
395: (r = sshbuf_put_string(attest, NULL, 0)) != 0) {
396: error("%s: buffer error: %s", __func__, ssh_err(r));
397: goto out;
398: }
399: }
400: /* success */
401: *keyp = key;
402: key = NULL; /* transferred */
403: r = 0;
404: out:
405: sshsk_free(skp);
406: sshkey_free(key);
407: sshsk_free_enroll_response(resp);
408: explicit_bzero(randchall, sizeof(randchall));
409: return r;
410: }
411:
1.15 naddy 412: #ifdef WITH_OPENSSL
1.3 markus 413: static int
1.9 markus 414: sshsk_ecdsa_sig(struct sk_sign_response *resp, struct sshbuf *sig)
1.3 markus 415: {
416: struct sshbuf *inner_sig = NULL;
417: int r = SSH_ERR_INTERNAL_ERROR;
418:
1.8 markus 419: /* Check response validity */
1.11 markus 420: if (resp->sig_r == NULL || resp->sig_s == NULL) {
1.8 markus 421: error("%s: sk_sign response invalid", __func__);
422: r = SSH_ERR_INVALID_FORMAT;
423: goto out;
424: }
1.3 markus 425: if ((inner_sig = sshbuf_new()) == NULL) {
426: r = SSH_ERR_ALLOC_FAIL;
427: goto out;
428: }
1.9 markus 429: /* Prepare and append inner signature object */
1.3 markus 430: if ((r = sshbuf_put_bignum2_bytes(inner_sig,
431: resp->sig_r, resp->sig_r_len)) != 0 ||
432: (r = sshbuf_put_bignum2_bytes(inner_sig,
1.16 djm 433: resp->sig_s, resp->sig_s_len)) != 0) {
1.3 markus 434: debug("%s: buffer error: %s", __func__, ssh_err(r));
435: goto out;
436: }
1.16 djm 437: if ((r = sshbuf_put_stringb(sig, inner_sig)) != 0 ||
438: (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
439: (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
1.9 markus 440: debug("%s: buffer error: %s", __func__, ssh_err(r));
441: goto out;
442: }
1.3 markus 443: #ifdef DEBUG_SK
444: fprintf(stderr, "%s: sig_r:\n", __func__);
445: sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
446: fprintf(stderr, "%s: sig_s:\n", __func__);
447: sshbuf_dump_data(resp->sig_s, resp->sig_s_len, stderr);
1.9 markus 448: fprintf(stderr, "%s: inner:\n", __func__);
449: sshbuf_dump(inner_sig, stderr);
1.5 markus 450: #endif
451: r = 0;
1.9 markus 452: out:
1.5 markus 453: sshbuf_free(inner_sig);
454: return r;
455: }
1.15 naddy 456: #endif /* WITH_OPENSSL */
1.5 markus 457:
458: static int
1.9 markus 459: sshsk_ed25519_sig(struct sk_sign_response *resp, struct sshbuf *sig)
1.5 markus 460: {
461: int r = SSH_ERR_INTERNAL_ERROR;
462:
1.8 markus 463: /* Check response validity */
464: if (resp->sig_r == NULL) {
465: error("%s: sk_sign response invalid", __func__);
466: r = SSH_ERR_INVALID_FORMAT;
467: goto out;
468: }
1.9 markus 469: if ((r = sshbuf_put_string(sig,
1.5 markus 470: resp->sig_r, resp->sig_r_len)) != 0 ||
1.9 markus 471: (r = sshbuf_put_u8(sig, resp->flags)) != 0 ||
472: (r = sshbuf_put_u32(sig, resp->counter)) != 0) {
1.5 markus 473: debug("%s: buffer error: %s", __func__, ssh_err(r));
474: goto out;
475: }
476: #ifdef DEBUG_SK
477: fprintf(stderr, "%s: sig_r:\n", __func__);
478: sshbuf_dump_data(resp->sig_r, resp->sig_r_len, stderr);
1.3 markus 479: #endif
480: r = 0;
1.9 markus 481: out:
482: return 0;
1.3 markus 483: }
484:
1.1 djm 485: int
1.18 djm 486: sshsk_sign(const char *provider_path, struct sshkey *key,
1.1 djm 487: u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
488: u_int compat)
489: {
490: struct sshsk_provider *skp = NULL;
491: int r = SSH_ERR_INTERNAL_ERROR;
1.7 markus 492: int type, alg;
1.1 djm 493: struct sk_sign_response *resp = NULL;
494: struct sshbuf *inner_sig = NULL, *sig = NULL;
495: uint8_t message[32];
1.13 djm 496:
1.14 djm 497: debug("%s: provider \"%s\", key %s, flags 0x%02x", __func__,
1.13 djm 498: provider_path, sshkey_type(key), key->sk_flags);
1.1 djm 499:
500: if (sigp != NULL)
501: *sigp = NULL;
502: if (lenp != NULL)
503: *lenp = 0;
1.5 markus 504: type = sshkey_type_plain(key->type);
505: switch (type) {
1.15 naddy 506: #ifdef WITH_OPENSSL
1.5 markus 507: case KEY_ECDSA_SK:
1.7 markus 508: alg = SSH_SK_ECDSA;
509: break;
1.15 naddy 510: #endif /* WITH_OPENSSL */
1.5 markus 511: case KEY_ED25519_SK:
1.7 markus 512: alg = SSH_SK_ED25519;
1.5 markus 513: break;
514: default:
515: return SSH_ERR_INVALID_ARGUMENT;
516: }
1.1 djm 517: if (provider_path == NULL ||
518: key->sk_key_handle == NULL ||
519: key->sk_application == NULL || *key->sk_application == '\0') {
520: r = SSH_ERR_INVALID_ARGUMENT;
521: goto out;
522: }
523: if ((skp = sshsk_open(provider_path)) == NULL) {
524: r = SSH_ERR_INVALID_FORMAT; /* XXX sshsk_open return code? */
525: goto out;
526: }
527:
528: /* hash data to be signed before it goes to the security key */
529: if ((r = ssh_digest_memory(SSH_DIGEST_SHA256, data, datalen,
530: message, sizeof(message))) != 0) {
531: error("%s: hash application failed: %s", __func__, ssh_err(r));
532: r = SSH_ERR_INTERNAL_ERROR;
533: goto out;
534: }
1.7 markus 535: if ((r = skp->sk_sign(alg, message, sizeof(message),
1.1 djm 536: key->sk_application,
537: sshbuf_ptr(key->sk_key_handle), sshbuf_len(key->sk_key_handle),
538: key->sk_flags, &resp)) != 0) {
539: debug("%s: sk_sign failed with code %d", __func__, r);
540: goto out;
541: }
1.9 markus 542: /* Assemble signature */
543: if ((sig = sshbuf_new()) == NULL) {
544: r = SSH_ERR_ALLOC_FAIL;
545: goto out;
546: }
547: if ((r = sshbuf_put_cstring(sig, sshkey_ssh_name_plain(key))) != 0) {
548: debug("%s: buffer error (outer): %s", __func__, ssh_err(r));
549: goto out;
550: }
1.5 markus 551: switch (type) {
1.15 naddy 552: #ifdef WITH_OPENSSL
1.5 markus 553: case KEY_ECDSA_SK:
1.9 markus 554: if ((r = sshsk_ecdsa_sig(resp, sig)) != 0)
1.5 markus 555: goto out;
556: break;
1.15 naddy 557: #endif /* WITH_OPENSSL */
1.5 markus 558: case KEY_ED25519_SK:
1.9 markus 559: if ((r = sshsk_ed25519_sig(resp, sig)) != 0)
1.5 markus 560: goto out;
561: break;
562: }
1.1 djm 563: #ifdef DEBUG_SK
1.5 markus 564: fprintf(stderr, "%s: sig_flags = 0x%02x, sig_counter = %u\n",
565: __func__, resp->flags, resp->counter);
1.1 djm 566: fprintf(stderr, "%s: hashed message:\n", __func__);
567: sshbuf_dump_data(message, sizeof(message), stderr);
568: fprintf(stderr, "%s: sigbuf:\n", __func__);
569: sshbuf_dump(sig, stderr);
570: #endif
571: if (sigp != NULL) {
572: if ((*sigp = malloc(sshbuf_len(sig))) == NULL) {
573: r = SSH_ERR_ALLOC_FAIL;
574: goto out;
575: }
576: memcpy(*sigp, sshbuf_ptr(sig), sshbuf_len(sig));
577: }
578: if (lenp != NULL)
579: *lenp = sshbuf_len(sig);
580: /* success */
581: r = 0;
582: out:
583: explicit_bzero(message, sizeof(message));
584: sshsk_free(skp);
585: sshsk_free_sign_response(resp);
586: sshbuf_free(sig);
587: sshbuf_free(inner_sig);
588: return r;
589: }