Skip to content

Commit 6d63d29

Browse files
authored
Make sure IV for AES-CBC is initialized (Azure#16176)
Relates to Azure#14887
1 parent 6a4dd4e commit 6d63d29

File tree

3 files changed

+125
-14
lines changed

3 files changed

+125
-14
lines changed

sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/AesCryptographyProvider.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ public override EncryptResult Encrypt(EncryptOptions options, CancellationToken
8181
EncryptionAlgorithm algorithm = options.Algorithm;
8282
if (algorithm.GetAesCbcEncryptionAlgorithm() is AesCbc aesCbc)
8383
{
84+
// Make sure the IV is initialized.
85+
options.Initialize();
86+
8487
using ICryptoTransform encryptor = aesCbc.CreateEncryptor(KeyMaterial.K, options.Iv);
8588

8689
byte[] plaintext = options.Plaintext;

sdk/keyvault/Azure.Security.KeyVault.Keys/src/Cryptography/RemoteCryptographyClient.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ public virtual async Task<Response<EncryptResult>> EncryptAsync(EncryptOptions o
4949

5050
try
5151
{
52+
// Make sure the IV is initialized.
53+
// TODO: Remove this call once the service will initialized it: https://github.com/Azure/azure-sdk-for-net/issues/16175
54+
options.Initialize();
55+
5256
return await Pipeline.SendRequestAsync(RequestMethod.Post, options, () => new EncryptResult { Algorithm = options.Algorithm }, cancellationToken, "/encrypt").ConfigureAwait(false);
5357
}
5458
catch (Exception e)
@@ -66,6 +70,10 @@ public virtual Response<EncryptResult> Encrypt(EncryptOptions options, Cancellat
6670

6771
try
6872
{
73+
// Make sure the IV is initialized.
74+
// TODO: Remove this call once the service will initialized it: https://github.com/Azure/azure-sdk-for-net/issues/16175
75+
options.Initialize();
76+
6977
return Pipeline.SendRequest(RequestMethod.Post, options, () => new EncryptResult { Algorithm = options.Algorithm }, cancellationToken, "/encrypt");
7078
}
7179
catch (Exception e)

sdk/keyvault/Azure.Security.KeyVault.Keys/tests/AesCryptographyProviderTests.cs

Lines changed: 114 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public void DecryptionAlgorithmNotSupported()
137137
Assert.AreEqual("invalid", e.GetProperty<string>("algorithm"));
138138
}
139139

140-
[TestCaseSource(nameof(EncryptDecryptRoundtripsData))]
140+
[TestCaseSource(nameof(GetEncryptionAlgorithms), methodParams: new object[] { true })]
141141
public void EncryptDecryptRoundtrips(EncryptionAlgorithm algorithm)
142142
{
143143
// Use a 256-bit key which will be truncated based on the selected algorithm.
@@ -171,6 +171,73 @@ public void EncryptDecryptRoundtrips(EncryptionAlgorithm algorithm)
171171
#endif
172172
Assert.IsNotNull(encrypted);
173173

174+
switch (algorithm.ToString())
175+
{
176+
// TODO: Move to new test to make sure CryptoClient and LocalCryptoClient initialize a null ICM for AES-CBC(PAD).
177+
case EncryptionAlgorithm.A128CbcValue:
178+
CollectionAssert.AreEqual(
179+
new byte[] { 0x63, 0x23, 0x21, 0xaf, 0x94, 0xf9, 0xe1, 0x21, 0xc2, 0xbd, 0xb1, 0x1b, 0x04, 0x89, 0x8c, 0x3a },
180+
encrypted.Ciphertext);
181+
CollectionAssert.AreEqual(iv, encrypted.Iv);
182+
Assert.IsNull(encrypted.AuthenticationTag);
183+
Assert.IsNull(encrypted.AdditionalAuthenticatedData);
184+
break;
185+
186+
case EncryptionAlgorithm.A192CbcValue:
187+
CollectionAssert.AreEqual(
188+
new byte[] { 0x95, 0x9d, 0x75, 0x91, 0x09, 0x8b, 0x70, 0x0b, 0x9c, 0xfe, 0xaf, 0xcd, 0x60, 0x1f, 0xaa, 0x79 },
189+
encrypted.Ciphertext);
190+
CollectionAssert.AreEqual(iv, encrypted.Iv);
191+
Assert.IsNull(encrypted.AuthenticationTag);
192+
Assert.IsNull(encrypted.AdditionalAuthenticatedData);
193+
break;
194+
195+
case EncryptionAlgorithm.A256CbcValue:
196+
CollectionAssert.AreEqual(
197+
new byte[] { 0xf4, 0xe8, 0x5a, 0xa4, 0xa8, 0xb3, 0xff, 0xc3, 0x85, 0x89, 0x17, 0x9a, 0x70, 0x09, 0x96, 0x7f },
198+
encrypted.Ciphertext);
199+
CollectionAssert.AreEqual(iv, encrypted.Iv);
200+
Assert.IsNull(encrypted.AuthenticationTag);
201+
Assert.IsNull(encrypted.AdditionalAuthenticatedData);
202+
break;
203+
204+
case EncryptionAlgorithm.A128CbcPadValue:
205+
CollectionAssert.AreEqual(
206+
new byte[] { 0xec, 0xb2, 0x63, 0x4c, 0xe0, 0x04, 0xe0, 0x31, 0x2d, 0x9a, 0x77, 0xb2, 0x11, 0xe5, 0x28, 0x7f },
207+
encrypted.Ciphertext);
208+
CollectionAssert.AreEqual(iv, encrypted.Iv);
209+
Assert.IsNull(encrypted.AuthenticationTag);
210+
Assert.IsNull(encrypted.AdditionalAuthenticatedData);
211+
break;
212+
213+
case EncryptionAlgorithm.A192CbcPadValue:
214+
CollectionAssert.AreEqual(
215+
new byte[] { 0xc3, 0x4e, 0x1b, 0xe7, 0x6e, 0xa1, 0xf1, 0xc3, 0x24, 0xae, 0x05, 0x1b, 0x0e, 0x32, 0xac, 0xb4 },
216+
encrypted.Ciphertext);
217+
CollectionAssert.AreEqual(iv, encrypted.Iv);
218+
Assert.IsNull(encrypted.AuthenticationTag);
219+
Assert.IsNull(encrypted.AdditionalAuthenticatedData);
220+
break;
221+
222+
case EncryptionAlgorithm.A256CbcPadValue:
223+
CollectionAssert.AreEqual(
224+
new byte[] { 0x4e, 0xbd, 0x78, 0xda, 0x90, 0x73, 0xc8, 0x97, 0x67, 0x2b, 0xa1, 0x0a, 0x41, 0x67, 0xf8, 0x99 },
225+
encrypted.Ciphertext);
226+
CollectionAssert.AreEqual(iv, encrypted.Iv);
227+
Assert.IsNull(encrypted.AuthenticationTag);
228+
Assert.IsNull(encrypted.AdditionalAuthenticatedData);
229+
break;
230+
231+
case EncryptionAlgorithm.A128GcmValue:
232+
case EncryptionAlgorithm.A192GcmValue:
233+
case EncryptionAlgorithm.A256GcmValue:
234+
Assert.IsNotNull(encrypted.Ciphertext);
235+
Assert.IsNotNull(encrypted.Iv);
236+
Assert.IsNotNull(encrypted.AuthenticationTag);
237+
CollectionAssert.AreEqual(aad, encrypted.AdditionalAuthenticatedData);
238+
break;
239+
}
240+
174241
DecryptOptions decryptOptions = algorithm.IsAesGcm() ?
175242
new DecryptOptions(algorithm, encrypted.Ciphertext, encrypted.Iv, encrypted.AuthenticationTag, encrypted.AdditionalAuthenticatedData) :
176243
new DecryptOptions(algorithm, encrypted.Ciphertext, encrypted.Iv);
@@ -182,19 +249,52 @@ public void EncryptDecryptRoundtrips(EncryptionAlgorithm algorithm)
182249
StringAssert.StartsWith("plaintext", Encoding.UTF8.GetString(decrypted.Plaintext));
183250
}
184251

185-
private static IEnumerable EncryptDecryptRoundtripsData => new[]
252+
[TestCaseSource(nameof(GetEncryptionAlgorithms), methodParams: new object[] { false })]
253+
public void InitializesIv(EncryptionAlgorithm algorithm)
254+
{
255+
// Use a 256-bit key which will be truncated based on the selected algorithm.
256+
byte[] k = new byte[] { 0xe2, 0x7e, 0xd0, 0xc8, 0x45, 0x12, 0xbb, 0xd5, 0x5b, 0x6a, 0xf4, 0x34, 0xd2, 0x37, 0xc1, 0x1f, 0xeb, 0xa3, 0x11, 0x87, 0x0f, 0x80, 0xf2, 0xc2, 0xe3, 0x36, 0x42, 0x60, 0xf3, 0x1c, 0x82, 0xc8 };
257+
258+
JsonWebKey key = new JsonWebKey(new[] { KeyOperation.Encrypt, KeyOperation.Decrypt })
259+
{
260+
K = k,
261+
};
262+
263+
AesCryptographyProvider provider = new AesCryptographyProvider(key, null);
264+
265+
byte[] plaintext = Encoding.UTF8.GetBytes("plaintext");
266+
267+
EncryptOptions encryptOptions = new EncryptOptions(algorithm, plaintext, null, null);
268+
EncryptResult encrypted = provider.Encrypt(encryptOptions, default);
269+
270+
Assert.IsNotNull(encryptOptions.Iv);
271+
CollectionAssert.AreEqual(encryptOptions.Iv, encrypted.Iv);
272+
273+
DecryptOptions decryptOptions = new DecryptOptions(algorithm, encrypted.Ciphertext, encrypted.Iv);
274+
DecryptResult decrypted = provider.Decrypt(decryptOptions, default);
275+
276+
Assert.IsNotNull(decrypted);
277+
278+
// AES-CBC will be zero-padded.
279+
StringAssert.StartsWith("plaintext", Encoding.UTF8.GetString(decrypted.Plaintext));
280+
}
281+
282+
private static IEnumerable GetEncryptionAlgorithms(bool includeAesGcm)
186283
{
187-
EncryptionAlgorithm.A128Cbc,
188-
EncryptionAlgorithm.A192Cbc,
189-
EncryptionAlgorithm.A256Cbc,
190-
191-
EncryptionAlgorithm.A128CbcPad,
192-
EncryptionAlgorithm.A192CbcPad,
193-
EncryptionAlgorithm.A256CbcPad,
194-
195-
EncryptionAlgorithm.A128Gcm,
196-
EncryptionAlgorithm.A192Gcm,
197-
EncryptionAlgorithm.A256Gcm,
198-
};
284+
yield return EncryptionAlgorithm.A128Cbc;
285+
yield return EncryptionAlgorithm.A192Cbc;
286+
yield return EncryptionAlgorithm.A256Cbc;
287+
288+
yield return EncryptionAlgorithm.A128CbcPad;
289+
yield return EncryptionAlgorithm.A192CbcPad;
290+
yield return EncryptionAlgorithm.A256CbcPad;
291+
292+
if (includeAesGcm)
293+
{
294+
yield return EncryptionAlgorithm.A128Gcm;
295+
yield return EncryptionAlgorithm.A192Gcm;
296+
yield return EncryptionAlgorithm.A256Gcm;
297+
}
298+
}
199299
}
200300
}

0 commit comments

Comments
 (0)