mfrc522 0.0.1 → 0.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 +4 -4
- data/lib/mfrc522.rb +136 -15
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0b93d1ee7b7e4227a995c53b735a324b10aa1610
|
4
|
+
data.tar.gz: 1e003e1c431ed8a37152a2e375ba957fc66757af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1f70e7173cf0118b6f9f20cf3913d26887f1d633b9ae5933250081c37762bf2b6babc0e08521e17ddf1e91f49368cc5ebefe4bcae652e5fcfd5a02f9996fc7d7
|
7
|
+
data.tar.gz: e8b2f2a2db1270bd0a54fc997989964a5016224b8782089727a091fb7b77181f5bc6f58f78d422af110d47bb0593125ccd980b4a031f1962f42cd3f252f7df79
|
data/lib/mfrc522.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'pi_piper'
|
2
|
+
|
2
3
|
include PiPiper
|
3
4
|
|
4
5
|
class Mfrc522
|
@@ -25,8 +26,10 @@ class Mfrc522
|
|
25
26
|
# The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6)
|
26
27
|
# The PICC_MF_READ and PICC_MF_WRITE can also be used for MIFARE Ultralight.
|
27
28
|
PICC_UL_WRITE = 0xA2 # Writes one 4 byte page to the PICC.
|
29
|
+
# The commands here is for 3DES Authentication
|
30
|
+
PICC_MF_3DES_AUTH = 0x1A
|
28
31
|
#
|
29
|
-
PICC_MF_ACK =
|
32
|
+
PICC_MF_ACK = 0x0A # Mifare Acknowledge
|
30
33
|
|
31
34
|
# PCD commands
|
32
35
|
PCD_Idle = 0x00 # no action, cancels current command execution
|
@@ -98,6 +101,7 @@ class Mfrc522
|
|
98
101
|
TestDAC2Reg = 0x3A # defines the test value for TestDAC2
|
99
102
|
TestADCReg = 0x3B # shows the value of ADC I and Q channels
|
100
103
|
|
104
|
+
# Constructor
|
101
105
|
def initialize(nrstpd = 24, chip = 0, spd = 8000000, timer = 50)
|
102
106
|
chip_option = { 0 => PiPiper::Spi::CHIP_SELECT_0,
|
103
107
|
1 => PiPiper::Spi::CHIP_SELECT_1,
|
@@ -124,11 +128,13 @@ class Mfrc522
|
|
124
128
|
antenna_on # Turn antenna on. They were disabled by the reset.
|
125
129
|
end
|
126
130
|
|
131
|
+
# MFRC522 software reset
|
127
132
|
def soft_reset
|
128
133
|
write_spi(CommandReg, PCD_SoftReset)
|
129
134
|
sleep 1.0 / 20.0 # wait 50ms
|
130
135
|
end
|
131
136
|
|
137
|
+
# Read from SPI communication
|
132
138
|
def read_spi(reg)
|
133
139
|
output = 0
|
134
140
|
PiPiper::Spi.begin do |spi|
|
@@ -144,6 +150,7 @@ class Mfrc522
|
|
144
150
|
output
|
145
151
|
end
|
146
152
|
|
153
|
+
# Write to SPI communication
|
147
154
|
def write_spi(reg, values)
|
148
155
|
PiPiper::Spi.begin do |spi|
|
149
156
|
spi.chip_select_active_low(true)
|
@@ -156,25 +163,30 @@ class Mfrc522
|
|
156
163
|
end
|
157
164
|
end
|
158
165
|
|
166
|
+
# Helper for setting bits by mask
|
159
167
|
def write_spi_set_bitmask(reg, mask)
|
160
168
|
value = read_spi(reg)
|
161
169
|
write_spi(reg, value | mask)
|
162
170
|
end
|
163
171
|
|
172
|
+
# Helper for clearing bits by mask
|
164
173
|
def write_spi_clear_bitmask(reg, mask)
|
165
174
|
value = read_spi(reg)
|
166
175
|
write_spi(reg, value & (~mask))
|
167
176
|
end
|
168
177
|
|
178
|
+
# Turn antenna on
|
169
179
|
def antenna_on
|
170
180
|
value = read_spi(TxControlReg)
|
171
181
|
write_spi_set_bitmask(TxControlReg, 0x03) if (value & 0x03) != 0x03
|
172
182
|
end
|
173
183
|
|
184
|
+
# Turn antenna off
|
174
185
|
def antenna_off
|
175
186
|
write_spi_clear_bitmask(TxControlReg, 0x03)
|
176
187
|
end
|
177
188
|
|
189
|
+
# Modify and show antenna gain level
|
178
190
|
# level = 1: 18dB, 2: 23dB, 3: 33dB, 4: 38dB, 5: 43dB, 6: 48dB
|
179
191
|
def antenna_gain(level = nil)
|
180
192
|
unless level.nil?
|
@@ -184,6 +196,7 @@ class Mfrc522
|
|
184
196
|
(read_spi(RFCfgReg) & 0x70) >> 4
|
185
197
|
end
|
186
198
|
|
199
|
+
# Calculate CRC using MFRC522's built-in coprocessor
|
187
200
|
def calculate_crc(data)
|
188
201
|
write_spi(CommandReg, PCD_Idle) # Stop any active command.
|
189
202
|
write_spi(DivIrqReg, 0x04) # Clear the CRCIRq interrupt request bit
|
@@ -209,6 +222,7 @@ class Mfrc522
|
|
209
222
|
return :status_ok, result
|
210
223
|
end
|
211
224
|
|
225
|
+
# Calculate and append CRC to data
|
212
226
|
def append_crc(data)
|
213
227
|
status, crc = calculate_crc(data)
|
214
228
|
return status if status != :status_ok
|
@@ -217,6 +231,7 @@ class Mfrc522
|
|
217
231
|
return :status_ok, data
|
218
232
|
end
|
219
233
|
|
234
|
+
# Check CRC using MFRC522's built-in coprocessor
|
220
235
|
def check_crc(data)
|
221
236
|
status, crc = calculate_crc(data[0..-3])
|
222
237
|
return status if status != :status_ok
|
@@ -225,6 +240,7 @@ class Mfrc522
|
|
225
240
|
return :status_ok
|
226
241
|
end
|
227
242
|
|
243
|
+
# PICC transceive helper
|
228
244
|
def communicate_with_picc(command, send_data, framing_bit = 0, check_crc = false)
|
229
245
|
wait_irq = 0x00
|
230
246
|
wait_irq = 0x10 if command == PCD_MFAuthent
|
@@ -288,7 +304,7 @@ class Mfrc522
|
|
288
304
|
status, _received_data, valid_bits = communicate_with_picc(PCD_Transceive, picc_command, 0x07)
|
289
305
|
|
290
306
|
return status if status != :status_ok
|
291
|
-
return :
|
307
|
+
return :status_unknown_data if valid_bits != 0 # REQA or WUPA command return 16 bits(full byte)
|
292
308
|
|
293
309
|
return :status_ok
|
294
310
|
end
|
@@ -311,6 +327,8 @@ class Mfrc522
|
|
311
327
|
return status
|
312
328
|
end
|
313
329
|
|
330
|
+
# Select PICC for further communication
|
331
|
+
#
|
314
332
|
# PICC must be in state ACTIVE
|
315
333
|
def picc_select
|
316
334
|
# Description of buffer structure:
|
@@ -344,6 +362,8 @@ class Mfrc522
|
|
344
362
|
for current_cascade_level in 0..2
|
345
363
|
buffer = [select_level[current_cascade_level]]
|
346
364
|
current_level_known_bits = 0
|
365
|
+
received_data = []
|
366
|
+
valid_bits = 0
|
347
367
|
|
348
368
|
loop do
|
349
369
|
if current_level_known_bits >= 32 # Prepare to do a complete select if we knew everything
|
@@ -368,7 +388,7 @@ class Mfrc522
|
|
368
388
|
return status if status != :status_ok
|
369
389
|
|
370
390
|
# Append received UID into buffer if not doing full select
|
371
|
-
buffer = buffer[0...all_full_byte] + received_data if current_level_known_bits < 32
|
391
|
+
buffer = buffer[0...all_full_byte] + received_data[0..3] if current_level_known_bits < 32
|
372
392
|
|
373
393
|
# Handle collision
|
374
394
|
if status == :status_collision
|
@@ -413,6 +433,37 @@ class Mfrc522
|
|
413
433
|
return :status_ok, uid, sak
|
414
434
|
end
|
415
435
|
|
436
|
+
# Lookup error message
|
437
|
+
def error_type(error)
|
438
|
+
case error
|
439
|
+
when :status_ok
|
440
|
+
'It worked'
|
441
|
+
when :status_pcd_timeout
|
442
|
+
'Reader did not responding'
|
443
|
+
when :status_picc_timeout
|
444
|
+
'Tag did not responding'
|
445
|
+
when :status_crc_error
|
446
|
+
'CRC check failed'
|
447
|
+
when :status_mifare_nack
|
448
|
+
'Tag sent negative acknowledge'
|
449
|
+
when :status_collision
|
450
|
+
'Multiple tags detected'
|
451
|
+
when :status_unknown_data
|
452
|
+
'Incorrect data received'
|
453
|
+
when :status_internal_error
|
454
|
+
'Something went wrong but it shouldnt happen'
|
455
|
+
when :status_sak_error
|
456
|
+
'Incorrect select acknowledge'
|
457
|
+
when :status_auth_failed
|
458
|
+
'Authentication failed'
|
459
|
+
when :status_error
|
460
|
+
'Something went wrong'
|
461
|
+
else
|
462
|
+
'Unknown error type'
|
463
|
+
end
|
464
|
+
end
|
465
|
+
|
466
|
+
# Lookup PICC name using sak
|
416
467
|
def picc_type(sak)
|
417
468
|
sak &= 0x7F
|
418
469
|
|
@@ -440,6 +491,12 @@ class Mfrc522
|
|
440
491
|
end
|
441
492
|
end
|
442
493
|
|
494
|
+
# Check if Mifare PICC
|
495
|
+
def mifare?(sak)
|
496
|
+
sak & 0x20 != 1
|
497
|
+
end
|
498
|
+
|
499
|
+
# Start encrypted Crypto1 communication between reader and Mifare PICC
|
443
500
|
#
|
444
501
|
# PICC must be selected before calling for authentication
|
445
502
|
# Remember to deauthenticate after communication, or no new communication can be made
|
@@ -447,13 +504,13 @@ class Mfrc522
|
|
447
504
|
# Accept PICC_MF_AUTH_KEY_A or PICC_MF_AUTH_KEY_B command
|
448
505
|
# Checks datasheets for block address numbering of your PICC
|
449
506
|
#
|
450
|
-
def
|
507
|
+
def mifare_crypto1_authenticate(command, block_addr, sector_key, uid)
|
451
508
|
#
|
452
509
|
# Buffer[12]: {command, block_addr, sector_key[6], uid[4]}
|
453
510
|
#
|
454
511
|
buffer = [command, block_addr]
|
455
512
|
buffer += sector_key[0..5]
|
456
|
-
buffer += uid[
|
513
|
+
buffer += uid[-4..-1]
|
457
514
|
|
458
515
|
status, _received_data, _valid_bits = communicate_with_picc(PCD_MFAuthent, buffer)
|
459
516
|
|
@@ -463,11 +520,63 @@ class Mfrc522
|
|
463
520
|
return :status_ok
|
464
521
|
end
|
465
522
|
|
466
|
-
|
523
|
+
# Stop Mifare encrypted communication
|
524
|
+
def mifare_crypto1_deauthenticate
|
467
525
|
write_spi_clear_bitmask(Status2Reg, 0x08) # Clear MFCrypto1On bit
|
468
526
|
end
|
469
527
|
|
470
|
-
|
528
|
+
def mifare_3des_authenticate(des_key)
|
529
|
+
require 'openssl'
|
530
|
+
require 'securerandom'
|
531
|
+
|
532
|
+
# Cipher
|
533
|
+
cipher = OpenSSL::Cipher.new 'des-ede3-cbc'
|
534
|
+
cipher.key = [des_key*2].pack('H*')
|
535
|
+
cipher.padding = 0
|
536
|
+
|
537
|
+
# Ask for authentication
|
538
|
+
buffer = [PICC_MF_3DES_AUTH, 0x00]
|
539
|
+
status, received_data = mifare_transceive(buffer)
|
540
|
+
return status if status != :status_ok
|
541
|
+
return :status_unknown_data if received_data[0] != 0xAF
|
542
|
+
|
543
|
+
# Use received data as IV for next transmission
|
544
|
+
next_iv = received_data[1..8]
|
545
|
+
|
546
|
+
# Decrypt challenge random number and rotate it by 8 bits
|
547
|
+
cipher.decrypt
|
548
|
+
cipher.iv = "\x00"*8
|
549
|
+
challenge = received_data[1..8].pack('C*')
|
550
|
+
challenge = cipher.update(challenge) + cipher.final
|
551
|
+
challenge.rotate!
|
552
|
+
|
553
|
+
# Generate 8 bytes random number and encrypt the response
|
554
|
+
random_number = SecureRandom.random_bytes(8)
|
555
|
+
cipher.encrypt
|
556
|
+
cipher.iv = next_iv
|
557
|
+
response = cipher.update(challenge.pack('C*') + random_number) + cipher.final
|
558
|
+
|
559
|
+
# Receive verification
|
560
|
+
buffer = [0xAF, [response].unpack('H*')]
|
561
|
+
status, received_data = mifare_transceive(buffer)
|
562
|
+
return status if status != :status_ok
|
563
|
+
return :status_unknown_data if received_data[0] != 0x00
|
564
|
+
|
565
|
+
# Check if verification matches random_number rotated by 8 bits
|
566
|
+
cipher.decrypt
|
567
|
+
cipher.iv = [response[-16..-1]].pack('H*')
|
568
|
+
verification = received_data[1..8].pack('C*')
|
569
|
+
verification = cipher.update(verification) + cipher.final
|
570
|
+
|
571
|
+
if random_number.bytes.rotate! != verification
|
572
|
+
picc_halt
|
573
|
+
return :status_auth_failed
|
574
|
+
end
|
575
|
+
|
576
|
+
return :status_ok
|
577
|
+
end
|
578
|
+
|
579
|
+
# Helper that append CRC to buffer and check CRC or Mifare acknowledge
|
471
580
|
def mifare_transceive(send_data, accept_timeout = false)
|
472
581
|
# Append CRC
|
473
582
|
status, send_data = append_crc(send_data)
|
@@ -478,25 +587,32 @@ class Mfrc522
|
|
478
587
|
return :status_ok if status == :status_picc_timeout && accept_timeout
|
479
588
|
return status if status != :status_ok
|
480
589
|
|
481
|
-
#
|
482
|
-
|
590
|
+
# Data exists, check CRC and return
|
591
|
+
if received_data.size > 1
|
592
|
+
return :status_crc_error if received_data.count < 2 || valid_bits != 0
|
593
|
+
|
594
|
+
status = check_crc(received_data)
|
595
|
+
return status, received_data
|
596
|
+
end
|
597
|
+
|
598
|
+
# Data doesn't exist, check mifare acknowledge
|
599
|
+
return :status_error if received_data.size != 1 || valid_bits != 4 # ACK is 4 bits long
|
483
600
|
return :status_mifare_nack if received_data[0] != PICC_MF_ACK
|
484
601
|
|
485
602
|
return :status_ok
|
486
603
|
end
|
487
604
|
|
605
|
+
# Read Mifare block address
|
488
606
|
def mifare_read(block_addr)
|
489
607
|
buffer = [PICC_MF_READ, block_addr]
|
490
608
|
|
491
|
-
status,
|
492
|
-
return status if status != :status_ok
|
493
|
-
|
494
|
-
status, received_data, _valid_bits = communicate_with_picc(PCD_Transceive, buffer, 0, true)
|
609
|
+
status, received_data = mifare_transceive(buffer)
|
495
610
|
return status if status != :status_ok
|
496
611
|
|
497
612
|
return :status_ok, received_data
|
498
613
|
end
|
499
614
|
|
615
|
+
# Write Mifare block address
|
500
616
|
def mifare_write(block_addr, send_data)
|
501
617
|
buffer = [PICC_MF_WRITE, block_addr]
|
502
618
|
|
@@ -511,6 +627,7 @@ class Mfrc522
|
|
511
627
|
return :status_ok
|
512
628
|
end
|
513
629
|
|
630
|
+
# Write helper for Mifare UL
|
514
631
|
def mifare_ultralight_write(page, send_data)
|
515
632
|
# Page 2-15, each 4 bytes
|
516
633
|
buffer = [PICC_UL_WRITE, page]
|
@@ -543,7 +660,7 @@ class Mfrc522
|
|
543
660
|
# byte 13: copy of byte 12 with inverted bits (aka. XOR 255)
|
544
661
|
# byte 14: copy of byte 12
|
545
662
|
# byte 15: copy of byte 13
|
546
|
-
|
663
|
+
buffer = []
|
547
664
|
buffer[0] = value & 0xFF
|
548
665
|
buffer[1] = (value >> 8) & 0xFF
|
549
666
|
buffer[2] = (value >> 16) & 0xFF
|
@@ -561,7 +678,7 @@ class Mfrc522
|
|
561
678
|
buffer[14] = buffer[12]
|
562
679
|
buffer[15] = buffer[13]
|
563
680
|
|
564
|
-
mifare_write(
|
681
|
+
mifare_write(block_addr, buffer)
|
565
682
|
end
|
566
683
|
|
567
684
|
# Helper for increment, decrement, and restore command
|
@@ -585,21 +702,25 @@ class Mfrc522
|
|
585
702
|
return :status_ok
|
586
703
|
end
|
587
704
|
|
705
|
+
# Mifare increment helper
|
588
706
|
# MIFARE Classic only
|
589
707
|
def mifare_increment(block_addr, delta)
|
590
708
|
mifare_two_step(PICC_MF_INCREMENT, block_addr, delta)
|
591
709
|
end
|
592
710
|
|
711
|
+
# Mifare decrement helper
|
593
712
|
# MIFARE Classic only
|
594
713
|
def mifare_decrement(block_addr, delta)
|
595
714
|
mifare_two_step(PICC_MF_DECREMENT, block_addr, delta)
|
596
715
|
end
|
597
716
|
|
717
|
+
# Mifare restore helper
|
598
718
|
# MIFARE Classic only
|
599
719
|
def mifare_restore(block_addr)
|
600
720
|
mifare_two_step(PICC_MF_RESTORE, block_addr, 0)
|
601
721
|
end
|
602
722
|
|
723
|
+
# Mifare transfer helper
|
603
724
|
# MIFARE Classic only
|
604
725
|
def mifare_transfer(block_addr)
|
605
726
|
buffer = [PICC_MF_TRANSFER, block_addr]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mfrc522
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- atitan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-22 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pi_piper
|
@@ -57,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
57
|
version: '0'
|
58
58
|
requirements: []
|
59
59
|
rubyforge_project:
|
60
|
-
rubygems_version: 2.
|
60
|
+
rubygems_version: 2.5.1
|
61
61
|
signing_key:
|
62
62
|
specification_version: 4
|
63
63
|
summary: MFRC522 RFID Reader Library for RaspberryPi
|