13

Windows CNG API を使用すると、GCM モードで AES を使用して、認証を使用してデータの個々のブロックを暗号化および復号化できます。複数のバッファを連続して暗号化および復号化したいと考えています。

CNG のドキュメントによると、次のシナリオがサポートされています。

暗号化または復号化への入力が複数のバッファに分散している場合は、BCryptEncrypt 関数と BCryptDecrypt 関数への呼び出しを連鎖させる必要があります。連鎖は、dwFlags メンバーで BCRYPT_AUTH_MODE_IN_PROGRESS_FLAG フラグを設定することによって示されます。

私がそれを正しく理解していれば、これは、BCryptEncrypt複数のバッファーで順番に呼び出し、最後に結合されたバッファーの認証タグを取得できることを意味します。同様にBCryptDecrypt、実際の認証チェックを最後まで延期しながら、複数のバッファーで順番に呼び出すことができます。しかし、それを機能させることはできません。値dwFlagsが無視されているようです。を使用するたびに、 で定義されている と等しいBCRYPT_AUTH_MODE_IN_PROGRESS_FLAGの戻り値を取得します。0xc000a002STATUS_AUTH_TAG_MISMATCHntstatus.h

パラメータpbIVが in/out としてマークされていても、パラメータが指す要素は によってpbIV変更されませんBCryptEncrypt()。それは期待されていますか?ポインターが指す構造pbNonce内のフィールドも調べましたが、そのフィールドも変更されていません。また、「手動で」IV を進め、カウンター スキームに従ってコンテンツを自分で変更しようとしましたが、それも役に立ちませんでした。BCRYPT_AUTHENTICATED_CIPHER_MODE_INFOpPaddingInfo

BCryptEncryptand/orBCryptDecrypt関数を正常にチェーンするための正しい手順は何ですか?

4

2 に答える 2

13

私はそれを機能させることができました。問題は MSDN にあるようです。BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG代わりに設定について言及する必要がありBCRYPT_AUTH_MODE_IN_PROGRESS_FLAGます。

#include <windows.h>
#include <assert.h>
#include <vector>
#include <Bcrypt.h>
#pragma comment(lib, "bcrypt.lib")

std::vector<BYTE> MakePatternBytes(size_t a_Length)
{
    std::vector<BYTE> result(a_Length);
    for (size_t i = 0; i < result.size(); i++)
    {
        result[i] = (BYTE)i;
    }

    return result;
}

std::vector<BYTE> MakeRandomBytes(size_t a_Length)
{
    std::vector<BYTE> result(a_Length);
    for (size_t i = 0; i < result.size(); i++)
    {
        result[i] = (BYTE)rand();
    }

    return result;
}

