mfrc522 1.0.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- module Mifare
1
+ module MIFARE
2
2
  class Key
3
3
  attr_reader :type
4
4
  attr_reader :cipher_suite
@@ -12,25 +12,52 @@ module Mifare
12
12
  init_cipher
13
13
  end
14
14
 
15
+ # Padding Method according to ISO-9797
16
+ def padding_mode(mode)
17
+ if mode != 1 && mode != 2
18
+ raise UsageError, 'Unknown padding mode'
19
+ end
20
+ @padding_mode = mode
21
+ end
22
+
15
23
  def key
16
24
  @key.bytes
17
25
  end
18
26
 
19
- def encrypt(data, cbc_mode = :send)
27
+ def encrypt(data, cbc_mode: :send, data_length: nil)
20
28
  @cipher.encrypt
21
29
 
22
30
  # Add padding if not a complete block
23
- until data.size % @block_size == 0
24
- data << 0x00
31
+ if data.size % @block_size != 0
32
+ raise UsageError, 'Padding mode not set' unless @padding_mode
33
+ data << 0x80 if @padding_mode == 2
34
+ until data.size % @block_size == 0
35
+ data << 0x00
36
+ end
25
37
  end
26
38
 
27
39
  cbc_crypt(data, cbc_mode)
28
40
  end
29
41
 
30
- def decrypt(data, cbc_mode = :receive)
42
+ def decrypt(data, cbc_mode: :receive, data_length: nil)
31
43
  @cipher.decrypt
32
44
 
33
- cbc_crypt(data, cbc_mode)
45
+ data = cbc_crypt(data, cbc_mode)
46
+ if @padding_mode == 1
47
+ data[0...data_length]
48
+ elsif @padding_mode == 2
49
+ str = data.pack('C*')
50
+ str.sub! /#{0x80.chr}#{0x00.chr}*\z/, ''
51
+ str.bytes
52
+ else
53
+ raise UsageError, 'Padding mode not set'
54
+ end
55
+ end
56
+
57
+ def set_iv(iv)
58
+ iv = iv.pack('C*')
59
+ raise UsageError, 'Incorrect IV length' if iv.size != @block_size
60
+ @cipher_iv = iv
34
61
  end
35
62
 
36
63
  def clear_iv
@@ -42,7 +69,7 @@ module Mifare
42
69
  data = Array.new(@block_size, 0)
43
70
 
44
71
  clear_iv
45
- data = encrypt(data, :receive)
72
+ data = encrypt(data, cbc_mode: :receive)
46
73
 
47
74
  @cmac_subkey1 = bit_shift_left(data)
48
75
  @cmac_subkey1[-1] ^= r if data[0] & 0x80 != 0
@@ -53,7 +80,7 @@ module Mifare
53
80
 
54
81
  def calculate_cmac(data)
55
82
  if @cmac_subkey1.nil? || @cmac_subkey2.nil?
56
- raise 'Generate subkeys before calculating CMAC'
83
+ raise UsageError, 'Generate subkeys before calculating CMAC'
57
84
  end
58
85
 
59
86
  # Separate from input object
@@ -92,7 +119,7 @@ module Mifare
92
119
 
93
120
  if key_type == :des
94
121
  if @key_size != 8 && @key_size != 16 && @key_size != 24
95
- raise UnexpectedDataError, 'Incorrect key length'
122
+ raise UsageError, 'Incorrect key length'
96
123
  end
97
124
 
98
125
  # Data block size for DES is 8 bytes
@@ -114,7 +141,7 @@ module Mifare
114
141
 
115
142
  elsif key_type == :aes
116
143
  if @key_size != 16
117
- raise UnexpectedDataError, 'Incorrect key length'
144
+ raise UsageError, 'Incorrect key length'
118
145
  end
119
146
 
120
147
  # data block size for AES is 16 bytes
@@ -122,7 +149,7 @@ module Mifare
122
149
  @key = key
123
150
  @cipher_suite = 'aes-128-cbc'
124
151
  else
125
- raise UnexpectedDataError, 'Unknown key type'
152
+ raise UsageError, 'Unknown key type'
126
153
  end
127
154
 
128
155
  @key = @key.pack('C*')
@@ -162,7 +189,7 @@ module Mifare
162
189
 
163
190
  output_data.bytes
164
191
  else
165
- raise UnexpectedDataError, 'Unknown CBC mode'
192
+ raise UsageError, 'Unknown CBC mode'
166
193
  end
167
194
  end
168
195
 
