/
mifare_crypto.c
1447 lines (1332 loc) · 60 KB
/
mifare_crypto.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* -----------------------------------------------------------------------------
* ----- MIFARE_CRYPTO.C -----
* ----- EASYPAY -----
* -----------------------------------------------------------------------------
*
* File Description:
* This is a library of functions for Mifare DESFire Crypto Operations
*
* Table of Contents:
* Rol - ROL operation (byte level)
* Lsl - LSL operation (bit level)
* CmacGenerateSubkeys - get 2 subkeys from key
* Cmac - MAC generation process of CMAC
*
* Crc32 - get a CRC32 checksum
* Crc32Append - get a CRC32 checksum and append to data
* MifareCrc32 - get a Desfire CRC32 checksum
* MifareCrc32Append - get a Desfire CRC32 checksum and append to data
* MifareCrc16 - get a Desfire ISO1444-3 Type A CRC16 checksum
* MifareCrc16Append - get a Desfire CRC16 checksum and append to data
*
* KeyBlockSize - get key block size
* PaddedDataLength - size required to store nbytes of data in a buffer of
* size n*block_size
* MacedDataLength - buffer size required to MAC nbytes of data
* EncipheredDataLength - buffer size required to encipher nbytes of
* data and a CRC
* MifareCryptoPreprocessData - Data Encipher before transmission
* MifareCryptoPostprocessData - Data Decipher/Verification after reception.
* MifareCipherSingleBlock
* MifareCipherBlocksChained - performs all CBC ciphering/deciphering
*
* Documentation Sources:
* - libfreefare
*
* - NIST Special Publication 800-38B
* Recommendation for Block Cipher Modes of Operation: The CMAC Mode for
* Authentication
* May 2005
*
* - http://www.ross.net/crc/download/crc_v3.txt
* A Painless Guide to CRC Error Detection Algorithms
*
* Assumptions:
* - Data from PICC has status byte at the end,
* - MSByte is at lowest index (0)
* - bit^n = bit repeated n times, ex: 0^3 = 000
*
* ToDo:
* Check all uses of CIPH()
* STATUS byte is expected to come last (See Postprocess)
*
* Compiler:
* HI-TECH C Compiler for PIC18 MCUs (http://www.htsoft.com/)
*
* Revision History:
* Jan. 02, 2013 Nnoduka Eruchalu Initial Revision
* May 03, 2013 Nnoduka Eruchalu Removed dynamic memory allocation
* So changed functions to accept
* pointers to pre-allocated data
*/
#include <string.h> /* for mem* operations */
#include <stdlib.h>
#include "mifare.h"
#include "mifare_crypto.h"
#include "des.h" /* for des_ecb_encrypt() */
#include "aes.h" /* for AES operations */
/* --------------------------------------
* Functions Local To This File
* --------------------------------------
*/
/* XOR operation (byte level) */
static void Xor(uint8_t *ivect, uint8_t *data, size_t len);
/* size of MACing produced with the key */
static size_t KeyMacingLength(mifare_desfire_key *key);
/*
* Xor
* Description: Bytewise Xor: data = data XOR ivect
*
* Arguments: ivect = pointer to initialization vector
* data = pointer to data block [modified]
* len = length of data & ivect
* Return: None
*
* Operation:
* Loop through and do bytewise xors
*
* Revision History:
* Jan. 03, 2012 Nnoduka Eruchalu Initial Revision
* May 03, 2013 Nnoduka Eruchalu Updated Comments
*/
static void Xor(uint8_t *ivect, uint8_t *data, size_t len)
{
size_t i; /* index into arrays */
for (i=0; i< len; i++) {
data[i] ^= ivect[i];
}
}
/*
* Rol
* Description: Rotate Byte left. (Move MSByte over to LSByte position, and
* shift up other bytes)
* Ex: Rol {0,1,2,3,4} = {1,2,3,4,0}
*
* Arguments: data = pointer to data block [modified]
* len = length of data block
* Return: None
*
* Operation: Copy first byte; shift all other bytes down one index, and place
* first byte in now empty last slot.
*
* Assumptions: MSByte is at index 0
*
* Revision History:
* Jan. 03, 2012 Nnoduka Eruchalu Initial Revision
* May 03, 2013 Nnoduka Eruchalu Updated Comments
*/
void Rol(uint8_t *data, size_t len)
{
size_t i; /* index into data */
uint8_t first = data[0];
for (i = 0; i < len-1; i++) {
data[i] = data[i+1];
}
data[len-1] = first;
}
/*
* Lsl
* Description: Logical Bit Shift Left. Shift MSB out, and place a 0 at LSB
* position
* Ex: LSL {5,1,2,3,4} = {1,2,3,4,0}
*
* Arguments: data = pointer to data block [modified]
* len = length of data block
*
* Operation: For each byte position, shift out highest bit with
* (data[n] << 1) and add in the highest bit from next lower byte
* with (data[n+1] >> 7)
* The addition is done with a bitwise OR (|)
* For the Least significant byte however, there is no next lower
* byte so this is handled last and simply set to data[len-1] <<= 1
*
* Assumptions: MSByte is at index 0
*
* Revision History:
* Jan. 03, 2012 Nnoduka Eruchalu Initial Revision
* May 03, 2013 Nnoduka Eruchalu Updated Comments
*/
void Lsl(uint8_t *data, size_t len)
{
size_t n; /* index into data */
for (n = 0; n < len - 1; n++) {
data[n] = (data[n] << 1) | (data[n+1] >> 7);
}
data[len - 1] <<= 1;
}
/*
* CmacGenerateSubkeys
* Description: Derive Subkeys K1 and K2
*
* Arguments: key = block cipher key
* Return: None
*
* Operation: Get key block size; it is length of each subkey, and is used in
* determining R, also of length "key block size"
* R is a bit string that is used in subkey generation process.
* If block size is 8 bytes, R=0x1B; if 16 bytes, R = 0x87,
*
* Steps:
* 1. Let l = Ciph(key, 0^b)
* 2. If MSBit(L) == 0, then K1 = L << 1;
* Else K1 = (L << 1) XOR R
* 3. If MSBit(K1) = 0, then K2 = (K1 << 1);
* Else K2 = (K1 << 1) XOR R
*
* Assumptions: MSB is at index 0
*
* Revision History:
* Jan. 03, 2012 Nnoduka Eruchalu Initial Revision
*/
void CmacGenerateSubkeys(mifare_desfire_key *key)
{
int kbs = KeyBlockSize (key); /* get key block size; */
uint8_t R = (kbs == 8) ? 0x1B : 0x87; /* use this to determine R and */
/* length of subkeys */
uint8_t l[MAX_CRYPTO_BLOCK_SIZE]; /* allocate memory that is at */
uint8_t ivect[MAX_CRYPTO_BLOCK_SIZE]; /* least of size KeyBlockSize */
memset (l, 0, kbs); /* start block cipher */
memset (ivect, 0, kbs); /* on a zero'd init block */
/* store block cipher result in l */
MifareCipherBlocksChained(NULL, key, ivect, l, kbs, MCD_RECEIVE,MCO_ENCIPHER);
/* Used to compute CMAC on complete blocks */
memcpy (key->cmac_sk1, l, kbs); /* K1 = L */
Lsl(key->cmac_sk1, kbs); /* K1 = L << 1, if MSBit(L)==0 */
if (l[0] & 0x80) /* if however MSBit(L) != 0 */
key->cmac_sk1[kbs-1] ^= R; /* then K1 = (L << 1) xor R */
/* Used to compute CMAC on the last block if non-complete */
memcpy (key->cmac_sk2, key->cmac_sk1, kbs); /* K2 = K1 */
Lsl(key->cmac_sk2, kbs); /* K2=(K1<<1), if MSBit(K2)==0 */
if (key->cmac_sk1[0] & 0x80) /* if however MSBit(K1) != 0 */
key->cmac_sk2[kbs-1] ^= R; /* then K2 = (K1 << 1) xor R */
}
/*
* Cmac
* Description: MAC generation process of CMAC
*
* Arguments: key = pointer to block cipher key
* ivect = pointer to zero'd init vector block
* data = pointer message to generate MAC on
* len = number of message bytes
* cmac = pointer to buffer for saving CMAC [modified]
* Return: None
*
* Operation:
* 1. Apply the subkey generation process to Key, K, to produce K1 and K2.
* 2. If message (M) bit length (Mlen) = 0, let n=1, else, n=ceiling(Mlen/b)
* Note that Mlen = 8*len and b = 8*kbs
* 3. Let M1, M2, ..., M{n-1}, Mn* denote the unique sequence of bit strings
* such that M = M1 (concat) M2 (concat) ... (concat) M{n-1} (concat) Mn*,
* where M1, M2, ... M{n-1} are complete blocks.
* 4. If Mn* is a complete block, let Mn = K1 xor Mn*;
* else let Mn = K2 xor (Mn* concat 10^j), where j = n*(kbs) -Mlen -1
* 5. Let C0 = 0^b
* 6. For i=1 to n, let Ci = CIPH(K, (C{i-1} xor Mi))
* 7. Let T= Most Significant Tlen bits of (Cn), where Tlen is MAC length
* parameter (equal to kbs*8)
* 8. Return T in cmac buffer.
*
* Assumptions: MSB is at index 0
* data len > key block size
* ivect is all 0s
*
* Revision History:
* Jan. 03, 2012 Nnoduka Eruchalu Initial Revision
*/
void Cmac(mifare_desfire_key *key, uint8_t *ivect, uint8_t *data, size_t len,
uint8_t *cmac)
{
int kbs = KeyBlockSize(key); /* get key block size and set message, */
/*M, to buffer of size n blocks (n*kbs)*/
/* allocate buffer at least as big as PaddedDataLength(len, kbs) */
uint8_t buffer[MAX_PADDED_DATA_LENGTH];
memcpy(buffer, data, len); /* load msg buffer with data */
/* if Mn* isn't a complete block */
if ((!len) || (len % kbs)) { /* (has padded bytes) */
buffer[len++] = 0x80; /* set Mn* = Mn* (concat) 10^j */
while (len % kbs) { /* where j = (n*kbs) - Mlen - 1 bits */
buffer[len++] = 0x00; /* note n*kbs - Mlen = puts you at the */
} /* beginning of the first bit of the */
/* byte following the last byte of Mn* */
Xor(key->cmac_sk2, buffer + len - kbs, kbs); /* Mn = K2 xor (updated Mn*) */
} else { /*Mn* a complete block(no padded bytes)*/
Xor(key->cmac_sk1, buffer + len - kbs, kbs); /* Mn = K1 xor Mn* */
}
/* initialization vector is C0 and should be all 0s */
/* get Ci = CIPH(K, (C{i-1} xor Mi)) for i=1 to n*/
MifareCipherBlocksChained (NULL, key, ivect, buffer, len, MCD_SEND,
MCO_ENCIPHER);
memcpy (cmac, ivect, kbs); /* return top kbs bytes of Cn */
}
/*
* Crc32
* Description: Generate a CRC checksum of width 32 for given data array of
* length len.
*
* Arguments: data = pointer to data block
* len = length of data block
* crc = ptr to CRC checksum array, in big endian format [modified]
* Return: None
*
* Operation:
* Choose, the generator polynomial: [32-bit width so Ethernet]
* x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 + x+1
*
* TRUNCATED HEX
* -------------
* BINARY : 1 0000 0100 1100 0001 0001 1101 1011 0111 = 04C11DB7
* REVERSED: 1110 1101 1011 1000 1000 0011 0010 0000 1 = EDB88320
*
* This uses the UNREVERSED POLY
*
* The CRC checksum is the remainder of CRC division throught a register as
* drawn below.
*
* For the pruposes of example, consier a poly for CRC4 and the poly = 10111.
* Then, to perform the division, we need to use a 4bit register:
*
* 3 2 1 0 Bits
* +---+---+---+---+
* Pop! <-- | | | | | <-- augmented message (Message (concat) 0 bits)
* +---+---+---+---+
* 1 0 1 1 1 = The Poly
*
* To perform the division perform the following:
* 1. Load the register with 0 bits.
* 2. Augment the message by appending W zero bits to the end of it.
* 3. While (more message bits)
* Begin
* Shift the register left by one bit, reading the next bit of the
* augmented message into register bit position 0.
* If (a 1 bit popped out of the register during this shift)
* Register = Register XOR Poly
* End
* 4. The register now contains the remainder.
*
*
* Documentation:
* http://www.ross.net/crc/download/crc_v3.txt
*
* Test this function's outputs with:
* http://depa.usst.edu.cn/chenjq/www2/SDesign/JavaScript/CRCcalculation.htm
* http://ghsi.de/CRC/index.php?Polynom=100000100110000010001110110110111&Message=123456
*
* Revision History:
* Jan. 04, 2012 Nnoduka Eruchalu Initial Revision
*/
void Crc32(uint8_t *data, size_t len, uint8_t *crc)
{
uint32_t reg = 0; /* load register with 0 bits */
int current_bit; /* need current bit and byte values to keep track of */
uint8_t current_byte; /* shifts into register */
int byte; /* index into data message bytes */
uint8_t popped_bit; /* bit popped out of register after shift */
len += CRC32_NUMBYTES; /* augment the message by updating length */
/* these extra bits will be zero's */
while(len--) { /* shift in bits of the data message keeping track */
current_byte = *data++; /* keeping track of current byte and bit values */
for(current_bit=8; current_bit > 0; current_bit--) { /* if bit to popped */
popped_bit = reg >> (CRC32_NUMBITS -1); /* from reg is a 1 then */
reg = (reg << 1) | ((len < CRC32_NUMBYTES) ? 0 : (current_byte >> 7));
current_byte <<= 1; /* xor in the generator polynomial to register */
if (popped_bit) reg ^= CRC32_POLY;
}
}
for(byte=0; byte < CRC32_NUMBYTES; byte++) { /* save the reg in crc array */
crc[byte] = reg >> (8*(CRC32_NUMBYTES - 1 - byte)) & 0xFF;
}
}
/*
* Crc32Append
* Description: Get Crc32 checksum and append to data.
*
* Arguments: data = pointer to data array
* len = length of data array
* Return: None
*
* Operation: Call Crc32, and the pointer for the location of the crc value is
* the address of byte right after LSB of the data.
*
* Assumptions: Data is augmented i.e. has space for one extra byte, after its
* LSB. This is where the CRC will be saved
*
* Revision History:
* Jan. 04, 2012 Nnoduka Eruchalu Initial Revision
*/
void Crc32Append(uint8_t *data, size_t len)
{
Crc32(data, len, data+len);
}
/*
* MifareCrc32
* Description: Generate a MIFARE DESFire CRC checksum of width 32 for given
* data array of length len.
*
* Arguments: data = pointer to data block
* len = length of data block
* crc = ptr to generated CRC checksum array, as it's to be
* transmitted (little endian) [modified]
* Return: None
*
* Operation:
* Choose, the generator polynomial: [32-bit width so Ethernet]
* x32 + x26 + x23 + x22 + x16 + x12 + x11 + x10 + x8 + x7 + x5 + x4 + x2 +x+1
*
* TRUNCATED HEX
* -------------
* BINARY : 1 0000 0100 1100 0001 0001 1101 1011 0111 = 04C11DB7
* REVERSED: 1110 1101 1011 1000 1000 0011 0010 0000 1 = EDB88320
*
* This uses the REVERSED POLY
*
* Documentation:
* libfreefare source code
*
* Test Values:
* data[26] ={0x3D, 0x00, 0x00, 0x00, 0x00, 0x12, 0x00, 0x00, 0x01, 0x02,
* 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12,
* 0x13, 0x14, 0x15, 0x16, 0x17, 0x18};
* CRC = 0x59F71A9C
* and transmit as {9C, 1A, F7, 59}
*
* Revision History:
* Jan. 05, 2012 Nnoduka Eruchalu Initial Revision
*/
void MifareCrc32 (uint8_t *data, size_t len, uint8_t *crc)
{
uint32_t reg = 0xFFFFFFFF; /* load register with 1 bits */
int current_bit; /* need current bit and byte values to keep */
uint8_t current_byte; /* track of shifts into register */
size_t byte; /* index into CRC array bytes */
uint8_t popped_bit; /* bit popped out of register after shift */
while(len--) { /* shift in bytes of data message keeping track */
current_byte = *data++; /* of current byte and bit values */
reg ^= current_byte;
for (current_bit = 7; current_bit >= 0; current_bit--) {
popped_bit = (reg) & 0x00000001;
reg >>= 1;
if (popped_bit) reg ^= CRC32_POLY_REV;
}
}
for(byte=0; byte < CRC32_NUMBYTES; byte++) { /* save the reg in crc array */
crc[byte] = (reg >> (8*byte)) & 0xFF; /* in little endian format */
}
}
/*
* MifareCrc32Append
* Description: Get a DESFire Crc32 checksum and append to data.
*
* Arguments: data = pointer to data array
* len = length of data array
* Return: None
*
* Operation: Call MifareCrc32, and the pointer for the location of the crc
* value is the address of byte right after LSB of the data.
*
* Assumptions: Data is augmented i.e. has space for one extra byte, after its
* LSB. This is where the CRC will be saved
*
* Revision History:
* Jan. 05, 2012 Nnoduka Eruchalu Initial Revision
*/
void MifareCrc32Append(uint8_t *data, size_t len)
{
MifareCrc32(data, len, data+len);
}
/*
* MifareCrc16
* Description: Generate a MIFARE DESFire CRC checksum of width 16 for given
* data array of length len.
* This is done as specified for ISO 14443-3 Type A.
*
* Arguments: data = pointer to data block
* len = length of data block
* crc = ptr to generated CRC checksum array, as it's to be
* transmitted (little endian) [modified]
* Return: None
*
* Operation:
* Use the specified generator polynomial: [16-bit width]
* x16 + x12 + x5 + 1
*
* The process of encoding and decoding may be conveniently carried out by
* a 16-stage cyclic shift register with appropriate feedback gates.
* The flip-flops of the register shall be numbered from FF0 to FF15. FF0 shall
* be the leftmost flip-flop where data is shifted in. FF15 shall be the
* rightmost flip-flop where data is shifted out.
*
* Below is the initial content of the register.
* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 | 0 | 1 | 1 | 0 | 0 | 0 | 1 | 1 |
* +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
* message --> --> Pop!
*
*
* Note that the LSByte of the CRC is transmitted first, so the positions of MSB
* and LSB are swapped before returning.
*
* Documentation:
* http://www.waazaa.org/download/fcd-14443-3.pdf
*
* Test Values:
* data = {0x00, 0x00}, CRC = 0x1EA0, and transmit as {0xA0, 0x1E}
* data = {0x12, 0x34}, CRC = 0xCF26, and transmit as {0x26, 0xCF}
*
* Revision History:
* Jan. 05, 2012 Nnoduka Eruchalu Initial Revision
*/
void MifareCrc16 (uint8_t *data, size_t len, uint8_t *crc)
{
uint8_t bt; /* get current data byte */
uint16_t reg = 0x6363; /* ITU-V.41 */
do {
bt = *data++; /* loop through data bytes */
bt = (bt ^ (uint8_t) (reg & 0x00FF));
bt = (bt ^ (bt << 4));
reg = (reg >> 8) ^ ((uint16_t) bt << 8) ^ ((uint16_t) bt << 3) ^
((uint16_t) bt >> 4);
} while (--len); /* stay within data array limits */
*crc++ = (uint8_t) (reg & 0xFF); /* transmit LSB first */
*crc = (uint8_t) ((reg >> 8) & 0xFF); /* transmit MSB second */
}
/*
* MifareCrc16Append
* Description: Get a DESFire Crc16 checksum and append to data.
*
* Arguments: data = pointer to data array
* len = length of data array
* Return: None
*
* Operation: Call MifareCrc16, and the pointer for the location of the crc
* value is the address of byte right after LSB of the data.
*
* Assumptions: Data is augmented i.e. has space for one extra byte, after its
* LSB. This is where the CRC will be saved
*
* Revision History:
* Jan. 05, 2012 Nnoduka Eruchalu Initial Revision
*/
void MifareCrc16Append(uint8_t *data, size_t len)
{
MifareCrc16(data, len, data+len);
}
/*
* KeyBlockSize
* Description: Get MIFARE DESFire key block size
*
* Arguments: key = block cipher key
* Return: block_size = key block size
*
* Operation: If crypto operation is DES, 3DES, 3K3DES the block size is 8,
* else if crypto operation is AES the block size is 16.
*
* Revision History:
* Jan. 05, 2012 Nnoduka Eruchalu Initial Revision
*/
size_t KeyBlockSize(mifare_desfire_key *key)
{
size_t block_size;
switch (key->type) {
case T_DES: /* if crypto operation is DES, 3DES, 3K3DES */
case T_3DES: /* the block size is 8 */
case T_3K3DES:
block_size = 8;
break;
case T_AES: /* if crypto operation is AES, the block size is 16 */
block_size = 16;
break;
}
return block_size;
}
/*
* KeyMacingLength
* Description: Size of MACing produced with the key
*
* Arguments: key = block cipher key
* Return: mac_length = number of MAC/CMAC bytes
*
* Operation: If crypto operation is DES or 3DES, Data Authenticity is done
* Eusing MAC
* Else if crypto operation is 3K3DES or AES, Data Authenticity is
* done using CMAC
*
* Revision History:
* Jan. 05, 2012 Nnoduka Eruchalu Initial Revision
*/
static size_t KeyMacingLength(mifare_desfire_key *key)
{
size_t mac_length;
switch (key->type) { /* if crypto operation is DES or 3DES, Data */
case T_DES: /* Authenticity is done using MAC */
case T_3DES:
mac_length = MAC_LENGTH;
break;
case T_3K3DES: /* if crypto operation is 3K3DES or AES, Data */
case T_AES: /* Authenticity is done using CMAC */
mac_length = CMAC_LENGTH;
break;
}
return mac_length;
}
/*
* PaddedDataLength
* Description: Size required to store nbytes of data in a buffer of size
* n*block_size
* If nbytes == 0, n=1 else n=ceiling(nbytes/block_size);
*
* Arguments: nbytes = number of bytes to be padded
* block_size = block size to pad with
* Return: number of bytes after padding to block_size
*
* Operation: If nbytes==0 or nbytes is not a multiple of block_size, return
* n*block_size where n = max(1, ceiling(nbytes/block_size))
* else, return nbytes.
*
* Revision History:
* Jan. 05, 2012 Nnoduka Eruchalu Initial Revision
*/
size_t PaddedDataLength(size_t nbytes, size_t block_size)
{
if ((!nbytes) || (nbytes % block_size)) /* if numbytes is 0 or nbytes is not*/
return ((nbytes / block_size) + 1) * block_size;/*a multiple of block_size*/
else /* return n*block_size where n=max(1, ceiling(nbytes/block_size)) */
return nbytes; /* else no need to paa data bytes */
}
/*
* MacedDataLength
* Description: Buffer size required to MAC nbytes of data
*
* Arguments: key = Block cipher key
* nbytes = number of data bytes to be MACed
* Return: number of bytes after adding MAC bytes
*
* Operation: Buffer size is nbytes + KeyMacingLength(key)
*
* Revision History:
* Jan. 05, 2012 Nnoduka Eruchalu Initial Revision
*/
size_t MacedDataLength(mifare_desfire_key* key, size_t nbytes)
{
return (nbytes + KeyMacingLength(key));
}
/*
* EncipheredDataLength
* Description: Buffer size required to encipher nbytes of data and CRC
*
* Arguments: tag = Mifare DESFire PICC
* nbytes = number of data bytes to be Enciphered
* communication_settings = comm. settings between PCD and PICC
* Return: number of bytes after enciphering data
*
* Operation: Actual number of enciphered data bytes is:
* nbytes + crc length (2 or 4)
* This has to be padded to multiples of (key block size)
* Crc Length of 2 is for Legacy Authentication, and 4 is for
* New Authentication (DESFire EV1 only)
*
* Revision History:
* Jan. 05, 2012 Nnoduka Eruchalu Initial Revision
*/
size_t EncipheredDataLength(mifare_tag* tag, size_t nbytes,
int communication_settings)
{
size_t crc_length = 0; /* crc length: initialize it for NO_CRC */
size_t block_size; /* key block size */
if (!(communication_settings & NO_CRC)) {
switch (tag->authentication_scheme) { /* DESFire CRC is 2 bytes for */
case AS_LEGACY: /* legacy authentication */
crc_length = 2;
break;
case AS_NEW: /* DESFire CRC is 4 bytes for new */
crc_length = 4; /* authentication schemes */
break;
}
}
block_size = KeyBlockSize(&tag->session_key); /* get key block size */
/* enciphered data consisting of nbytes and crc is padded to multiples of */
/* key block size */
return PaddedDataLength (nbytes + crc_length, block_size);
}
/*
* MifareCryptoPreprocessData
* Description: Data Encipher before transmission.
*
* Arguments: tag = PICC
* data = pointer to data to be transmitted
* nbytes =ptr to number of data bytes to be transmitted [modified]
* offset = offset of command plus headers
*
* |<---------------- data ---------------->|
* |<--- offset -->| |
* +---------------+------------------------+
* | CMD + HEADERS | DATA TO BE TRANSMITTED |
* +---------------+------------------------+
*
* Return: - pointer to data or crypto buffer, so this doesn't allocate any
* new memory;
* if pointer is NULL, there was an error and data couldnt be
* preprocessed.
* - nbytes is [modified] length of processed data.
*
* Operation:
* If plain data transfer then function should be returning a pointer to data,
* so start by initializing result to point to the data array.
* allocate array to contain Legacy authentication scheme MAC bytes,
* and start by assuming MAC will be appended.
*
* If the passed in tag doesn't have a session key, then there will be no
* encrypted data, so simply return the pointer to data array
*
* Now we are ready for the actual preprocessing, and this depends on the
* data transmission mode (contained in communication_settings).
*
* 1. Plain Data Transfer
* If legacy authentication scheme, there is no preprocessing to be done.
*
* When using new authentication methods, PLAIN data transmission from the
* PICC to the PCD are CMACed, so we have to maintain the cryptographic
* initialization vector up-to-date to check data integrity later.
* The only difference with CMACed data transmission is that the CMAC is not
* appended to the data sent by the PCD to the PICC.
*
* 2. Plain data transfer with MAC
* If legacy authentication scheme, only generate MAC if settings say so.
* If a MAC is to be generated, first determine the encrypted data length.
* Encrypted data length accounts for the offset. This is done by first
* subtracting offset from nbytes before computing the padded data length.
* Offset is then added back to padded data length.
* Before we do any encrypting, we need to be sure the crypto buffer is big
* enough, and if we cant make this so we abort.
* Next, fill the crypto buffer with data and add 0 padding.
* Perform CBC encipher for sending, to get and save the MAC bytes.
* Finally append these MAC bytes, and update nbytes to account for this.
*
* If new authentication scheme, MAC will be generated by CMAC, and this will
* simply be called CMAC from here on out.
* Only generate CMAC if communication settings say so.
* If CMAC is to be generated, generate it by calling Cmac().
* This CMAC should not be be appended if communication mode is PLAIN.
* If however communication mode isn't PLAIN, but MACED, then we will
* be returning an updated crypto buffer, so first ensure it's big enough to
* hold MACed data. If it isn't abort. If however crypto buffer is big enough,
* copy the data into it, then copy the CMAC into it.
* Update nbytes to account for the added CMAC bytes.
*
* 3. DES/3DES encrypted data transfer
* Message Format:
*
* |<-------------- data -------------->|
* |<--- offset -->| |
* +---------------+--------------------+-----+---------+
* | CMD + HEADERS | DATA TO BE SECURED | CRC | PADDING |
* +---------------+--------------------+-----+---------+ ----------------
* | |<~~~~v~~~~~~~~~~~~~>| ^ | | (DES / 3DES)
* | | `---- Crc16() ----' | |
* | | | ^ | | ----- *or* -----
* |<~~~~~~~~~~~~~~~~~~~~v~~~~~~~~~~~~~>| ^ | | (3K3DES / AES)
* | `---- Crc32() ----' | |
* | | ---- *then* ----
* |<---------------------------------->|
* Encipher() / Decipher()
*
* Only Encipher Data if settings say so.
* If Data is to be encrypted, first get encrypted data length, again
* accounting for the offset.
* This encrypted data will be saved in the crypto buffer so it has to be big
* enough for this.
* If crypto buffer is indeed big enough for this, load it with data, then
* the CRC, and finally load in 0s for the padding.
* The CRC is CRC16 if using legacy auth., and CRC32 if using new auth.
* nbytes has to be updated to be the encrypted data length.
*
* Revision History:
* Jan. 05, 2012 Nnoduka Eruchalu Initial Revision
*/
uint8_t *MifareCryptoPreprocessData(mifare_tag *tag, uint8_t *data,
size_t *nbytes, off_t offset,
int communication_settings)
{
uint8_t *res = data; /* with no processing, result is original data */
uint8_t mac[MAC_LENGTH]; /* setup MAC array */
size_t edl, mdl; /* encrypted and MACed data length */
uint8_t append_mac = TRUE; /* start by assuming we need to append MAC */
mifare_desfire_key *key = &tag->session_key; /* get pointer to session key */
if(!key) /* if no session key, there can't be crypto */
return data;
switch(communication_settings & MDCM_MASK) { /* toggle based on comm. mode */
case MDCM_PLAIN: /* plain data transfer */
if (AS_LEGACY == tag->authentication_scheme)
break; /* do nothing if legacy authentication scheme */
append_mac = FALSE; /* when using new auth. scheme need to CMAC */
/* without appending to data; so pass through */
case MDCM_MACED: /* plain data transfer with MAC */
switch (tag->authentication_scheme) {
case AS_LEGACY: /* if legacy authentication scheme and not to */
if (!(communication_settings & MAC_COMMAND)) /* MAC command, then done */
break;
/* if however it is required to MAC data, then process data */
edl = PaddedDataLength(*nbytes - offset, KeyBlockSize(&tag->session_key))
+offset; /* grab encrypted data length */
/* crypto buffer should be (or is reallocated) to at least as big as edl*/
res = tag->crypto_buffer;
memcpy(res, data, *nbytes); /* Fill in crypto buffer with */
memset(res + *nbytes, 0, edl - *nbytes); /* data and add 0 padding */
/* CBC encipher for sending, to get MAC */
MifareCipherBlocksChained(tag, NULL, NULL, res + offset, edl - offset,
MCD_SEND, MCO_ENCIPHER);
memcpy(mac, res + edl - 8, MAC_LENGTH); /* save MAC value */
memcpy(res, data, *nbytes); /* copy provided data again (was */
/* overwritten by MifareCipherBlocksChained)*/
/* finally append MAC, by first grabbing MACed data length */
mdl = MacedDataLength(&tag->session_key, *nbytes - offset) + offset;
/* crypto buffer should be (or is reallocated) to at least as big as mdl*/
/* res = tag->crypto_buffer; */
memcpy(res + *nbytes, mac, MAC_LENGTH); /* append MAC then */
*nbytes += MAC_LENGTH; /* update number of bytes to account for MAC */
break; /* done handling legacy auth. scheme */
case AS_NEW: /* if new auth. sceme and not to MAC command,*/
if (!(communication_settings & CMAC_COMMAND)) /* then done */
break;
/* if however it's required to MAC command, then use CMAC generation */
Cmac(key, tag->ivect, res, *nbytes, tag->cmac);
if (append_mac) { /* don't append MAC if passed through from MDCM_PLAIN */
mdl = MacedDataLength(key, *nbytes);
/* crypto buffer is (or is reallocated to) at least as big as mdl */
res = tag->crypto_buffer;
memcpy(res, data, *nbytes); /* fill in crypto buffer with data and */
memcpy(res + *nbytes, tag->cmac, CMAC_LENGTH); /* save CMAC */
*nbytes += CMAC_LENGTH; /* update # of bytes to account for CMAC */
}
break; /* done handling new auth. scheme */
} /* end switch(tag->authentication_scheme) */
break; /* done handling MACed data transfer */
case MDCM_ENCIPHERED: /* DES/3DES ecnrypted data transfer */
if (!(communication_settings & ENC_COMMAND)) /* if not to encipher data */
break; /* then done */
/* if however it is required to encipher data, then process data */
edl = EncipheredDataLength(tag, *nbytes - offset, communication_settings)
+ offset;
/* crypto buffer is (or is reallocated to) at least as big as edl */
res = tag->crypto_buffer;
memcpy(res, data, *nbytes);/* fill in crypto buffer with data and */
if (!(communication_settings & NO_CRC)) { /* if CRC is needed, protocol */
switch (tag->authentication_scheme) { /* depends on auth. scheme */
case AS_LEGACY: /* legacy authentication uses Crc16 */
MifareCrc16Append(res + offset, *nbytes - offset);
*nbytes += 2; /* account for 2 extra CRC Bytes */
break;
case AS_NEW: /* new authentication uses Crc32 */
MifareCrc32Append(res, *nbytes);
*nbytes += 4; /* account for 4 extra CRC bytes */
break;
} /* end switch(tag->authentication_scheme) */
} /* end CRC required check */
memset(res + *nbytes, 0, edl - *nbytes); /* pad crypto buffer with 0s */
*nbytes = edl; /* record actual encrypted data length */
MifareCipherBlocksChained (tag, NULL, NULL, res + offset, *nbytes - offset,
MCD_SEND, (AS_NEW == tag->authentication_scheme)
? MCO_ENCIPHER : MCO_DECIPHER);
break; /* done handling encrypted data transfer */
default: /* unknown communication settings */
res = NULL; /* do nothing, just setup a blank result ptr */
*nbytes = -1;
break;
} /* end switch(communication_settings&MDCM_MASK)*/
return res; /* return pointer to preprocessed data */
}
/*
* MifareCryptoPostprocessData
* Description: Data Decipher/Verification after reception.
*
* Arguments: tag = PICC
* data = pointer to received data
* nbytes = pointer to number of received data bytes [modified]
* communication settings
*
* Return: - pointer to decrypted and verified data. NULL if data couldn't
* be verified.
* The data is now just payload followed by status byte
* Again this is a pointer to data or crypto buffer, so doesn't
* allocate any new memory.
* - nbytes [modified] to account for this (so excludes MAC, CRC
* and padding)
*
* Operation:
* if no processing is to be done, the returned data will be the passed in
* data, so initialize result to that.
* for comparisons we create a copy of the passed in data (that will undergo
* encryption). This should start out as an empty array.
*
* If no session key, then no processing can be done; assume plain
* communication with legacy authentication, so just return data.
* If only 1 byte is Rx'd, then its just a status byte, so return it.
*
* Now move on to data verification which depends on the data transmission mode
* contained in the communication settings:
*
* 1. Plain Data Transfer
* If legacy authentication, there is nothing to verify here. so done
* If new authentication, then we expect there to be a CMAC appended to the
* plain data. Do a passthrough in the switch-case statement and let the
* MDCM_MACED case handle this.
*
* 2. Plain data transfer with MAC
* If legacy authentication only verify the MAC if communication settings
* require this.
* Update nbytes to not count # of MAC bytes.
* Get length of data to be enciphered, which is all data bytes but status.
* Allocate memory for this.
* Copy over all data bytes (excluding status byte), with appropriate padding.
* Go on to Generate the MAC and append this to the duplicate data buffer.
* Compare this generated data and MAC buffer to the original data buffer. If
* they aren't equivalent, then there is a problem.
*
* If new authentication mode, and CMAC is appended to Tx'd commands, then
* there is nothing else to be done here.
* If however we are using new auth, and the communication settings require
* Rx'd CMACs are verified, then do just that.
* The first step is checking that the total number of bytes (including status
* byte) isn't less than 9. If it is, then there will be no space for a CMAC,
* which is fixed at 8 bytes.
* Save the first CMAC byte, then move the status byte to its position, right
* after the payload.
* Generate CMAC for new payload (including status).
* If communication settings require CMAC verification, restore the original
* first cmac byte to its old slot right after the payload, (where status byte
* is now).
* If generated cmac is not equal to the passed in CMAC, then CMAC isn't
* verified and there is a crypto error.
*
* 3. DES/3DES encrypted data transfer
* Use the following data format for reference
*
* AS_LEGACY:
* ,-----------------+-------------------------------+--==----+
* \ BLOCK n-1 | BLOCK n | STATUS |
* / PAYLOAD | CRC0 | CRC1 | 0x80? | 0x000000000000 | 0x00 |
* `-----------------+-------------------------------+--------+
*
* <------------ DATA ------------>
* FRAME = PAYLOAD + CRC(PAYLOAD) + PADDING
*
* AS_NEW:
* ,-----------------------+------------------------------------------+------+
* \ BLOCK n-1 | BLOCK n |STATUS|
* / PAYLOAD|CRC0|CRC1|CRC2| CRC3|0x80?|0x0000000000000000000000000000| 0x00 |
* `-----------------------+------------------------------------------+------+
* <-------------------------------- DATA --------------------------->|
*
* <----------------- DATA ---------------->