int _tmain(int argc, _TCHAR* argv[])
{
    NTSTATUS bcryptResult = 0;
    DWORD bytesDone = 0;

    BCRYPT_ALG_HANDLE algHandle = 0;
    bcryptResult = BCryptOpenAlgorithmProvider(&algHandle, BCRYPT_AES_ALGORITHM, 0, 0);
    assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptOpenAlgorithmProvider");

    bcryptResult = BCryptSetProperty(algHandle, BCRYPT_CHAINING_MODE, (BYTE*)BCRYPT_CHAIN_MODE_GCM, sizeof(BCRYPT_CHAIN_MODE_GCM), 0);
    assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptSetProperty(BCRYPT_CHAINING_MODE)");

    BCRYPT_AUTH_TAG_LENGTHS_STRUCT authTagLengths;
    bcryptResult = BCryptGetProperty(algHandle, BCRYPT_AUTH_TAG_LENGTH, (BYTE*)&authTagLengths, sizeof(authTagLengths), &bytesDone, 0);
    assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGetProperty(BCRYPT_AUTH_TAG_LENGTH)");

    DWORD blockLength = 0;
    bcryptResult = BCryptGetProperty(algHandle, BCRYPT_BLOCK_LENGTH, (BYTE*)&blockLength, sizeof(blockLength), &bytesDone, 0);
    assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGetProperty(BCRYPT_BLOCK_LENGTH)");

    BCRYPT_KEY_HANDLE keyHandle = 0;
    {
        const std::vector<BYTE> key = MakeRandomBytes(blockLength);
        bcryptResult = BCryptGenerateSymmetricKey(algHandle, &keyHandle, 0, 0, (PUCHAR)&key[0], key.size(), 0);
        assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptGenerateSymmetricKey");
    }

    const size_t GCM_NONCE_SIZE = 12;
    const std::vector<BYTE> origNonce = MakeRandomBytes(GCM_NONCE_SIZE);
    const std::vector<BYTE> origData  = MakePatternBytes(256);

    // Encrypt data as a whole
    std::vector<BYTE> encrypted = origData;
    std::vector<BYTE> authTag(authTagLengths.dwMinLength);
    {
        BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
        BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
        authInfo.pbNonce = (PUCHAR)&origNonce[0];
        authInfo.cbNonce = origNonce.size();
        authInfo.pbTag   = &authTag[0];
        authInfo.cbTag   = authTag.size();

        bcryptResult = BCryptEncrypt
            (
            keyHandle,
            &encrypted[0], encrypted.size(),
            &authInfo,
            0, 0,
            &encrypted[0], encrypted.size(),
            &bytesDone, 0
            );

        assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptEncrypt");
        assert(bytesDone == encrypted.size());
    }

    // Decrypt data in two parts
    std::vector<BYTE> decrypted = encrypted;
    {
        DWORD partSize = decrypted.size() / 2;

        std::vector<BYTE> macContext(authTagLengths.dwMaxLength);

        BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo;
        BCRYPT_INIT_AUTH_MODE_INFO(authInfo);
        authInfo.pbNonce = (PUCHAR)&origNonce[0];
        authInfo.cbNonce = origNonce.size();
        authInfo.pbTag   = &authTag[0];
        authInfo.cbTag   = authTag.size();
        authInfo.pbMacContext = &macContext[0];
        authInfo.cbMacContext = macContext.size();

        // IV value is ignored on first call to BCryptDecrypt.
        // This buffer will be used to keep internal IV used for chaining.
        std::vector<BYTE> contextIV(blockLength);

        // First part
        authInfo.dwFlags = BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG;
        bcryptResult = BCryptDecrypt
            (
            keyHandle,
            &decrypted[0*partSize], partSize,
            &authInfo,
            &contextIV[0], contextIV.size(),
            &decrypted[0*partSize], partSize,
            &bytesDone, 0
            );

        assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptDecrypt");
        assert(bytesDone == partSize);

        // Second part
        authInfo.dwFlags &= ~BCRYPT_AUTH_MODE_CHAIN_CALLS_FLAG;
        bcryptResult = BCryptDecrypt
            (
            keyHandle,
            &decrypted[1*partSize], partSize,
            &authInfo,
            &contextIV[0], contextIV.size(),
            &decrypted[1*partSize], partSize,
            &bytesDone, 0
            );

        assert(BCRYPT_SUCCESS(bcryptResult) || !"BCryptDecrypt");
        assert(bytesDone == partSize);
    }

    // Check decryption
    assert(decrypted == origData);

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

    return 0;
}
于 2015-06-20T19:32:17.367 に答える
4

