tlv 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
#++
|