mfrc522 1.0.2 → 3.0.0

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.
@@ -1,4 +1,4 @@
1
- module Mifare
1
+ module MIFARE
2
2
  class Classic < ::PICC
3
3
  CMD_AUTH_KEY_A = 0x60 # Perform authentication with Key A
4
4
  CMD_AUTH_KEY_B = 0x61 # Perform authentication with Key B
@@ -8,10 +8,27 @@ module Mifare
8
8
  CMD_INCREMENT = 0xC1 # Increments the contents of a block and stores the result in the internal data register.
9
9
  CMD_RESTORE = 0xC2 # Reads the contents of a block into the internal data register.
10
10
  CMD_TRANSFER = 0xB0 # Writes the contents of the internal data register to a block.
11
+ MF_ACK = 0x0A # Mifare Acknowledge
12
+
13
+ def initialize(pcd, uid, sak)
14
+ super
15
+ # Set transceive timeout to 15ms
16
+ @pcd.internal_timer(50)
17
+ end
18
+
19
+ def transceive(send_data, accept_timeout = false)
20
+ received_data, valid_bits = picc_transceive(send_data, accept_timeout, true)
21
+ return if received_data.nil? && valid_bits.nil? && accept_timeout
22
+ unless valid_bits == 0
23
+ raise UnexpectedDataError, 'Incorrect Mifare ACK format' if received_data.size != 1 || valid_bits != 4 # ACK is 4 bits long
24
+ raise MifareNakError, "Mifare NAK detected: 0x#{received_data[0].to_bytehex}" if received_data[0] != MF_ACK
25
+ end
26
+ received_data
27
+ end
11
28
 
12
29
  def auth(block_addr, key = {})
13
30
  if key[:a].nil? && key[:b].nil?
14
- raise UnexpectedDataError, 'Missing key data'
31
+ raise UsageError, 'Missing key data'
15
32
  end
16
33
 
17
34
  if key[:a]
@@ -24,7 +41,7 @@ module Mifare
24
41
 
25
42
  key = [key].pack('H*').bytes
26
43
  if key.size != 6
27
- raise UnexpectedDataError, "Expect 6 bytes auth key, got: #{key.size} byte"
44
+ raise UsageError, "Expect 6 bytes auth key, got: #{key.size} byte"
28
45
  end
29
46
 
30
47
  @pcd.mifare_crypto1_authenticate(cmd, block_addr, key, @uid)
@@ -35,29 +52,33 @@ module Mifare
35
52
  end
36
53
 
37
54
  def read(block_addr)
38
- buffer = [CMD_READ, block_addr]
39
-
40
- @pcd.picc_transceive(buffer)
55
+ transceive([CMD_READ, block_addr])
41
56
  end
42
57
 
43
58
  def write(block_addr, send_data)
44
59
  if send_data.size != 16
45
- raise UnexpectedDataError, "Expect 16 bytes data, got: #{send_data.size} byte"
60
+ raise UsageError, "Expect 16 bytes data, got: #{send_data.size} byte"
46
61
  end
47
62
 
48
- buffer = [CMD_WRITE, block_addr]
49
-
50
63
  # Ask PICC if we can write to block_addr
51
- @pcd.picc_transceive(buffer)
64
+ transceive([CMD_WRITE, block_addr])
52
65
 
53
66
  # Then start transfer our data
54
- @pcd.picc_transceive(send_data)
67
+ transceive(send_data)
55
68
  end
56
69
 
57
70
  def read_value(block_addr)
58
71
  received_data = read(block_addr)
59
72
 
60
- received_data[0..3].to_sint
73
+ value = received_data[0..3].to_sint
74
+ value1 = ~(received_data[4..7].to_sint)
75
+ value2 = received_data[8..11].to_sint
76
+
77
+ if value != value1 || value != value2
78
+ raise UnexpectedDataError, 'Invalid value block'
79
+ end
80
+
81
+ value
61
82
  end
62
83
 
63
84
  def write_value(block_addr, value)
@@ -86,9 +107,9 @@ module Mifare
86
107
  buffer[10] = buffer[2]
87
108
  buffer[11] = buffer[3]
88
109
  buffer[12] = block_addr
89
- buffer[13] = ~block_addr
110
+ buffer[13] = ~buffer[12]
90
111
  buffer[14] = buffer[12]
