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.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/mfrc522.rb +136 -15
  3. metadata +3 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 76a0b3ccef69418c9bf365ca7ec159ad003c4fc2
4
- data.tar.gz: 6024457f6053c4ae84dc28d839ee344bf0252365
3
+ metadata.gz: 0b93d1ee7b7e4227a995c53b735a324b10aa1610
4
+ data.tar.gz: 1e003e1c431ed8a37152a2e375ba957fc66757af
5
5
  SHA512:
6
- metadata.gz: 76f98482e7e002bec39c51fc8b695e9dd25cbe16021c412397f743be6cd6b64747c5bdc29d993d63996194ad4828ccbd3f9736aae85768a38b7fc7916e52d110
7
- data.tar.gz: a0fb447aa70005e6d4f1ae1d350783fe89663666c6ba5cdebaa053b00fe72dd30fe037df4af055d5689edabc83d041f9a77c220d778b39d435ab936ca8554623
6
+ metadata.gz: 1f70e7173cf0118b6f9f20cf3913d26887f1d633b9ae5933250081c37762bf2b6babc0e08521e17ddf1e91f49368cc5ebefe4bcae652e5fcfd5a02f9996fc7d7
7
+ data.tar.gz: e8b2f2a2db1270bd0a54fc997989964a5016224b8782089727a091fb7b77181f5bc6f58f78d422af110d47bb0593125ccd980b4a031f1962f42cd3f252f7df79
@@ -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 = 0xA # Mifare Acknowledge
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 :status_error if valid_bits != 0 # REQA or WUPA command return 16 bits(full byte)
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 mifare_authenticate(command, block_addr, sector_key, uid)
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[0..3]
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
- def mifare_deauthenticate
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
- # Helper that append crc to buffer and check mifare acknowledge
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
- # Check mifare acknowledge
482
- return :status_error if received_data.count != 1 || valid_bits != 4 # ACK is 4 bits long
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, buffer = append_crc(buffer)
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(blockAddr, buffer)
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.1
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-03-09 00:00:00.000000000 Z
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.4.6
60
+ rubygems_version: 2.5.1
61
61
  signing_key:
62
62
  specification_version: 4
63
63
  summary: MFRC522 RFID Reader Library for RaspberryPi