Primary_Credential_Encryption_Changes

Primary Credential Encryption Changes

There were some enhancement changes made to security by introducing changes to the encryption of the Primary credential which is part of the API request body.

Existing Encryption Methodology for Primary Credential

NOTE: This is still applicable for WiFi mode of operation.

  1. Primary credential is 8 bytes (16 hex digits) in length.

  2. The credential value is filled with b0s until the byte boundary and 0xFF for the rest of the bytes (to form an 8 byte credential ID).

  3. MT20W fills 0xFF as the most significant 8 bytes to form a 16 byte (128 bits) block of data for encryption.

  4. MT20W encrypts (AES 256) the 16 bytes (from step 2) using the Site Key as the encryption key and zeros for Init Vector.

  5. This encrypted value (16 bytes OR 32 hex digits) is encoded in Base 64 format (24 bytes) and sent as the Primary credential data to ORCA.

  6. ORCA decodes the Base 64 encoded data and then decrypts the Primary value from step 4, using the site key as the decryption key and zeros for Init Vector.

  7. The decryption results in a 16 byte (32 hex digits) data and ORCA pulls the least significant 8 byte to get the Primary Credential value.

Bit Format for UNencrypted Data: 16 bytes

Bit position0 -78-15
DataRaw card data0xFF

Encryption Example

Raw card data as printed on the card: 0x9400016009A4

Unencrypted data Encrypted data Encoded data Site Key    
Bit positionData Bit positionData Bit positionData Bit positionData
00x94 00xEB 00x36 00xD4
10x00 10x10 10x78 10xBE
20x01 20x94 20x43 20x94
30x60 30xDA 30x55 30x94
40x09 40x0B 40x32 40xB8
50xA4 50xCE 50x67 50x3F
60xFF 60x42 60x76 60xF0
70xFF 70xE5 70x4F 70xC0
80xFF 80x9E 80x51 80x18
90xFF 90x41 90x75 90x87
100xFF 100xB7 100x57 100x24
110xFF 110xCF 110x65 110xE2
120xFF 120xEF 120x51 120x56
130xFF 130x51 130x62 130xC9
140xFF 140x8F 140x66 140xEE
150xFF 150x4A 150x50 150x51
      160x37 160x1D
      170x31 170x40
      180x47 180x07
      190x50 190x6A
      200x53 200x52
      210x67 210xA0
      220x3D 220x82
      230x3D 230xC5
      240x00 240xDE
         250x9D
         260x30
         270xF3
         280x5B
         290x16
         300x85
         310x28

Encryption Changes for Primary Credential

The Primary credential is 8-bytes (16 hex digits) in length. The following steps are used to encrypt the Primary credential:

  1. The credential value is filled with binary bit 0s up to the byte boundary and 0xFF for the rest of the bytes (to form an 8-byte credential ID).

  2. The MT20W fills 0xFF as the most significant 8-bytes to form a 16-byte (128 bits) block of data.

  3. A random number of 12 bytes is generated and used as the first part of data to be encrypted. This is followed by the Primary credential of 16-bytes mentioned in the previous step.

  4. Two additional bytes of 0x00s are added to the data to make it 30-bytes long.

  5. CRC is calculated on this entire data and placed in the last two bytes of the 32-byte unencrypted data array.

  6. The MT20W encrypts (AES 256) the 32-bytes (from above step) using the Site Key as the encryption key and zeros for Init Vector.

  7. This encrypted value (32-bytes OR 64 hex digits) is encoded in Base64 format (44-bytes) and sent as the Primary credential data to ORCA.

Bit Format for UNencrypted Data: 16 bytes

Bit position0 -1112-1920-2728-2930-31
Data12-byte Random numberRaw card data padded with 0xFF to form 8 bytes0xFF0X00CRC-16

Encryption Example

Raw card data as printed on the card: 0x9400016009A4.

UNencrypted data Encrypted data Encoded data Site Key    
Bit positionData Bit positionData Bit positionData Bit positionData
00xAE 00x51 00x55 00xD4
10xB0 10xAF 10x61 10xBE
20x8A 20x52 20x39 20x94
30xF2 30x14 30x53 30x94
40x44 40xA6 40x46 40xB8
50x7E 50xB5 50x4B 50x3F
60x0C 60xF0 60x61 60xF0
70x7C 70xBC 70x31 70xC0
80xF8 80xDA 80x38 80x18
90xF5 90x59 90x4C 90x87
100x3A 100xE6 100x7A 100x24
110x3B 110xCB 110x61 110xE2
120x94 120x11 120x57 120x56
130x00 130xA3 130x65 130xC9
140x01 140xD6 140x62 140xEE
150x60 150xC9 150x4C 150x51
160x09 160x80 160x45 160x1D
170xA4 170x63 170x61 170x40
180xFF 180x0E 180x50 180x07
190xFF 190x5D 190x57 190x6A
200xFF 200xAA 200x79 200x52
210xFF 210xA6 210x59 210xA0
220xFF 220xBE 220x42 220x82
230xFF 230x45 230x6A 230xC5
240xFF 240x22 240x44 240xDE
250xFF 250xCA 250x6C 250x9D
260xFF 260xFC 260x32 260x30
270xFF 270x43 270x71 270xF3
280x00 280xA5 280x70 280x5B
290x00 290x44 290x72 290x16
300XE9 300x42 300x35 300x85
310x4C 310xE9 310x46 310x28
      320x49   
      330x73   
      340x72   
      350x38   
      360x51   
      370x36   
      380x56   
      390x45   
      400x51   
      410x75   
      420x6B   
      430x3D   
      440x6D   

Decryption of Primary

API must decode Base 64 and decrypt the Primary value using the site key as the decryption key. The decryption results in a 32-byte (32 hex digits) data and API pulls the masked 8-bytes to get the Primary Credential value.

The following steps are used to decrypt the Primary credential:

  1. Decode the base64 formatted Primary credential.

  2. Decrypt the decoded data using the Site key. This results in 32-byte un-encrypted Primary credential (Byte array of Hex values).

  3. The last two bytes of this array contains CRC. This CRC follows [LSB, MSB] format, hence reverse the CRC byte array.

  4. Compute the CRC16 for the first 30 bytes excluding the CRC.

  5. Compare the CRC obtained with the CRC of incoming request. If the CRC check fails, then API returns Bad Request with a message Credential data is corrupt.

  6. Once the CRC check is passed, Primary data (Total of from Byte[12] to Byte[27] – includes raw card data (8bytes) and padded 0xFF(8bytes)), is extracted from UNencrypted data. (See the Section: Encryption Changes for Primary Credential).

Computing CRC16

This is the method used to compute CRC. 0x10FFFF is used for masking in CRC 16 calculation and 0x1021 is the polynomial used.

public uint ComputeCrc16(byte [] bytes)
     {  
       uint crc = 0;
       foreach (byte b in bytes)
       {  
         crc ^= (uint) (b << 8);
         for (int i = 0; i < 8; i++)
         {
           if ((crc & 0x8000)! = 0)
           {
             crc = (((crc << 1) & 0x10FFFF) ^ 0x1021);
           }
           else
           {
             crc <<= 1;
           }
         }
       }
       return crc;}

 

Last Modified: October 15, 2019