mfrc522 0.1.2 → 0.2.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 +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
|