Provide ASN1_STRING_new_not_owned()
This function provides the ability to construct an ASN1_STRING containing data that is not owned by the constructed ASN1_STRING. The resulting ASN1_STRING, when freed, will not free the data, and it is the caller's resposibility to ensure that the data lives past the lifetime of any returned ASN1_STRING. Why? you may ask? Many places where ->data and ->length were used directly in the past before the opaquification of ASN1_STRING were for this purpose, whether used for actual static data, or to turn bytes created by and in control of the caller into an ASN1_STRING for temporary use as an input. This function makes this easier to do without making copies. The function deliberately does not allow the creation of a BIT_STRING as this would require also always providing unused bits, which is annoying and unnecessary for almost all potential use cases. For: https://github.com/openssl/openssl/issues/29861 For: https://github.com/openssl/openssl/issues/30162 Reviewed-by: Neil Horman <nhorman@openssl.org> Reviewed-by: Saša Nedvědický <sashan@openssl.org> MergeDate: Wed Jun 3 11:42:49 2026 (Merged from https://github.com/openssl/openssl/pull/30964)
This commit is contained in:
parent
46b5165d44
commit
68c0321e90
10 changed files with 200 additions and 22 deletions
|
|
@ -109,6 +109,12 @@ OpenSSL Releases
|
|||
|
||||
*Bob Beck*
|
||||
|
||||
* The API function `ASN1_STRING_new_not_owned` has been added to the
|
||||
libcrypto. It provides the ability to construct an ASN1_STRING with data
|
||||
for which ownership is not taken by the created ASN1_STRING object.
|
||||
|
||||
*Bob Beck*
|
||||
|
||||
* Added AVX2 optimized ML-DSA NTT operations on `x86_64`.
|
||||
|
||||
*Marcel Cornu and Tomasz Kantecki*
|
||||
|
|
|
|||
|
|
@ -263,12 +263,6 @@ int ASN1_BIT_STRING_set1(ASN1_BIT_STRING *abs, const uint8_t *data, size_t lengt
|
|||
if (length > 0 && (data[length - 1] & ((1 << unused_bits) - 1)) != 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* XXX - ASN1_STRING_set() and asn1_bit_string_set_unused_bits() preserve the
|
||||
* state of flags irrelevant to ASN1_BIT_STRING. Should we explicitly
|
||||
* clear them?
|
||||
*/
|
||||
|
||||
if (!ASN1_STRING_set(abs, data, (int)length))
|
||||
return 0;
|
||||
abs->type = V_ASN1_BIT_STRING;
|
||||
|
|
|
|||
|
|
@ -248,9 +248,15 @@ int ASN1_object_size(int constructed, int length, int tag)
|
|||
return ret + length;
|
||||
}
|
||||
|
||||
void ossl_asn1_bit_string_set_unused_bits(ASN1_STRING *str, unsigned int num)
|
||||
void ossl_asn1_bit_string_clear_unused_bits(ASN1_STRING *str)
|
||||
{
|
||||
str->flags &= ~0x07;
|
||||
str->flags &= ~ASN1_STRING_FLAG_BITS_LEFT;
|
||||
}
|
||||
|
||||
void ossl_asn1_bit_string_set_unused_bits(ASN1_STRING *str, unsigned int num)
|
||||
{
|
||||
ossl_asn1_bit_string_clear_unused_bits(str);
|
||||
str->flags |= ASN1_STRING_FLAG_BITS_LEFT | (num & 0x07);
|
||||
}
|
||||
|
||||
|
|
@ -309,6 +315,13 @@ int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len_in)
|
|||
ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LARGE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((str->flags & ASN1_STRING_FLAG_DATA_NOT_OWNED)) {
|
||||
str->data = NULL;
|
||||
str->length = 0;
|
||||
str->flags &= ~ASN1_STRING_FLAG_DATA_NOT_OWNED;
|
||||
}
|
||||
|
||||
if ((size_t)str->length <= len || str->data == NULL) {
|
||||
c = str->data;
|
||||
#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
|
||||
|
|
@ -337,12 +350,17 @@ int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len_in)
|
|||
str->data[len] = '\0';
|
||||
#endif
|
||||
}
|
||||
ossl_asn1_bit_string_clear_unused_bits(str);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len)
|
||||
{
|
||||
OPENSSL_free(str->data);
|
||||
if (!(str->flags & ASN1_STRING_FLAG_DATA_NOT_OWNED)) {
|
||||
OPENSSL_clear_free(str->data, str->length);
|
||||
}
|
||||
str->flags &= ~ASN1_STRING_FLAG_DATA_NOT_OWNED;
|
||||
str->data = data;
|
||||
str->length = len;
|
||||
}
|
||||
|
|
@ -363,30 +381,75 @@ ASN1_STRING *ASN1_STRING_type_new(int type)
|
|||
return ret;
|
||||
}
|
||||
|
||||
void ossl_asn1_string_embed_free(ASN1_STRING *a, int embed)
|
||||
ASN1_STRING *ASN1_STRING_new_not_owned(int type, const uint8_t *data,
|
||||
size_t length)
|
||||
{
|
||||
ASN1_STRING *ret;
|
||||
|
||||
if (type == V_ASN1_BIT_STRING)
|
||||
return NULL;
|
||||
|
||||
if (data == NULL || length == 0)
|
||||
return NULL;
|
||||
|
||||
if (length > INT_MAX)
|
||||
return NULL;
|
||||
|
||||
ret = OPENSSL_zalloc(sizeof(*ret));
|
||||
if (ret == NULL)
|
||||
return NULL;
|
||||
|
||||
ret->type = type;
|
||||
ret->data = (unsigned char *)data;
|
||||
ret->length = (int)length;
|
||||
ret->flags |= ASN1_STRING_FLAG_DATA_NOT_OWNED;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ossl_asn1_string_free_internal(ASN1_STRING *a, int clear, int embed)
|
||||
{
|
||||
if (a == NULL)
|
||||
return;
|
||||
if (!(a->flags & ASN1_STRING_FLAG_NDEF))
|
||||
OPENSSL_free(a->data);
|
||||
if (embed == 0)
|
||||
OPENSSL_free(a);
|
||||
|
||||
if ((a->flags & ASN1_STRING_FLAG_DATA_NOT_OWNED)) {
|
||||
a->data = NULL;
|
||||
a->length = 0;
|
||||
a->flags &= ~ASN1_STRING_FLAG_DATA_NOT_OWNED;
|
||||
}
|
||||
|
||||
if (!(a->flags & ASN1_STRING_FLAG_NDEF)) {
|
||||
if (clear)
|
||||
OPENSSL_clear_free(a->data, a->length);
|
||||
else
|
||||
OPENSSL_free(a->data);
|
||||
}
|
||||
/*
|
||||
* TODO(beck): Add an assert here to verify that the embed arg is
|
||||
* always set to match the flag, and then get rid of the arg.
|
||||
*/
|
||||
if (!embed && !(a->flags & ASN1_STRING_FLAG_EMBED)) {
|
||||
if (clear)
|
||||
OPENSSL_clear_free(a, sizeof(*a));
|
||||
else
|
||||
OPENSSL_free(a);
|
||||
}
|
||||
}
|
||||
|
||||
void ASN1_STRING_free(ASN1_STRING *a)
|
||||
{
|
||||
if (a == NULL)
|
||||
return;
|
||||
ossl_asn1_string_embed_free(a, a->flags & ASN1_STRING_FLAG_EMBED);
|
||||
|
||||
ossl_asn1_string_free_internal(a, 0, a->flags & ASN1_STRING_FLAG_EMBED);
|
||||
}
|
||||
|
||||
void ASN1_STRING_clear_free(ASN1_STRING *a)
|
||||
{
|
||||
if (a == NULL)
|
||||
return;
|
||||
if (a->data && !(a->flags & ASN1_STRING_FLAG_NDEF))
|
||||
OPENSSL_cleanse(a->data, a->length);
|
||||
ASN1_STRING_free(a);
|
||||
|
||||
ossl_asn1_string_free_internal(a, 1, a->flags & ASN1_STRING_FLAG_EMBED);
|
||||
}
|
||||
|
||||
int ASN1_STRING_cmp(const ASN1_STRING *a, const ASN1_STRING *b)
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ DEFINE_STACK_OF(MIME_PARAM)
|
|||
typedef struct mime_header_st MIME_HEADER;
|
||||
DEFINE_STACK_OF(MIME_HEADER)
|
||||
|
||||
void ossl_asn1_string_embed_free(ASN1_STRING *a, int embed);
|
||||
void ossl_asn1_string_free_internal(ASN1_STRING *a, int clear, int embed);
|
||||
|
||||
int ossl_asn1_get_choice_selector(ASN1_VALUE **pval, const ASN1_ITEM *it);
|
||||
int ossl_asn1_get_choice_selector_const(const ASN1_VALUE **pval,
|
||||
|
|
|
|||
|
|
@ -205,7 +205,7 @@ void ossl_asn1_primitive_free(ASN1_VALUE **pval, const ASN1_ITEM *it, int embed)
|
|||
break;
|
||||
|
||||
default:
|
||||
ossl_asn1_string_embed_free((ASN1_STRING *)*pval, embed);
|
||||
ossl_asn1_string_free_internal((ASN1_STRING *)*pval, 0, embed);
|
||||
break;
|
||||
}
|
||||
*pval = NULL;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
=head1 NAME
|
||||
|
||||
ASN1_STRING_new, ASN1_STRING_type_new, ASN1_STRING_free -
|
||||
ASN1_STRING_new, ASN1_STRING_type_new, ASN1_STRING_free,
|
||||
ASN1_STRING_new_not_owned -
|
||||
ASN1_STRING allocation functions
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
|
@ -11,6 +12,7 @@ ASN1_STRING allocation functions
|
|||
|
||||
ASN1_STRING *ASN1_STRING_new(void);
|
||||
ASN1_STRING *ASN1_STRING_type_new(int type);
|
||||
ASN1_STRING *ASN1_STRING_new_not_owned(int type, const uint8_t *data, size_t length);
|
||||
void ASN1_STRING_free(ASN1_STRING *a);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
|
@ -21,6 +23,15 @@ is undefined.
|
|||
ASN1_STRING_type_new() returns an allocated B<ASN1_STRING> structure of
|
||||
type I<type>.
|
||||
|
||||
ASN1_STRING_new_not_owned() returns an allocated B<ASN1_STRING>
|
||||
structure of type I<type>, and sets the data returned string to the
|
||||
bytes at I<data> of length I<len>. Ownership of I<data> is not
|
||||
transferred, and it is the caller's responsibility to ensure that
|
||||
I<data> outlives any successfully returned result. The provided I<type>
|
||||
must not be V_ASN1_BIT_STRING, the provided I<length> must be greater
|
||||
than 0, and I<data> must not be NULL. It is an error if the provided
|
||||
data size exceeds the internal limit of the ASN1_STRING implementation.
|
||||
|
||||
ASN1_STRING_free() frees up I<a>.
|
||||
If I<a> is NULL nothing is done.
|
||||
|
||||
|
|
@ -29,10 +40,16 @@ If I<a> is NULL nothing is done.
|
|||
Other string types call the B<ASN1_STRING> functions. For example
|
||||
ASN1_OCTET_STRING_new() calls ASN1_STRING_type_new(V_ASN1_OCTET_STRING).
|
||||
|
||||
ASN1_STRING_new_not_owned() does not automatically call strlen()
|
||||
to determine a length, or guarantee that the data is a C string. The
|
||||
data contained in the string and the lifetime of it are the
|
||||
responsibility of the caller.
|
||||
|
||||
=head1 RETURN VALUES
|
||||
|
||||
ASN1_STRING_new() and ASN1_STRING_type_new() return a valid
|
||||
B<ASN1_STRING> structure or NULL if an error occurred.
|
||||
ASN1_STRING_new(), ASN1_STRING_type_new(), and
|
||||
ASN1_STRING_new_not_owned() return a valid B<ASN1_STRING>
|
||||
structure or NULL if an error occurred.
|
||||
|
||||
ASN1_STRING_free() does not return a value.
|
||||
|
||||
|
|
@ -40,6 +57,10 @@ ASN1_STRING_free() does not return a value.
|
|||
|
||||
L<ERR_get_error(3)>
|
||||
|
||||
=head1 HISTORY
|
||||
|
||||
ASN1_STRING_new_not_owned() was added in OpenSSL 4.1.
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2002-2023 The OpenSSL Project Authors. All Rights Reserved.
|
||||
|
|
|
|||
|
|
@ -43,6 +43,9 @@
|
|||
/* String is embedded and only content should be freed */
|
||||
#define ASN1_STRING_FLAG_EMBED 0x080
|
||||
|
||||
/* Data is static and should not be freed. */
|
||||
#define ASN1_STRING_FLAG_DATA_NOT_OWNED 0x100
|
||||
|
||||
/* This is the base type that holds just about everything :-) */
|
||||
struct asn1_string_st {
|
||||
int length;
|
||||
|
|
@ -183,6 +186,7 @@ EVP_PKEY *ossl_d2i_PrivateKey_legacy(int keytype, EVP_PKEY **a,
|
|||
OSSL_LIB_CTX *libctx, const char *propq);
|
||||
X509_ALGOR *ossl_X509_ALGOR_from_nid(int nid, int ptype, void *pval);
|
||||
|
||||
void ossl_asn1_bit_string_clear_unused_bits(ASN1_STRING *str);
|
||||
void ossl_asn1_bit_string_set_unused_bits(ASN1_STRING *str, unsigned int num);
|
||||
|
||||
int asn1_item_embed_d2i(ASN1_VALUE **pval, const unsigned char **in,
|
||||
|
|
|
|||
|
|
@ -530,6 +530,8 @@ void ASN1_STRING_clear_free(ASN1_STRING *a);
|
|||
int ASN1_STRING_copy(ASN1_STRING *dst, const ASN1_STRING *str);
|
||||
DECLARE_ASN1_DUP_FUNCTION(ASN1_STRING)
|
||||
ASN1_STRING *ASN1_STRING_type_new(int type);
|
||||
ASN1_STRING *ASN1_STRING_new_not_owned(int type, const uint8_t *data,
|
||||
size_t length);
|
||||
int ASN1_STRING_cmp(const ASN1_STRING *a, const ASN1_STRING *b);
|
||||
/*
|
||||
* Since this is used to store all sorts of things, via macros, for now,
|
||||
|
|
|
|||
|
|
@ -389,9 +389,96 @@ asn1_bit_string_set1_test(int idx)
|
|||
return abs_set1_test(abs_set1_tests, idx);
|
||||
}
|
||||
|
||||
static int
|
||||
asn1_string_new_not_owned_test(void)
|
||||
{
|
||||
int success = 0;
|
||||
ASN1_STRING *tmp = NULL;
|
||||
char *tmpstring = NULL;
|
||||
static const uint8_t data[] = { 0xba, 0xdb, 0x0b, 0xba, 0xdb, 0x0b, 0xba, 0xdb, 0x0b };
|
||||
static const uint8_t data2[] = { 0xba, 0xdb, 0x0b, 0xba, 0xdb, 0x0b, 0xba, 0xdb, 0x0b };
|
||||
|
||||
if (!TEST_ptr(tmp = ASN1_STRING_new_not_owned(V_ASN1_OCTET_STRING, data, sizeof(data))))
|
||||
goto err;
|
||||
|
||||
ASN1_STRING_clear_free(tmp);
|
||||
tmp = NULL;
|
||||
|
||||
if (!TEST_mem_eq(data, sizeof(data), data2, sizeof(data2)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_ptr(tmp = ASN1_STRING_new_not_owned(V_ASN1_OCTET_STRING, data, sizeof(data))))
|
||||
goto err;
|
||||
|
||||
if (!TEST_true(ASN1_STRING_set(tmp, "muppet", (int)strlen("muppet"))))
|
||||
goto err;
|
||||
|
||||
if (!TEST_mem_eq(data, sizeof(data), data2, sizeof(data2)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_int_eq(ASN1_STRING_length(tmp), (int)strlen("muppet")))
|
||||
goto err;
|
||||
|
||||
if (!TEST_mem_eq(ASN1_STRING_get0_data(tmp), strlen("muppet"), "muppet", strlen("muppet")))
|
||||
goto err;
|
||||
|
||||
ASN1_STRING_clear_free(tmp);
|
||||
tmp = NULL;
|
||||
|
||||
if (!TEST_ptr(tmp = ASN1_STRING_new_not_owned(V_ASN1_OCTET_STRING, data, sizeof(data))))
|
||||
goto err;
|
||||
|
||||
if (!TEST_ptr(tmpstring = strdup("puppet")))
|
||||
goto err;
|
||||
|
||||
ASN1_STRING_set0(tmp, tmpstring, 4);
|
||||
|
||||
if (!TEST_mem_eq(data, sizeof(data), data2, sizeof(data2)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_int_eq(ASN1_STRING_length(tmp), 4))
|
||||
goto err;
|
||||
|
||||
if (!TEST_mem_eq(ASN1_STRING_get0_data(tmp), strlen("puppet"), "puppet", strlen("puppet")))
|
||||
goto err;
|
||||
|
||||
memset((uint8_t *)ASN1_STRING_get0_data(tmp), 'z', ASN1_STRING_length(tmp));
|
||||
|
||||
if (!TEST_mem_eq(data, sizeof(data), data2, sizeof(data2)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_mem_eq(tmpstring, strlen("puppet"), "zzzzet", strlen("puppet")))
|
||||
goto err;
|
||||
|
||||
ASN1_STRING_clear_free(tmp);
|
||||
tmpstring = NULL;
|
||||
tmp = NULL;
|
||||
|
||||
if (TEST_ptr(tmp = ASN1_STRING_new_not_owned(V_ASN1_BIT_STRING, data, sizeof(data))))
|
||||
goto err;
|
||||
|
||||
if (TEST_ptr(tmp = ASN1_STRING_new_not_owned(V_ASN1_OCTET_STRING, NULL, sizeof(data))))
|
||||
goto err;
|
||||
|
||||
if (TEST_ptr(tmp = ASN1_STRING_new_not_owned(V_ASN1_OCTET_STRING, data, 0)))
|
||||
goto err;
|
||||
|
||||
if (!TEST_mem_eq(data, sizeof(data), data2, sizeof(data2)))
|
||||
goto err;
|
||||
|
||||
success = 1;
|
||||
|
||||
err:
|
||||
ASN1_STRING_clear_free(tmp);
|
||||
free(tmpstring);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
int setup_tests(void)
|
||||
{
|
||||
ADD_ALL_TESTS(asn1_bit_string_get_length_test, OSSL_NELEM(abs_get_length_tests));
|
||||
ADD_ALL_TESTS(asn1_bit_string_set1_test, OSSL_NELEM(abs_set1_tests));
|
||||
ADD_TEST(asn1_string_new_not_owned_test);
|
||||
return 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5721,3 +5721,4 @@ CRYPTO_atomic_store_ptr ? 4_1_0 EXIST::FUNCTION:
|
|||
CRYPTO_atomic_cmp_exch_ptr ? 4_1_0 EXIST::FUNCTION:
|
||||
EVP_EC_affine2oct ? 4_1_0 EXIST::FUNCTION:
|
||||
OPENSSL_sk_set_copy_thunks ? 4_1_0 EXIST::FUNCTION:
|
||||
ASN1_STRING_new_not_owned ? 4_1_0 EXIST::FUNCTION:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue