private static BCryptKeyHandle PasswordToPbkdfKeyHandleStep2(BCryptAlgorithmHandle pbkdf2AlgHandle, byte *pbPassword, uint cbPassword, KeyDerivationPrf prf)
        {
            const uint PBKDF2_MAX_KEYLENGTH_IN_BYTES = 2048; // GetSupportedKeyLengths() on a Win8 box; value should never be lowered in any future version of Windows

            if (cbPassword <= PBKDF2_MAX_KEYLENGTH_IN_BYTES)
            {
                // Common case: the password is small enough to be consumed directly by the PBKDF2 algorithm.
                return(pbkdf2AlgHandle.GenerateSymmetricKey(pbPassword, cbPassword));
            }
            else
            {
                // Rare case: password is very long; we must hash manually.
                // PBKDF2 uses the PRFs in HMAC mode, and when the HMAC input key exceeds the hash function's
                // block length the key is hashed and run back through the key initialization function.

                BCryptAlgorithmHandle prfAlgorithmHandle; // cached; don't dispose
                switch (prf)
                {
                case KeyDerivationPrf.Sha1:
                    prfAlgorithmHandle = CachedAlgorithmHandles.SHA1;
                    break;

                case KeyDerivationPrf.Sha256:
                    prfAlgorithmHandle = CachedAlgorithmHandles.SHA256;
                    break;

                case KeyDerivationPrf.Sha512:
                    prfAlgorithmHandle = CachedAlgorithmHandles.SHA512;
                    break;

                default:
                    throw CryptoUtil.Fail("Unrecognized PRF.");
                }

                // Final sanity check: don't hash the password if the HMAC key initialization function wouldn't have done it for us.
                if (cbPassword <= prfAlgorithmHandle.GetHashBlockLength() /* in bytes */)
                {
                    return(pbkdf2AlgHandle.GenerateSymmetricKey(pbPassword, cbPassword));
                }

                // Hash the password and use the hash as input to PBKDF2.
                uint cbPasswordDigest = prfAlgorithmHandle.GetHashDigestLength();
                CryptoUtil.Assert(cbPasswordDigest > 0, "cbPasswordDigest > 0");
                fixed(byte *pbPasswordDigest = new byte[cbPasswordDigest])
                {
                    try
                    {
                        using (var hashHandle = prfAlgorithmHandle.CreateHash())
                        {
                            hashHandle.HashData(pbPassword, cbPassword, pbPasswordDigest, cbPasswordDigest);
                        }
                        return(pbkdf2AlgHandle.GenerateSymmetricKey(pbPasswordDigest, cbPasswordDigest));
                    }
                    finally
                    {
                        UnsafeBufferUtil.SecureZeroMemory(pbPasswordDigest, cbPasswordDigest);
                    }
                }
            }
        }
Exemple #2
0
        private byte[] CreateContextHeader()
        {
            var retVal = new byte[checked (
                                      1                                     /* KDF alg */
                                      + 1                                   /* chaining mode */
                                      + sizeof(uint)                        /* sym alg key size */
                                      + sizeof(uint)                        /* sym alg block size */
                                      + sizeof(uint)                        /* hmac alg key size */
                                      + sizeof(uint)                        /* hmac alg digest size */
                                      + _symmetricAlgorithmBlockSizeInBytes /* ciphertext of encrypted empty string */
                                      + _hmacAlgorithmDigestLengthInBytes /* digest of HMACed empty string */)];

            fixed(byte *pbRetVal = retVal)
            {
                byte *ptr = pbRetVal;

                // First is the two-byte header
                *(ptr++) = 0; // 0x00 = SP800-108 CTR KDF w/ HMACSHA512 PRF
                *(ptr++) = 0; // 0x00 = CBC encryption + HMAC authentication

                // Next is information about the symmetric algorithm (key size followed by block size)
                BitHelpers.WriteTo(ref ptr, _symmetricAlgorithmSubkeyLengthInBytes);
                BitHelpers.WriteTo(ref ptr, _symmetricAlgorithmBlockSizeInBytes);

                // Next is information about the HMAC algorithm (key size followed by digest size)
                BitHelpers.WriteTo(ref ptr, _hmacAlgorithmSubkeyLengthInBytes);
                BitHelpers.WriteTo(ref ptr, _hmacAlgorithmDigestLengthInBytes);

                // See the design document for an explanation of the following code.
                var tempKeys = new byte[_symmetricAlgorithmSubkeyLengthInBytes + _hmacAlgorithmSubkeyLengthInBytes];

                fixed(byte *pbTempKeys = tempKeys)
                {
                    byte dummy;

                    // Derive temporary keys for encryption + HMAC.
                    using (var provider = SP800_108_CTR_HMACSHA512Util.CreateEmptyProvider())
                    {
                        provider.DeriveKey(
                            pbLabel: &dummy,
                            cbLabel: 0,
                            pbContext: &dummy,
                            cbContext: 0,
                            pbDerivedKey: pbTempKeys,
                            cbDerivedKey: (uint)tempKeys.Length);
                    }

                    // At this point, tempKeys := { K_E || K_H }.
                    byte *pbSymmetricEncryptionSubkey = pbTempKeys;
                    byte *pbHmacSubkey = &pbTempKeys[_symmetricAlgorithmSubkeyLengthInBytes];

                    // Encrypt a zero-length input string with an all-zero IV and copy the ciphertext to the return buffer.
                    using (var symmetricKeyHandle = _symmetricAlgorithmHandle.GenerateSymmetricKey(pbSymmetricEncryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes))
                    {
                        fixed(byte *pbIV = new byte[_symmetricAlgorithmBlockSizeInBytes] /* will be zero-initialized */)
                        {
                            DoCbcEncrypt(
                                symmetricKeyHandle: symmetricKeyHandle,
                                pbIV: pbIV,
                                pbInput: &dummy,
                                cbInput: 0,
                                pbOutput: ptr,
                                cbOutput: _symmetricAlgorithmBlockSizeInBytes);
                        }
                    }
                    ptr += _symmetricAlgorithmBlockSizeInBytes;

                    // MAC a zero-length input string and copy the digest to the return buffer.
                    using (var hashHandle = _hmacAlgorithmHandle.CreateHmac(pbHmacSubkey, _hmacAlgorithmSubkeyLengthInBytes))
                    {
                        hashHandle.HashData(
                            pbInput: &dummy,
                            cbInput: 0,
                            pbHashDigest: ptr,
                            cbHashDigest: _hmacAlgorithmDigestLengthInBytes);
                    }

                    ptr += _hmacAlgorithmDigestLengthInBytes;
                    CryptoUtil.Assert(ptr - pbRetVal == retVal.Length, "ptr - pbRetVal == retVal.Length");
                }
            }

            // retVal := { version || chainingMode || symAlgKeySize || symAlgBlockSize || hmacAlgKeySize || hmacAlgDigestSize || E("") || MAC("") }.
            return(retVal);
        }
Exemple #3
0
    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 }

        var cbPlaintext = checked (cbCiphertext - (KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES));

        var 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;
                    var  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);
            }
        }
    }