diff --git a/Makefile.in b/Makefile.in index be2d39e5d..db16cd464 100644 --- a/Makefile.in +++ b/Makefile.in @@ -31,13 +31,13 @@ OPTION_HEADERS += localoptions.h endif COMMONOBJS=dbutil.o buffer.o dbhelpers.o \ - dss.o bignum.o \ + dss.o ed25519.o ed25519_crypto.o bignum.o \ signkey.o rsa.o dbrandom.o \ queue.o \ atomicio.o compat.o fake-rfc2553.o \ ltc_prng.o ecc.o ecdsa.o crypto_desc.o \ dbmalloc.o \ - gensignkey.o gendss.o genrsa.o + gensignkey.o gendss.o gened25519.o genrsa.o SVROBJS=svr-kex.o svr-auth.o sshpty.o \ svr-authpasswd.o svr-authpubkey.o svr-authpubkeyoptions.o svr-session.o svr-service.o \ diff --git a/common-algo.c b/common-algo.c index 2f896ab9b..f5a92f736 100644 --- a/common-algo.c +++ b/common-algo.c @@ -238,6 +238,9 @@ algo_type sshhostkey[] = { #endif #if DROPBEAR_DSS {"ssh-dss", DROPBEAR_SIGNKEY_DSS, NULL, 1, NULL}, +#endif +#ifdef DROPBEAR_ED25519 + {"ssh-ed25519", DROPBEAR_SIGNKEY_ED25519, NULL, 1, NULL}, #endif {NULL, 0, NULL, 0, NULL} }; diff --git a/common-session.c b/common-session.c index 96dd4dc2d..fb72e2bf2 100644 --- a/common-session.c +++ b/common-session.c @@ -28,7 +28,6 @@ #include "packet.h" #include "algo.h" #include "buffer.h" -#include "dss.h" #include "ssh.h" #include "dbrandom.h" #include "kex.h" diff --git a/default_options.h b/default_options.h index 86fb25a16..52a2745f3 100644 --- a/default_options.h +++ b/default_options.h @@ -116,6 +116,7 @@ IMPORTANT: Some options will require "make clean" after changes */ * code (either ECDSA or ECDH) increases binary size - around 30kB * on x86-64 */ #define DROPBEAR_ECDSA 1 +#define DROPBEAR_ED25519 1 /* RSA must be >=1024 */ #define DROPBEAR_DEFAULT_RSA_SIZE 2048 diff --git a/dropbearkey.c b/dropbearkey.c index dd0e69798..e14e78a09 100644 --- a/dropbearkey.c +++ b/dropbearkey.c @@ -43,6 +43,14 @@ * mp_int y * mp_int x * + * ECDSA: + * string "ecdsa-sha2-nistp256" or "ecdsa-sha2-nistp384" or "ecdsa-sha2-nistp521" + * (not discribed here further) + * + * ed25519: + * string "ssh-ed25519" + * string 64 bytes: private_key (32 bytes) + public_key (32 bytes) + * */ #include "includes.h" #include "signkey.h" @@ -75,6 +83,9 @@ static void printhelp(char * progname) { #endif #if DROPBEAR_ECDSA " ecdsa\n" +#endif +#ifdef DROPBEAR_ED25519 + " ed25519\n" #endif "-f filename Use filename for the secret key.\n" " ~/.ssh/id_dropbear is recommended for client keys.\n" @@ -94,6 +105,9 @@ static void printhelp(char * progname) { "521 " #endif "\n" +#endif +#ifdef DROPBEAR_ED25519 + " ed25519 has a fixed size of 256 bits\n" #endif "-y Just print the publickey and fingerprint for the\n private key in .\n" #if DEBUG_TRACE @@ -224,6 +238,12 @@ int main(int argc, char ** argv) { keytype = DROPBEAR_SIGNKEY_ECDSA_KEYGEN; } #endif +#ifdef DROPBEAR_ED25519 + if (strcmp(typetext, "ed25519") == 0) + { + keytype = DROPBEAR_SIGNKEY_ED25519; + } +#endif if (keytype == DROPBEAR_SIGNKEY_NONE) { fprintf(stderr, "Unknown key type '%s'\n", typetext); diff --git a/ed25519.c b/ed25519.c new file mode 100644 index 000000000..d6bb47ccd --- /dev/null +++ b/ed25519.c @@ -0,0 +1,130 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" +#include "dbutil.h" +#include "ed25519.h" +#include "ed25519_crypto.h" +#include "buffer.h" +#include "ssh.h" + +/* Handle ed25519 (Edwards 25519 elliptic curve) + * operations, such as key reading, signing, verification. Key generation + * will be in gened25519.c, since it isn't required in the server itself. + */ + +#ifdef DROPBEAR_ED25519 + +/* Load a ed25519 key from a buffer, initialising the values. + * The key will have the same format as buf_put_ed25519_key. + * These should be freed with ed25519_key_free. + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int buf_get_ed25519_pub_key(buffer* buf, dropbear_ed25519_key *key) { + /* Format (same as the base64-encoded 2nd word in each line of + * ~/.ssh/authorized_keys used by dropbear and OpenSSH): + * + * "\0\0\0\x0bssh-ed25519\0\0\0 " + pk, + * where pk is 32 bytes of public key. + * It's 51 bytes in total. + */ + if (buf->pos + 51 > buf->len || + 0 != memcmp(buf->data + buf->pos, "\0\0\0\x0bssh-ed25519\0\0\0 ", 19) + ) return DROPBEAR_FAILURE; + memset(key->spk, '\0', 32); /* Secret key not known. */ + memcpy(key->spk + 32, buf->data + buf->pos + 19, 32); + buf->pos += 51; + return DROPBEAR_SUCCESS; +} + +/* Loads a private ed25519 key from a buffer + * Returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int buf_get_ed25519_priv_key(buffer* buf, dropbear_ed25519_key *key) { + /* Format (not shared with OpenSSH): + * + * "\0\0\0\x0bssh-ed25519\0\0\0@" + sk + pk, + * where sk is 32 bytes of secret key (private key). + * where pk is 32 bytes of public key. + * It's 83 bytes in total. + */ + if (buf->pos + 83 > buf->len || + 0 != memcmp(buf->data + buf->pos, "\0\0\0\x0bssh-ed25519\0\0\0@", 19) + ) return DROPBEAR_FAILURE; + memcpy(key->spk, buf->data + buf->pos + 19, 64); + buf->pos += 83; + return DROPBEAR_SUCCESS; +} + + +/* Clear and free the memory used by a public or private key */ +void ed25519_key_free(dropbear_ed25519_key *key) { + (void)key; +} + +/* put the ed25519 public key into the buffer in the required format. */ +void buf_put_ed25519_pub_key(buffer* buf, dropbear_ed25519_key *key) { + dropbear_assert(key != NULL); + buf_putstring(buf, SSH_SIGNKEY_ED25519, SSH_SIGNKEY_ED25519_LEN); + buf_putstring(buf, key->spk + 32, 32); +} + +/* put the ed25519 private key into the buffer in the required format. */ +void buf_put_ed25519_priv_key(buffer* buf, dropbear_ed25519_key *key) { + dropbear_assert(key != NULL); + buf_putstring(buf, SSH_SIGNKEY_ED25519, SSH_SIGNKEY_ED25519_LEN); + buf_putstring(buf, key->spk, 64); +} + +#ifdef DROPBEAR_SIGNKEY_VERIFY +/* Verify a ed25519 signature (in buf) made on data by the key given. + * returns DROPBEAR_SUCCESS or DROPBEAR_FAILURE */ +int buf_ed25519_verify(buffer* buf, dropbear_ed25519_key *key, buffer *data_buf) { + char sm[512]; + int res; + /* Typical data_size is 148 bytes for ssh. */ + unsigned data_size; + if (buf_getint(buf) != 64 || + buf->len - buf->pos != 64 || + (data_size = data_buf->len) > sizeof(sm) - 64) { + return DROPBEAR_FAILURE; + } + memcpy(sm, buf->data + buf->pos, 64); + memcpy(sm + 64, data_buf->data, data_size); + res = ed25519_crypto_verify(sm, data_size, key->spk + 32); + return res ? DROPBEAR_FAILURE : DROPBEAR_SUCCESS; +} +#endif /* DROPBEAR_SIGNKEY_VERIFY */ + +/* Sign the data presented with key, writing the signature contents + * to buf */ +void buf_put_ed25519_sign(buffer* buf, dropbear_ed25519_key *key, buffer *data_buf) { + char sm[512]; + /* Typical data_size is 32 bytes for ssh. */ + unsigned data_size = data_buf->len; + if (data_size > sizeof(sm) - 64) dropbear_exit("ed25519 message to sign too long"); + ed25519_crypto_sign(sm, data_buf->data, data_size, key->spk); + buf_putstring(buf, SSH_SIGNKEY_ED25519, SSH_SIGNKEY_ED25519_LEN); + buf_putstring(buf, sm, 64); +} + +#endif /* DROPBEAR_ED25519 */ diff --git a/ed25519.h b/ed25519.h new file mode 100644 index 000000000..2e94f6fe1 --- /dev/null +++ b/ed25519.h @@ -0,0 +1,49 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#ifndef DROPBEAR_ED25519_H_ +#define DROPBEAR_ED25519_H_ + +#include "includes.h" +#include "buffer.h" + +#ifdef DROPBEAR_ED25519 + +typedef struct { + char spk[64]; /* The first 32 bytes is th private part. */ +} dropbear_ed25519_key; + +void buf_put_ed25519_sign(buffer* buf, dropbear_ed25519_key *key, buffer *data_buf); +#ifdef DROPBEAR_SIGNKEY_VERIFY +int buf_ed25519_verify(buffer* buf, dropbear_ed25519_key *key, buffer *data_buf); +#endif +int buf_get_ed25519_pub_key(buffer* buf, dropbear_ed25519_key *key); +int buf_get_ed25519_priv_key(buffer* buf, dropbear_ed25519_key *key); +void buf_put_ed25519_pub_key(buffer* buf, dropbear_ed25519_key *key); +void buf_put_ed25519_priv_key(buffer* buf, dropbear_ed25519_key *key); +void ed25519_key_free(dropbear_ed25519_key *key); + +#endif /* DROPBEAR_ED25519 */ + +#endif /* DROPBEAR_ED25519_H_ */ diff --git a/ed25519_crypto.c b/ed25519_crypto.c new file mode 100644 index 000000000..a302e3128 --- /dev/null +++ b/ed25519_crypto.c @@ -0,0 +1,460 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" +#include "ed25519_crypto.h" + +#ifdef DROPBEAR_ED25519 + +/* Implementation is based on tweetnacl.c v20140427. + * + * Please note that some functions (including the public API functions) have + * been modified, so the API is not compatible anymore with NaCl or + * TweetNaCl. + * + * TODO(pts): Is tinyssh's implementation faster? + * TODO(pts): Reuse a faster sha512 hash in libtomcrypt? + * Is it faster? Does it add extra code? + * It can be use do reduce copying in buf_ed25519_verify and + * buf_ed25519_sign, in a multi-step call to sha512. + */ + +#define FOR(i,n) for (i = 0;i < n;++i) + +typedef uint8_t u8; +typedef uint32_t u32; +typedef uint64_t u64; +typedef int64_t i64; +typedef i64 gf[16]; + +static const u8 _0[16], _9[32] = {9}; +static const gf + gf0, + gf1 = {1}, + _121665 = {0xDB41,1}, + D = {0x78a3, 0x1359, 0x4dca, 0x75eb, 0xd8ab, 0x4141, 0x0a4d, 0x0070, 0xe898, 0x7779, 0x4079, 0x8cc7, 0xfe73, 0x2b6f, 0x6cee, 0x5203}, + D2 = {0xf159, 0x26b2, 0x9b94, 0xebd6, 0xb156, 0x8283, 0x149a, 0x00e0, 0xd130, 0xeef3, 0x80f2, 0x198e, 0xfce7, 0x56df, 0xd9dc, 0x2406}, + X = {0xd51a, 0x8f25, 0x2d60, 0xc956, 0xa7b2, 0x9525, 0xc760, 0x692c, 0xdc5c, 0xfdd6, 0xe231, 0xc0a4, 0x53fe, 0xcd6e, 0x36d3, 0x2169}, + Y = {0x6658, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666, 0x6666}, + I = {0xa0b0, 0x4a0e, 0x1b27, 0xc4ee, 0xe478, 0xad2f, 0x1806, 0x2f43, 0xd7a7, 0x3dfb, 0x0099, 0x2b4d, 0xdf0b, 0x4fc1, 0x2480, 0x2b83}; + +static u64 dl64(const u8 *x) { + u64 u=0; + u32 i; + FOR(i,8) u=(u<<8)|x[i]; + return u; +} + +static void ts64(u8 *x,u64 u) { + int i; + for (i = 7;i >= 0;--i) { x[i] = u; u >>= 8; } +} + +static int vn(const u8 *x,const u8 *y,unsigned n) { + u32 i,d = 0; + FOR(i,n) d |= x[i]^y[i]; + return (1 & ((d - 1) >> 8)) - 1; +} + +static int crypto_verify_32(const u8 *x,const u8 *y) { + return vn(x,y,32); +} + +static void set25519(gf r, const gf a) { + int i; + FOR(i,16) r[i]=a[i]; +} + +static void car25519(gf o) { + int i; + i64 c; + FOR(i,16) { + o[i]+=(1LL<<16); + c=o[i]>>16; + o[(i+1)*(i<15)]+=c-1+37*(c-1)*(i==15); + o[i]-=c<<16; + } +} + +static void sel25519(gf p,gf q,int b) { + i64 t,i,c=~(b-1); + FOR(i,16) { + t= c&(p[i]^q[i]); + p[i]^=t; + q[i]^=t; + } +} + +static void pack25519(u8 *o,const gf n) { + int i,j,b; + gf m,t; + FOR(i,16) t[i]=n[i]; + car25519(t); + car25519(t); + car25519(t); + FOR(j,2) { + m[0]=t[0]-0xffed; + for(i=1;i<15;i++) { + m[i]=t[i]-0xffff-((m[i-1]>>16)&1); + m[i-1]&=0xffff; + } + m[15]=t[15]-0x7fff-((m[14]>>16)&1); + b=(m[15]>>16)&1; + m[14]&=0xffff; + sel25519(t,m,1-b); + } + FOR(i,16) { + o[2*i]=t[i]&0xff; + o[2*i+1]=t[i]>>8; + } +} + +static int neq25519(const gf a, const gf b) { + u8 c[32],d[32]; + pack25519(c,a); + pack25519(d,b); + return crypto_verify_32(c,d); +} + +static u8 par25519(const gf a) { + u8 d[32]; + pack25519(d,a); + return d[0]&1; +} + +static void unpack25519(gf o, const u8 *n) { + int i; + FOR(i,16) o[i]=n[2*i]+((i64)n[2*i+1]<<8); + o[15]&=0x7fff; +} + +static void A(gf o,const gf a,const gf b) { + int i; + FOR(i,16) o[i]=a[i]+b[i]; +} + +static void Z(gf o,const gf a,const gf b) { + int i; + FOR(i,16) o[i]=a[i]-b[i]; +} + +static void M(gf o,const gf a,const gf b) { + i64 i,j,t[31]; + FOR(i,31) t[i]=0; + FOR(i,16) FOR(j,16) t[i+j]+=a[i]*b[j]; + FOR(i,15) t[i]+=38*t[i+16]; + FOR(i,16) o[i]=t[i]; + car25519(o); + car25519(o); +} + +static void S(gf o,const gf a) { + M(o,a,a); +} + +static void inv25519(gf o,const gf i) { + gf c; + int a; + FOR(a,16) c[a]=i[a]; + for(a=253;a>=0;a--) { + S(c,c); + if(a!=2&&a!=4) M(c,c,i); + } + FOR(a,16) o[a]=c[a]; +} + +static void pow2523(gf o,const gf i) { + gf c; + int a; + FOR(a,16) c[a]=i[a]; + for(a=250;a>=0;a--) { + S(c,c); + if(a!=1) M(c,c,i); + } + FOR(a,16) o[a]=c[a]; +} + +static u64 R(u64 x,int c) { return (x >> c) | (x << (64 - c)); } +static u64 Ch(u64 x,u64 y,u64 z) { return (x & y) ^ (~x & z); } +static u64 Maj(u64 x,u64 y,u64 z) { return (x & y) ^ (x & z) ^ (y & z); } +static u64 Sigma0(u64 x) { return R(x,28) ^ R(x,34) ^ R(x,39); } +static u64 Sigma1(u64 x) { return R(x,14) ^ R(x,18) ^ R(x,41); } +static u64 sigma0(u64 x) { return R(x, 1) ^ R(x, 8) ^ (x >> 7); } +static u64 sigma1(u64 x) { return R(x,19) ^ R(x,61) ^ (x >> 6); } + +static const u64 K[80] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, 0xe9b5dba58189dbbcULL, + 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, + 0xd807aa98a3030242ULL, 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, 0xc19bf174cf692694ULL, + 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, + 0x2de92c6f592b0275ULL, 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, 0xbf597fc7beef0ee4ULL, + 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, + 0x27b70a8546d22ffcULL, 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, 0x92722c851482353bULL, + 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, + 0xd192e819d6ef5218ULL, 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, 0x34b0bcb5e19b48a8ULL, + 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, + 0x748f82ee5defb2fcULL, 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, 0xc67178f2e372532bULL, + 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, + 0x06f067aa72176fbaULL, 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, 0x431d67c49c100d4cULL, + 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +static int crypto_hashblocks(u8 *x,const u8 *m,u32 n) { + u64 z[8],b[8],a[8],w[16],t; + int i,j; + FOR(i,8) z[i] = a[i] = dl64(x + 8 * i); + while (n >= 128) { + FOR(i,16) w[i] = dl64(m + 8 * i); + FOR(i,80) { + FOR(j,8) b[j] = a[j]; + t = a[7] + Sigma1(a[4]) + Ch(a[4],a[5],a[6]) + K[i] + w[i%16]; + b[7] = t + Sigma0(a[0]) + Maj(a[0],a[1],a[2]); + b[3] += t; + FOR(j,8) a[(j+1)%8] = b[j]; + if (i%16 == 15) + FOR(j,16) + w[j] += w[(j+9)%16] + sigma0(w[(j+1)%16]) + sigma1(w[(j+14)%16]); + } + FOR(i,8) { a[i] += z[i]; z[i] = a[i]; } + m += 128; + n -= 128; + } + FOR(i,8) ts64(x+8*i,z[i]); + return n; +} + +static const u8 iv[64] = { + 0x6a,0x09,0xe6,0x67,0xf3,0xbc,0xc9,0x08, + 0xbb,0x67,0xae,0x85,0x84,0xca,0xa7,0x3b, + 0x3c,0x6e,0xf3,0x72,0xfe,0x94,0xf8,0x2b, + 0xa5,0x4f,0xf5,0x3a,0x5f,0x1d,0x36,0xf1, + 0x51,0x0e,0x52,0x7f,0xad,0xe6,0x82,0xd1, + 0x9b,0x05,0x68,0x8c,0x2b,0x3e,0x6c,0x1f, + 0x1f,0x83,0xd9,0xab,0xfb,0x41,0xbd,0x6b, + 0x5b,0xe0,0xcd,0x19,0x13,0x7e,0x21,0x79 +} ; + +static int crypto_hash(u8 *out,const u8 *m,u32 n) { + u8 h[64],x[256]; + u32 i,b = n; + FOR(i,64) h[i] = iv[i]; + crypto_hashblocks(h,m,n); + m += n; + n &= 127; + m -= n; + FOR(i,256) x[i] = 0; + FOR(i,n) x[i] = m[i]; + x[n] = 128; + n = 256-128*(n<112); + x[n-9] = 0; + ts64(x+n-8,b<<3); + crypto_hashblocks(h,x,n); + FOR(i,64) out[i] = h[i]; + return 0; +} + +static void add(gf p[4],gf q[4]) { + gf a,b,c,d,t,e,f,g,h; + + Z(a, p[1], p[0]); + Z(t, q[1], q[0]); + M(a, a, t); + A(b, p[0], p[1]); + A(t, q[0], q[1]); + M(b, b, t); + M(c, p[3], q[3]); + M(c, c, D2); + M(d, p[2], q[2]); + A(d, d, d); + Z(e, b, a); + Z(f, d, c); + A(g, d, c); + A(h, b, a); + M(p[0], e, f); + M(p[1], h, g); + M(p[2], g, f); + M(p[3], e, h); +} + +static void cswap(gf p[4],gf q[4],u8 b) { + int i; + FOR(i,4) + sel25519(p[i],q[i],b); +} + +static void pack(u8 *r,gf p[4]) { + gf tx, ty, zi; + inv25519(zi, p[2]); + M(tx, p[0], zi); + M(ty, p[1], zi); + pack25519(r, ty); + r[31] ^= par25519(tx) << 7; +} + +static void scalarmult(gf p[4],gf q[4],const u8 *s) { + int i; + set25519(p[0],gf0); + set25519(p[1],gf1); + set25519(p[2],gf1); + set25519(p[3],gf0); + for (i = 255;i >= 0;--i) { + u8 b = (s[i/8]>>(i&7))&1; + cswap(p,q,b); + add(q,p); + add(p,p); + cswap(p,q,b); + } +} + +static void scalarbase(gf p[4],const u8 *s) { + gf q[4]; + set25519(q[0],X); + set25519(q[1],Y); + set25519(q[2],gf1); + M(q[3],X,Y); + scalarmult(p,q,s); +} + +static const u64 L[32] = {0xed, 0xd3, 0xf5, 0x5c, 0x1a, 0x63, 0x12, 0x58, 0xd6, 0x9c, 0xf7, 0xa2, 0xde, 0xf9, 0xde, 0x14, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x10}; + +static void modL(u8 *r,i64 x[64]) { + i64 carry,i,j; + for (i = 63;i >= 32;--i) { + carry = 0; + for (j = i - 32;j < i - 12;++j) { + x[j] += carry - 16 * x[i] * L[j - (i - 32)]; + carry = (x[j] + 128) >> 8; + x[j] -= carry << 8; + } + x[j] += carry; + x[i] = 0; + } + carry = 0; + FOR(j,32) { + x[j] += carry - (x[31] >> 4) * L[j]; + carry = x[j] >> 8; + x[j] &= 255; + } + FOR(j,32) x[j] -= carry * L[j]; + FOR(i,32) { + x[i+1] += x[i] >> 8; + r[i] = x[i] & 255; + } +} + +static void reduce(u8 *r) { + i64 x[64],i; + FOR(i,64) x[i] = (u64) r[i]; + FOR(i,64) r[i] = 0; + modL(r,x); +} + +static int unpackneg(gf r[4],const u8 p[32]) { + gf t, chk, num, den, den2, den4, den6; + set25519(r[2],gf1); + unpack25519(r[1],p); + S(num,r[1]); + M(den,num,D); + Z(num,num,r[2]); + A(den,r[2],den); + S(den2,den); + S(den4,den2); + M(den6,den4,den2); + M(t,den6,num); + M(t,t,den); + pow2523(t,t); + M(t,t,num); + M(t,t,den); + M(t,t,den); + M(r[0],t,den); + S(chk,r[0]); + M(chk,chk,den); + if (neq25519(chk, num)) M(r[0],r[0],I); + S(chk,r[0]); + M(chk,chk,den); + if (neq25519(chk, num)) return -1; + if (par25519(r[0]) == (p[31]>>7)) Z(r[0],gf0,r[0]); + M(r[3],r[0],r[1]); + return 0; +} + +void ed25519_crypto_sign(u8 *sm,const u8 *m,u32 n,const u8 *sk) { + u8 d[64],h[64],r[64]; + u32 i,j; + i64 x[64]; + gf p[4]; + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + FOR(i,n) sm[64 + i] = m[i]; + FOR(i,32) sm[32 + i] = d[32 + i]; + crypto_hash(r, sm+32, n+32); + reduce(r); + scalarbase(p,r); + pack(sm,p); + FOR(i,32) sm[i+32] = sk[i+32]; + crypto_hash(h,sm,n + 64); + reduce(h); + FOR(i,64) x[i] = 0; + FOR(i,32) x[i] = (u64) r[i]; + FOR(i,32) FOR(j,32) x[i+j] += h[i] * (u64) d[j]; + modL(sm + 32,x); +} + +int ed25519_crypto_verify(u8 *sm,u32 n,const u8 *pk) { + u32 i; + u8 t[32],h[64]; + gf p[4],q[4]; + u8 s[32]; + if (unpackneg(q,pk)) return -1; + FOR(i,32) s[i] = sm[i+32]; + FOR(i,32) sm[i+32] = pk[i]; + crypto_hash(h,sm,n + 64); + reduce(h); + scalarmult(p,q,h); + scalarbase(q,s); + add(p,q); + pack(t,p); + return crypto_verify_32(sm, t); +} + +void ed25519_crypto_getpublic(u8 *pk, const u8 *sk) { + u8 d[64]; + gf p[4]; + crypto_hash(d, sk, 32); + d[0] &= 248; + d[31] &= 127; + d[31] |= 64; + scalarbase(p,d); + pack(pk,p); +} + +#endif /* DROPBEAR_ED25519 */ diff --git a/ed25519_crypto.h b/ed25519_crypto.h new file mode 100644 index 000000000..d8978c122 --- /dev/null +++ b/ed25519_crypto.h @@ -0,0 +1,68 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#ifndef DROPBEAR_ED25519_CRYPTO_H_ +#define DROPBEAR_ED25519_CRYPTO_H_ + +#include "includes.h" +#include "buffer.h" + +#ifdef DROPBEAR_ED25519 + +/* This API is based on the NaCl and TweetNaCl APIs, but it is not + * compatible with them. + */ + +/* Generates a public verification key from a private ed25519 signing key. + * + * Input: private signing key is sk[:32]. It can be any random 32 bytes. + * Output: public verification key is pk[:32]. + */ +void ed25519_crypto_getpublic(uint8_t *pk, const uint8_t *sk); + +/* Signs a message using ed25119. + * + * Input: message is m[:n]. + * Input: private key is sk[:32]. + * Input: public key is sk[32 : 64]. If not available, generate it from + * the private key using ed25519_crypto_getpublic. + * Output: signature is sm[:64]. + * Output: copy of message is sm[64 : 64 + n]. + */ +void ed25519_crypto_sign(uint8_t *sm, const uint8_t *m, uint32_t n, + const uint8_t *sk); + +/* Verifies a signed ed25119 message. + * + * Input: signature is sm[:64]. + * Input: message is sm[64 : 64 + n]. + * Input: public key is pk[:32]. + * Output: success (i.e. good signature) is indicated by returning 0. + * Output: as a side effect, overwrites (with any bytes) sm[32 : 64]. + */ +int ed25519_crypto_verify(uint8_t *sm, uint32_t n, const uint8_t *pk); + +#endif /* DROPBEAR_ED25519 */ + +#endif /* DROPBEAR_ED25519_CRYPTO_H_ */ diff --git a/filelist.txt b/filelist.txt index 8281c1402..823342e56 100644 --- a/filelist.txt +++ b/filelist.txt @@ -99,6 +99,8 @@ rsa.c RSA asymmetric crypto routines dss.c DSS asymmetric crypto routines +ed25519.c ed25519 asymmetric crypto routines + gendss.c DSS key generation genrsa.c RSA key generation diff --git a/gened25519.c b/gened25519.c new file mode 100644 index 000000000..06713c910 --- /dev/null +++ b/gened25519.c @@ -0,0 +1,44 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#include "includes.h" +#include "dbrandom.h" +#include "dbutil.h" +#include "ed25519_crypto.h" +#include "gened25519.h" + +#ifdef DROPBEAR_ED25519 + +dropbear_ed25519_key * gen_ed25519_priv_key(unsigned int size) { + dropbear_ed25519_key *key; + if (size != 256) { + dropbear_exit("ed25519 keys have a fixed size of 256 bits"); + } + key = m_malloc(sizeof(*key)); + genrandom(key->spk, 32); + ed25519_crypto_getpublic(key->spk + 32, key->spk); + return key; +} + +#endif /* DROPBEAR_ED25519 */ diff --git a/gened25519.h b/gened25519.h new file mode 100644 index 000000000..859fb1f71 --- /dev/null +++ b/gened25519.h @@ -0,0 +1,37 @@ +/* + * Dropbear - a SSH2 server + * + * Copyright (c) 2002,2003 Matt Johnston + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. */ + +#ifndef DROPBEAR_GENED25519_H_ +#define DROPBEAR_GENED25519_H_ + +#include "includes.h" +#include "ed25519.h" + +#ifdef DROPBEAR_ED25519 + +dropbear_ed25519_key * gen_ed25519_priv_key(unsigned int size); + +#endif /* DROPBEAR_ED25519 */ + +#endif /* DROPBEAR_GENED25519_H_ */ diff --git a/gensignkey.c b/gensignkey.c index 8317feaf1..089cad645 100644 --- a/gensignkey.c +++ b/gensignkey.c @@ -4,6 +4,7 @@ #include "ecdsa.h" #include "genrsa.h" #include "gendss.h" +#include "gened25519.h" #include "signkey.h" #include "dbrandom.h" @@ -68,6 +69,10 @@ static int get_default_bits(enum signkey_type keytype) return 384; case DROPBEAR_SIGNKEY_ECDSA_NISTP256: return 256; +#endif +#ifdef DROPBEAR_ED25519 + case DROPBEAR_SIGNKEY_ED25519: + return 256; #endif default: return 0; @@ -118,6 +123,11 @@ int signkey_generate(enum signkey_type keytype, int bits, const char* filename, *signkey_key_ptr(key, keytype) = ecckey; } break; +#endif +#ifdef DROPBEAR_ED25519 + case DROPBEAR_SIGNKEY_ED25519: + key->ed25519key = gen_ed25519_priv_key(bits); + break; #endif default: dropbear_exit("Internal error"); diff --git a/keyimport.c b/keyimport.c index ea3164c95..cda678374 100644 --- a/keyimport.c +++ b/keyimport.c @@ -352,7 +352,7 @@ struct mpint_pos { void *start; int bytes; }; * Code to read and write OpenSSH private keys. */ -enum { OSSH_DSA, OSSH_RSA, OSSH_EC }; +enum { OSSH_DSA, OSSH_RSA, OSSH_EC, OSSH_OSSH }; struct openssh_key { int type; int encrypted; @@ -365,7 +365,14 @@ static struct openssh_key *load_openssh_key(const char *filename) { struct openssh_key *ret; FILE *fp = NULL; - char buffer[256]; + char buffer0[256], *buffer = buffer0 + 4; + /* This is to support base64-encoded lines with base64 digit count + * not divisible by 4. `BEGIN OPENSSH PRIVATE KEY' has it. + * + * Number of extra characters to prepend for base64 encoding. Can be 0, 1, 2 or 3. + * They are in buffer - b64extra ... buffer. + */ + int b64extra = 0, b64len, b64xlen; char *errmsg = NULL, *p = NULL; int headers_done; unsigned long len, outlen; @@ -385,7 +392,7 @@ static struct openssh_key *load_openssh_key(const char *filename) errmsg = "Unable to open key file"; goto error; } - if (!fgets(buffer, sizeof(buffer), fp) || + if (!fgets(buffer, sizeof(buffer0) - 4, fp) || 0 != strncmp(buffer, "-----BEGIN ", 11) || 0 != strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) { errmsg = "File does not begin with OpenSSH key header"; @@ -397,17 +404,25 @@ static struct openssh_key *load_openssh_key(const char *filename) ret->type = OSSH_DSA; else if (!strcmp(buffer, "-----BEGIN EC PRIVATE KEY-----\n")) ret->type = OSSH_EC; + else if (!strcmp(buffer, "-----BEGIN OPENSSH PRIVATE KEY-----\n")) + ret->type = OSSH_OSSH; else { errmsg = "Unrecognised key type"; goto error; } headers_done = 0; + buffer = buffer0 + 4; while (1) { - if (!fgets(buffer, sizeof(buffer), fp)) { + if (!fgets(buffer, sizeof(buffer0) - 4, fp) || buffer[0] == '\0') { errmsg = "Unexpected end of file"; goto error; } + len = strlen(buffer); + if (len == sizeof(buffer) - 5) { + errmsg = "Line too long"; + goto error; + } if (0 == strncmp(buffer, "-----END ", 9) && 0 == strcmp(buffer+strlen(buffer)-17, "PRIVATE KEY-----\n")) break; /* done */ @@ -447,7 +462,6 @@ static struct openssh_key *load_openssh_key(const char *filename) } } else { headers_done = 1; - len = strlen(buffer); outlen = len*4/3; if (ret->keyblob_len + outlen > ret->keyblob_size) { ret->keyblob_size = ret->keyblob_len + outlen + 256; @@ -455,14 +469,23 @@ static struct openssh_key *load_openssh_key(const char *filename) ret->keyblob_size); } outlen = ret->keyblob_size - ret->keyblob_len; - if (base64_decode((const unsigned char *)buffer, len, - ret->keyblob + ret->keyblob_len, &outlen) != CRYPT_OK){ + if (buffer[len - 1] == '\n') buffer[--len] = '\0'; + b64len = len + b64extra; + b64xlen = b64len & ~3; + if (b64xlen != 0 && base64_decode((const unsigned char *)(buffer - b64extra), b64xlen, ret->keyblob + ret->keyblob_len, &outlen) != CRYPT_OK) { errmsg = "Error decoding base64"; goto error; } + if ((b64extra = (b64len & 3)) != 0) { + memcpy(buffer - b64extra, buffer + b64xlen, b64extra); + } ret->keyblob_len += outlen; } } + if (b64extra) { + errmsg = "bas64 junk in the end"; + goto error; + } if (ret->keyblob_len == 0 || !ret->keyblob) { errmsg = "Key body not present"; @@ -474,11 +497,11 @@ static struct openssh_key *load_openssh_key(const char *filename) goto error; } - m_burn(buffer, sizeof(buffer)); + m_burn(buffer0, sizeof(buffer0)); return ret; error: - m_burn(buffer, sizeof(buffer)); + m_burn(buffer0, sizeof(buffer0)); if (ret) { if (ret->keyblob) { m_burn(ret->keyblob, ret->keyblob_size); @@ -569,6 +592,29 @@ static sign_key *openssh_read(const char *filename, const char * UNUSED(passphra #endif } + p = key->keyblob; + len = key->keyblob_len; + if (key->type != OSSH_OSSH) { +#ifdef DROPBEAR_ED25519 + } else if (len >= 64 && 0 == memcmp(p, "openssh-key-v1\0\0\0\0\4none\0\0\0\4none\0\0\0\0\0\0\0\1\0\0\0\x33\0\0\0\x0bssh-ed25519\0\0\0 ", 62)) { + if (len < 229 || + 0 != memcmp(p + 106, "\0\0\0\x0bssh-ed25519\0\0\0 ", 19) || + 0 != memcmp(p + 157, "\0\0\0@", 4)) { + errmsg = "bad ssh-ed25519 private key"; + goto error; + } + retkey->type = DROPBEAR_SIGNKEY_ED25519; + retkey->ed25519key = m_malloc(sizeof(*retkey->ed25519key)); + memcpy(retkey->ed25519key->spk, p + 161, 64); + /* At p[225:], there is struct.pack('>L', len(comment)) + comment, but there is no way for us to return it in retkey. */ + goto no_error; +#endif + } else { + errmsg = "unknown OpenSSH private key type"; + goto error; + } + + /* * Now we have a decrypted key blob, which contains an ASN.1 * encoded private key. We must now untangle the ASN.1. @@ -587,10 +633,9 @@ static sign_key *openssh_read(const char *filename, const char * UNUSED(passphra * order. */ - p = key->keyblob; /* Expect the SEQUENCE header. Take its absence as a failure to decrypt. */ - ret = ber_read_id_len(p, key->keyblob_len, &id, &len, &flags); + ret = ber_read_id_len(p, len, &id, &len, &flags); p += ret; if (ret < 0 || id != 16 || len < 0 || key->keyblob+key->keyblob_len-p < len) { @@ -809,6 +854,9 @@ static sign_key *openssh_read(const char *filename, const char * UNUSED(passphra } } +#ifdef DROPBEAR_ED25519 + no_error: +#endif errmsg = NULL; /* no error */ retval = retkey; @@ -845,6 +893,21 @@ static int openssh_write(const char *filename, sign_key *key, mp_int dmp1, dmq1, iqmp, tmpval; /* for rsa */ #endif +#ifdef DROPBEAR_ED25519 + if (key->type == DROPBEAR_SIGNKEY_ED25519) { + const char *spk = key->ed25519key->spk; + header = "-----BEGIN OPENSSH PRIVATE KEY-----\n"; + footer = "-----END OPENSSH PRIVATE KEY-----\n"; + outblob = m_malloc(outlen = 234); + /* checkstr is "Dror", mentioned twice below. */ + memcpy(outblob, "openssh-key-v1\0\0\0\0\x04none\0\0\0\x04none\0\0\0\0\0\0\0\x01\0\0\0""3\0\0\0\x0bssh-ed25519\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x88""DrorDror\0\0\0\x0bssh-ed25519\0\0\0 \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0@\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\x01\x02\x03\x04\x05", 234); + memcpy(outblob + 161, spk, 64); /* private_key + public_key. */ + memcpy(outblob + 62, spk + 32, 32); /* public_key. */ + memcpy(outblob + 125, spk + 32, 32); /* public_key. */ + goto write_file; + } +#endif + if ( #if DROPBEAR_RSA key->type == DROPBEAR_SIGNKEY_RSA || @@ -1157,6 +1220,10 @@ static int openssh_write(const char *filename, sign_key *key, goto error; } +#ifdef DROPBEAR_ED25519 + write_file: +#endif + /* * And save it. We'll use Unix line endings just in case it's * subsequently transferred in binary mode. diff --git a/signkey.c b/signkey.c index 88f06c762..425e0b018 100644 --- a/signkey.c +++ b/signkey.c @@ -39,8 +39,11 @@ static const char * const signkey_names[DROPBEAR_SIGNKEY_NUM_NAMED] = { #if DROPBEAR_ECDSA "ecdsa-sha2-nistp256", "ecdsa-sha2-nistp384", - "ecdsa-sha2-nistp521" + "ecdsa-sha2-nistp521", #endif /* DROPBEAR_ECDSA */ +#ifdef DROPBEAR_ED25519 + "ssh-ed25519", +#endif }; /* malloc a new sign_key and set the dss and rsa keys to NULL */ @@ -128,6 +131,10 @@ signkey_key_ptr(sign_key *key, enum signkey_type type) { #if DROPBEAR_DSS case DROPBEAR_SIGNKEY_DSS: return (void**)&key->dsskey; +#endif +#ifdef DROPBEAR_ED25519 + case DROPBEAR_SIGNKEY_ED25519: + return (void**)&key->ed25519key; #endif default: return NULL; @@ -200,6 +207,16 @@ int buf_get_pub_key(buffer *buf, sign_key *key, enum signkey_type *type) { } } #endif +#ifdef DROPBEAR_ED25519 + if (keytype == DROPBEAR_SIGNKEY_ED25519) { + ed25519_key_free(key->ed25519key); + key->ed25519key = m_malloc(sizeof(*key->ed25519key)); + ret = buf_get_ed25519_pub_key(buf, key->ed25519key); + if (ret == DROPBEAR_FAILURE) { + m_free(key->ed25519key); + } + } +#endif TRACE2(("leave buf_get_pub_key")) @@ -270,6 +287,16 @@ int buf_get_priv_key(buffer *buf, sign_key *key, enum signkey_type *type) { } } #endif +#ifdef DROPBEAR_ED25519 + if (keytype == DROPBEAR_SIGNKEY_ED25519) { + ed25519_key_free(key->ed25519key); + key->ed25519key = m_malloc(sizeof(*key->ed25519key)); + ret = buf_get_ed25519_priv_key(buf, key->ed25519key); + if (ret == DROPBEAR_FAILURE) { + m_free(key->ed25519key); + } + } +#endif TRACE2(("leave buf_get_priv_key")) @@ -302,6 +329,11 @@ void buf_put_pub_key(buffer* buf, sign_key *key, enum signkey_type type) { buf_put_ecdsa_pub_key(pubkeys, *eck); } } +#endif +#ifdef DROPBEAR_ED25519 + if (type == DROPBEAR_SIGNKEY_ED25519) { + buf_put_ed25519_pub_key(pubkeys, key->ed25519key); + } #endif if (pubkeys->len == 0) { dropbear_exit("Bad key types in buf_put_pub_key"); @@ -341,6 +373,13 @@ void buf_put_priv_key(buffer* buf, sign_key *key, enum signkey_type type) { return; } } +#endif +#ifdef DROPBEAR_ED25519 + if (type == DROPBEAR_SIGNKEY_ED25519) { + buf_put_ed25519_priv_key(buf, key->ed25519key); + TRACE(("leave buf_put_priv_key: ed25519 done")) + return; + } #endif dropbear_exit("Bad key types in put pub key"); } @@ -379,6 +418,10 @@ void sign_key_free(sign_key *key) { key->ecckey521 = NULL; } #endif +#endif +#ifdef DROPBEAR_ED25519 + ed25519_key_free(key->ed25519key); + key->ed25519key = NULL; #endif m_free(key->filename); @@ -503,6 +546,11 @@ void buf_put_sign(buffer* buf, sign_key *key, enum signkey_type type, buf_put_ecdsa_sign(sigblob, *eck, data_buf); } } +#endif +#ifdef DROPBEAR_ED25519 + if (type == DROPBEAR_SIGNKEY_ED25519) { + buf_put_ed25519_sign(sigblob, key->ed25519key, data_buf); + } #endif if (sigblob->len == 0) { dropbear_exit("Non-matching signing type"); @@ -555,6 +603,14 @@ int buf_verify(buffer * buf, sign_key *key, const buffer *data_buf) { } } #endif +#ifdef DROPBEAR_ED25519 + if (type == DROPBEAR_SIGNKEY_ED25519) { + if (key->ed25519key == NULL) { + dropbear_exit("No ed25519 key to verify signature"); + } + return buf_ed25519_verify(buf, key->ed25519key, data_buf); + } +#endif dropbear_exit("Non-matching signing type"); return DROPBEAR_FAILURE; diff --git a/signkey.h b/signkey.h index 59df3ee5c..ee1eec805 100644 --- a/signkey.h +++ b/signkey.h @@ -28,6 +28,7 @@ #include "buffer.h" #include "dss.h" #include "rsa.h" +#include "ed25519.h" enum signkey_type { #if DROPBEAR_RSA @@ -41,6 +42,9 @@ enum signkey_type { DROPBEAR_SIGNKEY_ECDSA_NISTP384, DROPBEAR_SIGNKEY_ECDSA_NISTP521, #endif /* DROPBEAR_ECDSA */ +#ifdef DROPBEAR_ED25519 + DROPBEAR_SIGNKEY_ED25519, +#endif DROPBEAR_SIGNKEY_NUM_NAMED, DROPBEAR_SIGNKEY_ECDSA_KEYGEN = 70, /* just "ecdsa" for keygen */ DROPBEAR_SIGNKEY_ANY = 80, @@ -78,6 +82,9 @@ struct SIGN_key { ecc_key * ecckey521; #endif #endif +#ifdef DROPBEAR_ED25519 + dropbear_ed25519_key * ed25519key; +#endif }; typedef struct SIGN_key sign_key; diff --git a/ssh.h b/ssh.h index f40b65ae7..db723b813 100644 --- a/ssh.h +++ b/ssh.h @@ -105,6 +105,8 @@ #define SSH_SIGNKEY_DSS_LEN 7 #define SSH_SIGNKEY_RSA "ssh-rsa" #define SSH_SIGNKEY_RSA_LEN 7 +#define SSH_SIGNKEY_ED25519 "ssh-ed25519" +#define SSH_SIGNKEY_ED25519_LEN 11 /* Agent commands. These aren't part of the spec, and are defined * only on the openssh implementation. */ diff --git a/svr-runopts.c b/svr-runopts.c index d6c78df4e..c8580aa40 100644 --- a/svr-runopts.c +++ b/svr-runopts.c @@ -511,6 +511,13 @@ static void loadhostkey(const char *keyfile, int fatal_duplicate) { } #endif #endif /* DROPBEAR_ECDSA */ + +#ifdef DROPBEAR_ED25519 + if (type == DROPBEAR_SIGNKEY_ED25519) { + loadhostkey_helper("ed25519", (void**)&read_key->ed25519key, (void**)&svr_opts.hostkey->ed25519key, fatal_duplicate); + } +#endif + sign_key_free(read_key); TRACE(("leave loadhostkey")) } @@ -575,7 +582,6 @@ void load_all_hostkeys() { - If there is a ecdsa hostkey at startup we choose that that size. - If we generate at runtime we choose the default ecdsa size. - Otherwise no ecdsa keys will be advertised */ - /* check if any keys were loaded at startup */ loaded_any_ecdsa = 0 @@ -615,6 +621,14 @@ void load_all_hostkeys() { #endif #endif /* DROPBEAR_ECDSA */ +#ifdef DROPBEAR_ED25519 + if (!svr_opts.delay_hostkey && !svr_opts.hostkey->ed25519key) { + disablekey(DROPBEAR_SIGNKEY_ED25519); + } else { + any_keys = 1; + } +#endif + if (!any_keys) { dropbear_exit("No hostkeys available. 'dropbear -R' may be useful or run dropbearkey."); } diff --git a/svr-session.c b/svr-session.c index a81639841..8ea8868ff 100644 --- a/svr-session.c +++ b/svr-session.c @@ -28,7 +28,6 @@ #include "packet.h" #include "algo.h" #include "buffer.h" -#include "dss.h" #include "ssh.h" #include "dbrandom.h" #include "kex.h" diff --git a/sysoptions.h b/sysoptions.h index 5bdb3e3e3..3e9dfb84a 100644 --- a/sysoptions.h +++ b/sysoptions.h @@ -184,7 +184,7 @@ If you test it please contact the Dropbear author */ /* For a 4096 bit DSS key, empirically determined */ #define MAX_PRIVKEY_SIZE 1700 -#define MAX_HOSTKEYS 3 +#define MAX_HOSTKEYS 4 /* The maximum size of the bignum portion of the kexhash buffer */ /* Sect. 8 of the transport rfc 4253, K_S + e + f + K */ @@ -247,7 +247,7 @@ If you test it please contact the Dropbear author */ #error "At least one encryption algorithm must be enabled. AES128 is recommended." #endif -#if !(DROPBEAR_RSA || DROPBEAR_DSS || DROPBEAR_ECDSA) +#if !(DROPBEAR_RSA || DROPBEAR_DSS || DROPBEAR_ECDSA || DROPBEAR_ED25519) #error "At least one hostkey or public-key algorithm must be enabled; RSA is recommended." #endif