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