< Summary

Class:Microsoft.Azure.KeyVault.WebKey.BCRYPT_ECC_PARAMETER_HEADER
Assembly:Microsoft.Azure.KeyVault.WebKey
File(s):C:\Git\azure-sdk-for-net\sdk\keyvault\Microsoft.Azure.KeyVault.WebKey\src\ECParameters.cs
Covered lines:71
Uncovered lines:53
Coverable lines:124
Total lines:536
Line coverage:57.2% (71 of 124)
Covered branches:22
Total branches:52
Branch coverage:42.3% (22 of 52)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
get_KeySizeInBytes()-100%100%
get_CurveBlobSize()-100%100%
get_KeyBlobSize()-100%100%
ToString()-0%100%
GetBytesDesc(...)-0%0%
Equals(...)-53.57%50%
GetHashCode()-0%100%
BytesEquals(...)-60%60%
ReadFrom(...)-100%100%
WriteTo(...)-100%100%
AlignAndWrite(...)-33.33%30%

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>
 20        public string Curve { get; set; }
 21
 22        /// <summary>
 23        /// X coordinate for the Elliptic Curve point.
 24        /// </summary>
 25        public byte[] X { get; set; }
 26
 27        /// <summary>
 28        /// Y coordinate for the Elliptic Curve point.
 29        /// </summary>
 30        public byte[] Y { get; set; }
 31
 32        /// <summary>
 33        /// ECC private key.
 34        /// </summary>
 35        public byte[] D { get; set; }
 36
 37        internal ECDsaCng ToEcdsa( bool includePrivateParameters )
 38        {
 39            switch ( Curve )
 40            {
 41                case JsonWebKeyCurveName.P256:
 42                    return ToNistCurveEcdsa( includePrivateParameters ? BCRYPT_ECDSA_PRIVATE_P256_MAGIC : BCRYPT_ECDSA_P
 43
 44                case JsonWebKeyCurveName.P384:
 45                    return ToNistCurveEcdsa( includePrivateParameters ? BCRYPT_ECDSA_PRIVATE_P384_MAGIC : BCRYPT_ECDSA_P
 46
 47                case JsonWebKeyCurveName.P521:
 48                    return ToNistCurveEcdsa( includePrivateParameters ? BCRYPT_ECDSA_PRIVATE_P521_MAGIC : BCRYPT_ECDSA_P
 49
 50                case JsonWebKeyCurveName.P256K:
 51                    return ToGenericCurveEcdsa( includePrivateParameters ? BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC : BCRYPT_E
 52
 53                default:
 54                    var curveDesc = Curve == null ? "null" : $"\"{Curve}\"";
 55                    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 );
 63            var sizeOfX = sizeInBytes;
 64            var sizeOfY = sizeInBytes;
 65            var sizeOfD = includePrivateParameters ? sizeInBytes : 0;
 66
 67            var keyBlob = new byte[sizeOfdwMagic + sizeOfdwSize + sizeOfX + sizeOfY + sizeOfD];
 68
 69            using ( var writer = new BinaryWriter( new MemoryStream( keyBlob ) ) )
 70            {
 71                writer.Write( dwMagic );
 72                writer.Write( sizeInBytes );
 73                AlignAndWrite( writer, X, sizeInBytes, nameof( X ) );
 74                AlignAndWrite( writer, Y, sizeInBytes, nameof( Y ) );
 75                if ( includePrivateParameters )
 76                    AlignAndWrite( writer, D, sizeInBytes, nameof( D ) );
 77            }
 78
 79            var key = CngKey.Import( keyBlob, includePrivateParameters ? CngKeyBlobFormat.EccPrivateBlob : CngKeyBlobFor
 80            return new ECDsaCng( key );
 81        }
 82
 83        private ECDsaCng ToGenericCurveEcdsa( int dwMagic, BCRYPT_ECC_PARAMETER_HEADER curveParameters, bool includePriv
 84        {
 85            var sizeInBytes = curveParameters.KeySizeInBytes;
 86            var keyBlob = new byte[curveParameters.KeyBlobSize];
 87
 88            using ( var writer = new BinaryWriter( new MemoryStream( keyBlob ) ) )
 89            {
 90                writer.Write( dwMagic );
 91                curveParameters.WriteTo( writer );
 92
 93                AlignAndWrite( writer, X, sizeInBytes, nameof( X ) );
 94                AlignAndWrite( writer, Y, sizeInBytes, nameof( Y ) );
 95                if ( includePrivateParameters )
 96                    AlignAndWrite( writer, D, sizeInBytes, nameof( D ) );
 97            }
 98
 99            var key = CngKey.Import( keyBlob, includePrivateParameters ? CngKeyBlobFormat_EccFullPrivateBlob : CngKeyBlo
 100            return new ECDsaCng( key );
 101        }
 102
 103        private static void AlignAndWrite( BinaryWriter writer, byte[] bytes, int size, string paramName )
 104        {
 105            if ( bytes == null )
 106                throw new ArgumentException( $"Value of {paramName} is null." );
 107
 108            if ( bytes.Length >= size )
 109            {
 110                for ( var i = 0; i < bytes.Length - size; ++i )
 111                    if ( bytes[i] != 0 )
 112                        throw new ArgumentException( $"Value of {paramName} is bigger than allowed for this curve." );
 113
 114                writer.Write( bytes, bytes.Length - size, size );
 115                return;
 116            }
 117
 118            for ( var i = bytes.Length; i < size; ++i )
 119                writer.Write( (byte) 0 );
 120
 121            writer.Write( bytes );
 122        }
 123
 124        internal static ECParameters FromEcdsa( ECDsaCng ecdsa, bool includePrivateParameters )
 125        {
 126            if ( ecdsa == null )
 127                throw new ArgumentNullException( nameof( ecdsa ) );
 128
 129            var keyBlobFormat = includePrivateParameters ? CngKeyBlobFormat.EccPrivateBlob : CngKeyBlobFormat.EccPublicB
 130            var keyBlob = ecdsa.Key.Export( keyBlobFormat );
 131
 132            // If curve is generic, we need to export again to get the full blob.
 133            var dwMagic = BitConverter.ToInt32( keyBlob, 0 );
 134            switch ( dwMagic )
 135            {
 136                case BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC:
 137                    keyBlob = ecdsa.Key.Export( CngKeyBlobFormat_EccFullPublicBlob );
 138                    break;
 139                case BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC:
 140                    keyBlob = ecdsa.Key.Export( CngKeyBlobFormat_EccFullPrivateBlob );
 141                    break;
 142            }
 143
 144            var result = new ECParameters();
 145            using ( var reader = new BinaryReader( new MemoryStream( keyBlob ) ) )
 146            {
 147                dwMagic = reader.ReadInt32();
 148                switch ( dwMagic )
 149                {
 150                    case BCRYPT_ECDSA_PUBLIC_P256_MAGIC:
 151                        ThrowIfPrivateParametersNeeded( includePrivateParameters, BCRYPT_ECDSA_PRIVATE_P256_MAGIC, dwMag
 152                        ReadNistBlob( reader, 32, result, false );
 153                        result.Curve = JsonWebKeyCurveName.P256;
 154                        break;
 155
 156                    case BCRYPT_ECDSA_PRIVATE_P256_MAGIC:
 157                        ReadNistBlob( reader, 32, result, true );
 158                        result.Curve = JsonWebKeyCurveName.P256;
 159                        break;
 160
 161                    case BCRYPT_ECDSA_PUBLIC_P384_MAGIC:
 162                        ThrowIfPrivateParametersNeeded( includePrivateParameters, BCRYPT_ECDSA_PRIVATE_P384_MAGIC, dwMag
 163                        ReadNistBlob( reader, 48, result, false );
 164                        result.Curve = JsonWebKeyCurveName.P384;
 165                        break;
 166
 167                    case BCRYPT_ECDSA_PRIVATE_P384_MAGIC:
 168                        ReadNistBlob( reader, 48, result, true );
 169                        result.Curve = JsonWebKeyCurveName.P384;
 170                        break;
 171
 172                    case BCRYPT_ECDSA_PUBLIC_P521_MAGIC:
 173                        ThrowIfPrivateParametersNeeded( includePrivateParameters, BCRYPT_ECDSA_PRIVATE_P521_MAGIC, dwMag
 174                        ReadNistBlob( reader, 66, result, false );
 175                        result.Curve = JsonWebKeyCurveName.P521;
 176                        break;
 177
 178                    case BCRYPT_ECDSA_PRIVATE_P521_MAGIC:
 179                        ReadNistBlob( reader, 66, result, true );
 180                        result.Curve = JsonWebKeyCurveName.P521;
 181                        break;
 182
 183                    case BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC:
 184                        ThrowIfPrivateParametersNeeded( includePrivateParameters, BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC, dw
 185                        ReadGenericBlob( reader, 32, result, false );
 186                        break;
 187
 188                    case BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC:
 189                        ReadGenericBlob( reader, 32, result, true );
 190                        break;
 191
 192                    default:
 193                        throw new NotSupportedException( $"Unexpected CNG key blob type. Magic number: 0x{dwMagic:X}." )
 194                }
 195            }
 196
 197            return result;
 198        }
 199
 200        private static void ThrowIfPrivateParametersNeeded( bool privateParametersNeeded, int expectedMagic, int actualM
 201        {
 202            if ( privateParametersNeeded )
 203                throw new InvalidOperationException( $"CNG returned key blob without private parameters. Expected magic:
 204        }
 205
 206        private static void ReadNistBlob( BinaryReader reader, int expectedSize, ECParameters dest, bool includePrivateP
 207        {
 208            var size = reader.ReadInt32();
 209            dest.X = ValidateSize( reader.ReadBytes( size ), expectedSize, nameof( dest.X ) );
 210            dest.Y = ValidateSize( reader.ReadBytes( size ), expectedSize, nameof( dest.Y ) );
 211            if ( includePrivateParameters )
 212                dest.D = ValidateSize( reader.ReadBytes( size ), expectedSize, nameof( dest.D ) );
 213        }
 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
 229            var curve = new BCRYPT_ECC_PARAMETER_HEADER();
 230            curve.ReadFrom( reader );
 231
 232            if ( !curve.Equals( Secp256k1 ) )
 233                throw new NotSupportedException( $"Unsupported curve: {curve}" );
 234
 235            dest.Curve = JsonWebKeyCurveName.P256K;
 236
 237            var cbFieldLength = curve.Prime.Length;
 238            dest.X = ValidateSize( reader.ReadBytes( cbFieldLength ), expectedSize, nameof( dest.X ) );
 239            dest.Y = ValidateSize( reader.ReadBytes( cbFieldLength ), expectedSize, nameof( dest.Y ) );
 240            if ( includePrivateParameters )
 241                dest.D = ValidateSize( reader.ReadBytes( cbFieldLength ), expectedSize, nameof( dest.D ) );
 242        }
 243
 244        private static BCRYPT_ECC_PARAMETER_HEADER _secp256k1;
 245
 246        private static BCRYPT_ECC_PARAMETER_HEADER Secp256k1
 247        {
 248            get
 249            {
 250                if ( _secp256k1 != null )
 251                    return _secp256k1;
 252
 253                return _secp256k1 = new BCRYPT_ECC_PARAMETER_HEADER
 254                {
 255                    dwVersion = 0x1,
 256                    dwCurveType = 0x1,
 257                    dwCurveGenerationAlgId = 0x0,
 258                    Prime = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x
 259                    A = new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
 260                    B = new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
 261                    Gx = new byte[] {0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87,
 262                    Gy = new byte[] {0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC, 0xE, 0x11, 
 263                    Order = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x
 264                    Cofactor = new byte[] {0x1},
 265                    Seed = new byte[] { },
 266                };
 267            }
 268        }
 269
 270        private static byte[] ValidateSize( byte[] bytes, int expectedSize, string fieldName )
 271        {
 272            if ( bytes.Length > expectedSize )
 273                for ( var i = 0; i < bytes.Length - expectedSize; ++i )
 274                    if ( bytes[i] != 0 )
 275                        throw new InvalidOperationException( $"Key parameter {fieldName} is larger than expected." );
 276            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
 291        private static readonly CngKeyBlobFormat CngKeyBlobFormat_EccFullPublicBlob = new CngKeyBlobFormat( BCRYPT_ECCFU
 292        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
 30315        public int KeySizeInBytes => Prime.Length;
 316
 317        public int CurveBlobSize
 318        {
 319            get
 320            {
 30321                var cbFieldLength = Prime.Length;
 30322                var cbSubgroupOrder = Order.Length;
 30323                var cbCofactor = Cofactor.Length;
 30324                var cbSeed = Seed.Length;
 325
 30326                var size = 7 * sizeof( uint ) + 5 * cbFieldLength + cbSubgroupOrder + cbCofactor + cbSeed;
 327
 30328                return size;
 329            }
 330        }
 331
 332        public int KeyBlobSize
 333        {
 334            get
 335            {
 30336                var cbFieldLength = Prime.Length;
 30337                var size = sizeof( uint ) + CurveBlobSize + 3 * cbFieldLength;
 30338                return size;
 339            }
 340        }
 341
 342        public override string ToString()
 343        {
 0344            var sb = new StringBuilder();
 0345            sb.AppendLine( "{" );
 0346            sb.AppendLine( $"    {nameof( dwVersion )} = 0x{dwVersion:X}," );
 0347            sb.AppendLine( $"    {nameof( dwCurveType )} = 0x{dwCurveType:X}," );
 0348            sb.AppendLine( $"    {nameof( dwCurveGenerationAlgId )} = 0x{dwCurveGenerationAlgId:X}," );
 0349            sb.AppendLine( $"    {nameof( Prime )} = {GetBytesDesc( Prime )}," );
 0350            sb.AppendLine( $"    {nameof( A )} = {GetBytesDesc( A )}," );
 0351            sb.AppendLine( $"    {nameof( B )} = {GetBytesDesc( B )}," );
 0352            sb.AppendLine( $"    {nameof( Gx )} = {GetBytesDesc( Gx )}," );
 0353            sb.AppendLine( $"    {nameof( Gy )} = {GetBytesDesc( Gy )}," );
 0354            sb.AppendLine( $"    {nameof( Order )} = {GetBytesDesc( Order )}," );
 0355            sb.AppendLine( $"    {nameof( Cofactor )} = {GetBytesDesc( Cofactor )}," );
 0356            sb.AppendLine( $"    {nameof( Seed )} = {GetBytesDesc( Seed )}," );
 0357            sb.AppendLine( "}" );
 0358            return sb.ToString();
 359        }
 360
 361        private static string GetBytesDesc( byte[] bytes )
 362        {
 0363            if ( bytes == null )
 0364                return "null";
 365
 0366            var sb = new StringBuilder();
 0367            sb.Append( "new byte[] { " );
 0368            for ( var i = 0; i < bytes.Length; ++i )
 369            {
 0370                if ( i > 0 )
 0371                    sb.Append( ", " );
 0372                sb.Append( "0x" ).Append( bytes[i].ToString( "X" ) );
 373            }
 0374            sb.Append( " }" );
 375
 0376            return sb.ToString();
 377        }
 378
 379        public override bool Equals( object obj )
 380        {
 12381            if ( obj == this )
 0382                return true;
 383
 12384            var other = obj as BCRYPT_ECC_PARAMETER_HEADER;
 12385            if ( other == null )
 0386                return false;
 387
 12388            if ( other.dwVersion != dwVersion )
 0389                return false;
 390
 12391            if ( other.dwCurveType != dwCurveType )
 0392                return false;
 393
 12394            if ( other.dwCurveGenerationAlgId != dwCurveGenerationAlgId )
 0395                return false;
 396
 12397            if ( !BytesEquals( other.Prime, Prime ) )
 0398                return false;
 399
 12400            if ( !BytesEquals( other.A, A ) )
 0401                return false;
 402
 12403            if ( !BytesEquals( other.B, B ) )
 0404                return false;
 405
 12406            if ( !BytesEquals( other.Gx, Gx ) )
 0407                return false;
 408
 12409            if ( !BytesEquals( other.Gy, Gy ) )
 0410                return false;
 411
 12412            if ( !BytesEquals( other.Order, Order ) )
 0413                return false;
 414
 12415            if ( !BytesEquals( other.Cofactor, Cofactor ) )
 0416                return false;
 12417            if ( !BytesEquals( other.Seed, Seed ) )
 0418                return false;
 419
 12420            return true;
 421        }
 422
 423        public override int GetHashCode()
 424        {
 0425            return BCRYPT_ECC_PRIME_SHORT_WEIERSTRASS_CURVE.GetHashCode()
 0426                * BCRYPT_ECC_PRIME_TWISTED_EDWARDS_CURVE.GetHashCode()
 0427                * BCRYPT_ECC_PRIME_MONTGOMERY_CURVE.GetHashCode();
 428        }
 429
 430
 431        private static bool BytesEquals( byte[] a, byte[] b )
 432        {
 96433            if ( a == b )
 0434                return true;
 435
 96436            if ( ( a == null ) != ( b == null ) )
 0437                return false;
 438
 96439            if ( a.Length != b.Length )
 0440                return false;
 441
 4824442            for ( var i = 0; i < a.Length; ++i )
 2316443                if ( a[i] != b[i] )
 0444                    return false;
 445
 96446            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
 12472            dwVersion = reader.ReadInt32();
 12473            dwCurveType = reader.ReadInt32();
 12474            dwCurveGenerationAlgId = reader.ReadInt32();
 12475            var cbFieldLength = reader.ReadInt32();
 12476            var cbSubgroupOrder = reader.ReadInt32();
 12477            var cbCofactor = reader.ReadInt32();
 12478            var cbSeed = reader.ReadInt32();
 479
 12480            Prime = reader.ReadBytes( cbFieldLength );
 12481            A = reader.ReadBytes( cbFieldLength );
 12482            B = reader.ReadBytes( cbFieldLength );
 12483            Gx = reader.ReadBytes( cbFieldLength );
 12484            Gy = reader.ReadBytes( cbFieldLength );
 12485            Order = reader.ReadBytes( cbSubgroupOrder );
 12486            Cofactor = reader.ReadBytes( cbCofactor );
 12487            Seed = reader.ReadBytes( cbSeed );
 12488        }
 489
 490        public void WriteTo( BinaryWriter writer )
 491        {
 30492            var cbFieldLength = Prime.Length;
 30493            var cbSubgroupOrder = Order.Length;
 30494            var cbCofactor = Cofactor.Length;
 30495            var cbSeed = Seed.Length;
 496
 30497            writer.Write( dwVersion );
 30498            writer.Write( dwCurveType );
 30499            writer.Write( dwCurveGenerationAlgId );
 30500            writer.Write( cbFieldLength );
 30501            writer.Write( cbSubgroupOrder );
 30502            writer.Write( cbCofactor );
 30503            writer.Write( cbSeed );
 504
 30505            AlignAndWrite( writer, Prime, cbFieldLength, nameof( Prime ) );
 30506            AlignAndWrite( writer, A, cbFieldLength, nameof( A ) );
 30507            AlignAndWrite( writer, B, cbFieldLength, nameof( B ) );
 30508            AlignAndWrite( writer, Gx, cbFieldLength, nameof( Gx ) );
 30509            AlignAndWrite( writer, Gy, cbFieldLength, nameof( Gy ) );
 30510            AlignAndWrite( writer, Order, cbSubgroupOrder, nameof( Order ) );
 30511            AlignAndWrite( writer, Cofactor, cbCofactor, nameof( Cofactor ) );
 30512            AlignAndWrite( writer, Seed, cbSeed, nameof( Seed ) );
 30513        }
 514
 515        private static void AlignAndWrite( BinaryWriter writer, byte[] bytes, int size, string paramName )
 516        {
 240517            if ( bytes == null )
 0518                throw new ArgumentException( $"Value of {paramName} is null." );
 519
 240520            if ( bytes.Length >= size )
 521            {
 0522                for ( var i = 0; i < bytes.Length - size; ++i )
 0523                    if ( bytes[i] != 0 )
 0524                        throw new ArgumentException( $"Value of {paramName} is bigger than allowed for this curve." );
 525
 240526                writer.Write( bytes, bytes.Length - size, size );
 240527                return;
 528            }
 529
 0530            for ( var i = bytes.Length; i < size; ++i )
 0531                writer.Write( (byte) 0 );
 532
 0533            writer.Write( bytes );
 0534        }
 535    }
 536}