@Codeguardの回答により、私が取り組んでいたプロジェクトが完了し、そもそもこの質問/回答を見つけることができました。ただし、まだ苦労した問題がいくつかありました。以下は、注意が必要な部分を示したプロセスです。上記のリンクで実際のコードを表示できます。

  1. を使用BCryptOpenAlgorithmProviderしてアルゴリズム プロバイダを開くために使用しBCRYPT_AES_ALGORITHMます。
  2. BCryptSetPropertyをに設定するBCRYPT_CHAINING_MODEために使用しBCRYPT_CHAIN_MODE_GCMます。
  3. 暗号化/復号化操作のために BCrypt ライブラリで使用するために割り当てるをBCryptGetProperty取得するために使用します。BCRYPT_OBJECT_LENGTH実装によっては、次のことも必要になる場合があります。
    • IV のスクラッチ スペースBCryptGetPropertyを決定して割り当てるために使用します。BCRYPT_BLOCK_SIZEWindows API は呼び出しごとに IV を更新し、呼び出し元はその使用に必要なメモリを提供する責任があります。
    • 可能な限り最大のタグのスクラッチ スペースBCryptGetPropertyを決定して割り当てるために使用します。BCRYPT_AUTH_TAG_LENGTHIV と同様に、呼び出し元は、API が毎回更新するこのスペースを提供する責任があります。
  4. BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO構造体 を初期化します。
    • で構造体を初期化しますBCRYPT_INIT_AUTH_MODE_INFO()
    • pbNonceandフィールドを初期化しcbNonceます。BCryptEncrypt/への最初の呼び出しBCryptDecryptでは、IV は入力として無視され、このフィールドは「IV」として使用されることに注意してください。ただし、IV パラメータはその最初の呼び出しによって更新され、後続の呼び出しで使用されるため、そのためのスペースを提供する必要があります。さらに、フィールドpbNonceとフィールドは、 /cbNonceへのすべての呼び出しに対して設定されたままにしておく必要があります (最初の呼び出しの後は使用されません) 。BCryptEncryptBCryptDecrypt
    • 初期化pbAuthDataしてcbAuthData. 私のプロジェクトでは、 /の最初の呼び出しの直前にこれらのフィールドを設定し、その直後にBCryptEncrypt/BCryptDecryptにリセットしました。これらの呼び出し中に、入力および出力パラメーターとして/を渡すことができます。NULL0NULL0
    • 初期化pbTagしてcbTag. /タグが取得またはチェックされる最後の呼び出しまで可能ですが、設定するpbTag必要があります。NULLBCryptEncryptBCryptDecryptcbTagBCryptEncryptBCryptDecrypt
    • 初期化pbMacContextしてcbMacContext. これらは、tag/mac の現在の状態を追跡するために使用するBCryptEncrypt/のスクラッチ スペースを指します。BCryptDecrypt
    • に初期化cbAADcbDataます0。API はこれらのフィールドを使用するため、いつでも読み取ることができますが、最初に に設定した後は更新しないでください0
    • に初期化dwFlagsBCRYPT_AUTH_MODE_CHAIN_CALLS_FLAGます。|=初期化後、またはを使用してこのフィールドを変更する必要があります&=。Windows は、呼び出し元が変更しないように注意する必要があるこのフィールド内のフラグも設定します。
  5. BCryptGenerateSymmetricKey暗号化/復号化に使用するキーをインポートするために使用します。操作中に使用するために、BCRYPT_OBJECT_LENGTHこの呼び出しに関連付けられたメモリを提供する必要があることに注意してください。BCryptEncryptBCryptDecrypt
  6. BCryptEncryptある場合は、AAD を使用して/を呼び出しBCryptDecryptます。この呼び出しでは、入力も出力用のスペースも提供する必要はありません。cbAAD(呼び出しが成功すると、構造体のフィールドに反映された AAD のサイズを確認できますBCRYPT_AUTHENTICATED_CIPHER_MODE_INFO。)
    1. pbAuthDataとを設定cbAuthDataして、AAD を反映します。
    2. BCryptEncryptまたはを呼び出しBCryptDecryptます。
    3. 設定pbAuthDataして に戻りcbAuthDataます。NULL0
  7. 呼び出しBCryptEncrypt/ BCryptDecrypt"N - 1" 回
    • 各呼び出しに渡されるデータの量は、アルゴリズムのブロック サイズの倍数でなければなりません。
    • 呼び出しのパラメーターを 以外に設定しないでください。dwFlags0
    • 出力スペースは、入力のサイズ以上でなければなりません
  8. BCryptEncrypt/BCryptDecryptを最後に 1 回呼び出します (プレーン/暗号テキストの入力/出力の有無にかかわらず)。入力のサイズは、この呼び出しのアルゴリズムのブロック サイズの倍数である必要はありません。dwFlagsはまだ に設定されてい0ます。
    1. 操作が暗号化であるか復号化であるかに応じて、構造体のpbTagフィールドをBCRYPT_AUTHENTICATED_CIPHER_MODE_INFO、生成されたタグを格納する場所、または照合するタグの場所に設定します。
    2. 構文を使用して、構造体のフィールドBCRYPT_AUTH_MODE_CHAIN_CALLS_FLAGから を削除します。dwFlagsBCRYPT_AUTHENTICATED_CIPHER_MODE_INFO&=
  9. 電話BCryptDestroyKey
  10. 電話BCryptCloseAlgorithmProvider

この時点で、 に関連付けられているスペースを一掃するのが賢明BCRYPT_OBJECT_LENGTHです。

于 2020-11-16T19:29:20.333 に答える