Skip to content

Commit acdcaf7

Browse files
Always run self test on encryptors (#66413)
1 parent 1ae00e6 commit acdcaf7

9 files changed

Lines changed: 120 additions & 3 deletions

src/DataProtection/DataProtection/src/Cng/CbcAuthenticatedEncryptor.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ public CbcAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHandle
5858
AlgorithmAssert.IsAllowableValidationAlgorithmDigestSize(checked(_hmacAlgorithmDigestLengthInBytes * 8));
5959

6060
_contextHeader = CreateContextHeader();
61+
62+
try
63+
{
64+
this.PerformSelfTest();
65+
}
66+
catch
67+
{
68+
_sp800_108_ctr_hmac_provider.Dispose();
69+
throw;
70+
}
6171
}
6272

6373
public void Decrypt<TWriter>(ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> additionalAuthenticatedData, ref TWriter destination) where TWriter : IBufferWriter<byte>

src/DataProtection/DataProtection/src/Cng/CngGcmAuthenticatedEncryptor.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,16 @@ public CngGcmAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHand
5353
_symmetricAlgorithmHandle = symmetricAlgorithmHandle;
5454
_symmetricAlgorithmSubkeyLengthInBytes = symmetricAlgorithmKeySizeInBytes;
5555
_contextHeader = CreateContextHeader();
56+
57+
try
58+
{
59+
this.PerformSelfTest();
60+
}
61+
catch
62+
{
63+
_sp800_108_ctr_hmac_provider.Dispose();
64+
throw;
65+
}
5666
}
5767

5868
public void Decrypt<TWriter>(ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> additionalAuthenticatedData, ref TWriter destination) where TWriter : IBufferWriter<byte>

src/DataProtection/DataProtection/src/Managed/AesGcmAuthenticatedEncryptor.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,16 @@ public AesGcmAuthenticatedEncryptor(ISecret keyDerivationKey, int derivedKeySize
6363
}
6464

6565
_genRandom = genRandom ?? ManagedGenRandomImpl.Instance;
66+
67+
try
68+
{
69+
this.PerformSelfTest();
70+
}
71+
catch
72+
{
73+
_keyDerivationKey.Dispose();
74+
throw;
75+
}
6676
}
6777

6878
public void Decrypt<TWriter>(ReadOnlySpan<byte> ciphertext, ReadOnlySpan<byte> additionalAuthenticatedData, ref TWriter destination)

src/DataProtection/DataProtection/src/Managed/ManagedAuthenticatedEncryptor.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,16 @@ public ManagedAuthenticatedEncryptor(Secret keyDerivationKey, Func<SymmetricAlgo
7070
AlgorithmAssert.IsAllowableValidationAlgorithmDigestSize(checked((uint)_validationAlgorithmDigestLengthInBytes * 8));
7171

7272
_contextHeader = CreateContextHeader();
73+
74+
try
75+
{
76+
this.PerformSelfTest();
77+
}
78+
catch
79+
{
80+
_keyDerivationKey.Dispose();
81+
throw;
82+
}
7383
}
7484

7585
#if NET

src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/Aes/AesAuthenticatedEncryptorTests.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@ public void Roundtrip_AesGcm_TryEncryptDecrypt_CorrectlyEstimatesDataLength(int
2727

2828
RoundtripEncryptionHelpers.AssertTryEncryptTryDecryptParity(encryptor, plaintext, aad);
2929
}
30+
31+
[Fact]
32+
public void Constructor_PerformsSelfTest_ConsumesRandomBytes()
33+
{
34+
var genRandom = new SequentialGenRandom();
35+
byte initialValue = genRandom.CurrentValue;
36+
37+
Secret kdk = new Secret(new byte[512 / 8]);
38+
_ = new AesGcmAuthenticatedEncryptor(kdk,
39+
derivedKeySizeInBytes: 256 / 8,
40+
genRandom: genRandom);
41+
42+
// Indirectly testing that SelfTest ran by checking that random bytes were consumed
43+
Assert.NotEqual(initialValue, genRandom.CurrentValue);
44+
}
3045
}
3146

3247
#endif

src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/Cng/CbcAuthenticatedEncryptorTests.cs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,13 @@ public void Encrypt_KnownKey()
9696
{
9797
// Arrange
9898
Secret kdk = new Secret(Encoding.UTF8.GetBytes("master key"));
99+
var genRandom = new SequentialGenRandom();
99100
CbcAuthenticatedEncryptor encryptor = new CbcAuthenticatedEncryptor(kdk,
100101
symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_CBC,
101102
symmetricAlgorithmKeySizeInBytes: 256 / 8,
102103
hmacAlgorithmHandle: CachedAlgorithmHandles.HMAC_SHA256,
103-
genRandom: new SequentialGenRandom());
104+
genRandom: genRandom);
105+
genRandom.Reset();
104106
ArraySegment<byte> plaintext = new ArraySegment<byte>(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, 2, 3);
105107
ArraySegment<byte> aad = new ArraySegment<byte>(new byte[] { 7, 6, 5, 4, 3, 2, 1, 0 }, 1, 4);
106108

