// 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[] Unprotect(byte[] protectedData) { // The entire operation is wrapped in a 'checked' block because any overflows should be treated as failures. checked { // We want to check that the input is in the form: // protectedData := IV || Enc(Kenc, IV, clearData) || Sign(Kval, IV || Enc(Kenc, IV, clearData)) // Definitions used in this method: // encryptedPayload := Enc(Kenc, IV, clearData) // signature := Sign(Kval, IV || encryptedPayload) // These SymmetricAlgorithm instances are single-use; we wrap it in a 'using' block. using (SymmetricAlgorithm decryptionAlgorithm = _cryptoAlgorithmFactory.GetEncryptionAlgorithm()) { decryptionAlgorithm.Key = _encryptionKey.GetKeyMaterial(); // These KeyedHashAlgorithm instances are single-use; we wrap it in a 'using' block. using (KeyedHashAlgorithm validationAlgorithm = _cryptoAlgorithmFactory.GetValidationAlgorithm()) { validationAlgorithm.Key = _validationKey.GetKeyMaterial(); // First, we need to verify that protectedData is even long enough to contain // the required components (IV, encryptedPayload, signature). int ivByteCount = decryptionAlgorithm.BlockSize / 8; // IV length is equal to the block size int signatureByteCount = validationAlgorithm.HashSize / 8; int encryptedPayloadByteCount = protectedData.Length - ivByteCount - signatureByteCount; if (encryptedPayloadByteCount <= 0) { // protectedData doesn't meet minimum length requirements return(null); } // If that check passes, we need to detect payload tampering. // Compute the signature over the IV and encrypted payload // computedSignature := Sign(Kval, IV || encryptedPayload) byte[] computedSignature = validationAlgorithm.ComputeHash(protectedData, 0, ivByteCount + encryptedPayloadByteCount); if (!CryptoUtil.BuffersAreEqual( buffer1: protectedData, buffer1Offset: ivByteCount + encryptedPayloadByteCount, buffer1Count: signatureByteCount, buffer2: computedSignature, buffer2Offset: 0, buffer2Count: computedSignature.Length)) { // the computed signature didn't match the incoming signature, which is a sign of payload tampering return(null); } // At this point, we're certain that we generated the signature over this payload, // so we can go ahead with decryption. // Populate the IV from the incoming stream byte[] iv = new byte[ivByteCount]; Buffer.BlockCopy(protectedData, 0, iv, 0, iv.Length); decryptionAlgorithm.IV = iv; // Write the decrypted payload to the memory stream. using (MemoryStream memStream = new MemoryStream()) { using (ICryptoTransform decryptor = decryptionAlgorithm.CreateDecryptor()) { using (CryptoStream cryptoStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Write)) { cryptoStream.Write(protectedData, ivByteCount, encryptedPayloadByteCount); cryptoStream.FlushFinalBlock(); // At this point // memStream := clearData byte[] clearData = memStream.ToArray(); return(clearData); } } } } } } }