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 CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- YmQ5NDY5MzE2MTUwMDJlMjEwYjNkMjU0ZDkxZTU1YmRjYTAyODUyOA==
4
+ NWMzYmQwMDJkYzBlMmMzZjM0NmRhNTdmYWJhNjk0Y2E2MDM5ZGYwOQ==
5
5
  data.tar.gz: !binary |-
6
- M2U3Mjc5ZTY3YWI0MTg3ZTYwZTZmYzExOThhYjNkM2Y5NzVhMWQzZQ==
6
+ Mjk2ZmVmNGIxMDQwY2I0ZDFjNDJmZTYxZWJjNTM1NjlkOTkwMDYzMA==
7
7
  SHA512:
8
8
  metadata.gz: !binary |-
9
- NzMwMTFlZWVlZmUwNDIzOWEzYWQ5NzIwMDY5N2UzNTViNTYwYTQwYzI1OTQ1
10
- ZmFkNzE4NjAzZDk4M2UxMzFjMTFlODliZmVkNjdhN2Y2NDk2ZWJhOWY4ZGE4
11
- NDhlYTI2NjY2NGE1NzVhZDRmMTg4NTk2ZDNkMzQwY2UxOWYzZjE=
9
+ NGJkOTIxOWY1YTNjY2JlN2FhMTQ2NDNmNDkzNWQzYTI2OTdlNGRkOTk3MDVj
10
+ YTdmNjA0N2UzNzgxNjYyMDc5NjY4NzI2MWIzNTRiMjg3YzUyY2ZmOGU2MTVl
11
+ YmI3NzM0NTc5MDFhYWIxZmY2OGU1NTYwZGY0OTAzNTE0NTI0MDI=
12
12
  data.tar.gz: !binary |-
13
- ZWNjMzUxZGQ2OGEyNDc5OGNlNzgwNzBhMTI5MWQ3MmU1NWRmZTc4ZjA2NWRl
14
- M2YzMTc0NGZkMTM1ZDliNDVlNmRlZmU4NjA5NzY1ZmZkYTEzMjRjYmY4NGIy
15
- ZWY5ZTc4NjJjYTZlN2QyYzJjMGRmODM2MWFlZDJmNjFhZmMyY2Q=
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
@@ -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
- yield tag
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
@@ -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 select(aid = nil, &block)
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 deselect
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 select(&block)
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 select tag: #{res}"
50
+ raise Mifare::Error, "Can't connect to tag: #{res}"
51
51
  end
52
52
  end
53
53
 
54
- def deselect
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.new('Wrong key type') unless [:key_a, :key_b].include? key_type
63
- raise Mifare::Error.new('Wrong key length') unless [6, 12].include? key.size
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('H*'))
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 == res
71
- @auth_block = block_num
72
- else
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.new('Not authenticated') unless block_num
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.new("Can't read block 0x%02x" % block_num) unless 0 == res
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('ASCII-8BIT')
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.new('Not authenticated') unless block_num
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, [data].pack('H*'))
103
+ data_ptr.put_bytes(0, write_data)
100
104
 
101
105
  res = Mifare.mifare_classic_write(@pointer, block_num, data_ptr)
102
- (0 == res) || raise(Mifare::Error.new("Can't write block 0x%02x" % block_num))
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.new('Not authenticated') unless block_num
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
- (0 == res) || raise(Mifare::Error.new("Can't init value block 0x%02x" % block_num))
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.new('Not authenticated') unless block_num
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.new("Can't read value block 0x%02x" % block_num) unless 0 == res
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.new('Not authenticated') unless block_num
142
+ raise Mifare::Error, "Not authenticated" unless block_num
139
143
 
140
144
  res = Mifare.mifare_classic_increment(@pointer, block_num, amount)
141
- (0 == res) || raise(Mifare::Error.new("Can't increment block 0x%02x" % block_num))
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.new('Not authenticated') unless block_num
151
+ raise Mifare::Error, "Not authenticated" unless block_num
148
152
 
149
153
  res = Mifare.mifare_classic_decrement(@pointer, block_num, amount)
150
- (0 == res) || raise(Mifare::Error.new("Can't decrement block 0x%02x" % block_num))
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.new('Not authenticated') unless block_num
159
+ raise Mifare::Error, "Not authenticated" unless block_num
156
160
 
157
161
  res = Mifare.mifare_classic_transfer(@pointer, block_num)
158
- (0 == res) || raise(Mifare::Error.new("Can't transfer to block 0x%02x" % block_num))
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
@@ -51,7 +51,7 @@ module Mifare
51
51
  end
52
52
 
53
53
  # frees memory allocated for mifare tag
54
- def deselect
54
+ def disconnect
55
55
  Mifare.freefare_free_tag(@pointer)
56
56
  end
57
57
 
@@ -14,18 +14,18 @@ module Mifare
14
14
 
15
15
  module Ultralight
16
16
  class Tag < Mifare::Tag
17
- def select(&block)
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 select tag: #{res}"
24
+ raise Mifare::Error, "Can't connect to tag: #{res}"
25
25
  end
26
26
  end
27
27
 
28
- def deselect
28
+ def disconnect
29
29
  Mifare.mifare_ultralight_disconnect(@pointer)
30
30
  super
31
31
  end
@@ -6,12 +6,12 @@ module NFC
6
6
  @processed = false
7
7
  end
8
8
 
9
- def select(&block)
9
+ def connect(&block)
10
10
  if block_given?
11
11
  begin
12
12
  self.instance_eval(&block)
13
13
  ensure
14
- deselect
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.0.0
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: ! "Don't forget to install libnfc and libfreefare\nsee installation
50
- instructions here: \n\t https://github.com/maximchick/ruby-nfc"
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