@@ -165,4 +167,22 @@ public void Roundtrip_TryEncryptDecrypt_CorrectlyEstimatesDataLength(int symmetr
165167
RoundtripEncryptionHelpers.AssertTryEncryptTryDecryptParity(encryptor, plaintext, aad);
166168
}
167169
#endif
170+
171+
[ConditionalFact]
172+
[ConditionalRunTestOnlyOnWindows]
173+
public void Constructor_PerformsSelfTest_ConsumesRandomBytes()
174+
{
175+
var genRandom = new SequentialGenRandom();
176+
byte initialValue = genRandom.CurrentValue;
177+
178+
Secret kdk = new Secret(new byte[512 / 8]);
179+
_ = new CbcAuthenticatedEncryptor(kdk,
180+
symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_CBC,
181+
symmetricAlgorithmKeySizeInBytes: 256 / 8,
182+
hmacAlgorithmHandle: CachedAlgorithmHandles.HMAC_SHA256,
183+
genRandom: genRandom);
184+
185+
// Indirectly testing that SelfTest ran by checking that random bytes were consumed
186+
Assert.NotEqual(initialValue, genRandom.CurrentValue);
187+
}
168188
}

src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/Cng/GcmAuthenticatedEncryptorTests.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,9 @@ public void Encrypt_KnownKey()
8787
{
8888
// Arrange
8989
Secret kdk = new Secret(Encoding.UTF8.GetBytes("master key"));
90-
CngGcmAuthenticatedEncryptor encryptor = new CngGcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 128 / 8, genRandom: new SequentialGenRandom());
90+
var genRandom = new SequentialGenRandom();
91+
CngGcmAuthenticatedEncryptor encryptor = new CngGcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 128 / 8, genRandom: genRandom);
92+
genRandom.Reset();
9193
ArraySegment<byte> plaintext = new ArraySegment<byte>(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, 2, 3);
9294
ArraySegment<byte> aad = new ArraySegment<byte>(new byte[] { 7, 6, 5, 4, 3, 2, 1, 0 }, 1, 4);
9395

@@ -129,4 +131,21 @@ public void Roundtrip_CngGcm_TryEncryptDecrypt_CorrectlyEstimatesDataLength(int
129131
RoundtripEncryptionHelpers.AssertTryEncryptTryDecryptParity(encryptor, plaintext, aad);
130132
}
131133
#endif
134+
135+
[ConditionalFact]
136+
[ConditionalRunTestOnlyOnWindows]
137+
public void Constructor_PerformsSelfTest_ConsumesRandomBytes()
138+
{
139+
var genRandom = new SequentialGenRandom();
140+
byte initialValue = genRandom.CurrentValue;
141+
142+
Secret kdk = new Secret(new byte[512 / 8]);
143+
_ = new CngGcmAuthenticatedEncryptor(kdk,
144+
CachedAlgorithmHandles.AES_GCM,
145+
symmetricAlgorithmKeySizeInBytes: 256 / 8,
146+
genRandom: genRandom);
147+
148+
// Indirectly testing that SelfTest ran by checking that random bytes were consumed
149+
Assert.NotEqual(initialValue, genRandom.CurrentValue);
150+
}
132151
}

src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/Managed/ManagedAuthenticatedEncryptorTests.cs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,13 @@ public void Encrypt_KnownKey()
9191
{
9292
// Arrange
9393
Secret kdk = new Secret(Encoding.UTF8.GetBytes("master key"));
94+
var genRandom = new SequentialGenRandom();
9495
ManagedAuthenticatedEncryptor encryptor = new ManagedAuthenticatedEncryptor(kdk,
9596
symmetricAlgorithmFactory: Aes.Create,
9697
symmetricAlgorithmKeySizeInBytes: 256 / 8,
9798
validationAlgorithmFactory: () => new HMACSHA256(),
98-
genRandom: new SequentialGenRandom());
99+
genRandom: genRandom);
100+
genRandom.Reset();
99101
ArraySegment<byte> plaintext = new ArraySegment<byte>(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, 2, 3);
100102
ArraySegment<byte> aad = new ArraySegment<byte>(new byte[] { 7, 6, 5, 4, 3, 2, 1, 0 }, 1, 4);
101103

@@ -176,4 +178,21 @@ public void TimeLimitedDataProtector_WithJsonPayloadNearBufferBoundary_SucceedsW
176178
Assert.Equal(jsonPayload, unprotectedData);
177179
Assert.True(expiration > DateTimeOffset.UtcNow, "Expiration should be in the future");
178180
}
181+
182+
[Fact]
183+
public void Constructor_PerformsSelfTest_ConsumesRandomBytes()
184+
{
185+
var genRandom = new SequentialGenRandom();
186+
byte initialValue = genRandom.CurrentValue;
187+
188+
Secret kdk = new Secret(new byte[512 / 8]);
189+
_ = new ManagedAuthenticatedEncryptor(kdk,
190+
symmetricAlgorithmFactory: Aes.Create,
191+
symmetricAlgorithmKeySizeInBytes: 256 / 8,
192+
validationAlgorithmFactory: () => new HMACSHA256(),
193+
genRandom: genRandom);
194+
195+
// Indirectly testing that SelfTest ran by checking that random bytes were consumed
196+
Assert.NotEqual(initialValue, genRandom.CurrentValue);
197+
}
179198
}

src/DataProtection/DataProtection/test/Microsoft.AspNetCore.DataProtection.Tests/SequentialGenRandom.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,10 @@ internal unsafe class SequentialGenRandom : IBCryptGenRandom, IManagedGenRandom
1111
{
1212
private byte _value;
1313

14+
internal byte CurrentValue => _value;
15+
16+
internal void Reset() => _value = 0;
17+
1418
public byte[] GenRandom(int numBytes)
1519
{
1620
byte[] bytes = new byte[numBytes];

0 commit comments

Comments
 (0)