91
- buffer[15] = buffer[13]
112
+ buffer[15] = ~buffer[12]
92
113
 
93
114
  write(block_addr, buffer)
94
115
  end
@@ -110,9 +131,7 @@ module Mifare
110
131
 
111
132
  # Transfer: Writes the contents of the internal Transfer Buffer to a value block
112
133
  def transfer(block_addr)
113
- buffer = [CMD_TRANSFER, block_addr]
114
-
115
- @pcd.picc_transceive(buffer)
134
+ transceive([CMD_TRANSFER, block_addr])
116
135
  end
117
136
 
118
137
  private
@@ -123,10 +142,10 @@ module Mifare
123
142
  send_data = [].append_uint(value, 4)
124
143
 
125
144
  # Ask PICC if we can write to block_addr
126
- @pcd.picc_transceive(buffer)
145
+ transceive(buffer)
127
146
 
128
147
  # Then start transfer our data
129
- @pcd.picc_transceive(send_data, true) # Accept timeout
148
+ transceive(send_data, true) # Accept timeout
130
149
  end
131
150
  end
132
151
  end
@@ -1,5 +1,5 @@
1
- module Mifare
2
- class DESFire < ::ISO144434
1
+ module MIFARE
2
+ class DESFire < ::PICC
3
3
  # Security Related Commands
4
4
  CMD_DES_AUTH = 0x1A # Authenticate with DES, 2K3DES, 3K3DES key
5
5
  CMD_AES_AUTH = 0xAA # Authenticate with AES-128 key
@@ -7,14 +7,18 @@ module Mifare
7
7
  CMD_CHANGE_KEY_SETTING = 0x54 # Changes the master key settings on PICC and application level.
8
8
  CMD_GET_KEY_VERSION = 0x64 # Reads out the current key version of any key stored on the PICC.
9
9
  CMD_CHANGE_KEY = 0xC4 # Changes any key stored on the PICC.
10
+ CMD_SET_CONFIGURATION = 0x5C # Configures and pre-personalizes the card with app default key, UID, and ATS string setup
10
11
 
11
12
  # PICC Level Commands
12
13
  CMD_CREATE_APP = 0xCA # Creates new applications on the PICC.
13
14
  CMD_DELETE_APP = 0xDA # Permanently deactivates applications on the PICC.
14
15
  CMD_GET_APP_IDS = 0x6A # Returns the Application IDentifiers of all applications on a PICC.
15
16
  CMD_SELECT_APP = 0x5A # Selects one specific application for further access.
17
+ CMD_FREE_MEMORY = 0x6E # Returns the free memory available on the card
18
+ CMD_GET_DF_NAMES = 0x6D # Returns the DF names
16
19
  CMD_GET_CARD_VERSION = 0x60 # Returns manufacturing related data of the PICC.
17
20
  CMD_FORMAT_CARD = 0xFC # Releases the PICC user memory.
21
+ CMD_GET_CARD_UID = 0x51 # Returns the UID
18
22
 
19
23
  # Application Level Commands
20
24
  CMD_GET_FILE_IDS = 0x6F # Returns the File IDentifiers of all active files within the currently selected application.
@@ -67,45 +71,46 @@ module Mifare
67
71
  KEY_TYPE = {'des-ede-cbc' => 0x00, 'des-ede3-cbc' => 0x40, 'aes-128-cbc' => 0x80}
68
72
 
69
73
  KEY_SETTING = Struct.new(
70
- # Key number(0x00~0x0D) required for `change_key`
71
- # 0x0E means same key, 0x0F freezes all keys
74
+ # Key number required for `change_key`
75
+ # 0x00 means only master key is accepted
76
+ # 0x01~0x0D chooses an app key to change any key
77
+ # 0x0E means keys can only change themselves
78
+ # 0x0F freezes all keys, except master key
79
+ # to freeze master key, use :masterkey_changeable
72
80
  :privileged_key,
73
81
  # Set if master key can be modified
74
- :mk_changeable,
75
- # Set if listing requires master key
76
- :listing_without_mk,
77
- # Set if create or delete requires master key
78
- :create_delete_without_mk,
82
+ :masterkey_changeable,
83
+ # Set if file management requires auth
84
+ :file_management_without_auth,
85
+ # Set if file configuration requires auth
86
+ :file_configurable_without_auth,
79
87
  # Set if this setting can be modified
80
- :configuration_changeable) do
81
- def initialize(*data)
88
+ :key_setting_changeable) do
89
+ def initialize(*)
82
90
  super
