Skip to content

Commit 008e2f7

Browse files
Brent Schmaltzbrentschmaltz
authored andcommitted
Enforce key sizes when creating HMAC
1 parent 06165eb commit 008e2f7

7 files changed

Lines changed: 228 additions & 30 deletions

File tree

src/Microsoft.IdentityModel.Tokens/CryptoProviderFactory.cs

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,24 +428,101 @@ public virtual KeyedHashAlgorithm CreateKeyedHashAlgorithm(byte[] keyBytes, stri
428428
if (CustomCryptoProvider != null && CustomCryptoProvider.IsSupportedAlgorithm(algorithm, keyBytes))
429429
{
430430
if (!(CustomCryptoProvider.Create(algorithm, keyBytes) is KeyedHashAlgorithm keyedHashAlgorithm))
431-
throw LogHelper.LogExceptionMessage(new InvalidOperationException(LogHelper.FormatInvariant(LogMessages.IDX10647, LogHelper.MarkAsNonPII(algorithm), LogHelper.MarkAsNonPII(typeof(KeyedHashAlgorithm)))));
431+
throw LogHelper.LogExceptionMessage(
432+
new InvalidOperationException(
433+
LogHelper.FormatInvariant(
434+
LogMessages.IDX10647,
435+
LogHelper.MarkAsNonPII(algorithm),
436+
LogHelper.MarkAsNonPII(typeof(KeyedHashAlgorithm)))));
432437

433438
return keyedHashAlgorithm;
434439
}
435440

441+
// In the case of Aes128CbcHmacSha256, Aes192CbcHmacSha384, Aes256CbcHmacSha512 which are Authenticated Encryption algorithms
442+
// SymmetricSignatureProvider will get passed a key with 1/2 the minimum keysize expected size for the HashAlgorithm. 16 bytes for SHA256, instead of 32 bytes.
443+
// see: https://datatracker.ietf.org/doc/html/rfc7518#section-5.2.2.1
436444
switch (algorithm)
437445
{
446+
case SecurityAlgorithms.Aes128CbcHmacSha256:
447+
{
448+
if (keyBytes.Length < 16)
449+
throw LogHelper.LogExceptionMessage(
450+
new InvalidOperationException(
451+
LogHelper.FormatInvariant(LogMessages.IDX10720,
452+
LogHelper.MarkAsNonPII(algorithm),
453+
LogHelper.MarkAsNonPII("128"),
454+
LogHelper.MarkAsNonPII(keyBytes.Length * 8))));
455+
456+
return new HMACSHA256(keyBytes);
457+
}
458+
459+
case SecurityAlgorithms.Aes192CbcHmacSha384:
460+
{
461+
if (keyBytes.Length < 24)
462+
throw LogHelper.LogExceptionMessage(
463+
new InvalidOperationException(
464+
LogHelper.FormatInvariant(LogMessages.IDX10720,
465+
LogHelper.MarkAsNonPII(algorithm),
466+
LogHelper.MarkAsNonPII("192"),
467+
LogHelper.MarkAsNonPII(keyBytes.Length * 8))));
468+
469+
return new HMACSHA384(keyBytes);
470+
}
471+
472+
case SecurityAlgorithms.Aes256CbcHmacSha512:
473+
{
474+
if (keyBytes.Length < 32)
475+
throw LogHelper.LogExceptionMessage(
476+
new InvalidOperationException(
477+
LogHelper.FormatInvariant(LogMessages.IDX10720,
478+
LogHelper.MarkAsNonPII(algorithm),
479+
LogHelper.MarkAsNonPII("256"),
480+
LogHelper.MarkAsNonPII(keyBytes.Length * 8))));
481+
482+
return new HMACSHA512(keyBytes);
483+
}
484+
438485
case SecurityAlgorithms.HmacSha256Signature:
439486
case SecurityAlgorithms.HmacSha256:
487+
{
488+
if (keyBytes.Length < 32)
489+
throw LogHelper.LogExceptionMessage(
490+
new InvalidOperationException(
491+
LogHelper.FormatInvariant(LogMessages.IDX10720,
492+
LogHelper.MarkAsNonPII(algorithm),
493+
LogHelper.MarkAsNonPII("256"),
494+
LogHelper.MarkAsNonPII(keyBytes.Length * 8))));
495+
440496
return new HMACSHA256(keyBytes);
497+
}
441498

442499
case SecurityAlgorithms.HmacSha384Signature:
443500
case SecurityAlgorithms.HmacSha384:
501+
{
502+
if (keyBytes.Length < 48)
503+
throw LogHelper.LogExceptionMessage(
504+
new InvalidOperationException(
505+
LogHelper.FormatInvariant(LogMessages.IDX10720,
506+
LogHelper.MarkAsNonPII(algorithm),
507+
LogHelper.MarkAsNonPII("384"),
508+
LogHelper.MarkAsNonPII(keyBytes.Length * 8))));
509+
444510
return new HMACSHA384(keyBytes);
511+
}
445512

446513
case SecurityAlgorithms.HmacSha512Signature:
447514
case SecurityAlgorithms.HmacSha512:
515+
{
516+
if (keyBytes.Length < 64)
517+
throw LogHelper.LogExceptionMessage(
518+
new InvalidOperationException(
519+
LogHelper.FormatInvariant(LogMessages.IDX10720,
520+
LogHelper.MarkAsNonPII(algorithm),
521+
LogHelper.MarkAsNonPII("512"),
522+
LogHelper.MarkAsNonPII(keyBytes.Length * 8))));
523+
448524
return new HMACSHA512(keyBytes);
525+
}
449526

450527
default:
451528
throw LogHelper.LogExceptionMessage(new NotSupportedException(LogHelper.FormatInvariant(LogMessages.IDX10666, LogHelper.MarkAsNonPII(algorithm))));

src/Microsoft.IdentityModel.Tokens/Encryption/AuthenticatedEncryptionProvider.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ private struct AuthenticatedKeys
2828
private CryptoProviderFactory _cryptoProviderFactory;
2929
private bool _disposed;
3030
private Lazy<bool> _keySizeIsValid;
31-
private string _hmacAlgorithm;
3231
private Lazy<SymmetricSignatureProvider> _symmetricSignatureProvider;
3332
private DecryptionDelegate DecryptFunction;
3433
private EncryptionDelegate EncryptFunction;
@@ -86,7 +85,6 @@ private void InitializeUsingAesGcm()
8685
private void InitializeUsingAesCbc()
8786
{
8887
_authenticatedkeys = new Lazy<AuthenticatedKeys>(CreateAuthenticatedKeys);
89-
_hmacAlgorithm = GetHmacAlgorithm(Algorithm);
9088
_symmetricSignatureProvider = new Lazy<SymmetricSignatureProvider>(CreateSymmetricSignatureProvider);
9189
EncryptFunction = EncryptWithAesCbc;
9290
DecryptFunction = DecryptWithAesCbc;
@@ -208,9 +206,9 @@ internal SymmetricSignatureProvider CreateSymmetricSignatureProvider()
208206
SymmetricSignatureProvider symmetricSignatureProvider;
209207

210208
if (Key.CryptoProviderFactory.GetType() == typeof(CryptoProviderFactory))
211-
symmetricSignatureProvider = Key.CryptoProviderFactory.CreateForSigning(_authenticatedkeys.Value.HmacKey, _hmacAlgorithm, false) as SymmetricSignatureProvider;
209+
symmetricSignatureProvider = Key.CryptoProviderFactory.CreateForSigning(_authenticatedkeys.Value.HmacKey, Algorithm, false) as SymmetricSignatureProvider;
212210
else
213-
symmetricSignatureProvider = Key.CryptoProviderFactory.CreateForSigning(_authenticatedkeys.Value.HmacKey, _hmacAlgorithm) as SymmetricSignatureProvider;
211+
symmetricSignatureProvider = Key.CryptoProviderFactory.CreateForSigning(_authenticatedkeys.Value.HmacKey, Algorithm) as SymmetricSignatureProvider;
214212

215213
if (symmetricSignatureProvider == null)
216214
throw LogHelper.LogExceptionMessage(new ArgumentException(LogHelper.FormatInvariant(LogMessages.IDX10649, LogHelper.MarkAsNonPII(Algorithm))));

src/Microsoft.IdentityModel.Tokens/LogMessages.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ internal static class LogMessages
223223
public const string IDX10717 = "IDX10717: '{0} + {1}' must not be greater than {2}, '{3} + {4} > {5}'.";
224224
public const string IDX10718 = "IDX10718: AlgorithmToValidate is not supported: '{0}'. Algorithm '{1}'.";
225225
public const string IDX10719 = "IDX10719: SignatureSize (in bytes) was expected to be '{0}', was '{1}'.";
226+
public const string IDX10720 = "IDX10720: Unable to create KeyedHashAlgorithm for algorithm '{0}', the key size must be greater than: '{1}' bits, key has '{2}' bits.";
226227

227228
// Json specific errors
228229
//public const string IDX10801 = "IDX10801:"

test/Microsoft.IdentityModel.Tokens.Tests/ReferenceTests.cs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Security.Cryptography;
77
using System.Text;
88
using Microsoft.IdentityModel.JsonWebTokens;
9+
using Microsoft.IdentityModel.Logging;
910
using Microsoft.IdentityModel.TestUtils;
1011
using Microsoft.IdentityModel.Tokens;
1112
using Xunit;
@@ -72,7 +73,8 @@ public void AesGcmReferenceTest()
7273
[Theory, MemberData(nameof(AuthenticatedEncryptionTheoryData))]
7374
public void AuthenticatedEncryptionReferenceTest(AuthenticationEncryptionTestParams testParams)
7475
{
75-
var context = new CompareContext();
76+
var context = TestUtilities.WriteHeader("AuthenticatedEncryptionReferenceTest", testParams);
77+
7678
var providerForEncryption = CryptoProviderFactory.Default.CreateAuthenticatedEncryptionProvider(testParams.EncryptionKey, testParams.Algorithm);
7779
var providerForDecryption = CryptoProviderFactory.Default.CreateAuthenticatedEncryptionProvider(testParams.DecryptionKey, testParams.Algorithm);
7880
var plaintext = providerForDecryption.Decrypt(testParams.Ciphertext, testParams.AuthenticationData, testParams.IV, testParams.AuthenticationTag);
@@ -99,7 +101,7 @@ public static TheoryData<AuthenticationEncryptionTestParams> AuthenticatedEncryp
99101
{
100102
var theoryData = new TheoryData<AuthenticationEncryptionTestParams>();
101103

102-
theoryData.Add(new AuthenticationEncryptionTestParams
104+
theoryData.Add(new AuthenticationEncryptionTestParams("AES_128_CBC_HMAC_SHA_256")
103105
{
104106
Algorithm = AES_128_CBC_HMAC_SHA_256.Algorithm,
105107
AuthenticationData = AES_128_CBC_HMAC_SHA_256.A,
@@ -108,11 +110,10 @@ public static TheoryData<AuthenticationEncryptionTestParams> AuthenticatedEncryp
108110
DecryptionKey = new SymmetricSecurityKey(AES_128_CBC_HMAC_SHA_256.K) { KeyId = "DecryptionKey.AES_128_CBC_HMAC_SHA_256.K" },
109111
EncryptionKey = new SymmetricSecurityKey(AES_128_CBC_HMAC_SHA_256.K) { KeyId = "EncryptionKey.AES_128_CBC_HMAC_SHA_256.K" },
110112
IV = AES_128_CBC_HMAC_SHA_256.IV,
111-
Plaintext = AES_128_CBC_HMAC_SHA_256.P,
112-
TestId = "AES_128_CBC_HMAC_SHA_256"
113+
Plaintext = AES_128_CBC_HMAC_SHA_256.P
113114
});
114115

115-
theoryData.Add(new AuthenticationEncryptionTestParams
116+
theoryData.Add(new AuthenticationEncryptionTestParams("AES_192_CBC_HMAC_SHA_384")
116117
{
117118
Algorithm = AES_192_CBC_HMAC_SHA_384.Algorithm,
118119
AuthenticationData = AES_192_CBC_HMAC_SHA_384.A,
@@ -121,11 +122,10 @@ public static TheoryData<AuthenticationEncryptionTestParams> AuthenticatedEncryp
121122
DecryptionKey = new SymmetricSecurityKey(AES_192_CBC_HMAC_SHA_384.K) { KeyId = "DecryptionKey.AES_192_CBC_HMAC_SHA_384.K" },
122123
EncryptionKey = new SymmetricSecurityKey(AES_192_CBC_HMAC_SHA_384.K) { KeyId = "EncryptionKey.AES_192_CBC_HMAC_SHA_384.K" },
123124
IV = AES_192_CBC_HMAC_SHA_384.IV,
124-
Plaintext = AES_192_CBC_HMAC_SHA_384.P,
125-
TestId = "AES_192_CBC_HMAC_SHA_384"
125+
Plaintext = AES_192_CBC_HMAC_SHA_384.P
126126
});
127127

128-
theoryData.Add(new AuthenticationEncryptionTestParams
128+
theoryData.Add(new AuthenticationEncryptionTestParams("AES_256_CBC_HMAC_SHA_512")
129129
{
130130
Algorithm = AES_256_CBC_HMAC_SHA_512.Algorithm,
131131
AuthenticationData = AES_256_CBC_HMAC_SHA_512.A,
@@ -134,16 +134,19 @@ public static TheoryData<AuthenticationEncryptionTestParams> AuthenticatedEncryp
134134
DecryptionKey = new SymmetricSecurityKey(AES_256_CBC_HMAC_SHA_512.K) { KeyId = "DecryptionKey.AES_256_CBC_HMAC_SHA_512.K" },
135135
EncryptionKey = new SymmetricSecurityKey(AES_256_CBC_HMAC_SHA_512.K) { KeyId = "EncryptionKey.AES_256_CBC_HMAC_SHA_512.K" },
136136
IV = AES_256_CBC_HMAC_SHA_512.IV,
137-
Plaintext = AES_256_CBC_HMAC_SHA_512.P,
138-
TestId = "AES_256_CBC_HMAC_SHA_512"
137+
Plaintext = AES_256_CBC_HMAC_SHA_512.P
139138
});
140139

141140
return theoryData;
142141
}
143142
}
144143

145-
public class AuthenticationEncryptionTestParams
144+
public class AuthenticationEncryptionTestParams : TheoryDataBase
146145
{
146+
public AuthenticationEncryptionTestParams() { }
147+
148+
public AuthenticationEncryptionTestParams(string testId) : base(testId) { }
149+
147150
public string Algorithm { get; set; }
148151
public byte[] AuthenticationData { get; set; }
149152
public byte[] AuthenticationTag { get; set; }
@@ -152,7 +155,6 @@ public class AuthenticationEncryptionTestParams
152155
public SecurityKey EncryptionKey { get; set; }
153156
public byte[] IV { get; set; }
154157
public byte[] Plaintext { get; set; }
155-
public string TestId { get; set; }
156158

157159
public override string ToString()
158160
{

0 commit comments

Comments
 (0)