ECDiffieHellmanCng.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Core / System / Security / Cryptography / ECDiffieHellmanCng.cs / 1305376 / ECDiffieHellmanCng.cs

                            // ==++== 
//
//   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 {
    ///  
    ///     Key derivation functions used to transform the raw secret agreement into key material
    /// 
    public enum ECDiffieHellmanKeyDerivationFunction {
        Hash, 
        Hmac,
        Tls 
    } 

    ///  
    ///     Wrapper for CNG's implementation of elliptic curve Diffie-Hellman key exchange
    /// 
    [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);
        } 

        //  
        //  
        // 
        [System.Security.SecurityCritical] 
        public ECDiffieHellmanCng(int keySize) {
            Contract.Ensures(LegalKeySizesValue != null);

            if (!NCryptNative.NCryptSupported) { 
                throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
            } 
 
            LegalKeySizesValue = s_legalKeySizes;
            KeySize = keySize; 
        }

        // 
        //  
        // 
        //  
        [System.Security.SecurityCritical] 
        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).
            new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); 
            Key = CngKey.Open(key.Handle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
            CodeAccessPermission.RevertAssert(); 
 
            KeySize = m_key.KeySize;
        } 

        /// 
        ///     Hash algorithm used with the Hash and HMAC KDFs
        ///  
        public CngAlgorithm HashAlgorithm {
            get { 
                Contract.Ensures(Contract.Result() != null); 
                return m_hashAlgorithm;
            } 

            set {
                Contract.Ensures(m_hashAlgorithm != null);
 
                if (m_hashAlgorithm == null) {
                    throw new ArgumentNullException("value"); 
                } 

                m_hashAlgorithm = value; 
            }
        }

        ///  
        ///     Key used with the HMAC KDF
        ///  
        [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; }
        }

        ///  
        ///     KDF used to transform the secret agreement into key material
        ///  
        public ECDiffieHellmanKeyDerivationFunction KeyDerivationFunction { 
            get {
                Contract.Ensures(Contract.Result() >= ECDiffieHellmanKeyDerivationFunction.Hash && 
                                 Contract.Result() <= 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;
            } 
        } 

        ///  
        ///     Label bytes used for the TLS KDF
        /// 
        [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; } 
        } 

        ///  
        ///     Bytes to append to the raw secret agreement before processing by the KDF
        /// 
        [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; } 
        } 

        ///  
        ///     Bytes to prepend to the raw secret agreement before processing by the KDF
        /// 
        [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; } 
        } 

        ///  
        ///     Seed bytes used for the TLS KDF
        /// 
        [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; } 
        } 

        ///  
        ///     Full key pair being used for key generation
        /// 
        public CngKey Key {
            get { 
                Contract.Ensures(Contract.Result() != null);
                Contract.Ensures(Contract.Result().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; 
            }
        }

        ///  
        ///     Public key used to generate key material with the second party
        ///  
        public override ECDiffieHellmanPublicKey PublicKey { 
            // 
            //  
            // 
            [System.Security.SecurityCritical]
            get {
                Contract.Ensures(Contract.Result() != null); 
                return new ECDiffieHellmanCngPublicKey(Key);
            } 
        } 

        ///  
        ///     Use the secret agreement as the HMAC key rather than supplying a seperate one
        /// 
        public bool UseSecretAgreementAsHmacKey {
            get { return HmacKey == null; } 
        }
 
        ///  
        ///     Given a second party's public key, derive shared key material
        ///  
        public override byte[] DeriveKeyMaterial(ECDiffieHellmanPublicKey otherPartyPublicKey) {
            Contract.Ensures(Contract.Result() != 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);
            } 
        }

        /// 
        ///     Given a second party's public key, derive shared key material 
        /// 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        [System.Security.SecurityCritical] 
        public byte[] DeriveKeyMaterial(CngKey otherPartyPublicKey) {
            Contract.Ensures(Contract.Result() != 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(); 
            SafeNCryptKeyHandle localKey = Key.Handle;
            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); 
                } 
            }
        } 

        /// 
        ///     Get a handle to the secret agreement generated between two parties
        ///  
        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);
            } 
        }

        /// 
        ///     Get a handle to the secret agreement between two parties 
        /// 
        //  
        //  
        // 
        [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");
            }

            return NCryptNative.DeriveSecretAgreement(Key.Handle, otherPartyPublicKey.Handle); 
        }
 
        ///  
        ///     Clean up the algorithm
        ///  
        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() != null);
 
            if (format != ECKeyXmlFormat.Rfc4050) { 
                throw new ArgumentOutOfRangeException("format");
            } 

            return Rfc4050KeyFormatter.ToXml(Key);
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   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 {
    ///  
    ///     Key derivation functions used to transform the raw secret agreement into key material
    /// 
    public enum ECDiffieHellmanKeyDerivationFunction {
        Hash, 
        Hmac,
        Tls 
    } 

    ///  
    ///     Wrapper for CNG's implementation of elliptic curve Diffie-Hellman key exchange
    /// 
    [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);
        } 

        //  
        //  
        // 
        [System.Security.SecurityCritical] 
        public ECDiffieHellmanCng(int keySize) {
            Contract.Ensures(LegalKeySizesValue != null);

            if (!NCryptNative.NCryptSupported) { 
                throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
            } 
 
            LegalKeySizesValue = s_legalKeySizes;
            KeySize = keySize; 
        }

        // 
        //  
        // 
        //  
        [System.Security.SecurityCritical] 
        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).
            new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert(); 
            Key = CngKey.Open(key.Handle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
            CodeAccessPermission.RevertAssert(); 
 
            KeySize = m_key.KeySize;
        } 

        /// 
        ///     Hash algorithm used with the Hash and HMAC KDFs
        ///  
        public CngAlgorithm HashAlgorithm {
            get { 
                Contract.Ensures(Contract.Result() != null); 
                return m_hashAlgorithm;
            } 

            set {
                Contract.Ensures(m_hashAlgorithm != null);
 
                if (m_hashAlgorithm == null) {
                    throw new ArgumentNullException("value"); 
                } 

                m_hashAlgorithm = value; 
            }
        }

        ///  
        ///     Key used with the HMAC KDF
        ///  
        [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; }
        }

        ///  
        ///     KDF used to transform the secret agreement into key material
        ///  
        public ECDiffieHellmanKeyDerivationFunction KeyDerivationFunction { 
            get {
                Contract.Ensures(Contract.Result() >= ECDiffieHellmanKeyDerivationFunction.Hash && 
                                 Contract.Result() <= 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;
            } 
        } 

        ///  
        ///     Label bytes used for the TLS KDF
        /// 
        [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; } 
        } 

        ///  
        ///     Bytes to append to the raw secret agreement before processing by the KDF
        /// 
        [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; } 
        } 

        ///  
        ///     Bytes to prepend to the raw secret agreement before processing by the KDF
        /// 
        [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; } 
        } 

        ///  
        ///     Seed bytes used for the TLS KDF
        /// 
        [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; } 
        } 

        ///  
        ///     Full key pair being used for key generation
        /// 
        public CngKey Key {
            get { 
                Contract.Ensures(Contract.Result() != null);
                Contract.Ensures(Contract.Result().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; 
            }
        }

        ///  
        ///     Public key used to generate key material with the second party
        ///  
        public override ECDiffieHellmanPublicKey PublicKey { 
            // 
            //  
            // 
            [System.Security.SecurityCritical]
            get {
                Contract.Ensures(Contract.Result() != null); 
                return new ECDiffieHellmanCngPublicKey(Key);
            } 
        } 

        ///  
        ///     Use the secret agreement as the HMAC key rather than supplying a seperate one
        /// 
        public bool UseSecretAgreementAsHmacKey {
            get { return HmacKey == null; } 
        }
 
        ///  
        ///     Given a second party's public key, derive shared key material
        ///  
        public override byte[] DeriveKeyMaterial(ECDiffieHellmanPublicKey otherPartyPublicKey) {
            Contract.Ensures(Contract.Result() != 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);
            } 
        }

        /// 
        ///     Given a second party's public key, derive shared key material 
        /// 
        //  
        //  
        // 
        //  
        // 
        // 
        // 
        [System.Security.SecurityCritical] 
        public byte[] DeriveKeyMaterial(CngKey otherPartyPublicKey) {
            Contract.Ensures(Contract.Result() != 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(); 
            SafeNCryptKeyHandle localKey = Key.Handle;
            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); 
                } 
            }
        } 

        /// 
        ///     Get a handle to the secret agreement generated between two parties
        ///  
        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);
            } 
        }

        /// 
        ///     Get a handle to the secret agreement between two parties 
        /// 
        //  
        //  
        // 
        [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");
            }

            return NCryptNative.DeriveSecretAgreement(Key.Handle, otherPartyPublicKey.Handle); 
        }
 
        ///  
        ///     Clean up the algorithm
        ///  
        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() != null);
 
            if (format != ECKeyXmlFormat.Rfc4050) { 
                throw new ArgumentOutOfRangeException("format");
            } 

            return Rfc4050KeyFormatter.ToXml(Key);
        }
    } 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK