// 'pbNonce' must point to a 96-bit buffer. // 'pbTag' must point to a 128-bit buffer. // 'pbEncryptedData' must point to a buffer the same length as 'pbPlaintextData'. private void DoGcmEncrypt(byte *pbKey, uint cbKey, byte *pbNonce, byte *pbPlaintextData, uint cbPlaintextData, byte *pbEncryptedData, byte *pbTag) { BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authCipherInfo; BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.Init(out authCipherInfo); authCipherInfo.pbNonce = pbNonce; authCipherInfo.cbNonce = NONCE_SIZE_IN_BYTES; authCipherInfo.pbTag = pbTag; authCipherInfo.cbTag = TAG_SIZE_IN_BYTES; using (var keyHandle = _symmetricAlgorithmHandle.GenerateSymmetricKey(pbKey, cbKey)) { uint cbResult; int ntstatus = UnsafeNativeMethods.BCryptEncrypt( hKey: keyHandle, pbInput: pbPlaintextData, cbInput: cbPlaintextData, pPaddingInfo: &authCipherInfo, pbIV: null, cbIV: 0, pbOutput: pbEncryptedData, cbOutput: cbPlaintextData, pcbResult: out cbResult, dwFlags: 0); UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); CryptoUtil.Assert(cbResult == cbPlaintextData, "cbResult == cbPlaintextData"); } }
protected override byte[] DecryptImpl(byte *pbCiphertext, uint cbCiphertext, byte *pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData) { // Argument checking: input must at the absolute minimum contain a key modifier, nonce, and tag if (cbCiphertext < KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES) { throw Error.CryptCommon_PayloadInvalid(); } // Assumption: pbCipherText := { keyModifier || nonce || encryptedData || authenticationTag } uint cbPlaintext = checked (cbCiphertext - (KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES)); byte[] retVal = new byte[cbPlaintext]; fixed(byte *pbRetVal = retVal) { // Calculate offsets byte *pbKeyModifier = pbCiphertext; byte *pbNonce = &pbKeyModifier[KEY_MODIFIER_SIZE_IN_BYTES]; byte *pbEncryptedData = &pbNonce[NONCE_SIZE_IN_BYTES]; byte *pbAuthTag = &pbEncryptedData[cbPlaintext]; // Use the KDF to recreate the symmetric block cipher key // We'll need a temporary buffer to hold the symmetric encryption subkey byte *pbSymmetricDecryptionSubkey = stackalloc byte[checked ((int)_symmetricAlgorithmSubkeyLengthInBytes)]; try { _sp800_108_ctr_hmac_provider.DeriveKeyWithContextHeader( pbLabel: pbAdditionalAuthenticatedData, cbLabel: cbAdditionalAuthenticatedData, contextHeader: _contextHeader, pbContext: pbKeyModifier, cbContext: KEY_MODIFIER_SIZE_IN_BYTES, pbDerivedKey: pbSymmetricDecryptionSubkey, cbDerivedKey: _symmetricAlgorithmSubkeyLengthInBytes); // Perform the decryption operation using (var decryptionSubkeyHandle = _symmetricAlgorithmHandle.GenerateSymmetricKey(pbSymmetricDecryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes)) { byte dummy; byte *pbPlaintext = (pbRetVal != null) ? pbRetVal : &dummy; // CLR doesn't like pinning empty buffers BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authInfo; BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.Init(out authInfo); authInfo.pbNonce = pbNonce; authInfo.cbNonce = NONCE_SIZE_IN_BYTES; authInfo.pbTag = pbAuthTag; authInfo.cbTag = TAG_SIZE_IN_BYTES; // The call to BCryptDecrypt will also validate the authentication tag uint cbDecryptedBytesWritten; int ntstatus = UnsafeNativeMethods.BCryptDecrypt( hKey: decryptionSubkeyHandle, pbInput: pbEncryptedData, cbInput: cbPlaintext, pPaddingInfo: &authInfo, pbIV: null, // IV not used; nonce provided in pPaddingInfo cbIV: 0, pbOutput: pbPlaintext, cbOutput: cbPlaintext, pcbResult: out cbDecryptedBytesWritten, dwFlags: 0); UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); CryptoUtil.Assert(cbDecryptedBytesWritten == cbPlaintext, "cbDecryptedBytesWritten == cbPlaintext"); // At this point, retVal := { decryptedPayload } // And we're done! return(retVal); } } finally { // The buffer contains key material, so delete it. UnsafeBufferUtil.SecureZeroMemory(pbSymmetricDecryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes); } } }