title: BCryptEncrypt returns STATUS_INVALID_PARAMETER on AES-GCM
body: <p>I'm trying to implement AES GCM using <a href="https://docs.microsoft.com/en-us/windows/win32/seccng/cng-portal" rel="nofollow noreferrer">CNG Windows API</a> and stuck on last step.</p>
  <hr />
  <p><strong>Disclaimer: Do not be afraid with that amount of code, most of it is just WinAPI functions and structures declaration, scroll down to actual question text. Thanks.</strong></p>
  <hr />
  <p>Uses:</p>
  <pre class="lang-pascal prettyprint-override"><code>uses System.Classes, Winapi.Windows, System.SysUtils;
  </code></pre>
  <p>Interface section <em>(<strong>NOT</strong> everything is correct here <strong>(see <a href="https://stackoverflow.com/a/64816507/10824407">accepted answer</a>)</strong>, added just in case if somebody will try to reproduce)</em>:</p>
  <pre class="lang-pascal prettyprint-override"><code>type
    BCRYPT_KEY_LENGTHS_STRUCT = packed record
      dwMinLength, dwMaxLength, dwIncrement: ULONG;
    end;
    BCRYPT_AUTH_TAG_LENGTHS_STRUCT = BCRYPT_KEY_LENGTHS_STRUCT;
    
    BCRYPT_KEY_DATA_BLOB_HEADER = packed record
      dwMagic, dwVersion, cbKeyData: ULONG;
    end;
    
    BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO = packed record
      cbSize, dwInfoVersion: ULONG;
      pbNonce: Pointer;
      cbNonce: ULONG;
      pbAuthData: Pointer;
      cbAuthData: ULONG;
      pbTag: Pointer;
      cbTag: ULONG;
      pbMacContext: Pointer;
      cbMacContext, cbAAD: ULONG;
      cbData: ULONGLONG;
      dwFlags: ULONG;
    end;
  
  const
    BCRYPT_CHAINING_MODE = 'ChainingMode';
    BCRYPT_CHAIN_MODE_GCM = 'ChainingModeGCM';
    BCRYPT_AUTH_TAG_LENGTH = 'AuthTagLength';
    BCRYPT_KEY_DATA_BLOB = 'KeyDataBlob';
    //
    BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION = $00000001;
    //
    BCrypt = 'Bcrypt.dll';
  
  function BCryptOpenAlgorithmProvider(var phAlgorithm: Pointer;
    pszAlgId: PWideChar; pszImplementation: PWideChar; dwFlags: ULONG): DWORD;
    stdcall; external BCrypt;
  function BCryptSetProperty(hObject: Pointer; pszProperty: PWideChar;
    pbInput: Pointer; cbInput: ULONG; dwFlags: ULONG): DWORD; stdcall;
    external BCrypt;
  function BCryptGetProperty(hObject: Pointer; pszProperty: PWideChar;
    pbOutput: Pointer; cbOutput: ULONG; var pcbResult: ULONG; dwFlagd: ULONG)
    : DWORD; stdcall; external BCrypt;
  function BCryptGenerateSymmetricKey(hAlgorithm: Pointer; var phKey: Pointer;
    pbKeyObject: Pointer; cbKeyObject: ULONG; pbSecret: Pointer; cbSecret: ULONG;
    dwFlags: ULONG): DWORD; stdcall; external BCrypt;
  function BCryptGenRandom(phAlgorithm: Pointer; pbBuffer: Pointer;
    cbBuffer: ULONG; dwFlags: ULONG): DWORD; stdcall; external BCrypt;
  function BCryptExportKey(hKey: Pointer; hExportKey: Pointer;
    pszBlobType: PWideChar; pbOutput: Pointer; cbOutput: ULONG;
    var pcbResult: ULONG; dwFlags: ULONG): DWORD; stdcall; external BCrypt;
  function BCryptEncrypt(hKey: Pointer; pbInput: Pointer; cbInput: ULONG;
    pPaddingInfo: Pointer; pbIV: Pointer; cbIV: ULONG; pbOutput: Pointer;
    cbOutput: ULONG; var pcbResult: ULONG; dwFlags: ULONG): DWORD; stdcall;
  function BCryptDestroyKey(hKey: Pointer): DWORD; stdcall; external BCrypt;
  function BCryptCloseAlgorithmProvider(hAlgorithm: Pointer; dwFlags: ULONG)
    : DWORD; stdcall; external BCrypt;
  </code></pre>
  <p>Implementation:</p>
  <pre class="lang-pascal prettyprint-override"><code>function GetCryptoRandomBytes(var Buffer: TBytes; Size: DWORD): Boolean;
  var
    Status: DWORD;
    hAlgorithm, hKey: Pointer;
  begin
    result := False;
    Status := BCryptOpenAlgorithmProvider(hAlgorithm,
      BCRYPT_RNG_ALGORITHM, nil, 0);
    if Status = 0 then
    begin
      SetLength(Buffer, Size);
      Status := BCryptGenRandom(hAlgorithm, Buffer, Size, 0);
  
      if Status = 0 then
        result := True;
    end;
    BCryptCloseAlgorithmProvider(hAlgorithm, 0)
  end;
  
  
  function AESGCMEncrypt(var Data, AAD, Key, IV, Tag, EncryptedData: TBytes;
    KeyLength: DWORD = 32): Boolean;
  const
    AES_GCM_IV_LENGTH = 12;  // nonce
  var
    Status, KeyLen, BytesDone, BlockLength: DWORD;
    hAlgorithm, hKey: Pointer;
    TagLength: BCRYPT_AUTH_TAG_LENGTHS_STRUCT;
    KeyDataBlobHeader: BCRYPT_KEY_DATA_BLOB_HEADER;
    AuthCiferModeInfo: BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO;
    KeyTemp: TBytes;
  begin
    result := False;
    BytesDone := 0;
  
    Status := BCryptOpenAlgorithmProvider(hAlgorithm,
      BCRYPT_AES_ALGORITHM, nil, 0);
    if Status = 0 then
    begin
      KeyLen := Length(BCRYPT_CHAIN_MODE_GCM);
      Status := BCryptSetProperty(hAlgorithm, BCRYPT_CHAINING_MODE,
        PChar(BCRYPT_CHAIN_MODE_GCM), BytesDone, 0);
  
      if Status = 0 then
      begin
        KeyLen := SizeOf(TagLength);
        Status := BCryptGetProperty(hAlgorithm, BCRYPT_AUTH_TAG_LENGTH,
          @TagLength, KeyLen, BytesDone, 0);
  
        if (Status = 0) and GetCryptoRandomBytes(KeyTemp, KeyLength) then
        begin
          Status := BCryptGenerateSymmetricKey(hAlgorithm, hKey, nil, 0, KeyTemp,
            KeyLength, 0);
  
          if Status = 0 then
          begin
            Status := BCryptExportKey(hKey, nil, BCRYPT_KEY_DATA_BLOB, nil, 0,
              KeyLen, 0);  // Get size
  
            if Status = 0 then
            begin
              SetLength(KeyTemp, KeyLen);
              Status := BCryptExportKey(hKey, nil, BCRYPT_KEY_DATA_BLOB, KeyTemp,
                KeyLen, KeyLen, 0);
  
              if Status = 0 then
              begin
                Move(KeyTemp[0], KeyDataBlobHeader, SizeOf(KeyDataBlobHeader));
                SetLength(Key, KeyDataBlobHeader.cbKeyData);
                Move(KeyTemp[SizeOf(KeyDataBlobHeader)], Key[0],
                  KeyDataBlobHeader.cbKeyData);
  
                if GetCryptoRandomBytes(IV, AES_GCM_IV_LENGTH) then
                begin
                  SetLength(Tag, TagLength.dwMaxLength);
                  SetLength(EncryptedData, Length(Data)); // same length as source
  
                  FillChar(AuthCiferModeInfo, SizeOf(AuthCiferModeInfo), #0);
                  with AuthCiferModeInfo do
                  begin
                    cbSize := SizeOf(AuthCiferModeInfo);
                    dwInfoVersion := BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO_VERSION;
                    pbNonce := IV;
                    cbNonce := AES_GCM_IV_LENGTH;
                    pbAuthData := AAD;
                    cbAuthData := Length(AAD);
                    pbTag := Tag;
                    cbTag := TagLength.dwMaxLength;
                  end;
  
                  KeyLen := Length(Data);
                  Status := BCryptEncrypt(hKey, Data, KeyLen, @AuthCiferModeInfo,
                    nil, 0, EncryptedData, KeyLen, BytesDone, 0);
                    
                  // Status = $C000000D = STATUS_INVALID_PARAMETER
  
                  if Status = 0 then
                    result := True
                  else // Free all buffers
                  begin
                    SetLength(Tag, 0);
                    SetLength(EncryptedData, 0);
                    SetLength(Key, 0);
                    SetLength(IV, 0);
                  end;
                end
                else // Free all buffers
                begin
                  SetLength(Key, 0);
                  SetLength(IV, 0);
                end;
              end;
            end;
            BCryptDestroyKey(hKey);
          end;
          SetLength(KeyTemp, 0); // Free buffer
        end;
      end;
    end;
    BCryptCloseAlgorithmProvider(hAlgorithm, 0);
  end;
  </code></pre>
  <hr />
  <p><strong>Here actual question started</strong></p>
  <p>I know it seems like there's a lot of code, but it's not the point. All works perfectly except last <a href="https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptencrypt" rel="nofollow noreferrer"><code>BCryptEncrypt()</code></a> call which returns <code>STATUS_INVALID_PARAMETER</code> (<code>0xC000000D</code>).</p>
  <p>I've tried to follow docs, so I have no idea which of parameters doesn't fit. I guess issue is in <a href="https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_authenticated_cipher_mode_info" rel="nofollow noreferrer"><code>BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO</code></a> structure, but can't find it.</p>
  <p>So, <em>dear WinAPI Gods</em>, could you please point me what have I done wrong? I would really appreciate any help: links, debugigng ideas, etc.</p>
  <p>Here's how I call this function:</p>
  <pre class="lang-pascal prettyprint-override"><code>var
    Key, Tag, IV, AAD, Data, EncData: TBytes;
    src, add_data: string;
  begin
    src := 'test_string_1234';
    add_data := '12345678';
    Data := TEncoding.UTF8.GetBytes(src);
    AAD := TEncoding.UTF8.GetBytes(add_data);
  
    AESGCMEncrypt(Data, AAD, Key, IV, Tag, EncData);
  end;
  </code></pre>
  <p><em>P.S. I'm using Delphi 10.3 Community.</em></p>
  <hr />
  <p><strong>UPDATE</strong></p>
  <p>I've tried to check how this API works in C++ and ... just look. Here is <code>BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO</code> structure (<em>copied from <a href="https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/ns-bcrypt-bcrypt_authenticated_cipher_mode_info" rel="nofollow noreferrer">docs</a></em>):</p>
  <pre class="lang-cpp prettyprint-override"><code>typedef struct _BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO {
    ULONG     cbSize;         // 4 bytes
    ULONG     dwInfoVersion;  // 4 bytes
    PUCHAR    pbNonce;        // 8 bytes (on x64)
    ULONG     cbNonce;        // 4 bytes
    PUCHAR    pbAuthData;     // 8 bytes (on x64)
    ULONG     cbAuthData;     // 4 bytes
    PUCHAR    pbTag;          // 8 bytes (on x64)
    ULONG     cbTag;          // 4 bytes
    PUCHAR    pbMacContext;   // 8 bytes (on x64)
    ULONG     cbMacContext;   // 4 bytes
    ULONG     cbAAD;          // 4 bytes
    ULONGLONG cbData;         // 8 bytes
    ULONG     dwFlags;        // 4 bytes
  } BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO, *PBCRYPT_AUTHENTICATED_CIPHER_MODE_INFO;
  </code></pre>
  <p>Let's calculate total size: <code>4 + 4 + 8 + 4 + 8 + 4 + 8 + 4 + 8 + 4 + 4 + 8 + 4 = 72</code>. <strong>BUT</strong> <code>sizeof(BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO)</code> returns <code>88</code>. I have no clue, where 16 additional bytes came from. Could somebody explain me??</p>