Может ли шифрование bcrypts AES-256 GCM повредить ZIP-файлы?

Я пытаюсь зашифровать ZIP-файл с помощью AES-256 GCM на C и расшифровать его на Python. Вот код C, который я использую:

NTSTATUS generateRandomBytes(BYTE *buffer, ULONG length)
{
    BCRYPT_ALG_HANDLE hProvider;
    NTSTATUS status = BCryptOpenAlgorithmProvider(&hProvider, BCRYPT_RNG_ALGORITHM, NULL, 0);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    status = BCryptGenRandom(hProvider, buffer, length, 0);
    BCryptCloseAlgorithmProvider(hProvider, 0);
    return status;
}

NTSTATUS encrypt_AES_GCM(const BYTE *plainData, ULONG plainDataLength,
                         const BYTE *iv, ULONG ivLength,
                         const BYTE *key, ULONG keyLength,
                         BYTE *encryptedData, ULONG encryptedDataLength,
                         BYTE *authTag, ULONG authTagLength)
{
    NTSTATUS status = 0;
    DWORD bytesDone = 0;
    BCRYPT_ALG_HANDLE algHandle = 0;
    status = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_AES_ALGORITHM, NULL, 0);
    if (!NT_SUCCESS(status))
    {
        return status;
    }

    status = BCryptSetProperty(algHandle, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
    if (!NT_SUCCESS(status))
    {
        BCryptCloseAlgorithmProvider(algHandle, 0);
        return status;
    }

    BCRYPT_KEY_HANDLE keyHandle = 0;
    status = BCryptGenerateSymmetricKey(algHandle, &keyHandle, NULL, 0, (PUCHAR)key, keyLength, 0);
    if (!NT_SUCCESS(status))
    {
        BCryptCloseAlgorithmProvider(algHandle, 0);
        return status;
    }

    BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
    BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
    authInfo.pbNonce = (PUCHAR)iv;
    authInfo.cbNonce = ivLength;
    authInfo.pbTag = authTag;
    authInfo.cbTag = authTagLength;

    status = BCryptEncrypt(keyHandle, (PUCHAR)plainData, plainDataLength, &authInfo, NULL, 0, encryptedData, encryptedDataLength, &bytesDone, 0);
    if (!NT_SUCCESS(status))
    {
        BCryptDestroyKey(keyHandle);
        BCryptCloseAlgorithmProvider(algHandle, 0);
        return status;
    }

    BCryptDestroyKey(keyHandle);
    BCryptCloseAlgorithmProvider(algHandle, 0);

    return status;
}

int GCM_Encrypt_File(char *FileToEncrypt, char *keys, char *OutputFile)
{
    int fd;
    long file_size;
    char *input;
    _sopen_s(&fd, FileToEncrypt, _O_RDONLY, _SH_DENYRW, _S_IREAD);
    file_size = _filelength(fd);
    input = (char *)malloc(file_size);
    size_t bytes_read = fread(input, 1, file_size, _fdopen(fd, "rb"));
    BYTE key[32];
    BYTE iv[12];
    BYTE KeyTagIV[60];
    BYTE *encrypted;
    ULONG encryptedSize = file_size;
    ULONG authTagLength = 16;
    BYTE authTag[16];

    // Generate key and iv
    generateRandomBytes(key, sizeof(key));
    generateRandomBytes(iv, sizeof(iv));
    // Concatenate key and IV
    memcpy(KeyTagIV, key, sizeof(key));
    memcpy(KeyTagIV + sizeof(key), iv, sizeof(iv));
    // Print the info

    encrypted = (BYTE *)malloc(encryptedSize);

    // Encrypt
    encrypt_AES_GCM((BYTE *)input, file_size, iv, 12, key, 32, encrypted, encryptedSize, authTag, authTagLength);
    
    // Print data
    printf("KEY:\n");
    for (ULONG i = 0; i < sizeof(key); i++)
    {
        printf("%02X", key[i]);
    }
    printf("\n");
    printf("IV:\n");
    for (ULONG i = 0; i < sizeof(iv); i++)
    {
        printf("%02X", iv[i]);
    }
    printf("\n");
    printf("Authentication tag:\n");
    for (ULONG i = 0; i < authTagLength; i++)
    {
        printf("%02X", authTag[i]);
    }
    printf("\n");

    // Add authentication tag to the KeyTagIV variable 
    memcpy(KeyTagIV + 44, authTag, authTagLength);
    memcpy(keys, KeyTagIV, 60);
    printf("FULL:\n");
    for (ULONG i = 0; i < 60; i++)
    {
        printf("%02X", KeyTagIV[i]);
    }
    printf("\n");
    FILE *encryptedFile = fopen(OutputFile, "wb");
    fwrite(encrypted, sizeof(char), file_size, encryptedFile);
    free(encrypted);
    return 1;
}

