Module: TLV

Defined in:
lib/tlv/b.rb,
lib/tlv/raw.rb,
lib/tlv/tag.rb,
lib/tlv/tlv.rb,
lib/tlv/field.rb,
lib/tlv/parse.rb,
lib/tlv/constructed.rb,
lib/tlv/parser/parser.rb

Defined Under Namespace

Classes: TLV, TLV_Object

Constant Summary collapse

DICTIONARIES =
{}
UNIVERSAL =

universal numbers, source en.wikipedia.org/wiki/Basic_Encoding_Rules

[
['EOC (End-of-Content)',"P",0x00],
['BOOLEAN',"P",0x01],
['INTEGER',"P",0x02],
['BIT STRING',"P/C",0x03],
['OCTET STRING',"P/C",0x04],
['NULL',"P",0x05],
['OBJECT IDENTIFIER',"P",0x06],
['Object Descriptor',"P",0x07],
['EXTERNAL',"C",0x08],
['REAL (float)',"P",0x09],
['ENUMERATED',"P",0x0A],
['EMBEDDED PDV',"C",0x0B],
['UTF8String',"P/C",0x0C],
['RELATIVE-OID',"P",0x0D],
['SEQUENCE and SEQUENCE OF',"C",0x10],
['SET and SET OF',"C",0x11],
['NumericString',"P/C",0x12],
['PrintableString',"P/C",0x13],
['T61String',"P/C",0x14],
['VideotexString',"P/C",0x15],
['IA5String',"P/C",0x16],
['UTCTime',"P/C",0x17],
['GeneralizedTime',"P/C",0x18],
['GraphicString',"P/C",0x19],
['VisibleString',"P/C",0x1A],
['GeneralString',"P/C",0x1B],
['UniversalString',"P/C",0x1C],
['CHARACTER STRING',"P/C",0x1D],
['BMPString',"P/C",0x1E],
]

Class Method Summary collapse

Class Method Details

._dump(tlvs, dictionary, indent = "") ⇒ Object

:nodoc:



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/tlv/parser/parser.rb', line 114

def self._dump (tlvs, dictionary, indent = "") #:nodoc: 
  dump = ""
  tlvs.each {|tlv|
    dump += "%s%-6s : %d" % [indent, "0x"+b2s(tlv.tag), tlv.length]
    if tlv.children
      dump += " ["+dictionary[tlv.tag]+"]" if (dictionary[tlv.tag])
      dump += "\n"
      tlv.children.each {|child|
        dump += _dump([child], dictionary, indent+"  ")
      }
    else
      if (tlv.value.length < 17)
        dump += " : " + b2s(tlv.value) 
        dump += "  (#{tlv.value})" if printable?(tlv.value)
        dump += " ["+dictionary[tlv.tag]+"]" if (dictionary[tlv.tag])
      else
        dump += " ["+dictionary[tlv.tag]+"]" if (dictionary[tlv.tag])
        dump +="\n"
        dump += Hexy.dump(tlv.value, :indent=>indent.length+2)
      end

      dump += "\n"
    end
  }
  #puts ">>>>"
  #puts dump
  #puts "<<<<"
  dump
end

._parse(bytes) ⇒ Object

– Stop RDOC here.



69
70
71
72
73
74
75
76
77
# File 'lib/tlv/parser/parser.rb', line 69

def self._parse bytes #:nodoc: 
  tlvs = []
  begin
    tlv, rest = _parse_tlv bytes
    tlvs << tlv
    bytes = rest
  end while rest && rest.length != 0
  tlvs
end

._parse_dgi(bytes) ⇒ Object

:nodoc:



79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/tlv/parser/parser.rb', line 79

def self._parse_dgi bytes #:nodoc: 
  dgis = []
  begin
    dgi = TLV_Object.new
    tag, rest    = DGI.get_tag bytes
    dgi.tag      = tag
    len, rest    = DGI.get_length rest
    dgi.length   = len
    if tag == "\x80\x00" || tag == "\x90\x00"
      # no children
      dgi.value = rest[0,len]
    else
      dgi.children = _parse rest[0,len]
    end
    dgis << dgi
    bytes = rest[len, rest.length]
  end while bytes && bytes.length != 0
  dgis
end

._parse_tlv(bytes) ⇒ Object

:nodoc:



100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/tlv/parser/parser.rb', line 100

def self._parse_tlv bytes #:nodoc: 
  tlv = TLV_Object.new
  tag, rest = TLV.get_tag bytes
  tlv.tag = tag
  len, rest = TLV.get_length rest
  tlv.length = len
  if (tag[0] & 0x20) != 0x00 # constructed object
    tlv.children = _parse rest[0,len]
  else
    tlv.value = rest[0,len]
  end
  [tlv, rest[len, rest.length]]
end

.b2s(bytestr) ⇒ Object



4
5
6
7
8
# File 'lib/tlv/tlv.rb', line 4

def self.b2s bytestr
  return "" unless bytestr
  r = bytestr.unpack("H*")[0]
  r.length > 1 ? r : "  "
end

.parse(bytes, dictionary = {}) ⇒ Object

Attempt to parse a (series) or BER encoded datastructures. May be be passed a

"\x00"=> "Tag Name"

encoded dictionary to provide names for tags. Some dictionaries are predefined in TLV::DICTIONARIES

Parameters: bytes : a string of raw bytes to decode dictionary : a tag=>name dictionary for tagname lookup

Returns: a string respresenation of the datastructure



22
23
24
# File 'lib/tlv/parser/parser.rb', line 22

def self.parse bytes, dictionary={}
  _dump(_parse(bytes), dictionary)
end

.parse_dgi(bytes, dictionary = {}) ⇒ Object

Attempt to decode a DGI encoded datastructure. This is used in EMV (CPS). see parse



42
43
44
# File 'lib/tlv/parser/parser.rb', line 42

def self.parse_dgi bytes, dictionary={}
  _dump(_parse_dgi(bytes), dictionary) 
end

.parse_dgi_hex(hex_str, dictionary = {}) ⇒ Object

Attempt to decode a DGI encoded datastructure. This is used in EMV (CPS). see parse_hex



51
52
53
# File 'lib/tlv/parser/parser.rb', line 51

def self.parse_dgi_hex hex_str, dictionary={}
  self.parse_dgi s2b(hex_str), dictionary
end

.parse_hex(hex_str, dictionary = {}) ⇒ Object

Attempt to decode a (series) of BER encoded datastructures (see parse)

The data passed to this method is expected to be hex formated instead of in binary form.



33
34
35
# File 'lib/tlv/parser/parser.rb', line 33

def self.parse_hex hex_str, dictionary={}
  self.parse s2b(hex_str), dictionary
end

.printable?(bytes) ⇒ Boolean

heuristics to determine whether a string of bytes is worth printing. If more than 90% of bytes are printable, will return true.

Returns:

  • (Boolean)


57
58
59
60
61
62
63
64
65
# File 'lib/tlv/parser/parser.rb', line 57

def self.printable? bytes
  count = 0
  count_printable = 0
  bytes.each_byte {|b|
    count += 1
    count_printable += 1 if ((0x20..0x7e).include?(b) || (0xA0..0xff).include?(b)) 
  }
  return (count_printable.to_f/count) > 0.90
end

.s2b(string) ⇒ Object



9
10
11
12
13
14
# File 'lib/tlv/tlv.rb', line 9

def self.s2b string
  return "" unless string
  string = string.gsub(/\s+/, "")
  string = "0" + string unless (string.length % 2 == 0)
  [string].pack("H*") 
end