mfrc522 1.0.6 → 2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6970a8238b5e32859472bec37f4a0c7327dd8bf7
4
- data.tar.gz: cbeac674373f82caa74e5fc91179176bb2c40c86
3
+ metadata.gz: 4c15749e6896a2e58865f9e6639bc829fa717068
4
+ data.tar.gz: e340cba7fdc0810a3d581b8a14d329bba72886e4
5
5
  SHA512:
6
- metadata.gz: edbb612b4d103e712eff156431ebdcc55b601cbf74a666d39a34b8246f027d5cfd5d2a938fcb4b33612f7970187bd68983e5d391c05b3eb586a33374e8b9f02e
7
- data.tar.gz: 3affc064c30c146115c0c0d1894fd1e71a519af1557700178b07ba1a5e3655e55a0102e989f6c3e4a3da04e1164dbe7a56604b997d46e0c5364aa3a6ba30a753
6
+ metadata.gz: 7b88ed0c68070b2bafe8e3bd7e25304852a24bd88c609b1688b2c5e1ce830114c3540801482c7aee5276cd357226dfb49884fea1c9c79fba56e6a9c47a73d293
7
+ data.tar.gz: 94e20de85a0185679b75a2902516406f6e5ed499433f5195f26493621ddac779932ca3c61b88a2c487a044db1e0ac56a489d9ec4a21aae47b842b190d782d98f
@@ -32,4 +32,42 @@ class Array
32
32
  sign = (self.last & 0x80 != 0) ? (-1 ^ ((1 << ((self.size * 8) - 1)) - 1)) : 0
33
33
  sign | self.to_uint
34
34
  end
35
+
36
+ def append_crc16
37
+ append_uint(crc16, 2)
38
+ end
39
+
40
+ def check_crc16(remove_after_check = false)
41
+ orig_crc = pop(2)
42
+ old_crc = (orig_crc[1] << 8) + orig_crc[0]
43
+ new_crc = crc16
44
+ concat(orig_crc) unless remove_after_check
45
+ old_crc == new_crc
46
+ end
47
+
48
+ def xor(array2)
49
+ zip(array2).map{|x, y| x ^ y }
50
+ end
51
+
52
+ def to_bytehex
53
+ map{|x| x.to_bytehex}
54
+ end
55
+
56
+ private
57
+
58
+ def crc16
59
+ crc = 0x6363
60
+ self.each do |byte|
61
+ bb = (byte ^ crc) & 0xFF
62
+ bb = (bb ^ (bb << 4)) & 0xFF
63
+ crc = (crc >> 8) ^ (bb << 8) ^ (bb << 3) ^ (bb >> 4)
64
+ end
65
+ crc & 0xFFFF
66
+ end
67
+ end
68
+
69
+ class Numeric
70
+ def to_bytehex
71
+ self.to_s(16).rjust(2, '0').upcase
72
+ end
35
73
  end
@@ -5,6 +5,7 @@ class IncorrectCRCError < CommunicationError; end
5
5
  class CollisionError < CommunicationError; end
6
6
 
7
7
  class UnexpectedDataError < StandardError; end
8
+ class UsageError < StandardError; end
8
9
 
9
10
  class MifareNakError < StandardError; end
10
11
 
@@ -7,13 +7,14 @@ require 'core_ext'
7
7
  require 'exceptions'
8
8
 
9
9
  require 'picc'
10
- require 'iso144434'
11
10
 
12
11
  require 'mifare/key'
13
12
  require 'mifare/classic'
13
+ require 'mifare/plus'
14
+ require 'mifare/des_fire'
14
15
  require 'mifare/ultralight'
15
16
  require 'mifare/ultralight_c'
16
- require 'mifare/des_fire'
17
+ require 'mifare/ultralight_ev1'
17
18
 
18
19
  include PiPiper
19
20
 
@@ -27,8 +28,6 @@ class MFRC522
27
28
  PICC_SEL_CL2 = 0x95 # Anti collision/Select, Cascade Level 2
28
29
  PICC_SEL_CL3 = 0x97 # Anti collision/Select, Cascade Level 3
29
30
  PICC_HLTA = 0x50 # HaLT command, Type A. Instructs an ACTIVE PICC to go to state HALT.
30
- # Mifare Acknowledge
31
- PICC_MF_ACK = 0x0A
32
31
 
33
32
  # PCD commands
34
33
  PCD_Idle = 0x00 # no action, cancels current command execution
@@ -135,6 +134,12 @@ class MFRC522
135
134
 
136
135
  # Reset PCD config to default
137
136
  def pcd_config_reset
137
+ # Stop current command
138
+ write_spi(CommandReg, PCD_Idle)
139
+
140
+ # Stop crypto1 communication
141
+ mifare_crypto1_deauthenticate
142
+
138
143
  # Clear ValuesAfterColl bit