83
- default
91
+ self.privileged_key = 0 if self.privileged_key.nil?
92
+ self.masterkey_changeable = true if self.masterkey_changeable.nil?
93
+ self.file_management_without_auth = true if self.file_management_without_auth.nil?
94
+ self.file_configurable_without_auth = true if self.file_configurable_without_auth.nil?
95
+ self.key_setting_changeable = true if self.key_setting_changeable.nil?
84
96
  end
85
97
 
86
- def default
87
- self[:privileged_key] = 0 unless privileged_key
88
- self[:mk_changeable] = true unless mk_changeable
89
- self[:listing_without_mk] = true unless listing_without_mk
90
- self[:create_delete_without_mk] = true unless create_delete_without_mk
91
- self[:configuration_changeable] = true unless configuration_changeable
98
+ def self.import(byte)
99
+ self.new(
100
+ (byte >> 4) & 0x0F,
101
+ byte & 0x01 != 0,
102
+ byte & 0x02 != 0,
103
+ byte & 0x04 != 0,
104
+ byte & 0x08 != 0
105
+ )
92
106
  end
93
107
 
94
- def import(byte)
95
- self[:privileged_key] = (byte >> 4) & 0x0F
96
- self[:mk_changeable] = byte & 0x01 != 0
97
- self[:listing_without_mk] = (byte >> 1) & 0x01 != 0
98
- self[:create_delete_without_mk] = (byte >> 2) & 0x01 != 0
99
- self[:configuration_changeable] = (byte >> 3) & 0x01 != 0
100
- self
101
- end
102
-
103
- def to_uint
104
- output = (privileged_key << 4)
105
- output |= 0x01 if mk_changeable
106
- output |= 0x02 if listing_without_mk
107
- output |= 0x04 if create_delete_without_mk
108
- output |= 0x08 if configuration_changeable
108
+ def export
109
+ output = (self.privileged_key << 4)
110
+ output |= 0x01 if self.masterkey_changeable
111
+ output |= 0x02 if self.file_management_without_auth
112
+ output |= 0x04 if self.file_configurable_without_auth
113
+ output |= 0x08 if self.key_setting_changeable
109
114
  output
110
115
  end
111
116
  end
@@ -119,15 +124,16 @@ module Mifare
119
124
 
120
125
  # value 0x00 ~ 0x0D are key numbers, 0x0E grants free access, 0x0F always denies access
121
126
  FILE_PERMISSION = Struct.new(:read_access, :write_access, :read_write_access, :change_access) do
122
- def import(byte)
123
- self[:change_access] = byte & 0x0F
124
- self[:read_write_access] = (byte >> 4) & 0x0F
125
- self[:write_access] = (byte >> 8) & 0x0F
126
- self[:read_access] = (byte >> 12) & 0x0F
127
- self
127
+ def self.import(byte)
128
+ self.new(
129
+ (byte >> 12) & 0x0F,
130
+ (byte >> 8) & 0x0F,
131
+ (byte >> 4) & 0x0F,
132
+ byte & 0x0F
133
+ )
128
134
  end
129
135
 
130
- def to_uint
136
+ def export
131
137
  (read_access << 12) | (write_access << 8) | (read_write_access << 4) | change_access
132
138
  end
133
139
  end
@@ -160,7 +166,6 @@ module Mifare
160
166
  def initialize(pcd, uid, sak)
161
167
  super
162
168
  invalid_auth
163
- @cmac_buffer = []
164
169
  @selected_app = false
165
170
  end
166
171
 
@@ -168,12 +173,16 @@ module Mifare
168
173
  @authed.is_a? Numeric
169
174
  end
170
175
 
176
+ def select
177
+ iso_select
178
+ end
179
+
171
180
  def deselect
172
- super
173
181
  invalid_auth
182
+ iso_deselect
174
183
  end
175
184
 
176
- def transceive(cmd: , plain_data: [], data: [], tx: nil, rx: nil, expect: nil, return_data: nil, receive_all: nil, receive_length: nil)
185
+ def transceive(cmd: , plain_data: [], data: [], tx: nil, rx: nil, expect: nil, return_data: nil, receive_length: nil, encrypt_padding: nil)
177
186
  # Session key is needed for encryption
