From f3f64e62b6417f5994854c2b2c25544ebd4c224d Mon Sep 17 00:00:00 2001 From: samuel40791765 Date: Fri, 10 May 2024 21:13:47 +0000 Subject: [PATCH] add back ASN1_dup with tests --- crypto/asn1/a_dup.c | 20 ++++++++++++++++++ crypto/asn1/asn1_test.cc | 45 ++++++++++++++++++++++++++++++++++++++++ include/openssl/asn1.h | 15 +++++++++++--- 3 files changed, 77 insertions(+), 3 deletions(-) diff --git a/crypto/asn1/a_dup.c b/crypto/asn1/a_dup.c index b37a5c61b95..538aad3a8e3 100644 --- a/crypto/asn1/a_dup.c +++ b/crypto/asn1/a_dup.c @@ -59,6 +59,26 @@ #include #include +void *ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, void *input) { + if (i2d == NULL || d2i == NULL || input == NULL) { + OPENSSL_PUT_ERROR(ASN1, ERR_R_PASSED_NULL_PARAMETER); + return NULL; + } + + // Size and allocate |buf|. + unsigned char *buf = NULL; + int buf_len = i2d(input, &buf); + if (buf == NULL) { + return NULL; + } + + // |buf| needs to be converted to |const| to be passed in. + const unsigned char *temp_input = buf; + char *ret = d2i(NULL, &temp_input, buf_len); + OPENSSL_free(buf); + return ret; +} + // ASN1_ITEM version of dup: this follows the model above except we don't // need to allocate the buffer. At some point this could be rewritten to // directly dup the underlying structure instead of doing and encode and diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc index 1e0f453cc1e..e57a0ac4d8d 100644 --- a/crypto/asn1/asn1_test.cc +++ b/crypto/asn1/asn1_test.cc @@ -2412,6 +2412,51 @@ TEST(ASN1Test, LargeString) { #endif } +TEST(ASN1Test, ASN1Dup) { + const uint8_t *tag = kTag128; + bssl::UniquePtr asn1( + d2i_ASN1_TYPE(nullptr, &tag, sizeof(kTag128))); + ASSERT_TRUE(asn1); + EXPECT_EQ(128, asn1->type); + bssl::UniquePtr asn1_copy((ASN1_TYPE *)ASN1_dup( + (i2d_of_void *)i2d_ASN1_TYPE, (d2i_of_void *)d2i_ASN1_TYPE, asn1.get())); + ASSERT_TRUE(asn1_copy); + EXPECT_EQ(ASN1_TYPE_cmp(asn1.get(), asn1_copy.get()), 0); + + bssl::UniquePtr key(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + ASSERT_TRUE(key); + ASSERT_TRUE(EC_KEY_generate_key(key.get())); + bssl::UniquePtr key_copy( + (EC_KEY *)ASN1_dup((i2d_of_void *)i2d_ECPrivateKey, + (d2i_of_void *)d2i_ECPrivateKey, key.get())); + ASSERT_TRUE(key_copy); + EXPECT_EQ(BN_cmp(EC_KEY_get0_private_key(key.get()), + EC_KEY_get0_private_key(key_copy.get())), + 0); + EXPECT_EQ(EC_GROUP_cmp(EC_KEY_get0_group(key.get()), + EC_KEY_get0_group(key_copy.get()), nullptr), + 0); + EXPECT_EQ(EC_POINT_cmp(EC_KEY_get0_group(key_copy.get()), + EC_KEY_get0_public_key(key.get()), + EC_KEY_get0_public_key(key_copy.get()), nullptr), + 0); + + bssl::UniquePtr evp_pkey(EVP_PKEY_new()); + X509_PUBKEY *tmp_key = nullptr; + ASSERT_TRUE(evp_pkey); + ASSERT_TRUE(EVP_PKEY_set1_EC_KEY(evp_pkey.get(), key.get())); + ASSERT_TRUE(X509_PUBKEY_set(&tmp_key, evp_pkey.get())); + bssl::UniquePtr x509_pubkey(tmp_key); + bssl::UniquePtr x509_pubkey_copy((X509_PUBKEY *)ASN1_dup( + (i2d_of_void *)i2d_X509_PUBKEY, (d2i_of_void *)d2i_X509_PUBKEY, + x509_pubkey.get())); + ASSERT_TRUE(x509_pubkey_copy); + EXPECT_EQ( + ASN1_STRING_cmp(X509_PUBKEY_get0_public_key(x509_pubkey.get()), + X509_PUBKEY_get0_public_key(x509_pubkey_copy.get())), + 0); +} + // The ASN.1 macros do not work on Windows shared library builds, where usage of // |OPENSSL_EXPORT| is a bit stricter. #if !defined(OPENSSL_WINDOWS) || !defined(BORINGSSL_SHARED_LIBRARY) diff --git a/include/openssl/asn1.h b/include/openssl/asn1.h index 581b87369b5..69bb666a0db 100644 --- a/include/openssl/asn1.h +++ b/include/openssl/asn1.h @@ -275,8 +275,7 @@ int i2d_SAMPLE(const SAMPLE *in, uint8_t **outp); // CHECKED_I2D_OF casts a given pointer to i2d_of_void* and statically checks // that it was a pointer to |type|'s |i2d| function. -#define CHECKED_I2D_OF(type, i2d) \ - ((i2d_of_void*) (1 ? i2d : ((I2D_OF(type))0))) +#define CHECKED_I2D_OF(type, i2d) ((i2d_of_void *)(1 ? i2d : ((I2D_OF(type))0))) // The following typedefs are sometimes used for pointers to functions like // |d2i_SAMPLE| and |i2d_SAMPLE|. Note, however, that these act on |void*|. @@ -391,6 +390,16 @@ OPENSSL_EXPORT ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **out, OPENSSL_EXPORT int ASN1_item_i2d(ASN1_VALUE *val, unsigned char **outp, const ASN1_ITEM *it); +// ASN1_dup returns a newly-allocated copy of |x| by re-encoding with |i2d| and +// |d2i|. |i2d| and |d2i| must be the corresponding type functions of |x|. NULL +// is returned on error. +// +// WARNING: DO NOT USE. Casting the result of this function to the wrong type, +// or passing a pointer of the wrong type into this function, are potentially +// exploitable memory errors. Prefer directly calling |i2d| and |d2i| or other +// type-specific functions. +OPENSSL_EXPORT void *ASN1_dup(i2d_of_void *i2d, d2i_of_void *d2i, void *x); + // ASN1_item_dup returns a newly-allocated copy of |x|, or NULL on error. |x| // must be an object of |it|'s C type. // @@ -443,7 +452,7 @@ OPENSSL_EXPORT int ASN1_i2d_bio(i2d_of_void *i2d, BIO *out, void *in); // forces the user to use undefined C behavior and will cause failures when // running against undefined behavior sanitizers in clang. #define ASN1_i2d_bio_of(type, i2d, out, in) \ - (ASN1_i2d_bio(CHECKED_I2D_OF(type, i2d), out, CHECKED_PTR_OF(type, in))) + (ASN1_i2d_bio(CHECKED_I2D_OF(type, i2d), out, CHECKED_PTR_OF(type, in))) // ASN1_item_unpack parses |oct|'s contents as |it|'s ASN.1 type. It returns a // newly-allocated instance of |it|'s C type on success, or NULL on error.