Class: MIFARE::Classic

Inherits:
PICC
  • Object
show all
Defined in:
lib/mifare/classic.rb

Constant Summary collapse

CMD_AUTH_KEY_A =

Perform authentication with Key A

0x60
CMD_AUTH_KEY_B =

Perform authentication with Key B

0x61
CMD_READ =

Reads one 16 byte block from the authenticated sector of the PICC.

0x30
CMD_WRITE =

Writes one 16 byte block to the authenticated sector of the PICC.

0xA0
CMD_DECREMENT =

Decrements the contents of a block and stores the result in the internal data register.

0xC0
CMD_INCREMENT =

Increments the contents of a block and stores the result in the internal data register.

0xC1
CMD_RESTORE =

Reads the contents of a block into the internal data register.

0xC2
CMD_TRANSFER =

Writes the contents of the internal data register to a block.

0xB0
MF_ACK =

Mifare Acknowledge

0x0A

Constants inherited from PICC

PICC::CMD_ADDITIONAL_FRAME, PICC::CMD_DESELECT, PICC::CMD_PPS, PICC::CMD_RATS, PICC::FSCI_to_FSC

Instance Attribute Summary

Attributes inherited from PICC

#sak, #uid

Instance Method Summary collapse

Methods inherited from PICC

#halt, identify_model, #iso_deselect, #iso_select, #iso_transceive, #picc_transceive, #restart_communication

Constructor Details

#initialize(pcd, uid, sak) ⇒ Classic

Returns a new instance of Classic.



13
14
15
16
17
# File 'lib/mifare/classic.rb', line 13

def initialize(pcd, uid, sak)
  super
  # Set transceive timeout to 15ms
  @pcd.internal_timer(50)
end

Instance Method Details

#auth(block_addr, key = {}) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/mifare/classic.rb', line 29

def auth(block_addr, key = {})
  if key[:a].nil? && key[:b].nil?
    raise UsageError, 'Missing key data'
  end

  if key[:a]
    cmd = CMD_AUTH_KEY_A
    key = key[:a]
  else
    cmd = CMD_AUTH_KEY_B
    key = key[:b]
  end

  key = [key].pack('H*').bytes
  if key.size != 6
    raise UsageError, "Expect 6 bytes auth key, got: #{key.size} byte"
  end

  @pcd.mifare_crypto1_authenticate(cmd, block_addr, key, @uid)
end

#deauthObject



50
51
52
# File 'lib/mifare/classic.rb', line 50

def deauth
  @pcd.mifare_crypto1_deauthenticate
end

#decrement(block_addr, delta) ⇒ Object

Decrement: Decrements the contents of a block and stores the result in the internal Transfer Buffer



123
124
125
# File 'lib/mifare/classic.rb', line 123

def decrement(block_addr, delta)
  two_step(CMD_DECREMENT, block_addr, delta)
end

#increment(block_addr, delta) ⇒ Object

Increment: Increments the contents of a block and stores the result in the internal Transfer Buffer



118
119
120
# File 'lib/mifare/classic.rb', line 118

def increment(block_addr, delta)
  two_step(CMD_INCREMENT, block_addr, delta)
end

#read(block_addr) ⇒ Object



54
55
56
# File 'lib/mifare/classic.rb', line 54

def read(block_addr)
  transceive([CMD_READ, block_addr])
end

#read_value(block_addr) ⇒ Object



70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/mifare/classic.rb', line 70

def read_value(block_addr)
  received_data = read(block_addr)

  value = received_data[0..3].to_sint
  value1 = ~(received_data[4..7].to_sint)
  value2 = received_data[8..11].to_sint

  if value != value1 || value != value2
    raise UnexpectedDataError, 'Invalid value block'
  end

  value
end

#restore(block_addr) ⇒ Object

Restore: Moves the contents of a block into the internal Transfer Buffer



128
129
130
# File 'lib/mifare/classic.rb', line 128

def restore(block_addr)
  two_step(CMD_RESTORE, block_addr, 0)
end

#transceive(send_data, accept_timeout = false) ⇒ Object



19
20
21
22
23
24
25
26
27
# File 'lib/mifare/classic.rb', line 19

def transceive(send_data, accept_timeout = false)
  received_data, valid_bits = picc_transceive(send_data, accept_timeout, true)
  return if received_data.nil? && valid_bits.nil? && accept_timeout
  unless valid_bits == 0
    raise UnexpectedDataError, 'Incorrect Mifare ACK format' if received_data.size != 1 || valid_bits != 4 # ACK is 4 bits long
    raise MifareNakError, "Mifare NAK detected: 0x#{received_data[0].to_bytehex}" if received_data[0] != MF_ACK
  end
  received_data
end

#transfer(block_addr) ⇒ Object

Transfer: Writes the contents of the internal Transfer Buffer to a value block



133
134
135
# File 'lib/mifare/classic.rb', line 133

def transfer(block_addr)
  transceive([CMD_TRANSFER, block_addr])
end

#write(block_addr, send_data) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
# File 'lib/mifare/classic.rb', line 58

def write(block_addr, send_data)
  if send_data.size != 16
    raise UsageError, "Expect 16 bytes data, got: #{send_data.size} byte"
  end

  # Ask PICC if we can write to block_addr
  transceive([CMD_WRITE, block_addr])

  # Then start transfer our data
  transceive(send_data)
end

#write_value(block_addr, value) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/mifare/classic.rb', line 84

def write_value(block_addr, value)
  # Value block format
  #
  # byte 0..3:   32 bit value in little endian
  # byte 4..7:   copy of byte 0..3, with inverted bits (aka. XOR 255)
  # byte 8..11:  copy of byte 0..3
  # byte 12:     index of backup block (can be any value)
  # byte 13:     copy of byte 12 with inverted bits (aka. XOR 255)
  # byte 14:     copy of byte 12
  # byte 15:     copy of byte 13
  value = [].append_sint(value, 4)

  buffer = []
  buffer[0]  = value[0]
  buffer[1]  = value[1]
  buffer[2]  = value[2]
  buffer[3]  = value[3]
  buffer[4]  = ~buffer[0]
  buffer[5]  = ~buffer[1]
  buffer[6]  = ~buffer[2]
  buffer[7]  = ~buffer[3]
  buffer[8]  = buffer[0]
  buffer[9]  = buffer[1]
  buffer[10] = buffer[2]
  buffer[11] = buffer[3]
  buffer[12] = block_addr
  buffer[13] = ~buffer[12]
  buffer[14] = buffer[12]
  buffer[15] = ~buffer[12]

  write(block_addr, buffer)
end