Deprecate UTF8_putc/getc() and use uint32_t for unicode Unicode codepoints fit in 32 bits so we can use uint32_t instead of unsigned long. Also use OPENSSL_load_XX_be instead of manual shifting. Reviewed-by: Viktor Dukhovni <viktor@openssl.org> Reviewed-by: Norbert Pocs <norbertp@openssl.org> MergeDate: Mon May 18 11:02:37 2026 (Merged from https://github.com/openssl/openssl/pull/30967)
diff --git a/CHANGES.md b/CHANGES.md index cb6771d..75bc601 100644 --- a/CHANGES.md +++ b/CHANGES.md
@@ -142,6 +142,12 @@ *William McCormack* + * The undocumented public functions `UTF8_putc()` and `UTF8_getc()` + were deprecated, and their functionality moved internal to the + library. No public replacement is planned. + + *Bob Beck* + * Added IKEV2 KDF (EVP_KDF-IKEV2KDF) implementation. *Helen Zhang*
diff --git a/crypto/asn1/a_mbstr.c b/crypto/asn1/a_mbstr.c index 05b8ac8..89ba832 100644 --- a/crypto/asn1/a_mbstr.c +++ b/crypto/asn1/a_mbstr.c
@@ -12,19 +12,20 @@ #include "internal/cryptlib.h" #include "internal/unicode.h" #include <openssl/asn1.h> +#include <openssl/byteorder.h> #include <crypto/asn1.h> static int traverse_string(const unsigned char *p, int len, int inform, - int (*rfunc)(unsigned long value, void *in), + int (*rfunc)(uint32_t value, void *in), void *arg); -static int in_utf8(unsigned long value, void *arg); -static int out_utf8(unsigned long value, void *arg); -static int type_str(unsigned long value, void *arg); -static int cpy_asc(unsigned long value, void *arg); -static int cpy_bmp(unsigned long value, void *arg); -static int cpy_univ(unsigned long value, void *arg); -static int cpy_utf8(unsigned long value, void *arg); +static int in_utf8(uint32_t value, void *arg); +static int out_utf8(uint32_t value, void *arg); +static int type_str(uint32_t value, void *arg); +static int cpy_asc(uint32_t value, void *arg); +static int cpy_bmp(uint32_t value, void *arg); +static int cpy_univ(uint32_t value, void *arg); +static int cpy_utf8(uint32_t value, void *arg); /* * These functions take a string in UTF8, ASCII or multibyte form and a mask @@ -42,7 +43,7 @@ } int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len, - int inform, unsigned long mask, + int inform, unsigned long mask_in, long minsize, long maxsize) { int str_type; @@ -52,7 +53,8 @@ ASN1_STRING *dest; unsigned char *p; int nchar; - int (*cpyfunc)(unsigned long, void *) = NULL; + uint32_t mask = (uint32_t)mask_in; + int (*cpyfunc)(uint32_t, void *) = NULL; if (len == -1) { size_t len_s = strlen((const char *)in); @@ -224,27 +226,25 @@ */ static int traverse_string(const unsigned char *p, int len, int inform, - int (*rfunc)(unsigned long value, void *in), + int (*rfunc)(uint32_t value, void *in), void *arg) { - unsigned long value; + uint32_t value; int ret; while (len) { if (inform == MBSTRING_ASC) { value = *p++; len--; } else if (inform == MBSTRING_BMP) { - value = *p++ << 8; - value |= *p++; + uint16_t tmp; + p = OPENSSL_load_u16_be(&tmp, p); + value = tmp; len -= 2; } else if (inform == MBSTRING_UNIV) { - value = ((unsigned long)*p++) << 24; - value |= ((unsigned long)*p++) << 16; - value |= *p++ << 8; - value |= *p++; + p = OPENSSL_load_u32_be(&value, p); len -= 4; } else { - ret = UTF8_getc(p, len, &value); + ret = ossl_utf8_getc_internal(p, len, &value); if (ret < 0) return -1; len -= ret; @@ -263,7 +263,7 @@ /* Just count number of characters */ -static int in_utf8(unsigned long value, void *arg) +static int in_utf8(uint32_t value, void *arg) { int *nchar; @@ -276,11 +276,11 @@ /* Determine size of output as a UTF8 String */ -static int out_utf8(unsigned long value, void *arg) +static int out_utf8(uint32_t value, void *arg) { int *outlen, len; - len = UTF8_putc(NULL, -1, value); + len = ossl_utf8_putc_internal(NULL, -1, value); if (len <= 0) return len; outlen = arg; @@ -293,10 +293,10 @@ * "mask". */ -static int type_str(unsigned long value, void *arg) +static int type_str(uint32_t value, void *arg) { - unsigned long usable_types = *((unsigned long *)arg); - unsigned long types = usable_types; + uint32_t usable_types = *((uint32_t *)arg); + uint32_t types = usable_types; const int native = value > INT_MAX ? INT_MAX : ossl_fromascii(value); /* @@ -332,13 +332,13 @@ types &= ~B_ASN1_UTF8STRING; if (!types) return -1; - *((unsigned long *)arg) = types; + *((uint32_t *)arg) = types; return 1; } /* Copy one byte per character ASCII like strings */ -static int cpy_asc(unsigned long value, void *arg) +static int cpy_asc(uint32_t value, void *arg) { unsigned char **p, *q; p = arg; @@ -350,7 +350,7 @@ /* Copy two byte per character BMPStrings */ -static int cpy_bmp(unsigned long value, void *arg) +static int cpy_bmp(uint32_t value, void *arg) { unsigned char **p, *q; p = arg; @@ -363,7 +363,7 @@ /* Copy four byte per character UniversalStrings */ -static int cpy_univ(unsigned long value, void *arg) +static int cpy_univ(uint32_t value, void *arg) { unsigned char **p, *q; p = arg; @@ -378,13 +378,13 @@ /* Copy to a UTF8String */ -static int cpy_utf8(unsigned long value, void *arg) +static int cpy_utf8(uint32_t value, void *arg) { unsigned char **p; int ret; p = arg; /* We already know there is enough room so pass 0xff as the length */ - ret = UTF8_putc(*p, 0xff, value); + ret = ossl_utf8_putc_internal(*p, 0xff, value); if (ret < 0) return ret; *p += ret;
diff --git a/crypto/asn1/a_strex.c b/crypto/asn1/a_strex.c index f36f526..e488c87 100644 --- a/crypto/asn1/a_strex.c +++ b/crypto/asn1/a_strex.c
@@ -13,9 +13,11 @@ #include "internal/sizes.h" #include "internal/unicode.h" #include "crypto/asn1.h" +#include <openssl/byteorder.h> #include <openssl/crypto.h> #include <openssl/x509.h> #include <openssl/asn1.h> +#include <inttypes.h> #include "charmap.h" @@ -57,11 +59,11 @@ /* * This function handles display of strings, one character at a time. It is - * passed an unsigned long for each character because it could come from 2 or + * passed a uint32_t for each character because it could come from 2 or * even 4 byte forms. */ -static int do_esc_char(unsigned long c, unsigned short flags, char *do_quotes, +static int do_esc_char(uint32_t c, unsigned short flags, char *do_quotes, char_io *io_ch, void *arg) { unsigned short chflgs; @@ -71,13 +73,13 @@ if (c > UNICODE_MAX) return -1; if (c > 0xffff) { - BIO_snprintf(tmphex, sizeof(tmphex), "\\W%08lX", c); + BIO_snprintf(tmphex, sizeof(tmphex), "\\W%08" PRIX32, c); if (!io_ch(arg, tmphex, 10)) return -1; return 10; } if (c > 0xff) { - BIO_snprintf(tmphex, sizeof(tmphex), "\\U%04lX", c); + BIO_snprintf(tmphex, sizeof(tmphex), "\\U%04" PRIX32, c); if (!io_ch(arg, tmphex, 6)) return -1; return 6; @@ -131,14 +133,14 @@ * appropriate. */ -static int do_buf(unsigned char *buf, int buflen, +static int do_buf(const unsigned char *buf, int buflen, int type, unsigned short flags, char *quotes, char_io *io_ch, void *arg) { int i, outlen, len, charwidth; unsigned short orflags; - unsigned char *p, *q; - unsigned long c; + const unsigned char *p, *q; + uint32_t c; p = buf; q = buf + buflen; @@ -163,6 +165,8 @@ } while (p != q) { + uint16_t tmp; + if (p == buf && flags & ASN1_STRFLGS_ESC_2253) orflags = CHARTYPE_FIRST_ESC_2253; else @@ -170,15 +174,12 @@ switch (charwidth) { case 4: - c = ((unsigned long)*p++) << 24; - c |= ((unsigned long)*p++) << 16; - c |= ((unsigned long)*p++) << 8; - c |= *p++; + p = OPENSSL_load_u32_be(&c, p); break; case 2: - c = ((unsigned long)*p++) << 8; - c |= *p++; + p = OPENSSL_load_u16_be(&tmp, p); + c = tmp; break; case 1: @@ -186,7 +187,7 @@ break; case 0: - i = UTF8_getc(p, buflen, &c); + i = ossl_utf8_getc_internal(p, buflen, &c); if (i < 0) return -1; /* Invalid UTF8String */ buflen -= i; @@ -199,7 +200,7 @@ orflags = CHARTYPE_LAST_ESC_2253; if (type & BUF_TYPE_CONVUTF8) { unsigned char utfbuf[6]; - int utflen = UTF8_putc(utfbuf, sizeof(utfbuf), c); + int utflen = ossl_utf8_putc_internal(utfbuf, sizeof(utfbuf), c); if (utflen < 0) return -1; /* error happened with UTF8 */
diff --git a/crypto/asn1/a_utf8.c b/crypto/asn1/a_utf8.c index fb5999f..76f5ccb 100644 --- a/crypto/asn1/a_utf8.c +++ b/crypto/asn1/a_utf8.c
@@ -11,6 +11,7 @@ #include "internal/cryptlib.h" #include "internal/unicode.h" #include <openssl/asn1.h> +#include <crypto/asn1.h> /* UTF8 utilities */ @@ -25,10 +26,10 @@ * -4 = character encoded incorrectly (not minimal length). */ -int UTF8_getc(const unsigned char *str, int len, unsigned long *val) +int ossl_utf8_getc_internal(const unsigned char *str, int len, uint32_t *val) { const unsigned char *p; - unsigned long value; + uint32_t value; int ret; if (len <= 0) return 0; @@ -82,6 +83,21 @@ return ret; } +#if !defined(OPENSSL_NO_DEPRECATED_4_1) +int UTF8_getc(const unsigned char *str, int len, unsigned long *val) +{ + uint32_t value = 0; + int ret; + + ret = ossl_utf8_getc_internal(str, len, &value); + + if (ret) + *val = (unsigned long)value; + + return ret; +} +#endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */ + /* * This takes a character 'value' and writes the UTF8 encoded value in 'str' * where 'str' is a buffer containing 'len' characters. Returns the number of @@ -90,7 +106,7 @@ * characters. It will need at most 4 characters. */ -int UTF8_putc(unsigned char *str, int len, unsigned long value) +int ossl_utf8_putc_internal(unsigned char *str, int len, uint32_t value) { if (!str) len = 4; /* Maximum we will need */ @@ -135,3 +151,10 @@ } return -2; } + +#if !defined(OPENSSL_NO_DEPRECATED_4_1) +int UTF8_putc(unsigned char *str, int len, unsigned long value) +{ + return ossl_utf8_putc_internal(str, len, (uint32_t)value); +} +#endif
diff --git a/crypto/pkcs12/p12_utl.c b/crypto/pkcs12/p12_utl.c index 0b130fc..c864c9c 100644 --- a/crypto/pkcs12/p12_utl.c +++ b/crypto/pkcs12/p12_utl.c
@@ -12,6 +12,7 @@ #include <openssl/pkcs12.h> #include "p12_local.h" #include "crypto/pkcs7/pk7_local.h" +#include <crypto/asn1.h> /* Cheap and nasty Unicode stuff */ @@ -79,13 +80,14 @@ { int ulen, i, j; unsigned char *unitmp, *ret; - unsigned long utf32chr = 0; + uint32_t utf32chr = 0; if (asclen == -1) asclen = (int)strlen(asc); for (ulen = 0, i = 0; i < asclen; i += j) { - j = UTF8_getc((const unsigned char *)asc + i, asclen - i, &utf32chr); + j = ossl_utf8_getc_internal((const unsigned char *)asc + i, asclen - i, + &utf32chr); /* * Following condition is somewhat opportunistic is sense that @@ -121,7 +123,8 @@ return NULL; /* re-run the loop writing down UTF-16 characters in big-endian order */ for (unitmp = ret, i = 0; i < asclen; i += j) { - j = UTF8_getc((const unsigned char *)asc + i, asclen - i, &utf32chr); + j = ossl_utf8_getc_internal((const unsigned char *)asc + i, asclen - i, + &utf32chr); if (utf32chr >= 0x10000) { /* pair if UTF-16 characters */ unsigned int hi, lo; @@ -149,7 +152,7 @@ static int bmp_to_utf8(char *str, const unsigned char *utf16, int len) { - unsigned long utf32chr; + uint32_t utf32chr; if (len == 0) return 0; @@ -175,9 +178,8 @@ utf32chr += 0x10000; } - return UTF8_putc((unsigned char *)str, 4, utf32chr); + return ossl_utf8_putc_internal((unsigned char *)str, 4, utf32chr); } - char *OPENSSL_uni2utf8(const unsigned char *uni, int unilen) { int asclen, i, j;
diff --git a/include/crypto/asn1.h b/include/crypto/asn1.h index 6f969ea..f7dc785 100644 --- a/include/crypto/asn1.h +++ b/include/crypto/asn1.h
@@ -192,4 +192,7 @@ ASN1_TIME *ossl_asn1_time_from_tm(ASN1_TIME *s, struct tm *ts, int type); +int ossl_utf8_getc_internal(const unsigned char *str, int len, uint32_t *val); +int ossl_utf8_putc_internal(unsigned char *str, int len, uint32_t value); + #endif /* ndef OSSL_CRYPTO_ASN1_H */
diff --git a/include/internal/unicode.h b/include/internal/unicode.h index d1dae6d..f09bfc7 100644 --- a/include/internal/unicode.h +++ b/include/internal/unicode.h
@@ -12,11 +12,12 @@ #pragma once #include <openssl/e_os2.h> +#include <stdint.h> typedef enum { - SURROGATE_MIN = 0xd800UL, - SURROGATE_MAX = 0xdfffUL, - UNICODE_MAX = 0x10ffffUL, + SURROGATE_MIN = UINT32_C(0xd800), + SURROGATE_MAX = UINT32_C(0xdfff), + UNICODE_MAX = UINT32_C(0x10ffff), UNICODE_LIMIT } UNICODE_CONSTANTS;
diff --git a/include/openssl/asn1.h.in b/include/openssl/asn1.h.in index 7beef17..cce5670 100644 --- a/include/openssl/asn1.h.in +++ b/include/openssl/asn1.h.in
@@ -618,8 +618,10 @@ DECLARE_ASN1_FUNCTIONS(ASN1_NULL) DECLARE_ASN1_FUNCTIONS(ASN1_BMPSTRING) -int UTF8_getc(const unsigned char *str, int len, unsigned long *val); -int UTF8_putc(unsigned char *str, int len, unsigned long value); +#if !defined(OPENSSL_NO_DEPRECATED_4_1) +OSSL_DEPRECATEDIN_4_1 int UTF8_getc(const unsigned char *str, int len, unsigned long *val); +OSSL_DEPRECATEDIN_4_1 int UTF8_putc(unsigned char *str, int len, unsigned long value); +#endif /* !defined(OPENSSL_NO_DEPRECATED_4_1) */ /* clang-format off */ {-
diff --git a/test/asn1_internal_test.c b/test/asn1_internal_test.c index 83b5a2c..8edea7c 100644 --- a/test/asn1_internal_test.c +++ b/test/asn1_internal_test.c
@@ -195,9 +195,9 @@ static int test_invalid_utf8(void) { const unsigned char inv_utf8[] = "\xF4\x90\x80\x80"; - unsigned long val; + uint32_t val; - if (!TEST_int_lt(UTF8_getc(inv_utf8, sizeof(inv_utf8), &val), 0)) + if (!TEST_int_lt(ossl_utf8_getc_internal(inv_utf8, sizeof(inv_utf8), &val), 0)) return 0; return 1; }
diff --git a/util/libcrypto.num b/util/libcrypto.num index 90c81f1..dbaac8b 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num
@@ -2646,8 +2646,8 @@ ASN1_BMPSTRING_free 2645 4_0_0 EXIST::FUNCTION: ASN1_BMPSTRING_new 2646 4_0_0 EXIST::FUNCTION: ASN1_BMPSTRING_it 2647 4_0_0 EXIST::FUNCTION: -UTF8_getc 2648 4_0_0 EXIST::FUNCTION: -UTF8_putc 2649 4_0_0 EXIST::FUNCTION: +UTF8_getc 2648 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_1 +UTF8_putc 2649 4_0_0 EXIST::FUNCTION:DEPRECATEDIN_4_1 d2i_ASN1_PRINTABLE 2650 4_0_0 EXIST::FUNCTION: i2d_ASN1_PRINTABLE 2651 4_0_0 EXIST::FUNCTION: ASN1_PRINTABLE_free 2652 4_0_0 EXIST::FUNCTION: