// Implements the KeyDerivationFunction delegate signature; public entry point to the API. public static CryptographicKey DeriveKey(CryptographicKey keyDerivationKey, Purpose purpose) { // After consultation with the crypto board, we have decided to use HMACSHA512 as the PRF // to our KDF. The reason for this is that our PRF is an HMAC, so the total entropy of the // PRF is given by MIN(key derivation key length, HMAC block size). It is conceivable that // a developer might specify a key greater than 256 bits in length, at which point using // a shorter PRF like HMACSHA256 starts discarding entropy. But from the crypto team's // perspective it is unreasonable for a developer to supply a key greater than 512 bits, // so there's no real harm in us limiting our PRF entropy to 512 bits (HMACSHA512). // // On 64-bit platforms, HMACSHA512 matches or outperforms HMACSHA256 in our perf testing. // On 32-bit platforms, HMACSHA512 is around 1/3 the speed of HMACSHA256. In both cases, we // try to cache the derived CryptographicKey wherever we can, so this shouldn't be a // bottleneck regardless. using (HMACSHA512 hmac = CryptoAlgorithms.CreateHMACSHA512(keyDerivationKey.GetKeyMaterial())) { byte[] label, context; purpose.GetKeyDerivationParameters(out label, out context); byte[] derivedKey = DeriveKeyImpl(hmac, label, context, keyDerivationKey.KeyLength); return(new CryptographicKey(derivedKey)); } }
public byte[] Protect(byte[] clearData) { // The entire operation is wrapped in a 'checked' block because any overflows should be treated as failures. checked { // These SymmetricAlgorithm instances are single-use; we wrap it in a 'using' block. using (SymmetricAlgorithm encryptionAlgorithm = _cryptoAlgorithmFactory.GetEncryptionAlgorithm()) { // Initialize the algorithm with the specified key and an appropriate IV encryptionAlgorithm.Key = _encryptionKey.GetKeyMaterial(); if (_predictableIV) { // The caller wanted the output to be predictable (e.g. for caching), so we'll create an // appropriate IV directly from the input buffer. The IV length is equal to the block size. encryptionAlgorithm.IV = CryptoUtil.CreatePredictableIV(clearData, encryptionAlgorithm.BlockSize); } else { // If the caller didn't ask for a predictable IV, just let the algorithm itself choose one. encryptionAlgorithm.GenerateIV(); } byte[] iv = encryptionAlgorithm.IV; using (MemoryStream memStream = new MemoryStream()) { memStream.Write(iv, 0, iv.Length); // At this point: // memStream := IV // Write the encrypted payload to the memory stream. using (ICryptoTransform encryptor = encryptionAlgorithm.CreateEncryptor()) { using (CryptoStream cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write)) { cryptoStream.Write(clearData, 0, clearData.Length); cryptoStream.FlushFinalBlock(); // At this point: // memStream := IV || Enc(Kenc, IV, clearData) // These KeyedHashAlgorithm instances are single-use; we wrap it in a 'using' block. using (KeyedHashAlgorithm signingAlgorithm = _cryptoAlgorithmFactory.GetValidationAlgorithm()) { // Initialize the algorithm with the specified key signingAlgorithm.Key = _validationKey.GetKeyMaterial(); // Compute the signature byte[] signature = signingAlgorithm.ComputeHash(memStream.GetBuffer(), 0, (int)memStream.Length); // At this point: // memStream := IV || Enc(Kenc, IV, clearData) // signature := Sign(Kval, IV || Enc(Kenc, IV, clearData)) // Append the signature to the encrypted payload memStream.Write(signature, 0, signature.Length); // At this point: // memStream := IV || Enc(Kenc, IV, clearData) || Sign(Kval, IV || Enc(Kenc, IV, clearData)) // Algorithm complete byte[] protectedData = memStream.ToArray(); return(protectedData); } } } } } } }