diff --git a/README.md b/README.md index 4f2e46cf..d2b30756 100644 --- a/README.md +++ b/README.md @@ -22,6 +22,11 @@ On some systems the optional ldconfig command is needed after installing. To use the key generation function in wolfSSH, wolfSSL will need to be configured with keygen: `--enable-keygen`. +When using X.509 certificates for user authentication, wolfSSL must not be +built without TLS enabled. wolfSSH uses wolfSSL's certificate manager system +for X.509, including OCSP lookups. To allow OCSP, add `--enable-ocsp` to the +wolfSSL configure. + If the bulk of wolfSSL code isn't desired, wolfSSL can be configured with the crypto only option: `--enable-cryptonly`. @@ -402,6 +407,7 @@ behavior, give the echoserver the command line option `-f`. $ ./examples/echoserver/echoserver -f + POST-QUANTUM ============ @@ -462,3 +468,24 @@ NOTE: when prompted, enter the password which is "upthehill". You can type a line of text and when you press enter, the line will be echoed back. Use CTRL-C to terminate the connection. + + +CERTIFICATE SUPPORT +=================== + +wolfSSH can accept X.509 certificates in place of just public keys when +authenticating a user. This feature is currently a work in process. + +To compile wolfSSH with X.509 support, use the `--enable-certs` build option +or define `WOLFSSH_CERTS`: + + $ ./configure --enable-certs + $ make + +To provide a CA root certificate to validate a user's certificate, give the +echoserver the command line option `-a`. + + $ ./examples/echoserver/echoserver -a ./keys/ca-cert.pem + +The echoserver and client have a fake user named "john" whose certificate +will be used for authentication. diff --git a/examples/client/client.c b/examples/client/client.c index 2bff58e6..3d1ff654 100644 --- a/examples/client/client.c +++ b/examples/client/client.c @@ -198,6 +198,11 @@ static word32 userPrivateKeyTypeSz = 0; static byte isPrivate = 0; +#ifdef WOLFSSH_CERTS +static const byte publicKeyType[] = "x509v3-ecdsa-sha2-nistp256"; +static const byte privateKeyType[] = "ecdsa-sha2-nistp256"; +#endif + #ifndef WOLFSSH_NO_RSA static const char* hanselPublicRsa = "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9P3ZFowOsONXHD5MwWiCciXytBRZGho" @@ -392,7 +397,11 @@ static int wsUserAuth(byte authType, /* in the case that the name is hansel or in the case that the user * passed in a public key file, use public key auth */ if ((XSTRNCMP((char*)authData->username, "hansel", - authData->usernameSz) == 0) || pubKeyName != NULL) { + authData->usernameSz) == 0) || + (XSTRNCMP((char*)authData->username, "john", + authData->usernameSz) == 0) || + pubKeyName != NULL) { + if (authType == WOLFSSH_USERAUTH_PASSWORD) { printf("rejecting password type with %s in favor of pub key\n", (char*)authData->username); @@ -683,6 +692,62 @@ static THREAD_RET readPeer(void* in) } #endif /* !SINGLE_THREADED && !WOLFSSL_NUCLEUS */ + +#ifdef WOLFSSH_CERTS + +static int load_der_file(const char* filename, byte** out, word32* outSz) +{ + WFILE* file; + byte* in; + word32 inSz; + int ret; + + if (filename == NULL || out == NULL || outSz == NULL) + return -1; + + ret = WFOPEN(&file, filename, "rb"); + if (ret != 0 || file == WBADFILE) + return -1; + + if (WFSEEK(file, 0, WSEEK_END) != 0) { + WFCLOSE(file); + return -1; + } + inSz = (word32)WFTELL(file); + WREWIND(file); + + if (inSz == 0) { + WFCLOSE(file); + return -1; + } + + in = (byte*)WMALLOC(inSz, NULL, 0); + if (in == NULL) { + WFCLOSE(file); + return -1; + } + + ret = (int)WFREAD(in, 1, inSz, file); + if (ret <= 0 || (word32)ret != inSz) { + ret = -1; + WFREE(in, NULL, 0); + in = 0; + inSz = 0; + } + else + ret = 0; + + *out = in; + *outSz = inSz; + + WFCLOSE(file); + + return ret; +} + +#endif /* WOLFSSH_CERTS */ + + #if defined(WOLFSSL_PTHREADS) && defined(WOLFSSL_TEST_GLOBAL_REQ) static int callbackGlobalReq(WOLFSSH *ssh, void *buf, word32 sz, int reply, void *ctx) @@ -970,6 +1035,22 @@ THREAD_RETURN WOLFSSH_THREAD client_test(void* args) if (ret != 0) err_sys("Couldn't load private key file."); } +#ifdef WOLFSSH_CERTS + if (XSTRCMP("john", username) == 0) { + ret = load_der_file("./keys/john-cert.der", + &userPublicKey, &userPublicKeySz); + if (ret != 0) err_sys("Couldn't load certificate file."); + ret = load_der_file("./keys/john-key.der", + &userPrivateKey, &userPrivateKeySz); + if (ret != 0) err_sys("Couldn't load private key file."); + + userPublicKeyType = publicKeyType; + userPublicKeyTypeSz = (word32)WSTRLEN((const char*)publicKeyType); + userPrivateKeyType = privateKeyType; + userPrivateKeyTypeSz = (word32)WSTRLEN((const char*)privateKeyType); + } + else +#endif if (pubKeyName == NULL) { byte* p = userPublicKey; userPublicKeySz = sizeof(userPublicKeyBuf); diff --git a/examples/echoserver/echoserver.c b/examples/echoserver/echoserver.c index d28e238c..8102e4be 100644 --- a/examples/echoserver/echoserver.c +++ b/examples/echoserver/echoserver.c @@ -1714,6 +1714,26 @@ static int LoadPublicKeyBuffer(byte* buf, word32 bufSz, PwMapList* list) } +#ifdef WOLFSSH_CERTS +static int LoadCertBuffer(byte* buf, word32 bufSz, PwMapList* list) +{ + if (list == NULL) + return -1; + + if (buf == NULL || bufSz == 0) + return 0; + + if (PwMapNew(list, + WOLFSSH_USERAUTH_PUBLICKEY, + (const byte*)"john", 4, buf, bufSz) == NULL ) { + return -1; + } + + return 0; +} +#endif + + static int wsUserAuth(byte authType, WS_UserAuthData* authData, void* ctx) @@ -1828,6 +1848,9 @@ static void ShowUsage(void) printf(" -d set the home directory for SFTP connections\n"); #endif printf(" -j load in a public key to accept from peer\n"); +#ifdef WOLFSSH_CERTS + printf(" -a load in a root CA certificate file\n"); +#endif } @@ -1865,13 +1888,16 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) const char* defaultSftpPath = NULL; char nonBlock = 0; char* userPubKey = NULL; + #ifdef WOLFSSH_CERTS + char* caCert = NULL; + #endif int argc = serverArgs->argc; char** argv = serverArgs->argv; serverArgs->return_code = 0; if (argc > 0) { - while ((ch = mygetopt(argc, argv, "?1d:efEp:R:Nj:")) != -1) { + while ((ch = mygetopt(argc, argv, "?1a:d:efEp:R:Nj:")) != -1) { switch (ch) { case '?' : ShowUsage(); @@ -1881,6 +1907,11 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) multipleConnections = 0; break; + case 'a': + #ifdef WOLFSSH_CERTS + caCert = myoptarg; + #endif + break; case 'e' : userEcc = 1; break; @@ -2026,6 +2057,57 @@ THREAD_RETURN WOLFSSH_THREAD echoserver_test(void* args) LoadPublicKeyBuffer(userBuf, userBufSz, &pwMapList); } + #ifdef WOLFSSH_CERTS + { + byte* certBuf = NULL; + word32 certBufSz = 0; + const char* filename = "./keys/john-cert.der"; + + load_file(filename, NULL, &certBufSz); + + if (certBufSz == 0) { + fprintf(stderr, + "Couldn't find size of file %s.\n", filename); + WEXIT(EXIT_FAILURE); + } + + certBuf = (byte*)WMALLOC(certBufSz, NULL, 0); + if (certBuf == NULL) { + fprintf(stderr, "WMALLOC failed\n"); + WEXIT(EXIT_FAILURE); + } + load_file(filename, certBuf, &certBufSz); + LoadCertBuffer(certBuf, certBufSz, &pwMapList); + } + if (caCert) { + byte* certBuf = NULL; + word32 certBufSz = 0; + int ret = 0; + + load_file(caCert, NULL, &certBufSz); + + if (certBufSz == 0) { + fprintf(stderr, + "Couldn't find size of file %s.\n", caCert); + WEXIT(EXIT_FAILURE); + } + + certBuf = (byte*)WMALLOC(certBufSz, NULL, 0); + if (certBuf == NULL) { + fprintf(stderr, "WMALLOC failed\n"); + WEXIT(EXIT_FAILURE); + } + load_file(caCert, certBuf, &certBufSz); + ret = wolfSSH_CTX_AddRootCert_buffer(ctx, certBuf, certBufSz, + WOLFSSH_FORMAT_ASN1); + if (ret != 0) { + fprintf(stderr, "Couldn't add root cert\n"); + WEXIT(EXIT_FAILURE); + } + WFREE(certBuf, NULL, 0); + } + #endif + bufSz = (word32)WSTRLEN(samplePasswordBuffer); WMEMCPY(keyLoadBuf, samplePasswordBuffer, bufSz); keyLoadBuf[bufSz] = 0; diff --git a/keys/ca-ecc-cert.der b/keys/ca-ecc-cert.der new file mode 100644 index 00000000..e3c0c8e7 Binary files /dev/null and b/keys/ca-ecc-cert.der differ diff --git a/keys/ca-ecc-cert.pem b/keys/ca-ecc-cert.pem new file mode 100644 index 00000000..1d0148d0 --- /dev/null +++ b/keys/ca-ecc-cert.pem @@ -0,0 +1,53 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 29:bf:2b:cd:bf:55:54:49:85:b3:69:4e:e1:85:37:79:1e:81:f9:c2 + Signature Algorithm: ecdsa-with-SHA256 + Issuer: C = US, ST = Washington, L = Seattle, O = wolfSSL, OU = Development, CN = www.wolfssl.com, emailAddress = info@wolfssl.com + Validity + Not Before: Feb 15 12:50:24 2022 GMT + Not After : Nov 11 12:50:24 2024 GMT + Subject: C = US, ST = Washington, L = Seattle, O = wolfSSL, OU = Development, CN = www.wolfssl.com, emailAddress = info@wolfssl.com + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:02:d3:d9:6e:d6:01:8e:45:c8:b9:90:31:e5:c0: + 4c:e3:9e:ad:29:38:98:ba:10:d6:e9:09:2a:80:a9: + 2e:17:2a:b9:8a:bf:33:83:46:e3:95:0b:e4:77:40: + b5:3b:43:45:33:0f:61:53:7c:37:44:c1:cb:fc:80: + ca:e8:43:ea:a7 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Key Identifier: + 56:8E:9A:C3:F0:42:DE:18:B9:45:55:6E:F9:93:CF:EA:C3:F3:A5:21 + 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 + + X509v3 Basic Constraints: critical + CA:TRUE + X509v3 Key Usage: critical + Digital Signature, Certificate Sign, CRL Sign + Signature Algorithm: ecdsa-with-SHA256 + 30:44:02:20:78:ed:4c:1c:a7:2d:b3:35:0b:1d:46:a3:37:31: + 0b:8a:05:39:c8:28:31:58:35:f1:98:f7:4b:72:c0:4f:e6:7f: + 02:20:02:f2:09:2b:3a:e1:36:92:bf:58:6a:03:12:2d:79:e6: + bd:06:45:61:b9:0e:39:e1:9c:f0:a8:2e:0b:1e:8c:b2 +-----BEGIN CERTIFICATE----- +MIIClDCCAjugAwIBAgIUKb8rzb9VVEmFs2lO4YU3eR6B+cIwCgYIKoZIzj0EAwIw +gZcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdT +ZWF0dGxlMRAwDgYDVQQKDAd3b2xmU1NMMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEY +MBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdv +bGZzc2wuY29tMB4XDTIyMDIxNTEyNTAyNFoXDTI0MTExMTEyNTAyNFowgZcxCzAJ +BgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdTZWF0dGxl +MRAwDgYDVQQKDAd3b2xmU1NMMRQwEgYDVQQLDAtEZXZlbG9wbWVudDEYMBYGA1UE +AwwPd3d3LndvbGZzc2wuY29tMR8wHQYJKoZIhvcNAQkBFhBpbmZvQHdvbGZzc2wu +Y29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEAtPZbtYBjkXIuZAx5cBM456t +KTiYuhDW6QkqgKkuFyq5ir8zg0bjlQvkd0C1O0NFMw9hU3w3RMHL/IDK6EPqp6Nj +MGEwHQYDVR0OBBYEFFaOmsPwQt4YuUVVbvmTz+rD86UhMB8GA1UdIwQYMBaAFFaO +msPwQt4YuUVVbvmTz+rD86UhMA8GA1UdEwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQD +AgGGMAoGCCqGSM49BAMCA0cAMEQCIHjtTBynLbM1Cx1GozcxC4oFOcgoMVg18Zj3 +S3LAT+Z/AiAC8gkrOuE2kr9YagMSLXnmvQZFYbkOOeGc8KguCx6Msg== +-----END CERTIFICATE----- diff --git a/keys/john-cert.der b/keys/john-cert.der new file mode 100644 index 00000000..a5fc4944 Binary files /dev/null and b/keys/john-cert.der differ diff --git a/keys/john-cert.pem b/keys/john-cert.pem new file mode 100644 index 00000000..31a286c6 --- /dev/null +++ b/keys/john-cert.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICSzCCAfGgAwIBAgIQUg79CEuTa/LBX88kspbmFzAKBggqhkjOPQQDAjCBlzEL +MAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0 +bGUxEDAOBgNVBAoMB3dvbGZTU0wxFDASBgNVBAsMC0RldmVsb3BtZW50MRgwFgYD +VQQDDA93d3cud29sZnNzbC5jb20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNz +bC5jb20wIhgPMjAyMjAyMjYyMjEyMzNaGA8yMDIzMDcxMjIyMTIzM1owgZExCzAJ +BgNVBAYTAlVTMQswCQYDVQQIDAJXQTEQMA4GA1UEBwwHU2VhdHRsZTEUMBIGA1UE +CgwLd29sZlNTTCBJbmMxFDASBgNVBAsMC0RldmVsb3BtZW50MRYwFAYDVQQDDA1K +b2huIFNhZnJhbmVrMR8wHQYJKoZIhvcNAQkBFhBqb2huQHdvbGZzc2wuY29tMFkw +EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEtwW0ReBpgLwDxGQuzKFZg52AGKsquO5 +Jhwq4ij43dh5P8ACXtHRxf48Y/UfrhNLacro7fQ2umLgocgYEEtV4aMfMB0wGwYD +VR0RBBQwEoEQam9obkB3b2xmc3NsLmNvbTAKBggqhkjOPQQDAgNIADBFAiEAtsaS +gxyAAWzJ+nSku+VnVz821mL5tnw2rxTUKnWYg10CIE8/UF6OKGcJMJcUpTPc4G7F +IYffUYF+T1BAhyEwTsxx +-----END CERTIFICATE----- diff --git a/keys/john-key.der b/keys/john-key.der new file mode 100644 index 00000000..e011d7b4 Binary files /dev/null and b/keys/john-key.der differ diff --git a/keys/john-key.pem b/keys/john-key.pem new file mode 100644 index 00000000..cca58050 --- /dev/null +++ b/keys/john-key.pem @@ -0,0 +1,9 @@ +ASN1 OID: prime256v1 +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIEkbklf9L+5N9RuSnwmbgv/yCarqVK3j+PagHLtcDsw7oAoGCCqGSM49 +AwEHoUQDQgAEEtwW0ReBpgLwDxGQuzKFZg52AGKsquO5Jhwq4ij43dh5P8ACXtHR +xf48Y/UfrhNLacro7fQ2umLgocgYEEtV4Q== +-----END EC PRIVATE KEY----- diff --git a/keys/john-key.pub b/keys/john-key.pub new file mode 100644 index 00000000..9c72014b --- /dev/null +++ b/keys/john-key.pub @@ -0,0 +1 @@ +ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBBLcFtEXgaYC8A8RkLsyhWYOdgBirKrjuSYcKuIo+N3YeT/AAl7R0cX+PGP1H64TS2nK6O30Nrpi4KHIGBBLVeE= diff --git a/keys/server-cert.der b/keys/server-cert.der new file mode 100644 index 00000000..fcecf41a Binary files /dev/null and b/keys/server-cert.der differ diff --git a/keys/server-cert.pem b/keys/server-cert.pem new file mode 100644 index 00000000..444644b0 --- /dev/null +++ b/keys/server-cert.pem @@ -0,0 +1,57 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 3 (0x3) + Signature Algorithm: ecdsa-with-SHA256 + Issuer: C = US, ST = Washington, L = Seattle, O = wolfSSL, OU = Development, CN = www.wolfssl.com, emailAddress = info@wolfssl.com + Validity + Not Before: Dec 20 23:07:25 2021 GMT + Not After : Sep 15 23:07:25 2024 GMT + Subject: C = US, ST = Washington, L = Seattle, O = Eliptic, OU = ECC, CN = www.wolfssl.com, emailAddress = info@wolfssl.com + Subject Public Key Info: + Public Key Algorithm: id-ecPublicKey + Public-Key: (256 bit) + pub: + 04:bb:33:ac:4c:27:50:4a:c6:4a:a5:04:c3:3c:de: + 9f:36:db:72:2d:ce:94:ea:2b:fa:cb:20:09:39:2c: + 16:e8:61:02:e9:af:4d:d3:02:93:9a:31:5b:97:92: + 21:7f:f0:cf:18:da:91:11:02:34:86:e8:20:58:33: + 0b:80:34:89:d8 + ASN1 OID: prime256v1 + NIST CURVE: P-256 + X509v3 extensions: + X509v3 Subject Key Identifier: + 5D:5D:26:EF:AC:7E:36:F9:9B:76:15:2B:4A:25:02:23:EF:B2:89:30 + 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 + + X509v3 Basic Constraints: critical + CA:FALSE + X509v3 Key Usage: critical + Digital Signature, Key Encipherment, Key Agreement + X509v3 Extended Key Usage: + TLS Web Server Authentication + Netscape Cert Type: + SSL Server + Signature Algorithm: ecdsa-with-SHA256 + 30:44:02:20:5a:67:b9:ee:02:34:27:1b:d4:c4:35:7b:ed:59: + 8e:63:c4:8a:b7:e9:92:c1:8a:76:b0:8b:cd:24:49:78:ba:ef: + 02:20:29:b8:b6:5f:83:f7:56:6a:f1:4d:d9:9f:52:2a:f9:8f: + 53:14:49:8b:5f:5e:87:af:7f:ca:2e:e0:d8:e7:75:0c +-----BEGIN CERTIFICATE----- +MIICoDCCAkegAwIBAgIBAzAKBggqhkjOPQQDAjCBlzELMAkGA1UEBhMCVVMxEzAR +BgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxEDAOBgNVBAoMB3dv +bGZTU0wxFDASBgNVBAsMC0RldmVsb3BtZW50MRgwFgYDVQQDDA93d3cud29sZnNz +bC5jb20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29sZnNzbC5jb20wHhcNMjExMjIw +MjMwNzI1WhcNMjQwOTE1MjMwNzI1WjCBjzELMAkGA1UEBhMCVVMxEzARBgNVBAgM +Cldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUxEDAOBgNVBAoMB0VsaXB0aWMx +DDAKBgNVBAsMA0VDQzEYMBYGA1UEAwwPd3d3LndvbGZzc2wuY29tMR8wHQYJKoZI +hvcNAQkBFhBpbmZvQHdvbGZzc2wuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcD +QgAEuzOsTCdQSsZKpQTDPN6fNttyLc6U6iv6yyAJOSwW6GEC6a9N0wKTmjFbl5Ih +f/DPGNqREQI0huggWDMLgDSJ2KOBiTCBhjAdBgNVHQ4EFgQUXV0m76x+NvmbdhUr +SiUCI++yiTAwHwYDVR0jBBgwFoAUVo6aw/BC3hi5RVVu+ZPP6sPzpSEwDAYDVR0T +AQH/BAIwADAOBgNVHQ8BAf8EBAMCA6gwEwYDVR0lBAwwCgYIKwYBBQUHAwEwEQYJ +YIZIAYb4QgEBBAQDAgZAMAoGCCqGSM49BAMCA0cAMEQCIFpnue4CNCcb1MQ1e+1Z +jmPEirfpksGKdrCLzSRJeLrvAiApuLZfg/dWavFN2Z9SKvmPUxRJi19eh69/yi7g +2Od1DA== +-----END CERTIFICATE----- diff --git a/keys/server-key.pem b/keys/server-key.pem new file mode 100644 index 00000000..03e7a617 --- /dev/null +++ b/keys/server-key.pem @@ -0,0 +1,9 @@ +ASN1 OID: prime256v1 +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIEW2aQJznGyFoThbcujox6zEA41TNQT6bCjcNI3hqAmMoAoGCCqGSM49 +AwEHoUQDQgAEuzOsTCdQSsZKpQTDPN6fNttyLc6U6iv6yyAJOSwW6GEC6a9N0wKT +mjFbl5Ihf/DPGNqREQI0huggWDMLgDSJ2A== +-----END EC PRIVATE KEY----- diff --git a/src/certman.c b/src/certman.c index 1c651e84..4b1cdb3b 100644 --- a/src/certman.c +++ b/src/certman.c @@ -76,13 +76,37 @@ #endif +struct WOLFSSH_CERTMAN { + void* heap; + WOLFSSL_CERT_MANAGER* cm; +}; + + static WOLFSSH_CERTMAN* _CertMan_init(WOLFSSH_CERTMAN* cm, void* heap) { - (void)heap; WLOG_ENTER(); if (cm != NULL) { WMEMSET(cm, 0, sizeof *cm); + cm->cm = wolfSSL_CertManagerNew_ex(heap); + if (cm->cm != NULL) { + int ret; + + ret = wolfSSL_CertManagerEnableOCSP(cm->cm, + WOLFSSL_OCSP_CHECKALL); + + if (ret == WOLFSSL_SUCCESS) { + WLOG(WS_LOG_CERTMAN, "Enabled OCSP"); + } + else { + WLOG(WS_LOG_CERTMAN, "Couldn't enable OCSP"); + wolfSSL_CertManagerFree(cm->cm); + cm = NULL; + } + } + else { + cm = NULL; + } } WLOG_LEAVE_PTR(cm); @@ -96,6 +120,9 @@ static void _CertMan_ResourceFree(WOLFSSH_CERTMAN* cm, void* heap) WLOG_ENTER(); if (cm != NULL) { + if (cm->cm != NULL) { + wolfSSL_CertManagerFree(cm->cm); + } WMEMSET(cm, 0, sizeof *cm); } @@ -137,17 +164,33 @@ void wolfSSH_CERTMAN_free(WOLFSSH_CERTMAN* cm) } -WOLFSSH_CERTMAN* wolfSSH_CERTMAN_init(WOLFSSH_CERTMAN* cm, void* heap) +int wolfSSH_CERTMAN_LoadRootCA_buffer(WOLFSSH_CERTMAN* cm, + const unsigned char* rootCa, word32 rootCaSz) { + int ret = WS_SUCCESS; + WLOG_ENTER(); - if (cm != NULL) { - cm = _CertMan_init(cm, heap); - } + ret = wolfSSL_CertManagerLoadCABuffer(cm->cm, rootCa, rootCaSz, + WOLFSSL_FILETYPE_ASN1); - WLOG_LEAVE_PTR(cm); - return cm; + WLOG_LEAVE(ret); + return ret; } +int wolfSSH_CERTMAN_VerifyCert_buffer(WOLFSSH_CERTMAN* cm, + const unsigned char* cert, word32 certSz) +{ + int ret = WS_SUCCESS; + + WLOG_ENTER(); + + ret = wolfSSL_CertManagerVerifyBuffer(cm->cm, cert, certSz, + WOLFSSL_FILETYPE_ASN1); + + WLOG_LEAVE(ret); + return ret; +} + #endif /* WOLFSSH_CERTS */ diff --git a/src/internal.c b/src/internal.c index 16c948ec..78abb730 100644 --- a/src/internal.c +++ b/src/internal.c @@ -443,6 +443,7 @@ static HandshakeInfo* HandshakeInfoNew(void* heap) WMEMSET(newHs, 0, sizeof(HandshakeInfo)); newHs->kexId = ID_NONE; newHs->pubKeyId = ID_NONE; + newHs->sigId = ID_NONE; newHs->encryptId = ID_NONE; newHs->macId = ID_NONE; newHs->blockSz = MIN_BLOCK_SZ; @@ -519,6 +520,9 @@ WOLFSSH_CTX* CtxInit(WOLFSSH_CTX* ctx, byte side, void* heap) ctx->banner = cannedBanner; ctx->bannerSz = cannedBannerSz; #endif /* DEBUG_WOLFSSH */ +#ifdef WOLFSSH_CERTS + ctx->certMan = wolfSSH_CERTMAN_new(ctx->heap); +#endif /* WOLFSSH_CERTS */ ctx->windowSz = DEFAULT_WINDOW_SZ; ctx->maxPacketSz = DEFAULT_MAX_PACKET_SZ; @@ -534,6 +538,11 @@ void CtxResourceFree(WOLFSSH_CTX* ctx) ForceZero(ctx->privateKey, ctx->privateKeySz); WFREE(ctx->privateKey, ctx->heap, DYNTYPE_PRIVKEY); } +#ifdef WOLFSSH_CERTS + if (ctx->certMan) { + wolfSSH_CERTMAN_free(ctx->certMan); + } +#endif } @@ -727,12 +736,14 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx, int format, int type) { int dynamicType = 0; + int wcType; int ret; void* heap = NULL; byte* der; word32 derSz, scratch = 0; union wolfSSH_key *key_ptr = NULL; + (void)wcType; (void)dynamicType; (void)heap; @@ -743,37 +754,76 @@ int wolfSSH_ProcessBuffer(WOLFSSH_CTX* ctx, format != WOLFSSH_FORMAT_RAW) return WS_BAD_FILETYPE_E; - if (type == BUFTYPE_CA) + if (type == BUFTYPE_CA) { dynamicType = DYNTYPE_CA; - else if (type == BUFTYPE_CERT) + wcType = CA_TYPE; + } + else if (type == BUFTYPE_CERT) { dynamicType = DYNTYPE_CERT; - else if (type == BUFTYPE_PRIVKEY) + wcType = CERT_TYPE; + } + else if (type == BUFTYPE_PRIVKEY) { dynamicType = DYNTYPE_PRIVKEY; + wcType = PRIVATEKEY_TYPE; + } else return WS_BAD_ARGUMENT; heap = ctx->heap; - if (format == WOLFSSH_FORMAT_PEM) - return WS_UNIMPLEMENTED_E; - else { - /* format is ASN1 or RAW */ + if (format == WOLFSSH_FORMAT_ASN1 || format == WOLFSSH_FORMAT_RAW) { + if (in[0] != 0x30) + return WS_BAD_FILETYPE_E; der = (byte*)WMALLOC(inSz, heap, dynamicType); if (der == NULL) return WS_MEMORY_E; WMEMCPY(der, in, inSz); derSz = inSz; } + #ifdef WOLFSSH_CERTS + else if (format == WOLFSSH_FORMAT_PEM) { + /* The der size will be smaller than the pem size. */ + der = (byte*)WMALLOC(inSz, heap, dynamicType); + if (der == NULL) + return WS_MEMORY_E; + + derSz = wc_CertPemToDer(in, inSz, der, inSz, wcType); + if (derSz < 0) { + WFREE(der, heap, dynamicType); + return WS_BAD_FILE_E; + } + } + #endif /* WOLFSSH_CERTS */ + else { + return WS_UNIMPLEMENTED_E; + } /* Maybe decrypt */ if (type == BUFTYPE_PRIVKEY) { - if (ctx->privateKey) + if (ctx->privateKey) { + ForceZero(ctx->privateKey, ctx->privateKeySz); WFREE(ctx->privateKey, heap, dynamicType); + } ctx->privateKey = der; ctx->privateKeySz = derSz; ctx->useEcc = 0; } + #ifdef WOLFSSH_CERTS + else if (type == BUFTYPE_CERT) { + if (ctx->cert != NULL) + WFREE(ctx->cert, heap, 0); + ctx->cert = der; + ctx->certSz = derSz; + ctx->useCert = 1; + } + else if (type == BUFTYPE_CA) { + if (ctx->certMan != NULL) { + ret = wolfSSH_CERTMAN_LoadRootCA_buffer(ctx->certMan, der, derSz); + } + WFREE(der, heap, dynamicType); + } + #endif /* WOLFSSH_CERTS */ else { WFREE(der, heap, dynamicType); return WS_UNIMPLEMENTED_E; @@ -2326,6 +2376,9 @@ static INLINE enum wc_HashType HashForId(byte id) #endif #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256 case ID_ECDSA_SHA2_NISTP256: +#ifdef WOLFSSH_CERTS + case ID_X509V3_ECDSA_SHA2_NISTP256: +#endif return WC_HASH_TYPE_SHA256; #endif #ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 @@ -2340,6 +2393,9 @@ static INLINE enum wc_HashType HashForId(byte id) #endif #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384 case ID_ECDSA_SHA2_NISTP384: +#ifdef WOLFSSH_CERTS + case ID_X509V3_ECDSA_SHA2_NISTP384: +#endif return WC_HASH_TYPE_SHA384; #endif @@ -2350,6 +2406,9 @@ static INLINE enum wc_HashType HashForId(byte id) #endif #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521 case ID_ECDSA_SHA2_NISTP521: +#ifdef WOLFSSH_CERTS + case ID_X509V3_ECDSA_SHA2_NISTP521: +#endif return WC_HASH_TYPE_SHA512; #endif default: @@ -2428,6 +2487,45 @@ static INLINE byte AeadModeForId(byte id) } +/* We have pairs of PubKey types that use the same signature, + * i.e. ecdsa-sha2-nistp256 and x509v3-ecdsa-sha2-nistp256. */ +static INLINE byte SigTypeForId(byte id) +{ + switch (id) { +#ifndef WOLFSSH_NO_SSHA_RSA_SHA1 + #ifdef WOLFSSH_CERTS + case ID_X509V3_SSH_RSA: + #endif + case ID_SSH_RSA: + return ID_SSH_RSA; +#endif +#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256 + #ifdef WOLFSSH_CERTS + case ID_X509V3_ECDSA_SHA2_NISTP256: + #endif + case ID_ECDSA_SHA2_NISTP256: + return ID_ECDSA_SHA2_NISTP256; +#endif +#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384 + #ifdef WOLFSSH_CERTS + case ID_X509V3_ECDSA_SHA2_NISTP384: + #endif + case ID_ECDSA_SHA2_NISTP384: + return ID_ECDSA_SHA2_NISTP384; +#endif +#ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521 + #ifdef WOLFSSH_CERTS + case ID_X509V3_ECDSA_SHA2_NISTP521: + #endif + case ID_ECDSA_SHA2_NISTP521: + return ID_ECDSA_SHA2_NISTP521; +#endif + default: + return 0; + } +} + + static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) { int ret = WS_SUCCESS; @@ -2514,8 +2612,10 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256 case ECC_SECP256R1: if (ssh->ctx->useCert) { + #ifdef WOLFSSH_CERTS cannedKeyAlgo = cannedKeyAlgoX509Ecc256; cannedKeyAlgoSz = cannedKeyAlgoX509Ecc256Sz; + #endif } else { cannedKeyAlgo = cannedKeyAlgoEcc256; @@ -2526,8 +2626,10 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384 case ECC_SECP384R1: if (ssh->ctx->useCert) { + #ifdef WOLFSSH_CERTS cannedKeyAlgo = cannedKeyAlgoX509Ecc384; cannedKeyAlgoSz = cannedKeyAlgoX509Ecc384Sz; + #endif } else { cannedKeyAlgo = cannedKeyAlgoEcc384; @@ -2538,8 +2640,10 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521 case ECC_SECP521R1: if (ssh->ctx->useCert) { + #ifdef WOLFSSH_CERTS cannedKeyAlgo = cannedKeyAlgoX509Ecc521; cannedKeyAlgoSz = cannedKeyAlgoX509Ecc521Sz; + #endif } else { cannedKeyAlgo = cannedKeyAlgoEcc521; @@ -2552,8 +2656,10 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) else { #ifndef WOLFSSH_NO_SSH_RSA_SHA1 if (ssh->ctx->useCert) { + #ifdef WOLFSSH_CERTS cannedKeyAlgo = cannedKeyAlgoX509Rsa; cannedKeyAlgoSz = cannedKeyAlgoX509RsaSz; + #endif } else { cannedKeyAlgo = cannedKeyAlgoRsa; @@ -2573,8 +2679,10 @@ static int DoKexInit(WOLFSSH* ssh, byte* buf, word32 len, word32* idx) WLOG(WS_LOG_DEBUG, "Unable to negotiate Server Host Key Algo"); return WS_MATCH_KEY_ALGO_E; } - else + else { ssh->handshake->pubKeyId = algoId; + ssh->handshake->sigId = SigTypeForId(algoId); + } } } @@ -4539,6 +4647,162 @@ static int DoUserAuthRequestEcc(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestEcc(), ret = %d", ret); return ret; } + + +#ifdef WOLFSSH_CERTS +static int DoUserAuthRequestEccCert(WOLFSSH* ssh, WS_UserAuthData_PublicKey* pk, + byte hashId, byte* digest, word32 digestSz) +{ + const byte* publicKeyType; + word32 publicKeyTypeSz = 0; + word32 i = 0; + word32 sz; + int ret = WS_SUCCESS; + mp_int *sig_r_ptr = NULL, *sig_s_ptr = NULL; + ecc_key *key_ptr = NULL; +#ifndef WOLFSSH_SMALL_STACK + mp_int sig_r, sig_s; + ecc_key s_key; +#endif + (void)hashId; + + WLOG(WS_LOG_DEBUG, "Entering DoUserAuthRequestEccCert()"); + + if (ssh == NULL || ssh->ctx == NULL || pk == NULL || digest == NULL || + digestSz == 0) { + + ret = WS_BAD_ARGUMENT; + } + + if (ret == WS_SUCCESS) { +#ifdef WOLFSSH_SMALL_STACK + key_ptr = (ecc_key*)WMALLOC(sizeof(ecc_key), ssh->ctx->heap, + DYNTYPE_PUBKEY); + sig_r_ptr = (mp_int*)WMALLOC(sizeof(mp_int), ssh->ctx->heap, DYNTYPE_MPINT); + sig_s_ptr = (mp_int*)WMALLOC(sizeof(mp_int), ssh->ctx->heap, DYNTYPE_MPINT); + if (key_ptr == NULL || sig_r_ptr == NULL || sig_s_ptr == NULL) + ret = WS_MEMORY_E; +#else + key_ptr = &s_key; + sig_r_ptr = &sig_r; + sig_s_ptr = &sig_s; +#endif + } + + if (ret == WS_SUCCESS) { + ret = wc_ecc_init_ex(key_ptr, ssh->ctx->heap, INVALID_DEVID); + if (ret == 0) { + ret = WS_SUCCESS; + } + } + + if (ret == WS_SUCCESS) { + byte big[1024]; + word32 bigSz = sizeof big; + DecodedCert cert; + + wc_InitDecodedCert(&cert, pk->publicKey, pk->publicKeySz, + ssh->ctx->heap); + ret = wc_ParseCert(&cert, CA_TYPE, 0, NULL); + ret = wc_GetPubKeyDerFromCert(&cert, big, &bigSz); + if (ret == 0) { + word32 idx = 0; + ret = wc_EccPublicKeyDecode(big, &idx, key_ptr, bigSz); + } + wc_FreeDecodedCert(&cert); + } + + if (ret != 0) { + WLOG(WS_LOG_DEBUG, "Could not decode public key"); + ret = WS_CRYPTO_FAILED; + } + + if (ret == WS_SUCCESS) { + i = 0; + /* First check that the signature's public key type matches the one + * we are expecting. */ + ret = GetSize(&publicKeyTypeSz, pk->signature, pk->signatureSz, &i); + } + + if (ret == WS_SUCCESS) { + publicKeyType = pk->signature + i; + i += publicKeyTypeSz; + (void)publicKeyType; + } + + if (ret == WS_SUCCESS) { + /* Get the size of the signature blob. */ + ret = GetSize(&sz, pk->signature, pk->signatureSz, &i); + } + + if (ret == WS_SUCCESS) { + /* Get R and S. */ + ret = GetSize(&sz, pk->signature, pk->signatureSz, &i); + } + + if (ret == WS_SUCCESS) { + if (mp_init(sig_r_ptr) != MP_OKAY) { + ret = WS_FATAL_ERROR; + } + } + + if (ret == WS_SUCCESS) { + ret = mp_read_unsigned_bin(sig_r_ptr, pk->signature + i, sz); + if (ret != 0) + ret = WS_PARSE_E; + else + ret = WS_SUCCESS; + } + + if (ret == WS_SUCCESS) { + i += sz; + ret = GetSize(&sz, pk->signature, pk->signatureSz, &i); + } + + if (ret == WS_SUCCESS) { + if (mp_init(sig_s_ptr) != MP_OKAY) { + ret = WS_FATAL_ERROR; + } + } + + if (ret == WS_SUCCESS) { + ret = mp_read_unsigned_bin(sig_s_ptr, pk->signature + i, sz); + if (ret != 0) + ret = WS_PARSE_E; + else + ret = WS_SUCCESS; + } + + if (ret == WS_SUCCESS) { + int status = 0; + + ret = wc_ecc_verify_hash_ex(sig_r_ptr, sig_s_ptr, digest, digestSz, &status, key_ptr); + if (ret != 0) { + WLOG(WS_LOG_DEBUG, "Could not verify signature"); + ret = WS_CRYPTO_FAILED; + } + else + ret = status ? WS_SUCCESS : WS_ECC_E; + } + + if (key_ptr) + wc_ecc_free(key_ptr); + if (sig_r_ptr) + mp_clear(sig_r_ptr); + if (sig_s_ptr) + mp_clear(sig_s_ptr); +#ifdef WOLFSSH_SMALL_STACK + if (key_ptr) + WFREE(key_ptr, ssh->ctx->heap, DYNTYPE_PUBKEY); + if (sig_r_ptr) + WFREE(sig_r_ptr, ssh->ctx->heap, DYNTYPE_MPINT); + if (sig_s_ptr) + WFREE(sig_s_ptr, ssh->ctx->heap, DYNTYPE_MPINT); +#endif + WLOG(WS_LOG_DEBUG, "Leaving DoUserAuthRequestEccCert(), ret = %d", ret); + return ret; +} +#endif #endif @@ -4579,6 +4843,18 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, if (ret == WS_SUCCESS) { pk->publicKey = buf + begin; begin += pk->publicKeySz; + { + word32 l = 0, m = 0; + + /* Skip the name */ + ret = GetSize(&l, pk->publicKey, pk->publicKeySz, &m); + m += l; + /* Get the cert count */ + ret = GetUint32(&l, pk->publicKey, pk->publicKeySz, &m); + ret = GetSize(&l, pk->publicKey, pk->publicKeySz, &m); + pk->publicKeySz = l; + pk->publicKey = pk->publicKey + m; + } if (pk->hasSignature) { ret = GetSize(&pk->signatureSz, buf, len, &begin); @@ -4669,13 +4945,14 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, /* The rest of the fields in the signature are already * in the buffer. Just need to account for the sizes. */ - if (ret == 0) + if (ret == 0) { ret = wc_HashUpdate(&hash, hashId, pk->dataToSign, authData->usernameSz + authData->serviceNameSz + authData->authNameSz + BOOLEAN_SZ + - pk->publicKeyTypeSz + pk->publicKeySz + - (UINT32_SZ * 5)); + (pk->publicKeyTypeSz*2) + pk->publicKeySz + + (UINT32_SZ * 9)); + } if (ret == 0) { ret = wc_HashFinal(&hash, hashId, digest); @@ -4687,19 +4964,37 @@ static int DoUserAuthRequestPublicKey(WOLFSSH* ssh, WS_UserAuthData* authData, wc_HashFree(&hash, hashId); if (ret == WS_SUCCESS) { - if (pkTypeId == ID_SSH_RSA) { -#ifndef WOLFSSH_NO_RSA - ret = DoUserAuthRequestRsa(ssh, pk, - hashId, digest, digestSz); -#endif - } - else if (pkTypeId == ID_ECDSA_SHA2_NISTP256 || - pkTypeId == ID_ECDSA_SHA2_NISTP384 || - pkTypeId == ID_ECDSA_SHA2_NISTP521) { -#ifndef WOLFSSH_NO_ECDSA - ret = DoUserAuthRequestEcc(ssh, pk, - hashId, digest, digestSz); -#endif + switch (pkTypeId) { + #ifndef WOLFSSH_NO_RSA + case ID_SSH_RSA: + ret = DoUserAuthRequestRsa(ssh, pk, + hashId, digest, digestSz); + break; + #ifdef WOLFSSH_CERTS + case ID_X509V3_SSH_RSA: + ret = DoUserAuthRequestRsa(ssh, pk, + hashId, digest, digestSz); + break; + #endif + #endif + #ifndef WOLFSSH_NO_ECDSA + case ID_ECDSA_SHA2_NISTP256: + case ID_ECDSA_SHA2_NISTP384: + case ID_ECDSA_SHA2_NISTP521: + ret = DoUserAuthRequestEcc(ssh, pk, + hashId, digest, digestSz); + break; + #ifdef WOLFSSH_CERTS + case ID_X509V3_ECDSA_SHA2_NISTP256: + case ID_X509V3_ECDSA_SHA2_NISTP384: + case ID_X509V3_ECDSA_SHA2_NISTP521: + ret = DoUserAuthRequestEccCert(ssh, pk, + hashId, digest, digestSz); + break; + #endif + #endif + default: + ret = WS_INVALID_ALGO_ID; } } @@ -6728,10 +7023,20 @@ static const char cannedMacAlgoNames[] = #endif static const char cannedKeyAlgoClientNames[] = - "x509v3-ecdsa-sha2-nistp521," - "x509v3-ecdsa-sha2-nistp384," - "x509v3-ecdsa-sha2-nistp256," - "x509v3-ssh-rsa," +#ifdef WOLFSSH_CERTS + #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521 + "x509v3-ecdsa-sha2-nistp521," + #endif + #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP384 + "x509v3-ecdsa-sha2-nistp384," + #endif + #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP256 + "x509v3-ecdsa-sha2-nistp256," + #endif + #ifndef WOLFSSH_NO_SSH_RSA_SHA1 + "x509v3-ssh-rsa," + #endif +#endif #ifndef WOLFSSH_NO_ECDSA_SHA2_NISTP521 "ecdsa-sha2-nistp521," #endif @@ -6809,8 +7114,10 @@ static const word32 cannedMacAlgoNamesSz = sizeof(cannedMacAlgoNames) - 2; static const word32 cannedKeyAlgoClientNamesSz = sizeof(cannedKeyAlgoClientNames) - 2; static const word32 cannedKeyAlgoRsaNamesSz = sizeof(cannedKeyAlgoRsaNames) - 1; +#ifdef WOLFSSH_CERTS static const word32 cannedKeyAlgoX509RsaNamesSz = sizeof(cannedKeyAlgoX509RsaNames) - 1; +#endif #if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECDH) static const word32 cannedKeyAlgoEcc256NamesSz = sizeof(cannedKeyAlgoEcc256Names) - 1; @@ -6863,9 +7170,11 @@ int SendKexInit(WOLFSSH* ssh) #if !defined(WOLFSSH_NO_ECDSA) && !defined(WOLFSSH_NO_ECDH) case ECC_SECP256R1: if (ssh->ctx->useCert) { + #ifdef WOLFSSH_CERTS cannedKeyAlgoNames = cannedKeyAlgoX509Ecc256Names; cannedKeyAlgoNamesSz = cannedKeyAlgoX509Ecc256NamesSz; + #endif } else { cannedKeyAlgoNames = cannedKeyAlgoEcc256Names; @@ -6874,9 +7183,11 @@ int SendKexInit(WOLFSSH* ssh) break; case ECC_SECP384R1: if (ssh->ctx->useCert) { + #ifdef WOLFSSH_CERTS cannedKeyAlgoNames = cannedKeyAlgoX509Ecc384Names; cannedKeyAlgoNamesSz = cannedKeyAlgoX509Ecc384NamesSz; + #endif } else { cannedKeyAlgoNames = cannedKeyAlgoEcc384Names; @@ -6885,9 +7196,11 @@ int SendKexInit(WOLFSSH* ssh) break; case ECC_SECP521R1: if (ssh->ctx->useCert) { + #ifdef WOLFSSH_CERTS cannedKeyAlgoNames = cannedKeyAlgoX509Ecc521Names; cannedKeyAlgoNamesSz = cannedKeyAlgoX509Ecc521NamesSz; + #endif } else { cannedKeyAlgoNames = cannedKeyAlgoEcc521Names; @@ -6898,8 +7211,10 @@ int SendKexInit(WOLFSSH* ssh) #endif default: if (ssh->ctx->useCert) { + #ifdef WOLFSSH_CERTS cannedKeyAlgoNames = cannedKeyAlgoX509RsaNames; cannedKeyAlgoNamesSz = cannedKeyAlgoX509RsaNamesSz; + #endif } else { cannedKeyAlgoNames = cannedKeyAlgoRsaNames; @@ -6989,6 +7304,7 @@ int SendKexInit(WOLFSSH* ssh) struct wolfSSH_sigKeyBlockFull { byte pubKeyId; + byte sigId; word32 sz; const char *name; word32 nameSz; @@ -7108,7 +7424,8 @@ int SendKexDhReply(WOLFSSH* ssh) if (ret == WS_SUCCESS) { sigKeyBlock_ptr->pubKeyId = ssh->handshake->pubKeyId; - sigKeyBlock_ptr->name = IdToName(ssh->handshake->pubKeyId); + sigKeyBlock_ptr->sigId = ssh->handshake->sigId; + sigKeyBlock_ptr->name = IdToName(ssh->handshake->sigId); sigKeyBlock_ptr->nameSz = (word32)strlen(sigKeyBlock_ptr->name); switch (ssh->handshake->kexId) { @@ -7172,7 +7489,7 @@ int SendKexDhReply(WOLFSSH* ssh) * and I_S. Next add K_S, the server's public host key. K_S will * either be RSA or ECDSA public key blob. */ if (ret == WS_SUCCESS) { - if (sigKeyBlock_ptr->pubKeyId == ID_SSH_RSA) { + if (sigKeyBlock_ptr->sigId == ID_SSH_RSA) { #ifndef WOLFSSH_NO_SSH_RSA_SHA1 /* Decode the user-configured RSA private key. */ sigKeyBlock_ptr->sk.rsa.eSz = sizeof(sigKeyBlock_ptr->sk.rsa.e); @@ -7272,12 +7589,12 @@ int SendKexDhReply(WOLFSSH* ssh) sigKeyBlock_ptr->sk.rsa.nSz); #endif /* WOLFSSH_NO_SSH_RSA_SHA1 */ } - else if (sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP256 || - sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP384 || - sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP521) { + else if (sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP256 || + sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP384 || + sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP521) { #ifndef WOLFSSH_NO_ECDSA sigKeyBlock_ptr->sk.ecc.primeName = - PrimeNameForId(ssh->handshake->pubKeyId); + PrimeNameForId(ssh->handshake->sigId); sigKeyBlock_ptr->sk.ecc.primeNameSz = (word32)strlen(sigKeyBlock_ptr->sk.ecc.primeName); @@ -7786,14 +8103,14 @@ int SendKexDhReply(WOLFSSH* ssh) } if (sigKeyBlock_ptr != NULL) { - if (sigKeyBlock_ptr->pubKeyId == ID_SSH_RSA) { + if (sigKeyBlock_ptr->sigId == ID_SSH_RSA) { #ifndef WOLFSSH_NO_SSH_RSA_SHA1 wc_FreeRsaKey(&sigKeyBlock_ptr->sk.rsa.key); #endif } - else if (sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP256 || - sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP384 || - sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP521) { + else if (sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP256 || + sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP384 || + sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP521) { #ifndef WOLFSSH_NO_ECDSA wc_ecc_free(&sigKeyBlock_ptr->sk.ecc.key); #endif @@ -7825,7 +8142,7 @@ int SendKexDhReply(WOLFSSH* ssh) idx += LENGTH_SZ; WMEMCPY(output + idx, sigKeyBlock_ptr->name, sigKeyBlock_ptr->nameSz); idx += sigKeyBlock_ptr->nameSz; - if (sigKeyBlock_ptr->pubKeyId == ID_SSH_RSA) { + if (sigKeyBlock_ptr->sigId == ID_SSH_RSA) { #ifndef WOLFSSH_NO_SSH_RSA_SHA1 c32toa(sigKeyBlock_ptr->sk.rsa.eSz + sigKeyBlock_ptr->sk.rsa.ePad, output + idx); @@ -7841,9 +8158,9 @@ int SendKexDhReply(WOLFSSH* ssh) idx += sigKeyBlock_ptr->sk.rsa.nSz; #endif } - else if (sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP256 || - sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP384 || - sigKeyBlock_ptr->pubKeyId == ID_ECDSA_SHA2_NISTP521) { + else if (sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP256 || + sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP384 || + sigKeyBlock_ptr->sigId == ID_ECDSA_SHA2_NISTP521) { #ifndef WOLFSSH_NO_ECDSA c32toa(sigKeyBlock_ptr->sk.ecc.primeNameSz, output + idx); idx += LENGTH_SZ; @@ -9079,7 +9396,218 @@ static int BuildUserAuthRequestEcc(WOLFSSH* ssh, #endif return ret; } -#endif + + +#ifdef WOLFSSH_CERTS + +static int PrepareUserAuthRequestEccCert(WOLFSSH* ssh, word32* payloadSz, + const WS_UserAuthData* authData, WS_KeySignature* keySig) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering PrepareUserAuthRequestEccCert()"); + if (ssh == NULL || payloadSz == NULL || authData == NULL || keySig == NULL) + ret = WS_BAD_ARGUMENT; + + if (ret == WS_SUCCESS) + ret = wc_ecc_init(&keySig->ks.ecc.key); + + if (ret == WS_SUCCESS) { + word32 idx = 0; + #if 0 + #ifdef WOLFSSH_AGENT + if (ssh->agentEnabled) { + word32 sz; + const byte* c = (const byte*)authData->sf.publicKey.publicKey; + + ato32(c + idx, &sz); + idx += LENGTH_SZ + sz; + ato32(c + idx, &sz); + idx += LENGTH_SZ + sz; + ato32(c + idx, &sz); + idx += LENGTH_SZ; + c += idx; + idx = 0; + + ret = wc_ecc_import_x963(c, sz, &keySig->ks.ecc.key); + } + else + #endif + #endif + ret = wc_EccPrivateKeyDecode(authData->sf.publicKey.privateKey, + &idx, &keySig->ks.ecc.key, + authData->sf.publicKey.privateKeySz); + } + + if (ret == WS_SUCCESS) { + *payloadSz += (LENGTH_SZ + authData->sf.publicKey.publicKeyTypeSz) + + (UINT32_SZ * 2); /* certificate and ocsp counts */ + + if (authData->sf.publicKey.hasSignature) { + int sigSz = wc_ecc_sig_size(&keySig->ks.ecc.key); + + if (sigSz >= 0) { + /* 5 lengths: sig(R), sig(S), sig, sig-type, sig-blob */ + *payloadSz += (LENGTH_SZ * 5) + (word32)sigSz + + authData->sf.publicKey.publicKeyTypeSz; + keySig->sigSz = sigSz; + } + else + ret = sigSz; + } + } + + WLOG(WS_LOG_DEBUG, "Leaving PrepareUserAuthRequestEccCert(), ret = %d", + ret); + return ret; +} + + +static int BuildUserAuthRequestEccCert(WOLFSSH* ssh, + byte* output, word32* idx, + const WS_UserAuthData* authData, + const byte* sigStart, word32 sigStartIdx, + WS_KeySignature* keySig) +{ + wc_HashAlg hash; + byte digest[WC_MAX_DIGEST_SIZE]; + word32 digestSz; + word32 begin; + enum wc_HashType hashId = WC_HASH_TYPE_SHA; + int ret = WS_SUCCESS; + byte* r; + byte* s; + byte sig[139]; /* wc_ecc_sig_size() for a prime521 key. */ + word32 sigSz = sizeof(sig), rSz, sSz; + byte* checkData = NULL; + word32 checkDataSz = 0; + + if (ssh == NULL || output == NULL || idx == NULL || authData == NULL || + sigStart == NULL || keySig == NULL) { + ret = WS_BAD_ARGUMENT; + return ret; + } + + begin = *idx; + + if (ret == WS_SUCCESS) { + hashId = HashForId(keySig->keySigId); + WMEMSET(digest, 0, sizeof(digest)); + digestSz = wc_HashGetDigestSize(hashId); + checkDataSz = LENGTH_SZ + ssh->sessionIdSz + (begin - sigStartIdx); + checkData = (byte*)WMALLOC(checkDataSz, ssh->ctx->heap, DYNTYPE_TEMP); + if (checkData == NULL) + ret = WS_MEMORY_E; + } + + if (ret == WS_SUCCESS) { + word32 i = 0; + + c32toa(ssh->sessionIdSz, checkData + i); + i += LENGTH_SZ; + WMEMCPY(checkData + i, ssh->sessionId, ssh->sessionIdSz); + i += ssh->sessionIdSz; + WMEMCPY(checkData + i, sigStart, begin - sigStartIdx); + } + + #if 0 + #ifdef WOLFSSH_AGENT + if (ssh->agentEnabled) { + if (ret == WS_SUCCESS) + ret = wolfSSH_AGENT_SignRequest(ssh, checkData, checkDataSz, + sig, &sigSz, + authData->sf.publicKey.publicKey, + authData->sf.publicKey.publicKeySz, 0); + if (ret == WS_SUCCESS) { + c32toa(sigSz, output + begin); + begin += LENGTH_SZ; + XMEMCPY(output + begin, sig, sigSz); + begin += sigSz; + } + } + else + #endif + #endif + { + if (ret == WS_SUCCESS) { + WLOG(WS_LOG_INFO, "Signing hash with ECDSA."); + ret = wc_HashInit(&hash, hashId); + if (ret == WS_SUCCESS) + ret = wc_HashUpdate(&hash, hashId, checkData, checkDataSz); + if (ret == WS_SUCCESS) + ret = wc_HashFinal(&hash, hashId, digest); + if (ret == WS_SUCCESS) + ret = wc_ecc_sign_hash(digest, digestSz, sig, &sigSz, + ssh->rng, &keySig->ks.ecc.key); + if (ret != WS_SUCCESS) { + WLOG(WS_LOG_DEBUG, "SUAR: Bad ECC Sign"); + ret = WS_ECC_E; + } + } + + if (ret == WS_SUCCESS) { + rSz = sSz = sizeof(sig) / 2; + r = sig; + s = sig + rSz; + ret = wc_ecc_sig_to_rs(sig, sigSz, r, &rSz, s, &sSz); + } + + if (ret == WS_SUCCESS) { + byte rPad; + byte sPad; + + /* adds a byte of padding if needed to avoid negative values */ + rPad = (r[0] & 0x80) ? 1 : 0; + sPad = (s[0] & 0x80) ? 1 : 0; + c32toa(rSz + rPad + sSz + sPad + + cannedKeyAlgoEcc256NamesSz + LENGTH_SZ * 4, + output + begin); + begin += LENGTH_SZ; + + c32toa(cannedKeyAlgoEcc256NamesSz, output + begin); + begin += LENGTH_SZ; + + WMEMCPY(output + begin, cannedKeyAlgoEcc256Names, + cannedKeyAlgoEcc256NamesSz); + begin += cannedKeyAlgoEcc256NamesSz; + + c32toa(rSz + rPad + sSz + sPad + LENGTH_SZ * 2, output + begin); + begin += LENGTH_SZ; + + c32toa(rSz + rPad, output + begin); + begin += LENGTH_SZ; + + if (rPad) + output[begin++] = 0; + + WMEMCPY(output + begin, r, rSz); + begin += rSz; + + c32toa(sSz + sPad, output + begin); + begin += LENGTH_SZ; + + if (sPad) + output[begin++] = 0; + + WMEMCPY(output + begin, s, sSz); + begin += sSz; + } + } + + if (ret == WS_SUCCESS) + *idx = begin; + + if (checkData != NULL) { + ForceZero(checkData, checkDataSz); + WFREE(checkData, ssh->ctx->heap, DYNTYPE_TEMP); + } + + return ret; +} + +#endif /* WOLFSSH_CERTS */ + +#endif /* WOLFSSH_NO_ECDSA */ #if !defined(WOLFSSH_NO_RSA) || !defined(WOLFSSH_NO_ECDSA) @@ -9099,26 +9627,42 @@ static int PrepareUserAuthRequestPublicKey(WOLFSSH* ssh, word32* payloadSz, if (ret == WS_SUCCESS) { /* Add the boolean size to the payload, and the lengths of - * the public key algorithm name, and the public key length. */ + * the public key algorithm name, and the public key length. + * For the X509 types, this accounts for ONLY one certificate.*/ *payloadSz += BOOLEAN_SZ + (LENGTH_SZ * 2) + authData->sf.publicKey.publicKeyTypeSz + authData->sf.publicKey.publicKeySz; } - if (keySig->keySigId == ID_SSH_RSA) { -#ifndef WOLFSSH_NO_RSA - ret = PrepareUserAuthRequestRsa(ssh, payloadSz, authData, keySig); -#endif + switch (keySig->keySigId) { + #ifndef WOLFSSH_NO_RSA + case ID_SSH_RSA: + ret = PrepareUserAuthRequestRsa(ssh, payloadSz, authData, keySig); + break; + #ifdef WOLFSSH_CERTS + case ID_X509V3_SSH_RSA: + ret = PrepareUserAuthRequestRsa(ssh, payloadSz, authData, keySig); + break; + #endif + #endif + #ifndef WOLFSSH_NO_ECDSA + case ID_ECDSA_SHA2_NISTP256: + case ID_ECDSA_SHA2_NISTP384: + case ID_ECDSA_SHA2_NISTP521: + ret = PrepareUserAuthRequestEcc(ssh, payloadSz, authData, keySig); + break; + #ifdef WOLFSSH_CERTS + case ID_X509V3_ECDSA_SHA2_NISTP256: + case ID_X509V3_ECDSA_SHA2_NISTP384: + case ID_X509V3_ECDSA_SHA2_NISTP521: + ret = PrepareUserAuthRequestEccCert(ssh, + payloadSz, authData, keySig); + break; + #endif + #endif + default: + ret = WS_INVALID_ALGO_ID; } - else if (keySig->keySigId == ID_ECDSA_SHA2_NISTP256 || - keySig->keySigId == ID_ECDSA_SHA2_NISTP384 || - keySig->keySigId == ID_ECDSA_SHA2_NISTP521) { -#ifndef WOLFSSH_NO_ECDSA - ret = PrepareUserAuthRequestEcc(ssh, payloadSz, authData, keySig); -#endif - } - else - ret = WS_INVALID_ALGO_ID; return ret; } @@ -9142,33 +9686,78 @@ static int BuildUserAuthRequestPublicKey(WOLFSSH* ssh, begin = *idx; pk = &authData->sf.publicKey; output[begin++] = pk->hasSignature; - c32toa(pk->publicKeyTypeSz, output + begin); - begin += LENGTH_SZ; - WMEMCPY(output + begin, pk->publicKeyType, pk->publicKeyTypeSz); - begin += pk->publicKeyTypeSz; - c32toa(pk->publicKeySz, output + begin); - begin += LENGTH_SZ; - WMEMCPY(output + begin, pk->publicKey, pk->publicKeySz); - begin += pk->publicKeySz; if (pk->hasSignature) { - if (keySig->keySigId == ID_SSH_RSA) { -#ifndef WOLFSSH_NO_RSA - ret = BuildUserAuthRequestRsa(ssh, output, &begin, - authData, sigStart, sigStartIdx, keySig); -#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 - ret = BuildUserAuthRequestEcc(ssh, output, &begin, - authData, sigStart, sigStartIdx, keySig); -#endif + switch (keySig->keySigId) { + #ifndef WOLFSSH_NO_RSA + case ID_SSH_RSA: + #ifdef WOLFSSH_CERTS + case ID_X509V3_SSH_RSA: + #endif + c32toa(pk->publicKeyTypeSz, output + begin); + begin += LENGTH_SZ; + WMEMCPY(output + begin, pk->publicKeyType, pk->publicKeyTypeSz); + begin += pk->publicKeyTypeSz; + c32toa(pk->publicKeySz, output + begin); + begin += LENGTH_SZ; + WMEMCPY(output + begin, pk->publicKey, pk->publicKeySz); + begin += pk->publicKeySz; + ret = BuildUserAuthRequestRsa(ssh, output, &begin, + authData, sigStart, sigStartIdx, keySig); + break; + #endif + #ifndef WOLFSSH_NO_ECDSA + case ID_ECDSA_SHA2_NISTP256: + case ID_ECDSA_SHA2_NISTP384: + case ID_ECDSA_SHA2_NISTP521: + c32toa(pk->publicKeyTypeSz, output + begin); + begin += LENGTH_SZ; + WMEMCPY(output + begin, pk->publicKeyType, pk->publicKeyTypeSz); + begin += pk->publicKeyTypeSz; + c32toa(pk->publicKeySz, output + begin); + begin += LENGTH_SZ; + WMEMCPY(output + begin, pk->publicKey, pk->publicKeySz); + begin += pk->publicKeySz; + ret = BuildUserAuthRequestEcc(ssh, output, &begin, + authData, sigStart, sigStartIdx, keySig); + break; + #ifdef WOLFSSH_CERTS + case ID_X509V3_ECDSA_SHA2_NISTP256: + case ID_X509V3_ECDSA_SHA2_NISTP384: + case ID_X509V3_ECDSA_SHA2_NISTP521: + c32toa(pk->publicKeyTypeSz, output + begin); + begin += LENGTH_SZ; + WMEMCPY(output + begin, pk->publicKeyType, pk->publicKeyTypeSz); + begin += pk->publicKeyTypeSz; + c32toa((LENGTH_SZ * 2) + (UINT32_SZ * 2) + + pk->publicKeyTypeSz + pk->publicKeySz, + output + begin); + begin += LENGTH_SZ; + c32toa(pk->publicKeyTypeSz, output + begin); + begin += LENGTH_SZ; + WMEMCPY(output + begin, pk->publicKeyType, pk->publicKeyTypeSz); + begin += pk->publicKeyTypeSz; + c32toa(1, output + begin); /* cert count */ + begin += UINT32_SZ; + c32toa(pk->publicKeySz, output + begin); + begin += LENGTH_SZ; + WMEMCPY(output + begin, pk->publicKey, pk->publicKeySz); + begin += pk->publicKeySz; + c32toa(0, output + begin); /* ocsp count */ + begin += UINT32_SZ; + ret = BuildUserAuthRequestEccCert(ssh, output, &begin, + authData, sigStart, sigStartIdx, keySig); + break; + #endif + #endif + default: + ret = WS_INVALID_ALGO_ID; } } - else + else { + /* TODO: Is this right? */ ret = WS_INVALID_ALGO_ID; + } if (ret == WS_SUCCESS) *idx = begin; @@ -9339,8 +9928,9 @@ int SendUserAuthRequest(WOLFSSH* ssh, byte authId, int addSig) if (authId == ID_USERAUTH_PUBLICKEY) CleanupUserAuthRequestPublicKey(keySig_ptr); - if (ret == WS_SUCCESS) + if (ret == WS_SUCCESS) { ret = wolfSSH_SendPacket(ssh); + } if (ret != WS_WANT_WRITE && ret != WS_SUCCESS) PurgePacket(ssh); @@ -10734,20 +11324,31 @@ void DumpOctetString(const byte* input, word32 inputSz) int rows = inputSz / LINE_WIDTH; int remainder = inputSz % LINE_WIDTH; int i,j; + char text[17]; + byte c; for (i = 0; i < rows; i++) { + XMEMSET(text, 0, sizeof text); printf("%04X: ", i * LINE_WIDTH); for (j = 0; j < LINE_WIDTH; j++) { - printf("%02X ", input[i * LINE_WIDTH + j]); + c = input[i * LINE_WIDTH + j]; + printf("%02X ", c); + text[j] = isprint(c) ? (char)c : '.'; } - printf("\n"); + printf(" %s\n", text); } if (remainder) { + XMEMSET(text, 0, sizeof text); printf("%04X: ", i * LINE_WIDTH); for (j = 0; j < remainder; j++) { - printf("%02X ", input[i * LINE_WIDTH + j]); + c = input[i * LINE_WIDTH + j]; + printf("%02X ", c); + text[j] = isprint(c) ? c : '.'; } - printf("\n"); + for (; j < LINE_WIDTH; j++) { + printf(" "); + } + printf(" %s\n", text); } } diff --git a/src/ssh.c b/src/ssh.c index 37a775a0..af6991a1 100644 --- a/src/ssh.c +++ b/src/ssh.c @@ -1680,6 +1680,39 @@ int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX* ctx, } +#ifdef WOLFSSH_CERTS + +int wolfSSH_CTX_UseCert_buffer(WOLFSSH_CTX* ctx, + const byte* cert, word32 certSz, int format) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_UseCert_buffer()"); + + ret = wolfSSH_ProcessBuffer(ctx, cert, certSz, format, BUFTYPE_CERT); + + WLOG(WS_LOG_DEBUG, "Leaving wolfSSH_CTX_UseCert_buffer(), ret = %d", ret); + return ret; +} + + +int wolfSSH_CTX_AddRootCert_buffer(WOLFSSH_CTX* ctx, + const byte* cert, word32 certSz, int format) +{ + int ret = WS_SUCCESS; + + WLOG(WS_LOG_DEBUG, "Entering wolfSSH_CTX_AddRootCert_buffer()"); + + ret = wolfSSH_ProcessBuffer(ctx, cert, certSz, format, BUFTYPE_CA); + + WLOG(WS_LOG_DEBUG, + "Leaving wolfSSH_CTX_AddRootCert_buffer(), ret = %d", ret); + return ret; +} + +#endif /* WOLFSSH_CERTS */ + + int wolfSSH_CTX_SetWindowPacketSize(WOLFSSH_CTX* ctx, word32 windowSz, word32 maxPacketSz) { diff --git a/tests/api.c b/tests/api.c index bda2cb6d..8c75fd65 100644 --- a/tests/api.c +++ b/tests/api.c @@ -659,6 +659,95 @@ static void test_wolfSSH_CTX_UsePrivateKey_buffer(void) } +#ifdef WOLFSSH_CERTS +static int load_file(const char* filename, byte** buf, word32* bufSz) +{ + FILE* f = NULL; + int ret = 0; + + if (filename == NULL || buf == NULL || bufSz == NULL) + ret = -1; + + if (ret == 0) { + f = fopen(filename, "rb"); + if (f == NULL) + ret = -2; + } + + if (ret == 0) { + fseek(f, 0, XSEEK_END); + *bufSz = (word32)ftell(f); + rewind(f); + } + + if (ret == 0) { + *buf = (byte*)malloc(*bufSz); + if (*buf == NULL) + ret = -3; + } + + if (ret == 0) { + int readSz; + readSz = (int)fread(*buf, 1, *bufSz, f); + if (readSz < (int)*bufSz) + ret = -4; + } + + if (f != NULL) + fclose(f); + + return ret; +} +#endif + + +static void test_wolfSSH_CTX_UseCert_buffer(void) +{ +#ifdef WOLFSSH_CERTS + + WOLFSSH_CTX* ctx = NULL; + byte* cert = NULL; + word32 certSz = 0; + + ctx = wolfSSH_CTX_new(WOLFSSH_ENDPOINT_SERVER, NULL); + AssertNotNull(ctx); + + AssertIntEQ(0, load_file("./keys/server-cert.pem", &cert, &certSz)); + AssertNotNull(cert); + AssertIntNE(0, certSz); + + AssertIntEQ(WS_BAD_ARGUMENT, + wolfSSH_CTX_UseCert_buffer(NULL, cert, certSz, WOLFSSH_FORMAT_PEM)); + AssertIntEQ(WS_BAD_ARGUMENT, + wolfSSH_CTX_UseCert_buffer(ctx, NULL, certSz, WOLFSSH_FORMAT_PEM)); + AssertIntEQ(WS_BAD_ARGUMENT, + wolfSSH_CTX_UseCert_buffer(ctx, NULL, 0, WOLFSSH_FORMAT_PEM)); + + AssertIntEQ(WS_SUCCESS, + wolfSSH_CTX_UseCert_buffer(ctx, cert, certSz, WOLFSSH_FORMAT_PEM)); + + AssertIntEQ(WS_BAD_FILETYPE_E, + wolfSSH_CTX_UseCert_buffer(ctx, cert, certSz, WOLFSSH_FORMAT_ASN1)); + AssertIntEQ(WS_BAD_FILETYPE_E, + wolfSSH_CTX_UseCert_buffer(ctx, cert, certSz, WOLFSSH_FORMAT_RAW)); + AssertIntEQ(WS_BAD_FILETYPE_E, + wolfSSH_CTX_UseCert_buffer(ctx, cert, certSz, 99)); + + free(cert); + + AssertIntEQ(0, load_file("./keys/server-cert.der", &cert, &certSz)); + AssertNotNull(cert); + AssertIntNE(0, certSz); + + AssertIntEQ(WS_SUCCESS, + wolfSSH_CTX_UseCert_buffer(ctx, cert, certSz, WOLFSSH_FORMAT_ASN1)); + + wolfSSH_CTX_free(ctx); + free(cert); +#endif /* WOLFSSH_CERTS */ +} + + static void test_wolfSSH_CertMan(void) { #ifdef WOLFSSH_CERTMAN @@ -1000,6 +1089,7 @@ int main(void) test_wolfSSH_ConvertConsole(); test_wolfSSH_CTX_UsePrivateKey_buffer(); test_wolfSSH_RealPath(); + test_wolfSSH_CTX_UseCert_buffer(); test_wolfSSH_CertMan(); /* SCP tests */ diff --git a/wolfssh/certman.h b/wolfssh/certman.h index fbb841a9..3d2218d7 100644 --- a/wolfssh/certman.h +++ b/wolfssh/certman.h @@ -36,10 +36,7 @@ extern "C" { #endif -struct WOLFSSH_CERTMAN { - void* heap; - unsigned char reserved[512]; -}; +struct WOLFSSH_CERTMAN; typedef struct WOLFSSH_CERTMAN WOLFSSH_CERTMAN; @@ -50,7 +47,12 @@ WOLFSSH_API void wolfSSH_CERTMAN_free(WOLFSSH_CERTMAN* cm); WOLFSSH_API -WOLFSSH_CERTMAN* wolfSSH_CERTMAN_init(WOLFSSH_CERTMAN* cm, void* heap); +int wolfSSH_CERTMAN_LoadRootCA_buffer(WOLFSSH_CERTMAN* cm, + const unsigned char* rootCa, word32 rootCaSz); + +WOLFSSH_API +int wolfSSH_CERTMAN_VerifyCert_buffer(WOLFSSH_CERTMAN* cm, + const unsigned char* cert, word32 certSz); #ifdef __cplusplus diff --git a/wolfssh/internal.h b/wolfssh/internal.h index 3fd83ba4..5e6ac86a 100644 --- a/wolfssh/internal.h +++ b/wolfssh/internal.h @@ -42,6 +42,9 @@ #ifdef WOLFSSH_AGENT #include #endif /* WOLFSSH_AGENT */ +#ifdef WOLFSSH_CERTS + #include +#endif /* WOLFSSH_CERTS */ #if !defined (ALIGN16) @@ -415,6 +418,9 @@ struct WOLFSSH_CTX { WS_CallbackFwd fwdCb; /* WOLFSSH-FWD callback */ WS_CallbackFwdIO fwdIoCb; /* WOLFSSH-FWD IO callback */ #endif /* WOLFSSH_FWD */ +#ifdef WOLFSSH_CERTS + WOLFSSH_CERTMAN* certMan; +#endif /* WOLFSSH_CERTS */ WS_CallbackPublicKeyCheck publicKeyCheckCb; /* Check server's public key callback */ @@ -424,6 +430,8 @@ struct WOLFSSH_CTX { #ifndef WOLFSSH_NO_SABER_LEVEL1_SHA256 byte useSaber:1; /* Depends on the private key */ #endif + byte* cert; + word32 certSz; byte useCert; word32 highwaterMark; const char* banner; @@ -457,6 +465,7 @@ typedef struct HandshakeInfo { byte kexId; byte kexIdGuess; byte pubKeyId; + byte sigId; byte encryptId; byte macId; byte hashId; diff --git a/wolfssh/ssh.h b/wolfssh/ssh.h index c92cb615..e499ef06 100644 --- a/wolfssh/ssh.h +++ b/wolfssh/ssh.h @@ -226,6 +226,12 @@ WOLFSSH_API char* wolfSSH_GetUsername(WOLFSSH*); WOLFSSH_API int wolfSSH_CTX_SetBanner(WOLFSSH_CTX*, const char*); WOLFSSH_API int wolfSSH_CTX_UsePrivateKey_buffer(WOLFSSH_CTX*, const byte*, word32, int); +#ifdef WOLFSSH_CERTS + WOLFSSH_API int wolfSSH_CTX_UseCert_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); +#endif /* WOLFSSH_CERTS */ WOLFSSH_API int wolfSSH_CTX_SetWindowPacketSize(WOLFSSH_CTX*, word32, word32); WOLFSSH_API int wolfSSH_accept(WOLFSSH*);