4

AES CCM モード( 256 ビット キーの長さ)で大きなファイルを暗号化するタスクに取り組んでいます。暗号化のその他のパラメーターは次のとおりです。

  • タグサイズ: 8 バイト
  • ivサイズ: 12 バイト

すでにOpenSSL 1.0.1cを使用しているので、このタスクにも使用したいと思いました。

ファイルのサイズは事前にはわからず、非常に大きくなる可能性があります。そのため、ブロックごとに読み取り、各ブロックをEVP_EncryptUpdateでファイル サイズまで個別に暗号化する必要がありました。

残念ながら、ファイル全体が一度に暗号化されている場合にのみ、暗号化が機能します。複数回呼び出そうとすると、EVP_EncryptUpdate からエラーが発生したり、奇妙なクラッシュが発生したりします。Windows 7 と Ubuntu Linux で gcc 4.7.2 を使用して暗号化をテストしました。

ブロックごとにデータを暗号化することは不可能 (または可能) であるというOpenSSLサイトの情報を見つけることができませんでした。

追加の参照:

私が達成しようとしたことを示す以下のコードを参照してください。残念ながら、for ループで示されている場所で失敗しています。

#include <QByteArray>
#include <openssl/evp.h>

// Key in HEX representation
static const char keyHex[] = "d896d105b05aaec8305d5442166d5232e672f8d5c6dfef6f5bf67f056c4cf420";
static const char ivHex[]  = "71d90ebb12037f90062d4fdb";

// Test patterns
static const char orig1[] = "Very secret message.";

const int c_tagBytes      = 8;
const int c_keyBytes      = 256 / 8;
const int c_ivBytes       = 12;

bool Encrypt()
{
    EVP_CIPHER_CTX *ctx;
    ctx = EVP_CIPHER_CTX_new();
    EVP_CIPHER_CTX_init(ctx);

    QByteArray keyArr = QByteArray::fromHex(keyHex);
    QByteArray ivArr = QByteArray::fromHex(ivHex);

    auto key = reinterpret_cast<const unsigned char*>(keyArr.constData());
    auto iv = reinterpret_cast<const unsigned char*>(ivArr.constData());

    // Initialize the context with the alg only
    bool success = EVP_EncryptInit(ctx, EVP_aes_256_ccm(), nullptr, nullptr);
    if (!success) {
        printf("EVP_EncryptInit failed.\n");
        return success;
    }

    success = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, c_ivBytes, nullptr);
    if (!success) {
        printf("EVP_CIPHER_CTX_ctrl(EVP_CTRL_CCM_SET_IVLEN) failed.\n");
        return success;
    }
    success = EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, c_tagBytes, nullptr);
    if (!success) {
        printf("EVP_CIPHER_CTX_ctrl(EVP_CTRL_CCM_SET_TAG) failed.\n");
        return success;
    }

    success = EVP_EncryptInit(ctx, nullptr, key, iv);
    if (!success) {
        printf("EVP_EncryptInit failed.\n");
        return success;
    }

    const int bsize = 16;
    const int loops = 5;
    const int finsize = sizeof(orig1)-1; // Don't encrypt '\0'

    // Tell the alg we will encrypt size bytes
    // http://www.fredriks.se/?p=23
    int outl = 0;
    success = EVP_EncryptUpdate(ctx, nullptr, &outl, nullptr, loops*bsize + finsize);
    if (!success) {
        printf("EVP_EncryptUpdate for size failed.\n");
        return success;
    }
    printf("Set input size. outl: %d\n", outl);

    // Additional authentication data (AAD) is not used, but 0 must still be
    // passed to the function call:
    // http://incog-izick.blogspot.in/2011/08/using-openssl-aes-gcm.html
    static const unsigned char aadDummy[] = "dummyaad";
    success = EVP_EncryptUpdate(ctx, nullptr, &outl, aadDummy, 0);
    if (!success) {
        printf("EVP_EncryptUpdate for AAD failed.\n");
        return success;
    }
    printf("Set dummy AAD. outl: %d\n", outl);

    const unsigned char *in = reinterpret_cast<const unsigned char*>(orig1);
    unsigned char out[1000];
    int len;

    // Simulate multiple input data blocks (for example reading from file)
    for (int i = 0; i < loops; ++i) {
        // ** This function fails ***
        if (!EVP_EncryptUpdate(ctx, out+outl, &len, in, bsize)) {
            printf("DHAesDevice: EVP_EncryptUpdate failed.\n");
            return false;
        }
        outl += len;
    }

    if (!EVP_EncryptUpdate(ctx, out+outl, &len, in, finsize)) {
        printf("DHAesDevice: EVP_EncryptUpdate failed.\n");
        return false;
    }
    outl += len;

    int finlen;
    // Finish with encryption
    if (!EVP_EncryptFinal(ctx, out + outl, &finlen)) {
        printf("DHAesDevice: EVP_EncryptFinal failed.\n");
        return false;
    }
    outl += finlen;
    // Append the tag to the end of the encrypted output
    if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_GET_TAG, c_tagBytes, out + outl)) {
        printf("DHAesDevice: EVP_CIPHER_CTX_ctrl failed.\n");
        return false;
    };
    outl += c_tagBytes;
    out[outl] = '\0';

    EVP_CIPHER_CTX_cleanup(ctx);
    EVP_CIPHER_CTX_free(ctx);

    QByteArray enc(reinterpret_cast<const char*>(out));

    printf("Plain text size: %d\n", loops*bsize + finsize);
    printf("Encrypted data size: %d\n", outl);

    printf("Encrypted data: %s\n", enc.toBase64().data());

    return true;
}

編集(間違った解決策)

受け取ったフィードバックにより、別の方向に考えるようになり、ファイルの合計サイズではなく、暗号化されるブロックごとにサイズの EVP_EncryptUpdate を呼び出す必要があることがわかりました。ブロックが暗号化される直前に移動しました: 次のように:

for (int i = 0; i < loops; ++i) {
    int buflen;
    (void)EVP_EncryptUpdate(m_ctx, nullptr, &buflen, nullptr, bsize);
    // Resize the output buffer to buflen here
    // ...
    // Encrypt into target buffer
    (void)EVP_EncryptUpdate(m_ctx, out, &len, in, buflen);
    outl += len;
}

ブロックごとの AES CCM 暗号化はこのように機能しますが、各ブロックが独立したメッセージとして扱われるため、正しく機能しません。

編集2

OpenSSL の実装は、完全なメッセージが一度に暗号化されている場合にのみ正しく機能します。

http://marc.info/?t=136256200100001&r=1&w=1

代わりに Crypto++ を使用することにしました。

4

2 に答える 2

0

AEAD-CCM モードでは、関連付けられたデータがコンテキストにフィードされた後にデータを暗号化することはできません。すべてのデータを暗号化し、関連付けられたデータを渡した後にのみ暗号化します。

于 2013-02-28T15:11:13.390 に答える
0

ここでいくつかの誤解を見つけました

まず第一に EVP_EncryptUpdate(ctx, nullptr, &outl をこの方法で呼び出すことは、必要な出力バッファーの量を知ることで、バッファーを割り当て、2 番目の引数をデータを保持するのに十分な大きさの有効なバッファーとして指定します。

また、暗号化された出力を実際に追加するときに、間違った (以前の呼び出しで上書きされた) 値を渡しています。

于 2013-02-28T15:17:05.097 に答える