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