Imported Upstream version 4.0.0~alpha1

Former-commit-id: 806294f5ded97629b74c85c09952f2a74fe182d9
This commit is contained in:
Jo Shields
2015-04-07 09:35:12 +01:00
parent 283343f570
commit 3c1f479b9d
22469 changed files with 2931443 additions and 869343 deletions

View File

@@ -0,0 +1,8 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
// moved to mscorlib.dll
[assembly:System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.Security.Cryptography.Aes))]

View File

@@ -0,0 +1,435 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts;
using Microsoft.Win32.SafeHandles;
namespace System.Security.Cryptography {
/// <summary>
/// AES wrapper around the CAPI implementation.
/// </summary>
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class AesCryptoServiceProvider : Aes {
private static volatile KeySizes[] s_supportedKeySizes;
private static volatile int s_defaultKeySize;
[SecurityCritical]
private SafeCspHandle m_cspHandle;
// Note that keys are stored in CAPI rather than directly in the KeyValue property, which should not
// be used to retrieve the key value directly.
[SecurityCritical]
private SafeCapiKeyHandle m_key;
[System.Security.SecurityCritical]
public AesCryptoServiceProvider () {
Contract.Ensures(m_cspHandle != null && !m_cspHandle.IsInvalid && !m_cspHandle.IsClosed);
// On Windows XP the AES CSP has the prototype name, but on newer operating systems it has the
// standard name
string providerName = CapiNative.ProviderNames.MicrosoftEnhancedRsaAes;
if (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor == 1) {
providerName = CapiNative.ProviderNames.MicrosoftEnhancedRsaAesPrototype;
}
m_cspHandle = CapiNative.AcquireCsp(null,
providerName,
CapiNative.ProviderType.RsaAes,
CapiNative.CryptAcquireContextFlags.VerifyContext,
true);
// CAPI will not allow feedback sizes greater than 64 bits
FeedbackSizeValue = 8;
// Get the different AES key sizes supported by this platform, raising an error if there are no
// supported key sizes.
int defaultKeySize = 0;
KeySizes[] keySizes = FindSupportedKeySizes(m_cspHandle, out defaultKeySize);
if (keySizes.Length != 0) {
Debug.Assert(defaultKeySize > 0, "defaultKeySize > 0");
KeySizeValue = defaultKeySize;
}
else {
throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
}
}
/// <summary>
/// Value of the symmetric key used for encryption / decryption
/// </summary>
public override byte[] Key {
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
get {
Contract.Ensures(m_key != null && !m_key.IsInvalid && !m_key.IsClosed);
Contract.Ensures(Contract.Result<byte[]>() != null &&
Contract.Result<byte[]>().Length == KeySizeValue / 8);
if (m_key == null || m_key.IsInvalid || m_key.IsClosed) {
GenerateKey();
}
// We don't hold onto a key value directly, so we need to export it from CAPI when the user
// wants a byte array representation.
byte[] keyValue = CapiNative.ExportSymmetricKey(m_key);
return keyValue;
}
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
set {
Contract.Ensures(m_key != null && !m_key.IsInvalid && !m_key.IsClosed);
if (value == null) {
throw new ArgumentNullException("value");
}
byte[] keyValue = (byte[])value.Clone();
if (!ValidKeySize(keyValue.Length * 8)) {
throw new CryptographicException(SR.GetString(SR.Cryptography_InvalidKeySize));
}
// Import the key, then close any current key and replace with the new one. We need to make
// sure the import is successful before closing the current key to avoid having an algorithm
// with no valid keys.
SafeCapiKeyHandle importedKey = CapiNative.ImportSymmetricKey(m_cspHandle,
GetAlgorithmId(keyValue.Length * 8),
keyValue);
if (m_key != null) {
m_key.Dispose();
}
m_key = importedKey;
KeySizeValue = keyValue.Length * 8;
}
}
/// <summary>
/// Size, in bits, of the key
/// </summary>
public override int KeySize {
get { return base.KeySize; }
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
set {
base.KeySize = value;
// Since the key size is being reset, we need to reset the key itself as well
if (m_key != null) {
m_key.Dispose();
}
}
}
/// <summary>
/// Create an object to perform AES decryption with the current key and IV
/// </summary>
/// <returns></returns>
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
public override ICryptoTransform CreateDecryptor() {
Contract.Ensures(Contract.Result<ICryptoTransform>() != null);
if (m_key == null || m_key.IsInvalid || m_key.IsClosed) {
throw new CryptographicException(SR.GetString(SR.Cryptography_DecryptWithNoKey));
}
return CreateDecryptor(m_key, IVValue);
}
/// <summary>
/// Create an object to perform AES decryption with the given key and IV
/// </summary>
[System.Security.SecurityCritical]
public override ICryptoTransform CreateDecryptor(byte[] key, byte[] iv) {
Contract.Ensures(Contract.Result<ICryptoTransform>() != null);
if (key == null) {
throw new ArgumentNullException("key");
}
if (!ValidKeySize(key.Length * 8)) {
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidKeySize), "key");
}
if (iv != null && iv.Length * 8 != BlockSizeValue) {
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidIVSize), "iv");
}
byte[] keyCopy = (byte[])key.Clone();
byte[] ivCopy = null;
if (iv != null) {
ivCopy = (byte[])iv.Clone();
}
using (SafeCapiKeyHandle importedKey = CapiNative.ImportSymmetricKey(m_cspHandle, GetAlgorithmId(keyCopy.Length * 8), keyCopy)) {
return CreateDecryptor(importedKey, ivCopy);
}
}
/// <summary>
/// Create an object to perform AES decryption
/// </summary>
[System.Security.SecurityCritical]
private ICryptoTransform CreateDecryptor(SafeCapiKeyHandle key, byte[] iv) {
Contract.Requires(key != null);
Contract.Ensures(Contract.Result<ICryptoTransform>() != null);
return new CapiSymmetricAlgorithm(BlockSizeValue,
FeedbackSizeValue,
m_cspHandle,
key,
iv,
Mode,
PaddingValue,
EncryptionMode.Decrypt);
}
/// <summary>
/// Create an object to do AES encryption with the current key and IV
/// </summary>
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
public override ICryptoTransform CreateEncryptor() {
Contract.Ensures(Contract.Result<ICryptoTransform>() != null);
if (m_key == null || m_key.IsInvalid || m_key.IsClosed) {
GenerateKey();
}
// ECB is the only mode which does not require an IV -- generate one here if we don't have one yet.
if (Mode != CipherMode.ECB && IVValue == null) {
GenerateIV();
}
return CreateEncryptor(m_key, IVValue);
}
/// <summary>
/// Create an object to do AES encryption with the given key and IV
/// </summary>
[System.Security.SecurityCritical]
public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv) {
Contract.Ensures(Contract.Result<ICryptoTransform>() != null);
if (key == null) {
throw new ArgumentNullException("key");
}
if (!ValidKeySize(key.Length * 8)) {
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidKeySize), "key");
}
if (iv != null && iv.Length * 8 != BlockSizeValue) {
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidIVSize), "iv");
}
byte[] keyCopy = (byte[])key.Clone();
byte[] ivCopy = null;
if (iv != null) {
ivCopy = (byte[])iv.Clone();
}
using (SafeCapiKeyHandle importedKey = CapiNative.ImportSymmetricKey(m_cspHandle, GetAlgorithmId(keyCopy.Length * 8), keyCopy)) {
return CreateEncryptor(importedKey, ivCopy);
}
}
/// <summary>
/// Create an object to perform AES encryption
/// </summary>
[System.Security.SecurityCritical]
private ICryptoTransform CreateEncryptor(SafeCapiKeyHandle key, byte[] iv) {
Contract.Requires(key != null);
Contract.Ensures(Contract.Result<ICryptoTransform>() != null);
return new CapiSymmetricAlgorithm(BlockSizeValue,
FeedbackSizeValue,
m_cspHandle,
key,
iv,
Mode,
PaddingValue,
EncryptionMode.Encrypt);
}
/// <summary>
/// Release any CAPI handles we're holding onto
/// </summary>
[System.Security.SecurityCritical]
protected override void Dispose(bool disposing) {
Contract.Ensures(!disposing || m_key == null || m_key.IsClosed);
Contract.Ensures(!disposing || m_cspHandle == null || m_cspHandle.IsClosed);
try {
if (disposing) {
if (m_key != null) {
m_key.Dispose();
}
if (m_cspHandle != null) {
m_cspHandle.Dispose();
}
}
}
finally {
base.Dispose(disposing);
}
}
/// <summary>
/// Get the size of AES keys supported by the given CSP, and which size should be used by default.
///
/// We assume that the same CSP will always be used by all instances of the AesCryptoServiceProvider
/// in the current AppDomain. If we add the ability for users to choose which CSP to use on a
/// per-instance basis, we need to update the code to account for the CSP when checking the cached
/// key size values.
/// </summary>
[System.Security.SecurityCritical]
private static KeySizes[] FindSupportedKeySizes(SafeCspHandle csp, out int defaultKeySize) {
Contract.Requires(csp != null);
Contract.Ensures(Contract.Result<KeySizes[]>() != null);
// If this platform has any supported algorithm sizes, then the default key size should be set to a
// reasonable value.
Contract.Ensures(Contract.Result<KeySizes[]>().Length == 0 ||
(Contract.ValueAtReturn<int>(out defaultKeySize) > 0 && Contract.ValueAtReturn<int>(out defaultKeySize) % 8 == 0));
if (s_supportedKeySizes == null) {
List<KeySizes> keySizes = new List<KeySizes>();
int maxKeySize = 0;
//
// Enumerate the CSP's supported algorithms to see what key sizes it supports for AES
//
CapiNative.PROV_ENUMALGS algorithm =
CapiNative.GetProviderParameterStruct<CapiNative.PROV_ENUMALGS>(csp,
CapiNative.ProviderParameter.EnumerateAlgorithms,
CapiNative.ProviderParameterFlags.RestartEnumeration);
// Translate between CAPI AES algorithm IDs and supported key sizes
while (algorithm.aiAlgId != CapiNative.AlgorithmId.None) {
switch (algorithm.aiAlgId) {
case CapiNative.AlgorithmId.Aes128:
keySizes.Add(new KeySizes(128, 128, 0));
if (128 > maxKeySize) {
maxKeySize = 128;
}
break;
case CapiNative.AlgorithmId.Aes192:
keySizes.Add(new KeySizes(192, 192, 0));
if (192 > maxKeySize) {
maxKeySize = 192;
}
break;
case CapiNative.AlgorithmId.Aes256:
keySizes.Add(new KeySizes(256, 256, 0));
if (256 > maxKeySize) {
maxKeySize = 256;
}
break;
default:
break;
}
algorithm = CapiNative.GetProviderParameterStruct<CapiNative.PROV_ENUMALGS>(csp,
CapiNative.ProviderParameter.EnumerateAlgorithms,
CapiNative.ProviderParameterFlags.None);
}
s_supportedKeySizes = keySizes.ToArray();
s_defaultKeySize = maxKeySize;
}
defaultKeySize = s_defaultKeySize;
return s_supportedKeySizes;
}
/// <summary>
/// Generate a new random key
/// </summary>
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
public override void GenerateKey() {
Contract.Ensures(m_key != null && !m_key.IsInvalid & !m_key.IsClosed);
Contract.Assert(m_cspHandle != null);
SafeCapiKeyHandle key = null;
RuntimeHelpers.PrepareConstrainedRegions();
try {
if (!CapiNative.UnsafeNativeMethods.CryptGenKey(m_cspHandle,
GetAlgorithmId(KeySizeValue),
CapiNative.KeyFlags.Exportable,
out key)) {
throw new CryptographicException(Marshal.GetLastWin32Error());
}
}
finally {
if (key != null && !key.IsInvalid) {
key.SetParentCsp(m_cspHandle);
}
}
if (m_key != null) {
m_key.Dispose();
}
m_key = key;
}
/// <summary>
/// Generate a random initialization vector
/// </summary>
[System.Security.SecurityCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
public override void GenerateIV() {
Contract.Ensures(IVValue != null && IVValue.Length == BlockSizeValue / 8);
Contract.Assert(m_cspHandle != null);
Contract.Assert(BlockSizeValue % 8 == 0);
byte[] iv = new byte[BlockSizeValue / 8];
if (!CapiNative.UnsafeNativeMethods.CryptGenRandom(m_cspHandle, iv.Length, iv)) {
throw new CryptographicException(Marshal.GetLastWin32Error());
}
IVValue = iv;
}
/// <summary>
/// Map an AES key size to the corresponding CAPI algorithm ID
/// </summary>
private static CapiNative.AlgorithmId GetAlgorithmId(int keySize) {
// We should always return either a data encryption algorithm ID or None if we don't recognize the key size
Contract.Ensures(
((((int)Contract.Result<CapiNative.AlgorithmId>()) & (int)CapiNative.AlgorithmClass.DataEncryption) == (int)CapiNative.AlgorithmClass.DataEncryption) ||
Contract.Result<CapiNative.AlgorithmId>() == CapiNative.AlgorithmId.None);
switch (keySize) {
case 128:
return CapiNative.AlgorithmId.Aes128;
case 192:
return CapiNative.AlgorithmId.Aes192;
case 256:
return CapiNative.AlgorithmId.Aes256;
default:
return CapiNative.AlgorithmId.None;
}
}
}
}

