mirror of https://github.com/wolfSSL/wolfssh.git
Add certificate loading APIs with content-based format detection
parent
492e8cd65d
commit
c2d169872e
|
|
@ -83,6 +83,9 @@ keys/*-ossh-*cert.pub
|
|||
random-test.txt
|
||||
random-test-result.txt
|
||||
test.dat
|
||||
# Scratch file the api tests write in the working directory. Removed on a
|
||||
# clean run, left behind when one aborts.
|
||||
ossh-cert-line.tmp
|
||||
|
||||
# test output
|
||||
tests/*.test
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ EXTRA_DIST+= \
|
|||
keys/pubkeys-rsa.txt keys/passwd.txt keys/ca-cert-ecc.der \
|
||||
keys/ca-cert-ecc.pem keys/ca-key-ecc.der keys/ca-key-ecc.pem \
|
||||
keys/server-cert.der keys/server-cert.pem \
|
||||
keys/server-cert-ed25519.der keys/server-cert-ed25519.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 \
|
||||
|
|
|
|||
|
|
@ -34,6 +34,16 @@ openssl req -subj '/C=US/ST=Washington/L=Seattle/O=Eliptic/OU=ECC/CN=www.wolfssl
|
|||
openssl x509 -req -in server-cert.csr -days 3650 -extfile "$CONFIG" -extensions v3_server -CA ca-cert-ecc.pem -CAkey ca-key-ecc.pem -text -out server-cert.pem -set_serial 8
|
||||
openssl x509 -in server-cert.pem -outform DER -out server-cert.der
|
||||
|
||||
# renew server-cert-ed25519. Ed25519 has no x509v3-* SSH algorithm name, so
|
||||
# this certificate exercises the rejection of an unmappable key type. Its key
|
||||
# is not a host key, so it is dropped once the certificate is signed.
|
||||
openssl genpkey -algorithm ed25519 -out server-cert-ed25519-key.pem
|
||||
openssl req -subj '/C=US/ST=Washington/L=Seattle/O=Eliptic/OU=Ed25519/CN=www.wolfssl.com/emailAddress=server@example.com' -key server-cert-ed25519-key.pem -out server-cert-ed25519.csr -config "$CONFIG" -new -nodes
|
||||
|
||||
openssl x509 -req -in server-cert-ed25519.csr -days 3650 -extfile "$CONFIG" -extensions v3_server -CA ca-cert-ecc.pem -CAkey ca-key-ecc.pem -text -out server-cert-ed25519.pem -set_serial 9
|
||||
openssl x509 -in server-cert-ed25519.pem -outform DER -out server-cert-ed25519.der
|
||||
rm -f server-cert-ed25519-key.pem server-cert-ed25519.csr
|
||||
|
||||
rm index.*
|
||||
if [ -n "$1" ]; then
|
||||
rm -f "$CONFIG"
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -0,0 +1,50 @@
|
|||
Certificate:
|
||||
Data:
|
||||
Version: 3 (0x2)
|
||||
Serial Number: 9 (0x9)
|
||||
Signature Algorithm: ecdsa-with-SHA256
|
||||
Issuer: C=US, ST=Washington, L=Seattle, O=wolfSSL, OU=Development, CN=www.wolfssl.com, emailAddress=ca@example.com
|
||||
Validity
|
||||
Not Before: Aug 3 05:11:17 2026 GMT
|
||||
Not After : Jul 31 05:11:17 2036 GMT
|
||||
Subject: C=US, ST=Washington, L=Seattle, O=Eliptic, OU=Ed25519, CN=www.wolfssl.com, emailAddress=server@example.com
|
||||
Subject Public Key Info:
|
||||
Public Key Algorithm: ED25519
|
||||
ED25519 Public-Key:
|
||||
pub:
|
||||
69:97:cf:00:fe:3c:27:d0:1b:3f:14:60:5a:b9:68:
|
||||
a1:8c:89:64:93:ee:89:8c:48:31:a1:05:b1:f6:77:
|
||||
bb:57
|
||||
X509v3 extensions:
|
||||
X509v3 Subject Key Identifier:
|
||||
F7:D0:2B:CD:7D:A0:B7:1B:90:EB:F3:E8:E6:D0:A5:E9:C7:5F:E6:72
|
||||
X509v3 Authority Key Identifier:
|
||||
keyid:56:8E:9A:C3:F0:42:DE:18:B9:45:55:6E:F9:93:CF:EA:C3:F3:A5:21
|
||||
DirName:/C=US/ST=Washington/L=Seattle/O=wolfSSL/OU=Development/CN=www.wolfssl.com/emailAddress=ca@example.com
|
||||
serial:06
|
||||
X509v3 Subject Alternative Name:
|
||||
DNS:example, IP Address:127.0.0.1
|
||||
Signature Algorithm: ecdsa-with-SHA256
|
||||
Signature Value:
|
||||
30:44:02:20:58:41:57:d7:27:fd:e0:a6:25:bf:40:11:31:5a:
|
||||
e1:6d:7c:60:35:dc:04:f9:6e:2e:8c:d5:4b:41:1d:65:d8:16:
|
||||
02:20:36:9c:e1:fa:4d:e1:aa:1f:19:4e:41:99:4d:79:ee:ed:
|
||||
fb:7c:68:7e:0a:48:c6:12:7d:44:12:be:59:55:d3:6f
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIC7jCCApWgAwIBAgIBCTAKBggqhkjOPQQDAjCBlTELMAkGA1UEBhMCVVMxEzAR
|
||||
BgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxEDAOBgNVBAoMB3dv
|
||||
bGZTU0wxFDASBgNVBAsMC0RldmVsb3BtZW50MRgwFgYDVQQDDA93d3cud29sZnNz
|
||||
bC5jb20xHTAbBgkqhkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tMB4XDTI2MDgwMzA1
|
||||
MTExN1oXDTM2MDczMTA1MTExN1owgZUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApX
|
||||
YXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxlMRAwDgYDVQQKDAdFbGlwdGljMRAw
|
||||
DgYDVQQLDAdFZDI1NTE5MRgwFgYDVQQDDA93d3cud29sZnNzbC5jb20xITAfBgkq
|
||||
hkiG9w0BCQEWEnNlcnZlckBleGFtcGxlLmNvbTAqMAUGAytlcAMhAGmXzwD+PCfQ
|
||||
Gz8UYFq5aKGMiWST7omMSDGhBbH2d7tXo4IBATCB/jAdBgNVHQ4EFgQU99ArzX2g
|
||||
txuQ6/Po5tCl6cdf5nIwgcIGA1UdIwSBujCBt4AUVo6aw/BC3hi5RVVu+ZPP6sPz
|
||||
pSGhgZukgZgwgZUxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAw
|
||||
DgYDVQQHDAdTZWF0dGxlMRAwDgYDVQQKDAd3b2xmU1NMMRQwEgYDVQQLDAtEZXZl
|
||||
bG9wbWVudDEYMBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMR0wGwYJKoZIhvcNAQkB
|
||||
Fg5jYUBleGFtcGxlLmNvbYIBBjAYBgNVHREEETAPggdleGFtcGxlhwR/AAABMAoG
|
||||
CCqGSM49BAMCA0cAMEQCIFhBV9cn/eCmJb9AETFa4W18YDXcBPluLozVS0EdZdgW
|
||||
AiA2nOH6TeGqHxlOQZlNee7t+3xofgpIxhJ9RBK+WVXTbw==
|
||||
-----END CERTIFICATE-----
|
||||
|
|
@ -1951,13 +1951,9 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap,
|
|||
ret = wc_MlDsaKey_PublicKeyDecode(&key->ks.mldsa.key,
|
||||
in, inSz, &idx);
|
||||
if (ret != 0) {
|
||||
/* Length-only fallback for local key/cert loading
|
||||
* (wolfSSH_ReadKey_buffer_ex / IdentifyCert) when SPKI
|
||||
* OID decode fails: wc_MlDsaKey_ImportPubRaw accepts an
|
||||
* ML-DSA blob of exactly 1312/1952/2592 bytes and tags
|
||||
* it ID_MLDSA44/65/87. Not used on the remote user-auth
|
||||
* path; do not wire this probe into remote auth assuming
|
||||
* it cryptographically validates the key. */
|
||||
/* Local loading only: size alone tags a raw ML-DSA
|
||||
* blob when SPKI OID decode fails. This does not
|
||||
* validate the key, so keep it off remote auth. */
|
||||
struct { byte level; byte id; } kProbe[3];
|
||||
word32 nProbe = 0, li;
|
||||
#ifndef WOLFSSH_NO_MLDSA44
|
||||
|
|
@ -2076,17 +2072,10 @@ int IdentifyAsn1Key(const byte* in, word32 inSz, int isPrivate, void* heap,
|
|||
|
||||
|
||||
#ifdef WOLFSSH_CERTS
|
||||
/*
|
||||
* Identifies the flavor of an X.509 certificate, RSA, ML-DSA or ECDSA, returns
|
||||
* the key type ID. The process is to decode the certificate and pass the
|
||||
* public key to IdentifyAsn1Key.
|
||||
*
|
||||
* @param in certificate to identify
|
||||
* @param inSz size of certificate
|
||||
* @param heap heap to use for memory allocation
|
||||
* @return keyId as int, WS_MEMORY_E, WS_UNIMPLEMENTED_E
|
||||
*/
|
||||
static int IdentifyCert(const byte* in, word32 inSz, void* heap)
|
||||
/* Identifies the key held inside an X.509 certificate, returning its plain
|
||||
key type ID or a WS_ error. See IdentifyCert() for the x509v3-* algorithm
|
||||
ID sent on the wire. */
|
||||
static int IdentifyCertKey(const byte* in, word32 inSz, void* heap)
|
||||
{
|
||||
struct DecodedCert* cert = NULL;
|
||||
#ifndef WOLFSSH_SMALL_STACK
|
||||
|
|
@ -2106,13 +2095,22 @@ static int IdentifyCert(const byte* in, word32 inSz, void* heap)
|
|||
}
|
||||
#endif
|
||||
|
||||
/* Each wolfSSL result below is mapped where it is produced, so this
|
||||
function returns only a key ID or a WS_ code. */
|
||||
if (ret == 0) {
|
||||
wc_InitDecodedCert(cert, in, inSz, heap);
|
||||
ret = wc_ParseCert(cert, CERT_TYPE, 0, NULL);
|
||||
if (ret != 0) {
|
||||
ret = WS_PARSE_E;
|
||||
}
|
||||
}
|
||||
if (ret == 0) {
|
||||
/* Asking with no buffer answers with the length and LENGTH_ONLY_E. */
|
||||
ret = wc_GetPubKeyDerFromCert(cert, NULL, &keySz);
|
||||
if (ret == LENGTH_ONLY_E) {
|
||||
if (ret != LENGTH_ONLY_E) {
|
||||
ret = WS_PARSE_E;
|
||||
}
|
||||
else {
|
||||
ret = 0;
|
||||
key = (byte*)WMALLOC(keySz, heap, DYNTYPE_PUBKEY);
|
||||
if (key == NULL) {
|
||||
|
|
@ -2123,6 +2121,9 @@ static int IdentifyCert(const byte* in, word32 inSz, void* heap)
|
|||
|
||||
if (ret == 0) {
|
||||
ret = wc_GetPubKeyDerFromCert(cert, key, &keySz);
|
||||
if (ret != 0) {
|
||||
ret = WS_PARSE_E;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == 0) {
|
||||
|
|
@ -2233,6 +2234,31 @@ static INLINE byte CertTypeForId(byte id)
|
|||
return id;
|
||||
}
|
||||
|
||||
|
||||
/* Identifies an X.509 certificate, returning the x509v3-* algorithm ID sent
|
||||
on the wire. A key type with no x509v3 name in this build is rejected
|
||||
rather than reported under its plain key name. */
|
||||
int IdentifyCert(const byte* in, word32 inSz, void* heap)
|
||||
{
|
||||
byte certId;
|
||||
int ret;
|
||||
|
||||
ret = IdentifyCertKey(in, inSz, heap);
|
||||
|
||||
if (ret >= 0) {
|
||||
certId = CertTypeForId((byte)ret);
|
||||
if (certId == (byte)ret) {
|
||||
WLOG(WS_LOG_DEBUG, "No x509v3 algorithm for this certificate");
|
||||
ret = WS_INVALID_ALGO_ID;
|
||||
}
|
||||
else {
|
||||
ret = (int)certId;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#define HINTISSET(x) ((x) != WOLFSSH_MAX_PVT_KEYS)
|
||||
|
||||
static int UpdateHostCertificates(WOLFSSH_CTX* ctx,
|
||||
|
|
@ -2326,18 +2352,14 @@ static int UpdateHostCertificates(WOLFSSH_CTX* ctx,
|
|||
}
|
||||
|
||||
static int SetHostCertificate(WOLFSSH_CTX* ctx,
|
||||
byte keyId, byte* der, word32 derSz, int dynamicType)
|
||||
byte certId, byte* der, word32 derSz, int dynamicType)
|
||||
{
|
||||
/*
|
||||
* The keyId is for the key inside the certificate. wolfSSH_ProcessBuffer
|
||||
* will decode the certificate, get the public key inside, and identify
|
||||
* that. keyId will be: ssh-rsa, ecdsa-sha2-nistp256, etc.
|
||||
*/
|
||||
/* The certId is the x509v3-* algorithm the certificate is presented as,
|
||||
* identified by wolfSSH_ProcessBuffer before calling here. */
|
||||
|
||||
word32 destIdx,
|
||||
certIdx = WOLFSSH_MAX_PVT_KEYS, keyIdx = WOLFSSH_MAX_PVT_KEYS;
|
||||
int ret = WS_SUCCESS;
|
||||
byte certId = CertTypeForId(keyId);
|
||||
|
||||
/* Look for the specified certId. Add it if not present,
|
||||
* replace it if present. Call UpdateHostCertificate().
|
||||
|
|
@ -2347,7 +2369,10 @@ static int SetHostCertificate(WOLFSSH_CTX* ctx,
|
|||
if (ctx->privateKey[destIdx].publicKeyFmt == certId) {
|
||||
certIdx = destIdx;
|
||||
}
|
||||
if (ctx->privateKey[destIdx].publicKeyFmt == keyId) {
|
||||
/* The key for this certificate sits in the slot whose plain
|
||||
algorithm maps onto certId. */
|
||||
else if (CertTypeForId(ctx->privateKey[destIdx].publicKeyFmt)
|
||||
== certId) {
|
||||
keyIdx = destIdx;
|
||||
}
|
||||
}
|
||||
|
|
@ -2618,12 +2643,16 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx,
|
|||
WFREE(der, heap, dynamicType);
|
||||
return ret;
|
||||
}
|
||||
keyId = (byte)ret;
|
||||
ret = SetHostCertificate(ctx, keyId, der, derSz, dynamicType);
|
||||
ret = SetHostCertificate(ctx, (byte)ret, der, derSz, dynamicType);
|
||||
}
|
||||
else if (type == BUFTYPE_CA) {
|
||||
if (ctx->certMan != NULL) {
|
||||
ret = wolfSSH_CERTMAN_LoadRootCA_buffer(ctx->certMan, der, derSz);
|
||||
/* That call answers in wolfSSL codes apart from these two, so
|
||||
anything else becomes one of ours. */
|
||||
if (ret != WS_SUCCESS && ret != WS_BAD_ARGUMENT) {
|
||||
ret = WS_PARSE_E;
|
||||
}
|
||||
}
|
||||
else {
|
||||
WLOG(WS_LOG_DEBUG, "Error no cert manager set");
|
||||
|
|
|
|||
429
src/ssh.c
429
src/ssh.c
|
|
@ -1786,6 +1786,9 @@ char* wolfSSH_GetUsername(WOLFSSH* ssh)
|
|||
#ifndef WOLFSSH_NO_MLDSA
|
||||
#include <wolfssl/wolfcrypt/dilithium.h>
|
||||
#endif
|
||||
#ifdef WOLFSSH_OSSH_CERTS
|
||||
#include <wolfssh/ossh.h>
|
||||
#endif
|
||||
|
||||
union wolfSSH_key {
|
||||
#ifndef WOLFSSH_NO_RSA
|
||||
|
|
@ -2373,34 +2376,265 @@ int wolfSSH_ReadPublicKey_buffer(const byte* in, word32 inSz, int format,
|
|||
}
|
||||
|
||||
|
||||
#if defined(WOLFSSH_CERTS) || defined(WOLFSSH_OSSH_CERTS)
|
||||
|
||||
#ifdef WOLFSSH_CERTS
|
||||
static const char* CertBeginPrefix = "-----BEGIN CERTIFICATE-----";
|
||||
#endif
|
||||
|
||||
/* Longest algorithm name is ecdsa-sha2-nistp521-cert-v01@openssh.com. */
|
||||
#define WOLFSSH_MAX_CERT_ALGO_NAME_SZ 48
|
||||
|
||||
/* Identifies a certificate from its content, without decoding or allocating.
|
||||
An x509v3-* line holds a wire chain, not a certificate, so it is declined. */
|
||||
static int SniffCertForm(const byte* in, word32 inSz, byte* flavor,
|
||||
byte* certId)
|
||||
{
|
||||
#ifdef WOLFSSH_OSSH_CERTS
|
||||
word32 tokenSz = 0;
|
||||
byte id;
|
||||
#endif
|
||||
int ret = WS_BAD_FILETYPE_E;
|
||||
|
||||
#ifdef WOLFSSH_CERTS
|
||||
if (in[0] == 0x30) {
|
||||
*flavor = WOLFSSH_CERT_FLAVOR_X509;
|
||||
ret = WOLFSSH_FORMAT_ASN1;
|
||||
}
|
||||
/* Searched for, not anchored: openssl writes a dump ahead of the header. */
|
||||
else if (WSTRNSTR((const char*)in, CertBeginPrefix, inSz) != NULL) {
|
||||
*flavor = WOLFSSH_CERT_FLAVOR_X509;
|
||||
ret = WOLFSSH_FORMAT_PEM;
|
||||
}
|
||||
#endif /* WOLFSSH_CERTS */
|
||||
|
||||
#ifdef WOLFSSH_OSSH_CERTS
|
||||
if (ret == WS_BAD_FILETYPE_E) {
|
||||
while (tokenSz < inSz && tokenSz < WOLFSSH_MAX_CERT_ALGO_NAME_SZ
|
||||
&& in[tokenSz] != ' ' && in[tokenSz] != '\t'
|
||||
&& in[tokenSz] != '\r' && in[tokenSz] != '\n') {
|
||||
tokenSz++;
|
||||
}
|
||||
|
||||
if (tokenSz > 0 && tokenSz < WOLFSSH_MAX_CERT_ALGO_NAME_SZ) {
|
||||
id = NameToId((const char*)in, tokenSz);
|
||||
|
||||
switch (id) {
|
||||
case ID_OSSH_CERT_RSA:
|
||||
case ID_OSSH_CERT_ECDSA_SHA2_NISTP256:
|
||||
case ID_OSSH_CERT_ECDSA_SHA2_NISTP384:
|
||||
case ID_OSSH_CERT_ECDSA_SHA2_NISTP521:
|
||||
case ID_OSSH_CERT_ED25519:
|
||||
*flavor = WOLFSSH_CERT_FLAVOR_OSSH;
|
||||
*certId = id;
|
||||
ret = WOLFSSH_FORMAT_SSH;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
WOLFSSH_UNUSED(certId);
|
||||
#endif /* WOLFSSH_OSSH_CERTS */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#ifdef WOLFSSH_CERTS
|
||||
|
||||
/* Decodes a PEM certificate to DER and identifies it. */
|
||||
static int DoPemCert(const byte* in, word32 inSz, byte** out, word32* outSz,
|
||||
const byte** outType, word32* outTypeSz, void* heap)
|
||||
{
|
||||
byte* der;
|
||||
word32 derSz = 0;
|
||||
int ret;
|
||||
|
||||
der = (byte*)WMALLOC(inSz, heap, DYNTYPE_CERT);
|
||||
if (der == NULL) {
|
||||
return WS_MEMORY_E;
|
||||
}
|
||||
|
||||
ret = wc_CertPemToDer(in, (int)inSz, der, (int)inSz, CERT_TYPE);
|
||||
if (ret <= 0) {
|
||||
WLOG(WS_LOG_DEBUG, "PEM to DER of certificate failed.");
|
||||
WFREE(der, heap, DYNTYPE_CERT);
|
||||
return WS_BAD_FILE_E;
|
||||
}
|
||||
derSz = (word32)ret;
|
||||
|
||||
ret = IdentifyCert(der, derSz, heap);
|
||||
if (ret < 0) {
|
||||
WFREE(der, heap, DYNTYPE_CERT);
|
||||
}
|
||||
else {
|
||||
*out = der;
|
||||
*outSz = derSz;
|
||||
*outType = (const byte*)IdToName((byte)ret);
|
||||
*outTypeSz = (word32)WSTRLEN((const char*)*outType);
|
||||
ret = WS_SUCCESS;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Identifies a DER certificate before copying it out. */
|
||||
static int DoDerCert(const byte* in, word32 inSz, byte** out, word32* outSz,
|
||||
const byte** outType, word32* outTypeSz, void* heap)
|
||||
{
|
||||
byte* der;
|
||||
int ret;
|
||||
|
||||
ret = IdentifyCert(in, inSz, heap);
|
||||
|
||||
if (ret >= 0) {
|
||||
der = (byte*)WMALLOC(inSz, heap, DYNTYPE_CERT);
|
||||
if (der == NULL) {
|
||||
ret = WS_MEMORY_E;
|
||||
}
|
||||
else {
|
||||
WMEMCPY(der, in, inSz);
|
||||
*out = der;
|
||||
*outSz = inSz;
|
||||
*outType = (const byte*)IdToName((byte)ret);
|
||||
*outTypeSz = (word32)WSTRLEN((const char*)*outType);
|
||||
ret = WS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* WOLFSSH_CERTS */
|
||||
|
||||
|
||||
#ifdef WOLFSSH_OSSH_CERTS
|
||||
|
||||
/* Decodes an OpenSSH certificate line and checks that it parses. */
|
||||
static int DoOsshCert(const byte* in, word32 inSz, byte certId, byte** out,
|
||||
word32* outSz, const byte** outType, word32* outTypeSz, void* heap)
|
||||
{
|
||||
WS_OsshCert cert;
|
||||
byte* blob;
|
||||
int ret;
|
||||
|
||||
blob = (byte*)WMALLOC(inSz, heap, DYNTYPE_CERT);
|
||||
if (blob == NULL) {
|
||||
return WS_MEMORY_E;
|
||||
}
|
||||
|
||||
/* DoSshPubKey decodes into a non-NULL *out rather than allocating, so it
|
||||
fills blob and leaves it ours to free. Were it to allocate instead,
|
||||
blob would leak and the free below would take the wrong type. */
|
||||
*out = blob;
|
||||
*outSz = inSz;
|
||||
|
||||
ret = DoSshPubKey(in, inSz, out, outSz, outType, outTypeSz, heap);
|
||||
|
||||
if (ret == WS_SUCCESS) {
|
||||
/* The blob has no envelope, so only a parse tells it from a key. */
|
||||
ret = OsshCertParse(&cert, certId, *out, *outSz);
|
||||
if (ret != WS_SUCCESS) {
|
||||
WLOG(WS_LOG_DEBUG, "OpenSSH certificate is malformed.");
|
||||
}
|
||||
}
|
||||
|
||||
if (ret != WS_SUCCESS) {
|
||||
WFREE(blob, heap, DYNTYPE_CERT);
|
||||
*out = NULL;
|
||||
*outSz = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* WOLFSSH_OSSH_CERTS */
|
||||
|
||||
|
||||
/* Reads a certificate from the buffer in to out, detecting its form from the
|
||||
content. See ssh.h for what the caller owns. */
|
||||
int wolfSSH_ReadCert_buffer(const byte* in, word32 inSz,
|
||||
byte** out, word32* outSz, const byte** outType, word32* outTypeSz,
|
||||
byte* flavor, void* heap)
|
||||
{
|
||||
byte certId = ID_UNKNOWN;
|
||||
int format;
|
||||
int ret;
|
||||
|
||||
if (in == NULL || inSz == 0 || out == NULL || outSz == NULL ||
|
||||
outType == NULL || outTypeSz == NULL || flavor == NULL) {
|
||||
return WS_BAD_ARGUMENT;
|
||||
}
|
||||
|
||||
*out = NULL;
|
||||
*outSz = 0;
|
||||
*outType = NULL;
|
||||
*outTypeSz = 0;
|
||||
*flavor = WOLFSSH_CERT_FLAVOR_UNKNOWN;
|
||||
|
||||
format = SniffCertForm(in, inSz, flavor, &certId);
|
||||
if (format < 0) {
|
||||
WLOG(WS_LOG_DEBUG, "Unable to identify the certificate");
|
||||
return format;
|
||||
}
|
||||
|
||||
ret = WS_BAD_FILETYPE_E;
|
||||
|
||||
switch (format) {
|
||||
#ifdef WOLFSSH_CERTS
|
||||
case WOLFSSH_FORMAT_PEM:
|
||||
ret = DoPemCert(in, inSz, out, outSz, outType, outTypeSz, heap);
|
||||
break;
|
||||
|
||||
case WOLFSSH_FORMAT_ASN1:
|
||||
ret = DoDerCert(in, inSz, out, outSz, outType, outTypeSz, heap);
|
||||
break;
|
||||
#endif /* WOLFSSH_CERTS */
|
||||
#ifdef WOLFSSH_OSSH_CERTS
|
||||
case WOLFSSH_FORMAT_SSH:
|
||||
ret = DoOsshCert(in, inSz, certId, out, outSz, outType, outTypeSz,
|
||||
heap);
|
||||
break;
|
||||
#endif /* WOLFSSH_OSSH_CERTS */
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* The sniff names the form and a decoder may fill an out parameter before
|
||||
failing, so a failure clears them all rather than leave a partial
|
||||
answer. Decoders free whatever they allocated. */
|
||||
if (ret != WS_SUCCESS) {
|
||||
*out = NULL;
|
||||
*outSz = 0;
|
||||
*outType = NULL;
|
||||
*outTypeSz = 0;
|
||||
*flavor = WOLFSSH_CERT_FLAVOR_UNKNOWN;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* WOLFSSH_CERTS || WOLFSSH_OSSH_CERTS */
|
||||
|
||||
|
||||
#if !defined(NO_FILESYSTEM) && !defined(WOLFSSH_USER_FILESYSTEM)
|
||||
|
||||
/* Reads a key from the file name into a buffer. If the key starts with the
|
||||
string "ssh-rsa" or "ecdsa-sha2-nistp256", it is considered an SSH format
|
||||
public key, if it has "----BEGIN" it is considered PEM formatted,
|
||||
otherwise it is considered an ASN.1 private key. The buffer is passed to
|
||||
wolfSSH_ReadKey_buffer() for processing. */
|
||||
int wolfSSH_ReadKey_file(const char* name,
|
||||
byte** out, word32* outSz, const byte** outType, word32* outTypeSz,
|
||||
byte* isPrivate, void* heap)
|
||||
/* Reads the file name into a buffer allocated with an extra byte holding a
|
||||
nul terminator. The caller frees out with DYNTYPE_FILE. */
|
||||
static int ReadFileIntoBuffer(const char* name, byte** out, word32* outSz,
|
||||
void* heap)
|
||||
{
|
||||
|
||||
WFILE* file;
|
||||
#ifdef MICROCHIP_MPLAB_HARMONY
|
||||
WFILE f = WBADFILE;
|
||||
#endif
|
||||
byte* in;
|
||||
word32 inSz;
|
||||
int format;
|
||||
int ret;
|
||||
|
||||
if (name == NULL)
|
||||
return WS_BAD_FILE_E;
|
||||
|
||||
if (out == NULL || outSz == NULL || outType == NULL || outTypeSz == NULL ||
|
||||
isPrivate == NULL)
|
||||
return WS_BAD_ARGUMENT;
|
||||
|
||||
#ifdef MICROCHIP_MPLAB_HARMONY
|
||||
file = &f;
|
||||
ret = WFOPEN(NULL, &file, name, WOLFSSH_O_RDONLY);
|
||||
|
|
@ -2429,10 +2663,43 @@ int wolfSSH_ReadKey_file(const char* name,
|
|||
}
|
||||
|
||||
ret = (int)WFREAD(NULL, in, 1, inSz, file);
|
||||
WFCLOSE(NULL, file);
|
||||
|
||||
if (ret <= 0 || (word32)ret != inSz) {
|
||||
ret = WS_BAD_FILE_E;
|
||||
WS_FORCEZERO(in, inSz);
|
||||
WFREE(in, heap, DYNTYPE_FILE);
|
||||
return WS_BAD_FILE_E;
|
||||
}
|
||||
else {
|
||||
|
||||
in[inSz] = 0;
|
||||
*out = in;
|
||||
*outSz = inSz;
|
||||
|
||||
return WS_SUCCESS;
|
||||
}
|
||||
|
||||
|
||||
/* Reads a key from the file name into a buffer. An SSH algorithm name marks
|
||||
an SSH format public key and "-----BEGIN " a PEM one, otherwise it is an
|
||||
ASN.1 private key. The buffer goes to wolfSSH_ReadKey_buffer_ex(). */
|
||||
int wolfSSH_ReadKey_file(const char* name,
|
||||
byte** out, word32* outSz, const byte** outType, word32* outTypeSz,
|
||||
byte* isPrivate, void* heap)
|
||||
{
|
||||
byte* in;
|
||||
word32 inSz;
|
||||
int format;
|
||||
int ret;
|
||||
|
||||
if (name == NULL)
|
||||
return WS_BAD_FILE_E;
|
||||
|
||||
if (out == NULL || outSz == NULL || outType == NULL || outTypeSz == NULL ||
|
||||
isPrivate == NULL)
|
||||
return WS_BAD_ARGUMENT;
|
||||
|
||||
ret = ReadFileIntoBuffer(name, &in, &inSz, heap);
|
||||
if (ret == WS_SUCCESS) {
|
||||
if (WSTRNSTR((const char*)in, "ssh-rsa", inSz) == (const char*)in
|
||||
|| WSTRNSTR((const char*)in,
|
||||
"ecdsa-sha2-nistp", inSz) == (const char*)in
|
||||
|
|
@ -2444,7 +2711,6 @@ int wolfSSH_ReadKey_file(const char* name,
|
|||
"x509v3-ssh-mldsa-", inSz) == (const char*)in) {
|
||||
*isPrivate = 0;
|
||||
format = WOLFSSH_FORMAT_SSH;
|
||||
in[inSz] = 0;
|
||||
}
|
||||
else if (WSTRNSTR((const char*)in, PrivBeginOpenSSH, inSz) != NULL) {
|
||||
*isPrivate = 1;
|
||||
|
|
@ -2464,15 +2730,55 @@ int wolfSSH_ReadKey_file(const char* name,
|
|||
|
||||
ret = wolfSSH_ReadKey_buffer_ex(in, inSz, format,
|
||||
out, outSz, outType, outTypeSz, *isPrivate, heap);
|
||||
}
|
||||
|
||||
WFCLOSE(NULL, file);
|
||||
WS_FORCEZERO(in, inSz);
|
||||
WFREE(in, heap, DYNTYPE_FILE);
|
||||
WS_FORCEZERO(in, inSz);
|
||||
WFREE(in, heap, DYNTYPE_FILE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#if defined(WOLFSSH_CERTS) || defined(WOLFSSH_OSSH_CERTS)
|
||||
|
||||
/* Reads a certificate file and passes it to wolfSSH_ReadCert_buffer(). */
|
||||
int wolfSSH_ReadCert_file(const char* name,
|
||||
byte** out, word32* outSz, const byte** outType, word32* outTypeSz,
|
||||
byte* flavor, void* heap)
|
||||
{
|
||||
byte* in;
|
||||
word32 inSz;
|
||||
int ret;
|
||||
|
||||
if (name == NULL)
|
||||
return WS_BAD_FILE_E;
|
||||
|
||||
if (out == NULL || outSz == NULL || outType == NULL || outTypeSz == NULL ||
|
||||
flavor == NULL)
|
||||
return WS_BAD_ARGUMENT;
|
||||
|
||||
*out = NULL;
|
||||
*outSz = 0;
|
||||
*outType = NULL;
|
||||
*outTypeSz = 0;
|
||||
*flavor = WOLFSSH_CERT_FLAVOR_UNKNOWN;
|
||||
|
||||
ret = ReadFileIntoBuffer(name, &in, &inSz, heap);
|
||||
if (ret == WS_SUCCESS) {
|
||||
ret = wolfSSH_ReadCert_buffer(in, inSz,
|
||||
out, outSz, outType, outTypeSz, flavor, heap);
|
||||
|
||||
/* A certificate is public, but the file may hold a private key
|
||||
beside it, so scrub it like the key reader does. */
|
||||
WS_FORCEZERO(in, inSz);
|
||||
WFREE(in, heap, DYNTYPE_FILE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* WOLFSSH_CERTS || WOLFSSH_OSSH_CERTS */
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
|
@ -2930,6 +3236,81 @@ int wolfSSH_CTX_AddRootCert_buffer(WOLFSSH_CTX* ctx,
|
|||
return ret;
|
||||
}
|
||||
|
||||
|
||||
#if !defined(NO_FILESYSTEM) && !defined(WOLFSSH_USER_FILESYSTEM)
|
||||
|
||||
/* Reads a certificate file and hands the PEM or DER it holds to the CTX. */
|
||||
static int UseCertFile(WOLFSSH_CTX* ctx, const char* name, int type)
|
||||
{
|
||||
byte* in;
|
||||
word32 inSz;
|
||||
byte certId = ID_UNKNOWN;
|
||||
byte flavor = WOLFSSH_CERT_FLAVOR_UNKNOWN;
|
||||
int format;
|
||||
int ret;
|
||||
|
||||
if (ctx == NULL || name == NULL) {
|
||||
return WS_BAD_ARGUMENT;
|
||||
}
|
||||
|
||||
ret = ReadFileIntoBuffer(name, &in, &inSz, ctx->heap);
|
||||
if (ret == WS_SUCCESS) {
|
||||
format = SniffCertForm(in, inSz, &flavor, &certId);
|
||||
|
||||
if (format < 0) {
|
||||
ret = format;
|
||||
}
|
||||
else if (format == WOLFSSH_FORMAT_SSH) {
|
||||
/* A CTX takes the certificate itself, not a public key line. */
|
||||
WLOG(WS_LOG_DEBUG, "Certificate file is not PEM or DER");
|
||||
ret = WS_BAD_FILETYPE_E;
|
||||
}
|
||||
else {
|
||||
ret = wolfSSH_ProcessBuffer(ctx, in, inSz, format, type);
|
||||
}
|
||||
|
||||
WS_FORCEZERO(in, inSz);
|
||||
WFREE(in, ctx->heap, DYNTYPE_FILE);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Load in a X509 certificate file that has public key to use
|
||||
* return WS_SUCCESS on success
|
||||
*/
|
||||
int wolfSSH_CTX_UseCert_file(WOLFSSH_CTX* ctx, const char* name)
|
||||
{
|
||||
int ret;
|
||||
|
||||
WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_UseCert_file()");
|
||||
|
||||
ret = UseCertFile(ctx, name, BUFTYPE_CERT);
|
||||
|
||||
WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_CTX_UseCert_file(), ret = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/* Add a CA file for verifying the peer's certificate with.
|
||||
* returns WS_SUCCESS on success
|
||||
*/
|
||||
int wolfSSH_CTX_AddRootCert_file(WOLFSSH_CTX* ctx, const char* name)
|
||||
{
|
||||
int ret;
|
||||
|
||||
WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_AddRootCert_file()");
|
||||
|
||||
ret = UseCertFile(ctx, name, BUFTYPE_CA);
|
||||
|
||||
WLOG(WS_LOG_DEBUG,
|
||||
"Leaving wolfSSH_CTX_AddRootCert_file(), ret = %d", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* !NO_FILESYSTEM && !WOLFSSH_USER_FILESYSTEM */
|
||||
|
||||
#endif /* WOLFSSH_CERTS */
|
||||
|
||||
|
||||
|
|
|
|||
492
tests/api.c
492
tests/api.c
|
|
@ -53,6 +53,12 @@
|
|||
#endif
|
||||
#ifdef WOLFSSH_OSSH_CERTS
|
||||
#include <wolfssh/ossh.h>
|
||||
#ifdef WOLFSSL_BASE64_ENCODE
|
||||
/* Declared rather than including coding.h, whose Base16_Decode
|
||||
* collides with the one wolfssh/test.h defines. */
|
||||
WOLFSSL_API int Base64_Encode_NoNl(const byte* in, word32 inLen,
|
||||
byte* out, word32* outLen);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)
|
||||
|
|
@ -686,8 +692,10 @@ static void test_wolfSSH_CTX_UseCert_buffer(void)
|
|||
AssertIntEQ(WS_BAD_ARGUMENT,
|
||||
wolfSSH_CTX_UseCert_buffer(ctx, NULL, 0, WOLFSSH_FORMAT_PEM));
|
||||
|
||||
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
||||
AssertIntEQ(WS_SUCCESS,
|
||||
wolfSSH_CTX_UseCert_buffer(ctx, cert, certSz, WOLFSSH_FORMAT_PEM));
|
||||
#endif
|
||||
|
||||
AssertIntEQ(WS_BAD_FILETYPE_E,
|
||||
wolfSSH_CTX_UseCert_buffer(ctx, cert, certSz, WOLFSSH_FORMAT_ASN1));
|
||||
|
|
@ -703,8 +711,10 @@ static void test_wolfSSH_CTX_UseCert_buffer(void)
|
|||
AssertNotNull(cert);
|
||||
AssertIntNE(0, certSz);
|
||||
|
||||
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
||||
AssertIntEQ(WS_SUCCESS,
|
||||
wolfSSH_CTX_UseCert_buffer(ctx, cert, certSz, WOLFSSH_FORMAT_ASN1));
|
||||
#endif
|
||||
|
||||
wolfSSH_CTX_free(ctx);
|
||||
free(cert);
|
||||
|
|
@ -712,6 +722,364 @@ static void test_wolfSSH_CTX_UseCert_buffer(void)
|
|||
}
|
||||
|
||||
|
||||
#if defined(WOLFSSH_CERTS) || defined(WOLFSSH_OSSH_CERTS)
|
||||
|
||||
/* Public key lines. An x509v3-* line carries an RFC 6187 wire chain rather
|
||||
* than a certificate, so this API declines it the same as a plain key. */
|
||||
static const char x509v3EccLine[] =
|
||||
"x509v3-ecdsa-sha2-nistp256 AAAAB3NzaC1yc2EAAAA=\n";
|
||||
static const char sshRsaLine[] = "ssh-rsa AAAAB3NzaC1yc2EAAAA=\n";
|
||||
static const byte notACert[] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
|
||||
|
||||
#if defined(WOLFSSH_OSSH_CERTS) && !defined(WOLFSSH_NO_ED25519) && \
|
||||
!defined(NO_FILESYSTEM) && !defined(WOLFSSH_USER_FILESYSTEM)
|
||||
#define WOLFSSH_TEST_OSSH_CERT_FILE
|
||||
|
||||
/* The name routes the line. The blob behind it is not a certificate, so a
|
||||
* reader that decodes the blob rejects it there instead. */
|
||||
static const char osshCertLine[] =
|
||||
"ssh-ed25519-cert-v01@openssh.com AAAAB3NzaC1yc2EAAAA=\n";
|
||||
static const char osshCertPath[] = "./ossh-cert-line.tmp";
|
||||
|
||||
/* Stages content in a file, the only form the file APIs take. Returns 0 on
|
||||
* success. */
|
||||
static int writeTmpFile(const char* path, const void* data, size_t sz)
|
||||
{
|
||||
FILE* f = NULL;
|
||||
int ret = 0;
|
||||
|
||||
f = fopen(path, "wb");
|
||||
if (f == NULL)
|
||||
ret = -1;
|
||||
|
||||
if (ret == 0) {
|
||||
if (fwrite(data, 1, sz, f) != sz)
|
||||
ret = -2;
|
||||
/* Close either way, but keep the write error as the reason. */
|
||||
if (fclose(f) != 0 && ret == 0)
|
||||
ret = -3;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* OSSH_CERTS && ED25519 && FILESYSTEM */
|
||||
|
||||
|
||||
static void test_wolfSSH_ReadCert_buffer(void)
|
||||
{
|
||||
byte* out = NULL;
|
||||
word32 outSz = 0;
|
||||
const byte* outType = NULL;
|
||||
word32 outTypeSz = 0;
|
||||
byte flavor = 0xFF;
|
||||
#ifdef WOLFSSH_CERTS
|
||||
byte* cert = NULL;
|
||||
word32 certSz = 0;
|
||||
#ifndef WOLFSSH_NO_ED25519
|
||||
int ret;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
/* Every out parameter is required, and so is a non-empty input. */
|
||||
AssertIntEQ(WS_BAD_ARGUMENT, wolfSSH_ReadCert_buffer(NULL,
|
||||
sizeof(notACert), &out, &outSz, &outType, &outTypeSz,
|
||||
&flavor, NULL));
|
||||
AssertIntEQ(WS_BAD_ARGUMENT, wolfSSH_ReadCert_buffer(notACert, 0,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertIntEQ(WS_BAD_ARGUMENT, wolfSSH_ReadCert_buffer(notACert,
|
||||
sizeof(notACert), NULL, &outSz, &outType, &outTypeSz,
|
||||
&flavor, NULL));
|
||||
AssertIntEQ(WS_BAD_ARGUMENT, wolfSSH_ReadCert_buffer(notACert,
|
||||
sizeof(notACert), &out, &outSz, &outType, &outTypeSz,
|
||||
NULL, NULL));
|
||||
|
||||
/* Content that is not a certificate, a public key line included. */
|
||||
AssertIntEQ(WS_BAD_FILETYPE_E, wolfSSH_ReadCert_buffer(notACert,
|
||||
sizeof(notACert), &out, &outSz, &outType, &outTypeSz,
|
||||
&flavor, NULL));
|
||||
AssertIntEQ(WS_BAD_FILETYPE_E, wolfSSH_ReadCert_buffer(
|
||||
(const byte*)sshRsaLine, (word32)WSTRLEN(sshRsaLine),
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertIntEQ(WS_BAD_FILETYPE_E, wolfSSH_ReadCert_buffer(
|
||||
(const byte*)x509v3EccLine, (word32)WSTRLEN(x509v3EccLine),
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
/* A rejection must not leave the caller's flavor standing as an answer. */
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_UNKNOWN);
|
||||
|
||||
#ifdef WOLFSSH_CERTS
|
||||
AssertIntEQ(0, load_file("./keys/server-cert.pem", &cert, &certSz));
|
||||
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
||||
AssertIntEQ(WS_SUCCESS, wolfSSH_ReadCert_buffer(cert, certSz,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNotNull(out);
|
||||
AssertIntGT(outSz, 0);
|
||||
AssertIntEQ(out[0], 0x30);
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_X509);
|
||||
AssertStrEQ((const char*)outType, "x509v3-ecdsa-sha2-nistp256");
|
||||
AssertIntEQ(outTypeSz, (word32)WSTRLEN((const char*)outType));
|
||||
WFREE(out, NULL, DYNTYPE_CERT);
|
||||
out = NULL;
|
||||
#else
|
||||
/* Every fixture is ECDSA P-256. With its x509v3 name compiled out the key
|
||||
* identifies but has no name to be reported under. */
|
||||
AssertIntEQ(WS_INVALID_ALGO_ID, wolfSSH_ReadCert_buffer(cert, certSz,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNull(out);
|
||||
#endif
|
||||
|
||||
/* Half a PEM loses the CERTIFICATE header, so the sniff declines it. */
|
||||
AssertIntEQ(WS_BAD_FILETYPE_E, wolfSSH_ReadCert_buffer(cert, certSz / 2,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
free(cert);
|
||||
cert = NULL;
|
||||
|
||||
AssertIntEQ(0, load_file("./keys/server-cert.der", &cert, &certSz));
|
||||
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
||||
AssertIntEQ(WS_SUCCESS, wolfSSH_ReadCert_buffer(cert, certSz,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNotNull(out);
|
||||
AssertIntEQ(outSz, certSz);
|
||||
AssertIntEQ(0, WMEMCMP(out, cert, certSz));
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_X509);
|
||||
AssertStrEQ((const char*)outType, "x509v3-ecdsa-sha2-nistp256");
|
||||
WFREE(out, NULL, DYNTYPE_CERT);
|
||||
out = NULL;
|
||||
#else
|
||||
AssertIntEQ(WS_INVALID_ALGO_ID, wolfSSH_ReadCert_buffer(cert, certSz,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNull(out);
|
||||
#endif
|
||||
free(cert);
|
||||
cert = NULL;
|
||||
|
||||
/* Not a certificate behind the DER header. The wolfSSL error is mapped,
|
||||
* so a WS_ code reaches the caller. The read above left a name behind, so
|
||||
* this also shows a failure clearing one. */
|
||||
AssertIntEQ(WS_PARSE_E, wolfSSH_ReadCert_buffer(
|
||||
(const byte*)"\x30\x82\x01\x02",
|
||||
4, &out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNull(out);
|
||||
AssertIntEQ(outSz, 0);
|
||||
AssertNull(outType);
|
||||
AssertIntEQ(outTypeSz, 0);
|
||||
|
||||
/* A private key is the likeliest mistake, and the DER one also leads with
|
||||
* 0x30, so only the parse tells them apart. */
|
||||
AssertIntEQ(0, load_file("./keys/server-key-ecc.der", &cert, &certSz));
|
||||
AssertIntEQ(WS_PARSE_E, wolfSSH_ReadCert_buffer(cert, certSz,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNull(out);
|
||||
free(cert);
|
||||
cert = NULL;
|
||||
|
||||
/* The PEM one has no CERTIFICATE header, so the sniff declines it. Every
|
||||
* out param goes in set to prove a rejection clears them all. */
|
||||
AssertIntEQ(0, load_file("./keys/server-key-ecc.pem", &cert, &certSz));
|
||||
out = cert;
|
||||
outSz = 0xDEADBEEF;
|
||||
outType = cert;
|
||||
outTypeSz = 0xDEADBEEF;
|
||||
flavor = WOLFSSH_CERT_FLAVOR_X509;
|
||||
AssertIntEQ(WS_BAD_FILETYPE_E, wolfSSH_ReadCert_buffer(cert, certSz,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNull(out);
|
||||
AssertIntEQ(outSz, 0);
|
||||
AssertNull(outType);
|
||||
AssertIntEQ(outTypeSz, 0);
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_UNKNOWN);
|
||||
free(cert);
|
||||
cert = NULL;
|
||||
|
||||
#ifndef WOLFSSH_NO_ED25519
|
||||
/* Ed25519 must never load: it has no x509v3 name, and today the key
|
||||
* inside is not identified either. Which of the two rejections lands
|
||||
* depends on how wolfSSL returns a cert's public key, so accept both. */
|
||||
AssertIntEQ(0, load_file("./keys/server-cert-ed25519.der",
|
||||
&cert, &certSz));
|
||||
ret = wolfSSH_ReadCert_buffer(cert, certSz,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL);
|
||||
AssertTrue(ret == WS_UNIMPLEMENTED_E || ret == WS_INVALID_ALGO_ID);
|
||||
AssertNull(out);
|
||||
free(cert);
|
||||
cert = NULL;
|
||||
|
||||
/* The PEM form makes that same DER first, so rejecting it must free it. */
|
||||
AssertIntEQ(0, load_file("./keys/server-cert-ed25519.pem",
|
||||
&cert, &certSz));
|
||||
ret = wolfSSH_ReadCert_buffer(cert, certSz,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL);
|
||||
AssertTrue(ret == WS_UNIMPLEMENTED_E || ret == WS_INVALID_ALGO_ID);
|
||||
AssertNull(out);
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_UNKNOWN);
|
||||
free(cert);
|
||||
cert = NULL;
|
||||
#endif /* WOLFSSH_NO_ED25519 */
|
||||
#endif /* WOLFSSH_CERTS */
|
||||
}
|
||||
|
||||
|
||||
static void test_wolfSSH_ReadCert_file(void)
|
||||
{
|
||||
/* The arguments are checked ahead of any certificate, so those cases hold for
|
||||
* an OpenSSH-only build too. */
|
||||
#if !defined(NO_FILESYSTEM) && !defined(WOLFSSH_USER_FILESYSTEM)
|
||||
byte* out = NULL;
|
||||
word32 outSz = 0;
|
||||
const byte* outType = NULL;
|
||||
word32 outTypeSz = 0;
|
||||
byte flavor = 0xFF;
|
||||
byte stale[1];
|
||||
|
||||
AssertIntEQ(WS_BAD_FILE_E, wolfSSH_ReadCert_file(NULL,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertIntEQ(WS_BAD_ARGUMENT, wolfSSH_ReadCert_file("./keys/server-cert.pem",
|
||||
NULL, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
|
||||
/* A file that never opens still clears every out parameter. Sentinels go
|
||||
in, so a stale pointer surviving the call would fail here. */
|
||||
out = stale;
|
||||
outSz = 0xDEADBEEF;
|
||||
outType = stale;
|
||||
outTypeSz = 0xDEADBEEF;
|
||||
flavor = 0xFF;
|
||||
AssertIntEQ(WS_BAD_FILE_E, wolfSSH_ReadCert_file("./keys/no-such-cert.pem",
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNull(out);
|
||||
AssertIntEQ(outSz, 0);
|
||||
AssertNull(outType);
|
||||
AssertIntEQ(outTypeSz, 0);
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_UNKNOWN);
|
||||
|
||||
/* A directory fails further in, opening but not reading. */
|
||||
out = stale;
|
||||
outSz = 0xDEADBEEF;
|
||||
flavor = 0xFF;
|
||||
AssertIntEQ(WS_BAD_FILE_E, wolfSSH_ReadCert_file("./keys",
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNull(out);
|
||||
AssertIntEQ(outSz, 0);
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_UNKNOWN);
|
||||
|
||||
#if defined(WOLFSSH_CERTS) && !defined(WOLFSSH_NO_ECDSA_SHA2_NISTP256)
|
||||
AssertIntEQ(WS_SUCCESS, wolfSSH_ReadCert_file("./keys/server-cert.pem",
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNotNull(out);
|
||||
AssertIntEQ(out[0], 0x30);
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_X509);
|
||||
AssertStrEQ((const char*)outType, "x509v3-ecdsa-sha2-nistp256");
|
||||
WFREE(out, NULL, DYNTYPE_CERT);
|
||||
out = NULL;
|
||||
|
||||
AssertIntEQ(WS_SUCCESS, wolfSSH_ReadCert_file("./keys/server-cert.der",
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNotNull(out);
|
||||
AssertIntEQ(out[0], 0x30);
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_X509);
|
||||
WFREE(out, NULL, DYNTYPE_CERT);
|
||||
#endif /* WOLFSSH_CERTS && !WOLFSSH_NO_ECDSA_SHA2_NISTP256 */
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void test_wolfSSH_CTX_UseCert_file(void)
|
||||
{
|
||||
#if defined(WOLFSSH_CERTS) && !defined(NO_FILESYSTEM) && \
|
||||
!defined(WOLFSSH_USER_FILESYSTEM) && !defined(WOLFSSH_NO_SERVER)
|
||||
WOLFSSH_CTX* ctx = NULL;
|
||||
#ifndef WOLFSSH_NO_ED25519
|
||||
int ret;
|
||||
#endif
|
||||
|
||||
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
|
||||
AssertNotNull(ctx);
|
||||
|
||||
AssertIntEQ(WS_BAD_ARGUMENT,
|
||||
wolfSSH_CTX_UseCert_file(NULL, "./keys/server-cert.pem"));
|
||||
AssertIntEQ(WS_BAD_ARGUMENT, wolfSSH_CTX_UseCert_file(ctx, NULL));
|
||||
AssertIntEQ(WS_BAD_FILE_E,
|
||||
wolfSSH_CTX_UseCert_file(ctx, "./keys/no-such-cert.pem"));
|
||||
|
||||
/* Both encodings load through the same call. */
|
||||
#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256
|
||||
AssertIntEQ(WS_SUCCESS,
|
||||
wolfSSH_CTX_UseCert_file(ctx, "./keys/server-cert.pem"));
|
||||
AssertIntEQ(WS_SUCCESS,
|
||||
wolfSSH_CTX_UseCert_file(ctx, "./keys/server-cert.der"));
|
||||
#endif
|
||||
|
||||
/* A CTX takes the certificate itself, not a public key line. */
|
||||
AssertIntEQ(WS_BAD_FILETYPE_E,
|
||||
wolfSSH_CTX_UseCert_file(ctx, "./keys/id_ecdsa.pub"));
|
||||
#ifdef WOLFSSH_TEST_OSSH_CERT_FILE
|
||||
AssertIntEQ(0, writeTmpFile(osshCertPath, osshCertLine,
|
||||
WSTRLEN(osshCertLine)));
|
||||
AssertIntEQ(WS_BAD_FILETYPE_E, wolfSSH_CTX_UseCert_file(ctx, osshCertPath));
|
||||
AssertIntEQ(0, remove(osshCertPath));
|
||||
#endif
|
||||
|
||||
#ifndef WOLFSSH_NO_ED25519
|
||||
/* Refused here too, on the same codes as wolfSSH_ReadCert_file(). */
|
||||
ret = wolfSSH_CTX_UseCert_file(ctx, "./keys/server-cert-ed25519.der");
|
||||
AssertTrue(ret == WS_UNIMPLEMENTED_E || ret == WS_INVALID_ALGO_ID);
|
||||
ret = wolfSSH_CTX_UseCert_file(ctx, "./keys/server-cert-ed25519.pem");
|
||||
AssertTrue(ret == WS_UNIMPLEMENTED_E || ret == WS_INVALID_ALGO_ID);
|
||||
#endif
|
||||
|
||||
wolfSSH_CTX_free(ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void test_wolfSSH_CTX_AddRootCert_file(void)
|
||||
{
|
||||
#if defined(WOLFSSH_CERTS) && !defined(NO_FILESYSTEM) && \
|
||||
!defined(WOLFSSH_USER_FILESYSTEM) && !defined(WOLFSSH_NO_SERVER)
|
||||
WOLFSSH_CTX* ctx = NULL;
|
||||
|
||||
ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL);
|
||||
AssertNotNull(ctx);
|
||||
|
||||
AssertIntEQ(WS_BAD_ARGUMENT,
|
||||
wolfSSH_CTX_AddRootCert_file(NULL, "./keys/ca-cert-ecc.pem"));
|
||||
AssertIntEQ(WS_BAD_ARGUMENT, wolfSSH_CTX_AddRootCert_file(ctx, NULL));
|
||||
AssertIntEQ(WS_BAD_FILE_E,
|
||||
wolfSSH_CTX_AddRootCert_file(ctx, "./keys/no-such-ca.pem"));
|
||||
|
||||
AssertIntEQ(WS_SUCCESS,
|
||||
wolfSSH_CTX_AddRootCert_file(ctx, "./keys/ca-cert-ecc.pem"));
|
||||
AssertIntEQ(WS_SUCCESS,
|
||||
wolfSSH_CTX_AddRootCert_file(ctx, "./keys/ca-cert-ecc.der"));
|
||||
|
||||
/* A CTX takes the certificate itself, not a public key line. */
|
||||
AssertIntEQ(WS_BAD_FILETYPE_E,
|
||||
wolfSSH_CTX_AddRootCert_file(ctx, "./keys/id_ecdsa.pub"));
|
||||
|
||||
/* The cert manager rejects a non-CA in wolfSSL's codes; this path maps. */
|
||||
AssertIntEQ(WS_PARSE_E,
|
||||
wolfSSH_CTX_AddRootCert_file(ctx, "./keys/server-key-ecc.der"));
|
||||
#ifdef WOLFSSH_TEST_OSSH_CERT_FILE
|
||||
AssertIntEQ(0, writeTmpFile(osshCertPath, osshCertLine,
|
||||
WSTRLEN(osshCertLine)));
|
||||
AssertIntEQ(WS_BAD_FILETYPE_E,
|
||||
wolfSSH_CTX_AddRootCert_file(ctx, osshCertPath));
|
||||
AssertIntEQ(0, remove(osshCertPath));
|
||||
#endif
|
||||
|
||||
wolfSSH_CTX_free(ctx);
|
||||
#endif
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void test_wolfSSH_ReadCert_buffer(void) { ; }
|
||||
static void test_wolfSSH_ReadCert_file(void) { ; }
|
||||
static void test_wolfSSH_CTX_UseCert_file(void) { ; }
|
||||
static void test_wolfSSH_CTX_AddRootCert_file(void) { ; }
|
||||
|
||||
#endif /* WOLFSSH_CERTS || WOLFSSH_OSSH_CERTS */
|
||||
|
||||
|
||||
static void test_wolfSSH_CTX_UsePrivateKey_buffer_pem(void)
|
||||
{
|
||||
#if defined(WOLFSSH_CERTS) && !defined(WOLFSSH_NO_SERVER)
|
||||
|
|
@ -2204,6 +2572,124 @@ static const byte ossh_vec_ecc[] = {
|
|||
};
|
||||
#endif /* WOLFSSH_NO_ECDSA_SHA2_NISTP256 */
|
||||
|
||||
#ifdef WOLFSSL_BASE64_ENCODE
|
||||
|
||||
/* Renders a blob as the "name base64" line an authorized_keys style file
|
||||
* holds. Returns the length written to line. */
|
||||
static word32 buildOsshCertLine(byte* line, word32 lineCap,
|
||||
const byte* vec, word32 vecSz)
|
||||
{
|
||||
static const char name[] = "ssh-ed25519-cert-v01@openssh.com ";
|
||||
word32 nameSz, b64Sz;
|
||||
|
||||
nameSz = (word32)WSTRLEN(name);
|
||||
AssertIntGT(lineCap, nameSz + 2);
|
||||
WMEMCPY(line, name, nameSz);
|
||||
|
||||
b64Sz = lineCap - nameSz - 2;
|
||||
AssertIntEQ(0, Base64_Encode_NoNl(vec, vecSz, line + nameSz, &b64Sz));
|
||||
line[nameSz + b64Sz] = '\n';
|
||||
|
||||
return nameSz + b64Sz + 1;
|
||||
}
|
||||
|
||||
|
||||
/* Reads a certificate vector back through the public API in the "name base64"
|
||||
* line form an authorized_keys style file holds. */
|
||||
static void test_wolfSSH_ReadCert_buffer_ossh(void)
|
||||
{
|
||||
byte line[2048];
|
||||
byte* out = NULL;
|
||||
const byte* outType = NULL;
|
||||
word32 outSz = 0, outTypeSz = 0, lineSz;
|
||||
byte flavor = 0xFF;
|
||||
|
||||
lineSz = buildOsshCertLine(line, (word32)sizeof(line),
|
||||
ossh_vec_ed, (word32)sizeof(ossh_vec_ed));
|
||||
|
||||
AssertIntEQ(WS_SUCCESS, wolfSSH_ReadCert_buffer(line, lineSz,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNotNull(out);
|
||||
AssertIntEQ(outSz, (word32)sizeof(ossh_vec_ed));
|
||||
AssertIntEQ(0, WMEMCMP(out, ossh_vec_ed, sizeof(ossh_vec_ed)));
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_OSSH);
|
||||
AssertStrEQ((const char*)outType, "ssh-ed25519-cert-v01@openssh.com");
|
||||
AssertIntEQ(outTypeSz, (word32)WSTRLEN((const char*)outType));
|
||||
WFREE(out, NULL, DYNTYPE_CERT);
|
||||
out = NULL;
|
||||
|
||||
/* Half a blob still forms a valid line, so the parse must reject it. The
|
||||
* line names its algorithm, so outType is set before that parse runs and
|
||||
* has to be taken back with the rest. */
|
||||
lineSz = buildOsshCertLine(line, (word32)sizeof(line),
|
||||
ossh_vec_ed, (word32)sizeof(ossh_vec_ed) / 2);
|
||||
|
||||
AssertIntLT(wolfSSH_ReadCert_buffer(line, lineSz,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL), 0);
|
||||
AssertNull(out);
|
||||
AssertIntEQ(outSz, 0);
|
||||
AssertNull(outType);
|
||||
AssertIntEQ(outTypeSz, 0);
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_UNKNOWN);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void test_wolfSSH_ReadCert_buffer_ossh(void) { ; }
|
||||
|
||||
#endif /* WOLFSSL_BASE64_ENCODE */
|
||||
|
||||
#ifdef WOLFSSH_TEST_OSSH_CERT_FILE
|
||||
|
||||
/* Reads the same line form from a file. This is the only certificate the
|
||||
* file reader takes in a build without X.509 support. */
|
||||
static void test_wolfSSH_ReadCert_file_ossh(void)
|
||||
{
|
||||
byte* out = NULL;
|
||||
const byte* outType = NULL;
|
||||
word32 outSz = 0, outTypeSz = 0;
|
||||
byte flavor = 0xFF;
|
||||
#ifdef WOLFSSL_BASE64_ENCODE
|
||||
byte line[2048];
|
||||
word32 lineSz;
|
||||
|
||||
lineSz = buildOsshCertLine(line, (word32)sizeof(line),
|
||||
ossh_vec_ed, (word32)sizeof(ossh_vec_ed));
|
||||
AssertIntEQ(0, writeTmpFile(osshCertPath, line, lineSz));
|
||||
|
||||
AssertIntEQ(WS_SUCCESS, wolfSSH_ReadCert_file(osshCertPath,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL));
|
||||
AssertNotNull(out);
|
||||
AssertIntEQ(outSz, (word32)sizeof(ossh_vec_ed));
|
||||
AssertIntEQ(0, WMEMCMP(out, ossh_vec_ed, sizeof(ossh_vec_ed)));
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_OSSH);
|
||||
AssertStrEQ((const char*)outType, "ssh-ed25519-cert-v01@openssh.com");
|
||||
AssertIntEQ(outTypeSz, (word32)WSTRLEN((const char*)outType));
|
||||
WFREE(out, NULL, DYNTYPE_CERT);
|
||||
out = NULL;
|
||||
AssertIntEQ(0, remove(osshCertPath));
|
||||
#endif /* WOLFSSL_BASE64_ENCODE */
|
||||
|
||||
/* The name only routes the file; the blob still has to parse. Rejecting
|
||||
* it clears the name the line had already supplied. */
|
||||
AssertIntEQ(0, writeTmpFile(osshCertPath, osshCertLine,
|
||||
WSTRLEN(osshCertLine)));
|
||||
AssertIntLT(wolfSSH_ReadCert_file(osshCertPath,
|
||||
&out, &outSz, &outType, &outTypeSz, &flavor, NULL), 0);
|
||||
AssertNull(out);
|
||||
AssertIntEQ(outSz, 0);
|
||||
AssertNull(outType);
|
||||
AssertIntEQ(outTypeSz, 0);
|
||||
AssertIntEQ(flavor, WOLFSSH_CERT_FLAVOR_UNKNOWN);
|
||||
AssertIntEQ(0, remove(osshCertPath));
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void test_wolfSSH_ReadCert_file_ossh(void) { ; }
|
||||
|
||||
#endif /* WOLFSSH_TEST_OSSH_CERT_FILE */
|
||||
|
||||
/* Parse, verify the CA signature, and validate the options of each committed
|
||||
* certificate vector; then flip the final signature byte and confirm the
|
||||
* verification fails while the parse still succeeds. */
|
||||
|
|
@ -5773,6 +6259,10 @@ int wolfSSH_ApiTest(int argc, char** argv)
|
|||
test_wolfSSH_ConvertConsole();
|
||||
test_wolfSSH_CTX_UsePrivateKey_buffer();
|
||||
test_wolfSSH_CTX_UseCert_buffer();
|
||||
test_wolfSSH_CTX_UseCert_file();
|
||||
test_wolfSSH_CTX_AddRootCert_file();
|
||||
test_wolfSSH_ReadCert_buffer();
|
||||
test_wolfSSH_ReadCert_file();
|
||||
test_wolfSSH_CTX_UsePrivateKey_buffer_pem();
|
||||
test_wolfSSH_CTX_SetWindowPacketSize();
|
||||
test_wolfSSH_CertMan();
|
||||
|
|
@ -5801,6 +6291,8 @@ int wolfSSH_ApiTest(int argc, char** argv)
|
|||
#endif
|
||||
#ifdef WOLFSSH_OSSH_CERTS
|
||||
#ifndef WOLFSSH_NO_ED25519
|
||||
test_wolfSSH_ReadCert_buffer_ossh();
|
||||
test_wolfSSH_ReadCert_file_ossh();
|
||||
test_wolfSSH_OsshCert_valid();
|
||||
#ifdef WOLFSSH_TEST_OSSH_VEC_ECC
|
||||
test_wolfSSH_OsshCert_ecc_curve_mismatch();
|
||||
|
|
|
|||
|
|
@ -1328,6 +1328,9 @@ WOLFSSH_LOCAL int GetOpenSshKey(WS_KeySignature *key,
|
|||
WOLFSSH_LOCAL int GetOpenSshPublicKey(WS_KeySignature *key,
|
||||
const byte* buf, word32 len, word32* idx);
|
||||
#endif
|
||||
#ifdef WOLFSSH_CERTS
|
||||
WOLFSSH_LOCAL int IdentifyCert(const byte* in, word32 inSz, void* heap);
|
||||
#endif
|
||||
|
||||
|
||||
/* Parsing functions */
|
||||
|
|
|
|||
|
|
@ -104,6 +104,19 @@ WOLFSSH_API int wolfSSH_ReadKey_file(const char* name,
|
|||
byte** out, word32* outSz, const byte** outType, word32* outTypeSz,
|
||||
byte* isPrivate, void* heap);
|
||||
|
||||
#if defined(WOLFSSH_CERTS) || defined(WOLFSSH_OSSH_CERTS)
|
||||
/* Decodes a PEM/DER X.509 cert or OpenSSH cert line, detected from content.
|
||||
* Caller frees out via heap; on failure every out param is cleared. */
|
||||
WOLFSSH_API int wolfSSH_ReadCert_buffer(const byte* in, word32 inSz,
|
||||
byte** out, word32* outSz, const byte** outType, word32* outTypeSz,
|
||||
byte* flavor, void* heap);
|
||||
#if !defined(NO_FILESYSTEM) && !defined(WOLFSSH_USER_FILESYSTEM)
|
||||
WOLFSSH_API int wolfSSH_ReadCert_file(const char* name,
|
||||
byte** out, word32* outSz, const byte** outType, word32* outTypeSz,
|
||||
byte* flavor, void* heap);
|
||||
#endif
|
||||
#endif /* WOLFSSH_CERTS || WOLFSSH_OSSH_CERTS */
|
||||
|
||||
/* SetAlgoList* validate the list, returning WS_SUCCESS, WS_INVALID_ALGO_ID for
|
||||
* a bad list, or WS_SSH_CTX_NULL_E / WS_SSH_NULL_E for a NULL ctx / ssh.
|
||||
* Kex/Cipher/Mac reject NULL. Key accepts NULL only on a server, restoring the
|
||||
|
|
@ -494,6 +507,13 @@ WOLFSSH_API int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX* ctx,
|
|||
const byte* cert, word32 certSz, int format);
|
||||
WOLFSSH_API int wolfSSH_CTX_AddRootCert_buffer(WOLFSSH_CTX* ctx,
|
||||
const byte* cert, word32 certSz, int format);
|
||||
#if !defined(NO_FILESYSTEM) && !defined(WOLFSSH_USER_FILESYSTEM)
|
||||
/* PEM or DER is detected from the file's content. */
|
||||
WOLFSSH_API int wolfSSH_CTX_UseCert_file(WOLFSSH_CTX* ctx,
|
||||
const char* name);
|
||||
WOLFSSH_API int wolfSSH_CTX_AddRootCert_file(WOLFSSH_CTX* ctx,
|
||||
const char* name);
|
||||
#endif
|
||||
#endif /* WOLFSSH_CERTS */
|
||||
WOLFSSH_API int wolfSSH_CTX_SetWindowPacketSize(WOLFSSH_CTX* ctx,
|
||||
word32 windowSz, word32 maxPacketSz);
|
||||
|
|
@ -600,6 +620,16 @@ enum WS_FormatTypes {
|
|||
};
|
||||
|
||||
|
||||
/* X.509 certificates are consumed by UseCert/AddRootCert, OpenSSH ones by
|
||||
the trusted user CA machinery. UNKNOWN leads so that a zeroed flavor is
|
||||
not a claim about the content. */
|
||||
enum WS_CertFlavors {
|
||||
WOLFSSH_CERT_FLAVOR_UNKNOWN,
|
||||
WOLFSSH_CERT_FLAVOR_X509,
|
||||
WOLFSSH_CERT_FLAVOR_OSSH
|
||||
};
|
||||
|
||||
|
||||
/* bit map */
|
||||
#define WOLFSSH_USERAUTH_PASSWORD 0x01
|
||||
#define WOLFSSH_USERAUTH_PUBLICKEY 0x02
|
||||
|
|
|
|||
Loading…
Reference in New Issue