CngGcmAuthenticatedEncryptionSettings.cs 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. // Copyright (c) .NET Foundation. All rights reserved.
  2. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
  3. using System;
  4. using Microsoft.AspNetCore.Cryptography;
  5. using Microsoft.AspNetCore.Cryptography.Cng;
  6. using Microsoft.AspNetCore.Cryptography.SafeHandles;
  7. using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
  8. using Microsoft.AspNetCore.DataProtection.Cng;
  9. using Microsoft.Extensions.Logging;
  10. namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
  11. {
  12. /// <summary>
  13. /// Settings for configuring an authenticated encryption mechanism which uses
  14. /// Windows CNG algorithms in GCM encryption + authentication modes.
  15. /// </summary>
  16. public sealed class CngGcmAuthenticatedEncryptionSettings : IInternalAuthenticatedEncryptionSettings
  17. {
  18. /// <summary>
  19. /// The name of the algorithm to use for symmetric encryption.
  20. /// This property corresponds to the 'pszAlgId' parameter of BCryptOpenAlgorithmProvider.
  21. /// This property is required to have a value.
  22. /// </summary>
  23. /// <remarks>
  24. /// The algorithm must support CBC-style encryption and must have a block size exactly
  25. /// 128 bits.
  26. /// The default value is 'AES'.
  27. /// </remarks>
  28. [ApplyPolicy]
  29. public string EncryptionAlgorithm { get; set; } = Constants.BCRYPT_AES_ALGORITHM;
  30. /// <summary>
  31. /// The name of the provider which contains the implementation of the symmetric encryption algorithm.
  32. /// This property corresponds to the 'pszImplementation' parameter of BCryptOpenAlgorithmProvider.
  33. /// This property is optional.
  34. /// </summary>
  35. /// <remarks>
  36. /// The default value is null.
  37. /// </remarks>
  38. [ApplyPolicy]
  39. public string EncryptionAlgorithmProvider { get; set; } = null;
  40. /// <summary>
  41. /// The length (in bits) of the key that will be used for symmetric encryption.
  42. /// This property is required to have a value.
  43. /// </summary>
  44. /// <remarks>
  45. /// The key length must be 128 bits or greater.
  46. /// The default value is 256.
  47. /// </remarks>
  48. [ApplyPolicy]
  49. public int EncryptionAlgorithmKeySize { get; set; } = 256;
  50. /// <summary>
  51. /// Validates that this <see cref="CngGcmAuthenticatedEncryptionSettings"/> is well-formed, i.e.,
  52. /// that the specified algorithm actually exists and can be instantiated properly.
  53. /// An exception will be thrown if validation fails.
  54. /// </summary>
  55. public void Validate()
  56. {
  57. // Run a sample payload through an encrypt -> decrypt operation to make sure data round-trips properly.
  58. using (var encryptor = CreateAuthenticatedEncryptorInstance(Secret.Random(512 / 8)))
  59. {
  60. encryptor.PerformSelfTest();
  61. }
  62. }
  63. /*
  64. * HELPER ROUTINES
  65. */
  66. internal GcmAuthenticatedEncryptor CreateAuthenticatedEncryptorInstance(ISecret secret, ILogger logger = null)
  67. {
  68. return new GcmAuthenticatedEncryptor(
  69. keyDerivationKey: new Secret(secret),
  70. symmetricAlgorithmHandle: GetSymmetricBlockCipherAlgorithmHandle(logger),
  71. symmetricAlgorithmKeySizeInBytes: (uint)(EncryptionAlgorithmKeySize / 8));
  72. }
  73. private BCryptAlgorithmHandle GetSymmetricBlockCipherAlgorithmHandle(ILogger logger)
  74. {
  75. // basic argument checking
  76. if (String.IsNullOrEmpty(EncryptionAlgorithm))
  77. {
  78. throw Error.Common_PropertyCannotBeNullOrEmpty(nameof(EncryptionAlgorithm));
  79. }
  80. if (EncryptionAlgorithmKeySize < 0)
  81. {
  82. throw Error.Common_PropertyMustBeNonNegative(nameof(EncryptionAlgorithmKeySize));
  83. }
  84. BCryptAlgorithmHandle algorithmHandle = null;
  85. logger?.OpeningCNGAlgorithmFromProviderWithChainingModeGCM(EncryptionAlgorithm, EncryptionAlgorithmProvider);
  86. // Special-case cached providers
  87. if (EncryptionAlgorithmProvider == null)
  88. {
  89. if (EncryptionAlgorithm == Constants.BCRYPT_AES_ALGORITHM) { algorithmHandle = CachedAlgorithmHandles.AES_GCM; }
  90. }
  91. // Look up the provider dynamically if we couldn't fetch a cached instance
  92. if (algorithmHandle == null)
  93. {
  94. algorithmHandle = BCryptAlgorithmHandle.OpenAlgorithmHandle(EncryptionAlgorithm, EncryptionAlgorithmProvider);
  95. algorithmHandle.SetChainingMode(Constants.BCRYPT_CHAIN_MODE_GCM);
  96. }
  97. // make sure we're using a block cipher with an appropriate key size & block size
  98. CryptoUtil.Assert(algorithmHandle.GetCipherBlockLength() == 128 / 8, "GCM requires a block cipher algorithm with a 128-bit block size.");
  99. AlgorithmAssert.IsAllowableSymmetricAlgorithmKeySize(checked((uint)EncryptionAlgorithmKeySize));
  100. // make sure the provided key length is valid
  101. algorithmHandle.GetSupportedKeyLengths().EnsureValidKeyLength((uint)EncryptionAlgorithmKeySize);
  102. // all good!
  103. return algorithmHandle;
  104. }
  105. IInternalAuthenticatedEncryptorConfiguration IInternalAuthenticatedEncryptionSettings.ToConfiguration(IServiceProvider services)
  106. {
  107. return new CngGcmAuthenticatedEncryptorConfiguration(this, services);
  108. }
  109. }
  110. }