Skip to content

Commit

Permalink
more detailed example for IsoDep::Tag and Mifare::Classic::Tag, Mifar…
Browse files Browse the repository at this point in the history
…e::Classic::Tag code cleanup
  • Loading branch information
Maxim Chechel committed Oct 15, 2014
1 parent f72195d commit 6ef14df
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 44 deletions.
41 changes: 24 additions & 17 deletions examples/listen.rb
Original file line number Diff line number Diff line change
@@ -1,43 +1,50 @@
require 'ruby-nfc'
require 'logger'

logger = Logger.new(STDOUT)
$logger = Logger.new(STDOUT)

puts "Library version: %s" % NFC.version
def p(str)
$logger.debug str
end

p "Library version: #{NFC.version}"
readers = NFC::Reader.all
puts "Available readers: %s" % readers.to_s
p "Available readers: #{readers}"

# The order of tag types in poll arguments defines priority of tag types
readers[0].poll(IsoDep::Tag, Mifare::Classic::Tag, Mifare::Ultralight::Tag) do |tag|
begin
puts "Applied #{tag.class.name}: #{tag}"
p "Applied #{tag.class.name}: #{tag}"

case tag
when Mifare::Classic::Tag
# Perform authentication to block 0x04 with the Key A that equals
# to "\xFF\xFF\xFF\xFF\xFF\xFF" you can also use "FFFFFFFFFFFF"
# representation. In this case it will be automatically packed to 6 bytes
if auth(4, :key_a, "FFFFFFFFFFFF")
puts "authenticated!"
processed! # mark tag as processed so even if it supports different
# protocol poll method will continue with another physical
# tag
# Mifare::Classic::Tag.read method reads contents of last authenticated
# block
p "Contents of block 0x04: #{tag.read.unpack('H*').pop}"
# Making random 16-byte string
rnd = Array.new(16).map{rand(255)}.pack('C*')
tag.write(rnd)
p "New value: #{rnd.unpack('H*').pop}"
processed!
else
p "Authentication failed!"
end
when Mifare::Ultralight::Tag
puts "Page 1: %s" % read(1).unpack('H*').pop
when Mifare::Ultralight::Tag
p "Page 1: #{read(1).unpack('H*').pop}"
processed!
when IsoDep::Tag
select! ["F75246544101"].pack('H*')
# sending APDU command to tag using send_apdu method
apdu = ['A00D010018B455CAF0F331AF703EFA2E2D744EC7E22AA64076CD19F6D0'].pack('H*')
puts send_apdu(apdu)
p send_apdu(apdu)

# sending APDU command with "<<" operator which is alias to send_apdu
# response = tag << apdu
# puts response.unpack('H*').pop
response = tag << apdu
p "status word: #{response.sw.to_s(16)} data: #{response.data.unpack('H*').pop}"
processed!
end
rescue Exception => e
logger.debug e
p e
end
end
58 changes: 31 additions & 27 deletions lib/ruby-nfc/tags/mifare/classic.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def connect(&block)
if 0 == res
super
else
raise Mifare::Error, "Can't connect tag: #{res}"
raise Mifare::Error, "Can't connect to tag: #{res}"
end
end

Expand All @@ -59,58 +59,62 @@ def disconnect
# keytype can be :key_a or :key_b
# key - hexadecimal string key representation like "FFFFFFFFFFFF"
def auth(block_num, key_type, key)
raise Mifare::Error.new('Wrong key type') unless [:key_a, :key_b].include? key_type
raise Mifare::Error.new('Wrong key length') unless [6, 12].include? key.size
raise Mifare::Error, "Wrong key type" unless [:key_a, :key_b].include? key_type
raise Mifare::Error, "Wrong key length" unless [6, 12].include? key.size

key_ptr = FFI::MemoryPointer.new(:uchar, 6)
key_ptr.put_bytes(0, 6 == key.size ? key : [key].pack('H*'))
key_ptr.put_bytes(0, 6 == key.size ? key : [key].pack("H*"))

res = Mifare.mifare_classic_authenticate(@pointer, block_num, key_ptr,
key_type)
if 0 == res
@auth_block = block_num
else
raise Mifare::Error.new("Can't autenticate to block 0x%02x" % block_num)
end
raise Mifare::Error, "Can't autenticate to block 0x%02x" % block_num if 0 != res

@auth_block = block_num
end

# block number to read
def read(block_num = nil)
block_num ||= @auth_block
raise Mifare::Error.new('Not authenticated') unless block_num
raise Mifare::Error, "Not authenticated" unless block_num

