mfrc522 0.0.1 → 0.1.0

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