Не такий вже ти і страшний, XTS-AES

Вітаю,% username%!


Сьогоднішня стаття навіяна думками написати безкоштовний аналог програми для шифрування файлів в DropBox, а саме аспектом режиму шифрування файлів посекторно (для можливості читати\писати з/в довільне місце)

Ми поговоримо про режим шифрування XTS-AES, що застосовується у всіх популярних дискошифровалках (TrueCrypt, DiskCryptor).

Він описаний в IEEE P1619 ./D16 (Standard for Cryptographic Protection of Data on Block-Oriented Storage Devices) і вважається найбезпечнішим способом зберігати дані посекторно.

Першо-напершо визначимо вхідні дані для роботи:

  1. 256/512 біт ключа (може бути SHA-256/512 (сіль + пароль) або що-небудь на зразок KDF)
  2. Адреса (номер) сектору
  3. Собсно блок даних довжини кратної 128 битам (розмір блоку AES)

Спрощено, алгоритм наступний:

  1. Розбивка ключа на два. Перша частина стає ключем шифрування даних (k1), друга - ключем для генерації tweak value (k2)

Таким чином, якщо у нас використовується ключ в 512 біт, то ми його пиляємо на 2х256 і використовуємо в AES-256

  1. Конвертуємо номер сектору в масив байт і шифруємо його ключем k2. Це наше tweak value
  2. Йдемо по масиву даних блоками розміром по 16 байт і для кожного блоку:
  3. ксоримо його з tweak value
  4. шифруємо/розшифровуємо його ключем k1
  5. Знову ксорим вже (рас) шифрований блок даних з tweak value. Зберігаємо його, це і буде потрібний нам (за/рас) шифрований блок сектора
  6. Множимо tweak value на поліном ^ = x128 + x7 + x2 + x + 1

Найбільш незрозуміле тут (для мене, зокрема) це множення на поліном. Алгоритмічно це просто зсув всього масиву на 1 біт + xor на останньому кроці.

  1. private const int GF_128_FDBK = 0x87;
  2. private const int AES_BLK_BYTES = 16;
  3. ...
  4. //множимо T (weak value)
  5. Cin = 0 ;//біт перенесення
  6. for (j = 0; j < AES_BLK_BYTES; j++)
  7. {
  8. Cout = (T[j] >> 7) & 1;
  9. T[j] = (byte)(((T[j] << 1) + Cin) & 0xFF);
  10. Cin = Cout;
  11. }
  12. if (Cout != 0)
  13. {
  14. T[0] ^= GF_128_FDBK;
  15. }

В принципі, ніякого криміналу. Робиться це для того, щоб tweak value був різним для кожного блоку даних всередині сектора.

Питання до математично підкованої аудиторії: Чому вибрано саме множення на поліном у GF (2) за модулем x128 + x7 + x2 + x + 1? Моїх (не) знань вистачає лише припустити, що тут замішані циклічні групи, і все це певний аналог циклічного зрушення.

Робочий код на C #, який навіть проходить стандартні тести:

  1. class XTS
  2. {
  3. private const int GF_128_FDBK = 0x87;
  4. private const int AES_BLK_BYTES = 16;
  5. public static byte[] encryptSector(byte[] inData, byte[] dataEncryptionKey, byte[] tweakEncryptionKey, UInt64 sectorNumber, bool encrypt)
  6. {
  7. byte [] outData = new byte [inData.Length] ;//тут буде результат. Розмір inData має бути коротким 32!
  8. uint i, j; // local counters
  9. var T = new byte[AES_BLK_BYTES]; // tweak value
  10. var x = new byte [AES _ BLK _ BYTES] ;//буфер для шифрованого блоку даних
  11. //конвертуємо номер сектора в масив байт
  12. Copy(BitConverter.GetBytes(sectorNumber), T, 8);
  13. //після шифрування в T у нас tweak value. true означає шифрувати
  14. processAES(tweakEncryptionKey, T, true);
  15.      
  16. //Обробляємо по AES_BLK_BYTES байт за раз
  17. for (i = 0; i < inData.Length; i += AES_BLK_BYTES)
  18. {
  19. //ксорим tweak value з шматком даних
  20. for (j = 0; j < AES_BLK_BYTES; j++)
  21. {
  22. x[j] = (byte)(inData[i + j] ^ T[j]);
  23. }
  24. //шифруємо/розшифровуємо блок
  25. processAES(dataEncryptionKey, x, encrypt);
  26. //ксорим tweak value з обробленим блоком даних
  27. for (j = 0; j < AES_BLK_BYTES; j++)
  28. {
  29. outData[i + j] = (byte)(x[j] ^ T[j]);
  30. }
  31. //Множимо tweak value на
  32. j = AES_BLK_BYTES;
  33. int t = T[AES_BLK_BYTES - 1];
  34. while (--j != 0)
  35. T[j] = (byte)((T[j] << 1) | ((T[j - 1] & 0x80) != 0 ? 1 : 0));
  36. T[0] = (byte)((T[0] << 1) ^ ((t & 0x80) != 0 ? 0x87 : 0x00));
  37. }
  38. return outData;
  39. }
  40. private static void processAES(byte[] k, byte[] T, bool encrypt)
  41. {
  42. / * AesFastEngine взято з BouncyCastle. Ви можете замінити на стандартну
  43. * реалізацію, або взагалі використовувати інший алгоритм, тільки враховуйте
  44. * Розмір блоку шифрування.
  45. */
  46. var engine = new AesFastEngine();
  47. Init(encrypt, new KeyParameter(k));
  48. ProcessBlock(T, 0, T, 0);
  49. }
  50. }

______________________

Використовувати AES, як видно, не обов'язково. Правда, про це нічого не сказано в стандарті.

Домашнім завданням буде реалізувати обробку сектора для розмірів не кратних 32.

А я продовжу написання шифрувалки для DropBox)

COM_SPPAGEBUILDER_NO_ITEMS_FOUND