View File

@@ -0,0 +1,141 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
#if !SILVERLIGHT
using System.Diagnostics.Contracts;
#endif // !SILVERLIGHT
namespace System.Security.Cryptography {
/// <summary>
/// Managed implementation of the AES algorithm. AES is esentially Rijndael with a fixed block size
/// and iteration count, so we just wrap the RijndaelManaged class and allow only 128 bit blocks to
/// be used.
/// </summary>
public sealed class AesManaged : Aes {
private RijndaelManaged m_rijndael;
public AesManaged() {
#if !SILVERLIGHT
Contract.Ensures(m_rijndael != null);
if (CryptoConfig.AllowOnlyFipsAlgorithms) {
throw new InvalidOperationException(SR.GetString(SR.Cryptography_NonCompliantFIPSAlgorithm));
}
#endif // !SILVERLIGHT
m_rijndael = new RijndaelManaged();
m_rijndael.BlockSize = BlockSize;
m_rijndael.KeySize = KeySize;
}
#if !SILVERLIGHT
public override int FeedbackSize {
get { return m_rijndael.FeedbackSize; }
set { m_rijndael.FeedbackSize = value; }
}
#endif // !SILVERLIGHT
public override byte[] IV {
get { return m_rijndael.IV; }
set { m_rijndael.IV = value; }
}
public override byte[] Key {
get { return m_rijndael.Key; }
set { m_rijndael.Key = value; }
}
public override int KeySize {
get { return m_rijndael.KeySize; }
set { m_rijndael.KeySize = value; }
}
#if !SILVERLIGHT
public override CipherMode Mode {
get { return m_rijndael.Mode; }
set {
Contract.Ensures(m_rijndael.Mode != CipherMode.CFB && m_rijndael.Mode != CipherMode.OFB);
// RijndaelManaged will implicitly change the block size of an algorithm to match the number
// of feedback bits being used. Since AES requires a block size of 128 bits, we cannot allow
// the user to use the feedback modes, as this will end up breaking that invarient.
if (value == CipherMode.CFB || value == CipherMode.OFB) {
throw new CryptographicException(SR.GetString(SR.Cryptography_InvalidCipherMode));
}
m_rijndael.Mode = value;
}
}
public override PaddingMode Padding {
get { return m_rijndael.Padding; }
set { m_rijndael.Padding = value; }
}
#endif // !SILVERLIGHT
public override ICryptoTransform CreateDecryptor() {
return m_rijndael.CreateDecryptor();
}
public override ICryptoTransform CreateDecryptor(byte[] key, byte[] iv) {
if (key == null) {
throw new ArgumentNullException("key");
}
#if !SILVERLIGHT
if (!ValidKeySize(key.Length * 8)) {
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidKeySize), "key");
}
if (iv != null && iv.Length * 8 != BlockSizeValue) {
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidIVSize), "iv");
}
#endif
return m_rijndael.CreateDecryptor(key, iv);
}
public override ICryptoTransform CreateEncryptor() {
return m_rijndael.CreateEncryptor();
}
public override ICryptoTransform CreateEncryptor(byte[] key, byte[] iv) {
if (key == null) {
throw new ArgumentNullException("key");
}
#if !SILVERLIGHT
if (!ValidKeySize(key.Length * 8)) {
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidKeySize), "key");
}
if (iv != null && iv.Length * 8 != BlockSizeValue) {
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidIVSize), "iv");
}
#endif // SILVERLIGHT
return m_rijndael.CreateEncryptor(key, iv);
}
protected override void Dispose(bool disposing) {
try {
if (disposing) {
(m_rijndael as IDisposable).Dispose();
}
}
finally {
base.Dispose(disposing);
}
}
public override void GenerateIV() {
m_rijndael.GenerateIV();
}
public override void GenerateKey() {
m_rijndael.GenerateKey();
}
}
}

View File

