< Summary

Class:Microsoft.Azure.KeyVault.EcKeyCurveParameters
Assembly:Microsoft.Azure.KeyVault.Cryptography
File(s):C:\Git\azure-sdk-for-net\sdk\keyvault\Microsoft.Azure.KeyVault.Cryptography\src\EcKey.cs
Covered lines:50
Uncovered lines:31
Coverable lines:81
Total lines:649
Line coverage:61.7% (50 of 81)
Covered branches:4
Total branches:18
Branch coverage:22.2% (4 of 18)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
get_CurveType()-100%100%
get_Hash()-100%100%
get_Prime()-100%100%
get_A()-100%100%
get_B()-100%100%
get_Gx()-100%100%
get_Gy()-100%100%
get_Order()-100%100%
get_Cofactor()-100%100%
get_Seed()-100%100%
get_KeySizeInBytes()-100%100%
get_CurveBlobSize()-100%100%
get_CurveBlob()-100%100%
get_KeyBlobSize()-100%100%
ToString()-0%0%
GetBytesDesc(...)-0%0%
WriteTo(...)-100%100%
ToCurveType(...)-25%25%
ToCurveGenerationAlgId(...)-66.67%50%
AlignAndWrite(...)-30%25%

File(s)

C:\Git\azure-sdk-for-net\sdk\keyvault\Microsoft.Azure.KeyVault.Cryptography\src\EcKey.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License. See License.txt in the project root for
 3// license information.
 4
 5using System;
 6using System.IO;
 7using System.Security.Cryptography;
 8using System.Text;
 9using System.Threading;
 10using System.Threading.Tasks;
 11using Microsoft.Azure.KeyVault.Core;
 12using Microsoft.Azure.KeyVault.Cryptography;
 13using Microsoft.Azure.KeyVault.Cryptography.Algorithms;
 14
 15#if NETSTANDARD
 16using TaskException = System.Threading.Tasks.Task;
 17#endif
 18
 19namespace Microsoft.Azure.KeyVault
 20{
 21    /// <summary>
 22    /// An elliptic curve key.
 23    /// </summary>
 24    public class EcKey : IKey
 25    {
 26        public const string P256 = "P-256";
 27        public const string P384 = "P-384";
 28        public const string P521 = "P-521";
 29        public const string P256K = "P-256K";
 30
 31        private static readonly string DefaultCurve = P256;
 32
 33        private ECDsa _ecdsa;
 34
 35        /// <summary>
 36        /// Key Identifier
 37        /// </summary>
 38        public string Kid { get; }
 39
 40        /// <summary>
 41        /// Constructor, creates a P-256 key with a GUID identifier.
 42        /// </summary>
 43        public EcKey() : this( Guid.NewGuid().ToString( "D" ), DefaultCurve )
 44        {
 45        }
 46
 47        /// <summary>
 48        /// Constructor, creates a P-256 key.
 49        /// </summary>
 50        /// <param name="kid">The key identifier to use</param>
 51        public EcKey( string kid ) : this( kid, DefaultCurve )
 52        {
 53        }
 54
 55        /// <summary>
 56        /// Constructor.
 57        /// </summary>
 58        /// <param name="kid">The key identifier to use</param>
 59        /// <param name="curve">The name of elliptic curve to use</param>
 60        public EcKey( string kid, string curve )
 61        {
 62            if ( string.IsNullOrWhiteSpace( kid ) )
 63                throw new ArgumentNullException( nameof( kid ) );
 64
 65            Kid = kid;
 66
 67            var kcp = new CngKeyCreationParameters();
 68            kcp.ExportPolicy = CngExportPolicies.AllowPlaintextExport;
 69            kcp.KeyUsage = CngKeyUsages.Signing;
 70
 71            CngAlgorithm cngAlgo;
 72            switch ( curve )
 73            {
 74                case P256:
 75                    cngAlgo = CngAlgorithm.ECDsaP256;
 76                    DefaultSignatureAlgorithm = Es256.AlgorithmName;
 77                    break;
 78
 79                case P384:
 80                    cngAlgo = CngAlgorithm.ECDsaP384;
 81                    DefaultSignatureAlgorithm = Es384.AlgorithmName;
 82                    break;
 83
 84                case P521:
 85                    cngAlgo = CngAlgorithm.ECDsaP521;
 86                    DefaultSignatureAlgorithm = Es512.AlgorithmName;
 87                    break;
 88
 89                case P256K:
 90                    cngAlgo = CngAlgorithm_Generic_ECDSA;
 91                    kcp.Parameters.Add( new CngProperty( NativeMethods.BCRYPT_ECC_PARAMETERS, Secp256k1Parameters.CurveB
 92                    DefaultSignatureAlgorithm = ES256K.AlgorithmName;
 93                    break;
 94
 95                default:
 96                    throw new ArgumentException( $"Unsupported curve: \"{curve}\"." );
 97            }
 98
 99            var key = CngKey.Create( cngAlgo, null, kcp );
 100            _ecdsa = new ECDsaCng( key );
 101        }
 102
 103        /// <summary>
 104        /// Constructor
 105        /// </summary>
 106        /// <param name="kid">The key identifier to use</param>
 107        /// <param name="curve">The name of elliptic curve to use; supported values are constants described in this clas
 108        /// <param name="x">The value of public point field X.</param>
 109        /// <param name="y">The value of public point field Y.</param>
 110        /// <param name="d">The value of private key D. If null, only the public key operations will be allowed.</param>
 111        public EcKey( string kid, string curve, byte[] x, byte[] y, byte[] d = null )
 112        {
 113            if ( string.IsNullOrWhiteSpace( kid ) )
 114                throw new ArgumentNullException( nameof( kid ) );
 115
 116            if ( string.IsNullOrWhiteSpace( curve ) )
 117                throw new ArgumentNullException( nameof( curve ) );
 118
 119            if ( x == null )
 120                throw new ArgumentNullException( nameof( x ) );
 121
 122            if ( y == null )
 123                throw new ArgumentNullException( nameof( y ) );
 124
 125            Kid = kid;
 126
 127            CngKey key;
 128
 129            switch ( curve )
 130            {
 131                case P256:
 132                    key = ImportNistKey( NativeMethods.BCRYPT_ECDSA_PRIVATE_P256_MAGIC, NativeMethods.BCRYPT_ECDSA_PUBLI
 133                    DefaultSignatureAlgorithm = Es256.AlgorithmName;
 134                    break;
 135
 136                case P384:
 137                    key = ImportNistKey( NativeMethods.BCRYPT_ECDSA_PRIVATE_P384_MAGIC, NativeMethods.BCRYPT_ECDSA_PUBLI
 138                    DefaultSignatureAlgorithm = Es384.AlgorithmName;
 139                    break;
 140
 141                case P521:
 142                    key = ImportNistKey( NativeMethods.BCRYPT_ECDSA_PRIVATE_P521_MAGIC, NativeMethods.BCRYPT_ECDSA_PUBLI
 143                    DefaultSignatureAlgorithm = Es512.AlgorithmName;
 144                    break;
 145
 146                case P256K:
 147                    key = ImportGenericKey( Secp256k1Parameters, x, y, d );
 148                    DefaultSignatureAlgorithm = ES256K.AlgorithmName;
 149                    break;
 150
 151                default:
 152                    throw new ArgumentException( $"Invalid curve name: \"{curve}\"", nameof( curve ) );
 153            }
 154
 155            _ecdsa = new ECDsaCng( key );
 156        }
 157
 158        private CngKey ImportNistKey( int privateMagic, int publicMagic, int size, byte[] x, byte[] y, byte[] d )
 159        {
 160            if ( d == null || IsZero( d ) )
 161                return ImportNistPublicKey( publicMagic, size, x, y );
 162
 163            return ImportNistPrivateKey( privateMagic, size, x, y, d );
 164        }
 165
 166        private CngKey ImportNistPublicKey( int magic, int sizeInBytes, byte[] x, byte[] y )
 167        {
 168            // Format described by BCRYPT_ECCKEY_BLOB (BCRYPT_ECCPUBLIC_BLOB).
 169            // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375520(v=vs.85).aspx
 170
 171            var keyBlob = new byte[4 + 4 + sizeInBytes + sizeInBytes];
 172            using ( var writer = new BinaryWriter( new MemoryStream( keyBlob ) ) )
 173            {
 174                writer.Write( magic );
 175                writer.Write( sizeInBytes );
 176                AlignAndWrite( writer, x, sizeInBytes, nameof( x ) );
 177                AlignAndWrite( writer, y, sizeInBytes, nameof( y ) );
 178            }
 179
 180            return CngKey.Import( keyBlob, CngKeyBlobFormat.EccPublicBlob );
 181        }
 182
 183        private CngKey ImportNistPrivateKey( int magic, int sizeInBytes, byte[] x, byte[] y, byte[] d )
 184        {
 185            // Format described by BCRYPT_ECCKEY_BLOB (BCRYPT_ECCPRIVATE_BLOB).
 186            // https://msdn.microsoft.com/en-us/library/windows/desktop/aa375520(v=vs.85).aspx
 187
 188            var keyBlob = new byte[4 + 4 + sizeInBytes + sizeInBytes + sizeInBytes];
 189            using ( var writer = new BinaryWriter( new MemoryStream( keyBlob ) ) )
 190            {
 191                writer.Write( magic );
 192                writer.Write( sizeInBytes );
 193                AlignAndWrite( writer, x, sizeInBytes, nameof( x ) );
 194                AlignAndWrite( writer, y, sizeInBytes, nameof( y ) );
 195                AlignAndWrite( writer, d, sizeInBytes, nameof( d ) );
 196            }
 197
 198            return CngKey.Import( keyBlob, CngKeyBlobFormat.EccPrivateBlob );
 199        }
 200
 201        private CngKey ImportGenericKey( EcKeyCurveParameters curveParameters, byte[] x, byte[] y, byte[] d )
 202        {
 203            if ( d == null || IsZero( d ) )
 204                return ImportGenericPublicKey( curveParameters, x, y );
 205
 206            return ImportGenericPrivateKey( curveParameters, x, y, d );
 207        }
 208
 209        private CngKey ImportGenericPublicKey( EcKeyCurveParameters curveParameters, byte[] x, byte[] y )
 210        {
 211            /*  struct BCRYPT_ECCFULLKEY_BLOB
 212                {
 213                    ULONG dwMagic;              // BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC or BCRYPT_ECDSA_PRIVATE_GENERIC_MAG
 214                    BCRYPT_ECC_PARAMETER_HEADER curveParameters;
 215                    BYTE Qx[cbFieldLength]      // X-coordinate of the public point.
 216                    BYTE Qy[cbFieldLength]      // Y-coordinate of the public point.
 217                    BYTE d[cbSubgroupOrder]     // Private key. Zero if only public key is required.
 218                }
 219            */
 220
 221            var keyBlob = new byte[curveParameters.KeyBlobSize];
 222            using ( var writer = new BinaryWriter( new MemoryStream( keyBlob ) ) )
 223            {
 224                writer.Write( NativeMethods.BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC );
 225                curveParameters.WriteTo( writer );
 226
 227                var size = curveParameters.KeySizeInBytes;
 228                AlignAndWrite( writer, x, size, nameof( x ) );
 229                AlignAndWrite( writer, y, size, nameof( y ) );
 230            }
 231
 232            return CngKey.Import( keyBlob, CngKeyBlobFormat_Generic_Public );
 233        }
 234
 235        private CngKey ImportGenericPrivateKey( EcKeyCurveParameters curveParameters, byte[] x, byte[] y, byte[] d )
 236        {
 237            /*  struct BCRYPT_ECCFULLKEY_BLOB
 238                {
 239                    ULONG dwMagic;              // BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC or BCRYPT_ECDSA_PRIVATE_GENERIC_MAG
 240                    BCRYPT_ECC_PARAMETER_HEADER curveParameters;
 241                    BYTE Qx[cbFieldLength]      // X-coordinate of the public point.
 242                    BYTE Qy[cbFieldLength]      // Y-coordinate of the public point.
 243                    BYTE d[cbSubgroupOrder]     // Private key. Zero if only public key is required.
 244                }
 245            */
 246
 247            var keyBlob = new byte[curveParameters.KeyBlobSize];
 248            using ( var writer = new BinaryWriter( new MemoryStream( keyBlob ) ) )
 249            {
 250                writer.Write( NativeMethods.BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC );
 251                curveParameters.WriteTo( writer );
 252
 253                var size = curveParameters.KeySizeInBytes;
 254                AlignAndWrite( writer, x, size, nameof( x ) );
 255                AlignAndWrite( writer, y, size, nameof( y ) );
 256                AlignAndWrite( writer, d, size, nameof( d ) );
 257            }
 258
 259            return CngKey.Import( keyBlob, CngKeyBlobFormat_Generic_Private );
 260        }
 261
 262        /// <summary>
 263        /// Constructor.
 264        /// </summary>
 265        /// <param name="kid">The key identifier to use</param>
 266        /// <param name="ecdsa">The ECDsa object for the key</param>
 267        /// <remarks>The ECDsa object is IDisposable, this class will hold a
 268        /// reference to the ECDsa object but will not dispose it, the caller
 269        /// of this constructor is responsible for the lifetime of this
 270        /// parameter.</remarks>
 271        public EcKey( string kid, ECDsa ecdsa )
 272        {
 273            if ( string.IsNullOrWhiteSpace( kid ) )
 274                throw new ArgumentNullException( nameof( kid ) );
 275
 276            if ( ecdsa == null )
 277                throw new ArgumentNullException( nameof( ecdsa ) );
 278
 279            Kid = kid;
 280
 281            // NOTE: ECDsa is disposable and that may lead to runtime errors later.
 282            _ecdsa = ecdsa;
 283        }
 284
 285        // Intentionally excluded.
 286        //~EcKey()
 287        //{
 288        //    Dispose( false );
 289        //}
 290
 291        public void Dispose()
 292        {
 293            Dispose( true );
 294            GC.SuppressFinalize( this );
 295        }
 296
 297        protected virtual void Dispose( bool disposing )
 298        {
 299            // Clean up managed resources if Dispose was called
 300            if ( disposing )
 301            {
 302                if ( _ecdsa != null )
 303                {
 304                    _ecdsa.Dispose();
 305                    _ecdsa = null;
 306                }
 307            }
 308        }
 309
 310        #region IKey implementation
 311
 312        public string DefaultEncryptionAlgorithm => null;
 313
 314        public string DefaultKeyWrapAlgorithm => null;
 315
 316        public string DefaultSignatureAlgorithm { get; set; }
 317
 318        public Task<byte[]> DecryptAsync( byte[] ciphertext, byte[] iv, byte[] authenticationData = null, byte[] authent
 319        {
 320            throw MethodNotSupported( nameof( DecryptAsync ) );
 321        }
 322
 323        public Task<Tuple<byte[], byte[], string>> EncryptAsync( byte[] plaintext, byte[] iv = null, byte[] authenticati
 324        {
 325            throw MethodNotSupported( nameof( EncryptAsync ) );
 326        }
 327
 328        public Task<Tuple<byte[], string>> WrapKeyAsync( byte[] key, string algorithm = RsaOaep.AlgorithmName, Cancellat
 329        {
 330            throw MethodNotSupported( nameof( WrapKeyAsync ) );
 331        }
 332
 333        public Task<byte[]> UnwrapKeyAsync( byte[] encryptedKey, string algorithm = RsaOaep.AlgorithmName, CancellationT
 334        {
 335            throw MethodNotSupported( nameof( UnwrapKeyAsync ) );
 336        }
 337
 338        private Exception MethodNotSupported( string methodName )
 339        {
 340            throw new NotSupportedException( $"Method {methodName} cannot is not supported by EcKey {Kid}." );
 341        }
 342
 343        public Task<Tuple<byte[], string>> SignAsync( byte[] digest, string algorithm, CancellationToken token = default
 344        {
 345            if ( _ecdsa == null )
 346                throw new ObjectDisposedException( $"EcKey {Kid} is disposed" );
 347
 348            if ( algorithm == null )
 349                algorithm = DefaultSignatureAlgorithm;
 350
 351            if ( digest == null )
 352                throw new ArgumentNullException( nameof( digest ) );
 353
 354            var algo = AlgorithmResolver.Default[algorithm] as AsymmetricSignatureAlgorithm;
 355            var transform = algo?.CreateSignatureTransform( _ecdsa );
 356
 357            if ( algo == null || transform == null )
 358                throw new NotSupportedException( $"algorithm {algorithm} is not supported by EcKey {Kid}" );
 359
 360            try
 361            {
 362                var result = new Tuple<byte[], string>( transform.Sign( digest ), algorithm );
 363
 364                return Task.FromResult( result );
 365            }
 366            catch ( Exception ex )
 367            {
 368                return TaskException.FromException<Tuple<byte[], string>>( ex );
 369            }
 370        }
 371
 372        public Task<bool> VerifyAsync( byte[] digest, byte[] signature, string algorithm, CancellationToken token = defa
 373        {
 374            if ( _ecdsa == null )
 375                throw new ObjectDisposedException( $"EcKey {Kid} is disposed" );
 376
 377            if ( digest == null )
 378                throw new ArgumentNullException( nameof( digest ) );
 379
 380            if ( signature == null )
 381                throw new ArgumentNullException( nameof( signature ) );
 382
 383            if ( algorithm == null )
 384                algorithm = DefaultSignatureAlgorithm;
 385
 386            var algo = AlgorithmResolver.Default[algorithm] as AsymmetricSignatureAlgorithm;
 387            var transform = algo?.CreateSignatureTransform( _ecdsa );
 388
 389            if ( algo == null || transform == null )
 390                throw new NotSupportedException( $"algorithm {algorithm} is not supported by EcKey {Kid}" );
 391
 392            try
 393            {
 394                var result = transform.Verify( digest, signature );
 395
 396                return Task.FromResult( result );
 397            }
 398            catch ( Exception ex )
 399            {
 400                return TaskException.FromException<bool>( ex );
 401            }
 402        }
 403
 404        #endregion
 405
 406        #region Utilities
 407
 408        private static bool IsZero( byte[] v )
 409        {
 410            if ( v == null )
 411                return true;
 412
 413            for ( var i = 0; i < v.Length; ++i )
 414                if ( v[i] != 0 )
 415                    return false;
 416
 417            return true;
 418        }
 419
 420        private static void AlignAndWrite( BinaryWriter writer, byte[] bytes, int size, string paramName )
 421        {
 422            if ( bytes.Length >= size )
 423            {
 424                for ( var i = 0; i < bytes.Length - size; ++i )
 425                    if ( bytes[i] != 0 )
 426                        throw new ArgumentException( $"Value of {paramName} is bigger than allowed for this key." );
 427
 428                writer.Write( bytes, bytes.Length - size, size );
 429                return;
 430            }
 431
 432            for ( var i = bytes.Length; i < size; ++i )
 433                writer.Write( (byte) 0 );
 434            writer.Write( bytes );
 435        }
 436
 437        private static readonly CngKeyBlobFormat CngKeyBlobFormat_Generic_Public = new CngKeyBlobFormat( NativeMethods.B
 438        private static readonly CngKeyBlobFormat CngKeyBlobFormat_Generic_Private = new CngKeyBlobFormat( NativeMethods.
 439
 440        private static readonly CngAlgorithm CngAlgorithm_Generic_ECDSA = new CngAlgorithm( "ECDSA" );
 441
 442        private static EcKeyCurveParameters _secp256k1Parameters;
 443
 444        private static EcKeyCurveParameters Secp256k1Parameters
 445        {
 446            get
 447            {
 448                if ( _secp256k1Parameters != null )
 449                    return _secp256k1Parameters;
 450
 451                return _secp256k1Parameters = new EcKeyCurveParameters
 452                {
 453                    // Copied from http://www.secg.org/sec2-v2.pdf and verified to match https://en.bitcoin.it/wiki/Secp
 454                    CurveType = EcKeyCurveType.PrimeShortWeierstrass,
 455                    Hash = null,
 456                    Prime = Convert.FromBase64String( "/////////////////////////////////////v///C8=" ),
 457                    A = Convert.FromBase64String( "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" ),
 458                    B = Convert.FromBase64String( "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAc=" ),
 459                    Gx = Convert.FromBase64String( "eb5mfvncu6xVoGKVzocLBwKb/NstzijZWfKBWxb4F5g=" ),
 460                    Gy = Convert.FromBase64String( "SDradyajxGVdpPv8DhEIqP0XtEimhVQZnEfQj/sQ1Lg=" ),
 461                    Order = Convert.FromBase64String( "/////////////////////rqu3OavSKA7v9JejNA2QUE=" ),
 462                    Cofactor = Convert.FromBase64String( "AQ==" ),
 463                    Seed = Convert.FromBase64String( "" ),
 464                };
 465            }
 466        }
 467
 468        #endregion
 469    }
 470
 471    internal sealed class EcKeyCurveParameters
 472    {
 8473        public EcKeyCurveType CurveType { get; set; }
 8474        public string Hash { get; set; }
 28475        public byte[] Prime { get; set; }
 8476        public byte[] A { get; set; }
 8477        public byte[] B { get; set; }
 8478        public byte[] Gx { get; set; }
 8479        public byte[] Gy { get; set; }
 20480        public byte[] Order { get; set; }
 20481        public byte[] Cofactor { get; set; }
 20482        public byte[] Seed { get; set; }
 483
 4484        public int KeySizeInBytes => Prime.Length;
 485
 486        public int CurveBlobSize
 487        {
 488            get
 489            {
 6490                var cbFieldLength = Prime.Length;
 6491                var cbSubgroupOrder = Order.Length;
 6492                var cbCofactor = Cofactor.Length;
 6493                var cbSeed = Seed.Length;
 494
 6495                var size = 7 * sizeof( uint ) + 5 * cbFieldLength + cbSubgroupOrder + cbCofactor + cbSeed;
 496
 6497                return size;
 498            }
 499        }
 500
 501        public byte[] CurveBlob
 502        {
 503            get
 504            {
 2505                var result = new byte[CurveBlobSize];
 2506                using ( var writer = new BinaryWriter( new MemoryStream( result ) ) )
 2507                    WriteTo( writer );
 2508                return result;
 509            }
 510        }
 511
 512        public int KeyBlobSize
 513        {
 514            get
 515            {
 4516                var cbFieldLength = Prime.Length;
 4517                var size = sizeof( uint ) + CurveBlobSize + 3 * cbFieldLength;
 4518                return size;
 519            }
 520        }
 521
 522        public override string ToString()
 523        {
 0524            var sb = new StringBuilder();
 0525            sb.AppendLine( $"new {nameof( EcKeyCurveParameters )}" );
 0526            sb.AppendLine( "{" );
 0527            sb.AppendLine( $"    {nameof( CurveType )} = {nameof( EcKeyCurveType )}.{CurveType}," );
 0528            var hashDesc = Hash == null ? "null" : $"\"{Hash}\"";
 0529            sb.AppendLine( $"    {nameof( Hash )} = {hashDesc}," );
 0530            sb.AppendLine( $"    {nameof( Prime )} = {GetBytesDesc( Prime )}," );
 0531            sb.AppendLine( $"    {nameof( A )} = {GetBytesDesc( A )}," );
 0532            sb.AppendLine( $"    {nameof( B )} = {GetBytesDesc( B )}," );
 0533            sb.AppendLine( $"    {nameof( Gx )} = {GetBytesDesc( Gx )}," );
 0534            sb.AppendLine( $"    {nameof( Gy )} = {GetBytesDesc( Gy )}," );
 0535            sb.AppendLine( $"    {nameof( Order )} = {GetBytesDesc( Order )}," );
 0536            sb.AppendLine( $"    {nameof( Cofactor )} = {GetBytesDesc( Cofactor )}," );
 0537            sb.AppendLine( $"    {nameof( Seed )} = {GetBytesDesc( Seed )}," );
 0538            sb.AppendLine( "}" );
 0539            return sb.ToString();
 540        }
 541
 542        private static string GetBytesDesc( byte[] bytes )
 543        {
 0544            if ( bytes == null )
 0545                return "null";
 0546            var base64 = Convert.ToBase64String( bytes );
 0547            return $"Convert.FromBase64String(\"{base64}\")";
 548        }
 549
 550        public void WriteTo( BinaryWriter writer )
 551        {
 552            /*
 553                struct BCRYPT_ECC_PARAMETER_HEADER
 554                {
 555                    ULONG                   dwVersion;              //Version of the structure
 556                    ECC_CURVE_TYPE_ENUM     dwCurveType;            //Supported curve types.
 557                    ECC_CURVE_ALG_ID_ENUM   dwCurveGenerationAlgId; //For X.592 verification purposes, if we include See
 558                    ULONG                   cbFieldLength;          //Byte length of the fields P, A, B, X, Y.
 559                    ULONG                   cbSubgroupOrder;        //Byte length of the subgroup.
 560                    ULONG                   cbCofactor;             //Byte length of cofactor of G in E.
 561                    ULONG                   cbSeed;                 //Byte length of the seed used to generate the curve
 562                    //P[cbFieldLength]              Prime specifying the base field.
 563                    //A[cbFieldLength]              Coefficient A of the equation y^2 = x^3 + A*x + B mod p
 564                    //B[cbFieldLength]              Coefficient B of the equation y^2 = x^3 + A*x + B mod p
 565                    //Gx[cbFieldLength]             X-coordinate of the base point.
 566                    //Gy[cbFieldLength]             Y-coordinate of the base point.
 567                    //n[cbSubgroupOrder]            Order of the group generated by G = (x,y)
 568                    //h[cbCofactor]                 Cofactor of G in E.
 569                    //S[cbSeed]                     Seed of the curve.
 570                }
 571             */
 572
 6573            var cbFieldLength = Prime.Length;
 6574            var cbSubgroupOrder = Order.Length;
 6575            var cbCofactor = Cofactor.Length;
 6576            var cbSeed = Seed.Length;
 577
 6578            writer.Write( 1 ); // version
 6579            writer.Write( ToCurveType( CurveType ) );
 6580            writer.Write( ToCurveGenerationAlgId( Hash ) );
 6581            writer.Write( cbFieldLength );
 6582            writer.Write( cbSubgroupOrder );
 6583            writer.Write( cbCofactor );
 6584            writer.Write( cbSeed );
 585
 6586            AlignAndWrite( writer, Prime, cbFieldLength, nameof( Prime ) );
 6587            AlignAndWrite( writer, A, cbFieldLength, nameof( A ) );
 6588            AlignAndWrite( writer, B, cbFieldLength, nameof( B ) );
 6589            AlignAndWrite( writer, Gx, cbFieldLength, nameof( Gx ) );
 6590            AlignAndWrite( writer, Gy, cbFieldLength, nameof( Gy ) );
 6591            AlignAndWrite( writer, Order, cbSubgroupOrder, nameof( Order ) );
 6592            AlignAndWrite( writer, Cofactor, cbCofactor, nameof( Cofactor ) );
 6593            AlignAndWrite( writer, Seed, cbSeed, nameof( Seed ) );
 6594        }
 595
 596        private static int ToCurveType( EcKeyCurveType type )
 597        {
 598            switch ( type )
 599            {
 600                case EcKeyCurveType.PrimeMontgomery:
 0601                    return NativeMethods.BCRYPT_ECC_PRIME_MONTGOMERY_CURVE;
 602
 603                case EcKeyCurveType.PrimeShortWeierstrass:
 6604                    return NativeMethods.BCRYPT_ECC_PRIME_SHORT_WEIERSTRASS_CURVE;
 605
 606                case EcKeyCurveType.PrimeTwistedEdwards:
 0607                    return NativeMethods.BCRYPT_ECC_PRIME_TWISTED_EDWARDS_CURVE;
 608
 609                default:
 0610                    throw new InvalidOperationException( $"Unsupported curve type: {type}" );
 611            }
 612        }
 613
 614        private static int ToCurveGenerationAlgId( string hash )
 615        {
 6616            if ( !string.IsNullOrWhiteSpace( hash ) )
 0617                throw new InvalidOperationException( $"Unsupported curve generation hash algorithm: {hash}" );
 6618            return NativeMethods.BCRYPT_NO_CURVE_GENERATION_ALG_ID;
 619        }
 620
 621        private static void AlignAndWrite( BinaryWriter writer, byte[] bytes, int size, string paramName )
 622        {
 48623            if ( bytes.Length >= size )
 624            {
 0625                for ( var i = 0; i < bytes.Length - size; ++i )
 0626                    if ( bytes[i] != 0 )
 0627                        throw new ArgumentException( $"Value of {paramName} is bigger than allowed for this curve." );
 628
 48629                writer.Write( bytes, bytes.Length - size, size );
 48630                return;
 631            }
 632
 0633            for ( var i = bytes.Length; i < size; ++i )
 0634                writer.Write( (byte) 0 );
 635
 0636            writer.Write( bytes );
 0637        }
 638    }
 639
 640    internal enum EcKeyCurveType
 641    {
 642        Characteristic2,
 643        Implicit,
 644        Named,
 645        PrimeMontgomery,
 646        PrimeShortWeierstrass,
 647        PrimeTwistedEdwards
 648    }
 649}