From 7438e5c96a1b2778923402b29d2585b7859452c1 Mon Sep 17 00:00:00 2001 From: Pankaj Garg Date: Wed, 9 Sep 2020 15:58:18 -0700 Subject: [PATCH] Add crypto API to generate a Certificate Signing Request (#2537) * Add crypto API to generate a CSR * Restyled by clang-format * Enable mbedTLS X509 write in EFR32 configuration * Fix example compilation errors * Add more mbedTLS specific flags * Fix nrf builds * Fix more targets * Review comments * Restyled by clang-format * address review comments Co-authored-by: Restyled.io --- config/nrfconnect/nrfconnect-app.cmake | 3 + examples/lighting-app/nrf5/BUILD.gn | 7 +- examples/lock-app/nrf5/BUILD.gn | 7 +- src/crypto/CHIPCryptoPAL.h | 10 ++ src/crypto/CHIPCryptoPALOpenSSL.cpp | 136 ++++++++++++++++++ src/crypto/CHIPCryptoPALmbedTLS.cpp | 53 +++++++ src/crypto/tests/CHIPCryptoPALTest.cpp | 12 ++ .../EFR32/efr32-chip-mbedtls-config.h | 5 + 8 files changed, 231 insertions(+), 2 deletions(-) diff --git a/config/nrfconnect/nrfconnect-app.cmake b/config/nrfconnect/nrfconnect-app.cmake index 0433a11fb1ef33..097eb7aa566e01 100644 --- a/config/nrfconnect/nrfconnect-app.cmake +++ b/config/nrfconnect/nrfconnect-app.cmake @@ -62,6 +62,9 @@ include(chip-lib) set(CHIP_COMMON_FLAGS -D_SYS__PTHREADTYPES_H_ -DMBEDTLS_CONFIG_FILE= + -DMBEDTLS_PK_WRITE_C + -DMBEDTLS_X509_CREATE_C + -DMBEDTLS_X509_CSR_WRITE_C -isystem${ZEPHYR_BASE}/include/posix -isystem${ZEPHYR_BASE}/../mbedtls/include -I${CMAKE_CURRENT_SOURCE_DIR} diff --git a/examples/lighting-app/nrf5/BUILD.gn b/examples/lighting-app/nrf5/BUILD.gn index fe468093243b49..0ed5c27ec3243f 100644 --- a/examples/lighting-app/nrf5/BUILD.gn +++ b/examples/lighting-app/nrf5/BUILD.gn @@ -49,7 +49,12 @@ nrf5_sdk("sdk") { defines += [ "BUILD_RELEASE=1" ] } - defines += [ "USE_APP_CONFIG" ] + defines += [ + "USE_APP_CONFIG", + "MBEDTLS_PK_WRITE_C", + "MBEDTLS_X509_CREATE_C", + "MBEDTLS_X509_CSR_WRITE_C", + ] } nrf5_executable("lighting_app") { diff --git a/examples/lock-app/nrf5/BUILD.gn b/examples/lock-app/nrf5/BUILD.gn index b83b6c60526c82..ad6b09a24ce560 100644 --- a/examples/lock-app/nrf5/BUILD.gn +++ b/examples/lock-app/nrf5/BUILD.gn @@ -49,7 +49,12 @@ nrf5_sdk("sdk") { defines += [ "BUILD_RELEASE=1" ] } - defines += [ "USE_APP_CONFIG" ] + defines += [ + "USE_APP_CONFIG", + "MBEDTLS_PK_WRITE_C", + "MBEDTLS_X509_CREATE_C", + "MBEDTLS_X509_CSR_WRITE_C", + ] } nrf5_executable("lock_app") { diff --git a/src/crypto/CHIPCryptoPAL.h b/src/crypto/CHIPCryptoPAL.h index 2e11db1fe57ec1..14b9ec462e4b60 100644 --- a/src/crypto/CHIPCryptoPAL.h +++ b/src/crypto/CHIPCryptoPAL.h @@ -43,6 +43,7 @@ const size_t kMax_ECDSA_Signature_Length = 72; const size_t kMAX_FE_Length = kP256_FE_Length; const size_t kMAX_Point_Length = kP256_Point_Length; const size_t kMAX_Hash_Length = kSHA256_Hash_Length; +const size_t kMAX_CSR_Length = 512; const size_t kP256_PrivateKey_Length = 32; const size_t kP256_PublicKey_Length = 65; @@ -308,6 +309,15 @@ CHIP_ERROR pbkdf2_sha256(const uint8_t * password, size_t plen, const uint8_t * **/ CHIP_ERROR NewECPKeypair(ECPKey & pubkey, ECPKey & privkey); +/** @brief Generate a new Certificate Signing Request (CSR). + * @param pubkey public key that'll be inserted in the CSR + * @param privkey private key to sign the CSR + * @param csr Newly generated CSR + * @param csr_length The caller provides the length of input buffer (csr). The function returns the actual length of generated CSR. + * @return Returns a CHIP_ERROR on error, CHIP_NO_ERROR otherwise + **/ +CHIP_ERROR NewCertificateSigningRequest(ECPKey & pubkey, ECPKey & privkey, uint8_t * csr, size_t & csr_length); + /** * The below class implements the draft 01 version of the Spake2+ protocol as * defined in https://www.ietf.org/id/draft-bar-cfrg-spake2plus-01.html. diff --git a/src/crypto/CHIPCryptoPALOpenSSL.cpp b/src/crypto/CHIPCryptoPALOpenSSL.cpp index 514a7c7562200d..118a3784b78313 100644 --- a/src/crypto/CHIPCryptoPALOpenSSL.cpp +++ b/src/crypto/CHIPCryptoPALOpenSSL.cpp @@ -31,8 +31,10 @@ #include #include #include +#include #include #include +#include #include #include @@ -874,6 +876,140 @@ CHIP_ERROR NewECPKeypair(ECPKey & pubkey, ECPKey & privkey) return error; } +CHIP_ERROR NewCertificateSigningRequest(ECPKey & pubkey, ECPKey & privkey, uint8_t * out_csr, size_t & csr_length) +{ + ERR_clear_error(); + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + int nid = NID_undef; + + X509_REQ * x509_req = X509_REQ_new(); + EVP_PKEY * evp_pkey = nullptr; + BIGNUM * pvt_key = nullptr; + EC_GROUP * group = nullptr; + EC_POINT * key_point = nullptr; + + EC_KEY * ec_key = nullptr; + ECName curve = MapECName(pubkey.Type()); + + BIO * bioMem = nullptr; + BUF_MEM * bptr = nullptr; + + VerifyOrExit(curve == MapECName(privkey.Type()), error = CHIP_ERROR_INVALID_ARGUMENT); + + nid = _nidForCurve(curve); + VerifyOrExit(nid != NID_undef, error = CHIP_ERROR_INVALID_ARGUMENT); + + result = X509_REQ_set_version(x509_req, 0); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + + group = EC_GROUP_new_by_curve_name(nid); + VerifyOrExit(group != nullptr, error = CHIP_ERROR_INTERNAL); + + key_point = EC_POINT_new(group); + VerifyOrExit(key_point != nullptr, error = CHIP_ERROR_INTERNAL); + + result = EC_POINT_oct2point(group, key_point, Uint8::to_const_uchar(pubkey), pubkey.Length(), nullptr); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + + ec_key = EC_KEY_new_by_curve_name(nid); + VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_INTERNAL); + + result = EC_KEY_set_public_key(ec_key, key_point); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + + result = EC_KEY_check_key(ec_key); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + + evp_pkey = EVP_PKEY_new(); + VerifyOrExit(evp_pkey != nullptr, error = CHIP_ERROR_INTERNAL); + + result = EVP_PKEY_set1_EC_KEY(evp_pkey, ec_key); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + + result = X509_REQ_set_pubkey(x509_req, evp_pkey); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + + EC_KEY_free(ec_key); + EVP_PKEY_free(evp_pkey); + + ec_key = EC_KEY_new_by_curve_name(nid); + VerifyOrExit(ec_key != nullptr, error = CHIP_ERROR_INTERNAL); + + pvt_key = BN_bin2bn(Uint8::to_const_uchar(privkey), privkey.Length(), nullptr); + VerifyOrExit(pvt_key != nullptr, error = CHIP_ERROR_INTERNAL); + + result = EC_KEY_set_private_key(ec_key, pvt_key); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + + evp_pkey = EVP_PKEY_new(); + VerifyOrExit(evp_pkey != nullptr, error = CHIP_ERROR_INTERNAL); + + result = EVP_PKEY_set1_EC_KEY(evp_pkey, ec_key); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + + result = X509_REQ_sign(x509_req, evp_pkey, EVP_sha256()); + VerifyOrExit(result > 0, error = CHIP_ERROR_INTERNAL); + + bioMem = BIO_new(BIO_s_mem()); + VerifyOrExit(bioMem != nullptr, error = CHIP_ERROR_INTERNAL); + + result = PEM_write_bio_X509_REQ(bioMem, x509_req); + VerifyOrExit(result == 1, error = CHIP_ERROR_INTERNAL); + + BIO_get_mem_ptr(bioMem, &bptr); + { + size_t input_length = csr_length; + csr_length = bptr->length; + VerifyOrExit(bptr->length <= input_length, error = CHIP_ERROR_BUFFER_TOO_SMALL); + memset(out_csr, 0, input_length); + } + + BIO_read(bioMem, out_csr, bptr->length); + +exit: + if (ec_key != nullptr) + { + EC_KEY_free(ec_key); + ec_key = nullptr; + } + + if (group != nullptr) + { + EC_GROUP_free(group); + group = nullptr; + } + + if (evp_pkey != nullptr) + { + EVP_PKEY_free(evp_pkey); + evp_pkey = nullptr; + } + + if (bioMem != nullptr) + { + BIO_free(bioMem); + bioMem = nullptr; + } + + if (pvt_key != nullptr) + { + BN_free(pvt_key); + pvt_key = nullptr; + } + + if (key_point != nullptr) + { + EC_POINT_free(key_point); + key_point = nullptr; + } + + X509_REQ_free(x509_req); + + _logSSLError(); + return error; +} + #define init_point(_point_) \ do \ { \ diff --git a/src/crypto/CHIPCryptoPALmbedTLS.cpp b/src/crypto/CHIPCryptoPALmbedTLS.cpp index 155d7c899ac73c..d252e5e42eb391 100644 --- a/src/crypto/CHIPCryptoPALmbedTLS.cpp +++ b/src/crypto/CHIPCryptoPALmbedTLS.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -577,6 +578,58 @@ CHIP_ERROR NewECPKeypair(ECPKey & pubkey, ECPKey & privkey) return error; } +CHIP_ERROR NewCertificateSigningRequest(ECPKey & pubkey, ECPKey & privkey, uint8_t * out_csr, size_t & csr_length) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + int result = 0; + size_t length = csr_length; + + mbedtls_ecp_keypair * keypair = nullptr; + + mbedtls_x509write_csr csr; + mbedtls_x509write_csr_init(&csr); + + mbedtls_pk_context pk; + mbedtls_pk_init(&pk); + + mbedtls_ecp_group_id group = MapECPGroupId(pubkey.Type()); + + const mbedtls_pk_info_t * pk_info = mbedtls_pk_info_from_type(MBEDTLS_PK_ECKEY); + VerifyOrExit(pk_info != nullptr, error = CHIP_ERROR_INTERNAL); + + result = mbedtls_pk_setup(&pk, pk_info); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + keypair = mbedtls_pk_ec(pk); + mbedtls_ecp_keypair_init(keypair); + + VerifyOrExit(group == MapECPGroupId(privkey.Type()), error = CHIP_ERROR_INVALID_ARGUMENT); + + result = mbedtls_ecp_group_load(&keypair->grp, group); + VerifyOrExit(result == 0, error = CHIP_ERROR_INTERNAL); + + result = mbedtls_ecp_point_read_binary(&keypair->grp, &keypair->Q, Uint8::to_const_uchar(pubkey), pubkey.Length()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INVALID_ARGUMENT); + + result = mbedtls_mpi_read_binary(&keypair->d, Uint8::to_const_uchar(privkey), privkey.Length()); + VerifyOrExit(result == 0, error = CHIP_ERROR_INVALID_ARGUMENT); + + mbedtls_x509write_csr_set_key(&csr, &pk); + + mbedtls_x509write_csr_set_md_alg(&csr, MBEDTLS_MD_SHA256); + + result = mbedtls_x509write_csr_pem(&csr, out_csr, length, CryptoRNG, nullptr); + VerifyOrExit(result >= 0, error = CHIP_ERROR_INTERNAL); + csr_length = result; + result = 0; + +exit: + mbedtls_x509write_csr_free(&csr); + mbedtls_pk_free(&pk); + _log_mbedTLS_error(result); + return error; +} + typedef struct Spake2p_Context { mbedtls_ecp_group curve; diff --git a/src/crypto/tests/CHIPCryptoPALTest.cpp b/src/crypto/tests/CHIPCryptoPALTest.cpp index a82394b01eef91..ce880732cef89c 100644 --- a/src/crypto/tests/CHIPCryptoPALTest.cpp +++ b/src/crypto/tests/CHIPCryptoPALTest.cpp @@ -921,6 +921,17 @@ static void TestP256_Keygen(nlTestSuite * inSuite, void * inContext) ECDSA_validate_msg_signature(test_msg, msglen, pubkey, pubkey.Length(), test_sig, siglen) == CHIP_NO_ERROR); } +static void TestCSR_Gen(nlTestSuite * inSuite, void * inContext) +{ + P256PublicKey pubkey; + P256PrivateKey privkey; + uint8_t csr[kMAX_CSR_Length]; + size_t length = sizeof(csr); + + NL_TEST_ASSERT(inSuite, NewECPKeypair(pubkey, privkey) == CHIP_NO_ERROR); + NL_TEST_ASSERT(inSuite, NewCertificateSigningRequest(pubkey, privkey, csr, length) == CHIP_NO_ERROR); +} + static void TestSPAKE2P_spake2p_FEMul(nlTestSuite * inSuite, void * inContext) { uint8_t fe_out[kMAX_FE_Length]; @@ -1349,6 +1360,7 @@ static const nlTest sTests[] = { NL_TEST_DEF("Test adding entropy sources", TestAddEntropySources), NL_TEST_DEF("Test PBKDF2 SHA256", TestPBKDF2_SHA256_TestVectors), NL_TEST_DEF("Test P256 Keygen", TestP256_Keygen), + NL_TEST_DEF("Test CSR Generation", TestCSR_Gen), NL_TEST_DEF("Test Spake2p_spake2p FEMul", TestSPAKE2P_spake2p_FEMul), NL_TEST_DEF("Test Spake2p_spake2p FELoad/FEWrite", TestSPAKE2P_spake2p_FELoadWrite), NL_TEST_DEF("Test Spake2p_spake2p Mac", TestSPAKE2P_spake2p_Mac), diff --git a/src/platform/EFR32/efr32-chip-mbedtls-config.h b/src/platform/EFR32/efr32-chip-mbedtls-config.h index e6f3968632a726..6b9d7b4d134ce1 100644 --- a/src/platform/EFR32/efr32-chip-mbedtls-config.h +++ b/src/platform/EFR32/efr32-chip-mbedtls-config.h @@ -191,6 +191,7 @@ typedef void mbedtls_ecp_restart_ctx; #define MBEDTLS_NO_PLATFORM_ENTROPY #define MBEDTLS_PK_C #define MBEDTLS_PK_PARSE_C +#define MBEDTLS_PK_WRITE_C #define MBEDTLS_PLATFORM_C #define MBEDTLS_PLATFORM_MEMORY #define MBEDTLS_PLATFORM_NO_STD_FUNCTIONS @@ -205,6 +206,10 @@ typedef void mbedtls_ecp_restart_ctx; #define MBEDTLS_SSL_TLS_C #define MBEDTLS_ERROR_STRERROR_DUMMY #define MBEDTLS_HKDF_C +#define MBEDTLS_X509_CREATE_C +#define MBEDTLS_X509_CSR_WRITE_C +#define MBEDTLS_BASE64_C +#define MBEDTLS_PEM_WRITE_C #if OPENTHREAD_CONFIG_BORDER_AGENT_ENABLE || OPENTHREAD_CONFIG_COMMISSIONER_ENABLE || OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE #define MBEDTLS_SSL_COOKIE_C