178
187
  if (tx == :encrypt || rx == :encrypt) && !@authed
179
188
  raise UnauthenticatedError
@@ -185,6 +194,10 @@ module Mifare
185
194
 
186
195
  buffer = [cmd] + plain_data
187
196
 
197
+ if @authed
198
+ @session_key.padding_mode(encrypt_padding || 1)
199
+ end
200
+
188
201
  if tx == :encrypt
189
202
  # Calculate CRC on whole frame
190
203
  data.append_uint(crc32(buffer, data), 4)
@@ -194,22 +207,21 @@ module Mifare
194
207
 
195
208
  buffer.concat(data)
196
209
 
197
- if (tx == :cmac || tx == :send_cmac) && cmd != CMD_ADDITIONAL_FRAME && @authed
198
- @cmac_buffer = buffer
199
- cmac = @session_key.calculate_cmac(@cmac_buffer)
210
+ if tx != :encrypt && tx != :none && cmd != CMD_ADDITIONAL_FRAME && @authed
211
+ cmac = @session_key.calculate_cmac(buffer)
200
212
  # Only first 8 bytes of CMAC are transmitted
201
- buffer.concat(cmac[0..7]) if tx == :send_cmac
213
+ buffer.concat(cmac[0..7]) if tx == :mac
202
214
  end
203
215
 
204
216
  received_data = []
205
217
  card_status = nil
206
218
  loop do
207
- receive_buffer = super(buffer.shift(@max_inf_size))
219
+ receive_buffer = iso_transceive(buffer.shift(@max_inf_size))
208
220
 
209
221
  card_status = receive_buffer.shift
210
222
  received_data.concat(receive_buffer)
211
223
 
212
- break if card_status != ST_ADDITIONAL_FRAME || (buffer.empty? && !receive_all)
224
+ break if card_status != ST_ADDITIONAL_FRAME || (buffer.empty? && expect == ST_ADDITIONAL_FRAME)
213
225
 
214
226
  buffer.unshift(CMD_ADDITIONAL_FRAME)
215
227
  end
@@ -218,72 +230,59 @@ module Mifare
218
230
 
219
231
  unless error_msg.empty?
220
232
  invalid_auth
221
- raise ReceiptStatusError, "0x#{card_status.to_s(16).rjust(2, '0').upcase} - #{error_msg}"
233
+ raise ReceiptStatusError, "0x#{card_status.to_bytehex} - #{error_msg}"
222
234
  end
223
235
 
224
236
  if expect && expect != card_status
225
237
  raise UnexpectedDataError, 'Card status does not match expected value'
226
238
  end
227
239
 
228
- if (rx == :cmac || rx == :send_cmac) && (card_status == ST_SUCCESS || card_status == ST_ADDITIONAL_FRAME) && @authed
229
- @cmac_buffer = [] if cmd != CMD_ADDITIONAL_FRAME
230
- @cmac_buffer.concat(received_data) if card_status == ST_ADDITIONAL_FRAME
231
-
232
- if received_data.size >= 8 && card_status == ST_SUCCESS
233
- received_cmac = received_data.pop(8)
234
- @cmac_buffer.concat(received_data + [card_status])
235
- cmac = @session_key.calculate_cmac(@cmac_buffer)
236
- # Only first 8 bytes of CMAC are transmitted
237
- if cmac[0..7] != received_cmac
238
- raise ReceiptIntegrityError
239
- end
240
- end
241
- end
242
-
243
240
  if rx == :encrypt
244
241
  if receive_length.nil?
245
- raise UnexpectedDataError, 'Lack of receive length for removing padding'
242
+ raise UsageError, 'Lack of receive length for removing padding'
246
243
  end
247
- received_data = @session_key.decrypt(received_data)
248
- received_data = remove_padding_bytes(received_data, receive_length)
244
+ @session_key.padding_mode((receive_length > 0) ? 1 : 2)
245
+ receive_length += 4 # CRC32
246
+ received_data = @session_key.decrypt(received_data, data_length: receive_length)
249
247
  received_crc = received_data.pop(4).to_uint
250
248
  crc = crc32(received_data, card_status)
251
249
  if crc != received_crc
252
250
  raise ReceiptIntegrityError
253
251
  end
