ASN1_ITEM_D2I(3)

πŸ“¦ version 4.4.0, in alpine 3.24, arch rolling

πŸ’… ASN1_item_d2i(3) β€” decode and encode ASN.1 objects

okay so welcome to the ASN1_ITEM_D2I(3) page, bestie β€” this is the whole squad that flips ASN.1 values between BER bytes and internal C structures, and honestly it’s giving β€œglamorous but be careful.” ✨

🌸 NAME 🌸

okay so the cast of this page is: ASN1_item_d2i, ASN1_item_d2i_bio, ASN1_item_d2i_fp, d2i_ASN1_TYPE, ASN1_item_i2d, ASN1_item_i2d_bio, ASN1_item_i2d_fp, i2d_ASN1_TYPE, ASN1_item_dup, and ASN1_item_print β€” their whole thing is decode and encode ASN.1 objects. periodt. πŸ’–

✨ SYNOPSIS ✨

okay so first things first: the library is libcrypto, and the include is exactly this:

library β€œlibcrypto” #include <openssl/asn1.h>

and here are the prototypes, bestie β€” copy them exactly, I’m not playing:

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 πŸ¦‹

okay so the whole vibe: these functions convert ASN.1 values from their BER encoding to internal C structures (that’s the d2i direction) and vice versa (i2d). Unlike the C structures which contain pointers to sub-objects, BER is a serialized encoding β€” suitable for transfer over the network and for storage in a file. slay. πŸ’…

  • 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 successful, *der_in is advanced to the byte following the parsed data.
  • If decoding succeeds and val_out or *val_out is NULL, a new object is allocated.
  • If decoding succeeds and *val_out is not NULL, it is assumed to point to a valid populated object and an attempt is made to reuse it. It must not be an empty structure such as one returned by ASN1_item_new(3) or by one of the various type-specific *_new() functions. This β€œreuse” capability is present for backward compatibility, but its use is strongly discouraged; see the β€œBUGS” section below.
🚨 bestie, the reuse thing is lowkey a trap. it’s there for backward compatibility, but it is strongly discouraged. do not make it your main character.
  • ASN1_item_d2i_bio() and ASN1_item_d2i_fp() β€” similar to ASN1_item_d2i() except that they read from a BIO or FILE, respectively.
  • d2i_ASN1_TYPE() β€” similar to ASN1_item_d2i() except that it does not require a desired type to be specified by the user, but instead returns an ASN1_TYPE wrapper object containing both the type and the value found in the input.
  • ASN1_item_i2d() β€” encodes the object pointed to by val_in into DER format.
  • If *der_out is not NULL, it writes the DER-encoded data to the buffer at *der_out and increments it to point after the data just written. In this case, it is the responsibility of the user to make sure that the buffer pointed to by *der_out is long enough, such that no buffer overflow can occur.
  • If *der_out is NULL, memory is allocated for a buffer, and *der_out is not incremented, but points to the start of the data just written.
  • If der_out is NULL, the encoded bytes are not written anywhere but discarded. For val_in objects of variable encoding size, this is sometimes used to first find the number of bytes that will be written. Then, a sufficient amount of memory is allocated before calling ASN1_item_i2d() again. This explicit double-call technique is often not needed because the auto-allocation technique described in the previous paragraph can be used.
  • ASN1_item_i2d_bio() and ASN1_item_i2d_fp() β€” similar to ASN1_item_i2d() except that they write to a BIO or FILE, respectively.
  • i2d_ASN1_TYPE() β€” similar to ASN1_item_i2d() except that the type and the value are not provided separately, but 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().

πŸŽ€ 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. In addition, if val_out is not NULL, the pointer is also written to *val_out. If an error occurs, NULL is returned.
  • 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.
  • ASN1_item_dup() returns the new ASN1_VALUE object or NULL if an error occurs.

🌈 EXAMPLES 🌈

Many type-specific wrapper functions exist. 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. iykyk. 🫢

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 πŸ‘‘

🌟 HISTORY 🌟

  • d2i_ASN1_TYPE() and i2d_ASN1_TYPE() first appeared in SSLeay 0.5.1 and have been available since OpenBSD 2.4.
  • 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.

πŸ’€ CAVEATS πŸ’€

  • If the type described by it fails to match the true type of val_in or *val_out, buffer overflows and segmentation faults are likely to occur. For more details about why the type ASN1_VALUE constitutes dangerous user interface design, see ASN1_item_new(3).
  • The encoded data is in binary form and may contain embedded NUL bytes. Functions such as strlen(3) will not return the correct length of the encoded data.
  • While the way that *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. πŸ™ˆ

Using a temporary pointer into the buffer is mandatory. A common mistake is to attempt to use a buffer directly as follows:

      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. 😭

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.

πŸ“ 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.
  • In some versions of OpenSSL, the β€œreuse” behaviour is broken such that some parts of the reused object may persist if they are not present in the new one.
  • 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. πŸ’€

πŸ’– that’s the whole page, bestie β€” source/version: Debian; date: June 8, 2025; manual name: ASN1_ITEM_D2I(3). bye! πŸŽ€

πŸ“¦ where this page comes from