CTR-AES256 Encrypt ne correspond pas à OpenSSL -aes-256-ctr

Mon problème est que je ne peux pas obtenir la sortie AES 256 CTR du code C ci-dessous pour correspondre à la sortie de la commande OpenSSL ci-dessous.

Le code C produit ceci:

5f b7 18 d1 28 62 7f 50 35 ba e9 67 a7 17 ab 22 f9 e4 09 ce 23 26 7b 93 82 02 d3 87 eb 01 26 ac 96 2c 01 8c c8 af f3 de a4 18 7f 29 46 00 2e 00 

La ligne de commande OpenSSL produit ceci:

 5f b7 18 d1 28 62 7f 50 35 ba e9 67 a7 17 ab 22 3c 01 11 bd 39 14 74 76 31 57 a6 53 f9 00 09 b4 6f a9 49 bc 6d 00 77 24 2d ef b9 c4 

Notez que les 16 premiers octets sont identiques car le nonceIV était identique, cependant, lorsque le nonceIV est mis à jour à la prochaine itération, XOR’d avec le texte en clair, les 16 prochains octets diffèrent, etc.

Je ne peux pas comprendre pourquoi cela se produit? Quelqu’un sait pourquoi les codes hexadécimaux sont différents après le premier segment de 16 octets?

Avertissement: je ne suis pas un expert en C.

Merci!!

Fox.txt

 The quick brown fox jumped over the lazy dog 

Ensuite, exécutez la commande OpenSSL suivante pour créer foxy.exe.

 openssl enc -aes-256-ctr -in fox.txt -out foxy.exe -K 603deb1015ca71be2b73aef0857d77811f352c073b6108d72d9810a30914dff4 -iv f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff -nosalt -nopad -p 

Voici ce que foxy.exe contient:

 5f b7 18 d1 28 62 7f 50 35 ba e9 67 a7 17 ab 22 3c 01 11 bd 39 14 74 76 31 57 a6 53 f9 00 09 b4 6f a9 49 bc 6d 00 77 24 2d ef b9 c4 

Voici le code.

  #include  // What is AES CTR // // AES - CTR (counter) mode is another popular symmesortingc encryption algorithm. // // It is advantageous because of a few features : // 1. The data size does not have to be multiple of 16 bytes. // 2. The encryption or decryption for all blocks of the data can happen in parallel, allowing faster implementation. // 3. Encryption and decryption use identical implementation. // // Very important note : choice of initial counter is critical to the security of CTR mode. // The requirement is that the same counter and AES key combination can never to used to encrypt more than more one 16 - byte block. // Notes // ----- // * CTR mode does not require padding to block boundaries. // // * The IV size of AES is 16 bytes. // // * CTR mode doesn't need separate encrypt and decrypt method. Encryption key can be set once. // // * AES is a block cipher : it takes as input a 16 byte plaintext block, // a secret key (16, 24 or 32 bytes) and outputs another 16 byte ciphertext block. // // References // ---------- // https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29 // https://www.cryptopp.com/wiki/CTR_Mode#Counter_Increment // https://modexp.wordpress.com/2016/03/10/windows-ctr-mode-with-crypto-api/ // https://msdn.microsoft.com/en-us/library/windows/desktop/jj650836(v=vs.85).aspx // http://www.cryptogrium.com/aes-ctr.html // http://www.bierkandt.org/encryption/symmesortingc_encryption.php #define IV_SIZE 16 #define AES_BLOCK_SIZE 16 typedef struct _key_hdr_t { PUBLICKEYSTRUC hdr; // Indicates the type of BLOB and the algorithm that the key uses. DWORD len; // The size, in bytes, of the key material. char key[32]; // The key material. } key_hdr; // NIST specifies two types of counters. // // First is a counter which is made up of a nonce and counter. // The nonce is random, and the remaining bytes are counter bytes (which are incremented). // For example, a 16 byte block cipher might use the high 8 bytes as a nonce, and the low 8 bytes as a counter. // // Second is a counter block, where all bytes are counter bytes and can be incremented as carries are generated. // For example, in a 16 byte block cipher, all 16 bytes are counter bytes. // // This uses the second method, which means the entire byte block is treated as counter bytes. void IncrementCounterByOne(char *inout) { int i; for (i = 16 - 1; i >= 0; i--) { inout[i]++; if (inout[i]) { break; } } } void XOR(char *plaintext, char *ciphertext, int plaintext_len) { int i; for (i = 0; i hdr.bType = PLAINTEXTKEYBLOB; key->hdr.bVersion = CUR_BLOB_VERSION; key->hdr.reserved = 0; key->hdr.aiKeyAlg = GetAlgorithmIdentifier(aeskeylenbits); key->len = GetKeyLengthBytes(aeskeylenbits); memmove(key->key, pKey, key->len); } // point = pointer to the start of the plaintext, extent is the size (44 bytes) void __stdcall AESCTR(char *point, int extent, char *pKey, char *pIV, unsigned int aeskeylenbits, char *bufOut) { HCRYPTPROV hProv; HCRYPTKEY hSession; key_hdr key; DWORD IV_len; div_t aesblocks; char nonceIV[64]; char tIV[64]; char *bufIn; bufIn = point; memmove(nonceIV, pIV, IV_SIZE); SetKeyData(&key, aeskeylenbits, pKey); CryptAcquireContext(&hProv, NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT | CRYPT_SILENT); CryptImportKey(hProv, (PBYTE)&key, sizeof(key), 0, CRYPT_NO_SALT, &hSession); aesblocks = div(extent, AES_BLOCK_SIZE); while (aesblocks.quot != 0) { IV_len = IV_SIZE; memmove(tIV, nonceIV, IV_SIZE); CryptEncrypt(hSession, 0, FALSE, 0, (BYTE *)tIV, &IV_len, sizeof(tIV)); XOR(bufIn, tIV, AES_BLOCK_SIZE); IncrementCounterByOne(nonceIV); bufIn += AES_BLOCK_SIZE; aesblocks.quot--; } if (aesblocks.rem != 0) { memmove(tIV, nonceIV, IV_SIZE); CryptEncrypt(hSession, 0, TRUE, 0, (BYTE *)tIV, &IV_len, sizeof(tIV)); XOR(bufIn, tIV, aesblocks.rem); } memmove(bufOut, point, extent); CryptDestroyKey(hSession); CryptReleaseContext(hProv, 0); } 