@@ -0,0 +1,124 @@
1
+ module MIFARE
2
+ class Plus < ::PICC
3
+ CMD_WRITE_PERSO = 0xA8
4
+ CMD_COMMIT_PERSO = 0xAA
5
+ CMD_MULTI_BLOCK_READ = 0x38
6
+ CMD_MULTI_BLOCK_WRITE = 0xA8
7
+ CMD_FIRST_AUTH = 0x70
8
+ CMD_SECOND_AUTH = 0x72
9
+ CMD_FOLLOWING_AUTH = 0x76
10
+ CMD_RESET_AUTH = 0x78
11
+ CMD_VC_DESELECT = 0x48
12
+ MF_ACK = 0x0A
13
+
14
+ def initialize(pcd, uid, sak)
15
+ super
16
+ invalid_auth
17
+ reset_counter
18
+ end
19
+
20
+ def authed?
21
+ !@transaction_identifier.empty?
22
+ end
23
+
24
+ def transceive(cmd: , plain_data: [], data: [], tx: nil, rx: nil)
25
+ raise UsageError, 'Call `iso_select` before using commands' unless @iso_selected
26
+ iso_transceive(send_data)
27
+ end
28
+
29
+ def auth(key_number, auth_key)
30
+ cmd = authed? ? CMD_FOLLOWING_AUTH : CMD_FIRST_AUTH
31
+ auth_key.padding_mode(2)
32
+
33
+ buffer = [cmd].append_uint(key_number, 2)
34
+ buffer << 0x00 unless authed? # No PCDCaps2 to send
35
+
36
+ # Ask for authentication
37
+ received_data = iso_transceive(buffer)
38
+
39
+ # Receive challenge
40
+ auth_key.clear_iv
41
+ auth_key.set_iv(generate_iv(:decrypt)) if authed?
42
+ challenge = auth_key.decrypt(received_data)
43
+ challenge_rot = challenge.rotate
44
+
45
+ # Generate random number and encrypt it with rotated challenge
46
+ random_number = SecureRandom.random_bytes(received_data.size).bytes
47
+ auth_key.clear_iv
48
+ auth_key.set_iv(generate_iv(:encrypt)) if authed?
49
+ response = auth_key.encrypt(random_number + challenge_rot)
50
+
51
+ # Send challenge response
52
+ received_data = iso_transceive([CMD_SECOND_AUTH, *response])
53
+
54
+ # Check if verification matches rotated random_number
55
+ auth_key.clear_iv
56
+ auth_key.set_iv(generate_iv(:decrypt)) if authed?
57
+ verification = auth_key.decrypt(received_data)
58
+ @transaction_identifier = verification.shift(4)
59
+ response_rot = verification.shift(16)
60
+
61
+ if random_number.rotate != response_rot
62
+ raise ReceiptIntegrityError, 'Authentication Failed'
63
+ end
64
+
65
+ byte_a = random_number[11..15]
66
+ byte_b = challenge[11..15]
67
+ byte_c = random_number[4..8]
68
+ byte_d = challenge[4..8]
69
+ byte_e = random_number[7..11]
70
+ byte_f = challenge[7..11]
71
+ byte_g = random_number[0..4]
72
+ byte_h = challenge[0..4]
73
+ byte_i = byte_c.xor(byte_d)
74
+ byte_j = byte_g.xor(byte_h)
75
+
76
+ enc_key_base = byte_a + byte_b + byte_i + [0x11]
77
+ mac_key_base = byte_e + byte_f + byte_j + [0x22]
78
+
79
+ auth_key.clear_iv
80
+ auth_key.set_iv(generate_iv(:encrypt)) if authed?
81
+ enc_key = auth_key.encrypt(enc_key_base)
82
+ @enc_key = Key.new(:aes, enc_key)
83
+
84
+ auth_key.clear_iv
85
+ auth_key.set_iv(generate_iv(:encrypt)) if authed?
86
+ mac_key = auth_key.encrypt(mac_key_base)
87
+ @mac_key = Key.new(:aes, mac_key)
88
+
89
+ reset_counter
90
+ end
91
+
92
+ protected
93
+
94
+ def generate_iv(operation)
95
+ buffer = [].append_uint(@read_counter, 2).append_uint(@write_counter, 2)
96
+ buffer.concat(buffer, buffer)
97
+
98
+ if operation == :encrypt
99
+ buffer.unshift(@transaction_identifier)
100
+ elsif operation == :decrypt
101
+ buffer.concat(@transaction_identifier)
102
+ else
103
+ raise UsageError, 'Unknown operation mode'
104
+ end
105
+ end
106
+
107
+ def generate_mac_payload
108
+
109
+ end
110
+
111
+ def reset_counter
112
+ @session_read_counter = 0
113
+ @read_counter = 0
114
+ @write_counter = 0
115
+ end
116
+
117
+ def invalid_auth
118
+ reset_counter
119
+ @transaction_identifier = []
120
+ @enc_key = nil
121
+ @mac_key = nil
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,11 @@
1
+ module MIFARE
2
+ module PlusSL0
3
+ def write_perso(block_addr, data)
4
+ transceive(cmd: CMD_WRITE_PERSO, data: [block_addr, *data])
5
+ end
6
+
7
+ def commit_perso
8
+ transceive(cmd: CMD_COMMIT_PERSO)
9
+ end
10
+ end
11
+ end
File without changes
@@ -0,0 +1,11 @@
1
+ module MIFARE
2
+ module PlusSL3
3
+ def read(addr, tx, rx)
4
+
5
+ end
6
+
7
+ def write(addr, data, tx, rx)
8
+
9
+ end
10
+ end
11
+ end
@@ -1,35 +1,77 @@
1
- module Mifare
1
+ module MIFARE
2
2
  class Ultralight < ::PICC
