Merge branch 'ajbaaska/service-desc-cleanup' into dev
This commit is contained in:
commit
eb17453322
|
|
@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
return arraySegment.Array;
|
return arraySegment.Array;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] retVal = new byte[arraySegment.Count];
|
var retVal = new byte[arraySegment.Count];
|
||||||
Buffer.BlockCopy(arraySegment.Array, arraySegment.Offset, retVal, 0, retVal.Length);
|
Buffer.BlockCopy(arraySegment.Array, arraySegment.Offset, retVal, 0, retVal.Length);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||||
public static byte[] Encrypt(this IAuthenticatedEncryptor encryptor, ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize)
|
public static byte[] Encrypt(this IAuthenticatedEncryptor encryptor, ArraySegment<byte> plaintext, ArraySegment<byte> additionalAuthenticatedData, uint preBufferSize, uint postBufferSize)
|
||||||
{
|
{
|
||||||
// Can we call the optimized version?
|
// Can we call the optimized version?
|
||||||
IOptimizedAuthenticatedEncryptor optimizedEncryptor = encryptor as IOptimizedAuthenticatedEncryptor;
|
var optimizedEncryptor = encryptor as IOptimizedAuthenticatedEncryptor;
|
||||||
if (optimizedEncryptor != null)
|
if (optimizedEncryptor != null)
|
||||||
{
|
{
|
||||||
return optimizedEncryptor.Encrypt(plaintext, additionalAuthenticatedData, preBufferSize, postBufferSize);
|
return optimizedEncryptor.Encrypt(plaintext, additionalAuthenticatedData, preBufferSize, postBufferSize);
|
||||||
|
|
@ -25,8 +25,8 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
byte[] temp = encryptor.Encrypt(plaintext, additionalAuthenticatedData);
|
var temp = encryptor.Encrypt(plaintext, additionalAuthenticatedData);
|
||||||
byte[] retVal = new byte[checked(preBufferSize + temp.Length + postBufferSize)];
|
var retVal = new byte[checked(preBufferSize + temp.Length + postBufferSize)];
|
||||||
Buffer.BlockCopy(temp, 0, retVal, checked((int)preBufferSize), temp.Length);
|
Buffer.BlockCopy(temp, 0, retVal, checked((int)preBufferSize), temp.Length);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
@ -39,13 +39,13 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||||
public static void PerformSelfTest(this IAuthenticatedEncryptor encryptor)
|
public static void PerformSelfTest(this IAuthenticatedEncryptor encryptor)
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
Guid plaintextAsGuid = Guid.NewGuid();
|
var plaintextAsGuid = Guid.NewGuid();
|
||||||
byte[] plaintextAsBytes = plaintextAsGuid.ToByteArray();
|
var plaintextAsBytes = plaintextAsGuid.ToByteArray();
|
||||||
byte[] aad = Guid.NewGuid().ToByteArray();
|
var aad = Guid.NewGuid().ToByteArray();
|
||||||
|
|
||||||
// Act
|
// Act
|
||||||
byte[] protectedData = encryptor.Encrypt(new ArraySegment<byte>(plaintextAsBytes), new ArraySegment<byte>(aad));
|
var protectedData = encryptor.Encrypt(new ArraySegment<byte>(plaintextAsBytes), new ArraySegment<byte>(aad));
|
||||||
byte[] roundTrippedData = encryptor.Decrypt(new ArraySegment<byte>(protectedData), new ArraySegment<byte>(aad));
|
var roundTrippedData = encryptor.Decrypt(new ArraySegment<byte>(protectedData), new ArraySegment<byte>(aad));
|
||||||
|
|
||||||
// Assert
|
// Assert
|
||||||
CryptoUtil.Assert(roundTrippedData != null && roundTrippedData.Length == plaintextAsBytes.Length && plaintextAsGuid == new Guid(roundTrippedData),
|
CryptoUtil.Assert(roundTrippedData != null && roundTrippedData.Length == plaintextAsBytes.Length && plaintextAsGuid == new Guid(roundTrippedData),
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure we're using a hash algorithm. We require a minimum 128-bit digest.
|
// Make sure we're using a hash algorithm. We require a minimum 128-bit digest.
|
||||||
uint digestSize = algorithmHandle.GetHashDigestLength();
|
var digestSize = algorithmHandle.GetHashDigestLength();
|
||||||
AlgorithmAssert.IsAllowableValidationAlgorithmDigestSize(checked(digestSize * 8));
|
AlgorithmAssert.IsAllowableValidationAlgorithmDigestSize(checked(digestSize * 8));
|
||||||
|
|
||||||
// all good!
|
// all good!
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.Configurat
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
XElement masterKeyElement = new XElement("masterKey",
|
var masterKeyElement = new XElement("masterKey",
|
||||||
new XComment(" Warning: the key below is in an unencrypted form. "),
|
new XComment(" Warning: the key below is in an unencrypted form. "),
|
||||||
new XElement("value", unprotectedSecretAsBase64String));
|
new XElement("value", unprotectedSecretAsBase64String));
|
||||||
masterKeyElement.MarkAsRequiresEncryption();
|
masterKeyElement.MarkAsRequiresEncryption();
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
|
|
||||||
private byte[] CreateContextHeader()
|
private byte[] CreateContextHeader()
|
||||||
{
|
{
|
||||||
byte[] retVal = new byte[checked(
|
var retVal = new byte[checked(
|
||||||
1 /* KDF alg */
|
1 /* KDF alg */
|
||||||
+ 1 /* chaining mode */
|
+ 1 /* chaining mode */
|
||||||
+ sizeof(uint) /* sym alg key size */
|
+ sizeof(uint) /* sym alg key size */
|
||||||
|
|
@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
BitHelpers.WriteTo(ref ptr, _hmacAlgorithmDigestLengthInBytes);
|
BitHelpers.WriteTo(ref ptr, _hmacAlgorithmDigestLengthInBytes);
|
||||||
|
|
||||||
// See the design document for an explanation of the following code.
|
// See the design document for an explanation of the following code.
|
||||||
byte[] tempKeys = new byte[_symmetricAlgorithmSubkeyLengthInBytes + _hmacAlgorithmSubkeyLengthInBytes];
|
var tempKeys = new byte[_symmetricAlgorithmSubkeyLengthInBytes + _hmacAlgorithmSubkeyLengthInBytes];
|
||||||
fixed (byte* pbTempKeys = tempKeys)
|
fixed (byte* pbTempKeys = tempKeys)
|
||||||
{
|
{
|
||||||
byte dummy;
|
byte dummy;
|
||||||
|
|
@ -151,7 +151,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
|
|
||||||
// Assumption: pbCipherText := { keyModifier | IV | encryptedData | MAC(IV | encryptedPayload) }
|
// Assumption: pbCipherText := { keyModifier | IV | encryptedData | MAC(IV | encryptedPayload) }
|
||||||
|
|
||||||
uint cbEncryptedData = checked(cbCiphertext - (KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes + _hmacAlgorithmDigestLengthInBytes));
|
var cbEncryptedData = checked(cbCiphertext - (KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes + _hmacAlgorithmDigestLengthInBytes));
|
||||||
|
|
||||||
// Calculate offsets
|
// Calculate offsets
|
||||||
byte* pbKeyModifier = pbCiphertext;
|
byte* pbKeyModifier = pbCiphertext;
|
||||||
|
|
@ -161,7 +161,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
|
|
||||||
// Use the KDF to recreate the symmetric encryption and HMAC subkeys
|
// Use the KDF to recreate the symmetric encryption and HMAC subkeys
|
||||||
// We'll need a temporary buffer to hold them
|
// We'll need a temporary buffer to hold them
|
||||||
uint cbTempSubkeys = checked(_symmetricAlgorithmSubkeyLengthInBytes + _hmacAlgorithmSubkeyLengthInBytes);
|
var cbTempSubkeys = checked(_symmetricAlgorithmSubkeyLengthInBytes + _hmacAlgorithmSubkeyLengthInBytes);
|
||||||
byte* pbTempSubkeys = stackalloc byte[checked((int)cbTempSubkeys)];
|
byte* pbTempSubkeys = stackalloc byte[checked((int)cbTempSubkeys)];
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
@ -224,7 +224,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
// assume PKCS#7). So unfortunately we're stuck with the temporary buffer.
|
// assume PKCS#7). So unfortunately we're stuck with the temporary buffer.
|
||||||
// (Querying the output size won't mutate the IV.)
|
// (Querying the output size won't mutate the IV.)
|
||||||
uint dwEstimatedDecryptedByteCount;
|
uint dwEstimatedDecryptedByteCount;
|
||||||
int ntstatus = UnsafeNativeMethods.BCryptDecrypt(
|
var ntstatus = UnsafeNativeMethods.BCryptDecrypt(
|
||||||
hKey: symmetricKeyHandle,
|
hKey: symmetricKeyHandle,
|
||||||
pbInput: pbInput,
|
pbInput: pbInput,
|
||||||
cbInput: cbInput,
|
cbInput: cbInput,
|
||||||
|
|
@ -237,7 +237,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
dwFlags: BCryptEncryptFlags.BCRYPT_BLOCK_PADDING);
|
dwFlags: BCryptEncryptFlags.BCRYPT_BLOCK_PADDING);
|
||||||
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
|
UnsafeNativeMethods.ThrowExceptionForBCryptStatus(ntstatus);
|
||||||
|
|
||||||
byte[] decryptedPayload = new byte[dwEstimatedDecryptedByteCount];
|
var decryptedPayload = new byte[dwEstimatedDecryptedByteCount];
|
||||||
uint dwActualDecryptedByteCount;
|
uint dwActualDecryptedByteCount;
|
||||||
fixed (byte* pbDecryptedPayload = decryptedPayload)
|
fixed (byte* pbDecryptedPayload = decryptedPayload)
|
||||||
{
|
{
|
||||||
|
|
@ -268,7 +268,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// payload takes up only a partial buffer
|
// payload takes up only a partial buffer
|
||||||
byte[] resizedDecryptedPayload = new byte[dwActualDecryptedByteCount];
|
var resizedDecryptedPayload = new byte[dwActualDecryptedByteCount];
|
||||||
Buffer.BlockCopy(decryptedPayload, 0, resizedDecryptedPayload, 0, resizedDecryptedPayload.Length);
|
Buffer.BlockCopy(decryptedPayload, 0, resizedDecryptedPayload, 0, resizedDecryptedPayload.Length);
|
||||||
return resizedDecryptedPayload;
|
return resizedDecryptedPayload;
|
||||||
}
|
}
|
||||||
|
|
@ -282,7 +282,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
UnsafeBufferUtil.BlockCopy(from: pbIV, to: pbClonedIV, byteCount: _symmetricAlgorithmBlockSizeInBytes);
|
UnsafeBufferUtil.BlockCopy(from: pbIV, to: pbClonedIV, byteCount: _symmetricAlgorithmBlockSizeInBytes);
|
||||||
|
|
||||||
uint dwEncryptedBytes;
|
uint dwEncryptedBytes;
|
||||||
int ntstatus = UnsafeNativeMethods.BCryptEncrypt(
|
var ntstatus = UnsafeNativeMethods.BCryptEncrypt(
|
||||||
hKey: symmetricKeyHandle,
|
hKey: symmetricKeyHandle,
|
||||||
pbInput: pbInput,
|
pbInput: pbInput,
|
||||||
cbInput: cbInput,
|
cbInput: cbInput,
|
||||||
|
|
@ -303,13 +303,13 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
{
|
{
|
||||||
// This buffer will be used to hold the symmetric encryption and HMAC subkeys
|
// This buffer will be used to hold the symmetric encryption and HMAC subkeys
|
||||||
// used in the generation of this payload.
|
// used in the generation of this payload.
|
||||||
uint cbTempSubkeys = checked(_symmetricAlgorithmSubkeyLengthInBytes + _hmacAlgorithmSubkeyLengthInBytes);
|
var cbTempSubkeys = checked(_symmetricAlgorithmSubkeyLengthInBytes + _hmacAlgorithmSubkeyLengthInBytes);
|
||||||
byte* pbTempSubkeys = stackalloc byte[checked((int)cbTempSubkeys)];
|
byte* pbTempSubkeys = stackalloc byte[checked((int)cbTempSubkeys)];
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Randomly generate the key modifier and IV.
|
// Randomly generate the key modifier and IV.
|
||||||
uint cbKeyModifierAndIV = checked(KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes);
|
var cbKeyModifierAndIV = checked(KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes);
|
||||||
byte* pbKeyModifierAndIV = stackalloc byte[checked((int)cbKeyModifierAndIV)];
|
byte* pbKeyModifierAndIV = stackalloc byte[checked((int)cbKeyModifierAndIV)];
|
||||||
_genRandom.GenRandom(pbKeyModifierAndIV, cbKeyModifierAndIV);
|
_genRandom.GenRandom(pbKeyModifierAndIV, cbKeyModifierAndIV);
|
||||||
|
|
||||||
|
|
@ -335,10 +335,10 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
{
|
{
|
||||||
// We can't assume PKCS#7 padding (maybe the underlying provider is really 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.
|
// so we need to query the padded output size before we can allocate the return value array.
|
||||||
uint cbOutputCiphertext = GetCbcEncryptedOutputSizeWithPadding(symmetricKeyHandle, pbPlaintext, cbPlaintext);
|
var cbOutputCiphertext = GetCbcEncryptedOutputSizeWithPadding(symmetricKeyHandle, pbPlaintext, cbPlaintext);
|
||||||
|
|
||||||
// Allocate return value array and start copying some data
|
// Allocate return value array and start copying some data
|
||||||
byte[] retVal = new byte[checked(cbPreBuffer + KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes + cbOutputCiphertext + _hmacAlgorithmDigestLengthInBytes + cbPostBuffer)];
|
var retVal = new byte[checked(cbPreBuffer + KEY_MODIFIER_SIZE_IN_BYTES + _symmetricAlgorithmBlockSizeInBytes + cbOutputCiphertext + _hmacAlgorithmDigestLengthInBytes + cbPostBuffer)];
|
||||||
fixed (byte* pbRetVal = retVal)
|
fixed (byte* pbRetVal = retVal)
|
||||||
{
|
{
|
||||||
// Calculate offsets
|
// Calculate offsets
|
||||||
|
|
@ -395,7 +395,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
// Calling BCryptEncrypt with a null output pointer will cause it to return the total number
|
// Calling BCryptEncrypt with a null output pointer will cause it to return the total number
|
||||||
// of bytes required for the output buffer.
|
// of bytes required for the output buffer.
|
||||||
uint dwResult;
|
uint dwResult;
|
||||||
int ntstatus = UnsafeNativeMethods.BCryptEncrypt(
|
var ntstatus = UnsafeNativeMethods.BCryptEncrypt(
|
||||||
hKey: symmetricKeyHandle,
|
hKey: symmetricKeyHandle,
|
||||||
pbInput: pbInput,
|
pbInput: pbInput,
|
||||||
cbInput: cbInput,
|
cbInput: cbInput,
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
{
|
{
|
||||||
Debug.Assert(secret != null);
|
Debug.Assert(secret != null);
|
||||||
|
|
||||||
byte[] plaintextSecret = new byte[secret.Length];
|
var plaintextSecret = new byte[secret.Length];
|
||||||
fixed (byte* pbPlaintextSecret = plaintextSecret)
|
fixed (byte* pbPlaintextSecret = plaintextSecret)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -66,24 +66,24 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
{
|
{
|
||||||
byte dummy; // provides a valid memory address if the secret or entropy has zero length
|
byte dummy; // provides a valid memory address if the secret or entropy has zero length
|
||||||
|
|
||||||
DATA_BLOB dataIn = new DATA_BLOB()
|
var dataIn = new DATA_BLOB()
|
||||||
{
|
{
|
||||||
cbData = cbSecret,
|
cbData = cbSecret,
|
||||||
pbData = (pbSecret != null) ? pbSecret : &dummy
|
pbData = (pbSecret != null) ? pbSecret : &dummy
|
||||||
};
|
};
|
||||||
DATA_BLOB entropy = new DATA_BLOB()
|
var entropy = new DATA_BLOB()
|
||||||
{
|
{
|
||||||
cbData = cbOptionalEntropy,
|
cbData = cbOptionalEntropy,
|
||||||
pbData = (pbOptionalEntropy != null) ? pbOptionalEntropy : &dummy
|
pbData = (pbOptionalEntropy != null) ? pbOptionalEntropy : &dummy
|
||||||
};
|
};
|
||||||
DATA_BLOB dataOut = default(DATA_BLOB);
|
var dataOut = default(DATA_BLOB);
|
||||||
|
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
RuntimeHelpers.PrepareConstrainedRegions();
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||||||
#endif
|
#endif
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool success = UnsafeNativeMethods.CryptProtectData(
|
var success = UnsafeNativeMethods.CryptProtectData(
|
||||||
pDataIn: &dataIn,
|
pDataIn: &dataIn,
|
||||||
szDataDescr: IntPtr.Zero,
|
szDataDescr: IntPtr.Zero,
|
||||||
pOptionalEntropy: &entropy,
|
pOptionalEntropy: &entropy,
|
||||||
|
|
@ -93,12 +93,12 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
pDataOut: out dataOut);
|
pDataOut: out dataOut);
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
int errorCode = Marshal.GetLastWin32Error();
|
var errorCode = Marshal.GetLastWin32Error();
|
||||||
throw new CryptographicException(errorCode);
|
throw new CryptographicException(errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
int dataLength = checked((int)dataOut.cbData);
|
var dataLength = checked((int)dataOut.cbData);
|
||||||
byte[] retVal = new byte[dataLength];
|
var retVal = new byte[dataLength];
|
||||||
Marshal.Copy((IntPtr)dataOut.pbData, retVal, 0, dataLength);
|
Marshal.Copy((IntPtr)dataOut.pbData, retVal, 0, dataLength);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
@ -118,7 +118,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
Debug.Assert(secret != null);
|
Debug.Assert(secret != null);
|
||||||
Debug.Assert(protectionDescriptorHandle != null);
|
Debug.Assert(protectionDescriptorHandle != null);
|
||||||
|
|
||||||
byte[] plaintextSecret = new byte[secret.Length];
|
var plaintextSecret = new byte[secret.Length];
|
||||||
fixed (byte* pbPlaintextSecret = plaintextSecret)
|
fixed (byte* pbPlaintextSecret = plaintextSecret)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -147,7 +147,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
// Perform the encryption operation, putting the protected data into LocalAlloc-allocated memory.
|
// Perform the encryption operation, putting the protected data into LocalAlloc-allocated memory.
|
||||||
LocalAllocHandle protectedData;
|
LocalAllocHandle protectedData;
|
||||||
uint cbProtectedData;
|
uint cbProtectedData;
|
||||||
int ntstatus = UnsafeNativeMethods.NCryptProtectSecret(
|
var ntstatus = UnsafeNativeMethods.NCryptProtectSecret(
|
||||||
hDescriptor: protectionDescriptorHandle,
|
hDescriptor: protectionDescriptorHandle,
|
||||||
dwFlags: NCRYPT_SILENT_FLAG,
|
dwFlags: NCRYPT_SILENT_FLAG,
|
||||||
pbData: pbData,
|
pbData: pbData,
|
||||||
|
|
@ -162,12 +162,12 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
// Copy the data from LocalAlloc-allocated memory into a managed memory buffer.
|
// Copy the data from LocalAlloc-allocated memory into a managed memory buffer.
|
||||||
using (protectedData)
|
using (protectedData)
|
||||||
{
|
{
|
||||||
byte[] retVal = new byte[cbProtectedData];
|
var retVal = new byte[cbProtectedData];
|
||||||
if (cbProtectedData > 0)
|
if (cbProtectedData > 0)
|
||||||
{
|
{
|
||||||
fixed (byte* pbRetVal = retVal)
|
fixed (byte* pbRetVal = retVal)
|
||||||
{
|
{
|
||||||
bool handleAcquired = false;
|
var handleAcquired = false;
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
RuntimeHelpers.PrepareConstrainedRegions();
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -206,24 +206,24 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
{
|
{
|
||||||
byte dummy; // provides a valid memory address if the secret or entropy has zero length
|
byte dummy; // provides a valid memory address if the secret or entropy has zero length
|
||||||
|
|
||||||
DATA_BLOB dataIn = new DATA_BLOB()
|
var dataIn = new DATA_BLOB()
|
||||||
{
|
{
|
||||||
cbData = cbProtectedData,
|
cbData = cbProtectedData,
|
||||||
pbData = (pbProtectedData != null) ? pbProtectedData : &dummy
|
pbData = (pbProtectedData != null) ? pbProtectedData : &dummy
|
||||||
};
|
};
|
||||||
DATA_BLOB entropy = new DATA_BLOB()
|
var entropy = new DATA_BLOB()
|
||||||
{
|
{
|
||||||
cbData = cbOptionalEntropy,
|
cbData = cbOptionalEntropy,
|
||||||
pbData = (pbOptionalEntropy != null) ? pbOptionalEntropy : &dummy
|
pbData = (pbOptionalEntropy != null) ? pbOptionalEntropy : &dummy
|
||||||
};
|
};
|
||||||
DATA_BLOB dataOut = default(DATA_BLOB);
|
var dataOut = default(DATA_BLOB);
|
||||||
|
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
RuntimeHelpers.PrepareConstrainedRegions();
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||||||
#endif
|
#endif
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
bool success = UnsafeNativeMethods.CryptUnprotectData(
|
var success = UnsafeNativeMethods.CryptUnprotectData(
|
||||||
pDataIn: &dataIn,
|
pDataIn: &dataIn,
|
||||||
ppszDataDescr: IntPtr.Zero,
|
ppszDataDescr: IntPtr.Zero,
|
||||||
pOptionalEntropy: &entropy,
|
pOptionalEntropy: &entropy,
|
||||||
|
|
@ -233,7 +233,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
pDataOut: out dataOut);
|
pDataOut: out dataOut);
|
||||||
if (!success)
|
if (!success)
|
||||||
{
|
{
|
||||||
int errorCode = Marshal.GetLastWin32Error();
|
var errorCode = Marshal.GetLastWin32Error();
|
||||||
throw new CryptographicException(errorCode);
|
throw new CryptographicException(errorCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -271,7 +271,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
// First, decrypt the payload into LocalAlloc-allocated memory.
|
// First, decrypt the payload into LocalAlloc-allocated memory.
|
||||||
LocalAllocHandle unencryptedPayloadHandle;
|
LocalAllocHandle unencryptedPayloadHandle;
|
||||||
uint cbUnencryptedPayload;
|
uint cbUnencryptedPayload;
|
||||||
int ntstatus = UnsafeNativeMethods.NCryptUnprotectSecret(
|
var ntstatus = UnsafeNativeMethods.NCryptUnprotectSecret(
|
||||||
phDescriptor: IntPtr.Zero,
|
phDescriptor: IntPtr.Zero,
|
||||||
dwFlags: NCRYPT_SILENT_FLAG,
|
dwFlags: NCRYPT_SILENT_FLAG,
|
||||||
pbProtectedBlob: pbData,
|
pbProtectedBlob: pbData,
|
||||||
|
|
@ -290,7 +290,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
// the window is extremely small and AppDomain unloads should not happen here in practice.
|
// the window is extremely small and AppDomain unloads should not happen here in practice.
|
||||||
using (unencryptedPayloadHandle)
|
using (unencryptedPayloadHandle)
|
||||||
{
|
{
|
||||||
bool handleAcquired = false;
|
var handleAcquired = false;
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
RuntimeHelpers.PrepareConstrainedRegions();
|
RuntimeHelpers.PrepareConstrainedRegions();
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -331,7 +331,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
NCryptDescriptorHandle descriptorHandle;
|
NCryptDescriptorHandle descriptorHandle;
|
||||||
LocalAllocHandle unprotectedDataHandle;
|
LocalAllocHandle unprotectedDataHandle;
|
||||||
uint cbUnprotectedData;
|
uint cbUnprotectedData;
|
||||||
int ntstatus = UnsafeNativeMethods.NCryptUnprotectSecret(
|
var ntstatus = UnsafeNativeMethods.NCryptUnprotectSecret(
|
||||||
phDescriptor: out descriptorHandle,
|
phDescriptor: out descriptorHandle,
|
||||||
dwFlags: NCRYPT_UNPROTECT_NO_DECRYPT,
|
dwFlags: NCRYPT_UNPROTECT_NO_DECRYPT,
|
||||||
pbProtectedBlob: pbData,
|
pbProtectedBlob: pbData,
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
|
|
||||||
private byte[] CreateContextHeader()
|
private byte[] CreateContextHeader()
|
||||||
{
|
{
|
||||||
byte[] retVal = new byte[checked(
|
var retVal = new byte[checked(
|
||||||
1 /* KDF alg */
|
1 /* KDF alg */
|
||||||
+ 1 /* chaining mode */
|
+ 1 /* chaining mode */
|
||||||
+ sizeof(uint) /* sym alg key size */
|
+ sizeof(uint) /* sym alg key size */
|
||||||
|
|
@ -77,7 +77,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
BitHelpers.WriteTo(ref ptr, TAG_SIZE_IN_BYTES);
|
BitHelpers.WriteTo(ref ptr, TAG_SIZE_IN_BYTES);
|
||||||
|
|
||||||
// See the design document for an explanation of the following code.
|
// See the design document for an explanation of the following code.
|
||||||
byte[] tempKeys = new byte[_symmetricAlgorithmSubkeyLengthInBytes];
|
var tempKeys = new byte[_symmetricAlgorithmSubkeyLengthInBytes];
|
||||||
fixed (byte* pbTempKeys = tempKeys)
|
fixed (byte* pbTempKeys = tempKeys)
|
||||||
{
|
{
|
||||||
byte dummy;
|
byte dummy;
|
||||||
|
|
@ -125,9 +125,9 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
|
|
||||||
// Assumption: pbCipherText := { keyModifier || nonce || encryptedData || authenticationTag }
|
// Assumption: pbCipherText := { keyModifier || nonce || encryptedData || authenticationTag }
|
||||||
|
|
||||||
uint cbPlaintext = checked(cbCiphertext - (KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES));
|
var cbPlaintext = checked(cbCiphertext - (KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + TAG_SIZE_IN_BYTES));
|
||||||
|
|
||||||
byte[] retVal = new byte[cbPlaintext];
|
var retVal = new byte[cbPlaintext];
|
||||||
fixed (byte* pbRetVal = retVal)
|
fixed (byte* pbRetVal = retVal)
|
||||||
{
|
{
|
||||||
// Calculate offsets
|
// Calculate offsets
|
||||||
|
|
@ -166,7 +166,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
|
|
||||||
// The call to BCryptDecrypt will also validate the authentication tag
|
// The call to BCryptDecrypt will also validate the authentication tag
|
||||||
uint cbDecryptedBytesWritten;
|
uint cbDecryptedBytesWritten;
|
||||||
int ntstatus = UnsafeNativeMethods.BCryptDecrypt(
|
var ntstatus = UnsafeNativeMethods.BCryptDecrypt(
|
||||||
hKey: decryptionSubkeyHandle,
|
hKey: decryptionSubkeyHandle,
|
||||||
pbInput: pbEncryptedData,
|
pbInput: pbEncryptedData,
|
||||||
cbInput: cbPlaintext,
|
cbInput: cbPlaintext,
|
||||||
|
|
@ -216,7 +216,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
using (var keyHandle = _symmetricAlgorithmHandle.GenerateSymmetricKey(pbKey, cbKey))
|
using (var keyHandle = _symmetricAlgorithmHandle.GenerateSymmetricKey(pbKey, cbKey))
|
||||||
{
|
{
|
||||||
uint cbResult;
|
uint cbResult;
|
||||||
int ntstatus = UnsafeNativeMethods.BCryptEncrypt(
|
var ntstatus = UnsafeNativeMethods.BCryptEncrypt(
|
||||||
hKey: keyHandle,
|
hKey: keyHandle,
|
||||||
pbInput: pbPlaintextData,
|
pbInput: pbPlaintextData,
|
||||||
cbInput: cbPlaintextData,
|
cbInput: cbPlaintextData,
|
||||||
|
|
@ -236,7 +236,7 @@ namespace Microsoft.AspNetCore.DataProtection.Cng
|
||||||
{
|
{
|
||||||
// Allocate a buffer to hold the key modifier, nonce, encrypted data, and tag.
|
// Allocate a buffer to hold the key modifier, nonce, encrypted data, and tag.
|
||||||
// In GCM, the encrypted output will be the same length as the plaintext input.
|
// In GCM, the encrypted output will be the same length as the plaintext input.
|
||||||
byte[] retVal = new byte[checked(cbPreBuffer + KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + cbPlaintext + TAG_SIZE_IN_BYTES + cbPostBuffer)];
|
var retVal = new byte[checked(cbPreBuffer + KEY_MODIFIER_SIZE_IN_BYTES + NONCE_SIZE_IN_BYTES + cbPlaintext + TAG_SIZE_IN_BYTES + cbPostBuffer)];
|
||||||
fixed (byte* pbRetVal = retVal)
|
fixed (byte* pbRetVal = retVal)
|
||||||
{
|
{
|
||||||
// Calculate offsets
|
// Calculate offsets
|
||||||
|
|
|
||||||
|
|
@ -528,7 +528,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
private static void RemoveAllServicesOfType(IServiceCollection services, Type serviceType)
|
private static void RemoveAllServicesOfType(IServiceCollection services, Type serviceType)
|
||||||
{
|
{
|
||||||
// We go backward since we're modifying the collection in-place.
|
// We go backward since we're modifying the collection in-place.
|
||||||
for (int i = services.Count - 1; i >= 0; i--)
|
for (var i = services.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
if (services[i]?.ServiceType == serviceType)
|
if (services[i]?.ServiceType == serviceType)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -24,20 +24,6 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal static class DataProtectionServiceDescriptors
|
internal static class DataProtectionServiceDescriptors
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IConfigureOptions{DataProtectionOptions}"/> backed by the host-provided defaults.
|
|
||||||
/// </summary>
|
|
||||||
public static ServiceDescriptor ConfigureOptions_DataProtectionOptions()
|
|
||||||
{
|
|
||||||
return ServiceDescriptor.Transient<IConfigureOptions<DataProtectionOptions>>(services =>
|
|
||||||
{
|
|
||||||
return new ConfigureOptions<DataProtectionOptions>(options =>
|
|
||||||
{
|
|
||||||
options.ApplicationDiscriminator = services.GetApplicationUniqueIdentifier();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An <see cref="IConfigureOptions{KeyManagementOptions}"/> where the key lifetime is specified explicitly.
|
/// An <see cref="IConfigureOptions{KeyManagementOptions}"/> where the key lifetime is specified explicitly.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -53,14 +39,6 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IAuthenticatedEncryptorConfiguration"/> backed by default algorithmic options.
|
|
||||||
/// </summary>
|
|
||||||
public static ServiceDescriptor IAuthenticatedEncryptorConfiguration_Default()
|
|
||||||
{
|
|
||||||
return IAuthenticatedEncryptorConfiguration_FromSettings(new AuthenticatedEncryptionSettings());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An <see cref="IAuthenticatedEncryptorConfiguration"/> backed by an <see cref="IInternalAuthenticatedEncryptionSettings"/>.
|
/// An <see cref="IAuthenticatedEncryptorConfiguration"/> backed by an <see cref="IInternalAuthenticatedEncryptionSettings"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -79,18 +57,6 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IDataProtectionProvider"/> backed by the default keyring.
|
|
||||||
/// </summary>
|
|
||||||
public static ServiceDescriptor IDataProtectionProvider_Default()
|
|
||||||
{
|
|
||||||
return ServiceDescriptor.Singleton<IDataProtectionProvider>(
|
|
||||||
services => DataProtectionProviderFactory.GetProviderFromServices(
|
|
||||||
options: services.GetRequiredService<IOptions<DataProtectionOptions>>().Value,
|
|
||||||
services: services,
|
|
||||||
mustCreateImmediately: true /* this is the ultimate fallback */));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An ephemeral <see cref="IDataProtectionProvider"/>.
|
/// An ephemeral <see cref="IDataProtectionProvider"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
@ -110,14 +76,6 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
return ServiceDescriptor.Singleton<IKeyEscrowSink>(services => services.GetActivator().CreateInstance<IKeyEscrowSink>(implementationTypeName));
|
return ServiceDescriptor.Singleton<IKeyEscrowSink>(services => services.GetActivator().CreateInstance<IKeyEscrowSink>(implementationTypeName));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IKeyManager"/> backed by the default XML key manager.
|
|
||||||
/// </summary>
|
|
||||||
public static ServiceDescriptor IKeyManager_Default()
|
|
||||||
{
|
|
||||||
return ServiceDescriptor.Singleton<IKeyManager>(services => new XmlKeyManager(services));
|
|
||||||
}
|
|
||||||
|
|
||||||
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|
@ -167,14 +125,6 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
return ServiceDescriptor.Singleton<IXmlRepository>(services => new FileSystemXmlRepository(directory, services));
|
return ServiceDescriptor.Singleton<IXmlRepository>(services => new FileSystemXmlRepository(directory, services));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// An <see cref="IXmlRepository"/> backed by volatile in-process memory.
|
|
||||||
/// </summary>
|
|
||||||
public static ServiceDescriptor IXmlRepository_InMemory()
|
|
||||||
{
|
|
||||||
return ServiceDescriptor.Singleton<IXmlRepository>(services => new EphemeralXmlRepository(services));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// An <see cref="IXmlRepository"/> backed by the Windows registry.
|
/// An <see cref="IXmlRepository"/> backed by the Windows registry.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,14 @@ using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Microsoft.AspNetCore.Cryptography.Cng;
|
using Microsoft.AspNetCore.Cryptography.Cng;
|
||||||
using Microsoft.AspNetCore.DataProtection;
|
using Microsoft.AspNetCore.DataProtection;
|
||||||
|
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption;
|
||||||
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
using Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel;
|
||||||
using Microsoft.AspNetCore.DataProtection.Cng;
|
using Microsoft.AspNetCore.DataProtection.Cng;
|
||||||
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
using Microsoft.AspNetCore.DataProtection.KeyManagement;
|
||||||
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
|
using Microsoft.AspNetCore.DataProtection.KeyManagement.Internal;
|
||||||
using Microsoft.AspNetCore.DataProtection.Repositories;
|
using Microsoft.AspNetCore.DataProtection.Repositories;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
|
|
||||||
namespace Microsoft.Extensions.DependencyInjection
|
namespace Microsoft.Extensions.DependencyInjection
|
||||||
|
|
@ -34,7 +36,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
// we'll not use the fallback at all.
|
// we'll not use the fallback at all.
|
||||||
yield return ServiceDescriptor.Singleton<IDefaultKeyServices>(services =>
|
yield return ServiceDescriptor.Singleton<IDefaultKeyServices>(services =>
|
||||||
{
|
{
|
||||||
ILogger log = services.GetLogger(typeof(DataProtectionServices));
|
var log = services.GetLogger(typeof(DataProtectionServices));
|
||||||
|
|
||||||
ServiceDescriptor keyEncryptorDescriptor = null;
|
ServiceDescriptor keyEncryptorDescriptor = null;
|
||||||
ServiceDescriptor keyRepositoryDescriptor = null;
|
ServiceDescriptor keyRepositoryDescriptor = null;
|
||||||
|
|
@ -92,7 +94,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
{
|
{
|
||||||
// Final fallback - use an ephemeral repository since we don't know where else to go.
|
// Final fallback - use an ephemeral repository since we don't know where else to go.
|
||||||
// This can only be used for development scenarios.
|
// This can only be used for development scenarios.
|
||||||
keyRepositoryDescriptor = DataProtectionServiceDescriptors.IXmlRepository_InMemory();
|
keyRepositoryDescriptor = ServiceDescriptor.Singleton<IXmlRepository>(
|
||||||
|
s => new EphemeralXmlRepository(s));
|
||||||
|
|
||||||
log?.UsingEphemeralKeyRepository();
|
log?.UsingEphemeralKeyRepository();
|
||||||
}
|
}
|
||||||
|
|
@ -106,8 +109,13 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
});
|
});
|
||||||
|
|
||||||
// Provide root key management and data protection services
|
// Provide root key management and data protection services
|
||||||
yield return DataProtectionServiceDescriptors.IKeyManager_Default();
|
yield return ServiceDescriptor.Singleton<IKeyManager>(services => new XmlKeyManager(services));
|
||||||
yield return DataProtectionServiceDescriptors.IDataProtectionProvider_Default();
|
|
||||||
|
yield return ServiceDescriptor.Singleton<IDataProtectionProvider>(
|
||||||
|
services => DataProtectionProviderFactory.GetProviderFromServices(
|
||||||
|
options: services.GetRequiredService<IOptions<DataProtectionOptions>>().Value,
|
||||||
|
services: services,
|
||||||
|
mustCreateImmediately: true /* this is the ultimate fallback */));
|
||||||
|
|
||||||
// Provide services required for XML encryption
|
// Provide services required for XML encryption
|
||||||
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
#if !NETSTANDARD1_3 // [[ISSUE60]] Remove this #ifdef when Core CLR gets support for EncryptedXml
|
||||||
|
|
@ -115,10 +123,16 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Hook up the logic which allows populating default options
|
// Hook up the logic which allows populating default options
|
||||||
yield return DataProtectionServiceDescriptors.ConfigureOptions_DataProtectionOptions();
|
yield return ServiceDescriptor.Transient<IConfigureOptions<DataProtectionOptions>>(services =>
|
||||||
|
{
|
||||||
|
return new ConfigureOptions<DataProtectionOptions>(options =>
|
||||||
|
{
|
||||||
|
options.ApplicationDiscriminator = services.GetApplicationUniqueIdentifier();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Read and apply policy from the registry, overriding any other defaults.
|
// Read and apply policy from the registry, overriding any other defaults.
|
||||||
bool encryptorConfigurationReadFromRegistry = false;
|
var encryptorConfigurationReadFromRegistry = false;
|
||||||
if (OSVersionUtil.IsWindows())
|
if (OSVersionUtil.IsWindows())
|
||||||
{
|
{
|
||||||
foreach (var descriptor in RegistryPolicyResolver.ResolveDefaultPolicy())
|
foreach (var descriptor in RegistryPolicyResolver.ResolveDefaultPolicy())
|
||||||
|
|
@ -134,7 +148,8 @@ namespace Microsoft.Extensions.DependencyInjection
|
||||||
// Finally, provide a fallback encryptor configuration if one wasn't already specified.
|
// Finally, provide a fallback encryptor configuration if one wasn't already specified.
|
||||||
if (!encryptorConfigurationReadFromRegistry)
|
if (!encryptorConfigurationReadFromRegistry)
|
||||||
{
|
{
|
||||||
yield return DataProtectionServiceDescriptors.IAuthenticatedEncryptorConfiguration_Default();
|
yield return DataProtectionServiceDescriptors.IAuthenticatedEncryptorConfiguration_FromSettings(
|
||||||
|
new AuthenticatedEncryptionSettings());;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
{
|
{
|
||||||
public static InvalidOperationException CertificateXmlEncryptor_CertificateNotFound(string thumbprint)
|
public static InvalidOperationException CertificateXmlEncryptor_CertificateNotFound(string thumbprint)
|
||||||
{
|
{
|
||||||
string message = Resources.FormatCertificateXmlEncryptor_CertificateNotFound(thumbprint);
|
var message = Resources.FormatCertificateXmlEncryptor_CertificateNotFound(thumbprint);
|
||||||
return new InvalidOperationException(message);
|
return new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
|
|
||||||
public static ArgumentException Common_BufferIncorrectlySized(string parameterName, int actualSize, int expectedSize)
|
public static ArgumentException Common_BufferIncorrectlySized(string parameterName, int actualSize, int expectedSize)
|
||||||
{
|
{
|
||||||
string message = Resources.FormatCommon_BufferIncorrectlySized(actualSize, expectedSize);
|
var message = Resources.FormatCommon_BufferIncorrectlySized(actualSize, expectedSize);
|
||||||
return new ArgumentException(message, parameterName);
|
return new ArgumentException(message, parameterName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -33,19 +33,19 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
|
|
||||||
public static CryptographicException CryptCommon_PayloadInvalid()
|
public static CryptographicException CryptCommon_PayloadInvalid()
|
||||||
{
|
{
|
||||||
string message = Resources.CryptCommon_PayloadInvalid;
|
var message = Resources.CryptCommon_PayloadInvalid;
|
||||||
return new CryptographicException(message);
|
return new CryptographicException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InvalidOperationException Common_PropertyCannotBeNullOrEmpty(string propertyName)
|
public static InvalidOperationException Common_PropertyCannotBeNullOrEmpty(string propertyName)
|
||||||
{
|
{
|
||||||
string message = String.Format(CultureInfo.CurrentCulture, Resources.Common_PropertyCannotBeNullOrEmpty, propertyName);
|
var message = String.Format(CultureInfo.CurrentCulture, Resources.Common_PropertyCannotBeNullOrEmpty, propertyName);
|
||||||
return new InvalidOperationException(message);
|
return new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InvalidOperationException Common_PropertyMustBeNonNegative(string propertyName)
|
public static InvalidOperationException Common_PropertyMustBeNonNegative(string propertyName)
|
||||||
{
|
{
|
||||||
string message = String.Format(CultureInfo.CurrentCulture, Resources.Common_PropertyMustBeNonNegative, propertyName);
|
var message = String.Format(CultureInfo.CurrentCulture, Resources.Common_PropertyMustBeNonNegative, propertyName);
|
||||||
return new InvalidOperationException(message);
|
return new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -56,13 +56,13 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
|
|
||||||
public static CryptographicException Common_KeyNotFound(Guid id)
|
public static CryptographicException Common_KeyNotFound(Guid id)
|
||||||
{
|
{
|
||||||
string message = String.Format(CultureInfo.CurrentCulture, Resources.Common_KeyNotFound, id);
|
var message = String.Format(CultureInfo.CurrentCulture, Resources.Common_KeyNotFound, id);
|
||||||
return new CryptographicException(message);
|
return new CryptographicException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CryptographicException Common_KeyRevoked(Guid id)
|
public static CryptographicException Common_KeyRevoked(Guid id)
|
||||||
{
|
{
|
||||||
string message = String.Format(CultureInfo.CurrentCulture, Resources.Common_KeyRevoked, id);
|
var message = String.Format(CultureInfo.CurrentCulture, Resources.Common_KeyRevoked, id);
|
||||||
return new CryptographicException(message);
|
return new CryptographicException(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
|
|
||||||
public static InvalidOperationException XmlKeyManager_DuplicateKey(Guid keyId)
|
public static InvalidOperationException XmlKeyManager_DuplicateKey(Guid keyId)
|
||||||
{
|
{
|
||||||
string message = String.Format(CultureInfo.CurrentCulture, Resources.XmlKeyManager_DuplicateKey, keyId);
|
var message = String.Format(CultureInfo.CurrentCulture, Resources.XmlKeyManager_DuplicateKey, keyId);
|
||||||
return new InvalidOperationException(message);
|
return new InvalidOperationException(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
|
|
||||||
public DefaultKeyResolution ResolveDefaultKeyPolicy(DateTimeOffset now, IEnumerable<IKey> allKeys)
|
public DefaultKeyResolution ResolveDefaultKeyPolicy(DateTimeOffset now, IEnumerable<IKey> allKeys)
|
||||||
{
|
{
|
||||||
DefaultKeyResolution retVal = default(DefaultKeyResolution);
|
var retVal = default(DefaultKeyResolution);
|
||||||
retVal.DefaultKey = FindDefaultKey(now, allKeys, out retVal.FallbackKey, out retVal.ShouldGenerateNewKey);
|
retVal.DefaultKey = FindDefaultKey(now, allKeys, out retVal.FallbackKey, out retVal.ShouldGenerateNewKey);
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
internal CacheableKeyRing WithTemporaryExtendedLifetime(DateTimeOffset now)
|
internal CacheableKeyRing WithTemporaryExtendedLifetime(DateTimeOffset now)
|
||||||
{
|
{
|
||||||
TimeSpan extension = TimeSpan.FromMinutes(2);
|
var extension = TimeSpan.FromMinutes(2);
|
||||||
return new CacheableKeyRing(CancellationToken.None, now + extension, KeyRing);
|
return new CacheableKeyRing(CancellationToken.None, now + extension, KeyRing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -71,7 +71,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
{
|
{
|
||||||
// simple double-check lock pattern
|
// simple double-check lock pattern
|
||||||
// we can't use LazyInitializer<T> because we don't have a simple value factory
|
// we can't use LazyInitializer<T> because we don't have a simple value factory
|
||||||
IAuthenticatedEncryptor encryptor = Volatile.Read(ref _encryptor);
|
var encryptor = Volatile.Read(ref _encryptor);
|
||||||
if (encryptor == null)
|
if (encryptor == null)
|
||||||
{
|
{
|
||||||
lock (this)
|
lock (this)
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
{
|
{
|
||||||
if (originalPurposes != null && originalPurposes.Length > 0)
|
if (originalPurposes != null && originalPurposes.Length > 0)
|
||||||
{
|
{
|
||||||
string[] newPurposes = new string[originalPurposes.Length + 1];
|
var newPurposes = new string[originalPurposes.Length + 1];
|
||||||
Array.Copy(originalPurposes, 0, newPurposes, 0, originalPurposes.Length);
|
Array.Copy(originalPurposes, 0, newPurposes, 0, originalPurposes.Length);
|
||||||
newPurposes[originalPurposes.Length] = newPurpose;
|
newPurposes[originalPurposes.Length] = newPurpose;
|
||||||
return newPurposes;
|
return newPurposes;
|
||||||
|
|
@ -83,7 +83,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
}
|
}
|
||||||
|
|
||||||
UnprotectStatus status;
|
UnprotectStatus status;
|
||||||
byte[] retVal = UnprotectCore(protectedData, ignoreRevocationErrors, status: out status);
|
var retVal = UnprotectCore(protectedData, ignoreRevocationErrors, status: out status);
|
||||||
requiresMigration = (status != UnprotectStatus.Ok);
|
requiresMigration = (status != UnprotectStatus.Ok);
|
||||||
wasRevoked = (status == UnprotectStatus.DecryptionKeyWasRevoked);
|
wasRevoked = (status == UnprotectStatus.DecryptionKeyWasRevoked);
|
||||||
return retVal;
|
return retVal;
|
||||||
|
|
@ -117,10 +117,10 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
|
|
||||||
// We'll need to apply the default key id to the template if it hasn't already been applied.
|
// We'll need to apply the default key id to the template if it hasn't already been applied.
|
||||||
// If the default key id has been updated since the last call to Protect, also write back the updated template.
|
// If the default key id has been updated since the last call to Protect, also write back the updated template.
|
||||||
byte[] aad = _aadTemplate.GetAadForKey(defaultKeyId, isProtecting: true);
|
var aad = _aadTemplate.GetAadForKey(defaultKeyId, isProtecting: true);
|
||||||
|
|
||||||
// We allocate a 20-byte pre-buffer so that we can inject the magic header and key id into the return value.
|
// We allocate a 20-byte pre-buffer so that we can inject the magic header and key id into the return value.
|
||||||
byte[] retVal = defaultEncryptorInstance.Encrypt(
|
var retVal = defaultEncryptorInstance.Encrypt(
|
||||||
plaintext: new ArraySegment<byte>(plaintext),
|
plaintext: new ArraySegment<byte>(plaintext),
|
||||||
additionalAuthenticatedData: new ArraySegment<byte>(aad),
|
additionalAuthenticatedData: new ArraySegment<byte>(aad),
|
||||||
preBufferSize: (uint)(sizeof(uint) + sizeof(Guid)),
|
preBufferSize: (uint)(sizeof(uint) + sizeof(Guid)),
|
||||||
|
|
@ -324,7 +324,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
{
|
{
|
||||||
writer.WriteBigEndian(MAGIC_HEADER_V0);
|
writer.WriteBigEndian(MAGIC_HEADER_V0);
|
||||||
Debug.Assert(ms.Position == sizeof(uint));
|
Debug.Assert(ms.Position == sizeof(uint));
|
||||||
long posPurposeCount = writer.Seek(sizeof(Guid), SeekOrigin.Current); // skip over where the key id will be stored; we'll fill it in later
|
var posPurposeCount = writer.Seek(sizeof(Guid), SeekOrigin.Current); // skip over where the key id will be stored; we'll fill it in later
|
||||||
writer.Seek(sizeof(uint), SeekOrigin.Current); // skip over where the purposeCount will be stored; we'll fill it in later
|
writer.Seek(sizeof(uint), SeekOrigin.Current); // skip over where the purposeCount will be stored; we'll fill it in later
|
||||||
|
|
||||||
uint purposeCount = 0;
|
uint purposeCount = 0;
|
||||||
|
|
@ -347,7 +347,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
{
|
{
|
||||||
// Multiple threads might be trying to read and write the _aadTemplate field
|
// Multiple threads might be trying to read and write the _aadTemplate field
|
||||||
// simultaneously. We need to make sure all accesses to it are thread-safe.
|
// simultaneously. We need to make sure all accesses to it are thread-safe.
|
||||||
byte[] existingTemplate = Volatile.Read(ref _aadTemplate);
|
var existingTemplate = Volatile.Read(ref _aadTemplate);
|
||||||
Debug.Assert(existingTemplate.Length >= sizeof(uint) /* MAGIC_HEADER */ + sizeof(Guid) /* keyId */);
|
Debug.Assert(existingTemplate.Length >= sizeof(uint) /* MAGIC_HEADER */ + sizeof(Guid) /* keyId */);
|
||||||
|
|
||||||
// If the template is already initialized to this key id, return it.
|
// If the template is already initialized to this key id, return it.
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
|
|
||||||
_logger?.UsingKeyAsDefaultKey(defaultKey.KeyId);
|
_logger?.UsingKeyAsDefaultKey(defaultKey.KeyId);
|
||||||
|
|
||||||
DateTimeOffset nextAutoRefreshTime = now + GetRefreshPeriodWithJitter(_keyManagementOptions.KeyRingRefreshPeriod);
|
var nextAutoRefreshTime = now + GetRefreshPeriodWithJitter(_keyManagementOptions.KeyRingRefreshPeriod);
|
||||||
|
|
||||||
// The cached keyring should expire at the earliest of (default key expiration, next auto-refresh time).
|
// The cached keyring should expire at the earliest of (default key expiration, next auto-refresh time).
|
||||||
// Since the refresh period and safety window are not user-settable, we can guarantee that there's at
|
// Since the refresh period and safety window are not user-settable, we can guarantee that there's at
|
||||||
|
|
@ -139,7 +139,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
// update the keyring, and all other threads will continue to use the existing cached
|
// update the keyring, and all other threads will continue to use the existing cached
|
||||||
// keyring while the first thread performs the update. There is an exception: if there
|
// keyring while the first thread performs the update. There is an exception: if there
|
||||||
// is no usable existing cached keyring, all callers must block until the keyring exists.
|
// is no usable existing cached keyring, all callers must block until the keyring exists.
|
||||||
bool acquiredLock = false;
|
var acquiredLock = false;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Monitor.TryEnter(_cacheableKeyRingLockObj, (existingCacheableKeyRing != null) ? 0 : Timeout.Infinite, ref acquiredLock);
|
Monitor.TryEnter(_cacheableKeyRingLockObj, (existingCacheableKeyRing != null) ? 0 : Timeout.Infinite, ref acquiredLock);
|
||||||
|
|
|
||||||
|
|
@ -142,7 +142,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
{
|
{
|
||||||
// ProcessKeyElement can return null in the case of failure, and if this happens we'll move on.
|
// ProcessKeyElement can return null in the case of failure, and if this happens we'll move on.
|
||||||
// Still need to throw if we see duplicate keys with the same id.
|
// Still need to throw if we see duplicate keys with the same id.
|
||||||
KeyBase key = ProcessKeyElement(element);
|
var key = ProcessKeyElement(element);
|
||||||
if (key != null)
|
if (key != null)
|
||||||
{
|
{
|
||||||
if (keyIdToKeyMap.ContainsKey(key.KeyId))
|
if (keyIdToKeyMap.ContainsKey(key.KeyId))
|
||||||
|
|
@ -154,7 +154,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
}
|
}
|
||||||
else if (element.Name == RevocationElementName)
|
else if (element.Name == RevocationElementName)
|
||||||
{
|
{
|
||||||
object revocationInfo = ProcessRevocationElement(element);
|
var revocationInfo = ProcessRevocationElement(element);
|
||||||
if (revocationInfo is Guid)
|
if (revocationInfo is Guid)
|
||||||
{
|
{
|
||||||
// a single key was revoked
|
// a single key was revoked
|
||||||
|
|
@ -276,7 +276,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// only one key is being revoked
|
// only one key is being revoked
|
||||||
Guid keyId = XmlConvert.ToGuid(keyIdAsString);
|
var keyId = XmlConvert.ToGuid(keyIdAsString);
|
||||||
_logger?.FoundRevocationOfKey(keyId);
|
_logger?.FoundRevocationOfKey(keyId);
|
||||||
return keyId;
|
return keyId;
|
||||||
}
|
}
|
||||||
|
|
@ -397,7 +397,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
var possiblyEncryptedKeyElement = KeyEncryptor?.EncryptIfNecessary(keyElement) ?? keyElement;
|
var possiblyEncryptedKeyElement = KeyEncryptor?.EncryptIfNecessary(keyElement) ?? keyElement;
|
||||||
|
|
||||||
// Persist it to the underlying repository and trigger the cancellation token.
|
// Persist it to the underlying repository and trigger the cancellation token.
|
||||||
string friendlyName = Invariant($"key-{keyId:D}");
|
var friendlyName = Invariant($"key-{keyId:D}");
|
||||||
KeyRepository.StoreElement(possiblyEncryptedKeyElement, friendlyName);
|
KeyRepository.StoreElement(possiblyEncryptedKeyElement, friendlyName);
|
||||||
TriggerAndResetCacheExpirationToken();
|
TriggerAndResetCacheExpirationToken();
|
||||||
|
|
||||||
|
|
@ -415,11 +415,11 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
// Figure out who will be deserializing this
|
// Figure out who will be deserializing this
|
||||||
XElement descriptorElement = keyElement.Element(DescriptorElementName);
|
var descriptorElement = keyElement.Element(DescriptorElementName);
|
||||||
string descriptorDeserializerTypeName = (string)descriptorElement.Attribute(DeserializerTypeAttributeName);
|
string descriptorDeserializerTypeName = (string)descriptorElement.Attribute(DeserializerTypeAttributeName);
|
||||||
|
|
||||||
// Decrypt the descriptor element and pass it to the descriptor for consumption
|
// Decrypt the descriptor element and pass it to the descriptor for consumption
|
||||||
XElement unencryptedInputToDeserializer = descriptorElement.Elements().Single().DecryptElement(_activator);
|
var unencryptedInputToDeserializer = descriptorElement.Elements().Single().DecryptElement(_activator);
|
||||||
var deserializerInstance = _activator.CreateInstance<IAuthenticatedEncryptorDescriptorDeserializer>(descriptorDeserializerTypeName);
|
var deserializerInstance = _activator.CreateInstance<IAuthenticatedEncryptorDescriptorDeserializer>(descriptorDeserializerTypeName);
|
||||||
var descriptorInstance = deserializerInstance.ImportFromXml(unencryptedInputToDeserializer);
|
var descriptorInstance = deserializerInstance.ImportFromXml(unencryptedInputToDeserializer);
|
||||||
|
|
||||||
|
|
@ -450,7 +450,7 @@ namespace Microsoft.AspNetCore.DataProtection.KeyManagement
|
||||||
new XElement(ReasonElementName, reason));
|
new XElement(ReasonElementName, reason));
|
||||||
|
|
||||||
// Persist it to the underlying repository and trigger the cancellation token
|
// Persist it to the underlying repository and trigger the cancellation token
|
||||||
string friendlyName = Invariant($"revocation-{keyId:D}");
|
var friendlyName = Invariant($"revocation-{keyId:D}");
|
||||||
KeyRepository.StoreElement(revocationElement, friendlyName);
|
KeyRepository.StoreElement(revocationElement, friendlyName);
|
||||||
TriggerAndResetCacheExpirationToken();
|
TriggerAndResetCacheExpirationToken();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ namespace Microsoft.AspNetCore.DataProtection.Managed
|
||||||
var EMPTY_ARRAY = new byte[0];
|
var EMPTY_ARRAY = new byte[0];
|
||||||
var EMPTY_ARRAY_SEGMENT = new ArraySegment<byte>(EMPTY_ARRAY);
|
var EMPTY_ARRAY_SEGMENT = new ArraySegment<byte>(EMPTY_ARRAY);
|
||||||
|
|
||||||
byte[] retVal = new byte[checked(
|
var retVal = new byte[checked(
|
||||||
1 /* KDF alg */
|
1 /* KDF alg */
|
||||||
+ 1 /* chaining mode */
|
+ 1 /* chaining mode */
|
||||||
+ sizeof(uint) /* sym alg key size */
|
+ sizeof(uint) /* sym alg key size */
|
||||||
|
|
@ -82,7 +82,7 @@ namespace Microsoft.AspNetCore.DataProtection.Managed
|
||||||
+ _symmetricAlgorithmBlockSizeInBytes /* ciphertext of encrypted empty string */
|
+ _symmetricAlgorithmBlockSizeInBytes /* ciphertext of encrypted empty string */
|
||||||
+ _validationAlgorithmDigestLengthInBytes /* digest of HMACed empty string */)];
|
+ _validationAlgorithmDigestLengthInBytes /* digest of HMACed empty string */)];
|
||||||
|
|
||||||
int idx = 0;
|
var idx = 0;
|
||||||
|
|
||||||
// First is the two-byte header
|
// First is the two-byte header
|
||||||
retVal[idx++] = 0; // 0x00 = SP800-108 CTR KDF w/ HMACSHA512 PRF
|
retVal[idx++] = 0; // 0x00 = SP800-108 CTR KDF w/ HMACSHA512 PRF
|
||||||
|
|
@ -97,7 +97,7 @@ namespace Microsoft.AspNetCore.DataProtection.Managed
|
||||||
BitHelpers.WriteTo(retVal, ref idx, _validationAlgorithmDigestLengthInBytes);
|
BitHelpers.WriteTo(retVal, ref idx, _validationAlgorithmDigestLengthInBytes);
|
||||||
|
|
||||||
// See the design document for an explanation of the following code.
|
// See the design document for an explanation of the following code.
|
||||||
byte[] tempKeys = new byte[_symmetricAlgorithmSubkeyLengthInBytes + _validationAlgorithmSubkeyLengthInBytes];
|
var tempKeys = new byte[_symmetricAlgorithmSubkeyLengthInBytes + _validationAlgorithmSubkeyLengthInBytes];
|
||||||
ManagedSP800_108_CTR_HMACSHA512.DeriveKeys(
|
ManagedSP800_108_CTR_HMACSHA512.DeriveKeys(
|
||||||
kdk: EMPTY_ARRAY,
|
kdk: EMPTY_ARRAY,
|
||||||
label: EMPTY_ARRAY_SEGMENT,
|
label: EMPTY_ARRAY_SEGMENT,
|
||||||
|
|
@ -114,7 +114,7 @@ namespace Microsoft.AspNetCore.DataProtection.Managed
|
||||||
rgbKey: new ArraySegment<byte>(tempKeys, 0, _symmetricAlgorithmSubkeyLengthInBytes).AsStandaloneArray(),
|
rgbKey: new ArraySegment<byte>(tempKeys, 0, _symmetricAlgorithmSubkeyLengthInBytes).AsStandaloneArray(),
|
||||||
rgbIV: new byte[_symmetricAlgorithmBlockSizeInBytes]))
|
rgbIV: new byte[_symmetricAlgorithmBlockSizeInBytes]))
|
||||||
{
|
{
|
||||||
byte[] ciphertext = cryptoTransform.TransformFinalBlock(EMPTY_ARRAY, 0, 0);
|
var ciphertext = cryptoTransform.TransformFinalBlock(EMPTY_ARRAY, 0, 0);
|
||||||
CryptoUtil.Assert(ciphertext != null && ciphertext.Length == _symmetricAlgorithmBlockSizeInBytes, "ciphertext != null && ciphertext.Length == _symmetricAlgorithmBlockSizeInBytes");
|
CryptoUtil.Assert(ciphertext != null && ciphertext.Length == _symmetricAlgorithmBlockSizeInBytes, "ciphertext != null && ciphertext.Length == _symmetricAlgorithmBlockSizeInBytes");
|
||||||
Buffer.BlockCopy(ciphertext, 0, retVal, idx, ciphertext.Length);
|
Buffer.BlockCopy(ciphertext, 0, retVal, idx, ciphertext.Length);
|
||||||
}
|
}
|
||||||
|
|
@ -125,7 +125,7 @@ namespace Microsoft.AspNetCore.DataProtection.Managed
|
||||||
// MAC a zero-length input string and copy the digest to the return buffer.
|
// MAC a zero-length input string and copy the digest to the return buffer.
|
||||||
using (var hashAlg = CreateValidationAlgorithm(new ArraySegment<byte>(tempKeys, _symmetricAlgorithmSubkeyLengthInBytes, _validationAlgorithmSubkeyLengthInBytes).AsStandaloneArray()))
|
using (var hashAlg = CreateValidationAlgorithm(new ArraySegment<byte>(tempKeys, _symmetricAlgorithmSubkeyLengthInBytes, _validationAlgorithmSubkeyLengthInBytes).AsStandaloneArray()))
|
||||||
{
|
{
|
||||||
byte[] digest = hashAlg.ComputeHash(EMPTY_ARRAY);
|
var digest = hashAlg.ComputeHash(EMPTY_ARRAY);
|
||||||
CryptoUtil.Assert(digest != null && digest.Length == _validationAlgorithmDigestLengthInBytes, "digest != null && digest.Length == _validationAlgorithmDigestLengthInBytes");
|
CryptoUtil.Assert(digest != null && digest.Length == _validationAlgorithmDigestLengthInBytes, "digest != null && digest.Length == _validationAlgorithmDigestLengthInBytes");
|
||||||
Buffer.BlockCopy(digest, 0, retVal, idx, digest.Length);
|
Buffer.BlockCopy(digest, 0, retVal, idx, digest.Length);
|
||||||
}
|
}
|
||||||
|
|
@ -187,16 +187,16 @@ namespace Microsoft.AspNetCore.DataProtection.Managed
|
||||||
}
|
}
|
||||||
|
|
||||||
ArraySegment<byte> keyModifier = new ArraySegment<byte>(protectedPayload.Array, keyModifierOffset, ivOffset - keyModifierOffset);
|
ArraySegment<byte> keyModifier = new ArraySegment<byte>(protectedPayload.Array, keyModifierOffset, ivOffset - keyModifierOffset);
|
||||||
byte[] iv = new byte[_symmetricAlgorithmBlockSizeInBytes];
|
var iv = new byte[_symmetricAlgorithmBlockSizeInBytes];
|
||||||
Buffer.BlockCopy(protectedPayload.Array, ivOffset, iv, 0, iv.Length);
|
Buffer.BlockCopy(protectedPayload.Array, ivOffset, iv, 0, iv.Length);
|
||||||
|
|
||||||
// Step 2: Decrypt the KDK and use it to restore the original encryption and MAC keys.
|
// Step 2: Decrypt the KDK and use it to restore the original encryption and MAC keys.
|
||||||
// We pin all unencrypted keys to limit their exposure via GC relocation.
|
// We pin all unencrypted keys to limit their exposure via GC relocation.
|
||||||
|
|
||||||
byte[] decryptedKdk = new byte[_keyDerivationKey.Length];
|
var decryptedKdk = new byte[_keyDerivationKey.Length];
|
||||||
byte[] decryptionSubkey = new byte[_symmetricAlgorithmSubkeyLengthInBytes];
|
var decryptionSubkey = new byte[_symmetricAlgorithmSubkeyLengthInBytes];
|
||||||
byte[] validationSubkey = new byte[_validationAlgorithmSubkeyLengthInBytes];
|
var validationSubkey = new byte[_validationAlgorithmSubkeyLengthInBytes];
|
||||||
byte[] derivedKeysBuffer = new byte[checked(decryptionSubkey.Length + validationSubkey.Length)];
|
var derivedKeysBuffer = new byte[checked(decryptionSubkey.Length + validationSubkey.Length)];
|
||||||
|
|
||||||
fixed (byte* __unused__1 = decryptedKdk)
|
fixed (byte* __unused__1 = decryptedKdk)
|
||||||
fixed (byte* __unused__2 = decryptionSubkey)
|
fixed (byte* __unused__2 = decryptionSubkey)
|
||||||
|
|
@ -289,8 +289,8 @@ namespace Microsoft.AspNetCore.DataProtection.Managed
|
||||||
// Step 1: Generate a random key modifier and IV for this operation.
|
// Step 1: Generate a random key modifier and IV for this operation.
|
||||||
// Both will be equal to the block size of the block cipher algorithm.
|
// Both will be equal to the block size of the block cipher algorithm.
|
||||||
|
|
||||||
byte[] keyModifier = _genRandom.GenRandom(KEY_MODIFIER_SIZE_IN_BYTES);
|
var keyModifier = _genRandom.GenRandom(KEY_MODIFIER_SIZE_IN_BYTES);
|
||||||
byte[] iv = _genRandom.GenRandom(_symmetricAlgorithmBlockSizeInBytes);
|
var iv = _genRandom.GenRandom(_symmetricAlgorithmBlockSizeInBytes);
|
||||||
|
|
||||||
// Step 2: Copy the key modifier and the IV to the output stream since they'll act as a header.
|
// Step 2: Copy the key modifier and the IV to the output stream since they'll act as a header.
|
||||||
|
|
||||||
|
|
@ -302,10 +302,10 @@ namespace Microsoft.AspNetCore.DataProtection.Managed
|
||||||
// Step 3: Decrypt the KDK, and use it to generate new encryption and HMAC keys.
|
// Step 3: Decrypt the KDK, and use it to generate new encryption and HMAC keys.
|
||||||
// We pin all unencrypted keys to limit their exposure via GC relocation.
|
// We pin all unencrypted keys to limit their exposure via GC relocation.
|
||||||
|
|
||||||
byte[] decryptedKdk = new byte[_keyDerivationKey.Length];
|
var decryptedKdk = new byte[_keyDerivationKey.Length];
|
||||||
byte[] encryptionSubkey = new byte[_symmetricAlgorithmSubkeyLengthInBytes];
|
var encryptionSubkey = new byte[_symmetricAlgorithmSubkeyLengthInBytes];
|
||||||
byte[] validationSubkey = new byte[_validationAlgorithmSubkeyLengthInBytes];
|
var validationSubkey = new byte[_validationAlgorithmSubkeyLengthInBytes];
|
||||||
byte[] derivedKeysBuffer = new byte[checked(encryptionSubkey.Length + validationSubkey.Length)];
|
var derivedKeysBuffer = new byte[checked(encryptionSubkey.Length + validationSubkey.Length)];
|
||||||
|
|
||||||
fixed (byte* __unused__1 = decryptedKdk)
|
fixed (byte* __unused__1 = decryptedKdk)
|
||||||
fixed (byte* __unused__2 = encryptionSubkey)
|
fixed (byte* __unused__2 = encryptionSubkey)
|
||||||
|
|
@ -345,12 +345,12 @@ namespace Microsoft.AspNetCore.DataProtection.Managed
|
||||||
{
|
{
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
// As an optimization, avoid duplicating the underlying buffer if we're on desktop CLR.
|
// As an optimization, avoid duplicating the underlying buffer if we're on desktop CLR.
|
||||||
byte[] underlyingBuffer = outputStream.GetBuffer();
|
var underlyingBuffer = outputStream.GetBuffer();
|
||||||
#else
|
#else
|
||||||
byte[] underlyingBuffer = outputStream.ToArray();
|
var underlyingBuffer = outputStream.ToArray();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
byte[] mac = validationAlgorithm.ComputeHash(underlyingBuffer, KEY_MODIFIER_SIZE_IN_BYTES, checked((int)outputStream.Length - KEY_MODIFIER_SIZE_IN_BYTES));
|
var mac = validationAlgorithm.ComputeHash(underlyingBuffer, KEY_MODIFIER_SIZE_IN_BYTES, checked((int)outputStream.Length - KEY_MODIFIER_SIZE_IN_BYTES));
|
||||||
outputStream.Write(mac, 0, mac.Length);
|
outputStream.Write(mac, 0, mac.Length);
|
||||||
|
|
||||||
// At this point, outputStream := { keyModifier || IV || ciphertext || MAC(IV || ciphertext) }
|
// At this point, outputStream := { keyModifier || IV || ciphertext || MAC(IV || ciphertext) }
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace Microsoft.AspNetCore.DataProtection.Managed
|
||||||
|
|
||||||
public byte[] GenRandom(int numBytes)
|
public byte[] GenRandom(int numBytes)
|
||||||
{
|
{
|
||||||
byte[] bytes = new byte[numBytes];
|
var bytes = new byte[numBytes];
|
||||||
_rng.GetBytes(bytes);
|
_rng.GetBytes(bytes);
|
||||||
return bytes;
|
return bytes;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
{
|
{
|
||||||
if (propInfo.IsDefined(typeof(ApplyPolicyAttribute)))
|
if (propInfo.IsDefined(typeof(ApplyPolicyAttribute)))
|
||||||
{
|
{
|
||||||
object valueFromRegistry = key.GetValue(propInfo.Name);
|
var valueFromRegistry = key.GetValue(propInfo.Name);
|
||||||
if (valueFromRegistry != null)
|
if (valueFromRegistry != null)
|
||||||
{
|
{
|
||||||
if (propInfo.PropertyType == typeof(string))
|
if (propInfo.PropertyType == typeof(string))
|
||||||
|
|
@ -68,7 +68,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
{
|
{
|
||||||
foreach (string sinkFromRegistry in sinksFromRegistry.Split(';'))
|
foreach (string sinkFromRegistry in sinksFromRegistry.Split(';'))
|
||||||
{
|
{
|
||||||
string candidate = sinkFromRegistry.Trim();
|
var candidate = sinkFromRegistry.Trim();
|
||||||
if (!String.IsNullOrEmpty(candidate))
|
if (!String.IsNullOrEmpty(candidate))
|
||||||
{
|
{
|
||||||
typeof(IKeyEscrowSink).AssertIsAssignableFrom(Type.GetType(candidate, throwOnError: true));
|
typeof(IKeyEscrowSink).AssertIsAssignableFrom(Type.GetType(candidate, throwOnError: true));
|
||||||
|
|
@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static ServiceDescriptor[] ResolveDefaultPolicy()
|
public static ServiceDescriptor[] ResolveDefaultPolicy()
|
||||||
{
|
{
|
||||||
RegistryKey subKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\DotNetPackages\Microsoft.AspNetCore.DataProtection");
|
var subKey = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\DotNetPackages\Microsoft.AspNetCore.DataProtection");
|
||||||
if (subKey != null)
|
if (subKey != null)
|
||||||
{
|
{
|
||||||
using (subKey)
|
using (subKey)
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
||||||
throw new ArgumentNullException(nameof(element));
|
throw new ArgumentNullException(nameof(element));
|
||||||
}
|
}
|
||||||
|
|
||||||
XElement cloned = new XElement(element); // makes a deep copy so caller doesn't inadvertently modify it
|
var cloned = new XElement(element); // makes a deep copy so caller doesn't inadvertently modify it
|
||||||
|
|
||||||
// under lock for thread safety
|
// under lock for thread safety
|
||||||
lock (_storedElements)
|
lock (_storedElements)
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
||||||
{
|
{
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
// Environment.GetFolderPath returns null if the user profile isn't loaded.
|
// Environment.GetFolderPath returns null if the user profile isn't loaded.
|
||||||
string folderPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
var folderPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||||
if (!String.IsNullOrEmpty(folderPath))
|
if (!String.IsNullOrEmpty(folderPath))
|
||||||
{
|
{
|
||||||
return GetKeyStorageDirectoryFromBaseAppDataPath(folderPath);
|
return GetKeyStorageDirectoryFromBaseAppDataPath(folderPath);
|
||||||
|
|
@ -161,7 +161,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
||||||
// the %HOME% variable to build up our base key storage path.
|
// the %HOME% variable to build up our base key storage path.
|
||||||
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID")))
|
if (!String.IsNullOrEmpty(Environment.GetEnvironmentVariable("WEBSITE_INSTANCE_ID")))
|
||||||
{
|
{
|
||||||
string homeEnvVar = Environment.GetEnvironmentVariable("HOME");
|
var homeEnvVar = Environment.GetEnvironmentVariable("HOME");
|
||||||
if (!String.IsNullOrEmpty(homeEnvVar))
|
if (!String.IsNullOrEmpty(homeEnvVar))
|
||||||
{
|
{
|
||||||
return GetKeyStorageDirectoryFromBaseAppDataPath(homeEnvVar);
|
return GetKeyStorageDirectoryFromBaseAppDataPath(homeEnvVar);
|
||||||
|
|
@ -202,7 +202,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
||||||
|
|
||||||
if (!IsSafeFilename(friendlyName))
|
if (!IsSafeFilename(friendlyName))
|
||||||
{
|
{
|
||||||
string newFriendlyName = Guid.NewGuid().ToString();
|
var newFriendlyName = Guid.NewGuid().ToString();
|
||||||
_logger?.NameIsNotSafeFileName(friendlyName, newFriendlyName);
|
_logger?.NameIsNotSafeFileName(friendlyName, newFriendlyName);
|
||||||
friendlyName = newFriendlyName;
|
friendlyName = newFriendlyName;
|
||||||
}
|
}
|
||||||
|
|
@ -217,8 +217,8 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
||||||
// crashes mid-write, we won't end up with a corrupt .xml file.
|
// crashes mid-write, we won't end up with a corrupt .xml file.
|
||||||
|
|
||||||
Directory.Create(); // won't throw if the directory already exists
|
Directory.Create(); // won't throw if the directory already exists
|
||||||
string tempFilename = Path.Combine(Directory.FullName, Guid.NewGuid().ToString() + ".tmp");
|
var tempFilename = Path.Combine(Directory.FullName, Guid.NewGuid().ToString() + ".tmp");
|
||||||
string finalFilename = Path.Combine(Directory.FullName, filename + ".xml");
|
var finalFilename = Path.Combine(Directory.FullName, filename + ".xml");
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -88,7 +88,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
||||||
|
|
||||||
foreach (string valueName in RegistryKey.GetValueNames())
|
foreach (string valueName in RegistryKey.GetValueNames())
|
||||||
{
|
{
|
||||||
XElement element = ReadElementFromRegKey(RegistryKey, valueName);
|
var element = ReadElementFromRegKey(RegistryKey, valueName);
|
||||||
if (element != null)
|
if (element != null)
|
||||||
{
|
{
|
||||||
yield return element;
|
yield return element;
|
||||||
|
|
@ -107,7 +107,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
||||||
// Even though this is in HKLM, WAS ensures that applications hosted in IIS are properly isolated.
|
// Even though this is in HKLM, WAS ensures that applications hosted in IIS are properly isolated.
|
||||||
// See APP_POOL::EnsureSharedMachineKeyStorage in WAS source for more info.
|
// See APP_POOL::EnsureSharedMachineKeyStorage in WAS source for more info.
|
||||||
// The version number will need to change if IIS hosts Core CLR directly.
|
// The version number will need to change if IIS hosts Core CLR directly.
|
||||||
string aspnetAutoGenKeysBaseKeyName = Invariant($@"SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\{WindowsIdentity.GetCurrent().User.Value}");
|
var aspnetAutoGenKeysBaseKeyName = Invariant($@"SOFTWARE\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeys\{WindowsIdentity.GetCurrent().User.Value}");
|
||||||
var aspnetBaseKey = hklmBaseKey.OpenSubKey(aspnetAutoGenKeysBaseKeyName, writable: true);
|
var aspnetBaseKey = hklmBaseKey.OpenSubKey(aspnetAutoGenKeysBaseKeyName, writable: true);
|
||||||
if (aspnetBaseKey != null)
|
if (aspnetBaseKey != null)
|
||||||
{
|
{
|
||||||
|
|
@ -143,7 +143,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
||||||
{
|
{
|
||||||
_logger?.ReadingDataFromRegistryKeyValue(regKey, valueName);
|
_logger?.ReadingDataFromRegistryKeyValue(regKey, valueName);
|
||||||
|
|
||||||
string data = regKey.GetValue(valueName) as string;
|
var data = regKey.GetValue(valueName) as string;
|
||||||
return (!String.IsNullOrEmpty(data)) ? XElement.Parse(data) : null;
|
return (!String.IsNullOrEmpty(data)) ? XElement.Parse(data) : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -156,7 +156,7 @@ namespace Microsoft.AspNetCore.DataProtection.Repositories
|
||||||
|
|
||||||
if (!IsSafeRegistryValueName(friendlyName))
|
if (!IsSafeRegistryValueName(friendlyName))
|
||||||
{
|
{
|
||||||
string newFriendlyName = Guid.NewGuid().ToString();
|
var newFriendlyName = Guid.NewGuid().ToString();
|
||||||
_logger?.NameIsNotSafeRegistryValueName(friendlyName, newFriendlyName);
|
_logger?.NameIsNotSafeRegistryValueName(friendlyName, newFriendlyName);
|
||||||
friendlyName = newFriendlyName;
|
friendlyName = newFriendlyName;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,13 +13,13 @@ namespace Microsoft.AspNetCore.DataProtection.SP800_108
|
||||||
public static void DeriveKeys(byte[] kdk, ArraySegment<byte> label, ArraySegment<byte> context, Func<byte[], HashAlgorithm> prfFactory, ArraySegment<byte> output)
|
public static void DeriveKeys(byte[] kdk, ArraySegment<byte> label, ArraySegment<byte> context, Func<byte[], HashAlgorithm> prfFactory, ArraySegment<byte> output)
|
||||||
{
|
{
|
||||||
// make copies so we can mutate these local vars
|
// make copies so we can mutate these local vars
|
||||||
int outputOffset = output.Offset;
|
var outputOffset = output.Offset;
|
||||||
int outputCount = output.Count;
|
var outputCount = output.Count;
|
||||||
|
|
||||||
using (HashAlgorithm prf = prfFactory(kdk))
|
using (var prf = prfFactory(kdk))
|
||||||
{
|
{
|
||||||
// See SP800-108, Sec. 5.1 for the format of the input to the PRF routine.
|
// See SP800-108, Sec. 5.1 for the format of the input to the PRF routine.
|
||||||
byte[] prfInput = new byte[checked(sizeof(uint) /* [i]_2 */ + label.Count + 1 /* 0x00 */ + context.Count + sizeof(uint) /* [K]_2 */)];
|
var prfInput = new byte[checked(sizeof(uint) /* [i]_2 */ + label.Count + 1 /* 0x00 */ + context.Count + sizeof(uint) /* [K]_2 */)];
|
||||||
|
|
||||||
// Copy [L]_2 to prfInput since it's stable over all iterations
|
// Copy [L]_2 to prfInput since it's stable over all iterations
|
||||||
uint outputSizeInBits = (uint)checked((int)outputCount * 8);
|
uint outputSizeInBits = (uint)checked((int)outputCount * 8);
|
||||||
|
|
@ -32,7 +32,7 @@ namespace Microsoft.AspNetCore.DataProtection.SP800_108
|
||||||
Buffer.BlockCopy(label.Array, label.Offset, prfInput, sizeof(uint), label.Count);
|
Buffer.BlockCopy(label.Array, label.Offset, prfInput, sizeof(uint), label.Count);
|
||||||
Buffer.BlockCopy(context.Array, context.Offset, prfInput, sizeof(int) + label.Count + 1, context.Count);
|
Buffer.BlockCopy(context.Array, context.Offset, prfInput, sizeof(int) + label.Count + 1, context.Count);
|
||||||
|
|
||||||
int prfOutputSizeInBytes = prf.GetDigestSizeInBytes();
|
var prfOutputSizeInBytes = prf.GetDigestSizeInBytes();
|
||||||
for (uint i = 1; outputCount > 0; i++)
|
for (uint i = 1; outputCount > 0; i++)
|
||||||
{
|
{
|
||||||
// Copy [i]_2 to prfInput since it mutates with each iteration
|
// Copy [i]_2 to prfInput since it mutates with each iteration
|
||||||
|
|
@ -42,9 +42,9 @@ namespace Microsoft.AspNetCore.DataProtection.SP800_108
|
||||||
prfInput[3] = (byte)(i);
|
prfInput[3] = (byte)(i);
|
||||||
|
|
||||||
// Run the PRF and copy the results to the output buffer
|
// Run the PRF and copy the results to the output buffer
|
||||||
byte[] prfOutput = prf.ComputeHash(prfInput);
|
var prfOutput = prf.ComputeHash(prfInput);
|
||||||
CryptoUtil.Assert(prfOutputSizeInBytes == prfOutput.Length, "prfOutputSizeInBytes == prfOutput.Length");
|
CryptoUtil.Assert(prfOutputSizeInBytes == prfOutput.Length, "prfOutputSizeInBytes == prfOutput.Length");
|
||||||
int numBytesToCopyThisIteration = Math.Min(prfOutputSizeInBytes, outputCount);
|
var numBytesToCopyThisIteration = Math.Min(prfOutputSizeInBytes, outputCount);
|
||||||
Buffer.BlockCopy(prfOutput, 0, output.Array, outputOffset, numBytesToCopyThisIteration);
|
Buffer.BlockCopy(prfOutput, 0, output.Array, outputOffset, numBytesToCopyThisIteration);
|
||||||
Array.Clear(prfOutput, 0, prfOutput.Length); // contains key material, so delete it
|
Array.Clear(prfOutput, 0, prfOutput.Length); // contains key material, so delete it
|
||||||
|
|
||||||
|
|
@ -57,7 +57,7 @@ namespace Microsoft.AspNetCore.DataProtection.SP800_108
|
||||||
|
|
||||||
public static void DeriveKeysWithContextHeader(byte[] kdk, ArraySegment<byte> label, byte[] contextHeader, ArraySegment<byte> context, Func<byte[], HashAlgorithm> prfFactory, ArraySegment<byte> output)
|
public static void DeriveKeysWithContextHeader(byte[] kdk, ArraySegment<byte> label, byte[] contextHeader, ArraySegment<byte> context, Func<byte[], HashAlgorithm> prfFactory, ArraySegment<byte> output)
|
||||||
{
|
{
|
||||||
byte[] combinedContext = new byte[checked(contextHeader.Length + context.Count)];
|
var combinedContext = new byte[checked(contextHeader.Length + context.Count)];
|
||||||
Buffer.BlockCopy(contextHeader, 0, combinedContext, 0, contextHeader.Length);
|
Buffer.BlockCopy(contextHeader, 0, combinedContext, 0, contextHeader.Length);
|
||||||
Buffer.BlockCopy(context.Array, context.Offset, combinedContext, contextHeader.Length, context.Count);
|
Buffer.BlockCopy(context.Array, context.Offset, combinedContext, contextHeader.Length, context.Count);
|
||||||
DeriveKeys(kdk, label, new ArraySegment<byte>(combinedContext), prfFactory, output);
|
DeriveKeys(kdk, label, new ArraySegment<byte>(combinedContext), prfFactory, output);
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.DataProtection.SP800_108
|
||||||
{
|
{
|
||||||
public static void DeriveKeyWithContextHeader(this ISP800_108_CTR_HMACSHA512Provider provider, byte* pbLabel, uint cbLabel, byte[] contextHeader, byte* pbContext, uint cbContext, byte* pbDerivedKey, uint cbDerivedKey)
|
public static void DeriveKeyWithContextHeader(this ISP800_108_CTR_HMACSHA512Provider provider, byte* pbLabel, uint cbLabel, byte[] contextHeader, byte* pbContext, uint cbContext, byte* pbDerivedKey, uint cbDerivedKey)
|
||||||
{
|
{
|
||||||
uint cbCombinedContext = checked((uint)contextHeader.Length + cbContext);
|
var cbCombinedContext = checked((uint)contextHeader.Length + cbContext);
|
||||||
|
|
||||||
// Try allocating the combined context on the stack to avoid temporary managed objects; only fall back to heap if buffers are too large.
|
// Try allocating the combined context on the stack to avoid temporary managed objects; only fall back to heap if buffers are too large.
|
||||||
byte[] heapAllocatedCombinedContext = (cbCombinedContext > Constants.MAX_STACKALLOC_BYTES) ? new byte[cbCombinedContext] : null;
|
byte[] heapAllocatedCombinedContext = (cbCombinedContext > Constants.MAX_STACKALLOC_BYTES) ? new byte[cbCombinedContext] : null;
|
||||||
|
|
|
||||||
|
|
@ -39,7 +39,7 @@ namespace Microsoft.AspNetCore.DataProtection.SP800_108
|
||||||
// Creates a provider from the given secret.
|
// Creates a provider from the given secret.
|
||||||
public static ISP800_108_CTR_HMACSHA512Provider CreateProvider(Secret kdk)
|
public static ISP800_108_CTR_HMACSHA512Provider CreateProvider(Secret kdk)
|
||||||
{
|
{
|
||||||
uint secretLengthInBytes = checked((uint)kdk.Length);
|
var secretLengthInBytes = checked((uint)kdk.Length);
|
||||||
if (secretLengthInBytes == 0)
|
if (secretLengthInBytes == 0)
|
||||||
{
|
{
|
||||||
return CreateEmptyProvider();
|
return CreateEmptyProvider();
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ namespace Microsoft.AspNetCore.DataProtection.SP800_108
|
||||||
// NOTE: pbDerivedKey and cbDerivedKey are modified as data is copied to the output buffer.
|
// NOTE: pbDerivedKey and cbDerivedKey are modified as data is copied to the output buffer.
|
||||||
|
|
||||||
// this will be zero-inited
|
// this will be zero-inited
|
||||||
byte[] tempInputBuffer = new byte[checked(
|
var tempInputBuffer = new byte[checked(
|
||||||
sizeof(int) /* [i] */
|
sizeof(int) /* [i] */
|
||||||
+ cbLabel /* Label */
|
+ cbLabel /* Label */
|
||||||
+ 1 /* 0x00 */
|
+ 1 /* 0x00 */
|
||||||
|
|
|
||||||
|
|
@ -45,14 +45,14 @@ namespace Microsoft.AspNetCore.DataProtection.SP800_108
|
||||||
pBuffers[2].cbBuffer = checked(SHA512_ALG_CHAR_COUNT * sizeof(char));
|
pBuffers[2].cbBuffer = checked(SHA512_ALG_CHAR_COUNT * sizeof(char));
|
||||||
|
|
||||||
// Add the header which points to the buffers
|
// Add the header which points to the buffers
|
||||||
BCryptBufferDesc bufferDesc = default(BCryptBufferDesc);
|
var bufferDesc = default(BCryptBufferDesc);
|
||||||
BCryptBufferDesc.Initialize(ref bufferDesc);
|
BCryptBufferDesc.Initialize(ref bufferDesc);
|
||||||
bufferDesc.cBuffers = 3;
|
bufferDesc.cBuffers = 3;
|
||||||
bufferDesc.pBuffers = pBuffers;
|
bufferDesc.pBuffers = pBuffers;
|
||||||
|
|
||||||
// Finally, invoke the KDF
|
// Finally, invoke the KDF
|
||||||
uint numBytesDerived;
|
uint numBytesDerived;
|
||||||
int ntstatus = UnsafeNativeMethods.BCryptKeyDerivation(
|
var ntstatus = UnsafeNativeMethods.BCryptKeyDerivation(
|
||||||
hKey: _keyHandle,
|
hKey: _keyHandle,
|
||||||
pParameterList: &bufferDesc,
|
pParameterList: &bufferDesc,
|
||||||
pbDerivedKey: pbDerivedKey,
|
pbDerivedKey: pbDerivedKey,
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
throw new ArgumentNullException(nameof(secret));
|
throw new ArgumentNullException(nameof(secret));
|
||||||
}
|
}
|
||||||
|
|
||||||
Secret other = secret as Secret;
|
var other = secret as Secret;
|
||||||
if (other != null)
|
if (other != null)
|
||||||
{
|
{
|
||||||
// Fast-track: simple deep copy scenario.
|
// Fast-track: simple deep copy scenario.
|
||||||
|
|
@ -85,7 +85,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
{
|
{
|
||||||
// Copy the secret to a temporary managed buffer, then protect the buffer.
|
// Copy the secret to a temporary managed buffer, then protect the buffer.
|
||||||
// We pin the temp buffer and zero it out when we're finished to limit exposure of the secret.
|
// We pin the temp buffer and zero it out when we're finished to limit exposure of the secret.
|
||||||
byte[] tempPlaintextBuffer = new byte[secret.Length];
|
var tempPlaintextBuffer = new byte[secret.Length];
|
||||||
fixed (byte* pbTempPlaintextBuffer = tempPlaintextBuffer)
|
fixed (byte* pbTempPlaintextBuffer = tempPlaintextBuffer)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -136,14 +136,14 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
// mark this memory page as non-pageable, but this is fraught with peril.
|
// mark this memory page as non-pageable, but this is fraught with peril.
|
||||||
if (!OSVersionUtil.IsWindows())
|
if (!OSVersionUtil.IsWindows())
|
||||||
{
|
{
|
||||||
SecureLocalAllocHandle handle = SecureLocalAllocHandle.Allocate((IntPtr)checked((int)cbPlaintext));
|
var handle = SecureLocalAllocHandle.Allocate((IntPtr)checked((int)cbPlaintext));
|
||||||
UnsafeBufferUtil.BlockCopy(from: pbPlaintext, to: handle, byteCount: cbPlaintext);
|
UnsafeBufferUtil.BlockCopy(from: pbPlaintext, to: handle, byteCount: cbPlaintext);
|
||||||
return handle;
|
return handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We need to make sure we're a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE.
|
// We need to make sure we're a multiple of CRYPTPROTECTMEMORY_BLOCK_SIZE.
|
||||||
uint numTotalBytesToAllocate = cbPlaintext;
|
var numTotalBytesToAllocate = cbPlaintext;
|
||||||
uint numBytesPaddingRequired = CRYPTPROTECTMEMORY_BLOCK_SIZE - (numTotalBytesToAllocate % CRYPTPROTECTMEMORY_BLOCK_SIZE);
|
var numBytesPaddingRequired = CRYPTPROTECTMEMORY_BLOCK_SIZE - (numTotalBytesToAllocate % CRYPTPROTECTMEMORY_BLOCK_SIZE);
|
||||||
if (numBytesPaddingRequired == CRYPTPROTECTMEMORY_BLOCK_SIZE)
|
if (numBytesPaddingRequired == CRYPTPROTECTMEMORY_BLOCK_SIZE)
|
||||||
{
|
{
|
||||||
numBytesPaddingRequired = 0; // we're already a proper multiple of the block size
|
numBytesPaddingRequired = 0; // we're already a proper multiple of the block size
|
||||||
|
|
@ -152,7 +152,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
CryptoUtil.Assert(numTotalBytesToAllocate % CRYPTPROTECTMEMORY_BLOCK_SIZE == 0, "numTotalBytesToAllocate % CRYPTPROTECTMEMORY_BLOCK_SIZE == 0");
|
CryptoUtil.Assert(numTotalBytesToAllocate % CRYPTPROTECTMEMORY_BLOCK_SIZE == 0, "numTotalBytesToAllocate % CRYPTPROTECTMEMORY_BLOCK_SIZE == 0");
|
||||||
|
|
||||||
// Allocate and copy plaintext data; padding is uninitialized / undefined.
|
// Allocate and copy plaintext data; padding is uninitialized / undefined.
|
||||||
SecureLocalAllocHandle encryptedMemoryHandle = SecureLocalAllocHandle.Allocate((IntPtr)numTotalBytesToAllocate);
|
var encryptedMemoryHandle = SecureLocalAllocHandle.Allocate((IntPtr)numTotalBytesToAllocate);
|
||||||
UnsafeBufferUtil.BlockCopy(from: pbPlaintext, to: encryptedMemoryHandle, byteCount: cbPlaintext);
|
UnsafeBufferUtil.BlockCopy(from: pbPlaintext, to: encryptedMemoryHandle, byteCount: cbPlaintext);
|
||||||
|
|
||||||
// Finally, CryptProtectMemory the whole mess.
|
// Finally, CryptProtectMemory the whole mess.
|
||||||
|
|
@ -187,7 +187,7 @@ namespace Microsoft.AspNetCore.DataProtection
|
||||||
return new Secret(ManagedGenRandomImpl.Instance.GenRandom(numBytes));
|
return new Secret(ManagedGenRandomImpl.Instance.GenRandom(numBytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] bytes = new byte[numBytes];
|
var bytes = new byte[numBytes];
|
||||||
fixed (byte* pbBytes = bytes)
|
fixed (byte* pbBytes = bytes)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
||||||
// ...
|
// ...
|
||||||
// </EncryptedData>
|
// </EncryptedData>
|
||||||
|
|
||||||
XElement encryptedElement = EncryptElement(plaintextElement);
|
var encryptedElement = EncryptElement(plaintextElement);
|
||||||
return new EncryptedXmlInfo(encryptedElement, typeof(EncryptedXmlDecryptor));
|
return new EncryptedXmlInfo(encryptedElement, typeof(EncryptedXmlDecryptor));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
||||||
// <value>{base64}</value>
|
// <value>{base64}</value>
|
||||||
// </encryptedKey>
|
// </encryptedKey>
|
||||||
|
|
||||||
byte[] protectedSecret = Convert.FromBase64String((string)encryptedElement.Element("value"));
|
var protectedSecret = Convert.FromBase64String((string)encryptedElement.Element("value"));
|
||||||
if (_logger.IsDebugLevelEnabled())
|
if (_logger.IsDebugLevelEnabled())
|
||||||
{
|
{
|
||||||
string protectionDescriptorRule;
|
string protectionDescriptorRule;
|
||||||
|
|
@ -74,7 +74,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
||||||
_logger.DecryptingSecretElementUsingWindowsDPAPING(protectionDescriptorRule);
|
_logger.DecryptingSecretElementUsingWindowsDPAPING(protectionDescriptorRule);
|
||||||
}
|
}
|
||||||
|
|
||||||
using (Secret secret = DpapiSecretSerializerHelper.UnprotectWithDpapiNG(protectedSecret))
|
using (var secret = DpapiSecretSerializerHelper.UnprotectWithDpapiNG(protectedSecret))
|
||||||
{
|
{
|
||||||
return secret.ToXElement();
|
return secret.ToXElement();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,7 +49,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
||||||
|
|
||||||
CryptoUtil.AssertPlatformIsWindows8OrLater();
|
CryptoUtil.AssertPlatformIsWindows8OrLater();
|
||||||
|
|
||||||
int ntstatus = UnsafeNativeMethods.NCryptCreateProtectionDescriptor(protectionDescriptorRule, (uint)flags, out _protectionDescriptorHandle);
|
var ntstatus = UnsafeNativeMethods.NCryptCreateProtectionDescriptor(protectionDescriptorRule, (uint)flags, out _protectionDescriptorHandle);
|
||||||
UnsafeNativeMethods.ThrowExceptionForNCryptStatus(ntstatus);
|
UnsafeNativeMethods.ThrowExceptionForNCryptStatus(ntstatus);
|
||||||
CryptoUtil.AssertSafeHandleIsValid(_protectionDescriptorHandle);
|
CryptoUtil.AssertSafeHandleIsValid(_protectionDescriptorHandle);
|
||||||
|
|
||||||
|
|
@ -72,14 +72,14 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
||||||
throw new ArgumentNullException(nameof(plaintextElement));
|
throw new ArgumentNullException(nameof(plaintextElement));
|
||||||
}
|
}
|
||||||
|
|
||||||
string protectionDescriptorRuleString = _protectionDescriptorHandle.GetProtectionDescriptorRuleString();
|
var protectionDescriptorRuleString = _protectionDescriptorHandle.GetProtectionDescriptorRuleString();
|
||||||
_logger?.EncryptingToWindowsDPAPINGUsingProtectionDescriptorRule(protectionDescriptorRuleString);
|
_logger?.EncryptingToWindowsDPAPINGUsingProtectionDescriptorRule(protectionDescriptorRuleString);
|
||||||
|
|
||||||
// Convert the XML element to a binary secret so that it can be run through DPAPI
|
// Convert the XML element to a binary secret so that it can be run through DPAPI
|
||||||
byte[] cngDpapiEncryptedData;
|
byte[] cngDpapiEncryptedData;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (Secret plaintextElementAsSecret = plaintextElement.ToSecret())
|
using (var plaintextElementAsSecret = plaintextElement.ToSecret())
|
||||||
{
|
{
|
||||||
cngDpapiEncryptedData = DpapiSecretSerializerHelper.ProtectWithDpapiNG(plaintextElementAsSecret, _protectionDescriptorHandle);
|
cngDpapiEncryptedData = DpapiSecretSerializerHelper.ProtectWithDpapiNG(plaintextElementAsSecret, _protectionDescriptorHandle);
|
||||||
}
|
}
|
||||||
|
|
@ -115,7 +115,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
||||||
|
|
||||||
// Creates a SID=... protection descriptor string for the current user.
|
// Creates a SID=... protection descriptor string for the current user.
|
||||||
// Reminder: DPAPI:NG provides only encryption, not authentication.
|
// Reminder: DPAPI:NG provides only encryption, not authentication.
|
||||||
using (WindowsIdentity currentIdentity = WindowsIdentity.GetCurrent())
|
using (var currentIdentity = WindowsIdentity.GetCurrent())
|
||||||
{
|
{
|
||||||
// use the SID to create an SDDL string
|
// use the SID to create an SDDL string
|
||||||
return Invariant($"SID={currentIdentity.User.Value}");
|
return Invariant($"SID={currentIdentity.User.Value}");
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,8 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
||||||
// <value>{base64}</value>
|
// <value>{base64}</value>
|
||||||
// </encryptedKey>
|
// </encryptedKey>
|
||||||
|
|
||||||
byte[] protectedSecret = Convert.FromBase64String((string)encryptedElement.Element("value"));
|
var protectedSecret = Convert.FromBase64String((string)encryptedElement.Element("value"));
|
||||||
using (Secret secret = DpapiSecretSerializerHelper.UnprotectWithDpapi(protectedSecret))
|
using (var secret = DpapiSecretSerializerHelper.UnprotectWithDpapi(protectedSecret))
|
||||||
{
|
{
|
||||||
return secret.ToXElement();
|
return secret.ToXElement();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -73,7 +73,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
||||||
byte[] dpapiEncryptedData;
|
byte[] dpapiEncryptedData;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
using (Secret plaintextElementAsSecret = plaintextElement.ToSecret())
|
using (var plaintextElementAsSecret = plaintextElement.ToSecret())
|
||||||
{
|
{
|
||||||
dpapiEncryptedData = DpapiSecretSerializerHelper.ProtectWithDpapi(plaintextElementAsSecret, protectToLocalMachine: _protectToLocalMachine);
|
dpapiEncryptedData = DpapiSecretSerializerHelper.ProtectWithDpapi(plaintextElementAsSecret, protectToLocalMachine: _protectToLocalMachine);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,7 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
||||||
element.Save(memoryStream);
|
element.Save(memoryStream);
|
||||||
|
|
||||||
#if !NETSTANDARD1_3
|
#if !NETSTANDARD1_3
|
||||||
byte[] underlyingBuffer = memoryStream.GetBuffer();
|
var underlyingBuffer = memoryStream.GetBuffer();
|
||||||
fixed (byte* __unused__ = underlyingBuffer) // try to limit this moving around in memory while we allocate
|
fixed (byte* __unused__ = underlyingBuffer) // try to limit this moving around in memory while we allocate
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|
@ -168,13 +168,13 @@ namespace Microsoft.AspNetCore.DataProtection.XmlEncryption
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static XElement ToXElement(this Secret secret)
|
public static XElement ToXElement(this Secret secret)
|
||||||
{
|
{
|
||||||
byte[] plaintextSecret = new byte[secret.Length];
|
var plaintextSecret = new byte[secret.Length];
|
||||||
fixed (byte* __unused__ = plaintextSecret) // try to keep the GC from moving it around
|
fixed (byte* __unused__ = plaintextSecret) // try to keep the GC from moving it around
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
secret.WriteSecretIntoBuffer(new ArraySegment<byte>(plaintextSecret));
|
secret.WriteSecretIntoBuffer(new ArraySegment<byte>(plaintextSecret));
|
||||||
MemoryStream memoryStream = new MemoryStream(plaintextSecret, writable: false);
|
var memoryStream = new MemoryStream(plaintextSecret, writable: false);
|
||||||
return XElement.Load(memoryStream);
|
return XElement.Load(memoryStream);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue