-
Notifications
You must be signed in to change notification settings - Fork 5
Encoding
RASN.1 can only encode ASN.1 data to DER encoding. But, as DER encoding is a subset of BER one, RASN.1 produces outputs which may be read as BER data.
Let's take an ASN.1 grammar, for example:
seq ::= SEQUENCE {
present BOOLEAN,
value INTEGER OPTIONAL
}
First, we have to create objects to represent this structure:
present = RASN1::Types::Boolean.new
value = RASN1::Types::Integer.new(optional: true)
seq = RASN1::Types::Sequence.new(value: [present, value])Here, seq object is a SEQUENCE containing a BOOLEAN (present object) and an INTEGER
(value object). To encode, we have to set a value for each one:
present.value = true
value.value = 1_234_567Then, encoding is as simple as:
seq.to_der #=> "\x30\x08\x01\x01\xFF\x02\x03\x12\xD6\x87"In our model, value is declared as optional, which means value is encoded only if it
has a value:
value.value = nil
seq.to_der #=> "\x30\x03\x01\x01\xFF"ASN.1 permits to define a tag with a default value. If this default value is set, it will not be encoded in DER string:
value = RASN1::Types::Integer.new(default: 0)
value.value = 1
value.to_der #=> "x02\x01\x01"
value.value = 0
value.to_der #=> ""Tagged values may also be defined. Let's take an example from X.509 certificates. Such a certificate have a version number, defined this way:
TBSCertificate ::= SEQUENCE {
version [0] EXPLICIT Version DEFAULT v1,
...
}
Version ::= INTEGER { v1(0), v2(1), v3(2) }
To encode such a value, do:
VERSION = { 'v1' => 0, 'v2' => 1, 'v3' => 2 }
ver = RASN1::Types::Enumerated.new(enum: VERSION, explicit: 0, class: :context, default: 'v1', constructed: true)
tbs_cert = RASN1::Types::Sequence.new
tbs_cert.value << verHere, enumerated is defined from VERSION enum hash. It is an explicit tagged value
with tag 0, in context class. Its default value is 'v1' (or 0), and it is a CONSTRUCTED
tag (in X.509, all tagged values are constructed).
Thus defined, ver.to_der will give an empty string, as an absent version is equivalent
to v1 (default value).
To encode a v3, do:
ver.value = 'v3' # or 2
ver.to_der # => "\xA0\x03\x02\x01\x02"Here, our contructed tag is A0, where 0 is our tag, and A means CONSTRUCTED type in
CONTEXT class. 03 is the length of the tagged value, and its value is 020102.
This value is also an ASN.1 tag : an INTEGER (02) consisting one 1 byte (01). Its value
is 02. So we have our version of value 2 (i.e. v3) in an explicit tagged value.
RASN1 also supports implicit tagged values: use implicit option.
A SEQUENCE OF or a SET OF is like an array, but this array may only contain one type of data. This type may be a basic type (BOOLEAN, INTEGER,...) or a user defined type (usually through a SEQUENCE).
These 2 types have same implementation but different tags. Next, I will only write about SEQUENCE OF, but SET OF works exactly the same way.
PRIMITIVE types are ASN.1 basic types but SEQUENCE, SET, SEQUENCE OF and SET OF.
Let's take a simple example: a sequence of integers, defined this way in ASN.1 notation:
Integers ::= SEQUENCE OF INTEGER
With RASN1, you can define it this way:
integers = RASN1::Types::SequenceOf.new(RASN1::Types::Integer)You may set SEQUENCE OF content this way:
integers << RASN1::Types::Integer.new(value: 1)
integers << 2 # a RASN1::Types::Integer is automatically inferred from Ruby integer
integers << 3
integers.to_der #=> "\x30\x09\x02\x01\x01\x02\x01\x02\x02\x01\x03"It is encoded as a SEQUENCE of three INTEGERS (yes, encoded as a SEQUENCE, and not a SEQUENCE OF, this is a part of ASN.1 magic!).
A SEQUENCE OF may contain CONSTRUCTED types (i.e SEQUENCE, SET, or even SEQUENCE OF and SET OF).
For this ASN.1 definition:
example ::= SEQUENCE OF MyType
MyType ::= SEQUENCE {
id INTEGER,
data OCTET STRING
}
We have this ruby code:
id = RASN1::Types::Integer.new
data = RASN1::Types::OctetString.new
my_type = RASN1::Types::Sequence.new(value: [id, data])
example = RASN1::Types::SequenceOf.new(my_type)Then, we have to add values to our SEQUENCE OF:
example << [0, 'abcd']
example << [1, 'qwerty']
example.to_der #=> "\x30\x18\x30\x09\x02\x01\x00\x04\x04abcd\x30\x0b\x02\x01\x01\x04\x06qwerty"Types constructors have multiple options, all defined in API documentation (see http://www.rubydoc.info/gems/rasn1/RASN1/Types/Base#initialize).
There is also a more powerful way to define ASN.1 grammar: RASN1 models.