@@ -0,0 +1,240 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts;
using Microsoft.Win32.SafeHandles;
namespace System.Security.Cryptography {
/// <summar>
/// Implementation of SafeBCryptAlgorithmHandle Cache.
/// </summary>
internal sealed class BCryptAlgorithmHandleCache {
[SecurityCritical]
private Dictionary<string, WeakReference> m_algorithmHandles;
[SecurityCritical]
public BCryptAlgorithmHandleCache()
{
m_algorithmHandles = new Dictionary<string, WeakReference>();
}
[SecuritySafeCritical]
public SafeBCryptAlgorithmHandle GetCachedAlgorithmHandle(string algorithm, string implementation) {
string handleKey = algorithm + implementation;
SafeBCryptAlgorithmHandle algorithmHandle = null;
if (m_algorithmHandles.ContainsKey(handleKey))
{
algorithmHandle = m_algorithmHandles[handleKey].Target as SafeBCryptAlgorithmHandle;
if (algorithmHandle != null)
{
return algorithmHandle;
}
}
algorithmHandle = BCryptNative.OpenAlgorithm(algorithm, implementation);
m_algorithmHandles[handleKey] = new WeakReference(algorithmHandle);
return algorithmHandle;
}
}
/// <summary>
/// Implementation of a generic BCrypt hashing algorithm, concrete HashAlgorithm classes
/// implemented by BCrypt can contain an instance of this class and delegate the work to it.
/// </summary>
internal sealed class BCryptHashAlgorithm : IDisposable {
[ThreadStatic]
[SecurityCritical]
private static BCryptAlgorithmHandleCache _algorithmCache;
[SecurityCritical]
private SafeBCryptAlgorithmHandle m_algorithmHandle;
[SecurityCritical]
private SafeBCryptHashHandle m_hashHandle;
// SafeCritical - we're not exposing out anything that we want to prevent untrusted code from getting at
[SecuritySafeCritical]
public BCryptHashAlgorithm(CngAlgorithm algorithm, string implementation) {
Contract.Requires(algorithm != null);
Contract.Requires(!String.IsNullOrEmpty(implementation));
Contract.Ensures(m_algorithmHandle != null && !m_algorithmHandle.IsInvalid && !m_algorithmHandle.IsClosed);
Contract.Ensures(m_hashHandle != null && !m_hashHandle.IsInvalid && !m_hashHandle.IsClosed);
// Make sure CNG is supported on this platform
if (!BCryptNative.BCryptSupported) {
throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
}
if (_algorithmCache == null)
{
_algorithmCache = new BCryptAlgorithmHandleCache();
}
m_algorithmHandle = _algorithmCache.GetCachedAlgorithmHandle(algorithm.Algorithm, implementation);
Initialize();
}
/// <summary>
/// Clean up the hash algorithm
/// </summary>
[SecuritySafeCritical]
public void Dispose() {
Contract.Ensures(m_hashHandle == null || m_hashHandle.IsClosed);
Contract.Ensures(m_algorithmHandle == null || m_algorithmHandle.IsClosed);
if (m_hashHandle != null) {
m_hashHandle.Dispose();
}
if (m_algorithmHandle != null) {
m_algorithmHandle = null;
}
}
/// <summary>
/// Reset the hash algorithm to begin hashing a new set of data
/// </summary>
// SafeCritical - we're not exposing out anything that we want to prevent untrusted code from getting
// at. We've also made sure not to leak any native resources out to partial trust code
// and we control all native inputs.
[SecuritySafeCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
public void Initialize() {
Contract.Ensures(m_hashHandle != null && !m_hashHandle.IsInvalid && !m_hashHandle.IsClosed);
Contract.Assert(m_algorithmHandle != null);
// Try to create a new hash algorithm to use
SafeBCryptHashHandle newHashAlgorithm = null;
IntPtr hashObjectBuffer = IntPtr.Zero;
// Creating a BCRYPT_HASH_HANDLE requires providing a buffer to hold the hash object in, which
// is tied to the lifetime of the hash handle. Wrap this in a CER so we can tie the lifetimes together
// safely.
RuntimeHelpers.PrepareConstrainedRegions();
try {
int hashObjectSize = BCryptNative.GetInt32Property(m_algorithmHandle,
BCryptNative.ObjectPropertyName.ObjectLength);
Debug.Assert(hashObjectSize > 0, "hashObjectSize > 0");
// Allocate in a CER because we could fail between the alloc and the assignment
RuntimeHelpers.PrepareConstrainedRegions();
try { }
finally {
hashObjectBuffer = Marshal.AllocCoTaskMem(hashObjectSize);
}
BCryptNative.ErrorCode error = BCryptNative.UnsafeNativeMethods.BCryptCreateHash(m_algorithmHandle,
out newHashAlgorithm,
hashObjectBuffer,
hashObjectSize,
IntPtr.Zero,
0,
0);
if (error != BCryptNative.ErrorCode.Success) {
throw new CryptographicException((int)error);
}
}
finally {
// Make sure we've successfully transfered ownership of the hash object buffer to the safe handle
if (hashObjectBuffer != IntPtr.Zero) {
// If we created the safe handle, it needs to own the buffer and free it in release
if (newHashAlgorithm != null) {
newHashAlgorithm.HashObject = hashObjectBuffer;
}
else {
Marshal.FreeCoTaskMem(hashObjectBuffer);
}
}
}
// If we could create it, dispose of any old hash handle we had and replace it with the new one
if (m_hashHandle != null) {
m_hashHandle.Dispose();
}
m_hashHandle = newHashAlgorithm;
}
/// <summary>
/// Hash a block of data
/// </summary>
[SecuritySafeCritical]
public void HashCore(byte[] array, int ibStart, int cbSize) {
Contract.Assert(m_hashHandle != null);
if (array == null) {
throw new ArgumentNullException("array");
}
if (ibStart < 0 || ibStart > array.Length - cbSize) {
throw new ArgumentOutOfRangeException("ibStart");
}
if (cbSize < 0 || cbSize > array.Length) {
throw new ArgumentOutOfRangeException("cbSize");
}
byte[] hashData = new byte[cbSize];
Buffer.BlockCopy(array, ibStart, hashData, 0, cbSize);
BCryptNative.ErrorCode error = BCryptNative.UnsafeNativeMethods.BCryptHashData(m_hashHandle,
hashData,
hashData.Length,
0);
if (error != BCryptNative.ErrorCode.Success) {
throw new CryptographicException((int)error);
}
}
/// <summary>
/// Complete the hash, returning its value
/// </summary>
[SecuritySafeCritical]
public byte[] HashFinal() {
Contract.Ensures(Contract.Result<byte[]>() != null);
Contract.Assert(m_hashHandle != null);
int hashSize = BCryptNative.GetInt32Property(m_hashHandle, BCryptNative.HashPropertyName.HashLength);
byte[] hashValue = new byte[hashSize];
BCryptNative.ErrorCode error = BCryptNative.UnsafeNativeMethods.BCryptFinishHash(m_hashHandle,
hashValue,
hashValue.Length,
0);
if (error != BCryptNative.ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return hashValue;
}
[SecuritySafeCritical]
public void HashStream(Stream stream) {
Contract.Requires(stream != null);
// Read the data 4KB at a time, providing similar read characteristics to a standard HashAlgorithm
byte[] buffer = new byte[4096];
int bytesRead = 0;
do {
bytesRead = stream.Read(buffer, 0, buffer.Length);
if (bytesRead > 0) {
HashCore(buffer, 0, bytesRead);
}
} while (bytesRead > 0);
}
}
}

View File

@@ -0,0 +1,330 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts;
using Microsoft.Win32;
using Microsoft.Win32.SafeHandles;
namespace System.Security.Cryptography {
/// <summary>
/// Native interop with CNG's BCrypt layer. Native definitions can be found in bcrypt.h
/// </summary>
internal static class BCryptNative {
/// <summary>
/// Well known algorithm names
/// </summary>
internal static class AlgorithmName {
public const string ECDHP256 = "ECDH_P256"; // BCRYPT_ECDH_P256_ALGORITHM
public const string ECDHP384 = "ECDH_P384"; // BCRYPT_ECDH_P384_ALGORITHM
public const string ECDHP521 = "ECDH_P521"; // BCRYPT_ECDH_P521_ALGORITHM
public const string ECDsaP256 = "ECDSA_P256"; // BCRYPT_ECDSA_P256_ALGORITHM
public const string ECDsaP384 = "ECDSA_P384"; // BCRYPT_ECDSA_P384_ALGORITHM
public const string ECDsaP521 = "ECDSA_P521"; // BCRYPT_ECDSA_P521_ALGORITHM
public const string MD5 = "MD5"; // BCRYPT_MD5_ALGORITHM
public const string Sha1 = "SHA1"; // BCRYPT_SHA1_ALGORITHM
public const string Sha256 = "SHA256"; // BCRYPT_SHA256_ALGORITHM
public const string Sha384 = "SHA384"; // BCRYPT_SHA384_ALGORITHM
public const string Sha512 = "SHA512"; // BCRYPT_SHA512_ALGORITHM
}
/// <summary>
/// Result codes from BCrypt APIs
/// </summary>
internal enum ErrorCode {
Success = 0x00000000, // STATUS_SUCCESS
BufferToSmall = unchecked((int)0xC0000023), // STATUS_BUFFER_TOO_SMALL
ObjectNameNotFound = unchecked((int)0xC0000034) // SATUS_OBJECT_NAME_NOT_FOUND
}
/// <summary>
/// Well known BCrypt hash property names
/// </summary>
internal static class HashPropertyName {
public const string HashLength = "HashDigestLength"; // BCRYPT_HASH_LENGTH
}
/// <summary>
/// Magic numbers identifying blob types
/// </summary>
internal enum KeyBlobMagicNumber {
ECDHPublicP256 = 0x314B4345, // BCRYPT_ECDH_PUBLIC_P256_MAGIC
ECDHPublicP384 = 0x334B4345, // BCRYPT_ECDH_PUBLIC_P384_MAGIC
ECDHPublicP521 = 0x354B4345, // BCRYPT_ECDH_PUBLIC_P521_MAGIC
ECDsaPublicP256 = 0x31534345, // BCRYPT_ECDSA_PUBLIC_P256_MAGIC
ECDsaPublicP384 = 0x33534345, // BCRYPT_ECDSA_PUBLIC_P384_MAGIC
ECDsaPublicP521 = 0x35534345 // BCRYPT_ECDSA_PUBLIC_P521_MAGIC
}
/// <summary>
/// Well known KDF names
/// </summary>
internal static class KeyDerivationFunction {
public const string Hash = "HASH"; // BCRYPT_KDF_HASH
public const string Hmac = "HMAC"; // BCRYPT_KDF_HMAC
public const string Tls = "TLS_PRF"; // BCRYPT_KDF_TLS_PRF
}
/// <summary>
/// Well known BCrypt provider names
/// </summary>
internal static class ProviderName {
public const string MicrosoftPrimitiveProvider = "Microsoft Primitive Provider"; // MS_PRIMITIVE_PROVIDER
}
/// <summary>
/// Well known BCrypt object property names
/// </summary>
internal static class ObjectPropertyName {
public const string ObjectLength = "ObjectLength"; // BCRYPT_OBJECT_LENGTH
}
#pragma warning disable 618 // Have not migrated to v4 transparency yet
[SecurityCritical(SecurityCriticalScope.Everything)]
#pragma warning restore 618
[SuppressUnmanagedCodeSecurity]
internal static class UnsafeNativeMethods {
/// <summary>
/// Create a hash object
/// </summary>
[DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode BCryptCreateHash(SafeBCryptAlgorithmHandle hAlgorithm,
[Out] out SafeBCryptHashHandle phHash,
IntPtr pbHashObject,
int cbHashObject,
IntPtr pbSecret,
int cbSecret,
int dwFlags);
/// <summary>
/// Get a property from a BCrypt algorithm object
/// </summary>
[DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode BCryptGetProperty(SafeBCryptAlgorithmHandle hObject,
string pszProperty,
[MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
int cbOutput,
[In, Out] ref int pcbResult,
int flags);
/// <summary>
/// Get a property from a BCrypt algorithm object
/// </summary>
[DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
internal static extern ErrorCode BCryptGetAlgorithmProperty(SafeBCryptAlgorithmHandle hObject,
string pszProperty,
[MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
int cbOutput,
[In, Out] ref int pcbResult,
int flags);
/// <summary>
/// Get a property from a BCrypt hash object
/// </summary>
[DllImport("bcrypt.dll", EntryPoint = "BCryptGetProperty", CharSet = CharSet.Unicode)]
internal static extern ErrorCode BCryptGetHashProperty(SafeBCryptHashHandle hObject,
string pszProperty,
[MarshalAs(UnmanagedType.LPArray), In, Out] byte[] pbOutput,
int cbOutput,
[In, Out] ref int pcbResult,
int flags);
/// <summary>
/// Get the hash value of the data
/// </summary>
[DllImport("bcrypt.dll")]
internal static extern ErrorCode BCryptFinishHash(SafeBCryptHashHandle hHash,
[MarshalAs(UnmanagedType.LPArray), Out] byte[] pbInput,
int cbInput,
int dwFlags);
/// <summary>
/// Hash a block of data
/// </summary>
[DllImport("bcrypt.dll")]
internal static extern ErrorCode BCryptHashData(SafeBCryptHashHandle hHash,
[MarshalAs(UnmanagedType.LPArray), In] byte[] pbInput,
int cbInput,
int dwFlags);
/// <summary>
/// Get a handle to an algorithm provider
/// </summary>
[DllImport("bcrypt.dll", CharSet = CharSet.Unicode)]
internal static extern ErrorCode BCryptOpenAlgorithmProvider([Out] out SafeBCryptAlgorithmHandle phAlgorithm,
string pszAlgId, // BCryptAlgorithm
string pszImplementation, // ProviderNames
int dwFlags);
}
//
// Wrapper and utility functions
//
/// <summary>
/// Adapter to wrap specific BCryptGetProperty P/Invokes with a generic handle type
/// </summary>
#pragma warning disable 618 // System.Core.dll still uses SecurityRuleSet.Level1
[SecurityCritical(SecurityCriticalScope.Everything)]
#pragma warning restore 618
private delegate ErrorCode BCryptPropertyGetter<T>(T hObject,
string pszProperty,
byte[] pbOutput,
int cbOutput,
ref int pcbResult,
int dwFlags) where T : SafeHandle;
private static volatile bool s_haveBcryptSupported;
private static volatile bool s_bcryptSupported;
/// <summary>
/// Determine if BCrypt is supported on the current machine
/// </summary>
internal static bool BCryptSupported {
[SecuritySafeCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
get {
if (!s_haveBcryptSupported)
{
// Attempt to load bcrypt.dll to see if the BCrypt CNG APIs are available on the machine
using (SafeLibraryHandle bcrypt = Microsoft.Win32.UnsafeNativeMethods.LoadLibraryEx("bcrypt", IntPtr.Zero, 0)) {
s_bcryptSupported = !bcrypt.IsInvalid;
s_haveBcryptSupported = true;
}
}
return s_bcryptSupported;
}
}
/// <summary>
/// Get the value of a DWORD property of a BCrypt object
/// </summary>
[System.Security.SecurityCritical]
internal static int GetInt32Property<T>(T algorithm, string property) where T : SafeHandle {
Contract.Requires(algorithm != null);
Contract.Requires(property == HashPropertyName.HashLength ||
property == ObjectPropertyName.ObjectLength);
return BitConverter.ToInt32(GetProperty(algorithm, property), 0);
}
/// <summary>
/// Get the value of a property of a BCrypt object
/// </summary>
[System.Security.SecurityCritical]
internal static byte[] GetProperty<T>(T algorithm, string property) where T : SafeHandle {
Contract.Requires(algorithm != null);
Contract.Requires(!String.IsNullOrEmpty(property));
Contract.Ensures(Contract.Result<byte[]>() != null);
BCryptPropertyGetter<T> getter = null;
if (typeof(T) == typeof(SafeBCryptAlgorithmHandle)) {
getter = new BCryptPropertyGetter<SafeBCryptAlgorithmHandle>(UnsafeNativeMethods.BCryptGetAlgorithmProperty)
as BCryptPropertyGetter<T>;
}
else if (typeof(T) == typeof(SafeBCryptHashHandle)) {
getter = new BCryptPropertyGetter<SafeBCryptHashHandle>(UnsafeNativeMethods.BCryptGetHashProperty)
as BCryptPropertyGetter<T>;
}
Debug.Assert(getter != null, "Unknown handle type");
// Figure out how big the property is
int bufferSize = 0;
ErrorCode error = getter(algorithm, property, null, 0, ref bufferSize, 0);
if (error != ErrorCode.BufferToSmall && error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
// Allocate the buffer, and return the property
Debug.Assert(bufferSize > 0, "bufferSize > 0");
byte[] buffer = new byte[bufferSize];
error = getter(algorithm, property, buffer, buffer.Length, ref bufferSize, 0);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return buffer;
}
/// <summary>
/// Map an algorithm identifier to a key size and magic number
/// </summary>
internal static void MapAlgorithmIdToMagic(string algorithm,
out KeyBlobMagicNumber algorithmMagic,
out int keySize) {
Contract.Requires(!String.IsNullOrEmpty(algorithm));
switch (algorithm) {
case AlgorithmName.ECDHP256:
algorithmMagic = KeyBlobMagicNumber.ECDHPublicP256;
keySize = 256;
break;
case AlgorithmName.ECDHP384:
algorithmMagic = KeyBlobMagicNumber.ECDHPublicP384;
keySize = 384;
break;
case AlgorithmName.ECDHP521:
algorithmMagic = KeyBlobMagicNumber.ECDHPublicP521;
keySize = 521;
break;
case AlgorithmName.ECDsaP256:
algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP256;
keySize = 256;
break;
case AlgorithmName.ECDsaP384:
algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP384;
keySize = 384;
break;
case AlgorithmName.ECDsaP521:
algorithmMagic = KeyBlobMagicNumber.ECDsaPublicP521;
keySize = 521;
break;
default:
throw new ArgumentException(SR.GetString(SR.Cryptography_UnknownEllipticCurveAlgorithm));
}
}
/// <summary>
/// Open a handle to an algorithm provider
/// </summary>
[System.Security.SecurityCritical]
internal static SafeBCryptAlgorithmHandle OpenAlgorithm(string algorithm, string implementation) {
Contract.Requires(!String.IsNullOrEmpty(algorithm));
Contract.Requires(!String.IsNullOrEmpty(implementation));
Contract.Ensures(Contract.Result<SafeBCryptAlgorithmHandle>() != null &&
!Contract.Result<SafeBCryptAlgorithmHandle>().IsInvalid &&
!Contract.Result<SafeBCryptAlgorithmHandle>().IsClosed);
SafeBCryptAlgorithmHandle algorithmHandle = null;
ErrorCode error = UnsafeNativeMethods.BCryptOpenAlgorithmProvider(out algorithmHandle,
algorithm,
implementation,
0);
if (error != ErrorCode.Success) {
throw new CryptographicException((int)error);
}
return algorithmHandle;
}
}
}

View File

@@ -0,0 +1,140 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Diagnostics.Contracts;
using Microsoft.Win32.SafeHandles;
namespace System.Security.Cryptography {
/// <summary>
/// Implementation of a generic CAPI hashing algorithm, concrete HashAlgorithm classes
/// implemented by CAPI can contain an instance of this class and delegate the work to it
/// </summary>
internal sealed class CapiHashAlgorithm : IDisposable {
private CapiNative.AlgorithmId m_algorithmId;
[SecurityCritical]
private SafeCspHandle m_cspHandle;
[SecurityCritical]
private SafeCapiHashHandle m_hashHandle;
[SecuritySafeCritical]
// SafeCritical - we're not exposing out anything that we want to prevent untrusted code from getting at
public CapiHashAlgorithm(string provider,
CapiNative.ProviderType providerType,
CapiNative.AlgorithmId algorithm) {
Contract.Requires(!String.IsNullOrEmpty(provider));
Contract.Requires((CapiNative.AlgorithmClass)((uint)algorithm & (uint)CapiNative.AlgorithmClass.Hash) == CapiNative.AlgorithmClass.Hash);
Contract.Ensures(m_cspHandle != null && !m_cspHandle.IsInvalid && !m_cspHandle.IsClosed);
Contract.Ensures(m_hashHandle != null && !m_hashHandle.IsInvalid && !m_hashHandle.IsClosed);
m_algorithmId = algorithm;
m_cspHandle = CapiNative.AcquireCsp(null,
provider,
providerType,
CapiNative.CryptAcquireContextFlags.VerifyContext,
true);
Initialize();
}
[SecuritySafeCritical]
public void Dispose() {
Contract.Ensures(m_hashHandle == null || m_hashHandle.IsClosed);
Contract.Ensures(m_cspHandle == null || m_cspHandle.IsClosed);
if (m_hashHandle != null) {
m_hashHandle.Dispose();
}
if (m_cspHandle != null) {
m_cspHandle.Dispose();
}
}
/// <summary>
/// Reset the hash algorithm to begin hashing a new set of data
/// </summary>
[SecuritySafeCritical]
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reviewed")]
public void Initialize() {
Contract.Ensures(m_hashHandle != null && !m_hashHandle.IsInvalid && !m_hashHandle.IsClosed);
Contract.Assert(m_cspHandle != null);
// Try to create a new hash algorithm to use
SafeCapiHashHandle newHashAlgorithm = null;
RuntimeHelpers.PrepareConstrainedRegions();
try {
if (!CapiNative.UnsafeNativeMethods.CryptCreateHash(m_cspHandle,
m_algorithmId,
SafeCapiKeyHandle.InvalidHandle,
0,
out newHashAlgorithm)) {
// BadAlgorithmId means that this CSP does not support the specified algorithm, which means
// that we're on a platform that does not support the given hash function.
int error = Marshal.GetLastWin32Error();
if (error == (int)CapiNative.ErrorCode.BadAlgorithmId) {
throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
}
else {
throw new CryptographicException(error);
}
}
}
finally {
if (newHashAlgorithm != null && !newHashAlgorithm.IsInvalid) {
newHashAlgorithm.SetParentCsp(m_cspHandle);
}
}
// If we created a new algorithm, dispose of the old one and use the new one
Debug.Assert(newHashAlgorithm != null, "newHashAlgorithm != null");
if (m_hashHandle != null) {
m_hashHandle.Dispose();
}
m_hashHandle = newHashAlgorithm;
}
/// <summary>
/// Hash a block of data
/// </summary>
[SecuritySafeCritical]
public void HashCore(byte[] array, int ibStart, int cbSize) {
Contract.Assert(m_hashHandle != null);
if (array == null) {
throw new ArgumentNullException("array");
}
if (ibStart < 0 || ibStart > array.Length - cbSize) {
throw new ArgumentOutOfRangeException("ibStart");
}
if (cbSize < 0 || cbSize > array.Length) {
throw new ArgumentOutOfRangeException("cbSize");
}
byte[] hashData = new byte[cbSize];
Buffer.BlockCopy(array, ibStart, hashData, 0, cbSize);
if (!CapiNative.UnsafeNativeMethods.CryptHashData(m_hashHandle, hashData, cbSize, 0)) {
throw new CryptographicException(Marshal.GetLastWin32Error());
}
}
/// <summary>
/// Complete the hash, returning its value
/// </summary>
[SecuritySafeCritical]
public byte[] HashFinal() {
Contract.Ensures(Contract.Result<byte[]>() != null);
Contract.Assert(m_hashHandle != null);
return CapiNative.GetHashParameter(m_hashHandle, CapiNative.HashParameter.HashValue);
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,234 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Diagnostics.Contracts;
namespace System.Security.Cryptography {
/// <summary>
/// Utility class to strongly type algorithms used with CNG. Since all CNG APIs which require an
/// algorithm name take the name as a string, we use this string wrapper class to specifically mark
/// which parameters are expected to be algorithms. We also provide a list of well known algorithm
/// names, which helps Intellisense users find a set of good algorithm names to use.
/// </summary>
[Serializable]
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class CngAlgorithm : IEquatable<CngAlgorithm> {
private static volatile CngAlgorithm s_ecdhp256;
private static volatile CngAlgorithm s_ecdhp384;
private static volatile CngAlgorithm s_ecdhp521;
private static volatile CngAlgorithm s_ecdsap256;
private static volatile CngAlgorithm s_ecdsap384;
private static volatile CngAlgorithm s_ecdsap521;
private static volatile CngAlgorithm s_md5;
private static volatile CngAlgorithm s_sha1;
private static volatile CngAlgorithm s_sha256;
private static volatile CngAlgorithm s_sha384;
private static volatile CngAlgorithm s_sha512;
private string m_algorithm;
public CngAlgorithm(string algorithm) {
Contract.Ensures(!String.IsNullOrEmpty(m_algorithm));
if (algorithm == null) {
throw new ArgumentNullException("algorithm");
}
if (algorithm.Length == 0) {
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidAlgorithmName, algorithm), "algorithm");
}
m_algorithm = algorithm;
}
/// <summary>
/// Name of the algorithm
/// </summary>
public string Algorithm {
get {
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
return m_algorithm;
}
}
public static bool operator==(CngAlgorithm left, CngAlgorithm right) {
if (Object.ReferenceEquals(left, null)) {
return Object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
[Pure]
public static bool operator !=(CngAlgorithm left, CngAlgorithm right) {
if (Object.ReferenceEquals(left, null)) {
return !Object.ReferenceEquals(right, null);
}
return !left.Equals(right);
}
public override bool Equals(object obj) {
Contract.Assert(m_algorithm != null);
return Equals(obj as CngAlgorithm);
}
public bool Equals(CngAlgorithm other) {
if (Object.ReferenceEquals(other, null)) {
return false;
}
return m_algorithm.Equals(other.Algorithm);
}
public override int GetHashCode() {
Contract.Assert(m_algorithm != null);
return m_algorithm.GetHashCode();
}
public override string ToString() {
Contract.Assert(m_algorithm != null);
return m_algorithm;
}
//
// Well known algorithms
//
public static CngAlgorithm ECDiffieHellmanP256 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_ecdhp256 == null) {
s_ecdhp256 = new CngAlgorithm(BCryptNative.AlgorithmName.ECDHP256);
}
return s_ecdhp256;
}
}
public static CngAlgorithm ECDiffieHellmanP384 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_ecdhp384 == null) {
s_ecdhp384 = new CngAlgorithm(BCryptNative.AlgorithmName.ECDHP384);
}
return s_ecdhp384;
}
}
public static CngAlgorithm ECDiffieHellmanP521 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_ecdhp521 == null) {
s_ecdhp521 = new CngAlgorithm(BCryptNative.AlgorithmName.ECDHP521);
}
return s_ecdhp521;
}
}
public static CngAlgorithm ECDsaP256 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_ecdsap256 == null) {
s_ecdsap256 = new CngAlgorithm(BCryptNative.AlgorithmName.ECDsaP256);
}
return s_ecdsap256;
}
}
public static CngAlgorithm ECDsaP384 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_ecdsap384 == null) {
s_ecdsap384 = new CngAlgorithm(BCryptNative.AlgorithmName.ECDsaP384);
}
return s_ecdsap384;
}
}
public static CngAlgorithm ECDsaP521 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_ecdsap521 == null) {
s_ecdsap521 = new CngAlgorithm(BCryptNative.AlgorithmName.ECDsaP521);
}
return s_ecdsap521;
}
}
public static CngAlgorithm MD5 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_md5 == null) {
s_md5 = new CngAlgorithm(BCryptNative.AlgorithmName.MD5);
}
return s_md5;
}
}
public static CngAlgorithm Sha1 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_sha1 == null) {
s_sha1 = new CngAlgorithm(BCryptNative.AlgorithmName.Sha1);
}
return s_sha1;
}
}
public static CngAlgorithm Sha256 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_sha256 == null) {
s_sha256 = new CngAlgorithm(BCryptNative.AlgorithmName.Sha256);
}
return s_sha256;
}
}
public static CngAlgorithm Sha384 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_sha384 == null) {
s_sha384 = new CngAlgorithm(BCryptNative.AlgorithmName.Sha384);
}
return s_sha384;
}
}
public static CngAlgorithm Sha512 {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
if (s_sha512 == null) {
s_sha512 = new CngAlgorithm(BCryptNative.AlgorithmName.Sha512);
}
return s_sha512;
}
}
}
}

