ASN1_MBSTRING_COPY(3)
π ASN1_MBSTRING_COPY(3) π
okay so this is from the Library Functions Manual bestie, and it's literally all about copying a multibyte string into an ASN.1 string object. seven whole functions, one page, let's go π«Ά
β¨ NAME β¨
ASN1_mbstring_copy, ASN1_mbstring_ncopy, ASN1_STRING_set_by_NID, ASN1_STRING_set_default_mask, ASN1_STRING_set_default_mask_asc, ASN1_STRING_get_default_mask, ASN1_tag2bit β the whole squad whose job is to copy a multibyte string into an ASN.1 string object. that's the vibe. π¦
πΈ SYNOPSIS πΈ
library libcrypto
#include <openssl/asn1.h>
int
ASN1_mbstring_copy(ASN1_STRING **out, const unsigned char *in,
int inbytes, int inform, unsigned long mask);
int
ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in,
int inbytes, int inform, unsigned long mask, long minchars,
long maxchars);
ASN1_STRING *
ASN1_STRING_set_by_NID(ASN1_STRING **out, const unsigned char *in,
int inbytes, int inform, int nid);
void
ASN1_STRING_set_default_mask(unsigned long mask);
int
ASN1_STRING_set_default_mask_asc(const char *maskname);
unsigned long
ASN1_STRING_get_default_mask(void);
unsigned long
ASN1_tag2bit(int tag);
π DESCRIPTION π
so ASN1_mbstring_copy() takes inbytes bytes starting at in, treats them as a multibyte string, and copies the whole thing to *out β optionally swapping the encoding while it's at it. and if inbytes is negative? no worries babe, it just falls back to the strlen(3) of in instead. iconic behaviour honestly.
the inform argument is literally "hey what encoding is in in", and here's the tea:
MBSTRING_ASCβ ISO-Latin-1MBSTRING_BMPβ UTF-16MBSTRING_UNIVβ UTF-32MBSTRING_UTF8β UTF-8
then the bit mask is like, the set of ASN.1 string types you're willing to accept as output. here's the chart bestie:
B_ASN1_PRINTABLESTRINGβ gives youASN1_PRINTABLESTRINGβ default: yesB_ASN1_IA5STRINGβ gives youASN1_IA5STRINGβ default: noB_ASN1_T61STRINGβ gives youASN1_T61STRINGβ default: yesB_ASN1_BMPSTRINGβ gives youASN1_BMPSTRINGβ default: yesB_ASN1_UNIVERSALSTRINGβ gives youASN1_UNIVERSALSTRINGβ default: no- any other bit β gives you
ASN1_UTF8STRINGβ default: yes
the way it works: the first type from that table that's included in the mask argument and can actually represent in is the one that wins and becomes your output type. and that "default" column? that's just telling you whether the type counts as acceptable when mask has the special value 0. cute right π
also these bit mask constants each bundle up several of the bits above, so you don't have to spell them out:
B_ASN1_DIRECTORYSTRINGβ PRI yes, IA5 no, T61 yes, BMP yes, UNI yes, UTF8 yesDIRSTRING_TYPEβ PRI yes, IA5 no, T61 yes, BMP yes, UNI no, UTF8 yesPKCS9STRING_TYPEβ PRI yes, IA5 yes, T61 yes, BMP yes, UNI no, UTF8 yes
okay now the actual flow, pay attention π
- if
outisNULL, theninform,inbytes, andinget validated and the output type gets figured out and returned β but literally nothing is copied. it's giving window shopping. - otherwise, if
*outisNULL, a brand new output object of the output type is allocated, and a pointer to it is dropped into*out. - otherwise,
**outis used as the output object. any data already sitting in it gets freed, and its type gets changed to the output type. out with the old, in with the new π - finally,
inis copied into the output object, changing the character encoding ifinformdoesn't match whatever encoding the output type uses. slay.
ASN1_mbstring_ncopy() is the same deal, except the number of characters in in gets restricted to the range from minchars to maxchars, inclusive. and if maxchars is 0, there's no upper limit on characters at all β unlimited characters, no cap (well, no max cap π).
ASN1_STRING_set_by_NID() is also similar, but with these differences, and yes you need all of them:
- if
outisNULL, a new output object is allocated and returned instead of skipping the copying. so no window shopping here, we're actually buying. - if
nidhas a global string table entry that you can retrieve withASN1_STRING_TABLE_get(3), thenmask,minchars, andmaxcharsare taken from that string table entry. for some values ofnid, an additional global mask gets AND'ed into the mask before it's used. the default value of that global mask isB_ASN1_UTF8STRING. - if
nidhas no global string table entry, thenB_ASN1_PRINTABLESTRING | B_ASN1_T61STRING | B_ASN1_BMPSTRING | B_ASN1_UTF8STRINGis used instead of the mask from the table, and the global mask also gets AND'ed into it. bestie gets the fallback fit. - even though success and failure happen in the same situations, the return value is different.
ASN1_STRING_type(3)can be used to determine the type of the return value. iykyk.
ASN1_STRING_set_default_mask() sets the global mask used by ASN1_STRING_set_by_NID() to whatever you pass as the mask argument. simple as.
ASN1_STRING_set_default_mask_asc() sets the global mask like this:
"default"β anything"nombstr"β anything exceptB_ASN1_BMPSTRING | B_ASN1_UTF8STRING"pkix"β anything exceptB_ASN1_T61STRING"utf8only"βB_ASN1_UTF8STRING
and if the maskname argument starts with the substring "MASK:", then the rest of it gets interpreted as an unsigned long value using strtoul(3). sneaky but valid β¨
ASN1_tag2bit() translates ASN.1 data types to type bits, and here's the entire roster, no skipping:
V_ASN1_BIT_STRINGβB_ASN1_BIT_STRINGV_ASN1_BMPSTRINGβB_ASN1_BMPSTRINGV_ASN1_BOOLEANβ0V_ASN1_ENUMERATEDβB_ASN1_UNKNOWNV_ASN1_EOCβ0V_ASN1_EXTERNALβB_ASN1_UNKNOWNV_ASN1_GENERALIZEDTIMEβB_ASN1_GENERALIZEDTIMEV_ASN1_GENERALSTRINGβB_ASN1_GENERALSTRINGV_ASN1_GRAPHICSTRINGβB_ASN1_GRAPHICSTRINGV_ASN1_IA5STRINGβB_ASN1_IA5STRINGV_ASN1_INTEGERβ0V_ASN1_ISO64STRINGβB_ASN1_ISO64STRINGV_ASN1_NULLβ0V_ASN1_NUMERICSTRINGβB_ASN1_NUMERICSTRINGV_ASN1_OBJECTβ0V_ASN1_OBJECT_DESCRIPTORβB_ASN1_UNKNOWNV_ASN1_OCTET_STRINGβB_ASN1_OCTET_STRINGV_ASN1_PRINTABLESTRINGβB_ASN1_PRINTABLESTRINGV_ASN1_REALβB_ASN1_UNKNOWNV_ASN1_SEQUENCEβB_ASN1_SEQUENCEV_ASN1_SETβ0V_ASN1_T61STRINGβB_ASN1_T61STRINGV_ASN1_TELETEXSTRINGβB_ASN1_TELETEXSTRINGV_ASN1_UNDEFβ0V_ASN1_UNIVERSALSTRINGβB_ASN1_UNIVERSALSTRINGV_ASN1_UTCTIMEβB_ASN1_UTCTIMEV_ASN1_UTF8STRINGβB_ASN1_UTF8STRINGV_ASN1_VIDEOTEXSTRINGβB_ASN1_VIDEOTEXSTRINGV_ASN1_VISIBLESTRINGβB_ASN1_VISIBLESTRING11,13,14,15,29βB_ASN1_UNKNOWN- other (
< 0,> 30) β0
and in typical usage? the calling code calculates the bitwise AND of the return value and a mask describing the data types the calling code is willing to use. if the result of that AND operation is non-zero, the data type is adequate β and if it's zero, the calling code may need to raise an error. that's the whole vibe check right there π₯
π¦ RETURN VALUES π¦
ASN1_mbstring_copy() and ASN1_mbstring_ncopy() return the V_ASN1_* constant representing the output type, or -1 if:
informis invalid,inbytesorinis invalid for theinformencoding,incontains an UTF-16 surrogate, which is unsupported even for input using the UTF-16 encoding (yes, even then π),- or memory allocation fails.
ASN1_mbstring_ncopy() also returns -1 if in contains fewer than minchars or more than maxchars characters. length restrictions are real.
ASN1_STRING_set_by_NID() returns the new or changed ASN.1 string object, or NULL on failure.
ASN1_STRING_set_default_mask_asc() returns 1 if successful, or 0 if "MASK:" is not followed by a number, if the number is followed by a non-numeric character, or if the maskname is invalid.
ASN1_STRING_get_default_mask() returns the global mask. that's it, that's the whole function, and honestly? queen.
ASN1_tag2bit() returns a B_ASN1_* constant or 0.
π SEE ALSO π
ASN1_PRINTABLE_type(3), ASN1_STRING_new(3), ASN1_STRING_set(3), ASN1_STRING_TABLE_get(3), ASN1_UNIVERSALSTRING_to_string(3) β go read those too bestie, they're all connected π«Ά
β HISTORY β
ASN1_mbstring_copy(), ASN1_mbstring_ncopy(), ASN1_STRING_set_by_NID(), ASN1_STRING_set_default_mask(), ASN1_STRING_set_default_mask_asc(), and ASN1_STRING_get_default_mask() first appeared in OpenSSL 0.9.5 and have been available since OpenBSD 2.7.
ASN1_tag2bit() first appeared in OpenSSL 0.9.7 and has been available since OpenBSD 3.2. late bloomer, still iconic π
π BUGS π
if integer overflow occurs inASN1_STRING_set_default_mask_asc()while parsing a number following"MASK:", the function succeeds, essentially behaving in the same way as for"default". yes, it just quietly does a whole different thing. no cap, that's the bug.
passing"default"toASN1_STRING_set_default_mask_asc()does not restore the default mask. instead, passing"utf8only"does that. the way that is so counterintuitive π remember it, it'll save you.
okay that's the whole page diva π this baby comes to you courtesy of Debian, dated June 8, 2025 β go forth and copy your multibyte strings responsibly, mwah πβ¨πΈ
π¦ where this page comes from
- alpine 3.24: libressl-doc 4.3.1-r0 Β· custom
- arch rolling: libressl 4.4.0-1 Β· ISC AND custom:OpenSSL
- Copyright (c) 2021 Ingo Schwarze <schwarze@openbsd.org>
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms