Not so scary, XTS-AES
Greetings,% username%!
Today's article is inspired by the thoughts of writing a free analogue of the program for encrypting files in DropBox, namely, an aspect of the file encryption mode sector-by-sector (for the ability to read / write from / to any place)
We will talk about the XTS-AES encryption mode used in all popular disk encryptors (TrueCrypt, DiskCryptor).
It is described in IEEE P1619 ™ / D16 (Standard for Cryptographic Protection of Data on Block-Oriented Storage Devices) and is considered the safest way to store data sector-by-sector.
First of all, we will determine the input data for work:
- 256/512 key bits (maybe SHA-256/512 (salt + password) or something like KDF )
- Sector address (number)
- Own data block of length multiple of 128 bits (block size AES)
Simplified, the algorithm is as follows:
- Breakdown of a key into two. The first part becomes the data encryption key ( k1 ), the second - the key for generating tweak value ( k2 )
Thus, if we use a 512-bit key, we saw it on 2x256 and use it in AES-256 - We convert the sector number into a byte array and encrypt it with the k2 key . This is our tweak value
- We go through the data array in blocks of 16 bytes in size and for each block:
- 1.Ksorim it with tweak value
- 2. We encrypt / decrypt it with the key k1
- 3. Again Xorim already (races) an encrypted data block with tweak value . We save it, this will be the encrypted block of the sector we need (for / ras)
- 4. Multiply the tweak value by the polynomial α = x 128 + x 7 + x 2 + x + 1
The most incomprehensible here (for me, in particular) is polynomial multiplication. Algorithmically, this is simply a shift of the entire array by 1 bit + xor in the last step.
- private const int GF_128_FDBK = 0x87;
- private const int AES_BLK_BYTES = 16;
- ...
- // multiply T (weak value) by α
- Cin = 0; // carry bit
- for (j = 0; j <AES_BLK_BYTES; j ++)
- {
- Cout = (T [j] >> 7) & 1;
- T [j] = (byte) (((T [j] << 1) + Cin) & 0xFF);
- Cin = cout;
- }
- if (Cout! = 0)
- {
- T [0] ^ = GF_128_FDBK;
- }
In principle, no crime. This is done so that the tweak value is different for each data block within the sector.
Question to a mathematically savvy audience : Why was it chosen the multiplication by polynomial in GF (2) modulo x 128 + x 7 + x 2 + x + 1? My (non) knowledge is enough only to assume that cyclic groups are involved here, and all this is a kind of analogue of a cyclic shift.
C # working code that even passes standard tests:
- class XTS
- {
- private const int GF_128_FDBK = 0x87;
- private const int AES_BLK_BYTES = 16;
- public static byte [] encryptSector (byte [] inData, byte [] dataEncryptionKey, byte [] tweakEncryptionKey, UInt64 sectorNumber, bool encrypt)
- {
- byte [] outData = new byte [inData.Length]; // there will be a result. The inData size must be a multiple of 32!
- uint i, j; // local counters
- var T = new byte [AES_BLK_BYTES]; // tweak value
- var x = new byte [AES_BLK_BYTES]; // buffer for (over / dec) the encrypted data block
- // convert sector number to byte array
- Array.Copy (BitConverter.GetBytes (sectorNumber), T, 8);
- // after encryption in T, we have tweak value. true means encrypt
- processAES (tweakEncryptionKey, T, true);
- // Process AES_BLK_BYTES bytes at a time
- for (i = 0; i <inData.Length; i + = AES_BLK_BYTES)
- {
- // Xorim tweak value with a piece of data
- for (j = 0; j <AES_BLK_BYTES; j ++)
- {
- x [j] = (byte) (inData [i + j] ^ T [j]);
- }
- // encrypt / decrypt the block
- processAES (dataEncryptionKey, x, encrypt);
- // Xorim tweak value with the processed data block
- for (j = 0; j <AES_BLK_BYTES; j ++)
- {
- outData [i + j] = (byte) (x [j] ^ T [j]);
- }
- // Multiply tweak value by α
- j = AES_BLK_BYTES;
- int t = T [AES_BLK_BYTES - 1];
- while (--j! = 0)
- T [j] = (byte) ((T [j] << 1) | ((T [j - 1] & 0x80)! = 0? 1: 0));
- T [0] = (byte) ((T [0] << 1) ^ ((t & 0x80)! = 0? 0x87: 0x00));
- }
- return outData;
- }
- private static void processAES (byte [] k, byte [] T, bool encrypt)
- {
- / * AesFastEngine taken from BouncyCastle. You can replace the standard
- * implementation, or even use a different algorithm, just consider
- * encryption block size.
- * /
- var engine = new AesFastEngine ();
- engine.Init (encrypt, new KeyParameter (k));
- engine.ProcessBlock (T, 0, T, 0);
- }
- }
______________________
Using AES, as you can see, is not necessary. True, nothing is said about this in the standard.
The homework will be to implement sector processing for sizes not multiple of 32.
And I will continue to write the encryption for DropBox)