You've already forked linux-packaging-mono
Imported Upstream version 4.6.0.125
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
This commit is contained in:
parent
a569aebcfd
commit
e79aa3c0ed
@@ -0,0 +1,99 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="AspNetCryptoServiceProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Web.Configuration;
|
||||
|
||||
// The central ASP.NET class for providing ICryptoService instances.
|
||||
// Get an instance of this class via the static Instance property.
|
||||
|
||||
internal sealed class AspNetCryptoServiceProvider : ICryptoServiceProvider {
|
||||
|
||||
private static readonly Lazy<AspNetCryptoServiceProvider> _singleton = new Lazy<AspNetCryptoServiceProvider>(GetSingletonCryptoServiceProvider);
|
||||
|
||||
private readonly ICryptoAlgorithmFactory _cryptoAlgorithmFactory;
|
||||
private readonly IDataProtectorFactory _dataProtectorFactory;
|
||||
private readonly bool _isDataProtectorEnabled;
|
||||
private KeyDerivationFunction _keyDerivationFunction;
|
||||
private readonly MachineKeySection _machineKeySection;
|
||||
private readonly IMasterKeyProvider _masterKeyProvider;
|
||||
|
||||
// This constructor is used only for testing purposes and by the singleton provider
|
||||
// and should not otherwise be called during ASP.NET request processing.
|
||||
internal AspNetCryptoServiceProvider(MachineKeySection machineKeySection = null, ICryptoAlgorithmFactory cryptoAlgorithmFactory = null, IMasterKeyProvider masterKeyProvider = null, IDataProtectorFactory dataProtectorFactory = null, KeyDerivationFunction keyDerivationFunction = null) {
|
||||
_machineKeySection = machineKeySection;
|
||||
_cryptoAlgorithmFactory = cryptoAlgorithmFactory;
|
||||
_masterKeyProvider = masterKeyProvider;
|
||||
_dataProtectorFactory = dataProtectorFactory;
|
||||
_keyDerivationFunction = keyDerivationFunction;
|
||||
|
||||
// This CryptoServiceProvider is active if specified as such in the <system.web/machineKey> section
|
||||
IsDefaultProvider = (machineKeySection != null && machineKeySection.CompatibilityMode >= MachineKeyCompatibilityMode.Framework45);
|
||||
|
||||
// The DataProtectorCryptoService is active if specified as such in config
|
||||
_isDataProtectorEnabled = (machineKeySection != null && !String.IsNullOrWhiteSpace(machineKeySection.DataProtectorType));
|
||||
}
|
||||
|
||||
internal static AspNetCryptoServiceProvider Instance {
|
||||
get {
|
||||
return _singleton.Value;
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a value indicating whether this crypto service provider is the default
|
||||
// provider for the current application.
|
||||
internal bool IsDefaultProvider {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public ICryptoService GetCryptoService(Purpose purpose, CryptoServiceOptions options = CryptoServiceOptions.None) {
|
||||
ICryptoService cryptoService;
|
||||
if (_isDataProtectorEnabled && options == CryptoServiceOptions.None) {
|
||||
// We can only use DataProtector if it's configured and the caller didn't ask for any special behavior like cacheability
|
||||
cryptoService = GetDataProtectorCryptoService(purpose);
|
||||
}
|
||||
else {
|
||||
// Otherwise we fall back to using the <machineKey> algorithms for cryptography
|
||||
cryptoService = GetNetFXCryptoService(purpose, options);
|
||||
}
|
||||
|
||||
// always homogenize errors returned from the crypto service
|
||||
return new HomogenizingCryptoServiceWrapper(cryptoService);
|
||||
}
|
||||
|
||||
private DataProtectorCryptoService GetDataProtectorCryptoService(Purpose purpose) {
|
||||
// just return the ICryptoService directly
|
||||
return new DataProtectorCryptoService(_dataProtectorFactory, purpose);
|
||||
}
|
||||
|
||||
private NetFXCryptoService GetNetFXCryptoService(Purpose purpose, CryptoServiceOptions options) {
|
||||
// Extract the encryption and validation keys from the provided Purpose object
|
||||
CryptographicKey encryptionKey = purpose.GetDerivedEncryptionKey(_masterKeyProvider, _keyDerivationFunction);
|
||||
CryptographicKey validationKey = purpose.GetDerivedValidationKey(_masterKeyProvider, _keyDerivationFunction);
|
||||
|
||||
// and return the ICryptoService
|
||||
// (predictable IV turned on if the caller requested cacheable output)
|
||||
return new NetFXCryptoService(_cryptoAlgorithmFactory, encryptionKey, validationKey, predictableIV: (options == CryptoServiceOptions.CacheableOutput));
|
||||
}
|
||||
|
||||
private static AspNetCryptoServiceProvider GetSingletonCryptoServiceProvider() {
|
||||
// Provides all of the necessary dependencies for an application-level
|
||||
// AspNetCryptoServiceProvider.
|
||||
|
||||
MachineKeySection machineKeySection = MachineKeySection.GetApplicationConfig();
|
||||
|
||||
return new AspNetCryptoServiceProvider(
|
||||
machineKeySection: machineKeySection,
|
||||
cryptoAlgorithmFactory: new MachineKeyCryptoAlgorithmFactory(machineKeySection),
|
||||
masterKeyProvider: new MachineKeyMasterKeyProvider(machineKeySection),
|
||||
dataProtectorFactory: new MachineKeyDataProtectorFactory(machineKeySection),
|
||||
keyDerivationFunction: SP800_108.DeriveKey);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="CryptoAlgorithms.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
// Utility class to provide the "one true way" of getting instances of
|
||||
// cryptographic algorithms, like SymmetricAlgorithm and HashAlgorithm.
|
||||
|
||||
// From discussions with [....] and the crypto board, we should prefer
|
||||
// the CNG implementations of algorithms, then the CAPI implementations,
|
||||
// then finally managed implementations if there are no CNG / CAPI
|
||||
// implementations. The CNG / CAPI implementations are preferred for
|
||||
// expandability, FIPS-compliance, and performance.
|
||||
//
|
||||
// .NET Framework 4.5 allows us to make two core assumptions:
|
||||
// - The built-in HMAC classes have been updated for FIPS compliance.
|
||||
// - Since .NET 4.5 requires Windows Server 2008 or greater, we can
|
||||
// assume that CNG is available on the box.
|
||||
//
|
||||
// Note that some algorithms (MD5, DES, etc.) aren't FIPS-compliant
|
||||
// under any circumstance. Calling these methods when the OS is
|
||||
// configured to allow only FIPS-compliant algorithms will result
|
||||
// in an exception being thrown.
|
||||
//
|
||||
// The .NET Framework's built-in algorithms don't need to be created
|
||||
// under the application impersonation context since they don't depend
|
||||
// on the impersonated identity.
|
||||
|
||||
internal static class CryptoAlgorithms {
|
||||
|
||||
internal static Aes CreateAes() {
|
||||
return new AesCryptoServiceProvider();
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Cryptographic.Standard", "CA5351:DESCannotBeUsed", Justification = @"This is only used by legacy code; new features do not use this algorithm.")]
|
||||
[Obsolete("DES is deprecated and MUST NOT be used by new features. Consider using AES instead.")]
|
||||
internal static DES CreateDES() {
|
||||
return new DESCryptoServiceProvider();
|
||||
}
|
||||
|
||||
internal static HMACSHA1 CreateHMACSHA1() {
|
||||
return new HMACSHA1();
|
||||
}
|
||||
|
||||
internal static HMACSHA256 CreateHMACSHA256() {
|
||||
return new HMACSHA256();
|
||||
}
|
||||
|
||||
internal static HMACSHA384 CreateHMACSHA384() {
|
||||
return new HMACSHA384();
|
||||
}
|
||||
|
||||
internal static HMACSHA512 CreateHMACSHA512() {
|
||||
return new HMACSHA512();
|
||||
}
|
||||
|
||||
internal static HMACSHA512 CreateHMACSHA512(byte[] key) {
|
||||
return new HMACSHA512(key);
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Cryptographic.Standard", "CA5350:MD5CannotBeUsed", Justification = @"This is only used by legacy code; new features do not use this algorithm.")]
|
||||
[Obsolete("MD5 is deprecated and MUST NOT be used by new features. Consider using a SHA-2 algorithm instead.")]
|
||||
internal static MD5 CreateMD5() {
|
||||
return new MD5Cng();
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Cryptographic.Standard", "CA5354:SHA1CannotBeUsed", Justification = @"This is only used by legacy code; new features do not use this algorithm.")]
|
||||
[Obsolete("SHA1 is deprecated and MUST NOT be used by new features. Consider using a SHA-2 algorithm instead.")]
|
||||
internal static SHA1 CreateSHA1() {
|
||||
return new SHA1Cng();
|
||||
}
|
||||
|
||||
internal static SHA256 CreateSHA256() {
|
||||
return new SHA256Cng();
|
||||
}
|
||||
|
||||
[SuppressMessage("Microsoft.Cryptographic.Standard", "CA5353:TripleDESCannotBeUsed", Justification = @"This is only used by legacy code; new features do not use this algorithm.")]
|
||||
[Obsolete("3DES is deprecated and MUST NOT be used by new features. Consider using AES instead.")]
|
||||
internal static TripleDES CreateTripleDES() {
|
||||
return new TripleDESCryptoServiceProvider();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="CryptoServiceOptions.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
|
||||
// Describes options that can configure an ICryptoService.
|
||||
|
||||
internal enum CryptoServiceOptions {
|
||||
|
||||
// [default] no special behavior needed
|
||||
None = 0,
|
||||
|
||||
// the output of the Protect method will be cached, so the same plaintext should lead to the same ciphertext (no randomness)
|
||||
CacheableOutput,
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,156 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="CryptoUtil.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Web.Util;
|
||||
|
||||
// Contains helper methods for dealing with cryptographic operations.
|
||||
|
||||
internal static class CryptoUtil {
|
||||
|
||||
/// <summary>
|
||||
/// Similar to Encoding.UTF8, but throws on invalid bytes. Useful for security routines where we need
|
||||
/// strong guarantees that we're always producing valid UTF8 streams.
|
||||
/// </summary>
|
||||
public static readonly UTF8Encoding SecureUTF8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
|
||||
|
||||
/// <summary>
|
||||
/// Converts a byte array into its hexadecimal representation.
|
||||
/// </summary>
|
||||
/// <param name="data">The binary byte array.</param>
|
||||
/// <returns>The hexadecimal (uppercase) equivalent of the byte array.</returns>
|
||||
public static string BinaryToHex(byte[] data) {
|
||||
if (data == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
char[] hex = new char[checked(data.Length * 2)];
|
||||
|
||||
for (int i = 0; i < data.Length; i++) {
|
||||
byte thisByte = data[i];
|
||||
hex[2 * i] = NibbleToHex((byte)(thisByte >> 4)); // high nibble
|
||||
hex[2 * i + 1] = NibbleToHex((byte)(thisByte & 0xf)); // low nibble
|
||||
}
|
||||
|
||||
return new string(hex);
|
||||
}
|
||||
|
||||
// Determines if two buffer instances are equal, e.g. whether they contain the same payload. This method
|
||||
// is written in such a manner that it should take the same amount of time to execute regardless of
|
||||
// whether the result is success or failure. The modulus operation is intended to make the check take the
|
||||
// same amount of time, even if the buffers are of different lengths.
|
||||
//
|
||||
// !! DO NOT CHANGE THIS METHOD WITHOUT SECURITY
|
||||
[MethodImpl(MethodImplOptions.NoOptimization)]
|
||||
public static bool BuffersAreEqual(byte[] buffer1, int buffer1Offset, int buffer1Count, byte[] buffer2, int buffer2Offset, int buffer2Count) {
|
||||
Debug.ValidateArrayBounds(buffer1, buffer1Offset, buffer1Count);
|
||||
Debug.ValidateArrayBounds(buffer2, buffer2Offset, buffer2Count);
|
||||
|
||||
bool success = (buffer1Count == buffer2Count); // can't possibly be successful if the buffers are of different lengths
|
||||
for (int i = 0; i < buffer1Count; i++) {
|
||||
success &= (buffer1[buffer1Offset + i] == buffer2[buffer2Offset + (i % buffer2Count)]);
|
||||
}
|
||||
return success;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the SHA256 hash of a given input.
|
||||
/// </summary>
|
||||
/// <param name="input">The input over which to compute the hash.</param>
|
||||
/// <returns>The binary hash (32 bytes) of the input.</returns>
|
||||
public static byte[] ComputeSHA256Hash(byte[] input) {
|
||||
return ComputeSHA256Hash(input, 0, input.Length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the SHA256 hash of a given segment in a buffer.
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer over which to compute the hash.</param>
|
||||
/// <param name="offset">The offset at which to begin computing the hash.</param>
|
||||
/// <param name="count">The number of bytes in the buffer to include in the hash.</param>
|
||||
/// <returns>The binary hash (32 bytes) of the buffer segment.</returns>
|
||||
public static byte[] ComputeSHA256Hash(byte[] buffer, int offset, int count) {
|
||||
Debug.ValidateArrayBounds(buffer, offset, count);
|
||||
|
||||
using (SHA256 sha256 = CryptoAlgorithms.CreateSHA256()) {
|
||||
return sha256.ComputeHash(buffer, offset, count);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns an IV that's based solely on the contents of a buffer; useful for generating
|
||||
/// predictable IVs for ciphertexts that need to be cached. The output value is only
|
||||
/// appropriate for use as an IV and must not be used for any other purpose.
|
||||
/// </summary>
|
||||
/// <remarks>This method uses an iterated unkeyed SHA256 to calculate the IV.</remarks>
|
||||
/// <param name="buffer">The input buffer over which to calculate the IV.</param>
|
||||
/// <param name="ivBitLength">The requested length (in bits) of the IV to generate.</param>
|
||||
/// <returns>The calculated IV.</returns>
|
||||
public static byte[] CreatePredictableIV(byte[] buffer, int ivBitLength) {
|
||||
// Algorithm:
|
||||
// T_0 = SHA256(buffer)
|
||||
// T_n = SHA256(T_{n-1})
|
||||
// output = T_0 || T_1 || ... || T_n (as many blocks as necessary to reach ivBitLength)
|
||||
|
||||
byte[] output = new byte[ivBitLength / 8];
|
||||
int bytesCopied = 0;
|
||||
int bytesRemaining = output.Length;
|
||||
|
||||
using (SHA256 sha256 = CryptoAlgorithms.CreateSHA256()) {
|
||||
while (bytesRemaining > 0) {
|
||||
byte[] hashed = sha256.ComputeHash(buffer);
|
||||
|
||||
int bytesToCopy = Math.Min(bytesRemaining, hashed.Length);
|
||||
Buffer.BlockCopy(hashed, 0, output, bytesCopied, bytesToCopy);
|
||||
|
||||
bytesCopied += bytesToCopy;
|
||||
bytesRemaining -= bytesToCopy;
|
||||
|
||||
buffer = hashed; // next iteration (if it occurs) will operate over the block just hashed
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a hexadecimal string into its binary representation.
|
||||
/// </summary>
|
||||
/// <param name="data">The hex string.</param>
|
||||
/// <returns>The byte array corresponding to the contents of the hex string,
|
||||
/// or null if the input string is not a valid hex string.</returns>
|
||||
public static byte[] HexToBinary(string data) {
|
||||
if (data == null || data.Length % 2 != 0) {
|
||||
// input string length is not evenly divisible by 2
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] binary = new byte[data.Length / 2];
|
||||
|
||||
for (int i = 0; i < binary.Length; i++) {
|
||||
int highNibble = HttpEncoderUtility.HexToInt(data[2 * i]);
|
||||
int lowNibble = HttpEncoderUtility.HexToInt(data[2 * i + 1]);
|
||||
|
||||
if (highNibble == -1 || lowNibble == -1) {
|
||||
return null; // bad hex data
|
||||
}
|
||||
binary[i] = (byte)((highNibble << 4) | lowNibble);
|
||||
}
|
||||
|
||||
return binary;
|
||||
}
|
||||
|
||||
// converts a nibble (4 bits) to its uppercase hexadecimal character representation [0-9, A-F]
|
||||
private static char NibbleToHex(byte nibble) {
|
||||
return (char)((nibble < 10) ? (nibble + '0') : (nibble - 10 + 'A'));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,51 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="CryptographicKey.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Web.Util;
|
||||
|
||||
// Represents a key that can be used for a cryptographic operation.
|
||||
|
||||
internal sealed class CryptographicKey {
|
||||
|
||||
private readonly byte[] _keyMaterial;
|
||||
|
||||
public CryptographicKey(byte[] keyMaterial) {
|
||||
_keyMaterial = keyMaterial;
|
||||
}
|
||||
|
||||
// Returns the length of the key (in bits).
|
||||
public int KeyLength {
|
||||
get {
|
||||
return checked(_keyMaterial.Length * 8);
|
||||
}
|
||||
}
|
||||
|
||||
// Extracts the specified number of bits at the specified offset
|
||||
// and returns a new CryptographicKey. This is not appropriate
|
||||
// for subkey derivation, but it can be used if this cryptographic
|
||||
// key is actually two keys (like encryption + validation)
|
||||
// concatenated together. Inputs are specified as bit lengths.
|
||||
public CryptographicKey ExtractBits(int offset, int count) {
|
||||
Debug.Assert(offset % 8 == 0, "Offset must be divisible by 8.");
|
||||
Debug.Assert(count % 8 == 0, "Count must be divisible by 8.");
|
||||
|
||||
int offsetBytes = offset / 8;
|
||||
int countBytes = count / 8;
|
||||
|
||||
byte[] newKey = new byte[countBytes];
|
||||
Buffer.BlockCopy(_keyMaterial, offsetBytes, newKey, 0, countBytes);
|
||||
return new CryptographicKey(newKey);
|
||||
}
|
||||
|
||||
// Returns the raw key material as a byte array.
|
||||
public byte[] GetKeyMaterial() {
|
||||
return _keyMaterial;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="DataProtectorCryptoService.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
// Uses the DataProtector class to protect sensitive information
|
||||
|
||||
internal sealed class DataProtectorCryptoService : ICryptoService {
|
||||
|
||||
private readonly IDataProtectorFactory _dataProtectorFactory;
|
||||
private readonly Purpose _purpose;
|
||||
|
||||
public DataProtectorCryptoService(IDataProtectorFactory dataProtectorFactory, Purpose purpose) {
|
||||
_dataProtectorFactory = dataProtectorFactory;
|
||||
_purpose = purpose;
|
||||
}
|
||||
|
||||
// Wraps the common logic of working with a DataProtector instance.
|
||||
// 'protect' is TRUE if we're calling Protect, FALSE if we're calling Unprotect.
|
||||
private byte[] PerformOperation(byte[] data, bool protect) {
|
||||
// Since the DataProtector might depend on the impersonated context, we must
|
||||
// work with it only under app-level impersonation. The idea behind this is
|
||||
// that if the cryptographic routine is provided by an OS-level implementation
|
||||
// (like DPAPI), any keys will be locked to the account of the web application
|
||||
// itself.
|
||||
using (new ApplicationImpersonationContext()) {
|
||||
DataProtector dataProtector = null;
|
||||
try {
|
||||
dataProtector = _dataProtectorFactory.GetDataProtector(_purpose);
|
||||
return (protect) ? dataProtector.Protect(data) : dataProtector.Unprotect(data);
|
||||
}
|
||||
finally {
|
||||
// These instances are transient
|
||||
IDisposable disposable = dataProtector as IDisposable;
|
||||
if (disposable != null) {
|
||||
disposable.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Protect(byte[] clearData) {
|
||||
return PerformOperation(clearData, protect: true);
|
||||
}
|
||||
|
||||
public byte[] Unprotect(byte[] protectedData) {
|
||||
return PerformOperation(protectedData, protect: false);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="HomogenizingCryptoServiceWrapper.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
// Wraps an ICryptoService instance and homogenizes any exceptions that might occur.
|
||||
|
||||
internal sealed class HomogenizingCryptoServiceWrapper : ICryptoService {
|
||||
|
||||
public HomogenizingCryptoServiceWrapper(ICryptoService wrapped) {
|
||||
WrappedCryptoService = wrapped;
|
||||
}
|
||||
|
||||
internal ICryptoService WrappedCryptoService {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private static byte[] HomogenizeErrors(Func<byte[], byte[]> func, byte[] input) {
|
||||
// If the underlying method returns null or throws an exception, the
|
||||
// error will be homogenized as a single CryptographicException.
|
||||
|
||||
byte[] output = null;
|
||||
bool allowExceptionToBubble = false;
|
||||
|
||||
try {
|
||||
output = func(input);
|
||||
return output;
|
||||
}
|
||||
catch (ConfigurationException) {
|
||||
// ConfigurationException isn't a side channel; it means the application is misconfigured.
|
||||
// We need to bubble this up so that the developer can react to it.
|
||||
allowExceptionToBubble = true;
|
||||
throw;
|
||||
}
|
||||
finally {
|
||||
if (output == null && !allowExceptionToBubble) {
|
||||
throw new CryptographicException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Protect(byte[] clearData) {
|
||||
return HomogenizeErrors(WrappedCryptoService.Protect, clearData);
|
||||
}
|
||||
|
||||
public byte[] Unprotect(byte[] protectedData) {
|
||||
return HomogenizeErrors(WrappedCryptoService.Unprotect, protectedData);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ICryptoAlgorithmFactory.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
// Represents an object that can provide encryption + validation algorithm instances
|
||||
|
||||
internal interface ICryptoAlgorithmFactory {
|
||||
|
||||
// Gets a SymmetricAlgorithm instance that can be used for encryption / decryption
|
||||
SymmetricAlgorithm GetEncryptionAlgorithm();
|
||||
|
||||
// Gets a KeyedHashAlgorithm instance that can be used for signing / validation
|
||||
KeyedHashAlgorithm GetValidationAlgorithm();
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ICryptoService.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
|
||||
// Represents an object that can perform cryptographic operations.
|
||||
// Get an instance of this class via an ICryptoServiceProvider (like AspNetCryptoServiceProvider).
|
||||
|
||||
internal interface ICryptoService {
|
||||
|
||||
// Protects some data by applying appropriate cryptographic transformations to it.
|
||||
byte[] Protect(byte[] clearData);
|
||||
|
||||
// Returns the unprotected form of some protected data by validating and undoing the cryptographic transformations that led to it.
|
||||
byte[] Unprotect(byte[] protectedData);
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="ICryptoServiceProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
|
||||
// Represents an object that can provide ICryptoService instances.
|
||||
// Get an instance of this type via the AspNetCryptoServiceProvider.Instance singleton property.
|
||||
|
||||
internal interface ICryptoServiceProvider {
|
||||
|
||||
ICryptoService GetCryptoService(Purpose purpose, CryptoServiceOptions options = CryptoServiceOptions.None);
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,18 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="IDataProtectorFactory.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
// Represents an object that can provide DataProtector instances
|
||||
|
||||
internal interface IDataProtectorFactory {
|
||||
|
||||
DataProtector GetDataProtector(Purpose purpose);
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,21 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="IMasterKeyProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
|
||||
// Represents an object that can provide master encryption / validation keys
|
||||
|
||||
internal interface IMasterKeyProvider {
|
||||
|
||||
// encryption + decryption key
|
||||
CryptographicKey GetEncryptionKey();
|
||||
|
||||
// signing + validation key
|
||||
CryptographicKey GetValidationKey();
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="KeyDerivationFunction.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
|
||||
// A delegate that represents a cryptographic key derivation function (KDF).
|
||||
// A KDF takes a master key (the key derivation key) and a purpose string,
|
||||
// producing a derived key in the process.
|
||||
internal delegate CryptographicKey KeyDerivationFunction(CryptographicKey keyDerivationKey, Purpose purpose);
|
||||
|
||||
}
|
@@ -0,0 +1,135 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="MachineKeyCryptoAlgorithmFactory.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Web.Configuration;
|
||||
|
||||
// Can create cryptographic algorithms from a given <machineKey> element
|
||||
|
||||
internal sealed class MachineKeyCryptoAlgorithmFactory : ICryptoAlgorithmFactory {
|
||||
|
||||
private Func<SymmetricAlgorithm> _encryptionAlgorithmFactory;
|
||||
private readonly MachineKeySection _machineKeySection;
|
||||
private Func<KeyedHashAlgorithm> _validationAlgorithmFactory;
|
||||
|
||||
public MachineKeyCryptoAlgorithmFactory(MachineKeySection machineKeySection) {
|
||||
_machineKeySection = machineKeySection;
|
||||
}
|
||||
|
||||
public SymmetricAlgorithm GetEncryptionAlgorithm() {
|
||||
if (_encryptionAlgorithmFactory == null) {
|
||||
_encryptionAlgorithmFactory = GetEncryptionAlgorithmFactory();
|
||||
}
|
||||
return _encryptionAlgorithmFactory();
|
||||
}
|
||||
|
||||
private Func<SymmetricAlgorithm> GetEncryptionAlgorithmFactory() {
|
||||
return GetGenericAlgorithmFactory<SymmetricAlgorithm>(
|
||||
configAttributeName: "decryption",
|
||||
configAttributeValue: _machineKeySection.GetDecryptionAttributeSkipValidation(),
|
||||
switchStatement: algorithmName => {
|
||||
// We suppress CS0618 since some of the algorithms we support are marked with [Obsolete].
|
||||
// These deprecated algorithms are *not* enabled by default. Developers must opt-in to
|
||||
// them, so we're secure by default.
|
||||
#pragma warning disable 618
|
||||
switch (algorithmName) {
|
||||
case "AES":
|
||||
case "Auto": // currently "Auto" defaults to AES
|
||||
return CryptoAlgorithms.CreateAes;
|
||||
|
||||
case "DES":
|
||||
return CryptoAlgorithms.CreateDES;
|
||||
|
||||
case "3DES":
|
||||
return CryptoAlgorithms.CreateTripleDES;
|
||||
|
||||
default:
|
||||
return null; // unknown
|
||||
#pragma warning restore 618
|
||||
}
|
||||
},
|
||||
errorResourceString: SR.Wrong_decryption_enum);
|
||||
}
|
||||
|
||||
public KeyedHashAlgorithm GetValidationAlgorithm() {
|
||||
if (_validationAlgorithmFactory == null) {
|
||||
_validationAlgorithmFactory = GetValidationAlgorithmFactory();
|
||||
}
|
||||
return _validationAlgorithmFactory();
|
||||
}
|
||||
|
||||
private Func<KeyedHashAlgorithm> GetValidationAlgorithmFactory() {
|
||||
return GetGenericAlgorithmFactory<KeyedHashAlgorithm>(
|
||||
configAttributeName: "validation",
|
||||
configAttributeValue: _machineKeySection.GetValidationAttributeSkipValidation(),
|
||||
switchStatement: algorithmName => {
|
||||
switch (algorithmName) {
|
||||
case "SHA1":
|
||||
return CryptoAlgorithms.CreateHMACSHA1;
|
||||
|
||||
case "HMACSHA256":
|
||||
return CryptoAlgorithms.CreateHMACSHA256;
|
||||
|
||||
case "HMACSHA384":
|
||||
return CryptoAlgorithms.CreateHMACSHA384;
|
||||
|
||||
case "HMACSHA512":
|
||||
return CryptoAlgorithms.CreateHMACSHA512;
|
||||
|
||||
default:
|
||||
return null; // unknown
|
||||
}
|
||||
},
|
||||
errorResourceString: SR.Wrong_validation_enum_FX45);
|
||||
}
|
||||
|
||||
// Contains common logic for creating encryption / validation factories, including
|
||||
// custom algorithm lookup and exception handling.
|
||||
private Func<TResult> GetGenericAlgorithmFactory<TResult>(string configAttributeName, string configAttributeValue, Func<string, Func<TResult>> switchStatement, string errorResourceString) where TResult : class, IDisposable {
|
||||
Func<TResult> factory;
|
||||
|
||||
if (configAttributeValue != null && configAttributeValue.StartsWith("alg:", StringComparison.Ordinal)) {
|
||||
string algorithmName = configAttributeValue.Substring("alg:".Length);
|
||||
factory = () => {
|
||||
// Since the custom algorithm might depend on the impersonated
|
||||
// identity, we must instantiate it under app-level impersonation.
|
||||
using (new ApplicationImpersonationContext()) {
|
||||
return (TResult)CryptoConfig.CreateFromName(algorithmName);
|
||||
}
|
||||
};
|
||||
}
|
||||
else {
|
||||
// If using a built-in algorithm, consult the switch statement to get the factory.
|
||||
factory = switchStatement(configAttributeValue);
|
||||
}
|
||||
|
||||
// Invoke the factory once to make sure there aren't any configuration errors.
|
||||
Exception factoryCreationException = null;
|
||||
try {
|
||||
if (factory != null) {
|
||||
TResult algorithm = factory();
|
||||
if (algorithm != null) {
|
||||
algorithm.Dispose();
|
||||
return factory; // we know at this point the factory is good
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
factoryCreationException = ex;
|
||||
}
|
||||
|
||||
// If we reached this point, there was a failure:
|
||||
// the factory returned null, threw, or did something else unexpected.
|
||||
throw ConfigUtil.MakeConfigurationErrorsException(
|
||||
message: SR.GetString(errorResourceString),
|
||||
innerException: factoryCreationException, // can be null
|
||||
configProperty: _machineKeySection.ElementInformation.Properties[configAttributeName]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="MachineKeyDataProtectorFactory.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Web.Configuration;
|
||||
|
||||
// Can create DataProtector instances from a given <machineKey> element
|
||||
|
||||
internal sealed class MachineKeyDataProtectorFactory : IDataProtectorFactory {
|
||||
|
||||
private static readonly Purpose _creationTestingPurpose = new Purpose("test-1", "test-2", "test-3");
|
||||
|
||||
private Func<Purpose, DataProtector> _dataProtectorFactory;
|
||||
private readonly MachineKeySection _machineKeySection;
|
||||
|
||||
public MachineKeyDataProtectorFactory(MachineKeySection machineKeySection) {
|
||||
_machineKeySection = machineKeySection;
|
||||
}
|
||||
|
||||
public DataProtector GetDataProtector(Purpose purpose) {
|
||||
if (_dataProtectorFactory == null) {
|
||||
_dataProtectorFactory = GetDataProtectorFactory();
|
||||
}
|
||||
return _dataProtectorFactory(purpose);
|
||||
}
|
||||
|
||||
private Func<Purpose, DataProtector> GetDataProtectorFactory() {
|
||||
string applicationName = _machineKeySection.ApplicationName;
|
||||
string dataProtectorTypeName = _machineKeySection.DataProtectorType;
|
||||
|
||||
Func<Purpose, DataProtector> factory = purpose => {
|
||||
// Since the custom implementation might depend on the impersonated
|
||||
// identity, we must instantiate it under app-level impersonation.
|
||||
using (new ApplicationImpersonationContext()) {
|
||||
return DataProtector.Create(dataProtectorTypeName, applicationName, purpose.PrimaryPurpose, purpose.SpecificPurposes);
|
||||
}
|
||||
};
|
||||
|
||||
// Invoke the factory once to make sure there aren't any configuration errors.
|
||||
Exception factoryCreationException = null;
|
||||
try {
|
||||
DataProtector dataProtector = factory(_creationTestingPurpose);
|
||||
if (dataProtector != null) {
|
||||
IDisposable disposable = dataProtector as IDisposable;
|
||||
if (disposable != null) {
|
||||
disposable.Dispose();
|
||||
}
|
||||
return factory; // we know at this point the factory is good
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
factoryCreationException = ex;
|
||||
}
|
||||
|
||||
// If we reached this point, there was a failure:
|
||||
// the factory returned null, threw, or did something else unexpected.
|
||||
throw ConfigUtil.MakeConfigurationErrorsException(
|
||||
message: SR.GetString(SR.MachineKeyDataProtectorFactory_FactoryCreationFailed),
|
||||
innerException: factoryCreationException, // can be null
|
||||
configProperty: _machineKeySection.ElementInformation.Properties["dataProtectorType"]);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,174 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="MachineKeyMasterKeyProvider.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Web.Configuration;
|
||||
|
||||
// Gets this application's master keys from the <machineKey> element,
|
||||
// optionally going against the auto-gen keys if AutoGenerate has been specified.
|
||||
|
||||
internal sealed class MachineKeyMasterKeyProvider : IMasterKeyProvider {
|
||||
|
||||
private const int AUTOGEN_ENCRYPTION_OFFSET = 0;
|
||||
private const int AUTOGEN_ENCRYPTION_KEYLENGTH = 256; // AES-256
|
||||
private const int AUTOGEN_VALIDATION_OFFSET = AUTOGEN_ENCRYPTION_KEYLENGTH;
|
||||
private const int AUTOGEN_VALIDATION_KEYLENGTH = 256; // HMACSHA256
|
||||
|
||||
private const string AUTOGEN_KEYDERIVATION_PRIMARYPURPOSE = "MachineKeyDerivation";
|
||||
private const string AUTOGEN_KEYDERIVATION_ISOLATEAPPS_SPECIFICPURPOSE = "IsolateApps";
|
||||
private const string AUTOGEN_KEYDERIVATION_ISOLATEBYAPPID_SPECIFICPURPOSE = "IsolateByAppId";
|
||||
|
||||
private string _applicationId;
|
||||
private string _applicationName;
|
||||
private CryptographicKey _autogenKeys;
|
||||
private CryptographicKey _encryptionKey;
|
||||
private KeyDerivationFunction _keyDerivationFunction;
|
||||
private readonly MachineKeySection _machineKeySection;
|
||||
private CryptographicKey _validationKey;
|
||||
|
||||
// the only required parameter is 'machineKeySection'; other parameters are just used for unit testing
|
||||
internal MachineKeyMasterKeyProvider(MachineKeySection machineKeySection, string applicationId = null, string applicationName = null, CryptographicKey autogenKeys = null, KeyDerivationFunction keyDerivationFunction = null) {
|
||||
_machineKeySection = machineKeySection;
|
||||
_applicationId = applicationId;
|
||||
_applicationName = applicationName;
|
||||
_autogenKeys = autogenKeys;
|
||||
_keyDerivationFunction = keyDerivationFunction;
|
||||
}
|
||||
|
||||
internal string ApplicationName {
|
||||
get {
|
||||
if (_applicationName == null) {
|
||||
_applicationName = HttpRuntime.AppDomainAppVirtualPath ?? Process.GetCurrentProcess().MainModule.ModuleName;
|
||||
}
|
||||
return _applicationName;
|
||||
}
|
||||
}
|
||||
|
||||
internal string ApplicationId {
|
||||
get {
|
||||
if (_applicationId == null) {
|
||||
_applicationId = HttpRuntime.AppDomainAppId;
|
||||
}
|
||||
return _applicationId;
|
||||
}
|
||||
}
|
||||
|
||||
internal CryptographicKey AutogenKeys {
|
||||
get {
|
||||
if (_autogenKeys == null) {
|
||||
_autogenKeys = new CryptographicKey(HttpRuntime.s_autogenKeys);
|
||||
}
|
||||
return _autogenKeys;
|
||||
}
|
||||
}
|
||||
|
||||
internal KeyDerivationFunction KeyDerivationFunction {
|
||||
get {
|
||||
if (_keyDerivationFunction == null) {
|
||||
_keyDerivationFunction = SP800_108.DeriveKey;
|
||||
}
|
||||
return _keyDerivationFunction;
|
||||
}
|
||||
}
|
||||
|
||||
private static void AddSpecificPurposeString(IList<string> specificPurposes, string key, string value) {
|
||||
specificPurposes.Add(key + ": " + value);
|
||||
}
|
||||
|
||||
// Generates 'cryptographicKey' from either the raw key material specified in config
|
||||
// or from the auto-generated key found in the system registry, optionally performing
|
||||
// subkey derivation.
|
||||
private CryptographicKey GenerateCryptographicKey(string configAttributeName, string configAttributeValue, int autogenKeyOffset, int autogenKeyCount, string errorResourceString) {
|
||||
byte[] keyMaterial = CryptoUtil.HexToBinary(configAttributeValue);
|
||||
|
||||
// If <machineKey> contained a valid key, just use it verbatim.
|
||||
if (keyMaterial != null && keyMaterial.Length > 0) {
|
||||
return new CryptographicKey(keyMaterial);
|
||||
}
|
||||
|
||||
// Otherwise, we need to generate it.
|
||||
bool autoGenerate = false;
|
||||
bool isolateApps = false;
|
||||
bool isolateByAppId = false;
|
||||
|
||||
if (configAttributeValue != null) {
|
||||
foreach (string flag in configAttributeValue.Split(',')) {
|
||||
switch (flag) {
|
||||
case "AutoGenerate":
|
||||
autoGenerate = true;
|
||||
break;
|
||||
|
||||
case "IsolateApps":
|
||||
isolateApps = true;
|
||||
break;
|
||||
|
||||
case "IsolateByAppId":
|
||||
isolateByAppId = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw ConfigUtil.MakeConfigurationErrorsException(
|
||||
message: SR.GetString(errorResourceString),
|
||||
configProperty: _machineKeySection.ElementInformation.Properties[configAttributeName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!autoGenerate) {
|
||||
// at the absolute minimum, we must be configured to autogenerate
|
||||
throw ConfigUtil.MakeConfigurationErrorsException(
|
||||
message: SR.GetString(errorResourceString),
|
||||
configProperty: _machineKeySection.ElementInformation.Properties[configAttributeName]);
|
||||
}
|
||||
|
||||
// The key should be a subset of the auto-generated key (which is a concatenation of several keys)
|
||||
CryptographicKey keyDerivationKey = AutogenKeys.ExtractBits(autogenKeyOffset, autogenKeyCount);
|
||||
List<string> specificPurposes = new List<string>();
|
||||
|
||||
if (isolateApps) {
|
||||
// Use the application name to derive a new cryptographic key
|
||||
AddSpecificPurposeString(specificPurposes, AUTOGEN_KEYDERIVATION_ISOLATEAPPS_SPECIFICPURPOSE, ApplicationName);
|
||||
}
|
||||
|
||||
if (isolateByAppId) {
|
||||
// Use the application ID to derive a new cryptographic key
|
||||
AddSpecificPurposeString(specificPurposes, AUTOGEN_KEYDERIVATION_ISOLATEBYAPPID_SPECIFICPURPOSE, ApplicationId);
|
||||
}
|
||||
|
||||
// Don't use the auto-gen key directly; derive a new one based on specified parameters.
|
||||
Purpose purpose = new Purpose(AUTOGEN_KEYDERIVATION_PRIMARYPURPOSE, specificPurposes.ToArray());
|
||||
return KeyDerivationFunction(keyDerivationKey, purpose);
|
||||
}
|
||||
|
||||
public CryptographicKey GetEncryptionKey() {
|
||||
if (_encryptionKey == null) {
|
||||
_encryptionKey = GenerateCryptographicKey(
|
||||
configAttributeName: "decryptionKey",
|
||||
configAttributeValue: _machineKeySection.DecryptionKey,
|
||||
autogenKeyOffset: AUTOGEN_ENCRYPTION_OFFSET,
|
||||
autogenKeyCount: AUTOGEN_ENCRYPTION_KEYLENGTH,
|
||||
errorResourceString: SR.Invalid_decryption_key);
|
||||
}
|
||||
return _encryptionKey;
|
||||
}
|
||||
|
||||
public CryptographicKey GetValidationKey() {
|
||||
if (_validationKey == null) {
|
||||
_validationKey = GenerateCryptographicKey(
|
||||
configAttributeName: "validationKey",
|
||||
configAttributeValue: _machineKeySection.ValidationKey,
|
||||
autogenKeyOffset: AUTOGEN_VALIDATION_OFFSET,
|
||||
autogenKeyCount: AUTOGEN_VALIDATION_KEYLENGTH,
|
||||
errorResourceString: SR.Invalid_validation_key);
|
||||
}
|
||||
return _validationKey;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,186 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="NetFXCryptoService.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
/******************************************************************
|
||||
* !! WARNING !! *
|
||||
* This class contains cryptographic code. If you make changes to *
|
||||
* this class, please have it reviewed by the appropriate people. *
|
||||
******************************************************************/
|
||||
|
||||
// Uses .NET Framework classes to encrypt (SymmetricAlgorithm) and sign (KeyedHashAlgorithm) data.
|
||||
//
|
||||
// [PROTECT]
|
||||
// INPUT: clearData
|
||||
// OUTPUT: protectedData
|
||||
// ALGORITHM:
|
||||
// protectedData := IV || Enc(Kenc, IV, clearData) || Sign(Kval, IV || Enc(Kenc, IV, clearData))
|
||||
//
|
||||
// [UNPROTECT]
|
||||
// INPUT: protectedData
|
||||
// OUTPUT: clearData
|
||||
// ALGORITHM:
|
||||
// 1) Assume protectedData := IV || Enc(Kenc, IV, clearData) || Sign(Kval, IV || Enc(Kenc, IV, clearData))
|
||||
// 2) Validate the signature over the payload and strip it from the end
|
||||
// 3) Strip off the IV from the beginning of the payload
|
||||
// 4) Decrypt what remains of the payload, and return it as clearData
|
||||
|
||||
internal sealed class NetFXCryptoService : ICryptoService {
|
||||
|
||||
private readonly ICryptoAlgorithmFactory _cryptoAlgorithmFactory;
|
||||
private readonly CryptographicKey _encryptionKey;
|
||||
private readonly bool _predictableIV;
|
||||
private readonly CryptographicKey _validationKey;
|
||||
|
||||
public NetFXCryptoService(ICryptoAlgorithmFactory cryptoAlgorithmFactory, CryptographicKey encryptionKey, CryptographicKey validationKey, bool predictableIV = false) {
|
||||
_cryptoAlgorithmFactory = cryptoAlgorithmFactory;
|
||||
_encryptionKey = encryptionKey;
|
||||
_validationKey = validationKey;
|
||||
_predictableIV = predictableIV;
|
||||
}
|
||||
|
||||
public byte[] Protect(byte[] clearData) {
|
||||
// The entire operation is wrapped in a 'checked' block because any overflows should be treated as failures.
|
||||
checked {
|
||||
|
||||
// These SymmetricAlgorithm instances are single-use; we wrap it in a 'using' block.
|
||||
using (SymmetricAlgorithm encryptionAlgorithm = _cryptoAlgorithmFactory.GetEncryptionAlgorithm()) {
|
||||
// Initialize the algorithm with the specified key and an appropriate IV
|
||||
encryptionAlgorithm.Key = _encryptionKey.GetKeyMaterial();
|
||||
|
||||
if (_predictableIV) {
|
||||
// The caller wanted the output to be predictable (e.g. for caching), so we'll create an
|
||||
// appropriate IV directly from the input buffer. The IV length is equal to the block size.
|
||||
encryptionAlgorithm.IV = CryptoUtil.CreatePredictableIV(clearData, encryptionAlgorithm.BlockSize);
|
||||
}
|
||||
else {
|
||||
// If the caller didn't ask for a predictable IV, just let the algorithm itself choose one.
|
||||
encryptionAlgorithm.GenerateIV();
|
||||
}
|
||||
byte[] iv = encryptionAlgorithm.IV;
|
||||
|
||||
using (MemoryStream memStream = new MemoryStream()) {
|
||||
memStream.Write(iv, 0, iv.Length);
|
||||
|
||||
// At this point:
|
||||
// memStream := IV
|
||||
|
||||
// Write the encrypted payload to the memory stream.
|
||||
using (ICryptoTransform encryptor = encryptionAlgorithm.CreateEncryptor()) {
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memStream, encryptor, CryptoStreamMode.Write)) {
|
||||
cryptoStream.Write(clearData, 0, clearData.Length);
|
||||
cryptoStream.FlushFinalBlock();
|
||||
|
||||
// At this point:
|
||||
// memStream := IV || Enc(Kenc, IV, clearData)
|
||||
|
||||
// These KeyedHashAlgorithm instances are single-use; we wrap it in a 'using' block.
|
||||
using (KeyedHashAlgorithm signingAlgorithm = _cryptoAlgorithmFactory.GetValidationAlgorithm()) {
|
||||
// Initialize the algorithm with the specified key
|
||||
signingAlgorithm.Key = _validationKey.GetKeyMaterial();
|
||||
|
||||
// Compute the signature
|
||||
byte[] signature = signingAlgorithm.ComputeHash(memStream.GetBuffer(), 0, (int)memStream.Length);
|
||||
|
||||
// At this point:
|
||||
// memStream := IV || Enc(Kenc, IV, clearData)
|
||||
// signature := Sign(Kval, IV || Enc(Kenc, IV, clearData))
|
||||
|
||||
// Append the signature to the encrypted payload
|
||||
memStream.Write(signature, 0, signature.Length);
|
||||
|
||||
// At this point:
|
||||
// memStream := IV || Enc(Kenc, IV, clearData) || Sign(Kval, IV || Enc(Kenc, IV, clearData))
|
||||
|
||||
// Algorithm complete
|
||||
byte[] protectedData = memStream.ToArray();
|
||||
return protectedData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Unprotect(byte[] protectedData) {
|
||||
// The entire operation is wrapped in a 'checked' block because any overflows should be treated as failures.
|
||||
checked {
|
||||
|
||||
// We want to check that the input is in the form:
|
||||
// protectedData := IV || Enc(Kenc, IV, clearData) || Sign(Kval, IV || Enc(Kenc, IV, clearData))
|
||||
|
||||
// Definitions used in this method:
|
||||
// encryptedPayload := Enc(Kenc, IV, clearData)
|
||||
// signature := Sign(Kval, IV || encryptedPayload)
|
||||
|
||||
// These SymmetricAlgorithm instances are single-use; we wrap it in a 'using' block.
|
||||
using (SymmetricAlgorithm decryptionAlgorithm = _cryptoAlgorithmFactory.GetEncryptionAlgorithm()) {
|
||||
decryptionAlgorithm.Key = _encryptionKey.GetKeyMaterial();
|
||||
|
||||
// These KeyedHashAlgorithm instances are single-use; we wrap it in a 'using' block.
|
||||
using (KeyedHashAlgorithm validationAlgorithm = _cryptoAlgorithmFactory.GetValidationAlgorithm()) {
|
||||
validationAlgorithm.Key = _validationKey.GetKeyMaterial();
|
||||
|
||||
// First, we need to verify that protectedData is even long enough to contain
|
||||
// the required components (IV, encryptedPayload, signature).
|
||||
|
||||
int ivByteCount = decryptionAlgorithm.BlockSize / 8; // IV length is equal to the block size
|
||||
int signatureByteCount = validationAlgorithm.HashSize / 8;
|
||||
int encryptedPayloadByteCount = protectedData.Length - ivByteCount - signatureByteCount;
|
||||
if (encryptedPayloadByteCount <= 0) {
|
||||
// protectedData doesn't meet minimum length requirements
|
||||
return null;
|
||||
}
|
||||
|
||||
// If that check passes, we need to detect payload tampering.
|
||||
|
||||
// Compute the signature over the IV and encrypted payload
|
||||
// computedSignature := Sign(Kval, IV || encryptedPayload)
|
||||
byte[] computedSignature = validationAlgorithm.ComputeHash(protectedData, 0, ivByteCount + encryptedPayloadByteCount);
|
||||
|
||||
if (!CryptoUtil.BuffersAreEqual(
|
||||
buffer1: protectedData, buffer1Offset: ivByteCount + encryptedPayloadByteCount, buffer1Count: signatureByteCount,
|
||||
buffer2: computedSignature, buffer2Offset: 0, buffer2Count: computedSignature.Length)) {
|
||||
|
||||
// the computed signature didn't match the incoming signature, which is a sign of payload tampering
|
||||
return null;
|
||||
}
|
||||
|
||||
// At this point, we're certain that we generated the signature over this payload,
|
||||
// so we can go ahead with decryption.
|
||||
|
||||
// Populate the IV from the incoming stream
|
||||
byte[] iv = new byte[ivByteCount];
|
||||
Buffer.BlockCopy(protectedData, 0, iv, 0, iv.Length);
|
||||
decryptionAlgorithm.IV = iv;
|
||||
|
||||
// Write the decrypted payload to the memory stream.
|
||||
using (MemoryStream memStream = new MemoryStream()) {
|
||||
using (ICryptoTransform decryptor = decryptionAlgorithm.CreateDecryptor()) {
|
||||
using (CryptoStream cryptoStream = new CryptoStream(memStream, decryptor, CryptoStreamMode.Write)) {
|
||||
cryptoStream.Write(protectedData, ivByteCount, encryptedPayloadByteCount);
|
||||
cryptoStream.FlushFinalBlock();
|
||||
|
||||
// At this point
|
||||
// memStream := clearData
|
||||
|
||||
byte[] clearData = memStream.ToArray();
|
||||
return clearData;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,166 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="Purpose.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
|
||||
// Represents a purpose that can be passed to a cryptographic routine to control key derivation / ciphertext modification.
|
||||
// This is hardening the crypto routines to prevent playing ciphertext off of components that didn't generate them.
|
||||
//
|
||||
// !! IMPORTANT !!
|
||||
// The built-in purposes do not contain privileged information and are not meant to be treated as secrets. Any external
|
||||
// person can disassemble our code or look directly at our source to see what our Purpose objects are used for.
|
||||
//
|
||||
// PrimaryPurpose: This is a well-known string that identifies the reason for this Purpose. The pattern we use
|
||||
// is that PrimaryPurpose is the name of the consumer, making each consumer's Purpose unique.
|
||||
//
|
||||
// SpecificPurposes: These are extra optional strings that further differentiate Purpose objects that might have the
|
||||
// same PrimaryPurpose. The pattern we use is that if a single consumer has multiple Purposes, he can use
|
||||
// SpecificPurposes to uniquely identify them. The information here is generally not secret (we can put the type of
|
||||
// the currently executing Page here, for example), but it is valid to seed this property with a secret obtained
|
||||
// at runtime (such as a nonce shared between two parties).
|
||||
|
||||
internal sealed class Purpose {
|
||||
|
||||
// predefined purposes
|
||||
public static readonly Purpose AnonymousIdentificationModule_Ticket = new Purpose("AnonymousIdentificationModule.Ticket");
|
||||
public static readonly Purpose AssemblyResourceLoader_WebResourceUrl = new Purpose("AssemblyResourceLoader.WebResourceUrl");
|
||||
public static readonly Purpose FormsAuthentication_Ticket = new Purpose("FormsAuthentication.Ticket");
|
||||
public static readonly Purpose WebForms_Page_PreviousPageID = new Purpose("WebForms.Page.PreviousPageID");
|
||||
public static readonly Purpose RolePrincipal_Ticket = new Purpose("RolePrincipal.Ticket");
|
||||
public static readonly Purpose ScriptResourceHandler_ScriptResourceUrl = new Purpose("ScriptResourceHandler.ScriptResourceUrl");
|
||||
|
||||
// predefined ViewState purposes; they won't be used as-is (they're combined with the page information)
|
||||
public static readonly Purpose WebForms_ClientScriptManager_EventValidation = new Purpose("WebForms.ClientScriptManager.EventValidation");
|
||||
public static readonly Purpose WebForms_DetailsView_KeyTable = new Purpose("WebForms.DetailsView.KeyTable");
|
||||
public static readonly Purpose WebForms_GridView_DataKeys = new Purpose("WebForms.GridView.DataKeys");
|
||||
public static readonly Purpose WebForms_GridView_SortExpression = new Purpose("WebForms.GridView.SortExpression");
|
||||
public static readonly Purpose WebForms_HiddenFieldPageStatePersister_ClientState = new Purpose("WebForms.HiddenFieldPageStatePersister.ClientState");
|
||||
public static readonly Purpose WebForms_ScriptManager_HistoryState = new Purpose("WebForms.ScriptManager.HistoryState");
|
||||
public static readonly Purpose WebForms_SessionPageStatePersister_ClientState = new Purpose("WebForms.SessionPageStatePersister.ClientState");
|
||||
|
||||
// predefined miscellaneoous purposes; they won't be used as-is (they're combined with other specificPurposes)
|
||||
public static readonly Purpose User_MachineKey_Protect = new Purpose("User.MachineKey.Protect"); // used by the MachineKey static class Protect / Unprotect methods
|
||||
public static readonly Purpose User_ObjectStateFormatter_Serialize = new Purpose("User.ObjectStateFormatter.Serialize"); // used by ObjectStateFormatter.Serialize() if called manually
|
||||
|
||||
public readonly string PrimaryPurpose;
|
||||
public readonly string[] SpecificPurposes;
|
||||
|
||||
private byte[] _derivedKeyLabel;
|
||||
private byte[] _derivedKeyContext;
|
||||
|
||||
public Purpose(string primaryPurpose, params string[] specificPurposes)
|
||||
: this(primaryPurpose, specificPurposes, null, null) {
|
||||
}
|
||||
|
||||
// ctor for unit testing
|
||||
internal Purpose(string primaryPurpose, string[] specificPurposes, CryptographicKey derivedEncryptionKey, CryptographicKey derivedValidationKey) {
|
||||
PrimaryPurpose = primaryPurpose;
|
||||
SpecificPurposes = specificPurposes ?? new string[0];
|
||||
DerivedEncryptionKey = derivedEncryptionKey;
|
||||
DerivedValidationKey = derivedValidationKey;
|
||||
SaveDerivedKeys = (SpecificPurposes.Length == 0);
|
||||
}
|
||||
|
||||
// The cryptographic keys that were derived from this Purpose.
|
||||
internal CryptographicKey DerivedEncryptionKey { get; private set; }
|
||||
internal CryptographicKey DerivedValidationKey { get; private set; }
|
||||
|
||||
// Whether the derived key should be saved back to this Purpose object by the ICryptoService,
|
||||
// e.g. because this Purpose will be used over and over again. We assume that any built-in
|
||||
// Purpose object that is passed without any specific purposes is intended for repeated use,
|
||||
// hence the ICryptoService will try to cache cryptographic keys as a performance optimization.
|
||||
// If specific purposes have been specified, they were likely generated at runtime, hence it
|
||||
// is not appropriate for the keys to be cached in this instance.
|
||||
internal bool SaveDerivedKeys { get; set; }
|
||||
|
||||
// Returns a new Purpose which is the specified Purpose plus the specified SpecificPurpose.
|
||||
// Leaves the original Purpose unmodified.
|
||||
internal Purpose AppendSpecificPurpose(string specificPurpose) {
|
||||
// Append the specified specificPurpose to the existing list
|
||||
string[] newSpecificPurposes = new string[SpecificPurposes.Length + 1];
|
||||
Array.Copy(SpecificPurposes, newSpecificPurposes, SpecificPurposes.Length);
|
||||
newSpecificPurposes[newSpecificPurposes.Length - 1] = specificPurpose;
|
||||
return new Purpose(PrimaryPurpose, newSpecificPurposes);
|
||||
}
|
||||
|
||||
// Returns a new Purpose which is the specified Purpose plus the specified SpecificPurposes.
|
||||
// Leaves the original Purpose unmodified.
|
||||
internal Purpose AppendSpecificPurposes(IList<string> specificPurposes) {
|
||||
// No specific purposes to add
|
||||
if (specificPurposes == null || specificPurposes.Count == 0) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// Append the specified specificPurposes to the existing list
|
||||
string[] newSpecificPurposes = new string[SpecificPurposes.Length + specificPurposes.Count];
|
||||
Array.Copy(SpecificPurposes, newSpecificPurposes, SpecificPurposes.Length);
|
||||
specificPurposes.CopyTo(newSpecificPurposes, SpecificPurposes.Length);
|
||||
return new Purpose(PrimaryPurpose, newSpecificPurposes);
|
||||
}
|
||||
|
||||
public CryptographicKey GetDerivedEncryptionKey(IMasterKeyProvider masterKeyProvider, KeyDerivationFunction keyDerivationFunction) {
|
||||
// has a key already been stored?
|
||||
CryptographicKey actualDerivedKey = DerivedEncryptionKey;
|
||||
if (actualDerivedKey == null) {
|
||||
CryptographicKey masterKey = masterKeyProvider.GetEncryptionKey();
|
||||
actualDerivedKey = keyDerivationFunction(masterKey, this);
|
||||
|
||||
// only save the key back to storage if this Purpose is configured to do so
|
||||
if (SaveDerivedKeys) {
|
||||
DerivedEncryptionKey = actualDerivedKey;
|
||||
}
|
||||
}
|
||||
|
||||
return actualDerivedKey;
|
||||
}
|
||||
|
||||
public CryptographicKey GetDerivedValidationKey(IMasterKeyProvider masterKeyProvider, KeyDerivationFunction keyDerivationFunction) {
|
||||
// has a key already been stored?
|
||||
CryptographicKey actualDerivedKey = DerivedValidationKey;
|
||||
if (actualDerivedKey == null) {
|
||||
CryptographicKey masterKey = masterKeyProvider.GetValidationKey();
|
||||
actualDerivedKey = keyDerivationFunction(masterKey, this);
|
||||
|
||||
// only save the key back to storage if this Purpose is configured to do so
|
||||
if (SaveDerivedKeys) {
|
||||
DerivedValidationKey = actualDerivedKey;
|
||||
}
|
||||
}
|
||||
|
||||
return actualDerivedKey;
|
||||
}
|
||||
|
||||
// Returns a label and context suitable for passing into the SP800-108 KDF.
|
||||
internal void GetKeyDerivationParameters(out byte[] label, out byte[] context) {
|
||||
// The primary purpose can just be used as the label directly, since ASP.NET
|
||||
// is always in full control of the primary purpose (it's never user-specified).
|
||||
if (_derivedKeyLabel == null) {
|
||||
_derivedKeyLabel = CryptoUtil.SecureUTF8Encoding.GetBytes(PrimaryPurpose);
|
||||
}
|
||||
|
||||
// The specific purposes (which can contain nonce, identity, etc.) are concatenated
|
||||
// together to form the context. The BinaryWriter class prepends each element with
|
||||
// a 7-bit encoded length to guarantee uniqueness.
|
||||
if (_derivedKeyContext == null) {
|
||||
using (MemoryStream stream = new MemoryStream())
|
||||
using (BinaryWriter writer = new BinaryWriter(stream, CryptoUtil.SecureUTF8Encoding)) {
|
||||
foreach (string specificPurpose in SpecificPurposes) {
|
||||
writer.Write(specificPurpose);
|
||||
}
|
||||
_derivedKeyContext = stream.ToArray();
|
||||
}
|
||||
}
|
||||
|
||||
label = _derivedKeyLabel;
|
||||
context = _derivedKeyContext;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,119 @@
|
||||
//------------------------------------------------------------------------------
|
||||
// <copyright file="SP800_108.cs" company="Microsoft">
|
||||
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
// </copyright>
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
namespace System.Web.Security.Cryptography {
|
||||
using System;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
/******************************************************************
|
||||
* !! WARNING !! *
|
||||
* This class contains cryptographic code. If you make changes to *
|
||||
* this class, please have it reviewed by the appropriate people. *
|
||||
******************************************************************/
|
||||
|
||||
// Implements the NIST SP800-108 key derivation routine in counter mode with an HMAC PRF (HMACSHA512).
|
||||
// See: http://csrc.nist.gov/publications/nistpubs/800-108/sp800-108.pdf
|
||||
//
|
||||
// The algorithm is defined as follows:
|
||||
//
|
||||
// INPUTS:
|
||||
// PRF = The pseudo-random function used for key derivation; in our case, an HMAC.
|
||||
// KI = The key derivation key (master key) from which keys will be derived.
|
||||
// Label = The purpose of the derived key.
|
||||
// Context = Information related to the derived key, such as consuming party identities or a nonce.
|
||||
// L = The desired length (in bits) of the derived key.
|
||||
//
|
||||
// ALGORITHM:
|
||||
// Let n = ceil(L / HMAC-output-size)
|
||||
// For i = 1 to n,
|
||||
// K_i = PRF(KI, [i]_2 || Label || 0x00 || Context || [L]_2)
|
||||
// where [x]_2 = the big-endian representation of 'x'
|
||||
//
|
||||
// OUTPUT:
|
||||
// Result := K_1 || K_2 || ... || K_n, truncated to be L bits in length
|
||||
|
||||
internal static class SP800_108 {
|
||||
|
||||
// Implements the KeyDerivationFunction delegate signature; public entry point to the API.
|
||||
public static CryptographicKey DeriveKey(CryptographicKey keyDerivationKey, Purpose purpose) {
|
||||
// After consultation with the crypto board, we have decided to use HMACSHA512 as the PRF
|
||||
// to our KDF. The reason for this is that our PRF is an HMAC, so the total entropy of the
|
||||
// PRF is given by MIN(key derivation key length, HMAC block size). It is conceivable that
|
||||
// a developer might specify a key greater than 256 bits in length, at which point using
|
||||
// a shorter PRF like HMACSHA256 starts discarding entropy. But from the crypto team's
|
||||
// perspective it is unreasonable for a developer to supply a key greater than 512 bits,
|
||||
// so there's no real harm in us limiting our PRF entropy to 512 bits (HMACSHA512).
|
||||
//
|
||||
// On 64-bit platforms, HMACSHA512 matches or outperforms HMACSHA256 in our perf testing.
|
||||
// On 32-bit platforms, HMACSHA512 is around 1/3 the speed of HMACSHA256. In both cases, we
|
||||
// try to cache the derived CryptographicKey wherever we can, so this shouldn't be a
|
||||
// bottleneck regardless.
|
||||
|
||||
using (HMACSHA512 hmac = CryptoAlgorithms.CreateHMACSHA512(keyDerivationKey.GetKeyMaterial())) {
|
||||
byte[] label, context;
|
||||
purpose.GetKeyDerivationParameters(out label, out context);
|
||||
|
||||
byte[] derivedKey = DeriveKeyImpl(hmac, label, context, keyDerivationKey.KeyLength);
|
||||
return new CryptographicKey(derivedKey);
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: This method also exists in Win8 (as BCryptKeyDerivation) and QTD (as DeriveKeySP800_108).
|
||||
// However, the QTD implementation is currently incorrect, so we can't depend on it here. The below
|
||||
// is a correct implementation. When we take a Win8 dependency, we can call into BCryptKeyDerivation.
|
||||
private static byte[] DeriveKeyImpl(HMAC hmac, byte[] label, byte[] context, int keyLengthInBits) {
|
||||
// This entire method is checked because according to SP800-108 it is an error
|
||||
// for any single operation to result in overflow.
|
||||
checked {
|
||||
|
||||
// Make a buffer which is ____ || label || 0x00 || context || [l]_2.
|
||||
// We can reuse this buffer during each round.
|
||||
|
||||
int labelLength = (label != null) ? label.Length : 0;
|
||||
int contextLength = (context != null) ? context.Length : 0;
|
||||
byte[] buffer = new byte[4 /* [i]_2 */ + labelLength /* label */ + 1 /* 0x00 */ + contextLength /* context */ + 4 /* [L]_2 */];
|
||||
|
||||
if (labelLength != 0) {
|
||||
Buffer.BlockCopy(label, 0, buffer, 4, labelLength); // the 4 accounts for the [i]_2 length
|
||||
}
|
||||
if (contextLength != 0) {
|
||||
Buffer.BlockCopy(context, 0, buffer, 5 + labelLength, contextLength); // the '5 +' accounts for the [i]_2 length, the label, and the 0x00 byte
|
||||
}
|
||||
WriteUInt32ToByteArrayBigEndian((uint)keyLengthInBits, buffer, 5 + labelLength + contextLength); // the '5 +' accounts for the [i]_2 length, the label, the 0x00 byte, and the context
|
||||
|
||||
// Initialization
|
||||
|
||||
int numBytesWritten = 0;
|
||||
int numBytesRemaining = keyLengthInBits / 8;
|
||||
byte[] output = new byte[numBytesRemaining];
|
||||
|
||||
// Calculate each K_i value and copy the leftmost bits to the output buffer as appropriate.
|
||||
|
||||
for (uint i = 1; numBytesRemaining > 0; i++) {
|
||||
WriteUInt32ToByteArrayBigEndian(i, buffer, 0); // set the first 32 bits of the buffer to be the current iteration value
|
||||
byte[] K_i = hmac.ComputeHash(buffer);
|
||||
|
||||
// copy the leftmost bits of K_i into the output buffer
|
||||
int numBytesToCopy = Math.Min(numBytesRemaining, K_i.Length);
|
||||
Buffer.BlockCopy(K_i, 0, output, numBytesWritten, numBytesToCopy);
|
||||
numBytesWritten += numBytesToCopy;
|
||||
numBytesRemaining -= numBytesToCopy;
|
||||
}
|
||||
|
||||
// finished
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
||||
private static void WriteUInt32ToByteArrayBigEndian(uint value, byte[] buffer, int offset) {
|
||||
buffer[offset + 0] = (byte)(value >> 24);
|
||||
buffer[offset + 1] = (byte)(value >> 16);
|
||||
buffer[offset + 2] = (byte)(value >> 8);
|
||||
buffer[offset + 3] = (byte)(value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user