summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2014-07-02 15:28:02 +1000
committerDamien Miller <djm@mindrot.org>2014-07-02 15:28:02 +1000
commit8668706d0f52654fe64c0ca41a96113aeab8d2b8 (patch)
tree73e78e1ea3d39206e39870bbe0af17d6c430fb51
parent2cd7929250cf9e9f658d70dcd452f529ba08c942 (diff)
- djm@cvs.openbsd.org 2014/06/24 01:13:21
[Makefile.in auth-bsdauth.c auth-chall.c auth-options.c auth-rsa.c [auth2-none.c auth2-pubkey.c authfile.c authfile.h cipher-3des1.c [cipher-chachapoly.c cipher-chachapoly.h cipher.c cipher.h [digest-libc.c digest-openssl.c digest.h dns.c entropy.c hmac.h [hostfile.c key.c key.h krl.c monitor.c packet.c rsa.c rsa.h [ssh-add.c ssh-agent.c ssh-dss.c ssh-ecdsa.c ssh-ed25519.c [ssh-keygen.c ssh-pkcs11-client.c ssh-pkcs11-helper.c ssh-pkcs11.c [ssh-rsa.c sshbuf-misc.c sshbuf.h sshconnect.c sshconnect1.c [sshconnect2.c sshd.c sshkey.c sshkey.h [openbsd-compat/openssl-compat.c openbsd-compat/openssl-compat.h] New key API: refactor key-related functions to be more library-like, existing API is offered as a set of wrappers. with and ok markus@ Thanks also to Ben Hawkes, David Tomaschik, Ivan Fratric, Matthew Dempsky and Ron Bowes for a detailed review a few months ago. NB. This commit also removes portable OpenSSH support for OpenSSL <0.9.8e.
-rw-r--r--ChangeLog20
-rw-r--r--Makefile.in5
-rw-r--r--auth-bsdauth.c4
-rw-r--r--auth-chall.c5
-rw-r--r--auth-options.c14
-rw-r--r--auth-rsa.c5
-rw-r--r--auth2-none.c5
-rw-r--r--auth2-pubkey.c6
-rw-r--r--authfile.c1421
-rw-r--r--authfile.h63
-rw-r--r--cipher-3des1.c55
-rw-r--r--cipher-chachapoly.c25
-rw-r--r--cipher-chachapoly.h4
-rw-r--r--cipher.c363
-rw-r--r--cipher.h57
-rw-r--r--digest-libc.c27
-rw-r--r--digest-openssl.c36
-rw-r--r--digest.h7
-rw-r--r--dns.c4
-rw-r--r--entropy.c2
-rw-r--r--hmac.h5
-rw-r--r--hostfile.c3
-rw-r--r--key.c2803
-rw-r--r--key.h187
-rw-r--r--krl.c8
-rw-r--r--monitor.c5
-rw-r--r--openbsd-compat/openssl-compat.c141
-rw-r--r--openbsd-compat/openssl-compat.h118
-rw-r--r--packet.c38
-rw-r--r--rsa.c113
-rw-r--r--rsa.h6
-rw-r--r--ssh-add.c24
-rw-r--r--ssh-agent.c24
-rw-r--r--ssh-dss.c237
-rw-r--r--ssh-ecdsa.c232
-rw-r--r--ssh-ed25519.c183
-rw-r--r--ssh-keygen.c20
-rw-r--r--ssh-pkcs11-client.c4
-rw-r--r--ssh-pkcs11-helper.c8
-rw-r--r--ssh-pkcs11.c4
-rw-r--r--ssh-rsa.c260
-rw-r--r--sshbuf-misc.c16
-rw-r--r--sshbuf.h7
-rw-r--r--sshconnect.c4
-rw-r--r--sshconnect1.c18
-rw-r--r--sshconnect2.c8
-rw-r--r--sshd.c16
-rw-r--r--sshkey.c3843
-rw-r--r--sshkey.h222
49 files changed, 5812 insertions, 4873 deletions
diff --git a/ChangeLog b/ChangeLog
index 8f24fc6b..e821f6de 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -24,6 +24,26 @@
Readers of a broken KRL caused by this bug will fail closed, so no
should-have-been-revoked key will be accepted.
+ - djm@cvs.openbsd.org 2014/06/24 01:13:21
+ [Makefile.in auth-bsdauth.c auth-chall.c auth-options.c auth-rsa.c
+ [auth2-none.c auth2-pubkey.c authfile.c authfile.h cipher-3des1.c
+ [cipher-chachapoly.c cipher-chachapoly.h cipher.c cipher.h
+ [digest-libc.c digest-openssl.c digest.h dns.c entropy.c hmac.h
+ [hostfile.c key.c key.h krl.c monitor.c packet.c rsa.c rsa.h
+ [ssh-add.c ssh-agent.c ssh-dss.c ssh-ecdsa.c ssh-ed25519.c
+ [ssh-keygen.c ssh-pkcs11-client.c ssh-pkcs11-helper.c ssh-pkcs11.c
+ [ssh-rsa.c sshbuf-misc.c sshbuf.h sshconnect.c sshconnect1.c
+ [sshconnect2.c sshd.c sshkey.c sshkey.h
+ [openbsd-compat/openssl-compat.c openbsd-compat/openssl-compat.h]
+ New key API: refactor key-related functions to be more library-like,
+ existing API is offered as a set of wrappers.
+
+ with and ok markus@
+
+ Thanks also to Ben Hawkes, David Tomaschik, Ivan Fratric, Matthew
+ Dempsky and Ron Bowes for a detailed review a few months ago.
+ NB. This commit also removes portable OpenSSH support for OpenSSL
+ <0.9.8e.
20140618
- (tim) [openssh/session.c] Work around to get chroot sftp working on UnixWare
diff --git a/Makefile.in b/Makefile.in
index 6bca5b51..16659c0b 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -1,4 +1,4 @@
-# $Id: Makefile.in,v 1.359 2014/05/21 22:23:59 djm Exp $
+# $Id: Makefile.in,v 1.360 2014/07/02 05:28:03 djm Exp $
# uncomment if you run a non bourne compatable shell. Ie. csh
#SHELL = @SH@
@@ -68,7 +68,8 @@ LIBOPENSSH_OBJS=\
sshbuf.o \
sshbuf-getput-basic.o \
sshbuf-misc.o \
- sshbuf-getput-crypto.o
+ sshbuf-getput-crypto.o \
+ sshkey.o
LIBSSH_OBJS=${LIBOPENSSH_OBJS} \
authfd.o authfile.o bufaux.o bufbn.o buffer.o \
diff --git a/auth-bsdauth.c b/auth-bsdauth.c
index f4209c22..37ff893e 100644
--- a/auth-bsdauth.c
+++ b/auth-bsdauth.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-bsdauth.c,v 1.12 2014/03/12 04:50:32 djm Exp $ */
+/* $OpenBSD: auth-bsdauth.c,v 1.13 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2001 Markus Friedl. All rights reserved.
*
@@ -26,6 +26,8 @@
#include "includes.h"
#include <sys/types.h>
+#include <stdarg.h>
+#include <stdio.h>
#include <stdarg.h>
diff --git a/auth-chall.c b/auth-chall.c
index 0005aa88..cb3d522d 100644
--- a/auth-chall.c
+++ b/auth-chall.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-chall.c,v 1.13 2013/05/17 00:13:13 djm Exp $ */
+/* $OpenBSD: auth-chall.c,v 1.14 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2001 Markus Friedl. All rights reserved.
*
@@ -26,6 +26,9 @@
#include "includes.h"
#include <sys/types.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
#include <stdarg.h>
diff --git a/auth-options.c b/auth-options.c
index fa209eaa..9a3c270e 100644
--- a/auth-options.c
+++ b/auth-options.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-options.c,v 1.62 2013/12/19 00:27:57 djm Exp $ */
+/* $OpenBSD: auth-options.c,v 1.63 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -586,8 +586,8 @@ auth_cert_options(Key *k, struct passwd *pw)
if (key_cert_is_legacy(k)) {
/* All options are in the one field for v00 certs */
- if (parse_option_list(buffer_ptr(&k->cert->critical),
- buffer_len(&k->cert->critical), pw,
+ if (parse_option_list(buffer_ptr(k->cert->critical),
+ buffer_len(k->cert->critical), pw,
OPTIONS_CRITICAL|OPTIONS_EXTENSIONS, 1,
&cert_no_port_forwarding_flag,
&cert_no_agent_forwarding_flag,
@@ -599,14 +599,14 @@ auth_cert_options(Key *k, struct passwd *pw)
return -1;
} else {
/* Separate options and extensions for v01 certs */
- if (parse_option_list(buffer_ptr(&k->cert->critical),
- buffer_len(&k->cert->critical), pw,
+ if (parse_option_list(buffer_ptr(k->cert->critical),
+ buffer_len(k->cert->critical), pw,
OPTIONS_CRITICAL, 1, NULL, NULL, NULL, NULL, NULL,
&cert_forced_command,
&cert_source_address_done) == -1)
return -1;
- if (parse_option_list(buffer_ptr(&k->cert->extensions),
- buffer_len(&k->cert->extensions), pw,
+ if (parse_option_list(buffer_ptr(k->cert->extensions),
+ buffer_len(k->cert->extensions), pw,
OPTIONS_EXTENSIONS, 1,
&cert_no_port_forwarding_flag,
&cert_no_agent_forwarding_flag,
diff --git a/auth-rsa.c b/auth-rsa.c
index 5dad6c3d..1bddfa02 100644
--- a/auth-rsa.c
+++ b/auth-rsa.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth-rsa.c,v 1.86 2014/01/27 19:18:54 markus Exp $ */
+/* $OpenBSD: auth-rsa.c,v 1.87 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -144,7 +144,8 @@ auth_rsa_challenge_dialog(Key *key)
challenge = PRIVSEP(auth_rsa_generate_challenge(key));
/* Encrypt the challenge with the public key. */
- rsa_public_encrypt(encrypted_challenge, challenge, key->rsa);
+ if (rsa_public_encrypt(encrypted_challenge, challenge, key->rsa) != 0)
+ fatal("%s: rsa_public_encrypt failed", __func__);
/* Send the encrypted challenge to the client. */
packet_start(SSH_SMSG_AUTH_RSA_CHALLENGE);
diff --git a/auth2-none.c b/auth2-none.c
index c8c6c74a..5501b9d6 100644
--- a/auth2-none.c
+++ b/auth2-none.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-none.c,v 1.16 2010/06/25 08:46:17 djm Exp $ */
+/* $OpenBSD: auth2-none.c,v 1.17 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@@ -30,9 +30,10 @@
#include <sys/uio.h>
#include <fcntl.h>
-#include <stdarg.h>
#include <string.h>
#include <unistd.h>
+#include <stdarg.h>
+#include <stdio.h>
#include "atomicio.h"
#include "xmalloc.h"
diff --git a/auth2-pubkey.c b/auth2-pubkey.c
index 0fd27bb9..b2fd07a6 100644
--- a/auth2-pubkey.c
+++ b/auth2-pubkey.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: auth2-pubkey.c,v 1.39 2013/12/30 23:52:27 djm Exp $ */
+/* $OpenBSD: auth2-pubkey.c,v 1.40 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@@ -230,7 +230,7 @@ pubkey_auth_info(Authctxt *authctxt, const Key *key, const char *fmt, ...)
}
static int
-match_principals_option(const char *principal_list, struct KeyCert *cert)
+match_principals_option(const char *principal_list, struct sshkey_cert *cert)
{
char *result;
u_int i;
@@ -250,7 +250,7 @@ match_principals_option(const char *principal_list, struct KeyCert *cert)
}
static int
-match_principals_file(char *file, struct passwd *pw, struct KeyCert *cert)
+match_principals_file(char *file, struct passwd *pw, struct sshkey_cert *cert)
{
FILE *f;
char line[SSH_MAX_PUBKEY_BYTES], *cp, *ep, *line_opts;
diff --git a/authfile.c b/authfile.c
index 7cb90113..e93d8673 100644
--- a/authfile.c
+++ b/authfile.c
@@ -1,18 +1,5 @@
-/* $OpenBSD: authfile.c,v 1.106 2014/04/29 18:01:49 markus Exp $ */
+/* $OpenBSD: authfile.c,v 1.107 2014/06/24 01:13:21 djm Exp $ */
/*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- * All rights reserved
- * This file contains functions for reading and writing identity files, and
- * for reading the passphrase from the user.
- *
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose. Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
- *
- *
* Copyright (c) 2000, 2013 Markus Friedl. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -43,32 +30,15 @@
#include <sys/param.h>
#include <sys/uio.h>
-#ifdef WITH_OPENSSL
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-#endif
-
-/* compatibility with old or broken OpenSSL versions */
-#include "openbsd-compat/openssl-compat.h"
-
-#include "crypto_api.h"
-
#include <errno.h>
#include <fcntl.h>
-#include <stdarg.h>
#include <stdio.h>
+#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
-#ifdef HAVE_UTIL_H
-#include <util.h>
-#endif
-
-#include "xmalloc.h"
#include "cipher.h"
-#include "buffer.h"
#include "key.h"
#include "ssh.h"
#include "log.h"
@@ -76,667 +46,92 @@
#include "rsa.h"
#include "misc.h"
#include "atomicio.h"
-#include "uuencode.h"
-
-/* openssh private key file format */
-#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n"
-#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n"
-#define KDFNAME "bcrypt"
-#define AUTH_MAGIC "openssh-key-v1"
-#define SALT_LEN 16
-#define DEFAULT_CIPHERNAME "aes256-cbc"
-#define DEFAULT_ROUNDS 16
+#include "sshbuf.h"
+#include "ssherr.h"
#define MAX_KEY_FILE_SIZE (1024 * 1024)
-/* Version identification string for SSH v1 identity files. */
-static const char authfile_id_string[] =
- "SSH PRIVATE KEY FILE FORMAT 1.1\n";
-
-static int
-key_private_to_blob2(Key *prv, Buffer *blob, const char *passphrase,
- const char *comment, const char *ciphername, int rounds)
-{
- u_char *key, *cp, salt[SALT_LEN];
- size_t keylen, ivlen, blocksize, authlen;
- u_int len, check;
- int i, n;
- const Cipher *c;
- Buffer encoded, b, kdf;
- CipherContext ctx;
- const char *kdfname = KDFNAME;
-
- if (rounds <= 0)
- rounds = DEFAULT_ROUNDS;
- if (passphrase == NULL || !strlen(passphrase)) {
- ciphername = "none";
- kdfname = "none";
- } else if (ciphername == NULL)
- ciphername = DEFAULT_CIPHERNAME;
- else if (cipher_number(ciphername) != SSH_CIPHER_SSH2)
- fatal("invalid cipher");
-
- if ((c = cipher_by_name(ciphername)) == NULL)
- fatal("unknown cipher name");
- buffer_init(&kdf);
- blocksize = cipher_blocksize(c);
- keylen = cipher_keylen(c);
- ivlen = cipher_ivlen(c);
- authlen = cipher_authlen(c);
- key = xcalloc(1, keylen + ivlen);
- if (strcmp(kdfname, "none") != 0) {
- arc4random_buf(salt, SALT_LEN);
- if (bcrypt_pbkdf(passphrase, strlen(passphrase),
- salt, SALT_LEN, key, keylen + ivlen, rounds) < 0)
- fatal("bcrypt_pbkdf failed");
- buffer_put_string(&kdf, salt, SALT_LEN);
- buffer_put_int(&kdf, rounds);
- }
- cipher_init(&ctx, c, key, keylen, key + keylen , ivlen, 1);
- explicit_bzero(key, keylen + ivlen);
- free(key);
-
- buffer_init(&encoded);
- buffer_append(&encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC));
- buffer_put_cstring(&encoded, ciphername);
- buffer_put_cstring(&encoded, kdfname);
- buffer_put_string(&encoded, buffer_ptr(&kdf), buffer_len(&kdf));
- buffer_put_int(&encoded, 1); /* number of keys */
- key_to_blob(prv, &cp, &len); /* public key */
- buffer_put_string(&encoded, cp, len);
-
- explicit_bzero(cp, len);
- free(cp);
-
- buffer_free(&kdf);
-
- /* set up the buffer that will be encrypted */
- buffer_init(&b);
-
- /* Random check bytes */
- check = arc4random();
- buffer_put_int(&b, check);
- buffer_put_int(&b, check);
-
- /* append private key and comment*/
- key_private_serialize(prv, &b);
- buffer_put_cstring(&b, comment);
-
- /* padding */
- i = 0;
- while (buffer_len(&b) % blocksize)
- buffer_put_char(&b, ++i & 0xff);
-
- /* length */
- buffer_put_int(&encoded, buffer_len(&b));
-
- /* encrypt */
- cp = buffer_append_space(&encoded, buffer_len(&b) + authlen);
- if (cipher_crypt(&ctx, 0, cp, buffer_ptr(&b), buffer_len(&b), 0,
- authlen) != 0)
- fatal("%s: cipher_crypt failed", __func__);
- buffer_free(&b);
- cipher_cleanup(&ctx);
-
- /* uuencode */
- len = 2 * buffer_len(&encoded);
- cp = xmalloc(len);
- n = uuencode(buffer_ptr(&encoded), buffer_len(&encoded),
- (char *)cp, len);
- if (n < 0)
- fatal("%s: uuencode", __func__);
-
- buffer_clear(blob);
- buffer_append(blob, MARK_BEGIN, sizeof(MARK_BEGIN) - 1);
- for (i = 0; i < n; i++) {
- buffer_put_char(blob, cp[i]);
- if (i % 70 == 69)
- buffer_put_char(blob, '\n');
- }
- if (i % 70 != 69)
- buffer_put_char(blob, '\n');
- buffer_append(blob, MARK_END, sizeof(MARK_END) - 1);
- free(cp);
-
- return buffer_len(blob);
-}
-
-static Key *
-key_parse_private2(Buffer *blob, int type, const char *passphrase,
- char **commentp)
-{
- u_char *key = NULL, *cp, *salt = NULL, pad, last;
- char *comment = NULL, *ciphername = NULL, *kdfname = NULL;
- const u_char *kdfp;
- u_int keylen = 0, ivlen, blocksize, slen, klen, len, rounds, nkeys;
- u_int check1, check2, m1len, m2len;
- size_t authlen;
- const Cipher *c;
- Buffer b, encoded, copy, kdf;
- CipherContext ctx;
- Key *k = NULL;
- int dlen, ret, i;
-
- buffer_init(&b);
- buffer_init(&kdf);
- buffer_init(&encoded);
- buffer_init(&copy);
-
- /* uudecode */
- m1len = sizeof(MARK_BEGIN) - 1;
- m2len = sizeof(MARK_END) - 1;
- cp = buffer_ptr(blob);
- len = buffer_len(blob);
- if (len < m1len || memcmp(cp, MARK_BEGIN, m1len)) {
- debug("%s: missing begin marker", __func__);
- goto out;
- }
- cp += m1len;
- len -= m1len;
- while (len) {
- if (*cp != '\n' && *cp != '\r')
- buffer_put_char(&encoded, *cp);
- last = *cp;
- len--;
- cp++;
- if (last == '\n') {
- if (len >= m2len && !memcmp(cp, MARK_END, m2len)) {
- buffer_put_char(&encoded, '\0');
- break;
- }
- }
- }
- if (!len) {
- debug("%s: no end marker", __func__);
- goto out;
- }
- len = buffer_len(&encoded);
- if ((cp = buffer_append_space(&copy, len)) == NULL) {
- error("%s: buffer_append_space", __func__);
- goto out;
- }
- if ((dlen = uudecode(buffer_ptr(&encoded), cp, len)) < 0) {
- error("%s: uudecode failed", __func__);
- goto out;
- }
- if ((u_int)dlen > len) {
- error("%s: crazy uudecode length %d > %u", __func__, dlen, len);
- goto out;
- }
- buffer_consume_end(&copy, len - dlen);
- if (buffer_len(&copy) < sizeof(AUTH_MAGIC) ||
- memcmp(buffer_ptr(&copy), AUTH_MAGIC, sizeof(AUTH_MAGIC))) {
- error("%s: bad magic", __func__);
- goto out;
- }
- buffer_consume(&copy, sizeof(AUTH_MAGIC));
-
- ciphername = buffer_get_cstring_ret(&copy, NULL);
- if (ciphername == NULL ||
- (c = cipher_by_name(ciphername)) == NULL) {
- error("%s: unknown cipher name", __func__);
- goto out;
- }
- if ((passphrase == NULL || !strlen(passphrase)) &&
- strcmp(ciphername, "none") != 0) {
- /* passphrase required */
- goto out;
- }
- kdfname = buffer_get_cstring_ret(&copy, NULL);
- if (kdfname == NULL ||
- (strcmp(kdfname, "none") != 0 && strcmp(kdfname, "bcrypt") != 0)) {
- error("%s: unknown kdf name", __func__);
- goto out;
- }
- if (!strcmp(kdfname, "none") && strcmp(ciphername, "none") != 0) {
- error("%s: cipher %s requires kdf", __func__, ciphername);
- goto out;
- }
- /* kdf options */
- kdfp = buffer_get_string_ptr_ret(&copy, &klen);
- if (kdfp == NULL) {
- error("%s: kdf options not set", __func__);
- goto out;
- }
- if (klen > 0) {
- if ((cp = buffer_append_space(&kdf, klen)) == NULL) {
- error("%s: kdf alloc failed", __func__);
- goto out;
- }
- memcpy(cp, kdfp, klen);
- }
- /* number of keys */
- if (buffer_get_int_ret(&nkeys, &copy) < 0) {
- error("%s: key counter missing", __func__);
- goto out;
- }
- if (nkeys != 1) {
- error("%s: only one key supported", __func__);
- goto out;
- }
- /* pubkey */
- if ((cp = buffer_get_string_ret(&copy, &len)) == NULL) {
- error("%s: pubkey not found", __func__);
- goto out;
- }
- free(cp); /* XXX check pubkey against decrypted private key */
-
- /* size of encrypted key blob */
- len = buffer_get_int(&copy);
- blocksize = cipher_blocksize(c);
- authlen = cipher_authlen(c);
- if (len < blocksize) {
- error("%s: encrypted data too small", __func__);
- goto out;
- }
- if (len % blocksize) {
- error("%s: length not multiple of blocksize", __func__);
- goto out;
- }
-
- /* setup key */
- keylen = cipher_keylen(c);
- ivlen = cipher_ivlen(c);
- key = xcalloc(1, keylen + ivlen);
- if (!strcmp(kdfname, "bcrypt")) {
- if ((salt = buffer_get_string_ret(&kdf, &slen)) == NULL) {
- error("%s: salt not set", __func__);
- goto out;
- }
- if (buffer_get_int_ret(&rounds, &kdf) < 0) {
- error("%s: rounds not set", __func__);
- goto out;
- }
- if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen,
- key, keylen + ivlen, rounds) < 0) {
- error("%s: bcrypt_pbkdf failed", __func__);
- goto out;
- }
- }
-
- cp = buffer_append_space(&b, len);
- cipher_init(&ctx, c, key, keylen, key + keylen, ivlen, 0);
- ret = cipher_crypt(&ctx, 0, cp, buffer_ptr(&copy), len, 0, authlen);
- cipher_cleanup(&ctx);
- buffer_consume(&copy, len);
-
- /* fail silently on decryption errors */
- if (ret != 0) {
- debug("%s: decrypt failed", __func__);
- goto out;
- }
-
- if (buffer_len(&copy) != 0) {
- error("%s: key blob has trailing data (len = %u)", __func__,
- buffer_len(&copy));
- goto out;
- }
-
- /* check bytes */
- if (buffer_get_int_ret(&check1, &b) < 0 ||
- buffer_get_int_ret(&check2, &b) < 0) {
- error("check bytes missing");
- goto out;
- }
- if (check1 != check2) {
- debug("%s: decrypt failed: 0x%08x != 0x%08x", __func__,
- check1, check2);
- goto out;
- }
-
- k = key_private_deserialize(&b);
-
- /* comment */
- comment = buffer_get_cstring_ret(&b, NULL);
-
- i = 0;
- while (buffer_len(&b)) {
- if (buffer_get_char_ret(&pad, &b) == -1 ||
- pad != (++i & 0xff)) {
- error("%s: bad padding", __func__);
- key_free(k);
- k = NULL;
- goto out;
- }
- }
-
- if (k && commentp) {
- *commentp = comment;
- comment = NULL;
- }
-
- /* XXX decode pubkey and check against private */
- out:
- free(ciphername);
- free(kdfname);
- free(salt);
- free(comment);
- if (key)
- explicit_bzero(key, keylen + ivlen);
- free(key);
- buffer_free(&encoded);
- buffer_free(&copy);
- buffer_free(&kdf);
- buffer_free(&b);
- return k;
-}
-
-#ifdef WITH_SSH1
-/*
- * Serialises the authentication (private) key to a blob, encrypting it with
- * passphrase. The identification of the blob (lowest 64 bits of n) will
- * precede the key to provide identification of the key without needing a
- * passphrase.
- */
-static int
-key_private_rsa1_to_blob(Key *key, Buffer *blob, const char *passphrase,
- const char *comment)
-{
- Buffer buffer, encrypted;
- u_char buf[100], *cp;
- int i, cipher_num;
- CipherContext ciphercontext;
- const Cipher *cipher;
- u_int32_t rnd;
-
- /*
- * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting
- * to another cipher; otherwise use SSH_AUTHFILE_CIPHER.
- */
- cipher_num = (strcmp(passphrase, "") == 0) ?
- SSH_CIPHER_NONE : SSH_AUTHFILE_CIPHER;
- if ((cipher = cipher_by_number(cipher_num)) == NULL)
- fatal("save_private_key_rsa: bad cipher");
-
- /* This buffer is used to built the secret part of the private key. */
- buffer_init(&buffer);
-
- /* Put checkbytes for checking passphrase validity. */
- rnd = arc4random();
- buf[0] = rnd & 0xff;
- buf[1] = (rnd >> 8) & 0xff;
- buf[2] = buf[0];
- buf[3] = buf[1];
- buffer_append(&buffer, buf, 4);
-
- /*
- * Store the private key (n and e will not be stored because they
- * will be stored in plain text, and storing them also in encrypted
- * format would just give known plaintext).
- */
- buffer_put_bignum(&buffer, key->rsa->d);
- buffer_put_bignum(&buffer, key->rsa->iqmp);
- buffer_put_bignum(&buffer, key->rsa->q); /* reverse from SSL p */
- buffer_put_bignum(&buffer, key->rsa->p); /* reverse from SSL q */
-
- /* Pad the part to be encrypted until its size is a multiple of 8. */
- while (buffer_len(&buffer) % 8 != 0)
- buffer_put_char(&buffer, 0);
-
- /* This buffer will be used to contain the data in the file. */
- buffer_init(&encrypted);
-
- /* First store keyfile id string. */
- for (i = 0; authfile_id_string[i]; i++)
- buffer_put_char(&encrypted, authfile_id_string[i]);
- buffer_put_char(&encrypted, 0);
-
- /* Store cipher type. */
- buffer_put_char(&encrypted, cipher_num);
- buffer_put_int(&encrypted, 0); /* For future extension */
-
- /* Store public key. This will be in plain text. */
- buffer_put_int(&encrypted, BN_num_bits(key->rsa->n));
- buffer_put_bignum(&encrypted, key->rsa->n);
- buffer_put_bignum(&encrypted, key->rsa->e);
- buffer_put_cstring(&encrypted, comment);
-
- /* Allocate space for the private part of the key in the buffer. */
- cp = buffer_append_space(&encrypted, buffer_len(&buffer));
-
- cipher_set_key_string(&ciphercontext, cipher, passphrase,
- CIPHER_ENCRYPT);
- if (cipher_crypt(&ciphercontext, 0, cp,
- buffer_ptr(&buffer), buffer_len(&buffer), 0, 0) != 0)
- fatal("%s: cipher_crypt failed", __func__);
- cipher_cleanup(&ciphercontext);
- explicit_bzero(&ciphercontext, sizeof(ciphercontext));
-
- /* Destroy temporary data. */
- explicit_bzero(buf, sizeof(buf));
- buffer_free(&buffer);
-
- buffer_append(blob, buffer_ptr(&encrypted), buffer_len(&encrypted));
- buffer_free(&encrypted);
-
- return 1;
-}
-#endif
-
-#ifdef WITH_OPENSSL
-/* convert SSH v2 key in OpenSSL PEM format */
-static int
-key_private_pem_to_blob(Key *key, Buffer *blob, const char *_passphrase,
- const char *comment)
-{
- int success = 0;
- int blen, len = strlen(_passphrase);
- u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL;
-#if (OPENSSL_VERSION_NUMBER < 0x00907000L)
- const EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL;
-#else
- const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL;
-#endif
- const u_char *bptr;
- BIO *bio;
-
- if (len > 0 && len <= 4) {
- error("passphrase too short: have %d bytes, need > 4", len);
- return 0;
- }
- if ((bio = BIO_new(BIO_s_mem())) == NULL) {
- error("%s: BIO_new failed", __func__);
- return 0;
- }
- switch (key->type) {
- case KEY_DSA:
- success = PEM_write_bio_DSAPrivateKey(bio, key->dsa,
- cipher, passphrase, len, NULL, NULL);
- break;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA:
- success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa,
- cipher, passphrase, len, NULL, NULL);
- break;
-#endif
- case KEY_RSA:
- success = PEM_write_bio_RSAPrivateKey(bio, key->rsa,
- cipher, passphrase, len, NULL, NULL);
- break;
- }
- if (success) {
- if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0)
- success = 0;
- else
- buffer_append(blob, bptr, blen);
- }
- BIO_free(bio);
- return success;
-}
-#endif
-
/* Save a key blob to a file */
static int
-key_save_private_blob(Buffer *keybuf, const char *filename)
+sshkey_save_private_blob(struct sshbuf *keybuf, const char *filename)
{
- int fd;
+ int fd, oerrno;
- if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0) {
- error("open %s failed: %s.", filename, strerror(errno));
- return 0;
- }
- if (atomicio(vwrite, fd, buffer_ptr(keybuf),
- buffer_len(keybuf)) != buffer_len(keybuf)) {
- error("write to key file %s failed: %s", filename,
- strerror(errno));
+ if ((fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0600)) < 0)
+ return SSH_ERR_SYSTEM_ERROR;
+ if (atomicio(vwrite, fd, (u_char *)sshbuf_ptr(keybuf),
+ sshbuf_len(keybuf)) != sshbuf_len(keybuf)) {
+ oerrno = errno;
close(fd);
unlink(filename);
- return 0;
+ errno = oerrno;
+ return SSH_ERR_SYSTEM_ERROR;
}
close(fd);
- return 1;
-}
-
-/* Serialise "key" to buffer "blob" */
-static int
-key_private_to_blob(Key *key, Buffer *blob, const char *passphrase,
- const char *comment, int force_new_format, const char *new_format_cipher,
- int new_format_rounds)
-{
- switch (key->type) {
-#ifdef WITH_SSH1
- case KEY_RSA1:
- return key_private_rsa1_to_blob(key, blob, passphrase, comment);
-#endif
-#ifdef WITH_OPENSSL
- case KEY_DSA:
- case KEY_ECDSA:
- case KEY_RSA:
- if (force_new_format) {
- return key_private_to_blob2(key, blob, passphrase,
- comment, new_format_cipher, new_format_rounds);
- }
- return key_private_pem_to_blob(key, blob, passphrase, comment);
-#endif
- case KEY_ED25519:
- return key_private_to_blob2(key, blob, passphrase,
- comment, new_format_cipher, new_format_rounds);
- default:
- error("%s: cannot save key type %d", __func__, key->type);
- return 0;
- }
+ return 0;
}
int
-key_save_private(Key *key, const char *filename, const char *passphrase,
- const char *comment, int force_new_format, const char *new_format_cipher,
- int new_format_rounds)
+sshkey_save_private(struct sshkey *key, const char *filename,
+ const char *passphrase, const char *comment,
+ int force_new_format, const char *new_format_cipher, int new_format_rounds)
{
- Buffer keyblob;
- int success = 0;
+ struct sshbuf *keyblob = NULL;
+ int r;
- buffer_init(&keyblob);
- if (!key_private_to_blob(key, &keyblob, passphrase, comment,
- force_new_format, new_format_cipher, new_format_rounds))
+ if ((keyblob = sshbuf_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if ((r = sshkey_private_to_fileblob(key, keyblob, passphrase, comment,
+ force_new_format, new_format_cipher, new_format_rounds)) != 0)
goto out;
- if (!key_save_private_blob(&keyblob, filename))
+ if ((r = sshkey_save_private_blob(keyblob, filename)) != 0)
goto out;
- success = 1;
+ r = 0;
out:
- buffer_free(&keyblob);
- return success;
-}
-
-#ifdef WITH_SSH1
-/*
- * Parse the public, unencrypted portion of a RSA1 key.
- */
-static Key *
-key_parse_public_rsa1(Buffer *blob, char **commentp)
-{
- Key *pub;
- Buffer copy;
-
- /* Check that it is at least big enough to contain the ID string. */
- if (buffer_len(blob) < sizeof(authfile_id_string)) {
- debug3("Truncated RSA1 identifier");
- return NULL;
- }
-
- /*
- * Make sure it begins with the id string. Consume the id string
- * from the buffer.
- */
- if (memcmp(buffer_ptr(blob), authfile_id_string,
- sizeof(authfile_id_string)) != 0) {
- debug3("Incorrect RSA1 identifier");
- return NULL;
- }
- buffer_init(&copy);
- buffer_append(&copy, buffer_ptr(blob), buffer_len(blob));
- buffer_consume(&copy, sizeof(authfile_id_string));
-
- /* Skip cipher type and reserved data. */
- (void) buffer_get_char(&copy); /* cipher type */
- (void) buffer_get_int(&copy); /* reserved */
-
- /* Read the public key from the buffer. */
- (void) buffer_get_int(&copy);
- pub = key_new(KEY_RSA1);
- buffer_get_bignum(&copy, pub->rsa->n);
- buffer_get_bignum(&copy, pub->rsa->e);
- if (commentp)
- *commentp = buffer_get_string(&copy, NULL);
- /* The encrypted private part is not parsed by this function. */
- buffer_free(&copy);
-
- return pub;
+ sshbuf_free(keyblob);
+ return r;
}
-#endif
/* Load a key from a fd into a buffer */
int
-key_load_file(int fd, const char *filename, Buffer *blob)
+sshkey_load_file(int fd, const char *filename, struct sshbuf *blob)
{
u_char buf[1024];
size_t len;
struct stat st;
+ int r;
- if (fstat(fd, &st) < 0) {
- error("%s: fstat of key file %.200s%sfailed: %.100s", __func__,
- filename == NULL ? "" : filename,
- filename == NULL ? "" : " ",
- strerror(errno));
- return 0;
- }
+ if (fstat(fd, &st) < 0)
+ return SSH_ERR_SYSTEM_ERROR;
if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
- st.st_size > MAX_KEY_FILE_SIZE) {
- toobig:
- error("%s: key file %.200s%stoo large", __func__,
- filename == NULL ? "" : filename,
- filename == NULL ? "" : " ");
- return 0;
- }
- buffer_clear(blob);
+ st.st_size > MAX_KEY_FILE_SIZE)
+ return SSH_ERR_INVALID_FORMAT;
for (;;) {
if ((len = atomicio(read, fd, buf, sizeof(buf))) == 0) {
if (errno == EPIPE)
break;
- debug("%s: read from key file %.200s%sfailed: %.100s",
- __func__, filename == NULL ? "" : filename,
- filename == NULL ? "" : " ", strerror(errno));
- buffer_clear(blob);
- explicit_bzero(buf, sizeof(buf));
- return 0;
+ r = SSH_ERR_SYSTEM_ERROR;
+ goto out;
}
- buffer_append(blob, buf, len);
- if (buffer_len(blob) > MAX_KEY_FILE_SIZE) {
- buffer_clear(blob);
- explicit_bzero(buf, sizeof(buf));
- goto toobig;
+ if ((r = sshbuf_put(blob, buf, len)) != 0)
+ goto out;
+ if (sshbuf_len(blob) > MAX_KEY_FILE_SIZE) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
}
}
- explicit_bzero(buf, sizeof(buf));
if ((st.st_mode & (S_IFSOCK|S_IFCHR|S_IFIFO)) == 0 &&
- st.st_size != buffer_len(blob)) {
- debug("%s: key file %.200s%schanged size while reading",
- __func__, filename == NULL ? "" : filename,
- filename == NULL ? "" : " ");
- buffer_clear(blob);
- return 0;
+ st.st_size != (off_t)sshbuf_len(blob)) {
+ r = SSH_ERR_FILE_CHANGED;
+ goto out;
}
+ r = 0;
- return 1;
+ out:
+ explicit_bzero(buf, sizeof(buf));
+ if (r != 0)
+ sshbuf_reset(blob);
+ return r;
}
#ifdef WITH_SSH1
@@ -745,249 +140,65 @@ key_load_file(int fd, const char *filename, Buffer *blob)
* encountered (the file does not exist or is not readable), and the key
* otherwise.
*/
-static Key *
-key_load_public_rsa1(int fd, const char *filename, char **commentp)
-{
- Buffer buffer;
- Key *pub;
-
- buffer_init(&buffer);
- if (!key_load_file(fd, filename, &buffer)) {
- buffer_free(&buffer);
- return NULL;
- }
-
- pub = key_parse_public_rsa1(&buffer, commentp);
- if (pub == NULL)
- debug3("Could not load \"%s\" as a RSA1 public key", filename);
- buffer_free(&buffer);
- return pub;
-}
-
-/* load public key from private-key file, works only for SSH v1 */
-Key *
-key_load_public_type(int type, const char *filename, char **commentp)
-{
- Key *pub;
- int fd;
-
- if (type == KEY_RSA1) {
- fd = open(filename, O_RDONLY);
- if (fd < 0)
- return NULL;
- pub = key_load_public_rsa1(fd, filename, commentp);
- close(fd);
- return pub;
- }
- return NULL;
-}
-
-static Key *
-key_parse_private_rsa1(Buffer *blob, const char *passphrase, char **commentp)
+static int
+sshkey_load_public_rsa1(int fd, const char *filename,
+ struct sshkey **keyp, char **commentp)
{
- int check1, check2, cipher_type;
- Buffer decrypted;
- u_char *cp;
- CipherContext ciphercontext;
- const Cipher *cipher;
- Key *prv = NULL;
- Buffer copy;
-
- /* Check that it is at least big enough to contain the ID string. */
- if (buffer_len(blob) < sizeof(authfile_id_string)) {
- debug3("Truncated RSA1 identifier");
- return NULL;
- }
-
- /*
- * Make sure it begins with the id string. Consume the id string
- * from the buffer.
- */
- if (memcmp(buffer_ptr(blob), authfile_id_string,
- sizeof(authfile_id_string)) != 0) {
- debug3("Incorrect RSA1 identifier");
- return NULL;
- }
- buffer_init(&copy);
- buffer_append(&copy, buffer_ptr(blob), buffer_len(blob));
- buffer_consume(&copy, sizeof(authfile_id_string));
-
- /* Read cipher type. */
- cipher_type = buffer_get_char(&copy);
- (void) buffer_get_int(&copy); /* Reserved data. */
-
- /* Read the public key from the buffer. */
- (void) buffer_get_int(&copy);
- prv = key_new_private(KEY_RSA1);
-
- buffer_get_bignum(&copy, prv->rsa->n);
- buffer_get_bignum(&copy, prv->rsa->e);
- if (commentp)
- *commentp = buffer_get_string(&copy, NULL);
- else
- (void)buffer_get_string_ptr(&copy, NULL);
-
- /* Check that it is a supported cipher. */
- cipher = cipher_by_number(cipher_type);
- if (cipher == NULL) {
- debug("Unsupported RSA1 cipher %d", cipher_type);
- buffer_free(&copy);
- goto fail;
- }
- /* Initialize space for decrypted data. */
- buffer_init(&decrypted);
- cp = buffer_append_space(&decrypted, buffer_len(&copy));
-
- /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */
- cipher_set_key_string(&ciphercontext, cipher, passphrase,
- CIPHER_DECRYPT);
- if (cipher_crypt(&ciphercontext, 0, cp,
- buffer_ptr(&copy), buffer_len(&copy), 0, 0) != 0)
- fatal("%s: cipher_crypt failed", __func__);
- cipher_cleanup(&ciphercontext);
- explicit_bzero(&ciphercontext, sizeof(ciphercontext));
- buffer_free(&copy);
-
- check1 = buffer_get_char(&decrypted);
- check2 = buffer_get_char(&decrypted);
- if (check1 != buffer_get_char(&decrypted) ||
- check2 != buffer_get_char(&decrypted)) {
- if (strcmp(passphrase, "") != 0)
- debug("Bad passphrase supplied for RSA1 key");
- /* Bad passphrase. */
- buffer_free(&decrypted);
- goto fail;
- }
- /* Read the rest of the private key. */
- buffer_get_bignum(&decrypted, prv->rsa->d);
- buffer_get_bignum(&decrypted, prv->rsa->iqmp); /* u */
- /* in SSL and SSH v1 p and q are exchanged */
- buffer_get_bignum(&decrypted, prv->rsa->q); /* p */
- buffer_get_bignum(&decrypted, prv->rsa->p); /* q */
-
- /* calculate p-1 and q-1 */
- rsa_generate_additional_parameters(prv->rsa);
-
- buffer_free(&decrypted);
+ struct sshbuf *b = NULL;
+ int r;
- /* enable blinding */
- if (RSA_blinding_on(prv->rsa, NULL) != 1) {
- error("%s: RSA_blinding_on failed", __func__);
- goto fail;
- }
- return prv;
-
-fail:
+ *keyp = NULL;
if (commentp != NULL)
- free(*commentp);
- key_free(prv);
- return NULL;
+ *commentp = NULL;
+
+ if ((b = sshbuf_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if ((r = sshkey_load_file(fd, filename, b)) != 0)
+ goto out;
+ if ((r = sshkey_parse_public_rsa1_fileblob(b, keyp, commentp)) != 0)
+ goto out;
+ r = 0;
+ out:
+ sshbuf_free(b);
+ return r;
}
-#endif
+#endif /* WITH_SSH1 */
#ifdef WITH_OPENSSL
-static Key *
-key_parse_private_pem(Buffer *blob, int type, const char *passphrase,
- char **commentp)
+/* XXX Deprecate? */
+int
+sshkey_load_private_pem(int fd, int type, const char *passphrase,
+ struct sshkey **keyp, char **commentp)
{
- EVP_PKEY *pk = NULL;
- Key *prv = NULL;
- char *name = "<no key>";
- BIO *bio;
-
- if ((bio = BIO_new_mem_buf(buffer_ptr(blob),
- buffer_len(blob))) == NULL) {
- error("%s: BIO_new_mem_buf failed", __func__);
- return NULL;
- }
-
- pk = PEM_read_bio_PrivateKey(bio, NULL, NULL, (char *)passphrase);
- BIO_free(bio);
- if (pk == NULL) {
- debug("%s: PEM_read_PrivateKey failed", __func__);
- (void)ERR_get_error();
- } else if (pk->type == EVP_PKEY_RSA &&
- (type == KEY_UNSPEC||type==KEY_RSA)) {
- prv = key_new(KEY_UNSPEC);
- prv->rsa = EVP_PKEY_get1_RSA(pk);
- prv->type = KEY_RSA;
- name = "rsa w/o comment";
-#ifdef DEBUG_PK
- RSA_print_fp(stderr, prv->rsa, 8);
-#endif
- if (RSA_blinding_on(prv->rsa, NULL) != 1) {
- error("%s: RSA_blinding_on failed", __func__);
- key_free(prv);
- prv = NULL;
- }
- } else if (pk->type == EVP_PKEY_DSA &&
- (type == KEY_UNSPEC||type==KEY_DSA)) {
- prv = key_new(KEY_UNSPEC);
- prv->dsa = EVP_PKEY_get1_DSA(pk);
- prv->type = KEY_DSA;
- name = "dsa w/o comment";
-#ifdef DEBUG_PK
- DSA_print_fp(stderr, prv->dsa, 8);
-#endif
-#ifdef OPENSSL_HAS_ECC
- } else if (pk->type == EVP_PKEY_EC &&
- (type == KEY_UNSPEC||type==KEY_ECDSA)) {
- prv = key_new(KEY_UNSPEC);
- prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk);
- prv->type = KEY_ECDSA;
- if ((prv->ecdsa_nid = key_ecdsa_key_to_nid(prv->ecdsa)) == -1 ||
- key_curve_nid_to_name(prv->ecdsa_nid) == NULL ||
- key_ec_validate_public(EC_KEY_get0_group(prv->ecdsa),
- EC_KEY_get0_public_key(prv->ecdsa)) != 0 ||
- key_ec_validate_private(prv->ecdsa) != 0) {
- error("%s: bad ECDSA key", __func__);
- key_free(prv);
- prv = NULL;
- }
- name = "ecdsa w/o comment";
-#ifdef DEBUG_PK
- if (prv != NULL && prv->ecdsa != NULL)
- key_dump_ec_key(prv->ecdsa);
-#endif
-#endif /* OPENSSL_HAS_ECC */
- } else {
- error("%s: PEM_read_PrivateKey: mismatch or "
- "unknown EVP_PKEY save_type %d", __func__, pk->save_type);
- }
- if (pk != NULL)
- EVP_PKEY_free(pk);
- if (prv != NULL && commentp)
- *commentp = xstrdup(name);
- debug("read PEM private key done: type %s",
- prv ? key_type(prv) : "<unknown>");
- return prv;
-}
+ struct sshbuf *buffer = NULL;
+ int r;
-Key *
-key_load_private_pem(int fd, int type, const char *passphrase,
- char **commentp)
-{
- Buffer buffer;
- Key *prv;
+ *keyp = NULL;
+ if (commentp != NULL)
+ *commentp = NULL;
- buffer_init(&buffer);
- if (!key_load_file(fd, NULL, &buffer)) {
- buffer_free(&buffer);
- return NULL;
- }
- prv = key_parse_private_pem(&buffer, type, passphrase, commentp);
- buffer_free(&buffer);
- return prv;
+ if ((buffer = sshbuf_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if ((r = sshkey_load_file(fd, NULL, buffer)) != 0)
+ goto out;
+ if ((r = sshkey_parse_private_pem_fileblob(buffer, type, passphrase,
+ keyp, commentp)) != 0)
+ goto out;
+ r = 0;
+ out:
+ sshbuf_free(buffer);
+ return r;
}
-#endif
+#endif /* WITH_OPENSSL */
+/* XXX remove error() calls from here? */
int
-key_perm_ok(int fd, const char *filename)
+sshkey_perm_ok(int fd, const char *filename)
{
struct stat st;
if (fstat(fd, &st) < 0)
- return 0;
+ return SSH_ERR_SYSTEM_ERROR;
/*
* if a key owned by the user is accessed, then we check the
* permissions of the file. if the key owned by a different user,
@@ -1002,313 +213,311 @@ key_perm_ok(int fd, const char *filename)
error("@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@");
error("Permissions 0%3.3o for '%s' are too open.",
(u_int)st.st_mode & 0777, filename);
- error("It is required that your private key files are NOT accessible by others.");
+ error("It is recommended that your private key files are NOT accessible by others.");
error("This private key will be ignored.");
- return 0;
+ return SSH_ERR_KEY_BAD_PERMISSIONS;
}
- return 1;
+ return 0;
}
-static Key *
-key_parse_private_type(Buffer *blob, int type, const char *passphrase,
- char **commentp)
+/* XXX kill perm_ok now that we have SSH_ERR_KEY_BAD_PERMISSIONS? */
+int
+sshkey_load_private_type(int type, const char *filename, const char *passphrase,
+ struct sshkey **keyp, char **commentp, int *perm_ok)
{
- Key *k;
+ int fd, r;
+ struct sshbuf *buffer = NULL;
- switch (type) {
-#ifdef WITH_SSH1
- case KEY_RSA1:
- return key_parse_private_rsa1(blob, passphrase, commentp);
-#endif
-#ifdef WITH_OPENSSL
- case KEY_DSA:
- case KEY_ECDSA:
- case KEY_RSA:
- return key_parse_private_pem(blob, type, passphrase, commentp);
-#endif
- case KEY_ED25519:
- return key_parse_private2(blob, type, passphrase, commentp);
- case KEY_UNSPEC:
- if ((k = key_parse_private2(blob, type, passphrase, commentp)))
- return k;
-#ifdef WITH_OPENSSL
- return key_parse_private_pem(blob, type, passphrase, commentp);
-#endif
- default:
- error("%s: cannot parse key type %d", __func__, type);
- break;
- }
- return NULL;
-}
-
-Key *
-key_load_private_type(int type, const char *filename, const char *passphrase,
- char **commentp, int *perm_ok)
-{
- int fd;
- Key *ret;
- Buffer buffer;
+ *keyp = NULL;
+ if (commentp != NULL)
+ *commentp = NULL;
- fd = open(filename, O_RDONLY);
- if (fd < 0) {
- debug("could not open key file '%s': %s", filename,
- strerror(errno));
+ if ((fd = open(filename, O_RDONLY)) < 0) {
if (perm_ok != NULL)
*perm_ok = 0;
- return NULL;
+ return SSH_ERR_SYSTEM_ERROR;
}
- if (!key_perm_ok(fd, filename)) {
+ if (sshkey_perm_ok(fd, filename) != 0) {
if (perm_ok != NULL)
*perm_ok = 0;
- error("bad permissions: ignore key: %s", filename);
- close(fd);
- return NULL;
+ r = SSH_ERR_KEY_BAD_PERMISSIONS;
+ goto out;
}
if (perm_ok != NULL)
*perm_ok = 1;
- buffer_init(&buffer);
- if (!key_load_file(fd, filename, &buffer)) {
- buffer_free(&buffer);
- close(fd);
- return NULL;
+ if ((buffer = sshbuf_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
}
+ if ((r = sshkey_load_file(fd, filename, buffer)) != 0)
+ goto out;
+ if ((r = sshkey_parse_private_fileblob_type(buffer, type, passphrase,
+ keyp, commentp)) != 0)
+ goto out;
+ r = 0;
+ out:
close(fd);
- ret = key_parse_private_type(&buffer, type, passphrase, commentp);
- buffer_free(&buffer);
- return ret;
+ if (buffer != NULL)
+ sshbuf_free(buffer);
+ return r;
}
-Key *
-key_parse_private(Buffer *buffer, const char *filename,
- const char *passphrase, char **commentp)
+/* XXX this is almost identical to sshkey_load_private_type() */
+int
+sshkey_load_private(const char *filename, const char *passphrase,
+ struct sshkey **keyp, char **commentp)
{
-#ifdef WITH_SSH1
- Key *pub, *prv;
+ struct sshbuf *buffer = NULL;
+ int r, fd;
- /* it's a SSH v1 key if the public key part is readable */
- pub = key_parse_public_rsa1(buffer, commentp);
- if (pub == NULL) {
- prv = key_parse_private_type(buffer, KEY_UNSPEC,
- passphrase, NULL);
- /* use the filename as a comment for PEM */
- if (commentp && prv)
- *commentp = xstrdup(filename);
- } else {
- key_free(pub);
- /* key_parse_public_rsa1() has already loaded the comment */
- prv = key_parse_private_type(buffer, KEY_RSA1, passphrase,
- NULL);
- }
- return prv;
-#else
- return key_parse_private_type(buffer, KEY_UNSPEC,
- passphrase, commentp);
-#endif
-}
-
-Key *
-key_load_private(const char *filename, const char *passphrase,
- char **commentp)
-{
- Key *prv;
- Buffer buffer;
- int fd;
+ *keyp = NULL;
+ if (commentp != NULL)
+ *commentp = NULL;
- fd = open(filename, O_RDONLY);
- if (fd < 0) {
- debug("could not open key file '%s': %s", filename,
- strerror(errno));
- return NULL;
- }
- if (!key_perm_ok(fd, filename)) {
- error("bad permissions: ignore key: %s", filename);
- close(fd);
- return NULL;
+ if ((fd = open(filename, O_RDONLY)) < 0)
+ return SSH_ERR_SYSTEM_ERROR;
+ if (sshkey_perm_ok(fd, filename) != 0) {
+ r = SSH_ERR_KEY_BAD_PERMISSIONS;
+ goto out;
}
- buffer_init(&buffer);
- if (!key_load_file(fd, filename, &buffer)) {
- buffer_free(&buffer);
- close(fd);
- return NULL;
+ if ((buffer = sshbuf_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
}
+ if ((r = sshkey_load_file(fd, filename, buffer)) != 0 ||
+ (r = sshkey_parse_private_fileblob(buffer, passphrase, filename,
+ keyp, commentp)) != 0)
+ goto out;
+ r = 0;
+ out:
close(fd);
-
- prv = key_parse_private(&buffer, filename, passphrase, commentp);
- buffer_free(&buffer);
- return prv;
+ if (buffer != NULL)
+ sshbuf_free(buffer);
+ return r;
}
static int
-key_try_load_public(Key *k, const char *filename, char **commentp)
+sshkey_try_load_public(struct sshkey *k, const char *filename, char **commentp)
{
FILE *f;
char line[SSH_MAX_PUBKEY_BYTES];
char *cp;
u_long linenum = 0;
+ int r;
- f = fopen(filename, "r");
- if (f != NULL) {
- while (read_keyfile_line(f, filename, line, sizeof(line),
- &linenum) != -1) {
- cp = line;
- switch (*cp) {
- case '#':
- case '\n':
- case '\0':
- continue;
- }
- /* Abort loading if this looks like a private key */
- if (strncmp(cp, "-----BEGIN", 10) == 0)
- break;
- /* Skip leading whitespace. */
- for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
- ;
- if (*cp) {
- if (key_read(k, &cp) == 1) {
- cp[strcspn(cp, "\r\n")] = '\0';
- if (commentp) {
- *commentp = xstrdup(*cp ?
- cp : filename);
- }
- fclose(f);
- return 1;
+ if (commentp != NULL)
+ *commentp = NULL;
+ if ((f = fopen(filename, "r")) == NULL)
+ return SSH_ERR_SYSTEM_ERROR;
+ while (read_keyfile_line(f, filename, line, sizeof(line),
+ &linenum) != -1) {
+ cp = line;
+ switch (*cp) {
+ case '#':
+ case '\n':
+ case '\0':
+ continue;
+ }
+ /* Abort loading if this looks like a private key */
+ if (strncmp(cp, "-----BEGIN", 10) == 0 ||
+ strcmp(cp, "SSH PRIVATE KEY FILE") == 0)
+ break;
+ /* Skip leading whitespace. */
+ for (; *cp && (*cp == ' ' || *cp == '\t'); cp++)
+ ;
+ if (*cp) {
+ if ((r = sshkey_read(k, &cp)) == 0) {
+ cp[strcspn(cp, "\r\n")] = '\0';
+ if (commentp) {
+ *commentp = strdup(*cp ?
+ cp : filename);
+ if (*commentp == NULL)
+ r = SSH_ERR_ALLOC_FAIL;
}
+ fclose(f);
+ return r;
}
}
- fclose(f);
}
- return 0;
+ fclose(f);
+ return SSH_ERR_INVALID_FORMAT;
}
/* load public key from ssh v1 private or any pubkey file */
-Key *
-key_load_public(const char *filename, char **commentp)
+int
+sshkey_load_public(const char *filename, struct sshkey **keyp, char **commentp)
{
- Key *pub;
+ struct sshkey *pub = NULL;
char file[MAXPATHLEN];
+ int r, fd;
+
+ if (keyp != NULL)
+ *keyp = NULL;
+ if (commentp != NULL)
+ *commentp = NULL;
+ if ((fd = open(filename, O_RDONLY)) < 0)
+ goto skip;
#ifdef WITH_SSH1
/* try rsa1 private key */
- pub = key_load_public_type(KEY_RSA1, filename, commentp);
- if (pub != NULL)
- return pub;
+ r = sshkey_load_public_rsa1(fd, filename, keyp, commentp);
+ close(fd);
+ switch (r) {
+ case SSH_ERR_INTERNAL_ERROR:
+ case SSH_ERR_ALLOC_FAIL:
+ case SSH_ERR_INVALID_ARGUMENT:
+ case SSH_ERR_SYSTEM_ERROR:
+ case 0:
+ return r;
+ }
+#endif /* WITH_SSH1 */
+
+ /* try ssh2 public key */
+ if ((pub = sshkey_new(KEY_UNSPEC)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) {
+ if (keyp != NULL)
+ *keyp = pub;
+ return 0;
+ }
+ sshkey_free(pub);
+#ifdef WITH_SSH1
/* try rsa1 public key */
- pub = key_new(KEY_RSA1);
- if (key_try_load_public(pub, filename, commentp) == 1)
- return pub;
- key_free(pub);
-#endif
+ if ((pub = sshkey_new(KEY_RSA1)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if ((r = sshkey_try_load_public(pub, filename, commentp)) == 0) {
+ if (keyp != NULL)
+ *keyp = pub;
+ return 0;
+ }
+ sshkey_free(pub);
+#endif /* WITH_SSH1 */
- /* try ssh2 public key */
- pub = key_new(KEY_UNSPEC);
- if (key_try_load_public(pub, filename, commentp) == 1)
- return pub;
+ skip:
+ /* try .pub suffix */
+ if ((pub = sshkey_new(KEY_UNSPEC)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ r = SSH_ERR_ALLOC_FAIL; /* in case strlcpy or strlcat fail */
if ((strlcpy(file, filename, sizeof file) < sizeof(file)) &&
(strlcat(file, ".pub", sizeof file) < sizeof(file)) &&
- (key_try_load_public(pub, file, commentp) == 1))
- return pub;
- key_free(pub);
- return NULL;
+ (r = sshkey_try_load_public(pub, file, commentp)) == 0) {
+ if (keyp != NULL)
+ *keyp = pub;
+ return 0;
+ }
+ sshkey_free(pub);
+ return r;
}
/* Load the certificate associated with the named private key */
-Key *
-key_load_cert(const char *filename)
+int
+sshkey_load_cert(const char *filename, struct sshkey **keyp)
{
- Key *pub;
- char *file;
+ struct sshkey *pub = NULL;
+ char *file = NULL;
+ int r = SSH_ERR_INTERNAL_ERROR;
- pub = key_new(KEY_UNSPEC);
- xasprintf(&file, "%s-cert.pub", filename);
- if (key_try_load_public(pub, file, NULL) == 1) {
- free(file);
- return pub;
+ *keyp = NULL;
+
+ if (asprintf(&file, "%s-cert.pub", filename) == -1)
+ return SSH_ERR_ALLOC_FAIL;
+
+ if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
+ goto out;
}
- free(file);
- key_free(pub);
- return NULL;
+ if ((r = sshkey_try_load_public(pub, file, NULL)) != 0)
+ goto out;
+
+ *keyp = pub;
+ pub = NULL;
+ r = 0;
+
+ out:
+ if (file != NULL)
+ free(file);
+ if (pub != NULL)
+ sshkey_free(pub);
+ return r;
}
/* Load private key and certificate */
-Key *
-key_load_private_cert(int type, const char *filename, const char *passphrase,
- int *perm_ok)
+int
+sshkey_load_private_cert(int type, const char *filename, const char *passphrase,
+ struct sshkey **keyp, int *perm_ok)
{
- Key *key, *pub;
+ struct sshkey *key = NULL, *cert = NULL;
+ int r;
+
+ *keyp = NULL;
switch (type) {
#ifdef WITH_OPENSSL
case KEY_RSA:
case KEY_DSA:
case KEY_ECDSA:
-#endif
case KEY_ED25519:
+#endif /* WITH_OPENSSL */
+ case KEY_UNSPEC:
break;
default:
- error("%s: unsupported key type", __func__);
- return NULL;
+ return SSH_ERR_KEY_TYPE_UNKNOWN;
}
- if ((key = key_load_private_type(type, filename,
- passphrase, NULL, perm_ok)) == NULL)
- return NULL;
-
- if ((pub = key_load_cert(filename)) == NULL) {
- key_free(key);
- return NULL;
- }
+ if ((r = sshkey_load_private_type(type, filename,
+ passphrase, &key, NULL, perm_ok)) != 0 ||
+ (r = sshkey_load_cert(filename, &cert)) != 0)
+ goto out;
/* Make sure the private key matches the certificate */
- if (key_equal_public(key, pub) == 0) {
- error("%s: certificate does not match private key %s",
- __func__, filename);
- } else if (key_to_certified(key, key_cert_is_legacy(pub)) != 0) {
- error("%s: key_to_certified failed", __func__);
- } else {
- key_cert_copy(pub, key);
- key_free(pub);
- return key;
+ if (sshkey_equal_public(key, cert) == 0) {
+ r = SSH_ERR_KEY_CERT_MISMATCH;
+ goto out;
}
- key_free(key);
- key_free(pub);
- return NULL;
+ if ((r = sshkey_to_certified(key, sshkey_cert_is_legacy(cert))) != 0 ||
+ (r = sshkey_cert_copy(cert, key)) != 0)
+ goto out;
+ r = 0;
+ *keyp = key;
+ key = NULL;
+ out:
+ if (key != NULL)
+ sshkey_free(key);
+ if (cert != NULL)
+ sshkey_free(cert);
+ return r;
}
/*
- * Returns 1 if the specified "key" is listed in the file "filename",
- * 0 if the key is not listed or -1 on error.
+ * Returns success if the specified "key" is listed in the file "filename",
+ * SSH_ERR_KEY_NOT_FOUND: if the key is not listed or another error.
* If strict_type is set then the key type must match exactly,
* otherwise a comparison that ignores certficiate data is performed.
*/
int
-key_in_file(Key *key, const char *filename, int strict_type)
+sshkey_in_file(struct sshkey *key, const char *filename, int strict_type)
{
FILE *f;
char line[SSH_MAX_PUBKEY_BYTES];
char *cp;
u_long linenum = 0;
- int ret = 0;
- Key *pub;
- int (*key_compare)(const Key *, const Key *) = strict_type ?
- key_equal : key_equal_public;
+ int r = 0;
+ struct sshkey *pub = NULL;
+ int (*sshkey_compare)(const struct sshkey *, const struct sshkey *) =
+ strict_type ? sshkey_equal : sshkey_equal_public;
if ((f = fopen(filename, "r")) == NULL) {
- if (errno == ENOENT) {
- debug("%s: keyfile \"%s\" missing", __func__, filename);
- return 0;
- } else {
- error("%s: could not open keyfile \"%s\": %s", __func__,
- filename, strerror(errno));
- return -1;
- }
+ if (errno == ENOENT)
+ return SSH_ERR_KEY_NOT_FOUND;
+ else
+ return SSH_ERR_SYSTEM_ERROR;
}
while (read_keyfile_line(f, filename, line, sizeof(line),
- &linenum) != -1) {
+ &linenum) != -1) {
cp = line;
/* Skip leading whitespace. */
@@ -1323,18 +532,24 @@ key_in_file(Key *key, const char *filename, int strict_type)
continue;
}
- pub = key_new(KEY_UNSPEC);
- if (key_read(pub, &cp) != 1) {
- key_free(pub);
- continue;
+ if ((pub = sshkey_new(KEY_UNSPEC)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
}
- if (key_compare(key, pub)) {
- ret = 1;
- key_free(pub);
- break;
+ if ((r = sshkey_read(pub, &cp)) != 0)
+ goto out;
+ if (sshkey_compare(key, pub)) {
+ r = 0;
+ goto out;
}
- key_free(pub);
+ sshkey_free(pub);
+ pub = NULL;
}
+ r = SSH_ERR_KEY_NOT_FOUND;
+ out:
+ if (pub != NULL)
+ sshkey_free(pub);
fclose(f);
- return ret;
+ return r;
}
+
diff --git a/authfile.h b/authfile.h
index 8ba1c2db..22310120 100644
--- a/authfile.h
+++ b/authfile.h
@@ -1,32 +1,51 @@
-/* $OpenBSD: authfile.h,v 1.17 2013/12/06 13:34:54 markus Exp $ */
+/* $OpenBSD: authfile.h,v 1.18 2014/06/24 01:13:21 djm Exp $ */
/*
- * Author: Tatu Ylonen <ylo@cs.hut.fi>
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- * All rights reserved
+ * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved.
*
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose. Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef AUTHFILE_H
#define AUTHFILE_H
-int key_save_private(Key *, const char *, const char *, const char *,
- int, const char *, int);
-int key_load_file(int, const char *, Buffer *);
-Key *key_load_cert(const char *);
-Key *key_load_public(const char *, char **);
-Key *key_load_public_type(int, const char *, char **);
-Key *key_parse_private(Buffer *, const char *, const char *, char **);
-Key *key_load_private(const char *, const char *, char **);
-Key *key_load_private_cert(int, const char *, const char *, int *);
-Key *key_load_private_type(int, const char *, const char *, char **, int *);
-Key *key_load_private_pem(int, int, const char *, char **);
-int key_perm_ok(int, const char *);
-int key_in_file(Key *, const char *, int);
+#ifdef WITH_LEAKMALLOC
+#include "leakmalloc.h"
+#endif
+
+struct sshbuf;
+struct sshkey;
+
+int sshkey_save_private(struct sshkey *, const char *,
+ const char *, const char *, int, const char *, int);
+int sshkey_load_file(int, const char *, struct sshbuf *);
+int sshkey_load_cert(const char *, struct sshkey **);
+int sshkey_load_public(const char *, struct sshkey **, char **);
+int sshkey_load_private(const char *, const char *, struct sshkey **, char **);
+int sshkey_load_private_cert(int, const char *, const char *,
+ struct sshkey **, int *);
+int sshkey_load_private_type(int, const char *, const char *,
+ struct sshkey **, char **, int *);
+int sshkey_load_private_pem(int, int, const char *, struct sshkey **, char **);
+int sshkey_perm_ok(int, const char *);
+int sshkey_in_file(struct sshkey *, const char *, int);
#endif
diff --git a/cipher-3des1.c b/cipher-3des1.c
index b2823592..5361f517 100644
--- a/cipher-3des1.c
+++ b/cipher-3des1.c
@@ -29,13 +29,11 @@
#include <openssl/evp.h>
-#include <stdarg.h>
#include <string.h>
#include "xmalloc.h"
#include "log.h"
-
-#include "openbsd-compat/openssl-compat.h"
+#include "ssherr.h"
/*
* This is used by SSH1:
@@ -57,7 +55,7 @@ struct ssh1_3des_ctx
};
const EVP_CIPHER * evp_ssh1_3des(void);
-void ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int);
+int ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int);
static int
ssh1_3des_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv,
@@ -67,11 +65,12 @@ ssh1_3des_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv,
u_char *k1, *k2, *k3;
if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) {
- c = xcalloc(1, sizeof(*c));
+ if ((c = calloc(1, sizeof(*c))) == NULL)
+ return 0;
EVP_CIPHER_CTX_set_app_data(ctx, c);
}
if (key == NULL)
- return (1);
+ return 1;
if (enc == -1)
enc = ctx->encrypt;
k1 = k2 = k3 = (u_char *) key;
@@ -85,44 +84,29 @@ ssh1_3des_init(EVP_CIPHER_CTX *ctx, const u_char *key, const u_char *iv,
EVP_CIPHER_CTX_init(&c->k1);
EVP_CIPHER_CTX_init(&c->k2);
EVP_CIPHER_CTX_init(&c->k3);
-#ifdef SSH_OLD_EVP
- EVP_CipherInit(&c->k1, EVP_des_cbc(), k1, NULL, enc);
- EVP_CipherInit(&c->k2, EVP_des_cbc(), k2, NULL, !enc);
- EVP_CipherInit(&c->k3, EVP_des_cbc(), k3, NULL, enc);
-#else
if (EVP_CipherInit(&c->k1, EVP_des_cbc(), k1, NULL, enc) == 0 ||
EVP_CipherInit(&c->k2, EVP_des_cbc(), k2, NULL, !enc) == 0 ||
EVP_CipherInit(&c->k3, EVP_des_cbc(), k3, NULL, enc) == 0) {
explicit_bzero(c, sizeof(*c));
free(c);
EVP_CIPHER_CTX_set_app_data(ctx, NULL);
- return (0);
+ return 0;
}
-#endif
- return (1);
+ return 1;
}
static int
-ssh1_3des_cbc(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src,
- LIBCRYPTO_EVP_INL_TYPE len)
+ssh1_3des_cbc(EVP_CIPHER_CTX *ctx, u_char *dest, const u_char *src, size_t len)
{
struct ssh1_3des_ctx *c;
- if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL) {
- error("ssh1_3des_cbc: no context");
- return (0);
- }
-#ifdef SSH_OLD_EVP
- EVP_Cipher(&c->k1, dest, (u_char *)src, len);
- EVP_Cipher(&c->k2, dest, dest, len);
- EVP_Cipher(&c->k3, dest, dest, len);
-#else
+ if ((c = EVP_CIPHER_CTX_get_app_data(ctx)) == NULL)
+ return 0;
if (EVP_Cipher(&c->k1, dest, (u_char *)src, len) == 0 ||
EVP_Cipher(&c->k2, dest, dest, len) == 0 ||
EVP_Cipher(&c->k3, dest, dest, len) == 0)
- return (0);
-#endif
- return (1);
+ return 0;
+ return 1;
}
static int
@@ -138,29 +122,28 @@ ssh1_3des_cleanup(EVP_CIPHER_CTX *ctx)
free(c);
EVP_CIPHER_CTX_set_app_data(ctx, NULL);
}
- return (1);
+ return 1;
}
-void
+int
ssh1_3des_iv(EVP_CIPHER_CTX *evp, int doset, u_char *iv, int len)
{
struct ssh1_3des_ctx *c;
if (len != 24)
- fatal("%s: bad 3des iv length: %d", __func__, len);
+ return SSH_ERR_INVALID_ARGUMENT;
if ((c = EVP_CIPHER_CTX_get_app_data(evp)) == NULL)
- fatal("%s: no 3des context", __func__);
+ return SSH_ERR_INTERNAL_ERROR;
if (doset) {
- debug3("%s: Installed 3DES IV", __func__);
memcpy(c->k1.iv, iv, 8);
memcpy(c->k2.iv, iv + 8, 8);
memcpy(c->k3.iv, iv + 16, 8);
} else {
- debug3("%s: Copying 3DES IV", __func__);
memcpy(iv, c->k1.iv, 8);
memcpy(iv + 8, c->k2.iv, 8);
memcpy(iv + 16, c->k3.iv, 8);
}
+ return 0;
}
const EVP_CIPHER *
@@ -176,8 +159,6 @@ evp_ssh1_3des(void)
ssh1_3des.init = ssh1_3des_init;
ssh1_3des.cleanup = ssh1_3des_cleanup;
ssh1_3des.do_cipher = ssh1_3des_cbc;
-#ifndef SSH_OLD_EVP
ssh1_3des.flags = EVP_CIPH_CBC_MODE | EVP_CIPH_VARIABLE_LENGTH;
-#endif
- return (&ssh1_3des);
+ return &ssh1_3des;
}
diff --git a/cipher-chachapoly.c b/cipher-chachapoly.c
index 251b94ec..0caccd29 100644
--- a/cipher-chachapoly.c
+++ b/cipher-chachapoly.c
@@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/* $OpenBSD: cipher-chachapoly.c,v 1.4 2014/01/31 16:39:19 tedu Exp $ */
+/* $OpenBSD: cipher-chachapoly.c,v 1.5 2014/06/24 01:13:21 djm Exp $ */
#include "includes.h"
@@ -24,16 +24,18 @@
#include <stdio.h> /* needed for misc.h */
#include "log.h"
-#include "misc.h"
+#include "sshbuf.h"
+#include "ssherr.h"
#include "cipher-chachapoly.h"
-void chachapoly_init(struct chachapoly_ctx *ctx,
+int chachapoly_init(struct chachapoly_ctx *ctx,
const u_char *key, u_int keylen)
{
if (keylen != (32 + 32)) /* 2 x 256 bit keys */
- fatal("%s: invalid keylen %u", __func__, keylen);
+ return SSH_ERR_INVALID_ARGUMENT;
chacha_keysetup(&ctx->main_ctx, key, 256);
chacha_keysetup(&ctx->header_ctx, key + 32, 256);
+ return 0;
}
/*
@@ -52,14 +54,14 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
u_char seqbuf[8];
const u_char one[8] = { 1, 0, 0, 0, 0, 0, 0, 0 }; /* NB little-endian */
u_char expected_tag[POLY1305_TAGLEN], poly_key[POLY1305_KEYLEN];
- int r = -1;
+ int r = SSH_ERR_INTERNAL_ERROR;
/*
* Run ChaCha20 once to generate the Poly1305 key. The IV is the
* packet sequence number.
*/
memset(poly_key, 0, sizeof(poly_key));
- put_u64(seqbuf, seqnr);
+ POKE_U64(seqbuf, seqnr);
chacha_ivsetup(&ctx->main_ctx, seqbuf, NULL);
chacha_encrypt_bytes(&ctx->main_ctx,
poly_key, poly_key, sizeof(poly_key));
@@ -71,8 +73,10 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
const u_char *tag = src + aadlen + len;
poly1305_auth(expected_tag, src, aadlen + len, poly_key);
- if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0)
+ if (timingsafe_bcmp(expected_tag, tag, POLY1305_TAGLEN) != 0) {
+ r = SSH_ERR_MAC_INVALID;
goto out;
+ }
}
/* Crypt additional data */
if (aadlen) {
@@ -88,7 +92,6 @@ chachapoly_crypt(struct chachapoly_ctx *ctx, u_int seqnr, u_char *dest,
poly_key);
}
r = 0;
-
out:
explicit_bzero(expected_tag, sizeof(expected_tag));
explicit_bzero(seqbuf, sizeof(seqbuf));
@@ -104,11 +107,11 @@ chachapoly_get_length(struct chachapoly_ctx *ctx,
u_char buf[4], seqbuf[8];
if (len < 4)
- return -1; /* Insufficient length */
- put_u64(seqbuf, seqnr);
+ return SSH_ERR_MESSAGE_INCOMPLETE;
+ POKE_U64(seqbuf, seqnr);
chacha_ivsetup(&ctx->header_ctx, seqbuf, NULL);
chacha_encrypt_bytes(&ctx->header_ctx, cp, buf, 4);
- *plenp = get_u32(buf);
+ *plenp = PEEK_U32(buf);
return 0;
}
diff --git a/cipher-chachapoly.h b/cipher-chachapoly.h
index 7948dcdc..b7072be7 100644
--- a/cipher-chachapoly.h
+++ b/cipher-chachapoly.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cipher-chachapoly.h,v 1.3 2014/05/02 03:27:54 djm Exp $ */
+/* $OpenBSD: cipher-chachapoly.h,v 1.4 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) Damien Miller 2013 <djm@mindrot.org>
@@ -28,7 +28,7 @@ struct chachapoly_ctx {
struct chacha_ctx main_ctx, header_ctx;
};
-void chachapoly_init(struct chachapoly_ctx *cpctx,
+int chachapoly_init(struct chachapoly_ctx *cpctx,
const u_char *key, u_int keylen)
__attribute__((__bounded__(__buffer__, 2, 3)));
int chachapoly_crypt(struct chachapoly_ctx *cpctx, u_int seqnr,
diff --git a/cipher.c b/cipher.c
index 5569d245..48ef105c 100644
--- a/cipher.c
+++ b/cipher.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cipher.c,v 1.98 2014/04/29 18:01:49 markus Exp $ */
+/* $OpenBSD: cipher.c,v 1.99 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -43,23 +43,19 @@
#include <stdarg.h>
#include <stdio.h>
-#include "xmalloc.h"
-#include "log.h"
-#include "misc.h"
#include "cipher.h"
-#include "buffer.h"
+#include "misc.h"
+#include "sshbuf.h"
+#include "ssherr.h"
#include "digest.h"
-/* compatibility with old or broken OpenSSL versions */
-#include "openbsd-compat/openssl-compat.h"
-
#ifdef WITH_SSH1
extern const EVP_CIPHER *evp_ssh1_bf(void);
extern const EVP_CIPHER *evp_ssh1_3des(void);
-extern void ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int);
+extern int ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int);
#endif
-struct Cipher {
+struct sshcipher {
char *name;
int number; /* for ssh1 only */
u_int block_size;
@@ -79,12 +75,12 @@ struct Cipher {
#endif
};
-static const struct Cipher ciphers[] = {
+static const struct sshcipher ciphers[] = {
#ifdef WITH_SSH1
{ "des", SSH_CIPHER_DES, 8, 8, 0, 0, 0, 1, EVP_des_cbc },
{ "3des", SSH_CIPHER_3DES, 8, 16, 0, 0, 0, 1, evp_ssh1_3des },
{ "blowfish", SSH_CIPHER_BLOWFISH, 8, 32, 0, 0, 0, 1, evp_ssh1_bf },
-#endif
+#endif /* WITH_SSH1 */
#ifdef WITH_OPENSSL
{ "none", SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null },
{ "3des-cbc", SSH_CIPHER_SSH2, 8, 24, 0, 0, 0, 1, EVP_des_ede3_cbc },
@@ -103,12 +99,12 @@ static const struct Cipher ciphers[] = {
{ "aes128-ctr", SSH_CIPHER_SSH2, 16, 16, 0, 0, 0, 0, EVP_aes_128_ctr },
{ "aes192-ctr", SSH_CIPHER_SSH2, 16, 24, 0, 0, 0, 0, EVP_aes_192_ctr },
{ "aes256-ctr", SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, 0, EVP_aes_256_ctr },
-#ifdef OPENSSL_HAVE_EVPGCM
+# ifdef OPENSSL_HAVE_EVPGCM
{ "aes128-gcm@openssh.com",
SSH_CIPHER_SSH2, 16, 16, 12, 16, 0, 0, EVP_aes_128_gcm },
{ "aes256-gcm@openssh.com",
SSH_CIPHER_SSH2, 16, 32, 12, 16, 0, 0, EVP_aes_256_gcm },
-#endif
+# endif /* OPENSSL_HAVE_EVPGCM */
#else /* WITH_OPENSSL */
{ "aes128-ctr", SSH_CIPHER_SSH2, 16, 16, 0, 0, 0, CFLAG_AESCTR, NULL },
{ "aes192-ctr", SSH_CIPHER_SSH2, 16, 24, 0, 0, 0, CFLAG_AESCTR, NULL },
@@ -117,18 +113,19 @@ static const struct Cipher ciphers[] = {
#endif /* WITH_OPENSSL */
{ "chacha20-poly1305@openssh.com",
SSH_CIPHER_SSH2, 8, 64, 0, 16, 0, CFLAG_CHACHAPOLY, NULL },
+
{ NULL, SSH_CIPHER_INVALID, 0, 0, 0, 0, 0, 0, NULL }
};
/*--*/
-/* Returns a list of supported ciphers separated by the specified char. */
+/* Returns a comma-separated list of supported ciphers. */
char *
cipher_alg_list(char sep, int auth_only)
{
- char *ret = NULL;
+ char *tmp, *ret = NULL;
size_t nlen, rlen = 0;
- const Cipher *c;
+ const struct sshcipher *c;
for (c = ciphers; c->name != NULL; c++) {
if (c->number != SSH_CIPHER_SSH2)
@@ -138,7 +135,11 @@ cipher_alg_list(char sep, int auth_only)
if (ret != NULL)
ret[rlen++] = sep;
nlen = strlen(c->name);
- ret = xrealloc(ret, 1, rlen + nlen + 2);
+ if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
+ free(ret);
+ return NULL;
+ }
+ ret = tmp;
memcpy(ret + rlen, c->name, nlen + 1);
rlen += nlen;
}
@@ -146,19 +147,19 @@ cipher_alg_list(char sep, int auth_only)
}
u_int
-cipher_blocksize(const Cipher *c)
+cipher_blocksize(const struct sshcipher *c)
{
return (c->block_size);
}
u_int
-cipher_keylen(const Cipher *c)
+cipher_keylen(const struct sshcipher *c)
{
return (c->key_len);
}
u_int
-cipher_seclen(const Cipher *c)
+cipher_seclen(const struct sshcipher *c)
{
if (strcmp("3des-cbc", c->name) == 0)
return 14;
@@ -166,13 +167,13 @@ cipher_seclen(const Cipher *c)
}
u_int
-cipher_authlen(const Cipher *c)
+cipher_authlen(const struct sshcipher *c)
{
return (c->auth_len);
}
u_int
-cipher_ivlen(const Cipher *c)
+cipher_ivlen(const struct sshcipher *c)
{
/*
* Default is cipher block size, except for chacha20+poly1305 that
@@ -183,13 +184,13 @@ cipher_ivlen(const Cipher *c)
}
u_int
-cipher_get_number(const Cipher *c)
+cipher_get_number(const struct sshcipher *c)
{
return (c->number);
}
u_int
-cipher_is_cbc(const Cipher *c)
+cipher_is_cbc(const struct sshcipher *c)
{
return (c->flags & CFLAG_CBC) != 0;
}
@@ -206,20 +207,20 @@ cipher_mask_ssh1(int client)
return mask;
}
-const Cipher *
+const struct sshcipher *
cipher_by_name(const char *name)
{
- const Cipher *c;
+ const struct sshcipher *c;
for (c = ciphers; c->name != NULL; c++)
if (strcmp(c->name, name) == 0)
return c;
return NULL;
}
-const Cipher *
+const struct sshcipher *
cipher_by_number(int id)
{
- const Cipher *c;
+ const struct sshcipher *c;
for (c = ciphers; c->name != NULL; c++)
if (c->number == id)
return c;
@@ -230,23 +231,22 @@ cipher_by_number(int id)
int
ciphers_valid(const char *names)
{
- const Cipher *c;
+ const struct sshcipher *c;
char *cipher_list, *cp;
char *p;
if (names == NULL || strcmp(names, "") == 0)
return 0;
- cipher_list = cp = xstrdup(names);
+ if ((cipher_list = cp = strdup(names)) == NULL)
+ return 0;
for ((p = strsep(&cp, CIPHER_SEP)); p && *p != '\0';
(p = strsep(&cp, CIPHER_SEP))) {
c = cipher_by_name(p);
if (c == NULL || c->number != SSH_CIPHER_SSH2) {
- debug("bad cipher %s [%s]", p, names);
free(cipher_list);
return 0;
}
}
- debug3("ciphers ok: [%s]", names);
free(cipher_list);
return 1;
}
@@ -259,7 +259,7 @@ ciphers_valid(const char *names)
int
cipher_number(const char *name)
{
- const Cipher *c;
+ const struct sshcipher *c;
if (name == NULL)
return -1;
for (c = ciphers; c->name != NULL; c++)
@@ -271,31 +271,33 @@ cipher_number(const char *name)
char *
cipher_name(int id)
{
- const Cipher *c = cipher_by_number(id);
+ const struct sshcipher *c = cipher_by_number(id);
return (c==NULL) ? "<unknown>" : c->name;
}
-void
-cipher_init(CipherContext *cc, const Cipher *cipher,
+const char *
+cipher_warning_message(const struct sshcipher_ctx *cc)
+{
+ if (cc == NULL || cc->cipher == NULL)
+ return NULL;
+ if (cc->cipher->number == SSH_CIPHER_DES)
+ return "use of DES is strongly discouraged due to "
+ "cryptographic weaknesses";
+ return NULL;
+}
+
+int
+cipher_init(struct sshcipher_ctx *cc, const struct sshcipher *cipher,
const u_char *key, u_int keylen, const u_char *iv, u_int ivlen,
int do_encrypt)
{
#ifdef WITH_OPENSSL
- static int dowarn = 1;
-#ifdef SSH_OLD_EVP
- EVP_CIPHER *type;
-#else
+ int ret = SSH_ERR_INTERNAL_ERROR;
const EVP_CIPHER *type;
int klen;
-#endif
u_char *junk, *discard;
if (cipher->number == SSH_CIPHER_DES) {
- if (dowarn) {
- error("Warning: use of DES is strongly discouraged "
- "due to cryptographic weaknesses");
- dowarn = 0;
- }
if (keylen > 8)
keylen = 8;
}
@@ -303,71 +305,70 @@ cipher_init(CipherContext *cc, const Cipher *cipher,
cc->plaintext = (cipher->number == SSH_CIPHER_NONE);
cc->encrypt = do_encrypt;
- if (keylen < cipher->key_len)
- fatal("cipher_init: key length %d is insufficient for %s.",
- keylen, cipher->name);
- if (iv != NULL && ivlen < cipher_ivlen(cipher))
- fatal("cipher_init: iv length %d is insufficient for %s.",
- ivlen, cipher->name);
- cc->cipher = cipher;
+ if (keylen < cipher->key_len ||
+ (iv != NULL && ivlen < cipher_ivlen(cipher)))
+ return SSH_ERR_INVALID_ARGUMENT;
+ cc->cipher = cipher;
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) {
- chachapoly_init(&cc->cp_ctx, key, keylen);
- return;
+ return chachapoly_init(&cc->cp_ctx, key, keylen);
}
#ifndef WITH_OPENSSL
if ((cc->cipher->flags & CFLAG_AESCTR) != 0) {
aesctr_keysetup(&cc->ac_ctx, key, 8 * keylen, 8 * ivlen);
aesctr_ivsetup(&cc->ac_ctx, iv);
- return;
+ return 0;
}
if ((cc->cipher->flags & CFLAG_NONE) != 0)
- return;
- fatal("unsupported cipher");
+ return 0;
+ return SSH_ERR_INVALID_ARGUMENT;
#else
type = (*cipher->evptype)();
EVP_CIPHER_CTX_init(&cc->evp);
-#ifdef SSH_OLD_EVP
- if (type->key_len > 0 && type->key_len != keylen) {
- debug("cipher_init: set keylen (%d -> %d)",
- type->key_len, keylen);
- type->key_len = keylen;
- }
- EVP_CipherInit(&cc->evp, type, (u_char *)key, (u_char *)iv,
- (do_encrypt == CIPHER_ENCRYPT));
-#else
if (EVP_CipherInit(&cc->evp, type, NULL, (u_char *)iv,
- (do_encrypt == CIPHER_ENCRYPT)) == 0)
- fatal("cipher_init: EVP_CipherInit failed for %s",
- cipher->name);
+ (do_encrypt == CIPHER_ENCRYPT)) == 0) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto bad;
+ }
if (cipher_authlen(cipher) &&
!EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_SET_IV_FIXED,
- -1, (u_char *)iv))
- fatal("cipher_init: EVP_CTRL_GCM_SET_IV_FIXED failed for %s",
- cipher->name);
+ -1, (u_char *)iv)) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto bad;
+ }
klen = EVP_CIPHER_CTX_key_length(&cc->evp);
if (klen > 0 && keylen != (u_int)klen) {
- debug2("cipher_init: set keylen (%d -> %d)", klen, keylen);
- if (EVP_CIPHER_CTX_set_key_length(&cc->evp, keylen) == 0)
- fatal("cipher_init: set keylen failed (%d -> %d)",
- klen, keylen);
+ if (EVP_CIPHER_CTX_set_key_length(&cc->evp, keylen) == 0) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto bad;
+ }
+ }
+ if (EVP_CipherInit(&cc->evp, NULL, (u_char *)key, NULL, -1) == 0) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto bad;
}
- if (EVP_CipherInit(&cc->evp, NULL, (u_char *)key, NULL, -1) == 0)
- fatal("cipher_init: EVP_CipherInit: set key failed for %s",
- cipher->name);
-#endif
if (cipher->discard_len > 0) {
- junk = xmalloc(cipher->discard_len);
- discard = xmalloc(cipher->discard_len);
- if (EVP_Cipher(&cc->evp, discard, junk,
- cipher->discard_len) == 0)
- fatal("evp_crypt: EVP_Cipher failed during discard");
+ if ((junk = malloc(cipher->discard_len)) == NULL ||
+ (discard = malloc(cipher->discard_len)) == NULL) {
+ if (junk != NULL)
+ free(junk);
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto bad;
+ }
+ ret = EVP_Cipher(&cc->evp, discard, junk, cipher->discard_len);
explicit_bzero(discard, cipher->discard_len);
free(junk);
free(discard);
+ if (ret != 1) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ bad:
+ EVP_CIPHER_CTX_cleanup(&cc->evp);
+ return ret;
+ }
}
#endif
+ return 0;
}
/*
@@ -379,16 +380,15 @@ cipher_init(CipherContext *cc, const Cipher *cipher,
* Use 'authlen' bytes at offset 'len'+'aadlen' as the authentication tag.
* This tag is written on encryption and verified on decryption.
* Both 'aadlen' and 'authlen' can be set to 0.
- * cipher_crypt() returns 0 on success and -1 if the decryption integrity
- * check fails.
*/
int
-cipher_crypt(CipherContext *cc, u_int seqnr, u_char *dest, const u_char *src,
- u_int len, u_int aadlen, u_int authlen)
+cipher_crypt(struct sshcipher_ctx *cc, u_int seqnr, u_char *dest,
+ const u_char *src, u_int len, u_int aadlen, u_int authlen)
{
- if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0)
- return chachapoly_crypt(&cc->cp_ctx, seqnr, dest, src, len,
- aadlen, authlen, cc->encrypt);
+ if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) {
+ return chachapoly_crypt(&cc->cp_ctx, seqnr, dest, src,
+ len, aadlen, authlen, cc->encrypt);
+ }
#ifndef WITH_OPENSSL
if ((cc->cipher->flags & CFLAG_AESCTR) != 0) {
if (aadlen)
@@ -401,46 +401,43 @@ cipher_crypt(CipherContext *cc, u_int seqnr, u_char *dest, const u_char *src,
memcpy(dest, src, aadlen + len);
return 0;
}
- fatal("unsupported cipher");
+ return SSH_ERR_INVALID_ARGUMENT;
#else
if (authlen) {
u_char lastiv[1];
if (authlen != cipher_authlen(cc->cipher))
- fatal("%s: authlen mismatch %d", __func__, authlen);
+ return SSH_ERR_INVALID_ARGUMENT;
/* increment IV */
if (!EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_IV_GEN,
1, lastiv))
- fatal("%s: EVP_CTRL_GCM_IV_GEN", __func__);
+ return SSH_ERR_LIBCRYPTO_ERROR;
/* set tag on decyption */
if (!cc->encrypt &&
!EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_SET_TAG,
authlen, (u_char *)src + aadlen + len))
- fatal("%s: EVP_CTRL_GCM_SET_TAG", __func__);
+ return SSH_ERR_LIBCRYPTO_ERROR;
}
if (aadlen) {
if (authlen &&
EVP_Cipher(&cc->evp, NULL, (u_char *)src, aadlen) < 0)
- fatal("%s: EVP_Cipher(aad) failed", __func__);
+ return SSH_ERR_LIBCRYPTO_ERROR;
memcpy(dest, src, aadlen);
}
if (len % cc->cipher->block_size)
- fatal("%s: bad plaintext length %d", __func__, len);
+ return SSH_ERR_INVALID_ARGUMENT;
if (EVP_Cipher(&cc->evp, dest + aadlen, (u_char *)src + aadlen,
len) < 0)
- fatal("%s: EVP_Cipher failed", __func__);
+ return SSH_ERR_LIBCRYPTO_ERROR;
if (authlen) {
/* compute tag (on encrypt) or verify tag (on decrypt) */
- if (EVP_Cipher(&cc->evp, NULL, NULL, 0) < 0) {
- if (cc->encrypt)
- fatal("%s: EVP_Cipher(final) failed", __func__);
- else
- return -1;
- }
+ if (EVP_Cipher(&cc->evp, NULL, NULL, 0) < 0)
+ return cc->encrypt ?
+ SSH_ERR_LIBCRYPTO_ERROR : SSH_ERR_MAC_INVALID;
if (cc->encrypt &&
!EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_GET_TAG,
authlen, dest + aadlen + len))
- fatal("%s: EVP_CTRL_GCM_GET_TAG", __func__);
+ return SSH_ERR_LIBCRYPTO_ERROR;
}
return 0;
#endif
@@ -448,61 +445,65 @@ cipher_crypt(CipherContext *cc, u_int seqnr, u_char *dest, const u_char *src,
/* Extract the packet length, including any decryption necessary beforehand */
int
-cipher_get_length(CipherContext *cc, u_int *plenp, u_int seqnr,
+cipher_get_length(struct sshcipher_ctx *cc, u_int *plenp, u_int seqnr,
const u_char *cp, u_int len)
{
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0)
return chachapoly_get_length(&cc->cp_ctx, plenp, seqnr,
cp, len);
if (len < 4)
- return -1;
+ return SSH_ERR_MESSAGE_INCOMPLETE;
*plenp = get_u32(cp);
return 0;
}
-void
-cipher_cleanup(CipherContext *cc)
+int
+cipher_cleanup(struct sshcipher_ctx *cc)
{
+ if (cc == NULL || cc->cipher == NULL)
+ return 0;
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0)
explicit_bzero(&cc->cp_ctx, sizeof(cc->cp_ctx));
else if ((cc->cipher->flags & CFLAG_AESCTR) != 0)
explicit_bzero(&cc->ac_ctx, sizeof(cc->ac_ctx));
#ifdef WITH_OPENSSL
else if (EVP_CIPHER_CTX_cleanup(&cc->evp) == 0)
- error("cipher_cleanup: EVP_CIPHER_CTX_cleanup failed");
+ return SSH_ERR_LIBCRYPTO_ERROR;
#endif
+ return 0;
}
/*
* Selects the cipher, and keys if by computing the MD5 checksum of the
* passphrase and using the resulting 16 bytes as the key.
*/
-
-void
-cipher_set_key_string(CipherContext *cc, const Cipher *cipher,
+int
+cipher_set_key_string(struct sshcipher_ctx *cc, const struct sshcipher *cipher,
const char *passphrase, int do_encrypt)
{
u_char digest[16];
+ int r = SSH_ERR_INTERNAL_ERROR;
- if (ssh_digest_memory(SSH_DIGEST_MD5, passphrase, strlen(passphrase),
- digest, sizeof(digest)) < 0)
- fatal("%s: md5 failed", __func__);
-
- cipher_init(cc, cipher, digest, 16, NULL, 0, do_encrypt);
+ if ((r = ssh_digest_memory(SSH_DIGEST_MD5,
+ passphrase, strlen(passphrase),
+ digest, sizeof(digest))) != 0)
+ goto out;
+ r = cipher_init(cc, cipher, digest, 16, NULL, 0, do_encrypt);
+ out:
explicit_bzero(digest, sizeof(digest));
+ return r;
}
/*
- * Exports an IV from the CipherContext required to export the key
+ * Exports an IV from the sshcipher_ctx required to export the key
* state back from the unprivileged child to the privileged parent
* process.
*/
-
int
-cipher_get_keyiv_len(const CipherContext *cc)
+cipher_get_keyiv_len(const struct sshcipher_ctx *cc)
{
- const Cipher *c = cc->cipher;
+ const struct sshcipher *c = cc->cipher;
int ivlen = 0;
if (c->number == SSH_CIPHER_3DES)
@@ -512,25 +513,25 @@ cipher_get_keyiv_len(const CipherContext *cc)
#ifdef WITH_OPENSSL
else
ivlen = EVP_CIPHER_CTX_iv_length(&cc->evp);
-#endif
+#endif /* WITH_OPENSSL */
return (ivlen);
}
-void
-cipher_get_keyiv(CipherContext *cc, u_char *iv, u_int len)
+int
+cipher_get_keyiv(struct sshcipher_ctx *cc, u_char *iv, u_int len)
{
- const Cipher *c = cc->cipher;
+ const struct sshcipher *c = cc->cipher;
#ifdef WITH_OPENSSL
- int evplen;
+ int evplen;
#endif
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) {
if (len != 0)
- fatal("%s: wrong iv length %d != %d", __func__, len, 0);
- return;
+ return SSH_ERR_INVALID_ARGUMENT;
+ return 0;
}
if ((cc->cipher->flags & CFLAG_NONE) != 0)
- return;
+ return 0;
switch (c->number) {
#ifdef WITH_OPENSSL
@@ -538,51 +539,42 @@ cipher_get_keyiv(CipherContext *cc, u_char *iv, u_int len)
case SSH_CIPHER_DES:
case SSH_CIPHER_BLOWFISH:
evplen = EVP_CIPHER_CTX_iv_length(&cc->evp);
- if (evplen <= 0)
- return;
+ if (evplen == 0)
+ return 0;
+ else if (evplen < 0)
+ return SSH_ERR_LIBCRYPTO_ERROR;
if ((u_int)evplen != len)
- fatal("%s: wrong iv length %d != %d", __func__,
- evplen, len);
-#ifdef USE_BUILTIN_RIJNDAEL
- if (c->evptype == evp_rijndael)
- ssh_rijndael_iv(&cc->evp, 0, iv, len);
- else
-#endif /* USE_BUILTIN_RIJNDAEL */
-#ifndef OPENSSL_HAVE_EVPCTR
- if (c->evptype == evp_aes_128_ctr)
- ssh_aes_ctr_iv(&cc->evp, 0, iv, len);
- else
-#endif /* OPENSSL_HAVE_EVPCTR */
+ return SSH_ERR_INVALID_ARGUMENT;
if (cipher_authlen(c)) {
if (!EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_IV_GEN,
- len, iv))
- fatal("%s: EVP_CTRL_GCM_IV_GEN", __func__);
- } else
+ len, iv))
+ return SSH_ERR_LIBCRYPTO_ERROR;
+ } else
memcpy(iv, cc->evp.iv, len);
break;
-#endif /* WITH_OPENSSL */
+#endif
#ifdef WITH_SSH1
case SSH_CIPHER_3DES:
- ssh1_3des_iv(&cc->evp, 0, iv, 24);
- break;
-#endif /* WITH_SSH1 */
+ return ssh1_3des_iv(&cc->evp, 0, iv, 24);
+#endif
default:
- fatal("%s: bad cipher %d", __func__, c->number);
+ return SSH_ERR_INVALID_ARGUMENT;
}
+ return 0;
}
-void
-cipher_set_keyiv(CipherContext *cc, u_char *iv)
+int
+cipher_set_keyiv(struct sshcipher_ctx *cc, const u_char *iv)
{
- const Cipher *c = cc->cipher;
+ const struct sshcipher *c = cc->cipher;
#ifdef WITH_OPENSSL
- int evplen = 0;
+ int evplen = 0;
#endif
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0)
- return;
+ return 0;
if ((cc->cipher->flags & CFLAG_NONE) != 0)
- return;
+ return 0;
switch (c->number) {
#ifdef WITH_OPENSSL
@@ -590,42 +582,37 @@ cipher_set_keyiv(CipherContext *cc, u_char *iv)
case SSH_CIPHER_DES:
case SSH_CIPHER_BLOWFISH:
evplen = EVP_CIPHER_CTX_iv_length(&cc->evp);
- if (evplen == 0)
- return;
-#ifdef USE_BUILTIN_RIJNDAEL
- if (c->evptype == evp_rijndael)
- ssh_rijndael_iv(&cc->evp, 1, iv, evplen);
- else
-#endif /* USE_BUILTIN_RIJNDAEL */
-#ifndef OPENSSL_HAVE_EVPCTR
- if (c->evptype == evp_aes_128_ctr)
- ssh_aes_ctr_iv(&cc->evp, 1, iv, evplen);
- else
-#endif /* OPENSSL_HAVE_EVPCTR */
+ if (evplen <= 0)
+ return SSH_ERR_LIBCRYPTO_ERROR;
if (cipher_authlen(c)) {
+ /* XXX iv arg is const, but EVP_CIPHER_CTX_ctrl isn't */
if (!EVP_CIPHER_CTX_ctrl(&cc->evp,
- EVP_CTRL_GCM_SET_IV_FIXED, -1, iv))
- fatal("%s: EVP_CTRL_GCM_SET_IV_FIXED failed",
- __func__);
- } else
- memcpy(cc->evp.iv, iv, evplen);
+ EVP_CTRL_GCM_SET_IV_FIXED, -1, (void *)iv))
+ return SSH_ERR_LIBCRYPTO_ERROR;
+ } else
+ memcpy(cc->evp.iv, iv, evplen);
break;
-#endif /* WITH_OPENSSL */
+#endif
#ifdef WITH_SSH1
case SSH_CIPHER_3DES:
- ssh1_3des_iv(&cc->evp, 1, iv, 24);
- break;
-#endif /* WITH_SSH1 */
+ return ssh1_3des_iv(&cc->evp, 1, (u_char *)iv, 24);
+#endif
default:
- fatal("%s: bad cipher %d", __func__, c->number);
+ return SSH_ERR_INVALID_ARGUMENT;
}
+ return 0;
}
+#ifdef WITH_OPENSSL
+#define EVP_X_STATE(evp) (evp).cipher_data
+#define EVP_X_STATE_LEN(evp) (evp).cipher->ctx_size
+#endif
+
int
-cipher_get_keycontext(const CipherContext *cc, u_char *dat)
+cipher_get_keycontext(const struct sshcipher_ctx *cc, u_char *dat)
{
#ifdef WITH_OPENSSL
- const Cipher *c = cc->cipher;
+ const struct sshcipher *c = cc->cipher;
int plen = 0;
if (c->evptype == EVP_rc4) {
@@ -636,15 +623,15 @@ cipher_get_keycontext(const CipherContext *cc, u_char *dat)
}
return (plen);
#else
- return (0);
+ return 0;
#endif
}
void
-cipher_set_keycontext(CipherContext *cc, u_char *dat)
+cipher_set_keycontext(struct sshcipher_ctx *cc, const u_char *dat)
{
#ifdef WITH_OPENSSL
- const Cipher *c = cc->cipher;
+ const struct sshcipher *c = cc->cipher;
int plen;
if (c->evptype == EVP_rc4) {
diff --git a/cipher.h b/cipher.h
index 5aa778f1..de74c1e3 100644
--- a/cipher.h
+++ b/cipher.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cipher.h,v 1.45 2014/04/29 18:01:49 markus Exp $ */
+/* $OpenBSD: cipher.h,v 1.46 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -37,6 +37,7 @@
#ifndef CIPHER_H
#define CIPHER_H
+#include <sys/types.h>
#include <openssl/evp.h>
#include "cipher-chachapoly.h"
#include "cipher-aesctr.h"
@@ -61,45 +62,47 @@
#define CIPHER_ENCRYPT 1
#define CIPHER_DECRYPT 0
-typedef struct Cipher Cipher;
-typedef struct CipherContext CipherContext;
-
-struct Cipher;
-struct CipherContext {
+struct sshcipher;
+struct sshcipher_ctx {
int plaintext;
int encrypt;
EVP_CIPHER_CTX evp;
struct chachapoly_ctx cp_ctx; /* XXX union with evp? */
struct aesctr_ctx ac_ctx; /* XXX union with evp? */
- const Cipher *cipher;
+ const struct sshcipher *cipher;
};
+typedef struct sshcipher Cipher ;
+typedef struct sshcipher_ctx CipherContext ;
+
u_int cipher_mask_ssh1(int);
-const Cipher *cipher_by_name(const char *);
-const Cipher *cipher_by_number(int);
+const struct sshcipher *cipher_by_name(const char *);
+const struct sshcipher *cipher_by_number(int);
int cipher_number(const char *);
char *cipher_name(int);
int ciphers_valid(const char *);
char *cipher_alg_list(char, int);
-void cipher_init(CipherContext *, const Cipher *, const u_char *, u_int,
- const u_char *, u_int, int);
-int cipher_crypt(CipherContext *, u_int, u_char *, const u_char *,
+int cipher_init(struct sshcipher_ctx *, const struct sshcipher *,
+ const u_char *, u_int, const u_char *, u_int, int);
+const char* cipher_warning_message(const struct sshcipher_ctx *);
+int cipher_crypt(struct sshcipher_ctx *, u_int, u_char *, const u_char *,
u_int, u_int, u_int);
-int cipher_get_length(CipherContext *, u_int *, u_int,
+int cipher_get_length(struct sshcipher_ctx *, u_int *, u_int,
const u_char *, u_int);
-void cipher_cleanup(CipherContext *);
-void cipher_set_key_string(CipherContext *, const Cipher *, const char *, int);
-u_int cipher_blocksize(const Cipher *);
-u_int cipher_keylen(const Cipher *);
-u_int cipher_seclen(const Cipher *);
-u_int cipher_authlen(const Cipher *);
-u_int cipher_ivlen(const Cipher *);
-u_int cipher_is_cbc(const Cipher *);
+int cipher_cleanup(struct sshcipher_ctx *);
+int cipher_set_key_string(struct sshcipher_ctx *, const struct sshcipher *,
+ const char *, int);
+u_int cipher_blocksize(const struct sshcipher *);
+u_int cipher_keylen(const struct sshcipher *);
+u_int cipher_seclen(const struct sshcipher *);
+u_int cipher_authlen(const struct sshcipher *);
+u_int cipher_ivlen(const struct sshcipher *);
+u_int cipher_is_cbc(const struct sshcipher *);
-u_int cipher_get_number(const Cipher *);
-void cipher_get_keyiv(CipherContext *, u_char *, u_int);
-void cipher_set_keyiv(CipherContext *, u_char *);
-int cipher_get_keyiv_len(const CipherContext *);
-int cipher_get_keycontext(const CipherContext *, u_char *);
-void cipher_set_keycontext(CipherContext *, u_char *);
+u_int cipher_get_number(const struct sshcipher *);
+int cipher_get_keyiv(struct sshcipher_ctx *, u_char *, u_int);
+int cipher_set_keyiv(struct sshcipher_ctx *, const u_char *);
+int cipher_get_keyiv_len(const struct sshcipher_ctx *);
+int cipher_get_keycontext(const struct sshcipher_ctx *, u_char *);
+void cipher_set_keycontext(struct sshcipher_ctx *, const u_char *);
#endif /* CIPHER_H */
diff --git a/digest-libc.c b/digest-libc.c
index 1804b069..1b4423a0 100644
--- a/digest-libc.c
+++ b/digest-libc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: digest-libc.c,v 1.2 2014/02/02 03:44:31 djm Exp $ */
+/* $OpenBSD: digest-libc.c,v 1.3 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2013 Damien Miller <djm@mindrot.org>
* Copyright (c) 2014 Markus Friedl. All rights reserved.
@@ -28,7 +28,8 @@
#include <sha1.h>
#include <sha2.h>
-#include "buffer.h"
+#include "ssherr.h"
+#include "sshbuf.h"
#include "digest.h"
typedef void md_init_fn(void *mdctx);
@@ -164,7 +165,7 @@ ssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to)
const struct ssh_digest *digest = ssh_digest_by_alg(from->alg);
if (digest == NULL || from->alg != to->alg)
- return -1;
+ return SSH_ERR_INVALID_ARGUMENT;
memcpy(to->mdctx, from->mdctx, digest->ctx_len);
return 0;
}
@@ -175,15 +176,15 @@ ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen)
const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg);
if (digest == NULL)
- return -1;
+ return SSH_ERR_INVALID_ARGUMENT;
digest->md_update(ctx->mdctx, m, mlen);
return 0;
}
int
-ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const Buffer *b)
+ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b)
{
- return ssh_digest_update(ctx, buffer_ptr(b), buffer_len(b));
+ return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b));
}
int
@@ -192,11 +193,11 @@ ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen)
const struct ssh_digest *digest = ssh_digest_by_alg(ctx->alg);
if (digest == NULL)
- return -1;
+ return SSH_ERR_INVALID_ARGUMENT;
if (dlen > UINT_MAX)
- return -1;
+ return SSH_ERR_INVALID_ARGUMENT;
if (dlen < digest->digest_len) /* No truncation allowed */
- return -1;
+ return SSH_ERR_INVALID_ARGUMENT;
digest->md_final(d, ctx->mdctx);
return 0;
}
@@ -223,16 +224,16 @@ ssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen)
struct ssh_digest_ctx *ctx = ssh_digest_start(alg);
if (ctx == NULL)
- return -1;
+ return SSH_ERR_INVALID_ARGUMENT;
if (ssh_digest_update(ctx, m, mlen) != 0 ||
ssh_digest_final(ctx, d, dlen) != 0)
- return -1;
+ return SSH_ERR_INVALID_ARGUMENT;
ssh_digest_free(ctx);
return 0;
}
int
-ssh_digest_buffer(int alg, const Buffer *b, u_char *d, size_t dlen)
+ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen)
{
- return ssh_digest_memory(alg, buffer_ptr(b), buffer_len(b), d, dlen);
+ return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen);
}
diff --git a/digest-openssl.c b/digest-openssl.c
index 863d37d0..de038013 100644
--- a/digest-openssl.c
+++ b/digest-openssl.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: digest-openssl.c,v 1.2 2014/02/02 03:44:31 djm Exp $ */
+/* $OpenBSD: digest-openssl.c,v 1.3 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2013 Damien Miller <djm@mindrot.org>
*
@@ -26,8 +26,9 @@
#include "openbsd-compat/openssl-compat.h"
-#include "buffer.h"
+#include "sshbuf.h"
#include "digest.h"
+#include "ssherr.h"
struct ssh_digest_ctx {
int alg;
@@ -98,9 +99,11 @@ ssh_digest_start(int alg)
int
ssh_digest_copy_state(struct ssh_digest_ctx *from, struct ssh_digest_ctx *to)
{
+ if (from->alg != to->alg)
+ return SSH_ERR_INVALID_ARGUMENT;
/* we have bcopy-style order while openssl has memcpy-style */
if (!EVP_MD_CTX_copy_ex(&to->mdctx, &from->mdctx))
- return -1;
+ return SSH_ERR_LIBCRYPTO_ERROR;
return 0;
}
@@ -108,14 +111,14 @@ int
ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen)
{
if (EVP_DigestUpdate(&ctx->mdctx, m, mlen) != 1)
- return -1;
+ return SSH_ERR_LIBCRYPTO_ERROR;
return 0;
}
int
-ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const Buffer *b)
+ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const struct sshbuf *b)
{
- return ssh_digest_update(ctx, buffer_ptr(b), buffer_len(b));
+ return ssh_digest_update(ctx, sshbuf_ptr(b), sshbuf_len(b));
}
int
@@ -125,13 +128,13 @@ ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen)
u_int l = dlen;
if (dlen > UINT_MAX)
- return -1;
+ return SSH_ERR_INVALID_ARGUMENT;
if (dlen < digest->digest_len) /* No truncation allowed */
- return -1;
+ return SSH_ERR_INVALID_ARGUMENT;
if (EVP_DigestFinal_ex(&ctx->mdctx, d, &l) != 1)
- return -1;
+ return SSH_ERR_LIBCRYPTO_ERROR;
if (l != digest->digest_len) /* sanity */
- return -1;
+ return SSH_ERR_INTERNAL_ERROR;
return 0;
}
@@ -149,18 +152,19 @@ int
ssh_digest_memory(int alg, const void *m, size_t mlen, u_char *d, size_t dlen)
{
struct ssh_digest_ctx *ctx = ssh_digest_start(alg);
+ int r;
if (ctx == NULL)
- return -1;
- if (ssh_digest_update(ctx, m, mlen) != 0 ||
- ssh_digest_final(ctx, d, dlen) != 0)
- return -1;
+ return SSH_ERR_INVALID_ARGUMENT;
+ if ((r = ssh_digest_update(ctx, m, mlen) != 0) ||
+ (r = ssh_digest_final(ctx, d, dlen) != 0))
+ return r;
ssh_digest_free(ctx);
return 0;
}
int
-ssh_digest_buffer(int alg, const Buffer *b, u_char *d, size_t dlen)
+ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen)
{
- return ssh_digest_memory(alg, buffer_ptr(b), buffer_len(b), d, dlen);
+ return ssh_digest_memory(alg, sshbuf_ptr(b), sshbuf_len(b), d, dlen);
}
diff --git a/digest.h b/digest.h
index 04295e27..edf6c87d 100644
--- a/digest.h
+++ b/digest.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: digest.h,v 1.4 2014/05/02 03:27:54 djm Exp $ */
+/* $OpenBSD: digest.h,v 1.5 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2013 Damien Miller <djm@mindrot.org>
*
@@ -47,14 +47,15 @@ int ssh_digest_memory(int alg, const void *m, size_t mlen,
u_char *d, size_t dlen)
__attribute__((__bounded__(__buffer__, 2, 3)))
__attribute__((__bounded__(__buffer__, 4, 5)));
-int ssh_digest_buffer(int alg, const Buffer *b, u_char *d, size_t dlen)
+int ssh_digest_buffer(int alg, const struct sshbuf *b, u_char *d, size_t dlen)
__attribute__((__bounded__(__buffer__, 3, 4)));
/* Update API */
struct ssh_digest_ctx *ssh_digest_start(int alg);
int ssh_digest_update(struct ssh_digest_ctx *ctx, const void *m, size_t mlen)
__attribute__((__bounded__(__buffer__, 2, 3)));
-int ssh_digest_update_buffer(struct ssh_digest_ctx *ctx, const Buffer *b);
+int ssh_digest_update_buffer(struct ssh_digest_ctx *ctx,
+ const struct sshbuf *b);
int ssh_digest_final(struct ssh_digest_ctx *ctx, u_char *d, size_t dlen)
__attribute__((__bounded__(__buffer__, 2, 3)));
void ssh_digest_free(struct ssh_digest_ctx *ctx);
diff --git a/dns.c b/dns.c
index c780f8ba..c4d073cf 100644
--- a/dns.c
+++ b/dns.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: dns.c,v 1.30 2014/04/20 09:24:26 logan Exp $ */
+/* $OpenBSD: dns.c,v 1.31 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2003 Wesley Griffin. All rights reserved.
@@ -34,6 +34,8 @@
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
#include "xmalloc.h"
#include "key.h"
diff --git a/entropy.c b/entropy.c
index e1a8e142..1e9d52ac 100644
--- a/entropy.c
+++ b/entropy.c
@@ -43,6 +43,8 @@
#include <openssl/crypto.h>
#include <openssl/err.h>
+#include "openbsd-compat/openssl-compat.h"
+
#include "ssh.h"
#include "misc.h"
#include "xmalloc.h"
diff --git a/hmac.h b/hmac.h
index 05813906..42b33d00 100644
--- a/hmac.h
+++ b/hmac.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: hmac.h,v 1.8 2014/05/02 03:27:54 djm Exp $ */
+/* $OpenBSD: hmac.h,v 1.9 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2014 Markus Friedl. All rights reserved.
*
@@ -21,6 +21,7 @@
/* Returns the algorithm's digest length in bytes or 0 for invalid algorithm */
size_t ssh_hmac_bytes(int alg);
+struct sshbuf;
struct ssh_hmac_ctx;
struct ssh_hmac_ctx *ssh_hmac_start(int alg);
@@ -29,7 +30,7 @@ int ssh_hmac_init(struct ssh_hmac_ctx *ctx, const void *key, size_t klen)
__attribute__((__bounded__(__buffer__, 2, 3)));
int ssh_hmac_update(struct ssh_hmac_ctx *ctx, const void *m, size_t mlen)
__attribute__((__bounded__(__buffer__, 2, 3)));
-int ssh_hmac_update_buffer(struct ssh_hmac_ctx *ctx, const Buffer *b);
+int ssh_hmac_update_buffer(struct ssh_hmac_ctx *ctx, const struct sshbuf *b);
int ssh_hmac_final(struct ssh_hmac_ctx *ctx, u_char *d, size_t dlen)
__attribute__((__bounded__(__buffer__, 2, 3)));
void ssh_hmac_free(struct ssh_hmac_ctx *ctx);
diff --git a/hostfile.c b/hostfile.c
index 91741cab..ee2daf45 100644
--- a/hostfile.c
+++ b/hostfile.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: hostfile.c,v 1.56 2014/04/29 18:01:49 markus Exp $ */
+/* $OpenBSD: hostfile.c,v 1.57 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -47,6 +47,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdarg.h>
#include "xmalloc.h"
#include "match.h"
diff --git a/key.c b/key.c
index e8fc5b1b..75327d49 100644
--- a/key.c
+++ b/key.c
@@ -1,2694 +1,469 @@
-/* $OpenBSD: key.c,v 1.117 2014/04/29 18:01:49 markus Exp $ */
+/* $OpenBSD: key.c,v 1.119 2014/06/30 12:54:39 djm Exp $ */
/*
- * read_bignum():
- * Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
- *
- * As far as I am concerned, the code I have written for this software
- * can be used freely for any purpose. Any derived versions of this
- * software must be clearly marked as such, and if the derived work is
- * incompatible with the protocol description in the RFC file, it must be
- * called by a name other than "ssh" or "Secure Shell".
- *
- *
- * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
- * Copyright (c) 2008 Alexander von Gernler. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * placed in the public domain
*/
#include "includes.h"
#include <sys/param.h>
#include <sys/types.h>
-
-#include "crypto_api.h"
-
-#include <openssl/evp.h>
-#include <openbsd-compat/openssl-compat.h>
-
+#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
-#include <string.h>
-#include "xmalloc.h"
+#define SSH_KEY_NO_DEFINE
#include "key.h"
-#include "rsa.h"
-#include "uuencode.h"
-#include "buffer.h"
-#include "log.h"
-#include "misc.h"
-#include "ssh2.h"
-#include "digest.h"
-
-static int to_blob(const Key *, u_char **, u_int *, int);
-static Key *key_from_blob2(const u_char *, u_int, int);
-
-static struct KeyCert *
-cert_new(void)
-{
- struct KeyCert *cert;
-
- cert = xcalloc(1, sizeof(*cert));
- buffer_init(&cert->certblob);
- buffer_init(&cert->critical);
- buffer_init(&cert->extensions);
- cert->key_id = NULL;
- cert->principals = NULL;
- cert->signature_key = NULL;
- return cert;
-}
-
-Key *
-key_new(int type)
-{
- Key *k;
-#ifdef WITH_OPENSSL
- RSA *rsa;
- DSA *dsa;
-#endif
-
- k = xcalloc(1, sizeof(*k));
- k->type = type;
- k->ecdsa = NULL;
- k->ecdsa_nid = -1;
- k->dsa = NULL;
- k->rsa = NULL;
- k->cert = NULL;
- k->ed25519_sk = NULL;
- k->ed25519_pk = NULL;
- switch (k->type) {
-#ifdef WITH_OPENSSL
- case KEY_RSA1:
- case KEY_RSA:
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- if ((rsa = RSA_new()) == NULL)
- fatal("key_new: RSA_new failed");
- if ((rsa->n = BN_new()) == NULL)
- fatal("key_new: BN_new failed");
- if ((rsa->e = BN_new()) == NULL)
- fatal("key_new: BN_new failed");
- k->rsa = rsa;
- break;
- case KEY_DSA:
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- if ((dsa = DSA_new()) == NULL)
- fatal("key_new: DSA_new failed");
- if ((dsa->p = BN_new()) == NULL)
- fatal("key_new: BN_new failed");
- if ((dsa->q = BN_new()) == NULL)
- fatal("key_new: BN_new failed");
- if ((dsa->g = BN_new()) == NULL)
- fatal("key_new: BN_new failed");
- if ((dsa->pub_key = BN_new()) == NULL)
- fatal("key_new: BN_new failed");
- k->dsa = dsa;
- break;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA:
- case KEY_ECDSA_CERT:
- /* Cannot do anything until we know the group */
- break;
-#endif
-#endif
- case KEY_ED25519:
- case KEY_ED25519_CERT:
- /* no need to prealloc */
- break;
- case KEY_UNSPEC:
- break;
- default:
- fatal("key_new: bad key type %d", k->type);
- break;
- }
-
- if (key_is_cert(k))
- k->cert = cert_new();
- return k;
-}
+#include "compat.h"
+#include "sshkey.h"
+#include "ssherr.h"
+#include "log.h"
+#include "authfile.h"
void
key_add_private(Key *k)
{
- switch (k->type) {
-#ifdef WITH_OPENSSL
- case KEY_RSA1:
- case KEY_RSA:
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- if ((k->rsa->d = BN_new()) == NULL)
- fatal("key_new_private: BN_new failed");
- if ((k->rsa->iqmp = BN_new()) == NULL)
- fatal("key_new_private: BN_new failed");
- if ((k->rsa->q = BN_new()) == NULL)
- fatal("key_new_private: BN_new failed");
- if ((k->rsa->p = BN_new()) == NULL)
- fatal("key_new_private: BN_new failed");
- if ((k->rsa->dmq1 = BN_new()) == NULL)
- fatal("key_new_private: BN_new failed");
- if ((k->rsa->dmp1 = BN_new()) == NULL)
- fatal("key_new_private: BN_new failed");
- break;
- case KEY_DSA:
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- if ((k->dsa->priv_key = BN_new()) == NULL)
- fatal("key_new_private: BN_new failed");
- break;
- case KEY_ECDSA:
- case KEY_ECDSA_CERT:
- /* Cannot do anything until we know the group */
- break;
-#endif
- case KEY_ED25519:
- case KEY_ED25519_CERT:
- /* no need to prealloc */
- break;
- case KEY_UNSPEC:
- break;
- default:
- break;
- }
+ int r;
+
+ if ((r = sshkey_add_private(k)) != 0)
+ fatal("%s: %s", __func__, ssh_err(r));
}
Key *
key_new_private(int type)
{
- Key *k = key_new(type);
-
- key_add_private(k);
- return k;
-}
-
-static void
-cert_free(struct KeyCert *cert)
-{
- u_int i;
-
- buffer_free(&cert->certblob);
- buffer_free(&cert->critical);
- buffer_free(&cert->extensions);
- free(cert->key_id);
- for (i = 0; i < cert->nprincipals; i++)
- free(cert->principals[i]);
- free(cert->principals);
- if (cert->signature_key != NULL)
- key_free(cert->signature_key);
- free(cert);
-}
-
-void
-key_free(Key *k)
-{
- if (k == NULL)
- fatal("key_free: key is NULL");
- switch (k->type) {
-#ifdef WITH_OPENSSL
- case KEY_RSA1:
- case KEY_RSA:
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- if (k->rsa != NULL)
- RSA_free(k->rsa);
- k->rsa = NULL;
- break;
- case KEY_DSA:
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- if (k->dsa != NULL)
- DSA_free(k->dsa);
- k->dsa = NULL;
- break;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA:
- case KEY_ECDSA_CERT:
- if (k->ecdsa != NULL)
- EC_KEY_free(k->ecdsa);
- k->ecdsa = NULL;
- break;
-#endif
- case KEY_ED25519:
- case KEY_ED25519_CERT:
- if (k->ed25519_pk) {
- explicit_bzero(k->ed25519_pk, ED25519_PK_SZ);
- free(k->ed25519_pk);
- k->ed25519_pk = NULL;
- }
- if (k->ed25519_sk) {
- explicit_bzero(k->ed25519_sk, ED25519_SK_SZ);
- free(k->ed25519_sk);
- k->ed25519_sk = NULL;
- }
- break;
- case KEY_UNSPEC:
- break;
- default:
- fatal("key_free: bad key type %d", k->type);
- break;
- }
- if (key_is_cert(k)) {
- if (k->cert != NULL)
- cert_free(k->cert);
- k->cert = NULL;
- }
-
- free(k);
-}
-
-static int
-cert_compare(struct KeyCert *a, struct KeyCert *b)
-{
- if (a == NULL && b == NULL)
- return 1;
- if (a == NULL || b == NULL)
- return 0;
- if (buffer_len(&a->certblob) != buffer_len(&b->certblob))
- return 0;
- if (timingsafe_bcmp(buffer_ptr(&a->certblob), buffer_ptr(&b->certblob),
- buffer_len(&a->certblob)) != 0)
- return 0;
- return 1;
-}
-
-/*
- * Compare public portions of key only, allowing comparisons between
- * certificates and plain keys too.
- */
-int
-key_equal_public(const Key *a, const Key *b)
-{
-#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
- BN_CTX *bnctx;
-#endif
-
- if (a == NULL || b == NULL ||
- key_type_plain(a->type) != key_type_plain(b->type))
- return 0;
-
- switch (a->type) {
-#ifdef WITH_OPENSSL
- case KEY_RSA1:
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- case KEY_RSA:
- return a->rsa != NULL && b->rsa != NULL &&
- BN_cmp(a->rsa->e, b->rsa->e) == 0 &&
- BN_cmp(a->rsa->n, b->rsa->n) == 0;
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- case KEY_DSA:
- return a->dsa != NULL && b->dsa != NULL &&
- BN_cmp(a->dsa->p, b->dsa->p) == 0 &&
- BN_cmp(a->dsa->q, b->dsa->q) == 0 &&
- BN_cmp(a->dsa->g, b->dsa->g) == 0 &&
- BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA_CERT:
- case KEY_ECDSA:
- if (a->ecdsa == NULL || b->ecdsa == NULL ||
- EC_KEY_get0_public_key(a->ecdsa) == NULL ||
- EC_KEY_get0_public_key(b->ecdsa) == NULL)
- return 0;
- if ((bnctx = BN_CTX_new()) == NULL)
- fatal("%s: BN_CTX_new failed", __func__);
- if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa),
- EC_KEY_get0_group(b->ecdsa), bnctx) != 0 ||
- EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa),
- EC_KEY_get0_public_key(a->ecdsa),
- EC_KEY_get0_public_key(b->ecdsa), bnctx) != 0) {
- BN_CTX_free(bnctx);
- return 0;
- }
- BN_CTX_free(bnctx);
- return 1;
-#endif /* OPENSSL_HAS_ECC */
-#endif /* WITH_OPENSSL */
- case KEY_ED25519:
- case KEY_ED25519_CERT:
- return a->ed25519_pk != NULL && b->ed25519_pk != NULL &&
- memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0;
- default:
- fatal("key_equal: bad key type %d", a->type);
- }
- /* NOTREACHED */
-}
+ Key *ret = NULL;
-int
-key_equal(const Key *a, const Key *b)
-{
- if (a == NULL || b == NULL || a->type != b->type)
- return 0;
- if (key_is_cert(a)) {
- if (!cert_compare(a->cert, b->cert))
- return 0;
- }
- return key_equal_public(a, b);
+ if ((ret = sshkey_new_private(type)) == NULL)
+ fatal("%s: failed", __func__);
+ return ret;
}
u_char*
key_fingerprint_raw(const Key *k, enum fp_type dgst_type,
u_int *dgst_raw_length)
{
- u_char *blob = NULL;
- u_char *retval = NULL;
- u_int len = 0;
- int hash_alg = -1;
-#ifdef WITH_OPENSSL
- int nlen, elen;
-#endif
-
- *dgst_raw_length = 0;
-
- /* XXX switch to DIGEST_* directly? */
- switch (dgst_type) {
- case SSH_FP_MD5:
- hash_alg = SSH_DIGEST_MD5;
- break;
- case SSH_FP_SHA1:
- hash_alg = SSH_DIGEST_SHA1;
- break;
- case SSH_FP_SHA256:
- hash_alg = SSH_DIGEST_SHA256;
- break;
- default:
- fatal("%s: bad digest type %d", __func__, dgst_type);
- }
- switch (k->type) {
-#ifdef WITH_OPENSSL
- case KEY_RSA1:
- nlen = BN_num_bytes(k->rsa->n);
- elen = BN_num_bytes(k->rsa->e);
- len = nlen + elen;
- blob = xmalloc(len);
- BN_bn2bin(k->rsa->n, blob);
- BN_bn2bin(k->rsa->e, blob + nlen);
- break;
- case KEY_DSA:
- case KEY_ECDSA:
- case KEY_RSA:
-#endif
- case KEY_ED25519:
- key_to_blob(k, &blob, &len);
- break;
-#ifdef WITH_OPENSSL
- case KEY_DSA_CERT_V00:
- case KEY_RSA_CERT_V00:
- case KEY_DSA_CERT:
- case KEY_ECDSA_CERT:
- case KEY_RSA_CERT:
-#endif
- case KEY_ED25519_CERT:
- /* We want a fingerprint of the _key_ not of the cert */
- to_blob(k, &blob, &len, 1);
- break;
- case KEY_UNSPEC:
- return retval;
- default:
- fatal("%s: bad key type %d", __func__, k->type);
- break;
- }
- if (blob != NULL) {
- retval = xmalloc(SSH_DIGEST_MAX_LENGTH);
- if ((ssh_digest_memory(hash_alg, blob, len,
- retval, SSH_DIGEST_MAX_LENGTH)) != 0)
- fatal("%s: digest_memory failed", __func__);
- explicit_bzero(blob, len);
- free(blob);
- *dgst_raw_length = ssh_digest_bytes(hash_alg);
- } else {
- fatal("%s: blob is null", __func__);
- }
- return retval;
-}
-
-static char *
-key_fingerprint_hex(u_char *dgst_raw, u_int dgst_raw_len)
-{
- char *retval;
- u_int i;
-
- retval = xcalloc(1, dgst_raw_len * 3 + 1);
- for (i = 0; i < dgst_raw_len; i++) {
- char hex[4];
- snprintf(hex, sizeof(hex), "%02x:", dgst_raw[i]);
- strlcat(retval, hex, dgst_raw_len * 3 + 1);
- }
-
- /* Remove the trailing ':' character */
- retval[(dgst_raw_len * 3) - 1] = '\0';
- return retval;
-}
-
-static char *
-key_fingerprint_bubblebabble(u_char *dgst_raw, u_int dgst_raw_len)
-{
- char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' };
- char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm',
- 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' };
- u_int i, j = 0, rounds, seed = 1;
- char *retval;
-
- rounds = (dgst_raw_len / 2) + 1;
- retval = xcalloc((rounds * 6), sizeof(char));
- retval[j++] = 'x';
- for (i = 0; i < rounds; i++) {
- u_int idx0, idx1, idx2, idx3, idx4;
- if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) {
- idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) +
- seed) % 6;
- idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15;
- idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) +
- (seed / 6)) % 6;
- retval[j++] = vowels[idx0];
- retval[j++] = consonants[idx1];
- retval[j++] = vowels[idx2];
- if ((i + 1) < rounds) {
- idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15;
- idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15;
- retval[j++] = consonants[idx3];
- retval[j++] = '-';
- retval[j++] = consonants[idx4];
- seed = ((seed * 5) +
- ((((u_int)(dgst_raw[2 * i])) * 7) +
- ((u_int)(dgst_raw[(2 * i) + 1])))) % 36;
- }
- } else {
- idx0 = seed % 6;
- idx1 = 16;
- idx2 = seed / 6;
- retval[j++] = vowels[idx0];
- retval[j++] = consonants[idx1];
- retval[j++] = vowels[idx2];
- }
- }
- retval[j++] = 'x';
- retval[j++] = '\0';
- return retval;
-}
-
-/*
- * Draw an ASCII-Art representing the fingerprint so human brain can
- * profit from its built-in pattern recognition ability.
- * This technique is called "random art" and can be found in some
- * scientific publications like this original paper:
- *
- * "Hash Visualization: a New Technique to improve Real-World Security",
- * Perrig A. and Song D., 1999, International Workshop on Cryptographic
- * Techniques and E-Commerce (CrypTEC '99)
- * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
- *
- * The subject came up in a talk by Dan Kaminsky, too.
- *
- * If you see the picture is different, the key is different.
- * If the picture looks the same, you still know nothing.
- *
- * The algorithm used here is a worm crawling over a discrete plane,
- * leaving a trace (augmenting the field) everywhere it goes.
- * Movement is taken from dgst_raw 2bit-wise. Bumping into walls
- * makes the respective movement vector be ignored for this turn.
- * Graphs are not unambiguous, because circles in graphs can be
- * walked in either direction.
- */
-
-/*
- * Field sizes for the random art. Have to be odd, so the starting point
- * can be in the exact middle of the picture, and FLDBASE should be >=8 .
- * Else pictures would be too dense, and drawing the frame would
- * fail, too, because the key type would not fit in anymore.
- */
-#define FLDBASE 8
-#define FLDSIZE_Y (FLDBASE + 1)
-#define FLDSIZE_X (FLDBASE * 2 + 1)
-static char *
-key_fingerprint_randomart(u_char *dgst_raw, u_int dgst_raw_len, const Key *k)
-{
- /*
- * Chars to be used after each other every time the worm
- * intersects with itself. Matter of taste.
- */
- char *augmentation_string = " .o+=*BOX@%&#/^SE";
- char *retval, *p;
- u_char field[FLDSIZE_X][FLDSIZE_Y];
- u_int i, b;
- int x, y;
- size_t len = strlen(augmentation_string) - 1;
-
- retval = xcalloc(1, (FLDSIZE_X + 3) * (FLDSIZE_Y + 2));
-
- /* initialize field */
- memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char));
- x = FLDSIZE_X / 2;
- y = FLDSIZE_Y / 2;
-
- /* process raw key */
- for (i = 0; i < dgst_raw_len; i++) {
- int input;
- /* each byte conveys four 2-bit move commands */
- input = dgst_raw[i];
- for (b = 0; b < 4; b++) {
- /* evaluate 2 bit, rest is shifted later */
- x += (input & 0x1) ? 1 : -1;
- y += (input & 0x2) ? 1 : -1;
-
- /* assure we are still in bounds */
- x = MAX(x, 0);
- y = MAX(y, 0);
- x = MIN(x, FLDSIZE_X - 1);
- y = MIN(y, FLDSIZE_Y - 1);
-
- /* augment the field */
- if (field[x][y] < len - 2)
- field[x][y]++;
- input = input >> 2;
- }
- }
-
- /* mark starting point and end point*/
- field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1;
- field[x][y] = len;
-
- /* fill in retval */
- snprintf(retval, FLDSIZE_X, "+--[%4s %4u]", key_type(k), key_size(k));
- p = strchr(retval, '\0');
-
- /* output upper border */
- for (i = p - retval - 1; i < FLDSIZE_X; i++)
- *p++ = '-';
- *p++ = '+';
- *p++ = '\n';
-
- /* output content */
- for (y = 0; y < FLDSIZE_Y; y++) {
- *p++ = '|';
- for (x = 0; x < FLDSIZE_X; x++)
- *p++ = augmentation_string[MIN(field[x][y], len)];
- *p++ = '|';
- *p++ = '\n';
- }
-
- /* output lower border */
- *p++ = '+';
- for (i = 0; i < FLDSIZE_X; i++)
- *p++ = '-';
- *p++ = '+';
-
- return retval;
-}
-
-char *
-key_fingerprint(const Key *k, enum fp_type dgst_type, enum fp_rep dgst_rep)
-{
- char *retval = NULL;
- u_char *dgst_raw;
- u_int dgst_raw_len;
-
- dgst_raw = key_fingerprint_raw(k, dgst_type, &dgst_raw_len);
- if (!dgst_raw)
- fatal("key_fingerprint: null from key_fingerprint_raw()");
- switch (dgst_rep) {
- case SSH_FP_HEX:
- retval = key_fingerprint_hex(dgst_raw, dgst_raw_len);
- break;
- case SSH_FP_BUBBLEBABBLE:
- retval = key_fingerprint_bubblebabble(dgst_raw, dgst_raw_len);
- break;
- case SSH_FP_RANDOMART:
- retval = key_fingerprint_randomart(dgst_raw, dgst_raw_len, k);
- break;
- default:
- fatal("key_fingerprint: bad digest representation %d",
- dgst_rep);
- break;
- }
- explicit_bzero(dgst_raw, dgst_raw_len);
- free(dgst_raw);
- return retval;
-}
-
-#ifdef WITH_SSH1
-/*
- * Reads a multiple-precision integer in decimal from the buffer, and advances
- * the pointer. The integer must already be initialized. This function is
- * permitted to modify the buffer. This leaves *cpp to point just beyond the
- * last processed (and maybe modified) character. Note that this may modify
- * the buffer containing the number.
- */
-static int
-read_bignum(char **cpp, BIGNUM * value)
-{
- char *cp = *cpp;
- int old;
-
- /* Skip any leading whitespace. */
- for (; *cp == ' ' || *cp == '\t'; cp++)
- ;
-
- /* Check that it begins with a decimal digit. */
- if (*cp < '0' || *cp > '9')
- return 0;
-
- /* Save starting position. */
- *cpp = cp;
-
- /* Move forward until all decimal digits skipped. */
- for (; *cp >= '0' && *cp <= '9'; cp++)
- ;
-
- /* Save the old terminating character, and replace it by \0. */
- old = *cp;
- *cp = 0;
-
- /* Parse the number. */
- if (BN_dec2bn(&value, *cpp) == 0)
- return 0;
-
- /* Restore old terminating character. */
- *cp = old;
-
- /* Move beyond the number and return success. */
- *cpp = cp;
- return 1;
-}
-
-static int
-write_bignum(FILE *f, BIGNUM *num)
-{
- char *buf = BN_bn2dec(num);
- if (buf == NULL) {
- error("write_bignum: BN_bn2dec() failed");
- return 0;
- }
- fprintf(f, " %s", buf);
- OPENSSL_free(buf);
- return 1;
+ u_char *ret = NULL;
+ size_t dlen;
+ int r;
+
+ if (dgst_raw_length != NULL)
+ *dgst_raw_length = 0;
+ if ((r = sshkey_fingerprint_raw(k, dgst_type, &ret, &dlen)) != 0)
+ fatal("%s: %s", __func__, ssh_err(r));
+ if (dlen > INT_MAX)
+ fatal("%s: giant len %zu", __func__, dlen);
+ *dgst_raw_length = dlen;
+ return ret;
}
-#endif
-/* returns 1 ok, -1 error */
int
key_read(Key *ret, char **cpp)
{
- Key *k;
- int success = -1;
- char *cp, *space;
- int len, n, type;
- u_char *blob;
-#ifdef WITH_SSH1
- u_int bits;
-#endif
-#ifdef OPENSSL_HAS_ECC
- int curve_nid = -1;
-#endif
-
- cp = *cpp;
-
- switch (ret->type) {
- case KEY_RSA1:
-#ifdef WITH_SSH1
- /* Get number of bits. */
- if (*cp < '0' || *cp > '9')
- return -1; /* Bad bit count... */
- for (bits = 0; *cp >= '0' && *cp <= '9'; cp++)
- bits = 10 * bits + *cp - '0';
- if (bits == 0)
- return -1;
- *cpp = cp;
- /* Get public exponent, public modulus. */
- if (!read_bignum(cpp, ret->rsa->e))
- return -1;
- if (!read_bignum(cpp, ret->rsa->n))
- return -1;
- /* validate the claimed number of bits */
- if ((u_int)BN_num_bits(ret->rsa->n) != bits) {
- verbose("key_read: claimed key size %d does not match "
- "actual %d", bits, BN_num_bits(ret->rsa->n));
- return -1;
- }
- success = 1;
-#endif
- break;
- case KEY_UNSPEC:
- case KEY_RSA:
- case KEY_DSA:
- case KEY_ECDSA:
- case KEY_ED25519:
- case KEY_DSA_CERT_V00:
- case KEY_RSA_CERT_V00:
- case KEY_DSA_CERT:
- case KEY_ECDSA_CERT:
- case KEY_RSA_CERT:
- case KEY_ED25519_CERT:
- space = strchr(cp, ' ');
- if (space == NULL) {
- debug3("key_read: missing whitespace");
- return -1;
- }
- *space = '\0';
- type = key_type_from_name(cp);
-#ifdef OPENSSL_HAS_ECC
- if (key_type_plain(type) == KEY_ECDSA &&
- (curve_nid = key_ecdsa_nid_from_name(cp)) == -1) {
- debug("key_read: invalid curve");
- return -1;
- }
-#endif
- *space = ' ';
- if (type == KEY_UNSPEC) {
- debug3("key_read: missing keytype");
- return -1;
- }
- cp = space+1;
- if (*cp == '\0') {
- debug3("key_read: short string");
- return -1;
- }
- if (ret->type == KEY_UNSPEC) {
- ret->type = type;
- } else if (ret->type != type) {
- /* is a key, but different type */
- debug3("key_read: type mismatch");
- return -1;
- }
- len = 2*strlen(cp);
- blob = xmalloc(len);
- n = uudecode(cp, blob, len);
- if (n < 0) {
- error("key_read: uudecode %s failed", cp);
- free(blob);
- return -1;
- }
- k = key_from_blob(blob, (u_int)n);
- free(blob);
- if (k == NULL) {
- error("key_read: key_from_blob %s failed", cp);
- return -1;
- }
- if (k->type != type) {
- error("key_read: type mismatch: encoding error");
- key_free(k);
- return -1;
- }
-#ifdef OPENSSL_HAS_ECC
- if (key_type_plain(type) == KEY_ECDSA &&
- curve_nid != k->ecdsa_nid) {
- error("key_read: type mismatch: EC curve mismatch");
- key_free(k);
- return -1;
- }
-#endif
-/*XXXX*/
- if (key_is_cert(ret)) {
- if (!key_is_cert(k)) {
- error("key_read: loaded key is not a cert");
- key_free(k);
- return -1;
- }
- if (ret->cert != NULL)
- cert_free(ret->cert);
- ret->cert = k->cert;
- k->cert = NULL;
- }
-#ifdef WITH_OPENSSL
- if (key_type_plain(ret->type) == KEY_RSA) {
- if (ret->rsa != NULL)
- RSA_free(ret->rsa);
- ret->rsa = k->rsa;
- k->rsa = NULL;
-#ifdef DEBUG_PK
- RSA_print_fp(stderr, ret->rsa, 8);
-#endif
- }
- if (key_type_plain(ret->type) == KEY_DSA) {
- if (ret->dsa != NULL)
- DSA_free(ret->dsa);
- ret->dsa = k->dsa;
- k->dsa = NULL;
-#ifdef DEBUG_PK
- DSA_print_fp(stderr, ret->dsa, 8);
-#endif
- }
-#ifdef OPENSSL_HAS_ECC
- if (key_type_plain(ret->type) == KEY_ECDSA) {
- if (ret->ecdsa != NULL)
- EC_KEY_free(ret->ecdsa);
- ret->ecdsa = k->ecdsa;
- ret->ecdsa_nid = k->ecdsa_nid;
- k->ecdsa = NULL;
- k->ecdsa_nid = -1;
-#ifdef DEBUG_PK
- key_dump_ec_key(ret->ecdsa);
-#endif
- }
-#endif
-#endif
- if (key_type_plain(ret->type) == KEY_ED25519) {
- free(ret->ed25519_pk);
- ret->ed25519_pk = k->ed25519_pk;
- k->ed25519_pk = NULL;
-#ifdef DEBUG_PK
- /* XXX */
-#endif
- }
- success = 1;
-/*XXXX*/
- key_free(k);
- if (success != 1)
- break;
- /* advance cp: skip whitespace and data */
- while (*cp == ' ' || *cp == '\t')
- cp++;
- while (*cp != '\0' && *cp != ' ' && *cp != '\t')
- cp++;
- *cpp = cp;
- break;
- default:
- fatal("key_read: bad key type: %d", ret->type);
- break;
- }
- return success;
+ return sshkey_read(ret, cpp) == 0 ? 1 : -1;
}
int
key_write(const Key *key, FILE *f)
{
- int n, success = 0;
-#ifdef WITH_SSH1
- u_int bits = 0;
-#endif
- u_int len;
- u_char *blob;
- char *uu;
-
- if (key_is_cert(key)) {
- if (key->cert == NULL) {
- error("%s: no cert data", __func__);
- return 0;
- }
- if (buffer_len(&key->cert->certblob) == 0) {
- error("%s: no signed certificate blob", __func__);
- return 0;
- }
- }
-
- switch (key->type) {
-#ifdef WITH_SSH1
- case KEY_RSA1:
- if (key->rsa == NULL)
- return 0;
- /* size of modulus 'n' */
- bits = BN_num_bits(key->rsa->n);
- fprintf(f, "%u", bits);
- if (write_bignum(f, key->rsa->e) &&
- write_bignum(f, key->rsa->n))
- return 1;
- error("key_write: failed for RSA key");
- return 0;
-#endif
-#ifdef WITH_OPENSSL
- case KEY_DSA:
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- if (key->dsa == NULL)
- return 0;
- break;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA:
- case KEY_ECDSA_CERT:
- if (key->ecdsa == NULL)
- return 0;
- break;
-#endif
- case KEY_RSA:
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- if (key->rsa == NULL)
- return 0;
- break;
-#endif
- case KEY_ED25519:
- case KEY_ED25519_CERT:
- if (key->ed25519_pk == NULL)
- return 0;
- break;
- default:
- return 0;
- }
-
- key_to_blob(key, &blob, &len);
- uu = xmalloc(2*len);
- n = uuencode(blob, len, uu, 2*len);
- if (n > 0) {
- fprintf(f, "%s %s", key_ssh_name(key), uu);
- success = 1;
- }
- free(blob);
- free(uu);
-
- return success;
-}
-
-const char *
-key_cert_type(const Key *k)
-{
- switch (k->cert->type) {
- case SSH2_CERT_TYPE_USER:
- return "user";
- case SSH2_CERT_TYPE_HOST:
- return "host";
- default:
- return "unknown";
- }
-}
-
-struct keytype {
- char *name;
- char *shortname;
- int type;
- int nid;
- int cert;
-};
-static const struct keytype keytypes[] = {
-#ifdef WITH_OPENSSL
-#ifdef WITH_SSH1
- { NULL, "RSA1", KEY_RSA1, 0, 0 },
-#endif
- { "ssh-rsa", "RSA", KEY_RSA, 0, 0 },
- { "ssh-dss", "DSA", KEY_DSA, 0, 0 },
-#ifdef OPENSSL_HAS_ECC
- { "ecdsa-sha2-nistp256", "ECDSA", KEY_ECDSA, NID_X9_62_prime256v1, 0 },
- { "ecdsa-sha2-nistp384", "ECDSA", KEY_ECDSA, NID_secp384r1, 0 },
-# ifdef OPENSSL_HAS_NISTP521
- { "ecdsa-sha2-nistp521", "ECDSA", KEY_ECDSA, NID_secp521r1, 0 },
-# endif
-#endif /* OPENSSL_HAS_ECC */
- { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", KEY_RSA_CERT, 0, 1 },
- { "ssh-dss-cert-v01@openssh.com", "DSA-CERT", KEY_DSA_CERT, 0, 1 },
-#ifdef OPENSSL_HAS_ECC
- { "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-CERT",
- KEY_ECDSA_CERT, NID_X9_62_prime256v1, 1 },
- { "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT",
- KEY_ECDSA_CERT, NID_secp384r1, 1 },
-# ifdef OPENSSL_HAS_NISTP521
- { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT",
- KEY_ECDSA_CERT, NID_secp521r1, 1 },
-# endif
-#endif /* OPENSSL_HAS_ECC */
- { "ssh-rsa-cert-v00@openssh.com", "RSA-CERT-V00",
- KEY_RSA_CERT_V00, 0, 1 },
- { "ssh-dss-cert-v00@openssh.com", "DSA-CERT-V00",
- KEY_DSA_CERT_V00, 0, 1 },
-#endif
- { "ssh-ed25519", "ED25519", KEY_ED25519, 0, 0 },
- { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT",
- KEY_ED25519_CERT, 0, 1 },
- { NULL, NULL, -1, -1, 0 }
-};
-
-const char *
-key_type(const Key *k)
-{
- const struct keytype *kt;
-
- for (kt = keytypes; kt->type != -1; kt++) {
- if (kt->type == k->type)
- return kt->shortname;
- }
- return "unknown";
-}
-
-static const char *
-key_ssh_name_from_type_nid(int type, int nid)
-{
- const struct keytype *kt;
-
- for (kt = keytypes; kt->type != -1; kt++) {
- if (kt->type == type && (kt->nid == 0 || kt->nid == nid))
- return kt->name;
- }
- return "ssh-unknown";
-}
-
-const char *
-key_ssh_name(const Key *k)
-{
- return key_ssh_name_from_type_nid(k->type, k->ecdsa_nid);
-}
-
-const char *
-key_ssh_name_plain(const Key *k)
-{
- return key_ssh_name_from_type_nid(key_type_plain(k->type),
- k->ecdsa_nid);
-}
-
-int
-key_type_from_name(char *name)
-{
- const struct keytype *kt;
-
- for (kt = keytypes; kt->type != -1; kt++) {
- /* Only allow shortname matches for plain key types */
- if ((kt->name != NULL && strcmp(name, kt->name) == 0) ||
- (!kt->cert && strcasecmp(kt->shortname, name) == 0))
- return kt->type;
- }
- debug2("key_type_from_name: unknown key type '%s'", name);
- return KEY_UNSPEC;
-}
-
-int
-key_ecdsa_nid_from_name(const char *name)
-{
- const struct keytype *kt;
-
- for (kt = keytypes; kt->type != -1; kt++) {
- if (kt->type != KEY_ECDSA && kt->type != KEY_ECDSA_CERT)
- continue;
- if (kt->name != NULL && strcmp(name, kt->name) == 0)
- return kt->nid;
- }
- debug2("%s: unknown/non-ECDSA key type '%s'", __func__, name);
- return -1;
-}
-
-char *
-key_alg_list(int certs_only, int plain_only)
-{
- char *ret = NULL;
- size_t nlen, rlen = 0;
- const struct keytype *kt;
-
- for (kt = keytypes; kt->type != -1; kt++) {
- if (kt->name == NULL)
- continue;
- if ((certs_only && !kt->cert) || (plain_only && kt->cert))
- continue;
- if (ret != NULL)
- ret[rlen++] = '\n';
- nlen = strlen(kt->name);
- ret = xrealloc(ret, 1, rlen + nlen + 2);
- memcpy(ret + rlen, kt->name, nlen + 1);
- rlen += nlen;
- }
- return ret;
-}
-
-int
-key_type_is_cert(int type)
-{
- const struct keytype *kt;
-
- for (kt = keytypes; kt->type != -1; kt++) {
- if (kt->type == type)
- return kt->cert;
- }
- return 0;
-}
-
-static int
-key_type_is_valid_ca(int type)
-{
- switch (type) {
- case KEY_RSA:
- case KEY_DSA:
- case KEY_ECDSA:
- case KEY_ED25519:
- return 1;
- default:
- return 0;
- }
-}
-
-u_int
-key_size(const Key *k)
-{
- switch (k->type) {
-#ifdef WITH_OPENSSL
- case KEY_RSA1:
- case KEY_RSA:
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- return BN_num_bits(k->rsa->n);
- case KEY_DSA:
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- return BN_num_bits(k->dsa->p);
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA:
- case KEY_ECDSA_CERT:
- return key_curve_nid_to_bits(k->ecdsa_nid);
-#endif
-#endif
- case KEY_ED25519:
- return 256; /* XXX */
- }
- return 0;
-}
-
-#ifdef WITH_OPENSSL
-static RSA *
-rsa_generate_private_key(u_int bits)
-{
- RSA *private = RSA_new();
- BIGNUM *f4 = BN_new();
-
- if (private == NULL)
- fatal("%s: RSA_new failed", __func__);
- if (f4 == NULL)
- fatal("%s: BN_new failed", __func__);
- if (!BN_set_word(f4, RSA_F4))
- fatal("%s: BN_new failed", __func__);
- if (!RSA_generate_key_ex(private, bits, f4, NULL))
- fatal("%s: key generation failed.", __func__);
- BN_free(f4);
- return private;
-}
-
-static DSA*
-dsa_generate_private_key(u_int bits)
-{
- DSA *private = DSA_new();
-
- if (private == NULL)
- fatal("%s: DSA_new failed", __func__);
- if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL,
- NULL, NULL))
- fatal("%s: DSA_generate_parameters failed", __func__);
- if (!DSA_generate_key(private))
- fatal("%s: DSA_generate_key failed.", __func__);
- return private;
-}
-
-int
-key_ecdsa_bits_to_nid(int bits)
-{
- switch (bits) {
-#ifdef OPENSSL_HAS_ECC
- case 256:
- return NID_X9_62_prime256v1;
- case 384:
- return NID_secp384r1;
-# ifdef OPENSSL_HAS_NISTP521
- case 521:
- return NID_secp521r1;
-# endif
-#endif
- default:
- return -1;
- }
-}
-
-#ifdef OPENSSL_HAS_ECC
-int
-key_ecdsa_key_to_nid(EC_KEY *k)
-{
- EC_GROUP *eg;
- int nids[] = {
- NID_X9_62_prime256v1,
- NID_secp384r1,
-# ifdef OPENSSL_HAS_NISTP521
- NID_secp521r1,
-# endif
- -1
- };
- int nid;
- u_int i;
- BN_CTX *bnctx;
- const EC_GROUP *g = EC_KEY_get0_group(k);
-
- /*
- * The group may be stored in a ASN.1 encoded private key in one of two
- * ways: as a "named group", which is reconstituted by ASN.1 object ID
- * or explicit group parameters encoded into the key blob. Only the
- * "named group" case sets the group NID for us, but we can figure
- * it out for the other case by comparing against all the groups that
- * are supported.
- */
- if ((nid = EC_GROUP_get_curve_name(g)) > 0)
- return nid;
- if ((bnctx = BN_CTX_new()) == NULL)
- fatal("%s: BN_CTX_new() failed", __func__);
- for (i = 0; nids[i] != -1; i++) {
- if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL)
- fatal("%s: EC_GROUP_new_by_curve_name failed",
- __func__);
- if (EC_GROUP_cmp(g, eg, bnctx) == 0)
- break;
- EC_GROUP_free(eg);
- }
- BN_CTX_free(bnctx);
- debug3("%s: nid = %d", __func__, nids[i]);
- if (nids[i] != -1) {
- /* Use the group with the NID attached */
- EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE);
- if (EC_KEY_set_group(k, eg) != 1)
- fatal("%s: EC_KEY_set_group", __func__);
- }
- return nids[i];
+ return sshkey_write(key, f) == 0 ? 1 : 0;
}
-static EC_KEY*
-ecdsa_generate_private_key(u_int bits, int *nid)
-{
- EC_KEY *private;
-
- if ((*nid = key_ecdsa_bits_to_nid(bits)) == -1)
- fatal("%s: invalid key length", __func__);
- if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL)
- fatal("%s: EC_KEY_new_by_curve_name failed", __func__);
- if (EC_KEY_generate_key(private) != 1)
- fatal("%s: EC_KEY_generate_key failed", __func__);
- EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
- return private;
-}
-#endif /* OPENSSL_HAS_ECC */
-#endif /* WITH_OPENSSL */
-
Key *
key_generate(int type, u_int bits)
{
- Key *k = key_new(KEY_UNSPEC);
- switch (type) {
-#ifdef WITH_OPENSSL
- case KEY_DSA:
- k->dsa = dsa_generate_private_key(bits);
- break;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA:
- k->ecdsa = ecdsa_generate_private_key(bits, &k->ecdsa_nid);
- break;
-#endif
- case KEY_RSA:
- case KEY_RSA1:
- k->rsa = rsa_generate_private_key(bits);
- break;
-#endif
- case KEY_RSA_CERT_V00:
- case KEY_DSA_CERT_V00:
- case KEY_RSA_CERT:
- case KEY_DSA_CERT:
- fatal("key_generate: cert keys cannot be generated directly");
-#endif
- case KEY_ED25519:
- k->ed25519_pk = xmalloc(ED25519_PK_SZ);
- k->ed25519_sk = xmalloc(ED25519_SK_SZ);
- crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk);
- break;
- default:
- fatal("key_generate: unknown type %d", type);
- }
- k->type = type;
- return k;
+ int r;
+ Key *ret = NULL;
+
+ if ((r = sshkey_generate(type, bits, &ret)) != 0)
+ fatal("%s: %s", __func__, ssh_err(r));
+ return ret;
}
void
-key_cert_copy(const Key *from_key, struct Key *to_key)
+key_cert_copy(const Key *from_key, Key *to_key)
{
- u_int i;
- const struct KeyCert *from;
- struct KeyCert *to;
-
- if (to_key->cert != NULL) {
- cert_free(to_key->cert);
- to_key->cert = NULL;
- }
+ int r;
- if ((from = from_key->cert) == NULL)
- return;
-
- to = to_key->cert = cert_new();
-
- buffer_append(&to->certblob, buffer_ptr(&from->certblob),
- buffer_len(&from->certblob));
-
- buffer_append(&to->critical,
- buffer_ptr(&from->critical), buffer_len(&from->critical));
- buffer_append(&to->extensions,
- buffer_ptr(&from->extensions), buffer_len(&from->extensions));
-
- to->serial = from->serial;
- to->type = from->type;
- to->key_id = from->key_id == NULL ? NULL : xstrdup(from->key_id);
- to->valid_after = from->valid_after;
- to->valid_before = from->valid_before;
- to->signature_key = from->signature_key == NULL ?
- NULL : key_from_private(from->signature_key);
-
- to->nprincipals = from->nprincipals;
- if (to->nprincipals > CERT_MAX_PRINCIPALS)
- fatal("%s: nprincipals (%u) > CERT_MAX_PRINCIPALS (%u)",
- __func__, to->nprincipals, CERT_MAX_PRINCIPALS);
- if (to->nprincipals > 0) {
- to->principals = xcalloc(from->nprincipals,
- sizeof(*to->principals));
- for (i = 0; i < to->nprincipals; i++)
- to->principals[i] = xstrdup(from->principals[i]);
- }
+ if ((r = sshkey_cert_copy(from_key, to_key)) != 0)
+ fatal("%s: %s", __func__, ssh_err(r));
}
Key *
key_from_private(const Key *k)
{
- Key *n = NULL;
- switch (k->type) {
-#ifdef WITH_OPENSSL
- case KEY_DSA:
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- n = key_new(k->type);
- if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) ||
- (BN_copy(n->dsa->q, k->dsa->q) == NULL) ||
- (BN_copy(n->dsa->g, k->dsa->g) == NULL) ||
- (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL))
- fatal("key_from_private: BN_copy failed");
- break;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA:
- case KEY_ECDSA_CERT:
- n = key_new(k->type);
- n->ecdsa_nid = k->ecdsa_nid;
- if ((n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid)) == NULL)
- fatal("%s: EC_KEY_new_by_curve_name failed", __func__);
- if (EC_KEY_set_public_key(n->ecdsa,
- EC_KEY_get0_public_key(k->ecdsa)) != 1)
- fatal("%s: EC_KEY_set_public_key failed", __func__);
- break;
-#endif
- case KEY_RSA:
- case KEY_RSA1:
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- n = key_new(k->type);
- if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) ||
- (BN_copy(n->rsa->e, k->rsa->e) == NULL))
- fatal("key_from_private: BN_copy failed");
- break;
-#endif
- case KEY_ED25519:
- case KEY_ED25519_CERT:
- n = key_new(k->type);
- if (k->ed25519_pk != NULL) {
- n->ed25519_pk = xmalloc(ED25519_PK_SZ);
- memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
- }
- break;
- default:
- fatal("key_from_private: unknown type %d", k->type);
- break;
- }
- if (key_is_cert(k))
- key_cert_copy(k, n);
- return n;
-}
-
-int
-key_names_valid2(const char *names)
-{
- char *s, *cp, *p;
-
- if (names == NULL || strcmp(names, "") == 0)
- return 0;
- s = cp = xstrdup(names);
- for ((p = strsep(&cp, ",")); p && *p != '\0';
- (p = strsep(&cp, ","))) {
- switch (key_type_from_name(p)) {
- case KEY_RSA1:
- case KEY_UNSPEC:
- free(s);
- return 0;
- }
- }
- debug3("key names ok: [%s]", names);
- free(s);
- return 1;
-}
-
-static int
-cert_parse(Buffer *b, Key *key, const u_char *blob, u_int blen)
-{
- u_char *principals, *critical, *exts, *sig_key, *sig;
- u_int signed_len, plen, clen, sklen, slen, kidlen, elen;
- Buffer tmp;
- char *principal;
- int ret = -1;
- int v00 = key->type == KEY_DSA_CERT_V00 ||
- key->type == KEY_RSA_CERT_V00;
-
- buffer_init(&tmp);
-
- /* Copy the entire key blob for verification and later serialisation */
- buffer_append(&key->cert->certblob, blob, blen);
-
- elen = 0; /* Not touched for v00 certs */
- principals = exts = critical = sig_key = sig = NULL;
- if ((!v00 && buffer_get_int64_ret(&key->cert->serial, b) != 0) ||
- buffer_get_int_ret(&key->cert->type, b) != 0 ||
- (key->cert->key_id = buffer_get_cstring_ret(b, &kidlen)) == NULL ||
- (principals = buffer_get_string_ret(b, &plen)) == NULL ||
- buffer_get_int64_ret(&key->cert->valid_after, b) != 0 ||
- buffer_get_int64_ret(&key->cert->valid_before, b) != 0 ||
- (critical = buffer_get_string_ret(b, &clen)) == NULL ||
- (!v00 && (exts = buffer_get_string_ret(b, &elen)) == NULL) ||
- (v00 && buffer_get_string_ptr_ret(b, NULL) == NULL) || /* nonce */
- buffer_get_string_ptr_ret(b, NULL) == NULL || /* reserved */
- (sig_key = buffer_get_string_ret(b, &sklen)) == NULL) {
- error("%s: parse error", __func__);
- goto out;
- }
-
- /* Signature is left in the buffer so we can calculate this length */
- signed_len = buffer_len(&key->cert->certblob) - buffer_len(b);
-
- if ((sig = buffer_get_string_ret(b, &slen)) == NULL) {
- error("%s: parse error", __func__);
- goto out;
- }
-
- if (key->cert->type != SSH2_CERT_TYPE_USER &&
- key->cert->type != SSH2_CERT_TYPE_HOST) {
- error("Unknown certificate type %u", key->cert->type);
- goto out;
- }
-
- buffer_append(&tmp, principals, plen);
- while (buffer_len(&tmp) > 0) {
- if (key->cert->nprincipals >= CERT_MAX_PRINCIPALS) {
- error("%s: Too many principals", __func__);
- goto out;
- }
- if ((principal = buffer_get_cstring_ret(&tmp, &plen)) == NULL) {
- error("%s: Principals data invalid", __func__);
- goto out;
- }
- key->cert->principals = xrealloc(key->cert->principals,
- key->cert->nprincipals + 1, sizeof(*key->cert->principals));
- key->cert->principals[key->cert->nprincipals++] = principal;
- }
-
- buffer_clear(&tmp);
-
- buffer_append(&key->cert->critical, critical, clen);
- buffer_append(&tmp, critical, clen);
- /* validate structure */
- while (buffer_len(&tmp) != 0) {
- if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL ||
- buffer_get_string_ptr_ret(&tmp, NULL) == NULL) {
- error("%s: critical option data invalid", __func__);
- goto out;
- }
- }
- buffer_clear(&tmp);
-
- buffer_append(&key->cert->extensions, exts, elen);
- buffer_append(&tmp, exts, elen);
- /* validate structure */
- while (buffer_len(&tmp) != 0) {
- if (buffer_get_string_ptr_ret(&tmp, NULL) == NULL ||
- buffer_get_string_ptr_ret(&tmp, NULL) == NULL) {
- error("%s: extension data invalid", __func__);
- goto out;
- }
- }
- buffer_clear(&tmp);
-
- if ((key->cert->signature_key = key_from_blob2(sig_key, sklen, 0))
- == NULL) {
- error("%s: Signature key invalid", __func__);
- goto out;
- }
- if (!key_type_is_valid_ca(key->cert->signature_key->type)) {
- error("%s: Invalid signature key type %s (%d)", __func__,
- key_type(key->cert->signature_key),
- key->cert->signature_key->type);
- goto out;
- }
+ int r;
+ Key *ret = NULL;
- switch (key_verify(key->cert->signature_key, sig, slen,
- buffer_ptr(&key->cert->certblob), signed_len)) {
- case 1:
- ret = 0;
- break; /* Good signature */
- case 0:
- error("%s: Invalid signature on certificate", __func__);
- goto out;
- case -1:
- error("%s: Certificate signature verification failed",
- __func__);
- goto out;
- }
-
- out:
- buffer_free(&tmp);
- free(principals);
- free(critical);
- free(exts);
- free(sig_key);
- free(sig);
+ if ((r = sshkey_from_private(k, &ret)) != 0)
+ fatal("%s: %s", __func__, ssh_err(r));
return ret;
}
-static Key *
-key_from_blob2(const u_char *blob, u_int blen, int allow_cert)
+static void
+fatal_on_fatal_errors(int r, const char *func, int extra_fatal)
{
- Buffer b;
- int rlen, type;
- u_int len;
- char *ktype = NULL, *curve = NULL;
- u_char *pk = NULL;
- Key *key = NULL;
-#ifdef OPENSSL_HAS_ECC
- EC_POINT *q = NULL;
- int nid = -1;
-#endif
-
-#ifdef DEBUG_PK
- dump_base64(stderr, blob, blen);
-#endif
- buffer_init(&b);
- buffer_append(&b, blob, blen);
- if ((ktype = buffer_get_cstring_ret(&b, NULL)) == NULL) {
- error("key_from_blob: can't read key type");
- goto out;
- }
-
- type = key_type_from_name(ktype);
-#ifdef OPENSSL_HAS_ECC
- if (key_type_plain(type) == KEY_ECDSA)
- nid = key_ecdsa_nid_from_name(ktype);
-#endif
- if (!allow_cert && key_type_is_cert(type)) {
- error("key_from_blob: certificate not allowed in this context");
- goto out;
- }
- switch (type) {
-#ifdef WITH_OPENSSL
- case KEY_RSA_CERT:
- (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */
- /* FALLTHROUGH */
- case KEY_RSA:
- case KEY_RSA_CERT_V00:
- key = key_new(type);
- if (buffer_get_bignum2_ret(&b, key->rsa->e) == -1 ||
- buffer_get_bignum2_ret(&b, key->rsa->n) == -1) {
- error("key_from_blob: can't read rsa key");
- goto badkey;
- }
-#ifdef DEBUG_PK
- RSA_print_fp(stderr, key->rsa, 8);
-#endif
- break;
- case KEY_DSA_CERT:
- (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */
- /* FALLTHROUGH */
- case KEY_DSA:
- case KEY_DSA_CERT_V00:
- key = key_new(type);
- if (buffer_get_bignum2_ret(&b, key->dsa->p) == -1 ||
- buffer_get_bignum2_ret(&b, key->dsa->q) == -1 ||
- buffer_get_bignum2_ret(&b, key->dsa->g) == -1 ||
- buffer_get_bignum2_ret(&b, key->dsa->pub_key) == -1) {
- error("key_from_blob: can't read dsa key");
- goto badkey;
- }
-#ifdef DEBUG_PK
- DSA_print_fp(stderr, key->dsa, 8);
-#endif
- break;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA_CERT:
- (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */
- /* FALLTHROUGH */
- case KEY_ECDSA:
- key = key_new(type);
- key->ecdsa_nid = nid;
- if ((curve = buffer_get_string_ret(&b, NULL)) == NULL) {
- error("key_from_blob: can't read ecdsa curve");
- goto badkey;
- }
- if (key->ecdsa_nid != key_curve_name_to_nid(curve)) {
- error("key_from_blob: ecdsa curve doesn't match type");
- goto badkey;
- }
- if (key->ecdsa != NULL)
- EC_KEY_free(key->ecdsa);
- if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid))
- == NULL)
- fatal("key_from_blob: EC_KEY_new_by_curve_name failed");
- if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL)
- fatal("key_from_blob: EC_POINT_new failed");
- if (buffer_get_ecpoint_ret(&b, EC_KEY_get0_group(key->ecdsa),
- q) == -1) {
- error("key_from_blob: can't read ecdsa key point");
- goto badkey;
- }
- if (key_ec_validate_public(EC_KEY_get0_group(key->ecdsa),
- q) != 0)
- goto badkey;
- if (EC_KEY_set_public_key(key->ecdsa, q) != 1)
- fatal("key_from_blob: EC_KEY_set_public_key failed");
-#ifdef DEBUG_PK
- key_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q);
-#endif
- break;
-#endif /* OPENSSL_HAS_ECC */
- case KEY_ED25519_CERT:
- (void)buffer_get_string_ptr_ret(&b, NULL); /* Skip nonce */
- /* FALLTHROUGH */
- case KEY_ED25519:
- if ((pk = buffer_get_string_ret(&b, &len)) == NULL) {
- error("key_from_blob: can't read ed25519 key");
- goto badkey;
- }
- if (len != ED25519_PK_SZ) {
- error("key_from_blob: ed25519 len %d != %d",
- len, ED25519_PK_SZ);
- goto badkey;
- }
- key = key_new(type);
- key->ed25519_pk = pk;
- pk = NULL;
- break;
- case KEY_UNSPEC:
- key = key_new(type);
- break;
- default:
- error("key_from_blob: cannot handle type %s", ktype);
- goto out;
- }
- if (key_is_cert(key) && cert_parse(&b, key, blob, blen) == -1) {
- error("key_from_blob: can't parse cert data");
- goto badkey;
- }
- rlen = buffer_len(&b);
- if (key != NULL && rlen != 0)
- error("key_from_blob: remaining bytes in key blob %d", rlen);
- out:
- free(ktype);
- free(curve);
- free(pk);
-#ifdef OPENSSL_HAS_ECC
- if (q != NULL)
- EC_POINT_free(q);
-#endif
- buffer_free(&b);
- return key;
-
- badkey:
- key_free(key);
- key = NULL;
- goto out;
+ if (r == SSH_ERR_INTERNAL_ERROR ||
+ r == SSH_ERR_ALLOC_FAIL ||
+ (extra_fatal != 0 && r == extra_fatal))
+ fatal("%s: %s", func, ssh_err(r));
}
Key *
key_from_blob(const u_char *blob, u_int blen)
{
- return key_from_blob2(blob, blen, 1);
+ int r;
+ Key *ret = NULL;
+
+ if ((r = sshkey_from_blob(blob, blen, &ret)) != 0) {
+ fatal_on_fatal_errors(r, __func__, 0);
+ error("%s: %s", __func__, ssh_err(r));
+ return NULL;
+ }
+ return ret;
}
-static int
-to_blob(const Key *key, u_char **blobp, u_int *lenp, int force_plain)
+int
+key_to_blob(const Key *key, u_char **blobp, u_int *lenp)
{
- Buffer b;
- int len, type;
+ u_char *blob;
+ size_t blen;
+ int r;
if (blobp != NULL)
*blobp = NULL;
if (lenp != NULL)
*lenp = 0;
- if (key == NULL) {
- error("key_to_blob: key == NULL");
+ if ((r = sshkey_to_blob(key, &blob, &blen)) != 0) {
+ fatal_on_fatal_errors(r, __func__, 0);
+ error("%s: %s", __func__, ssh_err(r));
return 0;
}
- buffer_init(&b);
- type = force_plain ? key_type_plain(key->type) : key->type;
- switch (type) {
-#ifdef WITH_OPENSSL
- case KEY_DSA_CERT_V00:
- case KEY_RSA_CERT_V00:
- case KEY_DSA_CERT:
- case KEY_ECDSA_CERT:
- case KEY_RSA_CERT:
-#endif
- case KEY_ED25519_CERT:
- /* Use the existing blob */
- buffer_append(&b, buffer_ptr(&key->cert->certblob),
- buffer_len(&key->cert->certblob));
- break;
-#ifdef WITH_OPENSSL
- case KEY_DSA:
- buffer_put_cstring(&b,
- key_ssh_name_from_type_nid(type, key->ecdsa_nid));
- buffer_put_bignum2(&b, key->dsa->p);
- buffer_put_bignum2(&b, key->dsa->q);
- buffer_put_bignum2(&b, key->dsa->g);
- buffer_put_bignum2(&b, key->dsa->pub_key);
- break;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA:
- buffer_put_cstring(&b,
- key_ssh_name_from_type_nid(type, key->ecdsa_nid));
- buffer_put_cstring(&b, key_curve_nid_to_name(key->ecdsa_nid));
- buffer_put_ecpoint(&b, EC_KEY_get0_group(key->ecdsa),
- EC_KEY_get0_public_key(key->ecdsa));
- break;
-#endif
- case KEY_RSA:
- buffer_put_cstring(&b,
- key_ssh_name_from_type_nid(type, key->ecdsa_nid));
- buffer_put_bignum2(&b, key->rsa->e);
- buffer_put_bignum2(&b, key->rsa->n);
- break;
-#endif
- case KEY_ED25519:
- buffer_put_cstring(&b,
- key_ssh_name_from_type_nid(type, key->ecdsa_nid));
- buffer_put_string(&b, key->ed25519_pk, ED25519_PK_SZ);
- break;
- default:
- error("key_to_blob: unsupported key type %d", key->type);
- buffer_free(&b);
- return 0;
- }
- len = buffer_len(&b);
+ if (blen > INT_MAX)
+ fatal("%s: giant len %zu", __func__, blen);
+ if (blobp != NULL)
+ *blobp = blob;
if (lenp != NULL)
- *lenp = len;
- if (blobp != NULL) {
- *blobp = xmalloc(len);
- memcpy(*blobp, buffer_ptr(&b), len);
- }
- explicit_bzero(buffer_ptr(&b), len);
- buffer_free(&b);
- return len;
+ *lenp = blen;
+ return blen;
}
int
-key_to_blob(const Key *key, u_char **blobp, u_int *lenp)
-{
- return to_blob(key, blobp, lenp, 0);
-}
-
-int
-key_sign(
- const Key *key,
- u_char **sigp, u_int *lenp,
+key_sign(const Key *key, u_char **sigp, u_int *lenp,
const u_char *data, u_int datalen)
{
- switch (key->type) {
-#ifdef WITH_OPENSSL
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- case KEY_DSA:
- return ssh_dss_sign(key, sigp, lenp, data, datalen);
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA_CERT:
- case KEY_ECDSA:
- return ssh_ecdsa_sign(key, sigp, lenp, data, datalen);
-#endif
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- case KEY_RSA:
- return ssh_rsa_sign(key, sigp, lenp, data, datalen);
-#endif
- case KEY_ED25519:
- case KEY_ED25519_CERT:
- return ssh_ed25519_sign(key, sigp, lenp, data, datalen);
- default:
- error("key_sign: invalid key type %d", key->type);
+ int r;
+ u_char *sig;
+ size_t siglen;
+
+ if (sigp != NULL)
+ *sigp = NULL;
+ if (lenp != NULL)
+ *lenp = 0;
+ if ((r = sshkey_sign(key, &sig, &siglen,
+ data, datalen, datafellows)) != 0) {
+ fatal_on_fatal_errors(r, __func__, 0);
+ error("%s: %s", __func__, ssh_err(r));
return -1;
}
+ if (siglen > INT_MAX)
+ fatal("%s: giant len %zu", __func__, siglen);
+ if (sigp != NULL)
+ *sigp = sig;
+ if (lenp != NULL)
+ *lenp = siglen;
+ return 0;
}
-/*
- * key_verify returns 1 for a correct signature, 0 for an incorrect signature
- * and -1 on error.
- */
int
-key_verify(
- const Key *key,
- const u_char *signature, u_int signaturelen,
+key_verify(const Key *key, const u_char *signature, u_int signaturelen,
const u_char *data, u_int datalen)
{
- if (signaturelen == 0)
- return -1;
+ int r;
- switch (key->type) {
-#ifdef WITH_OPENSSL
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- case KEY_DSA:
- return ssh_dss_verify(key, signature, signaturelen, data, datalen);
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA_CERT:
- case KEY_ECDSA:
- return ssh_ecdsa_verify(key, signature, signaturelen, data, datalen);
-#endif
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- case KEY_RSA:
- return ssh_rsa_verify(key, signature, signaturelen, data, datalen);
-#endif
- case KEY_ED25519:
- case KEY_ED25519_CERT:
- return ssh_ed25519_verify(key, signature, signaturelen, data, datalen);
- default:
- error("key_verify: invalid key type %d", key->type);
- return -1;
+ if ((r = sshkey_verify(key, signature, signaturelen,
+ data, datalen, datafellows)) != 0) {
+ fatal_on_fatal_errors(r, __func__, 0);
+ error("%s: %s", __func__, ssh_err(r));
+ return r == SSH_ERR_SIGNATURE_INVALID ? 0 : -1;
}
+ return 1;
}
-/* Converts a private to a public key */
Key *
key_demote(const Key *k)
{
- Key *pk;
-
- pk = xcalloc(1, sizeof(*pk));
- pk->type = k->type;
- pk->flags = k->flags;
- pk->ecdsa_nid = k->ecdsa_nid;
- pk->dsa = NULL;
- pk->ecdsa = NULL;
- pk->rsa = NULL;
- pk->ed25519_pk = NULL;
- pk->ed25519_sk = NULL;
-
- switch (k->type) {
-#ifdef WITH_OPENSSL
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- key_cert_copy(k, pk);
- /* FALLTHROUGH */
- case KEY_RSA1:
- case KEY_RSA:
- if ((pk->rsa = RSA_new()) == NULL)
- fatal("key_demote: RSA_new failed");
- if ((pk->rsa->e = BN_dup(k->rsa->e)) == NULL)
- fatal("key_demote: BN_dup failed");
- if ((pk->rsa->n = BN_dup(k->rsa->n)) == NULL)
- fatal("key_demote: BN_dup failed");
- break;
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- key_cert_copy(k, pk);
- /* FALLTHROUGH */
- case KEY_DSA:
- if ((pk->dsa = DSA_new()) == NULL)
- fatal("key_demote: DSA_new failed");
- if ((pk->dsa->p = BN_dup(k->dsa->p)) == NULL)
- fatal("key_demote: BN_dup failed");
- if ((pk->dsa->q = BN_dup(k->dsa->q)) == NULL)
- fatal("key_demote: BN_dup failed");
- if ((pk->dsa->g = BN_dup(k->dsa->g)) == NULL)
- fatal("key_demote: BN_dup failed");
- if ((pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL)
- fatal("key_demote: BN_dup failed");
- break;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA_CERT:
- key_cert_copy(k, pk);
- /* FALLTHROUGH */
- case KEY_ECDSA:
- if ((pk->ecdsa = EC_KEY_new_by_curve_name(pk->ecdsa_nid)) == NULL)
- fatal("key_demote: EC_KEY_new_by_curve_name failed");
- if (EC_KEY_set_public_key(pk->ecdsa,
- EC_KEY_get0_public_key(k->ecdsa)) != 1)
- fatal("key_demote: EC_KEY_set_public_key failed");
- break;
-#endif
- case KEY_ED25519_CERT:
- key_cert_copy(k, pk);
- /* FALLTHROUGH */
- case KEY_ED25519:
- if (k->ed25519_pk != NULL) {
- pk->ed25519_pk = xmalloc(ED25519_PK_SZ);
- memcpy(pk->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
- }
- break;
- default:
- fatal("key_demote: bad key type %d", k->type);
- break;
- }
-
- return (pk);
-}
-
-int
-key_is_cert(const Key *k)
-{
- if (k == NULL)
- return 0;
- return key_type_is_cert(k->type);
-}
+ int r;
+ Key *ret = NULL;
-/* Return the cert-less equivalent to a certified key type */
-int
-key_type_plain(int type)
-{
- switch (type) {
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- return KEY_RSA;
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- return KEY_DSA;
- case KEY_ECDSA_CERT:
- return KEY_ECDSA;
- case KEY_ED25519_CERT:
- return KEY_ED25519;
- default:
- return type;
- }
+ if ((r = sshkey_demote(k, &ret)) != 0)
+ fatal("%s: %s", __func__, ssh_err(r));
+ return ret;
}
-/* Convert a plain key to their _CERT equivalent */
int
key_to_certified(Key *k, int legacy)
{
- switch (k->type) {
- case KEY_RSA:
- k->cert = cert_new();
- k->type = legacy ? KEY_RSA_CERT_V00 : KEY_RSA_CERT;
- return 0;
- case KEY_DSA:
- k->cert = cert_new();
- k->type = legacy ? KEY_DSA_CERT_V00 : KEY_DSA_CERT;
- return 0;
- case KEY_ECDSA:
- if (legacy)
- fatal("%s: legacy ECDSA certificates are not supported",
- __func__);
- k->cert = cert_new();
- k->type = KEY_ECDSA_CERT;
- return 0;
- case KEY_ED25519:
- if (legacy)
- fatal("%s: legacy ED25519 certificates are not "
- "supported", __func__);
- k->cert = cert_new();
- k->type = KEY_ED25519_CERT;
- return 0;
- default:
- error("%s: key has incorrect type %s", __func__, key_type(k));
+ int r;
+
+ if ((r = sshkey_to_certified(k, legacy)) != 0) {
+ fatal_on_fatal_errors(r, __func__, 0);
+ error("%s: %s", __func__, ssh_err(r));
return -1;
}
+ return 0;
}
-/* Convert a certificate to its raw key equivalent */
int
key_drop_cert(Key *k)
{
- if (!key_type_is_cert(k->type)) {
- error("%s: key has incorrect type %s", __func__, key_type(k));
+ int r;
+
+ if ((r = sshkey_drop_cert(k)) != 0) {
+ fatal_on_fatal_errors(r, __func__, 0);
+ error("%s: %s", __func__, ssh_err(r));
return -1;
}
- cert_free(k->cert);
- k->cert = NULL;
- k->type = key_type_plain(k->type);
return 0;
}
-/* Sign a certified key, (re-)generating the signed certblob. */
int
key_certify(Key *k, Key *ca)
{
- Buffer principals;
- u_char *ca_blob, *sig_blob, nonce[32];
- u_int i, ca_len, sig_len;
+ int r;
- if (k->cert == NULL) {
- error("%s: key lacks cert info", __func__);
+ if ((r = sshkey_certify(k, ca)) != 0) {
+ fatal_on_fatal_errors(r, __func__, 0);
+ error("%s: %s", __func__, ssh_err(r));
return -1;
}
+ return 0;
+}
- if (!key_is_cert(k)) {
- error("%s: certificate has unknown type %d", __func__,
- k->cert->type);
- return -1;
- }
+int
+key_cert_check_authority(const Key *k, int want_host, int require_principal,
+ const char *name, const char **reason)
+{
+ int r;
- if (!key_type_is_valid_ca(ca->type)) {
- error("%s: CA key has unsupported type %s", __func__,
- key_type(ca));
+ if ((r = sshkey_cert_check_authority(k, want_host, require_principal,
+ name, reason)) != 0) {
+ fatal_on_fatal_errors(r, __func__, 0);
+ error("%s: %s", __func__, ssh_err(r));
return -1;
}
+ return 0;
+}
- key_to_blob(ca, &ca_blob, &ca_len);
-
- buffer_clear(&k->cert->certblob);
- buffer_put_cstring(&k->cert->certblob, key_ssh_name(k));
-
- /* -v01 certs put nonce first */
- arc4random_buf(&nonce, sizeof(nonce));
- if (!key_cert_is_legacy(k))
- buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
-
- /* XXX this substantially duplicates to_blob(); refactor */
- switch (k->type) {
#ifdef WITH_OPENSSL
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- buffer_put_bignum2(&k->cert->certblob, k->dsa->p);
- buffer_put_bignum2(&k->cert->certblob, k->dsa->q);
- buffer_put_bignum2(&k->cert->certblob, k->dsa->g);
- buffer_put_bignum2(&k->cert->certblob, k->dsa->pub_key);
- break;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA_CERT:
- buffer_put_cstring(&k->cert->certblob,
- key_curve_nid_to_name(k->ecdsa_nid));
- buffer_put_ecpoint(&k->cert->certblob,
- EC_KEY_get0_group(k->ecdsa),
- EC_KEY_get0_public_key(k->ecdsa));
- break;
-#endif
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- buffer_put_bignum2(&k->cert->certblob, k->rsa->e);
- buffer_put_bignum2(&k->cert->certblob, k->rsa->n);
- break;
-#endif
- case KEY_ED25519_CERT:
- buffer_put_string(&k->cert->certblob,
- k->ed25519_pk, ED25519_PK_SZ);
- break;
- default:
- error("%s: key has incorrect type %s", __func__, key_type(k));
- buffer_clear(&k->cert->certblob);
- free(ca_blob);
- return -1;
- }
-
- /* -v01 certs have a serial number next */
- if (!key_cert_is_legacy(k))
- buffer_put_int64(&k->cert->certblob, k->cert->serial);
-
- buffer_put_int(&k->cert->certblob, k->cert->type);
- buffer_put_cstring(&k->cert->certblob, k->cert->key_id);
-
- buffer_init(&principals);
- for (i = 0; i < k->cert->nprincipals; i++)
- buffer_put_cstring(&principals, k->cert->principals[i]);
- buffer_put_string(&k->cert->certblob, buffer_ptr(&principals),
- buffer_len(&principals));
- buffer_free(&principals);
-
- buffer_put_int64(&k->cert->certblob, k->cert->valid_after);
- buffer_put_int64(&k->cert->certblob, k->cert->valid_before);
- buffer_put_string(&k->cert->certblob,
- buffer_ptr(&k->cert->critical), buffer_len(&k->cert->critical));
-
- /* -v01 certs have non-critical options here */
- if (!key_cert_is_legacy(k)) {
- buffer_put_string(&k->cert->certblob,
- buffer_ptr(&k->cert->extensions),
- buffer_len(&k->cert->extensions));
- }
-
- /* -v00 certs put the nonce at the end */
- if (key_cert_is_legacy(k))
- buffer_put_string(&k->cert->certblob, nonce, sizeof(nonce));
-
- buffer_put_string(&k->cert->certblob, NULL, 0); /* reserved */
- buffer_put_string(&k->cert->certblob, ca_blob, ca_len);
- free(ca_blob);
+int
+key_ec_validate_public(const EC_GROUP *group, const EC_POINT *public)
+{
+ int r;
- /* Sign the whole mess */
- if (key_sign(ca, &sig_blob, &sig_len, buffer_ptr(&k->cert->certblob),
- buffer_len(&k->cert->certblob)) != 0) {
- error("%s: signature operation failed", __func__);
- buffer_clear(&k->cert->certblob);
+ if ((r = sshkey_ec_validate_public(group, public)) != 0) {
+ fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
+ error("%s: %s", __func__, ssh_err(r));
return -1;
}
- /* Append signature and we are done */
- buffer_put_string(&k->cert->certblob, sig_blob, sig_len);
- free(sig_blob);
-
return 0;
}
int
-key_cert_check_authority(const Key *k, int want_host, int require_principal,
- const char *name, const char **reason)
+key_ec_validate_private(const EC_KEY *key)
{
- u_int i, principal_matches;
- time_t now = time(NULL);
-
- if (want_host) {
- if (k->cert->type != SSH2_CERT_TYPE_HOST) {
- *reason = "Certificate invalid: not a host certificate";
- return -1;
- }
- } else {
- if (k->cert->type != SSH2_CERT_TYPE_USER) {
- *reason = "Certificate invalid: not a user certificate";
- return -1;
- }
- }
- if (now < 0) {
- error("%s: system clock lies before epoch", __func__);
- *reason = "Certificate invalid: not yet valid";
- return -1;
- }
- if ((u_int64_t)now < k->cert->valid_after) {
- *reason = "Certificate invalid: not yet valid";
- return -1;
- }
- if ((u_int64_t)now >= k->cert->valid_before) {
- *reason = "Certificate invalid: expired";
+ int r;
+
+ if ((r = sshkey_ec_validate_private(key)) != 0) {
+ fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
+ error("%s: %s", __func__, ssh_err(r));
return -1;
}
- if (k->cert->nprincipals == 0) {
- if (require_principal) {
- *reason = "Certificate lacks principal list";
- return -1;
- }
- } else if (name != NULL) {
- principal_matches = 0;
- for (i = 0; i < k->cert->nprincipals; i++) {
- if (strcmp(name, k->cert->principals[i]) == 0) {
- principal_matches = 1;
- break;
- }
- }
- if (!principal_matches) {
- *reason = "Certificate invalid: name is not a listed "
- "principal";
- return -1;
- }
- }
return 0;
}
+#endif /* WITH_OPENSSL */
-int
-key_cert_is_legacy(const Key *k)
+void
+key_private_serialize(const Key *key, struct sshbuf *b)
{
- switch (k->type) {
- case KEY_DSA_CERT_V00:
- case KEY_RSA_CERT_V00:
- return 1;
- default:
- return 0;
- }
-}
+ int r;
-#ifdef WITH_OPENSSL
-/* XXX: these are really begging for a table-driven approach */
-int
-key_curve_name_to_nid(const char *name)
-{
-#ifdef OPENSSL_HAS_ECC
- if (strcmp(name, "nistp256") == 0)
- return NID_X9_62_prime256v1;
- else if (strcmp(name, "nistp384") == 0)
- return NID_secp384r1;
-# ifdef OPENSSL_HAS_NISTP521
- else if (strcmp(name, "nistp521") == 0)
- return NID_secp521r1;
-# endif
-#endif
-
- debug("%s: unsupported EC curve name \"%.100s\"", __func__, name);
- return -1;
+ if ((r = sshkey_private_serialize(key, b)) != 0)
+ fatal("%s: %s", __func__, ssh_err(r));
}
-u_int
-key_curve_nid_to_bits(int nid)
+Key *
+key_private_deserialize(struct sshbuf *blob)
{
- switch (nid) {
-#ifdef OPENSSL_HAS_ECC
- case NID_X9_62_prime256v1:
- return 256;
- case NID_secp384r1:
- return 384;
-# ifdef OPENSSL_HAS_NISTP521
- case NID_secp521r1:
- return 521;
-# endif
-#endif
- default:
- error("%s: unsupported EC curve nid %d", __func__, nid);
- return 0;
+ int r;
+ Key *ret = NULL;
+
+ if ((r = sshkey_private_deserialize(blob, &ret)) != 0) {
+ fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
+ error("%s: %s", __func__, ssh_err(r));
+ return NULL;
}
+ return ret;
}
-const char *
-key_curve_nid_to_name(int nid)
-{
-#ifdef OPENSSL_HAS_ECC
- if (nid == NID_X9_62_prime256v1)
- return "nistp256";
- else if (nid == NID_secp384r1)
- return "nistp384";
-# ifdef OPENSSL_HAS_NISTP521
- else if (nid == NID_secp521r1)
- return "nistp521";
-# endif
-#endif
- error("%s: unsupported EC curve nid %d", __func__, nid);
- return NULL;
-}
+/* authfile.c */
-#ifdef OPENSSL_HAS_ECC
int
-key_ec_nid_to_hash_alg(int nid)
+key_save_private(Key *key, const char *filename, const char *passphrase,
+ const char *comment, int force_new_format, const char *new_format_cipher,
+ int new_format_rounds)
{
- int kbits = key_curve_nid_to_bits(nid);
-
- if (kbits == 0)
- fatal("%s: invalid nid %d", __func__, nid);
- /* RFC5656 section 6.2.1 */
- if (kbits <= 256)
- return SSH_DIGEST_SHA256;
- else if (kbits <= 384)
- return SSH_DIGEST_SHA384;
- else
- return SSH_DIGEST_SHA512;
+ int r;
+
+ if ((r = sshkey_save_private(key, filename, passphrase, comment,
+ force_new_format, new_format_cipher, new_format_rounds)) != 0) {
+ fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
+ error("%s: %s", __func__, ssh_err(r));
+ return 0;
+ }
+ return 1;
}
int
-key_ec_validate_public(const EC_GROUP *group, const EC_POINT *public)
+key_load_file(int fd, const char *filename, struct sshbuf *blob)
{
- BN_CTX *bnctx;
- EC_POINT *nq = NULL;
- BIGNUM *order, *x, *y, *tmp;
- int ret = -1;
-
- if ((bnctx = BN_CTX_new()) == NULL)
- fatal("%s: BN_CTX_new failed", __func__);
- BN_CTX_start(bnctx);
-
- /*
- * We shouldn't ever hit this case because bignum_get_ecpoint()
- * refuses to load GF2m points.
- */
- if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
- NID_X9_62_prime_field) {
- error("%s: group is not a prime field", __func__);
- goto out;
- }
-
- /* Q != infinity */
- if (EC_POINT_is_at_infinity(group, public)) {
- error("%s: received degenerate public key (infinity)",
- __func__);
- goto out;
- }
+ int r;
- if ((x = BN_CTX_get(bnctx)) == NULL ||
- (y = BN_CTX_get(bnctx)) == NULL ||
- (order = BN_CTX_get(bnctx)) == NULL ||
- (tmp = BN_CTX_get(bnctx)) == NULL)
- fatal("%s: BN_CTX_get failed", __func__);
-
- /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */
- if (EC_GROUP_get_order(group, order, bnctx) != 1)
- fatal("%s: EC_GROUP_get_order failed", __func__);
- if (EC_POINT_get_affine_coordinates_GFp(group, public,
- x, y, bnctx) != 1)
- fatal("%s: EC_POINT_get_affine_coordinates_GFp", __func__);
- if (BN_num_bits(x) <= BN_num_bits(order) / 2) {
- error("%s: public key x coordinate too small: "
- "bits(x) = %d, bits(order)/2 = %d", __func__,
- BN_num_bits(x), BN_num_bits(order) / 2);
- goto out;
- }
- if (BN_num_bits(y) <= BN_num_bits(order) / 2) {
- error("%s: public key y coordinate too small: "
- "bits(y) = %d, bits(order)/2 = %d", __func__,
- BN_num_bits(x), BN_num_bits(order) / 2);
- goto out;
+ if ((r = sshkey_load_file(fd, filename, blob)) != 0) {
+ fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
+ error("%s: %s", __func__, ssh_err(r));
+ return 0;
}
+ return 1;
+}
- /* nQ == infinity (n == order of subgroup) */
- if ((nq = EC_POINT_new(group)) == NULL)
- fatal("%s: BN_CTX_tmp failed", __func__);
- if (EC_POINT_mul(group, nq, NULL, public, order, bnctx) != 1)
- fatal("%s: EC_GROUP_mul failed", __func__);
- if (EC_POINT_is_at_infinity(group, nq) != 1) {
- error("%s: received degenerate public key (nQ != infinity)",
- __func__);
- goto out;
- }
+Key *
+key_load_cert(const char *filename)
+{
+ int r;
+ Key *ret = NULL;
- /* x < order - 1, y < order - 1 */
- if (!BN_sub(tmp, order, BN_value_one()))
- fatal("%s: BN_sub failed", __func__);
- if (BN_cmp(x, tmp) >= 0) {
- error("%s: public key x coordinate >= group order - 1",
- __func__);
- goto out;
- }
- if (BN_cmp(y, tmp) >= 0) {
- error("%s: public key y coordinate >= group order - 1",
- __func__);
- goto out;
+ if ((r = sshkey_load_cert(filename, &ret)) != 0) {
+ fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
+ if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT)
+ debug("%s: %s", __func__, ssh_err(r));
+ else
+ error("%s: %s", __func__, ssh_err(r));
+ return NULL;
}
- ret = 0;
- out:
- BN_CTX_free(bnctx);
- EC_POINT_free(nq);
return ret;
+
}
-int
-key_ec_validate_private(const EC_KEY *key)
+Key *
+key_load_public(const char *filename, char **commentp)
{
- BN_CTX *bnctx;
- BIGNUM *order, *tmp;
- int ret = -1;
-
- if ((bnctx = BN_CTX_new()) == NULL)
- fatal("%s: BN_CTX_new failed", __func__);
- BN_CTX_start(bnctx);
-
- if ((order = BN_CTX_get(bnctx)) == NULL ||
- (tmp = BN_CTX_get(bnctx)) == NULL)
- fatal("%s: BN_CTX_get failed", __func__);
-
- /* log2(private) > log2(order)/2 */
- if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, bnctx) != 1)
- fatal("%s: EC_GROUP_get_order failed", __func__);
- if (BN_num_bits(EC_KEY_get0_private_key(key)) <=
- BN_num_bits(order) / 2) {
- error("%s: private key too small: "
- "bits(y) = %d, bits(order)/2 = %d", __func__,
- BN_num_bits(EC_KEY_get0_private_key(key)),
- BN_num_bits(order) / 2);
- goto out;
- }
+ int r;
+ Key *ret = NULL;
- /* private < order - 1 */
- if (!BN_sub(tmp, order, BN_value_one()))
- fatal("%s: BN_sub failed", __func__);
- if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0) {
- error("%s: private key >= group order - 1", __func__);
- goto out;
+ if ((r = sshkey_load_public(filename, &ret, commentp)) != 0) {
+ fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
+ if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT)
+ debug("%s: %s", __func__, ssh_err(r));
+ else
+ error("%s: %s", __func__, ssh_err(r));
+ return NULL;
}
- ret = 0;
- out:
- BN_CTX_free(bnctx);
return ret;
}
-#endif
-#if defined(DEBUG_KEXECDH) || defined(DEBUG_PK)
-void
-key_dump_ec_point(const EC_GROUP *group, const EC_POINT *point)
-{
- BIGNUM *x, *y;
- BN_CTX *bnctx;
-
- if (point == NULL) {
- fputs("point=(NULL)\n", stderr);
- return;
+Key *
+key_load_private(const char *path, const char *passphrase,
+ char **commentp)
+{
+ int r;
+ Key *ret = NULL;
+
+ if ((r = sshkey_load_private(path, passphrase, &ret, commentp)) != 0) {
+ fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
+ if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT)
+ debug("%s: %s", __func__, ssh_err(r));
+ else
+ error("%s: %s", __func__, ssh_err(r));
+ return NULL;
}
- if ((bnctx = BN_CTX_new()) == NULL)
- fatal("%s: BN_CTX_new failed", __func__);
- BN_CTX_start(bnctx);
- if ((x = BN_CTX_get(bnctx)) == NULL || (y = BN_CTX_get(bnctx)) == NULL)
- fatal("%s: BN_CTX_get failed", __func__);
- if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
- NID_X9_62_prime_field)
- fatal("%s: group is not a prime field", __func__);
- if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y, bnctx) != 1)
- fatal("%s: EC_POINT_get_affine_coordinates_GFp", __func__);
- fputs("x=", stderr);
- BN_print_fp(stderr, x);
- fputs("\ny=", stderr);
- BN_print_fp(stderr, y);
- fputs("\n", stderr);
- BN_CTX_free(bnctx);
+ return ret;
}
-void
-key_dump_ec_key(const EC_KEY *key)
-{
- const BIGNUM *exponent;
-
- key_dump_ec_point(EC_KEY_get0_group(key), EC_KEY_get0_public_key(key));
- fputs("exponent=", stderr);
- if ((exponent = EC_KEY_get0_private_key(key)) == NULL)
- fputs("(NULL)", stderr);
- else
- BN_print_fp(stderr, EC_KEY_get0_private_key(key));
- fputs("\n", stderr);
+Key *
+key_load_private_cert(int type, const char *filename, const char *passphrase,
+ int *perm_ok)
+{
+ int r;
+ Key *ret = NULL;
+
+ if ((r = sshkey_load_private_cert(type, filename, passphrase,
+ &ret, perm_ok)) != 0) {
+ fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
+ if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT)
+ debug("%s: %s", __func__, ssh_err(r));
+ else
+ error("%s: %s", __func__, ssh_err(r));
+ return NULL;
+ }
+ return ret;
}
-#endif /* defined(DEBUG_KEXECDH) || defined(DEBUG_PK) */
-#endif /* OPENSSL_HAS_ECC */
-void
-key_private_serialize(const Key *key, Buffer *b)
-{
- buffer_put_cstring(b, key_ssh_name(key));
- switch (key->type) {
-#ifdef WITH_OPENSSL
- case KEY_RSA:
- buffer_put_bignum2(b, key->rsa->n);
- buffer_put_bignum2(b, key->rsa->e);
- buffer_put_bignum2(b, key->rsa->d);
- buffer_put_bignum2(b, key->rsa->iqmp);
- buffer_put_bignum2(b, key->rsa->p);
- buffer_put_bignum2(b, key->rsa->q);
- break;
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0)
- fatal("%s: no cert/certblob", __func__);
- buffer_put_string(b, buffer_ptr(&key->cert->certblob),
- buffer_len(&key->cert->certblob));
- buffer_put_bignum2(b, key->rsa->d);
- buffer_put_bignum2(b, key->rsa->iqmp);
- buffer_put_bignum2(b, key->rsa->p);
- buffer_put_bignum2(b, key->rsa->q);
- break;
- case KEY_DSA:
- buffer_put_bignum2(b, key->dsa->p);
- buffer_put_bignum2(b, key->dsa->q);
- buffer_put_bignum2(b, key->dsa->g);
- buffer_put_bignum2(b, key->dsa->pub_key);
- buffer_put_bignum2(b, key->dsa->priv_key);
- break;
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0)
- fatal("%s: no cert/certblob", __func__);
- buffer_put_string(b, buffer_ptr(&key->cert->certblob),
- buffer_len(&key->cert->certblob));
- buffer_put_bignum2(b, key->dsa->priv_key);
- break;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA:
- buffer_put_cstring(b, key_curve_nid_to_name(key->ecdsa_nid));
- buffer_put_ecpoint(b, EC_KEY_get0_group(key->ecdsa),
- EC_KEY_get0_public_key(key->ecdsa));
- buffer_put_bignum2(b, EC_KEY_get0_private_key(key->ecdsa));
- break;
- case KEY_ECDSA_CERT:
- if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0)
- fatal("%s: no cert/certblob", __func__);
- buffer_put_string(b, buffer_ptr(&key->cert->certblob),
- buffer_len(&key->cert->certblob));
- buffer_put_bignum2(b, EC_KEY_get0_private_key(key->ecdsa));
- break;
-#endif /* OPENSSL_HAS_ECC */
- case KEY_ED25519:
- buffer_put_string(b, key->ed25519_pk, ED25519_PK_SZ);
- buffer_put_string(b, key->ed25519_sk, ED25519_SK_SZ);
- break;
-#endif
-#endif
- case KEY_ED25519_CERT:
- if (key->cert == NULL || buffer_len(&key->cert->certblob) == 0)
- fatal("%s: no cert/certblob", __func__);
- buffer_put_string(b, buffer_ptr(&key->cert->certblob),
- buffer_len(&key->cert->certblob));
- buffer_put_string(b, key->ed25519_pk, ED25519_PK_SZ);
- buffer_put_string(b, key->ed25519_sk, ED25519_SK_SZ);
- break;
+Key *
+key_load_private_type(int type, const char *filename, const char *passphrase,
+ char **commentp, int *perm_ok)
+{
+ int r;
+ Key *ret = NULL;
+
+ if ((r = sshkey_load_private_type(type, filename, passphrase,
+ &ret, commentp, perm_ok)) != 0) {
+ fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
+ if ((r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT) ||
+ (r == SSH_ERR_KEY_WRONG_PASSPHRASE))
+ debug("%s: %s", __func__, ssh_err(r));
+ else
+ error("%s: %s", __func__, ssh_err(r));
+ return NULL;
}
+ return ret;
}
+#ifdef WITH_OPENSSL
Key *
-key_private_deserialize(Buffer *blob)
+key_load_private_pem(int fd, int type, const char *passphrase,
+ char **commentp)
{
- char *type_name;
- Key *k = NULL;
- u_char *cert;
- u_int len, pklen, sklen;
- int type;
-#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
- char *curve;
- BIGNUM *exponent;
- EC_POINT *q;
-#endif
-
- type_name = buffer_get_string(blob, NULL);
- type = key_type_from_name(type_name);
- switch (type) {
-#ifdef WITH_OPENSSL
- case KEY_DSA:
- k = key_new_private(type);
- buffer_get_bignum2(blob, k->dsa->p);
- buffer_get_bignum2(blob, k->dsa->q);
- buffer_get_bignum2(blob, k->dsa->g);
- buffer_get_bignum2(blob, k->dsa->pub_key);
- buffer_get_bignum2(blob, k->dsa->priv_key);
- break;
- case KEY_DSA_CERT_V00:
- case KEY_DSA_CERT:
- cert = buffer_get_string(blob, &len);
- if ((k = key_from_blob(cert, len)) == NULL)
- fatal("Certificate parse failed");
- free(cert);
- key_add_private(k);
- buffer_get_bignum2(blob, k->dsa->priv_key);
- break;
-#ifdef OPENSSL_HAS_ECC
- case KEY_ECDSA:
- k = key_new_private(type);
- k->ecdsa_nid = key_ecdsa_nid_from_name(type_name);
- curve = buffer_get_string(blob, NULL);
- if (k->ecdsa_nid != key_curve_name_to_nid(curve))
- fatal("%s: curve names mismatch", __func__);
- free(curve);
- k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid);
- if (k->ecdsa == NULL)
- fatal("%s: EC_KEY_new_by_curve_name failed",
- __func__);
- q = EC_POINT_new(EC_KEY_get0_group(k->ecdsa));
- if (q == NULL)
- fatal("%s: BN_new failed", __func__);
- if ((exponent = BN_new()) == NULL)
- fatal("%s: BN_new failed", __func__);
- buffer_get_ecpoint(blob,
- EC_KEY_get0_group(k->ecdsa), q);
- buffer_get_bignum2(blob, exponent);
- if (EC_KEY_set_public_key(k->ecdsa, q) != 1)
- fatal("%s: EC_KEY_set_public_key failed",
- __func__);
- if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1)
- fatal("%s: EC_KEY_set_private_key failed",
- __func__);
- if (key_ec_validate_public(EC_KEY_get0_group(k->ecdsa),
- EC_KEY_get0_public_key(k->ecdsa)) != 0)
- fatal("%s: bad ECDSA public key", __func__);
- if (key_ec_validate_private(k->ecdsa) != 0)
- fatal("%s: bad ECDSA private key", __func__);
- BN_clear_free(exponent);
- EC_POINT_free(q);
- break;
- case KEY_ECDSA_CERT:
- cert = buffer_get_string(blob, &len);
- if ((k = key_from_blob(cert, len)) == NULL)
- fatal("Certificate parse failed");
- free(cert);
- key_add_private(k);
- if ((exponent = BN_new()) == NULL)
- fatal("%s: BN_new failed", __func__);
- buffer_get_bignum2(blob, exponent);
- if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1)
- fatal("%s: EC_KEY_set_private_key failed",
- __func__);
- if (key_ec_validate_public(EC_KEY_get0_group(k->ecdsa),
- EC_KEY_get0_public_key(k->ecdsa)) != 0 ||
- key_ec_validate_private(k->ecdsa) != 0)
- fatal("%s: bad ECDSA key", __func__);
- BN_clear_free(exponent);
- break;
-#endif
- case KEY_RSA:
- k = key_new_private(type);
- buffer_get_bignum2(blob, k->rsa->n);
- buffer_get_bignum2(blob, k->rsa->e);
- buffer_get_bignum2(blob, k->rsa->d);
- buffer_get_bignum2(blob, k->rsa->iqmp);
- buffer_get_bignum2(blob, k->rsa->p);
- buffer_get_bignum2(blob, k->rsa->q);
-
- /* Generate additional parameters */
- rsa_generate_additional_parameters(k->rsa);
- break;
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- cert = buffer_get_string(blob, &len);
- if ((k = key_from_blob(cert, len)) == NULL)
- fatal("Certificate parse failed");
- free(cert);
- key_add_private(k);
- buffer_get_bignum2(blob, k->rsa->d);
- buffer_get_bignum2(blob, k->rsa->iqmp);
- buffer_get_bignum2(blob, k->rsa->p);
- buffer_get_bignum2(blob, k->rsa->q);
- break;
-#endif
-#endif
- case KEY_ED25519:
- k = key_new_private(type);
- k->ed25519_pk = buffer_get_string(blob, &pklen);
- k->ed25519_sk = buffer_get_string(blob, &sklen);
- if (pklen != ED25519_PK_SZ)
- fatal("%s: ed25519 pklen %d != %d",
- __func__, pklen, ED25519_PK_SZ);
- if (sklen != ED25519_SK_SZ)
- fatal("%s: ed25519 sklen %d != %d",
- __func__, sklen, ED25519_SK_SZ);
- break;
- case KEY_ED25519_CERT:
- cert = buffer_get_string(blob, &len);
- if ((k = key_from_blob(cert, len)) == NULL)
- fatal("Certificate parse failed");
- free(cert);
- key_add_private(k);
- k->ed25519_pk = buffer_get_string(blob, &pklen);
- k->ed25519_sk = buffer_get_string(blob, &sklen);
- if (pklen != ED25519_PK_SZ)
- fatal("%s: ed25519 pklen %d != %d",
- __func__, pklen, ED25519_PK_SZ);
- if (sklen != ED25519_SK_SZ)
- fatal("%s: ed25519 sklen %d != %d",
- __func__, sklen, ED25519_SK_SZ);
- break;
- default:
- free(type_name);
- buffer_clear(blob);
+ int r;
+ Key *ret = NULL;
+
+ if ((r = sshkey_load_private_pem(fd, type, passphrase,
+ &ret, commentp)) != 0) {
+ fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
+ error("%s: %s", __func__, ssh_err(r));
return NULL;
}
- free(type_name);
+ return ret;
+}
+#endif /* WITH_OPENSSL */
- /* enable blinding */
- switch (k->type) {
-#ifdef WITH_OPENSSL
- case KEY_RSA:
- case KEY_RSA_CERT_V00:
- case KEY_RSA_CERT:
- case KEY_RSA1:
- if (RSA_blinding_on(k->rsa, NULL) != 1) {
- error("%s: RSA_blinding_on failed", __func__);
- key_free(k);
- return NULL;
- }
- break;
-#endif
+int
+key_perm_ok(int fd, const char *filename)
+{
+ return sshkey_perm_ok(fd, filename) == 0 ? 1 : 0;
+}
+
+int
+key_in_file(Key *key, const char *filename, int strict_type)
+{
+ int r;
+
+ if ((r = sshkey_in_file(key, filename, strict_type)) != 0) {
+ fatal_on_fatal_errors(r, __func__, SSH_ERR_LIBCRYPTO_ERROR);
+ if (r == SSH_ERR_SYSTEM_ERROR && errno == ENOENT)
+ return 0;
+ error("%s: %s", __func__, ssh_err(r));
+ return r == SSH_ERR_KEY_NOT_FOUND ? 0 : -1;
}
- return k;
+ return 1;
}
diff --git a/key.h b/key.h
index d8ad13d0..4be4fedd 100644
--- a/key.h
+++ b/key.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: key.h,v 1.41 2014/01/09 23:20:00 djm Exp $ */
+/* $OpenBSD: key.h,v 1.42 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
@@ -26,141 +26,86 @@
#ifndef KEY_H
#define KEY_H
-#include "buffer.h"
-#include <openssl/rsa.h>
-#include <openssl/dsa.h>
-#ifdef OPENSSL_HAS_ECC
-#include <openssl/ec.h>
+#include "sshkey.h"
+
+typedef struct sshkey Key;
+
+#define types sshkey_types
+#define fp_type sshkey_fp_type
+#define fp_rep sshkey_fp_rep
+
+#ifndef SSH_KEY_NO_DEFINE
+#define key_new sshkey_new
+#define key_free sshkey_free
+#define key_equal_public sshkey_equal_public
+#define key_equal sshkey_equal
+#define key_fingerprint sshkey_fingerprint
+#define key_type sshkey_type
+#define key_cert_type sshkey_cert_type
+#define key_ssh_name sshkey_ssh_name
+#define key_ssh_name_plain sshkey_ssh_name_plain
+#define key_type_from_name sshkey_type_from_name
+#define key_ecdsa_nid_from_name sshkey_ecdsa_nid_from_name
+#define key_type_is_cert sshkey_type_is_cert
+#define key_size sshkey_size
+#define key_ecdsa_bits_to_nid sshkey_ecdsa_bits_to_nid
+#define key_ecdsa_key_to_nid sshkey_ecdsa_key_to_nid
+#define key_names_valid2 sshkey_names_valid2
+#define key_is_cert sshkey_is_cert
+#define key_type_plain sshkey_type_plain
+#define key_cert_is_legacy sshkey_cert_is_legacy
+#define key_curve_name_to_nid sshkey_curve_name_to_nid
+#define key_curve_nid_to_bits sshkey_curve_nid_to_bits
+#define key_curve_nid_to_name sshkey_curve_nid_to_name
+#define key_ec_nid_to_hash_alg sshkey_ec_nid_to_hash_alg
+#define key_dump_ec_point sshkey_dump_ec_point
+#define key_dump_ec_key sshkey_dump_ec_key
+#define key_fingerprint sshkey_fingerprint
#endif
-typedef struct Key Key;
-enum types {
- KEY_RSA1,
- KEY_RSA,
- KEY_DSA,
- KEY_ECDSA,
- KEY_ED25519,
- KEY_RSA_CERT,
- KEY_DSA_CERT,
- KEY_ECDSA_CERT,
- KEY_ED25519_CERT,
- KEY_RSA_CERT_V00,
- KEY_DSA_CERT_V00,
- KEY_UNSPEC
-};
-enum fp_type {
- SSH_FP_SHA1,
- SSH_FP_MD5,
- SSH_FP_SHA256
-};
-enum fp_rep {
- SSH_FP_HEX,
- SSH_FP_BUBBLEBABBLE,
- SSH_FP_RANDOMART
-};
-
-/* key is stored in external hardware */
-#define KEY_FLAG_EXT 0x0001
-
-#define CERT_MAX_PRINCIPALS 256
-struct KeyCert {
- Buffer certblob; /* Kept around for use on wire */
- u_int type; /* SSH2_CERT_TYPE_USER or SSH2_CERT_TYPE_HOST */
- u_int64_t serial;
- char *key_id;
- u_int nprincipals;
- char **principals;
- u_int64_t valid_after, valid_before;
- Buffer critical;
- Buffer extensions;
- Key *signature_key;
-};
-
-struct Key {
- int type;
- int flags;
- RSA *rsa;
- DSA *dsa;
- int ecdsa_nid; /* NID of curve */
-#ifdef OPENSSL_HAS_ECC
- EC_KEY *ecdsa;
-#else
- void *ecdsa;
-#endif
- struct KeyCert *cert;
- u_char *ed25519_sk;
- u_char *ed25519_pk;
-};
-
-#define ED25519_SK_SZ crypto_sign_ed25519_SECRETKEYBYTES
-#define ED25519_PK_SZ crypto_sign_ed25519_PUBLICKEYBYTES
-
-Key *key_new(int);
-void key_add_private(Key *);
-Key *key_new_private(int);
-void key_free(Key *);
-Key *key_demote(const Key *);
-int key_equal_public(const Key *, const Key *);
-int key_equal(const Key *, const Key *);
-char *key_fingerprint(const Key *, enum fp_type, enum fp_rep);
-u_char *key_fingerprint_raw(const Key *, enum fp_type, u_int *);
-const char *key_type(const Key *);
-const char *key_cert_type(const Key *);
-int key_write(const Key *, FILE *);
-int key_read(Key *, char **);
-u_int key_size(const Key *);
+void key_add_private(Key *);
+Key *key_new_private(int);
+void key_free(Key *);
+Key *key_demote(const Key *);
+u_char *key_fingerprint_raw(const Key *, enum fp_type, u_int *);
+int key_write(const Key *, FILE *);
+int key_read(Key *, char **);
Key *key_generate(int, u_int);
Key *key_from_private(const Key *);
-int key_type_from_name(char *);
-int key_is_cert(const Key *);
-int key_type_is_cert(int);
-int key_type_plain(int);
int key_to_certified(Key *, int);
int key_drop_cert(Key *);
int key_certify(Key *, Key *);
-void key_cert_copy(const Key *, struct Key *);
+void key_cert_copy(const Key *, Key *);
int key_cert_check_authority(const Key *, int, int, const char *,
const char **);
-int key_cert_is_legacy(const Key *);
+char *key_alg_list(int, int);
-int key_ecdsa_nid_from_name(const char *);
-int key_curve_name_to_nid(const char *);
-const char *key_curve_nid_to_name(int);
-u_int key_curve_nid_to_bits(int);
-int key_ecdsa_bits_to_nid(int);
-#ifdef OPENSSL_HAS_ECC
-int key_ecdsa_key_to_nid(EC_KEY *);
-int key_ec_nid_to_hash_alg(int nid);
-int key_ec_validate_public(const EC_GROUP *, const EC_POINT *);
-int key_ec_validate_private(const EC_KEY *);
-#endif
-char *key_alg_list(int, int);
+#ifdef WITH_OPENSSL
+int key_ec_validate_public(const EC_GROUP *, const EC_POINT *);
+int key_ec_validate_private(const EC_KEY *);
+#endif /* WITH_OPENSSL */
-Key *key_from_blob(const u_char *, u_int);
-int key_to_blob(const Key *, u_char **, u_int *);
-const char *key_ssh_name(const Key *);
-const char *key_ssh_name_plain(const Key *);
-int key_names_valid2(const char *);
+Key *key_from_blob(const u_char *, u_int);
+int key_to_blob(const Key *, u_char **, u_int *);
int key_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
int key_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
-int ssh_dss_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
-int ssh_dss_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
-int ssh_ecdsa_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
-int ssh_ecdsa_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
-int ssh_rsa_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
-int ssh_rsa_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
-int ssh_ed25519_sign(const Key *, u_char **, u_int *, const u_char *, u_int);
-int ssh_ed25519_verify(const Key *, const u_char *, u_int, const u_char *, u_int);
-
-#if defined(OPENSSL_HAS_ECC) && (defined(DEBUG_KEXECDH) || defined(DEBUG_PK))
-void key_dump_ec_point(const EC_GROUP *, const EC_POINT *);
-void key_dump_ec_key(const EC_KEY *);
-#endif
-
-void key_private_serialize(const Key *, Buffer *);
-Key *key_private_deserialize(Buffer *);
+void key_private_serialize(const Key *, struct sshbuf *);
+Key *key_private_deserialize(struct sshbuf *);
+
+/* authfile.c */
+int key_save_private(Key *, const char *, const char *, const char *,
+ int, const char *, int);
+int key_load_file(int, const char *, struct sshbuf *);
+Key *key_load_cert(const char *);
+Key *key_load_public(const char *, char **);
+Key *key_load_private(const char *, const char *, char **);
+Key *key_load_private_cert(int, const char *, const char *, int *);
+Key *key_load_private_type(int, const char *, const char *, char **, int *);
+Key *key_load_private_pem(int, int, const char *, char **);
+int key_perm_ok(int, const char *);
+int key_in_file(Key *, const char *, int);
#endif
diff --git a/krl.c b/krl.c
index 557a48eb..eb31df90 100644
--- a/krl.c
+++ b/krl.c
@@ -14,7 +14,7 @@
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
-/* $OpenBSD: krl.c,v 1.16 2014/06/24 00:52:02 djm Exp $ */
+/* $OpenBSD: krl.c,v 1.17 2014/06/24 01:13:21 djm Exp $ */
#include "includes.h"
@@ -366,7 +366,7 @@ plain_key_blob(const Key *key, u_char **blob, u_int *blen)
}
r = key_to_blob(kcopy, blob, blen);
free(kcopy);
- return r == 0 ? -1 : 0;
+ return r;
}
/* Revoke a key blob. Ownership of blob is transferred to the tree */
@@ -394,7 +394,7 @@ ssh_krl_revoke_key_explicit(struct ssh_krl *krl, const Key *key)
u_int len;
debug3("%s: revoke type %s", __func__, key_type(key));
- if (plain_key_blob(key, &blob, &len) != 0)
+ if (plain_key_blob(key, &blob, &len) < 0)
return -1;
return revoke_blob(&krl->revoked_keys, blob, len);
}
@@ -1130,7 +1130,7 @@ is_key_revoked(struct ssh_krl *krl, const Key *key)
/* Next, explicit keys */
memset(&rb, 0, sizeof(rb));
- if (plain_key_blob(key, &rb.blob, &rb.len) != 0)
+ if (plain_key_blob(key, &rb.blob, &rb.len) < 0)
return -1;
erb = RB_FIND(revoked_blob_tree, &krl->revoked_keys, &rb);
free(rb.blob);
diff --git a/monitor.c b/monitor.c
index 9391ae8d..68f4dbc7 100644
--- a/monitor.c
+++ b/monitor.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: monitor.c,v 1.133 2014/05/03 17:20:34 markus Exp $ */
+/* $OpenBSD: monitor.c,v 1.134 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright 2002 Niels Provos <provos@citi.umich.edu>
* Copyright 2002 Markus Friedl <markus@openbsd.org>
@@ -40,9 +40,10 @@
#endif
#include <pwd.h>
#include <signal.h>
-#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
+#include <stdarg.h>
+#include <stdio.h>
#include <unistd.h>
#ifdef HAVE_POLL_H
#include <poll.h>
diff --git a/openbsd-compat/openssl-compat.c b/openbsd-compat/openssl-compat.c
index 0e5f2cea..36570e4a 100644
--- a/openbsd-compat/openssl-compat.c
+++ b/openbsd-compat/openssl-compat.c
@@ -1,4 +1,4 @@
-/* $Id: openssl-compat.c,v 1.18 2014/06/17 13:06:08 dtucker Exp $ */
+/* $Id: openssl-compat.c,v 1.19 2014/07/02 05:28:07 djm Exp $ */
/*
* Copyright (c) 2005 Darren Tucker <dtucker@zip.com.au>
@@ -16,6 +16,7 @@
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
+#define SSH_DONT_OVERLOAD_OPENSSL_FUNCS
#include "includes.h"
#include <stdarg.h>
@@ -26,13 +27,8 @@
# include <openssl/conf.h>
#endif
-#ifndef HAVE_RSA_GET_DEFAULT_METHOD
-# include <openssl/rsa.h>
-#endif
-
#include "log.h"
-#define SSH_DONT_OVERLOAD_OPENSSL_FUNCS
#include "openssl-compat.h"
/*
@@ -70,139 +66,6 @@ ssh_compatible_openssl(long headerver, long libver)
return 0;
}
-#ifdef SSH_OLD_EVP
-int
-ssh_EVP_CipherInit(EVP_CIPHER_CTX *evp, const EVP_CIPHER *type,
- unsigned char *key, unsigned char *iv, int enc)
-{
- EVP_CipherInit(evp, type, key, iv, enc);
- return 1;
-}
-
-int
-ssh_EVP_Cipher(EVP_CIPHER_CTX *evp, char *dst, char *src, int len)
-{
- EVP_Cipher(evp, dst, src, len);
- return 1;
-}
-
-int
-ssh_EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *evp)
-{
- EVP_CIPHER_CTX_cleanup(evp);
- return 1;
-}
-#endif
-
-#ifndef HAVE_EVP_DIGESTINIT_EX
-int
-EVP_DigestInit_ex(EVP_MD_CTX *ctx, const EVP_MD *md, void *engine)
-{
- if (engine != NULL)
- fatal("%s: ENGINE is not supported", __func__);
-# ifdef OPENSSL_EVP_DIGESTUPDATE_VOID
- EVP_DigestInit(ctx, md);
- return 1;
-# else
- return EVP_DigestInit(ctx, md);
-# endif
-}
-#endif
-
-#ifndef HAVE_EVP_DIGESTFINAL_EX
-int
-EVP_DigestFinal_ex(EVP_MD_CTX *ctx, unsigned char *md, unsigned int *s)
-{
-# ifdef OPENSSL_EVP_DIGESTUPDATE_VOID
- EVP_DigestFinal(ctx, md, s);
- return 1;
-# else
- return EVP_DigestFinal(ctx, md, s);
-# endif
-}
-#endif
-
-#ifdef OPENSSL_EVP_DIGESTUPDATE_VOID
-int
-ssh_EVP_DigestUpdate(EVP_MD_CTX *ctx, const void *d, unsigned int cnt)
-{
- EVP_DigestUpdate(ctx, d, cnt);
- return 1;
-}
-#endif
-
-#ifndef HAVE_EVP_MD_CTX_COPY_EX
-int
-EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in)
-{
- return EVP_MD_CTX_copy(out, in);
-}
-#endif
-
-#ifndef HAVE_BN_IS_PRIME_EX
-int
-BN_is_prime_ex(const BIGNUM *p, int nchecks, BN_CTX *ctx, void *cb)
-{
- if (cb != NULL)
- fatal("%s: callback args not supported", __func__);
- return BN_is_prime(p, nchecks, NULL, ctx, NULL);
-}
-#endif
-
-#ifndef HAVE_RSA_GENERATE_KEY_EX
-int
-RSA_generate_key_ex(RSA *rsa, int bits, BIGNUM *bn_e, void *cb)
-{
- RSA *new_rsa, tmp_rsa;
- unsigned long e;
-
- if (cb != NULL)
- fatal("%s: callback args not supported", __func__);
- e = BN_get_word(bn_e);
- if (e == 0xffffffffL)
- fatal("%s: value of e too large", __func__);
- new_rsa = RSA_generate_key(bits, e, NULL, NULL);
- if (new_rsa == NULL)
- return 0;
- /* swap rsa/new_rsa then free new_rsa */
- tmp_rsa = *rsa;
- *rsa = *new_rsa;
- *new_rsa = tmp_rsa;
- RSA_free(new_rsa);
- return 1;
-}
-#endif
-
-#ifndef HAVE_DSA_GENERATE_PARAMETERS_EX
-int
-DSA_generate_parameters_ex(DSA *dsa, int bits, const unsigned char *seed,
- int seed_len, int *counter_ret, unsigned long *h_ret, void *cb)
-{
- DSA *new_dsa, tmp_dsa;
-
- if (cb != NULL)
- fatal("%s: callback args not supported", __func__);
- new_dsa = DSA_generate_parameters(bits, (unsigned char *)seed, seed_len,
- counter_ret, h_ret, NULL, NULL);
- if (new_dsa == NULL)
- return 0;
- /* swap dsa/new_dsa then free new_dsa */
- tmp_dsa = *dsa;
- *dsa = *new_dsa;
- *new_dsa = tmp_dsa;
- DSA_free(new_dsa);
- return 1;
-}
-#endif
-
-#ifndef HAVE_RSA_GET_DEFAULT_METHOD
-RSA_METHOD *
-RSA_get_default_method(void)
-{
- return RSA_PKCS1_SSLeay();
-}
-#endif
-
#ifdef USE_OPENSSL_ENGINE
void
ssh_OpenSSL_add_all_algorithms(void)
diff --git a/openbsd-compat/openssl-compat.h b/openbsd-compat/openssl-compat.h
index 199dcc88..d088d296 100644
--- a/openbsd-compat/openssl-compat.h
+++ b/openbsd-compat/openssl-compat.h
@@ -1,4 +1,4 @@
-/* $Id: openssl-compat.h,v 1.27 2014/06/17 13:06:08 dtucker Exp $ */
+/* $Id: openssl-compat.h,v 1.28 2014/07/02 05:28:07 djm Exp $ */
/*
* Copyright (c) 2005 Darren Tucker <dtucker@zip.com.au>
@@ -24,22 +24,8 @@
int ssh_compatible_openssl(long, long);
-/* Only in 0.9.8 */
-#ifndef OPENSSL_DSA_MAX_MODULUS_BITS
-# define OPENSSL_DSA_MAX_MODULUS_BITS 10000
-#endif
-#ifndef OPENSSL_RSA_MAX_MODULUS_BITS
-# define OPENSSL_RSA_MAX_MODULUS_BITS 16384
-#endif
-
-/* OPENSSL_free() is Free() in versions before OpenSSL 0.9.6 */
-#if !defined(OPENSSL_VERSION_NUMBER) || (OPENSSL_VERSION_NUMBER < 0x0090600f)
-# define OPENSSL_free(x) Free(x)
-#endif
-
-#if OPENSSL_VERSION_NUMBER < 0x00906000L
-# define SSH_OLD_EVP
-# define EVP_CIPHER_CTX_get_app_data(e) ((e)->app_data)
+#if (OPENSSL_VERSION_NUMBER <= 0x0090805fL)
+#error OpenSSL 0.9.8f or greater is required
#endif
#if OPENSSL_VERSION_NUMBER < 0x10000001L
@@ -48,31 +34,6 @@ int ssh_compatible_openssl(long, long);
# define LIBCRYPTO_EVP_INL_TYPE size_t
#endif
-#if (OPENSSL_VERSION_NUMBER < 0x00907000L) || defined(OPENSSL_LOBOTOMISED_AES)
-# define USE_BUILTIN_RIJNDAEL
-#endif
-
-#ifdef USE_BUILTIN_RIJNDAEL
-# include "rijndael.h"
-# define AES_KEY rijndael_ctx
-# define AES_BLOCK_SIZE 16
-# define AES_encrypt(a, b, c) rijndael_encrypt(c, a, b)
-# define AES_set_encrypt_key(a, b, c) rijndael_set_key(c, (char *)a, b, 1)
-# define EVP_aes_128_cbc evp_rijndael
-# define EVP_aes_192_cbc evp_rijndael
-# define EVP_aes_256_cbc evp_rijndael
-const EVP_CIPHER *evp_rijndael(void);
-void ssh_rijndael_iv(EVP_CIPHER_CTX *, int, u_char *, u_int);
-#endif
-
-#ifndef OPENSSL_HAVE_EVPCTR
-#define EVP_aes_128_ctr evp_aes_128_ctr
-#define EVP_aes_192_ctr evp_aes_128_ctr
-#define EVP_aes_256_ctr evp_aes_128_ctr
-const EVP_CIPHER *evp_aes_128_ctr(void);
-void ssh_aes_ctr_iv(EVP_CIPHER_CTX *, int, u_char *, size_t);
-#endif
-
/* Avoid some #ifdef. Code that uses these is unreachable without GCM */
#if !defined(OPENSSL_HAVE_EVPGCM) && !defined(EVP_CTRL_GCM_SET_IV_FIXED)
# define EVP_CTRL_GCM_SET_IV_FIXED -1
@@ -90,26 +51,9 @@ void ssh_aes_ctr_iv(EVP_CIPHER_CTX *, int, u_char *, size_t);
# endif
#endif
-#if OPENSSL_VERSION_NUMBER < 0x00907000L
-#define EVP_X_STATE(evp) &(evp).c
-#define EVP_X_STATE_LEN(evp) sizeof((evp).c)
-#else
-#define EVP_X_STATE(evp) (evp).cipher_data
-#define EVP_X_STATE_LEN(evp) (evp).cipher->ctx_size
-#endif
-
-/* OpenSSL 0.9.8e returns cipher key len not context key len */
-#if (OPENSSL_VERSION_NUMBER == 0x0090805fL)
-# define EVP_CIPHER_CTX_key_length(c) ((c)->key_len)
-#endif
-
-#ifndef HAVE_RSA_GET_DEFAULT_METHOD
-RSA_METHOD *RSA_get_default_method(void);
-#endif
-
/*
* We overload some of the OpenSSL crypto functions with ssh_* equivalents
- * which cater for older and/or less featureful OpenSSL version.
+ * to automatically handle OpenSSL engine initialisation.
*
* In order for the compat library to call the real functions, it must
* define SSH_DONT_OVERLOAD_OPENSSL_FUNCS before including this file and
@@ -117,19 +61,6 @@ RSA_METHOD *RSA_get_default_method(void);
*/
#ifndef SSH_DONT_OVERLOAD_OPENSSL_FUNCS
-# ifdef SSH_OLD_EVP
-# ifdef EVP_Cipher
-# undef EVP_Cipher
-# endif
-# define EVP_CipherInit(a,b,c,d,e) ssh_EVP_CipherInit((a),(b),(c),(d),(e))
-# define EVP_Cipher(a,b,c,d) ssh_EVP_Cipher((a),(b),(c),(d))
-# define EVP_CIPHER_CTX_cleanup(a) ssh_EVP_CIPHER_CTX_cleanup((a))
-# endif /* SSH_OLD_EVP */
-
-# ifdef OPENSSL_EVP_DIGESTUPDATE_VOID
-# define EVP_DigestUpdate(a,b,c) ssh_EVP_DigestUpdate((a),(b),(c))
-# endif
-
# ifdef USE_OPENSSL_ENGINE
# ifdef OpenSSL_add_all_algorithms
# undef OpenSSL_add_all_algorithms
@@ -137,48 +68,7 @@ RSA_METHOD *RSA_get_default_method(void);
# define OpenSSL_add_all_algorithms() ssh_OpenSSL_add_all_algorithms()
# endif
-# ifndef HAVE_BN_IS_PRIME_EX
-int BN_is_prime_ex(const BIGNUM *, int, BN_CTX *, void *);
-# endif
-
-# ifndef HAVE_DSA_GENERATE_PARAMETERS_EX
-int DSA_generate_parameters_ex(DSA *, int, const unsigned char *, int, int *,
- unsigned long *, void *);
-# endif
-
-# ifndef HAVE_RSA_GENERATE_KEY_EX
-int RSA_generate_key_ex(RSA *, int, BIGNUM *, void *);
-# endif
-
-# ifndef HAVE_EVP_DIGESTINIT_EX
-int EVP_DigestInit_ex(EVP_MD_CTX *, const EVP_MD *, void *);
-# endif
-
-# ifndef HAVE_EVP_DISESTFINAL_EX
-int EVP_DigestFinal_ex(EVP_MD_CTX *, unsigned char *, unsigned int *);
-# endif
-
-# ifndef EVP_MD_CTX_COPY_EX
-int EVP_MD_CTX_copy_ex(EVP_MD_CTX *, const EVP_MD_CTX *);
-# endif
-
-int ssh_EVP_CipherInit(EVP_CIPHER_CTX *, const EVP_CIPHER *, unsigned char *,
- unsigned char *, int);
-int ssh_EVP_Cipher(EVP_CIPHER_CTX *, char *, char *, int);
-int ssh_EVP_CIPHER_CTX_cleanup(EVP_CIPHER_CTX *);
void ssh_OpenSSL_add_all_algorithms(void);
-# ifndef HAVE_HMAC_CTX_INIT
-# define HMAC_CTX_init(a)
-# endif
-
-# ifndef HAVE_EVP_MD_CTX_INIT
-# define EVP_MD_CTX_init(a)
-# endif
-
-# ifndef HAVE_EVP_MD_CTX_CLEANUP
-# define EVP_MD_CTX_cleanup(a)
-# endif
-
#endif /* SSH_DONT_OVERLOAD_OPENSSL_FUNCS */
diff --git a/packet.c b/packet.c
index 3dd66d7d..b9725798 100644
--- a/packet.c
+++ b/packet.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.c,v 1.196 2014/05/03 17:20:34 markus Exp $ */
+/* $OpenBSD: packet.c,v 1.197 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -78,6 +78,7 @@
#include "canohost.h"
#include "misc.h"
#include "ssh.h"
+#include "ssherr.h"
#include "roaming.h"
#ifdef PACKET_DEBUG
@@ -222,6 +223,7 @@ void
packet_set_connection(int fd_in, int fd_out)
{
const Cipher *none = cipher_by_name("none");
+ int r;
if (none == NULL)
fatal("packet_set_connection: cannot load cipher 'none'");
@@ -229,10 +231,11 @@ packet_set_connection(int fd_in, int fd_out)
active_state = alloc_session_state();
active_state->connection_in = fd_in;
active_state->connection_out = fd_out;
- cipher_init(&active_state->send_context, none, (const u_char *)"",
- 0, NULL, 0, CIPHER_ENCRYPT);
- cipher_init(&active_state->receive_context, none, (const u_char *)"",
- 0, NULL, 0, CIPHER_DECRYPT);
+ if ((r = cipher_init(&active_state->send_context, none,
+ (const u_char *)"", 0, NULL, 0, CIPHER_ENCRYPT)) != 0 ||
+ (r = cipher_init(&active_state->receive_context, none,
+ (const u_char *)"", 0, NULL, 0, CIPHER_DECRYPT)) != 0)
+ fatal("%s: cipher_init: %s", __func__, ssh_err(r));
active_state->newkeys[MODE_IN] = active_state->newkeys[MODE_OUT] = NULL;
if (!active_state->initialized) {
active_state->initialized = 1;
@@ -329,13 +332,15 @@ void
packet_get_keyiv(int mode, u_char *iv, u_int len)
{
CipherContext *cc;
+ int r;
if (mode == MODE_OUT)
cc = &active_state->send_context;
else
cc = &active_state->receive_context;
- cipher_get_keyiv(cc, iv, len);
+ if ((r = cipher_get_keyiv(cc, iv, len)) != 0)
+ fatal("%s: cipher_get_keyiv: %s", __func__, ssh_err(r));
}
int
@@ -381,13 +386,15 @@ void
packet_set_iv(int mode, u_char *dat)
{
CipherContext *cc;
+ int r;
if (mode == MODE_OUT)
cc = &active_state->send_context;
else
cc = &active_state->receive_context;
- cipher_set_keyiv(cc, dat);
+ if ((r = cipher_set_keyiv(cc, dat)) != 0)
+ fatal("%s: cipher_set_keyiv: %s", __func__, ssh_err(r));
}
int
@@ -552,6 +559,7 @@ void
packet_set_encryption_key(const u_char *key, u_int keylen, int number)
{
const Cipher *cipher = cipher_by_number(number);
+ int r;
if (cipher == NULL)
fatal("packet_set_encryption_key: unknown cipher number %d", number);
@@ -561,10 +569,11 @@ packet_set_encryption_key(const u_char *key, u_int keylen, int number)
fatal("packet_set_encryption_key: keylen too big: %d", keylen);
memcpy(active_state->ssh1_key, key, keylen);
active_state->ssh1_keylen = keylen;
- cipher_init(&active_state->send_context, cipher, key, keylen, NULL,
- 0, CIPHER_ENCRYPT);
- cipher_init(&active_state->receive_context, cipher, key, keylen, NULL,
- 0, CIPHER_DECRYPT);
+ if ((r = cipher_init(&active_state->send_context, cipher,
+ key, keylen, NULL, 0, CIPHER_ENCRYPT)) != 0 ||
+ (r = cipher_init(&active_state->receive_context, cipher,
+ key, keylen, NULL, 0, CIPHER_DECRYPT)) != 0)
+ fatal("%s: cipher_init: %s", __func__, ssh_err(r));
}
u_int
@@ -744,7 +753,7 @@ set_newkeys(int mode)
Comp *comp;
CipherContext *cc;
u_int64_t *max_blocks;
- int crypt_type;
+ int r, crypt_type;
debug2("set_newkeys: mode %d", mode);
@@ -786,8 +795,9 @@ set_newkeys(int mode)
if (cipher_authlen(enc->cipher) == 0 && mac_init(mac) == 0)
mac->enabled = 1;
DBG(debug("cipher_init_context: %d", mode));
- cipher_init(cc, enc->cipher, enc->key, enc->key_len,
- enc->iv, enc->iv_len, crypt_type);
+ if ((r = cipher_init(cc, enc->cipher, enc->key, enc->key_len,
+ enc->iv, enc->iv_len, crypt_type)) != 0)
+ fatal("%s: cipher_init: %s", __func__, ssh_err(r));
/* Deleting the keys does not gain extra security */
/* explicit_bzero(enc->iv, enc->block_size);
explicit_bzero(enc->key, enc->key_len);
diff --git a/rsa.c b/rsa.c
index d0b5bbf5..5ecacef9 100644
--- a/rsa.c
+++ b/rsa.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rsa.c,v 1.31 2014/02/02 03:44:31 djm Exp $ */
+/* $OpenBSD: rsa.c,v 1.32 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -67,85 +67,122 @@
#include <stdarg.h>
#include <string.h>
-#include "xmalloc.h"
#include "rsa.h"
#include "log.h"
+#include "ssherr.h"
-void
+int
rsa_public_encrypt(BIGNUM *out, BIGNUM *in, RSA *key)
{
- u_char *inbuf, *outbuf;
- int len, ilen, olen;
+ u_char *inbuf = NULL, *outbuf = NULL;
+ int len, ilen, olen, r = SSH_ERR_INTERNAL_ERROR;
if (BN_num_bits(key->e) < 2 || !BN_is_odd(key->e))
- fatal("rsa_public_encrypt() exponent too small or not odd");
+ return SSH_ERR_INVALID_ARGUMENT;
olen = BN_num_bytes(key->n);
- outbuf = xmalloc(olen);
+ if ((outbuf = malloc(olen)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
ilen = BN_num_bytes(in);
- inbuf = xmalloc(ilen);
+ if ((inbuf = malloc(ilen)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
BN_bn2bin(in, inbuf);
if ((len = RSA_public_encrypt(ilen, inbuf, outbuf, key,
- RSA_PKCS1_PADDING)) <= 0)
- fatal("rsa_public_encrypt() failed");
+ RSA_PKCS1_PADDING)) <= 0) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
- if (BN_bin2bn(outbuf, len, out) == NULL)
- fatal("rsa_public_encrypt: BN_bin2bn failed");
+ if (BN_bin2bn(outbuf, len, out) == NULL) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ r = 0;
- explicit_bzero(outbuf, olen);
- explicit_bzero(inbuf, ilen);
- free(outbuf);
- free(inbuf);
+ out:
+ if (outbuf != NULL) {
+ explicit_bzero(outbuf, olen);
+ free(outbuf);
+ }
+ if (inbuf != NULL) {
+ explicit_bzero(inbuf, ilen);
+ free(inbuf);
+ }
+ return r;
}
int
rsa_private_decrypt(BIGNUM *out, BIGNUM *in, RSA *key)
{
- u_char *inbuf, *outbuf;
- int len, ilen, olen;
+ u_char *inbuf = NULL, *outbuf = NULL;
+ int len, ilen, olen, r = SSH_ERR_INTERNAL_ERROR;
olen = BN_num_bytes(key->n);
- outbuf = xmalloc(olen);
+ if ((outbuf = malloc(olen)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
ilen = BN_num_bytes(in);
- inbuf = xmalloc(ilen);
+ if ((inbuf = malloc(ilen)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
BN_bn2bin(in, inbuf);
if ((len = RSA_private_decrypt(ilen, inbuf, outbuf, key,
RSA_PKCS1_PADDING)) <= 0) {
- error("rsa_private_decrypt() failed");
- } else {
- if (BN_bin2bn(outbuf, len, out) == NULL)
- fatal("rsa_private_decrypt: BN_bin2bn failed");
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ } else if (BN_bin2bn(outbuf, len, out) == NULL) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ r = 0;
+ out:
+ if (outbuf != NULL) {
+ explicit_bzero(outbuf, olen);
+ free(outbuf);
+ }
+ if (inbuf != NULL) {
+ explicit_bzero(inbuf, ilen);
+ free(inbuf);
}
- explicit_bzero(outbuf, olen);
- explicit_bzero(inbuf, ilen);
- free(outbuf);
- free(inbuf);
- return len;
+ return r;
}
/* calculate p-1 and q-1 */
-void
+int
rsa_generate_additional_parameters(RSA *rsa)
{
- BIGNUM *aux;
- BN_CTX *ctx;
+ BIGNUM *aux = NULL;
+ BN_CTX *ctx = NULL;
+ int r;
- if ((aux = BN_new()) == NULL)
- fatal("rsa_generate_additional_parameters: BN_new failed");
if ((ctx = BN_CTX_new()) == NULL)
- fatal("rsa_generate_additional_parameters: BN_CTX_new failed");
+ return SSH_ERR_ALLOC_FAIL;
+ if ((aux = BN_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
if ((BN_sub(aux, rsa->q, BN_value_one()) == 0) ||
(BN_mod(rsa->dmq1, rsa->d, aux, ctx) == 0) ||
(BN_sub(aux, rsa->p, BN_value_one()) == 0) ||
- (BN_mod(rsa->dmp1, rsa->d, aux, ctx) == 0))
- fatal("rsa_generate_additional_parameters: BN_sub/mod failed");
-
+ (BN_mod(rsa->dmp1, rsa->d, aux, ctx) == 0)) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ r = 0;
+ out:
BN_clear_free(aux);
BN_CTX_free(ctx);
+ return r;
}
diff --git a/rsa.h b/rsa.h
index b841ea4e..c476707d 100644
--- a/rsa.h
+++ b/rsa.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: rsa.h,v 1.16 2006/03/25 22:22:43 djm Exp $ */
+/* $OpenBSD: rsa.h,v 1.17 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -19,8 +19,8 @@
#include <openssl/bn.h>
#include <openssl/rsa.h>
-void rsa_public_encrypt(BIGNUM *, BIGNUM *, RSA *);
+int rsa_public_encrypt(BIGNUM *, BIGNUM *, RSA *);
int rsa_private_decrypt(BIGNUM *, BIGNUM *, RSA *);
-void rsa_generate_additional_parameters(RSA *);
+int rsa_generate_additional_parameters(RSA *);
#endif /* RSA_H */
diff --git a/ssh-add.c b/ssh-add.c
index 3421452a..46b91cbd 100644
--- a/ssh-add.c
+++ b/ssh-add.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-add.c,v 1.109 2014/02/02 03:44:31 djm Exp $ */
+/* $OpenBSD: ssh-add.c,v 1.110 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -62,6 +62,7 @@
#include "authfile.h"
#include "pathnames.h"
#include "misc.h"
+#include "ssherr.h"
/* argv0 */
extern char *__progname;
@@ -170,7 +171,7 @@ add_file(AuthenticationConnection *ac, const char *filename, int key_only)
Key *private, *cert;
char *comment = NULL;
char msg[1024], *certpath = NULL;
- int fd, perms_ok, ret = -1;
+ int r, fd, perms_ok, ret = -1;
Buffer keyblob;
if (strcmp(filename, "-") == 0) {
@@ -201,12 +202,18 @@ add_file(AuthenticationConnection *ac, const char *filename, int key_only)
close(fd);
/* At first, try empty passphrase */
- private = key_parse_private(&keyblob, filename, "", &comment);
+ if ((r = sshkey_parse_private_fileblob(&keyblob, filename, "",
+ &private, &comment)) != 0 && r != SSH_ERR_KEY_WRONG_PASSPHRASE)
+ fatal("Cannot parse %s: %s", filename, ssh_err(r));
if (comment == NULL)
comment = xstrdup(filename);
/* try last */
- if (private == NULL && pass != NULL)
- private = key_parse_private(&keyblob, filename, pass, NULL);
+ if (private == NULL && pass != NULL) {
+ if ((r = sshkey_parse_private_fileblob(&keyblob, filename, pass,
+ &private, &comment)) != 0 &&
+ r != SSH_ERR_KEY_WRONG_PASSPHRASE)
+ fatal("Cannot parse %s: %s", filename, ssh_err(r));
+ }
if (private == NULL) {
/* clear passphrase since it did not work */
clear_pass();
@@ -220,8 +227,11 @@ add_file(AuthenticationConnection *ac, const char *filename, int key_only)
buffer_free(&keyblob);
return -1;
}
- private = key_parse_private(&keyblob, filename, pass,
- &comment);
+ if ((r = sshkey_parse_private_fileblob(&keyblob,
+ filename, pass, &private, &comment)) != 0 &&
+ r != SSH_ERR_KEY_WRONG_PASSPHRASE)
+ fatal("Cannot parse %s: %s",
+ filename, ssh_err(r));
if (private != NULL)
break;
clear_pass();
diff --git a/ssh-agent.c b/ssh-agent.c
index bc96ad70..693d763e 100644
--- a/ssh-agent.c
+++ b/ssh-agent.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-agent.c,v 1.185 2014/04/29 18:01:49 markus Exp $ */
+/* $OpenBSD: ssh-agent.c,v 1.186 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -278,7 +278,7 @@ process_authentication_challenge1(SocketEntry *e)
if (id != NULL && (!id->confirm || confirm_key(id) == 0)) {
Key *private = id->key;
/* Decrypt the challenge using the private key. */
- if (rsa_private_decrypt(challenge, challenge, private->rsa) <= 0)
+ if (rsa_private_decrypt(challenge, challenge, private->rsa) != 0)
goto failure;
/* The response is MD5 of decrypted challenge plus session id. */
@@ -365,12 +365,16 @@ process_sign_request2(SocketEntry *e)
static void
process_remove_identity(SocketEntry *e, int version)
{
- u_int blen, bits;
+ u_int blen;
int success = 0;
Key *key = NULL;
u_char *blob;
+#ifdef WITH_SSH1
+ u_int bits;
+#endif /* WITH_SSH1 */
switch (version) {
+#ifdef WITH_SSH1
case 1:
key = key_new(KEY_RSA1);
bits = buffer_get_int(&e->request);
@@ -381,6 +385,7 @@ process_remove_identity(SocketEntry *e, int version)
logit("Warning: identity keysize mismatch: actual %u, announced %u",
key_size(key), bits);
break;
+#endif /* WITH_SSH1 */
case 2:
blob = buffer_get_string(&e->request, &blen);
key = key_from_blob(blob, blen);
@@ -477,6 +482,7 @@ process_add_identity(SocketEntry *e, int version)
Key *k = NULL;
switch (version) {
+#ifdef WITH_SSH1
case 1:
k = key_new_private(KEY_RSA1);
(void) buffer_get_int(&e->request); /* ignored */
@@ -490,7 +496,9 @@ process_add_identity(SocketEntry *e, int version)
buffer_get_bignum(&e->request, k->rsa->p); /* q */
/* Generate additional parameters */
- rsa_generate_additional_parameters(k->rsa);
+ if (rsa_generate_additional_parameters(k->rsa) != 0)
+ fatal("%s: rsa_generate_additional_parameters "
+ "error", __func__);
/* enable blinding */
if (RSA_blinding_on(k->rsa, NULL) != 1) {
@@ -499,6 +507,7 @@ process_add_identity(SocketEntry *e, int version)
goto send;
}
break;
+#endif /* WITH_SSH1 */
case 2:
k = key_private_deserialize(&e->request);
if (k == NULL) {
@@ -507,11 +516,10 @@ process_add_identity(SocketEntry *e, int version)
}
break;
}
- comment = buffer_get_string(&e->request, NULL);
- if (k == NULL) {
- free(comment);
+ if (k == NULL)
goto send;
- }
+ comment = buffer_get_string(&e->request, NULL);
+
while (buffer_len(&e->request)) {
switch ((type = buffer_get_char(&e->request))) {
case SSH_AGENT_CONSTRAIN_LIFETIME:
diff --git a/ssh-dss.c b/ssh-dss.c
index 6b4abcb7..02d1ec2d 100644
--- a/ssh-dss.c
+++ b/ssh-dss.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-dss.c,v 1.31 2014/02/02 03:44:31 djm Exp $ */
+/* $OpenBSD: ssh-dss.c,v 1.32 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
*
@@ -33,157 +33,186 @@
#include <stdarg.h>
#include <string.h>
-#include "xmalloc.h"
-#include "buffer.h"
+#include "sshbuf.h"
#include "compat.h"
-#include "log.h"
-#include "key.h"
+#include "ssherr.h"
#include "digest.h"
+#define SSHKEY_INTERNAL
+#include "sshkey.h"
#define INTBLOB_LEN 20
#define SIGBLOB_LEN (2*INTBLOB_LEN)
int
-ssh_dss_sign(const Key *key, u_char **sigp, u_int *lenp,
- const u_char *data, u_int datalen)
+ssh_dss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
+ const u_char *data, size_t datalen, u_int compat)
{
- DSA_SIG *sig;
+ DSA_SIG *sig = NULL;
u_char digest[SSH_DIGEST_MAX_LENGTH], sigblob[SIGBLOB_LEN];
- u_int rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
- Buffer b;
-
- if (key == NULL || key_type_plain(key->type) != KEY_DSA ||
- key->dsa == NULL) {
- error("%s: no DSA key", __func__);
- return -1;
- }
-
- if (ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen,
- digest, sizeof(digest)) != 0) {
- error("%s: ssh_digest_memory failed", __func__);
- return -1;
- }
-
- sig = DSA_do_sign(digest, dlen, key->dsa);
- explicit_bzero(digest, sizeof(digest));
-
- if (sig == NULL) {
- error("ssh_dss_sign: sign failed");
- return -1;
+ size_t rlen, slen, len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
+ struct sshbuf *b = NULL;
+ int ret = SSH_ERR_INVALID_ARGUMENT;
+
+ if (lenp != NULL)
+ *lenp = 0;
+ if (sigp != NULL)
+ *sigp = NULL;
+
+ if (key == NULL || key->dsa == NULL ||
+ sshkey_type_plain(key->type) != KEY_DSA)
+ return SSH_ERR_INVALID_ARGUMENT;
+ if (dlen == 0)
+ return SSH_ERR_INTERNAL_ERROR;
+
+ if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen,
+ digest, sizeof(digest))) != 0)
+ goto out;
+
+ if ((sig = DSA_do_sign(digest, dlen, key->dsa)) == NULL) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
}
rlen = BN_num_bytes(sig->r);
slen = BN_num_bytes(sig->s);
if (rlen > INTBLOB_LEN || slen > INTBLOB_LEN) {
- error("bad sig size %u %u", rlen, slen);
- DSA_SIG_free(sig);
- return -1;
+ ret = SSH_ERR_INTERNAL_ERROR;
+ goto out;
}
explicit_bzero(sigblob, SIGBLOB_LEN);
- BN_bn2bin(sig->r, sigblob+ SIGBLOB_LEN - INTBLOB_LEN - rlen);
- BN_bn2bin(sig->s, sigblob+ SIGBLOB_LEN - slen);
- DSA_SIG_free(sig);
+ BN_bn2bin(sig->r, sigblob + SIGBLOB_LEN - INTBLOB_LEN - rlen);
+ BN_bn2bin(sig->s, sigblob + SIGBLOB_LEN - slen);
- if (datafellows & SSH_BUG_SIGBLOB) {
- if (lenp != NULL)
- *lenp = SIGBLOB_LEN;
+ if (compat & SSH_BUG_SIGBLOB) {
if (sigp != NULL) {
- *sigp = xmalloc(SIGBLOB_LEN);
+ if ((*sigp = malloc(SIGBLOB_LEN)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
memcpy(*sigp, sigblob, SIGBLOB_LEN);
}
+ if (lenp != NULL)
+ *lenp = SIGBLOB_LEN;
+ ret = 0;
} else {
/* ietf-drafts */
- buffer_init(&b);
- buffer_put_cstring(&b, "ssh-dss");
- buffer_put_string(&b, sigblob, SIGBLOB_LEN);
- len = buffer_len(&b);
- if (lenp != NULL)
- *lenp = len;
+ if ((b = sshbuf_new()) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((ret = sshbuf_put_cstring(b, "ssh-dss")) != 0 ||
+ (ret = sshbuf_put_string(b, sigblob, SIGBLOB_LEN)) != 0)
+ goto out;
+ len = sshbuf_len(b);
if (sigp != NULL) {
- *sigp = xmalloc(len);
- memcpy(*sigp, buffer_ptr(&b), len);
+ if ((*sigp = malloc(len)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ memcpy(*sigp, sshbuf_ptr(b), len);
}
- buffer_free(&b);
+ if (lenp != NULL)
+ *lenp = len;
+ ret = 0;
}
- return 0;
+ out:
+ explicit_bzero(digest, sizeof(digest));
+ if (sig != NULL)
+ DSA_SIG_free(sig);
+ if (b != NULL)
+ sshbuf_free(b);
+ return ret;
}
+
int
-ssh_dss_verify(const Key *key, const u_char *signature, u_int signaturelen,
- const u_char *data, u_int datalen)
+ssh_dss_verify(const struct sshkey *key,
+ const u_char *signature, size_t signaturelen,
+ const u_char *data, size_t datalen, u_int compat)
{
- DSA_SIG *sig;
- u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob;
- u_int len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
- int rlen, ret;
- Buffer b;
-
- if (key == NULL || key_type_plain(key->type) != KEY_DSA ||
- key->dsa == NULL) {
- error("%s: no DSA key", __func__);
- return -1;
- }
+ DSA_SIG *sig = NULL;
+ u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob = NULL;
+ size_t len, dlen = ssh_digest_bytes(SSH_DIGEST_SHA1);
+ int ret = SSH_ERR_INTERNAL_ERROR;
+ struct sshbuf *b = NULL;
+ char *ktype = NULL;
+
+ if (key == NULL || key->dsa == NULL ||
+ sshkey_type_plain(key->type) != KEY_DSA)
+ return SSH_ERR_INVALID_ARGUMENT;
+ if (dlen == 0)
+ return SSH_ERR_INTERNAL_ERROR;
/* fetch signature */
- if (datafellows & SSH_BUG_SIGBLOB) {
- sigblob = xmalloc(signaturelen);
+ if (compat & SSH_BUG_SIGBLOB) {
+ if ((sigblob = malloc(signaturelen)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
memcpy(sigblob, signature, signaturelen);
len = signaturelen;
} else {
/* ietf-drafts */
- char *ktype;
- buffer_init(&b);
- buffer_append(&b, signature, signaturelen);
- ktype = buffer_get_cstring(&b, NULL);
+ if ((b = sshbuf_from(signature, signaturelen)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
+ sshbuf_get_string(b, &sigblob, &len) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
if (strcmp("ssh-dss", ktype) != 0) {
- error("%s: cannot handle type %s", __func__, ktype);
- buffer_free(&b);
- free(ktype);
- return -1;
+ ret = SSH_ERR_KEY_TYPE_MISMATCH;
+ goto out;
}
- free(ktype);
- sigblob = buffer_get_string(&b, &len);
- rlen = buffer_len(&b);
- buffer_free(&b);
- if (rlen != 0) {
- error("%s: remaining bytes in signature %d",
- __func__, rlen);
- free(sigblob);
- return -1;
+ if (sshbuf_len(b) != 0) {
+ ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
+ goto out;
}
}
if (len != SIGBLOB_LEN) {
- fatal("bad sigbloblen %u != SIGBLOB_LEN", len);
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
}
/* parse signature */
- if ((sig = DSA_SIG_new()) == NULL)
- fatal("%s: DSA_SIG_new failed", __func__);
- if ((sig->r = BN_new()) == NULL)
- fatal("%s: BN_new failed", __func__);
- if ((sig->s = BN_new()) == NULL)
- fatal("ssh_dss_verify: BN_new failed");
+ if ((sig = DSA_SIG_new()) == NULL ||
+ (sig->r = BN_new()) == NULL ||
+ (sig->s = BN_new()) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
if ((BN_bin2bn(sigblob, INTBLOB_LEN, sig->r) == NULL) ||
- (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s) == NULL))
- fatal("%s: BN_bin2bn failed", __func__);
-
- /* clean up */
- explicit_bzero(sigblob, len);
- free(sigblob);
+ (BN_bin2bn(sigblob+ INTBLOB_LEN, INTBLOB_LEN, sig->s) == NULL)) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
/* sha1 the data */
- if (ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen,
- digest, sizeof(digest)) != 0) {
- error("%s: digest_memory failed", __func__);
- return -1;
+ if ((ret = ssh_digest_memory(SSH_DIGEST_SHA1, data, datalen,
+ digest, sizeof(digest))) != 0)
+ goto out;
+
+ switch (DSA_do_verify(digest, dlen, sig, key->dsa)) {
+ case 1:
+ ret = 0;
+ break;
+ case 0:
+ ret = SSH_ERR_SIGNATURE_INVALID;
+ goto out;
+ default:
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
}
- ret = DSA_do_verify(digest, dlen, sig, key->dsa);
+ out:
explicit_bzero(digest, sizeof(digest));
-
- DSA_SIG_free(sig);
-
- debug("%s: signature %s", __func__,
- ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error");
+ if (sig != NULL)
+ DSA_SIG_free(sig);
+ if (b != NULL)
+ sshbuf_free(b);
+ if (ktype != NULL)
+ free(ktype);
+ if (sigblob != NULL) {
+ explicit_bzero(sigblob, len);
+ free(sigblob);
+ }
return ret;
}
diff --git a/ssh-ecdsa.c b/ssh-ecdsa.c
index 551c9c46..1119db04 100644
--- a/ssh-ecdsa.c
+++ b/ssh-ecdsa.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-ecdsa.c,v 1.10 2014/02/03 23:28:00 djm Exp $ */
+/* $OpenBSD: ssh-ecdsa.c,v 1.11 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2010 Damien Miller. All rights reserved.
@@ -37,141 +37,155 @@
#include <string.h>
-#include "xmalloc.h"
-#include "buffer.h"
-#include "compat.h"
-#include "log.h"
-#include "key.h"
+#include "sshbuf.h"
+#include "ssherr.h"
#include "digest.h"
+#define SSHKEY_INTERNAL
+#include "sshkey.h"
+/* ARGSUSED */
int
-ssh_ecdsa_sign(const Key *key, u_char **sigp, u_int *lenp,
- const u_char *data, u_int datalen)
+ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
+ const u_char *data, size_t datalen, u_int compat)
{
- ECDSA_SIG *sig;
+ ECDSA_SIG *sig = NULL;
int hash_alg;
u_char digest[SSH_DIGEST_MAX_LENGTH];
- u_int len, dlen;
- Buffer b, bb;
+ size_t len, dlen;
+ struct sshbuf *b = NULL, *bb = NULL;
+ int ret = SSH_ERR_INTERNAL_ERROR;
- if (key == NULL || key_type_plain(key->type) != KEY_ECDSA ||
- key->ecdsa == NULL) {
- error("%s: no ECDSA key", __func__);
- return -1;
+ if (lenp != NULL)
+ *lenp = 0;
+ if (sigp != NULL)
+ *sigp = NULL;
+
+ if (key == NULL || key->ecdsa == NULL ||
+ sshkey_type_plain(key->type) != KEY_ECDSA)
+ return SSH_ERR_INVALID_ARGUMENT;
+
+ if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
+ (dlen = ssh_digest_bytes(hash_alg)) == 0)
+ return SSH_ERR_INTERNAL_ERROR;
+ if ((ret = ssh_digest_memory(hash_alg, data, datalen,
+ digest, sizeof(digest))) != 0)
+ goto out;
+
+ if ((sig = ECDSA_do_sign(digest, dlen, key->ecdsa)) == NULL) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
}
- hash_alg = key_ec_nid_to_hash_alg(key->ecdsa_nid);
- if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
- error("%s: bad hash algorithm %d", __func__, hash_alg);
- return -1;
- }
- if (ssh_digest_memory(hash_alg, data, datalen,
- digest, sizeof(digest)) != 0) {
- error("%s: digest_memory failed", __func__);
- return -1;
+ if ((bb = sshbuf_new()) == NULL || (b = sshbuf_new()) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
}
-
- sig = ECDSA_do_sign(digest, dlen, key->ecdsa);
- explicit_bzero(digest, sizeof(digest));
-
- if (sig == NULL) {
- error("%s: sign failed", __func__);
- return -1;
+ if ((ret = sshbuf_put_bignum2(bb, sig->r)) != 0 ||
+ (ret = sshbuf_put_bignum2(bb, sig->s)) != 0)
+ goto out;
+ if ((ret = sshbuf_put_cstring(b, sshkey_ssh_name_plain(key))) != 0 ||
+ (ret = sshbuf_put_stringb(b, bb)) != 0)
+ goto out;
+ len = sshbuf_len(b);
+ if (sigp != NULL) {
+ if ((*sigp = malloc(len)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ memcpy(*sigp, sshbuf_ptr(b), len);
}
-
- buffer_init(&bb);
- buffer_put_bignum2(&bb, sig->r);
- buffer_put_bignum2(&bb, sig->s);
- ECDSA_SIG_free(sig);
-
- buffer_init(&b);
- buffer_put_cstring(&b, key_ssh_name_plain(key));
- buffer_put_string(&b, buffer_ptr(&bb), buffer_len(&bb));
- buffer_free(&bb);
- len = buffer_len(&b);
if (lenp != NULL)
*lenp = len;
- if (sigp != NULL) {
- *sigp = xmalloc(len);
- memcpy(*sigp, buffer_ptr(&b), len);
- }
- buffer_free(&b);
-
- return 0;
+ ret = 0;
+ out:
+ explicit_bzero(digest, sizeof(digest));
+ if (b != NULL)
+ sshbuf_free(b);
+ if (bb != NULL)
+ sshbuf_free(bb);
+ if (sig != NULL)
+ ECDSA_SIG_free(sig);
+ return ret;
}
+
+/* ARGSUSED */
int
-ssh_ecdsa_verify(const Key *key, const u_char *signature, u_int signaturelen,
- const u_char *data, u_int datalen)
+ssh_ecdsa_verify(const struct sshkey *key,
+ const u_char *signature, size_t signaturelen,
+ const u_char *data, size_t datalen, u_int compat)
{
- ECDSA_SIG *sig;
+ ECDSA_SIG *sig = NULL;
int hash_alg;
- u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob;
- u_int len, dlen;
- int rlen, ret;
- Buffer b, bb;
- char *ktype;
-
- if (key == NULL || key_type_plain(key->type) != KEY_ECDSA ||
- key->ecdsa == NULL) {
- error("%s: no ECDSA key", __func__);
- return -1;
- }
+ u_char digest[SSH_DIGEST_MAX_LENGTH];
+ size_t dlen;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+ struct sshbuf *b = NULL, *sigbuf = NULL;
+ char *ktype = NULL;
+
+ if (key == NULL || key->ecdsa == NULL ||
+ sshkey_type_plain(key->type) != KEY_ECDSA)
+ return SSH_ERR_INVALID_ARGUMENT;
+
+ if ((hash_alg = sshkey_ec_nid_to_hash_alg(key->ecdsa_nid)) == -1 ||
+ (dlen = ssh_digest_bytes(hash_alg)) == 0)
+ return SSH_ERR_INTERNAL_ERROR;
/* fetch signature */
- buffer_init(&b);
- buffer_append(&b, signature, signaturelen);
- ktype = buffer_get_string(&b, NULL);
- if (strcmp(key_ssh_name_plain(key), ktype) != 0) {
- error("%s: cannot handle type %s", __func__, ktype);
- buffer_free(&b);
- free(ktype);
- return -1;
+ if ((b = sshbuf_from(signature, signaturelen)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if (sshbuf_get_cstring(b, &ktype, NULL) != 0 ||
+ sshbuf_froms(b, &sigbuf) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
}
- free(ktype);
- sigblob = buffer_get_string(&b, &len);
- rlen = buffer_len(&b);
- buffer_free(&b);
- if (rlen != 0) {
- error("%s: remaining bytes in signature %d", __func__, rlen);
- free(sigblob);
- return -1;
+ if (strcmp(sshkey_ssh_name_plain(key), ktype) != 0) {
+ ret = SSH_ERR_KEY_TYPE_MISMATCH;
+ goto out;
+ }
+ if (sshbuf_len(b) != 0) {
+ ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
+ goto out;
}
/* parse signature */
- if ((sig = ECDSA_SIG_new()) == NULL)
- fatal("%s: ECDSA_SIG_new failed", __func__);
-
- buffer_init(&bb);
- buffer_append(&bb, sigblob, len);
- buffer_get_bignum2(&bb, sig->r);
- buffer_get_bignum2(&bb, sig->s);
- if (buffer_len(&bb) != 0)
- fatal("%s: remaining bytes in inner sigblob", __func__);
- buffer_free(&bb);
-
- /* clean up */
- explicit_bzero(sigblob, len);
- free(sigblob);
-
- /* hash the data */
- hash_alg = key_ec_nid_to_hash_alg(key->ecdsa_nid);
- if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
- error("%s: bad hash algorithm %d", __func__, hash_alg);
- return -1;
+ if ((sig = ECDSA_SIG_new()) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (sshbuf_get_bignum2(sigbuf, sig->r) != 0 ||
+ sshbuf_get_bignum2(sigbuf, sig->s) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if (sshbuf_len(sigbuf) != 0) {
+ ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
+ goto out;
}
- if (ssh_digest_memory(hash_alg, data, datalen,
- digest, sizeof(digest)) != 0) {
- error("%s: digest_memory failed", __func__);
- return -1;
+ if ((ret = ssh_digest_memory(hash_alg, data, datalen,
+ digest, sizeof(digest))) != 0)
+ goto out;
+
+ switch (ECDSA_do_verify(digest, dlen, sig, key->ecdsa)) {
+ case 1:
+ ret = 0;
+ break;
+ case 0:
+ ret = SSH_ERR_SIGNATURE_INVALID;
+ goto out;
+ default:
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
}
- ret = ECDSA_do_verify(digest, dlen, sig, key->ecdsa);
+ out:
explicit_bzero(digest, sizeof(digest));
-
- ECDSA_SIG_free(sig);
-
- debug("%s: signature %s", __func__,
- ret == 1 ? "correct" : ret == 0 ? "incorrect" : "error");
+ if (sigbuf != NULL)
+ sshbuf_free(sigbuf);
+ if (b != NULL)
+ sshbuf_free(b);
+ if (sig != NULL)
+ ECDSA_SIG_free(sig);
+ free(ktype);
return ret;
}
diff --git a/ssh-ed25519.c b/ssh-ed25519.c
index 160d1f23..cb87d479 100644
--- a/ssh-ed25519.c
+++ b/ssh-ed25519.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-ed25519.c,v 1.3 2014/02/23 20:03:42 djm Exp $ */
+/* $OpenBSD: ssh-ed25519.c,v 1.4 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2013 Markus Friedl <markus@openbsd.org>
*
@@ -18,132 +18,149 @@
#include "includes.h"
#include <sys/types.h>
+#include <limits.h>
#include "crypto_api.h"
-#include <limits.h>
#include <string.h>
#include <stdarg.h>
#include "xmalloc.h"
#include "log.h"
#include "buffer.h"
-#include "key.h"
+#define SSHKEY_INTERNAL
+#include "sshkey.h"
+#include "ssherr.h"
#include "ssh.h"
int
-ssh_ed25519_sign(const Key *key, u_char **sigp, u_int *lenp,
- const u_char *data, u_int datalen)
+ssh_ed25519_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
+ const u_char *data, size_t datalen, u_int compat)
{
- u_char *sig;
- u_int slen, len;
+ u_char *sig = NULL;
+ size_t slen = 0, len;
unsigned long long smlen;
- int ret;
- Buffer b;
+ int r, ret;
+ struct sshbuf *b = NULL;
- if (key == NULL || key_type_plain(key->type) != KEY_ED25519 ||
- key->ed25519_sk == NULL) {
- error("%s: no ED25519 key", __func__);
- return -1;
- }
+ if (lenp != NULL)
+ *lenp = 0;
+ if (sigp != NULL)
+ *sigp = NULL;
- if (datalen >= UINT_MAX - crypto_sign_ed25519_BYTES) {
- error("%s: datalen %u too long", __func__, datalen);
- return -1;
- }
+ if (key == NULL ||
+ sshkey_type_plain(key->type) != KEY_ED25519 ||
+ key->ed25519_sk == NULL ||
+ datalen >= INT_MAX - crypto_sign_ed25519_BYTES)
+ return SSH_ERR_INVALID_ARGUMENT;
smlen = slen = datalen + crypto_sign_ed25519_BYTES;
- sig = xmalloc(slen);
+ if ((sig = malloc(slen)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
if ((ret = crypto_sign_ed25519(sig, &smlen, data, datalen,
key->ed25519_sk)) != 0 || smlen <= datalen) {
- error("%s: crypto_sign_ed25519 failed: %d", __func__, ret);
- free(sig);
- return -1;
+ r = SSH_ERR_INVALID_ARGUMENT; /* XXX better error? */
+ goto out;
}
/* encode signature */
- buffer_init(&b);
- buffer_put_cstring(&b, "ssh-ed25519");
- buffer_put_string(&b, sig, smlen - datalen);
- len = buffer_len(&b);
+ if ((b = sshbuf_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((r = sshbuf_put_cstring(b, "ssh-ed25519")) != 0 ||
+ (r = sshbuf_put_string(b, sig, smlen - datalen)) != 0)
+ goto out;
+ len = sshbuf_len(b);
+ if (sigp != NULL) {
+ if ((*sigp = malloc(len)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ memcpy(*sigp, sshbuf_ptr(b), len);
+ }
if (lenp != NULL)
*lenp = len;
- if (sigp != NULL) {
- *sigp = xmalloc(len);
- memcpy(*sigp, buffer_ptr(&b), len);
+ /* success */
+ r = 0;
+ out:
+ sshbuf_free(b);
+ if (sig != NULL) {
+ explicit_bzero(sig, slen);
+ free(sig);
}
- buffer_free(&b);
- explicit_bzero(sig, slen);
- free(sig);
- return 0;
+ return r;
}
int
-ssh_ed25519_verify(const Key *key, const u_char *signature, u_int signaturelen,
- const u_char *data, u_int datalen)
+ssh_ed25519_verify(const struct sshkey *key,
+ const u_char *signature, size_t signaturelen,
+ const u_char *data, size_t datalen, u_int compat)
{
- Buffer b;
- char *ktype;
- u_char *sigblob, *sm, *m;
- u_int len;
- unsigned long long smlen, mlen;
- int rlen, ret;
+ struct sshbuf *b = NULL;
+ char *ktype = NULL;
+ const u_char *sigblob;
+ u_char *sm = NULL, *m = NULL;
+ size_t len;
+ unsigned long long smlen = 0, mlen = 0;
+ int r, ret;
- if (key == NULL || key_type_plain(key->type) != KEY_ED25519 ||
- key->ed25519_pk == NULL) {
- error("%s: no ED25519 key", __func__);
- return -1;
- }
- buffer_init(&b);
- buffer_append(&b, signature, signaturelen);
- ktype = buffer_get_cstring(&b, NULL);
+ if (key == NULL ||
+ sshkey_type_plain(key->type) != KEY_ED25519 ||
+ key->ed25519_pk == NULL ||
+ datalen >= INT_MAX - crypto_sign_ed25519_BYTES)
+ return SSH_ERR_INVALID_ARGUMENT;
+
+ if ((b = sshbuf_from(signature, signaturelen)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if ((r = sshbuf_get_cstring(b, &ktype, NULL)) != 0 ||
+ (r = sshbuf_get_string_direct(b, &sigblob, &len)) != 0)
+ goto out;
if (strcmp("ssh-ed25519", ktype) != 0) {
- error("%s: cannot handle type %s", __func__, ktype);
- buffer_free(&b);
- free(ktype);
- return -1;
+ r = SSH_ERR_KEY_TYPE_MISMATCH;
+ goto out;
}
- free(ktype);
- sigblob = buffer_get_string(&b, &len);
- rlen = buffer_len(&b);
- buffer_free(&b);
- if (rlen != 0) {
- error("%s: remaining bytes in signature %d", __func__, rlen);
- free(sigblob);
- return -1;
+ if (sshbuf_len(b) != 0) {
+ r = SSH_ERR_UNEXPECTED_TRAILING_DATA;
+ goto out;
}
if (len > crypto_sign_ed25519_BYTES) {
- error("%s: len %u > crypto_sign_ed25519_BYTES %u", __func__,
- len, crypto_sign_ed25519_BYTES);
- free(sigblob);
- return -1;
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
}
+ if (datalen >= SIZE_MAX - len)
+ return SSH_ERR_INVALID_ARGUMENT;
smlen = len + datalen;
- sm = xmalloc(smlen);
+ mlen = smlen;
+ if ((sm = malloc(smlen)) == NULL || (m = xmalloc(mlen)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
memcpy(sm, sigblob, len);
memcpy(sm+len, data, datalen);
- mlen = smlen;
- m = xmalloc(mlen);
if ((ret = crypto_sign_ed25519_open(m, &mlen, sm, smlen,
key->ed25519_pk)) != 0) {
debug2("%s: crypto_sign_ed25519_open failed: %d",
__func__, ret);
}
- if (ret == 0 && mlen != datalen) {
- debug2("%s: crypto_sign_ed25519_open "
- "mlen != datalen (%llu != %u)", __func__, mlen, datalen);
- ret = -1;
+ if (ret != 0 || mlen != datalen) {
+ r = SSH_ERR_SIGNATURE_INVALID;
+ goto out;
}
/* XXX compare 'm' and 'data' ? */
-
- explicit_bzero(sigblob, len);
- explicit_bzero(sm, smlen);
- explicit_bzero(m, smlen); /* NB. mlen may be invalid if ret != 0 */
- free(sigblob);
- free(sm);
- free(m);
- debug("%s: signature %scorrect", __func__, (ret != 0) ? "in" : "");
-
- /* translate return code carefully */
- return (ret == 0) ? 1 : -1;
+ /* success */
+ r = 0;
+ out:
+ if (sm != NULL) {
+ explicit_bzero(sm, smlen);
+ free(sm);
+ }
+ if (m != NULL) {
+ explicit_bzero(m, smlen); /* NB mlen may be invalid if r != 0 */
+ free(m);
+ }
+ sshbuf_free(b);
+ free(ktype);
+ return r;
}
+
diff --git a/ssh-keygen.c b/ssh-keygen.c
index 085f1ec5..e2aa215b 100644
--- a/ssh-keygen.c
+++ b/ssh-keygen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keygen.c,v 1.246 2014/04/29 18:01:49 markus Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.247 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -482,7 +482,9 @@ do_convert_private_ssh2_from_blob(u_char *blob, u_int blen)
buffer_get_bignum_bits(&b, key->rsa->iqmp);
buffer_get_bignum_bits(&b, key->rsa->q);
buffer_get_bignum_bits(&b, key->rsa->p);
- rsa_generate_additional_parameters(key->rsa);
+ if (rsa_generate_additional_parameters(key->rsa) != 0)
+ fatal("%s: rsa_generate_additional_parameters "
+ "error", __func__);
break;
}
rlen = buffer_len(&b);
@@ -1637,12 +1639,12 @@ do_ca_sign(struct passwd *pw, int argc, char **argv)
public->cert->valid_after = cert_valid_from;
public->cert->valid_before = cert_valid_to;
if (v00) {
- prepare_options_buf(&public->cert->critical,
+ prepare_options_buf(public->cert->critical,
OPTIONS_CRITICAL|OPTIONS_EXTENSIONS);
} else {
- prepare_options_buf(&public->cert->critical,
+ prepare_options_buf(public->cert->critical,
OPTIONS_CRITICAL);
- prepare_options_buf(&public->cert->extensions,
+ prepare_options_buf(public->cert->extensions,
OPTIONS_EXTENSIONS);
}
public->cert->signature_key = key_from_private(ca);
@@ -1913,19 +1915,19 @@ do_show_cert(struct passwd *pw)
printf("\n");
}
printf(" Critical Options: ");
- if (buffer_len(&key->cert->critical) == 0)
+ if (buffer_len(key->cert->critical) == 0)
printf("(none)\n");
else {
printf("\n");
- show_options(&key->cert->critical, v00, 1);
+ show_options(key->cert->critical, v00, 1);
}
if (!v00) {
printf(" Extensions: ");
- if (buffer_len(&key->cert->extensions) == 0)
+ if (buffer_len(key->cert->extensions) == 0)
printf("(none)\n");
else {
printf("\n");
- show_options(&key->cert->extensions, v00, 0);
+ show_options(key->cert->extensions, v00, 0);
}
}
exit(0);
diff --git a/ssh-pkcs11-client.c b/ssh-pkcs11-client.c
index 6c9f9d2c..8c74864a 100644
--- a/ssh-pkcs11-client.c
+++ b/ssh-pkcs11-client.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-pkcs11-client.c,v 1.4 2013/05/17 00:13:14 djm Exp $ */
+/* $OpenBSD: ssh-pkcs11-client.c,v 1.5 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2010 Markus Friedl. All rights reserved.
*
@@ -30,6 +30,8 @@
#include <unistd.h>
#include <errno.h>
+#include <openssl/rsa.h>
+
#include "pathnames.h"
#include "xmalloc.h"
#include "buffer.h"
diff --git a/ssh-pkcs11-helper.c b/ssh-pkcs11-helper.c
index b7c52beb..0b1d8e4c 100644
--- a/ssh-pkcs11-helper.c
+++ b/ssh-pkcs11-helper.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-pkcs11-helper.c,v 1.7 2013/12/02 02:56:17 djm Exp $ */
+/* $OpenBSD: ssh-pkcs11-helper.c,v 1.8 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2010 Markus Friedl. All rights reserved.
*
@@ -169,7 +169,7 @@ process_sign(void)
{
u_char *blob, *data, *signature = NULL;
u_int blen, dlen, slen = 0;
- int ok = -1, ret;
+ int ok = -1;
Key *key, *found;
Buffer msg;
@@ -179,6 +179,9 @@ process_sign(void)
if ((key = key_from_blob(blob, blen)) != NULL) {
if ((found = lookup_key(key)) != NULL) {
+#ifdef WITH_OPENSSL
+ int ret;
+
slen = RSA_size(key->rsa);
signature = xmalloc(slen);
if ((ret = RSA_private_encrypt(dlen, data, signature,
@@ -186,6 +189,7 @@ process_sign(void)
slen = ret;
ok = 0;
}
+#endif /* WITH_OPENSSL */
}
key_free(key);
}
diff --git a/ssh-pkcs11.c b/ssh-pkcs11.c
index d3e87729..c96be3bd 100644
--- a/ssh-pkcs11.c
+++ b/ssh-pkcs11.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-pkcs11.c,v 1.13 2014/05/02 03:27:54 djm Exp $ */
+/* $OpenBSD: ssh-pkcs11.c,v 1.14 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2010 Markus Friedl. All rights reserved.
*
@@ -520,7 +520,7 @@ pkcs11_fetch_keys_filter(struct pkcs11_provider *p, CK_ULONG slotidx,
key = key_new(KEY_UNSPEC);
key->rsa = rsa;
key->type = KEY_RSA;
- key->flags |= KEY_FLAG_EXT;
+ key->flags |= SSHKEY_FLAG_EXT;
if (pkcs11_key_included(keysp, nkeys, key)) {
key_free(key);
} else {
diff --git a/ssh-rsa.c b/ssh-rsa.c
index c6f25b3e..fec1953b 100644
--- a/ssh-rsa.c
+++ b/ssh-rsa.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-rsa.c,v 1.51 2014/02/02 03:44:31 djm Exp $ */
+/* $OpenBSD: ssh-rsa.c,v 1.52 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2000, 2003 Markus Friedl <markus@openbsd.org>
*
@@ -25,163 +25,167 @@
#include <stdarg.h>
#include <string.h>
-#include "xmalloc.h"
-#include "log.h"
-#include "buffer.h"
-#include "key.h"
+#include "sshbuf.h"
#include "compat.h"
-#include "misc.h"
-#include "ssh.h"
+#include "ssherr.h"
+#define SSHKEY_INTERNAL
+#include "sshkey.h"
#include "digest.h"
-static int openssh_RSA_verify(int, u_char *, u_int, u_char *, u_int, RSA *);
+static int openssh_RSA_verify(int, u_char *, size_t, u_char *, size_t, RSA *);
/* RSASSA-PKCS1-v1_5 (PKCS #1 v2.0 signature) with SHA1 */
int
-ssh_rsa_sign(const Key *key, u_char **sigp, u_int *lenp,
- const u_char *data, u_int datalen)
+ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
+ const u_char *data, size_t datalen, u_int compat)
{
int hash_alg;
- u_char digest[SSH_DIGEST_MAX_LENGTH], *sig;
- u_int slen, dlen, len;
- int ok, nid;
- Buffer b;
+ u_char digest[SSH_DIGEST_MAX_LENGTH], *sig = NULL;
+ size_t slen;
+ u_int dlen, len;
+ int nid, ret = SSH_ERR_INTERNAL_ERROR;
+ struct sshbuf *b = NULL;
- if (key == NULL || key_type_plain(key->type) != KEY_RSA ||
- key->rsa == NULL) {
- error("%s: no RSA key", __func__);
- return -1;
- }
+ if (lenp != NULL)
+ *lenp = 0;
+ if (sigp != NULL)
+ *sigp = NULL;
+
+ if (key == NULL || key->rsa == NULL ||
+ sshkey_type_plain(key->type) != KEY_RSA)
+ return SSH_ERR_INVALID_ARGUMENT;
+ slen = RSA_size(key->rsa);
+ if (slen <= 0 || slen > SSHBUF_MAX_BIGNUM)
+ return SSH_ERR_INVALID_ARGUMENT;
/* hash the data */
hash_alg = SSH_DIGEST_SHA1;
nid = NID_sha1;
- if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
- error("%s: bad hash algorithm %d", __func__, hash_alg);
- return -1;
- }
- if (ssh_digest_memory(hash_alg, data, datalen,
- digest, sizeof(digest)) != 0) {
- error("%s: ssh_digest_memory failed", __func__);
- return -1;
- }
-
- slen = RSA_size(key->rsa);
- sig = xmalloc(slen);
-
- ok = RSA_sign(nid, digest, dlen, sig, &len, key->rsa);
- explicit_bzero(digest, sizeof(digest));
+ if ((dlen = ssh_digest_bytes(hash_alg)) == 0)
+ return SSH_ERR_INTERNAL_ERROR;
+ if ((ret = ssh_digest_memory(hash_alg, data, datalen,
+ digest, sizeof(digest))) != 0)
+ goto out;
- if (ok != 1) {
- int ecode = ERR_get_error();
+ if ((sig = malloc(slen)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
- error("%s: RSA_sign failed: %s", __func__,
- ERR_error_string(ecode, NULL));
- free(sig);
- return -1;
+ if (RSA_sign(nid, digest, dlen, sig, &len, key->rsa) != 1) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
}
if (len < slen) {
- u_int diff = slen - len;
- debug("slen %u > len %u", slen, len);
+ size_t diff = slen - len;
memmove(sig + diff, sig, len);
explicit_bzero(sig, diff);
} else if (len > slen) {
- error("%s: slen %u slen2 %u", __func__, slen, len);
- free(sig);
- return -1;
+ ret = SSH_ERR_INTERNAL_ERROR;
+ goto out;
}
/* encode signature */
- buffer_init(&b);
- buffer_put_cstring(&b, "ssh-rsa");
- buffer_put_string(&b, sig, slen);
- len = buffer_len(&b);
+ if ((b = sshbuf_new()) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((ret = sshbuf_put_cstring(b, "ssh-rsa")) != 0 ||
+ (ret = sshbuf_put_string(b, sig, slen)) != 0)
+ goto out;
+ len = sshbuf_len(b);
+ if (sigp != NULL) {
+ if ((*sigp = malloc(len)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ memcpy(*sigp, sshbuf_ptr(b), len);
+ }
if (lenp != NULL)
*lenp = len;
- if (sigp != NULL) {
- *sigp = xmalloc(len);
- memcpy(*sigp, buffer_ptr(&b), len);
+ ret = 0;
+ out:
+ explicit_bzero(digest, sizeof(digest));
+ if (sig != NULL) {
+ explicit_bzero(sig, slen);
+ free(sig);
}
- buffer_free(&b);
- explicit_bzero(sig, slen);
- free(sig);
-
+ if (b != NULL)
+ sshbuf_free(b);
return 0;
}
int
-ssh_rsa_verify(const Key *key, const u_char *signature, u_int signaturelen,
- const u_char *data, u_int datalen)
+ssh_rsa_verify(const struct sshkey *key,
+ const u_char *signature, size_t signaturelen,
+ const u_char *data, size_t datalen, u_int compat)
{
- Buffer b;
- int hash_alg;
- char *ktype;
- u_char digest[SSH_DIGEST_MAX_LENGTH], *sigblob;
- u_int len, dlen, modlen;
- int rlen, ret;
+ char *ktype = NULL;
+ int hash_alg, ret = SSH_ERR_INTERNAL_ERROR;
+ size_t len, diff, modlen, dlen;
+ struct sshbuf *b = NULL;
+ u_char digest[SSH_DIGEST_MAX_LENGTH], *osigblob, *sigblob = NULL;
- if (key == NULL || key_type_plain(key->type) != KEY_RSA ||
- key->rsa == NULL) {
- error("%s: no RSA key", __func__);
- return -1;
- }
+ if (key == NULL || key->rsa == NULL ||
+ sshkey_type_plain(key->type) != KEY_RSA ||
+ BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE)
+ return SSH_ERR_INVALID_ARGUMENT;
- if (BN_num_bits(key->rsa->n) < SSH_RSA_MINIMUM_MODULUS_SIZE) {
- error("%s: RSA modulus too small: %d < minimum %d bits",
- __func__, BN_num_bits(key->rsa->n),
- SSH_RSA_MINIMUM_MODULUS_SIZE);
- return -1;
+ if ((b = sshbuf_from(signature, signaturelen)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if (sshbuf_get_cstring(b, &ktype, NULL) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
}
- buffer_init(&b);
- buffer_append(&b, signature, signaturelen);
- ktype = buffer_get_cstring(&b, NULL);
if (strcmp("ssh-rsa", ktype) != 0) {
- error("%s: cannot handle type %s", __func__, ktype);
- buffer_free(&b);
- free(ktype);
- return -1;
+ ret = SSH_ERR_KEY_TYPE_MISMATCH;
+ goto out;
}
- free(ktype);
- sigblob = buffer_get_string(&b, &len);
- rlen = buffer_len(&b);
- buffer_free(&b);
- if (rlen != 0) {
- error("%s: remaining bytes in signature %d", __func__, rlen);
- free(sigblob);
- return -1;
+ if (sshbuf_get_string(b, &sigblob, &len) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if (sshbuf_len(b) != 0) {
+ ret = SSH_ERR_UNEXPECTED_TRAILING_DATA;
+ goto out;
}
/* RSA_verify expects a signature of RSA_size */
modlen = RSA_size(key->rsa);
if (len > modlen) {
- error("%s: len %u > modlen %u", __func__, len, modlen);
- free(sigblob);
- return -1;
+ ret = SSH_ERR_KEY_BITS_MISMATCH;
+ goto out;
} else if (len < modlen) {
- u_int diff = modlen - len;
- debug("%s: add padding: modlen %u > len %u", __func__,
- modlen, len);
- sigblob = xrealloc(sigblob, 1, modlen);
+ diff = modlen - len;
+ osigblob = sigblob;
+ if ((sigblob = realloc(sigblob, modlen)) == NULL) {
+ sigblob = osigblob; /* put it back for clear/free */
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
memmove(sigblob + diff, sigblob, len);
explicit_bzero(sigblob, diff);
len = modlen;
}
- /* hash the data */
hash_alg = SSH_DIGEST_SHA1;
if ((dlen = ssh_digest_bytes(hash_alg)) == 0) {
- error("%s: bad hash algorithm %d", __func__, hash_alg);
- return -1;
- }
- if (ssh_digest_memory(hash_alg, data, datalen,
- digest, sizeof(digest)) != 0) {
- error("%s: ssh_digest_memory failed", __func__);
- return -1;
+ ret = SSH_ERR_INTERNAL_ERROR;
+ goto out;
}
+ if ((ret = ssh_digest_memory(hash_alg, data, datalen,
+ digest, sizeof(digest))) != 0)
+ goto out;
ret = openssh_RSA_verify(hash_alg, digest, dlen, sigblob, len,
key->rsa);
+ out:
+ if (sigblob != NULL) {
+ explicit_bzero(sigblob, len);
+ free(sigblob);
+ }
+ if (ktype != NULL)
+ free(ktype);
+ if (b != NULL)
+ sshbuf_free(b);
explicit_bzero(digest, sizeof(digest));
- explicit_bzero(sigblob, len);
- free(sigblob);
- debug("%s: signature %scorrect", __func__, (ret == 0) ? "in" : "");
return ret;
}
@@ -204,15 +208,15 @@ static const u_char id_sha1[] = {
};
static int
-openssh_RSA_verify(int hash_alg, u_char *hash, u_int hashlen,
- u_char *sigbuf, u_int siglen, RSA *rsa)
+openssh_RSA_verify(int hash_alg, u_char *hash, size_t hashlen,
+ u_char *sigbuf, size_t siglen, RSA *rsa)
{
- u_int ret, rsasize, oidlen = 0, hlen = 0;
+ size_t ret, rsasize = 0, oidlen = 0, hlen = 0;
int len, oidmatch, hashmatch;
const u_char *oid = NULL;
u_char *decrypted = NULL;
- ret = 0;
+ ret = SSH_ERR_INTERNAL_ERROR;
switch (hash_alg) {
case SSH_DIGEST_SHA1:
oid = id_sha1;
@@ -223,37 +227,39 @@ openssh_RSA_verify(int hash_alg, u_char *hash, u_int hashlen,
goto done;
}
if (hashlen != hlen) {
- error("bad hashlen");
+ ret = SSH_ERR_INVALID_ARGUMENT;
goto done;
}
rsasize = RSA_size(rsa);
- if (siglen == 0 || siglen > rsasize) {
- error("bad siglen");
+ if (rsasize <= 0 || rsasize > SSHBUF_MAX_BIGNUM ||
+ siglen == 0 || siglen > rsasize) {
+ ret = SSH_ERR_INVALID_ARGUMENT;
+ goto done;
+ }
+ if ((decrypted = malloc(rsasize)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
goto done;
}
- decrypted = xmalloc(rsasize);
if ((len = RSA_public_decrypt(siglen, sigbuf, decrypted, rsa,
RSA_PKCS1_PADDING)) < 0) {
- error("RSA_public_decrypt failed: %s",
- ERR_error_string(ERR_get_error(), NULL));
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
goto done;
}
- if (len < 0 || (u_int)len != hlen + oidlen) {
- error("bad decrypted len: %d != %d + %d", len, hlen, oidlen);
+ if (len < 0 || (size_t)len != hlen + oidlen) {
+ ret = SSH_ERR_INVALID_FORMAT;
goto done;
}
oidmatch = timingsafe_bcmp(decrypted, oid, oidlen) == 0;
hashmatch = timingsafe_bcmp(decrypted + oidlen, hash, hlen) == 0;
- if (!oidmatch) {
- error("oid mismatch");
- goto done;
- }
- if (!hashmatch) {
- error("hash mismatch");
+ if (!oidmatch || !hashmatch) {
+ ret = SSH_ERR_SIGNATURE_INVALID;
goto done;
}
- ret = 1;
+ ret = 0;
done:
- free(decrypted);
+ if (decrypted) {
+ explicit_bzero(decrypted, rsasize);
+ free(decrypted);
+ }
return ret;
}
diff --git a/sshbuf-misc.c b/sshbuf-misc.c
index 22dbfd5a..bfeffe67 100644
--- a/sshbuf-misc.c
+++ b/sshbuf-misc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshbuf-misc.c,v 1.1 2014/04/30 05:29:56 djm Exp $ */
+/* $OpenBSD: sshbuf-misc.c,v 1.2 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2011 Damien Miller
*
@@ -33,12 +33,11 @@
#include "sshbuf.h"
void
-sshbuf_dump(struct sshbuf *buf, FILE *f)
+sshbuf_dump_data(const void *s, size_t len, FILE *f)
{
- const u_char *p = sshbuf_ptr(buf);
- size_t i, j, len = sshbuf_len(buf);
+ size_t i, j;
+ const u_char *p = (const u_char *)s;
- fprintf(f, "buffer %p len = %zu\n", buf, len);
for (i = 0; i < len; i += 16) {
fprintf(f, "%.4zd: ", i);
for (j = i; j < i + 16; j++) {
@@ -60,6 +59,13 @@ sshbuf_dump(struct sshbuf *buf, FILE *f)
}
}
+void
+sshbuf_dump(struct sshbuf *buf, FILE *f)
+{
+ fprintf(f, "buffer %p len = %zu\n", buf, sshbuf_len(buf));
+ sshbuf_dump_data(sshbuf_ptr(buf), sshbuf_len(buf), f);
+}
+
char *
sshbuf_dtob16(struct sshbuf *buf)
{
diff --git a/sshbuf.h b/sshbuf.h
index 1dd9be45..4561c063 100644
--- a/sshbuf.h
+++ b/sshbuf.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshbuf.h,v 1.2 2014/06/10 21:46:11 dtucker Exp $ */
+/* $OpenBSD: sshbuf.h,v 1.3 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2011 Damien Miller
*
@@ -216,9 +216,12 @@ int sshbuf_put_ec(struct sshbuf *buf, const EC_POINT *v, const EC_GROUP *g);
int sshbuf_put_eckey(struct sshbuf *buf, const EC_KEY *v);
#endif
-/* Dump the contents of the buffer to stderr in a human-readable format */
+/* Dump the contents of the buffer in a human-readable format */
void sshbuf_dump(struct sshbuf *buf, FILE *f);
+/* Dump specified memory in a human-readable format */
+void sshbuf_dump_data(const void *s, size_t len, FILE *f);
+
/* Return the hexadecimal representation of the contents of the buffer */
char *sshbuf_dtob16(struct sshbuf *buf);
diff --git a/sshconnect.c b/sshconnect.c
index 5d14ca6c..590dfe0f 100644
--- a/sshconnect.c
+++ b/sshconnect.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect.c,v 1.248 2014/04/29 18:01:49 markus Exp $ */
+/* $OpenBSD: sshconnect.c,v 1.249 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -709,7 +709,7 @@ check_host_cert(const char *host, const Key *host_key)
error("%s", reason);
return 0;
}
- if (buffer_len(&host_key->cert->critical) != 0) {
+ if (buffer_len(host_key->cert->critical) != 0) {
error("Certificate for %s contains unsupported "
"critical options(s)", host);
return 0;
diff --git a/sshconnect1.c b/sshconnect1.c
index 921408ec..62a7bd17 100644
--- a/sshconnect1.c
+++ b/sshconnect1.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect1.c,v 1.74 2014/02/02 03:44:32 djm Exp $ */
+/* $OpenBSD: sshconnect1.c,v 1.75 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -166,7 +166,7 @@ respond_to_rsa_challenge(BIGNUM * challenge, RSA * prv)
/* Decrypt the challenge using the private key. */
/* XXX think about Bleichenbacher, too */
- if (rsa_private_decrypt(challenge, challenge, prv) <= 0)
+ if (rsa_private_decrypt(challenge, challenge, prv) != 0)
packet_disconnect(
"respond_to_rsa_challenge: rsa_private_decrypt failed");
@@ -253,7 +253,7 @@ try_rsa_authentication(int idx)
* load the private key. Try first with empty passphrase; if it
* fails, ask for a passphrase.
*/
- if (public->flags & KEY_FLAG_EXT)
+ if (public->flags & SSHKEY_FLAG_EXT)
private = public;
else
private = key_load_private_type(KEY_RSA1, authfile, "", NULL,
@@ -302,7 +302,7 @@ try_rsa_authentication(int idx)
respond_to_rsa_challenge(challenge, private->rsa);
/* Destroy the private key unless it in external hardware. */
- if (!(private->flags & KEY_FLAG_EXT))
+ if (!(private->flags & SSHKEY_FLAG_EXT))
key_free(private);
/* We no longer need the challenge. */
@@ -592,8 +592,9 @@ ssh_kex(char *host, struct sockaddr *hostaddr)
BN_num_bits(server_key->rsa->n),
SSH_KEY_BITS_RESERVED);
}
- rsa_public_encrypt(key, key, server_key->rsa);
- rsa_public_encrypt(key, key, host_key->rsa);
+ if (rsa_public_encrypt(key, key, server_key->rsa) != 0 ||
+ rsa_public_encrypt(key, key, host_key->rsa) != 0)
+ fatal("%s: rsa_public_encrypt failed", __func__);
} else {
/* Host key has smaller modulus (or they are equal). */
if (BN_num_bits(server_key->rsa->n) <
@@ -604,8 +605,9 @@ ssh_kex(char *host, struct sockaddr *hostaddr)
BN_num_bits(host_key->rsa->n),
SSH_KEY_BITS_RESERVED);
}
- rsa_public_encrypt(key, key, host_key->rsa);
- rsa_public_encrypt(key, key, server_key->rsa);
+ if (rsa_public_encrypt(key, key, host_key->rsa) != 0 ||
+ rsa_public_encrypt(key, key, server_key->rsa) != 0)
+ fatal("%s: rsa_public_encrypt failed", __func__);
}
/* Destroy the public keys since we no longer need them. */
diff --git a/sshconnect2.c b/sshconnect2.c
index 65839843..eb1b0ae3 100644
--- a/sshconnect2.c
+++ b/sshconnect2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshconnect2.c,v 1.208 2014/06/05 22:17:50 djm Exp $ */
+/* $OpenBSD: sshconnect2.c,v 1.209 2014/06/24 01:13:21 djm Exp $ */
/*
* Copyright (c) 2000 Markus Friedl. All rights reserved.
* Copyright (c) 2008 Damien Miller. All rights reserved.
@@ -970,7 +970,7 @@ identity_sign(Identity *id, u_char **sigp, u_int *lenp,
* we have already loaded the private key or
* the private key is stored in external hardware
*/
- if (id->isprivate || (id->key->flags & KEY_FLAG_EXT))
+ if (id->isprivate || (id->key->flags & SSHKEY_FLAG_EXT))
return (key_sign(id->key, sigp, lenp, data, datalen));
/* load the private key from the file */
if ((prv = load_identity_file(id->filename, id->userprovided)) == NULL)
@@ -1178,12 +1178,12 @@ pubkey_prepare(Authctxt *authctxt)
}
/* Prefer PKCS11 keys that are explicitly listed */
TAILQ_FOREACH_SAFE(id, &files, next, tmp) {
- if (id->key == NULL || (id->key->flags & KEY_FLAG_EXT) == 0)
+ if (id->key == NULL || (id->key->flags & SSHKEY_FLAG_EXT) == 0)
continue;
found = 0;
TAILQ_FOREACH(id2, &files, next) {
if (id2->key == NULL ||
- (id2->key->flags & KEY_FLAG_EXT) == 0)
+ (id2->key->flags & SSHKEY_FLAG_EXT) == 0)
continue;
if (key_equal(id->key, id2->key)) {
TAILQ_REMOVE(&files, id, next);
diff --git a/sshd.c b/sshd.c
index 6e7192cf..67eb66a1 100644
--- a/sshd.c
+++ b/sshd.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sshd.c,v 1.426 2014/04/29 18:01:49 markus Exp $ */
+/* $OpenBSD: sshd.c,v 1.427 2014/06/24 01:13:21 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1031,8 +1031,10 @@ recv_rexec_state(int fd, Buffer *conf)
buffer_get_bignum(&m, sensitive_data.server_key->rsa->iqmp);
buffer_get_bignum(&m, sensitive_data.server_key->rsa->p);
buffer_get_bignum(&m, sensitive_data.server_key->rsa->q);
- rsa_generate_additional_parameters(
- sensitive_data.server_key->rsa);
+ if (rsa_generate_additional_parameters(
+ sensitive_data.server_key->rsa) != 0)
+ fatal("%s: rsa_generate_additional_parameters "
+ "error", __func__);
#else
fatal("ssh1 not supported");
#endif
@@ -2215,10 +2217,10 @@ ssh1_session_key(BIGNUM *session_key_int)
SSH_KEY_BITS_RESERVED);
}
if (rsa_private_decrypt(session_key_int, session_key_int,
- sensitive_data.server_key->rsa) <= 0)
+ sensitive_data.server_key->rsa) != 0)
rsafail++;
if (rsa_private_decrypt(session_key_int, session_key_int,
- sensitive_data.ssh1_host_key->rsa) <= 0)
+ sensitive_data.ssh1_host_key->rsa) != 0)
rsafail++;
} else {
/* Host key has bigger modulus (or they are equal). */
@@ -2233,10 +2235,10 @@ ssh1_session_key(BIGNUM *session_key_int)
SSH_KEY_BITS_RESERVED);
}
if (rsa_private_decrypt(session_key_int, session_key_int,
- sensitive_data.ssh1_host_key->rsa) < 0)
+ sensitive_data.ssh1_host_key->rsa) != 0)
rsafail++;
if (rsa_private_decrypt(session_key_int, session_key_int,
- sensitive_data.server_key->rsa) < 0)
+ sensitive_data.server_key->rsa) != 0)
rsafail++;
}
return (rsafail);
diff --git a/sshkey.c b/sshkey.c
new file mode 100644
index 00000000..24023d03
--- /dev/null
+++ b/sshkey.c
@@ -0,0 +1,3843 @@
+/* $OpenBSD: sshkey.c,v 1.2 2014/06/27 18:50:39 markus Exp $ */
+/*
+ * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
+ * Copyright (c) 2008 Alexander von Gernler. All rights reserved.
+ * Copyright (c) 2010,2011 Damien Miller. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "includes.h"
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <openssl/evp.h>
+#include <openssl/err.h>
+#include <openssl/pem.h>
+
+#include "crypto_api.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <util.h>
+
+#include "ssh2.h"
+#include "ssherr.h"
+#include "misc.h"
+#include "sshbuf.h"
+#include "rsa.h"
+#include "cipher.h"
+#include "digest.h"
+#define SSHKEY_INTERNAL
+#include "sshkey.h"
+
+/* openssh private key file format */
+#define MARK_BEGIN "-----BEGIN OPENSSH PRIVATE KEY-----\n"
+#define MARK_END "-----END OPENSSH PRIVATE KEY-----\n"
+#define MARK_BEGIN_LEN (sizeof(MARK_BEGIN) - 1)
+#define MARK_END_LEN (sizeof(MARK_END) - 1)
+#define KDFNAME "bcrypt"
+#define AUTH_MAGIC "openssh-key-v1"
+#define SALT_LEN 16
+#define DEFAULT_CIPHERNAME "aes256-cbc"
+#define DEFAULT_ROUNDS 16
+
+/* Version identification string for SSH v1 identity files. */
+#define LEGACY_BEGIN "SSH PRIVATE KEY FILE FORMAT 1.1\n"
+
+static int sshkey_from_blob_internal(const u_char *blob, size_t blen,
+ struct sshkey **keyp, int allow_cert);
+
+/* Supported key types */
+struct keytype {
+ const char *name;
+ const char *shortname;
+ int type;
+ int nid;
+ int cert;
+};
+static const struct keytype keytypes[] = {
+ { "ssh-ed25519", "ED25519", KEY_ED25519, 0, 0 },
+ { "ssh-ed25519-cert-v01@openssh.com", "ED25519-CERT",
+ KEY_ED25519_CERT, 0, 1 },
+#ifdef WITH_OPENSSL
+ { NULL, "RSA1", KEY_RSA1, 0, 0 },
+ { "ssh-rsa", "RSA", KEY_RSA, 0, 0 },
+ { "ssh-dss", "DSA", KEY_DSA, 0, 0 },
+# ifdef OPENSSL_HAS_ECC
+ { "ecdsa-sha2-nistp256", "ECDSA", KEY_ECDSA, NID_X9_62_prime256v1, 0 },
+ { "ecdsa-sha2-nistp384", "ECDSA", KEY_ECDSA, NID_secp384r1, 0 },
+# ifdef OPENSSL_HAS_NISTP521
+ { "ecdsa-sha2-nistp521", "ECDSA", KEY_ECDSA, NID_secp521r1, 0 },
+# endif /* OPENSSL_HAS_NISTP521 */
+# endif /* OPENSSL_HAS_ECC */
+ { "ssh-rsa-cert-v01@openssh.com", "RSA-CERT", KEY_RSA_CERT, 0, 1 },
+ { "ssh-dss-cert-v01@openssh.com", "DSA-CERT", KEY_DSA_CERT, 0, 1 },
+# ifdef OPENSSL_HAS_ECC
+ { "ecdsa-sha2-nistp256-cert-v01@openssh.com", "ECDSA-CERT",
+ KEY_ECDSA_CERT, NID_X9_62_prime256v1, 1 },
+ { "ecdsa-sha2-nistp384-cert-v01@openssh.com", "ECDSA-CERT",
+ KEY_ECDSA_CERT, NID_secp384r1, 1 },
+# ifdef OPENSSL_HAS_NISTP521
+ { "ecdsa-sha2-nistp521-cert-v01@openssh.com", "ECDSA-CERT",
+ KEY_ECDSA_CERT, NID_secp521r1, 1 },
+# endif /* OPENSSL_HAS_NISTP521 */
+# endif /* OPENSSL_HAS_ECC */
+ { "ssh-rsa-cert-v00@openssh.com", "RSA-CERT-V00",
+ KEY_RSA_CERT_V00, 0, 1 },
+ { "ssh-dss-cert-v00@openssh.com", "DSA-CERT-V00",
+ KEY_DSA_CERT_V00, 0, 1 },
+#endif /* WITH_OPENSSL */
+ { NULL, NULL, -1, -1, 0 }
+};
+
+const char *
+sshkey_type(const struct sshkey *k)
+{
+ const struct keytype *kt;
+
+ for (kt = keytypes; kt->type != -1; kt++) {
+ if (kt->type == k->type)
+ return kt->shortname;
+ }
+ return "unknown";
+}
+
+static const char *
+sshkey_ssh_name_from_type_nid(int type, int nid)
+{
+ const struct keytype *kt;
+
+ for (kt = keytypes; kt->type != -1; kt++) {
+ if (kt->type == type && (kt->nid == 0 || kt->nid == nid))
+ return kt->name;
+ }
+ return "ssh-unknown";
+}
+
+int
+sshkey_type_is_cert(int type)
+{
+ const struct keytype *kt;
+
+ for (kt = keytypes; kt->type != -1; kt++) {
+ if (kt->type == type)
+ return kt->cert;
+ }
+ return 0;
+}
+
+const char *
+sshkey_ssh_name(const struct sshkey *k)
+{
+ return sshkey_ssh_name_from_type_nid(k->type, k->ecdsa_nid);
+}
+
+const char *
+sshkey_ssh_name_plain(const struct sshkey *k)
+{
+ return sshkey_ssh_name_from_type_nid(sshkey_type_plain(k->type),
+ k->ecdsa_nid);
+}
+
+int
+sshkey_type_from_name(const char *name)
+{
+ const struct keytype *kt;
+
+ for (kt = keytypes; kt->type != -1; kt++) {
+ /* Only allow shortname matches for plain key types */
+ if ((kt->name != NULL && strcmp(name, kt->name) == 0) ||
+ (!kt->cert && strcasecmp(kt->shortname, name) == 0))
+ return kt->type;
+ }
+ return KEY_UNSPEC;
+}
+
+int
+sshkey_ecdsa_nid_from_name(const char *name)
+{
+ const struct keytype *kt;
+
+ for (kt = keytypes; kt->type != -1; kt++) {
+ if (kt->type != KEY_ECDSA && kt->type != KEY_ECDSA_CERT)
+ continue;
+ if (kt->name != NULL && strcmp(name, kt->name) == 0)
+ return kt->nid;
+ }
+ return -1;
+}
+
+char *
+key_alg_list(int certs_only, int plain_only)
+{
+ char *tmp, *ret = NULL;
+ size_t nlen, rlen = 0;
+ const struct keytype *kt;
+
+ for (kt = keytypes; kt->type != -1; kt++) {
+ if (kt->name == NULL)
+ continue;
+ if ((certs_only && !kt->cert) || (plain_only && kt->cert))
+ continue;
+ if (ret != NULL)
+ ret[rlen++] = '\n';
+ nlen = strlen(kt->name);
+ if ((tmp = realloc(ret, rlen + nlen + 2)) == NULL) {
+ free(ret);
+ return NULL;
+ }
+ ret = tmp;
+ memcpy(ret + rlen, kt->name, nlen + 1);
+ rlen += nlen;
+ }
+ return ret;
+}
+
+int
+sshkey_names_valid2(const char *names)
+{
+ char *s, *cp, *p;
+
+ if (names == NULL || strcmp(names, "") == 0)
+ return 0;
+ if ((s = cp = strdup(names)) == NULL)
+ return 0;
+ for ((p = strsep(&cp, ",")); p && *p != '\0';
+ (p = strsep(&cp, ","))) {
+ switch (sshkey_type_from_name(p)) {
+ case KEY_RSA1:
+ case KEY_UNSPEC:
+ free(s);
+ return 0;
+ }
+ }
+ free(s);
+ return 1;
+}
+
+u_int
+sshkey_size(const struct sshkey *k)
+{
+ switch (k->type) {
+#ifdef WITH_OPENSSL
+ case KEY_RSA1:
+ case KEY_RSA:
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ return BN_num_bits(k->rsa->n);
+ case KEY_DSA:
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ return BN_num_bits(k->dsa->p);
+ case KEY_ECDSA:
+ case KEY_ECDSA_CERT:
+ return sshkey_curve_nid_to_bits(k->ecdsa_nid);
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ case KEY_ED25519_CERT:
+ return 256; /* XXX */
+ }
+ return 0;
+}
+
+int
+sshkey_cert_is_legacy(const struct sshkey *k)
+{
+ switch (k->type) {
+ case KEY_DSA_CERT_V00:
+ case KEY_RSA_CERT_V00:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+static int
+sshkey_type_is_valid_ca(int type)
+{
+ switch (type) {
+ case KEY_RSA:
+ case KEY_DSA:
+ case KEY_ECDSA:
+ case KEY_ED25519:
+ return 1;
+ default:
+ return 0;
+ }
+}
+
+int
+sshkey_is_cert(const struct sshkey *k)
+{
+ if (k == NULL)
+ return 0;
+ return sshkey_type_is_cert(k->type);
+}
+
+/* Return the cert-less equivalent to a certified key type */
+int
+sshkey_type_plain(int type)
+{
+ switch (type) {
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ return KEY_RSA;
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ return KEY_DSA;
+ case KEY_ECDSA_CERT:
+ return KEY_ECDSA;
+ case KEY_ED25519_CERT:
+ return KEY_ED25519;
+ default:
+ return type;
+ }
+}
+
+#ifdef WITH_OPENSSL
+/* XXX: these are really begging for a table-driven approach */
+int
+sshkey_curve_name_to_nid(const char *name)
+{
+ if (strcmp(name, "nistp256") == 0)
+ return NID_X9_62_prime256v1;
+ else if (strcmp(name, "nistp384") == 0)
+ return NID_secp384r1;
+# ifdef OPENSSL_HAS_NISTP521
+ else if (strcmp(name, "nistp521") == 0)
+ return NID_secp521r1;
+# endif /* OPENSSL_HAS_NISTP521 */
+ else
+ return -1;
+}
+
+u_int
+sshkey_curve_nid_to_bits(int nid)
+{
+ switch (nid) {
+ case NID_X9_62_prime256v1:
+ return 256;
+ case NID_secp384r1:
+ return 384;
+# ifdef OPENSSL_HAS_NISTP521
+ case NID_secp521r1:
+ return 521;
+# endif /* OPENSSL_HAS_NISTP521 */
+ default:
+ return 0;
+ }
+}
+
+int
+sshkey_ecdsa_bits_to_nid(int bits)
+{
+ switch (bits) {
+ case 256:
+ return NID_X9_62_prime256v1;
+ case 384:
+ return NID_secp384r1;
+# ifdef OPENSSL_HAS_NISTP521
+ case 521:
+ return NID_secp521r1;
+# endif /* OPENSSL_HAS_NISTP521 */
+ default:
+ return -1;
+ }
+}
+
+const char *
+sshkey_curve_nid_to_name(int nid)
+{
+ switch (nid) {
+ case NID_X9_62_prime256v1:
+ return "nistp256";
+ case NID_secp384r1:
+ return "nistp384";
+# ifdef OPENSSL_HAS_NISTP521
+ case NID_secp521r1:
+ return "nistp521";
+# endif /* OPENSSL_HAS_NISTP521 */
+ default:
+ return NULL;
+ }
+}
+
+int
+sshkey_ec_nid_to_hash_alg(int nid)
+{
+ int kbits = sshkey_curve_nid_to_bits(nid);
+
+ if (kbits <= 0)
+ return -1;
+
+ /* RFC5656 section 6.2.1 */
+ if (kbits <= 256)
+ return SSH_DIGEST_SHA256;
+ else if (kbits <= 384)
+ return SSH_DIGEST_SHA384;
+ else
+ return SSH_DIGEST_SHA512;
+}
+#endif /* WITH_OPENSSL */
+
+static void
+cert_free(struct sshkey_cert *cert)
+{
+ u_int i;
+
+ if (cert == NULL)
+ return;
+ if (cert->certblob != NULL)
+ sshbuf_free(cert->certblob);
+ if (cert->critical != NULL)
+ sshbuf_free(cert->critical);
+ if (cert->extensions != NULL)
+ sshbuf_free(cert->extensions);
+ if (cert->key_id != NULL)
+ free(cert->key_id);
+ for (i = 0; i < cert->nprincipals; i++)
+ free(cert->principals[i]);
+ if (cert->principals != NULL)
+ free(cert->principals);
+ if (cert->signature_key != NULL)
+ sshkey_free(cert->signature_key);
+ explicit_bzero(cert, sizeof(*cert));
+ free(cert);
+}
+
+static struct sshkey_cert *
+cert_new(void)
+{
+ struct sshkey_cert *cert;
+
+ if ((cert = calloc(1, sizeof(*cert))) == NULL)
+ return NULL;
+ if ((cert->certblob = sshbuf_new()) == NULL ||
+ (cert->critical = sshbuf_new()) == NULL ||
+ (cert->extensions = sshbuf_new()) == NULL) {
+ cert_free(cert);
+ return NULL;
+ }
+ cert->key_id = NULL;
+ cert->principals = NULL;
+ cert->signature_key = NULL;
+ return cert;
+}
+
+struct sshkey *
+sshkey_new(int type)
+{
+ struct sshkey *k;
+#ifdef WITH_OPENSSL
+ RSA *rsa;
+ DSA *dsa;
+#endif /* WITH_OPENSSL */
+
+ if ((k = calloc(1, sizeof(*k))) == NULL)
+ return NULL;
+ k->type = type;
+ k->ecdsa = NULL;
+ k->ecdsa_nid = -1;
+ k->dsa = NULL;
+ k->rsa = NULL;
+ k->cert = NULL;
+ k->ed25519_sk = NULL;
+ k->ed25519_pk = NULL;
+ switch (k->type) {
+#ifdef WITH_OPENSSL
+ case KEY_RSA1:
+ case KEY_RSA:
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ if ((rsa = RSA_new()) == NULL ||
+ (rsa->n = BN_new()) == NULL ||
+ (rsa->e = BN_new()) == NULL) {
+ if (rsa != NULL)
+ RSA_free(rsa);
+ free(k);
+ return NULL;
+ }
+ k->rsa = rsa;
+ break;
+ case KEY_DSA:
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ if ((dsa = DSA_new()) == NULL ||
+ (dsa->p = BN_new()) == NULL ||
+ (dsa->q = BN_new()) == NULL ||
+ (dsa->g = BN_new()) == NULL ||
+ (dsa->pub_key = BN_new()) == NULL) {
+ if (dsa != NULL)
+ DSA_free(dsa);
+ free(k);
+ return NULL;
+ }
+ k->dsa = dsa;
+ break;
+ case KEY_ECDSA:
+ case KEY_ECDSA_CERT:
+ /* Cannot do anything until we know the group */
+ break;
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ case KEY_ED25519_CERT:
+ /* no need to prealloc */
+ break;
+ case KEY_UNSPEC:
+ break;
+ default:
+ free(k);
+ return NULL;
+ break;
+ }
+
+ if (sshkey_is_cert(k)) {
+ if ((k->cert = cert_new()) == NULL) {
+ sshkey_free(k);
+ return NULL;
+ }
+ }
+
+ return k;
+}
+
+int
+sshkey_add_private(struct sshkey *k)
+{
+ switch (k->type) {
+#ifdef WITH_OPENSSL
+ case KEY_RSA1:
+ case KEY_RSA:
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+#define bn_maybe_alloc_failed(p) (p == NULL && (p = BN_new()) == NULL)
+ if (bn_maybe_alloc_failed(k->rsa->d) ||
+ bn_maybe_alloc_failed(k->rsa->iqmp) ||
+ bn_maybe_alloc_failed(k->rsa->q) ||
+ bn_maybe_alloc_failed(k->rsa->p) ||
+ bn_maybe_alloc_failed(k->rsa->dmq1) ||
+ bn_maybe_alloc_failed(k->rsa->dmp1))
+ return SSH_ERR_ALLOC_FAIL;
+ break;
+ case KEY_DSA:
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ if (bn_maybe_alloc_failed(k->dsa->priv_key))
+ return SSH_ERR_ALLOC_FAIL;
+ break;
+#undef bn_maybe_alloc_failed
+ case KEY_ECDSA:
+ case KEY_ECDSA_CERT:
+ /* Cannot do anything until we know the group */
+ break;
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ case KEY_ED25519_CERT:
+ /* no need to prealloc */
+ break;
+ case KEY_UNSPEC:
+ break;
+ default:
+ return SSH_ERR_INVALID_ARGUMENT;
+ }
+ return 0;
+}
+
+struct sshkey *
+sshkey_new_private(int type)
+{
+ struct sshkey *k = sshkey_new(type);
+
+ if (k == NULL)
+ return NULL;
+ if (sshkey_add_private(k) != 0) {
+ sshkey_free(k);
+ return NULL;
+ }
+ return k;
+}
+
+void
+sshkey_free(struct sshkey *k)
+{
+ if (k == NULL)
+ return;
+ switch (k->type) {
+#ifdef WITH_OPENSSL
+ case KEY_RSA1:
+ case KEY_RSA:
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ if (k->rsa != NULL)
+ RSA_free(k->rsa);
+ k->rsa = NULL;
+ break;
+ case KEY_DSA:
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ if (k->dsa != NULL)
+ DSA_free(k->dsa);
+ k->dsa = NULL;
+ break;
+# ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA:
+ case KEY_ECDSA_CERT:
+ if (k->ecdsa != NULL)
+ EC_KEY_free(k->ecdsa);
+ k->ecdsa = NULL;
+ break;
+# endif /* OPENSSL_HAS_ECC */
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ case KEY_ED25519_CERT:
+ if (k->ed25519_pk) {
+ explicit_bzero(k->ed25519_pk, ED25519_PK_SZ);
+ free(k->ed25519_pk);
+ k->ed25519_pk = NULL;
+ }
+ if (k->ed25519_sk) {
+ explicit_bzero(k->ed25519_sk, ED25519_SK_SZ);
+ free(k->ed25519_sk);
+ k->ed25519_sk = NULL;
+ }
+ break;
+ case KEY_UNSPEC:
+ break;
+ default:
+ break;
+ }
+ if (sshkey_is_cert(k))
+ cert_free(k->cert);
+ explicit_bzero(k, sizeof(*k));
+ free(k);
+}
+
+static int
+cert_compare(struct sshkey_cert *a, struct sshkey_cert *b)
+{
+ if (a == NULL && b == NULL)
+ return 1;
+ if (a == NULL || b == NULL)
+ return 0;
+ if (sshbuf_len(a->certblob) != sshbuf_len(b->certblob))
+ return 0;
+ if (timingsafe_bcmp(sshbuf_ptr(a->certblob), sshbuf_ptr(b->certblob),
+ sshbuf_len(a->certblob)) != 0)
+ return 0;
+ return 1;
+}
+
+/*
+ * Compare public portions of key only, allowing comparisons between
+ * certificates and plain keys too.
+ */
+int
+sshkey_equal_public(const struct sshkey *a, const struct sshkey *b)
+{
+#ifdef WITH_OPENSSL
+ BN_CTX *bnctx;
+#endif /* WITH_OPENSSL */
+
+ if (a == NULL || b == NULL ||
+ sshkey_type_plain(a->type) != sshkey_type_plain(b->type))
+ return 0;
+
+ switch (a->type) {
+#ifdef WITH_OPENSSL
+ case KEY_RSA1:
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ case KEY_RSA:
+ return a->rsa != NULL && b->rsa != NULL &&
+ BN_cmp(a->rsa->e, b->rsa->e) == 0 &&
+ BN_cmp(a->rsa->n, b->rsa->n) == 0;
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ case KEY_DSA:
+ return a->dsa != NULL && b->dsa != NULL &&
+ BN_cmp(a->dsa->p, b->dsa->p) == 0 &&
+ BN_cmp(a->dsa->q, b->dsa->q) == 0 &&
+ BN_cmp(a->dsa->g, b->dsa->g) == 0 &&
+ BN_cmp(a->dsa->pub_key, b->dsa->pub_key) == 0;
+# ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA_CERT:
+ case KEY_ECDSA:
+ if (a->ecdsa == NULL || b->ecdsa == NULL ||
+ EC_KEY_get0_public_key(a->ecdsa) == NULL ||
+ EC_KEY_get0_public_key(b->ecdsa) == NULL)
+ return 0;
+ if ((bnctx = BN_CTX_new()) == NULL)
+ return 0;
+ if (EC_GROUP_cmp(EC_KEY_get0_group(a->ecdsa),
+ EC_KEY_get0_group(b->ecdsa), bnctx) != 0 ||
+ EC_POINT_cmp(EC_KEY_get0_group(a->ecdsa),
+ EC_KEY_get0_public_key(a->ecdsa),
+ EC_KEY_get0_public_key(b->ecdsa), bnctx) != 0) {
+ BN_CTX_free(bnctx);
+ return 0;
+ }
+ BN_CTX_free(bnctx);
+ return 1;
+# endif /* OPENSSL_HAS_ECC */
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ case KEY_ED25519_CERT:
+ return a->ed25519_pk != NULL && b->ed25519_pk != NULL &&
+ memcmp(a->ed25519_pk, b->ed25519_pk, ED25519_PK_SZ) == 0;
+ default:
+ return 0;
+ }
+ /* NOTREACHED */
+}
+
+int
+sshkey_equal(const struct sshkey *a, const struct sshkey *b)
+{
+ if (a == NULL || b == NULL || a->type != b->type)
+ return 0;
+ if (sshkey_is_cert(a)) {
+ if (!cert_compare(a->cert, b->cert))
+ return 0;
+ }
+ return sshkey_equal_public(a, b);
+}
+
+static int
+to_blob_buf(const struct sshkey *key, struct sshbuf *b, int force_plain)
+{
+ int type, ret = SSH_ERR_INTERNAL_ERROR;
+ const char *typename;
+
+ if (key == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+
+ type = force_plain ? sshkey_type_plain(key->type) : key->type;
+ typename = sshkey_ssh_name_from_type_nid(type, key->ecdsa_nid);
+
+ switch (type) {
+#ifdef WITH_OPENSSL
+ case KEY_DSA_CERT_V00:
+ case KEY_RSA_CERT_V00:
+ case KEY_DSA_CERT:
+ case KEY_ECDSA_CERT:
+ case KEY_RSA_CERT:
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519_CERT:
+ /* Use the existing blob */
+ /* XXX modified flag? */
+ if ((ret = sshbuf_putb(b, key->cert->certblob)) != 0)
+ return ret;
+ break;
+#ifdef WITH_OPENSSL
+ case KEY_DSA:
+ if (key->dsa == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+ if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
+ (ret = sshbuf_put_bignum2(b, key->dsa->p)) != 0 ||
+ (ret = sshbuf_put_bignum2(b, key->dsa->q)) != 0 ||
+ (ret = sshbuf_put_bignum2(b, key->dsa->g)) != 0 ||
+ (ret = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0)
+ return ret;
+ break;
+ case KEY_ECDSA:
+ if (key->ecdsa == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+ if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
+ (ret = sshbuf_put_cstring(b,
+ sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
+ (ret = sshbuf_put_eckey(b, key->ecdsa)) != 0)
+ return ret;
+ break;
+ case KEY_RSA:
+ if (key->rsa == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+ if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
+ (ret = sshbuf_put_bignum2(b, key->rsa->e)) != 0 ||
+ (ret = sshbuf_put_bignum2(b, key->rsa->n)) != 0)
+ return ret;
+ break;
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ if (key->ed25519_pk == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+ if ((ret = sshbuf_put_cstring(b, typename)) != 0 ||
+ (ret = sshbuf_put_string(b,
+ key->ed25519_pk, ED25519_PK_SZ)) != 0)
+ return ret;
+ break;
+ default:
+ return SSH_ERR_KEY_TYPE_UNKNOWN;
+ }
+ return 0;
+}
+
+int
+sshkey_to_blob_buf(const struct sshkey *key, struct sshbuf *b)
+{
+ return to_blob_buf(key, b, 0);
+}
+
+int
+sshkey_plain_to_blob_buf(const struct sshkey *key, struct sshbuf *b)
+{
+ return to_blob_buf(key, b, 1);
+}
+
+static int
+to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp, int force_plain)
+{
+ int ret = SSH_ERR_INTERNAL_ERROR;
+ size_t len;
+ struct sshbuf *b = NULL;
+
+ if (lenp != NULL)
+ *lenp = 0;
+ if (blobp != NULL)
+ *blobp = NULL;
+ if ((b = sshbuf_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if ((ret = to_blob_buf(key, b, force_plain)) != 0)
+ goto out;
+ len = sshbuf_len(b);
+ if (lenp != NULL)
+ *lenp = len;
+ if (blobp != NULL) {
+ if ((*blobp = malloc(len)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ memcpy(*blobp, sshbuf_ptr(b), len);
+ }
+ ret = 0;
+ out:
+ sshbuf_free(b);
+ return ret;
+}
+
+int
+sshkey_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
+{
+ return to_blob(key, blobp, lenp, 0);
+}
+
+int
+sshkey_plain_to_blob(const struct sshkey *key, u_char **blobp, size_t *lenp)
+{
+ return to_blob(key, blobp, lenp, 1);
+}
+
+int
+sshkey_fingerprint_raw(const struct sshkey *k, enum sshkey_fp_type dgst_type,
+ u_char **retp, size_t *lenp)
+{
+ u_char *blob = NULL, *ret = NULL;
+ size_t blob_len = 0;
+ int hash_alg = -1, r = SSH_ERR_INTERNAL_ERROR;
+
+ if (retp != NULL)
+ *retp = NULL;
+ if (lenp != NULL)
+ *lenp = 0;
+
+ switch (dgst_type) {
+ case SSH_FP_MD5:
+ hash_alg = SSH_DIGEST_MD5;
+ break;
+ case SSH_FP_SHA1:
+ hash_alg = SSH_DIGEST_SHA1;
+ break;
+ case SSH_FP_SHA256:
+ hash_alg = SSH_DIGEST_SHA256;
+ break;
+ default:
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+
+ if (k->type == KEY_RSA1) {
+#ifdef WITH_OPENSSL
+ int nlen = BN_num_bytes(k->rsa->n);
+ int elen = BN_num_bytes(k->rsa->e);
+
+ blob_len = nlen + elen;
+ if (nlen >= INT_MAX - elen ||
+ (blob = malloc(blob_len)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ BN_bn2bin(k->rsa->n, blob);
+ BN_bn2bin(k->rsa->e, blob + nlen);
+#endif /* WITH_OPENSSL */
+ } else if ((r = to_blob(k, &blob, &blob_len, 1)) != 0)
+ goto out;
+ if ((ret = calloc(1, SSH_DIGEST_MAX_LENGTH)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((r = ssh_digest_memory(hash_alg, blob, blob_len,
+ ret, SSH_DIGEST_MAX_LENGTH)) != 0)
+ goto out;
+ /* success */
+ if (retp != NULL) {
+ *retp = ret;
+ ret = NULL;
+ }
+ if (lenp != NULL)
+ *lenp = ssh_digest_bytes(hash_alg);
+ r = 0;
+ out:
+ free(ret);
+ if (blob != NULL) {
+ explicit_bzero(blob, blob_len);
+ free(blob);
+ }
+ return r;
+}
+
+static char *
+fingerprint_hex(u_char *dgst_raw, size_t dgst_raw_len)
+{
+ char *retval;
+ size_t i;
+
+ if ((retval = calloc(1, dgst_raw_len * 3 + 1)) == NULL)
+ return NULL;
+ for (i = 0; i < dgst_raw_len; i++) {
+ char hex[4];
+ snprintf(hex, sizeof(hex), "%02x:", dgst_raw[i]);
+ strlcat(retval, hex, dgst_raw_len * 3 + 1);
+ }
+
+ /* Remove the trailing ':' character */
+ retval[(dgst_raw_len * 3) - 1] = '\0';
+ return retval;
+}
+
+static char *
+fingerprint_bubblebabble(u_char *dgst_raw, size_t dgst_raw_len)
+{
+ char vowels[] = { 'a', 'e', 'i', 'o', 'u', 'y' };
+ char consonants[] = { 'b', 'c', 'd', 'f', 'g', 'h', 'k', 'l', 'm',
+ 'n', 'p', 'r', 's', 't', 'v', 'z', 'x' };
+ u_int i, j = 0, rounds, seed = 1;
+ char *retval;
+
+ rounds = (dgst_raw_len / 2) + 1;
+ if ((retval = calloc(rounds, 6)) == NULL)
+ return NULL;
+ retval[j++] = 'x';
+ for (i = 0; i < rounds; i++) {
+ u_int idx0, idx1, idx2, idx3, idx4;
+ if ((i + 1 < rounds) || (dgst_raw_len % 2 != 0)) {
+ idx0 = (((((u_int)(dgst_raw[2 * i])) >> 6) & 3) +
+ seed) % 6;
+ idx1 = (((u_int)(dgst_raw[2 * i])) >> 2) & 15;
+ idx2 = ((((u_int)(dgst_raw[2 * i])) & 3) +
+ (seed / 6)) % 6;
+ retval[j++] = vowels[idx0];
+ retval[j++] = consonants[idx1];
+ retval[j++] = vowels[idx2];
+ if ((i + 1) < rounds) {
+ idx3 = (((u_int)(dgst_raw[(2 * i) + 1])) >> 4) & 15;
+ idx4 = (((u_int)(dgst_raw[(2 * i) + 1]))) & 15;
+ retval[j++] = consonants[idx3];
+ retval[j++] = '-';
+ retval[j++] = consonants[idx4];
+ seed = ((seed * 5) +
+ ((((u_int)(dgst_raw[2 * i])) * 7) +
+ ((u_int)(dgst_raw[(2 * i) + 1])))) % 36;
+ }
+ } else {
+ idx0 = seed % 6;
+ idx1 = 16;
+ idx2 = seed / 6;
+ retval[j++] = vowels[idx0];
+ retval[j++] = consonants[idx1];
+ retval[j++] = vowels[idx2];
+ }
+ }
+ retval[j++] = 'x';
+ retval[j++] = '\0';
+ return retval;
+}
+
+/*
+ * Draw an ASCII-Art representing the fingerprint so human brain can
+ * profit from its built-in pattern recognition ability.
+ * This technique is called "random art" and can be found in some
+ * scientific publications like this original paper:
+ *
+ * "Hash Visualization: a New Technique to improve Real-World Security",
+ * Perrig A. and Song D., 1999, International Workshop on Cryptographic
+ * Techniques and E-Commerce (CrypTEC '99)
+ * sparrow.ece.cmu.edu/~adrian/projects/validation/validation.pdf
+ *
+ * The subject came up in a talk by Dan Kaminsky, too.
+ *
+ * If you see the picture is different, the key is different.
+ * If the picture looks the same, you still know nothing.
+ *
+ * The algorithm used here is a worm crawling over a discrete plane,
+ * leaving a trace (augmenting the field) everywhere it goes.
+ * Movement is taken from dgst_raw 2bit-wise. Bumping into walls
+ * makes the respective movement vector be ignored for this turn.
+ * Graphs are not unambiguous, because circles in graphs can be
+ * walked in either direction.
+ */
+
+/*
+ * Field sizes for the random art. Have to be odd, so the starting point
+ * can be in the exact middle of the picture, and FLDBASE should be >=8 .
+ * Else pictures would be too dense, and drawing the frame would
+ * fail, too, because the key type would not fit in anymore.
+ */
+#define FLDBASE 8
+#define FLDSIZE_Y (FLDBASE + 1)
+#define FLDSIZE_X (FLDBASE * 2 + 1)
+static char *
+fingerprint_randomart(u_char *dgst_raw, size_t dgst_raw_len,
+ const struct sshkey *k)
+{
+ /*
+ * Chars to be used after each other every time the worm
+ * intersects with itself. Matter of taste.
+ */
+ char *augmentation_string = " .o+=*BOX@%&#/^SE";
+ char *retval, *p;
+ u_char field[FLDSIZE_X][FLDSIZE_Y];
+ size_t i;
+ u_int b;
+ int x, y;
+ size_t len = strlen(augmentation_string) - 1;
+
+ if ((retval = calloc((FLDSIZE_X + 3), (FLDSIZE_Y + 2))) == NULL)
+ return NULL;
+
+ /* initialize field */
+ memset(field, 0, FLDSIZE_X * FLDSIZE_Y * sizeof(char));
+ x = FLDSIZE_X / 2;
+ y = FLDSIZE_Y / 2;
+
+ /* process raw key */
+ for (i = 0; i < dgst_raw_len; i++) {
+ int input;
+ /* each byte conveys four 2-bit move commands */
+ input = dgst_raw[i];
+ for (b = 0; b < 4; b++) {
+ /* evaluate 2 bit, rest is shifted later */
+ x += (input & 0x1) ? 1 : -1;
+ y += (input & 0x2) ? 1 : -1;
+
+ /* assure we are still in bounds */
+ x = MAX(x, 0);
+ y = MAX(y, 0);
+ x = MIN(x, FLDSIZE_X - 1);
+ y = MIN(y, FLDSIZE_Y - 1);
+
+ /* augment the field */
+ if (field[x][y] < len - 2)
+ field[x][y]++;
+ input = input >> 2;
+ }
+ }
+
+ /* mark starting point and end point*/
+ field[FLDSIZE_X / 2][FLDSIZE_Y / 2] = len - 1;
+ field[x][y] = len;
+
+ /* fill in retval */
+ snprintf(retval, FLDSIZE_X, "+--[%4s %4u]",
+ sshkey_type(k), sshkey_size(k));
+ p = strchr(retval, '\0');
+
+ /* output upper border */
+ for (i = p - retval - 1; i < FLDSIZE_X; i++)
+ *p++ = '-';
+ *p++ = '+';
+ *p++ = '\n';
+
+ /* output content */
+ for (y = 0; y < FLDSIZE_Y; y++) {
+ *p++ = '|';
+ for (x = 0; x < FLDSIZE_X; x++)
+ *p++ = augmentation_string[MIN(field[x][y], len)];
+ *p++ = '|';
+ *p++ = '\n';
+ }
+
+ /* output lower border */
+ *p++ = '+';
+ for (i = 0; i < FLDSIZE_X; i++)
+ *p++ = '-';
+ *p++ = '+';
+
+ return retval;
+}
+
+char *
+sshkey_fingerprint(const struct sshkey *k, enum sshkey_fp_type dgst_type,
+ enum sshkey_fp_rep dgst_rep)
+{
+ char *retval = NULL;
+ u_char *dgst_raw;
+ size_t dgst_raw_len;
+
+ if (sshkey_fingerprint_raw(k, dgst_type, &dgst_raw, &dgst_raw_len) != 0)
+ return NULL;
+ switch (dgst_rep) {
+ case SSH_FP_HEX:
+ retval = fingerprint_hex(dgst_raw, dgst_raw_len);
+ break;
+ case SSH_FP_BUBBLEBABBLE:
+ retval = fingerprint_bubblebabble(dgst_raw, dgst_raw_len);
+ break;
+ case SSH_FP_RANDOMART:
+ retval = fingerprint_randomart(dgst_raw, dgst_raw_len, k);
+ break;
+ default:
+ explicit_bzero(dgst_raw, dgst_raw_len);
+ free(dgst_raw);
+ return NULL;
+ }
+ explicit_bzero(dgst_raw, dgst_raw_len);
+ free(dgst_raw);
+ return retval;
+}
+
+#ifdef WITH_SSH1
+/*
+ * Reads a multiple-precision integer in decimal from the buffer, and advances
+ * the pointer. The integer must already be initialized. This function is
+ * permitted to modify the buffer. This leaves *cpp to point just beyond the
+ * last processed character.
+ */
+static int
+read_decimal_bignum(char **cpp, BIGNUM *v)
+{
+ char *cp;
+ size_t e;
+ int skip = 1; /* skip white space */
+
+ cp = *cpp;
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ e = strspn(cp, "0123456789");
+ if (e == 0)
+ return SSH_ERR_INVALID_FORMAT;
+ if (e > SSHBUF_MAX_BIGNUM * 3)
+ return SSH_ERR_BIGNUM_TOO_LARGE;
+ if (cp[e] == '\0')
+ skip = 0;
+ else if (index(" \t\r\n", cp[e]) == NULL)
+ return SSH_ERR_INVALID_FORMAT;
+ cp[e] = '\0';
+ if (BN_dec2bn(&v, cp) <= 0)
+ return SSH_ERR_INVALID_FORMAT;
+ *cpp = cp + e + skip;
+ return 0;
+}
+#endif /* WITH_SSH1 */
+
+/* returns 0 ok, and < 0 error */
+int
+sshkey_read(struct sshkey *ret, char **cpp)
+{
+ struct sshkey *k;
+ int retval = SSH_ERR_INVALID_FORMAT;
+ char *cp, *space;
+ int r, type, curve_nid = -1;
+ struct sshbuf *blob;
+#ifdef WITH_SSH1
+ char *ep;
+ u_long bits;
+#endif /* WITH_SSH1 */
+
+ cp = *cpp;
+
+ switch (ret->type) {
+ case KEY_RSA1:
+#ifdef WITH_SSH1
+ /* Get number of bits. */
+ bits = strtoul(cp, &ep, 10);
+ if (*cp == '\0' || index(" \t\r\n", *ep) == NULL ||
+ bits == 0 || bits > SSHBUF_MAX_BIGNUM * 8)
+ return SSH_ERR_INVALID_FORMAT; /* Bad bit count... */
+ /* Get public exponent, public modulus. */
+ if ((r = read_decimal_bignum(&ep, ret->rsa->e)) < 0)
+ return r;
+ if ((r = read_decimal_bignum(&ep, ret->rsa->n)) < 0)
+ return r;
+ *cpp = ep;
+ /* validate the claimed number of bits */
+ if (BN_num_bits(ret->rsa->n) != (int)bits)
+ return SSH_ERR_KEY_BITS_MISMATCH;
+ retval = 0;
+#endif /* WITH_SSH1 */
+ break;
+ case KEY_UNSPEC:
+ case KEY_RSA:
+ case KEY_DSA:
+ case KEY_ECDSA:
+ case KEY_ED25519:
+ case KEY_DSA_CERT_V00:
+ case KEY_RSA_CERT_V00:
+ case KEY_DSA_CERT:
+ case KEY_ECDSA_CERT:
+ case KEY_RSA_CERT:
+ case KEY_ED25519_CERT:
+ space = strchr(cp, ' ');
+ if (space == NULL)
+ return SSH_ERR_INVALID_FORMAT;
+ *space = '\0';
+ type = sshkey_type_from_name(cp);
+ if (sshkey_type_plain(type) == KEY_ECDSA &&
+ (curve_nid = sshkey_ecdsa_nid_from_name(cp)) == -1)
+ return SSH_ERR_EC_CURVE_INVALID;
+ *space = ' ';
+ if (type == KEY_UNSPEC)
+ return SSH_ERR_INVALID_FORMAT;
+ cp = space+1;
+ if (*cp == '\0')
+ return SSH_ERR_INVALID_FORMAT;
+ if (ret->type == KEY_UNSPEC) {
+ ret->type = type;
+ } else if (ret->type != type)
+ return SSH_ERR_KEY_TYPE_MISMATCH;
+ if ((blob = sshbuf_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ /* trim comment */
+ space = strchr(cp, ' ');
+ if (space)
+ *space = '\0';
+ if ((r = sshbuf_b64tod(blob, cp)) != 0) {
+ sshbuf_free(blob);
+ return r;
+ }
+ if ((r = sshkey_from_blob(sshbuf_ptr(blob),
+ sshbuf_len(blob), &k)) != 0) {
+ sshbuf_free(blob);
+ return r;
+ }
+ sshbuf_free(blob);
+ if (k->type != type) {
+ sshkey_free(k);
+ return SSH_ERR_KEY_TYPE_MISMATCH;
+ }
+ if (sshkey_type_plain(type) == KEY_ECDSA &&
+ curve_nid != k->ecdsa_nid) {
+ sshkey_free(k);
+ return SSH_ERR_EC_CURVE_MISMATCH;
+ }
+/*XXXX*/
+ if (sshkey_is_cert(ret)) {
+ if (!sshkey_is_cert(k)) {
+ sshkey_free(k);
+ return SSH_ERR_EXPECTED_CERT;
+ }
+ if (ret->cert != NULL)
+ cert_free(ret->cert);
+ ret->cert = k->cert;
+ k->cert = NULL;
+ }
+#ifdef WITH_OPENSSL
+ if (sshkey_type_plain(ret->type) == KEY_RSA) {
+ if (ret->rsa != NULL)
+ RSA_free(ret->rsa);
+ ret->rsa = k->rsa;
+ k->rsa = NULL;
+#ifdef DEBUG_PK
+ RSA_print_fp(stderr, ret->rsa, 8);
+#endif
+ }
+ if (sshkey_type_plain(ret->type) == KEY_DSA) {
+ if (ret->dsa != NULL)
+ DSA_free(ret->dsa);
+ ret->dsa = k->dsa;
+ k->dsa = NULL;
+#ifdef DEBUG_PK
+ DSA_print_fp(stderr, ret->dsa, 8);
+#endif
+ }
+# ifdef OPENSSL_HAS_ECC
+ if (sshkey_type_plain(ret->type) == KEY_ECDSA) {
+ if (ret->ecdsa != NULL)
+ EC_KEY_free(ret->ecdsa);
+ ret->ecdsa = k->ecdsa;
+ ret->ecdsa_nid = k->ecdsa_nid;
+ k->ecdsa = NULL;
+ k->ecdsa_nid = -1;
+#ifdef DEBUG_PK
+ sshkey_dump_ec_key(ret->ecdsa);
+#endif
+ }
+# endif /* OPENSSL_HAS_ECC */
+#endif /* WITH_OPENSSL */
+ if (sshkey_type_plain(ret->type) == KEY_ED25519) {
+ free(ret->ed25519_pk);
+ ret->ed25519_pk = k->ed25519_pk;
+ k->ed25519_pk = NULL;
+#ifdef DEBUG_PK
+ /* XXX */
+#endif
+ }
+ retval = 0;
+/*XXXX*/
+ sshkey_free(k);
+ if (retval != 0)
+ break;
+ /* advance cp: skip whitespace and data */
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ while (*cp != '\0' && *cp != ' ' && *cp != '\t')
+ cp++;
+ *cpp = cp;
+ break;
+ default:
+ return SSH_ERR_INVALID_ARGUMENT;
+ }
+ return retval;
+}
+
+int
+sshkey_write(const struct sshkey *key, FILE *f)
+{
+ int ret = SSH_ERR_INTERNAL_ERROR;
+ struct sshbuf *b = NULL, *bb = NULL;
+ char *uu = NULL;
+#ifdef WITH_SSH1
+ u_int bits = 0;
+ char *dec_e = NULL, *dec_n = NULL;
+#endif /* WITH_SSH1 */
+
+ if (sshkey_is_cert(key)) {
+ if (key->cert == NULL)
+ return SSH_ERR_EXPECTED_CERT;
+ if (sshbuf_len(key->cert->certblob) == 0)
+ return SSH_ERR_KEY_LACKS_CERTBLOB;
+ }
+ if ((b = sshbuf_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ switch (key->type) {
+#ifdef WITH_SSH1
+ case KEY_RSA1:
+ if (key->rsa == NULL || key->rsa->e == NULL ||
+ key->rsa->n == NULL) {
+ ret = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if ((dec_e = BN_bn2dec(key->rsa->e)) == NULL ||
+ (dec_n = BN_bn2dec(key->rsa->n)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ /* size of modulus 'n' */
+ if ((bits = BN_num_bits(key->rsa->n)) <= 0) {
+ ret = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if ((ret = sshbuf_putf(b, "%u %s %s", bits, dec_e, dec_n)) != 0)
+ goto out;
+#endif /* WITH_SSH1 */
+ break;
+#ifdef WITH_OPENSSL
+ case KEY_DSA:
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ case KEY_ECDSA:
+ case KEY_ECDSA_CERT:
+ case KEY_RSA:
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ case KEY_ED25519_CERT:
+ if ((bb = sshbuf_new()) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((ret = sshkey_to_blob_buf(key, bb)) != 0)
+ goto out;
+ if ((uu = sshbuf_dtob64(bb)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((ret = sshbuf_putf(b, "%s ", sshkey_ssh_name(key))) != 0)
+ goto out;
+ if ((ret = sshbuf_put(b, uu, strlen(uu))) != 0)
+ goto out;
+ break;
+ default:
+ ret = SSH_ERR_KEY_TYPE_UNKNOWN;
+ goto out;
+ }
+ if (fwrite(sshbuf_ptr(b), sshbuf_len(b), 1, f) != 1) {
+ if (feof(f))
+ errno = EPIPE;
+ ret = SSH_ERR_SYSTEM_ERROR;
+ goto out;
+ }
+ ret = 0;
+ out:
+ if (b != NULL)
+ sshbuf_free(b);
+ if (bb != NULL)
+ sshbuf_free(bb);
+ if (uu != NULL)
+ free(uu);
+#ifdef WITH_SSH1
+ if (dec_e != NULL)
+ OPENSSL_free(dec_e);
+ if (dec_n != NULL)
+ OPENSSL_free(dec_n);
+#endif /* WITH_SSH1 */
+ return ret;
+}
+
+const char *
+sshkey_cert_type(const struct sshkey *k)
+{
+ switch (k->cert->type) {
+ case SSH2_CERT_TYPE_USER:
+ return "user";
+ case SSH2_CERT_TYPE_HOST:
+ return "host";
+ default:
+ return "unknown";
+ }
+}
+
+#ifdef WITH_OPENSSL
+static int
+rsa_generate_private_key(u_int bits, RSA **rsap)
+{
+ RSA *private = NULL;
+ BIGNUM *f4 = NULL;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+
+ if (rsap == NULL ||
+ bits < SSH_RSA_MINIMUM_MODULUS_SIZE ||
+ bits > SSHBUF_MAX_BIGNUM * 8)
+ return SSH_ERR_INVALID_ARGUMENT;
+ *rsap = NULL;
+ if ((private = RSA_new()) == NULL || (f4 = BN_new()) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (!BN_set_word(f4, RSA_F4) ||
+ !RSA_generate_key_ex(private, bits, f4, NULL)) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ *rsap = private;
+ private = NULL;
+ ret = 0;
+ out:
+ if (private != NULL)
+ RSA_free(private);
+ if (f4 != NULL)
+ BN_free(f4);
+ return ret;
+}
+
+static int
+dsa_generate_private_key(u_int bits, DSA **dsap)
+{
+ DSA *private;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+
+ if (dsap == NULL || bits != 1024)
+ return SSH_ERR_INVALID_ARGUMENT;
+ if ((private = DSA_new()) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ *dsap = NULL;
+ if (!DSA_generate_parameters_ex(private, bits, NULL, 0, NULL,
+ NULL, NULL) || !DSA_generate_key(private)) {
+ DSA_free(private);
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ *dsap = private;
+ private = NULL;
+ ret = 0;
+ out:
+ if (private != NULL)
+ DSA_free(private);
+ return ret;
+}
+
+# ifdef OPENSSL_HAS_ECC
+int
+sshkey_ecdsa_key_to_nid(EC_KEY *k)
+{
+ EC_GROUP *eg;
+ int nids[] = {
+ NID_X9_62_prime256v1,
+ NID_secp384r1,
+# ifdef OPENSSL_HAS_NISTP521
+ NID_secp521r1,
+# endif /* OPENSSL_HAS_NISTP521 */
+ -1
+ };
+ int nid;
+ u_int i;
+ BN_CTX *bnctx;
+ const EC_GROUP *g = EC_KEY_get0_group(k);
+
+ /*
+ * The group may be stored in a ASN.1 encoded private key in one of two
+ * ways: as a "named group", which is reconstituted by ASN.1 object ID
+ * or explicit group parameters encoded into the key blob. Only the
+ * "named group" case sets the group NID for us, but we can figure
+ * it out for the other case by comparing against all the groups that
+ * are supported.
+ */
+ if ((nid = EC_GROUP_get_curve_name(g)) > 0)
+ return nid;
+ if ((bnctx = BN_CTX_new()) == NULL)
+ return -1;
+ for (i = 0; nids[i] != -1; i++) {
+ if ((eg = EC_GROUP_new_by_curve_name(nids[i])) == NULL) {
+ BN_CTX_free(bnctx);
+ return -1;
+ }
+ if (EC_GROUP_cmp(g, eg, bnctx) == 0)
+ break;
+ EC_GROUP_free(eg);
+ }
+ BN_CTX_free(bnctx);
+ if (nids[i] != -1) {
+ /* Use the group with the NID attached */
+ EC_GROUP_set_asn1_flag(eg, OPENSSL_EC_NAMED_CURVE);
+ if (EC_KEY_set_group(k, eg) != 1) {
+ EC_GROUP_free(eg);
+ return -1;
+ }
+ }
+ return nids[i];
+}
+
+static int
+ecdsa_generate_private_key(u_int bits, int *nid, EC_KEY **ecdsap)
+{
+ EC_KEY *private;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+
+ if (nid == NULL || ecdsap == NULL ||
+ (*nid = sshkey_ecdsa_bits_to_nid(bits)) == -1)
+ return SSH_ERR_INVALID_ARGUMENT;
+ *ecdsap = NULL;
+ if ((private = EC_KEY_new_by_curve_name(*nid)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (EC_KEY_generate_key(private) != 1) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ EC_KEY_set_asn1_flag(private, OPENSSL_EC_NAMED_CURVE);
+ *ecdsap = private;
+ private = NULL;
+ ret = 0;
+ out:
+ if (private != NULL)
+ EC_KEY_free(private);
+ return ret;
+}
+# endif /* OPENSSL_HAS_ECC */
+#endif /* WITH_OPENSSL */
+
+int
+sshkey_generate(int type, u_int bits, struct sshkey **keyp)
+{
+ struct sshkey *k;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+
+ if (keyp == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+ *keyp = NULL;
+ if ((k = sshkey_new(KEY_UNSPEC)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ switch (type) {
+ case KEY_ED25519:
+ if ((k->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL ||
+ (k->ed25519_sk = malloc(ED25519_SK_SZ)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ break;
+ }
+ crypto_sign_ed25519_keypair(k->ed25519_pk, k->ed25519_sk);
+ ret = 0;
+ break;
+#ifdef WITH_OPENSSL
+ case KEY_DSA:
+ ret = dsa_generate_private_key(bits, &k->dsa);
+ break;
+# ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA:
+ ret = ecdsa_generate_private_key(bits, &k->ecdsa_nid,
+ &k->ecdsa);
+ break;
+# endif /* OPENSSL_HAS_ECC */
+ case KEY_RSA:
+ case KEY_RSA1:
+ ret = rsa_generate_private_key(bits, &k->rsa);
+ break;
+#endif /* WITH_OPENSSL */
+ default:
+ ret = SSH_ERR_INVALID_ARGUMENT;
+ }
+ if (ret == 0) {
+ k->type = type;
+ *keyp = k;
+ } else
+ sshkey_free(k);
+ return ret;
+}
+
+int
+sshkey_cert_copy(const struct sshkey *from_key, struct sshkey *to_key)
+{
+ u_int i;
+ const struct sshkey_cert *from;
+ struct sshkey_cert *to;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+
+ if (to_key->cert != NULL) {
+ cert_free(to_key->cert);
+ to_key->cert = NULL;
+ }
+
+ if ((from = from_key->cert) == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+
+ if ((to = to_key->cert = cert_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+
+ if ((ret = sshbuf_putb(to->certblob, from->certblob)) != 0 ||
+ (ret = sshbuf_putb(to->critical, from->critical)) != 0 ||
+ (ret = sshbuf_putb(to->extensions, from->extensions) != 0))
+ return ret;
+
+ to->serial = from->serial;
+ to->type = from->type;
+ if (from->key_id == NULL)
+ to->key_id = NULL;
+ else if ((to->key_id = strdup(from->key_id)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ to->valid_after = from->valid_after;
+ to->valid_before = from->valid_before;
+ if (from->signature_key == NULL)
+ to->signature_key = NULL;
+ else if ((ret = sshkey_from_private(from->signature_key,
+ &to->signature_key)) != 0)
+ return ret;
+
+ if (from->nprincipals > SSHKEY_CERT_MAX_PRINCIPALS)
+ return SSH_ERR_INVALID_ARGUMENT;
+ if (from->nprincipals > 0) {
+ if ((to->principals = calloc(from->nprincipals,
+ sizeof(*to->principals))) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ for (i = 0; i < from->nprincipals; i++) {
+ to->principals[i] = strdup(from->principals[i]);
+ if (to->principals[i] == NULL) {
+ to->nprincipals = i;
+ return SSH_ERR_ALLOC_FAIL;
+ }
+ }
+ }
+ to->nprincipals = from->nprincipals;
+ return 0;
+}
+
+int
+sshkey_from_private(const struct sshkey *k, struct sshkey **pkp)
+{
+ struct sshkey *n = NULL;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+
+ if (pkp != NULL)
+ *pkp = NULL;
+
+ switch (k->type) {
+#ifdef WITH_OPENSSL
+ case KEY_DSA:
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ if ((n = sshkey_new(k->type)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if ((BN_copy(n->dsa->p, k->dsa->p) == NULL) ||
+ (BN_copy(n->dsa->q, k->dsa->q) == NULL) ||
+ (BN_copy(n->dsa->g, k->dsa->g) == NULL) ||
+ (BN_copy(n->dsa->pub_key, k->dsa->pub_key) == NULL)) {
+ sshkey_free(n);
+ return SSH_ERR_ALLOC_FAIL;
+ }
+ break;
+# ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA:
+ case KEY_ECDSA_CERT:
+ if ((n = sshkey_new(k->type)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ n->ecdsa_nid = k->ecdsa_nid;
+ n->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid);
+ if (n->ecdsa == NULL) {
+ sshkey_free(n);
+ return SSH_ERR_ALLOC_FAIL;
+ }
+ if (EC_KEY_set_public_key(n->ecdsa,
+ EC_KEY_get0_public_key(k->ecdsa)) != 1) {
+ sshkey_free(n);
+ return SSH_ERR_LIBCRYPTO_ERROR;
+ }
+ break;
+# endif /* OPENSSL_HAS_ECC */
+ case KEY_RSA:
+ case KEY_RSA1:
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ if ((n = sshkey_new(k->type)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if ((BN_copy(n->rsa->n, k->rsa->n) == NULL) ||
+ (BN_copy(n->rsa->e, k->rsa->e) == NULL)) {
+ sshkey_free(n);
+ return SSH_ERR_ALLOC_FAIL;
+ }
+ break;
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ case KEY_ED25519_CERT:
+ if ((n = sshkey_new(k->type)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if (k->ed25519_pk != NULL) {
+ if ((n->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
+ sshkey_free(n);
+ return SSH_ERR_ALLOC_FAIL;
+ }
+ memcpy(n->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
+ }
+ break;
+ default:
+ return SSH_ERR_KEY_TYPE_UNKNOWN;
+ }
+ if (sshkey_is_cert(k)) {
+ if ((ret = sshkey_cert_copy(k, n)) != 0) {
+ sshkey_free(n);
+ return ret;
+ }
+ }
+ *pkp = n;
+ return 0;
+}
+
+static int
+cert_parse(struct sshbuf *b, struct sshkey *key, const u_char *blob,
+ size_t blen)
+{
+ u_char *principals = NULL, *critical = NULL, *exts = NULL;
+ u_char *sig_key = NULL, *sig = NULL;
+ size_t signed_len, plen, clen, sklen, slen, kidlen, elen;
+ struct sshbuf *tmp;
+ char *principal;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+ int v00 = sshkey_cert_is_legacy(key);
+ char **oprincipals;
+
+ if ((tmp = sshbuf_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+
+ /* Copy the entire key blob for verification and later serialisation */
+ if ((ret = sshbuf_put(key->cert->certblob, blob, blen)) != 0)
+ return ret;
+
+ elen = 0; /* Not touched for v00 certs */
+ principals = exts = critical = sig_key = sig = NULL;
+ if ((!v00 && (ret = sshbuf_get_u64(b, &key->cert->serial)) != 0) ||
+ (ret = sshbuf_get_u32(b, &key->cert->type)) != 0 ||
+ (ret = sshbuf_get_cstring(b, &key->cert->key_id, &kidlen)) != 0 ||
+ (ret = sshbuf_get_string(b, &principals, &plen)) != 0 ||
+ (ret = sshbuf_get_u64(b, &key->cert->valid_after)) != 0 ||
+ (ret = sshbuf_get_u64(b, &key->cert->valid_before)) != 0 ||
+ (ret = sshbuf_get_string(b, &critical, &clen)) != 0 ||
+ (!v00 && (ret = sshbuf_get_string(b, &exts, &elen)) != 0) ||
+ (v00 && (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0) ||
+ (ret = sshbuf_get_string_direct(b, NULL, NULL)) != 0 ||
+ (ret = sshbuf_get_string(b, &sig_key, &sklen)) != 0) {
+ /* XXX debug print error for ret */
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+
+ /* Signature is left in the buffer so we can calculate this length */
+ signed_len = sshbuf_len(key->cert->certblob) - sshbuf_len(b);
+
+ if ((ret = sshbuf_get_string(b, &sig, &slen)) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+
+ if (key->cert->type != SSH2_CERT_TYPE_USER &&
+ key->cert->type != SSH2_CERT_TYPE_HOST) {
+ ret = SSH_ERR_KEY_CERT_UNKNOWN_TYPE;
+ goto out;
+ }
+
+ if ((ret = sshbuf_put(tmp, principals, plen)) != 0)
+ goto out;
+ while (sshbuf_len(tmp) > 0) {
+ if (key->cert->nprincipals >= SSHKEY_CERT_MAX_PRINCIPALS) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if ((ret = sshbuf_get_cstring(tmp, &principal, &plen)) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ oprincipals = key->cert->principals;
+ key->cert->principals = realloc(key->cert->principals,
+ (key->cert->nprincipals + 1) *
+ sizeof(*key->cert->principals));
+ if (key->cert->principals == NULL) {
+ free(principal);
+ key->cert->principals = oprincipals;
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ key->cert->principals[key->cert->nprincipals++] = principal;
+ }
+
+ sshbuf_reset(tmp);
+
+ if ((ret = sshbuf_put(key->cert->critical, critical, clen)) != 0 ||
+ (ret = sshbuf_put(tmp, critical, clen)) != 0)
+ goto out;
+
+ /* validate structure */
+ while (sshbuf_len(tmp) != 0) {
+ if ((ret = sshbuf_get_string_direct(tmp, NULL, NULL)) != 0 ||
+ (ret = sshbuf_get_string_direct(tmp, NULL, NULL)) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ }
+ sshbuf_reset(tmp);
+
+ if ((ret = sshbuf_put(key->cert->extensions, exts, elen)) != 0 ||
+ (ret = sshbuf_put(tmp, exts, elen)) != 0)
+ goto out;
+
+ /* validate structure */
+ while (sshbuf_len(tmp) != 0) {
+ if ((ret = sshbuf_get_string_direct(tmp, NULL, NULL)) != 0 ||
+ (ret = sshbuf_get_string_direct(tmp, NULL, NULL)) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ }
+ sshbuf_reset(tmp);
+
+ if (sshkey_from_blob_internal(sig_key, sklen,
+ &key->cert->signature_key, 0) != 0) {
+ ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
+ goto out;
+ }
+ if (!sshkey_type_is_valid_ca(key->cert->signature_key->type)) {
+ ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
+ goto out;
+ }
+
+ if ((ret = sshkey_verify(key->cert->signature_key, sig, slen,
+ sshbuf_ptr(key->cert->certblob), signed_len, 0)) != 0)
+ goto out;
+ ret = 0;
+
+ out:
+ sshbuf_free(tmp);
+ free(principals);
+ free(critical);
+ free(exts);
+ free(sig_key);
+ free(sig);
+ return ret;
+}
+
+static int
+sshkey_from_blob_internal(const u_char *blob, size_t blen,
+ struct sshkey **keyp, int allow_cert)
+{
+ struct sshbuf *b = NULL;
+ int type, nid = -1, ret = SSH_ERR_INTERNAL_ERROR;
+ char *ktype = NULL, *curve = NULL;
+ struct sshkey *key = NULL;
+ size_t len;
+ u_char *pk = NULL;
+#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
+ EC_POINT *q = NULL;
+#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
+
+#ifdef DEBUG_PK /* XXX */
+ dump_base64(stderr, blob, blen);
+#endif
+ *keyp = NULL;
+ if ((b = sshbuf_from(blob, blen)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if (sshbuf_get_cstring(b, &ktype, NULL) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+
+ type = sshkey_type_from_name(ktype);
+ if (sshkey_type_plain(type) == KEY_ECDSA)
+ nid = sshkey_ecdsa_nid_from_name(ktype);
+ if (!allow_cert && sshkey_type_is_cert(type)) {
+ ret = SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
+ goto out;
+ }
+ switch (type) {
+#ifdef WITH_OPENSSL
+ case KEY_RSA_CERT:
+ if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ /* FALLTHROUGH */
+ case KEY_RSA:
+ case KEY_RSA_CERT_V00:
+ if ((key = sshkey_new(type)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (sshbuf_get_bignum2(b, key->rsa->e) == -1 ||
+ sshbuf_get_bignum2(b, key->rsa->n) == -1) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+#ifdef DEBUG_PK
+ RSA_print_fp(stderr, key->rsa, 8);
+#endif
+ break;
+ case KEY_DSA_CERT:
+ if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ /* FALLTHROUGH */
+ case KEY_DSA:
+ case KEY_DSA_CERT_V00:
+ if ((key = sshkey_new(type)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (sshbuf_get_bignum2(b, key->dsa->p) == -1 ||
+ sshbuf_get_bignum2(b, key->dsa->q) == -1 ||
+ sshbuf_get_bignum2(b, key->dsa->g) == -1 ||
+ sshbuf_get_bignum2(b, key->dsa->pub_key) == -1) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+#ifdef DEBUG_PK
+ DSA_print_fp(stderr, key->dsa, 8);
+#endif
+ break;
+ case KEY_ECDSA_CERT:
+ if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ /* FALLTHROUGH */
+# ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA:
+ if ((key = sshkey_new(type)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ key->ecdsa_nid = nid;
+ if (sshbuf_get_cstring(b, &curve, NULL) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if (key->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
+ ret = SSH_ERR_EC_CURVE_MISMATCH;
+ goto out;
+ }
+ if (key->ecdsa != NULL)
+ EC_KEY_free(key->ecdsa);
+ if ((key->ecdsa = EC_KEY_new_by_curve_name(key->ecdsa_nid))
+ == NULL) {
+ ret = SSH_ERR_EC_CURVE_INVALID;
+ goto out;
+ }
+ if ((q = EC_POINT_new(EC_KEY_get0_group(key->ecdsa))) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (sshbuf_get_ec(b, q, EC_KEY_get0_group(key->ecdsa)) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if (sshkey_ec_validate_public(EC_KEY_get0_group(key->ecdsa),
+ q) != 0) {
+ ret = SSH_ERR_KEY_INVALID_EC_VALUE;
+ goto out;
+ }
+ if (EC_KEY_set_public_key(key->ecdsa, q) != 1) {
+ /* XXX assume it is a allocation error */
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+#ifdef DEBUG_PK
+ sshkey_dump_ec_point(EC_KEY_get0_group(key->ecdsa), q);
+#endif
+ break;
+# endif /* OPENSSL_HAS_ECC */
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519_CERT:
+ if (sshbuf_get_string_direct(b, NULL, NULL) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ /* FALLTHROUGH */
+ case KEY_ED25519:
+ if ((ret = sshbuf_get_string(b, &pk, &len)) != 0)
+ goto out;
+ if (len != ED25519_PK_SZ) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if ((key = sshkey_new(type)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ key->ed25519_pk = pk;
+ pk = NULL;
+ break;
+ case KEY_UNSPEC:
+ if ((key = sshkey_new(type)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ break;
+ default:
+ ret = SSH_ERR_KEY_TYPE_UNKNOWN;
+ goto out;
+ }
+
+ /* Parse certificate potion */
+ if (sshkey_is_cert(key) &&
+ (ret = cert_parse(b, key, blob, blen)) != 0)
+ goto out;
+
+ if (key != NULL && sshbuf_len(b) != 0) {
+ ret = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ ret = 0;
+ *keyp = key;
+ key = NULL;
+ out:
+ sshbuf_free(b);
+ sshkey_free(key);
+ free(ktype);
+ free(curve);
+ free(pk);
+#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
+ if (q != NULL)
+ EC_POINT_free(q);
+#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
+ return ret;
+}
+
+int
+sshkey_from_blob(const u_char *blob, size_t blen, struct sshkey **keyp)
+{
+ return sshkey_from_blob_internal(blob, blen, keyp, 1);
+}
+
+int
+sshkey_sign(const struct sshkey *key,
+ u_char **sigp, size_t *lenp,
+ const u_char *data, size_t datalen, u_int compat)
+{
+ if (sigp != NULL)
+ *sigp = NULL;
+ if (lenp != NULL)
+ *lenp = 0;
+ if (datalen > SSH_KEY_MAX_SIGN_DATA_SIZE)
+ return SSH_ERR_INVALID_ARGUMENT;
+ switch (key->type) {
+#ifdef WITH_OPENSSL
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ case KEY_DSA:
+ return ssh_dss_sign(key, sigp, lenp, data, datalen, compat);
+# ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA_CERT:
+ case KEY_ECDSA:
+ return ssh_ecdsa_sign(key, sigp, lenp, data, datalen, compat);
+# endif /* OPENSSL_HAS_ECC */
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ case KEY_RSA:
+ return ssh_rsa_sign(key, sigp, lenp, data, datalen, compat);
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ case KEY_ED25519_CERT:
+ return ssh_ed25519_sign(key, sigp, lenp, data, datalen, compat);
+ default:
+ return SSH_ERR_KEY_TYPE_UNKNOWN;
+ }
+}
+
+/*
+ * ssh_key_verify returns 0 for a correct signature and < 0 on error.
+ */
+int
+sshkey_verify(const struct sshkey *key,
+ const u_char *sig, size_t siglen,
+ const u_char *data, size_t dlen, u_int compat)
+{
+ if (siglen == 0)
+ return -1;
+
+ if (dlen > SSH_KEY_MAX_SIGN_DATA_SIZE)
+ return SSH_ERR_INVALID_ARGUMENT;
+ switch (key->type) {
+#ifdef WITH_OPENSSL
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ case KEY_DSA:
+ return ssh_dss_verify(key, sig, siglen, data, dlen, compat);
+# ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA_CERT:
+ case KEY_ECDSA:
+ return ssh_ecdsa_verify(key, sig, siglen, data, dlen, compat);
+# endif /* OPENSSL_HAS_ECC */
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ case KEY_RSA:
+ return ssh_rsa_verify(key, sig, siglen, data, dlen, compat);
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ case KEY_ED25519_CERT:
+ return ssh_ed25519_verify(key, sig, siglen, data, dlen, compat);
+ default:
+ return SSH_ERR_KEY_TYPE_UNKNOWN;
+ }
+}
+
+/* Converts a private to a public key */
+int
+sshkey_demote(const struct sshkey *k, struct sshkey **dkp)
+{
+ struct sshkey *pk;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+
+ if (dkp != NULL)
+ *dkp = NULL;
+
+ if ((pk = calloc(1, sizeof(*pk))) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ pk->type = k->type;
+ pk->flags = k->flags;
+ pk->ecdsa_nid = k->ecdsa_nid;
+ pk->dsa = NULL;
+ pk->ecdsa = NULL;
+ pk->rsa = NULL;
+ pk->ed25519_pk = NULL;
+ pk->ed25519_sk = NULL;
+
+ switch (k->type) {
+#ifdef WITH_OPENSSL
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ if ((ret = sshkey_cert_copy(k, pk)) != 0)
+ goto fail;
+ /* FALLTHROUGH */
+ case KEY_RSA1:
+ case KEY_RSA:
+ if ((pk->rsa = RSA_new()) == NULL ||
+ (pk->rsa->e = BN_dup(k->rsa->e)) == NULL ||
+ (pk->rsa->n = BN_dup(k->rsa->n)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto fail;
+ }
+ break;
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ if ((ret = sshkey_cert_copy(k, pk)) != 0)
+ goto fail;
+ /* FALLTHROUGH */
+ case KEY_DSA:
+ if ((pk->dsa = DSA_new()) == NULL ||
+ (pk->dsa->p = BN_dup(k->dsa->p)) == NULL ||
+ (pk->dsa->q = BN_dup(k->dsa->q)) == NULL ||
+ (pk->dsa->g = BN_dup(k->dsa->g)) == NULL ||
+ (pk->dsa->pub_key = BN_dup(k->dsa->pub_key)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto fail;
+ }
+ break;
+ case KEY_ECDSA_CERT:
+ if ((ret = sshkey_cert_copy(k, pk)) != 0)
+ goto fail;
+ /* FALLTHROUGH */
+# ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA:
+ pk->ecdsa = EC_KEY_new_by_curve_name(pk->ecdsa_nid);
+ if (pk->ecdsa == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto fail;
+ }
+ if (EC_KEY_set_public_key(pk->ecdsa,
+ EC_KEY_get0_public_key(k->ecdsa)) != 1) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto fail;
+ }
+ break;
+# endif /* OPENSSL_HAS_ECC */
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519_CERT:
+ if ((ret = sshkey_cert_copy(k, pk)) != 0)
+ goto fail;
+ /* FALLTHROUGH */
+ case KEY_ED25519:
+ if (k->ed25519_pk != NULL) {
+ if ((pk->ed25519_pk = malloc(ED25519_PK_SZ)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto fail;
+ }
+ memcpy(pk->ed25519_pk, k->ed25519_pk, ED25519_PK_SZ);
+ }
+ break;
+ default:
+ ret = SSH_ERR_KEY_TYPE_UNKNOWN;
+ fail:
+ sshkey_free(pk);
+ return ret;
+ }
+ *dkp = pk;
+ return 0;
+}
+
+/* Convert a plain key to their _CERT equivalent */
+int
+sshkey_to_certified(struct sshkey *k, int legacy)
+{
+ int newtype;
+
+ switch (k->type) {
+#ifdef WITH_OPENSSL
+ case KEY_RSA:
+ newtype = legacy ? KEY_RSA_CERT_V00 : KEY_RSA_CERT;
+ break;
+ case KEY_DSA:
+ newtype = legacy ? KEY_DSA_CERT_V00 : KEY_DSA_CERT;
+ break;
+ case KEY_ECDSA:
+ if (legacy)
+ return SSH_ERR_INVALID_ARGUMENT;
+ newtype = KEY_ECDSA_CERT;
+ break;
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ if (legacy)
+ return SSH_ERR_INVALID_ARGUMENT;
+ newtype = KEY_ED25519_CERT;
+ break;
+ default:
+ return SSH_ERR_INVALID_ARGUMENT;
+ }
+ if ((k->cert = cert_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ k->type = newtype;
+ return 0;
+}
+
+/* Convert a certificate to its raw key equivalent */
+int
+sshkey_drop_cert(struct sshkey *k)
+{
+ if (!sshkey_type_is_cert(k->type))
+ return SSH_ERR_KEY_TYPE_UNKNOWN;
+ cert_free(k->cert);
+ k->cert = NULL;
+ k->type = sshkey_type_plain(k->type);
+ return 0;
+}
+
+/* Sign a certified key, (re-)generating the signed certblob. */
+int
+sshkey_certify(struct sshkey *k, struct sshkey *ca)
+{
+ struct sshbuf *principals = NULL;
+ u_char *ca_blob = NULL, *sig_blob = NULL, nonce[32];
+ size_t i, ca_len, sig_len;
+ int ret = SSH_ERR_INTERNAL_ERROR;
+ struct sshbuf *cert;
+
+ if (k == NULL || k->cert == NULL ||
+ k->cert->certblob == NULL || ca == NULL)
+ return SSH_ERR_INVALID_ARGUMENT;
+ if (!sshkey_is_cert(k))
+ return SSH_ERR_KEY_TYPE_UNKNOWN;
+ if (!sshkey_type_is_valid_ca(ca->type))
+ return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
+
+ if ((ret = sshkey_to_blob(ca, &ca_blob, &ca_len)) != 0)
+ return SSH_ERR_KEY_CERT_INVALID_SIGN_KEY;
+
+ cert = k->cert->certblob; /* for readability */
+ sshbuf_reset(cert);
+ if ((ret = sshbuf_put_cstring(cert, sshkey_ssh_name(k))) != 0)
+ goto out;
+
+ /* -v01 certs put nonce first */
+ arc4random_buf(&nonce, sizeof(nonce));
+ if (!sshkey_cert_is_legacy(k)) {
+ if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0)
+ goto out;
+ }
+
+ /* XXX this substantially duplicates to_blob(); refactor */
+ switch (k->type) {
+#ifdef WITH_OPENSSL
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ if ((ret = sshbuf_put_bignum2(cert, k->dsa->p)) != 0 ||
+ (ret = sshbuf_put_bignum2(cert, k->dsa->q)) != 0 ||
+ (ret = sshbuf_put_bignum2(cert, k->dsa->g)) != 0 ||
+ (ret = sshbuf_put_bignum2(cert, k->dsa->pub_key)) != 0)
+ goto out;
+ break;
+# ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA_CERT:
+ if ((ret = sshbuf_put_cstring(cert,
+ sshkey_curve_nid_to_name(k->ecdsa_nid))) != 0 ||
+ (ret = sshbuf_put_ec(cert,
+ EC_KEY_get0_public_key(k->ecdsa),
+ EC_KEY_get0_group(k->ecdsa))) != 0)
+ goto out;
+ break;
+# endif /* OPENSSL_HAS_ECC */
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ if ((ret = sshbuf_put_bignum2(cert, k->rsa->e)) != 0 ||
+ (ret = sshbuf_put_bignum2(cert, k->rsa->n)) != 0)
+ goto out;
+ break;
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519_CERT:
+ if ((ret = sshbuf_put_string(cert,
+ k->ed25519_pk, ED25519_PK_SZ)) != 0)
+ goto out;
+ break;
+ default:
+ ret = SSH_ERR_INVALID_ARGUMENT;
+ }
+
+ /* -v01 certs have a serial number next */
+ if (!sshkey_cert_is_legacy(k)) {
+ if ((ret = sshbuf_put_u64(cert, k->cert->serial)) != 0)
+ goto out;
+ }
+
+ if ((ret = sshbuf_put_u32(cert, k->cert->type)) != 0 ||
+ (ret = sshbuf_put_cstring(cert, k->cert->key_id)) != 0)
+ goto out;
+
+ if ((principals = sshbuf_new()) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ for (i = 0; i < k->cert->nprincipals; i++) {
+ if ((ret = sshbuf_put_cstring(principals,
+ k->cert->principals[i])) != 0)
+ goto out;
+ }
+ if ((ret = sshbuf_put_stringb(cert, principals)) != 0 ||
+ (ret = sshbuf_put_u64(cert, k->cert->valid_after)) != 0 ||
+ (ret = sshbuf_put_u64(cert, k->cert->valid_before)) != 0 ||
+ (ret = sshbuf_put_stringb(cert, k->cert->critical)) != 0)
+ goto out;
+
+ /* -v01 certs have non-critical options here */
+ if (!sshkey_cert_is_legacy(k)) {
+ if ((ret = sshbuf_put_stringb(cert, k->cert->extensions)) != 0)
+ goto out;
+ }
+
+ /* -v00 certs put the nonce at the end */
+ if (sshkey_cert_is_legacy(k)) {
+ if ((ret = sshbuf_put_string(cert, nonce, sizeof(nonce))) != 0)
+ goto out;
+ }
+
+ if ((ret = sshbuf_put_string(cert, NULL, 0)) != 0 || /* Reserved */
+ (ret = sshbuf_put_string(cert, ca_blob, ca_len)) != 0)
+ goto out;
+
+ /* Sign the whole mess */
+ if ((ret = sshkey_sign(ca, &sig_blob, &sig_len, sshbuf_ptr(cert),
+ sshbuf_len(cert), 0)) != 0)
+ goto out;
+
+ /* Append signature and we are done */
+ if ((ret = sshbuf_put_string(cert, sig_blob, sig_len)) != 0)
+ goto out;
+ ret = 0;
+ out:
+ if (ret != 0)
+ sshbuf_reset(cert);
+ if (sig_blob != NULL)
+ free(sig_blob);
+ if (ca_blob != NULL)
+ free(ca_blob);
+ if (principals != NULL)
+ sshbuf_free(principals);
+ return ret;
+}
+
+int
+sshkey_cert_check_authority(const struct sshkey *k,
+ int want_host, int require_principal,
+ const char *name, const char **reason)
+{
+ u_int i, principal_matches;
+ time_t now = time(NULL);
+
+ if (reason != NULL)
+ *reason = NULL;
+
+ if (want_host) {
+ if (k->cert->type != SSH2_CERT_TYPE_HOST) {
+ *reason = "Certificate invalid: not a host certificate";
+ return SSH_ERR_KEY_CERT_INVALID;
+ }
+ } else {
+ if (k->cert->type != SSH2_CERT_TYPE_USER) {
+ *reason = "Certificate invalid: not a user certificate";
+ return SSH_ERR_KEY_CERT_INVALID;
+ }
+ }
+ if (now < 0) {
+ /* yikes - system clock before epoch! */
+ *reason = "Certificate invalid: not yet valid";
+ return SSH_ERR_KEY_CERT_INVALID;
+ }
+ if ((u_int64_t)now < k->cert->valid_after) {
+ *reason = "Certificate invalid: not yet valid";
+ return SSH_ERR_KEY_CERT_INVALID;
+ }
+ if ((u_int64_t)now >= k->cert->valid_before) {
+ *reason = "Certificate invalid: expired";
+ return SSH_ERR_KEY_CERT_INVALID;
+ }
+ if (k->cert->nprincipals == 0) {
+ if (require_principal) {
+ *reason = "Certificate lacks principal list";
+ return SSH_ERR_KEY_CERT_INVALID;
+ }
+ } else if (name != NULL) {
+ principal_matches = 0;
+ for (i = 0; i < k->cert->nprincipals; i++) {
+ if (strcmp(name, k->cert->principals[i]) == 0) {
+ principal_matches = 1;
+ break;
+ }
+ }
+ if (!principal_matches) {
+ *reason = "Certificate invalid: name is not a listed "
+ "principal";
+ return SSH_ERR_KEY_CERT_INVALID;
+ }
+ }
+ return 0;
+}
+
+int
+sshkey_private_serialize(const struct sshkey *key, struct sshbuf *b)
+{
+ int r = SSH_ERR_INTERNAL_ERROR;
+
+ if ((r = sshbuf_put_cstring(b, sshkey_ssh_name(key))) != 0)
+ goto out;
+ switch (key->type) {
+#ifdef WITH_OPENSSL
+ case KEY_RSA:
+ if ((r = sshbuf_put_bignum2(b, key->rsa->n)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->rsa->e)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0)
+ goto out;
+ break;
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->rsa->d)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->rsa->iqmp)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->rsa->p)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->rsa->q)) != 0)
+ goto out;
+ break;
+ case KEY_DSA:
+ if ((r = sshbuf_put_bignum2(b, key->dsa->p)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->dsa->q)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->dsa->g)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->dsa->pub_key)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0)
+ goto out;
+ break;
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
+ (r = sshbuf_put_bignum2(b, key->dsa->priv_key)) != 0)
+ goto out;
+ break;
+# ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA:
+ if ((r = sshbuf_put_cstring(b,
+ sshkey_curve_nid_to_name(key->ecdsa_nid))) != 0 ||
+ (r = sshbuf_put_eckey(b, key->ecdsa)) != 0 ||
+ (r = sshbuf_put_bignum2(b,
+ EC_KEY_get0_private_key(key->ecdsa))) != 0)
+ goto out;
+ break;
+ case KEY_ECDSA_CERT:
+ if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
+ (r = sshbuf_put_bignum2(b,
+ EC_KEY_get0_private_key(key->ecdsa))) != 0)
+ goto out;
+ break;
+# endif /* OPENSSL_HAS_ECC */
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ if ((r = sshbuf_put_string(b, key->ed25519_pk,
+ ED25519_PK_SZ)) != 0 ||
+ (r = sshbuf_put_string(b, key->ed25519_sk,
+ ED25519_SK_SZ)) != 0)
+ goto out;
+ break;
+ case KEY_ED25519_CERT:
+ if (key->cert == NULL || sshbuf_len(key->cert->certblob) == 0) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if ((r = sshbuf_put_stringb(b, key->cert->certblob)) != 0 ||
+ (r = sshbuf_put_string(b, key->ed25519_pk,
+ ED25519_PK_SZ)) != 0 ||
+ (r = sshbuf_put_string(b, key->ed25519_sk,
+ ED25519_SK_SZ)) != 0)
+ goto out;
+ break;
+ default:
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ /* success */
+ r = 0;
+ out:
+ return r;
+}
+
+int
+sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **kp)
+{
+ char *tname = NULL, *curve = NULL;
+ struct sshkey *k = NULL;
+ const u_char *cert;
+ size_t len, pklen = 0, sklen = 0;
+ int type, r = SSH_ERR_INTERNAL_ERROR;
+ u_char *ed25519_pk = NULL, *ed25519_sk = NULL;
+#ifdef WITH_OPENSSL
+ BIGNUM *exponent = NULL;
+#endif /* WITH_OPENSSL */
+
+ if (kp != NULL)
+ *kp = NULL;
+ if ((r = sshbuf_get_cstring(buf, &tname, NULL)) != 0)
+ goto out;
+ type = sshkey_type_from_name(tname);
+ switch (type) {
+#ifdef WITH_OPENSSL
+ case KEY_DSA:
+ if ((k = sshkey_new_private(type)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((r = sshbuf_get_bignum2(buf, k->dsa->p)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, k->dsa->q)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, k->dsa->g)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, k->dsa->pub_key)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0)
+ goto out;
+ break;
+ case KEY_DSA_CERT_V00:
+ case KEY_DSA_CERT:
+ if ((r = sshbuf_get_string_direct(buf, &cert, &len)) != 0 ||
+ (r = sshkey_from_blob(cert, len, &k)) != 0 ||
+ (r = sshkey_add_private(k)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, k->dsa->priv_key)) != 0)
+ goto out;
+ break;
+# ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA:
+ if ((k = sshkey_new_private(type)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((k->ecdsa_nid = sshkey_ecdsa_nid_from_name(tname)) == -1) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if ((r = sshbuf_get_cstring(buf, &curve, NULL)) != 0)
+ goto out;
+ if (k->ecdsa_nid != sshkey_curve_name_to_nid(curve)) {
+ r = SSH_ERR_EC_CURVE_MISMATCH;
+ goto out;
+ }
+ k->ecdsa = EC_KEY_new_by_curve_name(k->ecdsa_nid);
+ if (k->ecdsa == NULL || (exponent = BN_new()) == NULL) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if ((r = sshbuf_get_eckey(buf, k->ecdsa)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, exponent)))
+ goto out;
+ if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa),
+ EC_KEY_get0_public_key(k->ecdsa)) != 0) ||
+ (r = sshkey_ec_validate_private(k->ecdsa)) != 0)
+ goto out;
+ break;
+ case KEY_ECDSA_CERT:
+ if ((exponent = BN_new()) == NULL) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if ((r = sshbuf_get_string_direct(buf, &cert, &len)) != 0 ||
+ (r = sshkey_from_blob(cert, len, &k)) != 0 ||
+ (r = sshkey_add_private(k)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, exponent)) != 0)
+ goto out;
+ if (EC_KEY_set_private_key(k->ecdsa, exponent) != 1) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if ((r = sshkey_ec_validate_public(EC_KEY_get0_group(k->ecdsa),
+ EC_KEY_get0_public_key(k->ecdsa)) != 0) ||
+ (r = sshkey_ec_validate_private(k->ecdsa)) != 0)
+ goto out;
+ break;
+# endif /* OPENSSL_HAS_ECC */
+ case KEY_RSA:
+ if ((k = sshkey_new_private(type)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((r = sshbuf_get_bignum2(buf, k->rsa->n)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, k->rsa->e)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, k->rsa->d)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, k->rsa->iqmp)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, k->rsa->p)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, k->rsa->q)) != 0 ||
+ (r = rsa_generate_additional_parameters(k->rsa)) != 0)
+ goto out;
+ break;
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ if ((r = sshbuf_get_string_direct(buf, &cert, &len)) != 0 ||
+ (r = sshkey_from_blob(cert, len, &k)) != 0 ||
+ (r = sshkey_add_private(k)) != 0 ||
+ (r = sshbuf_get_bignum2(buf, k->rsa->d) != 0) ||
+ (r = sshbuf_get_bignum2(buf, k->rsa->iqmp) != 0) ||
+ (r = sshbuf_get_bignum2(buf, k->rsa->p) != 0) ||
+ (r = sshbuf_get_bignum2(buf, k->rsa->q) != 0) ||
+ (r = rsa_generate_additional_parameters(k->rsa)) != 0)
+ goto out;
+ break;
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ if ((k = sshkey_new_private(type)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 ||
+ (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0)
+ goto out;
+ if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ k->ed25519_pk = ed25519_pk;
+ k->ed25519_sk = ed25519_sk;
+ ed25519_pk = ed25519_sk = NULL;
+ break;
+ case KEY_ED25519_CERT:
+ if ((r = sshbuf_get_string_direct(buf, &cert, &len)) != 0 ||
+ (r = sshkey_from_blob(cert, len, &k)) != 0 ||
+ (r = sshkey_add_private(k)) != 0 ||
+ (r = sshbuf_get_string(buf, &ed25519_pk, &pklen)) != 0 ||
+ (r = sshbuf_get_string(buf, &ed25519_sk, &sklen)) != 0)
+ goto out;
+ if (pklen != ED25519_PK_SZ || sklen != ED25519_SK_SZ) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ k->ed25519_pk = ed25519_pk;
+ k->ed25519_sk = ed25519_sk;
+ ed25519_pk = ed25519_sk = NULL;
+ break;
+ default:
+ r = SSH_ERR_KEY_TYPE_UNKNOWN;
+ goto out;
+ }
+#ifdef WITH_OPENSSL
+ /* enable blinding */
+ switch (k->type) {
+ case KEY_RSA:
+ case KEY_RSA_CERT_V00:
+ case KEY_RSA_CERT:
+ case KEY_RSA1:
+ if (RSA_blinding_on(k->rsa, NULL) != 1) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ break;
+ }
+#endif /* WITH_OPENSSL */
+ /* success */
+ r = 0;
+ if (kp != NULL) {
+ *kp = k;
+ k = NULL;
+ }
+ out:
+ free(tname);
+ free(curve);
+#ifdef WITH_OPENSSL
+ if (exponent != NULL)
+ BN_clear_free(exponent);
+#endif /* WITH_OPENSSL */
+ sshkey_free(k);
+ if (ed25519_pk != NULL) {
+ explicit_bzero(ed25519_pk, pklen);
+ free(ed25519_pk);
+ }
+ if (ed25519_sk != NULL) {
+ explicit_bzero(ed25519_sk, sklen);
+ free(ed25519_sk);
+ }
+ return r;
+}
+
+#if defined(WITH_OPENSSL) && defined(OPENSSL_HAS_ECC)
+int
+sshkey_ec_validate_public(const EC_GROUP *group, const EC_POINT *public)
+{
+ BN_CTX *bnctx;
+ EC_POINT *nq = NULL;
+ BIGNUM *order, *x, *y, *tmp;
+ int ret = SSH_ERR_KEY_INVALID_EC_VALUE;
+
+ if ((bnctx = BN_CTX_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ BN_CTX_start(bnctx);
+
+ /*
+ * We shouldn't ever hit this case because bignum_get_ecpoint()
+ * refuses to load GF2m points.
+ */
+ if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
+ NID_X9_62_prime_field)
+ goto out;
+
+ /* Q != infinity */
+ if (EC_POINT_is_at_infinity(group, public))
+ goto out;
+
+ if ((x = BN_CTX_get(bnctx)) == NULL ||
+ (y = BN_CTX_get(bnctx)) == NULL ||
+ (order = BN_CTX_get(bnctx)) == NULL ||
+ (tmp = BN_CTX_get(bnctx)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+
+ /* log2(x) > log2(order)/2, log2(y) > log2(order)/2 */
+ if (EC_GROUP_get_order(group, order, bnctx) != 1 ||
+ EC_POINT_get_affine_coordinates_GFp(group, public,
+ x, y, bnctx) != 1) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if (BN_num_bits(x) <= BN_num_bits(order) / 2 ||
+ BN_num_bits(y) <= BN_num_bits(order) / 2)
+ goto out;
+
+ /* nQ == infinity (n == order of subgroup) */
+ if ((nq = EC_POINT_new(group)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (EC_POINT_mul(group, nq, NULL, public, order, bnctx) != 1) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if (EC_POINT_is_at_infinity(group, nq) != 1)
+ goto out;
+
+ /* x < order - 1, y < order - 1 */
+ if (!BN_sub(tmp, order, BN_value_one())) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if (BN_cmp(x, tmp) >= 0 || BN_cmp(y, tmp) >= 0)
+ goto out;
+ ret = 0;
+ out:
+ BN_CTX_free(bnctx);
+ if (nq != NULL)
+ EC_POINT_free(nq);
+ return ret;
+}
+
+int
+sshkey_ec_validate_private(const EC_KEY *key)
+{
+ BN_CTX *bnctx;
+ BIGNUM *order, *tmp;
+ int ret = SSH_ERR_KEY_INVALID_EC_VALUE;
+
+ if ((bnctx = BN_CTX_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ BN_CTX_start(bnctx);
+
+ if ((order = BN_CTX_get(bnctx)) == NULL ||
+ (tmp = BN_CTX_get(bnctx)) == NULL) {
+ ret = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+
+ /* log2(private) > log2(order)/2 */
+ if (EC_GROUP_get_order(EC_KEY_get0_group(key), order, bnctx) != 1) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if (BN_num_bits(EC_KEY_get0_private_key(key)) <=
+ BN_num_bits(order) / 2)
+ goto out;
+
+ /* private < order - 1 */
+ if (!BN_sub(tmp, order, BN_value_one())) {
+ ret = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if (BN_cmp(EC_KEY_get0_private_key(key), tmp) >= 0)
+ goto out;
+ ret = 0;
+ out:
+ BN_CTX_free(bnctx);
+ return ret;
+}
+
+void
+sshkey_dump_ec_point(const EC_GROUP *group, const EC_POINT *point)
+{
+ BIGNUM *x, *y;
+ BN_CTX *bnctx;
+
+ if (point == NULL) {
+ fputs("point=(NULL)\n", stderr);
+ return;
+ }
+ if ((bnctx = BN_CTX_new()) == NULL) {
+ fprintf(stderr, "%s: BN_CTX_new failed\n", __func__);
+ return;
+ }
+ BN_CTX_start(bnctx);
+ if ((x = BN_CTX_get(bnctx)) == NULL ||
+ (y = BN_CTX_get(bnctx)) == NULL) {
+ fprintf(stderr, "%s: BN_CTX_get failed\n", __func__);
+ return;
+ }
+ if (EC_METHOD_get_field_type(EC_GROUP_method_of(group)) !=
+ NID_X9_62_prime_field) {
+ fprintf(stderr, "%s: group is not a prime field\n", __func__);
+ return;
+ }
+ if (EC_POINT_get_affine_coordinates_GFp(group, point, x, y,
+ bnctx) != 1) {
+ fprintf(stderr, "%s: EC_POINT_get_affine_coordinates_GFp\n",
+ __func__);
+ return;
+ }
+ fputs("x=", stderr);
+ BN_print_fp(stderr, x);
+ fputs("\ny=", stderr);
+ BN_print_fp(stderr, y);
+ fputs("\n", stderr);
+ BN_CTX_free(bnctx);
+}
+
+void
+sshkey_dump_ec_key(const EC_KEY *key)
+{
+ const BIGNUM *exponent;
+
+ sshkey_dump_ec_point(EC_KEY_get0_group(key),
+ EC_KEY_get0_public_key(key));
+ fputs("exponent=", stderr);
+ if ((exponent = EC_KEY_get0_private_key(key)) == NULL)
+ fputs("(NULL)", stderr);
+ else
+ BN_print_fp(stderr, EC_KEY_get0_private_key(key));
+ fputs("\n", stderr);
+}
+#endif /* WITH_OPENSSL && OPENSSL_HAS_ECC */
+
+static int
+sshkey_private_to_blob2(const struct sshkey *prv, struct sshbuf *blob,
+ const char *passphrase, const char *comment, const char *ciphername,
+ int rounds)
+{
+ u_char *cp, *b64 = NULL, *key = NULL, *pubkeyblob = NULL;
+ u_char salt[SALT_LEN];
+ size_t i, pubkeylen, keylen, ivlen, blocksize, authlen;
+ u_int check;
+ int r = SSH_ERR_INTERNAL_ERROR;
+ struct sshcipher_ctx ciphercontext;
+ const struct sshcipher *cipher;
+ const char *kdfname = KDFNAME;
+ struct sshbuf *encoded = NULL, *encrypted = NULL, *kdf = NULL;
+
+ memset(&ciphercontext, 0, sizeof(ciphercontext));
+
+ if (rounds <= 0)
+ rounds = DEFAULT_ROUNDS;
+ if (passphrase == NULL || !strlen(passphrase)) {
+ ciphername = "none";
+ kdfname = "none";
+ } else if (ciphername == NULL)
+ ciphername = DEFAULT_CIPHERNAME;
+ else if (cipher_number(ciphername) != SSH_CIPHER_SSH2) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if ((cipher = cipher_by_name(ciphername)) == NULL) {
+ r = SSH_ERR_INTERNAL_ERROR;
+ goto out;
+ }
+
+ if ((kdf = sshbuf_new()) == NULL ||
+ (encoded = sshbuf_new()) == NULL ||
+ (encrypted = sshbuf_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ blocksize = cipher_blocksize(cipher);
+ keylen = cipher_keylen(cipher);
+ ivlen = cipher_ivlen(cipher);
+ authlen = cipher_authlen(cipher);
+ if ((key = calloc(1, keylen + ivlen)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (strcmp(kdfname, "bcrypt") == 0) {
+ arc4random_buf(salt, SALT_LEN);
+ if (bcrypt_pbkdf(passphrase, strlen(passphrase),
+ salt, SALT_LEN, key, keylen + ivlen, rounds) < 0) {
+ r = SSH_ERR_INVALID_ARGUMENT;
+ goto out;
+ }
+ if ((r = sshbuf_put_string(kdf, salt, SALT_LEN)) != 0 ||
+ (r = sshbuf_put_u32(kdf, rounds)) != 0)
+ goto out;
+ } else if (strcmp(kdfname, "none") != 0) {
+ /* Unsupported KDF type */
+ r = SSH_ERR_KEY_UNKNOWN_CIPHER;
+ goto out;
+ }
+ if ((r = cipher_init(&ciphercontext, cipher, key, keylen,
+ key + keylen, ivlen, 1)) != 0)
+ goto out;
+
+ if ((r = sshbuf_put(encoded, AUTH_MAGIC, sizeof(AUTH_MAGIC))) != 0 ||
+ (r = sshbuf_put_cstring(encoded, ciphername)) != 0 ||
+ (r = sshbuf_put_cstring(encoded, kdfname)) != 0 ||
+ (r = sshbuf_put_stringb(encoded, kdf)) != 0 ||
+ (r = sshbuf_put_u32(encoded, 1)) != 0 || /* number of keys */
+ (r = sshkey_to_blob(prv, &pubkeyblob, &pubkeylen)) != 0 ||
+ (r = sshbuf_put_string(encoded, pubkeyblob, pubkeylen)) != 0)
+ goto out;
+
+ /* set up the buffer that will be encrypted */
+
+ /* Random check bytes */
+ check = arc4random();
+ if ((r = sshbuf_put_u32(encrypted, check)) != 0 ||
+ (r = sshbuf_put_u32(encrypted, check)) != 0)
+ goto out;
+
+ /* append private key and comment*/
+ if ((r = sshkey_private_serialize(prv, encrypted)) != 0 ||
+ (r = sshbuf_put_cstring(encrypted, comment)) != 0)
+ goto out;
+
+ /* padding */
+ i = 0;
+ while (sshbuf_len(encrypted) % blocksize) {
+ if ((r = sshbuf_put_u8(encrypted, ++i & 0xff)) != 0)
+ goto out;
+ }
+
+ /* length in destination buffer */
+ if ((r = sshbuf_put_u32(encoded, sshbuf_len(encrypted))) != 0)
+ goto out;
+
+ /* encrypt */
+ if ((r = sshbuf_reserve(encoded,
+ sshbuf_len(encrypted) + authlen, &cp)) != 0)
+ goto out;
+ if ((r = cipher_crypt(&ciphercontext, 0, cp,
+ sshbuf_ptr(encrypted), sshbuf_len(encrypted), 0, authlen)) != 0)
+ goto out;
+
+ /* uuencode */
+ if ((b64 = sshbuf_dtob64(encoded)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+
+ sshbuf_reset(blob);
+ if ((r = sshbuf_put(blob, MARK_BEGIN, MARK_BEGIN_LEN)) != 0)
+ goto out;
+ for (i = 0; i < strlen(b64); i++) {
+ if ((r = sshbuf_put_u8(blob, b64[i])) != 0)
+ goto out;
+ /* insert line breaks */
+ if (i % 70 == 69 && (r = sshbuf_put_u8(blob, '\n')) != 0)
+ goto out;
+ }
+ if (i % 70 != 69 && (r = sshbuf_put_u8(blob, '\n')) != 0)
+ goto out;
+ if ((r = sshbuf_put(blob, MARK_END, MARK_END_LEN)) != 0)
+ goto out;
+
+ /* success */
+ r = 0;
+
+ out:
+ sshbuf_free(kdf);
+ sshbuf_free(encoded);
+ sshbuf_free(encrypted);
+ cipher_cleanup(&ciphercontext);
+ explicit_bzero(salt, sizeof(salt));
+ if (key != NULL) {
+ explicit_bzero(key, keylen + ivlen);
+ free(key);
+ }
+ if (pubkeyblob != NULL) {
+ explicit_bzero(pubkeyblob, pubkeylen);
+ free(pubkeyblob);
+ }
+ if (b64 != NULL) {
+ explicit_bzero(b64, strlen(b64));
+ free(b64);
+ }
+ return r;
+}
+
+static int
+sshkey_parse_private2(struct sshbuf *blob, int type, const char *passphrase,
+ struct sshkey **keyp, char **commentp)
+{
+ char *comment = NULL, *ciphername = NULL, *kdfname = NULL;
+ const struct sshcipher *cipher = NULL;
+ const u_char *cp;
+ int r = SSH_ERR_INTERNAL_ERROR;
+ size_t encoded_len;
+ size_t i, keylen = 0, ivlen = 0, slen = 0;
+ struct sshbuf *encoded = NULL, *decoded = NULL;
+ struct sshbuf *kdf = NULL, *decrypted = NULL;
+ struct sshcipher_ctx ciphercontext;
+ struct sshkey *k = NULL;
+ u_char *key = NULL, *salt = NULL, *dp, pad, last;
+ u_int blocksize, rounds, nkeys, encrypted_len, check1, check2;
+
+ memset(&ciphercontext, 0, sizeof(ciphercontext));
+ if (keyp != NULL)
+ *keyp = NULL;
+ if (commentp != NULL)
+ *commentp = NULL;
+
+ if ((encoded = sshbuf_new()) == NULL ||
+ (decoded = sshbuf_new()) == NULL ||
+ (decrypted = sshbuf_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+
+ /* check preamble */
+ cp = sshbuf_ptr(blob);
+ encoded_len = sshbuf_len(blob);
+ if (encoded_len < (MARK_BEGIN_LEN + MARK_END_LEN) ||
+ memcmp(cp, MARK_BEGIN, MARK_BEGIN_LEN) != 0) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ cp += MARK_BEGIN_LEN;
+ encoded_len -= MARK_BEGIN_LEN;
+
+ /* Look for end marker, removing whitespace as we go */
+ while (encoded_len > 0) {
+ if (*cp != '\n' && *cp != '\r') {
+ if ((r = sshbuf_put_u8(encoded, *cp)) != 0)
+ goto out;
+ }
+ last = *cp;
+ encoded_len--;
+ cp++;
+ if (last == '\n') {
+ if (encoded_len >= MARK_END_LEN &&
+ memcmp(cp, MARK_END, MARK_END_LEN) == 0) {
+ /* \0 terminate */
+ if ((r = sshbuf_put_u8(encoded, 0)) != 0)
+ goto out;
+ break;
+ }
+ }
+ }
+ if (encoded_len == 0) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+
+ /* decode base64 */
+ if ((r = sshbuf_b64tod(decoded, sshbuf_ptr(encoded))) != 0)
+ goto out;
+
+ /* check magic */
+ if (sshbuf_len(decoded) < sizeof(AUTH_MAGIC) ||
+ memcmp(sshbuf_ptr(decoded), AUTH_MAGIC, sizeof(AUTH_MAGIC))) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ /* parse public portion of key */
+ if ((r = sshbuf_consume(decoded, sizeof(AUTH_MAGIC))) != 0 ||
+ (r = sshbuf_get_cstring(decoded, &ciphername, NULL)) != 0 ||
+ (r = sshbuf_get_cstring(decoded, &kdfname, NULL)) != 0 ||
+ (r = sshbuf_froms(decoded, &kdf)) != 0 ||
+ (r = sshbuf_get_u32(decoded, &nkeys)) != 0 ||
+ (r = sshbuf_skip_string(decoded)) != 0 || /* pubkey */
+ (r = sshbuf_get_u32(decoded, &encrypted_len)) != 0)
+ goto out;
+
+ if ((cipher = cipher_by_name(ciphername)) == NULL) {
+ r = SSH_ERR_KEY_UNKNOWN_CIPHER;
+ goto out;
+ }
+ if ((passphrase == NULL || strlen(passphrase) == 0) &&
+ strcmp(ciphername, "none") != 0) {
+ /* passphrase required */
+ r = SSH_ERR_KEY_WRONG_PASSPHRASE;
+ goto out;
+ }
+ if (strcmp(kdfname, "none") != 0 && strcmp(kdfname, "bcrypt") != 0) {
+ r = SSH_ERR_KEY_UNKNOWN_CIPHER;
+ goto out;
+ }
+ if (!strcmp(kdfname, "none") && strcmp(ciphername, "none") != 0) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if (nkeys != 1) {
+ /* XXX only one key supported */
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+
+ /* check size of encrypted key blob */
+ blocksize = cipher_blocksize(cipher);
+ if (encrypted_len < blocksize || (encrypted_len % blocksize) != 0) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+
+ /* setup key */
+ keylen = cipher_keylen(cipher);
+ ivlen = cipher_ivlen(cipher);
+ if ((key = calloc(1, keylen + ivlen)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if (strcmp(kdfname, "bcrypt") == 0) {
+ if ((r = sshbuf_get_string(kdf, &salt, &slen)) != 0 ||
+ (r = sshbuf_get_u32(kdf, &rounds)) != 0)
+ goto out;
+ if (bcrypt_pbkdf(passphrase, strlen(passphrase), salt, slen,
+ key, keylen + ivlen, rounds) < 0) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ }
+
+ /* decrypt private portion of key */
+ if ((r = sshbuf_reserve(decrypted, encrypted_len, &dp)) != 0 ||
+ (r = cipher_init(&ciphercontext, cipher, key, keylen,
+ key + keylen, ivlen, 0)) != 0)
+ goto out;
+ if ((r = cipher_crypt(&ciphercontext, 0, dp, sshbuf_ptr(decoded),
+ sshbuf_len(decoded), 0, cipher_authlen(cipher))) != 0) {
+ /* an integrity error here indicates an incorrect passphrase */
+ if (r == SSH_ERR_MAC_INVALID)
+ r = SSH_ERR_KEY_WRONG_PASSPHRASE;
+ goto out;
+ }
+ if ((r = sshbuf_consume(decoded, encrypted_len)) != 0)
+ goto out;
+ /* there should be no trailing data */
+ if (sshbuf_len(decoded) != 0) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+
+ /* check check bytes */
+ if ((r = sshbuf_get_u32(decrypted, &check1)) != 0 ||
+ (r = sshbuf_get_u32(decrypted, &check2)) != 0)
+ goto out;
+ if (check1 != check2) {
+ r = SSH_ERR_KEY_WRONG_PASSPHRASE;
+ goto out;
+ }
+
+ /* Load the private key and comment */
+ if ((r = sshkey_private_deserialize(decrypted, &k)) != 0 ||
+ (r = sshbuf_get_cstring(decrypted, &comment, NULL)) != 0)
+ goto out;
+
+ /* Check deterministic padding */
+ i = 0;
+ while (sshbuf_len(decrypted)) {
+ if ((r = sshbuf_get_u8(decrypted, &pad)) != 0)
+ goto out;
+ if (pad != (++i & 0xff)) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ }
+
+ /* XXX decode pubkey and check against private */
+
+ /* success */
+ r = 0;
+ if (keyp != NULL) {
+ *keyp = k;
+ k = NULL;
+ }
+ if (commentp != NULL) {
+ *commentp = comment;
+ comment = NULL;
+ }
+ out:
+ pad = 0;
+ cipher_cleanup(&ciphercontext);
+ free(ciphername);
+ free(kdfname);
+ free(comment);
+ if (salt != NULL) {
+ explicit_bzero(salt, slen);
+ free(salt);
+ }
+ if (key != NULL) {
+ explicit_bzero(key, keylen + ivlen);
+ free(key);
+ }
+ sshbuf_free(encoded);
+ sshbuf_free(decoded);
+ sshbuf_free(kdf);
+ sshbuf_free(decrypted);
+ sshkey_free(k);
+ return r;
+}
+
+#if WITH_SSH1
+/*
+ * Serialises the authentication (private) key to a blob, encrypting it with
+ * passphrase. The identification of the blob (lowest 64 bits of n) will
+ * precede the key to provide identification of the key without needing a
+ * passphrase.
+ */
+static int
+sshkey_private_rsa1_to_blob(struct sshkey *key, struct sshbuf *blob,
+ const char *passphrase, const char *comment)
+{
+ struct sshbuf *buffer = NULL, *encrypted = NULL;
+ u_char buf[8];
+ int r, cipher_num;
+ struct sshcipher_ctx ciphercontext;
+ const struct sshcipher *cipher;
+ u_char *cp;
+
+ /*
+ * If the passphrase is empty, use SSH_CIPHER_NONE to ease converting
+ * to another cipher; otherwise use SSH_AUTHFILE_CIPHER.
+ */
+ cipher_num = (strcmp(passphrase, "") == 0) ?
+ SSH_CIPHER_NONE : SSH_CIPHER_3DES;
+ if ((cipher = cipher_by_number(cipher_num)) == NULL)
+ return SSH_ERR_INTERNAL_ERROR;
+
+ /* This buffer is used to build the secret part of the private key. */
+ if ((buffer = sshbuf_new()) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+
+ /* Put checkbytes for checking passphrase validity. */
+ if ((r = sshbuf_reserve(buffer, 4, &cp)) != 0)
+ goto out;
+ arc4random_buf(cp, 2);
+ memcpy(cp + 2, cp, 2);
+
+ /*
+ * Store the private key (n and e will not be stored because they
+ * will be stored in plain text, and storing them also in encrypted
+ * format would just give known plaintext).
+ * Note: q and p are stored in reverse order to SSL.
+ */
+ if ((r = sshbuf_put_bignum1(buffer, key->rsa->d)) != 0 ||
+ (r = sshbuf_put_bignum1(buffer, key->rsa->iqmp)) != 0 ||
+ (r = sshbuf_put_bignum1(buffer, key->rsa->q)) != 0 ||
+ (r = sshbuf_put_bignum1(buffer, key->rsa->p)) != 0)
+ goto out;
+
+ /* Pad the part to be encrypted to a size that is a multiple of 8. */
+ explicit_bzero(buf, 8);
+ if ((r = sshbuf_put(buffer, buf, 8 - (sshbuf_len(buffer) % 8))) != 0)
+ goto out;
+
+ /* This buffer will be used to contain the data in the file. */
+ if ((encrypted = sshbuf_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+
+ /* First store keyfile id string. */
+ if ((r = sshbuf_put(encrypted, LEGACY_BEGIN,
+ sizeof(LEGACY_BEGIN))) != 0)
+ goto out;
+
+ /* Store cipher type and "reserved" field. */
+ if ((r = sshbuf_put_u8(encrypted, cipher_num)) != 0 ||
+ (r = sshbuf_put_u32(encrypted, 0)) != 0)
+ goto out;
+
+ /* Store public key. This will be in plain text. */
+ if ((r = sshbuf_put_u32(encrypted, BN_num_bits(key->rsa->n))) != 0 ||
+ (r = sshbuf_put_bignum1(encrypted, key->rsa->n) != 0) ||
+ (r = sshbuf_put_bignum1(encrypted, key->rsa->e) != 0) ||
+ (r = sshbuf_put_cstring(encrypted, comment) != 0))
+ goto out;
+
+ /* Allocate space for the private part of the key in the buffer. */
+ if ((r = sshbuf_reserve(encrypted, sshbuf_len(buffer), &cp)) != 0)
+ goto out;
+
+ if ((r = cipher_set_key_string(&ciphercontext, cipher, passphrase,
+ CIPHER_ENCRYPT)) != 0)
+ goto out;
+ if ((r = cipher_crypt(&ciphercontext, 0, cp,
+ sshbuf_ptr(buffer), sshbuf_len(buffer), 0, 0)) != 0)
+ goto out;
+ if ((r = cipher_cleanup(&ciphercontext)) != 0)
+ goto out;
+
+ r = sshbuf_putb(blob, encrypted);
+
+ out:
+ explicit_bzero(&ciphercontext, sizeof(ciphercontext));
+ explicit_bzero(buf, sizeof(buf));
+ if (buffer != NULL)
+ sshbuf_free(buffer);
+ if (encrypted != NULL)
+ sshbuf_free(encrypted);
+
+ return r;
+}
+#endif /* WITH_SSH1 */
+
+#ifdef WITH_OPENSSL
+/* convert SSH v2 key in OpenSSL PEM format */
+static int
+sshkey_private_pem_to_blob(struct sshkey *key, struct sshbuf *blob,
+ const char *_passphrase, const char *comment)
+{
+ int success, r;
+ int blen, len = strlen(_passphrase);
+ u_char *passphrase = (len > 0) ? (u_char *)_passphrase : NULL;
+#if (OPENSSL_VERSION_NUMBER < 0x00907000L)
+ const EVP_CIPHER *cipher = (len > 0) ? EVP_des_ede3_cbc() : NULL;
+#else
+ const EVP_CIPHER *cipher = (len > 0) ? EVP_aes_128_cbc() : NULL;
+#endif
+ const u_char *bptr;
+ BIO *bio = NULL;
+
+ if (len > 0 && len <= 4)
+ return SSH_ERR_PASSPHRASE_TOO_SHORT;
+ if ((bio = BIO_new(BIO_s_mem())) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+
+ switch (key->type) {
+ case KEY_DSA:
+ success = PEM_write_bio_DSAPrivateKey(bio, key->dsa,
+ cipher, passphrase, len, NULL, NULL);
+ break;
+#ifdef OPENSSL_HAS_ECC
+ case KEY_ECDSA:
+ success = PEM_write_bio_ECPrivateKey(bio, key->ecdsa,
+ cipher, passphrase, len, NULL, NULL);
+ break;
+#endif
+ case KEY_RSA:
+ success = PEM_write_bio_RSAPrivateKey(bio, key->rsa,
+ cipher, passphrase, len, NULL, NULL);
+ break;
+ default:
+ success = 0;
+ break;
+ }
+ if (success == 0) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ if ((blen = BIO_get_mem_data(bio, &bptr)) <= 0) {
+ r = SSH_ERR_INTERNAL_ERROR;
+ goto out;
+ }
+ if ((r = sshbuf_put(blob, bptr, blen)) != 0)
+ goto out;
+ r = 0;
+ out:
+ BIO_free(bio);
+ return r;
+}
+#endif /* WITH_OPENSSL */
+
+/* Serialise "key" to buffer "blob" */
+int
+sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob,
+ const char *passphrase, const char *comment,
+ int force_new_format, const char *new_format_cipher, int new_format_rounds)
+{
+ switch (key->type) {
+#ifdef WITH_OPENSSL
+ case KEY_RSA1:
+ return sshkey_private_rsa1_to_blob(key, blob,
+ passphrase, comment);
+ case KEY_DSA:
+ case KEY_ECDSA:
+ case KEY_RSA:
+ if (force_new_format) {
+ return sshkey_private_to_blob2(key, blob, passphrase,
+ comment, new_format_cipher, new_format_rounds);
+ }
+ return sshkey_private_pem_to_blob(key, blob,
+ passphrase, comment);
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ return sshkey_private_to_blob2(key, blob, passphrase,
+ comment, new_format_cipher, new_format_rounds);
+ default:
+ return SSH_ERR_KEY_TYPE_UNKNOWN;
+ }
+}
+
+#ifdef WITH_SSH1
+/*
+ * Parse the public, unencrypted portion of a RSA1 key.
+ */
+int
+sshkey_parse_public_rsa1_fileblob(struct sshbuf *blob,
+ struct sshkey **keyp, char **commentp)
+{
+ int r;
+ struct sshkey *pub = NULL;
+ struct sshbuf *copy = NULL;
+
+ if (keyp != NULL)
+ *keyp = NULL;
+ if (commentp != NULL)
+ *commentp = NULL;
+
+ /* Check that it is at least big enough to contain the ID string. */
+ if (sshbuf_len(blob) < sizeof(LEGACY_BEGIN))
+ return SSH_ERR_INVALID_FORMAT;
+
+ /*
+ * Make sure it begins with the id string. Consume the id string
+ * from the buffer.
+ */
+ if (memcmp(sshbuf_ptr(blob), LEGACY_BEGIN, sizeof(LEGACY_BEGIN)) != 0)
+ return SSH_ERR_INVALID_FORMAT;
+ /* Make a working copy of the keyblob and skip past the magic */
+ if ((copy = sshbuf_fromb(blob)) == NULL)
+ return SSH_ERR_ALLOC_FAIL;
+ if ((r = sshbuf_consume(copy, sizeof(LEGACY_BEGIN))) != 0)
+ goto out;
+
+ /* Skip cipher type, reserved data and key bits. */
+ if ((r = sshbuf_get_u8(copy, NULL)) != 0 || /* cipher type */
+ (r = sshbuf_get_u32(copy, NULL)) != 0 || /* reserved */
+ (r = sshbuf_get_u32(copy, NULL)) != 0) /* key bits */
+ goto out;
+
+ /* Read the public key from the buffer. */
+ if ((pub = sshkey_new(KEY_RSA1)) == NULL ||
+ (r = sshbuf_get_bignum1(copy, pub->rsa->n)) != 0 ||
+ (r = sshbuf_get_bignum1(copy, pub->rsa->e)) != 0)
+ goto out;
+
+ /* Finally, the comment */
+ if ((r = sshbuf_get_string(copy, (u_char**)commentp, NULL)) != 0)
+ goto out;
+
+ /* The encrypted private part is not parsed by this function. */
+
+ r = 0;
+ if (keyp != NULL)
+ *keyp = pub;
+ else
+ sshkey_free(pub);
+ pub = NULL;
+
+ out:
+ if (copy != NULL)
+ sshbuf_free(copy);
+ if (pub != NULL)
+ sshkey_free(pub);
+ return r;
+}
+
+static int
+sshkey_parse_private_rsa1(struct sshbuf *blob, const char *passphrase,
+ struct sshkey **keyp, char **commentp)
+{
+ int r;
+ u_int16_t check1, check2;
+ u_int8_t cipher_type;
+ struct sshbuf *decrypted = NULL, *copy = NULL;
+ u_char *cp;
+ char *comment = NULL;
+ struct sshcipher_ctx ciphercontext;
+ const struct sshcipher *cipher;
+ struct sshkey *prv = NULL;
+
+ *keyp = NULL;
+ if (commentp != NULL)
+ *commentp = NULL;
+
+ /* Check that it is at least big enough to contain the ID string. */
+ if (sshbuf_len(blob) < sizeof(LEGACY_BEGIN))
+ return SSH_ERR_INVALID_FORMAT;
+
+ /*
+ * Make sure it begins with the id string. Consume the id string
+ * from the buffer.
+ */
+ if (memcmp(sshbuf_ptr(blob), LEGACY_BEGIN, sizeof(LEGACY_BEGIN)) != 0)
+ return SSH_ERR_INVALID_FORMAT;
+
+ if ((prv = sshkey_new_private(KEY_RSA1)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((copy = sshbuf_fromb(blob)) == NULL ||
+ (decrypted = sshbuf_new()) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ if ((r = sshbuf_consume(copy, sizeof(LEGACY_BEGIN))) != 0)
+ goto out;
+
+ /* Read cipher type. */
+ if ((r = sshbuf_get_u8(copy, &cipher_type)) != 0 ||
+ (r = sshbuf_get_u32(copy, NULL)) != 0) /* reserved */
+ goto out;
+
+ /* Read the public key and comment from the buffer. */
+ if ((r = sshbuf_get_u32(copy, NULL)) != 0 || /* key bits */
+ (r = sshbuf_get_bignum1(copy, prv->rsa->n)) != 0 ||
+ (r = sshbuf_get_bignum1(copy, prv->rsa->e)) != 0 ||
+ (r = sshbuf_get_cstring(copy, &comment, NULL)) != 0)
+ goto out;
+
+ /* Check that it is a supported cipher. */
+ cipher = cipher_by_number(cipher_type);
+ if (cipher == NULL) {
+ r = SSH_ERR_KEY_UNKNOWN_CIPHER;
+ goto out;
+ }
+ /* Initialize space for decrypted data. */
+ if ((r = sshbuf_reserve(decrypted, sshbuf_len(copy), &cp)) != 0)
+ goto out;
+
+ /* Rest of the buffer is encrypted. Decrypt it using the passphrase. */
+ if ((r = cipher_set_key_string(&ciphercontext, cipher, passphrase,
+ CIPHER_DECRYPT)) != 0)
+ goto out;
+ if ((r = cipher_crypt(&ciphercontext, 0, cp,
+ sshbuf_ptr(copy), sshbuf_len(copy), 0, 0)) != 0) {
+ cipher_cleanup(&ciphercontext);
+ goto out;
+ }
+ if ((r = cipher_cleanup(&ciphercontext)) != 0)
+ goto out;
+
+ if ((r = sshbuf_get_u16(decrypted, &check1)) != 0 ||
+ (r = sshbuf_get_u16(decrypted, &check2)) != 0)
+ goto out;
+ if (check1 != check2) {
+ r = SSH_ERR_KEY_WRONG_PASSPHRASE;
+ goto out;
+ }
+
+ /* Read the rest of the private key. */
+ if ((r = sshbuf_get_bignum1(decrypted, prv->rsa->d)) != 0 ||
+ (r = sshbuf_get_bignum1(decrypted, prv->rsa->iqmp)) != 0 ||
+ (r = sshbuf_get_bignum1(decrypted, prv->rsa->q)) != 0 ||
+ (r = sshbuf_get_bignum1(decrypted, prv->rsa->p)) != 0)
+ goto out;
+
+ /* calculate p-1 and q-1 */
+ if ((r = rsa_generate_additional_parameters(prv->rsa)) != 0)
+ goto out;
+
+ /* enable blinding */
+ if (RSA_blinding_on(prv->rsa, NULL) != 1) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ r = 0;
+ *keyp = prv;
+ prv = NULL;
+ if (commentp != NULL) {
+ *commentp = comment;
+ comment = NULL;
+ }
+ out:
+ explicit_bzero(&ciphercontext, sizeof(ciphercontext));
+ if (comment != NULL)
+ free(comment);
+ if (prv != NULL)
+ sshkey_free(prv);
+ if (copy != NULL)
+ sshbuf_free(copy);
+ if (decrypted != NULL)
+ sshbuf_free(decrypted);
+ return r;
+}
+#endif /* WITH_SSH1 */
+
+#ifdef WITH_OPENSSL
+/* XXX make private once ssh-keysign.c fixed */
+int
+sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
+ const char *passphrase, struct sshkey **keyp, char **commentp)
+{
+ EVP_PKEY *pk = NULL;
+ struct sshkey *prv = NULL;
+ char *name = "<no key>";
+ BIO *bio = NULL;
+ int r;
+
+ *keyp = NULL;
+ if (commentp != NULL)
+ *commentp = NULL;
+
+ if ((bio = BIO_new(BIO_s_mem())) == NULL || sshbuf_len(blob) > INT_MAX)
+ return SSH_ERR_ALLOC_FAIL;
+ if (BIO_write(bio, sshbuf_ptr(blob), sshbuf_len(blob)) !=
+ (int)sshbuf_len(blob)) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+
+ if ((pk = PEM_read_bio_PrivateKey(bio, NULL, NULL,
+ (char *)passphrase)) == NULL) {
+ r = SSH_ERR_KEY_WRONG_PASSPHRASE;
+ goto out;
+ }
+ if (pk->type == EVP_PKEY_RSA &&
+ (type == KEY_UNSPEC || type == KEY_RSA)) {
+ if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ prv->rsa = EVP_PKEY_get1_RSA(pk);
+ prv->type = KEY_RSA;
+ name = "rsa w/o comment";
+#ifdef DEBUG_PK
+ RSA_print_fp(stderr, prv->rsa, 8);
+#endif
+ if (RSA_blinding_on(prv->rsa, NULL) != 1) {
+ r = SSH_ERR_LIBCRYPTO_ERROR;
+ goto out;
+ }
+ } else if (pk->type == EVP_PKEY_DSA &&
+ (type == KEY_UNSPEC || type == KEY_DSA)) {
+ if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ prv->dsa = EVP_PKEY_get1_DSA(pk);
+ prv->type = KEY_DSA;
+ name = "dsa w/o comment";
+#ifdef DEBUG_PK
+ DSA_print_fp(stderr, prv->dsa, 8);
+#endif
+#ifdef OPENSSL_HAS_ECC
+ } else if (pk->type == EVP_PKEY_EC &&
+ (type == KEY_UNSPEC || type == KEY_ECDSA)) {
+ if ((prv = sshkey_new(KEY_UNSPEC)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ prv->ecdsa = EVP_PKEY_get1_EC_KEY(pk);
+ prv->type = KEY_ECDSA;
+ prv->ecdsa_nid = sshkey_ecdsa_key_to_nid(prv->ecdsa);
+ if (prv->ecdsa_nid == -1 ||
+ sshkey_curve_nid_to_name(prv->ecdsa_nid) == NULL ||
+ sshkey_ec_validate_public(EC_KEY_get0_group(prv->ecdsa),
+ EC_KEY_get0_public_key(prv->ecdsa)) != 0 ||
+ sshkey_ec_validate_private(prv->ecdsa) != 0) {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ name = "ecdsa w/o comment";
+# ifdef DEBUG_PK
+ if (prv != NULL && prv->ecdsa != NULL)
+ sshkey_dump_ec_key(prv->ecdsa);
+# endif
+#endif /* OPENSSL_HAS_ECC */
+ } else {
+ r = SSH_ERR_INVALID_FORMAT;
+ goto out;
+ }
+ if (commentp != NULL &&
+ (*commentp = strdup(name)) == NULL) {
+ r = SSH_ERR_ALLOC_FAIL;
+ goto out;
+ }
+ r = 0;
+ *keyp = prv;
+ prv = NULL;
+ out:
+ BIO_free(bio);
+ if (pk != NULL)
+ EVP_PKEY_free(pk);
+ if (prv != NULL)
+ sshkey_free(prv);
+ return r;
+}
+#endif /* WITH_OPENSSL */
+
+int
+sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
+ const char *passphrase, struct sshkey **keyp, char **commentp)
+{
+ int r;
+
+ *keyp = NULL;
+ if (commentp != NULL)
+ *commentp = NULL;
+
+ switch (type) {
+#ifdef WITH_OPENSSL
+ case KEY_RSA1:
+ return sshkey_parse_private_rsa1(blob, passphrase,
+ keyp, commentp);
+ case KEY_DSA:
+ case KEY_ECDSA:
+ case KEY_RSA:
+ return sshkey_parse_private_pem_fileblob(blob, type, passphrase,
+ keyp, commentp);
+#endif /* WITH_OPENSSL */
+ case KEY_ED25519:
+ return sshkey_parse_private2(blob, type, passphrase,
+ keyp, commentp);
+ case KEY_UNSPEC:
+ if ((r = sshkey_parse_private2(blob, type, passphrase, keyp,
+ commentp)) == 0)
+ return 0;
+#ifdef WITH_OPENSSL
+ return sshkey_parse_private_pem_fileblob(blob, type, passphrase,
+ keyp, commentp);
+#else
+ return SSH_ERR_INVALID_FORMAT;
+#endif /* WITH_OPENSSL */
+ default:
+ return SSH_ERR_KEY_TYPE_UNKNOWN;
+ }
+}
+
+int
+sshkey_parse_private_fileblob(struct sshbuf *buffer, const char *passphrase,
+ const char *filename, struct sshkey **keyp, char **commentp)
+{
+ int r;
+
+ if (keyp != NULL)
+ *keyp = NULL;
+ if (commentp != NULL)
+ *commentp = NULL;
+
+#ifdef WITH_SSH1
+ /* it's a SSH v1 key if the public key part is readable */
+ if ((r = sshkey_parse_public_rsa1_fileblob(buffer, NULL, NULL)) == 0) {
+ return sshkey_parse_private_fileblob_type(buffer, KEY_RSA1,
+ passphrase, keyp, commentp);
+ }
+#endif /* WITH_SSH1 */
+ if ((r = sshkey_parse_private_fileblob_type(buffer, KEY_UNSPEC,
+ passphrase, keyp, commentp)) == 0)
+ return 0;
+ return r;
+}
diff --git a/sshkey.h b/sshkey.h
new file mode 100644
index 00000000..4127db24
--- /dev/null
+++ b/sshkey.h
@@ -0,0 +1,222 @@
+/* $OpenBSD: sshkey.h,v 1.1 2014/06/24 01:16:58 djm Exp $ */
+
+/*
+ * Copyright (c) 2000, 2001 Markus Friedl. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef SSHKEY_H
+#define SSHKEY_H
+
+#include <sys/types.h>
+
+#ifdef WITH_OPENSSL
+#include <openssl/rsa.h>
+#include <openssl/dsa.h>
+#include <openssl/ec.h>
+#else /* OPENSSL */
+#define RSA void
+#define DSA void
+#define EC_KEY void
+#define EC_GROUP void
+#define EC_POINT void
+#endif /* WITH_OPENSSL */
+
+#define SSH_RSA_MINIMUM_MODULUS_SIZE 768
+#define SSH_KEY_MAX_SIGN_DATA_SIZE (1 << 20)
+
+struct sshbuf;
+
+/* Key types */
+enum sshkey_types {
+ KEY_RSA1,
+ KEY_RSA,
+ KEY_DSA,
+ KEY_ECDSA,
+ KEY_ED25519,
+ KEY_RSA_CERT,
+ KEY_DSA_CERT,
+ KEY_ECDSA_CERT,
+ KEY_ED25519_CERT,
+ KEY_RSA_CERT_V00,
+ KEY_DSA_CERT_V00,
+ KEY_UNSPEC
+};
+
+/* Fingerprint hash algorithms */
+enum sshkey_fp_type {
+ SSH_FP_SHA1,
+ SSH_FP_MD5,
+ SSH_FP_SHA256
+};
+
+/* Fingerprint representation formats */
+enum sshkey_fp_rep {
+ SSH_FP_HEX,
+ SSH_FP_BUBBLEBABBLE,
+ SSH_FP_RANDOMART
+};
+
+/* key is stored in external hardware */
+#define SSHKEY_FLAG_EXT 0x0001
+
+#define SSHKEY_CERT_MAX_PRINCIPALS 256
+/* XXX opaquify? */
+struct sshkey_cert {
+ struct sshbuf *certblob; /* Kept around for use on wire */
+ u_int type; /* SSH2_CERT_TYPE_USER or SSH2_CERT_TYPE_HOST */
+ u_int64_t serial;
+ char *key_id;
+ u_int nprincipals;
+ char **principals;
+ u_int64_t valid_after, valid_before;
+ struct sshbuf *critical;
+ struct sshbuf *extensions;
+ struct sshkey *signature_key;
+};
+
+/* XXX opaquify? */
+struct sshkey {
+ int type;
+ int flags;
+ RSA *rsa;
+ DSA *dsa;
+ int ecdsa_nid; /* NID of curve */
+ EC_KEY *ecdsa;
+ u_char *ed25519_sk;
+ u_char *ed25519_pk;
+ struct sshkey_cert *cert;
+};
+
+#define ED25519_SK_SZ crypto_sign_ed25519_SECRETKEYBYTES
+#define ED25519_PK_SZ crypto_sign_ed25519_PUBLICKEYBYTES
+
+struct sshkey *sshkey_new(int);
+int sshkey_add_private(struct sshkey *);
+struct sshkey *sshkey_new_private(int);
+void sshkey_free(struct sshkey *);
+int sshkey_demote(const struct sshkey *, struct sshkey **);
+int sshkey_equal_public(const struct sshkey *,
+ const struct sshkey *);
+int sshkey_equal(const struct sshkey *, const struct sshkey *);
+char *sshkey_fingerprint(const struct sshkey *,
+ enum sshkey_fp_type, enum sshkey_fp_rep);
+int sshkey_fingerprint_raw(const struct sshkey *k,
+ enum sshkey_fp_type dgst_type, u_char **retp, size_t *lenp);
+const char *sshkey_type(const struct sshkey *);
+const char *sshkey_cert_type(const struct sshkey *);
+int sshkey_write(const struct sshkey *, FILE *);
+int sshkey_read(struct sshkey *, char **);
+u_int sshkey_size(const struct sshkey *);
+
+int sshkey_generate(int type, u_int bits, struct sshkey **keyp);
+int sshkey_from_private(const struct sshkey *, struct sshkey **);
+int sshkey_type_from_name(const char *);
+int sshkey_is_cert(const struct sshkey *);
+int sshkey_type_is_cert(int);
+int sshkey_type_plain(int);
+int sshkey_to_certified(struct sshkey *, int);
+int sshkey_drop_cert(struct sshkey *);
+int sshkey_certify(struct sshkey *, struct sshkey *);
+int sshkey_cert_copy(const struct sshkey *, struct sshkey *);
+int sshkey_cert_check_authority(const struct sshkey *, int, int,
+ const char *, const char **);
+int sshkey_cert_is_legacy(const struct sshkey *);
+
+int sshkey_ecdsa_nid_from_name(const char *);
+int sshkey_curve_name_to_nid(const char *);
+const char * sshkey_curve_nid_to_name(int);
+u_int sshkey_curve_nid_to_bits(int);
+int sshkey_ecdsa_bits_to_nid(int);
+int sshkey_ecdsa_key_to_nid(EC_KEY *);
+int sshkey_ec_nid_to_hash_alg(int nid);
+int sshkey_ec_validate_public(const EC_GROUP *, const EC_POINT *);
+int sshkey_ec_validate_private(const EC_KEY *);
+const char *sshkey_ssh_name(const struct sshkey *);
+const char *sshkey_ssh_name_plain(const struct sshkey *);
+int sshkey_names_valid2(const char *);
+char *key_alg_list(int, int);
+
+int sshkey_from_blob(const u_char *, size_t, struct sshkey **);
+int sshkey_to_blob_buf(const struct sshkey *, struct sshbuf *);
+int sshkey_to_blob(const struct sshkey *, u_char **, size_t *);
+int sshkey_plain_to_blob_buf(const struct sshkey *, struct sshbuf *);
+int sshkey_plain_to_blob(const struct sshkey *, u_char **, size_t *);
+
+int sshkey_sign(const struct sshkey *, u_char **, size_t *,
+ const u_char *, size_t, u_int);
+int sshkey_verify(const struct sshkey *, const u_char *, size_t,
+ const u_char *, size_t, u_int);
+
+/* for debug */
+void sshkey_dump_ec_point(const EC_GROUP *, const EC_POINT *);
+void sshkey_dump_ec_key(const EC_KEY *);
+
+/* private key parsing and serialisation */
+int sshkey_private_serialize(const struct sshkey *key, struct sshbuf *buf);
+int sshkey_private_deserialize(struct sshbuf *buf, struct sshkey **keyp);
+
+/* private key file format parsing and serialisation */
+int sshkey_private_to_fileblob(struct sshkey *key, struct sshbuf *blob,
+ const char *passphrase, const char *comment,
+ int force_new_format, const char *new_format_cipher, int new_format_rounds);
+int sshkey_parse_public_rsa1_fileblob(struct sshbuf *blob,
+ struct sshkey **keyp, char **commentp);
+int sshkey_parse_private_pem_fileblob(struct sshbuf *blob, int type,
+ const char *passphrase, struct sshkey **keyp, char **commentp);
+int sshkey_parse_private_fileblob(struct sshbuf *buffer,
+ const char *passphrase, const char *filename, struct sshkey **keyp,
+ char **commentp);
+int sshkey_parse_private_fileblob_type(struct sshbuf *blob, int type,
+ const char *passphrase, struct sshkey **keyp, char **commentp);
+
+#ifdef SSHKEY_INTERNAL
+int ssh_rsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
+ const u_char *data, size_t datalen, u_int compat);
+int ssh_rsa_verify(const struct sshkey *key,
+ const u_char *signature, size_t signaturelen,
+ const u_char *data, size_t datalen, u_int compat);
+int ssh_dss_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
+ const u_char *data, size_t datalen, u_int compat);
+int ssh_dss_verify(const struct sshkey *key,
+ const u_char *signature, size_t signaturelen,
+ const u_char *data, size_t datalen, u_int compat);
+int ssh_ecdsa_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
+ const u_char *data, size_t datalen, u_int compat);
+int ssh_ecdsa_verify(const struct sshkey *key,
+ const u_char *signature, size_t signaturelen,
+ const u_char *data, size_t datalen, u_int compat);
+int ssh_ed25519_sign(const struct sshkey *key, u_char **sigp, size_t *lenp,
+ const u_char *data, size_t datalen, u_int compat);
+int ssh_ed25519_verify(const struct sshkey *key,
+ const u_char *signature, size_t signaturelen,
+ const u_char *data, size_t datalen, u_int compat);
+#endif
+
+#ifndef WITH_OPENSSL
+#undef RSA
+#undef DSA
+#undef EC_KEY
+#undef EC_GROUP
+#undef EC_POINT
+#endif /* WITH_OPENSSL */
+
+#endif /* SSHKEY_H */