iso7816 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.
@@ -0,0 +1,68 @@
1
+ module ISO7816
2
+
3
+ module APDU
4
+
5
+ def APDU.create_class name, ins
6
+ cl=
7
+ %Q(
8
+ class #{name} < APDU
9
+ cla \"\\x00\"
10
+ ins \"\\x#{ins}\"
11
+ end
12
+ )
13
+ eval(cl)
14
+ end
15
+
16
+ [
17
+ ["ACTIVATE_FILE", "44"],
18
+ ["APPEND_RECORD", "E2"],
19
+ ["CHANGE_REFERENCE_DATA", "24"],
20
+ ["CREATE_FILE", "E0"],
21
+ ["DEACTIVATE_FILE", "04"],
22
+ ["DELETE_FILE", "E4"],
23
+ ["DISABLE_VERIFICATION_REQUIREMENT", "26"],
24
+ ["ENABLE_VERIFICATION_REQUIREMENT", "28"],
25
+ ["ENVELOPE", "C2"],
26
+ ["ERASE_BINARY", "0E"],
27
+ ["ERASE_RECORD", "0C"],
28
+ ["EXTERNAL_AUTHENTICATE", "82"],
29
+ ["GENERAL_AUTHENTICATE", "86"],
30
+ ["GENERATE_ASYMMETRIC_KEY_PAIR", "46"],
31
+ ["GET_CHALLENGE", "84"],
32
+ ["GET_DATA", "CA"],
33
+ ["GET_RESPONSE", "C0"],
34
+ ["INTERNAL_AUTHENTICATE", "88"],
35
+ ["MANAGE_CHANNEL", "70"],
36
+ ["MANAGE_SECURITY_ENVIRONMENT", "22"],
37
+ ["PERFORM_SCQL_OPERATION", "10"],
38
+ ["PERFORM_SECURITY_OPERATION", "2A"],
39
+ ["PERFORM_TRANSACTION_OPERATION", "12"],
40
+ ["PERFORM_USER_OPERATION", "14"],
41
+ ["PUT_DATA", "DA"],
42
+ ["READ_BINARY", "B0"],
43
+ ["READ_RECORD", "B2"],
44
+ ["RESET_RETRY_COUNTER", "2C"],
45
+ ["SEARCH_BINARY", "A0"],
46
+ ["SEARCH_RECORD", "A2"],
47
+ ["SELECT", "A4"],
48
+ ["TERMINATE_CARD_USAGE", "FE"],
49
+ ["TERMINATE_DF", "E6"],
50
+ ["TERMINATE_EF", "E8"],
51
+ ["UPDATE_BINARY", "D6"],
52
+ ["UPDATE_RECORD", "DC"],
53
+ ["VERIFY", "20"],
54
+ ["WRITE_BINARY", "D0"],
55
+ ["WRITE_RECORD", "D2"]
56
+ ].each{|entry|
57
+ APDU.create_class *entry
58
+ }
59
+
60
+
61
+ class GET_RESPONSE < APDU
62
+ ins "\xc0"
63
+ end
64
+
65
+
66
+ end # APDU
67
+
68
+ end #7816
@@ -0,0 +1,102 @@
1
+ require 'smartcard'
2
+ module ISO7816
3
+ module PCSC
4
+
5
+ # adds default params to Smartcard to make them easier to work with.
6
+
7
+ class Context < Smartcard::PCSC::Context
8
+ def initialize scope=Smartcard::PCSC::SCOPE_SYSTEM
9
+ super
10
+ end
11
+
12
+ def list_readers reader_groups=nil
13
+ super
14
+ end
15
+ end
16
+
17
+ class Card < Smartcard::PCSC::Card
18
+ include Smartcard::PCSC
19
+ attr_accessor :ctx
20
+ def initialize( context = nil,
21
+ reader_name = nil,
22
+ share_mode = SHARE_EXCLUSIVE,
23
+ preferred_protocol = PROTOCOL_ANY)
24
+
25
+ @ctx = context || ISO7816::PCSC::Context.new
26
+ @r_name = reader_name || @ctx.list_readers()[0]
27
+ @share_mode = share_mode
28
+ @preferred_protocol = preferred_protocol
29
+
30
+ super(@ctx, @r_name, @share_mode, @preferred_protocol)
31
+ end
32
+
33
+ def disconnect disposition=DISPOSITION_UNPOWER, release_context=true
34
+ super(disposition)
35
+ @ctx.release if release_context
36
+ end
37
+
38
+ def reconnect share=nil, preferred_protocol=nil, ini= INITIALIZATION_UNPOWER
39
+ # use and remember passed params if set, else reuse last remembered params.
40
+ share ||= @share_mode
41
+ preferred_protocol ||= @preferred_protocol
42
+
43
+ @share_mode = share
44
+ @preferred_protocol = preferred_protocol
45
+
46
+ @status = nil
47
+ #puts @share_mode.class
48
+ #puts @preferred_protocol.class
49
+ #puts ini.class
50
+ super(@share_mode, @preferred_protocol, ini)
51
+ end
52
+
53
+ def _status
54
+ @status ||= self.status
55
+ end
56
+
57
+ def transmit send_data, send_io_request=nil, recv_io_request=nil
58
+ unless send_io_request
59
+ protocol = _status[:protocol]
60
+ send_io_request = case protocol
61
+ when PROTOCOL_T0:
62
+ IOREQUEST_T0
63
+ when PROTOCOL_T1:
64
+ puts "T1"
65
+ IOREQUEST_T1
66
+ when Smartcard::PCSC::PROTOCOL_RAW:
67
+ IOREQUEST_RAW
68
+ else
69
+ raise "weird protocol: #{protocol}"
70
+ end
71
+ end # send_io_request
72
+
73
+ recv_io_request ||= Smartcard::PCSC::IoRequest.new
74
+ super(send_data, send_io_request, recv_io_request)
75
+ end
76
+
77
+ def atr
78
+ _status[:atr]
79
+ end
80
+ def protocol?
81
+ case _status[:protocol]
82
+ when PROTOCOL_T0
83
+ "T0"
84
+ when PROTOCOL_T1
85
+ "T1"
86
+ when PROTOCOL_RAW
87
+ "RAW"
88
+ when PROTOCOL_T15
89
+ "T15"
90
+ when PROTOCOL_UNKNOWN
91
+ "UNKNOWN"
92
+ else
93
+ "UNKNOWN_UNKNOWN"
94
+ end
95
+ end
96
+ end #card
97
+
98
+ end # pcsc
99
+ end # iso7816
100
+
101
+
102
+
@@ -0,0 +1,10 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'iso7816'
5
+ require 'emv/emv'
6
+ require 'emv/emv_apdu'
7
+ require 'emv/cps_apdu'
8
+ require 'emv/data/cps_ini_update'
9
+ require 'emv/crypto/crypto'
10
+
@@ -0,0 +1,319 @@
1
+ module EMV
2
+ module APDU
3
+ module CPS
4
+
5
+
6
+ class SecureContext
7
+ # The Kenc key used in this session
8
+ attr_accessor :k_enc
9
+
10
+ # The Kmac key used in this session
11
+ attr_accessor :k_mac
12
+
13
+ # The Kdek key used in this session
14
+ attr_accessor :k_dek
15
+
16
+ # The challenge sent to the card, (Rterm)
17
+ attr_accessor :host_challenge
18
+
19
+ # The session key SKUenc
20
+ attr_accessor :sku_enc
21
+
22
+ attr_accessor :sku_mac
23
+ attr_accessor :sku_dek
24
+
25
+ # The card's response to initialize update containing:
26
+ # kmc_id (Identifier of the KMC)
27
+ # csn (Chip Serial Number)
28
+ # kmc_version (Version Number of Master key (KMC))
29
+ # sec_channel_proto_id (Identifier of Secure Channel Protocol)
30
+ # sequence_counter (Sequence Counter)
31
+ # challenge (Card challenge (r card))
32
+ # cryptogram (Card Cryptogram)
33
+
34
+ attr_reader :initialize_response
35
+
36
+ # The security level established by the EXTERNAL_AUTH command
37
+ # According to CPS Table 19
38
+ # One of:
39
+ # :enc_and_mac
40
+ # :mac
41
+ # :no_sec
42
+ attr_accessor :level
43
+
44
+
45
+ def initialize k_enc="\x00"*16, k_mac="\x00"*16, k_dek="\x00"*16, host_challenge="\x00"*8
46
+ @k_enc = k_enc
47
+ @k_mac = k_mac
48
+ @k_dek = k_dek
49
+
50
+ @host_challenge=host_challenge
51
+ @level = :no_sec
52
+ end
53
+
54
+ # Set the response returned by INITIALIZE UPDATE.
55
+ # * calculates the Session keys.
56
+ def initialize_response= resp
57
+
58
+ @initialize_response = resp
59
+
60
+
61
+ @sku_enc = EMV::Crypto.generate_session_key(k_enc,
62
+ @initialize_response.sequence_counter,
63
+ :enc)
64
+ @sku_mac = EMV::Crypto.generate_session_key(k_mac,
65
+ @initialize_response.sequence_counter,
66
+ :mac)
67
+
68
+ @sku_dek = EMV::Crypto.generate_session_key(k_dek,
69
+ @initialize_response.sequence_counter,
70
+ :dek)
71
+ end
72
+
73
+ # Verify the cryptogram sent by the card according to: CPS 3.2.5.10
74
+ def check_card_cryptogram
75
+ mac_ = host_challenge +
76
+ initialize_response.sequence_counter +
77
+ initialize_response.challenge
78
+
79
+ mac = EMV::Crypto.mac_for_personalization(sku_enc, mac_)
80
+ unless mac == initialize_response.cryptogram
81
+ raise %Q{
82
+ Invalid MAC returned from card!
83
+ host challenge: #{ISO7816.b2s(host_challenge)}
84
+ card seq : #{ISO7816.b2s(initialize_response.sequence_counter)}
85
+ card challenge: #{ISO7816.b2s(initialize_response.challenge)}
86
+ expected mac : #{ISO7816.b2s(mac)}
87
+ recv mac : #{ISO7816.b2s(initialize_response.cryptogram)}
88
+ k_enc : #{ISO7816.b2s(k_enc)}
89
+ k_mac : #{ISO7816.b2s(k_mac)}
90
+ k_dek : #{ISO7816.b2s(k_dek)}
91
+ }
92
+ end
93
+ end
94
+
95
+ # Calculates the host cryptogram according to CPS 3.2.6.6
96
+ def calculate_host_cryptogram
97
+ mac_ = initialize_response.sequence_counter +
98
+ initialize_response.challenge +
99
+ host_challenge
100
+ EMV::Crypto.mac_for_personalization(sku_enc, mac_)
101
+ end
102
+
103
+ # Calculate the C-MAC according to CP 5.4.2.2
104
+ def calculate_c_mac apdu
105
+ # data with placeholder for cmac
106
+ data = apdu.data
107
+ mac_ = apdu.cla +
108
+ apdu.ins +
109
+ apdu.p1 +
110
+ apdu.p2
111
+
112
+ mac_ << apdu.data.length+8
113
+ mac_ << data
114
+ if @c_mac # "prepend the c-mac computed for the previous command ..."
115
+ mac_ = @c_mac + mac_
116
+ end
117
+ @c_mac = EMV::Crypto.retail_mac(sku_mac, mac_)
118
+ @c_mac
119
+ end
120
+
121
+ # Retrieve the current seq number, this also increments the counter.
122
+ def store_data_seq_number
123
+ @store_data_seq_number ||= -1
124
+ @store_data_seq_number += 1
125
+ "" << @store_data_seq_number
126
+ end
127
+
128
+ def reset
129
+ @c_mac = nil
130
+ @store_data_seq_number = nil
131
+ @sku_enc = nil
132
+ @sku_mac = nil
133
+ @initialize_response = nil
134
+ end
135
+
136
+ # Encrypt data bytes according to CPS 5.5.2
137
+ def encrypt data
138
+ data = EMV::Crypto.pad data
139
+ cipher = OpenSSL::Cipher::Cipher.new("des-ede-cbc").encrypt
140
+ cipher.key = sku_enc
141
+ cipher.update data
142
+ end
143
+ end
144
+
145
+ class CPS_APDU < EMV::APDU::EMV_APDU
146
+ attr_accessor :secure_context
147
+
148
+ def initialize card, secure_context
149
+ super card
150
+ @secure_context= secure_context
151
+ end
152
+ end
153
+ class INITIALIZE_UPDATE < CPS_APDU
154
+ ins "\x50"
155
+
156
+ def key_version_number= kvn
157
+ self.p1= kvn
158
+ end
159
+ def send handle_more_data=true, card=nil
160
+ secure_context.reset
161
+ @data = secure_context.host_challenge
162
+
163
+ resp = super
164
+ if resp.status == "9000"
165
+ @secure_context.initialize_response = EMV::Data::InitializeUpdateData.new(resp.data)
166
+ @secure_context.check_card_cryptogram
167
+ end
168
+ resp
169
+ end
170
+ end
171
+
172
+ class C_MAC_APDU < CPS_APDU
173
+ # Provides the possibility to override the calculated c_mac
174
+ # with an arbitrary one for testing.
175
+ attr_writer :c_mac
176
+
177
+ def initialize card, secure_context
178
+ super
179
+ end
180
+
181
+ # calculate the c-mac according to 5.4.2.2
182
+ def c_mac
183
+ @c_mac ||= secure_context.calculate_c_mac(self)
184
+ @c_mac
185
+ end
186
+ end
187
+
188
+ class EXTERNAL_AUTHENTICATE < C_MAC_APDU
189
+ cla "\x84"
190
+ ins "\x82"
191
+ def initialize card, secure_context
192
+ super
193
+ self.security_level= secure_context.level if secure_context.level
194
+ end
195
+
196
+ #
197
+ # CPS 3.2.6
198
+ # :enc_and_mac
199
+ # :mac
200
+ # :no_sec
201
+ # or a sec level byte...
202
+ def security_level= level
203
+ case level
204
+ when :enc_and_mac
205
+ self.p1= 0x03
206
+ when :mac
207
+ self.p1= 0x01
208
+ when :no_sec
209
+ self.p1= 0x00
210
+ else
211
+ self.p1=level
212
+ end
213
+ @secure_context.level= level
214
+ end
215
+
216
+ #
217
+ # Sets the host cryptogram according to CPS 3.2.6.6
218
+ #
219
+ def cryptogram
220
+ @cryptogram ||= secure_context.calculate_host_cryptogram
221
+ @cryptogram
222
+ end
223
+
224
+ # Explicitly set a cryptogram, e.g. if an incorrect cryptogram is to be set
225
+ # for testing.
226
+ def cryptogram= bytes
227
+ @cryptogram= bytes
228
+ end
229
+
230
+
231
+ def send handle_more_data=true, card=nil
232
+ @data= self.cryptogram
233
+ @data+= c_mac # calculate the c_mac...
234
+ super
235
+ end
236
+
237
+
238
+ end
239
+ class STORE_DATA < C_MAC_APDU
240
+ ins "\xE2"
241
+
242
+ LAST_STORE_DATA_MASK = 0x80
243
+ ALL_DGI_ENC_MASK = 0x60
244
+ NO_DGI_ENC_MASK = 0x00
245
+ APP_DEPENDANT_MASK = 0x20
246
+
247
+ SECURE_MASK = 0x04
248
+
249
+ attr_reader :security_level
250
+
251
+ def initialize card, secure_context, data=""
252
+ super(card, secure_context)
253
+ #@security_level = secure_context.level
254
+ @cla= "\x84" if @security_level == :enc_and_mac
255
+ self.data= data
256
+ end
257
+ def security_level= level
258
+ @security_level = level
259
+ if [:enc_and_mac, :mac].include? level
260
+ secure
261
+ else
262
+ self.cla = 0x80
263
+ end
264
+ end
265
+ def secure
266
+ self.cla= cla[0] | SECURE_MASK
267
+ end
268
+ def secure?
269
+ (cla[0] & SECURE_MASK) == SECURE_MASK
270
+ end
271
+ def last_store_data
272
+ self.p1= (p1[0] | LAST_STORE_DATA_MASK)
273
+ end
274
+ def last_store_data?
275
+ (p1[0] & LAST_STORE_DATA_MASK) == LAST_STORE_DATA_MASK
276
+ end
277
+ def all_dgi_enc
278
+ self.p1= (p1[0] | ALL_DGI_ENC_MASK)
279
+ end
280
+ def all_dgi_enc?
281
+ (p1[0] & ALL_DGI_ENC_MASK) == ALL_DGI_ENC_MASK
282
+ end
283
+ def no_dgi_enc
284
+ self.p1= (p1[0] & ~ ALL_DGI_ENC_MASK)
285
+ end
286
+ def no_dgi_enc?
287
+ (p1[0] & NO_DGI_ENC_MASK) == NO_DGI_ENC_MASK
288
+ end
289
+ def app_dependant
290
+ self.p1= (p1[0] & ~ ALL_DGI_ENC_MASK) | APP_DEPENDANT_MASK
291
+ end
292
+ def app_dependant?
293
+ (p1[0] & APP_DEPENDANT_MASK) == APP_DEPENDANT_MASK
294
+ end
295
+
296
+ def send handle_more_data=true, card=nil
297
+
298
+ @p2 = secure_context.store_data_seq_number
299
+
300
+ # Secure Ctx security level may change in the course of a series of
301
+ # apdus, so we only no the current state just before sending.
302
+ self.security_level= @secure_context.level unless @security_level
303
+
304
+ unless @security_level == :no_sec
305
+ c_mac_ = self.c_mac # c_mac is calculated over unencrypted data
306
+ if @security_level == :enc_and_mac
307
+ @data= secure_context.encrypt(self.data)+c_mac_
308
+ else
309
+ @data= self.data+c_mac_
310
+ end
311
+ end
312
+ super
313
+ end
314
+
315
+ end
316
+
317
+ end #CPS
318
+ end # APDU
319
+ end #EMV