data_ptr = FFI::MemoryPointer.new(:uchar, 16)
res = Mifare.mifare_classic_read(@pointer, block_num, data_ptr)

raise Mifare::Error.new("Can't read block 0x%02x" % block_num) unless 0 == res
raise Mifare::Error, "Can't read block 0x%02x" % block_num if 0 != res

data_ptr.get_bytes(0, 16).force_encoding('ASCII-8BIT')
data_ptr.get_bytes(0, 16).force_encoding("ASCII-8BIT")
end

# @data - 16 bytes represented by hexadecimal string
# @block_num - number of block to write to
def write(data, block_num = nil)
raise Mifare::Error.new('Wrong data given') if data !~ /^[\da-f]{32}$/i

block_num ||= @auth_block
raise Mifare::Error.new('Not authenticated') unless block_num
raise Mifare::Error, "Not authenticated" unless block_num

write_data = if data =~ /^[\da-f]{32}$/i
[data].pack("H*")
elsif 16 == data.size
data.dup
else
raise Mifare::Error, "Wrong data given"
end

data_ptr = FFI::MemoryPointer.new(:uchar, 16)
data_ptr.put_bytes(0, [data].pack('H*'))
data_ptr.put_bytes(0, write_data)

res = Mifare.mifare_classic_write(@pointer, block_num, data_ptr)
(0 == res) || raise(Mifare::Error.new("Can't write block 0x%02x" % block_num))
raise Mifare::Error, "Can't write block 0x%02x" % block_num if 0 != res
end

# Create value block structure and write it to block
def init_value(value, addr = nil, block_num = nil)
block_num ||= @auth_block
raise Mifare::Error.new('Not authenticated') unless block_num
raise Mifare::Error, "Not authenticated" unless block_num

addr ||= 0

res = Mifare.mifare_classic_init_value(@pointer, block_num, value, addr)
(0 == res) || raise(Mifare::Error.new("Can't init value block 0x%02x" % block_num))
raise Mifare::Error, "Can't init value block 0x%02x" % block_num if 0 != res
end

# returns only value part of value block
Expand All @@ -122,40 +126,40 @@ def value(block_num = nil)
# returns value and addr
def value_with_addr(block_num = nil)
block_num ||= @auth_block
raise Mifare::Error.new('Not authenticated') unless block_num
raise Mifare::Error, "Not authenticated" unless block_num

value_ptr = FFI::MemoryPointer.new(:int32, 1)
addr_ptr = FFI::MemoryPointer.new(:uchar, 1)
res = Mifare.mifare_classic_read_value(@pointer, block_num, value_ptr, addr_ptr)
raise Mifare::Error.new("Can't read value block 0x%02x" % block_num) unless 0 == res
raise Mifare::Error, "Can't read value block 0x%02x" % block_num if 0 != res

[value_ptr.get_int32(0), addr_ptr.get_uchar(0)]
end

# Mifare classic increment
def inc(amount = 1, block_num = nil)
block_num ||= @auth_block
raise Mifare::Error.new('Not authenticated') unless block_num
raise Mifare::Error, "Not authenticated" unless block_num

res = Mifare.mifare_classic_increment(@pointer, block_num, amount)
(0 == res) || raise(Mifare::Error.new("Can't increment block 0x%02x" % block_num))
raise Mifare::Error, "Can't increment block 0x%02x" % block_num if 0 != res
end

# Mifare classic decrement
def dec(amount = 1, block_num = nil)
block_num ||= @auth_block
raise Mifare::Error.new('Not authenticated') unless block_num
raise Mifare::Error, "Not authenticated" unless block_num

res = Mifare.mifare_classic_decrement(@pointer, block_num, amount)
(0 == res) || raise(Mifare::Error.new("Can't decrement block 0x%02x" % block_num))
raise Mifare::Error, "Can't decrement block 0x%02x" % block_num if 0 != res
end

def transfer(block_num = nil)
block_num ||= @auth_block
raise Mifare::Error.new('Not authenticated') unless block_num
raise Mifare::Error, "Not authenticated" unless block_num

res = Mifare.mifare_classic_transfer(@pointer, block_num)
(0 == res) || raise(Mifare::Error.new("Can't transfer to block 0x%02x" % block_num))
raise Mifare::Error, "Can't transfer to block 0x%02x" % block_num if 0 != res
end

# Check's if our tag class is able to handle this LibNFC::Target
Expand Down

0 comments on commit 6ef14df

Please sign in to comment.