ASN1_ITEM_D2I(3)
π ASN1_ITEM_D2I β decode & encode ASN.1 objects, bestie β¨
okay so this is the ASN1_ITEM_D2I(3) manual page and honestly? it's giving crypto energy. we're talking about converting ASN.1 values between their BER encoded form and internal C structures. the d2i functions decode (BER β C), the i2d functions encode (C β BER). and yeah, BER is a serialized encoding β perfect for yeeting data over the network or stashing it in a file. unlike C structs which are full of pointers to sub-objects, BER is just bytes. iconic. π¦
β¨ SYNOPSIS β¨
#include <openssl/asn1.h>
ASN1_VALUE *
ASN1_item_d2i(ASN1_VALUE **val_out, const unsigned char **der_in,
long length, const ASN1_ITEM *it);
void *
ASN1_item_d2i_bio(const ASN1_ITEM *it, BIO *in_bio, void *val_out);
void *
ASN1_item_d2i_fp(const ASN1_ITEM *it, FILE *in_fp, void *val_out);
ASN1_TYPE *
d2i_ASN1_TYPE(ASN1_TYPE **val_out, const unsigned char **der_in,
long length);
int
ASN1_item_i2d(ASN1_VALUE *val_in, unsigned char **der_out,
const ASN1_ITEM *it);
int
ASN1_item_i2d_bio(const ASN1_ITEM *it, BIO *out_bio, void *val_in);
int
ASN1_item_i2d_fp(const ASN1_ITEM *it, FILE *out_fp, void *val_in);
int
i2d_ASN1_TYPE(ASN1_TYPE *val_in, unsigned char **der_out);
void *
ASN1_item_dup(const ASN1_ITEM *it, void *val_in);
int
ASN1_item_print(BIO *out_bio, ASN1_VALUE *val_in, int indent,
const ASN1_ITEM *it, const ASN1_PCTX *pctx);
πΈ DESCRIPTION πΈ
so these functions are literally the translators between BER encoding and internal C structures. d2i = BER β C (decode). i2d = C β BER (encode). and unlike C structures that are full of pointers to sub-objects, BER is a serialized encoding β perfect for sending over the network and storing in files. no cap. π
ASN1_item_d2i() interprets *der_in as a DER- or BER-encoded byte array and decodes one value of type it represented by up to length bytes. if it slays (success), *der_in is advanced to the byte following the parsed data. main character energy, honestly. β¨
if decoding succeeds and val_out or *val_out is NULL, a new object is allocated. fresh out the box. π
if decoding succeeds and *val_out is not NULL, it's assumed to point to a valid populated object and an attempt is made to reuse it. but babes, it must not be an empty structure like one returned by ASN1_item_new(3) or one of those type-specific *_new() functions. this "reuse" capability is there for backward compatibility, but its use is strongly discouraged β see the "BUGS" section below. it's giving deprecated vibes fr fr. π
ASN1_item_d2i_bio() and ASN1_item_d2i_fp() are similar to ASN1_item_d2i() except they read from a BIO or FILE, respectively. different source, same slay. π«Ά
d2i_ASN1_TYPE() is similar to ASN1_item_d2i() except it doesn't require a desired type to be specified by the user. instead it returns an ASN1_TYPE wrapper object containing both the type and the value found in the input. surprise surprise, it figured it out itself. π₯
ASN1_item_i2d() encodes the object pointed to by val_in into DER format. get it, encode! π
- if
*der_outis not NULL, it writes the DER-encoded data to the buffer at*der_outand increments it to point after the data just written. in this case, it's your responsibility to make sure the buffer at*der_outis long enough so no buffer overflow can occur. please don't skip this. π - if
*der_outis NULL, memory is allocated for a buffer, and*der_outis not incremented β it points to the start of the data just written. - if
der_outis NULL, the encoded bytes are not written anywhere β they're discarded. forval_inobjects of variable encoding size, this is sometimes used to first find the number of bytes that will be written. then you allocate enough memory before callingASN1_item_i2d()again. this explicit double-call technique is often not needed because the auto-allocation technique from the previous paragraph exists. so you do you, bestie. π
ASN1_item_i2d_bio() and ASN1_item_i2d_fp() are similar to ASN1_item_i2d() except they write to a BIO or FILE, respectively. slay in different directions. π
i2d_ASN1_TYPE() is similar to ASN1_item_i2d() except the type and value aren't provided separately β they come in the form of a single ASN1_TYPE object.
ASN1_item_dup() creates a deep copy of val_in by calling ASN1_item_i2d() and ASN1_item_d2i(). it's a whole round trip. obsessed. π
π RETURN VALUES π
if successful, ASN1_item_d2i(), ASN1_item_d2i_bio(), ASN1_item_d2i_fp(), and d2i_ASN1_TYPE() return a pointer to the decoded ASN.1 value. and if val_out is not NULL, the pointer is also written to *val_out. if an error occurs, NULL is returned. sad girl hours. π
ASN1_item_i2d() and i2d_ASN1_TYPE() return the number of bytes written, or a negative value if an error occurs.
ASN1_item_i2d_bio() and ASN1_item_i2d_fp() return 1 for success or 0 for failure. periodt. β
ASN1_item_dup() returns the new ASN1_VALUE object or NULL if an error occurs.
π¦ EXAMPLES π¦
so many type-specific wrapper functions exist, and using those wrappers is recommended in application code because it restores part of the type safety that the low-level interfaces using ASN1_VALUE lack. use the wrappers, bestie. trust. π
for example, to allocate a buffer and write the DER encoding of an X509 object into it:
X509 *x;
unsigned char *buf;
int len;
buf = NULL;
len = i2d_X509(x, &buf);
if (len < 0)
/* error */
attempt to decode a buffer:
X509 *x;
unsigned char *buf;
const unsigned char *p;
int len;
/* Set up buf and len to point to the input buffer. */
p = buf;
x = d2i_X509(NULL, &p, len);
if (x == NULL)
/* error */
equivalent technique:
X509 *x;
unsigned char *buf;
const unsigned char *p;
int len;
/* Set up buf and len to point to the input buffer. */
p = buf;
x = NULL;
if (d2i_X509(&x, &p, len) == NULL)
/* error */
π SEE ALSO π
ASN1_get_object(3), ASN1_item_digest(3), ASN1_item_new(3), ASN1_item_pack(3), ASN1_item_sign(3), ASN1_item_verify(3), ASN1_TYPE_new(3)
like a little friend group of man pages π«Ά
π HISTORY π
d2i_ASN1_TYPE() and i2d_ASN1_TYPE() first appeared in SSLeay 0.5.1 and have been available since OpenBSD 2.4. ancient icons. ποΈ
ASN1_item_d2i(), ASN1_item_d2i_bio(), ASN1_item_d2i_fp(), ASN1_item_i2d(), ASN1_item_i2d_bio(), ASN1_item_i2d_fp(), and ASN1_item_dup() first appeared in OpenSSL 0.9.7 and have been available since OpenBSD 3.2.
ASN1_item_print() first appeared in OpenSSL 1.0.0 and has been available since OpenBSD 4.9. late bloomer but still iconic. πΈ
π CAVEATS π
if the type described byitfails to match the true type ofval_inor*val_out, buffer overflows and segmentation faults are likely to occur. fr fr. for more details about why the typeASN1_VALUEconstitutes dangerous user interface design, seeASN1_item_new(3). it's giving chaos. π
the encoded data is in binary form and may contain embedded NUL bytes. so functions like strlen(3) will not return the correct length of the encoded data. do not use strlen on this. i'm begging. π
while the way *der_in and *der_out are incremented after the operation supports the typical usage patterns of reading or writing one object after another, this behaviour can trap the unwary. it's a trap, bestie. β οΈ
using a temporary pointer into the buffer is mandatory. a common mistake is to attempt to use a buffer directly like this:
X509 *x;
unsigned char *buf;
int len;
len = i2d_X509(x, NULL);
buf = malloc(len);
i2d_X509(x, &buf);
/* do something with buf[] */
free(buf);
this code will result in buf apparently containing garbage because it was incremented during i2d_X509() to point after the data just written. also buf will no longer contain the pointer allocated by malloc(3) and the subsequent call to free(3) is likely to crash. not the vibe. π
another trap to avoid is misuse of the val_out argument:
X509 *x;
if (d2i_X509(&x, &p, len) == NULL)
/* error */
this will probably crash somewhere in d2i_X509() because x is uninitialized and an attempt will be made to interpret its invalid content as an X509 object, typically causing a segmentation violation. if x is set to NULL first, then this will not happen. set it to NULL. always. π«Ά
π BUGS π
if the "reuse" capability is used, a valid object is passed in via *val_out, and an error occurs, then the object is not freed and may be left in an invalid or inconsistent state. messy. π
in some versions of OpenSSL, the "reuse" behaviour is broken such that some parts of the reused object may persist if they aren't present in the new one. yikes. π
in many versions of OpenSSL, ASN1_item_i2d() will not return an error if mandatory fields are not initialized due to a programming error. in that case, the encoded structure may contain invalid data and some fields may be missing entirely, such that trying to parse it with ASN1_item_d2i() may fail. so like... check your fields, bestie. π₯
and that's the whole page, bestie! this was ASN1_ITEM_D2I(3) from the Debian build, dated May 1, 2023. go forth and encode responsibly. mwah! πβ¨ππ¦πΈπ
π«Άππ₯ππβππππ§π
π¦ where this page comes from
- opensuse-leap 16.0: libressl-devel-doc 4.1.0-bp160.1.1 Β· OpenSSL
- Copyright (c) 2016, 2021 Ingo Schwarze <schwarze@openbsd.org>
- Copyright (c) 2002, 2003, 2015 The OpenSSL Project. All rights reserved
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms