class MIFARE::Classic

Constants

CMD_AUTH_KEY_A
CMD_AUTH_KEY_B
CMD_DECREMENT
CMD_INCREMENT
CMD_READ
CMD_RESTORE
CMD_TRANSFER
CMD_WRITE
MF_ACK

Public Class Methods

new(pcd, uid, sak) click to toggle source
Calls superclass method PICC::new
# File lib/mifare/classic.rb, line 13
def initialize(pcd, uid, sak)
  super
  # Set transceive timeout to 15ms
  @pcd.internal_timer(50)
end

Public Instance Methods

auth(block_addr, key = {}) click to toggle source
# 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
deauth() click to toggle source
# File lib/mifare/classic.rb, line 50
def deauth
  @pcd.mifare_crypto1_deauthenticate
end
decrement(block_addr, delta) click to toggle source

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

# File lib/mifare/classic.rb, line 123
def decrement(block_addr, delta)
  two_step(CMD_DECREMENT, block_addr, delta)
end
increment(block_addr, delta) click to toggle source

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

# File lib/mifare/classic.rb, line 118
def increment(block_addr, delta)
  two_step(CMD_INCREMENT, block_addr, delta)
end
read(block_addr) click to toggle source
# File lib/mifare/classic.rb, line 54
def read(block_addr)
  transceive([CMD_READ, block_addr])
end
read_value(block_addr) click to toggle source
# 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) click to toggle source

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

# 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) click to toggle source
# 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) click to toggle source

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

# File lib/mifare/classic.rb, line 133
def transfer(block_addr)
  transceive([CMD_TRANSFER, block_addr])
end
write(block_addr, send_data) click to toggle source
# 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) click to toggle source
# 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

Private Instance Methods

two_step(command, block_addr, value) click to toggle source

Helper for increment, decrement, and restore command

# File lib/mifare/classic.rb, line 140
def two_step(command, block_addr, value)
  buffer = [command, block_addr]
  send_data = [].append_uint(value, 4)

  # Ask PICC if we can write to block_addr
  transceive(buffer)

  # Then start transfer our data
  transceive(send_data, true) # Accept timeout
end