ruby-nfc 1.0.0 → 1.1.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 +8 -8
- data/LICENSE +21 -0
- data/lib/ruby-nfc/apdu/apdu.rb +92 -0
- data/lib/ruby-nfc/apdu/request.rb +42 -0
- data/lib/ruby-nfc/apdu/response.rb +48 -0
- data/lib/ruby-nfc/reader.rb +2 -2
- data/lib/ruby-nfc/tags/isodep.rb +54 -9
- data/lib/ruby-nfc/tags/mifare/classic.rb +33 -29
- data/lib/ruby-nfc/tags/mifare/tag.rb +1 -1
- data/lib/ruby-nfc/tags/mifare/ultralight.rb +3 -3
- data/lib/ruby-nfc/tags/tag.rb +4 -2
- metadata +21 -3
checksums.yaml
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
---
|
2
2
|
!binary "U0hBMQ==":
|
3
3
|
metadata.gz: !binary |-
|
4
|
-
|
4
|
+
NWMzYmQwMDJkYzBlMmMzZjM0NmRhNTdmYWJhNjk0Y2E2MDM5ZGYwOQ==
|
5
5
|
data.tar.gz: !binary |-
|
6
|
-
|
6
|
+
Mjk2ZmVmNGIxMDQwY2I0ZDFjNDJmZTYxZWJjNTM1NjlkOTkwMDYzMA==
|
7
7
|
SHA512:
|
8
8
|
metadata.gz: !binary |-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
NGJkOTIxOWY1YTNjY2JlN2FhMTQ2NDNmNDkzNWQzYTI2OTdlNGRkOTk3MDVj
|
10
|
+
YTdmNjA0N2UzNzgxNjYyMDc5NjY4NzI2MWIzNTRiMjg3YzUyY2ZmOGU2MTVl
|
11
|
+
YmI3NzM0NTc5MDFhYWIxZmY2OGU1NTYwZGY0OTAzNTE0NTI0MDI=
|
12
12
|
data.tar.gz: !binary |-
|
13
|
-
|
14
|
-
|
15
|
-
|
13
|
+
ODVmMTQzYzI2NGQ1Mjg0N2NiMGE5ZDgwOTE4ZmMxZjRjNDI2MjZkNjI1ZWEx
|
14
|
+
NWYyYWE0NTU2MWEyNzczYmRjM2I2Mjc3MDM4NzFiMmY0NDRjOWUwYTJmMWI1
|
15
|
+
ZjBiN2JhMTQ2OWFmNGM2OGE1MTMwODM1MGY2NjMzNzM1ZDlmZTY=
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014 Maxim M. Chechel <maximchick@gmail.com>
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module APDU
|
2
|
+
class Error < ::Exception; end
|
3
|
+
|
4
|
+
class Errno < ::Exception
|
5
|
+
STATUS_STRINGS = {
|
6
|
+
#0x6XXX => "Transmission protocol related codes
|
7
|
+
#0x61XX => "SW2 indicates the number of response bytes still available",
|
8
|
+
|
9
|
+
0x6200 => "No information given",
|
10
|
+
0x6281 => "Returned data may be corrupted",
|
11
|
+
0x6282 => "The end of the file has been reached before the end of reading",
|
12
|
+
0x6283 => "Invalid DF",
|
13
|
+
0x6284 => "Selected file is not valid. File descriptor error",
|
14
|
+
|
15
|
+
0x6300 => "Authentification failed. Invalid secret code or forbidden value",
|
16
|
+
0x6381 => "File filled up by the last write",
|
17
|
+
# 0x63CX => "Counter provided by 'X' (valued from 0 to 15) (exact meaning depending on the command)",
|
18
|
+
|
19
|
+
0x6501 => "Memory failure. There have been problems in writing or reading the EEPROM\n" +
|
20
|
+
"Other hardware problems may also bring this error.",
|
21
|
+
0x6581 => "Write problem / Memory failure / Unknown mode",
|
22
|
+
|
23
|
+
# 0x67XX => "Error, incorrect parameter P3 (ISO code)",
|
24
|
+
0x6700 => "Incorrect length or address range error",
|
25
|
+
|
26
|
+
0x6800 => "The request function is not supported by the card.",
|
27
|
+
0x6881 => "Logical channel not supported",
|
28
|
+
0x6882 => "Secure messaging not supported",
|
29
|
+
|
30
|
+
0x6900 => "No successful transaction executed during session",
|
31
|
+
0x6981 => "Cannot select indicated file, command not compatible with file organization",
|
32
|
+
0x6982 => "Access conditions not fulfilled",
|
33
|
+
0x6983 => "Secret code locked",
|
34
|
+
0x6984 => "Referenced data invalidated",
|
35
|
+
0x6985 => "No currently selected EF, no command to monitor / no Transaction Manager File",
|
36
|
+
0x6986 => "Command not allowed (no current EF)",
|
37
|
+
0x6987 => "Expected SM data objects missing",
|
38
|
+
0x6988 => "SM data objects incorrect",
|
39
|
+
|
40
|
+
0x6A00 => "Bytes P1 and/or P2 are incorrect.",
|
41
|
+
0x6A80 => "The parameters in the data field are incorrect",
|
42
|
+
0x6A81 => "Card is blocked or command not supported",
|
43
|
+
0x6A82 => "File not found",
|
44
|
+
0x6A83 => "Record not found",
|
45
|
+
0x6A84 => "There is insufficient memory space in record or file",
|
46
|
+
0x6A85 => "Lc inconsistent with TLV structure",
|
47
|
+
0x6A86 => "Incorrect parameters P1P2",
|
48
|
+
0x6A87 => "The P3 value is not consistent with the P1 and P2 values.",
|
49
|
+
0x6A88 => "Referenced data not found.",
|
50
|
+
|
51
|
+
0x6B00 => "Incorrect reference; illegal address; Invalid P1 or P2 parameter",
|
52
|
+
|
53
|
+
# 0x6CXX => "Incorrect P3 length.",
|
54
|
+
|
55
|
+
0x6D00 => "Command not allowed. Invalid instruction byte (INS)",
|
56
|
+
|
57
|
+
0x6E00 => "Incorrect application (CLA parameter of a command)",
|
58
|
+
|
59
|
+
0x6F00 => "Checking error",
|
60
|
+
|
61
|
+
0x9000 => "Command executed without error",
|
62
|
+
|
63
|
+
0x9100 => "Purse Balance error cannot perform transaction",
|
64
|
+
0x9102 => "Purse Balance error",
|
65
|
+
|
66
|
+
# 0x92XX => "Memory error",
|
67
|
+
0x9202 => "Write problem / Memory failure",
|
68
|
+
0x9240 => "Error, memory problem",
|
69
|
+
|
70
|
+
# 0x94XX => "File error",
|
71
|
+
0x9404 => "Purse selection error or invalid purse",
|
72
|
+
0x9406 => "Invalid purse detected during the replacement debit step",
|
73
|
+
0x9408 => "Key file selection error",
|
74
|
+
|
75
|
+
# 0x98XX => "Security error",
|
76
|
+
0x9800 => "Warning",
|
77
|
+
0x9804 => "Access authorization not fulfilled",
|
78
|
+
0x9806 => "Access authorization in Debit not fulfilled for the replacement debit step",
|
79
|
+
0x9820 => "No temporary transaction key established",
|
80
|
+
0x9834 => "Error, Update SSD order sequence not respected"
|
81
|
+
}
|
82
|
+
|
83
|
+
attr_accessor :code
|
84
|
+
|
85
|
+
def initialize(sw)
|
86
|
+
@code = sw
|
87
|
+
message = STATUS_STRINGS[sw] || "Unknown error"
|
88
|
+
|
89
|
+
super("#{sw.to_s(16).upcase} #{message}")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require_relative './apdu'
|
2
|
+
|
3
|
+
module APDU
|
4
|
+
class Request
|
5
|
+
attr_accessor :cla, :ins, :p1, :p2, :lc, :data, :le
|
6
|
+
|
7
|
+
def self.from_string(apdu)
|
8
|
+
raise APDU::Error, "APDU is too short: #{apdu.size}" if apdu.size < 5
|
9
|
+
|
10
|
+
apdu_8bit = apdu.dup
|
11
|
+
apdu_8bit.force_encoding('ASCII-8BIT')
|
12
|
+
|
13
|
+
req = self.new
|
14
|
+
req.cla, req.ins, req.p1, req.p2, req.lc, req.data = apdu.unpack('CCCCCA*')
|
15
|
+
|
16
|
+
if req.data.size == req.lc
|
17
|
+
req.le = 0
|
18
|
+
elsif req.data.size == req.lc + 1
|
19
|
+
req.le = req.data[-1,1].ord
|
20
|
+
req.data = req.data[0...-1]
|
21
|
+
else
|
22
|
+
raise APDU::Error, "Wrong Lc or wrong command data length"
|
23
|
+
end
|
24
|
+
|
25
|
+
req
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.from_hex_string(apdu)
|
29
|
+
raise APDU::Error, "Wrong format" if apdu !~ /^([a-fA-F0-9]{2}){5,128}$/
|
30
|
+
from_string([apdu].pack('H*'))
|
31
|
+
end
|
32
|
+
|
33
|
+
# Public: Build APDU command
|
34
|
+
def build
|
35
|
+
[self.to_s].pack('H*')
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_s
|
39
|
+
[cla, ins, p1, p2, lc, data, le].pack('CCCCCA*C').unpack('H*').pop.upcase
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require_relative './apdu'
|
2
|
+
|
3
|
+
module APDU
|
4
|
+
class Response
|
5
|
+
def initialize(response)
|
6
|
+
resp_8bit = response.dup
|
7
|
+
resp_8bit.force_encoding('ASCII-8BIT')
|
8
|
+
|
9
|
+
raise APDU::Error, "Response must be at least 2-bytes long" if resp_8bit.size < 2
|
10
|
+
|
11
|
+
@response = resp_8bit
|
12
|
+
end
|
13
|
+
|
14
|
+
# Public: Raises APDU::Errno if self.sw is not equal 0x9000
|
15
|
+
def raise_errno!
|
16
|
+
raise APDU::Errno.new(sw) if sw != 0x9000
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
# Public: Return Status Word of an APDU response. Status Word is a two-byte
|
21
|
+
# result code
|
22
|
+
def sw
|
23
|
+
@response[-2, 2].unpack('n').pop
|
24
|
+
end
|
25
|
+
|
26
|
+
# Public: Return high byte of Status Word
|
27
|
+
def sw1
|
28
|
+
@response[-2, 1].unpack('C').pop
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Return low byte of Status Word
|
32
|
+
def sw2
|
33
|
+
@response[-1,1].unpack('C').pop
|
34
|
+
end
|
35
|
+
|
36
|
+
def data
|
37
|
+
@response[0...-2]
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
@response.unpack('H*').pop.upcase
|
42
|
+
end
|
43
|
+
|
44
|
+
def [](index)
|
45
|
+
@response[index]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/ruby-nfc/reader.rb
CHANGED
@@ -26,7 +26,7 @@ module NFC
|
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
def poll(*card_types)
|
29
|
+
def poll(*card_types, &block)
|
30
30
|
connect
|
31
31
|
|
32
32
|
LibNFC.nfc_initiator_init(@ptr) # we'll be initiator not a target
|
@@ -57,7 +57,7 @@ module NFC
|
|
57
57
|
card_types.each do |card_type|
|
58
58
|
if card_type.match?(target)
|
59
59
|
tag = card_type.new(target, self)
|
60
|
-
|
60
|
+
tag.connect(&block)
|
61
61
|
# if this tag was marked as processed - continue with next tag
|
62
62
|
break if target.processed?
|
63
63
|
end
|
data/lib/ruby-nfc/tags/isodep.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require_relative '../nfc'
|
2
2
|
require_relative './tag'
|
3
|
+
require_relative '../apdu/request'
|
4
|
+
require_relative '../apdu/response'
|
3
5
|
|
4
6
|
module IsoDep
|
5
7
|
ISO_14443_4_COMPATIBLE = 0x20
|
@@ -12,7 +14,7 @@ module IsoDep
|
|
12
14
|
target[:nti][:nai][:btSak] & IsoDep::ISO_14443_4_COMPATIBLE > 0
|
13
15
|
end
|
14
16
|
|
15
|
-
def
|
17
|
+
def connect(&block)
|
16
18
|
@reader.set_flag(:NP_AUTO_ISO14443_4, true)
|
17
19
|
|
18
20
|
modulation = LibNFC::Modulation.new
|
@@ -33,22 +35,54 @@ module IsoDep
|
|
33
35
|
)
|
34
36
|
|
35
37
|
if res > 0
|
36
|
-
# trying to select applet if applet identifier was given
|
37
|
-
if aid
|
38
|
-
sw = send_apdu("\x00\xA4\x04\x00#{aid.size.chr}#{aid}")
|
39
|
-
raise IsoDep::Error, "Application not found: #{aid.unpack('H*').pop}" unless "\x90\x00" == sw
|
40
|
-
end
|
41
|
-
|
42
38
|
super(&block)
|
43
39
|
else
|
44
40
|
raise IsoDep::Error, "Can't select tag: #{res}"
|
45
41
|
end
|
46
42
|
end
|
47
43
|
|
48
|
-
def
|
44
|
+
def disconnect
|
49
45
|
0 == LibNFC.nfc_initiator_deselect_target(@reader.ptr)
|
50
46
|
end
|
51
47
|
|
48
|
+
# Public: select application with give AID
|
49
|
+
#
|
50
|
+
# aid - Identifier of the application that should be selected
|
51
|
+
#
|
52
|
+
# Returns APDU::Response
|
53
|
+
def select(aid)
|
54
|
+
send_apdu("\x00\xA4\x04\x00#{aid.size.chr}#{aid}")
|
55
|
+
end
|
56
|
+
|
57
|
+
# Public: same as select but raises an APDU::Errno exception if
|
58
|
+
# application not present on the card or SW is not equal to 0x9000
|
59
|
+
#
|
60
|
+
# aid - Identifier of the application that should be selected
|
61
|
+
#
|
62
|
+
# Returns APDU::Response
|
63
|
+
# Raises APDU::Errno
|
64
|
+
def select!(aid)
|
65
|
+
select(aid).raise_errno!
|
66
|
+
end
|
67
|
+
|
68
|
+
# Public: Select application with given AID (Application Identifier)
|
69
|
+
#
|
70
|
+
# aid - Application Identifier of an applet located on a card
|
71
|
+
#
|
72
|
+
# Returns nothing.
|
73
|
+
# Raises APDU::Errno if application with such AID doesn't exists on a card
|
74
|
+
def select(aid)
|
75
|
+
send_apdu!("\x00\xA4\x04\x00#{aid.size.chr}#{aid}")
|
76
|
+
end
|
77
|
+
|
78
|
+
# Public: Send APDU command to tag
|
79
|
+
#
|
80
|
+
# apdu - APDU command to send. see ISO/IEC 7816-4 or wiki for details.
|
81
|
+
# APDU is a binary string that should
|
82
|
+
#
|
83
|
+
#
|
84
|
+
# Returns APDU::Response object
|
85
|
+
# Raises IsoDep::Error if card didn't respond
|
52
86
|
def send_apdu(apdu)
|
53
87
|
cmd = apdu
|
54
88
|
cmd.force_encoding('ASCII-8BIT')
|
@@ -62,7 +96,18 @@ module IsoDep
|
|
62
96
|
|
63
97
|
raise IsoDep::Error, "APDU sending failed: #{res_len}" if res_len < 0
|
64
98
|
|
65
|
-
response_buffer.get_bytes(0, res_len).to_s
|
99
|
+
APDU::Response.new(response_buffer.get_bytes(0, res_len).to_s)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Public: Send APDU command to tag and raises APDU::Errno exception
|
103
|
+
# if SW not equal to 0x9000
|
104
|
+
#
|
105
|
+
# apdu - APDU command to transmit to the tag
|
106
|
+
#
|
107
|
+
# Returns APDU::Response object
|
108
|
+
# Raises APDU::Errno if SW is not equal to 0x9000
|
109
|
+
def send_apdu!(apdu)
|
110
|
+
send_apdu(apdu).raise_errno!
|
66
111
|
end
|
67
112
|
|
68
113
|
alias :'<<' :send_apdu
|
@@ -40,18 +40,18 @@ module Mifare
|
|
40
40
|
@auth_block = nil #last authenticated block
|
41
41
|
end
|
42
42
|
|
43
|
-
def
|
43
|
+
def connect(&block)
|
44
44
|
@reader.set_flag(:NP_AUTO_ISO14443_4, false)
|
45
45
|
|
46
46
|
res = Mifare.mifare_classic_connect(@pointer)
|
47
47
|
if 0 == res
|
48
48
|
super
|
49
49
|
else
|
50
|
-
raise Mifare::Error, "Can't
|
50
|
+
raise Mifare::Error, "Can't connect to tag: #{res}"
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
-
def
|
54
|
+
def disconnect
|
55
55
|
Mifare.mifare_classic_disconnect(@pointer)
|
56
56
|
super
|
57
57
|
end
|
@@ -59,58 +59,62 @@ module Mifare
|
|
59
59
|
# keytype can be :key_a or :key_b
|
60
60
|
# key - hexadecimal string key representation like "FFFFFFFFFFFF"
|
61
61
|
def auth(block_num, key_type, key)
|
62
|
-
raise Mifare::Error
|
63
|
-
raise Mifare::Error
|
62
|
+
raise Mifare::Error, "Wrong key type" unless [:key_a, :key_b].include? key_type
|
63
|
+
raise Mifare::Error, "Wrong key length" unless [6, 12].include? key.size
|
64
64
|
|
65
65
|
key_ptr = FFI::MemoryPointer.new(:uchar, 6)
|
66
|
-
key_ptr.put_bytes(0, 6 == key.size ? key : [key].pack(
|
66
|
+
key_ptr.put_bytes(0, 6 == key.size ? key : [key].pack("H*"))
|
67
67
|
|
68
68
|
res = Mifare.mifare_classic_authenticate(@pointer, block_num, key_ptr,
|
69
69
|
key_type)
|
70
|
-
if 0
|
71
|
-
|
72
|
-
|
73
|
-
raise Mifare::Error.new("Can't autenticate to block 0x%02x" % block_num)
|
74
|
-
end
|
70
|
+
raise Mifare::Error, "Can't autenticate to block 0x%02x" % block_num if 0 != res
|
71
|
+
|
72
|
+
@auth_block = block_num
|
75
73
|
end
|
76
74
|
|
77
75
|
# block number to read
|
78
76
|
def read(block_num = nil)
|
79
77
|
block_num ||= @auth_block
|
80
|
-
raise Mifare::Error
|
78
|
+
raise Mifare::Error, "Not authenticated" unless block_num
|
81
79
|
|
82
80
|
data_ptr = FFI::MemoryPointer.new(:uchar, 16)
|
83
81
|
res = Mifare.mifare_classic_read(@pointer, block_num, data_ptr)
|
84
82
|
|
85
|
-
raise Mifare::Error
|
83
|
+
raise Mifare::Error, "Can't read block 0x%02x" % block_num if 0 != res
|
86
84
|
|
87
|
-
data_ptr.get_bytes(0, 16).force_encoding(
|
85
|
+
data_ptr.get_bytes(0, 16).force_encoding("ASCII-8BIT")
|
88
86
|
end
|
89
87
|
|
90
88
|
# @data - 16 bytes represented by hexadecimal string
|
91
89
|
# @block_num - number of block to write to
|
92
90
|
def write(data, block_num = nil)
|
93
|
-
raise Mifare::Error.new('Wrong data given') if data !~ /^[\da-f]{32}$/i
|
94
|
-
|
95
91
|
block_num ||= @auth_block
|
96
|
-
raise Mifare::Error
|
92
|
+
raise Mifare::Error, "Not authenticated" unless block_num
|
93
|
+
|
94
|
+
write_data = if data =~ /^[\da-f]{32}$/i
|
95
|
+
[data].pack("H*")
|
96
|
+
elsif 16 == data.size
|
97
|
+
data.dup
|
98
|
+
else
|
99
|
+
raise Mifare::Error, "Wrong data given"
|
100
|
+
end
|
97
101
|
|
98
102
|
data_ptr = FFI::MemoryPointer.new(:uchar, 16)
|
99
|
-
data_ptr.put_bytes(0,
|
103
|
+
data_ptr.put_bytes(0, write_data)
|
100
104
|
|
101
105
|
res = Mifare.mifare_classic_write(@pointer, block_num, data_ptr)
|
102
|
-
|
106
|
+
raise Mifare::Error, "Can't write block 0x%02x" % block_num if 0 != res
|
103
107
|
end
|
104
108
|
|
105
109
|
# Create value block structure and write it to block
|
106
110
|
def init_value(value, addr = nil, block_num = nil)
|
107
111
|
block_num ||= @auth_block
|
108
|
-
raise Mifare::Error
|
112
|
+
raise Mifare::Error, "Not authenticated" unless block_num
|
109
113
|
|
110
114
|
addr ||= 0
|
111
115
|
|
112
116
|
res = Mifare.mifare_classic_init_value(@pointer, block_num, value, addr)
|
113
|
-
|
117
|
+
raise Mifare::Error, "Can't init value block 0x%02x" % block_num if 0 != res
|
114
118
|
end
|
115
119
|
|
116
120
|
# returns only value part of value block
|
@@ -122,12 +126,12 @@ module Mifare
|
|
122
126
|
# returns value and addr
|
123
127
|
def value_with_addr(block_num = nil)
|
124
128
|
block_num ||= @auth_block
|
125
|
-
raise Mifare::Error
|
129
|
+
raise Mifare::Error, "Not authenticated" unless block_num
|
126
130
|
|
127
131
|
value_ptr = FFI::MemoryPointer.new(:int32, 1)
|
128
132
|
addr_ptr = FFI::MemoryPointer.new(:uchar, 1)
|
129
133
|
res = Mifare.mifare_classic_read_value(@pointer, block_num, value_ptr, addr_ptr)
|
130
|
-
raise Mifare::Error
|
134
|
+
raise Mifare::Error, "Can't read value block 0x%02x" % block_num if 0 != res
|
131
135
|
|
132
136
|
[value_ptr.get_int32(0), addr_ptr.get_uchar(0)]
|
133
137
|
end
|
@@ -135,27 +139,27 @@ module Mifare
|
|
135
139
|
# Mifare classic increment
|
136
140
|
def inc(amount = 1, block_num = nil)
|
137
141
|
block_num ||= @auth_block
|
138
|
-
raise Mifare::Error
|
142
|
+
raise Mifare::Error, "Not authenticated" unless block_num
|
139
143
|
|
140
144
|
res = Mifare.mifare_classic_increment(@pointer, block_num, amount)
|
141
|
-
|
145
|
+
raise Mifare::Error, "Can't increment block 0x%02x" % block_num if 0 != res
|
142
146
|
end
|
143
147
|
|
144
148
|
# Mifare classic decrement
|
145
149
|
def dec(amount = 1, block_num = nil)
|
146
150
|
block_num ||= @auth_block
|
147
|
-
raise Mifare::Error
|
151
|
+
raise Mifare::Error, "Not authenticated" unless block_num
|
148
152
|
|
149
153
|
res = Mifare.mifare_classic_decrement(@pointer, block_num, amount)
|
150
|
-
|
154
|
+
raise Mifare::Error, "Can't decrement block 0x%02x" % block_num if 0 != res
|
151
155
|
end
|
152
156
|
|
153
157
|
def transfer(block_num = nil)
|
154
158
|
block_num ||= @auth_block
|
155
|
-
raise Mifare::Error
|
159
|
+
raise Mifare::Error, "Not authenticated" unless block_num
|
156
160
|
|
157
161
|
res = Mifare.mifare_classic_transfer(@pointer, block_num)
|
158
|
-
|
162
|
+
raise Mifare::Error, "Can't transfer to block 0x%02x" % block_num if 0 != res
|
159
163
|
end
|
160
164
|
|
161
165
|
# Check's if our tag class is able to handle this LibNFC::Target
|
@@ -14,18 +14,18 @@ module Mifare
|
|
14
14
|
|
15
15
|
module Ultralight
|
16
16
|
class Tag < Mifare::Tag
|
17
|
-
def
|
17
|
+
def connect(&block)
|
18
18
|
@reader.set_flag(:NP_AUTO_ISO14443_4, false)
|
19
19
|
|
20
20
|
res = Mifare.mifare_ultralight_connect(@pointer)
|
21
21
|
if 0 == res
|
22
22
|
super
|
23
23
|
else
|
24
|
-
raise Mifare::Error, "Can't
|
24
|
+
raise Mifare::Error, "Can't connect to tag: #{res}"
|
25
25
|
end
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
28
|
+
def disconnect
|
29
29
|
Mifare.mifare_ultralight_disconnect(@pointer)
|
30
30
|
super
|
31
31
|
end
|
data/lib/ruby-nfc/tags/tag.rb
CHANGED
@@ -6,12 +6,12 @@ module NFC
|
|
6
6
|
@processed = false
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
9
|
+
def connect(&block)
|
10
10
|
if block_given?
|
11
11
|
begin
|
12
12
|
self.instance_eval(&block)
|
13
13
|
ensure
|
14
|
-
|
14
|
+
disconnect
|
15
15
|
end
|
16
16
|
end
|
17
17
|
end
|
@@ -24,6 +24,8 @@ module NFC
|
|
24
24
|
@target.processed?
|
25
25
|
end
|
26
26
|
|
27
|
+
def disconnect; end
|
28
|
+
|
27
29
|
def uid
|
28
30
|
uid_size = @target[:nti][:nai][:szUidLen]
|
29
31
|
@target[:nti][:nai][:abtUid].to_s[0...uid_size]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-nfc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Maxim Chechel
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - ! '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
27
41
|
description: ! " \tThis gem is built on top of libnfc and libfreefare using ffi and
|
28
42
|
supports:\n\t\t * Reading and writing Mifare Classic and Ultralight tags\n\t\t *
|
29
43
|
Android HCE / Blackberry VTE emulated tags\n\t\t * Dual-interface smart cards like
|
@@ -33,7 +47,11 @@ executables: []
|
|
33
47
|
extensions: []
|
34
48
|
extra_rdoc_files: []
|
35
49
|
files:
|
50
|
+
- ./LICENSE
|
36
51
|
- ./lib/ruby-nfc.rb
|
52
|
+
- ./lib/ruby-nfc/apdu/apdu.rb
|
53
|
+
- ./lib/ruby-nfc/apdu/request.rb
|
54
|
+
- ./lib/ruby-nfc/apdu/response.rb
|
37
55
|
- ./lib/ruby-nfc/libnfc.rb
|
38
56
|
- ./lib/ruby-nfc/nfc.rb
|
39
57
|
- ./lib/ruby-nfc/reader.rb
|
@@ -46,8 +64,8 @@ homepage: https://github.com/maximchick/ruby-nfc
|
|
46
64
|
licenses:
|
47
65
|
- MIT
|
48
66
|
metadata: {}
|
49
|
-
post_install_message: ! "
|
50
|
-
instructions here: \n\
|
67
|
+
post_install_message: ! " \tDon't forget to install libnfc and libfreefare\n \tsee
|
68
|
+
installation instructions here: \n \thttps://github.com/maximchick/ruby-nfc\n"
|
51
69
|
rdoc_options: []
|
52
70
|
require_paths:
|
53
71
|
- lib
|