View File

@@ -0,0 +1,161 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Diagnostics.Contracts;
namespace System.Security.Cryptography {
/// <summary>
/// Utility class to strongly type algorithm groups used with CNG. Since all CNG APIs which require or
/// return an algorithm group name take the name as a string, we use this string wrapper class to
/// specifically mark which parameters and return values are expected to be algorithm groups. We also
/// provide a list of well known algorithm group names, which helps Intellisense users find a set of
/// good algorithm group names to use.
/// </summary>
[Serializable]
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class CngAlgorithmGroup : IEquatable<CngAlgorithmGroup> {
private static volatile CngAlgorithmGroup s_dh;
private static volatile CngAlgorithmGroup s_dsa;
private static volatile CngAlgorithmGroup s_ecdh;
private static volatile CngAlgorithmGroup s_ecdsa;
private static volatile CngAlgorithmGroup s_rsa;
private string m_algorithmGroup;
public CngAlgorithmGroup(string algorithmGroup) {
Contract.Ensures(!String.IsNullOrEmpty(m_algorithmGroup));
if (algorithmGroup == null) {
throw new ArgumentNullException("algorithmGroup");
}
if (algorithmGroup.Length == 0) {
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidAlgorithmGroup, algorithmGroup), "algorithmGroup");
}
m_algorithmGroup = algorithmGroup;
}
/// <summary>
/// Name of the algorithm group
/// </summary>
public string AlgorithmGroup {
get {
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
return m_algorithmGroup;
}
}
[Pure]
public static bool operator ==(CngAlgorithmGroup left, CngAlgorithmGroup right) {
if (Object.ReferenceEquals(left, null)) {
return Object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
[Pure]
public static bool operator !=(CngAlgorithmGroup left, CngAlgorithmGroup right) {
if (Object.ReferenceEquals(left, null)) {
return !Object.ReferenceEquals(right, null);
}
return !left.Equals(right);
}
public override bool Equals(object obj) {
Contract.Assert(m_algorithmGroup != null);
return Equals(obj as CngAlgorithmGroup);
}
public bool Equals(CngAlgorithmGroup other) {
if (Object.ReferenceEquals(other, null)) {
return false;
}
return m_algorithmGroup.Equals(other.AlgorithmGroup);
}
public override int GetHashCode() {
Contract.Assert(m_algorithmGroup != null);
return m_algorithmGroup.GetHashCode();
}
public override string ToString() {
Contract.Assert(m_algorithmGroup != null);
return m_algorithmGroup;
}
//
// Well known algorithm groups
//
public static CngAlgorithmGroup DiffieHellman {
get {
Contract.Ensures(Contract.Result<CngAlgorithmGroup>() != null);
if (s_dh == null) {
s_dh = new CngAlgorithmGroup("DH"); // NCRYPT_DH_ALGORITHM_GROUP
}
return s_dh;
}
}
public static CngAlgorithmGroup Dsa {
get {
Contract.Ensures(Contract.Result<CngAlgorithmGroup>() != null);
if (s_dsa == null) {
s_dsa = new CngAlgorithmGroup("DSA"); // NCRYPT_DSA_ALGORITHM_GROUP
}
return s_dsa;
}
}
public static CngAlgorithmGroup ECDiffieHellman {
[Pure]
get {
Contract.Ensures(Contract.Result<CngAlgorithmGroup>() != null);
if (s_ecdh == null) {
s_ecdh = new CngAlgorithmGroup("ECDH"); // NCRYPT_ECDH_ALGORITHM_GROUP
}
return s_ecdh;
}
}
public static CngAlgorithmGroup ECDsa {
[Pure]
get {
Contract.Ensures(Contract.Result<CngAlgorithmGroup>() != null);
if (s_ecdsa == null) {
s_ecdsa = new CngAlgorithmGroup("ECDSA"); // NCRYPT_ECDSA_ALGORITHM_GROUP
}
return s_ecdsa;
}
}
public static CngAlgorithmGroup Rsa {
get {
Contract.Ensures(Contract.Result<CngAlgorithmGroup>() != null);
if (s_rsa == null) {
s_rsa = new CngAlgorithmGroup("RSA"); // NCRYPT_RSA_ALGORITHM_GROUP
}
return s_rsa;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,170 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Diagnostics.Contracts;
namespace System.Security.Cryptography {
/// <summary>
/// Utility class to strongly type the format of key blobs used with CNG. Since all CNG APIs which
/// require or return a key blob format take the name as a string, we use this string wrapper class to
/// specifically mark which parameters and return values are expected to be key blob formats. We also
/// provide a list of well known blob formats, which helps Intellisense users find a set of good blob
/// formats to use.
/// </summary>
[Serializable]
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class CngKeyBlobFormat : IEquatable<CngKeyBlobFormat> {
private static volatile CngKeyBlobFormat s_eccPrivate;
private static volatile CngKeyBlobFormat s_eccPublic;
private static volatile CngKeyBlobFormat s_genericPrivate;
private static volatile CngKeyBlobFormat s_genericPublic;
private static volatile CngKeyBlobFormat s_opaqueTransport;
private static volatile CngKeyBlobFormat s_pkcs8Private;
private string m_format;
public CngKeyBlobFormat(string format) {
Contract.Ensures(!String.IsNullOrEmpty(m_format));
if (format == null) {
throw new ArgumentNullException("format");
}
if (format.Length == 0) {
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidKeyBlobFormat, format), "format");
}
m_format = format;
}
/// <summary>
/// Name of the blob format
/// </summary>
public string Format {
get {
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
return m_format;
}
}
public static bool operator ==(CngKeyBlobFormat left, CngKeyBlobFormat right) {
if (Object.ReferenceEquals(left, null)) {
return Object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
[Pure]
public static bool operator !=(CngKeyBlobFormat left, CngKeyBlobFormat right) {
if (Object.ReferenceEquals(left, null)) {
return !Object.ReferenceEquals(right, null);
}
return !left.Equals(right);
}
public override bool Equals(object obj) {
Contract.Assert(m_format != null);
return Equals(obj as CngKeyBlobFormat);
}
public bool Equals(CngKeyBlobFormat other) {
if (Object.ReferenceEquals(other, null)) {
return false;
}
return m_format.Equals(other.Format);
}
public override int GetHashCode() {
Contract.Assert(m_format != null);
return m_format.GetHashCode();
}
public override string ToString() {
Contract.Assert(m_format != null);
return m_format;
}
//
// Well known key blob formats
//
public static CngKeyBlobFormat EccPrivateBlob {
get {
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
if (s_eccPrivate == null) {
s_eccPrivate = new CngKeyBlobFormat("ECCPRIVATEBLOB"); // BCRYPT_ECCPRIVATE_BLOB
}
return s_eccPrivate;
}
}
public static CngKeyBlobFormat EccPublicBlob {
get {
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
if (s_eccPublic == null) {
s_eccPublic = new CngKeyBlobFormat("ECCPUBLICBLOB"); // BCRYPT_ECCPUBLIC_BLOB
}
return s_eccPublic;
}
}
public static CngKeyBlobFormat GenericPrivateBlob {
get {
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
if (s_genericPrivate == null) {
s_genericPrivate = new CngKeyBlobFormat("PRIVATEBLOB"); // BCRYPT_PRIVATE_KEY_BLOB
}
return s_genericPrivate;
}
}
public static CngKeyBlobFormat GenericPublicBlob {
get {
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
if (s_genericPublic == null) {
s_genericPublic = new CngKeyBlobFormat("PUBLICBLOB"); // BCRYPT_PUBLIC_KEY_BLOB
}
return s_genericPublic;
}
}
public static CngKeyBlobFormat OpaqueTransportBlob {
get {
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
if (s_opaqueTransport == null) {
s_opaqueTransport = new CngKeyBlobFormat("OpaqueTransport"); // NCRYPT_OPAQUETRANSPORT_BLOB
}
return s_opaqueTransport;
}
}
public static CngKeyBlobFormat Pkcs8PrivateBlob {
get {
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
if (s_pkcs8Private == null) {
s_pkcs8Private = new CngKeyBlobFormat("PKCS8_PRIVATEKEY"); // NCRYPT_PKCS8_PRIVATE_KEY_BLOB
}
return s_pkcs8Private;
}
}
}
}

View File

@@ -0,0 +1,115 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Security;
using System.Security.Permissions;
using System.Diagnostics.Contracts;
namespace System.Security.Cryptography {
/// <summary>
/// Settings to be applied to a CNG key before it is finalized.
/// </summary>
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class CngKeyCreationParameters {
private CngExportPolicies? m_exportPolicy;
private CngKeyCreationOptions m_keyCreationOptions;
private CngKeyUsages? m_keyUsage;
private CngPropertyCollection m_parameters = new CngPropertyCollection();
private IntPtr m_parentWindowHandle;
private CngProvider m_provider = CngProvider.MicrosoftSoftwareKeyStorageProvider;
private CngUIPolicy m_uiPolicy;
/// <summary>
/// How many times can this key be exported from the KSP
/// </summary>
public CngExportPolicies? ExportPolicy {
get { return m_exportPolicy; }
set { m_exportPolicy = value; }
}
/// <summary>
/// Flags controlling how to create the key
/// </summary>
public CngKeyCreationOptions KeyCreationOptions {
get { return m_keyCreationOptions; }
set { m_keyCreationOptions = value; }
}
/// <summary>
/// Which cryptographic operations are valid for use with this key
/// </summary>
public CngKeyUsages? KeyUsage {
get { return m_keyUsage; }
set { m_keyUsage = value; }
}
/// <summary>
/// Window handle to use as the parent for the dialog shown when the key is created
/// </summary>
public IntPtr ParentWindowHandle {
get { return m_parentWindowHandle; }
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
[SecuritySafeCritical]
set { m_parentWindowHandle = value; }
}
/// <summary>
/// Extra parameter values to set before the key is finalized
/// </summary>
public CngPropertyCollection Parameters {
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
[SecuritySafeCritical]
get {
Contract.Ensures(Contract.Result<CngPropertyCollection>() != null);
return m_parameters;
}
}
/// <summary>
/// Internal access to the parameters method without a demand
/// </summary>
internal CngPropertyCollection ParametersNoDemand {
get {
Contract.Ensures(Contract.Result<CngPropertyCollection>() != null);
return m_parameters;
}
}
/// <summary>
/// KSP to create the key in
/// </summary>
public CngProvider Provider {
get {
Contract.Ensures(Contract.Result<CngProvider>() != null);
return m_provider;
}
set {
if (value == null) {
throw new ArgumentNullException("value");
}
m_provider = value;
}
}
/// <summary>
/// Settings for UI shown on access to the key
/// </summary>
public CngUIPolicy UIPolicy {
get { return m_uiPolicy; }
[HostProtection(UI = true)]
[UIPermission(SecurityAction.Demand, Window = UIPermissionWindow.SafeSubWindows)]
[SecuritySafeCritical]
set { m_uiPolicy = value; }
}
}
}

View File

@@ -0,0 +1,156 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Collections.ObjectModel;
using System.Diagnostics.Contracts;
namespace System.Security.Cryptography {
/// <summary>
/// Wrapper represeting an arbitrary property of a CNG key or provider
/// </summary>
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public struct CngProperty : IEquatable<CngProperty> {
private string m_name;
private CngPropertyOptions m_propertyOptions;
private byte[] m_value;
private int? m_hashCode;
public CngProperty(string name, byte[] value, CngPropertyOptions options) {
if (name == null)
throw new ArgumentNullException("name");
// @
m_name = name;
m_propertyOptions = options;
m_hashCode = null;
if (value != null) {
m_value = value.Clone() as byte[];
}
else {
m_value = null;
}
}
/// <summary>
/// Name of the property
/// </summary>
public string Name {
get {
Contract.Ensures(Contract.Result<string>() != null);
return m_name;
}
}
/// <summary>
/// Options used to set / get the property
/// </summary>
public CngPropertyOptions Options {
get { return m_propertyOptions; }
}
/// <summary>
/// Direct value of the property -- if the value will be returned to user code or modified, use
/// GetValue() instead.
/// </summary>
internal byte[] Value {
get { return m_value; }
}
/// <summary>
/// Contents of the property
/// </summary>
/// <returns></returns>
public byte[] GetValue() {
byte[] value = null;
if (m_value != null) {
value = m_value.Clone() as byte[];
}
return value;
}
public static bool operator ==(CngProperty left, CngProperty right) {
return left.Equals(right);
}
public static bool operator !=(CngProperty left, CngProperty right) {
return !left.Equals(right);
}
public override bool Equals(object obj) {
if (obj == null || !(obj is CngProperty)) {
return false;
}
return Equals((CngProperty)obj);
}
public bool Equals(CngProperty other) {
//
// We will consider CNG properties equal only if the name, options and value are all also equal
//
if (!String.Equals(Name, other.Name, StringComparison.Ordinal)) {
return false;
}
if (Options != other.Options) {
return false;
}
if (m_value == null) {
return other.m_value == null;
}
if (other.m_value == null) {
return false;
}
if (m_value.Length != other.m_value.Length) {
return false;
}
for (int i = 0; i < m_value.Length; i++) {
if (m_value[i] != other.m_value[i]) {
return false;
}
}
return true;
}
public override int GetHashCode() {
if (!m_hashCode.HasValue) {
int hashCode = Name.GetHashCode() ^ Options.GetHashCode();
// The hash code for a byte is just the value of that byte. Since this will only modify the
// lower bits of the hash code, we'll xor each byte into different sections of the hash code
if (m_value != null) {
for (int i = 0; i < m_value.Length; i++) {
// Shift each byte forward by one byte, so that every 4 bytes has to potential to update
// each of the calculated hash code's bytes.
int shifted = (int)(m_value[i] << ((i % 4) * 8));
hashCode ^= shifted;
}
}
m_hashCode = hashCode;
}
return m_hashCode.Value;
}
}
/// <summary>
/// Strongly typed collection of CNG properties
/// </summary>
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class CngPropertyCollection : Collection<CngProperty> {
}
}

View File

@@ -0,0 +1,120 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.Contracts;
namespace System.Security.Cryptography {
/// <summary>
/// Utility class to strongly type providers used with CNG. Since all CNG APIs which require a
/// provider name take the name as a string, we use this string wrapper class to specifically mark
/// which parameters are expected to be providers. We also provide a list of well known provider
/// names, which helps Intellisense users find a set of good providernames to use.
/// </summary>
[Serializable]
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class CngProvider : IEquatable<CngProvider> {
private static volatile CngProvider s_msSmartCardKsp;
private static volatile CngProvider s_msSoftwareKsp;
private string m_provider;
public CngProvider(string provider) {
Contract.Ensures(!String.IsNullOrEmpty(m_provider));
if (provider == null) {
throw new ArgumentNullException("provider");
}
if (provider.Length == 0) {
throw new ArgumentException(SR.GetString(SR.Cryptography_InvalidProviderName, provider), "provider");
}
m_provider = provider;
}
/// <summary>
/// Name of the CNG provider
/// </summary>
public string Provider {
get {
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
return m_provider;
}
}
public static bool operator ==(CngProvider left, CngProvider right) {
if (Object.ReferenceEquals(left, null)) {
return Object.ReferenceEquals(right, null);
}
return left.Equals(right);
}
[Pure]
public static bool operator !=(CngProvider left, CngProvider right) {
if (Object.ReferenceEquals(left, null)) {
return !Object.ReferenceEquals(right, null);
}
return !left.Equals(right);
}
public override bool Equals(object obj) {
Contract.Assert(m_provider != null);
return Equals(obj as CngProvider);
}
public bool Equals(CngProvider other) {
if (Object.ReferenceEquals(other, null)) {
return false;
}
return m_provider.Equals(other.Provider);
}
public override int GetHashCode() {
Contract.Assert(m_provider != null);
return m_provider.GetHashCode();
}
public override string ToString() {
Contract.Assert(m_provider != null);
return m_provider.ToString();
}
//
// Well known NCrypt KSPs
//
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "CardKey", Justification = "This is not 'Smart Cardkey', but 'Smart Card Key'")]
[SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "SmartCard", Justification = "Smart Card is two words in the ncrypt usage")]
public static CngProvider MicrosoftSmartCardKeyStorageProvider {
get {
Contract.Ensures(Contract.Result<CngProvider>() != null);
if (s_msSmartCardKsp == null) {
s_msSmartCardKsp = new CngProvider("Microsoft Smart Card Key Storage Provider"); // MS_SMART_CARD_KEY_STORAGE_PROVIDER
}
return s_msSmartCardKsp;
}
}
public static CngProvider MicrosoftSoftwareKeyStorageProvider {
get {
Contract.Ensures(Contract.Result<CngProvider>() != null);
if (s_msSoftwareKsp == null) {
s_msSoftwareKsp = new CngProvider("Microsoft Software Key Storage Provider"); // MS_KEY_STORAGE_PROVIDER
}
return s_msSoftwareKsp;
}
}
}
}

View File

@@ -0,0 +1,89 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Diagnostics.Contracts;
namespace System.Security.Cryptography {
/// <summary>
/// Configuration parameters for the UI displayed by CNG when accessing a protected key
/// </summary>
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class CngUIPolicy {
private string m_creationTitle;
private string m_description;
private string m_friendlyName;
private CngUIProtectionLevels m_protectionLevel;
private string m_useContext;
public CngUIPolicy(CngUIProtectionLevels protectionLevel) :
this(protectionLevel, null) {
}
public CngUIPolicy(CngUIProtectionLevels protectionLevel, string friendlyName) :
this(protectionLevel, friendlyName, null) {
}
public CngUIPolicy(CngUIProtectionLevels protectionLevel, string friendlyName, string description) :
this(protectionLevel, friendlyName, description, null) {
}
public CngUIPolicy(CngUIProtectionLevels protectionLevel,
string friendlyName,
string description,
string useContext) :
this(protectionLevel, friendlyName, description, useContext, null) {
}
public CngUIPolicy(CngUIProtectionLevels protectionLevel,
string friendlyName,
string description,
string useContext,
string creationTitle) {
m_creationTitle = creationTitle;
m_description = description;
m_friendlyName = friendlyName;
m_protectionLevel = protectionLevel;
m_useContext = useContext;
}
/// <summary>
/// Title of the dialog box displaed when a newly created key is finalized, null for the default title
/// </summary>
public string CreationTitle {
get { return m_creationTitle; }
}
/// <summary>
/// Description text displayed in the dialog box when the key is accessed, null for the default text
/// </summary>
public string Description {
get { return m_description; }
}
/// <summary>
/// Friendly name to describe the key with in the dialog box that appears when the key is accessed,
/// null for default name
/// </summary>
public string FriendlyName {
get { return m_friendlyName; }
}
/// <summary>
/// Level of UI protection to apply to the key
/// </summary>
public CngUIProtectionLevels ProtectionLevel {
get { return m_protectionLevel; }
}
/// <summary>
/// Description of how the key will be used
/// </summary>
public string UseContext {
get { return m_useContext; }
}
}
}

View File

@@ -0,0 +1,47 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Runtime.Serialization;
namespace System.Security.Cryptography {
/// <summary>
/// Abstract base class for implementations of elliptic curve Diffie-Hellman to derive from
/// </summary>
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public abstract class ECDiffieHellman : AsymmetricAlgorithm {
public override string KeyExchangeAlgorithm {
get { return "ECDiffieHellman"; }
}
public override string SignatureAlgorithm {
get { return null; }
}
//
// Creation factory methods
//
public static new ECDiffieHellman Create() {
return Create(typeof(ECDiffieHellmanCng).FullName);
}
public static new ECDiffieHellman Create(string algorithm) {
if (algorithm == null) {
throw new ArgumentNullException("algorithm");
}
return CryptoConfig.CreateFromName(algorithm) as ECDiffieHellman;
}
//
// Key derivation
//
public abstract ECDiffieHellmanPublicKey PublicKey { get; }
public abstract byte[] DeriveKeyMaterial(ECDiffieHellmanPublicKey otherPartyPublicKey);
}
}

View File

@@ -0,0 +1,475 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security;
using System.Security.Permissions;
using System.Diagnostics.Contracts;
using Microsoft.Win32.SafeHandles;
namespace System.Security.Cryptography {
/// <summary>
/// Key derivation functions used to transform the raw secret agreement into key material
/// </summary>
public enum ECDiffieHellmanKeyDerivationFunction {
Hash,
Hmac,
Tls
}
/// <summary>
/// Wrapper for CNG's implementation of elliptic curve Diffie-Hellman key exchange
/// </summary>
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class ECDiffieHellmanCng : ECDiffieHellman {
private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(256, 384, 128), new KeySizes(521, 521, 0) };
private CngAlgorithm m_hashAlgorithm = CngAlgorithm.Sha256;
private byte[] m_hmacKey;
private CngKey m_key;
private ECDiffieHellmanKeyDerivationFunction m_kdf = ECDiffieHellmanKeyDerivationFunction.Hash;
private byte[] m_label;
private byte[] m_secretAppend;
private byte[] m_secretPrepend;
private byte[] m_seed;
//
// Constructors
//
public ECDiffieHellmanCng() : this(521) {
Contract.Ensures(LegalKeySizesValue != null);
}
public ECDiffieHellmanCng(int keySize) {
Contract.Ensures(LegalKeySizesValue != null);
if (!NCryptNative.NCryptSupported) {
throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
}
LegalKeySizesValue = s_legalKeySizes;
KeySize = keySize;
}
[SecuritySafeCritical]
public ECDiffieHellmanCng(CngKey key) {
Contract.Ensures(LegalKeySizesValue != null);
Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
if (key == null) {
throw new ArgumentNullException("key");
}
if (key.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "key");
}
if (!NCryptNative.NCryptSupported) {
throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
}
LegalKeySizesValue = s_legalKeySizes;
// Make a copy of the key so that we continue to work if it gets disposed before this algorithm
//
// This requires an assert for UnmanagedCode since we'll need to access the raw handles of the key
// and the handle constructor of CngKey. The assert is safe since ECDiffieHellmanCng will never
// expose the key handles to calling code (without first demanding UnmanagedCode via the Handle
// property of CngKey).
//
// The bizzare looking disposal of the key.Handle property is intentional - Handle returns a
// duplicate - without disposing it, we keep the key alive until the GC runs.
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
using (SafeNCryptKeyHandle importHandle = key.Handle) {
Key = CngKey.Open(importHandle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
}
CodeAccessPermission.RevertAssert();
KeySize = m_key.KeySize;
}
/// <summary>
/// Hash algorithm used with the Hash and HMAC KDFs
/// </summary>
public CngAlgorithm HashAlgorithm {
get {
Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
return m_hashAlgorithm;
}
set {
Contract.Ensures(m_hashAlgorithm != null);
if (m_hashAlgorithm == null) {
throw new ArgumentNullException("value");
}
m_hashAlgorithm = value;
}
}
/// <summary>
/// Key used with the HMAC KDF
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
public byte[] HmacKey {
get { return m_hmacKey; }
set { m_hmacKey = value; }
}
/// <summary>
/// KDF used to transform the secret agreement into key material
/// </summary>
public ECDiffieHellmanKeyDerivationFunction KeyDerivationFunction {
get {
Contract.Ensures(Contract.Result<ECDiffieHellmanKeyDerivationFunction>() >= ECDiffieHellmanKeyDerivationFunction.Hash &&
Contract.Result<ECDiffieHellmanKeyDerivationFunction>() <= ECDiffieHellmanKeyDerivationFunction.Tls);
return m_kdf;
}
set {
Contract.Ensures(m_kdf >= ECDiffieHellmanKeyDerivationFunction.Hash &&
m_kdf <= ECDiffieHellmanKeyDerivationFunction.Tls);
if (value < ECDiffieHellmanKeyDerivationFunction.Hash || value > ECDiffieHellmanKeyDerivationFunction.Tls) {
throw new ArgumentOutOfRangeException("value");
}
m_kdf = value;
}
}
/// <summary>
/// Label bytes used for the TLS KDF
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
public byte[] Label {
get { return m_label; }
set { m_label = value; }
}
/// <summary>
/// Bytes to append to the raw secret agreement before processing by the KDF
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
public byte[] SecretAppend {
get { return m_secretAppend; }
set { m_secretAppend = value; }
}
/// <summary>
/// Bytes to prepend to the raw secret agreement before processing by the KDF
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
public byte[] SecretPrepend {
get { return m_secretPrepend; }
set { m_secretPrepend = value; }
}
/// <summary>
/// Seed bytes used for the TLS KDF
/// </summary>
[SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Reviewed API design exception since these are really setters for explicit byte arrays rather than properties that will be iterated by users")]
public byte[] Seed {
get { return m_seed; }
set { m_seed = value; }
}
/// <summary>
/// Full key pair being used for key generation
/// </summary>
public CngKey Key {
get {
Contract.Ensures(Contract.Result<CngKey>() != null);
Contract.Ensures(Contract.Result<CngKey>().AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
// If the size of the key no longer matches our stored value, then we need to replace it with
// a new key of the correct size.
if (m_key != null && m_key.KeySize != KeySize) {
m_key.Dispose();
m_key = null;
}
if (m_key == null) {
// Map the current key size to a CNG algorithm name
CngAlgorithm algorithm = null;
switch (KeySize) {
case 256:
algorithm = CngAlgorithm.ECDiffieHellmanP256;
break;
case 384:
algorithm = CngAlgorithm.ECDiffieHellmanP384;
break;
case 521:
algorithm = CngAlgorithm.ECDiffieHellmanP521;
break;
default:
Debug.Assert(false, "Illegal key size set");
break;
}
m_key = CngKey.Create(algorithm);
}
return m_key;
}
private set {
Contract.Requires(value != null);
Contract.Ensures(m_key != null && m_key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
if (value.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey));
}
if (m_key != null) {
m_key.Dispose();
}
//
// We do not duplicate the handle because the only time the user has access to the key itself
// to dispose underneath us is when they construct via the CngKey constructor, which does a
// duplication. Otherwise all key lifetimes are controlled directly by the ECDiffieHellmanCng
// class.
//
m_key = value;
KeySize = m_key.KeySize;
}
}
/// <summary>
/// Public key used to generate key material with the second party
/// </summary>
public override ECDiffieHellmanPublicKey PublicKey {
get {
Contract.Ensures(Contract.Result<ECDiffieHellmanPublicKey>() != null);
return new ECDiffieHellmanCngPublicKey(Key);
}
}
/// <summary>
/// Use the secret agreement as the HMAC key rather than supplying a seperate one
/// </summary>
public bool UseSecretAgreementAsHmacKey {
get { return HmacKey == null; }
}
/// <summary>
/// Given a second party's public key, derive shared key material
/// </summary>
public override byte[] DeriveKeyMaterial(ECDiffieHellmanPublicKey otherPartyPublicKey) {
Contract.Ensures(Contract.Result<byte[]>() != null);
Contract.Assert(m_kdf >= ECDiffieHellmanKeyDerivationFunction.Hash &&
m_kdf <= ECDiffieHellmanKeyDerivationFunction.Tls);
if (otherPartyPublicKey == null) {
throw new ArgumentNullException("otherPartyPublicKey");
}
// We can only work with ECDiffieHellmanCngPublicKeys
ECDiffieHellmanCngPublicKey otherKey = otherPartyPublicKey as ECDiffieHellmanCngPublicKey;
if (otherPartyPublicKey == null) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgExpectedECDiffieHellmanCngPublicKey));
}
using (CngKey import = otherKey.Import()) {
return DeriveKeyMaterial(import);
}
}
/// <summary>
/// Given a second party's public key, derive shared key material
/// </summary>
[SecuritySafeCritical]
public byte[] DeriveKeyMaterial(CngKey otherPartyPublicKey) {
Contract.Ensures(Contract.Result<byte[]>() != null);
Contract.Assert(m_kdf >= ECDiffieHellmanKeyDerivationFunction.Hash &&
m_kdf <= ECDiffieHellmanKeyDerivationFunction.Tls);
if (otherPartyPublicKey == null) {
throw new ArgumentNullException("otherPartyPublicKey");
}
if (otherPartyPublicKey.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "otherPartyPublicKey");
}
if (otherPartyPublicKey.KeySize != KeySize) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHKeySizeMismatch), "otherPartyPublicKey");
}
NCryptNative.SecretAgreementFlags flags =
UseSecretAgreementAsHmacKey ? NCryptNative.SecretAgreementFlags.UseSecretAsHmacKey : NCryptNative.SecretAgreementFlags.None;
// We require access to the handles for generating key material. This is safe since we will never
// expose these handles to user code
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
// This looks horribly wrong - but accessing the handle property actually returns a duplicate handle, which
// we need to dispose of - otherwise, we're stuck keepign the resource alive until the GC runs. This explicitly
// is not disposing of the handle underlying the key dispite what the syntax looks like.
using (SafeNCryptKeyHandle localKey = Key.Handle)
using (SafeNCryptKeyHandle otherKey = otherPartyPublicKey.Handle) {
CodeAccessPermission.RevertAssert();
//
// Generating key material is a two phase process.
// 1. Generate the secret agreement
// 2. Pass the secret agreement through a KDF to get key material
//
using (SafeNCryptSecretHandle secretAgreement = NCryptNative.DeriveSecretAgreement(localKey, otherKey)) {
if (KeyDerivationFunction == ECDiffieHellmanKeyDerivationFunction.Hash) {
byte[] secretAppend = SecretAppend == null ? null : SecretAppend.Clone() as byte[];
byte[] secretPrepend = SecretPrepend == null ? null : SecretPrepend.Clone() as byte[];
return NCryptNative.DeriveKeyMaterialHash(secretAgreement,
HashAlgorithm.Algorithm,
secretPrepend,
secretAppend,
flags);
}
else if (KeyDerivationFunction == ECDiffieHellmanKeyDerivationFunction.Hmac) {
byte[] hmacKey = HmacKey == null ? null : HmacKey.Clone() as byte[];
byte[] secretAppend = SecretAppend == null ? null : SecretAppend.Clone() as byte[];
byte[] secretPrepend = SecretPrepend == null ? null : SecretPrepend.Clone() as byte[];
return NCryptNative.DeriveKeyMaterialHmac(secretAgreement,
HashAlgorithm.Algorithm,
hmacKey,
secretPrepend,
secretAppend,
flags);
}
else {
Debug.Assert(KeyDerivationFunction == ECDiffieHellmanKeyDerivationFunction.Tls, "Unknown KDF");
byte[] label = Label == null ? null : Label.Clone() as byte[];
byte[] seed = Seed == null ? null : Seed.Clone() as byte[];
if (label == null || seed == null) {
throw new InvalidOperationException(SR.GetString(SR.Cryptography_TlsRequiresLabelAndSeed));
}
return NCryptNative.DeriveKeyMaterialTls(secretAgreement, label, seed, flags);
}
}
}
}
/// <summary>
/// Get a handle to the secret agreement generated between two parties
/// </summary>
public SafeNCryptSecretHandle DeriveSecretAgreementHandle(ECDiffieHellmanPublicKey otherPartyPublicKey) {
if (otherPartyPublicKey == null) {
throw new ArgumentNullException("otherPartyPublicKey");
}
// We can only work with ECDiffieHellmanCngPublicKeys
ECDiffieHellmanCngPublicKey otherKey = otherPartyPublicKey as ECDiffieHellmanCngPublicKey;
if (otherPartyPublicKey == null) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgExpectedECDiffieHellmanCngPublicKey));
}
using (CngKey importedKey = otherKey.Import()) {
return DeriveSecretAgreementHandle(importedKey);
}
}
/// <summary>
/// Get a handle to the secret agreement between two parties
/// </summary>
[System.Security.SecurityCritical]
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
public SafeNCryptSecretHandle DeriveSecretAgreementHandle(CngKey otherPartyPublicKey) {
if (otherPartyPublicKey == null) {
throw new ArgumentNullException("otherPartyPublicKey");
}
if (otherPartyPublicKey.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "otherPartyPublicKey");
}
if (otherPartyPublicKey.KeySize != KeySize) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHKeySizeMismatch), "otherPartyPublicKey");
}
// This looks strange, but the Handle property returns a duplicate so we need to dispose of it when we're done
using (SafeNCryptKeyHandle localHandle = Key.Handle)
using (SafeNCryptKeyHandle otherPartyHandle = otherPartyPublicKey.Handle) {
return NCryptNative.DeriveSecretAgreement(localHandle, otherPartyHandle);
}
}
/// <summary>
/// Clean up the algorithm
/// </summary>
protected override void Dispose(bool disposing) {
try {
if (disposing) {
if (m_key != null) {
m_key.Dispose();
}
}
}
finally {
base.Dispose(disposing);
}
}
//
// XML Import
//
// See code:System.Security.Cryptography.ECDsaCng#ECCXMLFormat and
// code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
// elliptic curve XML formats.
//
public override void FromXmlString(string xmlString) {
throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
}
public void FromXmlString(string xml, ECKeyXmlFormat format) {
if (xml == null) {
throw new ArgumentNullException("xml");
}
if (format != ECKeyXmlFormat.Rfc4050) {
throw new ArgumentOutOfRangeException("format");
}
Key = Rfc4050KeyFormatter.FromXml(xml);
}
//
// XML Export
//
// See code:System.Security.Cryptography.ECDsaCng#ECCXMLFormat and
// code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
// elliptic curve XML formats.
//
public override string ToXmlString(bool includePrivateParameters) {
throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
}
public string ToXmlString(ECKeyXmlFormat format) {
Contract.Ensures(Contract.Result<string>() != null);
if (format != ECKeyXmlFormat.Rfc4050) {
throw new ArgumentOutOfRangeException("format");
}
return Rfc4050KeyFormatter.ToXml(Key);
}
}
}

