Code cleanup

Rename IAuthenticatedEncryptor2 -> IOptimizedAuthenticatedEncryptor
Rename ProtectedMemoryBlob -> Secret
Add some missing doc comments explaining
This commit is contained in:
Levi B 2015-02-20 11:36:12 -08:00
parent 71a2712c5a
commit 8ec6dc3712
35 changed files with 200 additions and 109 deletions

View File

@ -12,7 +12,7 @@ namespace Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption
public static byte[] Encrypt(this IAuthenticatedEncryptor encryptor, ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize)
{
// Can we call the optimized version?
IAuthenticatedEncryptor2 optimizedEncryptor = encryptor as IAuthenticatedEncryptor2;
IOptimizedAuthenticatedEncryptor optimizedEncryptor = encryptor as IOptimizedAuthenticatedEncryptor;
if (optimizedEncryptor != null)
{
return optimizedEncryptor.Encrypt(plaintext, additionalAuthenticatedData, preBufferSize, postBufferSize);

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption
{
// generate a 512-bit secret randomly
const int KDK_SIZE_IN_BYTES = 512 / 8;
var secret = ProtectedMemoryBlob.Random(KDK_SIZE_IN_BYTES);
var secret = Secret.Random(KDK_SIZE_IN_BYTES);
return new CngCbcAuthenticatedEncryptorConfiguration(_options, secret);
}
}

View File

@ -96,7 +96,7 @@ namespace Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption
// and we're good to go!
return new CbcAuthenticatedEncryptor(
keyDerivationKey: new ProtectedMemoryBlob(secret),
keyDerivationKey: new Secret(secret),
symmetricAlgorithmHandle: encryptionAlgorithmHandle,
symmetricAlgorithmKeySizeInBytes: encryptionAlgorithmKeySizeInBits / 8,
hmacAlgorithmHandle: hashAlgorithmHandle);

View File

@ -58,8 +58,8 @@ namespace Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption
byte[] decryptedSecretBytes = Convert.FromBase64String((string)decryptedSecretElement);
try
{
var protectedMemoryBlob = new ProtectedMemoryBlob(decryptedSecretBytes);
return new CngCbcAuthenticatedEncryptorConfiguration(options, protectedMemoryBlob);
var secret = new Secret(decryptedSecretBytes);
return new CngCbcAuthenticatedEncryptorConfiguration(options, secret);
}
finally
{

View File

@ -23,7 +23,7 @@ namespace Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption
{
// generate a 512-bit secret randomly
const int KDK_SIZE_IN_BYTES = 512 / 8;
var secret = ProtectedMemoryBlob.Random(KDK_SIZE_IN_BYTES);
var secret = Secret.Random(KDK_SIZE_IN_BYTES);
return new CngGcmAuthenticatedEncryptorConfiguration(_options, secret);
}
}

View File

@ -67,7 +67,7 @@ namespace Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption
// and we're good to go!
return new GcmAuthenticatedEncryptor(
keyDerivationKey: new ProtectedMemoryBlob(secret),
keyDerivationKey: new Secret(secret),
symmetricAlgorithmHandle: encryptionAlgorithmHandle,
symmetricAlgorithmKeySizeInBytes: encryptionAlgorithmKeySizeInBits / 8);
}

View File

@ -52,8 +52,8 @@ namespace Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption
byte[] decryptedSecretBytes = Convert.FromBase64String((string)decryptedSecretElement);
try
{
var protectedMemoryBlob = new ProtectedMemoryBlob(decryptedSecretBytes);
return new CngGcmAuthenticatedEncryptorConfiguration(options, protectedMemoryBlob);
var secret = new Secret(decryptedSecretBytes);
return new CngGcmAuthenticatedEncryptorConfiguration(options, secret);
}
finally
{

View File

@ -1,12 +0,0 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption
{
internal interface IAuthenticatedEncryptor2 : IAuthenticatedEncryptor
{
byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize);
}
}

View File

