ruby-nfc 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|