int main()
{
        // GCM Encrypt the File.
        char key_data[60];
        char path[] = "test.txt";
        char path1[] = "test.enc";
        GCM_Encrypt_File(path, key_data, path1);
        printf("Decryption Data:");
        for (int i = 0; i < 60; i++) {
            printf("%02X ", key_data[i]);
        }

    return 0;
}

и я пытаюсь расшифровать данные с помощью этого кода Python:

import rsa
from Crypto.Cipher import AES
import base64
import os
if __name__ == '__main__':
    # Decryption data in HEX KEY; IV; TAG
    KEYS = """540AA548ADBBF19820FEC1DDF9BC19B6230A746C0CF0EA87E083FDF314867DA525F299D2B9FEBC26A864A9F149D3A60B05E03CA9C3328E5AB10228DB"""
    encrypted_keys = bytearray.fromhex(KEYS)

    ENC_PATH = "test.enc"
    keys_data = encrypted_keys

    key = keys_data[:32]
    iv = keys_data[32:44]
    authTag = keys_data[44:60]
    ciphertext = open(ENC_PATH, mode = "rb").read()
    cipher = AES.new(key, AES.MODE_GCM, nonce=iv)
    plaintext = cipher.decrypt_and_verify(ciphertext, authTag)
    with open('decrypted_test.txt', 'wb') as f:
        f.write(plaintext)
    print("Done.")

#decrypt_rsa(input_filename, output_filename)

Кажется, что этот код работает, например, при шифровании файла .txt с помощью просто «Привет, мир!». Значит, он способен правильно его расшифровать.

Проблема в том, что когда я пытаюсь зашифровать ZIP-файл следующим образом:

Расшифрованные выходные данные выглядят следующим образом:

Файл .txt исчез, а test2.zip при попытке открытия сообщает «Поврежден». Файл test1.zip по-прежнему открывается правильно.

Может ли алгоритм его испортить? Или что-то не так в моем коде?

Учитывая, что AES существует уже несколько десятилетий, анализируется и повторно анализируется множеством блестящих математических умов и имеет обширный практический опыт работы в широком спектре заметных приложений, я думаю, можно с уверенностью предположить, что этот алгоритм герметичен. . Может ли быть ошибка в реализации bcrypt? Возможно, но маловероятно по тем же причинам. Наиболее вероятным объяснением проблемы, которую вы видите, являются ошибки в вашем коде.

yano 29.03.2024 16:50

Да, я пришел к такому же выводу. Но я не вижу в своем коде ничего особенно неправильного, поскольку код C способен правильно зашифровать «Hello, world!» .txt, и Python может расшифровать этот файл...

exomm 29.03.2024 17:19

Надеюсь, кто-нибудь, кто знает об этом (не я), придет, но есть некоторые общие мысли, которые помогут при отладке: используйте один и тот же жестко запрограммированный ключ, а не генерируйте каждый раз новый. Сравните ваши части шифрования/дешифрования с легкодоступными (и известными работающими) реализациями в Интернете, это, по крайней мере, сузит круг проблемных частей (или обеих).

yano 29.03.2024 17:41
ULONG encryptedSize = file_size; мне кажется подозрительным. Похоже, вы предполагаете, что длина зашифрованного файла будет такой же, как длина открытого текста, на что, я думаю, вы вообще не можете рассчитывать. Я знаю, что AES — это блочный шифр, который заполняет поля, если данные PT не совпадают полностью. Возможно, bcrypt скрывает от вас такие детали реализации, но, возможно, дважды проверьте документацию на наличие BCryptEncrypt.
yano 29.03.2024 17:44

@yano Я думаю, что реализация ключа работает, поскольку я пробовал это десятки раз, просто копируя ключ из вывода C в скрипт Python, и каждый раз расшифровка удавалась. Но у меня самого проблема кроется где-то в записи и чтении файлов.

exomm 29.03.2024 17:52

Я много раз читал в Интернете, что при дополнении PCKS1 открытый текст имеет тот же размер, что и зашифрованный текст. С помощью быстрого поиска в Google я получил это «GCM — это режим счетчика, поэтому зашифрованный текст имеет ту же длину, что и открытый текст, потому что это поточный шифр».. Я постараюсь больше узнать о двоичном шифровании с помощью реализации GCM bcrypt.

exomm 29.03.2024 17:57
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
6
81
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Вам необходимо открыть fd в двоичном режиме.

_sopen_s(&fd, FileToEncrypt, _O_RDONLY, _SH_DENYRW, _S_IREAD);

должно быть

_sopen_s(&fd, FileToEncrypt, _O_RDONLY | _O_BINARY, _SH_DENYRW, _S_IREAD);

Другие вопросы по теме