@ -0,0 +1,35 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
namespace Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption
{
/// <summary>
/// An optimized encryptor that can avoid buffer allocations in common code paths.
/// </summary>
internal interface IOptimizedAuthenticatedEncryptor : IAuthenticatedEncryptor
{
/// <summary>
/// Encrypts and tamper-proofs a piece of data.
/// </summary>
/// <param name="plaintext">The plaintext to encrypt. This input may be zero bytes in length.</param>
/// <param name="additionalAuthenticatedData">A piece of data which will not be included in
/// the returned ciphertext but which will still be covered by the authentication tag.
/// This input may be zero bytes in length. The same AAD must be specified in the corresponding
/// call to Decrypt.</param>
/// <param name="preBufferSize">The number of bytes to include before the ciphertext in the return value.</param>
/// <param name="postBufferSize">The number of bytes to include after the ciphertext in the return value.</param>
/// <returns>
/// A buffer containing the ciphertext and authentication tag.
/// If a non-zero pre-buffer or post-buffer size is specified, the returned buffer will contain appropriate padding
/// on either side of the ciphertext and authentication tag. For instance, if a pre-buffer size of 4 and a post-buffer
/// size of 7 are specified, and if the ciphertext and tag are a combined 48 bytes, then the returned buffer will
/// be a total 59 bytes in length. The first four bytes will be undefined, the next 48 bytes will contain the
/// ciphertext and tag, and the last seven bytes will be undefined. The intent is that the caller can overwrite the
/// pre-buffer or post-buffer with a header or footer without needing to allocate an additional buffer object.
/// </returns>
/// <remarks>All cryptography-related exceptions should be homogenized to CryptographicException.</remarks>
byte[] Encrypt(ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize);
}
}

View File