3
- CMD_READ = 0x30 # Reads 4 pages(16 bytes) from the PICC.
4
- CMD_WRITE = 0xA2 # Writes 1 page(4 bytes) to the PICC.
5
- CMD_3DES_AUTH = 0x1A # Ultralight C 3DES Authentication.
3
+ CMD_READ = 0x30 # Reads 4 pages(16 bytes) from the PICC.
4
+ CMD_FAST_READ = 0x3A # Reads pages within requested range
5
+ CMD_WRITE = 0xA2 # Writes 1 page(4 bytes) to the PICC.
6
+ CMD_COMP_WRITE = 0xA0
7
+ CMD_READ_CNT = 0x39
8
+ CMD_INCR_CNT = 0xA5
9
+ CMD_PWD_AUTH = 0x1B
10
+ CMD_3DES_AUTH = 0x1A # Ultralight C 3DES Authentication.
11
+ CMD_GET_VERSION = 0x60
12
+ CMD_READ_SIG = 0x3C
13
+ CMD_VCSL = 0x4B
14
+ CMD_CHECK_TEARING_EVENT = 0x3E
15
+ MF_ACK = 0x0A # Mifare Acknowledge
16
+
17
+ CARD_VERSION = Struct.new(
18
+ :vendor_id, :type, :subtype, :major_ver, :minor_ver, :storage_size, :protocol_type
19
+ )
6
20
 
7
21
  def initialize(pcd, uid, sak)
8
22
  super
9
- @model_c = false
23
+ # Set transceive timeout to 15ms
24
+ @pcd.internal_timer(50)
25
+
26
+ # Maximum fast read range
27
+ @max_range = ((@pcd.buffer_size - 2) / 4).to_i
10
28
 
11
29
  # Check if Ultralight C
12
- if support_3des_auth?
30
+ if @model_c = support_3des_auth?
13
31
  extend UltralightC
14
- @model_c = true
32
+ end
33
+
34
+ unless @version = check_version
35
+ if version[:major_ver] == 0x01
36
+ extend UltralightEV1
37
+ end
15
38
  end
16
39
  end
17
40
 
18
- def read(block_addr)
19
- buffer = [CMD_READ, block_addr]
41
+ def transceive(send_data)
42
+ received_data, valid_bits = picc_transceive(send_data, false, true)
43
+ unless valid_bits == 0
44
+ raise UnexpectedDataError, 'Incorrect Mifare ACK format' if received_data.size != 1 || valid_bits != 4 # ACK is 4 bits long
45
+ raise MifareNakError, "Mifare NAK detected: 0x#{received_data[0].to_bytehex}" if received_data[0] != MF_ACK
46
+ end
47
+ received_data
48
+ end
20
49
 
21
- @pcd.picc_transceive(buffer)
50
+ def read(block_addr)
51
+ transceive([CMD_READ, block_addr])
22
52
  end
23
53
 
24
54
  def write(page, send_data)
25
55
  if send_data.size != 4
26
- raise UnexpectedDataError, "Expect 4 bytes data, got: #{send_data.size} byte"
56
+ raise UsageError, "Expect 4 bytes data, got: #{send_data.size} byte"
27
57
  end
28
58
 
29
- buffer = [CMD_WRITE, page]
30
- buffer.concat(send_data)
59
+ transceive([CMD_WRITE, page, *send_data])
60
+ end
61
+
62
+ def get_version
63
+ version = transceive([CMD_GET_VERSION])
64
+
65
+ expo = (version[6] >> 1) & 0x0F
66
+ if version[6] & 0x01 == 0
67
+ size = 1 << expo
68
+ else
69
+ size = (1 << expo) | (1 << (expo - 1))
70
+ end
31
71
 