139
144
  write_spi_clear_bitmask(CollReg, 0x80)
140
145
 
@@ -159,10 +164,13 @@ class MFRC522
159
164
  # value = 0: 106kBd, 1: 212kBd, 2: 424kBd, 3: 848kBd
160
165
  def transceiver_baud_rate(direction, value = nil)
161
166
  reg = {tx: TxModeReg, rx: RxModeReg}
167
+ mod = {0 => 0x26, 1 => 0x15, 2 => 0x0A, 3 => 0x05}
162
168
 
163
169
  if value
170
+ @built_in_crc_disabled = (value == 0)
171
+ write_spi(ModWidthReg, mod.fetch(value))
164
172
  value <<= 4
165
- value |= 0x80 if value != 0
173
+ value |= 0x80 unless @built_in_crc_disabled
166
174
  write_spi(reg.fetch(direction), value)
167
175
  end
168
176
 
@@ -189,6 +197,10 @@ class MFRC522
189
197
  (read_spi(RFCfgReg) & 0x70) >> 4
190
198
  end
191
199
 
200
+ def buffer_size
201
+ 64
202
+ end
203
+
192
204
  # Wakes PICC from HALT or IDLE to ACTIVE state
193
205
  # Accept PICC_REQA and PICC_WUPA command
194
206
  def picc_request(picc_command)
@@ -201,7 +213,7 @@ class MFRC522
201
213
 
202
214
  # Instruct PICC in ACTIVE state go to HALT state
203
215
  def picc_halt
204
- buffer = append_crc([PICC_HLTA, 0])
216
+ buffer = [PICC_HLTA, 0].append_crc16
205
217
 
206
218
  status, _received_data, _valid_bits = communicate_with_picc(PCD_Transceive, buffer)
207
219
 
@@ -213,7 +225,7 @@ class MFRC522
213
225
  # Select PICC for further communication
214
226
  #
215
227
  # PICC must be in state ACTIVE
216
- def picc_select
228
+ def picc_select(disable_anticollision = false)
217
229
  # Description of buffer structure:
218
230
  #
219
231
  # Byte 0: SEL Indicates the Cascade Level: PICC_CMD_SEL_CL1, PICC_CMD_SEL_CL2 or PICC_CMD_SEL_CL3
@@ -281,7 +293,7 @@ class MFRC522
281
293
  buffer[6] = (buffer[2] ^ buffer[3] ^ buffer[4] ^ buffer[5]) # Block Check Character
282
294
 
283
295
  # Append CRC to buffer
284
- buffer = append_crc(buffer)
296
+ buffer.append_crc16
285
297
  else
286
298
  tx_last_bits = current_level_known_bits % 8
287
299
  uid_full_byte = current_level_known_bits / 8
@@ -299,6 +311,8 @@ class MFRC522
299
311
 
300
312
  if status != :status_ok && status != :status_collision
301
313
  raise CommunicationError, status
314
+ elsif status == :status_collision && disable_anticollision
315
+ raise CollisionError
302
316
  end
303
317
 
304
318
  if received_data.empty?
@@ -356,7 +370,7 @@ class MFRC522
356
370
  # Check the result of full select
357
371
  # Select Acknowledge is 1 byte + CRC16
358
372
  raise UnexpectedDataError, 'Unknown SAK format' if received_data.size != 3 || valid_bits != 0
359
- raise IncorrectCRCError unless check_crc(received_data)
373
+ raise IncorrectCRCError unless received_data.check_crc16(true)
360
374
 
361
375
  sak = received_data[0]
362
376
  break if (sak & 0x04) == 0 # No more cascade level
@@ -380,54 +394,6 @@ class MFRC522
380
394
  status && uid == new_uid
381
395
  end
382
396
 
383
- # Lookup PICC name using sak
384
- def identify_model(sak)
385
- # SAK coding separation reference:
386
- # http://cache.nxp.com/documents/application_note/AN10833.pdf
387
- # http://www.nxp.com/documents/application_note/130830.pdf
388
- if sak & 0x04 != 0
389
- return :picc_uid_not_complete
390
- end
391
-
392
- if sak & 0x02 != 0
393
- return :picc_reserved_future_use
394
- end
395
-
396
- if sak & 0x08 != 0
397
- if sak & 0x10 != 0
398
- return :picc_mifare_4k
399
- end
400
-
401
- if sak & 0x01 != 0
402
- return :picc_mifare_mini
403
- end
404
-
405
- return :picc_mifare_1k
406
- end
407
-
408
- if sak & 0x10 != 0
409
- if sak & 0x01 != 0
410
- return :picc_mifare_plus_4k_sl2
411
- end
412
-
413
- return :picc_mifare_plus_2k_sl2
414
- end
415
-
416
- if sak == 0x00
417
- return :picc_mifare_ultralight
418
- end
419
-
420
- if sak & 0x20 != 0
421
- return :picc_iso_14443_4
422
- end
423
-
424
- if sak & 0x40 != 0
425
- return :picc_iso_18092
426
- end
427
-
428
- return :picc_unknown
429
- end
430
-
431
397
  # Start Crypto1 communication between reader and Mifare PICC
