diff --git a/tinyssh-tests/fail.h b/tinyssh-tests/fail.h index 3eee6e5..9b461f2 100644 --- a/tinyssh-tests/fail.h +++ b/tinyssh-tests/fail.h @@ -9,7 +9,7 @@ #define fail(x) fail_(__FILE__, __LINE__, (x)) -static void fail_printdata(char *text, unsigned char *data, long long len) { +static void fail_printdata(const char *text, const unsigned char *data, long long len) { long long i; diff --git a/tinyssh-tests/sshcrypto_key_sk_ed25519test.c b/tinyssh-tests/sshcrypto_key_sk_ed25519test.c new file mode 100644 index 0000000..b2b1d21 --- /dev/null +++ b/tinyssh-tests/sshcrypto_key_sk_ed25519test.c @@ -0,0 +1,124 @@ +/* +20440414 +Jó Ágila Bitsch +Public domain. +*/ + +#include +#include +#include "fail.h" +#include "sshcrypto.h" + +static unsigned char authorized_keys[129] = "sk-ssh-ed25519@openssh.com AAAAGnNrLXNzaC1lZDI1NTE5QG9wZW5zc2guY29tAAAAINNQKmYkYiy++sy5lWeN+N0SJsaDWPx9JYqlRgiZKbVlAAAABHNzaDo=\n"; +static unsigned char id_sk_ed25519_bin[78] = { + 0x00, 0x00, 0x00, 0x4a, // -> uint32 length (of the public key bytes to follow) + 0x00, 0x00, 0x00, 0x1a, //-> uint32 length (of the public key type string) + 0x73, 0x6b, 0x2d, 0x73, 0x73, 0x68, 0x2d, 0x65, + 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x40, 0x6f, + 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, + 0x6f, 0x6d, // -> string "sk-ssh-ed25519@openssh.com" + 0x00, 0x00, 0x00, 0x20, // -> uint32 length (of the public key bytes) + 0xd3, 0x50, 0x2a, 0x66, 0x24, 0x62, 0x2c, 0xbe, + 0xfa, 0xcc, 0xb9, 0x95, 0x67, 0x8d, 0xf8, 0xdd, + 0x12, 0x26, 0xc6, 0x83, 0x58, 0xfc, 0x7d, 0x25, + 0x8a, 0xa5, 0x46, 0x08, 0x99, 0x29, 0xb5, 0x65, // -> 32 bytes of a ed25519 public key + 0x00, 0x00, 0x00, 0x04, // -> uint32 length of application string + 0x73, 0x73, 0x68, 0x3a, // -> application string "ssh:" + +}; +static unsigned char signature[107] = { + 0x00, 0x00, 0x00, 0x67, // -> uint32 length (of the signature bytes to follow) + 0x00, 0x00, 0x00, 0x1a, // -> uint32 length (of the signature algorithm name) + 0x73, 0x6b, 0x2d, 0x73, 0x73, 0x68, 0x2d, 0x65, + 0x64, 0x32, 0x35, 0x35, 0x31, 0x39, 0x40, 0x6f, + 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, + 0x6f, 0x6d, // -> string "sk-ssh-ed25519@openssh.com" + 0x00, 0x00, 0x00, 0x40, // -> uint32 length(=64) of signature itself + 0x9c, 0xd3, 0xa1, 0xd9, 0xdc, 0x01, 0xcb, 0xdd, + 0x0e, 0xe7, 0xbd, 0x29, 0x58, 0xcb, 0x79, 0x4a, + 0x75, 0xd8, 0xb4, 0xef, 0x3b, 0x57, 0x24, 0xbe, + 0x20, 0x39, 0x46, 0x0a, 0xdf, 0xcf, 0x15, 0x34, + 0x38, 0xff, 0xe7, 0xcf, 0x0e, 0xa3, 0x1b, 0x2b, + 0x75, 0xfd, 0x18, 0xd4, 0xab, 0x2a, 0x74, 0xc0, + 0xab, 0x7f, 0x39, 0xc0, 0x0f, 0x66, 0x5d, 0x60, + 0x11, 0x4d, 0xbb, 0xd8, 0xb8, 0xda, 0x4c, 0x05, // -> bytes of the signature + 0x01, // -> (byte) flags -> user present + 0x00, 0x00, 0x00, 0x08, // -> uint32 counter +}; +static unsigned char message[96] = { // SSHSIG over the authorized_keys + 0x53, 0x53, 0x48, 0x53, 0x49, 0x47, 0x00, 0x00, + 0x00, 0x04, 0x66, 0x69, 0x6c, 0x65, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x73, 0x68, + 0x61, 0x35, 0x31, 0x32, 0x00, 0x00, 0x00, 0x40, + 0xa8, 0x0b, 0xb9, 0xab, 0x9d, 0xbc, 0x68, 0xa3, + 0xef, 0x64, 0x18, 0x53, 0x22, 0xee, 0x31, 0xb0, + 0x39, 0x26, 0x03, 0x39, 0x25, 0x29, 0x4e, 0xa9, + 0x1b, 0xba, 0x3b, 0x6f, 0xf4, 0xb2, 0x7a, 0x90, + 0xb5, 0xf5, 0xce, 0x5f, 0x4b, 0x15, 0xb5, 0xab, + 0x98, 0xf5, 0xb9, 0x4c, 0xa3, 0x2f, 0x59, 0xbd, + 0xa0, 0x1d, 0xc0, 0x42, 0x6d, 0xd6, 0x32, 0x35, + 0xf0, 0x94, 0x0a, 0x5f, 0xe0, 0xe9, 0x83, 0xdf, +}; + +static void test_signopen(void) { + unsigned char pk[crypto_sign_ed25519_PUBLICKEYBYTES + 16]; + unsigned char sig[69]; + unsigned char buf_array[1024]; + struct buf buf; +// unsigned long long len; + unsigned char om[1024]; + unsigned long long omlen; + + buf_init(&buf, buf_array, sizeof buf_array); + + sk_ed25519_parsesignpk(pk, id_sk_ed25519_bin + 4, sizeof id_sk_ed25519_bin - 4); + sk_ed25519_putsignpk(&buf, pk); + if (memcmp(buf.buf, id_sk_ed25519_bin, sizeof id_sk_ed25519_bin)){ + fail_printdata("id", id_sk_ed25519_bin, sizeof id_sk_ed25519_bin); + fail_printdata("buf", buf.buf, buf.len); + fail("sk_ed25519_putsignpk() failure, please report it !!!!!!!!!"); + } + buf_purge(&buf); + + sk_ed25519_putsignpkbase64(&buf,pk); + if (memcmp(buf.buf, authorized_keys + 27, sizeof authorized_keys - 29)){ + fail_printdata("ak", authorized_keys, sizeof authorized_keys); + fail_printdata("buf", buf.buf, buf.len); + fail("sk_ed25519_putsignpkbase64() failure, please report it !!!!!!!!!"); + } + buf_purge(&buf); + + + sk_ed25519_parsesignature(sig, signature + 4, sizeof signature - 4); + sk_ed25519_putsignature(&buf, sig); + if (memcmp(buf.buf, signature, sizeof signature)){ + fail_printdata("insig", signature, sizeof signature); + fail_printdata("sig", sig, 69); + fail_printdata("buf", buf.buf, buf.len); + fail("sk_ed25519_putsignature() failure, please report it !!!!!!!!!"); + } + buf_purge(&buf); + + + buf_put(&buf, sig, sizeof sig); + buf_put(&buf, message, sizeof message); + + if (sk_ed25519_open(om, &omlen, buf.buf, buf.len, pk) != 0) { + fail_printdata("sm", buf.buf, buf.len); + fail_printdata("pk", pk, crypto_sign_ed25519_PUBLICKEYBYTES); + fail("sk_ed25519_open() failure, please report it !!!!!!!!!"); + } + + if (memcmp(buf.buf + 69, om, omlen)) { + fail_printdata("m", buf.buf, omlen); + fail_printdata("om", om, omlen); + fail("sk_ed25519_open() failure, messages do not match"); + } +} + + +int main(void) { + + test_signopen(); + _exit(0); +} diff --git a/tinyssh-tests/subprocess_signtest.c b/tinyssh-tests/subprocess_signtest.c index 4caa654..0ef1e35 100644 --- a/tinyssh-tests/subprocess_signtest.c +++ b/tinyssh-tests/subprocess_signtest.c @@ -41,7 +41,8 @@ int main(void) { if (mkdir(keydir, 0755) == -1) fail("unable to create test directory"); if (chdir(keydir) == -1) fail("unable to chdir to directory"); for (i = 0; sshcrypto_keys[i].name; ++i) { - + + if (sshcrypto_keys[i].sign_flagserver) continue; if (sshcrypto_keys[i].sign_keypair(sshcrypto_keys[i].sign_publickey, sk) != 0) fail("unable to generate key pair"); umask(022); create(sshcrypto_keys[i].sign_publickeyfilename, sshcrypto_keys[i].sign_publickey, sshcrypto_keys[i].sign_publickeybytes); @@ -54,6 +55,8 @@ int main(void) { for (i = 0; sshcrypto_keys[i].name; ++i) { + if (sshcrypto_keys[i].sign_flagserver) continue; + /* set globals */ sshcrypto_key_name = sshcrypto_keys[i].name; sshcrypto_sign = sshcrypto_keys[i].sign; diff --git a/tinyssh/LIBS b/tinyssh/LIBS index 333ef2b..41d7457 100644 --- a/tinyssh/LIBS +++ b/tinyssh/LIBS @@ -53,6 +53,7 @@ sshcrypto_kex_curve25519.o sshcrypto_kex_sntrup761x25519.o sshcrypto_key.o sshcrypto_key_ed25519.o +sshcrypto_key_sk_ed25519.o str.o stringparser.o subprocess_auth.o diff --git a/tinyssh/SOURCES b/tinyssh/SOURCES index adc5a3f..b526449 100644 --- a/tinyssh/SOURCES +++ b/tinyssh/SOURCES @@ -53,6 +53,7 @@ sshcrypto_kex_curve25519 sshcrypto_kex_sntrup761x25519 sshcrypto_key sshcrypto_key_ed25519 +sshcrypto_key_sk_ed25519 str stringparser subprocess_auth diff --git a/tinyssh/main_tinysshd_makekey.c b/tinyssh/main_tinysshd_makekey.c index 5f5898f..0c60f5d 100644 --- a/tinyssh/main_tinysshd_makekey.c +++ b/tinyssh/main_tinysshd_makekey.c @@ -55,6 +55,7 @@ int main_tinysshd_makekey(int argc, char **argv) { if (chdir(x) == -1) die_fatal("unable to chdir to directory", x, 0); for (i = 0; sshcrypto_keys[i].name; ++i) { + if (sshcrypto_keys[i].sign_flagserver) continue; if (sshcrypto_keys[i].sign_keypair(pk, sk) != 0) die_fatal("unable to generate key pair", x, 0); umask(022); create(x, sshcrypto_keys[i].sign_publickeyfilename, pk, sshcrypto_keys[i].sign_publickeybytes); diff --git a/tinyssh/sshcrypto.h b/tinyssh/sshcrypto.h index aba59f0..7d2ada6 100644 --- a/tinyssh/sshcrypto.h +++ b/tinyssh/sshcrypto.h @@ -63,12 +63,16 @@ extern void sntrup761x25519_putkemkey(struct buf *, const unsigned char *); #endif /* key - sign */ -#define sshcrypto_sign_PUBLICKEYMAX 32 /* space for ed25519 pk */ +#define sshcrypto_sign_APPLICATIONMAX 32 /* space for sk application id */ +#define sshcrypto_sign_PUBLICKEYMAX (32+sshcrypto_sign_APPLICATIONMAX) /* space for ed25519 pk */ #define sshcrypto_sign_SECRETKEYMAX 64 /* space for ed25519 sk */ -#define sshcrypto_sign_MAX 64 /* space for ed25519 sig */ -#define sshcrypto_sign_BASE64PUBLICKEYMAX 69 /* space for ed25519 in base64 + 0-terminator */ +//#define sshcrypto_sign_MAX 64 /* space for ed25519 sig */ +#define sshcrypto_sign_MAX (64+1+4) /* space for ed25519 sig + 1 byte flags + 4 byte counter*/ +#define sshcrypto_sign_BASE64PUBLICKEYMAX 118 /* space for sk-ed25519 with 32 byte application id in base64 + 0-terminator */ #define sshcrypto_sign_BASE64PUBLICKEYMIN 69 /* space for ed25519 in base64 + 0-terminator */ -#define sshcrypto_sign_NAMEMAX 12 /* space for string ssh-ed25519 + 0-terminator */ +//#define sshcrypto_sign_NAMEMAX 12 /* space for string ssh-ed25519 + 0-terminator */ +#define sshcrypto_sign_NAMEMAX 27 /* space for string sk-ssh-ed25519@openssh.com + 0-terminator */ + struct sshcrypto_key { const char *name; @@ -114,6 +118,15 @@ extern void ed25519_putsignpkbase64(struct buf *, const unsigned char *); extern int ed25519_parsesignpk(unsigned char *, const unsigned char *, long long); extern int ed25519_parsesignature(unsigned char *, const unsigned char *, long long); #endif +#ifdef crypto_sign_ed25519_BYTES +/* sshcrypto_key_sk_ed25519.c */ +extern int sk_ed25519_open(unsigned char *, unsigned long long *, const unsigned char *, unsigned long long, const unsigned char *); +extern void sk_ed25519_putsignature(struct buf *, const unsigned char *); +extern void sk_ed25519_putsignpk(struct buf *, const unsigned char *); +extern void sk_ed25519_putsignpkbase64(struct buf *, const unsigned char *); +extern int sk_ed25519_parsesignpk(unsigned char *, const unsigned char *, long long); +extern int sk_ed25519_parsesignature(unsigned char *, const unsigned char *, long long); +#endif /* cipher + mac */ diff --git a/tinyssh/sshcrypto_key.c b/tinyssh/sshcrypto_key.c index 1090257..42316d9 100644 --- a/tinyssh/sshcrypto_key.c +++ b/tinyssh/sshcrypto_key.c @@ -37,6 +37,27 @@ struct sshcrypto_key sshcrypto_keys[] = { ed25519_parsesignpk, }, #endif +#ifdef crypto_sign_ed25519_BYTES + { "sk-ssh-ed25519@openssh.com", + 0, + sk_ed25519_open, + 0, + {0}, + crypto_sign_ed25519_PUBLICKEYBYTES, + 0, + crypto_sign_ed25519_BYTES + 5, + "", + "", + sshcrypto_TYPENEWCRYPTO, + 1, + 0, + sk_ed25519_putsignature, + sk_ed25519_putsignpk, + sk_ed25519_putsignpkbase64, + sk_ed25519_parsesignature, + sk_ed25519_parsesignpk, + }, +#endif #if 0 { "pqkeyTODO", crypto_sign_ed25519, diff --git a/tinyssh/sshcrypto_key_sk_ed25519.c b/tinyssh/sshcrypto_key_sk_ed25519.c new file mode 100644 index 0000000..bc65ab0 --- /dev/null +++ b/tinyssh/sshcrypto_key_sk_ed25519.c @@ -0,0 +1,124 @@ +/* +20240414 +Jó Ágila Bitsch +Public domain. + +Specification: https://cvsweb.openbsd.org/cgi-bin/cvsweb/src/usr.bin/ssh/PROTOCOL.u2f?annotate=HEAD +*/ + +#include "crypto.h" +#include "packetparser.h" +#include "buf.h" +#include "byte.h" +#include "str.h" +#include "purge.h" +#include "sshcrypto.h" + +#ifdef crypto_sign_ed25519_BYTES +int sk_ed25519_open(unsigned char *m, unsigned long long *mlen, const unsigned char *sm, unsigned long long n, const unsigned char *pk) { + + unsigned char buf[crypto_sign_ed25519_BYTES + crypto_hash_sha256_BYTES + 1 + 4 + crypto_hash_sha256_BYTES]; + unsigned long long buflen = sizeof(buf); + long long i; + int ret = 1; + + byte_copy(buf, crypto_sign_ed25519_BYTES, sm); + + // application + crypto_hash_sha256(buf + crypto_sign_ed25519_BYTES, pk+crypto_sign_ed25519_PUBLICKEYBYTES, str_len(pk+crypto_sign_ed25519_PUBLICKEYBYTES)); + + // flags + counter + byte_copy(buf + crypto_sign_ed25519_BYTES + crypto_hash_sha256_BYTES, 1 + 4, sm + crypto_sign_ed25519_BYTES); + + // message + crypto_hash_sha256(buf + crypto_sign_ed25519_BYTES + crypto_hash_sha256_BYTES + 1 + 4, sm + crypto_sign_ed25519_BYTES + 5, n - crypto_sign_ed25519_BYTES - 5); + + ret = crypto_sign_ed25519_open(m, mlen, buf, buflen, pk); + + *mlen = n - 69; + for (i=*mlen; i > 0 ; i--) m[i-1] = sm[i+69-1]; + + return ret; +} + +void sk_ed25519_putsignature(struct buf *b, const unsigned char *x) { + + const char *name = "sk-ssh-ed25519@openssh.com"; + long long len = crypto_sign_ed25519_BYTES; + + buf_putnum32(b, len + str_len(name) + 8 + 1 + 4); + buf_putstring(b, name); + buf_putstringlen(b, x, len); + // put flag and counter + buf_put(b, x+crypto_sign_ed25519_BYTES, 5); +} + +void sk_ed25519_putsignpk(struct buf *b, const unsigned char *x) { + + const char *name = "sk-ssh-ed25519@openssh.com"; + long long len = crypto_sign_ed25519_PUBLICKEYBYTES; + + buf_putnum32(b, len + str_len(name) + 16); + buf_putstring(b, name); + buf_putstringlen(b, x, len); + len = str_len(x+crypto_sign_ed25519_PUBLICKEYBYTES); + buf_putstringlen(b, x+crypto_sign_ed25519_PUBLICKEYBYTES, len); +} +void sk_ed25519_putsignpkbase64(struct buf *b, const unsigned char *x) { + + unsigned char buf[34 + crypto_sign_ed25519_PUBLICKEYBYTES + 4 + sshcrypto_sign_APPLICATIONMAX]; + unsigned application_len = str_len(x+crypto_sign_ed25519_PUBLICKEYBYTES); + + byte_copy(buf, 34, "\0\0\0\032sk-ssh-ed25519@openssh.com\0\0\0\040"); + byte_copy(buf + 34, crypto_sign_ed25519_PUBLICKEYBYTES, x); + byte_copy(buf + 34 + crypto_sign_ed25519_PUBLICKEYBYTES, 3, "\0\0\0"); + buf[34 + crypto_sign_ed25519_PUBLICKEYBYTES + 3] = application_len; + byte_copy(buf + 34 + crypto_sign_ed25519_PUBLICKEYBYTES + 4, application_len, x+crypto_sign_ed25519_PUBLICKEYBYTES); + buf_putbase64(b, buf, 34 + crypto_sign_ed25519_PUBLICKEYBYTES + 4 + application_len); + purge(buf, sizeof buf); +} +int sk_ed25519_parsesignpk(unsigned char *buf, const unsigned char *x, long long xlen) { + + long long pos = 0; + crypto_uint32 len; + + pos = packetparser_uint32(x, xlen, pos, &len); + pos = packetparser_skip(x, xlen, pos, len); + if (!str_equaln((char *)x + pos - len, len, "sk-ssh-ed25519@openssh.com")) return 0; + + // string public key + pos = packetparser_uint32(x, xlen, pos, &len); + if (len != crypto_sign_ed25519_PUBLICKEYBYTES) return 0; + pos = packetparser_copy(x, xlen, pos, buf, len); + + // string application (user-specified, but typically "ssh:") + pos = packetparser_uint32(x, xlen, pos, &len); + // only a maximum size of sshcrypto_sign_APPLICATIONMAX allowed + if (len >= sshcrypto_sign_APPLICATIONMAX) return 0; + pos = packetparser_copy(x, xlen, pos, buf+crypto_sign_ed25519_PUBLICKEYBYTES, len); + buf[crypto_sign_ed25519_PUBLICKEYBYTES+len] = '\0'; + + pos = packetparser_end(x, xlen, pos); + return 1; +} +int sk_ed25519_parsesignature(unsigned char *buf, const unsigned char *x, long long xlen) { + + long long pos = 0; + crypto_uint32 len; + + pos = packetparser_uint32(x, xlen, pos, &len); + pos = packetparser_skip(x, xlen, pos, len); + if (!str_equaln((char *)x + pos - len, len, "sk-ssh-ed25519@openssh.com")) return 0; + + pos = packetparser_uint32(x, xlen, pos, &len); + if (len != crypto_sign_ed25519_BYTES) return 0; + len = len + 5; + pos = packetparser_skip(x, xlen, pos, len); + byte_copy(buf, len, x + pos - len); + pos = packetparser_end(x, xlen, pos); + + if ((buf[crypto_sign_ed25519_BYTES] & 0x8) != 0) return 0; // we can't handle extensions yet + + return 1; +} +#endif