From 65aa4354e5754649f5166f6e7a62053b3f8a022d Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 1 Feb 2021 01:49:36 -0800 Subject: [PATCH 01/31] Fun with spans and pointers --- eng/Dependencies.props | 1 + eng/Version.Details.xml | 4 ++ eng/Versions.props | 1 + .../Cryptography.Internal/src/CryptoUtil.cs | 11 ++++++ .../src/Cng/GcmAuthenticatedEncryptor.cs | 39 ++++++++++++++++--- 5 files changed, 51 insertions(+), 5 deletions(-) diff --git a/eng/Dependencies.props b/eng/Dependencies.props index bd6f4c69b9f4..ef2d12bfba33 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -75,6 +75,7 @@ and are generated based on the last package release. + diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 69ac6cf7bee2..3c99a51f29e8 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -238,6 +238,10 @@ https://github.com/dotnet/runtime e6f093cf0e711d9ba91b133b23a38c9a1911584d + + https://github.com/dotnet/runtime + e6f093cf0e711d9ba91b133b23a38c9a1911584d + https://github.com/dotnet/runtime e6f093cf0e711d9ba91b133b23a38c9a1911584d diff --git a/eng/Versions.props b/eng/Versions.props index 09023e4bfcef..d382c54756af 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -118,6 +118,7 @@ 6.0.0-preview.2.21080.7 6.0.0-preview.2.21080.7 + 6.0.0-preview.2.21080.7 6.0.0-preview.2.21080.7 6.0.0-preview.2.21080.7 6.0.0-preview.2.21080.7 diff --git a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs index 9fad4bdc46fb..e322533ef689 100644 --- a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs +++ b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs @@ -76,12 +76,23 @@ public static T Fail(string message) where T : class #endif public static bool TimeConstantBuffersAreEqual(byte* bufA, byte* bufB, uint count) { +#if NETSTANDARD2_0 || NET461 bool areEqual = true; for (uint i = 0; i < count; i++) { areEqual &= (bufA[i] == bufB[i]); } return areEqual; +#else + ReadOnlySpan bytesA, bytesB; + var byteCount = Convert.ToInt32(count); + unsafe + { + bytesA = new ReadOnlySpan(bufA, byteCount); + bytesB = new ReadOnlySpan(bufB, byteCount); + } + return CryptographicOperations.FixedTimeEquals(bytesA, bytesB); +#endif } [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)] diff --git a/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs index 6e47ee069481..54255649b4fb 100644 --- a/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Security.Cryptography; using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.Cryptography.Cng; using Microsoft.AspNetCore.Cryptography.SafeHandles; @@ -151,7 +152,7 @@ protected override byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byt cbDerivedKey: _symmetricAlgorithmSubkeyLengthInBytes); // Perform the decryption operation - +#if NETSTANDARD2_0 || NET461 using (var decryptionSubkeyHandle = _symmetricAlgorithmHandle.GenerateSymmetricKey(pbSymmetricDecryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes)) { byte dummy; @@ -179,11 +180,25 @@ protected override byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byt dwFlags: 0); UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); CryptoUtil.Assert(cbDecryptedBytesWritten == cbPlaintext, "cbDecryptedBytesWritten == cbPlaintext"); - - // At this point, retVal := { decryptedPayload } - // And we're done! - return retVal; } +#else + ReadOnlySpan nonce, key, encrypted, tag; + Span plaintext; + unsafe + { + nonce = new Span(pbNonce, (int)NONCE_SIZE_IN_BYTES); + key = new Span(pbSymmetricDecryptionSubkey, (int)_symmetricAlgorithmSubkeyLengthInBytes); + tag = new Span(pbAuthTag, (int)TAG_SIZE_IN_BYTES); + plaintext = new Span(retVal); + encrypted = new Span(pbEncryptedData, (int)cbPlaintext); + } + using var aes = new AesGcm(key); + aes.Decrypt(nonce, encrypted, tag, plaintext); +#endif + + // At this point, retVal := { decryptedPayload } + // And we're done! + return retVal; } finally { @@ -206,6 +221,7 @@ public override void Dispose() // 'pbEncryptedData' must point to a buffer the same length as 'pbPlaintextData'. private void DoGcmEncrypt(byte* pbKey, uint cbKey, byte* pbNonce, byte* pbPlaintextData, uint cbPlaintextData, byte* pbEncryptedData, byte* pbTag) { +#if NETSTANDARD2_0 || NET461 BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authCipherInfo; BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.Init(out authCipherInfo); authCipherInfo.pbNonce = pbNonce; @@ -230,6 +246,19 @@ private void DoGcmEncrypt(byte* pbKey, uint cbKey, byte* pbNonce, byte* pbPlaint UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); CryptoUtil.Assert(cbResult == cbPlaintextData, "cbResult == cbPlaintextData"); } +#else + Span nonce, key, plaintext, encrypted, tag; + unsafe + { + nonce = new Span(pbNonce, (int)NONCE_SIZE_IN_BYTES); + key = new Span(pbKey, (int)cbKey); + tag = new Span(pbTag, (int)TAG_SIZE_IN_BYTES); + plaintext = new Span(pbPlaintextData, (int)cbPlaintextData); + encrypted = new Span(pbEncryptedData, (int)cbPlaintextData); + } + using var aes = new AesGcm(key); + aes.Encrypt(nonce, plaintext, encrypted, tag); +#endif } protected override byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer) From d658fcca763917f69de99791bd0ca5d7a927690c Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 1 Feb 2021 10:58:07 -0800 Subject: [PATCH 02/31] Update Version.Details.xml --- eng/Version.Details.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index f91a8104bae0..1ec48e0f260e 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -240,7 +240,7 @@ https://github.com/dotnet/runtime - e6f093cf0e711d9ba91b133b23a38c9a1911584d + 0b23cdb2a6047f08966c3c033d028ed34d106cd8 https://github.com/dotnet/runtime From 4b4397561556cc7c39dd672cc41b5dea0dd28b20 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 1 Feb 2021 13:38:23 -0800 Subject: [PATCH 03/31] Cleanup --- eng/Versions.props | 1 - .../src/Cng/GcmAuthenticatedEncryptor.cs | 31 +++++++++---------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/eng/Versions.props b/eng/Versions.props index 4e616e44affd..d52be126def6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -118,7 +118,6 @@ 6.0.0-preview.2.21101.2 6.0.0-preview.2.21101.2 - 6.0.0-preview.2.21101.2 6.0.0-preview.2.21101.2 6.0.0-preview.2.21101.2 6.0.0-preview.2.21101.2 diff --git a/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs index 54255649b4fb..7dd252caf0ea 100644 --- a/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs @@ -182,18 +182,16 @@ protected override byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byt CryptoUtil.Assert(cbDecryptedBytesWritten == cbPlaintext, "cbDecryptedBytesWritten == cbPlaintext"); } #else - ReadOnlySpan nonce, key, encrypted, tag; - Span plaintext; unsafe { - nonce = new Span(pbNonce, (int)NONCE_SIZE_IN_BYTES); - key = new Span(pbSymmetricDecryptionSubkey, (int)_symmetricAlgorithmSubkeyLengthInBytes); - tag = new Span(pbAuthTag, (int)TAG_SIZE_IN_BYTES); - plaintext = new Span(retVal); - encrypted = new Span(pbEncryptedData, (int)cbPlaintext); + var nonce = new Span(pbNonce, (int)NONCE_SIZE_IN_BYTES); + var key = new Span(pbSymmetricDecryptionSubkey, (int)_symmetricAlgorithmSubkeyLengthInBytes); + var tag = new Span(pbAuthTag, (int)TAG_SIZE_IN_BYTES); + var plaintext = new Span(retVal); + var encrypted = new Span(pbEncryptedData, (int)cbPlaintext); + using var aes = new AesGcm(key); + aes.Decrypt(nonce, encrypted, tag, plaintext); } - using var aes = new AesGcm(key); - aes.Decrypt(nonce, encrypted, tag, plaintext); #endif // At this point, retVal := { decryptedPayload } @@ -247,17 +245,16 @@ private void DoGcmEncrypt(byte* pbKey, uint cbKey, byte* pbNonce, byte* pbPlaint CryptoUtil.Assert(cbResult == cbPlaintextData, "cbResult == cbPlaintextData"); } #else - Span nonce, key, plaintext, encrypted, tag; unsafe { - nonce = new Span(pbNonce, (int)NONCE_SIZE_IN_BYTES); - key = new Span(pbKey, (int)cbKey); - tag = new Span(pbTag, (int)TAG_SIZE_IN_BYTES); - plaintext = new Span(pbPlaintextData, (int)cbPlaintextData); - encrypted = new Span(pbEncryptedData, (int)cbPlaintextData); + var nonce = new Span(pbNonce, (int)NONCE_SIZE_IN_BYTES); + var key = new Span(pbKey, (int)cbKey); + var tag = new Span(pbTag, (int)TAG_SIZE_IN_BYTES); + var plaintext = new Span(pbPlaintextData, (int)cbPlaintextData); + var encrypted = new Span(pbEncryptedData, (int)cbPlaintextData); + using var aes = new AesGcm(key); + aes.Encrypt(nonce, plaintext, encrypted, tag); } - using var aes = new AesGcm(key); - aes.Encrypt(nonce, plaintext, encrypted, tag); #endif } From 0c6d717ac9808a5afc2b2a94501178c538fda4ab Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 1 Feb 2021 13:42:30 -0800 Subject: [PATCH 04/31] Flip check order --- .../Cryptography.Internal/src/CryptoUtil.cs | 16 +++---- .../src/Cng/GcmAuthenticatedEncryptor.cs | 48 +++++++++---------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs index e322533ef689..66245ff4004d 100644 --- a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs +++ b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs @@ -76,14 +76,7 @@ public static T Fail(string message) where T : class #endif public static bool TimeConstantBuffersAreEqual(byte* bufA, byte* bufB, uint count) { -#if NETSTANDARD2_0 || NET461 - bool areEqual = true; - for (uint i = 0; i < count; i++) - { - areEqual &= (bufA[i] == bufB[i]); - } - return areEqual; -#else +#if NETCOREAPP ReadOnlySpan bytesA, bytesB; var byteCount = Convert.ToInt32(count); unsafe @@ -92,6 +85,13 @@ public static bool TimeConstantBuffersAreEqual(byte* bufA, byte* bufB, uint coun bytesB = new ReadOnlySpan(bufB, byteCount); } return CryptographicOperations.FixedTimeEquals(bytesA, bytesB); +#else + bool areEqual = true; + for (uint i = 0; i < count; i++) + { + areEqual &= (bufA[i] == bufB[i]); + } + return areEqual; #endif } diff --git a/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs index 7dd252caf0ea..e8708ce2c2b9 100644 --- a/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs @@ -152,7 +152,18 @@ protected override byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byt cbDerivedKey: _symmetricAlgorithmSubkeyLengthInBytes); // Perform the decryption operation -#if NETSTANDARD2_0 || NET461 +#if NETCOREAPP + unsafe + { + var nonce = new Span(pbNonce, (int)NONCE_SIZE_IN_BYTES); + var key = new Span(pbSymmetricDecryptionSubkey, (int)_symmetricAlgorithmSubkeyLengthInBytes); + var tag = new Span(pbAuthTag, (int)TAG_SIZE_IN_BYTES); + var plaintext = new Span(retVal); + var encrypted = new Span(pbEncryptedData, (int)cbPlaintext); + using var aes = new AesGcm(key); + aes.Decrypt(nonce, encrypted, tag, plaintext); + } +#else using (var decryptionSubkeyHandle = _symmetricAlgorithmHandle.GenerateSymmetricKey(pbSymmetricDecryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes)) { byte dummy; @@ -181,17 +192,6 @@ protected override byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byt UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); CryptoUtil.Assert(cbDecryptedBytesWritten == cbPlaintext, "cbDecryptedBytesWritten == cbPlaintext"); } -#else - unsafe - { - var nonce = new Span(pbNonce, (int)NONCE_SIZE_IN_BYTES); - var key = new Span(pbSymmetricDecryptionSubkey, (int)_symmetricAlgorithmSubkeyLengthInBytes); - var tag = new Span(pbAuthTag, (int)TAG_SIZE_IN_BYTES); - var plaintext = new Span(retVal); - var encrypted = new Span(pbEncryptedData, (int)cbPlaintext); - using var aes = new AesGcm(key); - aes.Decrypt(nonce, encrypted, tag, plaintext); - } #endif // At this point, retVal := { decryptedPayload } @@ -219,7 +219,18 @@ public override void Dispose() // 'pbEncryptedData' must point to a buffer the same length as 'pbPlaintextData'. private void DoGcmEncrypt(byte* pbKey, uint cbKey, byte* pbNonce, byte* pbPlaintextData, uint cbPlaintextData, byte* pbEncryptedData, byte* pbTag) { -#if NETSTANDARD2_0 || NET461 +#if NETCOREAPP + unsafe + { + var nonce = new Span(pbNonce, (int)NONCE_SIZE_IN_BYTES); + var key = new Span(pbKey, (int)cbKey); + var tag = new Span(pbTag, (int)TAG_SIZE_IN_BYTES); + var plaintext = new Span(pbPlaintextData, (int)cbPlaintextData); + var encrypted = new Span(pbEncryptedData, (int)cbPlaintextData); + using var aes = new AesGcm(key); + aes.Encrypt(nonce, plaintext, encrypted, tag); + } +#else BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authCipherInfo; BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.Init(out authCipherInfo); authCipherInfo.pbNonce = pbNonce; @@ -244,17 +255,6 @@ private void DoGcmEncrypt(byte* pbKey, uint cbKey, byte* pbNonce, byte* pbPlaint UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); CryptoUtil.Assert(cbResult == cbPlaintextData, "cbResult == cbPlaintextData"); } -#else - unsafe - { - var nonce = new Span(pbNonce, (int)NONCE_SIZE_IN_BYTES); - var key = new Span(pbKey, (int)cbKey); - var tag = new Span(pbTag, (int)TAG_SIZE_IN_BYTES); - var plaintext = new Span(pbPlaintextData, (int)cbPlaintextData); - var encrypted = new Span(pbEncryptedData, (int)cbPlaintextData); - using var aes = new AesGcm(key); - aes.Encrypt(nonce, plaintext, encrypted, tag); - } #endif } From 4ae2bdbe6b80d1851809c7fb22ad0cc6f8f92ee1 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 2 Feb 2021 01:26:51 -0800 Subject: [PATCH 05/31] Update Versions.props --- eng/Versions.props | 1 + 1 file changed, 1 insertion(+) diff --git a/eng/Versions.props b/eng/Versions.props index d52be126def6..4e616e44affd 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -118,6 +118,7 @@ 6.0.0-preview.2.21101.2 6.0.0-preview.2.21101.2 + 6.0.0-preview.2.21101.2 6.0.0-preview.2.21101.2 6.0.0-preview.2.21101.2 6.0.0-preview.2.21101.2 From fba0a997f24739f386b3c0e7f74251f7c8cb4ea2 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 2 Feb 2021 01:46:49 -0800 Subject: [PATCH 06/31] Cleanup move decls into unsafe block --- .../Cryptography.Internal/src/CryptoUtil.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs index 66245ff4004d..e6685c776a50 100644 --- a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs +++ b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs @@ -77,14 +77,13 @@ public static T Fail(string message) where T : class public static bool TimeConstantBuffersAreEqual(byte* bufA, byte* bufB, uint count) { #if NETCOREAPP - ReadOnlySpan bytesA, bytesB; - var byteCount = Convert.ToInt32(count); unsafe { - bytesA = new ReadOnlySpan(bufA, byteCount); - bytesB = new ReadOnlySpan(bufB, byteCount); + var byteCount = Convert.ToInt32(count); + var bytesA = new ReadOnlySpan(bufA, byteCount); + var bytesB = new ReadOnlySpan(bufB, byteCount); + return CryptographicOperations.FixedTimeEquals(bytesA, bytesB); } - return CryptographicOperations.FixedTimeEquals(bytesA, bytesB); #else bool areEqual = true; for (uint i = 0; i < count; i++) From b97ecc0fa8d3d38d8d4ac7c89ce6f12359a6e007 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 2 Feb 2021 09:37:00 -0800 Subject: [PATCH 07/31] Update overload --- .../Cryptography.Internal/src/CryptoUtil.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs index e6685c776a50..487ee0ea99bc 100644 --- a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs +++ b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs @@ -101,12 +101,21 @@ public static bool TimeConstantBuffersAreEqual(byte[] bufA, int offsetA, int cou // An error at the call site isn't usable for timing attacks. Assert(countA == countB, "countA == countB"); +#if NETCOREAPP + unsafe + { + return CryptographicOperations.FixedTimeEquals( + ((Span)bufA).Slice(start: offsetA, length: countA), + ((Span)bufB).Slice(start: offsetB, length: countB)); + } +#else bool areEqual = true; for (int i = 0; i < countA; i++) { areEqual &= (bufA[offsetA + i] == bufB[offsetB + i]); } return areEqual; +#endif } } } From 2f766300ee61eb0771cecebe6db521bf64dc091f Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 2 Feb 2021 09:47:18 -0800 Subject: [PATCH 08/31] Update Version.Details.xml --- eng/Version.Details.xml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 1ec48e0f260e..9d36e42a6841 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -238,10 +238,6 @@ https://github.com/dotnet/runtime 0b23cdb2a6047f08966c3c033d028ed34d106cd8 - - https://github.com/dotnet/runtime - 0b23cdb2a6047f08966c3c033d028ed34d106cd8 - https://github.com/dotnet/runtime 0b23cdb2a6047f08966c3c033d028ed34d106cd8 From b81c9d455b736cc6e8283b146bbac5a46737a400 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 2 Feb 2021 09:47:35 -0800 Subject: [PATCH 09/31] Update Versions.props --- eng/Versions.props | 1 - 1 file changed, 1 deletion(-) diff --git a/eng/Versions.props b/eng/Versions.props index 4e616e44affd..d52be126def6 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -118,7 +118,6 @@ 6.0.0-preview.2.21101.2 6.0.0-preview.2.21101.2 - 6.0.0-preview.2.21101.2 6.0.0-preview.2.21101.2 6.0.0-preview.2.21101.2 6.0.0-preview.2.21101.2 From 717e30347de47d69aec7da466cd3a242c3d8053d Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 2 Feb 2021 10:14:46 -0800 Subject: [PATCH 10/31] Update src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs Co-authored-by: Pranav K --- src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs index 487ee0ea99bc..d919102b29c2 100644 --- a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs +++ b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs @@ -105,7 +105,7 @@ public static bool TimeConstantBuffersAreEqual(byte[] bufA, int offsetA, int cou unsafe { return CryptographicOperations.FixedTimeEquals( - ((Span)bufA).Slice(start: offsetA, length: countA), + bufA.AsSpan(start: offsetA, length: countA), ((Span)bufB).Slice(start: offsetB, length: countB)); } #else From d9f33b9bcf6a8683d9f8cef5dab6d4dea36bb326 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 2 Feb 2021 10:15:00 -0800 Subject: [PATCH 11/31] Update eng/Dependencies.props Co-authored-by: Pranav K --- eng/Dependencies.props | 1 - 1 file changed, 1 deletion(-) diff --git a/eng/Dependencies.props b/eng/Dependencies.props index ef2d12bfba33..bd6f4c69b9f4 100644 --- a/eng/Dependencies.props +++ b/eng/Dependencies.props @@ -75,7 +75,6 @@ and are generated based on the last package release. - From b1a59e8c70e53dbe1784ddac6a9fea55565fa56c Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Tue, 2 Feb 2021 10:15:30 -0800 Subject: [PATCH 12/31] Update CryptoUtil.cs --- src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs index d919102b29c2..bf87fc52ad7f 100644 --- a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs +++ b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs @@ -106,7 +106,7 @@ public static bool TimeConstantBuffersAreEqual(byte[] bufA, int offsetA, int cou { return CryptographicOperations.FixedTimeEquals( bufA.AsSpan(start: offsetA, length: countA), - ((Span)bufB).Slice(start: offsetB, length: countB)); + bufB.AsSpan(start: offsetB, length: countB)); } #else bool areEqual = true; From 37e4aa20bd09ac24cf232d1bf853459eee8f04b5 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 5 Feb 2021 02:50:27 -0800 Subject: [PATCH 13/31] Renames and add managed aesgcm encryptor --- .../AuthenticatedEncryptorFactory.cs | 7 +- .../CngGcmAuthenticatedEncryptorFactory.cs | 6 +- ...tor.cs => CngGcmAuthenticatedEncryptor.cs} | 11 +- .../Managed/AesGcmAuthenticatedEncryptor.cs | 233 ++++++++++++++++++ ...CngGcmAuthenticatedEncryptorFactoryTest.cs | 2 +- .../AuthenticatedEncryptorDescriptorTests.cs | 2 +- .../Cng/GcmAuthenticatedEncryptorTests.cs | 6 +- 7 files changed, 255 insertions(+), 12 deletions(-) rename src/DataProtection/DataProtection/src/Cng/{GcmAuthenticatedEncryptor.cs => CngGcmAuthenticatedEncryptor.cs} (96%) create mode 100644 src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs diff --git a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs index 151074ed205e..24012025bcf9 100644 --- a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs +++ b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs @@ -10,6 +10,7 @@ using Microsoft.AspNetCore.Cryptography.Cng; using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; using Microsoft.AspNetCore.DataProtection.KeyManagement; +using Microsoft.AspNetCore.DataProtection.Managed; using Microsoft.Extensions.Logging; namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption @@ -54,6 +55,9 @@ public AuthenticatedEncryptorFactory(ILoggerFactory loggerFactory) if (IsGcmAlgorithm(authenticatedConfiguration.EncryptionAlgorithm)) { +#if NETCOREAPP + return new AesGcmAuthenticatedEncryptor(new Secret(secret), GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm)); +#else // GCM requires CNG, and CNG is only supported on Windows. if (!OSVersionUtil.IsWindows()) { @@ -69,10 +73,11 @@ public AuthenticatedEncryptorFactory(ILoggerFactory loggerFactory) }; return new CngGcmAuthenticatedEncryptorFactory(_loggerFactory).CreateAuthenticatedEncryptorInstance(secret, configuration); +#endif } else { - if (OSVersionUtil.IsWindows()) + if (OSVersionUtil.IsWindows() && !IsGcmAlgorithm(authenticatedConfiguration.EncryptionAlgorithm)) { Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); // CNG preferred over managed implementations if running on Windows diff --git a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/CngGcmAuthenticatedEncryptorFactory.cs b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/CngGcmAuthenticatedEncryptorFactory.cs index f666b074c24d..ef291eaf708d 100644 --- a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/CngGcmAuthenticatedEncryptorFactory.cs +++ b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/CngGcmAuthenticatedEncryptorFactory.cs @@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption { /// - /// An for . + /// An for . /// public sealed class CngGcmAuthenticatedEncryptorFactory : IAuthenticatedEncryptorFactory { @@ -48,7 +48,7 @@ public CngGcmAuthenticatedEncryptorFactory(ILoggerFactory loggerFactory) [SupportedOSPlatform("windows")] [return: NotNullIfNotNull("configuration")] - internal GcmAuthenticatedEncryptor? CreateAuthenticatedEncryptorInstance( + internal CngGcmAuthenticatedEncryptor? CreateAuthenticatedEncryptorInstance( ISecret secret, CngGcmAuthenticatedEncryptorConfiguration configuration) { @@ -57,7 +57,7 @@ public CngGcmAuthenticatedEncryptorFactory(ILoggerFactory loggerFactory) return null; } - return new GcmAuthenticatedEncryptor( + return new CngGcmAuthenticatedEncryptor( keyDerivationKey: new Secret(secret), symmetricAlgorithmHandle: GetSymmetricBlockCipherAlgorithmHandle(configuration), symmetricAlgorithmKeySizeInBytes: (uint)(configuration.EncryptionAlgorithmKeySize / 8)); diff --git a/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs similarity index 96% rename from src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs rename to src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs index e8708ce2c2b9..39f318963972 100644 --- a/src/DataProtection/DataProtection/src/Cng/GcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Linq.Expressions; using System.Security.Cryptography; using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.Cryptography.Cng; @@ -22,7 +23,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng // going to the IV. This means that we'll only hit the 2^-32 probability limit after 2^96 encryption // operations, which will realistically never happen. (At the absurd rate of one encryption operation // per nanosecond, it would still take 180 times the age of the universe to hit 2^96 operations.) - internal unsafe sealed class GcmAuthenticatedEncryptor : CngAuthenticatedEncryptorBase + internal unsafe sealed class CngGcmAuthenticatedEncryptor : CngAuthenticatedEncryptorBase { // Having a key modifier ensures with overwhelming probability that no two encryption operations // will ever derive the same (encryption subkey, MAC subkey) pair. This limits an attacker's @@ -39,7 +40,7 @@ internal unsafe sealed class GcmAuthenticatedEncryptor : CngAuthenticatedEncrypt private readonly BCryptAlgorithmHandle _symmetricAlgorithmHandle; private readonly uint _symmetricAlgorithmSubkeyLengthInBytes; - public GcmAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHandle symmetricAlgorithmHandle, uint symmetricAlgorithmKeySizeInBytes, IBCryptGenRandom? genRandom = null) + public CngGcmAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHandle symmetricAlgorithmHandle, uint symmetricAlgorithmKeySizeInBytes, IBCryptGenRandom? genRandom = null) { // Is the key size appropriate? AlgorithmAssert.IsAllowableSymmetricAlgorithmKeySize(checked(symmetricAlgorithmKeySizeInBytes * 8)); @@ -278,7 +279,11 @@ protected override byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* // Use the KDF to generate a new symmetric block cipher key // We'll need a temporary buffer to hold the symmetric encryption subkey - byte* pbSymmetricEncryptionSubkey = stackalloc byte[checked((int)_symmetricAlgorithmSubkeyLengthInBytes)]; + var subKey = new byte[checked((int)_symmetricAlgorithmSubkeyLengthInBytes)]; + + //byte* pbSymmetricEncryptionSubkey = stackalloc byte[checked((int)_symmetricAlgorithmSubkeyLengthInBytes)]; + + fixed (byte* pbSymmetricEncryptionSubkey = subKey) try { _sp800_108_ctr_hmac_provider.DeriveKeyWithContextHeader( diff --git a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs new file mode 100644 index 000000000000..bb05043bb45f --- /dev/null +++ b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs @@ -0,0 +1,233 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Security.Cryptography; +using Microsoft.AspNetCore.Cryptography; +using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption; +using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel; +using Microsoft.AspNetCore.DataProtection.SP800_108; + +namespace Microsoft.AspNetCore.DataProtection.Managed +{ +#if NETCOREAPP + // An encryptor uses AesGcm to do encryption + internal unsafe sealed class AesGcmAuthenticatedEncryptor : IOptimizedAuthenticatedEncryptor, IDisposable + { + // Having a key modifier ensures with overwhelming probability that no two encryption operations + // will ever derive the same (encryption subkey, MAC subkey) pair. This limits an attacker's + // ability to mount a key-dependent chosen ciphertext attack. See also the class-level comment + // for how this is used to overcome GCM's IV limitations. + private const int KEY_MODIFIER_SIZE_IN_BYTES = 128 / 8; + + private const int NONCE_SIZE_IN_BYTES = 96 / 8; // GCM has a fixed 96-bit IV + private const int TAG_SIZE_IN_BYTES = 128 / 8; // we're hardcoding a 128-bit authentication tag size + + // 128 "00-01-00-00-00-10-00-00-00-0C-00-00-00-10-00-00-00-10-95-7C-50-FF-69-2E-38-8B-9A-D5-C7-68-9E-4B-9E-2B" + private static readonly byte[] AES_128_GCM_Header = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x95, 0x7C, 0x50, 0xFF, 0x69, 0x2E-38, 0x8B, 0x9A, 0xD5, 0xC7, 0x68, 0x9E, 0x4B, 0x9E, 0x2B }; + + // 192 "00-01-00-00-00-18-00-00-00-0C-00-00-00-10-00-00-00-10-0D-AA-01-3A-95-0A-DA-2B-79-8F-5F-F2-72-FA-D3-63" + private static readonly byte[] AES_192_GCM_Header = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x0D, 0xAA, 0x01, 0x3A, 0x95, 0x0A, 0xDA, 0x2B, 0x79, 0x8F, 0x5F, 0xF2, 0x72, 0xFA, 0xD3, 0x63 }; + + // 256 00-01-00-00-00-20-00-00-00-0C-00-00-00-10-00-00-00-10-E7-DC-CE-66-DF-85-5A-32-3A-6B-B7-BD-7A-59-BE-45 + private static readonly byte[] AES_256_GCM_Header = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0xE7, 0xDC, 0xCE, 0x66, 0xDF, 0x85, 0x5A, 0x32, 0x3A, 0x6B, 0xB7, 0xBD, 0x7A, 0x59, 0xBE, 0x45 }; + + private static readonly Func _kdkPrfFactory = key => new HMACSHA512(key); // currently hardcoded to SHA512 + + private readonly byte[] _contextHeader; + + private readonly Secret _keyDerivationKey; + private readonly int _derivedkeySizeInBytes; + private readonly IManagedGenRandom _genRandom; + + public AesGcmAuthenticatedEncryptor(Secret keyDerivationKey, int derivedKeySizesInBytes, IManagedGenRandom? genRandom = null) + { + _keyDerivationKey = keyDerivationKey; + _derivedkeySizeInBytes = derivedKeySizesInBytes / 8; + + switch (derivedKeySizesInBytes) + { + case 128: + _contextHeader = AES_128_GCM_Header; + break; + case 192: + _contextHeader = AES_128_GCM_Header; + break; + case 256: + _contextHeader = AES_128_GCM_Header; + break; + default: + throw Error.CryptCommon_GenericError(); // should never happen + } + + _genRandom = genRandom ?? ManagedGenRandomImpl.Instance; + } + + public byte[] Decrypt(ArraySegment ciphertext, ArraySegment additionalAuthenticatedData) + { + ciphertext.Validate(); + additionalAuthenticatedData.Validate(); + + // Argument checking: input must at the absolute minimum contain a key modifier, nonce, and tag + if (ciphertext.Count < KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES) + { + throw Error.CryptCommon_PayloadInvalid(); + } + + // Assumption: pbCipherText := { keyModifier || nonce || encryptedData || authenticationTag } + var plaintextBytes = ciphertext.Count - (KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES); + var plaintext = new byte[plaintextBytes]; + + try + { + // Step 1: Extract the key modifier from the payload. + + int keyModifierOffset; // position in ciphertext.Array where key modifier begins + int nonceOffset; // position in ciphertext.Array where key modifier ends / nonce begins + int encryptedDataOffset; // position in ciphertext.Array where nonce ends / encryptedData begins + int tagOffset; // position in ciphertext.Array where encrypted data ends + + checked + { + keyModifierOffset = ciphertext.Offset; + nonceOffset = keyModifierOffset + KEY_MODIFIER_SIZE_IN_BYTES; + encryptedDataOffset = nonceOffset + NONCE_SIZE_IN_BYTES; + tagOffset = encryptedDataOffset + plaintextBytes; + } + + var keyModifier = new ArraySegment(ciphertext.Array!, keyModifierOffset, KEY_MODIFIER_SIZE_IN_BYTES); + + // 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. + + var decryptedKdk = new byte[_keyDerivationKey.Length]; + var derivedKey = new byte[_derivedkeySizeInBytes]; + + fixed (byte* __unused__1 = decryptedKdk) + fixed (byte* __unused__2 = derivedKey) + { + try + { + _keyDerivationKey.WriteSecretIntoBuffer(new ArraySegment(decryptedKdk)); + ManagedSP800_108_CTR_HMACSHA512.DeriveKeysWithContextHeader( + kdk: decryptedKdk, + label: additionalAuthenticatedData, + contextHeader: _contextHeader, + context: keyModifier, + prfFactory: _kdkPrfFactory, + output: new ArraySegment(derivedKey)); + + // Perform the decryption operation + unsafe + { + var nonce = new Span(ciphertext.Array, nonceOffset, NONCE_SIZE_IN_BYTES); + var tag = new Span(ciphertext.Array, tagOffset, TAG_SIZE_IN_BYTES); + var encrypted = new Span(ciphertext.Array, encryptedDataOffset, plaintextBytes); + using var aes = new AesGcm(derivedKey); + aes.Decrypt(nonce, encrypted, tag, plaintext); + return plaintext; + } + } + finally + { + // delete since these contain secret material + Array.Clear(decryptedKdk, 0, decryptedKdk.Length); + Array.Clear(derivedKey, 0, derivedKey.Length); + } + } + } + catch (Exception ex) when (ex.RequiresHomogenization()) + { + // Homogenize all exceptions to CryptographicException. + throw Error.CryptCommon_GenericError(ex); + } + } + + public byte[] Encrypt(ArraySegment plaintext, ArraySegment additionalAuthenticatedData, uint preBufferSize, uint postBufferSize) + { + plaintext.Validate(); + additionalAuthenticatedData.Validate(); + + try + { + // Allocate a buffer to hold the key modifier, nonce, encrypted data, and tag. + // In GCM, the encrypted output will be the same length as the plaintext input. + var retVal = new byte[checked(preBufferSize + KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + plaintext.Count + TAG_SIZE_IN_BYTES + postBufferSize)]; + fixed (byte* pbRetVal = retVal) + { + int keyModifierOffset; // position in ciphertext.Array where key modifier begins + int nonceOffset; // position in ciphertext.Array where key modifier ends / nonce begins + int encryptedDataOffset; // position in ciphertext.Array where nonce ends / encryptedData begins + int tagOffset; // position in ciphertext.Array where encrypted data ends + + checked + { + keyModifierOffset = plaintext.Offset + (int)preBufferSize; + nonceOffset = keyModifierOffset + KEY_MODIFIER_SIZE_IN_BYTES; + encryptedDataOffset = nonceOffset + NONCE_SIZE_IN_BYTES; + tagOffset = encryptedDataOffset + plaintext.Count; + } + + // Randomly generate the key modifier and nonce + var keyModifierNonce = _genRandom.GenRandom(KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES); + + Buffer.BlockCopy(keyModifierNonce, 0, retVal, (int)preBufferSize, keyModifierNonce.Length); + + // At this point, retVal := { preBuffer | keyModifier | nonce | _____ | _____ | postBuffer } + + // Use the KDF to generate a new symmetric block cipher key + // We'll need a temporary buffer to hold the symmetric encryption subkey + var decryptedKdk = new byte[_keyDerivationKey.Length]; + var derivedKey = new byte[_derivedkeySizeInBytes]; + fixed (byte* __unused__1 = decryptedKdk) + fixed (byte* __unused__2 = derivedKey) + { + try + { + _keyDerivationKey.WriteSecretIntoBuffer(new ArraySegment(decryptedKdk)); + ManagedSP800_108_CTR_HMACSHA512.DeriveKeysWithContextHeader( + kdk: decryptedKdk, + label: additionalAuthenticatedData, + contextHeader: _contextHeader, + context: keyModifierNonce, + prfFactory: _kdkPrfFactory, + output: new ArraySegment(derivedKey)); + + // do gcm + var nonce = new Span(retVal, nonceOffset, NONCE_SIZE_IN_BYTES); + var tag = new Span(retVal, tagOffset, TAG_SIZE_IN_BYTES); + var encrypted = new Span(retVal, encryptedDataOffset, plaintext.Count); + using var aes = new AesGcm(derivedKey); + aes.Encrypt(nonce, plaintext, encrypted, tag); + + // At this point, retVal := { preBuffer | keyModifier | nonce | encryptedData | authenticationTag | postBuffer } + // And we're done! + return retVal; + } + finally + { + // delete since these contain secret material + Array.Clear(decryptedKdk, 0, decryptedKdk.Length); + Array.Clear(derivedKey, 0, derivedKey.Length); + } + } + } + } + catch (Exception ex) when (ex.RequiresHomogenization()) + { + // Homogenize all exceptions to CryptographicException. + throw Error.CryptCommon_GenericError(ex); + } + } + + public byte[] Encrypt(ArraySegment plaintext, ArraySegment additionalAuthenticatedData) + => Encrypt(plaintext, additionalAuthenticatedData, 0, 0); + + public void Dispose() + { + _keyDerivationKey.Dispose(); + } + } +#endif + } diff --git a/src/DataProtection/DataProtection/test/AuthenticatedEncryption/CngGcmAuthenticatedEncryptorFactoryTest.cs b/src/DataProtection/DataProtection/test/AuthenticatedEncryption/CngGcmAuthenticatedEncryptorFactoryTest.cs index 2a6fbcf307fe..ef6b3fa5663c 100644 --- a/src/DataProtection/DataProtection/test/AuthenticatedEncryption/CngGcmAuthenticatedEncryptorFactoryTest.cs +++ b/src/DataProtection/DataProtection/test/AuthenticatedEncryption/CngGcmAuthenticatedEncryptorFactoryTest.cs @@ -46,7 +46,7 @@ public void CreateEncrptorInstance_ExpectedDescriptorType_ReturnsEncryptor() // Assert Assert.NotNull(encryptor); - Assert.IsType(encryptor); + Assert.IsType(encryptor); } } } diff --git a/src/DataProtection/DataProtection/test/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorDescriptorTests.cs b/src/DataProtection/DataProtection/test/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorDescriptorTests.cs index c268589b27ac..b5c7794f70f7 100644 --- a/src/DataProtection/DataProtection/test/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorDescriptorTests.cs +++ b/src/DataProtection/DataProtection/test/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorDescriptorTests.cs @@ -62,7 +62,7 @@ public void CreateAuthenticatedEncryptor_RoundTripsData_CngGcmImplementation(Enc // Arrange var masterKey = Secret.Random(512 / 8); - var control = new GcmAuthenticatedEncryptor( + var control = new CngGcmAuthenticatedEncryptor( keyDerivationKey: masterKey, symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: (uint)(keyLengthInBits / 8)); diff --git a/src/DataProtection/DataProtection/test/Cng/GcmAuthenticatedEncryptorTests.cs b/src/DataProtection/DataProtection/test/Cng/GcmAuthenticatedEncryptorTests.cs index 2a5b3d465f9a..fa245d72cb47 100644 --- a/src/DataProtection/DataProtection/test/Cng/GcmAuthenticatedEncryptorTests.cs +++ b/src/DataProtection/DataProtection/test/Cng/GcmAuthenticatedEncryptorTests.cs @@ -20,7 +20,7 @@ public void Encrypt_Decrypt_RoundTrips() { // Arrange Secret kdk = new Secret(new byte[512 / 8]); - GcmAuthenticatedEncryptor encryptor = new GcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 256 / 8); + CngGcmAuthenticatedEncryptor encryptor = new CngGcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 256 / 8); ArraySegment plaintext = new ArraySegment(Encoding.UTF8.GetBytes("plaintext")); ArraySegment aad = new ArraySegment(Encoding.UTF8.GetBytes("aad")); @@ -38,7 +38,7 @@ public void Encrypt_Decrypt_Tampering_Fails() { // Arrange Secret kdk = new Secret(new byte[512 / 8]); - GcmAuthenticatedEncryptor encryptor = new GcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 256 / 8); + CngGcmAuthenticatedEncryptor encryptor = new CngGcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 256 / 8); ArraySegment plaintext = new ArraySegment(Encoding.UTF8.GetBytes("plaintext")); ArraySegment aad = new ArraySegment(Encoding.UTF8.GetBytes("aad")); byte[] validCiphertext = encryptor.Encrypt(plaintext, aad); @@ -82,7 +82,7 @@ public void Encrypt_KnownKey() { // Arrange Secret kdk = new Secret(Encoding.UTF8.GetBytes("master key")); - GcmAuthenticatedEncryptor encryptor = new GcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 128 / 8, genRandom: new SequentialGenRandom()); + CngGcmAuthenticatedEncryptor encryptor = new CngGcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 128 / 8, genRandom: new SequentialGenRandom()); ArraySegment plaintext = new ArraySegment(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, 2, 3); ArraySegment aad = new ArraySegment(new byte[] { 7, 6, 5, 4, 3, 2, 1, 0 }, 1, 4); From 98ac949b2ab14072f37633b2b7c0bfc9ddd30b58 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 5 Feb 2021 03:00:17 -0800 Subject: [PATCH 14/31] Cleanup --- .../src/Cng/CngGcmAuthenticatedEncryptor.cs | 29 ------------------- 1 file changed, 29 deletions(-) diff --git a/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs index 39f318963972..ea45ef18e8a0 100644 --- a/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs @@ -1,9 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Linq.Expressions; -using System.Security.Cryptography; using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.Cryptography.Cng; using Microsoft.AspNetCore.Cryptography.SafeHandles; @@ -153,18 +150,6 @@ protected override byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byt cbDerivedKey: _symmetricAlgorithmSubkeyLengthInBytes); // Perform the decryption operation -#if NETCOREAPP - unsafe - { - var nonce = new Span(pbNonce, (int)NONCE_SIZE_IN_BYTES); - var key = new Span(pbSymmetricDecryptionSubkey, (int)_symmetricAlgorithmSubkeyLengthInBytes); - var tag = new Span(pbAuthTag, (int)TAG_SIZE_IN_BYTES); - var plaintext = new Span(retVal); - var encrypted = new Span(pbEncryptedData, (int)cbPlaintext); - using var aes = new AesGcm(key); - aes.Decrypt(nonce, encrypted, tag, plaintext); - } -#else using (var decryptionSubkeyHandle = _symmetricAlgorithmHandle.GenerateSymmetricKey(pbSymmetricDecryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes)) { byte dummy; @@ -193,7 +178,6 @@ protected override byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byt UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); CryptoUtil.Assert(cbDecryptedBytesWritten == cbPlaintext, "cbDecryptedBytesWritten == cbPlaintext"); } -#endif // At this point, retVal := { decryptedPayload } // And we're done! @@ -220,18 +204,6 @@ public override void Dispose() // 'pbEncryptedData' must point to a buffer the same length as 'pbPlaintextData'. private void DoGcmEncrypt(byte* pbKey, uint cbKey, byte* pbNonce, byte* pbPlaintextData, uint cbPlaintextData, byte* pbEncryptedData, byte* pbTag) { -#if NETCOREAPP - unsafe - { - var nonce = new Span(pbNonce, (int)NONCE_SIZE_IN_BYTES); - var key = new Span(pbKey, (int)cbKey); - var tag = new Span(pbTag, (int)TAG_SIZE_IN_BYTES); - var plaintext = new Span(pbPlaintextData, (int)cbPlaintextData); - var encrypted = new Span(pbEncryptedData, (int)cbPlaintextData); - using var aes = new AesGcm(key); - aes.Encrypt(nonce, plaintext, encrypted, tag); - } -#else BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO authCipherInfo; BCRYPT_AUTHENTICATED_CIPHER_MODE_INFO.Init(out authCipherInfo); authCipherInfo.pbNonce = pbNonce; @@ -256,7 +228,6 @@ private void DoGcmEncrypt(byte* pbKey, uint cbKey, byte* pbNonce, byte* pbPlaint UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); CryptoUtil.Assert(cbResult == cbPlaintextData, "cbResult == cbPlaintextData"); } -#endif } protected override byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* pbAdditionalAuthenticatedData, uint cbAdditionalAuthenticatedData, uint cbPreBuffer, uint cbPostBuffer) From a85582cfb4e5569bcc70e64530fb4a21d3916dca Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 5 Feb 2021 03:05:26 -0800 Subject: [PATCH 15/31] Fixup --- .../AuthenticatedEncryptorFactory.cs | 2 +- .../src/Cng/CngGcmAuthenticatedEncryptor.cs | 62 +++++++++---------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs index 24012025bcf9..4653aea9b4b7 100644 --- a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs +++ b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs @@ -77,7 +77,7 @@ public AuthenticatedEncryptorFactory(ILoggerFactory loggerFactory) } else { - if (OSVersionUtil.IsWindows() && !IsGcmAlgorithm(authenticatedConfiguration.EncryptionAlgorithm)) + if (OSVersionUtil.IsWindows()) { Debug.Assert(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)); // CNG preferred over managed implementations if running on Windows diff --git a/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs index ea45ef18e8a0..3667d9f79915 100644 --- a/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs @@ -42,7 +42,7 @@ public CngGcmAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHand // Is the key size appropriate? AlgorithmAssert.IsAllowableSymmetricAlgorithmKeySize(checked(symmetricAlgorithmKeySizeInBytes * 8)); CryptoUtil.Assert(symmetricAlgorithmHandle.GetCipherBlockLength() == 128 / 8, "GCM requires a block cipher algorithm with a 128-bit block size."); - + _genRandom = genRandom ?? BCryptGenRandomImpl.Instance; _sp800_108_ctr_hmac_provider = SP800_108_CTR_HMACSHA512Util.CreateProvider(keyDerivationKey); _symmetricAlgorithmHandle = symmetricAlgorithmHandle; @@ -255,36 +255,36 @@ protected override byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* //byte* pbSymmetricEncryptionSubkey = stackalloc byte[checked((int)_symmetricAlgorithmSubkeyLengthInBytes)]; fixed (byte* pbSymmetricEncryptionSubkey = subKey) - try - { - _sp800_108_ctr_hmac_provider.DeriveKeyWithContextHeader( - pbLabel: pbAdditionalAuthenticatedData, - cbLabel: cbAdditionalAuthenticatedData, - contextHeader: _contextHeader, - pbContext: pbKeyModifier, - cbContext: KEY_MODIFIER_SIZE_IN_BYTES, - pbDerivedKey: pbSymmetricEncryptionSubkey, - cbDerivedKey: _symmetricAlgorithmSubkeyLengthInBytes); - - // Perform the encryption operation - DoGcmEncrypt( - pbKey: pbSymmetricEncryptionSubkey, - cbKey: _symmetricAlgorithmSubkeyLengthInBytes, - pbNonce: pbNonce, - pbPlaintextData: pbPlaintext, - cbPlaintextData: cbPlaintext, - pbEncryptedData: pbEncryptedData, - pbTag: pbAuthTag); - - // At this point, retVal := { preBuffer | keyModifier | nonce | encryptedData | authenticationTag | postBuffer } - // And we're done! - return retVal; - } - finally - { - // The buffer contains key material, so delete it. - UnsafeBufferUtil.SecureZeroMemory(pbSymmetricEncryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes); - } + try + { + _sp800_108_ctr_hmac_provider.DeriveKeyWithContextHeader( + pbLabel: pbAdditionalAuthenticatedData, + cbLabel: cbAdditionalAuthenticatedData, + contextHeader: _contextHeader, + pbContext: pbKeyModifier, + cbContext: KEY_MODIFIER_SIZE_IN_BYTES, + pbDerivedKey: pbSymmetricEncryptionSubkey, + cbDerivedKey: _symmetricAlgorithmSubkeyLengthInBytes); + + // Perform the encryption operation + DoGcmEncrypt( + pbKey: pbSymmetricEncryptionSubkey, + cbKey: _symmetricAlgorithmSubkeyLengthInBytes, + pbNonce: pbNonce, + pbPlaintextData: pbPlaintext, + cbPlaintextData: cbPlaintext, + pbEncryptedData: pbEncryptedData, + pbTag: pbAuthTag); + + // At this point, retVal := { preBuffer | keyModifier | nonce | encryptedData | authenticationTag | postBuffer } + // And we're done! + return retVal; + } + finally + { + // The buffer contains key material, so delete it. + UnsafeBufferUtil.SecureZeroMemory(pbSymmetricEncryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes); + } } } } From 052296fa4d85af72e4d88e50d9860d63520bb7fa Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Fri, 5 Feb 2021 03:09:25 -0800 Subject: [PATCH 16/31] Update CngGcmAuthenticatedEncryptor.cs --- .../src/Cng/CngGcmAuthenticatedEncryptor.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs index 3667d9f79915..23c4d44bad82 100644 --- a/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs @@ -177,11 +177,11 @@ protected override byte[] DecryptImpl(byte* pbCiphertext, uint cbCiphertext, byt dwFlags: 0); UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus); CryptoUtil.Assert(cbDecryptedBytesWritten == cbPlaintext, "cbDecryptedBytesWritten == cbPlaintext"); - } - // At this point, retVal := { decryptedPayload } - // And we're done! - return retVal; + // At this point, retVal := { decryptedPayload } + // And we're done! + return retVal; + } } finally { @@ -253,7 +253,7 @@ protected override byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* var subKey = new byte[checked((int)_symmetricAlgorithmSubkeyLengthInBytes)]; //byte* pbSymmetricEncryptionSubkey = stackalloc byte[checked((int)_symmetricAlgorithmSubkeyLengthInBytes)]; - + // TODO: REVERT fixed (byte* pbSymmetricEncryptionSubkey = subKey) try { From 7a932dee2e72459a4b9f784fbbf8be99e1927824 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 8 Feb 2021 09:56:30 -0800 Subject: [PATCH 17/31] Fix bugs --- .../AuthenticatedEncryptorFactory.cs | 2 +- .../Managed/AesGcmAuthenticatedEncryptor.cs | 25 +++++++++++-------- .../AuthenticatedEncryptorDescriptorTests.cs | 25 +++++++++++++++++++ 3 files changed, 40 insertions(+), 12 deletions(-) diff --git a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs index 4653aea9b4b7..6b11f24be8ad 100644 --- a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs +++ b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs @@ -56,7 +56,7 @@ public AuthenticatedEncryptorFactory(ILoggerFactory loggerFactory) if (IsGcmAlgorithm(authenticatedConfiguration.EncryptionAlgorithm)) { #if NETCOREAPP - return new AesGcmAuthenticatedEncryptor(new Secret(secret), GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm)); + return new AesGcmAuthenticatedEncryptor(new Secret(secret), GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm) / 8); #else // GCM requires CNG, and CNG is only supported on Windows. if (!OSVersionUtil.IsWindows()) diff --git a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs index bb05043bb45f..ebefb50d09a5 100644 --- a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs @@ -41,21 +41,21 @@ internal unsafe sealed class AesGcmAuthenticatedEncryptor : IOptimizedAuthentica private readonly int _derivedkeySizeInBytes; private readonly IManagedGenRandom _genRandom; - public AesGcmAuthenticatedEncryptor(Secret keyDerivationKey, int derivedKeySizesInBytes, IManagedGenRandom? genRandom = null) + public AesGcmAuthenticatedEncryptor(Secret keyDerivationKey, int derivedKeySizeInBytes, IManagedGenRandom? genRandom = null) { _keyDerivationKey = keyDerivationKey; - _derivedkeySizeInBytes = derivedKeySizesInBytes / 8; + _derivedkeySizeInBytes = derivedKeySizeInBytes; - switch (derivedKeySizesInBytes) + switch (_derivedkeySizeInBytes) { - case 128: + case 16: _contextHeader = AES_128_GCM_Header; break; - case 192: - _contextHeader = AES_128_GCM_Header; + case 24: + _contextHeader = AES_192_GCM_Header; break; - case 256: - _contextHeader = AES_128_GCM_Header; + case 32: + _contextHeader = AES_256_GCM_Header; break; default: throw Error.CryptCommon_GenericError(); // should never happen @@ -124,6 +124,7 @@ public byte[] Decrypt(ArraySegment ciphertext, ArraySegment addition var nonce = new Span(ciphertext.Array, nonceOffset, NONCE_SIZE_IN_BYTES); var tag = new Span(ciphertext.Array, tagOffset, TAG_SIZE_IN_BYTES); var encrypted = new Span(ciphertext.Array, encryptedDataOffset, plaintextBytes); + Console.WriteLine("Derived key: "+Convert.ToBase64String(derivedKey)); using var aes = new AesGcm(derivedKey); aes.Decrypt(nonce, encrypted, tag, plaintext); return plaintext; @@ -170,9 +171,11 @@ public byte[] Encrypt(ArraySegment plaintext, ArraySegment additiona } // Randomly generate the key modifier and nonce - var keyModifierNonce = _genRandom.GenRandom(KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES); + var keyModifier = _genRandom.GenRandom(KEY_MODIFIER_SIZE_IN_BYTES); + var nonceBytes = _genRandom.GenRandom(NONCE_SIZE_IN_BYTES); - Buffer.BlockCopy(keyModifierNonce, 0, retVal, (int)preBufferSize, keyModifierNonce.Length); + Buffer.BlockCopy(keyModifier, 0, retVal, (int)preBufferSize, keyModifier.Length); + Buffer.BlockCopy(nonceBytes, 0, retVal, (int)preBufferSize + keyModifier.Length, nonceBytes.Length); // At this point, retVal := { preBuffer | keyModifier | nonce | _____ | _____ | postBuffer } @@ -190,7 +193,7 @@ public byte[] Encrypt(ArraySegment plaintext, ArraySegment additiona kdk: decryptedKdk, label: additionalAuthenticatedData, contextHeader: _contextHeader, - context: keyModifierNonce, + context: keyModifier, prfFactory: _kdkPrfFactory, output: new ArraySegment(derivedKey)); diff --git a/src/DataProtection/DataProtection/test/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorDescriptorTests.cs b/src/DataProtection/DataProtection/test/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorDescriptorTests.cs index b5c7794f70f7..04b6e6ceaa12 100644 --- a/src/DataProtection/DataProtection/test/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorDescriptorTests.cs +++ b/src/DataProtection/DataProtection/test/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorDescriptorTests.cs @@ -76,6 +76,31 @@ public void CreateAuthenticatedEncryptor_RoundTripsData_CngGcmImplementation(Enc Assert.Equal(plaintext, roundTripPlaintext); } + [ConditionalTheory] + [ConditionalRunTestOnlyOnWindows] + [InlineData(EncryptionAlgorithm.AES_128_GCM)] + [InlineData(EncryptionAlgorithm.AES_192_GCM)] + [InlineData(EncryptionAlgorithm.AES_256_GCM)] + public void CreateAuthenticatedEncryptor_RoundTripsData_AesGcmImplementation(EncryptionAlgorithm encryptionAlgorithm) + { + // Parse test input + int keyLengthInBits = Int32.Parse(Regex.Match(encryptionAlgorithm.ToString(), @"^AES_(?\d{3})_GCM$").Groups["keyLength"].Value, CultureInfo.InvariantCulture); + + // Arrange + var masterKey = Secret.Random(512 / 8); + var control = new AesGcmAuthenticatedEncryptor( + keyDerivationKey: masterKey, + derivedKeySizeInBytes: (keyLengthInBits / 8)); + var test = CreateEncryptorInstanceFromDescriptor(CreateDescriptor(encryptionAlgorithm, ValidationAlgorithm.HMACSHA256 /* unused */, masterKey)); + + // Act & assert - data round trips properly from control to test + byte[] plaintext = new byte[] { 1, 2, 3, 4, 5 }; + byte[] aad = new byte[] { 2, 4, 6, 8, 0 }; + byte[] ciphertext = control.Encrypt(new ArraySegment(plaintext), new ArraySegment(aad)); + byte[] roundTripPlaintext = test.Decrypt(new ArraySegment(ciphertext), new ArraySegment(aad)); + Assert.Equal(plaintext, roundTripPlaintext); + } + public static TheoryData CreateAuthenticatedEncryptor_RoundTripsData_ManagedImplementationData => new TheoryData> { From 47dcec90a2e1a750539abd93b17f1a37854eb115 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 8 Feb 2021 10:02:02 -0800 Subject: [PATCH 18/31] Fix typo in context header :( --- .../DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs index ebefb50d09a5..aa504b2f7b64 100644 --- a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs @@ -25,7 +25,7 @@ internal unsafe sealed class AesGcmAuthenticatedEncryptor : IOptimizedAuthentica private const int TAG_SIZE_IN_BYTES = 128 / 8; // we're hardcoding a 128-bit authentication tag size // 128 "00-01-00-00-00-10-00-00-00-0C-00-00-00-10-00-00-00-10-95-7C-50-FF-69-2E-38-8B-9A-D5-C7-68-9E-4B-9E-2B" - private static readonly byte[] AES_128_GCM_Header = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x95, 0x7C, 0x50, 0xFF, 0x69, 0x2E-38, 0x8B, 0x9A, 0xD5, 0xC7, 0x68, 0x9E, 0x4B, 0x9E, 0x2B }; + private static readonly byte[] AES_128_GCM_Header = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x95, 0x7C, 0x50, 0xFF, 0x69, 0x2E, 0x38, 0x8B, 0x9A, 0xD5, 0xC7, 0x68, 0x9E, 0x4B, 0x9E, 0x2B }; // 192 "00-01-00-00-00-18-00-00-00-0C-00-00-00-10-00-00-00-10-0D-AA-01-3A-95-0A-DA-2B-79-8F-5F-F2-72-FA-D3-63" private static readonly byte[] AES_192_GCM_Header = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x0D, 0xAA, 0x01, 0x3A, 0x95, 0x0A, 0xDA, 0x2B, 0x79, 0x8F, 0x5F, 0xF2, 0x72, 0xFA, 0xD3, 0x63 }; From 53e3d8fd4ac5f5ff0783cab4d04f6bd5e2a8b294 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 8 Feb 2021 10:03:07 -0800 Subject: [PATCH 19/31] Cleanup --- .../DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs index aa504b2f7b64..34841468c03b 100644 --- a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs @@ -124,7 +124,6 @@ public byte[] Decrypt(ArraySegment ciphertext, ArraySegment addition var nonce = new Span(ciphertext.Array, nonceOffset, NONCE_SIZE_IN_BYTES); var tag = new Span(ciphertext.Array, tagOffset, TAG_SIZE_IN_BYTES); var encrypted = new Span(ciphertext.Array, encryptedDataOffset, plaintextBytes); - Console.WriteLine("Derived key: "+Convert.ToBase64String(derivedKey)); using var aes = new AesGcm(derivedKey); aes.Decrypt(nonce, encrypted, tag, plaintext); return plaintext; @@ -233,4 +232,4 @@ public void Dispose() } } #endif - } +} From 512a9f603f658c8e27aee8a8727a8465e85f330d Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 8 Feb 2021 10:05:27 -0800 Subject: [PATCH 20/31] Revert print buffer --- .../src/Cng/CngGcmAuthenticatedEncryptor.cs | 64 +++++++++---------- 1 file changed, 31 insertions(+), 33 deletions(-) diff --git a/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs index 23c4d44bad82..45fea954de76 100644 --- a/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs @@ -252,39 +252,37 @@ protected override byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* // We'll need a temporary buffer to hold the symmetric encryption subkey var subKey = new byte[checked((int)_symmetricAlgorithmSubkeyLengthInBytes)]; - //byte* pbSymmetricEncryptionSubkey = stackalloc byte[checked((int)_symmetricAlgorithmSubkeyLengthInBytes)]; - // TODO: REVERT - fixed (byte* pbSymmetricEncryptionSubkey = subKey) - try - { - _sp800_108_ctr_hmac_provider.DeriveKeyWithContextHeader( - pbLabel: pbAdditionalAuthenticatedData, - cbLabel: cbAdditionalAuthenticatedData, - contextHeader: _contextHeader, - pbContext: pbKeyModifier, - cbContext: KEY_MODIFIER_SIZE_IN_BYTES, - pbDerivedKey: pbSymmetricEncryptionSubkey, - cbDerivedKey: _symmetricAlgorithmSubkeyLengthInBytes); - - // Perform the encryption operation - DoGcmEncrypt( - pbKey: pbSymmetricEncryptionSubkey, - cbKey: _symmetricAlgorithmSubkeyLengthInBytes, - pbNonce: pbNonce, - pbPlaintextData: pbPlaintext, - cbPlaintextData: cbPlaintext, - pbEncryptedData: pbEncryptedData, - pbTag: pbAuthTag); - - // At this point, retVal := { preBuffer | keyModifier | nonce | encryptedData | authenticationTag | postBuffer } - // And we're done! - return retVal; - } - finally - { - // The buffer contains key material, so delete it. - UnsafeBufferUtil.SecureZeroMemory(pbSymmetricEncryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes); - } + byte* pbSymmetricEncryptionSubkey = 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: pbSymmetricEncryptionSubkey, + cbDerivedKey: _symmetricAlgorithmSubkeyLengthInBytes); + + // Perform the encryption operation + DoGcmEncrypt( + pbKey: pbSymmetricEncryptionSubkey, + cbKey: _symmetricAlgorithmSubkeyLengthInBytes, + pbNonce: pbNonce, + pbPlaintextData: pbPlaintext, + cbPlaintextData: cbPlaintext, + pbEncryptedData: pbEncryptedData, + pbTag: pbAuthTag); + + // At this point, retVal := { preBuffer | keyModifier | nonce | encryptedData | authenticationTag | postBuffer } + // And we're done! + return retVal; + } + finally + { + // The buffer contains key material, so delete it. + UnsafeBufferUtil.SecureZeroMemory(pbSymmetricEncryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes); + } } } } From fa4ee929cfc3655b2f111dbd30ab782fddea7519 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Mon, 8 Feb 2021 10:06:32 -0800 Subject: [PATCH 21/31] Cleanup --- .../DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs index 45fea954de76..1566fabba4f4 100644 --- a/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs @@ -250,8 +250,6 @@ protected override byte[] EncryptImpl(byte* pbPlaintext, uint cbPlaintext, byte* // Use the KDF to generate a new symmetric block cipher key // We'll need a temporary buffer to hold the symmetric encryption subkey - var subKey = new byte[checked((int)_symmetricAlgorithmSubkeyLengthInBytes)]; - byte* pbSymmetricEncryptionSubkey = stackalloc byte[checked((int)_symmetricAlgorithmSubkeyLengthInBytes)]; try { From bc1194dbc2a22ee3a8be28929e4e5dd6a3776873 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 24 Feb 2021 01:07:33 -0800 Subject: [PATCH 22/31] Code review feedback --- .../Cryptography.Internal/src/CryptoUtil.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs index bf87fc52ad7f..b51699912e0d 100644 --- a/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs +++ b/src/DataProtection/Cryptography.Internal/src/CryptoUtil.cs @@ -77,13 +77,10 @@ public static T Fail(string message) where T : class public static bool TimeConstantBuffersAreEqual(byte* bufA, byte* bufB, uint count) { #if NETCOREAPP - unsafe - { - var byteCount = Convert.ToInt32(count); - var bytesA = new ReadOnlySpan(bufA, byteCount); - var bytesB = new ReadOnlySpan(bufB, byteCount); - return CryptographicOperations.FixedTimeEquals(bytesA, bytesB); - } + var byteCount = Convert.ToInt32(count); + var bytesA = new ReadOnlySpan(bufA, byteCount); + var bytesB = new ReadOnlySpan(bufB, byteCount); + return CryptographicOperations.FixedTimeEquals(bytesA, bytesB); #else bool areEqual = true; for (uint i = 0; i < count; i++) From c1f8809bfca128e78810a64259d9e972f82c892e Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 24 Feb 2021 01:09:10 -0800 Subject: [PATCH 23/31] Update AuthenticatedEncryptorFactory.cs --- .../AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs index 6b11f24be8ad..64d7712a638d 100644 --- a/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs +++ b/src/DataProtection/DataProtection/src/AuthenticatedEncryption/AuthenticatedEncryptorFactory.cs @@ -56,7 +56,7 @@ public AuthenticatedEncryptorFactory(ILoggerFactory loggerFactory) if (IsGcmAlgorithm(authenticatedConfiguration.EncryptionAlgorithm)) { #if NETCOREAPP - return new AesGcmAuthenticatedEncryptor(new Secret(secret), GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm) / 8); + return new AesGcmAuthenticatedEncryptor(secret, GetAlgorithmKeySizeInBits(authenticatedConfiguration.EncryptionAlgorithm) / 8); #else // GCM requires CNG, and CNG is only supported on Windows. if (!OSVersionUtil.IsWindows()) From 6a866f40014fa719d8375801ab862ad1affc4ec7 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 24 Feb 2021 01:09:15 -0800 Subject: [PATCH 24/31] Update AesGcmAuthenticatedEncryptor.cs --- .../src/Managed/AesGcmAuthenticatedEncryptor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs index 34841468c03b..615c12e3c6cb 100644 --- a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs @@ -41,9 +41,9 @@ internal unsafe sealed class AesGcmAuthenticatedEncryptor : IOptimizedAuthentica private readonly int _derivedkeySizeInBytes; private readonly IManagedGenRandom _genRandom; - public AesGcmAuthenticatedEncryptor(Secret keyDerivationKey, int derivedKeySizeInBytes, IManagedGenRandom? genRandom = null) + public AesGcmAuthenticatedEncryptor(ISecret keyDerivationKey, int derivedKeySizeInBytes, IManagedGenRandom? genRandom = null) { - _keyDerivationKey = keyDerivationKey; + _keyDerivationKey = new Secret(keyDerivationKey); _derivedkeySizeInBytes = derivedKeySizeInBytes; switch (_derivedkeySizeInBytes) From 68f37c2d949bbdc1ac7d0ff61d06574afb216812 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 24 Feb 2021 01:13:45 -0800 Subject: [PATCH 25/31] Update AesGcmAuthenticatedEncryptor.cs --- .../src/Managed/AesGcmAuthenticatedEncryptor.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs index 615c12e3c6cb..2397fe18f517 100644 --- a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs @@ -18,12 +18,14 @@ internal unsafe sealed class AesGcmAuthenticatedEncryptor : IOptimizedAuthentica // Having a key modifier ensures with overwhelming probability that no two encryption operations // will ever derive the same (encryption subkey, MAC subkey) pair. This limits an attacker's // ability to mount a key-dependent chosen ciphertext attack. See also the class-level comment - // for how this is used to overcome GCM's IV limitations. + // on CngGcmAuthenticatedEncryptor for how this is used to overcome GCM's IV limitations. private const int KEY_MODIFIER_SIZE_IN_BYTES = 128 / 8; private const int NONCE_SIZE_IN_BYTES = 96 / 8; // GCM has a fixed 96-bit IV private const int TAG_SIZE_IN_BYTES = 128 / 8; // we're hardcoding a 128-bit authentication tag size + // See CngGcmAuthenticatedEncryptor.CreateContextHeader for how these were precomputed + // 128 "00-01-00-00-00-10-00-00-00-0C-00-00-00-10-00-00-00-10-95-7C-50-FF-69-2E-38-8B-9A-D5-C7-68-9E-4B-9E-2B" private static readonly byte[] AES_128_GCM_Header = new byte[] { 0x00, 0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x95, 0x7C, 0x50, 0xFF, 0x69, 0x2E, 0x38, 0x8B, 0x9A, 0xD5, 0xC7, 0x68, 0x9E, 0x4B, 0x9E, 0x2B }; @@ -58,7 +60,7 @@ public AesGcmAuthenticatedEncryptor(ISecret keyDerivationKey, int derivedKeySize _contextHeader = AES_256_GCM_Header; break; default: - throw Error.CryptCommon_GenericError(); // should never happen + throw throw CryptoUtil.Fail("Unexpected AES key size in bytes only support 16, 24, 32."); // should never happen } _genRandom = genRandom ?? ManagedGenRandomImpl.Instance; From a631a30c22a4d6e81b7c82e5ea1eb2c8b8babbc3 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 24 Feb 2021 01:14:33 -0800 Subject: [PATCH 26/31] Update AesGcmAuthenticatedEncryptor.cs --- .../src/Managed/AesGcmAuthenticatedEncryptor.cs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs index 2397fe18f517..993b339ba0de 100644 --- a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs @@ -121,15 +121,12 @@ public byte[] Decrypt(ArraySegment ciphertext, ArraySegment addition output: new ArraySegment(derivedKey)); // Perform the decryption operation - unsafe - { - var nonce = new Span(ciphertext.Array, nonceOffset, NONCE_SIZE_IN_BYTES); - var tag = new Span(ciphertext.Array, tagOffset, TAG_SIZE_IN_BYTES); - var encrypted = new Span(ciphertext.Array, encryptedDataOffset, plaintextBytes); - using var aes = new AesGcm(derivedKey); - aes.Decrypt(nonce, encrypted, tag, plaintext); - return plaintext; - } + var nonce = new Span(ciphertext.Array, nonceOffset, NONCE_SIZE_IN_BYTES); + var tag = new Span(ciphertext.Array, tagOffset, TAG_SIZE_IN_BYTES); + var encrypted = new Span(ciphertext.Array, encryptedDataOffset, plaintextBytes); + using var aes = new AesGcm(derivedKey); + aes.Decrypt(nonce, encrypted, tag, plaintext); + return plaintext; } finally { From 4e84ecf843679909cc1a06744326d327e232337d Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 24 Feb 2021 01:16:11 -0800 Subject: [PATCH 27/31] Update AesGcmAuthenticatedEncryptor.cs --- .../Managed/AesGcmAuthenticatedEncryptor.cs | 101 +++++++++--------- 1 file changed, 49 insertions(+), 52 deletions(-) diff --git a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs index 993b339ba0de..f2c63db59182 100644 --- a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs @@ -153,65 +153,62 @@ public byte[] Encrypt(ArraySegment plaintext, ArraySegment additiona // Allocate a buffer to hold the key modifier, nonce, encrypted data, and tag. // In GCM, the encrypted output will be the same length as the plaintext input. var retVal = new byte[checked(preBufferSize + KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + plaintext.Count + TAG_SIZE_IN_BYTES + postBufferSize)]; - fixed (byte* pbRetVal = retVal) + int keyModifierOffset; // position in ciphertext.Array where key modifier begins + int nonceOffset; // position in ciphertext.Array where key modifier ends / nonce begins + int encryptedDataOffset; // position in ciphertext.Array where nonce ends / encryptedData begins + int tagOffset; // position in ciphertext.Array where encrypted data ends + + checked { - int keyModifierOffset; // position in ciphertext.Array where key modifier begins - int nonceOffset; // position in ciphertext.Array where key modifier ends / nonce begins - int encryptedDataOffset; // position in ciphertext.Array where nonce ends / encryptedData begins - int tagOffset; // position in ciphertext.Array where encrypted data ends + keyModifierOffset = plaintext.Offset + (int)preBufferSize; + nonceOffset = keyModifierOffset + KEY_MODIFIER_SIZE_IN_BYTES; + encryptedDataOffset = nonceOffset + NONCE_SIZE_IN_BYTES; + tagOffset = encryptedDataOffset + plaintext.Count; + } - checked - { - keyModifierOffset = plaintext.Offset + (int)preBufferSize; - nonceOffset = keyModifierOffset + KEY_MODIFIER_SIZE_IN_BYTES; - encryptedDataOffset = nonceOffset + NONCE_SIZE_IN_BYTES; - tagOffset = encryptedDataOffset + plaintext.Count; - } + // Randomly generate the key modifier and nonce + var keyModifier = _genRandom.GenRandom(KEY_MODIFIER_SIZE_IN_BYTES); + var nonceBytes = _genRandom.GenRandom(NONCE_SIZE_IN_BYTES); - // Randomly generate the key modifier and nonce - var keyModifier = _genRandom.GenRandom(KEY_MODIFIER_SIZE_IN_BYTES); - var nonceBytes = _genRandom.GenRandom(NONCE_SIZE_IN_BYTES); + Buffer.BlockCopy(keyModifier, 0, retVal, (int)preBufferSize, keyModifier.Length); + Buffer.BlockCopy(nonceBytes, 0, retVal, (int)preBufferSize + keyModifier.Length, nonceBytes.Length); - Buffer.BlockCopy(keyModifier, 0, retVal, (int)preBufferSize, keyModifier.Length); - Buffer.BlockCopy(nonceBytes, 0, retVal, (int)preBufferSize + keyModifier.Length, nonceBytes.Length); + // At this point, retVal := { preBuffer | keyModifier | nonce | _____ | _____ | postBuffer } - // At this point, retVal := { preBuffer | keyModifier | nonce | _____ | _____ | postBuffer } + // Use the KDF to generate a new symmetric block cipher key + // We'll need a temporary buffer to hold the symmetric encryption subkey + var decryptedKdk = new byte[_keyDerivationKey.Length]; + var derivedKey = new byte[_derivedkeySizeInBytes]; + fixed (byte* __unused__1 = decryptedKdk) + fixed (byte* __unused__2 = derivedKey) + { + try + { + _keyDerivationKey.WriteSecretIntoBuffer(new ArraySegment(decryptedKdk)); + ManagedSP800_108_CTR_HMACSHA512.DeriveKeysWithContextHeader( + kdk: decryptedKdk, + label: additionalAuthenticatedData, + contextHeader: _contextHeader, + context: keyModifier, + prfFactory: _kdkPrfFactory, + output: new ArraySegment(derivedKey)); + + // do gcm + var nonce = new Span(retVal, nonceOffset, NONCE_SIZE_IN_BYTES); + var tag = new Span(retVal, tagOffset, TAG_SIZE_IN_BYTES); + var encrypted = new Span(retVal, encryptedDataOffset, plaintext.Count); + using var aes = new AesGcm(derivedKey); + aes.Encrypt(nonce, plaintext, encrypted, tag); - // Use the KDF to generate a new symmetric block cipher key - // We'll need a temporary buffer to hold the symmetric encryption subkey - var decryptedKdk = new byte[_keyDerivationKey.Length]; - var derivedKey = new byte[_derivedkeySizeInBytes]; - fixed (byte* __unused__1 = decryptedKdk) - fixed (byte* __unused__2 = derivedKey) + // At this point, retVal := { preBuffer | keyModifier | nonce | encryptedData | authenticationTag | postBuffer } + // And we're done! + return retVal; + } + finally { - try - { - _keyDerivationKey.WriteSecretIntoBuffer(new ArraySegment(decryptedKdk)); - ManagedSP800_108_CTR_HMACSHA512.DeriveKeysWithContextHeader( - kdk: decryptedKdk, - label: additionalAuthenticatedData, - contextHeader: _contextHeader, - context: keyModifier, - prfFactory: _kdkPrfFactory, - output: new ArraySegment(derivedKey)); - - // do gcm - var nonce = new Span(retVal, nonceOffset, NONCE_SIZE_IN_BYTES); - var tag = new Span(retVal, tagOffset, TAG_SIZE_IN_BYTES); - var encrypted = new Span(retVal, encryptedDataOffset, plaintext.Count); - using var aes = new AesGcm(derivedKey); - aes.Encrypt(nonce, plaintext, encrypted, tag); - - // At this point, retVal := { preBuffer | keyModifier | nonce | encryptedData | authenticationTag | postBuffer } - // And we're done! - return retVal; - } - finally - { - // delete since these contain secret material - Array.Clear(decryptedKdk, 0, decryptedKdk.Length); - Array.Clear(derivedKey, 0, derivedKey.Length); - } + // delete since these contain secret material + Array.Clear(decryptedKdk, 0, decryptedKdk.Length); + Array.Clear(derivedKey, 0, derivedKey.Length); } } } From 96b1b4bf982c63f4471643dabdfc6dde6fc6437e Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 24 Feb 2021 02:44:54 -0800 Subject: [PATCH 28/31] Update AesGcmAuthenticatedEncryptor.cs --- .../DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs index f2c63db59182..cec64fd0f5f4 100644 --- a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs @@ -60,7 +60,7 @@ public AesGcmAuthenticatedEncryptor(ISecret keyDerivationKey, int derivedKeySize _contextHeader = AES_256_GCM_Header; break; default: - throw throw CryptoUtil.Fail("Unexpected AES key size in bytes only support 16, 24, 32."); // should never happen + throw CryptoUtil.Fail("Unexpected AES key size in bytes only support 16, 24, 32."); // should never happen } _genRandom = genRandom ?? ManagedGenRandomImpl.Instance; From fb78d324b7efe80bb5a34c38b675075a939f561b Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 24 Feb 2021 08:55:01 -0800 Subject: [PATCH 29/31] Update AuthenticatedEncryptorDescriptorTests.cs --- .../AuthenticatedEncryptorDescriptorTests.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/DataProtection/DataProtection/test/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorDescriptorTests.cs b/src/DataProtection/DataProtection/test/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorDescriptorTests.cs index 04b6e6ceaa12..e7dac35da21d 100644 --- a/src/DataProtection/DataProtection/test/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorDescriptorTests.cs +++ b/src/DataProtection/DataProtection/test/AuthenticatedEncryption/ConfigurationModel/AuthenticatedEncryptorDescriptorTests.cs @@ -88,9 +88,10 @@ public void CreateAuthenticatedEncryptor_RoundTripsData_AesGcmImplementation(Enc // Arrange var masterKey = Secret.Random(512 / 8); - var control = new AesGcmAuthenticatedEncryptor( + var control = new CngGcmAuthenticatedEncryptor( keyDerivationKey: masterKey, - derivedKeySizeInBytes: (keyLengthInBits / 8)); + symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_GCM, + symmetricAlgorithmKeySizeInBytes: (uint)(keyLengthInBits / 8)); var test = CreateEncryptorInstanceFromDescriptor(CreateDescriptor(encryptionAlgorithm, ValidationAlgorithm.HMACSHA256 /* unused */, masterKey)); // Act & assert - data round trips properly from control to test From 75a1e0f774bc1b266b4543abbca83f8b42ad6d8b Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 24 Feb 2021 09:07:45 -0800 Subject: [PATCH 30/31] Update AesGcmAuthenticatedEncryptor.cs --- .../src/Managed/AesGcmAuthenticatedEncryptor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs index cec64fd0f5f4..4bad2a978e00 100644 --- a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +#if NETCOREAPP using System; using System.IO; using System.Security.Cryptography; @@ -11,7 +12,6 @@ namespace Microsoft.AspNetCore.DataProtection.Managed { -#if NETCOREAPP // An encryptor uses AesGcm to do encryption internal unsafe sealed class AesGcmAuthenticatedEncryptor : IOptimizedAuthenticatedEncryptor, IDisposable { @@ -227,5 +227,5 @@ public void Dispose() _keyDerivationKey.Dispose(); } } -#endif } +#endif From a752598586163c9f7d2938b7e43c207e0f6b3ee7 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Wed, 24 Feb 2021 09:07:56 -0800 Subject: [PATCH 31/31] Update src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs Co-authored-by: Pranav K --- .../DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs index 4bad2a978e00..d883dfeaaaed 100644 --- a/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs +++ b/src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs @@ -12,7 +12,7 @@ namespace Microsoft.AspNetCore.DataProtection.Managed { - // An encryptor uses AesGcm to do encryption + // An encryptor that uses AesGcm to do encryption internal unsafe sealed class AesGcmAuthenticatedEncryptor : IOptimizedAuthenticatedEncryptor, IDisposable { // Having a key modifier ensures with overwhelming probability that no two encryption operations