< Summary

Class:Microsoft.Azure.KeyVault.WebKey.ECParameters
Assembly:Microsoft.Azure.KeyVault.WebKey
File(s):C:\Git\azure-sdk-for-net\sdk\keyvault\Microsoft.Azure.KeyVault.WebKey\src\ECParameters.cs
Covered lines:114
Uncovered lines:17
Coverable lines:131
Total lines:536
Line coverage:87% (114 of 131)
Covered branches:63
Total branches:84
Branch coverage:75% (63 of 84)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
get_Curve()-100%100%
get_X()-100%100%
get_Y()-100%100%
get_D()-100%100%
ToEcdsa(...)-71.43%83.33%
ToNistCurveEcdsa(...)-100%100%
ToGenericCurveEcdsa(...)-100%100%
AlignAndWrite(...)-33.33%30%
FromEcdsa(...)-94.87%83.33%
ThrowIfPrivateParametersNeeded(...)-66.67%50%
ReadNistBlob(...)-100%100%
ReadGenericBlob(...)-90.91%75%
get_Secp256k1()-100%100%
ValidateSize(...)-40%33.33%
.cctor()-100%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\keyvault\Microsoft.Azure.KeyVault.WebKey\src\ECParameters.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;
 9
 10namespace Microsoft.Azure.KeyVault.WebKey
 11{
 12    /// <summary>
 13    /// EC parameters class.
 14    /// </summary>
 15    public class ECParameters
 16    {
 17        /// <summary>
 18        /// Name of this curve.
 19        /// </summary>
 31220        public string Curve { get; set; }
 21
 22        /// <summary>
 23        /// X coordinate for the Elliptic Curve point.
 24        /// </summary>
 31225        public byte[] X { get; set; }
 26
 27        /// <summary>
 28        /// Y coordinate for the Elliptic Curve point.
 29        /// </summary>
 31230        public byte[] Y { get; set; }
 31
 32        /// <summary>
 33        /// ECC private key.
 34        /// </summary>
 12035        public byte[] D { get; set; }
 36
 37        internal ECDsaCng ToEcdsa( bool includePrivateParameters )
 38        {
 10439            switch ( Curve )
 40            {
 41                case JsonWebKeyCurveName.P256:
 3042                    return ToNistCurveEcdsa( includePrivateParameters ? BCRYPT_ECDSA_PRIVATE_P256_MAGIC : BCRYPT_ECDSA_P
 43
 44                case JsonWebKeyCurveName.P384:
 2245                    return ToNistCurveEcdsa( includePrivateParameters ? BCRYPT_ECDSA_PRIVATE_P384_MAGIC : BCRYPT_ECDSA_P
 46
 47                case JsonWebKeyCurveName.P521:
 2248                    return ToNistCurveEcdsa( includePrivateParameters ? BCRYPT_ECDSA_PRIVATE_P521_MAGIC : BCRYPT_ECDSA_P
 49
 50                case JsonWebKeyCurveName.P256K:
 3051                    return ToGenericCurveEcdsa( includePrivateParameters ? BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC : BCRYPT_E
 52
 53                default:
 054                    var curveDesc = Curve == null ? "null" : $"\"{Curve}\"";
 055                    throw new InvalidOperationException( $"Invalid curve: {curveDesc}" );
 56            }
 57        }
 58
 59        private ECDsaCng ToNistCurveEcdsa( int dwMagic, int sizeInBytes, bool includePrivateParameters )
 60        {
 61            const int sizeOfdwMagic = sizeof( uint );
 62            const int sizeOfdwSize = sizeof( uint );
 7463            var sizeOfX = sizeInBytes;
 7464            var sizeOfY = sizeInBytes;
 7465            var sizeOfD = includePrivateParameters ? sizeInBytes : 0;
 66
 7467            var keyBlob = new byte[sizeOfdwMagic + sizeOfdwSize + sizeOfX + sizeOfY + sizeOfD];
 68
 7469            using ( var writer = new BinaryWriter( new MemoryStream( keyBlob ) ) )
 70            {
 7471                writer.Write( dwMagic );
 7472                writer.Write( sizeInBytes );
 7473                AlignAndWrite( writer, X, sizeInBytes, nameof( X ) );
 7474                AlignAndWrite( writer, Y, sizeInBytes, nameof( Y ) );
 7475                if ( includePrivateParameters )
 2076                    AlignAndWrite( writer, D, sizeInBytes, nameof( D ) );
 7477            }
 78
 7479            var key = CngKey.Import( keyBlob, includePrivateParameters ? CngKeyBlobFormat.EccPrivateBlob : CngKeyBlobFor
 7480            return new ECDsaCng( key );
 81        }
 82
 83        private ECDsaCng ToGenericCurveEcdsa( int dwMagic, BCRYPT_ECC_PARAMETER_HEADER curveParameters, bool includePriv
 84        {
 3085            var sizeInBytes = curveParameters.KeySizeInBytes;
 3086            var keyBlob = new byte[curveParameters.KeyBlobSize];
 87
 3088            using ( var writer = new BinaryWriter( new MemoryStream( keyBlob ) ) )
 89            {
 3090                writer.Write( dwMagic );
 3091                curveParameters.WriteTo( writer );
 92
 3093                AlignAndWrite( writer, X, sizeInBytes, nameof( X ) );
 3094                AlignAndWrite( writer, Y, sizeInBytes, nameof( Y ) );
 3095                if ( includePrivateParameters )
 896                    AlignAndWrite( writer, D, sizeInBytes, nameof( D ) );
 3097            }
 98
 3099            var key = CngKey.Import( keyBlob, includePrivateParameters ? CngKeyBlobFormat_EccFullPrivateBlob : CngKeyBlo
 30100            return new ECDsaCng( key );
 101        }
 102
 103        private static void AlignAndWrite( BinaryWriter writer, byte[] bytes, int size, string paramName )
 104        {
 236105            if ( bytes == null )
 0106                throw new ArgumentException( $"Value of {paramName} is null." );
 107
 236108            if ( bytes.Length >= size )
 109            {
 0110                for ( var i = 0; i < bytes.Length - size; ++i )
 0111                    if ( bytes[i] != 0 )
 0112                        throw new ArgumentException( $"Value of {paramName} is bigger than allowed for this curve." );
 113
 236114                writer.Write( bytes, bytes.Length - size, size );
 236115                return;
 116            }
 117
 0118            for ( var i = bytes.Length; i < size; ++i )
 0119                writer.Write( (byte) 0 );
 120
 0121            writer.Write( bytes );
 0122        }
 123
 124        internal static ECParameters FromEcdsa( ECDsaCng ecdsa, bool includePrivateParameters )
 125        {
 44126            if ( ecdsa == null )
 0127                throw new ArgumentNullException( nameof( ecdsa ) );
 128
 44129            var keyBlobFormat = includePrivateParameters ? CngKeyBlobFormat.EccPrivateBlob : CngKeyBlobFormat.EccPublicB
 44130            var keyBlob = ecdsa.Key.Export( keyBlobFormat );
 131
 132            // If curve is generic, we need to export again to get the full blob.
 44133            var dwMagic = BitConverter.ToInt32( keyBlob, 0 );
 134            switch ( dwMagic )
 135            {
 136                case BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC:
 10137                    keyBlob = ecdsa.Key.Export( CngKeyBlobFormat_EccFullPublicBlob );
 10138                    break;
 139                case BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC:
 2140                    keyBlob = ecdsa.Key.Export( CngKeyBlobFormat_EccFullPrivateBlob );
 141                    break;
 142            }
 143
 44144            var result = new ECParameters();
 44145            using ( var reader = new BinaryReader( new MemoryStream( keyBlob ) ) )
 146            {
 44147                dwMagic = reader.ReadInt32();
 148                switch ( dwMagic )
 149                {
 150                    case BCRYPT_ECDSA_PUBLIC_P256_MAGIC:
 10151                        ThrowIfPrivateParametersNeeded( includePrivateParameters, BCRYPT_ECDSA_PRIVATE_P256_MAGIC, dwMag
 10152                        ReadNistBlob( reader, 32, result, false );
 10153                        result.Curve = JsonWebKeyCurveName.P256;
 10154                        break;
 155
 156                    case BCRYPT_ECDSA_PRIVATE_P256_MAGIC:
 2157                        ReadNistBlob( reader, 32, result, true );
 2158                        result.Curve = JsonWebKeyCurveName.P256;
 2159                        break;
 160
 161                    case BCRYPT_ECDSA_PUBLIC_P384_MAGIC:
 8162                        ThrowIfPrivateParametersNeeded( includePrivateParameters, BCRYPT_ECDSA_PRIVATE_P384_MAGIC, dwMag
 8163                        ReadNistBlob( reader, 48, result, false );
 8164                        result.Curve = JsonWebKeyCurveName.P384;
 8165                        break;
 166
 167                    case BCRYPT_ECDSA_PRIVATE_P384_MAGIC:
 2168                        ReadNistBlob( reader, 48, result, true );
 2169                        result.Curve = JsonWebKeyCurveName.P384;
 2170                        break;
 171
 172                    case BCRYPT_ECDSA_PUBLIC_P521_MAGIC:
 8173                        ThrowIfPrivateParametersNeeded( includePrivateParameters, BCRYPT_ECDSA_PRIVATE_P521_MAGIC, dwMag
 8174                        ReadNistBlob( reader, 66, result, false );
 8175                        result.Curve = JsonWebKeyCurveName.P521;
 8176                        break;
 177
 178                    case BCRYPT_ECDSA_PRIVATE_P521_MAGIC:
 2179                        ReadNistBlob( reader, 66, result, true );
 2180                        result.Curve = JsonWebKeyCurveName.P521;
 2181                        break;
 182
 183                    case BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC:
 10184                        ThrowIfPrivateParametersNeeded( includePrivateParameters, BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC, dw
 10185                        ReadGenericBlob( reader, 32, result, false );
 10186                        break;
 187
 188                    case BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC:
 2189                        ReadGenericBlob( reader, 32, result, true );
 2190                        break;
 191
 192                    default:
 0193                        throw new NotSupportedException( $"Unexpected CNG key blob type. Magic number: 0x{dwMagic:X}." )
 194                }
 195            }
 196
 44197            return result;
 198        }
 199
 200        private static void ThrowIfPrivateParametersNeeded( bool privateParametersNeeded, int expectedMagic, int actualM
 201        {
 36202            if ( privateParametersNeeded )
 0203                throw new InvalidOperationException( $"CNG returned key blob without private parameters. Expected magic:
 36204        }
 205
 206        private static void ReadNistBlob( BinaryReader reader, int expectedSize, ECParameters dest, bool includePrivateP
 207        {
 32208            var size = reader.ReadInt32();
 32209            dest.X = ValidateSize( reader.ReadBytes( size ), expectedSize, nameof( dest.X ) );
 32210            dest.Y = ValidateSize( reader.ReadBytes( size ), expectedSize, nameof( dest.Y ) );
 32211            if ( includePrivateParameters )
 6212                dest.D = ValidateSize( reader.ReadBytes( size ), expectedSize, nameof( dest.D ) );
 32213        }
 214
 215        private static void ReadGenericBlob( BinaryReader reader, int expectedSize, ECParameters dest, bool includePriva
 216        {
 217            /*  struct BCRYPT_ECCFULLKEY_BLOB
 218                {
 219                    ULONG dwMagic;              // BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC or BCRYPT_ECDSA_PRIVATE_GENERIC_MAG
 220                    BCRYPT_ECC_PARAMETER_HEADER curveParameters;
 221                    BYTE Qx[cbFieldLength]      // X-coordinate of the public point.
 222                    BYTE Qy[cbFieldLength]      // Y-coordinate of the public point.
 223                    BYTE d[cbSubgroupOrder]     // Private key. Zero if only public key is required.
 224                }
 225            */
 226
 227            // The magic was read before.
 228
 12229            var curve = new BCRYPT_ECC_PARAMETER_HEADER();
 12230            curve.ReadFrom( reader );
 231
 12232            if ( !curve.Equals( Secp256k1 ) )
 0233                throw new NotSupportedException( $"Unsupported curve: {curve}" );
 234
 12235            dest.Curve = JsonWebKeyCurveName.P256K;
 236
 12237            var cbFieldLength = curve.Prime.Length;
 12238            dest.X = ValidateSize( reader.ReadBytes( cbFieldLength ), expectedSize, nameof( dest.X ) );
 12239            dest.Y = ValidateSize( reader.ReadBytes( cbFieldLength ), expectedSize, nameof( dest.Y ) );
 12240            if ( includePrivateParameters )
 2241                dest.D = ValidateSize( reader.ReadBytes( cbFieldLength ), expectedSize, nameof( dest.D ) );
 12242        }
 243
 244        private static BCRYPT_ECC_PARAMETER_HEADER _secp256k1;
 245
 246        private static BCRYPT_ECC_PARAMETER_HEADER Secp256k1
 247        {
 248            get
 249            {
 42250                if ( _secp256k1 != null )
 38251                    return _secp256k1;
 252
 4253                return _secp256k1 = new BCRYPT_ECC_PARAMETER_HEADER
 4254                {
 4255                    dwVersion = 0x1,
 4256                    dwCurveType = 0x1,
 4257                    dwCurveGenerationAlgId = 0x0,
 4258                    Prime = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x
 4259                    A = new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
 4260                    B = new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
 4261                    Gx = new byte[] {0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87,
 4262                    Gy = new byte[] {0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC, 0xE, 0x11, 
 4263                    Order = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x
 4264                    Cofactor = new byte[] {0x1},
 4265                    Seed = new byte[] { },
 4266                };
 267            }
 268        }
 269
 270        private static byte[] ValidateSize( byte[] bytes, int expectedSize, string fieldName )
 271        {
 96272            if ( bytes.Length > expectedSize )
 0273                for ( var i = 0; i < bytes.Length - expectedSize; ++i )
 0274                    if ( bytes[i] != 0 )
 0275                        throw new InvalidOperationException( $"Key parameter {fieldName} is larger than expected." );
 96276            return bytes;
 277        }
 278
 279        internal const int BCRYPT_ECDSA_PUBLIC_P256_MAGIC = 0x31534345;
 280        internal const int BCRYPT_ECDSA_PRIVATE_P256_MAGIC = 0x32534345;
 281        internal const int BCRYPT_ECDSA_PUBLIC_P384_MAGIC = 0x33534345;
 282        internal const int BCRYPT_ECDSA_PRIVATE_P384_MAGIC = 0x34534345;
 283        internal const int BCRYPT_ECDSA_PUBLIC_P521_MAGIC = 0x35534345;
 284        internal const int BCRYPT_ECDSA_PRIVATE_P521_MAGIC = 0x36534345;
 285        internal const int BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC = 0x50444345;
 286        internal const int BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC = 0x56444345;
 287
 288        internal const string BCRYPT_ECCFULLPUBLIC_BLOB = "ECCFULLPUBLICBLOB";
 289        internal const string BCRYPT_ECCFULLPRIVATE_BLOB = "ECCFULLPRIVATEBLOB";
 290
 4291        private static readonly CngKeyBlobFormat CngKeyBlobFormat_EccFullPublicBlob = new CngKeyBlobFormat( BCRYPT_ECCFU
 4292        private static readonly CngKeyBlobFormat CngKeyBlobFormat_EccFullPrivateBlob = new CngKeyBlobFormat( BCRYPT_ECCF
 293    }
 294
 295    internal sealed class BCRYPT_ECC_PARAMETER_HEADER
 296    {
 297        internal const int BCRYPT_ECC_PRIME_SHORT_WEIERSTRASS_CURVE = 0x1;
 298        internal const int BCRYPT_ECC_PRIME_TWISTED_EDWARDS_CURVE = 0x2;
 299        internal const int BCRYPT_ECC_PRIME_MONTGOMERY_CURVE = 0x3;
 300
 301        internal const int BCRYPT_NO_CURVE_GENERATION_ALG_ID = 0x0;
 302
 303        public int dwVersion;
 304        public int dwCurveType;
 305        public int dwCurveGenerationAlgId;
 306        public byte[] Prime;
 307        public byte[] A;
 308        public byte[] B;
 309        public byte[] Gx;
 310        public byte[] Gy;
 311        public byte[] Order;
 312        public byte[] Cofactor;
 313        public byte[] Seed;
 314
 315        public int KeySizeInBytes => Prime.Length;
 316
 317        public int CurveBlobSize
 318        {
 319            get
 320            {
 321                var cbFieldLength = Prime.Length;
 322                var cbSubgroupOrder = Order.Length;
 323                var cbCofactor = Cofactor.Length;
 324                var cbSeed = Seed.Length;
 325
 326                var size = 7 * sizeof( uint ) + 5 * cbFieldLength + cbSubgroupOrder + cbCofactor + cbSeed;
 327
 328                return size;
 329            }
 330        }
 331
 332        public int KeyBlobSize
 333        {
 334            get
 335            {
 336                var cbFieldLength = Prime.Length;
 337                var size = sizeof( uint ) + CurveBlobSize + 3 * cbFieldLength;
 338                return size;
 339            }
 340        }
 341
 342        public override string ToString()
 343        {
 344            var sb = new StringBuilder();
 345            sb.AppendLine( "{" );
 346            sb.AppendLine( $"    {nameof( dwVersion )} = 0x{dwVersion:X}," );
 347            sb.AppendLine( $"    {nameof( dwCurveType )} = 0x{dwCurveType:X}," );
 348            sb.AppendLine( $"    {nameof( dwCurveGenerationAlgId )} = 0x{dwCurveGenerationAlgId:X}," );
 349            sb.AppendLine( $"    {nameof( Prime )} = {GetBytesDesc( Prime )}," );
 350            sb.AppendLine( $"    {nameof( A )} = {GetBytesDesc( A )}," );
 351            sb.AppendLine( $"    {nameof( B )} = {GetBytesDesc( B )}," );
 352            sb.AppendLine( $"    {nameof( Gx )} = {GetBytesDesc( Gx )}," );
 353            sb.AppendLine( $"    {nameof( Gy )} = {GetBytesDesc( Gy )}," );
 354            sb.AppendLine( $"    {nameof( Order )} = {GetBytesDesc( Order )}," );
 355            sb.AppendLine( $"    {nameof( Cofactor )} = {GetBytesDesc( Cofactor )}," );
 356            sb.AppendLine( $"    {nameof( Seed )} = {GetBytesDesc( Seed )}," );
 357            sb.AppendLine( "}" );
 358            return sb.ToString();
 359        }
 360
 361        private static string GetBytesDesc( byte[] bytes )
 362        {
 363            if ( bytes == null )
 364                return "null";
 365
 366            var sb = new StringBuilder();
 367            sb.Append( "new byte[] { " );
 368            for ( var i = 0; i < bytes.Length; ++i )
 369            {
 370                if ( i > 0 )
 371                    sb.Append( ", " );
 372                sb.Append( "0x" ).Append( bytes[i].ToString( "X" ) );
 373            }
 374            sb.Append( " }" );
 375
 376            return sb.ToString();
 377        }
 378
 379        public override bool Equals( object obj )
 380        {
 381            if ( obj == this )
 382                return true;
 383
 384            var other = obj as BCRYPT_ECC_PARAMETER_HEADER;
 385            if ( other == null )
 386                return false;
 387
 388            if ( other.dwVersion != dwVersion )
 389                return false;
 390
 391            if ( other.dwCurveType != dwCurveType )
 392                return false;
 393
 394            if ( other.dwCurveGenerationAlgId != dwCurveGenerationAlgId )
 395                return false;
 396
 397            if ( !BytesEquals( other.Prime, Prime ) )
 398                return false;
 399
 400            if ( !BytesEquals( other.A, A ) )
 401                return false;
 402
 403            if ( !BytesEquals( other.B, B ) )
 404                return false;
 405
 406            if ( !BytesEquals( other.Gx, Gx ) )
 407                return false;
 408
 409            if ( !BytesEquals( other.Gy, Gy ) )
 410                return false;
 411
 412            if ( !BytesEquals( other.Order, Order ) )
 413                return false;
 414
 415            if ( !BytesEquals( other.Cofactor, Cofactor ) )
 416                return false;
 417            if ( !BytesEquals( other.Seed, Seed ) )
 418                return false;
 419
 420            return true;
 421        }
 422
 423        public override int GetHashCode()
 424        {
 425            return BCRYPT_ECC_PRIME_SHORT_WEIERSTRASS_CURVE.GetHashCode()
 426                * BCRYPT_ECC_PRIME_TWISTED_EDWARDS_CURVE.GetHashCode()
 427                * BCRYPT_ECC_PRIME_MONTGOMERY_CURVE.GetHashCode();
 428        }
 429
 430
 431        private static bool BytesEquals( byte[] a, byte[] b )
 432        {
 433            if ( a == b )
 434                return true;
 435
 436            if ( ( a == null ) != ( b == null ) )
 437                return false;
 438
 439            if ( a.Length != b.Length )
 440                return false;
 441
 442            for ( var i = 0; i < a.Length; ++i )
 443                if ( a[i] != b[i] )
 444                    return false;
 445
 446            return true;
 447        }
 448
 449        public void ReadFrom( BinaryReader reader )
 450        {
 451            /*
 452                struct BCRYPT_ECC_PARAMETER_HEADER
 453                {
 454                    ULONG                   dwVersion;              //Version of the structure
 455                    ECC_CURVE_TYPE_ENUM     dwCurveType;            //Supported curve types.
 456                    ECC_CURVE_ALG_ID_ENUM   dwCurveGenerationAlgId; //For X.592 verification purposes, if we include See
 457                    ULONG                   cbFieldLength;          //Byte length of the fields P, A, B, X, Y.
 458                    ULONG                   cbSubgroupOrder;        //Byte length of the subgroup.
 459                    ULONG                   cbCofactor;             //Byte length of cofactor of G in E.
 460                    ULONG                   cbSeed;                 //Byte length of the seed used to generate the curve
 461                    //P[cbFieldLength]              Prime specifying the base field.
 462                    //A[cbFieldLength]              Coefficient A of the equation y^2 = x^3 + A*x + B mod p
 463                    //B[cbFieldLength]              Coefficient B of the equation y^2 = x^3 + A*x + B mod p
 464                    //Gx[cbFieldLength]             X-coordinate of the base point.
 465                    //Gy[cbFieldLength]             Y-coordinate of the base point.
 466                    //n[cbSubgroupOrder]            Order of the group generated by G = (x,y)
 467                    //h[cbCofactor]                 Cofactor of G in E.
 468                    //S[cbSeed]                     Seed of the curve.
 469                }
 470             */
 471
 472            dwVersion = reader.ReadInt32();
 473            dwCurveType = reader.ReadInt32();
 474            dwCurveGenerationAlgId = reader.ReadInt32();
 475            var cbFieldLength = reader.ReadInt32();
 476            var cbSubgroupOrder = reader.ReadInt32();
 477            var cbCofactor = reader.ReadInt32();
 478            var cbSeed = reader.ReadInt32();
 479
 480            Prime = reader.ReadBytes( cbFieldLength );
 481            A = reader.ReadBytes( cbFieldLength );
 482            B = reader.ReadBytes( cbFieldLength );
 483            Gx = reader.ReadBytes( cbFieldLength );
 484            Gy = reader.ReadBytes( cbFieldLength );
 485            Order = reader.ReadBytes( cbSubgroupOrder );
 486            Cofactor = reader.ReadBytes( cbCofactor );
 487            Seed = reader.ReadBytes( cbSeed );
 488        }
 489
 490        public void WriteTo( BinaryWriter writer )
 491        {
 492            var cbFieldLength = Prime.Length;
 493            var cbSubgroupOrder = Order.Length;
 494            var cbCofactor = Cofactor.Length;
 495            var cbSeed = Seed.Length;
 496
 497            writer.Write( dwVersion );
 498            writer.Write( dwCurveType );
 499            writer.Write( dwCurveGenerationAlgId );
 500            writer.Write( cbFieldLength );
 501            writer.Write( cbSubgroupOrder );
 502            writer.Write( cbCofactor );
 503            writer.Write( cbSeed );
 504
 505            AlignAndWrite( writer, Prime, cbFieldLength, nameof( Prime ) );
 506            AlignAndWrite( writer, A, cbFieldLength, nameof( A ) );
 507            AlignAndWrite( writer, B, cbFieldLength, nameof( B ) );
 508            AlignAndWrite( writer, Gx, cbFieldLength, nameof( Gx ) );
 509            AlignAndWrite( writer, Gy, cbFieldLength, nameof( Gy ) );
 510            AlignAndWrite( writer, Order, cbSubgroupOrder, nameof( Order ) );
 511            AlignAndWrite( writer, Cofactor, cbCofactor, nameof( Cofactor ) );
 512            AlignAndWrite( writer, Seed, cbSeed, nameof( Seed ) );
 513        }
 514
 515        private static void AlignAndWrite( BinaryWriter writer, byte[] bytes, int size, string paramName )
 516        {
 517            if ( bytes == null )
 518                throw new ArgumentException( $"Value of {paramName} is null." );
 519
 520            if ( bytes.Length >= size )
 521            {
 522                for ( var i = 0; i < bytes.Length - size; ++i )
 523                    if ( bytes[i] != 0 )
 524                        throw new ArgumentException( $"Value of {paramName} is bigger than allowed for this curve." );
 525
 526                writer.Write( bytes, bytes.Length - size, size );
 527                return;
 528            }
 529
 530            for ( var i = bytes.Length; i < size; ++i )
 531                writer.Write( (byte) 0 );
 532
 533            writer.Write( bytes );
 534        }
 535    }
 536}