示例#1
0
        // 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);
                                }
                            }
                        }
                    }
                }
            }
        }