tlv 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +1 -0
- data/CHANGELOG +10 -0
- data/COPYING +728 -0
- data/LICENSE +58 -0
- data/README +38 -0
- data/Rakefile +163 -0
- data/THANKS +0 -0
- data/TODO +2 -0
- data/bin/parse_dgi +50 -0
- data/bin/parse_tlv +51 -0
- data/lib/tlv.rb +11 -0
- data/lib/tlv/b.rb +32 -0
- data/lib/tlv/constructed.rb +116 -0
- data/lib/tlv/dgi.rb +40 -0
- data/lib/tlv/field.rb +24 -0
- data/lib/tlv/parse.rb +87 -0
- data/lib/tlv/parser/dictionaries/asn.rb +63 -0
- data/lib/tlv/parser/dictionaries/dictionaries.rb +2 -0
- data/lib/tlv/parser/dictionaries/emv_tags.rb +130 -0
- data/lib/tlv/parser/parser.rb +167 -0
- data/lib/tlv/raw.rb +31 -0
- data/lib/tlv/tag.rb +51 -0
- data/lib/tlv/tlv.rb +144 -0
- data/lib/tlv/to_bytes.rb +76 -0
- data/test/constructed_test.rb +106 -0
- data/test/dgi_test.rb +122 -0
- data/test/tlv_tag_test.rb +89 -0
- data/test/tlv_test.rb +217 -0
- metadata +108 -0
data/lib/tlv/dgi.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module TLV
|
2
|
+
class DGI < TLV
|
3
|
+
|
4
|
+
def self.get_length(bytes)
|
5
|
+
len = bytes[0] #CPS 2.2 Creation of ...
|
6
|
+
if len == 0xFF
|
7
|
+
len = bytes[1,2].unpack("n")[0]
|
8
|
+
end
|
9
|
+
num = len > 0xfe ? 3 : 1
|
10
|
+
rest = bytes [num, bytes.length]
|
11
|
+
[len, rest]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.check_tag
|
15
|
+
raise "incorrect tag length, dgi must be 2 : #{tag}" unless tag.length==2
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.get_tag bytes
|
19
|
+
tag = bytes[0,2]
|
20
|
+
rest = bytes [2,bytes.length]
|
21
|
+
[tag, rest]
|
22
|
+
end
|
23
|
+
|
24
|
+
def get_len_bytes len
|
25
|
+
bytes = case len
|
26
|
+
when 0..0xfe : "" << len
|
27
|
+
when 0xff..65534 : "\xff"+[len].pack("n")
|
28
|
+
else
|
29
|
+
raise "Don't be silly"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
|
35
|
+
def primitive?
|
36
|
+
false
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end # module
|
data/lib/tlv/field.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
module TLV
|
2
|
+
class TLV
|
3
|
+
class Field
|
4
|
+
attr_accessor :display_name, :name, :length
|
5
|
+
def initialize clazz, desc, name=nil, len=0
|
6
|
+
@length=len
|
7
|
+
unless name
|
8
|
+
name = desc.gsub(/\s/, "_")
|
9
|
+
name = name.gsub(/\W/, "")
|
10
|
+
name = name.downcase.to_sym
|
11
|
+
end
|
12
|
+
@name = name
|
13
|
+
@display_name = desc
|
14
|
+
define_accessor clazz
|
15
|
+
end
|
16
|
+
def parse tlv, bytes, length
|
17
|
+
raise "not implemented! use subclass"
|
18
|
+
end
|
19
|
+
def define_accessor clazz
|
20
|
+
#raise "Not primitive: #{clazz.to_s}, TAG=#{TLV.b2s clazz.tag}" unless clazz.primitive?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end # module
|
data/lib/tlv/parse.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
module TLV
|
2
|
+
class TLV
|
3
|
+
# return [tlv, rest], the parsed TLV and any leftover bytes.
|
4
|
+
def self.parse bytes
|
5
|
+
tlv, _ = self._parse bytes
|
6
|
+
return tlv
|
7
|
+
end
|
8
|
+
|
9
|
+
def self._parse bytes
|
10
|
+
return nil unless bytes && bytes.length>0
|
11
|
+
tag, _ = self.get_tag bytes
|
12
|
+
impl = lookup(tag)
|
13
|
+
tlv = impl.new
|
14
|
+
rest = tlv.parse(bytes)
|
15
|
+
[tlv, rest]
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.get_tag bytes
|
19
|
+
tag = (bytes[0,1])
|
20
|
+
if (tag[0] & 0x1f) == 0x1f # last 5 bits set, 2 byte tag
|
21
|
+
tag = bytes[0,2]
|
22
|
+
if (tag[1] & 0x80) == 0x80 # bit 8 set -> 3 byte tag
|
23
|
+
tag = bytes[0,3]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
[tag, bytes[tag.length, bytes.length]]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.get_length bytes
|
30
|
+
len = bytes[0,1][0]
|
31
|
+
num_bytes=0
|
32
|
+
|
33
|
+
if (len & 0x80) == 0x80 # if MSB set
|
34
|
+
num_bytes = len & 0x0F # 4 LSB are num bytes total
|
35
|
+
raise "Don't be silly: #{b2s(bytes)}" if num_bytes > 4
|
36
|
+
len = bytes[1,num_bytes]
|
37
|
+
len = ("#{"\x00"*(4-num_bytes)}%s" % len).unpack("N")[0]
|
38
|
+
end
|
39
|
+
# this will return ALL the rest, not just `len` bytes of the rest. Does this make sense?
|
40
|
+
rest = bytes[1+num_bytes, bytes.length]
|
41
|
+
# TODO handle errors...
|
42
|
+
# warn if rest.length > length || rest.length < length ?
|
43
|
+
[len, rest]
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize bytes=nil
|
47
|
+
parse(bytes) if bytes
|
48
|
+
end
|
49
|
+
|
50
|
+
# returns the leftover bytes
|
51
|
+
def parse bytes
|
52
|
+
if tag
|
53
|
+
tag, rest = TLV.get_tag(bytes)
|
54
|
+
length, bytes = self.class.get_length(rest)
|
55
|
+
end
|
56
|
+
|
57
|
+
if self.class.primitive?
|
58
|
+
bytes = parse_fields bytes, length
|
59
|
+
else
|
60
|
+
bytes = parse_tlv bytes, length
|
61
|
+
end
|
62
|
+
|
63
|
+
bytes
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse_tlv bytes, length
|
68
|
+
b = bytes[0,length]
|
69
|
+
rest = bytes[length, bytes.length]
|
70
|
+
|
71
|
+
while b && b.length != 0
|
72
|
+
tlv, b = self.class._parse(b)
|
73
|
+
self.send("#{tlv.class.accessor_name}=", tlv)
|
74
|
+
end
|
75
|
+
|
76
|
+
rest
|
77
|
+
end
|
78
|
+
def parse_fields bytes, length
|
79
|
+
fields.each { |field|
|
80
|
+
bytes = field.parse(self, bytes, length)
|
81
|
+
} if fields
|
82
|
+
bytes
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
end
|
87
|
+
end # module
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#universal numbers, source http://en.wikipedia.org/wiki/Basic_Encoding_Rules
|
2
|
+
#
|
3
|
+
TLV::UNIVERSAL = [
|
4
|
+
['EOC (End-of-Content)',"P",0x00],
|
5
|
+
['BOOLEAN',"P",0x01],
|
6
|
+
['INTEGER',"P",0x02],
|
7
|
+
['BIT STRING',"P/C",0x03],
|
8
|
+
['OCTET STRING',"P/C",0x04],
|
9
|
+
['NULL',"P",0x05],
|
10
|
+
['OBJECT IDENTIFIER',"P",0x06],
|
11
|
+
['Object Descriptor',"P",0x07],
|
12
|
+
['EXTERNAL',"C",0x08],
|
13
|
+
['REAL (float)',"P",0x09],
|
14
|
+
['ENUMERATED',"P",0x0A],
|
15
|
+
['EMBEDDED PDV',"C",0x0B],
|
16
|
+
['UTF8String',"P/C",0x0C],
|
17
|
+
['RELATIVE-OID',"P",0x0D],
|
18
|
+
['SEQUENCE and SEQUENCE OF',"C",0x10],
|
19
|
+
['SET and SET OF',"C",0x11],
|
20
|
+
['NumericString',"P/C",0x12],
|
21
|
+
['PrintableString',"P/C",0x13],
|
22
|
+
['T61String',"P/C",0x14],
|
23
|
+
['VideotexString',"P/C",0x15],
|
24
|
+
['IA5String',"P/C",0x16],
|
25
|
+
['UTCTime',"P/C",0x17],
|
26
|
+
['GeneralizedTime',"P/C",0x18],
|
27
|
+
['GraphicString',"P/C",0x19],
|
28
|
+
['VisibleString',"P/C",0x1A],
|
29
|
+
['GeneralString',"P/C",0x1B],
|
30
|
+
['UniversalString',"P/C",0x1C],
|
31
|
+
['CHARACTER STRING',"P/C",0x1D],
|
32
|
+
['BMPString',"P/C",0x1E],
|
33
|
+
]
|
34
|
+
|
35
|
+
def make_dict
|
36
|
+
def add_primitive e,d
|
37
|
+
tag = ("" << e[2])
|
38
|
+
d[tag] = e[0]
|
39
|
+
end
|
40
|
+
def add_const e,d
|
41
|
+
tag = "" << (e[2] | 0x20)
|
42
|
+
d[tag] = e[0]
|
43
|
+
end
|
44
|
+
dict = {}
|
45
|
+
TLV::UNIVERSAL.each {|entry|
|
46
|
+
case entry[1]
|
47
|
+
when "P"
|
48
|
+
add_primitive entry, dict
|
49
|
+
when "C"
|
50
|
+
add_const entry, dict
|
51
|
+
when "P/C"
|
52
|
+
add_primitive entry, dict
|
53
|
+
add_const entry, dict
|
54
|
+
end
|
55
|
+
}
|
56
|
+
TLV::DICTIONARIES["ASN"] = dict
|
57
|
+
end
|
58
|
+
make_dict
|
59
|
+
|
60
|
+
#TLV::DICTIONARIES["ASN"].each_pair{|key,value|
|
61
|
+
# puts "#{TLV.b2s(key)} #{value}"
|
62
|
+
#}
|
63
|
+
|
@@ -0,0 +1,130 @@
|
|
1
|
+
TLV::DICTIONARIES["EMV"] = {
|
2
|
+
"\x42" => "Issuer Identification Number (IIN)",
|
3
|
+
"\x4F" => "Application Identifier (AID) - card",
|
4
|
+
"\x4F" => "AID",
|
5
|
+
"\x50" => "Application Label",
|
6
|
+
"\x57" => "Track 2 Equivalent Data",
|
7
|
+
"\x5A" => "Application Primary Account Number (PAN)",
|
8
|
+
"\x5A" => "PAN",
|
9
|
+
"\x5F\x20" => "Cardholder Name",
|
10
|
+
"\x5F\x24" => "Application Expiration Date",
|
11
|
+
"\x5F\x25" => "Application Effective Date",
|
12
|
+
"\x5F\x28" => "Issuer Country Code",
|
13
|
+
"\x5F\x2A" => "Transaction Currency Code",
|
14
|
+
"\x5F\x2D" => "Language Preference",
|
15
|
+
"\x5F\x30" => "Service Code",
|
16
|
+
"\x5F\x34" => "Application Primary Account Number (PAN) Sequence Number",
|
17
|
+
"\x5F\x36" => "Transaction Currency Exponent",
|
18
|
+
"\x5F\x50" => "Issuer URL",
|
19
|
+
"\x5F\x53" => "International Bank Account Number (IBAN)",
|
20
|
+
"\x5F\x54" => "Bank Identifier Code (BIC)",
|
21
|
+
"\x5F\x55" => "Issuer Country Code (alpha2 format)",
|
22
|
+
"\x5F\x56" => "Issuer Country Code (alpha3 format)",
|
23
|
+
"\x61" => "Application Template",
|
24
|
+
"\x6F" => "File Control Information (FCI) Template",
|
25
|
+
"\x70" => "READ RECORD Response Message Template",
|
26
|
+
"\x71" => "Issuer Script Template 1",
|
27
|
+
"\x72" => "Issuer Script Template 2",
|
28
|
+
"\x73" => "Directory Discretionary Template",
|
29
|
+
"\x77" => "Response Message Template Format 2",
|
30
|
+
"\x80" => "Response Message Template Format 1",
|
31
|
+
"\x81" => "Amount, Authorised (Binary)",
|
32
|
+
"\x82" => "Application Interchange Profile",
|
33
|
+
"\x83" => "Command Template",
|
34
|
+
"\x84" => "Dedicated File (DF) Name",
|
35
|
+
"\x86" => "Issuer Script Command",
|
36
|
+
"\x87" => "Application Priority Indicator",
|
37
|
+
"\x88" => "Short File Identifier (SFI)",
|
38
|
+
"\x88" => "SFI",
|
39
|
+
"\x89" => "Authorisation Code",
|
40
|
+
"\x8A" => "Authorisation Response Code",
|
41
|
+
"\x8C" => "Card Risk Management Data Object List 1 (CDOL1)",
|
42
|
+
"\x8C" => "CDOL1",
|
43
|
+
"\x8D" => "Card Risk Management Data Object List 2 (CDOL2)",
|
44
|
+
"\x8D" => "CDOL2",
|
45
|
+
"\x8E" => "Cardholder Verification Method (CVM) List",
|
46
|
+
"\x8E" => "CVM List",
|
47
|
+
"\x8F" => "Certification Authority Public Key Index",
|
48
|
+
"\x90" => "Issuer Public Key Certificate",
|
49
|
+
"\x91" => "Issuer Authentication Data",
|
50
|
+
"\x92" => "Issuer Public Key Remainder",
|
51
|
+
"\x93" => "Signed Static Application Data",
|
52
|
+
"\x94" => "Application File Locator (AFL)",
|
53
|
+
"\x94" => "Application File Locator",
|
54
|
+
"\x95" => "Terminal Verification Results",
|
55
|
+
"\x97" => "Transaction Certificate Data Object List (TDOL)",
|
56
|
+
"\x98" => "Transaction Certificate (TC) Hash Value",
|
57
|
+
"\x99" => "Transaction Personal Identification Number (PIN) Data",
|
58
|
+
"\x9A" => "Transaction Date",
|
59
|
+
"\x9B" => "Transaction Status Information",
|
60
|
+
"\x9C" => "Transaction Type",
|
61
|
+
"\x9D" => "Directory Definition File (DDF) Name",
|
62
|
+
"\x9F\x01" => "Acquirer Identifier",
|
63
|
+
"\x9F\x02" => "Amount, Authorised (Numeric)",
|
64
|
+
"\x9F\x03" => "Amount, Other (Numeric)",
|
65
|
+
"\x9F\x04" => "Amount, Other (Binary)",
|
66
|
+
"\x9F\x05" => "Application Discretionary Data",
|
67
|
+
"\x9F\x06" => "Application Identifier (AID) - terminal",
|
68
|
+
"\x9F\x07" => "Application Usage Control",
|
69
|
+
"\x9F\x08" => "Application Version Number",
|
70
|
+
"\x9F\x09" => "Application Version Number",
|
71
|
+
"\x9F\x0B" => "Cardholder Name Extended",
|
72
|
+
"\x9F\x0D" => "Issuer Action Code - Default",
|
73
|
+
"\x9F\x0E" => "Issuer Action Code - Denial",
|
74
|
+
"\x9F\x0F" => "Issuer Action Code - Online",
|
75
|
+
"\x9F\x10" => "Issuer Application Data",
|
76
|
+
"\x9F\x11" => "Issuer Code Table Index",
|
77
|
+
"\x9F\x12" => "Application Preferred Name",
|
78
|
+
"\x9F\x13" => "Last Online Application Transaction Counter (ATC) Register",
|
79
|
+
"\x9F\x14" => "Lower Consecutive Offline Limit",
|
80
|
+
"\x9F\x15" => "Merchant Category Code",
|
81
|
+
"\x9F\x16" => "Merchant Identifier",
|
82
|
+
"\x9F\x17" => "Personal Identification Number (PIN) Try Counter",
|
83
|
+
"\x9F\x18" => "Issuer Script Identifier",
|
84
|
+
"\x9F\x1A" => "Terminal Country Code",
|
85
|
+
"\x9F\x1B" => "Terminal Floor Limit",
|
86
|
+
"\x9F\x1C" => "Terminal Identification",
|
87
|
+
"\x9F\x1D" => "Terminal Risk Management Data",
|
88
|
+
"\x9F\x1E" => "Interface Device (IFD) Serial Number",
|
89
|
+
"\x9F\x1F" => "Track 1 Discretionary Data",
|
90
|
+
"\x9F\x20" => "Track 2 Discretionary Data",
|
91
|
+
"\x9F\x21" => "Transaction Time",
|
92
|
+
"\x9F\x23" => "Upper Consecutive Offline Limit",
|
93
|
+
"\x9F\x26" => "Application Cryptogram",
|
94
|
+
"\x9F\x27" => "Cryptogram Information Data",
|
95
|
+
"\x9F\x2D" => "ICC PIN Encipherment Public Key Certificate",
|
96
|
+
"\x9F\x2E" => "ICC PIN Encipherment Public Key Exponent",
|
97
|
+
"\x9F\x2F" => "ICC PIN Encipherment Public Key Remainder",
|
98
|
+
"\x9F\x32" => "Issuer Public Key Exponent",
|
99
|
+
"\x9F\x33" => "Terminal Capabilities",
|
100
|
+
"\x9F\x34" => "Cardholder Verification Method (CVM) Results",
|
101
|
+
"\x9F\x35" => "Terminal Type",
|
102
|
+
"\x9F\x36" => "Application Transaction Counter (ATC)",
|
103
|
+
"\x9F\x37" => "Unpredictable Number",
|
104
|
+
"\x9F\x38" => "Processing Options Data Object List (PDOL)",
|
105
|
+
"\x9F\x38" => "PDOL",
|
106
|
+
"\x9F\x39" => "Point-of-Service (POS) Entry Mode",
|
107
|
+
"\x9F\x3A" => "Amount, Reference Currency",
|
108
|
+
"\x9F\x3B" => "Application Reference Currency",
|
109
|
+
"\x9F\x3C" => "Transaction Reference Currency Code",
|
110
|
+
"\x9F\x3D" => "Transaction Reference Currency Exponent",
|
111
|
+
"\x9F\x40" => "Additional Terminal Capabilities",
|
112
|
+
"\x9F\x41" => "Transaction Sequence Counter",
|
113
|
+
"\x9F\x42" => "Application Currency Code",
|
114
|
+
"\x9F\x43" => "Application Reference Currency Exponent",
|
115
|
+
"\x9F\x44" => "Application Currency Exponent",
|
116
|
+
"\x9F\x45" => "Data Authentication Code",
|
117
|
+
"\x9F\x46" => "ICC Public Key Certificate",
|
118
|
+
"\x9F\x47" => "ICC Public Key Exponent",
|
119
|
+
"\x9F\x48" => "ICC Public Key Remainder",
|
120
|
+
"\x9F\x49" => "Dynamic Data Authentication Data Object List (DDOL)",
|
121
|
+
"\x9F\x49" => "DDOL",
|
122
|
+
"\x9F\x4A" => "Static Data Authentication Tag List",
|
123
|
+
"\x9F\x4B" => "Signed Dynamic Application Data",
|
124
|
+
"\x9F\x4C" => "ICC Dynamic Number",
|
125
|
+
"\x9F\x4D" => "Log Entry",
|
126
|
+
"\x9F\x4E" => "Merchant Name and Location",
|
127
|
+
"\x9F\x4F" => "Log Format",
|
128
|
+
"\xA5" => "File Control Information (FCI) Proprietary Template",
|
129
|
+
"\xBF\x0C" => "File Control Information (FCI) Issuer Discretionary Data",
|
130
|
+
}
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# Simple lib to deconstruct tlv data
|
2
|
+
# TLV.parse str
|
3
|
+
# TLV.parse_hex str
|
4
|
+
require 'hexy'
|
5
|
+
|
6
|
+
module TLV
|
7
|
+
DICTIONARIES = {}
|
8
|
+
# Attempt to parse a (series) or BER encoded
|
9
|
+
# datastructures. May be be passed a
|
10
|
+
#
|
11
|
+
# "\x00"=> "Tag Name"
|
12
|
+
#
|
13
|
+
# encoded dictionary to provide names for tags.
|
14
|
+
# Some dictionaries are predefined in TLV::DICTIONARIES
|
15
|
+
#
|
16
|
+
# Parameters:
|
17
|
+
# +bytes+ : a string of raw bytes to decode
|
18
|
+
# +dictionary+ : a tag=>name dictionary for tagname lookup
|
19
|
+
#
|
20
|
+
# Returns:
|
21
|
+
# a string respresenation of the datastructure
|
22
|
+
def self.parse bytes, dictionary={}
|
23
|
+
_dump(_parse(bytes), dictionary)
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# Attempt to decode a (series) of BER encoded
|
28
|
+
# datastructures (see parse)
|
29
|
+
#
|
30
|
+
# The data passed to this method is expected to
|
31
|
+
# be hex formated instead of in binary form.
|
32
|
+
#
|
33
|
+
def self.parse_hex hex_str, dictionary={}
|
34
|
+
self.parse s2b(hex_str), dictionary
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Attempt to decode a DGI encoded datastructure.
|
39
|
+
# This is used in EMV (CPS).
|
40
|
+
# see parse
|
41
|
+
#
|
42
|
+
def self.parse_dgi bytes, dictionary={}
|
43
|
+
_dump(_parse_dgi(bytes), dictionary)
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Attempt to decode a DGI encoded datastructure.
|
48
|
+
# This is used in EMV (CPS).
|
49
|
+
# see parse_hex
|
50
|
+
#
|
51
|
+
def self.parse_dgi_hex hex_str, dictionary={}
|
52
|
+
self.parse_dgi s2b(hex_str), dictionary
|
53
|
+
end
|
54
|
+
# heuristics to determine whether a string of bytes
|
55
|
+
# is worth printing. If more than 90% of bytes are
|
56
|
+
# printable, will return true.
|
57
|
+
def self.printable? bytes
|
58
|
+
count = 0
|
59
|
+
count_printable = 0
|
60
|
+
bytes.each_byte {|b|
|
61
|
+
count += 1
|
62
|
+
count_printable += 1 if ((0x20..0x7e).include?(b) || (0xA0..0xff).include?(b))
|
63
|
+
}
|
64
|
+
return (count_printable.to_f/count) > 0.90
|
65
|
+
end
|
66
|
+
|
67
|
+
#--
|
68
|
+
# Stop RDOC here.
|
69
|
+
def self._parse bytes #:nodoc:
|
70
|
+
tlvs = []
|
71
|
+
begin
|
72
|
+
tlv, rest = _parse_tlv bytes
|
73
|
+
tlvs << tlv
|
74
|
+
bytes = rest
|
75
|
+
end while rest && rest.length != 0
|
76
|
+
tlvs
|
77
|
+
end
|
78
|
+
|
79
|
+
def self._parse_dgi bytes #:nodoc:
|
80
|
+
dgis = []
|
81
|
+
begin
|
82
|
+
dgi = TLV_Object.new
|
83
|
+
tag, rest = DGI.get_tag bytes
|
84
|
+
dgi.tag = tag
|
85
|
+
len, rest = DGI.get_length rest
|
86
|
+
dgi.length = len
|
87
|
+
if tag == "\x80\x00" || tag == "\x90\x00"
|
88
|
+
# no children
|
89
|
+
dgi.value = rest[0,len]
|
90
|
+
else
|
91
|
+
dgi.children = _parse rest[0,len]
|
92
|
+
end
|
93
|
+
dgis << dgi
|
94
|
+
bytes = rest[len, rest.length]
|
95
|
+
end while bytes && bytes.length != 0
|
96
|
+
dgis
|
97
|
+
end
|
98
|
+
|
99
|
+
|
100
|
+
def self._parse_tlv bytes #:nodoc:
|
101
|
+
tlv = TLV_Object.new
|
102
|
+
tag, rest = TLV.get_tag bytes
|
103
|
+
tlv.tag = tag
|
104
|
+
len, rest = TLV.get_length rest
|
105
|
+
tlv.length = len
|
106
|
+
if (tag[0] & 0x20) != 0x00 # constructed object
|
107
|
+
tlv.children = _parse rest[0,len]
|
108
|
+
else
|
109
|
+
tlv.value = rest[0,len]
|
110
|
+
end
|
111
|
+
[tlv, rest[len, rest.length]]
|
112
|
+
end
|
113
|
+
|
114
|
+
def self._dump (tlvs, dictionary, indent = "") #:nodoc:
|
115
|
+
dump = ""
|
116
|
+
tlvs.each {|tlv|
|
117
|
+
dump += "%s%-6s : %d" % [indent, "0x"+b2s(tlv.tag), tlv.length]
|
118
|
+
if tlv.children
|
119
|
+
dump += " ["+dictionary[tlv.tag]+"]" if (dictionary[tlv.tag])
|
120
|
+
dump += "\n"
|
121
|
+
tlv.children.each {|child|
|
122
|
+
dump += _dump([child], dictionary, indent+" ")
|
123
|
+
}
|
124
|
+
else
|
125
|
+
if (tlv.value.length < 17)
|
126
|
+
dump += " : " + b2s(tlv.value)
|
127
|
+
dump += " (#{tlv.value})" if printable?(tlv.value)
|
128
|
+
dump += " ["+dictionary[tlv.tag]+"]" if (dictionary[tlv.tag])
|
129
|
+
else
|
130
|
+
dump += " ["+dictionary[tlv.tag]+"]" if (dictionary[tlv.tag])
|
131
|
+
dump +="\n"
|
132
|
+
dump += Hexy.dump(tlv.value, :indent=>indent.length+2)
|
133
|
+
end
|
134
|
+
|
135
|
+
dump += "\n"
|
136
|
+
end
|
137
|
+
}
|
138
|
+
#puts ">>>>"
|
139
|
+
#puts dump
|
140
|
+
#puts "<<<<"
|
141
|
+
dump
|
142
|
+
end
|
143
|
+
class TLV_Object
|
144
|
+
attr_accessor :tag, :length, :value, :children
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
if $0 == __FILE__
|
149
|
+
require "tlv"
|
150
|
+
# puts TLV.parse_hex("9f7103313233")
|
151
|
+
# puts
|
152
|
+
# puts TLV.parse_hex("32084102010243023456")
|
153
|
+
# puts
|
154
|
+
# puts TLV.parse_hex("320841020102430234")
|
155
|
+
# puts
|
156
|
+
# dict = {"\x32" => "Test Tag", "\x41" => "Other Tag"}
|
157
|
+
# puts TLV.parse_hex("32084102010243023456", dict)
|
158
|
+
# puts
|
159
|
+
# puts TLV.parse_hex("320841020102430234569f7103313233", dict)
|
160
|
+
# puts
|
161
|
+
dict = {"\x57" => "Track 2 Equivalent Data",
|
162
|
+
"\x70" => "READ RECORD Response Message Template"}
|
163
|
+
puts TLV.parse_dgi_hex("010142704057134451973022158124D12102011089573110000F5F201A4D55535445524D414E4E2F4D41582020202020202020202020209F1F0B0102030405060708090A0B")
|
164
|
+
puts
|
165
|
+
puts TLV.parse_dgi_hex("010142704057134451973022158124D12102011089573110000F5F201A4D55535445524D414E4E2F4D41582020202020202020202020209F1F0B0102030405060708090A0B02018670818390818047D56F644D05FF41180926D965765BBC1894E6F973FA6DD56FC69313E82E9480F3405D7A4056B3AB5F31293D22F55A460D540E954BCF74E3D056DA839E756D1C6AC4BAD76D2747E158288BDE28CEEB321C930ED2F40ED35884304DD3D69E87BBC81FBEE22ACD2F0851A5DCA6DAAC794E633A70072AF5B93103C115B225118B77 ", dict)
|
166
|
+
end
|
167
|
+
#++
|