432
398
  #
433
399
  # PICC must be selected before calling for authentication
@@ -455,29 +421,26 @@ class MFRC522
455
421
 
456
422
  # Append CRC to buffer and check CRC or Mifare acknowledge
457
423
  def picc_transceive(send_data, accept_timeout = false)
458
- send_data = append_crc(send_data)
424
+ send_data = send_data.dup
425
+ send_data.append_crc16 if @built_in_crc_disabled
459
426
 
460
- puts "Sending Data: #{send_data.map{|x|x.to_s(16).rjust(2,'0').upcase}}" if ENV['DEBUG']
427
+ puts "Sending Data: #{send_data.to_bytehex}" if ENV['DEBUG']
461
428
 
462
429
  # Transfer data
463
430
  status, received_data, valid_bits = communicate_with_picc(PCD_Transceive, send_data)
464
- return [] if status == :status_picc_timeout && accept_timeout
431
+ return if status == :status_picc_timeout && accept_timeout
465
432
  raise PICCTimeoutError if status == :status_picc_timeout
466
433
  raise CommunicationError, status if status != :status_ok
467
434
 
468
- puts "Received Data: #{received_data.map{|x|x.to_s(16).rjust(2,'0').upcase}}" if ENV['DEBUG']
435
+ puts "Received Data: #{received_data.to_bytehex}" if ENV['DEBUG']
436
+ puts "Valid bits: #{valid_bits}" if ENV['DEBUG']
469
437
 
470
- # Data exists, check CRC and return
471
- if received_data.size > 1
472
- raise IncorrectCRCError unless check_crc(received_data)
473
-
474
- return received_data[0..-3]
438
+ # Data exists, check CRC
439
+ if received_data.size > 2 && @built_in_crc_disabled
440
+ raise IncorrectCRCError unless received_data.check_crc16(true)
475
441
  end
476
442
 
477
- raise UnexpectedDataError, 'Incorrect Mifare ACK format' if received_data.size != 1 || valid_bits != 4 # ACK is 4 bits long
478
- raise MifareNakError, received_data[0] if received_data[0] != PICC_MF_ACK
479
-
480
- received_data
443
+ return received_data, valid_bits
481
444
  end
482
445
 
483
446
  private
@@ -551,7 +514,10 @@ class MFRC522
551
514
 
552
515
  # Check for error
553
516
  error = read_spi(ErrorReg)
554
- return :status_error if (error & 0x13) != 0 # BufferOvfl ParityErr ProtocolErr
517
+ return :status_buffer_overflow if (error & 0x10) != 0
518
+ return :status_crc_error if (error & 0x04) != 0
519
+ return :status_parity_error if (error & 0x02) != 0
520
+ return :status_protocol_error if (error & 0x01) != 0
555
521
 
556
522
  # Receiving data
557
523
  received_data = []
@@ -568,39 +534,4 @@ class MFRC522
568
534
 
569
535
  return status, received_data, valid_bits
570
536
  end
571
-
572
- def calculate_crc(data)
573
- write_spi(CommandReg, PCD_Idle) # Stop any active command.
574
- write_spi(DivIrqReg, 0x04) # Clear the CRCIRq interrupt request bit
575
- write_spi_set_bitmask(FIFOLevelReg, 0x80) # FlushBuffer = 1, FIFO initialization
576
- write_spi(FIFODataReg, data) # Write data to the FIFO
577
- write_spi(CommandReg, PCD_CalcCRC) # Start the calculation
578
-
579
- # Wait for the command to complete
580
- i = 5000
581
- loop do
582
- irq = read_spi(DivIrqReg)
583
- break if (irq & 0x04) != 0
584
- raise PCDTimeoutError, 'Error calculating CRC' if i == 0
585
- i -= 1
586
- end
587
-
588
- write_spi(CommandReg, PCD_Idle) # Stop calculating CRC for new content in the FIFO.
589
-
590
- [read_spi(CRCResultRegL), read_spi(CRCResultRegH)]
591
- end
592
-
593
- def append_crc(data)
594
- data + calculate_crc(data)
595
- end
596
-
597
- def check_crc(data)
598
- raise UnexpectedDataError, 'Data too short for CRC check' if data.size < 3
599
-
600
- data = data.dup
601
- crc = data.pop(2)
602
-
603
- crc == calculate_crc(data)
604
- end
605
-
606
537
  end
@@ -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