1
$\begingroup$

I generated an encrypted OpenSSH private key for test purposes:

-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAACmFlczI1Ni1jYmMAAAAGYmNyeXB0AAAAGAAAABCX18gzzE
urqzviTpYwDuLjAAAAEAAAAAEAAAAzAAAAC3NzaC1lZDI1NTE5AAAAIL3fL8j3ilzDkr/O
7rBBCIYaH/pE1VvU3r2IEy7M2vBaAAAAoAHqj8gYr4dNtxmQyoC+md10ZwY+GOuWIYmOxl
vDYQkGZWYAU4zeiu+GnWhJr935cR1IrJUdsPWkjtdYRh4HksY5DjFngSHxEYr5/HyfJN52
tCN4zoxVe9bHyWVAs/XdnfHAMSUL7f+tXXhSIOitwggn1/bUDqX3bYMk8qtEYbDweE0lS4
Q5TcIJEn3A255WyCJ+WKXTglTaKpDNXCucV9E=
-----END OPENSSH PRIVATE KEY-----

(key type is ed25519; password is "key")

PROTOCOL.key describes the format of the key. In decoding it they're using bcrypt with 160 rounds.

Quoting PROTOCOL.key,

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.

That's kinda vague. How do the IV / key get generated? Are they the same?

$\endgroup$
1
$\begingroup$

If I am reading the code right, it does indeed use the key as the IV, here are the relevant functions:

sshkey_parse_private2 from sshkey.c is the function that performs the KDF and decrypts the private key blob. The KDF takes the The first decryption function called within sshkey_parse_private2 is cipher_init, as follows:

r = cipher_init(&ciphercontext, cipher, key, keylen, key + keylen, ivlen, 0)) != 0)

The format of that function from cipher.c:

cipher_init(struct sshcipher_ctx **ccp, const struct sshcipher *cipher, const u_char *key, u_int keylen, const u_char *iv, u_int ivlen, int do_encrypt)

That means the IV passed to cipher_init is simply key + keylen

Additionally, the round count for bcrypt is 16

$\endgroup$
1
$\begingroup$

(I lost, but since I had written most of this already I'm posting it anyway.)

Right in the same source tree: https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/sshkey.c?rev=1.64&content-type=text/x-cvsweb-markup&only_with_tag=MAIN in function sshkey_private_to_blob2 after some simple checks and setup:

arc4random_buf(salt, SALT_LEN);
if (bcrypt_pbkdf(passphrase, strlen(passphrase),
    salt, SALT_LEN, key, keylen + ivlen, rounds) < 0) { /*error handling*/ }

and then

if ((r = cipher_init(&ciphercontext, cipher, key, keylen,
    key + keylen, ivlen, 1)) != 0) /* error handling */

so it is using bcrypt (within a slightly modified PBKDF2) to generate keylen+ivlen bytes then using the first keylen bytes as the key and the last ivlen bytes as the IV.

Also, the default number of rounds used in encryption here is 16, but a different number can be specified by the caller (in ssh-keygen using -a). Whatever value is used, and also the salt, are written in the file (as shown in your link) and used in decryption.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.