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