254
- end
255
-
256
- if expect
257
- if received_data.empty? && !return_data
258
- return true
259
- else
260
- return received_data
252
+ elsif rx != :none && @authed && received_data.size >= 8 && card_status == ST_SUCCESS
253
+ received_cmac = received_data.pop(8)
254
+ cmac = @session_key.calculate_cmac(received_data + [card_status])
255
+ # Only first 8 bytes of CMAC are transmitted
256
+ if rx == :mac && cmac[0..7] != received_cmac
257
+ raise ReceiptIntegrityError
261
258
  end
262
259
  end
263
260
 
264
- return card_status, received_data
261
+ received_data
265
262
  end
266
263
 
267
264
  def auth(key_number, auth_key)
268
265
  cmd = (auth_key.type == :des) ? CMD_DES_AUTH : CMD_AES_AUTH
266
+ rand_size = (auth_key.cipher_suite == 'des-ede-cbc') ? 8 : 16
269
267
  auth_key.clear_iv
268
+ auth_key.padding_mode(1)
270
269
 
271
270
  # Ask for authentication
272
- received_data = transceive(cmd: cmd, data: key_number, expect: ST_ADDITIONAL_FRAME)
271
+ received_data = transceive(cmd: cmd, data: key_number, expect: ST_ADDITIONAL_FRAME, tx: :none, rx: :none)
273
272
 
274
273
  # Receive challenge from DESFire
275
- challenge = auth_key.decrypt(received_data)
274
+ challenge = auth_key.decrypt(received_data, data_length: rand_size)
276
275
  challenge_rot = challenge.rotate
277
276
 
278
277
  # Generate random number and encrypt it with rotated challenge
279
- random_number = SecureRandom.random_bytes(received_data.size).bytes
278
+ random_number = SecureRandom.random_bytes(rand_size).bytes
280
279
  response = auth_key.encrypt(random_number + challenge_rot)
281
280
 
282
281
  # Send challenge response
283
- received_data = transceive(cmd: CMD_ADDITIONAL_FRAME, data: response, expect: ST_SUCCESS)
282
+ received_data = transceive(cmd: CMD_ADDITIONAL_FRAME, data: response, tx: :none, rx: :none)
284
283
 
285
284
  # Check if verification matches rotated random_number
286
- verification = auth_key.decrypt(received_data)
285
+ verification = auth_key.decrypt(received_data, data_length: rand_size)
287
286
 
288
287
  if random_number.rotate != verification
289
288
  halt
@@ -314,7 +313,7 @@ module Mifare
314
313
  end
315
314
 
316
315
  def get_app_ids
317
- ids = transceive(cmd: CMD_GET_APP_IDS, tx: :cmac, rx: :cmac, expect: ST_SUCCESS, return_data: true, receive_all: true)
316
+ ids = transceive(cmd: CMD_GET_APP_IDS, rx: :mac)
318
317
 
319
318
  return ids if ids.empty?
320
319
 
@@ -329,7 +328,7 @@ module Mifare
329
328
  end
330
329
 
331
330
  def select_app(id)
332
- transceive(cmd: CMD_SELECT_APP, data: convert_app_id(id), expect: ST_SUCCESS)
331
+ transceive(cmd: CMD_SELECT_APP, data: convert_app_id(id))
333
332
 
334
333
  invalid_auth
335
334
  @selected_app = id
@@ -337,21 +336,21 @@ module Mifare
337
336
 
338
337
  def create_app(id, key_setting, key_count, cipher_suite)
339
338
  raise UnauthenticatedError unless @authed
340
- raise UnexpectedDataError, 'An application can only hold up to 14 keys.' if key_count > 14
339
+ raise UsageError, 'An application can only hold up to 14 keys.' if key_count > 14
341
340
 
342
- buffer = convert_app_id(id) + [key_setting.to_uint, KEY_TYPE.fetch(cipher_suite) | key_count]
341
+ buffer = convert_app_id(id) + [key_setting.export, KEY_TYPE.fetch(cipher_suite) | key_count]
343
342
 
