diff --git a/apps/wolfssh/common.c b/apps/wolfssh/common.c index 3945866c2..7d0ccb44d 100644 --- a/apps/wolfssh/common.c +++ b/apps/wolfssh/common.c @@ -379,7 +379,7 @@ int ClientSetEcho(int type) newTerm.c_lflag &= ~(ICANON | ECHOE | ECHOK | ECHONL | ISIG); } else { - newTerm.c_lflag |= (ICANON | ECHONL); + newTerm.c_lflag |= ICANON; } if (tcsetattr(STDIN_FILENO, TCSANOW, &newTerm) != 0) { diff --git a/keys/id_ecdsa b/keys/id_ecdsa new file mode 100644 index 000000000..a73f1d4fe --- /dev/null +++ b/keys/id_ecdsa @@ -0,0 +1,9 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS +1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTAqdBgCp8bYSq2kQQ48/Ud8Iy6Mjnb +/fpB3LfSE/1kx9VaaE4FL3i9Gg2vDV0eLGM3PWksFNPhULxtcYJyjaBjAAAAqJAeleSQHp +XkAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMCp0GAKnxthKraR +BDjz9R3wjLoyOdv9+kHct9IT/WTH1VpoTgUveL0aDa8NXR4sYzc9aSwU0+FQvG1xgnKNoG +MAAAAgPrOgktioNqad/wHNC/rt/zVrpNqDnOwg9tNDFMOTwo8AAAANYm9iQGxvY2FsaG9z +dAECAw== +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/id_ecdsa.pub b/keys/id_ecdsa.pub new file mode 100644 index 000000000..22fb1dc32 --- /dev/null +++ b/keys/id_ecdsa.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMCp0GAKnxthKraRBDjz9R3wjLoyOdv9+kHct9IT/WTH1VpoTgUveL0aDa8NXR4sYzc9aSwU0+FQvG1xgnKNoGM= bob@localhost diff --git a/keys/id_rsa b/keys/id_rsa new file mode 100644 index 000000000..b1cd15885 --- /dev/null +++ b/keys/id_rsa @@ -0,0 +1,27 @@ +-----BEGIN OPENSSH PRIVATE KEY----- +b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn +NhAAAAAwEAAQAAAQEAy2cigZDlpBT+X2MJHAoHnfeFf6+LHm6BDkAT8V9ejHA4dY0Aepb6 +NbV6u/oYZlueKPeAZ3GNztR9szoL6FSlMvkd9oqvfoxjTGu71T0981ybJelqqGATGtevHU +6Jko/I0+lgSQFKWQJ7D3Dj2zlZpIXB2Q7xl/i9kFZgaIqFhUHdWO9JMOwCFwoDrhd8v5xk +y1v3OIIZDxiYxVIKbf2J07WbwiSFAxXfiX8TjUBDLFmtqt1AF6LjAyGyaRICXkaGJQ/QJ9 +sX85h9bkiPlGNAtQGQtNUg3tC9GqOkZ9tCKY1Efh/r0zosOA7ufxg6ymLpq1C4LU/4ENGH +kuRPAKvu8wAAA8gztJfmM7SX5gAAAAdzc2gtcnNhAAABAQDLZyKBkOWkFP5fYwkcCged94 +V/r4seboEOQBPxX16McDh1jQB6lvo1tXq7+hhmW54o94BncY3O1H2zOgvoVKUy+R32iq9+ +jGNMa7vVPT3zXJsl6WqoYBMa168dTomSj8jT6WBJAUpZAnsPcOPbOVmkhcHZDvGX+L2QVm +BoioWFQd1Y70kw7AIXCgOuF3y/nGTLW/c4ghkPGJjFUgpt/YnTtZvCJIUDFd+JfxONQEMs +Wa2q3UAXouMDIbJpEgJeRoYlD9An2xfzmH1uSI+UY0C1AZC01SDe0L0ao6Rn20IpjUR+H+ +vTOiw4Du5/GDrKYumrULgtT/gQ0YeS5E8Aq+7zAAAAAwEAAQAAAQEAvbdBiQXkGyn1pHST +/5IfTqia3OCX6td5ChicQUsJvgXBs2rDopQFZmkRxBjd/0K+/0jyfAl/EgZCBBRFHPsuZp +/S4ayzSV6aE6J8vMT1bnLWxwKyl7+csjGwRK6HRKtVzsnjI9TPSrw0mc9ax5PzV6/mgZUd +o/i+nszh+UASj5mYrBGqMiINspzX6YC+qoUHor3rEJOd9p1aO+N5+1fDKiDnlkM5IO0Qsz +GktuwL0fzv9zBnGfnWVJz3CorfP1OW5KCtrDn7BnkQf1eBeVLzq/uoglUjS4DNnVfLA67D +O4ZfwtnoW8Gr2R+KdvnypvHnDeY5X51r5PDgL4+7z47pWQAAAIBNFcAzHHE19ISGN8YRHk +23/r/3zfvzHU68GSKR1Xj/Y4LSdRTpSm3wBrdQ17f5B4V7RVl2CJvoPekTggnBDQlLJ7fU +NU93/nZrY9teYdrNh03buL54VVb5tUM+KN+27zERlTj0/LmYJupN97sZXmlgKsvLbcsnM2 +i7HuQQaFnsIQAAAIEA5wqFVatT9yovt8pS7rAyYUL/cqc50TZ/5Nwfy5uasRyf1BphHwEW +LEimBemVc+VrNwAkt6MFWuloK5ssqb1ubvtRI8Mntd15rRfZtq/foS3J8FJxueXLDWlECy +PmVyfVN1Vv4ZeirBy9BTYLiSuxMes+HYks3HucQhxIN1j8SA0AAACBAOFgRjfWXv1/93Jp +6CCJ5c98MWP+zu1FbLIlklxPb85osZqlazXHNPPEtblC4z+OqRGMCsv2683anU4ZzcTFIk +JS3lzeJ3tdAH4osQ5etKkV4mcdCmeRpjudB9VbaziVhPX02qkPWpM0ckPrgB3hVNUDPz89 +GtJd3mlhyY5IfFL/AAAADWJvYkBsb2NhbGhvc3QBAgMEBQ== +-----END OPENSSH PRIVATE KEY----- diff --git a/keys/id_rsa.pub b/keys/id_rsa.pub new file mode 100644 index 000000000..9cf541905 --- /dev/null +++ b/keys/id_rsa.pub @@ -0,0 +1 @@ +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLZyKBkOWkFP5fYwkcCged94V/r4seboEOQBPxX16McDh1jQB6lvo1tXq7+hhmW54o94BncY3O1H2zOgvoVKUy+R32iq9+jGNMa7vVPT3zXJsl6WqoYBMa168dTomSj8jT6WBJAUpZAnsPcOPbOVmkhcHZDvGX+L2QVmBoioWFQd1Y70kw7AIXCgOuF3y/nGTLW/c4ghkPGJjFUgpt/YnTtZvCJIUDFd+JfxONQEMsWa2q3UAXouMDIbJpEgJeRoYlD9An2xfzmH1uSI+UY0C1AZC01SDe0L0ao6Rn20IpjUR+H+vTOiw4Du5/GDrKYumrULgtT/gQ0YeS5E8Aq+7z bob@localhost diff --git a/keys/include.am b/keys/include.am index 5f314bb05..cc2aa720f 100644 --- a/keys/include.am +++ b/keys/include.am @@ -22,5 +22,6 @@ EXTRA_DIST+= \ keys/server-cert.der keys/server-cert.pem \ keys/fred-cert.der keys/fred-cert.pem \ keys/server-key.pem keys/fred-key.der keys/fred-key.pem \ + keys/id_ecdsa keys/id_ecdsa.pub keys/id_rsa keys/id_rsa.pub \ keys/renewcerts.sh keys/renewcerts.cnf diff --git a/src/internal.c b/src/internal.c index f8732546e..2b722f7d8 100644 --- a/src/internal.c +++ b/src/internal.c @@ -137,6 +137,8 @@ Set when all ECDH algorithms are disabled. Set to disable use of all ECDH algorithms for key agreement. Setting this will force all ECDH key agreement algorithms off. + WOLFSSH_KEY_QUANTITY_REQ + Number of keys required to be in an OpenSSH-style key wrapper. */ static const char sshProtoIdStr[] = "SSH-2.0-wolfSSHv" @@ -419,6 +421,15 @@ const char* GetErrorString(int err) case WS_MATCH_UA_KEY_ID_E: return "unable to match user auth key type"; + case WS_KEY_AUTH_MAGIC_E: + return "key auth magic check error"; + + case WS_KEY_CHECK_VAL_E: + return "key check value error"; + + case WS_KEY_FORMAT_E: + return "key format wrong error"; + default: return "Unknown error code"; } @@ -845,21 +856,51 @@ void SshResourceFree(WOLFSSH* ssh, void* heap) #endif } -union wolfSSH_key { + +typedef struct WS_KeySignature { + byte keySigId; + word32 sigSz; + const char *name; + word32 nameSz; + union { +#ifndef WOLFSSH_NO_RSA + struct { + RsaKey key; + } rsa; +#endif +#ifndef WOLFSSH_NO_ECDSA + struct { + ecc_key key; + } ecc; +#endif + } ks; +} WS_KeySignature; + + +static void wolfSSH_KEY_clean(WS_KeySignature* key) +{ + if (key != NULL) { + if (key->keySigId == ID_SSH_RSA) { #ifndef WOLFSSH_NO_RSA - RsaKey rsa; + wc_FreeRsaKey(&key->ks.rsa.key); #endif + } + else if (key->keySigId == ID_ECDSA_SHA2_NISTP256 || + key->keySigId == ID_ECDSA_SHA2_NISTP384 || + key->keySigId == ID_ECDSA_SHA2_NISTP521) { #ifndef WOLFSSH_NO_ECDSA - ecc_key ecc; + wc_ecc_free(&key->ks.ecc.key); #endif -}; + } + } +} /* - * Identifies the flavor of a key, RSA or ECDSA, and returns the key type ID. - * The process is to decode the key as if it was RSA and if that fails try - * to load it as if ECDSA. Both public and private keys can be decoded. - * For RSA keys, the key format is described as "ssh-rsa". + * Identifies the flavor of an ASN.1 key, RSA or ECDSA, and returns the key + * type ID. The process is to decode the key as if it was RSA and if that + * fails try to load it as if ECDSA. Both public and private keys can be + * decoded. For RSA keys, the key format is described as "ssh-rsa". * * @param in key to identify * @param inSz size of key @@ -867,88 +908,369 @@ union wolfSSH_key { * @param heap heap to use for memory allocation * @return keyId as int, WS_MEMORY_E, WS_UNIMPLEMENTED_E */ -int IdentifyKey(const byte* in, word32 inSz, int isPrivate, void* heap) +int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap) { - union wolfSSH_key *key = NULL; - int keyId = ID_UNKNOWN; + WS_KeySignature *key = NULL; word32 idx; int ret; int dynType = isPrivate ? DYNTYPE_PRIVKEY : DYNTYPE_PUBKEY; WOLFSSH_UNUSED(dynType); - key = (union wolfSSH_key*)WMALLOC(sizeof(union wolfSSH_key), heap, dynType); + key = (WS_KeySignature*)WMALLOC(sizeof(WS_KeySignature), heap, dynType); + + if (key == NULL) { + ret = WS_MEMORY_E; + } + else { + WMEMSET(key, 0, sizeof(*key)); + key->keySigId = ID_UNKNOWN; #ifndef WOLFSSH_NO_RSA - if (key != NULL) { /* Check RSA key */ - if (keyId == ID_UNKNOWN) { + if (key->keySigId == ID_UNKNOWN) { idx = 0; - ret = wc_InitRsaKey(&key->rsa, NULL); + ret = wc_InitRsaKey(&key->ks.rsa.key, NULL); if (ret == 0) { if (isPrivate) { - ret = wc_RsaPrivateKeyDecode(in, &idx, &key->rsa, inSz); + ret = wc_RsaPrivateKeyDecode(in, &idx, + &key->ks.rsa.key, inSz); } else { - ret = wc_RsaPublicKeyDecode(in, &idx, &key->rsa, inSz); + ret = wc_RsaPublicKeyDecode(in, &idx, + &key->ks.rsa.key, inSz); } /* If decode was successful, this is an RSA key. */ if (ret == 0) { - keyId = ID_SSH_RSA; + key->keySigId = ID_SSH_RSA; } } - wc_FreeRsaKey(&key->rsa); + wc_FreeRsaKey(&key->ks.rsa.key); } - } #endif /* WOLFSSH_NO_RSA */ #ifndef WOLFSSH_NO_ECDSA - if (key != NULL) { /* Check ECDSA key */ - if (keyId == ID_UNKNOWN) { + if (key->keySigId == ID_UNKNOWN) { idx = 0; - ret = wc_ecc_init_ex(&key->ecc, heap, INVALID_DEVID); + ret = wc_ecc_init_ex(&key->ks.ecc.key, heap, INVALID_DEVID); if (ret == 0) { if (isPrivate) { - ret = wc_EccPrivateKeyDecode(in, &idx, &key->ecc, inSz); + ret = wc_EccPrivateKeyDecode(in, &idx, + &key->ks.ecc.key, inSz); } else { - ret = wc_EccPublicKeyDecode(in, &idx, &key->ecc, inSz); + ret = wc_EccPublicKeyDecode(in, &idx, + &key->ks.ecc.key, inSz); } /* If decode was successful, this is an ECDSA key. */ if (ret == 0) { - switch (wc_ecc_get_curve_id(key->ecc.idx)) { + switch (wc_ecc_get_curve_id(key->ks.ecc.key.idx)) { case ECC_SECP256R1: - keyId = ID_ECDSA_SHA2_NISTP256; + key->keySigId = ID_ECDSA_SHA2_NISTP256; break; case ECC_SECP384R1: - keyId = ID_ECDSA_SHA2_NISTP384; + key->keySigId = ID_ECDSA_SHA2_NISTP384; break; case ECC_SECP521R1: - keyId = ID_ECDSA_SHA2_NISTP521; + key->keySigId = ID_ECDSA_SHA2_NISTP521; break; } } } - wc_ecc_free(&key->ecc); + wc_ecc_free(&key->ks.ecc.key); } - } #endif /* WOLFSSH_NO_ECDSA */ + if (key->keySigId == ID_UNKNOWN) { + ret = WS_UNIMPLEMENTED_E; + } + else { + ret = key->keySigId; + } + + WFREE(key, heap, dynType); + } + + return ret; +} + + +/* + * Utility function to read an Mpint from the stream directly into a mp_int. + */ +static INLINE int GetMpintToMp(mp_int* mp, + const byte* buf, word32 len, word32* idx) +{ + const byte* val = NULL; + word32 valSz = 0; + int ret; + + ret = GetMpint(&valSz, &val, buf, len, idx); + if (ret == WS_SUCCESS) + ret = mp_read_unsigned_bin(mp, val, valSz); + + return ret; +} + + +/* + * For the given RSA key, calculate p^-1 and q^-1. wolfCrypt's RSA + * code expects them, but the OpenSSH format key doesn't store them. + * TODO: Add a RSA read function to wolfCrypt to handle this condition. + */ +static INLINE int CalcRsaInverses(RsaKey* key) +{ + mp_int m; + int ret; + + ret = mp_init(&m); + if (ret == MP_OKAY) { + ret = mp_sub_d(&key->p, 1, &m); + if (ret == MP_OKAY) + ret = mp_mod(&key->d, &m, &key->dP); + if (ret == MP_OKAY) + ret = mp_sub_d(&key->q, 1, &m); + if (ret == MP_OKAY) + ret = mp_mod(&key->d, &m, &key->dQ); + mp_forcezero(&m); + } + + return ret; +} + + +/* + * Utility for GetOpenSshKey() to read in RSA keys. + */ +static int GetOpenSshKeyRsa(RsaKey* key, + const byte* buf, word32 len, word32* idx) +{ + int ret; + + ret = wc_InitRsaKey(key, NULL); + if (ret == WS_SUCCESS) + ret = GetMpintToMp(&key->n, buf, len, idx); + if (ret == WS_SUCCESS) + ret = GetMpintToMp(&key->e, buf, len, idx); + if (ret == WS_SUCCESS) + ret = GetMpintToMp(&key->d, buf, len, idx); + if (ret == WS_SUCCESS) + ret = GetMpintToMp(&key->u, buf, len, idx); + if (ret == WS_SUCCESS) + ret = GetMpintToMp(&key->p, buf, len, idx); + if (ret == WS_SUCCESS) + ret = GetMpintToMp(&key->q, buf, len, idx); + + /* Calculate dP and dQ for wolfCrypt. */ + if (ret == WS_SUCCESS) + ret = CalcRsaInverses(key); + + if (ret != WS_SUCCESS) + ret = WS_RSA_E; + + return ret; +} + + +/* + * Utility for GetOpenSshKey() to read in ECDSA keys. + */ +static int GetOpenSshKeyEcc(ecc_key* key, + const byte* buf, word32 len, word32* idx) +{ + const byte *name = NULL, *priv = NULL, *pub = NULL; + word32 nameSz = 0, privSz = 0, pubSz = 0; + int ret; + + ret = wc_ecc_init(key); + if (ret == WS_SUCCESS) + ret = GetStringRef(&nameSz, &name, buf, len, idx); /* curve name */ + if (ret == WS_SUCCESS) + ret = GetStringRef(&pubSz, &pub, buf, len, idx); /* Q */ + if (ret == WS_SUCCESS) + ret = GetMpint(&privSz, &priv, buf, len, idx); /* d */ + + if (ret == WS_SUCCESS) + ret = wc_ecc_import_private_key_ex(priv, privSz, pub, pubSz, + key, ECC_CURVE_DEF); + + if (ret != WS_SUCCESS) + ret = WS_ECC_E; + + return ret; +} + + +/* + * Decodes an OpenSSH format key. + */ +static int GetOpenSshKey(WS_KeySignature *key, + const byte* buf, word32 len, word32* idx) +{ + const char AuthMagic[] = "openssh-key-v1"; + const byte* str = NULL; + word32 keyCount = 0, strSz, i; + int ret = WS_SUCCESS; + + if (WSTRCMP(AuthMagic, (const char*)buf) != 0) { + ret = WS_KEY_AUTH_MAGIC_E; + } + + if (ret == WS_SUCCESS) { + *idx += (word32)WSTRLEN(AuthMagic) + 1; + ret = GetSkip(buf, len, idx); /* ciphername */ + } + + if (ret == WS_SUCCESS) + ret = GetSkip(buf, len, idx); /* kdfname */ + + if (ret == WS_SUCCESS) + ret = GetSkip(buf, len, idx); /* kdfoptions */ + + if (ret == WS_SUCCESS) + ret = GetUint32(&keyCount, buf, len, idx); /* key count */ + + if (ret == WS_SUCCESS) { + if (keyCount != WOLFSSH_KEY_QUANTITY_REQ) { + ret = WS_KEY_FORMAT_E; + } + } + + if (ret == WS_SUCCESS) { + strSz = 0; + ret = GetStringRef(&strSz, &str, buf, len, idx); + /* public buf */ + } + + if (ret == WS_SUCCESS) { + strSz = 0; + ret = GetStringRef(&strSz, &str, buf, len, idx); + /* list of private keys */ + + /* If there isn't a private key, the key file is bad. */ + if (ret == WS_SUCCESS && strSz == 0) { + ret = WS_KEY_FORMAT_E; + } + + if (ret == WS_SUCCESS) { + const byte* subStr = NULL; + word32 subStrSz = 0, subIdx = 0, check1 = 0, check2 = ~0; + byte keyId; + + idx = 0; + if (ret == WS_SUCCESS) + ret = GetUint32(&check1, str, strSz, &subIdx); /* checkint 1 */ + if (ret == WS_SUCCESS) + ret = GetUint32(&check2, str, strSz, &subIdx); /* checkint 2 */ + if (ret == WS_SUCCESS) { + if (check1 != check2) { + ret = WS_KEY_CHECK_VAL_E; + } + } + if (ret == WS_SUCCESS) { + for (i = 0; i < keyCount; i++) { + ret = GetStringRef(&subStrSz, &subStr, + str, strSz, &subIdx); + if (ret == WS_SUCCESS) { + keyId = NameToId((const char*)subStr, subStrSz); + key->keySigId = keyId; + } + if (ret == WS_SUCCESS) { + switch (keyId) { + #ifndef WOLFSSH_NO_RSA + case ID_SSH_RSA: + ret = GetOpenSshKeyRsa(&key->ks.rsa.key, + str, strSz, &subIdx); + break; + #endif + #ifndef WOLFSSH_NO_ECDSA + case ID_ECDSA_SHA2_NISTP256: + ret = GetOpenSshKeyEcc(&key->ks.ecc.key, + str, strSz, &subIdx); + break; + #endif + default: + ret = WS_UNIMPLEMENTED_E; + break; + } + if (ret == WS_SUCCESS) + ret = GetSkip(str, strSz, &subIdx); + /* key comment */ + } + } + /* Padding: Add increasing digits to pad to the nearest + * block size. Default block size is 8, but depends on + * the encryption algo. The private key chunk's length, + * and the length of the comment delimit the end of the + * encrypted blob. No added padding required. */ + if (ret == WS_SUCCESS) { + if (strSz % MIN_BLOCK_SZ == 0) { + if (strSz > subIdx) { + /* The padding starts at 1. */ + check2 = strSz - subIdx; + for (check1 = 1; + check1 <= check2; + check1++, subIdx++) { + if (check1 != str[subIdx]) { + /* Bad pad value. */ + } + } + } + } + } + } + } + } + + return ret; +} + + +/* + * Identifies the flavor of an OpenSSH key, RSA or ECDSA, and returns the + * key type ID. The process is to decode the key extracting the identifiers, + * and try to decode the key as the type indicated type. For RSA keys, the + * key format is described as "ssh-rsa". + * + * @param in key to identify + * @param inSz size of key + * @param heap heap to use for memory allocation + * @return keyId as int, WS_MEMORY_E, WS_UNIMPLEMENTED_E, + * WS_INVALID_ALGO_ID + */ +int IdentifyOpenSshKey(const byte* in, word32 inSz, void* heap) +{ + WS_KeySignature *key = NULL; + word32 idx = 0; + int ret; + + key = (WS_KeySignature*)WMALLOC(sizeof(WS_KeySignature), + heap, DYNTYPE_PRIVKEY); + if (key == NULL) { ret = WS_MEMORY_E; } - else if (keyId == ID_UNKNOWN) { - ret = WS_UNIMPLEMENTED_E; - } else { - ret = keyId; + WMEMSET(key, 0, sizeof(*key)); + key->keySigId = ID_NONE; + + ret = GetOpenSshKey(key, in, inSz, &idx); + + if (ret == WS_SUCCESS) { + ret = key->keySigId; + } + else if (key->keySigId == ID_UNKNOWN) { + ret = WS_UNIMPLEMENTED_E; + } + + wolfSSH_KEY_clean(key); + WFREE(key, heap, DYNTYPE_PRIVKEY); } - WFREE(key, heap, dynType); return ret; } @@ -958,7 +1280,7 @@ int IdentifyKey(const byte* in, word32 inSz, int isPrivate, void* heap) /* * Identifies the flavor of an X.509 certificate, RSA or ECDSA, and returns * the key type ID. The process is to decode the certificate and pass the - * public key to IdentifyKey. + * public key to IdentifyAsn1Key. * * @param in certificate to identify * @param inSz size of certificate @@ -1005,7 +1327,7 @@ static int IdentifyCert(const byte* in, word32 inSz, void* heap) } if (ret == 0) { - ret = IdentifyKey(key, keySz, 0, heap); + ret = IdentifyAsn1Key(key, keySz, 0, heap); } WFREE(key, heap, DYNTYPE_PUBKEY); @@ -1288,9 +1610,12 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx, if (ctx == NULL || in == NULL || inSz == 0) return WS_BAD_ARGUMENT; - if (format != WOLFSSH_FORMAT_ASN1 && format != WOLFSSH_FORMAT_PEM && - format != WOLFSSH_FORMAT_RAW) + if (format != WOLFSSH_FORMAT_ASN1 + && format != WOLFSSH_FORMAT_PEM + && format != WOLFSSH_FORMAT_RAW + && format != WOLFSSH_FORMAT_OPENSSH) { return WS_BAD_FILETYPE_E; + } if (type == BUFTYPE_CA) { dynamicType = DYNTYPE_CA; @@ -1341,7 +1666,7 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx, /* Maybe decrypt */ if (type == BUFTYPE_PRIVKEY) { - ret = IdentifyKey(der, derSz, 1, ctx->heap); + ret = IdentifyAsn1Key(der, derSz, 1, ctx->heap); if (ret < 0) { WFREE(der, heap, dynamicType); return ret; @@ -1713,6 +2038,11 @@ static const NameIdPair NameIdMap[] = { /* Ext Info IDs */ { ID_EXTINFO_SERVER_SIG_ALGS, "server-sig-algs" }, + + /* Curve Name IDs */ + { ID_CURVE_NISTP256, "nistp256" }, + { ID_CURVE_NISTP384, "nistp384" }, + { ID_CURVE_NISTP521, "nistp521" }, }; @@ -2397,6 +2727,26 @@ int GetSize(word32* v, const byte* buf, word32 len, word32* idx) } +int GetSkip(const byte* buf, word32 len, word32* idx) +{ + int result; + word32 sz; + + result = GetUint32(&sz, buf, len, idx); + + if (result == WS_SUCCESS) { + result = WS_BUFFER_E; + + if (*idx < len && sz <= len - *idx) { + *idx += sz; + result = WS_SUCCESS; + } + } + + return result; +} + + /* Gets the size of the mpint, and puts the pointer to the start of * buf's number into *mpint. This function does not copy. */ int GetMpint(word32* mpintSz, const byte** mpint, @@ -8814,7 +9164,7 @@ static int SendKexGetSigningKey(WOLFSSH* ssh, sigKeyBlock_ptr->sk.ecc.primeName = PrimeNameForId(ssh->handshake->pubKeyId); sigKeyBlock_ptr->sk.ecc.primeNameSz = - (word32)strlen(sigKeyBlock_ptr->sk.ecc.primeName); + (word32)WSTRLEN(sigKeyBlock_ptr->sk.ecc.primeName); /* Decode the user-configured ECDSA private key. */ sigKeyBlock_ptr->sk.ecc.qSz = @@ -9104,7 +9454,7 @@ int SendKexDhReply(WOLFSSH* ssh) sigKeyBlock_ptr->pubKeyName = IdToName(SigTypeForId(sigKeyBlock_ptr->pubKeyId)); sigKeyBlock_ptr->pubKeyNameSz = - (word32)strlen(sigKeyBlock_ptr->pubKeyName); + (word32)WSTRLEN(sigKeyBlock_ptr->pubKeyName); sigKeyBlock_ptr->pubKeyFmtId = sigKeyBlock_ptr->pubKeyId; if (sigKeyBlock_ptr->pubKeyId == ID_RSA_SHA2_256 || sigKeyBlock_ptr->pubKeyId == ID_RSA_SHA2_512) { @@ -9113,7 +9463,7 @@ int SendKexDhReply(WOLFSSH* ssh) sigKeyBlock_ptr->pubKeyFmtName = IdToName(sigKeyBlock_ptr->pubKeyFmtId); sigKeyBlock_ptr->pubKeyFmtNameSz = - (word32)strlen(sigKeyBlock_ptr->pubKeyFmtName); + (word32)WSTRLEN(sigKeyBlock_ptr->pubKeyFmtName); switch (ssh->handshake->kexId) { #ifndef WOLFSSH_NO_ECDH_SHA2_NISTP256 @@ -10556,40 +10906,6 @@ int SendExtInfo(WOLFSSH* ssh) } -typedef struct WS_KeySignature { - byte keySigId; - word32 sigSz; - const char *name; - word32 nameSz; - union { -#ifndef WOLFSSH_NO_RSA - struct { - RsaKey key; - byte e[256]; - word32 eSz; - byte ePad; - byte n[256]; - word32 nSz; - byte nPad; - } rsa; -#endif -#ifndef WOLFSSH_NO_ECDSA - struct { - ecc_key key; - word32 keyBlobSz; - const char *keyBlobName; - word32 keyBlobNameSz; - byte q[256]; - word32 qSz; - byte qPad; - const char *primeName; - word32 primeNameSz; - } ecc; -#endif - } ks; -} WS_KeySignature; - - /* Updates the payload size, and maybe loads keys. */ static int PrepareUserAuthRequestPassword(WOLFSSH* ssh, word32* payloadSz, const WS_UserAuthData* authData) @@ -10647,15 +10963,24 @@ static int PrepareUserAuthRequestRsa(WOLFSSH* ssh, word32* payloadSz, if (ret == WS_SUCCESS) { word32 idx = 0; #ifdef WOLFSSH_AGENT - if (ssh->agentEnabled) + if (ssh->agentEnabled) { ret = wc_RsaPublicKeyDecode(authData->sf.publicKey.publicKey, &idx, &keySig->ks.rsa.key, authData->sf.publicKey.publicKeySz); + } else #endif + { ret = wc_RsaPrivateKeyDecode(authData->sf.publicKey.privateKey, &idx, &keySig->ks.rsa.key, authData->sf.publicKey.privateKeySz); + if (ret != 0) { + idx = 0; + ret = GetOpenSshKey(keySig, + authData->sf.publicKey.privateKey, + authData->sf.publicKey.privateKeySz, &idx); + } + } } if (ret == WS_SUCCESS) { @@ -11037,9 +11362,17 @@ static int PrepareUserAuthRequestEcc(WOLFSSH* ssh, word32* payloadSz, } else #endif + { ret = wc_EccPrivateKeyDecode(authData->sf.publicKey.privateKey, &idx, &keySig->ks.ecc.key, authData->sf.publicKey.privateKeySz); + if (ret != 0) { + idx = 0; + ret = GetOpenSshKey(keySig, + authData->sf.publicKey.privateKey, + authData->sf.publicKey.privateKeySz, &idx); + } + } } if (ret == WS_SUCCESS) { @@ -11719,23 +12052,6 @@ static int BuildUserAuthRequestPublicKey(WOLFSSH* ssh, } -static void CleanupUserAuthRequestPublicKey(WS_KeySignature* keySig) -{ - if (keySig != NULL) { - if (keySig->keySigId == ID_SSH_RSA) { -#ifndef WOLFSSH_NO_RSA - wc_FreeRsaKey(&keySig->ks.rsa.key); -#endif - } - else if (keySig->keySigId == ID_ECDSA_SHA2_NISTP256 || - keySig->keySigId == ID_ECDSA_SHA2_NISTP384 || - keySig->keySigId == ID_ECDSA_SHA2_NISTP521) { -#ifndef WOLFSSH_NO_ECDSA - wc_ecc_free(&keySig->ks.ecc.key); -#endif - } - } -} #endif @@ -11885,7 +12201,7 @@ int SendUserAuthRequest(WOLFSSH* ssh, byte authType, int addSig) } if (authId == ID_USERAUTH_PUBLICKEY) - CleanupUserAuthRequestPublicKey(keySig_ptr); + wolfSSH_KEY_clean(keySig_ptr); if (ret == WS_SUCCESS) { ret = wolfSSH_SendPacket(ssh); @@ -13491,7 +13807,6 @@ int wolfSSH_CleanPath(WOLFSSH* ssh, char* in) #endif /* WOLFSSH_SFTP || WOLFSSH_SCP */ -#ifdef DEBUG_WOLFSSH #define LINE_WIDTH 16 void DumpOctetString(const byte* input, word32 inputSz) @@ -13527,7 +13842,6 @@ void DumpOctetString(const byte* input, word32 inputSz) } } -#endif #ifdef WOLFSSH_SFTP diff --git a/src/ssh.c b/src/ssh.c index 93a5df572..4e3758d72 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -1323,7 +1323,6 @@ void* wolfSSH_GetPublicKeyCheckCtx(WOLFSSH* ssh) return NULL; } -#ifdef WOLFSSH_TERM #if defined(WOLFSSH_TERM) && !defined(NO_FILESYSTEM) /* Used to resize terminal window with shell connections @@ -1361,8 +1360,6 @@ void wolfSSH_SetTerminalResizeCtx(WOLFSSH* ssh, void* usrCtx) } #endif -#endif - /* Used to set the channel request type sent in wolfSSH connect. The default * type set is shell if this function is not called. @@ -1489,138 +1486,278 @@ union wolfSSH_key { #endif }; -/* Reads a key from the buffer in to out. If the out buffer doesn't exist - it is created. The type of key is stored in outType. It'll be a pointer - to a constant string. Format indicates the format of the key, currently - either SSH format (a public key) or ASN.1 in DER or PEM format (a - private key). */ -int wolfSSH_ReadKey_buffer(const byte* in, word32 inSz, int format, - byte** out, word32* outSz, const byte** outType, word32* outTypeSz, +static const char* PrivBeginOpenSSH = "-----BEGIN OPENSSH PRIVATE KEY-----"; +static const char* PrivEndOpenSSH = "-----END OPENSSH PRIVATE KEY-----"; +static const char* PrivBeginPrefix = "-----BEGIN "; +/* static const char* PrivEndPrefix = "-----END "; */ +static const char* PrivSuffix = " PRIVATE KEY-----"; + + +static int DoSshPubKey(const byte* in, word32 inSz, byte** out, + word32* outSz, const byte** outType, word32* outTypeSz, void* heap) { - int ret = WS_SUCCESS; byte* newKey = NULL; + char* c; + char* last; + char* type = NULL; + char* key = NULL; + int ret = WS_SUCCESS; + word32 newKeySz, typeSz; + WOLFSSH_UNUSED(inSz); WOLFSSH_UNUSED(heap); - if (in == NULL || inSz == 0 || out == NULL || outSz == NULL || - outType == NULL || outTypeSz == NULL) - return WS_BAD_ARGUMENT; - - if (format == WOLFSSH_FORMAT_SSH) { - char* c; - char* last; - char* type = NULL; - char* key = NULL; - - /* - SSH format is: - type AAAABASE64ENCODEDKEYDATA comment - */ - c = WSTRDUP((const char*)in, heap, DYNTYPE_STRING); + /* + SSH format is: + type AAAABASE64ENCODEDKEYDATA comment + */ + c = WSTRDUP((const char*)in, heap, DYNTYPE_STRING); + if (c != NULL) { type = WSTRTOK(c, " \n", &last); key = WSTRTOK(NULL, " \n", &last); + } + else { + ret = WS_MEMORY_E; + } - if (type != NULL && key != NULL) { - const char* name; - word32 typeSz; - byte nameId; - - typeSz = (word32)WSTRLEN(type); - - nameId = NameToId(type, typeSz); - name = IdToName(nameId); - *outType = (const byte*)name; - *outTypeSz = typeSz; - - if (*out == NULL) { - /* set size based on sanity check in wolfSSL base64 decode - * function */ - *outSz = ((word32)WSTRLEN(key) * 3 + 3) / 4; - newKey = (byte*)WMALLOC(*outSz, heap, DYNTYPE_PRIVKEY); - if (newKey == NULL) { - return WS_MEMORY_E; - } - *out = newKey; - } - - ret = Base64_Decode((byte*)key, (word32)WSTRLEN(key), *out, outSz); - } - - if (ret != 0) { - WLOG(WS_LOG_DEBUG, "Base64 decode of public key failed."); + if (ret == WS_SUCCESS) { + if (type == NULL || key == NULL) { ret = WS_PARSE_E; } - - WFREE(c, heap, DYNTYPE_STRING); } - else if (format == WOLFSSH_FORMAT_ASN1) { + + if (ret == WS_SUCCESS) { + typeSz = (word32)WSTRLEN(type); + /* set size based on sanity check in wolfSSL base64 decode + * function */ + newKeySz = ((word32)WSTRLEN(key) * 3 + 3) / 4; if (*out == NULL) { - newKey = (byte*)WMALLOC(inSz, heap, DYNTYPE_PRIVKEY); + newKey = (byte*)WMALLOC(newKeySz, heap, DYNTYPE_PRIVKEY); if (newKey == NULL) { - return WS_MEMORY_E; + ret = WS_MEMORY_E; } - *out = newKey; } else { - if (*outSz < inSz) { - WLOG(WS_LOG_DEBUG, "DER private key output size too small"); - return WS_BUFFER_E; + if (*outSz < newKeySz) { + WLOG(WS_LOG_DEBUG, "PEM private key output size too small"); + ret = WS_BUFFER_E; + } + else { + newKey = *out; } - newKey = *out; } - *outSz = inSz; - WMEMCPY(newKey, in, inSz); + } - ret = IdentifyKey(in, inSz, 1, heap); - if (ret > 0) { - *outType = (const byte*)IdToName(ret); + if (ret == WS_SUCCESS) { + ret = Base64_Decode((byte*)key, (word32)WSTRLEN(key), + newKey, &newKeySz); + + if (ret == 0) { + *out = newKey; + *outSz = newKeySz; + *outType = (const byte *)IdToName(NameToId(type, typeSz)); *outTypeSz = (word32)WSTRLEN((const char*)*outType); ret = WS_SUCCESS; } + else { + WLOG(WS_LOG_DEBUG, "Base64 decode of public key failed."); + if (*out == NULL) { + WFREE(newKey, heap, DYNTYPE_PRIVKEY); + } + ret = WS_PARSE_E; + } } - else if (format == WOLFSSH_FORMAT_PEM) { - word32 newKeySz = inSz; /* binary will be smaller than PEM */ + WFREE(c, heap, DYNTYPE_STRING); + return ret; +} + + +static int DoAsn1Key(const byte* in, word32 inSz, byte** out, + word32* outSz, const byte** outType, word32* outTypeSz, + void* heap) +{ + int ret = WS_SUCCESS; + byte* newKey = NULL; + + WOLFSSH_UNUSED(heap); + + if (*out == NULL) { + newKey = (byte*)WMALLOC(inSz, heap, DYNTYPE_PRIVKEY); + if (newKey == NULL) { + return WS_MEMORY_E; + } + } + else { + if (*outSz < inSz) { + WLOG(WS_LOG_DEBUG, "DER private key output size too small"); + return WS_BUFFER_E; + } + newKey = *out; + } + + ret = IdentifyAsn1Key(in, inSz, 1, heap); + + if (ret > 0) { + *out = newKey; + *outSz = inSz; + WMEMCPY(newKey, in, inSz); + *outType = (const byte*)IdToName(ret); + *outTypeSz = (word32)WSTRLEN((const char*)*outType); + ret = WS_SUCCESS; + } + else { + WLOG(WS_LOG_DEBUG, "unable to identify key"); if (*out == NULL) { - newKey = (byte*)WMALLOC(newKeySz, heap, DYNTYPE_PRIVKEY); - if (newKey == NULL) { - return WS_MEMORY_E; - } - *out = newKey; + WFREE(newKey, heap, DYNTYPE_PRIVKEY); } - else { - if (*outSz < inSz) { - WLOG(WS_LOG_DEBUG, "PEM private key output size too small"); - return WS_BUFFER_E; - } - newKey = *out; + } + + return ret; +} + + +static int DoPemKey(const byte* in, word32 inSz, byte** out, + word32* outSz, const byte** outType, word32* outTypeSz, + void* heap) +{ + int ret = WS_SUCCESS; + byte* newKey = NULL; + word32 newKeySz = inSz; /* binary will be smaller than PEM */ + + WOLFSSH_UNUSED(heap); + + if (*out == NULL) { + newKey = (byte*)WMALLOC(inSz, heap, DYNTYPE_PRIVKEY); + if (newKey == NULL) { + return WS_MEMORY_E; + } + } + else { + if (*outSz < inSz) { + WLOG(WS_LOG_DEBUG, "PEM private key output size too small"); + return WS_BUFFER_E; } + newKey = *out; + } - /* If it is PEM, convert to ASN1 then process. */ - ret = wc_KeyPemToDer(in, inSz, newKey, newKeySz, NULL); - if (ret > 0) { - newKeySz = (word32)ret; - ret = WS_SUCCESS; + /* If it is PEM, convert to ASN1 then process. */ + ret = wc_KeyPemToDer(in, inSz, newKey, newKeySz, NULL); + if (ret > 0) { + newKeySz = (word32)ret; + ret = WS_SUCCESS; + } + else { + WLOG(WS_LOG_DEBUG, "Base64 decode of public key failed."); + ret = WS_PARSE_E; + } + + if (ret == WS_SUCCESS) { + ret = IdentifyAsn1Key(newKey, newKeySz, 1, heap); + } + + if (ret > 0) { + *out = newKey; + *outSz = newKeySz; + *outType = (const byte*)IdToName(ret); + *outTypeSz = (word32)WSTRLEN((const char*)*outType); + ret = WS_SUCCESS; + } + else { + WLOG(WS_LOG_DEBUG, "unable to identify key"); + if (*out == NULL) { + WFREE(newKey, heap, DYNTYPE_PRIVKEY); } - else { - WLOG(WS_LOG_DEBUG, "Base64 decode of public key failed."); - ret = WS_PARSE_E; + } + + return ret; +} + + +static int DoOpenSshKey(const byte* in, word32 inSz, byte** out, + word32* outSz, const byte** outType, word32* outTypeSz, + void* heap) +{ + int ret = WS_SUCCESS; + byte* newKey = NULL; + word32 newKeySz = inSz; /* binary will be smaller than PEM */ + + if (*out == NULL) { + newKey = (byte*)WMALLOC(newKeySz, heap, DYNTYPE_PRIVKEY); + if (newKey == NULL) { + return WS_MEMORY_E; } + } + else { + if (*outSz < inSz) { + WLOG(WS_LOG_DEBUG, "PEM private key output size too small"); + return WS_BUFFER_E; + } + newKey = *out; + } - if (ret == WS_SUCCESS) { - ret = IdentifyKey(newKey, newKeySz, 1, heap); - if (ret > 0) { - *outSz = newKeySz; - *outType = (const byte*)IdToName(ret); - *outTypeSz = (word32)WSTRLEN((const char*)*outType); - ret = WS_SUCCESS; - } - else { - WLOG(WS_LOG_DEBUG, "unable to identify key"); - } + in += WSTRLEN(PrivBeginOpenSSH); + inSz -= (word32)(WSTRLEN(PrivBeginOpenSSH) + WSTRLEN(PrivEndOpenSSH) + 2); + + ret = Base64_Decode((byte*)in, inSz, newKey, &newKeySz); + if (ret == 0) { + ret = WS_SUCCESS; + } + else { + WLOG(WS_LOG_DEBUG, "Base64 decode of public key failed."); + ret = WS_PARSE_E; + } + + if (ret == WS_SUCCESS) { + ret = IdentifyOpenSshKey(newKey, newKeySz, heap); + } + + if (ret > 0) { + *out = newKey; + *outSz = newKeySz; + *outType = (const byte*)IdToName(ret); + *outTypeSz = (word32)WSTRLEN((const char*)*outType); + ret = WS_SUCCESS; + } + else { + WLOG(WS_LOG_DEBUG, "unable to identify key"); + if (*out == NULL) { + WFREE(newKey, heap, DYNTYPE_PRIVKEY); } } + + return ret; +} + + +/* Reads a key from the buffer in to out. If the out buffer doesn't exist + it is created. The type of key is stored in outType. It'll be a pointer + to a constant string. Format indicates the format of the key, currently + either SSH format (a public key) or ASN.1 in DER or PEM format (a + private key). */ +int wolfSSH_ReadKey_buffer(const byte* in, word32 inSz, int format, + byte** out, word32* outSz, const byte** outType, word32* outTypeSz, + void* heap) +{ + int ret = WS_SUCCESS; + + if (in == NULL || inSz == 0 || out == NULL || outSz == NULL || + outType == NULL || outTypeSz == NULL) + return WS_BAD_ARGUMENT; + + if (format == WOLFSSH_FORMAT_SSH) { + ret = DoSshPubKey(in, inSz, out, outSz, outType, outTypeSz, heap); + } + else if (format == WOLFSSH_FORMAT_ASN1) { + ret = DoAsn1Key(in, inSz, out, outSz, outType, outTypeSz, heap); + } + else if (format == WOLFSSH_FORMAT_PEM) { + ret = DoPemKey(in, inSz, out, outSz, outType, outTypeSz, heap); + } + else if (format == WOLFSSH_FORMAT_OPENSSH) { + ret = DoOpenSshKey(in, inSz, out, outSz, outType, outTypeSz, heap); + } else { WLOG(WS_LOG_DEBUG, "Invalid key format"); ret = WS_BAD_ARGUMENT; @@ -1687,9 +1824,17 @@ int wolfSSH_ReadKey_file(const char* name, format = WOLFSSH_FORMAT_SSH; in[inSz] = 0; } - else if ((WSTRNSTR((const char*)in, "-----BEGIN ", inSz) +#if 0 + else if (WSTRNSTR((const char*)in, PrivBeginOpenSSH, inSz) != NULL && + WSTRNSTR((const char*)in, PrivEndOpenSSH, inSz) != NULL) { +#endif + else if (WSTRNSTR((const char*)in, PrivBeginOpenSSH, inSz) != NULL) { + *isPrivate = 1; + format = WOLFSSH_FORMAT_OPENSSH; + } + else if ((WSTRNSTR((const char*)in, PrivBeginPrefix, inSz) == (const char*)in) - && (WSTRNSTR((const char*)in, "PRIVATE KEY-----", inSz) + && (WSTRNSTR((const char*)in, PrivSuffix, inSz) != NULL)) { *isPrivate = 1; format = WOLFSSH_FORMAT_PEM; diff --git a/tests/api.c b/tests/api.c index 1e098fdfb..9353c4d2b 100644 --- a/tests/api.c +++ b/tests/api.c @@ -602,6 +602,203 @@ static void test_wolfSSH_CertMan(void) } +#define KEY_BUF_SZ 2048 + +#ifndef WOLFSSH_NO_RSA + +const char id_rsa[] = + "-----BEGIN OPENSSH PRIVATE KEY-----\n" + "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABFwAAAAdzc2gtcn\n" + "NhAAAAAwEAAQAAAQEAy2cigZDlpBT+X2MJHAoHnfeFf6+LHm6BDkAT8V9ejHA4dY0Aepb6\n" + "NbV6u/oYZlueKPeAZ3GNztR9szoL6FSlMvkd9oqvfoxjTGu71T0981ybJelqqGATGtevHU\n" + "6Jko/I0+lgSQFKWQJ7D3Dj2zlZpIXB2Q7xl/i9kFZgaIqFhUHdWO9JMOwCFwoDrhd8v5xk\n" + "y1v3OIIZDxiYxVIKbf2J07WbwiSFAxXfiX8TjUBDLFmtqt1AF6LjAyGyaRICXkaGJQ/QJ9\n" + "sX85h9bkiPlGNAtQGQtNUg3tC9GqOkZ9tCKY1Efh/r0zosOA7ufxg6ymLpq1C4LU/4ENGH\n" + "kuRPAKvu8wAAA8gztJfmM7SX5gAAAAdzc2gtcnNhAAABAQDLZyKBkOWkFP5fYwkcCged94\n" + "V/r4seboEOQBPxX16McDh1jQB6lvo1tXq7+hhmW54o94BncY3O1H2zOgvoVKUy+R32iq9+\n" + "jGNMa7vVPT3zXJsl6WqoYBMa168dTomSj8jT6WBJAUpZAnsPcOPbOVmkhcHZDvGX+L2QVm\n" + "BoioWFQd1Y70kw7AIXCgOuF3y/nGTLW/c4ghkPGJjFUgpt/YnTtZvCJIUDFd+JfxONQEMs\n" + "Wa2q3UAXouMDIbJpEgJeRoYlD9An2xfzmH1uSI+UY0C1AZC01SDe0L0ao6Rn20IpjUR+H+\n" + "vTOiw4Du5/GDrKYumrULgtT/gQ0YeS5E8Aq+7zAAAAAwEAAQAAAQEAvbdBiQXkGyn1pHST\n" + "/5IfTqia3OCX6td5ChicQUsJvgXBs2rDopQFZmkRxBjd/0K+/0jyfAl/EgZCBBRFHPsuZp\n" + "/S4ayzSV6aE6J8vMT1bnLWxwKyl7+csjGwRK6HRKtVzsnjI9TPSrw0mc9ax5PzV6/mgZUd\n" + "o/i+nszh+UASj5mYrBGqMiINspzX6YC+qoUHor3rEJOd9p1aO+N5+1fDKiDnlkM5IO0Qsz\n" + "GktuwL0fzv9zBnGfnWVJz3CorfP1OW5KCtrDn7BnkQf1eBeVLzq/uoglUjS4DNnVfLA67D\n" + "O4ZfwtnoW8Gr2R+KdvnypvHnDeY5X51r5PDgL4+7z47pWQAAAIBNFcAzHHE19ISGN8YRHk\n" + "23/r/3zfvzHU68GSKR1Xj/Y4LSdRTpSm3wBrdQ17f5B4V7RVl2CJvoPekTggnBDQlLJ7fU\n" + "NU93/nZrY9teYdrNh03buL54VVb5tUM+KN+27zERlTj0/LmYJupN97sZXmlgKsvLbcsnM2\n" + "i7HuQQaFnsIQAAAIEA5wqFVatT9yovt8pS7rAyYUL/cqc50TZ/5Nwfy5uasRyf1BphHwEW\n" + "LEimBemVc+VrNwAkt6MFWuloK5ssqb1ubvtRI8Mntd15rRfZtq/foS3J8FJxueXLDWlECy\n" + "PmVyfVN1Vv4ZeirBy9BTYLiSuxMes+HYks3HucQhxIN1j8SA0AAACBAOFgRjfWXv1/93Jp\n" + "6CCJ5c98MWP+zu1FbLIlklxPb85osZqlazXHNPPEtblC4z+OqRGMCsv2683anU4ZzcTFIk\n" + "JS3lzeJ3tdAH4osQ5etKkV4mcdCmeRpjudB9VbaziVhPX02qkPWpM0ckPrgB3hVNUDPz89\n" + "GtJd3mlhyY5IfFL/AAAADWJvYkBsb2NhbGhvc3QBAgMEBQ==\n" + "-----END OPENSSH PRIVATE KEY-----\n"; + +const char id_rsa_pub[] = + "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDLZyKBkOWkFP5fYwkcCged94V/r4seboEO" + "QBPxX16McDh1jQB6lvo1tXq7+hhmW54o94BncY3O1H2zOgvoVKUy+R32iq9+jGNMa7vVPT3z" + "XJsl6WqoYBMa168dTomSj8jT6WBJAUpZAnsPcOPbOVmkhcHZDvGX+L2QVmBoioWFQd1Y70kw" + "7AIXCgOuF3y/nGTLW/c4ghkPGJjFUgpt/YnTtZvCJIUDFd+JfxONQEMsWa2q3UAXouMDIbJp" + "EgJeRoYlD9An2xfzmH1uSI+UY0C1AZC01SDe0L0ao6Rn20IpjUR+H+vTOiw4Du5/GDrKYumr" + "ULgtT/gQ0YeS5E8Aq+7z bob@localhost\n"; + +#endif /* WOLFSSH_NO_RSA */ + +#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256 + +const char id_ecdsa[] = + "-----BEGIN OPENSSH PRIVATE KEY-----\n" + "b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS\n" + "1zaGEyLW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQTAqdBgCp8bYSq2kQQ48/Ud8Iy6Mjnb\n" + "/fpB3LfSE/1kx9VaaE4FL3i9Gg2vDV0eLGM3PWksFNPhULxtcYJyjaBjAAAAqJAeleSQHp\n" + "XkAAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBMCp0GAKnxthKraR\n" + "BDjz9R3wjLoyOdv9+kHct9IT/WTH1VpoTgUveL0aDa8NXR4sYzc9aSwU0+FQvG1xgnKNoG\n" + "MAAAAgPrOgktioNqad/wHNC/rt/zVrpNqDnOwg9tNDFMOTwo8AAAANYm9iQGxvY2FsaG9z\n" + "dAECAw==\n" + "-----END OPENSSH PRIVATE KEY-----\n"; + +const char id_ecdsa_pub[] = + "ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABB" + "BMCp0GAKnxthKraRBDjz9R3wjLoyOdv9+kHct9IT/WTH1VpoTgUveL0aDa8NXR4sYzc9aSwU" + "0+FQvG1xgnKNoGM= bob@localhost\n"; + +#endif /* WOLFSSH_NO_ECDSA_SHA2_NISTP256 */ + +static void test_wolfSSH_ReadKey(void) +{ +#if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256) + byte *key, *keyCheck, *derKey; + const byte* keyType; + word32 keySz, keyTypeSz, derKeySz; + int ret; +#endif + +#ifndef WOLFSSH_NO_RSA + + /* OpenSSH Format, ssh-rsa, private, need alloc */ + key = NULL; + keySz = 0; + keyType = NULL; + keyTypeSz = 0; + ret = wolfSSH_ReadKey_buffer((const byte*)id_rsa, (word32)WSTRLEN(id_rsa), + WOLFSSH_FORMAT_OPENSSH, &key, &keySz, &keyType, &keyTypeSz, NULL); + AssertIntEQ(ret, WS_SUCCESS); + AssertNotNull(key); + AssertIntGT(keySz, 0); + AssertStrEQ(keyType, "ssh-rsa"); + AssertIntEQ(keyTypeSz, (word32)WSTRLEN("ssh-rsa")); + WFREE(key, NULL, DYNTYPE_FILE); + + /* SSL PEM Format, ssh-rsa, private, need alloc */ + derKey = NULL; + derKeySz = 0; + key = NULL; + keySz = 0; + keyType = NULL; + keyTypeSz = 0; + ret = ConvertHexToBin(serverKeyRsaDer, &derKey, &derKeySz, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + AssertIntEQ(ret, 0); + ret = wolfSSH_ReadKey_buffer(derKey, derKeySz, WOLFSSH_FORMAT_ASN1, + &key, &keySz, &keyType, &keyTypeSz, NULL); + AssertIntEQ(ret, WS_SUCCESS); + AssertNotNull(key); + AssertIntGT(keySz, 0); + AssertStrEQ(keyType, "ssh-rsa"); + AssertIntEQ(keyTypeSz, (word32)WSTRLEN("ssh-rsa")); + WFREE(key, NULL, DYNTYPE_FILE); + WFREE(derKey, NULL, 0); + + /* OpenSSH Format, ssh-rsa, public, need alloc */ + key = NULL; + keySz = 0; + keyType = NULL; + keyTypeSz = 0; + ret = wolfSSH_ReadKey_buffer((const byte*)id_rsa_pub, + (word32)WSTRLEN(id_rsa_pub), WOLFSSH_FORMAT_SSH, + &key, &keySz, &keyType, &keyTypeSz, NULL); + AssertIntEQ(ret, WS_SUCCESS); + AssertNotNull(key); + AssertIntGT(keySz, 0); + AssertStrEQ(keyType, "ssh-rsa"); + AssertIntEQ(keyTypeSz, (word32)WSTRLEN("ssh-rsa")); + WFREE(key, NULL, DYNTYPE_FILE); + + /* OpenSSH Format, ssh-rsa, private, no alloc */ + keyCheck = (byte*)WMALLOC(KEY_BUF_SZ, NULL, DYNTYPE_FILE); + AssertNotNull(keyCheck); + key = keyCheck; + keySz = KEY_BUF_SZ; + keyType = NULL; + keyTypeSz = 0; + ret = wolfSSH_ReadKey_buffer((const byte*)id_rsa, (word32)WSTRLEN(id_rsa), + WOLFSSH_FORMAT_OPENSSH, &key, &keySz, &keyType, &keyTypeSz, NULL); + AssertIntEQ(ret, WS_SUCCESS); + AssertTrue(key == keyCheck); + AssertIntGT(keySz, 0); + AssertStrEQ(keyType, "ssh-rsa"); + AssertIntEQ(keyTypeSz, (word32)WSTRLEN("ssh-rsa")); + WFREE(keyCheck, NULL, DYNTYPE_FILE); + +#endif /* WOLFSSH_NO_RSA */ + +#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256 + + /* OpenSSH Format, ecdsa-sha2-nistp256, private, need alloc */ + key = NULL; + keySz = 0; + keyType = NULL; + keyTypeSz = 0; + ret = wolfSSH_ReadKey_buffer((const byte*)id_ecdsa, + (word32)WSTRLEN(id_ecdsa), WOLFSSH_FORMAT_OPENSSH, + &key, &keySz, &keyType, &keyTypeSz, NULL); + AssertIntEQ(ret, WS_SUCCESS); + AssertNotNull(key); + AssertIntGT(keySz, 0); + AssertStrEQ(keyType, "ecdsa-sha2-nistp256"); + AssertIntEQ(keyTypeSz, (word32)WSTRLEN("ecdsa-sha2-nistp256")); + WFREE(key, NULL, DYNTYPE_FILE); + + /* SSL DER Format, ecdsa-sha2-nistp256, private, need alloc */ + derKey = NULL; + derKeySz = 0; + key = NULL; + keySz = 0; + keyType = NULL; + keyTypeSz = 0; + ret = ConvertHexToBin(serverKeyEccDer, &derKey, &derKeySz, + NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); + AssertIntEQ(ret, WS_SUCCESS); + ret = wolfSSH_ReadKey_buffer(derKey, derKeySz, WOLFSSH_FORMAT_ASN1, + &key, &keySz, &keyType, &keyTypeSz, NULL); + AssertIntEQ(ret, WS_SUCCESS); + AssertNotNull(key); + AssertIntGT(keySz, 0); + AssertStrEQ(keyType, "ecdsa-sha2-nistp256"); + AssertIntEQ(keyTypeSz, (word32)WSTRLEN("ecdsa-sha2-nistp256")); + WFREE(key, NULL, DYNTYPE_FILE); + WFREE(derKey, NULL, 0); + + /* OpenSSH Format, ecdsa-sha2-nistp256, public, need alloc */ + key = NULL; + keySz = 0; + keyType = NULL; + keyTypeSz = 0; + ret = wolfSSH_ReadKey_buffer((const byte*)id_ecdsa_pub, + (word32)WSTRLEN(id_ecdsa_pub), WOLFSSH_FORMAT_SSH, + &key, &keySz, &keyType, &keyTypeSz, NULL); + AssertIntEQ(ret, WS_SUCCESS); + AssertNotNull(key); + AssertIntGT(keySz, 0); + AssertStrEQ(keyType, "ecdsa-sha2-nistp256"); + AssertIntEQ(keyTypeSz, (word32)WSTRLEN("ecdsa-sha2-nistp256")); + WFREE(key, NULL, DYNTYPE_FILE); + +#endif /* WOLFSSH_NO_ECDSA_SHA2_NISTP256 */ +} + + #ifdef WOLFSSH_SCP static int my_ScpRecv(WOLFSSH* ssh, int state, const char* basePath, @@ -1125,6 +1322,7 @@ int wolfSSH_ApiTest(int argc, char** argv) test_wolfSSH_CTX_UsePrivateKey_buffer(); test_wolfSSH_CTX_UseCert_buffer(); test_wolfSSH_CertMan(); + test_wolfSSH_ReadKey(); /* SCP tests */ test_wolfSSH_SCP_CB(); diff --git a/wolfssh/error.h b/wolfssh/error.h index d964b7ac3..598ec1a48 100644 --- a/wolfssh/error.h +++ b/wolfssh/error.h @@ -128,8 +128,11 @@ enum WS_ErrorCodes { WS_CERT_KEY_SIZE_E = -1087, /* Key size error */ WS_CTX_KEY_COUNT_E = -1088, /* Adding too many private keys */ WS_MATCH_UA_KEY_ID_E = -1089, /* Match user auth key key fail */ + WS_KEY_AUTH_MAGIC_E = -1090, /* OpenSSH key auth magic check fail */ + WS_KEY_CHECK_VAL_E = -1091, /* OpenSSH key check value fail */ + WS_KEY_FORMAT_E = -1092, /* OpenSSH key format fail */ - WS_LAST_E = -1089 /* Update this to indicate last error */ + WS_LAST_E = -1092 /* Update this to indicate last error */ }; diff --git a/wolfssh/internal.h b/wolfssh/internal.h index b7650cbeb..674f93de2 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -347,6 +347,10 @@ enum { ID_EXTINFO_SERVER_SIG_ALGS, + ID_CURVE_NISTP256, + ID_CURVE_NISTP384, + ID_CURVE_NISTP521, + ID_UNKNOWN }; @@ -420,6 +424,9 @@ enum { #ifndef WOLFSSH_MAX_PUB_KEY_ALGO #define WOLFSSH_MAX_PUB_KEY_ALGO (WOLFSSH_MAX_PVT_KEYS + 2) #endif +#ifndef WOLFSSH_KEY_QUANTITY_REQ + #define WOLFSSH_KEY_QUANTITY_REQ 1 +#endif WOLFSSH_LOCAL byte NameToId(const char*, word32); WOLFSSH_LOCAL const char* IdToName(byte); @@ -855,8 +862,9 @@ WOLFSSH_LOCAL void ChannelDelete(WOLFSSH_CHANNEL*, void*); WOLFSSH_LOCAL WOLFSSH_CHANNEL* ChannelFind(WOLFSSH*, word32, byte); WOLFSSH_LOCAL int ChannelRemove(WOLFSSH*, word32, byte); WOLFSSH_LOCAL int ChannelPutData(WOLFSSH_CHANNEL*, byte*, word32); -WOLFSSH_LOCAL int IdentifyKey(const byte* in, word32 inSz, +WOLFSSH_LOCAL int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap); +WOLFSSH_LOCAL int IdentifyOpenSshKey(const byte* in, word32 inSz, void* heap); WOLFSSH_LOCAL int wolfSSH_ProcessBuffer(WOLFSSH_CTX*, const byte*, word32, int, int); @@ -869,6 +877,7 @@ WOLFSSH_LOCAL int GetUint32(word32* v, const byte* buf, word32 len, word32* idx); WOLFSSH_LOCAL int GetSize(word32* v, const byte* buf, word32 len, word32* idx); +WOLFSSH_LOCAL int GetSkip(const byte* buf, word32 len, word32* idx); WOLFSSH_LOCAL int GetMpint(word32* mpintSz, const byte** mpint, const byte* buf, word32 len, word32* idx); WOLFSSH_LOCAL int GetString(char* s, word32* sSz, diff --git a/wolfssh/port.h b/wolfssh/port.h index 241fa5c5f..a0bfbc282 100644 --- a/wolfssh/port.h +++ b/wolfssh/port.h @@ -1390,7 +1390,7 @@ extern "C" { #ifndef WOLFSSH_UNUSED - #define WOLFSSH_UNUSED(arg) (void)(arg); + #define WOLFSSH_UNUSED(arg) (void)(arg) #endif diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index 492ab5dd4..ab315daad 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -311,6 +311,7 @@ enum WS_FormatTypes { WOLFSSH_FORMAT_PEM, WOLFSSH_FORMAT_RAW, WOLFSSH_FORMAT_SSH, + WOLFSSH_FORMAT_OPENSSH };