summaryrefslogtreecommitdiff
path: root/usr.bin
diff options
context:
space:
mode:
authorMarkus Friedl <markus@cvs.openbsd.org>2013-12-06 13:34:55 +0000
committerMarkus Friedl <markus@cvs.openbsd.org>2013-12-06 13:34:55 +0000
commit4b5b44f5080b9b0bd2e297236bde454a7b1d32aa (patch)
tree8e9c7092e995869014cbc24e064734070c560ff3 /usr.bin
parenta0a0b0e41189828b1c5f55581821bdde4cc693b1 (diff)
new private key format, bcrypt as KDF by default; details in PROTOCOL.key;
feedback and lots help from djm; ok djm@
Diffstat (limited to 'usr.bin')
-rw-r--r--usr.bin/ssh/Makefile.inc4
-rw-r--r--usr.bin/ssh/PROTOCOL.key68
-rw-r--r--usr.bin/ssh/authfile.c371
-rw-r--r--usr.bin/ssh/authfile.h5
-rw-r--r--usr.bin/ssh/cipher.c18
-rw-r--r--usr.bin/ssh/cipher.h4
-rw-r--r--usr.bin/ssh/key.c3
-rw-r--r--usr.bin/ssh/packet.c27
-rw-r--r--usr.bin/ssh/ssh-agent.c3
-rw-r--r--usr.bin/ssh/ssh-keygen.c51
10 files changed, 503 insertions, 51 deletions
diff --git a/usr.bin/ssh/Makefile.inc b/usr.bin/ssh/Makefile.inc
index 3d2090a3306..850de544203 100644
--- a/usr.bin/ssh/Makefile.inc
+++ b/usr.bin/ssh/Makefile.inc
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile.inc,v 1.39 2010/10/01 23:10:48 djm Exp $
+# $OpenBSD: Makefile.inc,v 1.40 2013/12/06 13:34:54 markus Exp $
CFLAGS+= -I${.CURDIR}/..
@@ -32,3 +32,5 @@ DPADD+= ${.CURDIR}/../lib/${__objdir}/libssh.a
LDADD+= -L${.CURDIR}/../lib -lssh
DPADD+= ${.CURDIR}/../lib/libssh.a
.endif
+
+LDADD+= -lutil
diff --git a/usr.bin/ssh/PROTOCOL.key b/usr.bin/ssh/PROTOCOL.key
new file mode 100644
index 00000000000..959bd7aeec2
--- /dev/null
+++ b/usr.bin/ssh/PROTOCOL.key
@@ -0,0 +1,68 @@
+This document describes the private key format for OpenSSH.
+
+1. Overall format
+
+The key consists of a header, a list of public keys, and
+an encrypted list of matching private keys.
+
+#define AUTH_MAGIC "openssh-key-v1"
+
+ byte[] AUTH_MAGIC
+ string ciphername
+ string kdfname
+ string kdfoptions
+ int number of keys N
+ string publickey1
+ string publickey2
+ ...
+ string publickeyN
+ string encrypted, padded list of private keys
+
+2. KDF options for kdfname "bcrypt"
+
+The options:
+
+ string salt
+ uint32 rounds
+
+are concatenated and represented as a string.
+
+3. Unencrypted list of N private keys
+
+The list of privatekey/comment pairs is padded with the
+bytes 1, 2, 3, ... until the total length is a multiple
+of the cipher block size.
+
+ uint32 checkint
+ uint32 checkint
+ string privatekey1
+ string comment1
+ string privatekey2
+ string comment2
+ ...
+ string privatekeyN
+ string commentN
+ char 1
+ char 2
+ char 3
+ ...
+ char padlen % 255
+
+Before the key is encrypted, a random integer is assigned
+to both checkint fields so successful decryption can be
+quickly checked by verifying that both checkint fields
+hold the same value.
+
+4. Encryption
+
+The KDF is used to derive a key, IV (and other values required by
+the cipher) from the passphrase. These values are then used to
+encrypt the unencrypted list of private keys.
+
+5. No encryption
+
+For unencrypted keys the cipher "none" and the KDF "none"
+are used with empty passphrases. The options if the KDF "none"
+are the empty string.
+
+$OpenBSD: PROTOCOL.key,v 1.1 2013/12/06 13:34:54 markus Exp $
diff --git a/usr.bin/ssh/authfile.c b/usr.bin/ssh/authfile.c
index 56241bf2801..c712653bf2c 100644
--- a/usr.bin/ssh/authfile.c
+++ b/usr.bin/ssh/authfile.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: authfile.c,v 1.98 2013/11/21 00:45:43 djm Exp $ */
+/* $OpenBSD: authfile.c,v 1.99 2013/12/06 13:34:54 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -13,7 +13,7 @@
* called by a name other than "ssh" or "Secure Shell".
*
*
- * Copyright (c) 2000 Markus Friedl. All rights reserved.
+ * Copyright (c) 2000, 2013 Markus Friedl. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -53,6 +53,8 @@
#include <string.h>
#include <unistd.h>
+#include <util.h>
+
#include "xmalloc.h"
#include "cipher.h"
#include "buffer.h"
@@ -63,6 +65,16 @@
#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
#define MAX_KEY_FILE_SIZE (1024 * 1024)
@@ -70,6 +82,333 @@
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);
+ memset(key, 0, 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);
+
+ memset(cp, 0, 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, *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") && !strcmp(kdfname, "bcrypt"))) {
+ 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)
+ memset(key, 0, keylen + ivlen);
+ free(key);
+ buffer_free(&encoded);
+ buffer_free(&copy);
+ buffer_free(&kdf);
+ buffer_free(&b);
+ return k;
+}
+
/*
* Serialises the authentication (private) key to a blob, encrypting it with
* passphrase. The identification of the blob (lowest 64 bits of n) will
@@ -144,8 +483,9 @@ key_private_rsa1_to_blob(Key *key, Buffer *blob, const char *passphrase,
cipher_set_key_string(&ciphercontext, cipher, passphrase,
CIPHER_ENCRYPT);
- cipher_crypt(&ciphercontext, 0, cp,
- buffer_ptr(&buffer), buffer_len(&buffer), 0, 0);
+ if (cipher_crypt(&ciphercontext, 0, cp,
+ buffer_ptr(&buffer), buffer_len(&buffer), 0, 0) != 0)
+ fatal("%s: cipher_crypt failed", __func__);
cipher_cleanup(&ciphercontext);
memset(&ciphercontext, 0, sizeof(ciphercontext));
@@ -228,7 +568,8 @@ key_save_private_blob(Buffer *keybuf, const char *filename)
/* Serialise "key" to buffer "blob" */
static int
key_private_to_blob(Key *key, Buffer *blob, const char *passphrase,
- const char *comment)
+ const char *comment, int force_new_format, const char *new_format_cipher,
+ int new_format_rounds)
{
switch (key->type) {
case KEY_RSA1:
@@ -236,6 +577,10 @@ key_private_to_blob(Key *key, Buffer *blob, const char *passphrase,
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);
default:
error("%s: cannot save key type %d", __func__, key->type);
@@ -245,13 +590,15 @@ key_private_to_blob(Key *key, Buffer *blob, const char *passphrase,
int
key_save_private(Key *key, const char *filename, const char *passphrase,
- const char *comment)
+ const char *comment, int force_new_format, const char *new_format_cipher,
+ int new_format_rounds)
{
Buffer keyblob;
int success = 0;
buffer_init(&keyblob);
- if (!key_private_to_blob(key, &keyblob, passphrase, comment))
+ if (!key_private_to_blob(key, &keyblob, passphrase, comment,
+ force_new_format, new_format_cipher, new_format_rounds))
goto out;
if (!key_save_private_blob(&keyblob, filename))
goto out;
@@ -462,8 +809,9 @@ key_parse_private_rsa1(Buffer *blob, const char *passphrase, char **commentp)
/* Rest of the buffer is encrypted. Decrypt it using the passphrase. */
cipher_set_key_string(&ciphercontext, cipher, passphrase,
CIPHER_DECRYPT);
- cipher_crypt(&ciphercontext, 0, cp,
- buffer_ptr(&copy), buffer_len(&copy), 0, 0);
+ if (cipher_crypt(&ciphercontext, 0, cp,
+ buffer_ptr(&copy), buffer_len(&copy), 0, 0) != 0)
+ fatal("%s: cipher_crypt failed", __func__);
cipher_cleanup(&ciphercontext);
memset(&ciphercontext, 0, sizeof(ciphercontext));
buffer_free(&copy);
@@ -625,6 +973,8 @@ static Key *
key_parse_private_type(Buffer *blob, int type, const char *passphrase,
char **commentp)
{
+ Key *k;
+
switch (type) {
case KEY_RSA1:
return key_parse_private_rsa1(blob, passphrase, commentp);
@@ -632,6 +982,8 @@ key_parse_private_type(Buffer *blob, int type, const char *passphrase,
case KEY_ECDSA:
case KEY_RSA:
case KEY_UNSPEC:
+ if ((k = key_parse_private2(blob, type, passphrase, commentp)))
+ return k;
return key_parse_private_pem(blob, type, passphrase, commentp);
default:
error("%s: cannot parse key type %d", __func__, type);
@@ -927,4 +1279,3 @@ key_in_file(Key *key, const char *filename, int strict_type)
fclose(f);
return ret;
}
-
diff --git a/usr.bin/ssh/authfile.h b/usr.bin/ssh/authfile.h
index 78349beb561..8ba1c2dbe5f 100644
--- a/usr.bin/ssh/authfile.h
+++ b/usr.bin/ssh/authfile.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: authfile.h,v 1.16 2011/05/04 21:15:29 djm Exp $ */
+/* $OpenBSD: authfile.h,v 1.17 2013/12/06 13:34:54 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -15,7 +15,8 @@
#ifndef AUTHFILE_H
#define AUTHFILE_H
-int key_save_private(Key *, const char *, const char *, const char *);
+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 **);
diff --git a/usr.bin/ssh/cipher.c b/usr.bin/ssh/cipher.c
index 67fc8ba898c..bfab3cea393 100644
--- a/usr.bin/ssh/cipher.c
+++ b/usr.bin/ssh/cipher.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cipher.c,v 1.92 2013/12/02 03:13:14 djm Exp $ */
+/* $OpenBSD: cipher.c,v 1.93 2013/12/06 13:34:54 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -324,17 +324,16 @@ 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.
*/
-void
+int
cipher_crypt(CipherContext *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) {
- if (chachapoly_crypt(&cc->cp_ctx, seqnr, dest, src, len, aadlen,
- authlen, cc->encrypt) != 0)
- fatal("Decryption integrity check failed");
- return;
- }
+ if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0)
+ return chachapoly_crypt(&cc->cp_ctx, seqnr, dest, src, len,
+ aadlen, authlen, cc->encrypt);
if (authlen) {
u_char lastiv[1];
@@ -367,13 +366,14 @@ cipher_crypt(CipherContext *cc, u_int seqnr, u_char *dest, const u_char *src,
if (cc->encrypt)
fatal("%s: EVP_Cipher(final) failed", __func__);
else
- fatal("Decryption integrity check failed");
+ return -1;
}
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 0;
}
/* Extract the packet length, including any decryption necessary beforehand */
diff --git a/usr.bin/ssh/cipher.h b/usr.bin/ssh/cipher.h
index 4e837a75412..d782456154b 100644
--- a/usr.bin/ssh/cipher.h
+++ b/usr.bin/ssh/cipher.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: cipher.h,v 1.42 2013/11/21 00:45:44 djm Exp $ */
+/* $OpenBSD: cipher.h,v 1.43 2013/12/06 13:34:54 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
@@ -81,7 +81,7 @@ 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);
-void cipher_crypt(CipherContext *, u_int, u_char *, const u_char *,
+int cipher_crypt(CipherContext *, u_int, u_char *, const u_char *,
u_int, u_int, u_int);
int cipher_get_length(CipherContext *, u_int *, u_int,
const u_char *, u_int);
diff --git a/usr.bin/ssh/key.c b/usr.bin/ssh/key.c
index aab342745d2..17bf1636189 100644
--- a/usr.bin/ssh/key.c
+++ b/usr.bin/ssh/key.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: key.c,v 1.107 2013/12/06 13:30:08 markus Exp $ */
+/* $OpenBSD: key.c,v 1.108 2013/12/06 13:34:54 markus Exp $ */
/*
* read_bignum():
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -1828,6 +1828,7 @@ key_certify(Key *k, Key *ca)
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) {
case KEY_DSA_CERT_V00:
case KEY_DSA_CERT:
diff --git a/usr.bin/ssh/packet.c b/usr.bin/ssh/packet.c
index e8614966c84..bdf567fbaec 100644
--- a/usr.bin/ssh/packet.c
+++ b/usr.bin/ssh/packet.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.c,v 1.190 2013/11/21 00:45:44 djm Exp $ */
+/* $OpenBSD: packet.c,v 1.191 2013/12/06 13:34:54 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -702,9 +702,10 @@ packet_send1(void)
buffer_append(&active_state->output, buf, 4);
cp = buffer_append_space(&active_state->output,
buffer_len(&active_state->outgoing_packet));
- cipher_crypt(&active_state->send_context, 0, cp,
+ if (cipher_crypt(&active_state->send_context, 0, cp,
buffer_ptr(&active_state->outgoing_packet),
- buffer_len(&active_state->outgoing_packet), 0, 0);
+ buffer_len(&active_state->outgoing_packet), 0, 0) != 0)
+ fatal("%s: cipher_crypt failed", __func__);
#ifdef PACKET_DEBUG
fprintf(stderr, "encrypted: ");
@@ -935,9 +936,10 @@ packet_send2_wrapped(void)
}
/* encrypt packet and append to output buffer. */
cp = buffer_append_space(&active_state->output, len + authlen);
- cipher_crypt(&active_state->send_context, active_state->p_send.seqnr,
+ if (cipher_crypt(&active_state->send_context, active_state->p_send.seqnr,
cp, buffer_ptr(&active_state->outgoing_packet),
- len - aadlen, aadlen, authlen);
+ len - aadlen, aadlen, authlen) != 0)
+ fatal("%s: cipher_crypt failed", __func__);
/* append unencrypted MAC */
if (mac && mac->enabled) {
if (mac->etm) {
@@ -1196,8 +1198,9 @@ packet_read_poll1(void)
/* Decrypt data to incoming_packet. */
buffer_clear(&active_state->incoming_packet);
cp = buffer_append_space(&active_state->incoming_packet, padded_len);
- cipher_crypt(&active_state->receive_context, 0, cp,
- buffer_ptr(&active_state->input), padded_len, 0, 0);
+ if (cipher_crypt(&active_state->receive_context, 0, cp,
+ buffer_ptr(&active_state->input), padded_len, 0, 0) != 0)
+ fatal("%s: cipher_crypt failed", __func__);
buffer_consume(&active_state->input, padded_len);
@@ -1292,9 +1295,10 @@ packet_read_poll2(u_int32_t *seqnr_p)
buffer_clear(&active_state->incoming_packet);
cp = buffer_append_space(&active_state->incoming_packet,
block_size);
- cipher_crypt(&active_state->receive_context,
+ if (cipher_crypt(&active_state->receive_context,
active_state->p_read.seqnr, cp,
- buffer_ptr(&active_state->input), block_size, 0, 0);
+ buffer_ptr(&active_state->input), block_size, 0, 0) != 0)
+ fatal("Decryption integrity check failed");
cp = buffer_ptr(&active_state->incoming_packet);
active_state->packlen = get_u32(cp);
if (active_state->packlen < 1 + 4 ||
@@ -1348,9 +1352,10 @@ packet_read_poll2(u_int32_t *seqnr_p)
macbuf = mac_compute(mac, active_state->p_read.seqnr,
buffer_ptr(&active_state->input), aadlen + need);
cp = buffer_append_space(&active_state->incoming_packet, aadlen + need);
- cipher_crypt(&active_state->receive_context,
+ if (cipher_crypt(&active_state->receive_context,
active_state->p_read.seqnr, cp,
- buffer_ptr(&active_state->input), need, aadlen, authlen);
+ buffer_ptr(&active_state->input), need, aadlen, authlen) != 0)
+ fatal("Decryption integrity check failed");
buffer_consume(&active_state->input, aadlen + need + authlen);
/*
* compute MAC over seqnr and packet,
diff --git a/usr.bin/ssh/ssh-agent.c b/usr.bin/ssh/ssh-agent.c
index 35756c8987a..a5ae47f40c9 100644
--- a/usr.bin/ssh/ssh-agent.c
+++ b/usr.bin/ssh/ssh-agent.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-agent.c,v 1.178 2013/12/06 13:30:08 markus Exp $ */
+/* $OpenBSD: ssh-agent.c,v 1.179 2013/12/06 13:34:54 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -470,7 +470,6 @@ process_add_identity(SocketEntry *e, int version)
/* Generate additional parameters */
rsa_generate_additional_parameters(k->rsa);
- /* enable blinding */
if (RSA_blinding_on(k->rsa, NULL) != 1) {
error("process_add_identity: RSA_blinding_on failed");
key_free(k);
diff --git a/usr.bin/ssh/ssh-keygen.c b/usr.bin/ssh/ssh-keygen.c
index 8c7a41f1efe..763be142c6b 100644
--- a/usr.bin/ssh/ssh-keygen.c
+++ b/usr.bin/ssh/ssh-keygen.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssh-keygen.c,v 1.236 2013/12/06 03:40:51 djm Exp $ */
+/* $OpenBSD: ssh-keygen.c,v 1.237 2013/12/06 13:34:54 markus Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1994 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -145,6 +145,18 @@ char *key_type_name = NULL;
/* Load key from this PKCS#11 provider */
char *pkcs11provider = NULL;
+/* Use new OpenSSH private key format when writing SSH2 keys instead of PEM */
+int use_new_format = 0;
+
+/* Cipher for new-format private keys */
+char *new_format_cipher = NULL;
+
+/*
+ * Number of KDF rounds to derive new format keys /
+ * number of primality trials when screening moduli.
+ */
+int rounds = 0;
+
/* argv0 */
extern char *__progname;
@@ -908,7 +920,8 @@ do_gen_all_hostkeys(struct passwd *pw)
public = key_from_private(private);
snprintf(comment, sizeof comment, "%s@%s", pw->pw_name,
hostname);
- if (!key_save_private(private, identity_file, "", comment)) {
+ if (!key_save_private(private, identity_file, "", comment,
+ use_new_format, new_format_cipher, rounds)) {
printf("Saving the key failed: %s.\n", identity_file);
key_free(private);
key_free(public);
@@ -1260,7 +1273,8 @@ do_change_passphrase(struct passwd *pw)
}
/* Save the file using the new passphrase. */
- if (!key_save_private(private, identity_file, passphrase1, comment)) {
+ if (!key_save_private(private, identity_file, passphrase1, comment,
+ use_new_format, new_format_cipher, rounds)) {
printf("Saving the key failed: %s.\n", identity_file);
memset(passphrase1, 0, strlen(passphrase1));
free(passphrase1);
@@ -1370,7 +1384,8 @@ do_change_comment(struct passwd *pw)
}
/* Save the file using the new passphrase. */
- if (!key_save_private(private, identity_file, passphrase, new_comment)) {
+ if (!key_save_private(private, identity_file, passphrase, new_comment,
+ use_new_format, new_format_cipher, rounds)) {
printf("Saving the key failed: %s.\n", identity_file);
memset(passphrase, 0, strlen(passphrase));
free(passphrase);
@@ -2117,7 +2132,7 @@ usage(void)
fprintf(stderr, "usage: %s [options]\n", __progname);
fprintf(stderr, "Options:\n");
fprintf(stderr, " -A Generate non-existent host keys for all key types.\n");
- fprintf(stderr, " -a trials Number of trials for screening DH-GEX moduli.\n");
+ fprintf(stderr, " -a number Number of KDF rounds for new key format or moduli primality tests.\n");
fprintf(stderr, " -B Show bubblebabble digest of key file.\n");
fprintf(stderr, " -b bits Number of bits in the key to create.\n");
fprintf(stderr, " -C comment Provide new comment.\n");
@@ -2145,6 +2160,7 @@ usage(void)
fprintf(stderr, " -N phrase Provide new passphrase.\n");
fprintf(stderr, " -n name,... User/host principal names to include in certificate\n");
fprintf(stderr, " -O option Specify a certificate option.\n");
+ fprintf(stderr, " -o Enforce new private key format.\n");
fprintf(stderr, " -P phrase Provide old passphrase.\n");
fprintf(stderr, " -p Change passphrase of private key file.\n");
fprintf(stderr, " -Q Test whether key(s) are revoked in KRL.\n");
@@ -2161,6 +2177,7 @@ usage(void)
fprintf(stderr, " -W gen Generator to use for generating DH-GEX moduli.\n");
fprintf(stderr, " -y Read private key file and print public key.\n");
fprintf(stderr, " -z serial Specify a serial number.\n");
+ fprintf(stderr, " -Z cipher Specify a cipher for new private key format.\n");
exit(1);
}
@@ -2178,7 +2195,7 @@ main(int argc, char **argv)
struct passwd *pw;
struct stat st;
int opt, type, fd;
- u_int32_t memory = 0, generator_wanted = 0, trials = 100;
+ u_int32_t memory = 0, generator_wanted = 0;
int do_gen_candidates = 0, do_screen_candidates = 0;
int gen_all_hostkeys = 0, gen_krl = 0, update_krl = 0, check_krl = 0;
unsigned long start_lineno = 0, lines_to_process = 0;
@@ -2206,9 +2223,9 @@ main(int argc, char **argv)
exit(1);
}
- /* Remaining characters: EUYZdow */
- while ((opt = getopt(argc, argv, "ABHLQXceghiklpquvxy"
- "C:D:F:G:I:J:K:M:N:O:P:R:S:T:V:W:a:b:f:j:m:n:r:s:t:z:")) != -1) {
+ /* Remaining characters: EUYdw */
+ while ((opt = getopt(argc, argv, "ABHLQXceghiklopquvxy"
+ "C:D:F:G:I:J:K:M:N:O:P:R:S:T:V:W:Z:a:b:f:g:j:m:n:r:s:t:z:")) != -1) {
switch (opt) {
case 'A':
gen_all_hostkeys = 1;
@@ -2266,6 +2283,9 @@ main(int argc, char **argv)
case 'n':
cert_principals = optarg;
break;
+ case 'o':
+ use_new_format = 1;
+ break;
case 'p':
change_passphrase = 1;
break;
@@ -2293,6 +2313,9 @@ main(int argc, char **argv)
case 'O':
add_cert_option(optarg);
break;
+ case 'Z':
+ new_format_cipher = optarg;
+ break;
case 'C':
identity_comment = optarg;
break;
@@ -2351,9 +2374,9 @@ main(int argc, char **argv)
optarg, errstr);
break;
case 'a':
- trials = (u_int32_t)strtonum(optarg, 1, UINT_MAX, &errstr);
+ rounds = (int)strtonum(optarg, 1, INT_MAX, &errstr);
if (errstr)
- fatal("Invalid number of trials: %s (%s)",
+ fatal("Invalid number: %s (%s)",
optarg, errstr);
break;
case 'M':
@@ -2512,7 +2535,8 @@ main(int argc, char **argv)
fatal("Couldn't open moduli file \"%s\": %s",
out_file, strerror(errno));
}
- if (prime_test(in, out, trials, generator_wanted, checkpoint,
+ if (prime_test(in, out, rounds == 0 ? 100 : rounds,
+ generator_wanted, checkpoint,
start_lineno, lines_to_process) != 0)
fatal("modulus screening failed");
return (0);
@@ -2604,7 +2628,8 @@ passphrase_again:
}
/* Save the key with the given passphrase and comment. */
- if (!key_save_private(private, identity_file, passphrase1, comment)) {
+ if (!key_save_private(private, identity_file, passphrase1, comment,
+ use_new_format, new_format_cipher, rounds)) {
printf("Saving the key failed: %s.\n", identity_file);
memset(passphrase1, 0, strlen(passphrase1));
free(passphrase1);