Commit 28179061bf for openssl.org
commit 28179061bfcdfe579f63a129bf0b97766a5d90a7
Author: Bob Beck <beck@openssl.org>
Date: Mon May 11 10:30:04 2026 -0600
Prepare a now opaque ASN1_STRING for the size_t rapture.
Now that ASN1_STRING is opaque, we can finally move away
from an int for the length internally. The remaining problematic
piece for this is that ASN1_STRING_length() returns an int
and is public API.
Therefore, we deprecate ASN1_STRING_length() and provide a
replacement ASN1_STRING_length_ex() that returns a size_t length.
We also provide setting functions that take size_t lengths,
they are ASN1_STRING_set_data() which takes a uint8_t data
pointer and a size_t length, and ASN1_STRING_set_string() which
takes a argument that must be a c string and will use strlen
to determine the length. (This replaces th previous arcane
behaviour of calling "strlen" on a magical input length value
of -1, which leads to bugs.) We then deprecate ASN1_STRING_set().
ASN1_STRING_set_string() requires a valid C string argument that
may not be NULL - refer to the documentation.
Both new functions do not magically add 0 bytes on the end of
values, as ASN1_STRING has already been documented for a long
time to not depend on this behaviour.
Both new functions do not allow the setting of values on an
ASN1_BIT_STRING, as ASN1_BIT_STRING_set1 must be used for that.
Note that this does *NOT* yet change ASN1_STRING to use size_t
internally, this must wait until the integer-returning ASN1_STRING_length()
has been deprecated, and then removed in future major. Once
ASN1_STRING_length() has been removed then ASN1_STRING internally
can change to using a size_t for the length of the data.
(And the setters will no longer return an error if the provided
size_t length exceeds INT_MAX)
Reviewed-by: Milan Broz <mbroz@openssl.org>
Reviewed-by: Norbert Pocs <norbertp@openssl.org>
MergeDate: Sat Jul 18 13:01:15 2026
(Merged from https://github.com/openssl/openssl/pull/31194)
diff --git a/crypto/asn1/a_octet.c b/crypto/asn1/a_octet.c
index 4efb8ec517..99df7539a1 100644
--- a/crypto/asn1/a_octet.c
+++ b/crypto/asn1/a_octet.c
@@ -25,5 +25,11 @@ int ASN1_OCTET_STRING_cmp(const ASN1_OCTET_STRING *a,
int ASN1_OCTET_STRING_set(ASN1_OCTET_STRING *x, const unsigned char *d,
int len)
{
- return ASN1_STRING_set(x, d, len);
+ if (len < -1) {
+ ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_SMALL);
+ return 0;
+ }
+ if (len == -1)
+ return ASN1_STRING_set_string(x, (const char *)d);
+ return ASN1_STRING_set_data(x, d, len);
}
diff --git a/crypto/asn1/asn1_lib.c b/crypto/asn1/asn1_lib.c
index 99903564e2..2bde572eaf 100644
--- a/crypto/asn1/asn1_lib.c
+++ b/crypto/asn1/asn1_lib.c
@@ -265,7 +265,8 @@ int ASN1_STRING_copy(ASN1_STRING *dst, const ASN1_STRING *str)
if (str == NULL)
return 0;
dst->type = str->type;
- if (!ASN1_STRING_set(dst, str->data, str->length))
+ if (!ossl_asn1_string_set_internal(dst, str->data, str->length,
+ /*add_nul_byte=*/0))
return 0;
/* Copy flags but preserve embed value */
dst->flags &= ASN1_STRING_FLAG_EMBED;
@@ -289,12 +290,18 @@ ASN1_STRING *ASN1_STRING_dup(const ASN1_STRING *str)
return ret;
}
-int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len_in)
+int ossl_asn1_string_set_internal(ASN1_STRING *str, const uint8_t *data,
+ int len_in, int add_nul_byte)
{
- unsigned char *c;
- const char *data = _data;
- size_t len;
+ size_t len, alloc_len;
+#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
+ /*
+ * Force no NUL byte for callers that are requesting it
+ * 0 length object data will be NULL
+ */
+ add_nul_byte = 0;
+#endif
if (len_in < -1) {
ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_SMALL);
return 0;
@@ -302,16 +309,17 @@ int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len_in)
if (len_in == -1) {
if (data == NULL)
return 0;
- len = strlen(data);
+ len = strlen((const char *)data);
} else {
len = (size_t)len_in;
}
/*
- * Verify that the length fits within an integer for assignment to
- * str->length below. The additional 1 is subtracted to allow for the
- * '\0' terminator even though this isn't strictly necessary.
+ * Add one to the length to allow for adding an a '\0' terminator
+ * "even though this isn't strictly necessary".
*/
- if (len > INT_MAX - 1) {
+ alloc_len = add_nul_byte ? len + 1 : len;
+
+ if (alloc_len > INT_MAX) {
ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LARGE);
return 0;
}
@@ -322,39 +330,47 @@ int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len_in)
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
- /* No NUL terminator in fuzzing builds */
- str->data = OPENSSL_realloc(c, len != 0 ? len : 1);
-#else
- str->data = OPENSSL_realloc(c, len + 1);
-#endif
- if (str->data == NULL) {
- str->data = c;
+ /* Ensure copying a 0 length data field is defined. */
+ if (alloc_len == 0) {
+ OPENSSL_free(str->data);
+ str->data = NULL;
+ str->length = 0;
+ return 1;
+ }
+
+ if ((size_t)str->length != alloc_len) {
+ uint8_t *c;
+ c = OPENSSL_realloc(str->length == 0 ? NULL : str->data, alloc_len);
+ if (c == NULL)
return 0;
- }
+ str->data = c;
}
+ /* length never includes the added \0 byte */
str->length = (int)len;
- if (data != NULL) {
+
+ if (data != NULL && str->data != NULL) {
memcpy(str->data, data, len);
-#ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
- /* Set the unused byte to something non NUL and printable. */
- if (len == 0)
- str->data[len] = '~';
-#else
- /*
- * Add a NUL terminator. This should not be necessary - but we add it as
- * a safety precaution
- */
- str->data[len] = '\0';
-#endif
+ if (add_nul_byte) {
+ /*
+ * Add a '\0' terminator. This should not be necessary - but we add it as
+ * a safety precaution
+ */
+ str->data[len] = '\0';
+ }
}
ossl_asn1_bit_string_clear_unused_bits(str);
return 1;
}
+#ifndef OPENSSL_NO_DEPRECATED_4_1
+int ASN1_STRING_set(ASN1_STRING *str, const void *_data, int len_in)
+{
+ return ossl_asn1_string_set_internal(str, (const uint8_t *)_data, len_in,
+ /*add_nul_byte=*/1);
+}
+#endif
+
void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len)
{
if (!(str->flags & ASN1_STRING_FLAG_DATA_NOT_OWNED)) {
@@ -365,6 +381,26 @@ void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len)
str->length = len;
}
+int ASN1_STRING_set_data(ASN1_STRING *str, const uint8_t *data, size_t len_in)
+{
+ if (str->type == V_ASN1_BIT_STRING) {
+ ERR_raise(ERR_LIB_ASN1, ASN1_R_ILLEGAL_BITSTRING_FORMAT);
+ return 0;
+ }
+ /* This will go away once ASN1_STRING can size_t internally */
+ if (len_in > INT_MAX) {
+ ERR_raise(ERR_LIB_ASN1, ASN1_R_TOO_LARGE);
+ return 0;
+ }
+ return ossl_asn1_string_set_internal(str, data, (int)len_in, /*add_nul_byte=*/0);
+}
+
+int ASN1_STRING_set_string(ASN1_STRING *str, const char *c_string)
+{
+ return ASN1_STRING_set_data(str, (const uint8_t *)c_string,
+ strlen(c_string));
+}
+
ASN1_STRING *ASN1_STRING_new(void)
{
return ASN1_STRING_type_new(V_ASN1_OCTET_STRING);
@@ -469,10 +505,17 @@ int ASN1_STRING_cmp(const ASN1_STRING *a, const ASN1_STRING *b)
}
}
+#ifndef OPENSSL_NO_DEPRECATED_4_1
int ASN1_STRING_length(const ASN1_STRING *x)
{
return x->length;
}
+#endif
+
+size_t ASN1_STRING_length_ex(const ASN1_STRING *x)
+{
+ return (size_t)x->length;
+}
#ifndef OPENSSL_NO_DEPRECATED_3_0
void ASN1_STRING_length_set(ASN1_STRING *x, int len)
diff --git a/doc/man3/ASN1_STRING_length.pod b/doc/man3/ASN1_STRING_length.pod
index 47cacb253a..5b047f084a 100644
--- a/doc/man3/ASN1_STRING_length.pod
+++ b/doc/man3/ASN1_STRING_length.pod
@@ -2,6 +2,7 @@
=head1 NAME
+ASN1_STRING_set_data, ASN1_STRING_set_string, ASN1_STRING_length_ex,
ASN1_STRING_dup, ASN1_STRING_cmp, ASN1_STRING_set, ASN1_STRING_length,
ASN1_STRING_type, ASN1_STRING_get0_data,
ASN1_STRING_to_UTF8 - ASN1_STRING utility functions
@@ -10,19 +11,30 @@ ASN1_STRING_to_UTF8 - ASN1_STRING utility functions
#include <openssl/asn1.h>
- int ASN1_STRING_length(ASN1_STRING *x);
const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *x);
ASN1_STRING *ASN1_STRING_dup(const ASN1_STRING *a);
int ASN1_STRING_cmp(ASN1_STRING *a, ASN1_STRING *b);
- int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len);
-
int ASN1_STRING_type(const ASN1_STRING *x);
int ASN1_STRING_to_UTF8(unsigned char **out, const ASN1_STRING *in);
+ int ASN1_STRING_set_data(ASN1_STRING *str, const uint8_t *data, size_t len);
+
+ int ASN1_STRING_set_string(ASN1_STRING *str, const char *data);
+
+ size_t ASN1_STRING_length_ex(const ASN1_STRING *x);
+
+The following functions have been deprecated since OpenSSL 4.1, and can be
+hidden entirely by defining B<OPENSSL_API_COMPAT> with a suitable version value,
+see L<openssl_user_macros(7)>:
+
+ int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len);
+
+ int ASN1_STRING_length(ASN1_STRING *x);
+
=head1 DESCRIPTION
These functions allow an B<ASN1_STRING> structure to be manipulated.
@@ -38,9 +50,25 @@ ASN1_STRING_dup() returns a copy of the structure I<a>.
ASN1_STRING_cmp() compares I<a> and I<b> returning 0 if the two
are identical. The string types and content are compared.
-ASN1_STRING_set() sets the data of string I<str> to the buffer
-I<data> or length I<len>. The supplied data is copied. If I<len>
-is -1 then the length is determined by strlen(data).
+ASN1_STRING_set() allocates memory for string I<str> to hold I<len>
+bytes of data. Any previously allocated memory owned by I<str> will be
+freed or re-used. If I<data> is not NULL, I<len> bytes are copied
+from the memory pointed to by I<data> to I<str>. If I<len> is -1 then
+the length is determined by strlen(data).
+
+ASN1_STRING_set_data() allocates memory for string I<str> to hold
+I<len> bytes of data. Any previously allocated memory owned by I<str>
+will be freed or re-used. If I<data> is not NULL, I<len> bytes are
+copied from the memory pointed to by I<data> to I<str>. It is an error
+to use this function on a string of type B<ASN1_BIT_STRING>.
+
+ASN1_STRING_set_string() allocates memory for the string I<str> and makes
+a copy of the characters from I<cstring>. Any previously
+allocated memory owned by I<str> will be freed or re-used. I<cstring>
+must point to a valid NUL-terminated C string, and must not be
+NULL. The terminating NUL byte is not included in the data copied into
+I<str>. It is an error to use this function on a string of type
+B<ASN1_BIT_STRING>.
ASN1_STRING_type() returns the type of I<x>, using standard constants
such as B<V_ASN1_OCTET_STRING>.
@@ -70,8 +98,9 @@ actual string type itself: for example for an IA5String the data will
be ASCII, for a BMPString two bytes per character in big endian
format, and for a UTF8String it will be in UTF8 format.
-Similar care should be take to ensure the data is in the correct format
-when calling ASN1_STRING_set().
+Similar care should be taken to ensure the data is in the correct
+format when calling ASN1_STRING_set(), ASN1_STRING_set_data(), or
+ASN1_STRING_set_string().
=head1 RETURN VALUES
@@ -86,7 +115,8 @@ error occurred.
ASN1_STRING_cmp() returns an integer greater than, equal to, or less than 0,
according to whether I<a> is greater than, equal to, or less than I<b>.
-ASN1_STRING_set() returns 1 on success or 0 on error.
+ASN1_STRING_set(), ASN1_STRING_set_data(), and
+ASN1_STRING_set_string() return 1 on success or 0 on error or failure.
ASN1_STRING_type() returns the type of I<x>.
@@ -97,6 +127,11 @@ negative value if an error occurred.
L<ERR_get_error(3)>
+=head1 HISTORY
+
+ASN1_STRING_set_data(), ASN1_STRING_set_string(), and ASN1_STRING_length_ex()
+were added in OpenSSL 4.1.
+
=head1 COPYRIGHT
Copyright 2002-2020 The OpenSSL Project Authors. All Rights Reserved.
diff --git a/include/openssl/asn1.h.in b/include/openssl/asn1.h.in
index a7291738ff..440a6b3766 100644
--- a/include/openssl/asn1.h.in
+++ b/include/openssl/asn1.h.in
@@ -540,9 +540,16 @@ 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,
* make its data void *
*/
+#if !defined(OPENSSL_NO_DEPRECATED_4_1)
+OSSL_DEPRECATEDIN_4_1_FOR(" Use ASN1_STRING_set_data() or ASN1_STRING_set_string() instead.")
int ASN1_STRING_set(ASN1_STRING *str, const void *data, int len);
-void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len);
+OSSL_DEPRECATEDIN_4_1_FOR(" Use ASN1_STRING_length_ex() instead.")
int ASN1_STRING_length(const ASN1_STRING *x);
+#endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */
+void ASN1_STRING_set0(ASN1_STRING *str, void *data, int len);
+int ASN1_STRING_set_data(ASN1_STRING *str, const uint8_t *data, size_t len);
+int ASN1_STRING_set_string(ASN1_STRING *str, const char *cstring);
+size_t ASN1_STRING_length_ex(const ASN1_STRING *x);
#ifndef OPENSSL_NO_DEPRECATED_3_0
OSSL_DEPRECATEDIN_3_0 void ASN1_STRING_length_set(ASN1_STRING *x, int n);
#endif
diff --git a/test/asn1_string_test.c b/test/asn1_string_test.c
index 6edc7619f7..24d679444b 100644
--- a/test/asn1_string_test.c
+++ b/test/asn1_string_test.c
@@ -390,6 +390,7 @@ asn1_bit_string_set1_test(int idx)
}
static int
+
asn1_string_new_not_owned_test(void)
{
int success = 0;
@@ -410,13 +411,13 @@ asn1_string_new_not_owned_test(void)
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"))))
+ if (!TEST_true(ASN1_STRING_set_string(tmp, "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")))
+ if (!TEST_size_t_eq(ASN1_STRING_length_ex(tmp), strlen("muppet")))
goto err;
if (!TEST_mem_eq(ASN1_STRING_get0_data(tmp), strlen("muppet"), "muppet", strlen("muppet")))
@@ -436,13 +437,13 @@ asn1_string_new_not_owned_test(void)
if (!TEST_mem_eq(data, sizeof(data), data2, sizeof(data2)))
goto err;
- if (!TEST_int_eq(ASN1_STRING_length(tmp), 4))
+ if (!TEST_size_t_eq(ASN1_STRING_length_ex(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));
+ memset((uint8_t *)ASN1_STRING_get0_data(tmp), 'z', ASN1_STRING_length_ex(tmp));
if (!TEST_mem_eq(data, sizeof(data), data2, sizeof(data2)))
goto err;
@@ -474,10 +475,88 @@ err:
return success;
}
+static int
+asn1_string_set_data_test(void)
+{
+ int success = 0;
+ ASN1_STRING *str = NULL;
+ const uint8_t *data;
+
+ if (!TEST_ptr(str = ASN1_STRING_new()))
+ goto err;
+
+ if (!TEST_false(ASN1_STRING_set_data(str, (uint8_t *)"hoobla", -1)))
+ goto err;
+
+ if (!TEST_false(ASN1_STRING_set_data(str, (uint8_t *)"hoobla", (size_t)INT_MAX + 1)))
+ goto err;
+
+ if (!TEST_true(ASN1_STRING_set_data(str, NULL, 10)))
+ goto err;
+
+ if (!TEST_true(ASN1_STRING_set_data(str, (uint8_t *)"hoobla", strlen("hoobla"))))
+ goto err;
+
+ data = ASN1_STRING_get0_data(str);
+
+ if (!TEST_size_t_eq(ASN1_STRING_length_ex(str), 6))
+ goto err;
+
+ if (!TEST_int_eq(memcmp("hoobla", data, strlen("hoobla")), 0))
+ goto err;
+
+ if (!TEST_true(ASN1_STRING_set_data(str, (uint8_t *)"hoobla", strlen("hoobla") + 1)))
+ goto err;
+
+ data = ASN1_STRING_get0_data(str);
+
+ if (!TEST_size_t_eq(ASN1_STRING_length_ex(str), 7))
+ goto err;
+
+ if (!TEST_int_eq(strcmp("hoobla", (char *)data), 0))
+ goto err;
+
+ success = 1;
+
+err:
+ ASN1_STRING_free(str);
+ return success;
+}
+
+static int
+asn1_string_set_string_test(void)
+{
+ int success = 0;
+ ASN1_STRING *str = NULL;
+
+ if (!TEST_ptr(str = ASN1_STRING_new()))
+ goto err;
+
+ if (!TEST_true(ASN1_STRING_set_string(str, "foo")))
+ goto err;
+
+ if (!TEST_size_t_eq(ASN1_STRING_length_ex(str), 3))
+ goto err;
+
+ if (!TEST_true(ASN1_STRING_set_string(str, "hoob\0la")))
+ goto err;
+
+ if (!TEST_size_t_eq(ASN1_STRING_length_ex(str), 4))
+ goto err;
+
+ success = 1;
+
+err:
+ ASN1_STRING_free(str);
+ 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);
+ ADD_TEST(asn1_string_set_data_test);
+ ADD_TEST(asn1_string_set_string_test);
return 1;
}
diff --git a/util/libcrypto.num b/util/libcrypto.num
index a51b72cf93..c7b7777cc4 100644
--- a/util/libcrypto.num
+++ b/util/libcrypto.num
@@ -2571,9 +2571,9 @@ ASN1_STRING_copy 2569 4_0_0 EXIST::FUNCTION:
ASN1_STRING_dup 2570 4_0_0 EXIST::FUNCTION:
ASN1_STRING_type_new 2571 4_0_0 EXIST::FUNCTION:
ASN1_STRING_cmp 2572 4_0_0 EXIST::FUNCTION:
-ASN1_STRING_set 2573 4_0_0 EXIST::FUNCTION:
+ASN1_STRING_set 2573 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_1
ASN1_STRING_set0 2574 4_0_0 EXIST::FUNCTION:
-ASN1_STRING_length 2575 4_0_0 EXIST::FUNCTION:
+ASN1_STRING_length 2575 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_1
ASN1_STRING_length_set 2576 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_3_0
ASN1_STRING_type 2577 4_0_0 EXIST::FUNCTION:
ASN1_STRING_get0_data 2578 4_0_0 EXIST::FUNCTION:
@@ -5724,3 +5724,6 @@ OPENSSL_sk_set_copy_thunks ? 4_1_0 EXIST::FUNCTION:
ASN1_STRING_new_not_owned ? 4_1_0 EXIST::FUNCTION:
EVP_KDF_CTX_get0_kdf ? 4_1_0 EXIST::FUNCTION:
EVP_KDF_CTX_get1_kdf ? 4_1_0 EXIST::FUNCTION:
+ASN1_STRING_set_data ? 4_1_0 EXIST::FUNCTION:
+ASN1_STRING_set_string ? 4_1_0 EXIST::FUNCTION:
+ASN1_STRING_length_ex ? 4_1_0 EXIST::FUNCTION: