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