Module: Smartcard::Gp::Asn1Ber
- Defined in:
- lib/smartcard/gp/asn1_ber.rb
Overview
Logic for encoding and decoding ASN.1-BER data as specified in X.690-0207.
Class Method Summary collapse
-
.decode(data, offset = 0, length = data.length - offset) ⇒ Object
Decodes a sequence of TLVs (tag-length-value).
-
.decode_length(data, offset) ⇒ Object
Decodes a TLV length.
-
.decode_tag(data, offset) ⇒ Object
Decodes a TLV tag (the data type).
-
.decode_tlv(data, offset) ⇒ Object
Decodes a TLV (tag-length-value).
-
.decode_value(data, offset, length) ⇒ Object
Decodes a TLV value.
-
.encode(tlvs) ⇒ Object
Encodes a sequence of TLVs (tag-length-value).
-
.encode_length(length) ⇒ Object
Encodes a TLV length (the length of the data).
-
.encode_tag(tag) ⇒ Object
Encodes a TLV tag (the data type).
-
.encode_tlv(tlv) ⇒ Object
Encodes a TLV (tag-length-value).
-
.map_value(value, tag) ⇒ Object
Maps a TLV value with a known tag to a Ruby data type.
-
.visit(tlvs, tag_path = [], &block) ⇒ Object
Visitor pattern for decoded TLVs.
Class Method Details
.decode(data, offset = 0, length = data.length - offset) ⇒ Object
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.
107 108 109 110 111 112 113 114 115 |
# File 'lib/smartcard/gp/asn1_ber.rb', line 107 def self.decode(data, offset = 0, length = data.length - offset) sequence = [] loop do break if offset >= length offset, tlv = decode_tlv data, offset sequence << tlv end sequence end |
.decode_length(data, offset) ⇒ Object
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.
51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/smartcard/gp/asn1_ber.rb', line 51 def self.decode_length(data, offset) return (offset + 1), data[offset] if (data[offset] & 0x80) == 0 len_bytes = (data[offset] & 0x7F) return (offset + 1), :indefinite if len_bytes == 0 length = 0 len_bytes.times do offset += 1 length = (length << 8) | data[offset] end return (offset + 1), length end |
.decode_tag(data, offset) ⇒ Object
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
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/smartcard/gp/asn1_ber.rb', line 24 def self.decode_tag(data, offset) class_bits = data[offset] >> 6 tag_class = [:universal, :application, :context, :private][class_bits] tag_primitive = (data[offset] & 0x20) == 0 tag_number = (data[offset] & 0x1F) if tag_number == 0x1F tag_number = 0 loop do offset += 1 tag_number <<= 7 tag_number |= (data[offset] & 0x7F) break if (data[offset] & 0x80) == 0 end end return (offset + 1), { :class => tag_class, :primitive => tag_primitive, :number => tag_number } end |
.decode_tlv(data, offset) ⇒ Object
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.
94 95 96 97 98 99 100 101 |
# File 'lib/smartcard/gp/asn1_ber.rb', line 94 def self.decode_tlv(data, offset) offset, tag = decode_tag data, offset offset, length = decode_length data, offset offset, value = decode_value data, offset, length tag[:value] = tag[:primitive] ? map_value(value, tag) : decode(value) return offset, tag end |
.decode_value(data, offset, length) ⇒ Object
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.
71 72 73 74 75 76 77 78 79 80 81 |
# File 'lib/smartcard/gp/asn1_ber.rb', line 71 def self.decode_value(data, offset, length) return offset + length, data[offset, length] unless length == :indefinite length = 0 loop do raise 'Unterminated data' if offset + length + 2 > data.length break if data[offset + length, 2] == [0, 0] length += 1 end return (offset + length + 2), data[offset, length] end |
.encode(tlvs) ⇒ Object
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.
176 177 178 |
# File 'lib/smartcard/gp/asn1_ber.rb', line 176 def self.encode(tlvs) tlvs.map { |tlv| encode_tlv tlv }.flatten end |
.encode_length(length) ⇒ Object
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.
147 148 149 150 151 152 153 154 155 156 |
# File 'lib/smartcard/gp/asn1_ber.rb', line 147 def self.encode_length(length) return [0x80] if length == :indefinite return [length] if length < 0x80 length_bytes = [] while length > 0 length_bytes << (length & 0xFF) length >>= 8 end [0x80 | length_bytes.length] + length_bytes.reverse end |
.encode_tag(tag) ⇒ Object
Encodes a TLV tag (the data type).
Args:
tag:: a hash with the keys produced by decode_tag.
Returns an array of byte values.
123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 |
# File 'lib/smartcard/gp/asn1_ber.rb', line 123 def self.encode_tag(tag) tag_classes = { :universal => 0, :application => 1, :context => 2, :private => 3 } tag_lead = (tag_classes[tag[:class]] << 6) | (tag[:primitive] ? 0x00 : 0x20) return [tag_lead | tag[:number]] if tag[:number] < 0x1F number_bytes, number = [], tag[:number] first = true while number != 0 byte = (number & 0x7F) number >>= 7 byte |= 0x80 unless first first = false number_bytes << byte end [tag_lead | 0x1F] + number_bytes.reverse end |
.encode_tlv(tlv) ⇒ Object
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.
165 166 167 168 |
# File 'lib/smartcard/gp/asn1_ber.rb', line 165 def self.encode_tlv(tlv) value = tlv[:primitive] ? tlv[:value] : encode(tlv[:value]) [encode_tag(tlv), encode_length(value.length), value].flatten end |
.map_value(value, tag) ⇒ Object
Maps a TLV value with a known tag to a Ruby data type.
84 85 86 87 |
# File 'lib/smartcard/gp/asn1_ber.rb', line 84 def self.map_value(value, tag) # TODO(costan): map primitive types if necessary value end |
.visit(tlvs, tag_path = [], &block) ⇒ Object
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.
188 189 190 191 192 193 194 195 196 |
# File 'lib/smartcard/gp/asn1_ber.rb', line 188 def self.visit(tlvs, tag_path = [], &block) tlvs.each do |tlv| tag_number = encode_tag(tlv).inject { |acc, v| (acc << 8) | v } new_tag_path = tag_path + [tag_number] yield new_tag_path, tlv[:value] next if tlv[:primitive] visit tlv[:value], new_tag_path, &block end end |