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:
parent
66983e2410
commit
e9aa28fe02
5
ssh.h
5
ssh.h
@ -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;
|
||||
|
30
sshpubk.c
30
sshpubk.c
@ -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), ¶ms);
|
||||
|
||||
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");
|
||||
|
||||
|
@ -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(
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user