| | 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 | |
|
| | 5 | | using System; |
| | 6 | | using System.IO; |
| | 7 | | using System.Security.Cryptography; |
| | 8 | | using System.Text; |
| | 9 | |
|
| | 10 | | namespace 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> |
| 312 | 20 | | public string Curve { get; set; } |
| | 21 | |
|
| | 22 | | /// <summary> |
| | 23 | | /// X coordinate for the Elliptic Curve point. |
| | 24 | | /// </summary> |
| 312 | 25 | | public byte[] X { get; set; } |
| | 26 | |
|
| | 27 | | /// <summary> |
| | 28 | | /// Y coordinate for the Elliptic Curve point. |
| | 29 | | /// </summary> |
| 312 | 30 | | public byte[] Y { get; set; } |
| | 31 | |
|
| | 32 | | /// <summary> |
| | 33 | | /// ECC private key. |
| | 34 | | /// </summary> |
| 120 | 35 | | public byte[] D { get; set; } |
| | 36 | |
|
| | 37 | | internal ECDsaCng ToEcdsa( bool includePrivateParameters ) |
| | 38 | | { |
| 104 | 39 | | switch ( Curve ) |
| | 40 | | { |
| | 41 | | case JsonWebKeyCurveName.P256: |
| 30 | 42 | | return ToNistCurveEcdsa( includePrivateParameters ? BCRYPT_ECDSA_PRIVATE_P256_MAGIC : BCRYPT_ECDSA_P |
| | 43 | |
|
| | 44 | | case JsonWebKeyCurveName.P384: |
| 22 | 45 | | return ToNistCurveEcdsa( includePrivateParameters ? BCRYPT_ECDSA_PRIVATE_P384_MAGIC : BCRYPT_ECDSA_P |
| | 46 | |
|
| | 47 | | case JsonWebKeyCurveName.P521: |
| 22 | 48 | | return ToNistCurveEcdsa( includePrivateParameters ? BCRYPT_ECDSA_PRIVATE_P521_MAGIC : BCRYPT_ECDSA_P |
| | 49 | |
|
| | 50 | | case JsonWebKeyCurveName.P256K: |
| 30 | 51 | | return ToGenericCurveEcdsa( includePrivateParameters ? BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC : BCRYPT_E |
| | 52 | |
|
| | 53 | | default: |
| 0 | 54 | | var curveDesc = Curve == null ? "null" : $"\"{Curve}\""; |
| 0 | 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 ); |
| 74 | 63 | | var sizeOfX = sizeInBytes; |
| 74 | 64 | | var sizeOfY = sizeInBytes; |
| 74 | 65 | | var sizeOfD = includePrivateParameters ? sizeInBytes : 0; |
| | 66 | |
|
| 74 | 67 | | var keyBlob = new byte[sizeOfdwMagic + sizeOfdwSize + sizeOfX + sizeOfY + sizeOfD]; |
| | 68 | |
|
| 74 | 69 | | using ( var writer = new BinaryWriter( new MemoryStream( keyBlob ) ) ) |
| | 70 | | { |
| 74 | 71 | | writer.Write( dwMagic ); |
| 74 | 72 | | writer.Write( sizeInBytes ); |
| 74 | 73 | | AlignAndWrite( writer, X, sizeInBytes, nameof( X ) ); |
| 74 | 74 | | AlignAndWrite( writer, Y, sizeInBytes, nameof( Y ) ); |
| 74 | 75 | | if ( includePrivateParameters ) |
| 20 | 76 | | AlignAndWrite( writer, D, sizeInBytes, nameof( D ) ); |
| 74 | 77 | | } |
| | 78 | |
|
| 74 | 79 | | var key = CngKey.Import( keyBlob, includePrivateParameters ? CngKeyBlobFormat.EccPrivateBlob : CngKeyBlobFor |
| 74 | 80 | | return new ECDsaCng( key ); |
| | 81 | | } |
| | 82 | |
|
| | 83 | | private ECDsaCng ToGenericCurveEcdsa( int dwMagic, BCRYPT_ECC_PARAMETER_HEADER curveParameters, bool includePriv |
| | 84 | | { |
| 30 | 85 | | var sizeInBytes = curveParameters.KeySizeInBytes; |
| 30 | 86 | | var keyBlob = new byte[curveParameters.KeyBlobSize]; |
| | 87 | |
|
| 30 | 88 | | using ( var writer = new BinaryWriter( new MemoryStream( keyBlob ) ) ) |
| | 89 | | { |
| 30 | 90 | | writer.Write( dwMagic ); |
| 30 | 91 | | curveParameters.WriteTo( writer ); |
| | 92 | |
|
| 30 | 93 | | AlignAndWrite( writer, X, sizeInBytes, nameof( X ) ); |
| 30 | 94 | | AlignAndWrite( writer, Y, sizeInBytes, nameof( Y ) ); |
| 30 | 95 | | if ( includePrivateParameters ) |
| 8 | 96 | | AlignAndWrite( writer, D, sizeInBytes, nameof( D ) ); |
| 30 | 97 | | } |
| | 98 | |
|
| 30 | 99 | | var key = CngKey.Import( keyBlob, includePrivateParameters ? CngKeyBlobFormat_EccFullPrivateBlob : CngKeyBlo |
| 30 | 100 | | return new ECDsaCng( key ); |
| | 101 | | } |
| | 102 | |
|
| | 103 | | private static void AlignAndWrite( BinaryWriter writer, byte[] bytes, int size, string paramName ) |
| | 104 | | { |
| 236 | 105 | | if ( bytes == null ) |
| 0 | 106 | | throw new ArgumentException( $"Value of {paramName} is null." ); |
| | 107 | |
|
| 236 | 108 | | if ( bytes.Length >= size ) |
| | 109 | | { |
| 0 | 110 | | for ( var i = 0; i < bytes.Length - size; ++i ) |
| 0 | 111 | | if ( bytes[i] != 0 ) |
| 0 | 112 | | throw new ArgumentException( $"Value of {paramName} is bigger than allowed for this curve." ); |
| | 113 | |
|
| 236 | 114 | | writer.Write( bytes, bytes.Length - size, size ); |
| 236 | 115 | | return; |
| | 116 | | } |
| | 117 | |
|
| 0 | 118 | | for ( var i = bytes.Length; i < size; ++i ) |
| 0 | 119 | | writer.Write( (byte) 0 ); |
| | 120 | |
|
| 0 | 121 | | writer.Write( bytes ); |
| 0 | 122 | | } |
| | 123 | |
|
| | 124 | | internal static ECParameters FromEcdsa( ECDsaCng ecdsa, bool includePrivateParameters ) |
| | 125 | | { |
| 44 | 126 | | if ( ecdsa == null ) |
| 0 | 127 | | throw new ArgumentNullException( nameof( ecdsa ) ); |
| | 128 | |
|
| 44 | 129 | | var keyBlobFormat = includePrivateParameters ? CngKeyBlobFormat.EccPrivateBlob : CngKeyBlobFormat.EccPublicB |
| 44 | 130 | | var keyBlob = ecdsa.Key.Export( keyBlobFormat ); |
| | 131 | |
|
| | 132 | | // If curve is generic, we need to export again to get the full blob. |
| 44 | 133 | | var dwMagic = BitConverter.ToInt32( keyBlob, 0 ); |
| | 134 | | switch ( dwMagic ) |
| | 135 | | { |
| | 136 | | case BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC: |
| 10 | 137 | | keyBlob = ecdsa.Key.Export( CngKeyBlobFormat_EccFullPublicBlob ); |
| 10 | 138 | | break; |
| | 139 | | case BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC: |
| 2 | 140 | | keyBlob = ecdsa.Key.Export( CngKeyBlobFormat_EccFullPrivateBlob ); |
| | 141 | | break; |
| | 142 | | } |
| | 143 | |
|
| 44 | 144 | | var result = new ECParameters(); |
| 44 | 145 | | using ( var reader = new BinaryReader( new MemoryStream( keyBlob ) ) ) |
| | 146 | | { |
| 44 | 147 | | dwMagic = reader.ReadInt32(); |
| | 148 | | switch ( dwMagic ) |
| | 149 | | { |
| | 150 | | case BCRYPT_ECDSA_PUBLIC_P256_MAGIC: |
| 10 | 151 | | ThrowIfPrivateParametersNeeded( includePrivateParameters, BCRYPT_ECDSA_PRIVATE_P256_MAGIC, dwMag |
| 10 | 152 | | ReadNistBlob( reader, 32, result, false ); |
| 10 | 153 | | result.Curve = JsonWebKeyCurveName.P256; |
| 10 | 154 | | break; |
| | 155 | |
|
| | 156 | | case BCRYPT_ECDSA_PRIVATE_P256_MAGIC: |
| 2 | 157 | | ReadNistBlob( reader, 32, result, true ); |
| 2 | 158 | | result.Curve = JsonWebKeyCurveName.P256; |
| 2 | 159 | | break; |
| | 160 | |
|
| | 161 | | case BCRYPT_ECDSA_PUBLIC_P384_MAGIC: |
| 8 | 162 | | ThrowIfPrivateParametersNeeded( includePrivateParameters, BCRYPT_ECDSA_PRIVATE_P384_MAGIC, dwMag |
| 8 | 163 | | ReadNistBlob( reader, 48, result, false ); |
| 8 | 164 | | result.Curve = JsonWebKeyCurveName.P384; |
| 8 | 165 | | break; |
| | 166 | |
|
| | 167 | | case BCRYPT_ECDSA_PRIVATE_P384_MAGIC: |
| 2 | 168 | | ReadNistBlob( reader, 48, result, true ); |
| 2 | 169 | | result.Curve = JsonWebKeyCurveName.P384; |
| 2 | 170 | | break; |
| | 171 | |
|
| | 172 | | case BCRYPT_ECDSA_PUBLIC_P521_MAGIC: |
| 8 | 173 | | ThrowIfPrivateParametersNeeded( includePrivateParameters, BCRYPT_ECDSA_PRIVATE_P521_MAGIC, dwMag |
| 8 | 174 | | ReadNistBlob( reader, 66, result, false ); |
| 8 | 175 | | result.Curve = JsonWebKeyCurveName.P521; |
| 8 | 176 | | break; |
| | 177 | |
|
| | 178 | | case BCRYPT_ECDSA_PRIVATE_P521_MAGIC: |
| 2 | 179 | | ReadNistBlob( reader, 66, result, true ); |
| 2 | 180 | | result.Curve = JsonWebKeyCurveName.P521; |
| 2 | 181 | | break; |
| | 182 | |
|
| | 183 | | case BCRYPT_ECDSA_PUBLIC_GENERIC_MAGIC: |
| 10 | 184 | | ThrowIfPrivateParametersNeeded( includePrivateParameters, BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC, dw |
| 10 | 185 | | ReadGenericBlob( reader, 32, result, false ); |
| 10 | 186 | | break; |
| | 187 | |
|
| | 188 | | case BCRYPT_ECDSA_PRIVATE_GENERIC_MAGIC: |
| 2 | 189 | | ReadGenericBlob( reader, 32, result, true ); |
| 2 | 190 | | break; |
| | 191 | |
|
| | 192 | | default: |
| 0 | 193 | | throw new NotSupportedException( $"Unexpected CNG key blob type. Magic number: 0x{dwMagic:X}." ) |
| | 194 | | } |
| | 195 | | } |
| | 196 | |
|
| 44 | 197 | | return result; |
| | 198 | | } |
| | 199 | |
|
| | 200 | | private static void ThrowIfPrivateParametersNeeded( bool privateParametersNeeded, int expectedMagic, int actualM |
| | 201 | | { |
| 36 | 202 | | if ( privateParametersNeeded ) |
| 0 | 203 | | throw new InvalidOperationException( $"CNG returned key blob without private parameters. Expected magic: |
| 36 | 204 | | } |
| | 205 | |
|
| | 206 | | private static void ReadNistBlob( BinaryReader reader, int expectedSize, ECParameters dest, bool includePrivateP |
| | 207 | | { |
| 32 | 208 | | var size = reader.ReadInt32(); |
| 32 | 209 | | dest.X = ValidateSize( reader.ReadBytes( size ), expectedSize, nameof( dest.X ) ); |
| 32 | 210 | | dest.Y = ValidateSize( reader.ReadBytes( size ), expectedSize, nameof( dest.Y ) ); |
| 32 | 211 | | if ( includePrivateParameters ) |
| 6 | 212 | | dest.D = ValidateSize( reader.ReadBytes( size ), expectedSize, nameof( dest.D ) ); |
| 32 | 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 | |
|
| 12 | 229 | | var curve = new BCRYPT_ECC_PARAMETER_HEADER(); |
| 12 | 230 | | curve.ReadFrom( reader ); |
| | 231 | |
|
| 12 | 232 | | if ( !curve.Equals( Secp256k1 ) ) |
| 0 | 233 | | throw new NotSupportedException( $"Unsupported curve: {curve}" ); |
| | 234 | |
|
| 12 | 235 | | dest.Curve = JsonWebKeyCurveName.P256K; |
| | 236 | |
|
| 12 | 237 | | var cbFieldLength = curve.Prime.Length; |
| 12 | 238 | | dest.X = ValidateSize( reader.ReadBytes( cbFieldLength ), expectedSize, nameof( dest.X ) ); |
| 12 | 239 | | dest.Y = ValidateSize( reader.ReadBytes( cbFieldLength ), expectedSize, nameof( dest.Y ) ); |
| 12 | 240 | | if ( includePrivateParameters ) |
| 2 | 241 | | dest.D = ValidateSize( reader.ReadBytes( cbFieldLength ), expectedSize, nameof( dest.D ) ); |
| 12 | 242 | | } |
| | 243 | |
|
| | 244 | | private static BCRYPT_ECC_PARAMETER_HEADER _secp256k1; |
| | 245 | |
|
| | 246 | | private static BCRYPT_ECC_PARAMETER_HEADER Secp256k1 |
| | 247 | | { |
| | 248 | | get |
| | 249 | | { |
| 42 | 250 | | if ( _secp256k1 != null ) |
| 38 | 251 | | return _secp256k1; |
| | 252 | |
|
| 4 | 253 | | return _secp256k1 = new BCRYPT_ECC_PARAMETER_HEADER |
| 4 | 254 | | { |
| 4 | 255 | | dwVersion = 0x1, |
| 4 | 256 | | dwCurveType = 0x1, |
| 4 | 257 | | dwCurveGenerationAlgId = 0x0, |
| 4 | 258 | | Prime = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x |
| 4 | 259 | | A = new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, |
| 4 | 260 | | B = new byte[] {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, |
| 4 | 261 | | Gx = new byte[] {0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC, 0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, |
| 4 | 262 | | Gy = new byte[] {0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65, 0x5D, 0xA4, 0xFB, 0xFC, 0xE, 0x11, |
| 4 | 263 | | Order = new byte[] {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x |
| 4 | 264 | | Cofactor = new byte[] {0x1}, |
| 4 | 265 | | Seed = new byte[] { }, |
| 4 | 266 | | }; |
| | 267 | | } |
| | 268 | | } |
| | 269 | |
|
| | 270 | | private static byte[] ValidateSize( byte[] bytes, int expectedSize, string fieldName ) |
| | 271 | | { |
| 96 | 272 | | if ( bytes.Length > expectedSize ) |
| 0 | 273 | | for ( var i = 0; i < bytes.Length - expectedSize; ++i ) |
| 0 | 274 | | if ( bytes[i] != 0 ) |
| 0 | 275 | | throw new InvalidOperationException( $"Key parameter {fieldName} is larger than expected." ); |
| 96 | 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 | |
|
| 4 | 291 | | private static readonly CngKeyBlobFormat CngKeyBlobFormat_EccFullPublicBlob = new CngKeyBlobFormat( BCRYPT_ECCFU |
| 4 | 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 | |
|
| | 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 | | } |