From 3a2fb9af5b137abd898bb6fbbb323e27c6c35eb1 Mon Sep 17 00:00:00 2001 From: Justin Smith Date: Fri, 11 Oct 2024 15:36:40 -0400 Subject: [PATCH] Marshalling/Unmarshalling DH public keys --- crypto/dh_extra/dh_test.cc | 107 +++++++++++++++++++++++++++++++ crypto/evp_extra/p_dh_asn1.c | 75 +++++++++++++++++++++- crypto/evp_extra/p_methods.c | 3 +- crypto/fipsmodule/evp/internal.h | 4 +- 4 files changed, 184 insertions(+), 5 deletions(-) diff --git a/crypto/dh_extra/dh_test.cc b/crypto/dh_extra/dh_test.cc index b9c1892aa47..d1d4c89f5f1 100644 --- a/crypto/dh_extra/dh_test.cc +++ b/crypto/dh_extra/dh_test.cc @@ -1051,3 +1051,110 @@ TEST(DHTest, DHCheckForStandardParams) { ASSERT_TRUE(DH_check(dh2.get(), &flags)); EXPECT_EQ(flags, 0); } + +TEST(DHTest, DHMarshalPubKey) { + const char* dh512_pem = + "-----BEGIN DH PARAMETERS-----\n" + "MEYCQQDqvLe5oX3p+Dw8T7NWG7nlWVFK58Ev74xvxYH72DC4kqfPEFPvNnCpFoRB\n" + "RdxOz7DZ6JO/GxobSRyAAI766+GDAgEC\n" + "-----END DH PARAMETERS-----"; + const uint64_t encoded_g = 2; + const char encoded_p_dec_str[] = "12294183602774786812319504504704470077603616440910559765086569005477513835495488680341310019770549315448633656928525381740662262980129138358936697450520963"; + + bssl::UniquePtr epkey_dh_params(nullptr); + { + const size_t pem_len = OPENSSL_strnlen(dh512_pem, 1024); + bssl::UniquePtr in_bio(BIO_new_mem_buf(dh512_pem, pem_len)); + ASSERT_TRUE(in_bio); + + epkey_dh_params.reset(PEM_read_bio_Parameters(in_bio.get(), nullptr)); + ASSERT_TRUE(epkey_dh_params); + } + + // Sanity check the Param parsing + { + DH *dh_params = EVP_PKEY_get0_DH(epkey_dh_params.get()); + const BIGNUM *p = DH_get0_p(dh_params); + const BIGNUM *g = DH_get0_g(dh_params); + uint64_t parsed_g = 0; + ASSERT_TRUE(BN_get_u64(g, &parsed_g)); + ASSERT_EQ(parsed_g, encoded_g); + const char *parsed_p_dec_str = BN_bn2dec(p); + ASSERT_NE(parsed_p_dec_str, nullptr); + ASSERT_EQ(OPENSSL_strcasecmp(encoded_p_dec_str, parsed_p_dec_str), 0); + OPENSSL_free((void *)parsed_p_dec_str); + } + + // Perform keygen operation + bssl::UniquePtr gen_dh(nullptr); + { + bssl::UniquePtr epkey_ctx( + EVP_PKEY_CTX_new(epkey_dh_params.get(), nullptr)); + ASSERT_TRUE(epkey_ctx); + + ASSERT_TRUE(EVP_PKEY_keygen_init(epkey_ctx.get())); + EVP_PKEY *gen_dh_raw = nullptr; + ASSERT_TRUE(EVP_PKEY_keygen(epkey_ctx.get(), &gen_dh_raw)); + gen_dh.reset(gen_dh_raw); + ASSERT_TRUE(gen_dh); + } + + // Marshall pubkey to der + const uint8_t* pubkey_der = NULL; + size_t pubkey_der_len = 0; + { + bssl::UniquePtr out_bio(BIO_new(BIO_s_mem())); + ASSERT_TRUE(out_bio); + ASSERT_TRUE(i2d_PUBKEY_bio(out_bio.get(), gen_dh.get())); + ASSERT_TRUE(BIO_flush(out_bio.get())); + ASSERT_TRUE(BIO_mem_contents(out_bio.get(), &pubkey_der, &pubkey_der_len)); + ASSERT_GT(pubkey_der_len, (size_t)0); + ASSERT_NE(pubkey_der, nullptr); + // We own the allocation after this + pubkey_der = (const uint8_t*)OPENSSL_memdup(pubkey_der, pubkey_der_len); + } + + // Parse der to pubkey + bssl::UniquePtr parsed_der_pubkey(nullptr); + { + bssl::UniquePtr in_bio(BIO_new_mem_buf(pubkey_der, pubkey_der_len)); + ASSERT_TRUE(in_bio); + EVP_PKEY* parsed_dh_pubkey_raw = nullptr; + ASSERT_TRUE(d2i_PUBKEY_bio(in_bio.get(), &parsed_dh_pubkey_raw)); + parsed_der_pubkey.reset(parsed_dh_pubkey_raw); + ASSERT_TRUE(parsed_der_pubkey); + } + + ASSERT_TRUE(EVP_PKEY_cmp(gen_dh.get(), parsed_der_pubkey.get())); + + // Marshall pubkey to PEM + const uint8_t* pubkey_pem = NULL; + size_t pubkey_pem_len = 0; + { + bssl::UniquePtr out_bio(BIO_new(BIO_s_mem())); + ASSERT_TRUE(out_bio); + ASSERT_TRUE(PEM_write_bio_PUBKEY(out_bio.get(), gen_dh.get())); + ASSERT_TRUE(BIO_flush(out_bio.get())); + ASSERT_TRUE(BIO_mem_contents(out_bio.get(), &pubkey_pem, &pubkey_pem_len)); + ASSERT_GT(pubkey_pem_len, (size_t)0); + ASSERT_NE(pubkey_pem, nullptr); + // We own the allocation after this + pubkey_pem = (const uint8_t*)OPENSSL_memdup(pubkey_pem, pubkey_pem_len); + } + + // Parse PEM to pubkey + bssl::UniquePtr parsed_pem_pubkey(nullptr); + { + bssl::UniquePtr in_bio(BIO_new_mem_buf(pubkey_pem, pubkey_pem_len)); + ASSERT_TRUE(in_bio); + EVP_PKEY* pem_pubkey_raw = NULL; + ASSERT_TRUE(PEM_read_bio_PUBKEY(in_bio.get(), &pem_pubkey_raw, NULL, NULL)); + parsed_pem_pubkey.reset(pem_pubkey_raw); + ASSERT_TRUE(parsed_pem_pubkey); + } + + ASSERT_TRUE(EVP_PKEY_cmp(gen_dh.get(), parsed_pem_pubkey.get())); + + OPENSSL_free((void*)pubkey_der); + OPENSSL_free((void*)pubkey_pem); +} diff --git a/crypto/evp_extra/p_dh_asn1.c b/crypto/evp_extra/p_dh_asn1.c index db5196f3e87..fc8cb1109aa 100644 --- a/crypto/evp_extra/p_dh_asn1.c +++ b/crypto/evp_extra/p_dh_asn1.c @@ -12,10 +12,71 @@ #include #include #include +#include -#include "internal.h" -#include "../internal.h" #include "../fipsmodule/cpucap/internal.h" +#include "../fipsmodule/dh/internal.h" +#include "../internal.h" +#include "internal.h" + +static int dh_pub_encode(CBB *out, const EVP_PKEY *key) { + CBB spki, algorithm, oid, key_bitstring; + if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) || + !CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) || + !CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) || + !CBB_add_bytes(&oid, dh_asn1_meth.oid, dh_asn1_meth.oid_len) || + !DH_marshal_parameters(&algorithm, key->pkey.dh) || + !CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) || + !CBB_add_u8(&key_bitstring, 0 /* padding */) || + !BN_marshal_asn1(&key_bitstring, key->pkey.dh->pub_key) || + !CBB_flush(out)) { + OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR); + return 0; + } + + return 1; +} + +static int dh_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) { + // RFC 2786 + BIGNUM *pubkey = NULL; + DH *dh = NULL; + if (out == NULL || params == NULL || CBS_len(params) == 0 || key == NULL || + CBS_len(key) == 0) { + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + goto err; + } + + dh = DH_parse_parameters(params); + if (dh == NULL) { + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + goto err; + } + + pubkey = BN_new(); + if (pubkey == NULL || !BN_parse_asn1_unsigned(key, pubkey)) { + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + goto err; + } + + int out_flags = 0; + if (!DH_check_pub_key(dh, pubkey, &out_flags) || out_flags != 0) { + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + goto err; + } + dh->pub_key = pubkey; + + if (!EVP_PKEY_assign_DH(out, dh)) { + OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR); + goto err; + } + return 1; + +err: + DH_free(dh); + BN_free(pubkey); + return 0; +} static void dh_free(EVP_PKEY *pkey) { @@ -80,6 +141,14 @@ static int dh_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b) { const EVP_PKEY_ASN1_METHOD dh_asn1_meth = { .pkey_id = EVP_PKEY_DH, + // 1.2.840.113549.1.3.1 + // ((1)*40 + (2)) = 42 = 0x2a + // 840 = 0b_0000110_1001000 => 0b_1000_0110_0100_1000 = 0x86 0x48 + // 113549 = 0b_0000110_1110111_0001101 => 0b_1000_0110_1111_0111_0000_1101 = 0x86 0xF7 0x0D + .oid = {0x2a, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x03, 0x01}, + .oid_len = 9, + .pem_str = "DH", + .info = "OpenSSL PKCS#3 DH method", .pub_cmp = dh_pub_cmp, .pkey_size = dh_size, .pkey_bits = dh_bits, @@ -87,6 +156,8 @@ const EVP_PKEY_ASN1_METHOD dh_asn1_meth = { .param_copy = dh_param_copy, .param_cmp = dh_param_cmp, .pkey_free = dh_free, + .pub_encode = dh_pub_encode, + .pub_decode = dh_pub_decode, }; int EVP_PKEY_set1_DH(EVP_PKEY *pkey, DH *key) { diff --git a/crypto/evp_extra/p_methods.c b/crypto/evp_extra/p_methods.c index 70f05734589..a7e86ef657f 100644 --- a/crypto/evp_extra/p_methods.c +++ b/crypto/evp_extra/p_methods.c @@ -27,7 +27,8 @@ const EVP_PKEY_ASN1_METHOD *const asn1_evp_pkey_methods[] = { &dilithium3_asn1_meth, #endif &kem_asn1_meth, - &hmac_asn1_meth + &hmac_asn1_meth, + &dh_asn1_meth }; const size_t asn1_evp_pkey_methods_size = sizeof(asn1_evp_pkey_methods)/sizeof(asn1_evp_pkey_methods[0]); diff --git a/crypto/fipsmodule/evp/internal.h b/crypto/fipsmodule/evp/internal.h index 847aee09df0..f90c6ff6c7c 100644 --- a/crypto/fipsmodule/evp/internal.h +++ b/crypto/fipsmodule/evp/internal.h @@ -375,10 +375,10 @@ void evp_pkey_set_cb_translate(BN_GENCB *cb, EVP_PKEY_CTX *ctx); #ifdef ENABLE_DILITHIUM #define NON_FIPS_EVP_PKEY_METHODS 3 -#define ASN1_EVP_PKEY_METHODS 9 +#define ASN1_EVP_PKEY_METHODS 10 #else #define NON_FIPS_EVP_PKEY_METHODS 2 -#define ASN1_EVP_PKEY_METHODS 8 +#define ASN1_EVP_PKEY_METHODS 9 #endif struct fips_evp_pkey_methods {