mfrc522 0.1.2 → 0.2.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 +115 -184
- data/lib/mifare/base.rb +22 -0
- data/lib/mifare/classic.rb +139 -0
- data/lib/mifare/des_fire.rb +7 -0
- data/lib/mifare/plus.rb +7 -0
- data/lib/mifare/ultralight.rb +44 -0
- data/lib/mifare/ultralight_c.rb +42 -0
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ae695036f039f29942ea9a3e46cb365fb1e4fe7
|
4
|
+
data.tar.gz: c144d7dcf96074d047a3eba55523a27f2b3a74fb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae5877e53851c774fdf68ac3ccb65fe3623d6a446e42e39f1ac843b0ecc8bcb7aaac68bf657ce632154d0e6876ee5786e884acebb9d9c53156818f6c9f1a8ecf
|
7
|
+
data.tar.gz: b8e8e5df57870a7da924c7a8dd207d2fa6ba89be281116c1b7ba5da5327bd3eb35b1e402bc80d61e06bce57293c8325f46dad25aca5409da129668c6f52e4ee7
|
data/lib/mfrc522.rb
CHANGED
@@ -1,8 +1,19 @@
|
|
1
1
|
require 'pi_piper'
|
2
2
|
|
3
|
+
# For 3DES auth
|
4
|
+
require 'openssl'
|
5
|
+
require 'securerandom'
|
6
|
+
|
7
|
+
require 'mifare/base'
|
8
|
+
require 'mifare/classic'
|
9
|
+
require 'mifare/ultralight'
|
10
|
+
require 'mifare/ultralight_c'
|
11
|
+
require 'mifare/plus'
|
12
|
+
require 'mifare/des_fire'
|
13
|
+
|
3
14
|
include PiPiper
|
4
15
|
|
5
|
-
class
|
16
|
+
class MFRC522
|
6
17
|
|
7
18
|
# PICC commands used by the PCD to manage communication with several PICCs (ISO 14443-3, Type A, section 6.4)
|
8
19
|
PICC_REQA = 0x26 # REQuest command, Type A. Invites PICCs in state IDLE to go to READY and prepare for anticollision or selection. 7 bit frame.
|
@@ -26,8 +37,8 @@ class Mfrc522
|
|
26
37
|
# The commands used for MIFARE Ultralight (from http://www.nxp.com/documents/data_sheet/MF0ICU1.pdf, Section 8.6)
|
27
38
|
# The PICC_MF_READ and PICC_MF_WRITE can also be used for MIFARE Ultralight.
|
28
39
|
PICC_UL_WRITE = 0xA2 # Writes one 4 byte page to the PICC.
|
29
|
-
# The commands here is for 3DES Authentication
|
30
|
-
|
40
|
+
# The commands here is for Ultralight 3DES Authentication
|
41
|
+
PICC_UL_3DES_AUTH = 0x1A
|
31
42
|
#
|
32
43
|
PICC_MF_ACK = 0x0A # Mifare Acknowledge
|
33
44
|
|
@@ -128,7 +139,7 @@ class Mfrc522
|
|
128
139
|
antenna_on # Turn antenna on. They were disabled by the reset.
|
129
140
|
end
|
130
141
|
|
131
|
-
#
|
142
|
+
# PCD software reset
|
132
143
|
def soft_reset
|
133
144
|
write_spi(CommandReg, PCD_SoftReset)
|
134
145
|
sleep 1.0 / 20.0 # wait 50ms
|
@@ -177,8 +188,7 @@ class Mfrc522
|
|
177
188
|
|
178
189
|
# Turn antenna on
|
179
190
|
def antenna_on
|
180
|
-
|
181
|
-
write_spi_set_bitmask(TxControlReg, 0x03) if (value & 0x03) != 0x03
|
191
|
+
write_spi_set_bitmask(TxControlReg, 0x03)
|
182
192
|
end
|
183
193
|
|
184
194
|
# Turn antenna off
|
@@ -240,7 +250,7 @@ class Mfrc522
|
|
240
250
|
return :status_ok
|
241
251
|
end
|
242
252
|
|
243
|
-
#
|
253
|
+
# PCD transceive helper
|
244
254
|
def communicate_with_picc(command, send_data, framing_bit = 0, check_crc = false)
|
245
255
|
wait_irq = 0x00
|
246
256
|
wait_irq = 0x10 if command == PCD_MFAuthent
|
@@ -309,7 +319,7 @@ class Mfrc522
|
|
309
319
|
return :status_ok
|
310
320
|
end
|
311
321
|
|
312
|
-
# Instruct PICC in ACTIVE state go to HALT
|
322
|
+
# Instruct PICC in ACTIVE state go to HALT state
|
313
323
|
def picc_halt
|
314
324
|
buffer = [PICC_HLTA, 0]
|
315
325
|
|
@@ -370,6 +380,7 @@ class Mfrc522
|
|
370
380
|
# ensure there's nothing weird in buffer
|
371
381
|
if buffer.size != 6 && !buffer.select{|b| !buffer.is_a?(Fixnum)}.empty?
|
372
382
|
current_level_known_bits = 0
|
383
|
+
buffer = []
|
373
384
|
next
|
374
385
|
end
|
375
386
|
|
@@ -439,6 +450,29 @@ class Mfrc522
|
|
439
450
|
return :status_ok, uid, sak
|
440
451
|
end
|
441
452
|
|
453
|
+
# Trying to wake it up again
|
454
|
+
def reestablish_picc_communication(uid)
|
455
|
+
status = picc_halt
|
456
|
+
return status if status != :status_ok
|
457
|
+
|
458
|
+
status = picc_request(PICC_WUPA)
|
459
|
+
return status if status != :status_ok
|
460
|
+
|
461
|
+
status, new_uid, new_sak = picc_select
|
462
|
+
return status if status != :status_ok
|
463
|
+
|
464
|
+
if uid == new_uid
|
465
|
+
return :status_ok
|
466
|
+
else
|
467
|
+
return :status_different_card_detected
|
468
|
+
end
|
469
|
+
end
|
470
|
+
|
471
|
+
# ISO/IEC 14443-4 select
|
472
|
+
def iso_select
|
473
|
+
|
474
|
+
end
|
475
|
+
|
442
476
|
# Lookup error message
|
443
477
|
def error_type(error)
|
444
478
|
case error
|
@@ -462,6 +496,12 @@ class Mfrc522
|
|
462
496
|
'Incorrect select acknowledge'
|
463
497
|
when :status_auth_failed
|
464
498
|
'Authentication failed'
|
499
|
+
when :status_data_length_error
|
500
|
+
'Incorrect input data length'
|
501
|
+
when :status_incorrect_input
|
502
|
+
'Incorrect input'
|
503
|
+
when :status_different_card_detected
|
504
|
+
'Different card detected while reselecting'
|
465
505
|
when :status_error
|
466
506
|
'Something went wrong'
|
467
507
|
else
|
@@ -470,36 +510,51 @@ class Mfrc522
|
|
470
510
|
end
|
471
511
|
|
472
512
|
# Lookup PICC name using sak
|
473
|
-
def
|
474
|
-
|
475
|
-
|
476
|
-
|
477
|
-
|
478
|
-
|
479
|
-
when 0x09
|
480
|
-
'PICC_TYPE_MIFARE_MINI'
|
481
|
-
when 0x08
|
482
|
-
'PICC_TYPE_MIFARE_1K'
|
483
|
-
when 0x18
|
484
|
-
'PICC_TYPE_MIFARE_4K'
|
485
|
-
when 0x00
|
486
|
-
'PICC_TYPE_MIFARE_UL'
|
487
|
-
when 0x10, 0x11
|
488
|
-
'PICC_TYPE_MIFARE_PLUS'
|
489
|
-
when 0x01
|
490
|
-
'PICC_TYPE_TNP3XXX'
|
491
|
-
when 0x20
|
492
|
-
'PICC_TYPE_ISO_14443_4'
|
493
|
-
when 0x40
|
494
|
-
'PICC_TYPE_ISO_18092'
|
495
|
-
else
|
496
|
-
'PICC_TYPE_UNKNOWN'
|
513
|
+
def identify_model(sak)
|
514
|
+
# SAK coding separation reference:
|
515
|
+
# http://cache.nxp.com/documents/application_note/AN10833.pdf
|
516
|
+
# http://www.nxp.com/documents/application_note/130830.pdf
|
517
|
+
if sak & 0x04 != 0
|
518
|
+
return :picc_uid_not_complete
|
497
519
|
end
|
498
|
-
end
|
499
520
|
|
500
|
-
|
501
|
-
|
502
|
-
|
521
|
+
if sak & 0x02 != 0
|
522
|
+
return :picc_reserved_future_use
|
523
|
+
end
|
524
|
+
|
525
|
+
if sak & 0x08 != 0
|
526
|
+
if sak & 0x10 != 0
|
527
|
+
return :picc_mifare_4k
|
528
|
+
end
|
529
|
+
|
530
|
+
if sak & 0x01 != 0
|
531
|
+
return :picc_mifare_mini
|
532
|
+
end
|
533
|
+
|
534
|
+
return :picc_mifare_1k
|
535
|
+
end
|
536
|
+
|
537
|
+
if sak & 0x10 != 0
|
538
|
+
if sak & 0x01 != 0
|
539
|
+
return :picc_mifare_plus_4k_sl2
|
540
|
+
end
|
541
|
+
|
542
|
+
return :picc_mifare_plus_2k_sl2
|
543
|
+
end
|
544
|
+
|
545
|
+
if sak == 0x00
|
546
|
+
return :picc_mifare_ultralight
|
547
|
+
end
|
548
|
+
|
549
|
+
if sak & 0x20 != 0
|
550
|
+
return :picc_iso_14443_4
|
551
|
+
end
|
552
|
+
|
553
|
+
if sak & 0x40 != 0
|
554
|
+
return :picc_iso_18092
|
555
|
+
end
|
556
|
+
|
557
|
+
return :picc_unknown
|
503
558
|
end
|
504
559
|
|
505
560
|
# Start encrypted Crypto1 communication between reader and Mifare PICC
|
@@ -516,7 +571,7 @@ class Mfrc522
|
|
516
571
|
#
|
517
572
|
buffer = [command, block_addr]
|
518
573
|
buffer += sector_key[0..5]
|
519
|
-
buffer += uid[
|
574
|
+
buffer += uid[0..3]
|
520
575
|
|
521
576
|
status, _received_data, _valid_bits = communicate_with_picc(PCD_MFAuthent, buffer)
|
522
577
|
|
@@ -531,50 +586,55 @@ class Mfrc522
|
|
531
586
|
write_spi_clear_bitmask(Status2Reg, 0x08) # Clear MFCrypto1On bit
|
532
587
|
end
|
533
588
|
|
534
|
-
def
|
535
|
-
require 'openssl'
|
536
|
-
require 'securerandom'
|
537
|
-
|
538
|
-
# Cipher
|
539
|
-
cipher = OpenSSL::Cipher.new 'des-ede3-cbc'
|
540
|
-
cipher.key = [des_key*2].pack('H*')
|
541
|
-
cipher.padding = 0
|
542
|
-
|
589
|
+
def mifare_ultralight_3des_check
|
543
590
|
# Ask for authentication
|
544
|
-
buffer = [
|
591
|
+
buffer = [PICC_UL_3DES_AUTH, 0x00]
|
545
592
|
status, received_data = mifare_transceive(buffer)
|
546
593
|
return status if status != :status_ok
|
547
594
|
return :status_unknown_data if received_data[0] != 0xAF
|
548
595
|
|
596
|
+
return :status_ok, received_data
|
597
|
+
end
|
598
|
+
|
599
|
+
def mifare_ultralight_3des_authenticate(des_key)
|
600
|
+
status, received_data = mifare_ultralight_3des_check
|
601
|
+
return status if status != :status_ok
|
602
|
+
|
549
603
|
# Use received data as IV for next transmission
|
550
604
|
next_iv = received_data[1..8]
|
551
605
|
|
606
|
+
# Cipher
|
607
|
+
cipher = OpenSSL::Cipher.new 'des-ede3-cbc'
|
608
|
+
cipher.key = [des_key*2].pack('H*')
|
609
|
+
cipher.padding = 0
|
610
|
+
|
552
611
|
# Decrypt challenge random number and rotate it by 8 bits
|
553
612
|
cipher.decrypt
|
554
613
|
cipher.iv = "\x00"*8
|
555
614
|
challenge = received_data[1..8].pack('C*')
|
556
615
|
challenge = cipher.update(challenge) + cipher.final
|
557
|
-
challenge.rotate
|
616
|
+
challenge = challenge.bytes.rotate
|
558
617
|
|
559
618
|
# Generate 8 bytes random number and encrypt the response
|
560
619
|
random_number = SecureRandom.random_bytes(8)
|
561
620
|
cipher.encrypt
|
562
|
-
cipher.iv = next_iv
|
563
|
-
response = cipher.update(challenge.pack('C*')
|
621
|
+
cipher.iv = next_iv.pack('C*')
|
622
|
+
response = cipher.update(random_number + challenge.pack('C*')) + cipher.final
|
623
|
+
response = response.bytes
|
564
624
|
|
565
625
|
# Receive verification
|
566
|
-
buffer = [0xAF
|
626
|
+
buffer = [0xAF] + response
|
567
627
|
status, received_data = mifare_transceive(buffer)
|
568
628
|
return status if status != :status_ok
|
569
629
|
return :status_unknown_data if received_data[0] != 0x00
|
570
630
|
|
571
631
|
# Check if verification matches random_number rotated by 8 bits
|
572
632
|
cipher.decrypt
|
573
|
-
cipher.iv =
|
633
|
+
cipher.iv = response[-8..-1].pack('C*')
|
574
634
|
verification = received_data[1..8].pack('C*')
|
575
635
|
verification = cipher.update(verification) + cipher.final
|
576
636
|
|
577
|
-
if random_number.bytes.rotate
|
637
|
+
if random_number.bytes.rotate != verification.bytes
|
578
638
|
picc_halt
|
579
639
|
return :status_auth_failed
|
580
640
|
end
|
@@ -595,10 +655,10 @@ class Mfrc522
|
|
595
655
|
|
596
656
|
# Data exists, check CRC and return
|
597
657
|
if received_data.size > 1
|
598
|
-
return :status_crc_error if received_data.
|
658
|
+
return :status_crc_error if received_data.size < 3 || valid_bits != 0
|
599
659
|
|
600
660
|
status = check_crc(received_data)
|
601
|
-
return status, received_data
|
661
|
+
return status, received_data[0..-3]
|
602
662
|
end
|
603
663
|
|
604
664
|
# Data doesn't exist, check mifare acknowledge
|
@@ -608,133 +668,4 @@ class Mfrc522
|
|
608
668
|
return :status_ok
|
609
669
|
end
|
610
670
|
|
611
|
-
# Read Mifare block address
|
612
|
-
def mifare_read(block_addr)
|
613
|
-
buffer = [PICC_MF_READ, block_addr]
|
614
|
-
|
615
|
-
status, received_data = mifare_transceive(buffer)
|
616
|
-
return status if status != :status_ok
|
617
|
-
|
618
|
-
return :status_ok, received_data
|
619
|
-
end
|
620
|
-
|
621
|
-
# Write Mifare block address
|
622
|
-
def mifare_write(block_addr, send_data)
|
623
|
-
buffer = [PICC_MF_WRITE, block_addr]
|
624
|
-
|
625
|
-
# Ask PICC if we can write to block_addr
|
626
|
-
status = mifare_transceive(buffer)
|
627
|
-
return status if status != :status_ok
|
628
|
-
|
629
|
-
# Then start transfer our data
|
630
|
-
status = mifare_transceive(send_data)
|
631
|
-
return status if status != :status_ok
|
632
|
-
|
633
|
-
return :status_ok
|
634
|
-
end
|
635
|
-
|
636
|
-
# Write helper for Mifare UL
|
637
|
-
def mifare_ultralight_write(page, send_data)
|
638
|
-
# Page 2-15, each 4 bytes
|
639
|
-
buffer = [PICC_UL_WRITE, page]
|
640
|
-
buffer += send_data[0..3]
|
641
|
-
|
642
|
-
status = mifare_transceive(buffer)
|
643
|
-
return status if status != :status_ok
|
644
|
-
|
645
|
-
return :status_ok
|
646
|
-
end
|
647
|
-
|
648
|
-
# Helper for reading value block
|
649
|
-
def mifare_get_value(block_addr)
|
650
|
-
status, received_data = mifare_read(block_addr)
|
651
|
-
return status if status != :status_ok
|
652
|
-
|
653
|
-
value = (received_data[3] << 24) + (received_data[2] << 16) + (received_data[1] << 8) + received_data[0]
|
654
|
-
|
655
|
-
return :status_ok, value
|
656
|
-
end
|
657
|
-
|
658
|
-
# Helper for writing value block
|
659
|
-
def mifare_set_value(block_addr, value)
|
660
|
-
# Value block format
|
661
|
-
#
|
662
|
-
# byte 0..3: 32 bit value in little endian
|
663
|
-
# byte 4..7: copy of byte 0..3, with inverted bits (aka. XOR 255)
|
664
|
-
# byte 8..11: copy of byte 0..3
|
665
|
-
# byte 12: index of backup block (can be any value)
|
666
|
-
# byte 13: copy of byte 12 with inverted bits (aka. XOR 255)
|
667
|
-
# byte 14: copy of byte 12
|
668
|
-
# byte 15: copy of byte 13
|
669
|
-
buffer = []
|
670
|
-
buffer[0] = value & 0xFF
|
671
|
-
buffer[1] = (value >> 8) & 0xFF
|
672
|
-
buffer[2] = (value >> 16) & 0xFF
|
673
|
-
buffer[3] = (value >> 24) & 0xFF
|
674
|
-
buffer[4] = ~buffer[0]
|
675
|
-
buffer[5] = ~buffer[1]
|
676
|
-
buffer[6] = ~buffer[2]
|
677
|
-
buffer[7] = ~buffer[3]
|
678
|
-
buffer[8] = buffer[0]
|
679
|
-
buffer[9] = buffer[1]
|
680
|
-
buffer[10] = buffer[2]
|
681
|
-
buffer[11] = buffer[3]
|
682
|
-
buffer[12] = block_addr
|
683
|
-
buffer[13] = ~block_addr
|
684
|
-
buffer[14] = buffer[12]
|
685
|
-
buffer[15] = buffer[13]
|
686
|
-
|
687
|
-
mifare_write(block_addr, buffer)
|
688
|
-
end
|
689
|
-
|
690
|
-
# Helper for increment, decrement, and restore command
|
691
|
-
def mifare_two_step(command, block_addr, value)
|
692
|
-
buffer = [command, block_addr]
|
693
|
-
send_data = [ # Split integer into array of bytes
|
694
|
-
value & 0xFF,
|
695
|
-
(value >> 8) & 0xFF,
|
696
|
-
(value >> 16) & 0xFF,
|
697
|
-
(value >> 24) & 0xFF
|
698
|
-
]
|
699
|
-
|
700
|
-
# Ask PICC if we can write to block_addr
|
701
|
-
status = mifare_transceive(buffer)
|
702
|
-
return status if status != :status_ok
|
703
|
-
|
704
|
-
# Then start transfer our data
|
705
|
-
status = mifare_transceive(send_data, true) # Accept timeout
|
706
|
-
return status if status != :status_ok
|
707
|
-
|
708
|
-
return :status_ok
|
709
|
-
end
|
710
|
-
|
711
|
-
# Mifare increment helper
|
712
|
-
# MIFARE Classic only
|
713
|
-
def mifare_increment(block_addr, delta)
|
714
|
-
mifare_two_step(PICC_MF_INCREMENT, block_addr, delta)
|
715
|
-
end
|
716
|
-
|
717
|
-
# Mifare decrement helper
|
718
|
-
# MIFARE Classic only
|
719
|
-
def mifare_decrement(block_addr, delta)
|
720
|
-
mifare_two_step(PICC_MF_DECREMENT, block_addr, delta)
|
721
|
-
end
|
722
|
-
|
723
|
-
# Mifare restore helper
|
724
|
-
# MIFARE Classic only
|
725
|
-
def mifare_restore(block_addr)
|
726
|
-
mifare_two_step(PICC_MF_RESTORE, block_addr, 0)
|
727
|
-
end
|
728
|
-
|
729
|
-
# Mifare transfer helper
|
730
|
-
# MIFARE Classic only
|
731
|
-
def mifare_transfer(block_addr)
|
732
|
-
buffer = [PICC_MF_TRANSFER, block_addr]
|
733
|
-
|
734
|
-
status = mifare_transceive(buffer)
|
735
|
-
return status if status != :status_ok
|
736
|
-
|
737
|
-
return :status_ok
|
738
|
-
end
|
739
671
|
end
|
740
|
-
|
data/lib/mifare/base.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Mifare
|
2
|
+
class Base
|
3
|
+
|
4
|
+
attr_reader :uid
|
5
|
+
attr_reader :sak
|
6
|
+
|
7
|
+
def initialize(pcd, uid, sak)
|
8
|
+
@pcd = pcd
|
9
|
+
@uid = uid
|
10
|
+
@sak = sak
|
11
|
+
end
|
12
|
+
|
13
|
+
def resume_communication
|
14
|
+
@pcd.reestablish_picc_communication(@uid)
|
15
|
+
end
|
16
|
+
|
17
|
+
def halt
|
18
|
+
@pcd.picc_halt
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
module Mifare
|
2
|
+
class Classic < Base
|
3
|
+
|
4
|
+
def auth(block_addr, key = {})
|
5
|
+
if key[:a]
|
6
|
+
@pcd.mifare_crypto1_authenticate(MFRC522::PICC_MF_AUTH_KEY_A, block_addr, key[:a], @uid)
|
7
|
+
elsif key[:b]
|
8
|
+
@pcd.mifare_crypto1_authenticate(MFRC522::PICC_MF_AUTH_KEY_B, block_addr, key[:b], @uid)
|
9
|
+
else
|
10
|
+
:status_incorrect_input
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def deauth
|
15
|
+
@pcd.mifare_crypto1_deauthenticate
|
16
|
+
end
|
17
|
+
|
18
|
+
def read(block_addr)
|
19
|
+
buffer = [MFRC522::PICC_MF_READ, block_addr]
|
20
|
+
|
21
|
+
status, received_data = @pcd.mifare_transceive(buffer)
|
22
|
+
return status if status != :status_ok
|
23
|
+
|
24
|
+
return :status_ok, received_data
|
25
|
+
end
|
26
|
+
|
27
|
+
def write(block_addr, send_data)
|
28
|
+
return :status_data_length_error if send_data.size != 16
|
29
|
+
|
30
|
+
buffer = [MFRC522::PICC_MF_WRITE, block_addr]
|
31
|
+
|
32
|
+
# Ask PICC if we can write to block_addr
|
33
|
+
status = @pcd.mifare_transceive(buffer)
|
34
|
+
return status if status != :status_ok
|
35
|
+
|
36
|
+
# Then start transfer our data
|
37
|
+
status = @pcd.mifare_transceive(send_data)
|
38
|
+
return status if status != :status_ok
|
39
|
+
|
40
|
+
return :status_ok
|
41
|
+
end
|
42
|
+
|
43
|
+
def read_value(block_addr)
|
44
|
+
status, received_data = read(block_addr)
|
45
|
+
return status if status != :status_ok
|
46
|
+
|
47
|
+
value = (received_data[3] << 24) +
|
48
|
+
(received_data[2] << 16) +
|
49
|
+
(received_data[1] << 8) +
|
50
|
+
received_data[0]
|
51
|
+
|
52
|
+
return :status_ok, value
|
53
|
+
end
|
54
|
+
|
55
|
+
def write_value(block_addr, value)
|
56
|
+
return :status_data_length_error if value.size > 4
|
57
|
+
|
58
|
+
# Value block format
|
59
|
+
#
|
60
|
+
# byte 0..3: 32 bit value in little endian
|
61
|
+
# byte 4..7: copy of byte 0..3, with inverted bits (aka. XOR 255)
|
62
|
+
# byte 8..11: copy of byte 0..3
|
63
|
+
# byte 12: index of backup block (can be any value)
|
64
|
+
# byte 13: copy of byte 12 with inverted bits (aka. XOR 255)
|
65
|
+
# byte 14: copy of byte 12
|
66
|
+
# byte 15: copy of byte 13
|
67
|
+
buffer = []
|
68
|
+
buffer[0] = value & 0xFF
|
69
|
+
buffer[1] = (value >> 8) & 0xFF
|
70
|
+
buffer[2] = (value >> 16) & 0xFF
|
71
|
+
buffer[3] = (value >> 24) & 0xFF
|
72
|
+
buffer[4] = ~buffer[0]
|
73
|
+
buffer[5] = ~buffer[1]
|
74
|
+
buffer[6] = ~buffer[2]
|
75
|
+
buffer[7] = ~buffer[3]
|
76
|
+
buffer[8] = buffer[0]
|
77
|
+
buffer[9] = buffer[1]
|
78
|
+
buffer[10] = buffer[2]
|
79
|
+
buffer[11] = buffer[3]
|
80
|
+
buffer[12] = block_addr
|
81
|
+
buffer[13] = ~block_addr
|
82
|
+
buffer[14] = buffer[12]
|
83
|
+
buffer[15] = buffer[13]
|
84
|
+
|
85
|
+
write(block_addr, buffer)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Increment: Increments the contents of a block and stores the result in the internal Transfer Buffer
|
89
|
+
def increment(block_addr, delta)
|
90
|
+
two_step(MFRC522::PICC_MF_INCREMENT, block_addr, delta)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Decrement: Decrements the contents of a block and stores the result in the internal Transfer Buffer
|
94
|
+
def decrement(block_addr, delta)
|
95
|
+
two_step(MFRC522::PICC_MF_DECREMENT, block_addr, delta)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Restore: Moves the contents of a block into the internal Transfer Buffer
|
99
|
+
def restore(block_addr)
|
100
|
+
two_step(MFRC522::PICC_MF_RESTORE, block_addr, 0)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Transfer: Writes the contents of the internal Transfer Buffer to a value block
|
104
|
+
def transfer(block_addr)
|
105
|
+
buffer = [MFRC522::PICC_MF_TRANSFER, block_addr]
|
106
|
+
|
107
|
+
status = @pcd.mifare_transceive(buffer)
|
108
|
+
return status if status != :status_ok
|
109
|
+
|
110
|
+
return :status_ok
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
# Helper for increment, decrement, and restore command
|
116
|
+
def two_step(command, block_addr, value)
|
117
|
+
return :status_data_length_error if value.size > 4
|
118
|
+
|
119
|
+
buffer = [command, block_addr]
|
120
|
+
send_data = [ # Split integer into array of bytes
|
121
|
+
value & 0xFF,
|
122
|
+
(value >> 8) & 0xFF,
|
123
|
+
(value >> 16) & 0xFF,
|
124
|
+
(value >> 24) & 0xFF
|
125
|
+
]
|
126
|
+
|
127
|
+
# Ask PICC if we can write to block_addr
|
128
|
+
status = @pcd.mifare_transceive(buffer)
|
129
|
+
return status if status != :status_ok
|
130
|
+
|
131
|
+
# Then start transfer our data
|
132
|
+
status = @pcd.mifare_transceive(send_data, true) # Accept timeout
|
133
|
+
return status if status != :status_ok
|
134
|
+
|
135
|
+
return :status_ok
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
end
|
data/lib/mifare/plus.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
module Mifare
|
2
|
+
class Ultralight < Base
|
3
|
+
|
4
|
+
def initialize(pcd, uid, sak)
|
5
|
+
super
|
6
|
+
@is_c = false
|
7
|
+
|
8
|
+
# Check if Ultralight C
|
9
|
+
status, received_data = @pcd.mifare_ultralight_3des_check
|
10
|
+
if status == :status_ok
|
11
|
+
extend UltralightC
|
12
|
+
@is_c = true
|
13
|
+
end
|
14
|
+
resume_communication
|
15
|
+
end
|
16
|
+
|
17
|
+
def read(block_addr)
|
18
|
+
buffer = [MFRC522::PICC_MF_READ, block_addr]
|
19
|
+
|
20
|
+
status, received_data = @pcd.mifare_transceive(buffer)
|
21
|
+
return status if status != :status_ok
|
22
|
+
|
23
|
+
return :status_ok, received_data
|
24
|
+
end
|
25
|
+
|
26
|
+
def write(page, send_data)
|
27
|
+
return :status_data_length_error if send_data.size != 4
|
28
|
+
|
29
|
+
# Page 2-15, each 4 bytes
|
30
|
+
buffer = [MFRC522::PICC_UL_WRITE, page]
|
31
|
+
buffer += send_data
|
32
|
+
|
33
|
+
status = @pcd.mifare_transceive(buffer)
|
34
|
+
return status if status != :status_ok
|
35
|
+
|
36
|
+
return :status_ok
|
37
|
+
end
|
38
|
+
|
39
|
+
def is_c?
|
40
|
+
@is_c
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Mifare
|
2
|
+
module UltralightC
|
3
|
+
|
4
|
+
# Using 16 bytes hex string for 3DES authentication
|
5
|
+
def auth(key)
|
6
|
+
@pcd.mifare_ultralight_3des_authenticate(key)
|
7
|
+
end
|
8
|
+
|
9
|
+
def write_des_key(key)
|
10
|
+
# key should be 16 bytes long
|
11
|
+
bytes = [key].pack('H*').unpack('C*')
|
12
|
+
return :status_data_length_error if bytes.size != 16
|
13
|
+
|
14
|
+
# Key1
|
15
|
+
write(0x2C, bytes[4..7].reverse)
|
16
|
+
write(0x2D, bytes[0..3].reverse)
|
17
|
+
# Key2
|
18
|
+
write(0x2E, bytes[12..15].reverse)
|
19
|
+
write(0x2F, bytes[8..11].reverse)
|
20
|
+
end
|
21
|
+
|
22
|
+
def counter_increment(value)
|
23
|
+
# you can set any value between 0x0000 to 0xFFFF on the first write (initialize)
|
24
|
+
# after initialized, counter can only be incremented by 0x01 ~ 0x0F
|
25
|
+
write(0x29, [value & 0xFF, (value >> 8) & 0xFF, 0x00, 0x00])
|
26
|
+
end
|
27
|
+
|
28
|
+
def enable_protection_from(block_addr)
|
29
|
+
# authentication will be required from `block_addr` to 0x2F
|
30
|
+
# valid value are from 0x03 to 0x30
|
31
|
+
# set to 0x30 to disable memory protection
|
32
|
+
write(0x2A, [block_addr & 0x3F, 0x00, 0x00, 0x00])
|
33
|
+
end
|
34
|
+
|
35
|
+
def set_protection_type(type)
|
36
|
+
# set to 0 for read-write access restriction (default)
|
37
|
+
# set to 1 for write access restriction
|
38
|
+
write(0x2B, [type & 0x01, 0x00, 0x00, 0x00])
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
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.
|
4
|
+
version: 0.2.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-08-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: pi_piper
|
@@ -37,6 +37,12 @@ extensions: []
|
|
37
37
|
extra_rdoc_files: []
|
38
38
|
files:
|
39
39
|
- lib/mfrc522.rb
|
40
|
+
- lib/mifare/base.rb
|
41
|
+
- lib/mifare/classic.rb
|
42
|
+
- lib/mifare/des_fire.rb
|
43
|
+
- lib/mifare/plus.rb
|
44
|
+
- lib/mifare/ultralight.rb
|
45
|
+
- lib/mifare/ultralight_c.rb
|
40
46
|
homepage: https://github.com/atitan/MFRC522_Ruby
|
41
47
|
licenses:
|
42
48
|
- MIT
|