344
- transceive(cmd: CMD_CREATE_APP, data: buffer, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
343
+ transceive(cmd: CMD_CREATE_APP, data: buffer, rx: :mac)
345
344
  end
346
345
 
347
346
  def delete_app(id)
348
347
  raise UnauthenticatedError unless @authed
349
348
 
350
- transceive(cmd: CMD_DELETE_APP, data: convert_app_id(id), tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
349
+ transceive(cmd: CMD_DELETE_APP, data: convert_app_id(id))
351
350
  end
352
351
 
353
352
  def get_card_version
354
- version = transceive(cmd: CMD_GET_CARD_VERSION, tx: :cmac, rx: :cmac, expect: ST_SUCCESS, receive_all: true)
353
+ version = transceive(cmd: CMD_GET_CARD_VERSION)
355
354
 
356
355
  CARD_VERSION.new(
357
356
  version[0], version[1], version[2], version[3], version[4], 1 << (version[5] / 2), version[6],
@@ -360,22 +359,63 @@ module Mifare
360
359
  )
361
360
  end
362
361
 
362
+ def get_free_memory
363
+ transceive(cmd: CMD_FREE_MEMORY)
364
+ end
365
+
366
+ def get_df_names
367
+ raise UsageError, 'App 0 should be selected before calling' unless @selected_app == 0
368
+
369
+ transceive(cmd: CMD_GET_DF_NAMES)
370
+ end
371
+
372
+ def get_card_uid
373
+ raise UnauthenticatedError unless @authed
374
+
375
+ transceive(cmd: CMD_GET_CARD_UID, rx: :encrypt, receive_length: 7)
376
+ end
377
+
363
378
  def format_card
364
379
  raise UnauthenticatedError unless @authed
365
380
 
366
- transceive(cmd: CMD_FORMAT_CARD, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
381
+ transceive(cmd: CMD_FORMAT_CARD)
382
+ end
383
+
384
+ def set_configuration_byte(disable_format, enable_random_uid)
385
+ raise UnauthenticatedError unless @authed
386
+
387
+ flag = 0
388
+ flag |= 0x01 if disable_format
389
+ flag |= 0x02 if enable_random_uid
390
+
391
+ transceive(cmd: CMD_SET_CONFIGURATION, plain_data: 0x00, data: [flag], tx: :encrypt)
392
+ end
393
+
394
+ def set_default_key(key)
395
+ raise UnauthenticatedError unless @authed
396
+
397
+ buffer = key.key
398
+ buffer.append_uint(key.version, 1)
399
+
400
+ transceive(cmd: CMD_SET_CONFIGURATION, plain_data: 0x01, data: buffer, tx: :encrypt)
401
+ end
402
+
403
+ def set_ats(ats)
404
+ raise UnauthenticatedError unless @authed
405
+
406
+ transceive(cmd: CMD_SET_CONFIGURATION, plain_data: 0x02, data: ats, tx: :encrypt, encrypt_padding: 2)
367
407
  end
368
408
 
369
409
  def get_key_version(key_number)
370
- received_data = transceive(cmd: CMD_GET_KEY_VERSION, data: key_number, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
410
+ received_data = transceive(cmd: CMD_GET_KEY_VERSION, data: key_number, rx: :mac)
371
411
 
372
412
  received_data[0]
373
413
  end
374
414
 
375
415
  def change_key(key_number, new_key, curr_key = nil)
376
416
  raise UnauthenticatedError unless @authed
377
- raise UnexpectedDataError, 'Invalid key number' if key_number > 13
378
-
417
+ raise UsageError, 'Invalid key number' if key_number > 13
418
+
379
419
  cryptogram = new_key.key
380
420
 
381
421
  same_key = (key_number == @authed)
@@ -385,7 +425,7 @@ module Mifare
385
425
 
386
426
  # XOR new key if we're using different one
387
427
  unless same_key
388
- cryptogram = cryptogram.zip(curr_key.key).map{|x, y| x ^ y }
428
+ cryptogram = cryptogram.xor(curr_key.key)
389
429
  end
390
430
 
391
431
  # AES stores key version separately
@@ -400,18 +440,19 @@ module Mifare
400
440
  end
401
441
 
402
442
  # Encrypt cryptogram
443
+ @session_key.padding_mode(1)
403
444
  buffer = [key_number] + @session_key.encrypt(cryptogram)
404
445
 
446
+ transceive(cmd: CMD_CHANGE_KEY, data: buffer, tx: :none, rx: :mac)
447
+
405
448
  # Change current used key will revoke authentication
406
449
  invalid_auth if same_key
407
-
408
- transceive(cmd: CMD_CHANGE_KEY, data: buffer, rx: :cmac, expect: ST_SUCCESS)
409
450
  end
410
451
 
411
452
  def get_key_setting
412
- received_data = transceive(cmd: CMD_GET_KEY_SETTING, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
453
+ received_data = transceive(cmd: CMD_GET_KEY_SETTING)
413
454
 
414
- { key_setting: KEY_SETTING.new.import(received_data[0]),
455
+ { key_setting: KEY_SETTING.import(received_data[0]),
415
456
  key_count: received_data[1] & 0x0F,
416
457
  key_type: KEY_TYPE.key(received_data[1] & 0xF0) }
417
458
  end
@@ -419,11 +460,11 @@ module Mifare
419
460
  def change_key_setting(key_setting)
420
461
  raise UnauthenticatedError unless @authed
421
462
 
422
- transceive(cmd: CMD_CHANGE_KEY_SETTING, data: key_setting.to_uint, tx: :encrypt, rx: :cmac, expect: ST_SUCCESS)
463
+ transceive(cmd: CMD_CHANGE_KEY_SETTING, data: key_setting.export, tx: :encrypt, rx: :mac)
423
464
  end
424
465
 
425
466
  def get_file_ids
426
- transceive(cmd: CMD_GET_FILE_IDS, tx: :cmac, rx: :cmac, expect: ST_SUCCESS, return_data: true)
467
+ transceive(cmd: CMD_GET_FILE_IDS)
427
468
  end
428
469
 
429
470
  def file_exist?(id)
@@ -431,12 +472,12 @@ module Mifare
431
472
  end
432
473
 
433
474
  def get_file_setting(id)
434
- received_data = transceive(cmd: CMD_GET_FILE_SETTING, data: id, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
475
+ received_data = transceive(cmd: CMD_GET_FILE_SETTING, data: id)
435
476
 
436
477
  file_setting = FILE_SETTING.new
437
478
  file_setting.type = FILE_TYPE.key(received_data.shift)
438
479
  file_setting.communication = FILE_COMMUNICATION.key(received_data.shift)
439
- file_setting.permission = FILE_PERMISSION.new.import(received_data.shift(2).to_uint)
480
+ file_setting.permission = FILE_PERMISSION.import(received_data.shift(2).to_uint)
440
481
 
441
482
  case file_setting.type
442
483
  when :std_data_file, :backup_data_file
@@ -458,15 +499,15 @@ module Mifare
458
499
  def change_file_setting(id, file_setting)
459
500
  buffer = []
460
501
  buffer.append_uint(FILE_COMMUNICATION.fetch(file_setting.communication), 1)
461
- buffer.append_uint(file_setting.permission.to_uint, 2)
502
+ buffer.append_uint(file_setting.permission.export, 2)
462
503
 
463
- transceive(cmd: CMD_CHANGE_FILE_SETTING, plain_data: id, data: buffer, tx: :encrypt, rx: :cmac, expect: ST_SUCCESS)
504
+ transceive(cmd: CMD_CHANGE_FILE_SETTING, plain_data: id, data: buffer, tx: :encrypt)
464
505
  end
465
506
 
466
507
  def create_file(id, file_setting)
467
508
  buffer = [id]
468
509
  buffer.append_uint(FILE_COMMUNICATION.fetch(file_setting.communication), 1)
469
- buffer.append_uint(file_setting.permission.to_uint, 2)
510
+ buffer.append_uint(file_setting.permission.export, 2)
470
511
 
471
512
  case file_setting.type
472
513
  when :std_data_file, :backup_data_file
@@ -483,22 +524,22 @@ module Mifare
483
524
 
484
525
  cmd = self.class.const_get("CMD_CREATE_#{file_setting.type.to_s.upcase}")
485
526
 
486
- transceive(cmd: cmd, data: buffer, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
527
+ transceive(cmd: cmd, data: buffer)
487
528
  end
488
529
 
489
530
  def delete_file(id)
490
- transceive(cmd: CMD_DELETE_FILE, data: id, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
531
+ transceive(cmd: CMD_DELETE_FILE, data: id)
491
532
  end
492
533
 
493
534
  def read_file(id, cmd, data, length)
494
535
  file_setting = get_file_setting(id)
495
536
  length *= file_setting.record_size if file_setting.record_size
496
- transceive(cmd: cmd, data: data, tx: :cmac, rx: convert_file_communication(file_setting.communication), expect: ST_SUCCESS, receive_all: true, receive_length: length)
537
+ transceive(cmd: cmd, data: data, rx: file_setting.communication, receive_length: length)
497
538
  end
498
539
 
499
540
  def write_file(id, cmd, plain_data, data)
500
541
  file_setting = get_file_setting(id)
501
- transceive(cmd: cmd, plain_data: plain_data, data: data, tx: convert_file_communication(file_setting.communication), rx: :cmac, expect: ST_SUCCESS)
542
+ transceive(cmd: cmd, plain_data: plain_data, data: data, tx: file_setting.communication)
502
543
  end
503
544
 
504
545
  def read_data(id, offset, length)
@@ -524,7 +565,7 @@ module Mifare
524
565
  end
525
566
 
526
567
  def credit_value(id, delta)
527
- raise UnexpectedDataError, 'Negative number is not allowed.' if delta < 0
568
+ raise UsageError, 'Negative number is not allowed.' if delta < 0
528
569
 
529
570
  buffer = []
530
571
  buffer.append_sint(delta, 4)
@@ -533,7 +574,7 @@ module Mifare
533
574
  end
534
575
 
535
576
  def debit_value(id, delta)
536
- raise UnexpectedDataError, 'Negative number is not allowed.' if delta < 0
577
+ raise UsageError, 'Negative number is not allowed.' if delta < 0
537
578
 
538
579
  buffer = []
539
580
  buffer.append_sint(delta, 4)
@@ -542,7 +583,7 @@ module Mifare
542
583
  end
543
584
 
544
585
  def limited_credit_value(id, delta)
545
- raise UnexpectedDataError, 'Negative number is not allowed.' if delta < 0
586
+ raise UsageError, 'Negative number is not allowed.' if delta < 0
546
587
 
547
588
  buffer = []
548
589
  buffer.append_sint(delta, 4)
@@ -569,15 +610,15 @@ module Mifare
569
610
  end
570
611
 
571
612
  def clear_record(id)
572
- transceive(cmd: CMD_CLEAR_RECORD_FILE, data: id, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
613
+ transceive(cmd: CMD_CLEAR_RECORD_FILE, data: id)
573
614
  end
574
615
 
575
616
  def commit_transaction
576
- transceive(cmd: CMD_COMMIT_TRANSACTION, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
617
+ transceive(cmd: CMD_COMMIT_TRANSACTION)
577
618
  end
578
619
 
579
620
  def abort_transaction
580
- transceive(cmd: CMD_ABORT_TRANSACTION, tx: :cmac, rx: :cmac, expect: ST_SUCCESS)
621
+ transceive(cmd: CMD_ABORT_TRANSACTION)
581
622
  end
582
623
 
583
624
  private
@@ -585,56 +626,14 @@ module Mifare
585
626
  def invalid_auth
586
627
  @authed = false
587
628
  @session_key = nil
588
- @cmac_buffer = []
589
629
  end
590
630
 
591
631
  def convert_app_id(id)
592
- raise UnexpectedDataError, 'Application ID overflow' if id < 0 || id >= (1 << 24)
632
+ raise UsageError, 'Application ID overflow' if id < 0 || id >= (1 << 24)
593
633
 
594
634
  [].append_uint(id, 3)
595
635
  end
596
636
 
597
- def crc32(*datas)
598
- crc = 0xFFFFFFFF
599
-
600
- datas.each do |data|
601
- data = [data] unless data.is_a? Array
602
- data.each do |byte|
603
- crc ^= byte
604
- 8.times do
605
- flag = crc & 0x01 > 0
606
- crc >>= 1
607
- crc ^= 0xEDB88320 if flag
608
- end
609
- end
610
- end
611
- crc
612
- end
613
-
614
- # Remove trailing padding bytes
615
- def remove_padding_bytes(data, length)
616
- if length == 0
617
- # padding format according to ISO 9797-1
618
- str = data.pack('C*')
619
- str.sub! /#{0x80.chr}#{0x00.chr}*\z/, ''
620
- str.bytes
621
- else
622
- # data length + 4 bytes CRC
623
- data[0...length + 4]
624
- end
625
- end
626
-
627
- def convert_file_communication(communication)
628
- case communication
629
- when :plain
630
- :cmac
631
- when :mac
632
- :send_cmac
633
- when :encrypt
634
- :encrypt
635
- end
636
- end
637
-
638
637
  def check_status_code(code)
639
638
  case code
640
639
  when ST_SUCCESS, ST_ADDITIONAL_FRAME