32
- @pcd.picc_transceive(buffer)
72
+ CARD_VERSION.new(
73
+ version[1], version[2], version[3], version[4], version[5], size, version[7]
74
+ )
33
75
  end
34
76
 
35
77
  def model_c?
@@ -38,20 +80,29 @@ module Mifare
38
80
 
39
81
  private
40
82
 
83
+ def check_version
84
+ begin
85
+ version = get_version
86
+ rescue CommunicationError
87
+ restart_communication
88
+ return nil
89
+ end
90
+ version
91
+ end
92
+
41
93
  # Check if PICC support Ultralight 3DES command
42
94
  def support_3des_auth?
43
95
  # Ask for authentication
44
96
  buffer = [CMD_3DES_AUTH, 0x00]
45
97
 
46
98
  begin
47
- @pcd.picc_transceive(buffer)
99
+ transceive(buffer)
48
100
  result = true
49
101
  rescue CommunicationError
50
102
  result = false
51
103
  end
52
104
 
53
- resume_communication
54
-
105
+ restart_communication
55
106
  result
56
107
  end
57
108
  end
@@ -1,19 +1,18 @@
1
- module Mifare
1
+ module MIFARE
2
2
  module UltralightC
3
- CMD_3DES_AUTH = 0x1A # Ultralight C 3DES Authentication.
4
-
5
3
  def auth(auth_key)
6
4
  if auth_key.cipher_suite != 'des-ede-cbc'
7
- raise UnexpectedDataError, 'Incorrect Auth Key Type'
5
+ raise UsageError, 'Incorrect Auth Key Type'
8
6
  end
9
7
 
10
8
  auth_key.clear_iv
9
+ auth_key.padding_mode(1)
11
10
 
12
11
  # Ask for authentication
13
12
  buffer = [CMD_3DES_AUTH, 0x00]
14
- received_data = @pcd.picc_transceive(buffer)
13
+ received_data = transceive(buffer)
15
14
  card_status = received_data.shift
16
- raise UnexpectedDataError, 'Incorrect response' if card_status != 0xAF
15
+ raise UnexpectedDataError, 'Auth failed: stage 1' if card_status != 0xAF
17
16
 
18
17
  challenge = auth_key.decrypt(received_data)
19
18
  challenge_rot = challenge.rotate
@@ -24,15 +23,15 @@ module Mifare
24
23
 
25
24
  # Send challenge response
26
25
  buffer = [0xAF] + response
27
- received_data = @pcd.picc_transceive(buffer)
26
+ received_data = transceive(buffer)
28
27
  card_status = received_data.shift
29
- raise UnexpectedDataError, 'Incorrect response' if card_status != 0x00
28
+ raise UnexpectedDataError, 'Auth failed: stage 2' if card_status != 0x00
30
29
 
31
30
  # Check if verification matches rotated random_number
32
31
  verification = auth_key.decrypt(received_data)
33
32
 
34
33
  if random_number.rotate != verification
35
- halt
34
+ restart_communication
36
35
  return @authed = false
37
36
  end
38
37
 
@@ -40,14 +39,14 @@ module Mifare
40
39
  end
41
40
 
42
41
  def authed?
43
- @authed
42
+ @authed || false
44
43
  end
45
44
 
46
45
  def write_des_key(key)
47
46
  # key should be 16 bytes long
48
47
  bytes = [key].pack('H*').bytes
49
48
  if bytes.size != 16
50
- raise UnexpectedDataError, "Expect 16 bytes 3DES key, got: #{bytes.size} byte"
49
+ raise UsageError, "Expect 16 bytes 3DES key, got: #{bytes.size} byte"
51
50
  end
52
51
 
53
52
  # Key1
@@ -60,7 +59,7 @@ module Mifare
60
59
 
61
60
  def counter_increment(value)
62
61
  if value < 0
63
- raise UnexpectedDataError, 'Expect positive integer for counter'
62
+ raise UsageError, 'Expect positive integer for counter'
64
63
  end
65
64
  # you can set any value between 0x0000 to 0xFFFF on the first write (initialize)
66
65
  # after initialized, counter can only be incremented by 0x01 ~ 0x0F
@@ -68,8 +67,8 @@ module Mifare
68
67
  end
69
68
 
70
69
  def enable_protection_from(block_addr)
71
- if block_addr >= 0x03 && block_addr <= 0x30
72
- raise UnexpectedDataError, 'Requested block beyond memory limit'
70
+ if block_addr < 0x03 || block_addr > 0x30
71
+ raise UsageError, 'Requested block beyond memory limit'
73
72
  end
74
73
  # authentication will be required from `block_addr` to 0x2F
75
74
  # valid value are from 0x03 to 0x30