1
0
mirror of https://git.tartarus.org/simon/putty.git synced 2025-01-24 16:52:24 +00:00

cgtest: add tests for elliptic-curve keys.

We've supported ECC keys for a while, but cgtest has never tested them
before. Now it does.

This wasn't quite as simple as adding two extra key types to the list.
I had to add a system of per-key-type flags in the tests to trigger
different expectations and workarounds: the new key types can't be
converted to and from ssh.com format, they behave differently from
rsa1 if you try (in that they'll get as far as asking for the
passphrase _before_ realising the key is an unsupported kind), and
also it turns out we disagree with OpenSSH ssh-keygen on the bit count
to write in the fingerprint of an ed25519 key. (We say 255, and they
say 256.)

But having fixed all those things, the tests pass.
This commit is contained in:
Simon Tatham 2020-01-13 22:32:03 +00:00
parent 356e14cd89
commit df577ab152

174
cmdgen.c
View File

@ -1235,38 +1235,71 @@ void filecmp(char *file1, char *file2, char *fmt, ...)
passes++;
}
char *cleanup_fp(char *s)
/*
* General-purpose flags word
*/
#define CGT_FLAGS(X) \
X(CGT_TYPE_KNOWN_EARLY) \
X(CGT_OPENSSH) \
X(CGT_SSHCOM) \
X(CGT_SSH_KEYGEN) \
X(CGT_ED25519) \
/* end of list */
#define FLAG_SHIFTS(name) name ## _shift,
enum { CGT_FLAGS(FLAG_SHIFTS) CGT_dummy_shift };
#define FLAG_VALUES(name) name = 1 << name ## _shift,
enum { CGT_FLAGS(FLAG_VALUES) CGT_dummy_flag };
char *cleanup_fp(char *s, unsigned flags)
{
ptrlen pl = ptrlen_from_asciz(s);
static const char separators[] = " \n\t";
/* Skip initial key type word if we find one */
if (ptrlen_startswith(pl, PTRLEN_LITERAL("ssh-"), NULL))
if (ptrlen_startswith(pl, PTRLEN_LITERAL("ssh-"), NULL) ||
ptrlen_startswith(pl, PTRLEN_LITERAL("ecdsa-"), NULL))
ptrlen_get_word(&pl, separators);
/* Expect two words giving the key length and the hash */
ptrlen bits = ptrlen_get_word(&pl, separators);
ptrlen hash = ptrlen_get_word(&pl, separators);
/* Strip "MD5:" prefix if it's present, and do nothing if it isn't */
ptrlen_startswith(hash, PTRLEN_LITERAL("MD5:"), &hash);
if (flags & CGT_SSH_KEYGEN) {
/* Strip "MD5:" prefix if it's present, and do nothing if it isn't */
ptrlen_startswith(hash, PTRLEN_LITERAL("MD5:"), &hash);
if (flags & CGT_ED25519) {
/* OpenSSH ssh-keygen lists ed25519 keys as 256 bits, not 255 */
if (ptrlen_eq_string(bits, "256"))
bits = PTRLEN_LITERAL("255");
}
}
return dupprintf("%.*s %.*s", PTRLEN_PRINTF(bits), PTRLEN_PRINTF(hash));
}
char *get_fp(char *filename)
char *get_line(char *filename)
{
FILE *fp;
char buf[256], *ret;
char *line;
fp = fopen(filename, "r");
if (!fp)
return NULL;
ret = fgets(buf, sizeof(buf), fp);
line = fgetline(fp);
fclose(fp);
if (!ret)
return line;
}
char *get_fp(char *filename, unsigned flags)
{
char *orig = get_line(filename);
if (!orig)
return NULL;
return cleanup_fp(buf);
char *toret = cleanup_fp(orig, flags);
sfree(orig);
return toret;
}
void check_fp(char *filename, char *fp, char *fmt, ...)
@ -1276,7 +1309,7 @@ void check_fp(char *filename, char *fp, char *fmt, ...)
if (!fp)
return;
newfp = get_fp(filename);
newfp = get_fp(filename, 0);
if (!strcmp(fp, newfp)) {
passes++;
@ -1297,10 +1330,20 @@ void check_fp(char *filename, char *fp, char *fmt, ...)
sfree(newfp);
}
static const struct cgtest_keytype {
const char *name;
unsigned flags;
} cgtest_keytypes[] = {
{ "rsa1", CGT_TYPE_KNOWN_EARLY },
{ "dsa", CGT_OPENSSH | CGT_SSHCOM },
{ "rsa", CGT_OPENSSH | CGT_SSHCOM },
{ "ecdsa", CGT_OPENSSH },
{ "ed25519", CGT_OPENSSH | CGT_ED25519 },
};
int main(int argc, char **argv)
{
int i;
static char *const keytypes[] = { "rsa1", "dsa", "rsa" };
while (--argc > 0) {
ptrlen arg = ptrlen_from_asciz(*++argv);
@ -1316,23 +1359,28 @@ int main(int argc, char **argv)
passes = fails = 0;
for (i = 0; i < lenof(keytypes); i++) {
for (i = 0; i < lenof(cgtest_keytypes); i++) {
const struct cgtest_keytype *keytype = &cgtest_keytypes[i];
bool supports_openssh = keytype->flags & CGT_OPENSSH;
bool supports_sshcom = keytype->flags & CGT_SSHCOM;
bool type_known_early = keytype->flags & CGT_TYPE_KNOWN_EARLY;
char filename[128], osfilename[128], scfilename[128];
char pubfilename[128], tmpfilename1[128], tmpfilename2[128];
char *fp = NULL;
sprintf(filename, "test-%s.ppk", keytypes[i]);
sprintf(pubfilename, "test-%s.pub", keytypes[i]);
sprintf(osfilename, "test-%s.os", keytypes[i]);
sprintf(scfilename, "test-%s.sc", keytypes[i]);
sprintf(tmpfilename1, "test-%s.tmp1", keytypes[i]);
sprintf(tmpfilename2, "test-%s.tmp2", keytypes[i]);
sprintf(filename, "test-%s.ppk", keytype->name);
sprintf(pubfilename, "test-%s.pub", keytype->name);
sprintf(osfilename, "test-%s.os", keytype->name);
sprintf(scfilename, "test-%s.sc", keytype->name);
sprintf(tmpfilename1, "test-%s.tmp1", keytype->name);
sprintf(tmpfilename2, "test-%s.tmp2", keytype->name);
/*
* Create an encrypted key.
*/
setup_passphrases("sponge", "sponge", NULL);
test(0, "puttygen", "-t", keytypes[i], "-o", filename, NULL);
test(0, "puttygen", "-t", keytype->name, "-o", filename, NULL);
/*
* List the public key in OpenSSH format.
@ -1344,11 +1392,20 @@ int main(int argc, char **argv)
fp = NULL;
cmdbuf = dupprintf("ssh-keygen -E md5 -l -f '%s' > '%s'",
pubfilename, tmpfilename1);
if (cgtest_verbose)
printf("OpenSSH fp check: %s\n", cmdbuf);
if (system(cmdbuf) ||
(fp = get_fp(tmpfilename1)) == NULL) {
(fp = get_fp(tmpfilename1,
CGT_SSH_KEYGEN | keytype->flags)) == NULL) {
printf("UNABLE to test fingerprint matching against OpenSSH");
}
sfree(cmdbuf);
if (fp && cgtest_verbose) {
char *line = get_line(tmpfilename1);
printf("OpenSSH fp: %s\n", line);
printf("Cleaned up: %s\n", fp);
sfree(line);
}
}
/*
@ -1369,9 +1426,9 @@ int main(int argc, char **argv)
* fingerprints we generate of this key throughout
* testing.
*/
fp = get_fp(tmpfilename1);
fp = get_fp(tmpfilename1, 0);
} else {
check_fp(tmpfilename1, fp, "%s initial fp", keytypes[i]);
check_fp(tmpfilename1, fp, "%s initial fp", keytype->name);
}
/*
@ -1412,19 +1469,19 @@ int main(int argc, char **argv)
/*
* Export the private key into OpenSSH format; no passphrase
* should be required since the key is currently unencrypted.
* For RSA1 keys, this should give an error.
*/
setup_passphrases(NULL);
test((i==0), "puttygen", "-O", "private-openssh", "-o", osfilename,
test(supports_openssh ? 0 : 1,
"puttygen", "-O", "private-openssh", "-o", osfilename,
filename, NULL);
if (i) {
if (supports_openssh) {
/*
* List the fingerprint of the OpenSSH-formatted key.
*/
setup_passphrases(NULL);
test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL);
check_fp(tmpfilename1, fp, "%s openssh clear fp", keytypes[i]);
check_fp(tmpfilename1, fp, "%s openssh clear fp", keytype->name);
/*
* List the public half of the OpenSSH-formatted key in
@ -1444,19 +1501,19 @@ int main(int argc, char **argv)
/*
* Export the private key into ssh.com format; no passphrase
* should be required since the key is currently unencrypted.
* For RSA1 keys, this should give an error.
*/
setup_passphrases(NULL);
test((i==0), "puttygen", "-O", "private-sshcom", "-o", scfilename,
filename, NULL);
test(supports_sshcom ? 0 : 1,
"puttygen", "-O", "private-sshcom",
"-o", scfilename, filename, NULL);
if (i) {
if (supports_sshcom) {
/*
* List the fingerprint of the ssh.com-formatted key.
*/
setup_passphrases(NULL);
test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL);
check_fp(tmpfilename1, fp, "%s ssh.com clear fp", keytypes[i]);
check_fp(tmpfilename1, fp, "%s ssh.com clear fp", keytype->name);
/*
* List the public half of the ssh.com-formatted key in
@ -1473,7 +1530,7 @@ int main(int argc, char **argv)
test(0, "puttygen", "-p", scfilename, NULL);
}
if (i) {
if (supports_openssh && supports_sshcom) {
/*
* Convert from OpenSSH into ssh.com.
*/
@ -1495,7 +1552,7 @@ int main(int argc, char **argv)
* the original.
*/
filecmp(filename, tmpfilename2,
"p->o->s->p clear %s", keytypes[i]);
"p->o->s->p clear %s", keytype->name);
/*
* Convert from ssh.com to OpenSSH.
@ -1518,7 +1575,7 @@ int main(int argc, char **argv)
* the original.
*/
filecmp(filename, tmpfilename2,
"p->s->o->p clear %s", keytypes[i]);
"p->s->o->p clear %s", keytype->name);
/*
* Finally, do a round-trip conversion between PuTTY
@ -1531,7 +1588,7 @@ int main(int argc, char **argv)
setup_passphrases(NULL);
test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL);
filecmp(filename, tmpfilename2,
"p->s->p clear %s", keytypes[i]);
"p->s->p clear %s", keytype->name);
}
/*
@ -1548,23 +1605,28 @@ int main(int argc, char **argv)
/*
* Export the private key into OpenSSH format, this time
* while encrypted. For RSA1 keys, this should give an
* error.
* while encrypted.
*/
if (i == 0)
setup_passphrases(NULL); /* error, hence no passphrase read */
else
if (!supports_openssh && type_known_early) {
/* We'll know far enough in advance that this combination
* is going to fail that we never ask for the passphrase */
setup_passphrases(NULL);
} else {
setup_passphrases("sponge2", NULL);
test((i==0), "puttygen", "-O", "private-openssh", "-o", osfilename,
}
test(supports_openssh ? 0 : 1,
"puttygen", "-O", "private-openssh", "-o", osfilename,
filename, NULL);
if (i) {
if (supports_openssh) {
/*
* List the fingerprint of the OpenSSH-formatted key.
*/
setup_passphrases("sponge2", NULL);
test(0, "puttygen", "-l", osfilename, "-o", tmpfilename1, NULL);
check_fp(tmpfilename1, fp, "%s openssh encrypted fp", keytypes[i]);
check_fp(tmpfilename1, fp, "%s openssh encrypted fp",
keytype->name);
/*
* List the public half of the OpenSSH-formatted key in
@ -1586,20 +1648,26 @@ int main(int argc, char **argv)
* while encrypted. For RSA1 keys, this should give an
* error.
*/
if (i == 0)
setup_passphrases(NULL); /* error, hence no passphrase read */
else
if (!supports_sshcom && type_known_early) {
/* We'll know far enough in advance that this combination
* is going to fail that we never ask for the passphrase */
setup_passphrases(NULL);
} else {
setup_passphrases("sponge2", NULL);
test((i==0), "puttygen", "-O", "private-sshcom", "-o", scfilename,
}
test(supports_sshcom ? 0 : 1,
"puttygen", "-O", "private-sshcom", "-o", scfilename,
filename, NULL);
if (i) {
if (supports_sshcom) {
/*
* List the fingerprint of the ssh.com-formatted key.
*/
setup_passphrases("sponge2", NULL);
test(0, "puttygen", "-l", scfilename, "-o", tmpfilename1, NULL);
check_fp(tmpfilename1, fp, "%s ssh.com encrypted fp", keytypes[i]);
check_fp(tmpfilename1, fp, "%s ssh.com encrypted fp",
keytype->name);
/*
* List the public half of the ssh.com-formatted key in
@ -1616,7 +1684,7 @@ int main(int argc, char **argv)
test(0, "puttygen", "-p", scfilename, NULL);
}
if (i) {
if (supports_openssh && supports_sshcom) {
/*
* Convert from OpenSSH into ssh.com.
*/
@ -1638,7 +1706,7 @@ int main(int argc, char **argv)
* the original.
*/
filecmp(filename, tmpfilename2,
"p->o->s->p encrypted %s", keytypes[i]);
"p->o->s->p encrypted %s", keytype->name);
/*
* Convert from ssh.com to OpenSSH.
@ -1661,7 +1729,7 @@ int main(int argc, char **argv)
* the original.
*/
filecmp(filename, tmpfilename2,
"p->s->o->p encrypted %s", keytypes[i]);
"p->s->o->p encrypted %s", keytype->name);
/*
* Finally, do a round-trip conversion between PuTTY
@ -1674,7 +1742,7 @@ int main(int argc, char **argv)
setup_passphrases("sponge2", NULL);
test(0, "puttygen", tmpfilename1, "-o", tmpfilename2, NULL);
filecmp(filename, tmpfilename2,
"p->s->p encrypted %s", keytypes[i]);
"p->s->p encrypted %s", keytype->name);
}
/*