From 753e0507c860d395a4d8c56a0d53bd1ef530c60b Mon Sep 17 00:00:00 2001 From: Sean Parkinson Date: Fri, 12 Jul 2024 11:23:47 +1000 Subject: [PATCH] ECC key load: fixes asn.c: Return the curve OID sum with alg_id for ECC keys. ssl_load.c: Don't permanently strip the PKCS#8 information as it contains the curve OID. --- certs/ecc/ca-secp256k1-cert.pem | 16 +++++++ certs/ecc/ca-secp256k1-key.pem | 5 +++ certs/ecc/genecc.sh | 7 +++ certs/ecc/include.am | 7 +++ certs/ecc/secp256k1-param.pem | 3 ++ certs/ecc/secp256k1-privkey.der | Bin 0 -> 118 bytes certs/ecc/secp256k1-privkey.pem | 5 +++ certs/ecc/server2-secp256k1-cert.der | Bin 0 -> 885 bytes certs/ecc/server2-secp256k1-cert.pem | 63 +++++++++++++++++++++++++++ src/ssl_load.c | 14 ------ tests/test-ecc-cust-curves.conf | 16 +++++++ wolfcrypt/src/asn.c | 50 ++++++++++++++++++--- wolfssl/wolfcrypt/asn.h | 3 ++ 13 files changed, 169 insertions(+), 20 deletions(-) create mode 100644 certs/ecc/ca-secp256k1-cert.pem create mode 100644 certs/ecc/ca-secp256k1-key.pem create mode 100644 certs/ecc/secp256k1-param.pem create mode 100644 certs/ecc/secp256k1-privkey.der create mode 100644 certs/ecc/secp256k1-privkey.pem create mode 100644 certs/ecc/server2-secp256k1-cert.der create mode 100644 certs/ecc/server2-secp256k1-cert.pem diff --git a/certs/ecc/ca-secp256k1-cert.pem b/certs/ecc/ca-secp256k1-cert.pem new file mode 100644 index 0000000000..419d027346 --- /dev/null +++ b/certs/ecc/ca-secp256k1-cert.pem @@ -0,0 +1,16 @@ +-----BEGIN CERTIFICATE----- +MIICkTCCAjagAwIBAgIUcmBIdEUi0WtpofKM3r46Llhv86IwCgYIKoZIzj0EAwIw +gZYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApXYXNoaW5ndG9uMRAwDgYDVQQHDAdT +ZWF0dGxlMREwDwYDVQQKDAhFbGxpcHRpYzESMBAGA1UECwwJU0VDUDI1NksxMRgw +FgYDVQQDDA93d3cud29sZnNzbC5jb20xHzAdBgkqhkiG9w0BCQEWEGluZm9Ad29s +ZnNzbC5jb20wHhcNMjQwNzEyMDIxODUwWhcNNDQwNzA3MDIxODUwWjCBljELMAkG +A1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1NlYXR0bGUx +ETAPBgNVBAoMCEVsbGlwdGljMRIwEAYDVQQLDAlTRUNQMjU2SzExGDAWBgNVBAMM +D3d3dy53b2xmc3NsLmNvbTEfMB0GCSqGSIb3DQEJARYQaW5mb0B3b2xmc3NsLmNv +bTBWMBAGByqGSM49AgEGBSuBBAAKA0IABORg5kLTHlkHvjnrRKUTmBXLdhxkdsIC +s0ybo6r3oD6qGjX33FsBHp9nwos93gT7CECrexVlEUrH4wbfMN42+46jYzBhMB0G +A1UdDgQWBBRzL8o6Lcvi46LBqy81zGRTZIwGLTAfBgNVHSMEGDAWgBRzL8o6Lcvi +46LBqy81zGRTZIwGLTAPBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBhjAK +BggqhkjOPQQDAgNJADBGAiEA0dfMbQlfjY+VhKuDfluyBtoAsQA1BqJnV+XBayBx +HYYCIQCJr2YrYRwW4OvtP0yIqUH2fViOq12aqf1ByStksuL3mg== +-----END CERTIFICATE----- diff --git a/certs/ecc/ca-secp256k1-key.pem b/certs/ecc/ca-secp256k1-key.pem new file mode 100644 index 0000000000..1c2e0bc546 --- /dev/null +++ b/certs/ecc/ca-secp256k1-key.pem @@ -0,0 +1,5 @@ +-----BEGIN PRIVATE KEY----- +MIGEAgEAMBAGByqGSM49AgEGBSuBBAAKBG0wawIBAQQgoiOQsNn1L/vT3mU0e+Rz +LMlaDEIuKp6RHZhKrPnOJOShRANCAATkYOZC0x5ZB74560SlE5gVy3YcZHbCArNM +m6Oq96A+qho199xbAR6fZ8KLPd4E+whAq3sVZRFKx+MG3zDeNvuO +-----END PRIVATE KEY----- diff --git a/certs/ecc/genecc.sh b/certs/ecc/genecc.sh index f90c5cbe93..d068d0d028 100755 --- a/certs/ecc/genecc.sh +++ b/certs/ecc/genecc.sh @@ -140,6 +140,13 @@ openssl x509 -req -in ./certs/ecc/client-bp256r1-req.pem -days 3650 -extfile ./c openssl x509 -inform pem -in ./certs/ecc/client-bp256r1-cert.pem -outform der -out ./certs/ecc/client-bp256r1-cert.der rm ./certs/ecc/client-bp256r1-req.pem +# Create self-signed ECC secp256k1 (Koblitz) certificate +openssl req -config ./certs/ecc/wolfssl.cnf -extensions v3_ca -x509 -nodes -newkey ec:certs/ecc/secp256k1-param.pem -keyout ./certs/ecc/ca-secp256k1-key.pem -out ./certs/ecc/ca-secp256k1-cert.pem -sha256 -days 7300 -batch -subj "/C=US/ST=Washington/L=Seattle/O=Elliptic/OU=SECP256K1/CN=www.wolfssl.com/emailAddress=info@wolfssl.com" +# Create server ECC secp256k1 (Koblitz) certificate +openssl req -config ./certs/ecc/wolfssl.cnf -sha256 -new -key ./certs/ecc/secp256k1-privkey.pem -out ./certs/ecc/server2-secp256k1-req.pem -subj "/C=US/ST=Washington/L=Seattle/O=Elliptic/OU=SECP256K1-SVR/CN=www.wolfssl.com/emailAddress=info@wolfssl.com/" +openssl x509 -req -in ./certs/ecc/server2-secp256k1-req.pem -days 3650 -extfile ./certs/ecc/wolfssl.cnf -extensions server_cert -CAkey ./certs/ecc/ca-secp256k1-key.pem -CA ./certs/ecc/ca-secp256k1-cert.pem -text -out ./certs/ecc/server2-secp256k1-cert.pem +openssl x509 -inform pem -in ./certs/ecc/server2-secp256k1-cert.pem -outform der -out ./certs/ecc/server2-secp256k1-cert.der +rm ./certs/ecc/server2-secp256k1-req.pem # update bad certificate with last byte in signature changed cp ./certs/server-ecc.der ./certs/test/server-cert-ecc-badsig.der diff --git a/certs/ecc/include.am b/certs/ecc/include.am index c5a4f858aa..3408449acf 100644 --- a/certs/ecc/include.am +++ b/certs/ecc/include.am @@ -16,6 +16,13 @@ EXTRA_DIST += \ certs/ecc/server-secp256k1-cert.der \ certs/ecc/server-secp256k1-cert.pem +EXTRA_DIST += \ + certs/ecc/ca-secp256k1-cert.pem \ + certs/ecc/ca-secp256k1-key.pem \ + certs/ecc/secp256k1-param.pem \ + certs/ecc/secp256k1-privkey.pem \ + certs/ecc/server2-secp256k1-cert.pem + # Brainpool Curves EXTRA_DIST += \ certs/ecc/bp256r1-key.der \ diff --git a/certs/ecc/secp256k1-param.pem b/certs/ecc/secp256k1-param.pem new file mode 100644 index 0000000000..32d952ea91 --- /dev/null +++ b/certs/ecc/secp256k1-param.pem @@ -0,0 +1,3 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQACg== +-----END EC PARAMETERS----- diff --git a/certs/ecc/secp256k1-privkey.der b/certs/ecc/secp256k1-privkey.der new file mode 100644 index 0000000000000000000000000000000000000000..46be0b2109b7986ebb05fca92c5aeeb6ac0d58cb GIT binary patch literal 118 zcmV-+0Ez!FbOHeZ1R(0WDS>tT?U5l!Ep@DCI7K#OGTjc!W+{=>idLPfN zBfEZwEN)`7H)vwC21l5zFbk6b12DQc6WTl&+kQAPGO_?;l+6Jcvdp2fd@N!tBA09$ z`ggQ>Y}#JC+r@E?)cahfB!7d(Yan@Lmd0}iji(z#iuF%f>7IV{c+tVt`le@6f>V0f zbQdbuffer, der->length, &p8AlgId); - if (ret > 0) { - /* Header stripped inline. */ - der->length = (word32)ret; - algId = p8AlgId; - } -#endif - /* Put the data into the SSL or SSL context object. */ ret = ProcessBufferPrivKeyHandleDer(ctx, ssl, &der, type); if (ret == 0) { diff --git a/tests/test-ecc-cust-curves.conf b/tests/test-ecc-cust-curves.conf index 697d96796e..6f24783e86 100644 --- a/tests/test-ecc-cust-curves.conf +++ b/tests/test-ecc-cust-curves.conf @@ -179,3 +179,19 @@ -k ./certs/ecc/bp256r1-key.pem -A ./certs/ecc/server-bp256r1-cert.pem -C + +# -- SECP256K1 without OID inside PKCS#8 -- +# server TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256 +-v 3 +-l ECDHE-ECDSA-AES128-GCM-SHA256 +-c ./certs/ecc/server2-secp256k1-cert.pem +-k ./certs/ecc/secp256k1-privkey.pem +-d + +# client TLSv1.2 ECDHE-ECDSA-AES128-GCM-SHA256 +-v 3 +-l ECDHE-ECDSA-AES128-GCM-SHA256 +-A ./certs/ecc/ca-secp256k1-cert.pem +-x +-C + diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 6a557b069f..11e2f0ddef 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -6858,6 +6858,7 @@ enum { * On out, start of encoded key. * @param [in] sz Size of data in buffer. * @param [out] algId Key's algorithm id from PKCS #8 header. + * @param [out] eccOid ECC curve OID. * @return Length of key data on success. * @return BAD_FUNC_ARG when input or inOutIdx is NULL. * @return ASN_PARSE_E when BER encoded data does not match ASN.1 items or @@ -6867,8 +6868,8 @@ enum { * @return ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a * non-zero length. */ -int ToTraditionalInline_ex(const byte* input, word32* inOutIdx, word32 sz, - word32* algId) +int ToTraditionalInline_ex2(const byte* input, word32* inOutIdx, word32 sz, + word32* algId, word32* eccOid) { #ifndef WOLFSSL_ASN_TEMPLATE word32 idx; @@ -6918,8 +6919,14 @@ int ToTraditionalInline_ex(const byte* input, word32* inOutIdx, word32 sz, #endif /* WC_RSA_PSS && !NO_RSA */ if (tag == ASN_OBJECT_ID) { - if (SkipObjectId(input, &idx, sz) < 0) - return ASN_PARSE_E; + if ((*algId == ECDSAk) && (eccOid != NULL)) { + if (GetObjectId(input, &idx, eccOid, oidCurveType, maxIdx) < 0) + return ASN_PARSE_E; + } + else { + if (SkipObjectId(input, &idx, sz) < 0) + return ASN_PARSE_E; + } } ret = GetOctetString(input, &idx, &length, sz); @@ -6940,6 +6947,8 @@ int ToTraditionalInline_ex(const byte* input, word32* inOutIdx, word32 sz, byte version = 0; word32 idx; + (void)eccOid; + /* Check validity of parameters. */ if (input == NULL || inOutIdx == NULL) { return BAD_FUNC_ARG; @@ -7013,6 +7022,11 @@ int ToTraditionalInline_ex(const byte* input, word32* inOutIdx, word32 sz, if (dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_NULL].tag != 0) { ret = ASN_PARSE_E; } + if (eccOid != NULL) { + ASNGetData* oidCurve = + &dataASN[PKCS8KEYASN_IDX_PKEY_ALGO_OID_CURVE]; + *eccOid = oidCurve->data.oid.sum; + } break; #endif #ifdef HAVE_ED25519 @@ -7072,6 +7086,29 @@ int ToTraditionalInline_ex(const byte* input, word32* inOutIdx, word32 sz, #endif } +/* Remove PKCS #8 header around an RSA, ECDSA, Ed25519, or Ed448. + * + * @param [in] input Buffer holding BER data. + * @param [in, out] inOutIdx On in, start of PKCS #8 encoding. + * On out, start of encoded key. + * @param [in] sz Size of data in buffer. + * @param [out] algId Key's algorithm id from PKCS #8 header. + * @return Length of key data on success. + * @return BAD_FUNC_ARG when input or inOutIdx is NULL. + * @return ASN_PARSE_E when BER encoded data does not match ASN.1 items or + * is invalid. + * @return BUFFER_E when data in buffer is too small. + * @return ASN_OBJECT_ID_E when the expected OBJECT_ID tag is not found. + * @return ASN_EXPECT_0_E when the INTEGER has the MSB set or NULL has a + * non-zero length. + */ +int ToTraditionalInline_ex(const byte* input, word32* inOutIdx, word32 sz, + word32* algId) +{ + return ToTraditionalInline_ex2(input, inOutIdx, sz, algId, NULL); +} + + /* TODO: test case */ int ToTraditionalInline(const byte* input, word32* inOutIdx, word32 sz) { @@ -33887,6 +33924,7 @@ int wc_EccPrivateKeyDecode(const byte* input, word32* inOutIdx, ecc_key* key, int curve_id = ECC_CURVE_DEF; #if defined(HAVE_PKCS8) || defined(HAVE_PKCS12) || defined(SM2) word32 algId = 0; + word32 eccOid = 0; #endif /* Validate parameters. */ @@ -33896,11 +33934,11 @@ int wc_EccPrivateKeyDecode(const byte* input, word32* inOutIdx, ecc_key* key, #if defined(HAVE_PKCS8) || defined(HAVE_PKCS12) || defined(SM2) /* if has pkcs8 header skip it */ - if (ToTraditionalInline_ex(input, inOutIdx, inSz, &algId) < 0) { + if (ToTraditionalInline_ex2(input, inOutIdx, inSz, &algId, &eccOid) < 0) { /* ignore error, did not have pkcs8 header */ } else { - curve_id = wc_ecc_get_oid(algId, NULL, NULL); + curve_id = wc_ecc_get_oid(eccOid, NULL, NULL); } #endif diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index 503c985790..02ebbbd929 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2216,6 +2216,9 @@ WOLFSSL_LOCAL int ToTraditionalInline(const byte* input, word32* inOutIdx, word32 length); WOLFSSL_LOCAL int ToTraditionalInline_ex(const byte* input, word32* inOutIdx, word32 length, word32* algId); +WOLFSSL_LOCAL int ToTraditionalInline_ex2(const byte* input, word32* inOutIdx, + word32 length, word32* algId, + word32* eccOid); WOLFSSL_LOCAL int ToTraditionalEnc(byte* input, word32 sz, const char* password, int passwordSz, word32* algId); WOLFSSL_ASN_API int UnTraditionalEnc(byte* key, word32 keySz, byte* out,