netstandard dependant facades update after 2.0 release (#5371)
[mono.git] / mcs / class / referencesource / System.Core / System / Security / Cryptography / ECDsaCng.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6
7 using System;
8 using System.Diagnostics;
9 using System.IO;
10 using System.Security;
11 using System.Security.Permissions;
12 using System.Diagnostics.Contracts;
13 using Microsoft.Win32.SafeHandles;
14
15 namespace System.Security.Cryptography {
16     /// <summary>
17     ///     Wrapper for NCrypt's implementation of elliptic curve DSA
18     /// </summary>
19     [System.Security.Permissions.HostProtection(MayLeakOnAbort = true)]
20     public sealed partial class ECDsaCng : ECDsa {
21 #if MONO
22         public ECDsaCng() : this(521) {
23         }
24
25         public ECDsaCng(int keySize) {
26             throw new NotImplementedException ();
27         }
28
29         [SecuritySafeCritical]
30         public ECDsaCng(CngKey key) {
31             throw new NotImplementedException ();
32         }
33
34         public ECDsaCng(ECCurve curve) {
35             throw new NotImplementedException ();
36         }
37
38         public CngAlgorithm HashAlgorithm { get; set; }
39
40         public CngKey Key {
41             get {
42                 throw new NotImplementedException ();
43             }
44
45             private set {
46                 throw new NotImplementedException ();
47             }
48         }
49
50         public override byte[] SignHash(byte[] hash) {
51             throw new NotImplementedException();
52         }
53
54         public override bool VerifyHash(byte[] hash, byte[] signature) {
55             throw new NotImplementedException();
56         }
57
58         public void FromXmlString (string xml, ECKeyXmlFormat format) {
59             throw new NotImplementedException();
60         }
61
62         public byte[] SignData (byte[] data) {
63             throw new NotImplementedException();
64         }
65
66         public byte[] SignData (System.IO.Stream data) {
67             throw new NotImplementedException();
68         }
69
70         public byte[] SignData (byte[] data, int offset, int count) {
71             throw new NotImplementedException();
72         }
73
74         public string ToXmlString (ECKeyXmlFormat format) {
75             throw new NotImplementedException();
76         }
77
78         public bool VerifyData (byte[] data, byte[] signature) {
79             throw new NotImplementedException();
80         }
81
82         public bool VerifyData (System.IO.Stream data, byte[] signature) {
83             throw new NotImplementedException();
84         }
85
86         public bool VerifyData (byte[] data, int offset, int count, byte[] signature) {
87             throw new NotImplementedException();
88         }
89 #else
90         private static KeySizes[] s_legalKeySizes = new KeySizes[] { new KeySizes(256, 384, 128), new KeySizes(521, 521, 0) };
91
92         private CngKey m_key;
93         private CngAlgorithm m_hashAlgorithm = CngAlgorithm.Sha256;
94
95         //
96         // Constructors
97         //
98
99         public ECDsaCng() : this(521) {
100             Contract.Ensures(LegalKeySizesValue != null);
101         }
102
103         public ECDsaCng(int keySize) {
104             Contract.Ensures(LegalKeySizesValue != null);
105
106             if (!NCryptNative.NCryptSupported) {
107                 throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
108             }
109
110             LegalKeySizesValue = s_legalKeySizes;
111             KeySize = keySize;
112         }
113
114         public ECDsaCng(ECCurve curve) {
115             // GenerateKey will already do all of the validation we need.
116             GenerateKey(curve);
117         }
118
119         [SecuritySafeCritical]
120         public ECDsaCng(CngKey key) {
121             Contract.Ensures(LegalKeySizesValue != null);
122             Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup));
123
124             if (key == null) {
125                 throw new ArgumentNullException("key");
126             }
127             if (!IsEccAlgorithmGroup(key.AlgorithmGroup)) {
128                 throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey), "key");
129             }
130
131             if (!NCryptNative.NCryptSupported) {
132                 throw new PlatformNotSupportedException(SR.GetString(SR.Cryptography_PlatformNotSupported));
133             }
134
135             LegalKeySizesValue = s_legalKeySizes;
136
137             // Make a copy of the key so that we continue to work if it gets disposed before this algorithm
138             //
139             // This requires an assert for UnmanagedCode since we'll need to access the raw handles of the key
140             // and the handle constructor of CngKey.  The assert is safe since ECDsaCng will never expose the
141             // key handles to calling code (without first demanding UnmanagedCode via the Handle property of
142             // CngKey).
143             //
144             // We also need to dispose of the key handle since CngKey.Handle returns a duplicate
145             new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
146             using (SafeNCryptKeyHandle keyHandle = key.Handle) {
147                 Key = CngKey.Open(keyHandle, key.IsEphemeral ? CngKeyHandleOpenOptions.EphemeralKey : CngKeyHandleOpenOptions.None);
148             }
149             CodeAccessPermission.RevertAssert();
150
151             // Our LegalKeySizes value stores the values that we encoded as being the correct
152             // legal key size limitations for this algorithm, as documented on MSDN.
153             //
154             // But on a new OS version we might not question if our limit is accurate, or MSDN
155             // could have been innacurate to start with.
156             //
157             // Since the key is already loaded, we know that Windows thought it to be valid;
158             // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
159             // check.
160             //
161             // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
162             // create a 384-bit RSA key, which we consider too small to be legal. It can also create
163             // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
164             // alignment requirement. (In both cases Windows loads it just fine)
165             KeySizeValue = m_key.KeySize;
166         }
167
168         /// <summary>
169         ///     Hash algorithm to use when generating a signature over arbitrary data
170         /// </summary>
171         public CngAlgorithm HashAlgorithm {
172             get {
173                 Contract.Ensures(Contract.Result<CngAlgorithm>() != null);
174                 return m_hashAlgorithm;
175             }
176
177             set {
178                 Contract.Ensures(m_hashAlgorithm != null);
179
180                 if (value == null) {
181                     throw new ArgumentNullException("value");
182                 }
183
184                 m_hashAlgorithm = value;
185             }
186         }
187
188         /// <summary>
189         ///     Key to use for signing
190         /// </summary>
191         public CngKey Key {
192             get {
193                 Contract.Ensures(Contract.Result<CngKey>() != null);
194                 Contract.Ensures(IsEccAlgorithmGroup(Contract.Result<CngKey>().AlgorithmGroup));
195                 Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup));
196
197                 // If the size of the key no longer matches our stored value, then we need to replace it with
198                 // a new key of the correct size.
199                 if (m_key != null && m_key.KeySize != KeySize) {
200                     m_key.Dispose();
201                     m_key = null;
202                 }
203
204                 if (m_key == null) {
205                     // Map the current key size to a CNG algorithm name
206                     CngAlgorithm algorithm = null;
207                     switch (KeySize) {
208                         case 256:
209                             algorithm = CngAlgorithm.ECDsaP256;
210                             break;
211
212                         case 384:
213                             algorithm = CngAlgorithm.ECDsaP384;
214                             break;
215
216                         case 521:
217                             algorithm = CngAlgorithm.ECDsaP521;
218                             break;
219
220                         default:
221                             Debug.Assert(false, "Illegal key size set");
222                             break;
223                     }
224
225                     m_key = CngKey.Create(algorithm);
226                 }
227
228                 return m_key;
229             }
230
231             private set {
232                 Contract.Requires(value != null);
233                 Contract.Ensures(m_key != null && IsEccAlgorithmGroup(m_key.AlgorithmGroup));
234
235                 if (!IsEccAlgorithmGroup(value.AlgorithmGroup)) {
236                     throw new ArgumentException(SR.GetString(SR.Cryptography_ArgECDsaRequiresECDsaKey));
237                 }
238
239                 if (m_key != null) {
240                     m_key.Dispose();
241                 }
242
243                 //
244                 // We do not duplicate the handle because the only time the user has access to the key itself
245                 // to dispose underneath us is when they construct via the CngKey constructor, which does a
246                 // copy. Otherwise all key lifetimes are controlled directly by the ECDsaCng class.
247                 //
248
249                 m_key = value;
250
251                 // Our LegalKeySizes value stores the values that we encoded as being the correct
252                 // legal key size limitations for this algorithm, as documented on MSDN.
253                 //
254                 // But on a new OS version we might not question if our limit is accurate, or MSDN
255                 // could have been innacurate to start with.
256                 //
257                 // Since the key is already loaded, we know that Windows thought it to be valid;
258                 // therefore we should set KeySizeValue directly to bypass the LegalKeySizes conformance
259                 // check.
260                 //
261                 // For RSA there are known cases where this change matters. RSACryptoServiceProvider can
262                 // create a 384-bit RSA key, which we consider too small to be legal. It can also create
263                 // a 1032-bit RSA key, which we consider illegal because it doesn't match our 64-bit
264                 // alignment requirement. (In both cases Windows loads it just fine)
265                 KeySizeValue = m_key.KeySize;
266             }
267         }
268
269         /// <summary>
270         ///     Clean up the algorithm
271         /// </summary>
272         protected override void Dispose(bool disposing) {
273             try {
274                 if (m_key != null) {
275                     m_key.Dispose();
276                 }
277             }
278             finally {
279                 base.Dispose(disposing);
280             }
281         }
282
283         //
284         // XML Import
285         //
286         // #ECCXMLFormat
287         //
288         // There is currently not a standard XML format for ECC keys, so we will not implement the default
289         // To/FromXmlString so that we're not tied to one format when a standard one does exist. Instead we'll
290         // use an overload which allows the user to specify the format they'd like to serialize into.
291         //
292         // See code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
293         // the currently supported format.
294         //
295
296         public override void FromXmlString(string xmlString) {
297             throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
298         }
299
300         public void FromXmlString(string xml, ECKeyXmlFormat format) {
301             if (xml == null) {
302                 throw new ArgumentNullException("xml");
303             }
304             if (format != ECKeyXmlFormat.Rfc4050) {
305                 throw new ArgumentOutOfRangeException("format");
306             }
307
308             bool isEcdh;
309             ECParameters parameters = Rfc4050KeyFormatter.FromXml(xml, out isEcdh);
310
311             // .NET 4.6.2 allowed ECDsaCng to wrap ECDH keys because of interop with non-Windows PFX files.
312             // As a result XML marked as ECDiffieHellman loaded just fine, so no check should be done on the
313             // key type.
314             ImportParameters(parameters);
315         }
316
317         //
318         // Signature generation
319         //
320
321         public byte[] SignData(byte[] data) {
322             Contract.Ensures(Contract.Result<byte[]>() != null);
323
324             if (data == null) {
325                 throw new ArgumentNullException("data");
326             }
327
328             return SignData(data, 0, data.Length);
329         }
330
331         [SecuritySafeCritical]
332         public byte[] SignData(byte[] data, int offset, int count) {
333             Contract.Ensures(Contract.Result<byte[]>() != null);
334
335             if (data == null) {
336                 throw new ArgumentNullException("data");
337             }
338             if (offset < 0 || offset > data.Length) {
339                 throw new ArgumentOutOfRangeException("offset");
340             }
341             if (count < 0 || count > data.Length - offset) {
342                 throw new ArgumentOutOfRangeException("count");
343             }
344
345             using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
346                 hashAlgorithm.HashCore(data, offset, count);
347                 byte[] hashValue = hashAlgorithm.HashFinal();
348
349                 return SignHash(hashValue);
350             }
351         }
352
353         [SecuritySafeCritical]
354         public byte[] SignData(Stream data) {
355             Contract.Ensures(Contract.Result<byte[]>() != null);
356
357             if (data == null) {
358                 throw new ArgumentNullException("data");
359             }
360
361             using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
362                 hashAlgorithm.HashStream(data);
363                 byte[] hashValue = hashAlgorithm.HashFinal();
364
365                 return SignHash(hashValue);
366             }
367         }
368
369         [SecuritySafeCritical]
370         public override byte[] SignHash(byte[] hash) {
371             if (hash == null) {
372                 throw new ArgumentNullException("hash");
373             }
374
375             // Make sure we're allowed to sign using this key
376             KeyContainerPermission permission = Key.BuildKeyContainerPermission(KeyContainerPermissionFlags.Sign);
377             if (permission != null) {
378                 permission.Demand();
379             }
380
381             // Now that know we have permission to use this key for signing, pull the key value out, which
382             // will require unmanaged code permission
383             new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
384
385             // This looks odd, but the key handle is actually a duplicate so we need to dispose it
386             using (SafeNCryptKeyHandle keyHandle = Key.Handle) {
387                 CodeAccessPermission.RevertAssert();
388
389                 return NCryptNative.SignHash(keyHandle, hash);
390             }
391         }
392
393         //
394         // XML Export
395         //
396         // See  code:System.Security.Cryptography.ECDsaCng#ECCXMLFormat and 
397         // code:System.Security.Cryptography.Rfc4050KeyFormatter#RFC4050ECKeyFormat for information about
398         // XML serialization of elliptic curve keys
399         //
400
401         public override string ToXmlString(bool includePrivateParameters) {
402             throw new NotImplementedException(SR.GetString(SR.Cryptography_ECXmlSerializationFormatRequired));
403         }
404
405         public string ToXmlString(ECKeyXmlFormat format) {
406             Contract.Ensures(Contract.Result<string>() != null);
407
408             if (format != ECKeyXmlFormat.Rfc4050) {
409                 throw new ArgumentOutOfRangeException("format");
410             }
411
412             ECParameters ecParams = ExportParameters(false);
413             return Rfc4050KeyFormatter.ToXml(ecParams, isEcdh: false);
414         }
415
416         //
417         // Signature verification
418         //
419
420         public bool VerifyData(byte[] data, byte[] signature) {
421             if (data == null) {
422                 throw new ArgumentNullException("data");
423             }
424
425             return VerifyData(data, 0, data.Length, signature);
426         }
427
428         [SecuritySafeCritical]
429         public bool VerifyData(byte[] data, int offset, int count, byte[] signature) {
430             if (data == null) {
431                 throw new ArgumentNullException("data");
432             }
433             if (offset < 0 || offset > data.Length) {
434                 throw new ArgumentOutOfRangeException("offset");
435             }
436             if (count < 0 || count > data.Length - offset) {
437                 throw new ArgumentOutOfRangeException("count");
438             }
439             if (signature == null) {
440                 throw new ArgumentNullException("signature");
441             }
442
443             using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
444                 hashAlgorithm.HashCore(data, offset, count);
445                 byte[] hashValue = hashAlgorithm.HashFinal();
446
447                 return VerifyHash(hashValue, signature);
448             }
449         }
450
451         [SecuritySafeCritical]
452         public bool VerifyData(Stream data, byte[] signature) {
453             if (data == null) {
454                 throw new ArgumentNullException("data");
455             }
456             if (signature == null) {
457                 throw new ArgumentNullException("signature");
458             }
459
460             using (BCryptHashAlgorithm hashAlgorithm = new BCryptHashAlgorithm(HashAlgorithm, BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
461                 hashAlgorithm.HashStream(data);
462                 byte[] hashValue = hashAlgorithm.HashFinal();
463
464                 return VerifyHash(hashValue, signature);
465             }
466         }
467
468         [SecuritySafeCritical]
469         public override bool VerifyHash(byte[] hash, byte[] signature) {
470             if (hash == null) {
471                 throw new ArgumentNullException("hash");
472             }
473             if (signature == null) {
474                 throw new ArgumentNullException("signature");
475             }
476
477             // We need to get the raw key handle to verify the signature. Asserting here is safe since verifiation
478             // is not a protected operation, and we do not expose the handle to the user code.
479             new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Assert();
480
481             // This looks odd, but Key.Handle is really a duplicate so we need to dispose it
482             using (SafeNCryptKeyHandle keyHandle = Key.Handle) {
483                 CodeAccessPermission.RevertAssert();
484
485                 return NCryptNative.VerifySignature(keyHandle, hash, signature);
486             }
487         }
488
489         public override void GenerateKey(ECCurve curve) {
490             curve.Validate();
491
492             if (m_key != null) {
493                 m_key.Dispose();
494                 m_key = null;
495             }
496
497             CngKey newKey = CngKey.Create(curve, name => CngKey.EcdsaCurveNameToAlgorithm(name));
498             m_key = newKey;
499             KeySizeValue = newKey.KeySize;
500         }
501
502         /// <summary>
503         ///     Helper property to get the NCrypt key handle
504         /// </summary>
505         private SafeNCryptKeyHandle KeyHandle {
506             [SecuritySafeCritical]
507             get { return Key.Handle; }
508         }
509
510         protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) {
511             // we're sealed and the base should have checked this before calling us
512             Debug.Assert(data != null);
513             Debug.Assert(offset >= 0 && offset <= data.Length);
514             Debug.Assert(count >= 0 && count <= data.Length - offset);
515             Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
516
517             using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
518                 hasher.HashCore(data, offset, count);
519                 return hasher.HashFinal();
520             }
521         }
522
523         protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) {
524             // we're sealed and the base should have checked this before calling us
525             Debug.Assert(data != null);
526             Debug.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
527
528             using (BCryptHashAlgorithm hasher = new BCryptHashAlgorithm(new CngAlgorithm(hashAlgorithm.Name), BCryptNative.ProviderName.MicrosoftPrimitiveProvider)) {
529                 hasher.HashStream(data);
530                 return hasher.HashFinal();
531             }
532         }
533
534         private static bool IsEccAlgorithmGroup(CngAlgorithmGroup algorithmGroup)
535         {
536             // Sometimes, when reading from certificates, ECDSA keys get identified as ECDH.
537             // Windows allows the ECDH keys to perform both key exchange (ECDH) and signing (ECDSA),
538             // so either value is acceptable for the ECDSA wrapper object.
539             //
540             // It is worth noting, however, that ECDSA-identified keys cannot be used for key exchange (ECDH) in CNG.
541             return algorithmGroup == CngAlgorithmGroup.ECDsa || algorithmGroup == CngAlgorithmGroup.ECDiffieHellman;
542         }
543 #endif
544     }
545 }