View File

@@ -0,0 +1,149 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Runtime.Serialization;
using System.Security;
using System.Security.Permissions;
using System.Diagnostics.Contracts;
using Microsoft.Win32.SafeHandles;
namespace System.Security.Cryptography {
/// <summary>
/// Public key used to do key exchange with the ECDiffieHellmanCng algorithm
/// </summary>
[Serializable]
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public sealed class ECDiffieHellmanCngPublicKey : ECDiffieHellmanPublicKey {
[NonSerialized]
private CngKey m_key;
private CngKeyBlobFormat m_format;
/// <summary>
/// Wrap a CNG key
/// </summary>
[SecuritySafeCritical]
internal ECDiffieHellmanCngPublicKey(CngKey key) : base(key.Export(CngKeyBlobFormat.EccPublicBlob)) {
Contract.Requires(key != null && key.AlgorithmGroup == CngAlgorithmGroup.ECDiffieHellman);
Contract.Ensures(m_format != null);
m_format = CngKeyBlobFormat.EccPublicBlob;
//
// We need to make a copy of the key to prevent the situation where the ECDiffieHellmanCng algorithm
// object is disposed (this disposing its key) before the ECDiffieHellmanCngPublic key is disposed.
//
// Accessing the handle in partial trust is safe because we're not exposing it back out to user code
//
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
// This looks odd, but .Handle returns a duplicate, so we need to dispose it
using (SafeNCryptKeyHandle importKey = key.Handle) {
m_key = CngKey.Open(importKey, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
}
CodeAccessPermission.RevertAssert();
}
/// <summary>
/// Format the key blob is expressed in
/// </summary>
public CngKeyBlobFormat BlobFormat {
get {
Contract.Ensures(Contract.Result<CngKeyBlobFormat>() != null);
Contract.Assert(m_format != null);
return m_format;
}
}
/// <summary>
/// Clean up the key
/// </summary>
protected override void Dispose(bool disposing) {
try {
if (disposing) {
if (m_key != null) {
m_key.Dispose();
}
}
}
finally {
base.Dispose(disposing);
}
}
/// <summary>
/// Hydrate a public key from a blob
/// </summary>
[SecuritySafeCritical]
public static ECDiffieHellmanPublicKey FromByteArray(byte[] publicKeyBlob, CngKeyBlobFormat format) {
if (publicKeyBlob == null) {
throw new ArgumentNullException("publicKeyBlob");
}
if (format == null) {
throw new ArgumentNullException("format");
}
using (CngKey imported = CngKey.Import(publicKeyBlob, format)) {
if (imported.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey));
}
return new ECDiffieHellmanCngPublicKey(imported);
}
}
/// <summary>
/// Hydrate a public key from XML
///
/// See code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information
/// about the XML format used.
/// </summary>
[SecuritySafeCritical]
public static ECDiffieHellmanCngPublicKey FromXmlString(string xml) {
if (xml == null) {
throw new ArgumentNullException("xml");
}
using (CngKey imported = Rfc4050KeyFormatter.FromXml(xml)) {
if (imported.AlgorithmGroup != CngAlgorithmGroup.ECDiffieHellman) {
throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDHRequiresECDHKey), "xml");
}
return new ECDiffieHellmanCngPublicKey(imported);
}
}
/// <summary>
/// Import the public key into CNG
/// </summary>
/// <returns></returns>
public CngKey Import() {
Contract.Ensures(Contract.Result<CngKey>() != null);
Contract.Assert(m_format != null);
return CngKey.Import(ToByteArray(), BlobFormat);
}
/// <summary>
/// Convert the key blob to XML
///
/// See code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information
/// about the XML format used.
/// </summary>
public override string ToXmlString() {
Contract.Ensures(!String.IsNullOrEmpty(Contract.Result<string>()));
if (m_key == null) {
m_key = Import();
}
return Rfc4050KeyFormatter.ToXml(m_key);
}
}
}

View File

@@ -0,0 +1,45 @@
// ==++==
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ==--==
using System;
using System.Runtime.Serialization;
using System.Diagnostics.Contracts;
namespace System.Security.Cryptography {
/// <summary>
/// Wrapper for public key material passed between parties during Diffie-Hellman key material generation
/// </summary>
[Serializable]
[System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
public abstract class ECDiffieHellmanPublicKey : IDisposable {
private byte[] m_keyBlob;
protected ECDiffieHellmanPublicKey(byte[] keyBlob) {
Contract.Ensures(m_keyBlob != null);
if (keyBlob == null) {
throw new ArgumentNullException("keyBlob");
}
m_keyBlob = keyBlob.Clone() as byte[];
}
public void Dispose() {
Dispose(true);
}
protected virtual void Dispose(bool disposing) {
return;
}
public virtual byte[] ToByteArray() {
Contract.Assert(m_keyBlob != null);
return m_keyBlob.Clone() as byte[];
}
public abstract string ToXmlString();
}
}

Some files were not shown because too many files have changed in this diff Show More