Module Smartcard::Gp::Asn1Ber
In: lib/smartcard/gp/asn1_ber.rb

Logic for encoding and decoding ASN.1-BER data as specified in X.690-0207.

Methods

Public Class methods

Decodes a sequence of TLVs (tag-length-value).

Returns an array with one element for each TLV in the sequence. See decode_tlv for the format of each array element.

[Source]

     # File lib/smartcard/gp/asn1_ber.rb, line 107
107:   def self.decode(data, offset = 0, length = data.length - offset)
108:     sequence = []
109:     loop do
110:       break if offset >= length
111:       offset, tlv = decode_tlv data, offset
112:       sequence << tlv
113:     end
114:     sequence
115:   end

Decodes a TLV length.

Args:

  data:: the array to decode from
  offset:: the position of the first byte containing the length

Returns the offset of the first byte after the length, and the length. The returned value might be +:indefinite+ if the encoding uses the indefinite length.

[Source]

    # File lib/smartcard/gp/asn1_ber.rb, line 51
51:   def self.decode_length(data, offset)
52:     return (offset + 1), data[offset] if (data[offset] & 0x80) == 0
53:     len_bytes = (data[offset] & 0x7F)
54:     return (offset + 1), :indefinite if len_bytes == 0
55:     length = 0
56:     len_bytes.times do
57:       offset += 1
58:       length = (length << 8) | data[offset]
59:     end
60:     return (offset + 1), length
61:   end

Decodes a TLV tag (the data type).

Args:

  data:: the array to decode from
  offset:: the position of the first byte containing the tag

Returns the offset of the first byte after the tag, and the tag information. Tag information is a hash with the following keys.

  :class:: the tag's class (symbol, named after X690-0207)
  :primitive:: if +false+, the tag's value is a sequence of TLVs
  :number:: the tag's number

[Source]

    # File lib/smartcard/gp/asn1_ber.rb, line 24
24:   def self.decode_tag(data, offset)
25:     class_bits = data[offset] >> 6 
26:     tag_class = [:universal, :application, :context, :private][class_bits]
27:     tag_primitive = (data[offset] & 0x20) == 0
28:     tag_number = (data[offset] & 0x1F)
29:     if tag_number == 0x1F
30:       tag_number = 0
31:       loop do      
32:         offset += 1
33:         tag_number <<= 7
34:         tag_number |= (data[offset] & 0x7F)
35:         break if (data[offset] & 0x80) == 0
36:       end
37:     end
38:     return (offset + 1), { :class => tag_class, :primitive => tag_primitive,
39:                            :number => tag_number }
40:   end

Decodes a TLV (tag-length-value).

Returns a hash that contains tag and value information. See decode_tag for the keys containing the tag information. Value information is contained in the :value: tag.

[Source]

     # File lib/smartcard/gp/asn1_ber.rb, line 94
 94:   def self.decode_tlv(data, offset)
 95:     offset, tag = decode_tag data, offset
 96:     offset, length = decode_length data, offset
 97:     offset, value = decode_value data, offset, length
 98:     
 99:     tag[:value] = tag[:primitive] ? map_value(value, tag) : decode(value)
100:     return offset, tag
101:   end

Decodes a TLV value.

Args:

  data:: the array to decode from
  offset:: the position of the first byte containing the length

Returns the offset of the first byte after the value, and the value.

[Source]

    # File lib/smartcard/gp/asn1_ber.rb, line 71
71:   def self.decode_value(data, offset, length)    
72:     return offset + length, data[offset, length] unless length == :indefinite
73:     
74:     length = 0
75:     loop do
76:       raise 'Unterminated data' if offset + length + 2 > data.length
77:       break if data[offset + length, 2] == [0, 0]
78:       length += 1
79:     end
80:     return (offset + length + 2), data[offset, length]
81:   end

Encodes a sequence of TLVs (tag-length-value).

Args:tlvs:: an array of hashes to be encoded as TLV

Returns an array of byte values.

[Source]

     # File lib/smartcard/gp/asn1_ber.rb, line 176
176:   def self.encode(tlvs)
177:     tlvs.map { |tlv| encode_tlv tlv }.flatten
178:   end

Encodes a TLV length (the length of the data).

Args:length:: the length to be encoded (number of :indefinite)

Returns an array of byte values.

[Source]

     # File lib/smartcard/gp/asn1_ber.rb, line 147
147:   def self.encode_length(length)
148:     return [0x80] if length == :indefinite
149:     return [length] if length < 0x80
150:     length_bytes = []
151:     while length > 0
152:       length_bytes << (length & 0xFF)
153:       length >>= 8
154:     end
155:     [0x80 | length_bytes.length] + length_bytes.reverse
156:   end

Encodes a TLV tag (the data type).

Args:

  tag:: a hash with the keys produced by decode_tag.

Returns an array of byte values.

[Source]

     # File lib/smartcard/gp/asn1_ber.rb, line 123
123:   def self.encode_tag(tag)
124:     tag_classes = { :universal => 0, :application => 1, :context => 2,
125:                     :private => 3 } 
126:     tag_lead = (tag_classes[tag[:class]] << 6) | (tag[:primitive] ? 0x00 : 0x20)
127:     return [tag_lead | tag[:number]] if tag[:number] < 0x1F
128:     
129:     number_bytes, number = [], tag[:number]
130:     first = true
131:     while number != 0
132:       byte = (number & 0x7F)
133:       number >>= 7
134:       byte |= 0x80 unless first
135:       first = false
136:       number_bytes << byte
137:     end
138:     [tag_lead | 0x1F] + number_bytes.reverse
139:   end

Encodes a TLV (tag-length-value).

Args:tlv:: hash with tag and value information, to be encoeded as TLV; see
      decode_tlv for the hash keys encoding the tag and value

Returns an array of byte values.

[Source]

     # File lib/smartcard/gp/asn1_ber.rb, line 165
165:   def self.encode_tlv(tlv)
166:     value = tlv[:primitive] ? tlv[:value] : encode(tlv[:value])
167:     [encode_tag(tlv), encode_length(value.length), value].flatten
168:   end

Maps a TLV value with a known tag to a Ruby data type.

[Source]

    # File lib/smartcard/gp/asn1_ber.rb, line 84
84:   def self.map_value(value, tag)
85:     # TODO(costan): map primitive types if necessary
86:     value
87:   end

Visitor pattern for decoded TLVs.

Args:

  tlvs:: the TLVs to visit
  tag_path:: internal, do not use

Yields: |tag_path, value| tag_path lists the numeric tags for the current value‘s tag, and all the parents’ tags.

[Source]

     # File lib/smartcard/gp/asn1_ber.rb, line 188
188:   def self.visit(tlvs, tag_path = [], &block)
189:     tlvs.each do |tlv|
190:       tag_number = encode_tag(tlv).inject { |acc, v| (acc << 8) | v }
191:       new_tag_path = tag_path + [tag_number]
192:       yield new_tag_path, tlv[:value]
193:       next if tlv[:primitive]
194:       visit tlv[:value], new_tag_path, &block
195:     end
196:   end

[Validate]