1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-09 17:38:00 +00:00

Expose openssh_bcrypt() to testcrypt, and test it.

I happened to notice in passing that this function doesn't have any
tests (although it will have been at least somewhat tested by the
cmdgen interop test system).

This involved writing a wrapper that passes the passphrase and salt as
ptrlens, and I decided it made more sense to make the same change to
the original function too and adjust the call sites appropriately.

I derived a test case by getting OpenSSH itself to make an encrypted
key file, and then using the inputs and output from the password hash
operation that decrypted it again.
This commit is contained in:
Simon Tatham 2021-12-24 09:56:30 +00:00
parent c1ddacf78f
commit 831accb2a9
6 changed files with 36 additions and 13 deletions

View File

@ -69,8 +69,7 @@ void bcrypt_genblock(int counter,
smemclr(&hashed_salt, sizeof(hashed_salt));
}
void openssh_bcrypt(const char *passphrase,
const unsigned char *salt, int saltbytes,
void openssh_bcrypt(ptrlen passphrase, ptrlen salt,
int rounds, unsigned char *out, int outbytes)
{
unsigned char hashed_passphrase[64];
@ -80,7 +79,7 @@ void openssh_bcrypt(const char *passphrase,
int modulus, residue, i, j, round;
/* Hash the passphrase to get the bcrypt key material */
hash_simple(&ssh_sha512, ptrlen_from_asciz(passphrase), hashed_passphrase);
hash_simple(&ssh_sha512, passphrase, hashed_passphrase);
/* We output key bytes in a scattered fashion to meld all output
* key blocks into all parts of the output. To do this, we pick a
@ -97,8 +96,8 @@ void openssh_bcrypt(const char *passphrase,
* by bcrypt in the following loop */
memset(outblock, 0, sizeof(outblock));
thissalt = salt;
thissaltbytes = saltbytes;
thissalt = salt.ptr;
thissaltbytes = salt.len;
for (round = 0; round < rounds; round++) {
bcrypt_genblock(round == 0 ? residue+1 : 0,
hashed_passphrase,

View File

@ -1363,9 +1363,8 @@ static ssh2_userkey *openssh_new_read(
memset(keybuf, 0, keysize);
break;
case ON_K_BCRYPT:
openssh_bcrypt(passphrase,
key->kdfopts.bcrypt.salt.ptr,
key->kdfopts.bcrypt.salt.len,
openssh_bcrypt(ptrlen_from_asciz(passphrase),
key->kdfopts.bcrypt.salt,
key->kdfopts.bcrypt.rounds,
keybuf, keysize);
break;
@ -1583,9 +1582,9 @@ static bool openssh_new_write(
unsigned char keybuf[48];
ssh_cipher *cipher;
openssh_bcrypt(passphrase,
bcrypt_salt, sizeof(bcrypt_salt), bcrypt_rounds,
keybuf, sizeof(keybuf));
openssh_bcrypt(ptrlen_from_asciz(passphrase),
make_ptrlen(bcrypt_salt, sizeof(bcrypt_salt)),
bcrypt_rounds, keybuf, sizeof(keybuf));
cipher = ssh_cipher_new(&ssh_aes256_sdctr);
ssh_cipher_setkey(cipher, keybuf);

3
ssh.h
View File

@ -1413,8 +1413,7 @@ void aes256_decrypt_pubkey(const void *key, const void *iv,
void des_encrypt_xdmauth(const void *key, void *blk, int len);
void des_decrypt_xdmauth(const void *key, void *blk, int len);
void openssh_bcrypt(const char *passphrase,
const unsigned char *salt, int saltbytes,
void openssh_bcrypt(ptrlen passphrase, ptrlen salt,
int rounds, unsigned char *out, int outbytes);
/*

View File

@ -2106,6 +2106,20 @@ culpa qui officia deserunt mollit anim id est laborum.
"aeae2a21201eef5e347de22c922192e8f46274b0c9d33e965155a91e7686"
"9d530e"))
def testOpenSSHBcrypt(self):
# Test case created by making an OpenSSH private key file
# using their own ssh-keygen, then decrypting it successfully
# using PuTTYgen and printing the inputs and outputs to
# openssh_bcrypt in the process. So this output key is known
# to agree with OpenSSH's own answer.
self.assertEqualBin(
openssh_bcrypt('test passphrase',
unhex('d0c3b40ace4afeaf8c0f81202ae36718'),
16, 48),
unhex('d78ba86e7273de0e007ab0ba256646823d5c902bc44293ae'
'78547e9a7f629be928cc78ff78a75a4feb7aa6f125079c7d'))
def testRSAVerify(self):
def blobs(n, e, d, p, q, iqmp):
pubblob = ssh_string(b"ssh-rsa") + ssh2_mpint(e) + ssh2_mpint(n)

View File

@ -447,6 +447,9 @@ FUNC_WRAPPED(val_string, argon2, ARG(argon2flavour, flavour), ARG(uint, mem),
ARG(val_string_ptrlen, K), ARG(val_string_ptrlen, X))
FUNC(val_string, argon2_long_hash, ARG(uint, length),
ARG(val_string_ptrlen, data))
FUNC_WRAPPED(val_string, openssh_bcrypt, ARG(val_string_ptrlen, passphrase),
ARG(val_string_ptrlen, salt), ARG(uint, rounds),
ARG(uint, outbytes))
/*
* Key generation functions.

View File

@ -1087,6 +1087,15 @@ strbuf *argon2_wrapper(Argon2Flavour flavour, uint32_t mem, uint32_t passes,
return out;
}
strbuf *openssh_bcrypt_wrapper(ptrlen passphrase, ptrlen salt,
unsigned rounds, unsigned outbytes)
{
strbuf *out = strbuf_new();
openssh_bcrypt(passphrase, salt, rounds,
strbuf_append(out, outbytes), outbytes);
return out;
}
strbuf *get_implementations_commasep(ptrlen alg)
{
strbuf *out = strbuf_new();