1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-08 08:58:00 +00:00

Restore the ability to write out PPK v2.

This commit adds the capability in principle to ppk_save_sb, by adding
a fmt_version field in the save parameters structure. As yet it's not
connected up to any user interface in PuTTYgen, but I think I'll need
to, because currently there's no way at all to convert PPK v3 back to
v2, and surely people will need to interoperate with older
installations of PuTTY, or with other PPK-consuming software.
This commit is contained in:
Simon Tatham 2021-02-21 22:29:10 +00:00
parent 66983e2410
commit e9aa28fe02
5 changed files with 45 additions and 19 deletions

5
ssh.h
View File

@ -1235,6 +1235,11 @@ int rsa1_load_f(const Filename *filename, RSAKey *key,
const char *passphrase, const char **errorstr);
typedef struct ppk_save_parameters {
unsigned fmt_version; /* currently 2 or 3 */
/*
* Parameters for fmt_version == 3
*/
Argon2Flavour argon2_flavour;
uint32_t argon2_mem; /* in Kb */
bool argon2_passes_auto;

View File

@ -1410,6 +1410,8 @@ void base64_encode(FILE *fp, const unsigned char *data, int datalen, int cpl)
}
const ppk_save_parameters ppk_save_default_parameters = {
.fmt_version = 3,
/*
* The Argon2 spec recommends the hybrid variant Argon2id, where
* you don't have a good reason to go with the pure Argon2d or
@ -1505,19 +1507,25 @@ strbuf *ppk_save_sb(ssh2_userkey *key, const char *passphrase,
* copy for us to retrieve. */
ppk_save_parameters params = *params_orig;
/* Invent a salt for the password hash. */
strbuf *passphrase_salt = strbuf_new();
if (params.salt)
put_data(passphrase_salt, params.salt, params.saltlen);
else
random_read(strbuf_append(passphrase_salt, 16), 16);
if (params.fmt_version == 3) {
/* Invent a salt for the password hash. */
if (params.salt)
put_data(passphrase_salt, params.salt, params.saltlen);
else
random_read(strbuf_append(passphrase_salt, 16), 16);
}
cipher_mac_keys_blob = strbuf_new();
ssh2_ppk_derive_keys(3, ciphertype,
ssh2_ppk_derive_keys(params.fmt_version, ciphertype,
ptrlen_from_asciz(passphrase ? passphrase : ""),
cipher_mac_keys_blob, &cipherkey, &cipheriv, &mackey,
ptrlen_from_strbuf(passphrase_salt), &params);
const ssh2_macalg *macalg = (params.fmt_version == 2 ?
&ssh_hmac_sha1 : &ssh_hmac_sha256);
/* Now create the MAC. */
{
strbuf *macdata;
@ -1529,8 +1537,7 @@ strbuf *ppk_save_sb(ssh2_userkey *key, const char *passphrase,
put_string(macdata, pub_blob->s, pub_blob->len);
put_string(macdata, priv_blob_encrypted, priv_encrypted_len);
mac_simple(&ssh_hmac_sha256, mackey,
ptrlen_from_strbuf(macdata), priv_mac);
mac_simple(macalg, mackey, ptrlen_from_strbuf(macdata), priv_mac);
strbuf_free(macdata);
}
@ -1541,12 +1548,13 @@ strbuf *ppk_save_sb(ssh2_userkey *key, const char *passphrase,
}
strbuf *out = strbuf_new_nm();
strbuf_catf(out, "PuTTY-User-Key-File-3: %s\n", ssh_key_ssh_id(key->key));
strbuf_catf(out, "PuTTY-User-Key-File-%u: %s\n",
params.fmt_version, ssh_key_ssh_id(key->key));
strbuf_catf(out, "Encryption: %s\n", cipherstr);
strbuf_catf(out, "Comment: %s\n", key->comment);
strbuf_catf(out, "Public-Lines: %d\n", base64_lines(pub_blob->len));
base64_encode_s(BinarySink_UPCAST(out), pub_blob->u, pub_blob->len, 64);
if (ciphertype->keylen != 0) {
if (params.fmt_version == 3 && ciphertype->keylen != 0) {
strbuf_catf(out, "Key-Derivation: %s\n",
params.argon2_flavour == Argon2d ? "Argon2d" :
params.argon2_flavour == Argon2i ? "Argon2i" : "Argon2id");
@ -1564,7 +1572,7 @@ strbuf *ppk_save_sb(ssh2_userkey *key, const char *passphrase,
base64_encode_s(BinarySink_UPCAST(out),
priv_blob_encrypted, priv_encrypted_len, 64);
strbuf_catf(out, "Private-MAC: ");
for (i = 0; i < 32; i++)
for (i = 0; i < macalg->len; i++)
strbuf_catf(out, "%02x", priv_mac[i]);
strbuf_catf(out, "\n");

View File

@ -2163,17 +2163,21 @@ Private-MAC: 6f5e588e475e55434106ec2c3569695b03f423228b44993a9e97d52ffe7be5a8
salt = unhex('37c3911bfefc8c1d11ec579627d2b3d9')
with queued_specific_random_data(salt):
self.assertEqual(ppk_save_sb(k1, comment, None, 'id', 8192, 13, 1),
self.assertEqual(ppk_save_sb(k1, comment, None,
3, 'id', 8192, 13, 1),
input_clear_key)
with queued_specific_random_data(salt):
self.assertEqual(ppk_save_sb(k2, comment, None, 'id', 8192, 13, 1),
self.assertEqual(ppk_save_sb(k2, comment, None,
3, 'id', 8192, 13, 1),
input_clear_key)
with queued_specific_random_data(salt):
self.assertEqual(ppk_save_sb(k1, comment, pp, 'id', 8192, 13, 1),
self.assertEqual(ppk_save_sb(k1, comment, pp,
3, 'id', 8192, 13, 1),
input_encrypted_key)
with queued_specific_random_data(salt):
self.assertEqual(ppk_save_sb(k2, comment, pp, 'id', 8192, 13, 1),
self.assertEqual(ppk_save_sb(k2, comment, pp,
3, 'id', 8192, 13, 1),
input_encrypted_key)
# And check we can still handle v2 key files.
@ -2219,6 +2223,13 @@ Private-MAC: 5b1f6f4cc43eb0060d2c3e181bc0129343adba2b
self.assertEqual(ssh_key_private_blob(k1), privblob)
self.assertEqual(ssh_key_private_blob(k2), privblob)
self.assertEqual(ppk_save_sb(k2, comment, None,
2, 'id', 8192, 13, 1),
v2_clear_key)
self.assertEqual(ppk_save_sb(k1, comment, pp,
2, 'id', 8192, 13, 1),
v2_encrypted_key)
def testRSA1LoadSave(self):
# Stability test of SSH-1 RSA key-file load/save functions.
input_clear_key = unhex(

View File

@ -1144,9 +1144,10 @@ int rsa1_load_s_wrapper(BinarySource *src, RSAKey *rsa, char **comment,
}
#define rsa1_load_s rsa1_load_s_wrapper
strbuf *ppk_save_sb_wrapper(ssh_key *key, const char *comment,
const char *passphrase, Argon2Flavour flavour,
uint32_t mem, uint32_t passes, uint32_t parallel)
strbuf *ppk_save_sb_wrapper(
ssh_key *key, const char *comment, const char *passphrase,
unsigned fmt_version, Argon2Flavour flavour,
uint32_t mem, uint32_t passes, uint32_t parallel)
{
/*
* For repeatable testing purposes, we never want a timing-dependent
@ -1154,6 +1155,7 @@ strbuf *ppk_save_sb_wrapper(ssh_key *key, const char *comment,
*/
ppk_save_parameters save_params;
memset(&save_params, 0, sizeof(save_params));
save_params.fmt_version = fmt_version;
save_params.argon2_flavour = flavour;
save_params.argon2_mem = mem;
save_params.argon2_passes_auto = false;

View File

@ -258,7 +258,7 @@ FUNC5(boolean, ppk_loadpub_s, val_string_binarysource, out_opt_val_string_asciz,
FUNC4(int, rsa1_loadpub_s, val_string_binarysource, out_val_string_binarysink, out_opt_val_string_asciz, out_opt_val_string_asciz_const)
FUNC4(opt_val_key, ppk_load_s, val_string_binarysource, out_opt_val_string_asciz, opt_val_string_asciz, out_opt_val_string_asciz_const)
FUNC5(int, rsa1_load_s, val_string_binarysource, val_rsa, out_opt_val_string_asciz, opt_val_string_asciz, out_opt_val_string_asciz_const)
FUNC7(val_string, ppk_save_sb, val_key, opt_val_string_asciz, opt_val_string_asciz, argon2flavour, uint, uint, uint)
FUNC8(val_string, ppk_save_sb, val_key, opt_val_string_asciz, opt_val_string_asciz, uint, argon2flavour, uint, uint, uint)
FUNC3(val_string, rsa1_save_sb, val_rsa, opt_val_string_asciz, opt_val_string_asciz)
/*