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: violation of the Basic Encoding Rules

(BER);

 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"))