// Creates a provider from the given secret. public static ISP800_108_CTR_HMACSHA512Provider CreateProvider(ProtectedMemoryBlob kdk) { uint secretLengthInBytes = checked ((uint)kdk.Length); if (secretLengthInBytes == 0) { return(CreateEmptyProvider()); } else { fixed(byte *pbPlaintextSecret = new byte[secretLengthInBytes]) { try { kdk.WriteSecretIntoBuffer(pbPlaintextSecret, checked ((int)secretLengthInBytes)); return(CreateProvider(pbPlaintextSecret, secretLengthInBytes)); } finally { UnsafeBufferUtil.SecureZeroMemory(pbPlaintextSecret, secretLengthInBytes); } } } }
public byte[] Decrypt(ArraySegment <byte> protectedPayload, ArraySegment <byte> additionalAuthenticatedData) { protectedPayload.Validate(); additionalAuthenticatedData.Validate(); // Argument checking - input must at the absolute minimum contain a key modifier, IV, and MAC if (protectedPayload.Count < checked (KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes + _validationAlgorithmDigestLengthInBytes)) { throw Error.CryptCommon_PayloadInvalid(); } // Assumption: protectedPayload := { keyModifier | IV | encryptedData | MAC(IV | encryptedPayload) } try { // Step 1: Extract the key modifier and IV from the payload. int keyModifierOffset; // position in protectedPayload.Array where key modifier begins int ivOffset; // position in protectedPayload.Array where key modifier ends / IV begins int ciphertextOffset; // position in protectedPayload.Array where IV ends / ciphertext begins int macOffset; // position in protectedPayload.Array where ciphertext ends / MAC begins int eofOffset; // position in protectedPayload.Array where MAC ends checked { keyModifierOffset = protectedPayload.Offset; ivOffset = keyModifierOffset + KEY_MODIFIER_SIZE_IN_BYTES; ciphertextOffset = ivOffset + _symmetricAlgorithmBlockSizeInBytes; } ArraySegment <byte> keyModifier = new ArraySegment <byte>(protectedPayload.Array, keyModifierOffset, ivOffset - keyModifierOffset); byte[] iv = new byte[_symmetricAlgorithmBlockSizeInBytes]; Buffer.BlockCopy(protectedPayload.Array, ivOffset, iv, 0, iv.Length); // Step 2: Decrypt the KDK and use it to restore the original encryption and MAC keys. // We pin all unencrypted keys to limit their exposure via GC relocation. byte[] decryptedKdk = new byte[_keyDerivationKey.Length]; byte[] decryptionSubkey = new byte[_symmetricAlgorithmSubkeyLengthInBytes]; byte[] validationSubkey = new byte[_validationAlgorithmSubkeyLengthInBytes]; byte[] derivedKeysBuffer = new byte[checked (decryptionSubkey.Length + validationSubkey.Length)]; fixed(byte *__unused__1 = decryptedKdk) fixed(byte *__unused__2 = decryptionSubkey) fixed(byte *__unused__3 = validationSubkey) fixed(byte *__unused__4 = derivedKeysBuffer) { try { _keyDerivationKey.WriteSecretIntoBuffer(new ArraySegment <byte>(decryptedKdk)); ManagedSP800_108_CTR_HMACSHA512.DeriveKeysWithContextHeader( kdk: decryptedKdk, label: additionalAuthenticatedData, contextHeader: _contextHeader, context: keyModifier, prfFactory: _kdkPrfFactory, output: new ArraySegment <byte>(derivedKeysBuffer)); Buffer.BlockCopy(derivedKeysBuffer, 0, decryptionSubkey, 0, decryptionSubkey.Length); Buffer.BlockCopy(derivedKeysBuffer, decryptionSubkey.Length, validationSubkey, 0, validationSubkey.Length); // Step 3: Calculate the correct MAC for this payload. // correctHash := MAC(IV || ciphertext) byte[] correctHash; using (var hashAlgorithm = CreateValidationAlgorithm(validationSubkey)) { checked { eofOffset = protectedPayload.Offset + protectedPayload.Count; macOffset = eofOffset - _validationAlgorithmDigestLengthInBytes; } correctHash = hashAlgorithm.ComputeHash(protectedPayload.Array, ivOffset, macOffset - ivOffset); } // Step 4: Validate the MAC provided as part of the payload. if (!CryptoUtil.TimeConstantBuffersAreEqual(correctHash, 0, correctHash.Length, protectedPayload.Array, macOffset, eofOffset - macOffset)) { throw Error.CryptCommon_PayloadInvalid(); // integrity check failure } // Step 5: Decipher the ciphertext and return it to the caller. using (var symmetricAlgorithm = CreateSymmetricAlgorithm()) using (var cryptoTransform = symmetricAlgorithm.CreateDecryptor(decryptionSubkey, iv)) { var outputStream = new MemoryStream(); using (var cryptoStream = new CryptoStream(outputStream, cryptoTransform, CryptoStreamMode.Write)) { cryptoStream.Write(protectedPayload.Array, ciphertextOffset, macOffset - ciphertextOffset); cryptoStream.FlushFinalBlock(); // At this point, outputStream := { plaintext }, and we're done! return(outputStream.ToArray()); } } } finally { // delete since these contain secret material Array.Clear(decryptedKdk, 0, decryptedKdk.Length); Array.Clear(decryptionSubkey, 0, decryptionSubkey.Length); Array.Clear(validationSubkey, 0, validationSubkey.Length); Array.Clear(derivedKeysBuffer, 0, derivedKeysBuffer.Length); } } } catch (Exception ex) when(!(ex is CryptographicException)) { // Homogenize all exceptions to CryptographicException. throw Error.CryptCommon_GenericError(ex); } }