@ -21,10 +21,10 @@ namespace Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption
// generate a 512-bit secret randomly
const int KDK_SIZE_IN_BYTES = 512 / 8;
byte[] kdk = ManagedGenRandomImpl.Instance.GenRandom(KDK_SIZE_IN_BYTES);
ProtectedMemoryBlob secret;
Secret secret;
try
{
secret = new ProtectedMemoryBlob(kdk);
secret = new Secret(kdk);
}
finally
{

View File

@ -70,7 +70,7 @@ namespace Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption
// We're good to go!
return new ManagedAuthenticatedEncryptor(
keyDerivationKey: new ProtectedMemoryBlob(secret),
keyDerivationKey: new Secret(secret),
symmetricAlgorithmFactory: encryptorFactory,
symmetricAlgorithmKeySizeInBytes: keySizeInBytes,
validationAlgorithmFactory: validatorFactory);

View File

@ -56,8 +56,8 @@ namespace Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption
byte[] decryptedSecretBytes = Convert.FromBase64String((string)decryptedSecretElement);
try
{
var protectedMemoryBlob = new ProtectedMemoryBlob(decryptedSecretBytes);
return new ManagedAuthenticatedEncryptorConfiguration(options, protectedMemoryBlob);
var secret = new Secret(decryptedSecretBytes);
return new ManagedAuthenticatedEncryptorConfiguration(options, secret);
}
finally
{

View File

@ -21,6 +21,20 @@ namespace Microsoft.AspNet.Security.DataProtection
bytePtr[3] = (byte)(value);
}
/// <summary>
/// Writes an unsigned 32-bit value to a memory address, big-endian.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void WriteTo(ref byte* ptr, uint value)
{
byte* pTemp = ptr;
pTemp[0] = (byte)(value >> 24);
pTemp[1] = (byte)(value >> 16);
pTemp[2] = (byte)(value >> 8);
pTemp[3] = (byte)(value);
ptr = &pTemp[4];
}
/// <summary>
/// Writes a signed 32-bit value to a memory address, big-endian.
/// </summary>

View File

@ -2,7 +2,6 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Cryptography;
using Microsoft.AspNet.Security.DataProtection.SafeHandles;
using Microsoft.AspNet.Security.DataProtection.SP800_108;
@ -41,7 +40,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
private readonly uint _symmetricAlgorithmBlockSizeInBytes;
private readonly uint _symmetricAlgorithmSubkeyLengthInBytes;
public CbcAuthenticatedEncryptor(ProtectedMemoryBlob keyDerivationKey, BCryptAlgorithmHandle symmetricAlgorithmHandle, uint symmetricAlgorithmKeySizeInBytes, BCryptAlgorithmHandle hmacAlgorithmHandle, IBCryptGenRandom genRandom = null)
public CbcAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHandle symmetricAlgorithmHandle, uint symmetricAlgorithmKeySizeInBytes, BCryptAlgorithmHandle hmacAlgorithmHandle, IBCryptGenRandom genRandom = null)
{
CryptoUtil.Assert(KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES,
"KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES");
@ -88,16 +87,12 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
*(ptr++) = 0; // 0x00 = CBC encryption + HMAC authentication
// Next is information about the symmetric algorithm (key size followed by block size)
BitHelpers.WriteTo(ptr, _symmetricAlgorithmSubkeyLengthInBytes);
ptr += sizeof(uint);
BitHelpers.WriteTo(ptr, _symmetricAlgorithmBlockSizeInBytes);
ptr += sizeof(uint);
BitHelpers.WriteTo(ref ptr, _symmetricAlgorithmSubkeyLengthInBytes);
BitHelpers.WriteTo(ref ptr, _symmetricAlgorithmBlockSizeInBytes);
// Next is information about the HMAC algorithm (key size followed by digest size)
BitHelpers.WriteTo(ptr, _hmacAlgorithmSubkeyLengthInBytes);
ptr += sizeof(uint);
BitHelpers.WriteTo(ptr, _hmacAlgorithmDigestLengthInBytes);
ptr += sizeof(uint);
BitHelpers.WriteTo(ref ptr, _hmacAlgorithmSubkeyLengthInBytes);
BitHelpers.WriteTo(ref ptr, _hmacAlgorithmDigestLengthInBytes);
// See the design document for an explanation of the following code.
byte[] tempKeys = new byte[_symmetricAlgorithmSubkeyLengthInBytes + _hmacAlgorithmSubkeyLengthInBytes];
@ -348,7 +343,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
using (var symmetricKeyHandle = _symmetricAlgorithmHandle.GenerateSymmetricKey(pbSymmetricEncryptionSubkey, _symmetricAlgorithmSubkeyLengthInBytes))
{
// We can't assume PKCS#7 padding (maybe the underlying provided is using CTS),
// We can't assume PKCS#7 padding (maybe the underlying provider is really using CTS),
// so we need to query the padded output size before we can allocate the return value array.
uint cbOutputCiphertext = GetCbcEncryptedOutputSizeWithPadding(symmetricKeyHandle, pbPlaintext, cbPlaintext);

View File

@ -2,12 +2,14 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Cryptography;
using Microsoft.AspNet.Security.DataProtection.AuthenticatedEncryption;
namespace Microsoft.AspNet.Security.DataProtection.Cng
{
internal unsafe abstract class CngAuthenticatedEncryptorBase : IAuthenticatedEncryptor, IDisposable
/// <summary>
/// Base class used for all CNG-related authentication encryption operations.
/// </summary>
internal unsafe abstract class CngAuthenticatedEncryptorBase : IOptimizedAuthenticatedEncryptor, IDisposable
{
public byte[] Decrypt(ArraySegment<byte> ciphertext, ArraySegment<byte> additionalAuthenticatedData)
{
@ -30,7 +32,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
pbAdditionalAuthenticatedData: (pbAdditionalAuthenticatedDataArray != null) ? &pbAdditionalAuthenticatedDataArray[additionalAuthenticatedData.Offset] : &dummy,
cbAdditionalAuthenticatedData: (uint)additionalAuthenticatedData.Count);
}
catch (Exception ex) when (!(ex is CryptographicException))
catch (Exception ex) when (ex.RequiresHomogenization())
{
// Homogenize to CryptographicException.
throw Error.CryptCommon_GenericError(ex);
@ -71,7 +73,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
cbPreBuffer: preBufferSize,
cbPostBuffer: postBufferSize);
}
catch (Exception ex) when (!(ex is CryptographicException))
catch (Exception ex) when (ex.RequiresHomogenization())
{
// Homogenize to CryptographicException.
throw Error.CryptCommon_GenericError(ex);

View File

@ -172,7 +172,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
}
}
public static ProtectedMemoryBlob UnprotectWithDpapi(byte[] protectedSecret)
public static Secret UnprotectWithDpapi(byte[] protectedSecret)
{
Debug.Assert(protectedSecret != null);
@ -185,7 +185,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
}
}
internal static ProtectedMemoryBlob UnprotectWithDpapiImpl(byte* pbProtectedData, uint cbProtectedData, byte* pbOptionalEntropy, uint cbOptionalEntropy)
internal static Secret UnprotectWithDpapiImpl(byte* pbProtectedData, uint cbProtectedData, byte* pbOptionalEntropy, uint cbOptionalEntropy)
{
byte dummy; // provides a valid memory address if the secret or entropy has zero length
@ -220,7 +220,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
throw new CryptographicException(errorCode);
}
return new ProtectedMemoryBlob(dataOut.pbData, checked((int)dataOut.cbData));
return new Secret(dataOut.pbData, checked((int)dataOut.cbData));
}
finally
{
@ -234,7 +234,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
}
}
public static ProtectedMemoryBlob UnprotectWithDpapiNG(byte[] protectedData)
public static Secret UnprotectWithDpapiNG(byte[] protectedData)
{
Debug.Assert(protectedData != null);
@ -247,7 +247,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
}
}
private static ProtectedMemoryBlob UnprotectWithDpapiNGImpl(byte* pbData, uint cbData)
private static Secret UnprotectWithDpapiNGImpl(byte* pbData, uint cbData)
{
Debug.Assert(pbData != null);
@ -280,7 +280,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
try
{
unencryptedPayloadHandle.DangerousAddRef(ref handleAcquired);
return new ProtectedMemoryBlob((byte*)unencryptedPayloadHandle.DangerousGetHandle(), checked((int)cbUnencryptedPayload));
return new Secret((byte*)unencryptedPayloadHandle.DangerousGetHandle(), checked((int)cbUnencryptedPayload));
}
finally
{

View File

@ -35,7 +35,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
private readonly BCryptAlgorithmHandle _symmetricAlgorithmHandle;
private readonly uint _symmetricAlgorithmSubkeyLengthInBytes;
public GcmAuthenticatedEncryptor(ProtectedMemoryBlob keyDerivationKey, BCryptAlgorithmHandle symmetricAlgorithmHandle, uint symmetricAlgorithmKeySizeInBytes, IBCryptGenRandom genRandom = null)
public GcmAuthenticatedEncryptor(Secret keyDerivationKey, BCryptAlgorithmHandle symmetricAlgorithmHandle, uint symmetricAlgorithmKeySizeInBytes, IBCryptGenRandom genRandom = null)
{
CryptoUtil.Assert(KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES,
"KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES");
@ -67,14 +67,10 @@ namespace Microsoft.AspNet.Security.DataProtection.Cng
*(ptr++) = 1; // 0x01 = GCM encryption + authentication
// Next is information about the symmetric algorithm (key size, nonce size, block size, tag size)
BitHelpers.WriteTo(ptr, _symmetricAlgorithmSubkeyLengthInBytes);
ptr += sizeof(uint);
BitHelpers.WriteTo(ptr, NONCE_SIZE_IN_BYTES);
ptr += sizeof(uint);
BitHelpers.WriteTo(ptr, TAG_SIZE_IN_BYTES); // block size
ptr += sizeof(uint);
BitHelpers.WriteTo(ptr, TAG_SIZE_IN_BYTES);
ptr += sizeof(uint);
BitHelpers.WriteTo(ref ptr, _symmetricAlgorithmSubkeyLengthInBytes);
BitHelpers.WriteTo(ref ptr, NONCE_SIZE_IN_BYTES);
BitHelpers.WriteTo(ref ptr, TAG_SIZE_IN_BYTES); // block size = tag size
BitHelpers.WriteTo(ref ptr, TAG_SIZE_IN_BYTES);
// See the design document for an explanation of the following code.
byte[] tempKeys = new byte[_symmetricAlgorithmSubkeyLengthInBytes];

View File

@ -48,6 +48,8 @@ namespace Microsoft.AspNet.Security.DataProtection
throw new CryptographicException("Assertion failed: " + message);
}
// Allows callers to write "var x = Method() ?? Fail<T>(message);" as a convenience to guard
// against a method returning null unexpectedly.
[MethodImpl(MethodImplOptions.NoInlining)]
public static T Fail<T>(string message) where T : class
{

View File

@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Security.DataProtection
byte[] protectedDataAsBytes = protector.Protect(unprotectedDataAsBytes);
return WebEncoders.Base64UrlEncode(protectedDataAsBytes);
}
catch (Exception ex) when (!(ex is CryptographicException))
catch (Exception ex) when (ex.RequiresHomogenization())
{
// Homogenize exceptions to CryptographicException
throw Error.CryptCommon_GenericError(ex);
@ -60,7 +60,7 @@ namespace Microsoft.AspNet.Security.DataProtection
byte[] unprotectedDataAsBytes = protector.Unprotect(protectedDataAsBytes);
return CryptoUtil.SecureUtf8Encoding.GetString(unprotectedDataAsBytes);
}
catch (Exception ex) when (!(ex is CryptographicException))
catch (Exception ex) when (ex.RequiresHomogenization())
{
// Homogenize exceptions to CryptographicException
throw Error.CryptCommon_GenericError(ex);

View File

@ -43,7 +43,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Dpapi
return _shim.Protect(unprotectedData, _combinedPurposes, _scope)
?? CryptoUtil.Fail<byte[]>("Null return value.");
}
catch (Exception ex) when (!(ex is CryptographicException))
catch (Exception ex) when (ex.RequiresHomogenization())
{
// Homogenize to CryptographicException
throw Error.CryptCommon_GenericError(ex);
@ -57,7 +57,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Dpapi
return _shim.Unprotect(protectedData, _combinedPurposes, _scope)
?? CryptoUtil.Fail<byte[]>("Null return value.");
}
catch (Exception ex) when (!(ex is CryptographicException))
catch (Exception ex) when (ex.RequiresHomogenization())
{
// Homogenize to CryptographicException
throw Error.CryptCommon_GenericError(ex);

View File

@ -32,7 +32,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Dpapi
public byte[] Unprotect(byte[] encryptedData, byte[] optionalEntropy, DataProtectionScope scope)
{
#if ASPNETCORE50
ProtectedMemoryBlob blob;
Secret blob;
fixed (byte* pbEncryptedData = encryptedData)
{
fixed (byte* pbOptionalEntropy = optionalEntropy)

View File

@ -50,7 +50,7 @@ namespace Microsoft.AspNet.Security.DataProtection
// Currently hardcoded to a 512-bit KDK.
private const int NUM_BYTES_IN_KDK = 512 / 8;
public IAuthenticatedEncryptor DefaultAuthenticatedEncryptor { get; } = new T().CreateAuthenticatedEncryptor(ProtectedMemoryBlob.Random(NUM_BYTES_IN_KDK));
public IAuthenticatedEncryptor DefaultAuthenticatedEncryptor { get; } = new T().CreateAuthenticatedEncryptor(Secret.Random(NUM_BYTES_IN_KDK));
public Guid DefaultKeyId { get; } = default(Guid);

View File

@ -0,0 +1,20 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Security.Cryptography;
namespace Microsoft.AspNet.Security.DataProtection
{
internal static class ExceptionExtensions
{
/// <summary>
/// Determines whether an exception must be homogenized by being wrapped inside a
/// CryptographicException before being rethrown.
/// </summary>
public static bool RequiresHomogenization(this Exception ex)
{
return !(ex is CryptographicException);
}
}
}

View File

@ -11,7 +11,7 @@ namespace Microsoft.AspNet.Security.DataProtection
public interface ISecret : IDisposable
{
/// <summary>
/// The length (in bytes) of the value.
/// The length (in bytes) of the secret value.
/// </summary>
int Length { get; }

View File

@ -131,7 +131,7 @@ namespace Microsoft.AspNet.Security.DataProtection.KeyManagement
postBufferSize: 0);
CryptoUtil.Assert(retVal != null && retVal.Length >= sizeof(uint) + sizeof(Guid), "retVal != null && retVal.Length >= sizeof(uint) + sizeof(Guid)");
}
catch (Exception ex) when (!(ex is CryptographicException))
catch (Exception ex) when (ex.RequiresHomogenization())
{
// homogenize all errors to CryptographicException
throw Error.Common_EncryptionFailed(ex);
@ -247,7 +247,7 @@ namespace Microsoft.AspNet.Security.DataProtection.KeyManagement
CryptoUtil.Assert(retVal != null, "retVal != null");
return retVal;
}
catch (Exception ex) when (!(ex is CryptographicException))
catch (Exception ex) when (ex.RequiresHomogenization())
{
// homogenize all failures to CryptographicException
throw Error.DecryptionFailed(ex);

View File

@ -111,7 +111,7 @@ namespace Microsoft.AspNet.Security.DataProtection.KeyManagement
var thisKey = ParseKeyElement(element);
if (idToKeyMap.ContainsKey(thisKey.KeyId))
{
CryptoUtil.Fail("TODO: Duplicate key.");
throw CryptoUtil.Fail("TODO: Duplicate key.");
}
idToKeyMap.Add(thisKey.KeyId, thisKey);
}
@ -140,7 +140,7 @@ namespace Microsoft.AspNet.Security.DataProtection.KeyManagement
}
else
{
CryptoUtil.Fail("TODO: Unknown element.");
throw CryptoUtil.Fail("TODO: Unknown element.");
}
}

View File

@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Managed
private readonly byte[] _contextHeader;
private readonly IManagedGenRandom _genRandom;
private readonly ProtectedMemoryBlob _keyDerivationKey;
private readonly Secret _keyDerivationKey;
private readonly Func<SymmetricAlgorithm> _symmetricAlgorithmFactory;
private readonly int _symmetricAlgorithmBlockSizeInBytes;
private readonly int _symmetricAlgorithmSubkeyLengthInBytes;
@ -44,7 +44,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Managed
private readonly int _validationAlgorithmSubkeyLengthInBytes;
private readonly Func<KeyedHashAlgorithm> _validationAlgorithmFactory;
public ManagedAuthenticatedEncryptor(ProtectedMemoryBlob keyDerivationKey, Func<SymmetricAlgorithm> symmetricAlgorithmFactory, int symmetricAlgorithmKeySizeInBytes, Func<KeyedHashAlgorithm> validationAlgorithmFactory, IManagedGenRandom genRandom = null)
public ManagedAuthenticatedEncryptor(Secret keyDerivationKey, Func<SymmetricAlgorithm> symmetricAlgorithmFactory, int symmetricAlgorithmKeySizeInBytes, Func<KeyedHashAlgorithm> validationAlgorithmFactory, IManagedGenRandom genRandom = null)
{
CryptoUtil.Assert(KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES,
"KEY_MODIFIER_SIZE_IN_BYTES <= symmetricAlgorithmKeySizeInBytes && symmetricAlgorithmKeySizeInBytes <= Constants.MAX_STACKALLOC_BYTES");
@ -278,7 +278,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Managed
}
}
}
catch (Exception ex) when (!(ex is CryptographicException))
catch (Exception ex) when (ex.RequiresHomogenization())
{
// Homogenize all exceptions to CryptographicException.
throw Error.CryptCommon_GenericError(ex);
@ -382,7 +382,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Managed
}
}
}
catch (Exception ex) when (!(ex is CryptographicException))
catch (Exception ex) when (ex.RequiresHomogenization())
{
// Homogenize all exceptions to CryptographicException.
throw Error.CryptCommon_GenericError(ex);

View File

@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Security.DataProtection.SP800_108
}
// Creates a provider from the given secret.
public static ISP800_108_CTR_HMACSHA512Provider CreateProvider(ProtectedMemoryBlob kdk)
public static ISP800_108_CTR_HMACSHA512Provider CreateProvider(Secret kdk)
{
uint secretLengthInBytes = checked((uint)kdk.Length);
if (secretLengthInBytes == 0)

View File

@ -8,7 +8,10 @@ using Microsoft.AspNet.Security.DataProtection.SafeHandles;
namespace Microsoft.AspNet.Security.DataProtection
{
public unsafe sealed class ProtectedMemoryBlob : IDisposable, ISecret
/// <summary>
/// Represents a secret value stored in memory.
/// </summary>
public unsafe sealed class Secret : IDisposable, ISecret
{
// from wincrypt.h
private const uint CRYPTPROTECTMEMORY_BLOCK_SIZE = 16;
@ -16,42 +19,57 @@ namespace Microsoft.AspNet.Security.DataProtection
private readonly SecureLocalAllocHandle _localAllocHandle;
private readonly uint _plaintextLength;
public ProtectedMemoryBlob(ArraySegment<byte> plaintext)
/// <summary>
/// Creates a new Secret from the provided input value, where the input value
/// is specified as an array segment.
/// </summary>
public Secret(ArraySegment<byte> value)
{
plaintext.Validate();
value.Validate();
_localAllocHandle = Protect(plaintext);
_plaintextLength = (uint)plaintext.Count;
_localAllocHandle = Protect(value);
_plaintextLength = (uint)value.Count;
}
public ProtectedMemoryBlob(byte[] plaintext)
: this(new ArraySegment<byte>(plaintext))
/// <summary>
/// Creates a new Secret from the provided input value, where the input value
/// is specified as an array.
/// </summary>
public Secret(byte[] value)
: this(new ArraySegment<byte>(value))
{
}
public ProtectedMemoryBlob(byte* plaintext, int plaintextLength)
/// <summary>
/// Creates a new Secret from the provided input value, where the input value
/// is specified as a pointer to unmanaged memory.
/// </summary>
public Secret(byte* secret, int secretLength)
{
if (plaintext == null)
if (secret == null)
{
throw new ArgumentNullException("plaintext");
throw new ArgumentNullException("secret");
}
if (plaintextLength < 0)
if (secretLength < 0)
{
throw new ArgumentOutOfRangeException("plaintextLength");
throw new ArgumentOutOfRangeException("secretLength");
}
_localAllocHandle = Protect(plaintext, (uint)plaintextLength);
_plaintextLength = (uint)plaintextLength;
_localAllocHandle = Protect(secret, (uint)secretLength);
_plaintextLength = (uint)secretLength;
}
public ProtectedMemoryBlob(ISecret secret)
/// <summary>
/// Creates a new Secret from another secret object.
/// </summary>
public Secret(ISecret secret)
{
if (secret == null)
{
throw new ArgumentNullException("secret");
}
ProtectedMemoryBlob other = secret as ProtectedMemoryBlob;
Secret other = secret as Secret;
if (other != null)
{
// Fast-track: simple deep copy scenario.
@ -79,6 +97,9 @@ namespace Microsoft.AspNet.Security.DataProtection
}
}
/// <summary>
/// The length (in bytes) of the secret value.
/// </summary>
public int Length
{
get
@ -87,6 +108,9 @@ namespace Microsoft.AspNet.Security.DataProtection
}
}
/// <summary>
/// Wipes the secret from memory.
/// </summary>
public void Dispose()
{
_localAllocHandle.Dispose();
@ -134,21 +158,25 @@ namespace Microsoft.AspNet.Security.DataProtection
return encryptedMemoryHandle;
}
public static ProtectedMemoryBlob Random(int numBytes)
/// <summary>
/// Returns a Secret comprised entirely of random bytes retrieved from
/// a cryptographically secure RNG.
/// </summary>
public static Secret Random(int numBytes)
{
CryptoUtil.Assert(numBytes >= 0, "numBytes >= 0");
if (numBytes == 0)
{
byte dummy;
return new ProtectedMemoryBlob(&dummy, 0);
return new Secret(&dummy, 0);
}
else
{
// Don't use CNG if we're not on Windows.
if (!OSVersionUtil.IsBCryptOnWin7OrLaterAvailable())
{
return new ProtectedMemoryBlob(ManagedGenRandomImpl.Instance.GenRandom(numBytes));
return new Secret(ManagedGenRandomImpl.Instance.GenRandom(numBytes));
}
byte[] bytes = new byte[numBytes];
@ -157,7 +185,7 @@ namespace Microsoft.AspNet.Security.DataProtection
try
{
BCryptUtil.GenRandom(pbBytes, (uint)numBytes);
return new ProtectedMemoryBlob(pbBytes, numBytes);
return new Secret(pbBytes, numBytes);
}
finally
{
@ -196,6 +224,12 @@ namespace Microsoft.AspNet.Security.DataProtection
}
}
/// <summary>
/// Writes the secret value to the specified buffer.
/// </summary>
/// <remarks>
/// The buffer size must exactly match the length of the secret value.
/// </remarks>
public void WriteSecretIntoBuffer(ArraySegment<byte> buffer)
{
// Parameter checking
@ -215,6 +249,12 @@ namespace Microsoft.AspNet.Security.DataProtection
}
}
/// <summary>
/// Writes the secret value to the specified buffer.
/// </summary>
/// <remarks>
/// The 'bufferLength' parameter must exactly match the length of the secret value.
/// </remarks>
public void WriteSecretIntoBuffer(byte* buffer, int bufferLength)
{
if (buffer == null)

View File

@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Security.DataProtection
expiration = new DateTimeOffset((long)utcTicksExpiration, TimeSpan.Zero);
return retVal;
}
catch (Exception ex) when (!(ex is CryptographicException))
catch (Exception ex) when (ex.RequiresHomogenization())
{
// Homogenize all failures to CryptographicException
throw Error.CryptCommon_GenericError(ex);

View File

@ -46,20 +46,19 @@ namespace Microsoft.AspNet.Security.DataProtection.XmlEncryption
public XElement Encrypt([NotNull] XElement plaintextElement)
{
// First, convert the XML element to a byte[] so that it can be encrypted.
ProtectedMemoryBlob secret;
Secret secret;
using (var memoryStream = new MemoryStream())
{
plaintextElement.Save(memoryStream);
#if !ASPNETCORE50
// If we're on full desktop CLR, utilize the underlying buffer directly as an optimization.
byte[] underlyingBuffer = memoryStream.GetBuffer();
secret = new ProtectedMemoryBlob(new ArraySegment<byte>(underlyingBuffer, 0, checked((int)memoryStream.Length)));
secret = new Secret(new ArraySegment<byte>(underlyingBuffer, 0, checked((int)memoryStream.Length)));
Array.Clear(underlyingBuffer, 0, underlyingBuffer.Length);
#else
// Otherwise, need to make a copy of the buffer.
byte[] clonedBuffer = memoryStream.ToArray();
secret = new ProtectedMemoryBlob(clonedBuffer);
secret = new Secret(clonedBuffer);
Array.Clear(clonedBuffer, 0, clonedBuffer.Length);
#endif
}

View File

@ -31,7 +31,7 @@ namespace Microsoft.AspNet.Security.DataProtection.XmlEncryption
public XElement Encrypt([NotNull] XElement plaintextElement)
{
// First, convert the XML element to a byte[] so that it can be encrypted.
ProtectedMemoryBlob secret;
Secret secret;
using (var memoryStream = new MemoryStream())
{
plaintextElement.Save(memoryStream);
@ -39,12 +39,12 @@ namespace Microsoft.AspNet.Security.DataProtection.XmlEncryption
#if !ASPNETCORE50
// If we're on full desktop CLR, utilize the underlying buffer directly as an optimization.
byte[] underlyingBuffer = memoryStream.GetBuffer();
secret = new ProtectedMemoryBlob(new ArraySegment<byte>(underlyingBuffer, 0, checked((int)memoryStream.Length)));
secret = new Secret(new ArraySegment<byte>(underlyingBuffer, 0, checked((int)memoryStream.Length)));
Array.Clear(underlyingBuffer, 0, underlyingBuffer.Length);
#else
// Otherwise, need to make a copy of the buffer.
byte[] clonedBuffer = memoryStream.ToArray();
secret = new ProtectedMemoryBlob(clonedBuffer);
secret = new Secret(clonedBuffer);
Array.Clear(clonedBuffer, 0, clonedBuffer.Length);
#endif
}

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Test.Cng
public void Encrypt_Decrypt_RoundTrips()
{
// Arrange
ProtectedMemoryBlob kdk = new ProtectedMemoryBlob(new byte[512 / 8]);
Secret kdk = new Secret(new byte[512 / 8]);
CbcAuthenticatedEncryptor encryptor = new CbcAuthenticatedEncryptor(kdk,
symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_CBC,
symmetricAlgorithmKeySizeInBytes: 256 / 8,
@ -39,7 +39,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Test.Cng
public void Encrypt_Decrypt_Tampering_Fails()
{
// Arrange
ProtectedMemoryBlob kdk = new ProtectedMemoryBlob(new byte[512 / 8]);
Secret kdk = new Secret(new byte[512 / 8]);
CbcAuthenticatedEncryptor encryptor = new CbcAuthenticatedEncryptor(kdk,
symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_CBC,
symmetricAlgorithmKeySizeInBytes: 256 / 8,
@ -86,7 +86,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Test.Cng
public void Encrypt_KnownKey()
{
// Arrange
ProtectedMemoryBlob kdk = new ProtectedMemoryBlob(Encoding.UTF8.GetBytes("master key"));
Secret kdk = new Secret(Encoding.UTF8.GetBytes("master key"));
CbcAuthenticatedEncryptor encryptor = new CbcAuthenticatedEncryptor(kdk,
symmetricAlgorithmHandle: CachedAlgorithmHandles.AES_CBC,
symmetricAlgorithmKeySizeInBytes: 256 / 8,

View File

@ -18,7 +18,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Test.Cng
public void Encrypt_Decrypt_RoundTrips()
{
// Arrange
ProtectedMemoryBlob kdk = new ProtectedMemoryBlob(new byte[512 / 8]);
Secret kdk = new Secret(new byte[512 / 8]);
GcmAuthenticatedEncryptor encryptor = new GcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 256 / 8);
ArraySegment<byte> plaintext = new ArraySegment<byte>(Encoding.UTF8.GetBytes("plaintext"));
ArraySegment<byte> aad = new ArraySegment<byte>(Encoding.UTF8.GetBytes("aad"));
@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Test.Cng
public void Encrypt_Decrypt_Tampering_Fails()
{
// Arrange
ProtectedMemoryBlob kdk = new ProtectedMemoryBlob(new byte[512 / 8]);
Secret kdk = new Secret(new byte[512 / 8]);
GcmAuthenticatedEncryptor encryptor = new GcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 256 / 8);
ArraySegment<byte> plaintext = new ArraySegment<byte>(Encoding.UTF8.GetBytes("plaintext"));
ArraySegment<byte> aad = new ArraySegment<byte>(Encoding.UTF8.GetBytes("aad"));
@ -80,7 +80,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Test.Cng
public void Encrypt_KnownKey()
{
// Arrange
ProtectedMemoryBlob kdk = new ProtectedMemoryBlob(Encoding.UTF8.GetBytes("master key"));
Secret kdk = new Secret(Encoding.UTF8.GetBytes("master key"));
GcmAuthenticatedEncryptor encryptor = new GcmAuthenticatedEncryptor(kdk, CachedAlgorithmHandles.AES_GCM, symmetricAlgorithmKeySizeInBytes: 128 / 8, genRandom: new SequentialGenRandom());
ArraySegment<byte> plaintext = new ArraySegment<byte>(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7 }, 2, 3);
ArraySegment<byte> aad = new ArraySegment<byte>(new byte[] { 7, 6, 5, 4, 3, 2, 1, 0 }, 1, 4);

View File

@ -16,7 +16,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Test.Managed
public void Encrypt_Decrypt_RoundTrips()
{
// Arrange
ProtectedMemoryBlob kdk = new ProtectedMemoryBlob(new byte[512 / 8]);
Secret kdk = new Secret(new byte[512 / 8]);
ManagedAuthenticatedEncryptor encryptor = new ManagedAuthenticatedEncryptor(kdk,
symmetricAlgorithmFactory: Aes.Create,
symmetricAlgorithmKeySizeInBytes: 256 / 8,
@ -36,7 +36,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Test.Managed
public void Encrypt_Decrypt_Tampering_Fails()
{
// Arrange
ProtectedMemoryBlob kdk = new ProtectedMemoryBlob(new byte[512 / 8]);
Secret kdk = new Secret(new byte[512 / 8]);
ManagedAuthenticatedEncryptor encryptor = new ManagedAuthenticatedEncryptor(kdk,
symmetricAlgorithmFactory: Aes.Create,
symmetricAlgorithmKeySizeInBytes: 256 / 8,
@ -82,7 +82,7 @@ namespace Microsoft.AspNet.Security.DataProtection.Test.Managed
public void Encrypt_KnownKey()
{
// Arrange
ProtectedMemoryBlob kdk = new ProtectedMemoryBlob(Encoding.UTF8.GetBytes("master key"));
Secret kdk = new Secret(Encoding.UTF8.GetBytes("master key"));
ManagedAuthenticatedEncryptor encryptor = new ManagedAuthenticatedEncryptor(kdk,
symmetricAlgorithmFactory: Aes.Create,
symmetricAlgorithmKeySizeInBytes: 256 / 8,