mfrc522 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|