]> git.ipfire.org Git - thirdparty/openssh-portable.git/commitdiff
- markus@cvs.openbsd.org 2013/12/06 13:34:54
authorDamien Miller <djm@mindrot.org>
Fri, 6 Dec 2013 23:41:55 +0000 (10:41 +1100)
committerDamien Miller <djm@mindrot.org>
Fri, 6 Dec 2013 23:41:55 +0000 (10:41 +1100)
     [authfile.c authfile.h cipher.c cipher.h key.c packet.c ssh-agent.c]
     [ssh-keygen.c PROTOCOL.key] new private key format, bcrypt as KDF by
     default; details in PROTOCOL.key; feedback and lots help from djm;
     ok djm@

ChangeLog
PROTOCOL.key [new file with mode: 0644]
authfile.c
authfile.h
cipher.c
cipher.h
key.c
packet.c
ssh-agent.c
ssh-keygen.c

index 0db8ab446882b55eeb7c7cc82496370e15a41630..76854907d073b3daca2728c5a49f5f1b71b70c20 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
    - markus@cvs.openbsd.org 2013/12/06 13:30:08
      [authfd.c key.c key.h ssh-agent.c]
      move private key (de)serialization to key.c; ok djm
+   - markus@cvs.openbsd.org 2013/12/06 13:34:54
+     [authfile.c authfile.h cipher.c cipher.h key.c packet.c ssh-agent.c]
+     [ssh-keygen.c PROTOCOL.key] new private key format, bcrypt as KDF by
+     default; details in PROTOCOL.key; feedback and lots help from djm;
+     ok djm@
 
 20131205
  - (djm) OpenBSD CVS Sync
diff --git a/PROTOCOL.key b/PROTOCOL.key
new file mode 100644 (file)
index 0000000..959bd7a
--- /dev/null
@@ -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 $
index d0c1089eb2049a170e1c88d9ae20442dc30d9feb..e38a3dd14dc7ba2e1baf55613573eb70430d3eda 100644 (file)
@@ -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
@@ -58,6 +58,8 @@
 #include <string.h>
 #include <unistd.h>
 
+#include <util.h>
+
 #include "xmalloc.h"
 #include "cipher.h"
 #include "buffer.h"
 #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)
 
 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
@@ -149,8 +488,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));
 
@@ -239,7 +579,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:
@@ -247,6 +588,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);
@@ -256,13 +601,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;
@@ -473,8 +820,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);
@@ -641,6 +989,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);
@@ -648,6 +998,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);
@@ -943,4 +1295,3 @@ key_in_file(Key *key, const char *filename, int strict_type)
        fclose(f);
        return ret;
 }
-
index 78349beb56158724a91e2fe19a0886a5dd110bf2..8ba1c2dbe5fdbf9322b78ecdd65c1f8f0fa3e72c 100644 (file)
@@ -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 **);
index fbb73014864da6394cfbd671d2ee39a5c9067c0f..76e6c5963311f1fd03fc5ecc7c60785463d9336f 100644 (file)
--- a/cipher.c
+++ b/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
@@ -344,17 +344,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];
 
@@ -387,13 +386,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 */
index 4e837a75412e4ab9185893f97a7301e084afad88..d782456154b27221195301d7031a7a39dc974151 100644 (file)
--- a/cipher.h
+++ b/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/key.c b/key.c
index 0282e0823831f5f25ad4ddc4aed7d12cfb68eb3c..c09f43f190116a54eeb133ee5cf3de1c021b9d1e 100644 (file)
--- a/key.c
+++ b/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
@@ -1892,6 +1892,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:
index 029bb4c98959bc5b2fbfa1a467e15a9f8001dbcd..6cf7edbb8504de2acf0e81763c6fa5ed79e0032b 100644 (file)
--- a/packet.c
+++ b/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
@@ -713,9 +713,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: ");
@@ -946,9 +947,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) {
@@ -1208,8 +1210,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);
 
@@ -1304,9 +1307,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 ||
@@ -1360,9 +1364,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,
index 0196f8f6bc3a652e3823ddb6bec585743323ce3f..579ee38403bbbe8d50dacc8a3707c231159b1768 100644 (file)
@@ -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
@@ -484,7 +484,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);
index e5e2f2f6c59370e6bd8492ef8168ffd0602b7239..533eed29122b9cb31e2463758a2c8cdf9526f4fe 100644 (file)
@@ -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
@@ -150,6 +150,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;
 
@@ -923,7 +935,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);
@@ -1275,7 +1288,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);
@@ -1385,7 +1399,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);
@@ -2132,7 +2147,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");
@@ -2160,6 +2175,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");
@@ -2176,6 +2192,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);
 }
@@ -2193,7 +2210,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;
@@ -2225,9 +2242,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;
@@ -2285,6 +2302,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;
@@ -2312,6 +2332,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;
@@ -2370,9 +2393,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':
@@ -2531,7 +2554,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);
@@ -2623,7 +2647,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);