1 Introduction to the ASN.1 Library🔗ℹ

This section provides a basic introduction to defining ASN.1 types and using the types to encode and decode data using DER.

> (require asn1)

ASN.1 has several familiar base types, such as INTEGER and IA5String. (IA5 is the same as ASCII—the 7-bit character set.)

Racket values are encoded as instances of an ASN.1 type using the asn1->bytes/DER function, which produces a bytestring (bytes?):

> (asn1->bytes/DER INTEGER 123456)

#"\2\3\1\342@"

> (asn1->bytes/DER INTEGER (expt 10 30))

#"\2\r\f\237,\234\320Ft\355\352@\0\0\0"

> (asn1->bytes/DER IA5String "I am the walrus.")

#"\26\20I am the walrus."

A DER bytestring is decoded according to an ASN.1 type to get a Racket value using the bytes->asn1/DER function:

> (bytes->asn1/DER INTEGER (asn1->bytes/DER INTEGER 123456))

123456

> (bytes->asn1/DER INTEGER (asn1->bytes/DER INTEGER (expt 10 30)))

1000000000000000000000000000000

> (bytes->asn1/DER IA5String (asn1->bytes/DER IA5String "I am the walrus."))

"I am the walrus."

Complex types are created using forms such as SEQUENCE, CHOICE, and SEQUENCE-OF. For example, here is an ASN.1 type for a sequence of integers:

> (define Integers (SEQUENCE-OF INTEGER))
> (asn1->bytes/DER Integers '(1 2 3 -1000))

#"0\r\2\1\1\2\1\2\2\1\3\2\2\374\30"

> (bytes->asn1/DER Integers (asn1->bytes/DER Integers '(1 2 3 -1000)))

'(1 2 3 -1000)

Unlike SEQUENCE-OF, SEQUENCE and CHOICE take multiple components, each labeled with a name and optionally a tagging directive.

Here is an ASN.1 type representing a three-dimensional point:

> (define Point (SEQUENCE [x INTEGER] [y INTEGER] [z INTEGER]))
> (asn1->bytes/DER Point (hasheq 'x 123 'y 456 'z 789))

#"0\v\2\1{\2\2\1\310\2\2\3\25"

And here’s one representing a reference to a person:

> (define Person (CHOICE [name IA5String] [number INTEGER]))
> (asn1->bytes/DER Person '(name "Jean"))

#"\26\4Jean"

> (asn1->bytes/DER Person '(number 24601))

#"\2\2`\31"

> (bytes->asn1/DER Person (asn1->bytes/DER Person '(number 24601)))

'(number 24601)

Sometimes components of a choice (and sometimes other structured types) must be given alternative tags because their default tags would not distinguish between them.

> (define Employee
    (CHOICE [name  #:implicit 0 IA5String]
            [title #:implicit 1 IA5String]))
> (asn1->bytes/DER Employee '(name "Ash"))

#"\200\3Ash"

> (asn1->bytes/DER Employee '(title "Boomstick Specialist"))

#"\201\24Boomstick Specialist"

Attempting to decode an ASN.1 value at a different type than it was encoded as usually results in an error:

> (bytes->asn1/DER INTEGER (asn1->bytes/DER IA5String "hello"))

bytes->asn1/DER: tag mismatch

  expected: universal 2 (INTEGER)

  decoded: universal 22 (IA5String)

> (bytes->asn1/DER Person (asn1->bytes/DER Employee '(name "Ash")))

bytes->asn1/DER: unknown variant for non-extensible CHOICE

  tag: context-specific 0

  type: '#<CHOICE: #<name: IA5String> #<number: INTEGER>>

If you don’t know the type of an ASN.1 encoding, you can decode it as the ANY type to see the frame structure without interpreting the primitive contents.

> (bytes->asn1/DER ANY
    (asn1->bytes/DER Point (hasheq 'x 123 'y 456 'z 789)))

(BER-frame

 'universal 16

 (list

  (BER-frame 'universal 2 #"{")

  (BER-frame 'universal 2 #"\1\310")

  (BER-frame 'universal 2 #"\3\25")))

> (bytes->asn1/DER ANY
    (asn1->bytes/DER Employee '(title "Boomstick Specialist")))

(BER-frame 'context-specific 1 #"Boomstick Specialist")

In this example, 'universal 16 is the tag for sequences, and 'universal 2 is the tag for integers.

The type ANY* is like ANY, but it additionally recognizes and translates standard tags:

> (bytes->asn1/DER ANY*
    (asn1->bytes/DER Point (hasheq 'x 123 'y 456 'z 789)))

'(sequence ((integer 123) (integer 456) (integer 789)))

> (bytes->asn1/DER ANY*
    (asn1->bytes/DER Employee '(title "Boomstick Specialist")))

(list 'any (BER-frame 'context-specific 1 #"Boomstick Specialist"))