Le pseudocode suggéré dans la section de remarques M $ CryptEncrypt () https://msdn.microsoft.com/en-us/library/windows/desktop/aa379924(v=vs.85).aspx m’a permis de résoudre ce problème. :

 // Set the IV for the original key. Do not use the original key for // encryption or decryption after doing this because the key's // feedback register will get modified and you cannot change it. CryptSetKeyParam(hOriginalKey, KP_IV, newIV) while(block = NextBlock()) { // Create a duplicate of the original key. This causes the // original key's IV to be copied into the duplicate key's // feedback register. hDuplicateKey = CryptDuplicateKey(hOriginalKey) // Encrypt the block with the duplicate key. CryptEncrypt(hDuplicateKey, block) // Destroy the duplicate key. Its feedback register has been // modified by the CryptEncrypt function, so it cannot be used // again. It will be re-duplicated in the next iteration of the // loop. CryptDestroyKey(hDuplicateKey) } 

Voici le code mis à jour avec les deux nouvelles lignes ajoutées:

 HCRYPTKEY hDuplicateKey; boolean final; while (aesblocks.quot != 0) { CryptDuplicateKey(hOriginalKey, NULL, 0, &hDuplicateKey); IV_len = IV_SIZE; memmove(tIV, nonceIV, IV_len); final = (aesblocks.quot == 1 && aesblocks.rem == 0) ? TRUE : FALSE; CryptEncrypt(hDuplicateKey, 0, final, 0, (BYTE *)tIV, &IV_len, sizeof(tIV)); XOR(bufIn, tIV, AES_BLOCK_SIZE); IncrementCounterByOne(nonceIV); bufIn += AES_BLOCK_SIZE; aesblocks.quot--; CryptDestroyKey(hDuplicateKey); } if (aesblocks.rem != 0) { CryptDuplicateKey(hOriginalKey, NULL, 0, &hDuplicateKey); final = TRUE; memmove(tIV, nonceIV, IV_SIZE); CryptEncrypt(hDuplicateKey, 0, final, 0, (BYTE *)tIV, &IV_len, sizeof(tIV)); XOR(bufIn, tIV, aesblocks.rem); CryptDestroyKey(hDuplicateKey); } 

Je ne connais pas bien les API Microsoft, mais je pense que CryptEncrypt () utilise le mode CBC par défaut. Par conséquent, la sortie du premier bloc de chiffrement est automatiquement introduite dans l’entrée du second bloc. Vous construisez vous-même le mode CTR en mode scratch (ce qui incidemment n’est généralement pas une bonne chose à faire – vous devez utiliser les fonctionnalités des bibliothèques de cryptage plutôt que de “rouler votre propre” crypto). Pour obtenir le résultat attendu, vous devez probablement faire en sorte que CryptEncrypt utilise AES en mode ECB – ce qui, à mon avis, peut être effectué à l’aide de CryptptSetKeyParam ( https://msdn.microsoft.com/en-us/library/aa380272.aspx ) et du paramètre KP_MODE à CRYPT_MODE_ECB.

Assurez-vous que votre fichier d’entrée ne contient aucun caractère supplémentaire tel que nouvelle ligne, etc. Openssl inclura ces caractères supplémentaires lors du chiffrement.