Merge pull request #3213 from henricm/fix-for-win-securestring-to-bstr
[mono.git] / mcs / class / referencesource / mscorlib / system / security / cryptography / dsacryptoserviceprovider.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>[....]</OWNER>
7 // 
8
9 //
10 // DSACryptoServiceProvider.cs
11 //
12 // CSP-based implementation of DSA
13 //
14
15 namespace System.Security.Cryptography {
16     using System;
17     using System.IO;
18     using System.Security.Cryptography.X509Certificates;
19     using System.Security.Permissions;
20     using System.Globalization;
21     using System.Runtime.Versioning;
22     using System.Diagnostics.Contracts;
23
24     // Object layout of the DSAParameters structure
25     internal class DSACspObject {
26         internal byte[] P;
27         internal byte[] Q;
28         internal byte[] G;
29         internal byte[] Y;
30         internal byte[] J;
31         internal byte[] X;
32         internal byte[] Seed;
33         internal int Counter;
34     }
35
36     [System.Runtime.InteropServices.ComVisible(true)]
37     public sealed class DSACryptoServiceProvider : DSA, ICspAsymmetricAlgorithm {
38         private int _dwKeySize;
39         private CspParameters _parameters;
40         private bool _randomKeyContainer;
41         [System.Security.SecurityCritical] // auto-generated
42         private SafeProvHandle _safeProvHandle;
43         [System.Security.SecurityCritical] // auto-generated
44         private SafeKeyHandle _safeKeyHandle;
45         private SHA1CryptoServiceProvider _sha1;
46
47         private static volatile CspProviderFlags s_UseMachineKeyStore = 0;
48
49         //
50         // public constructors
51         //
52
53         [ResourceExposure(ResourceScope.None)]
54         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
55         public DSACryptoServiceProvider() 
56             : this(0, new CspParameters(Constants.PROV_DSS_DH, null, null, s_UseMachineKeyStore)) {
57         }
58
59         [ResourceExposure(ResourceScope.None)]
60         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
61         public DSACryptoServiceProvider(int dwKeySize)
62             : this(dwKeySize, new CspParameters(Constants.PROV_DSS_DH, null, null, s_UseMachineKeyStore)) {
63         }
64
65         public DSACryptoServiceProvider(CspParameters parameters) 
66             : this(0, parameters) {
67         }
68
69         [System.Security.SecuritySafeCritical]  // auto-generated
70         public DSACryptoServiceProvider(int dwKeySize, CspParameters parameters) {
71             if (dwKeySize < 0)
72                 throw new ArgumentOutOfRangeException("dwKeySize", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
73             Contract.EndContractBlock();
74
75             _parameters = Utils.SaveCspParameters(CspAlgorithmType.Dss, parameters, s_UseMachineKeyStore, ref _randomKeyContainer);
76             LegalKeySizesValue = new KeySizes[] { new KeySizes(512, 1024, 64) }; // per the DSS spec
77             _dwKeySize = dwKeySize;
78             _sha1 = new SHA1CryptoServiceProvider();
79
80             // If this is not a random container we generate, create it eagerly 
81             // in the constructor so we can report any errors now.
82             if (!_randomKeyContainer || Environment.GetCompatibilityFlag(CompatibilityFlag.EagerlyGenerateRandomAsymmKeys))
83                 GetKeyPair();
84         }
85
86         //
87         // private methods
88         //
89
90         [System.Security.SecurityCritical]  // auto-generated
91         private void GetKeyPair () {
92             if (_safeKeyHandle == null) {
93                 lock (this) {
94                     if (_safeKeyHandle == null)
95                         Utils.GetKeyPairHelper(CspAlgorithmType.Dss, _parameters, _randomKeyContainer, _dwKeySize, ref _safeProvHandle, ref _safeKeyHandle);
96                 }
97             }
98         }
99
100         [System.Security.SecuritySafeCritical] // overrides public transparent member
101         protected override void Dispose(bool disposing) {
102             base.Dispose(disposing);
103
104             if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed)
105                 _safeKeyHandle.Dispose();
106             if (_safeProvHandle != null && !_safeProvHandle.IsClosed)
107                 _safeProvHandle.Dispose();
108         }
109
110         //
111         // public properties
112         //
113
114         [System.Runtime.InteropServices.ComVisible(false)]
115         public bool PublicOnly {
116             [System.Security.SecuritySafeCritical]  // auto-generated
117             get {
118                 GetKeyPair();
119                 byte[] publicKey = (byte[]) Utils._GetKeyParameter(_safeKeyHandle, Constants.CLR_PUBLICKEYONLY);
120                 return (publicKey[0] == 1);
121             }
122         }
123
124         [System.Runtime.InteropServices.ComVisible(false)]
125         public CspKeyContainerInfo CspKeyContainerInfo {
126             [System.Security.SecuritySafeCritical]  // auto-generated
127             get {
128                 GetKeyPair();
129                 return new CspKeyContainerInfo(_parameters, _randomKeyContainer);
130             }
131         }
132
133         public override int KeySize {
134             [System.Security.SecuritySafeCritical]  // auto-generated
135             get {
136                 GetKeyPair();
137                 byte[] keySize = (byte[]) Utils._GetKeyParameter(_safeKeyHandle, Constants.CLR_KEYLEN);
138                 _dwKeySize = (keySize[0] | (keySize[1] << 8) | (keySize[2] << 16) | (keySize[3] << 24));
139                 return _dwKeySize;
140             }
141         }
142
143         public override string KeyExchangeAlgorithm {
144             get { return null; }
145         }
146
147         public override string SignatureAlgorithm {
148             get { return "http://www.w3.org/2000/09/xmldsig#dsa-sha1"; }
149         }
150
151         public static bool UseMachineKeyStore {
152             get { return (s_UseMachineKeyStore == CspProviderFlags.UseMachineKeyStore); }
153             set { s_UseMachineKeyStore = (value ? CspProviderFlags.UseMachineKeyStore : 0); }
154         }
155
156         public bool PersistKeyInCsp {
157             [System.Security.SecuritySafeCritical]  // auto-generated
158             get {
159                 if (_safeProvHandle == null) {
160                     lock (this) {
161                         if (_safeProvHandle == null)
162                             _safeProvHandle = Utils.CreateProvHandle(_parameters, _randomKeyContainer);
163                     }
164                 }
165                 return Utils.GetPersistKeyInCsp(_safeProvHandle);
166             }
167             [System.Security.SecuritySafeCritical]  // auto-generated
168             set {
169                 bool oldPersistKeyInCsp = this.PersistKeyInCsp;
170                 if (value == oldPersistKeyInCsp)
171                     return;
172
173                 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
174                 if (!value) {
175                     KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Delete);
176                     kp.AccessEntries.Add(entry);
177                 } else {
178                     KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Create);
179                     kp.AccessEntries.Add(entry);
180                 }
181                 kp.Demand();
182
183                 Utils.SetPersistKeyInCsp(_safeProvHandle, value);
184             }
185         }
186
187         //
188         // public methods
189         //
190
191         [System.Security.SecuritySafeCritical]  // auto-generated
192         public override DSAParameters ExportParameters (bool includePrivateParameters) {
193             GetKeyPair();
194             if (includePrivateParameters) {
195                 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
196                 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Export);
197                 kp.AccessEntries.Add(entry);
198                 kp.Demand();
199             }
200             DSACspObject dsaCspObject = new DSACspObject();
201             int blobType = includePrivateParameters ? Constants.PRIVATEKEYBLOB : Constants.PUBLICKEYBLOB;
202             // _ExportKey will check for failures and throw an exception
203             Utils._ExportKey(_safeKeyHandle, blobType, dsaCspObject);
204             return DSAObjectToStruct(dsaCspObject);
205         }
206
207         [System.Security.SecuritySafeCritical]  // auto-generated
208         [System.Runtime.InteropServices.ComVisible(false)]
209         public byte[] ExportCspBlob (bool includePrivateParameters) {
210             GetKeyPair();
211             return Utils.ExportCspBlobHelper(includePrivateParameters, _parameters, _safeKeyHandle);
212         }
213
214         [System.Security.SecuritySafeCritical]  // auto-generated
215         public override void ImportParameters(DSAParameters parameters) {
216             DSACspObject dsaCspObject = DSAStructToObject(parameters);
217             // Free the current key handle
218             if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed)
219                 _safeKeyHandle.Dispose();
220             _safeKeyHandle = SafeKeyHandle.InvalidHandle;
221
222             if (IsPublic(parameters)) {
223                 // Use our CRYPT_VERIFYCONTEXT handle, CRYPT_EXPORTABLE is not applicable to public only keys, so pass false
224                 Utils._ImportKey(Utils.StaticDssProvHandle, Constants.CALG_DSS_SIGN, (CspProviderFlags) 0, dsaCspObject, ref _safeKeyHandle);
225             } else {
226                 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
227                 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Import);
228                 kp.AccessEntries.Add(entry);
229                 kp.Demand();
230                 if (_safeProvHandle == null)
231                     _safeProvHandle = Utils.CreateProvHandle(_parameters, _randomKeyContainer);
232                 // Now, import the key into the CSP; _ImportKey will check for failures.
233                 Utils._ImportKey(_safeProvHandle, Constants.CALG_DSS_SIGN, _parameters.Flags, dsaCspObject, ref _safeKeyHandle);
234             }
235         }
236
237         [System.Security.SecuritySafeCritical]  // auto-generated
238         [System.Runtime.InteropServices.ComVisible(false)]
239         public void ImportCspBlob (byte[] keyBlob) {
240             Utils.ImportCspBlobHelper(CspAlgorithmType.Dss, keyBlob, IsPublic(keyBlob), ref _parameters, _randomKeyContainer, ref _safeProvHandle, ref _safeKeyHandle);
241         }
242
243         public byte[] SignData(Stream inputStream) {
244             byte[] hashVal = _sha1.ComputeHash(inputStream);
245             return SignHash(hashVal, null);
246         }
247
248         public byte[] SignData(byte[] buffer) {
249             byte[] hashVal = _sha1.ComputeHash(buffer);
250             return SignHash(hashVal, null);
251         }
252
253         public byte[] SignData(byte[] buffer, int offset, int count) {
254             byte[] hashVal = _sha1.ComputeHash(buffer, offset, count);
255             return SignHash(hashVal, null);
256         }
257
258         public bool VerifyData(byte[] rgbData, byte[] rgbSignature) {
259             byte[] hashVal = _sha1.ComputeHash(rgbData);
260             return VerifyHash(hashVal, null, rgbSignature);
261         }
262
263         override public byte[] CreateSignature(byte[] rgbHash) {
264             return SignHash(rgbHash, null);
265         }
266
267         override public bool VerifySignature(byte[] rgbHash, byte[] rgbSignature) {
268             return VerifyHash(rgbHash, null, rgbSignature);
269         }
270
271         protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm)
272         {
273             // we're sealed and the base should have checked this before calling us
274             Contract.Assert(data != null);
275             Contract.Assert(offset >= 0 && offset <= data.Length);
276             Contract.Assert(count >= 0 && count <= data.Length - offset);
277             Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
278
279             if (hashAlgorithm != HashAlgorithmName.SHA1)
280             {
281                 throw new CryptographicException(Environment.GetResourceString("Cryptography_UnknownHashAlgorithm", hashAlgorithm.Name));
282             }
283
284             return _sha1.ComputeHash(data, offset, count);
285         }
286
287         protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm)
288         {
289             // we're sealed and the base should have checked this before calling us
290             Contract.Assert(data != null);
291             Contract.Assert(!String.IsNullOrEmpty(hashAlgorithm.Name));
292
293             if (hashAlgorithm != HashAlgorithmName.SHA1)
294             {
295                 throw new CryptographicException(Environment.GetResourceString("Cryptography_UnknownHashAlgorithm", hashAlgorithm.Name));
296             }
297
298             return _sha1.ComputeHash(data);
299         }
300
301         [System.Security.SecuritySafeCritical]  // auto-generated
302         public byte[] SignHash(byte[] rgbHash, string str) {
303             if (rgbHash == null)
304                 throw new ArgumentNullException("rgbHash");
305             Contract.EndContractBlock();
306             if (PublicOnly)
307                 throw new CryptographicException(Environment.GetResourceString("Cryptography_CSP_NoPrivateKey"));
308
309             int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm);
310             if (rgbHash.Length != _sha1.HashSize / 8)
311                 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidHashSize", "SHA1", _sha1.HashSize / 8));
312
313             GetKeyPair();
314             if (!CspKeyContainerInfo.RandomlyGenerated) {
315                 KeyContainerPermission kp = new KeyContainerPermission(KeyContainerPermissionFlags.NoFlags);
316                 KeyContainerPermissionAccessEntry entry = new KeyContainerPermissionAccessEntry(_parameters, KeyContainerPermissionFlags.Sign);
317                 kp.AccessEntries.Add(entry);
318                 kp.Demand();
319             }
320             return Utils.SignValue(_safeKeyHandle, _parameters.KeyNumber, Constants.CALG_DSS_SIGN, calgHash, rgbHash);
321         }
322
323         [System.Security.SecuritySafeCritical]  // auto-generated
324         public bool VerifyHash(byte[] rgbHash, string str, byte[] rgbSignature) {
325             if (rgbHash == null)
326                 throw new ArgumentNullException("rgbHash");
327             if (rgbSignature == null)
328                 throw new ArgumentNullException("rgbSignature");
329             Contract.EndContractBlock();
330
331             int calgHash = X509Utils.NameOrOidToAlgId(str, OidGroup.HashAlgorithm);
332             if (rgbHash.Length != _sha1.HashSize / 8) 
333                 throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidHashSize", "SHA1", _sha1.HashSize / 8));
334
335             GetKeyPair();
336             return Utils.VerifySign(_safeKeyHandle, Constants.CALG_DSS_SIGN, calgHash, rgbHash, rgbSignature);
337         }
338
339         //
340         // private static methods
341         //
342
343         private static DSAParameters DSAObjectToStruct (DSACspObject dsaCspObject) {
344             DSAParameters dsaParams = new DSAParameters();
345             dsaParams.P = dsaCspObject.P;
346             dsaParams.Q = dsaCspObject.Q;
347             dsaParams.G = dsaCspObject.G;
348             dsaParams.Y = dsaCspObject.Y;
349             dsaParams.J = dsaCspObject.J;
350             dsaParams.X = dsaCspObject.X;
351             dsaParams.Seed = dsaCspObject.Seed;
352             dsaParams.Counter = dsaCspObject.Counter;
353             return dsaParams;
354         }
355
356         private static DSACspObject DSAStructToObject (DSAParameters dsaParams) {
357             DSACspObject dsaCspObject = new DSACspObject();
358             dsaCspObject.P = dsaParams.P;
359             dsaCspObject.Q = dsaParams.Q;
360             dsaCspObject.G = dsaParams.G;
361             dsaCspObject.Y = dsaParams.Y;
362             dsaCspObject.J = dsaParams.J;
363             dsaCspObject.X = dsaParams.X;
364             dsaCspObject.Seed = dsaParams.Seed;
365             dsaCspObject.Counter = dsaParams.Counter;
366             return dsaCspObject;
367         }
368
369         private static bool IsPublic (DSAParameters dsaParams) {
370             return (dsaParams.X == null);
371         }
372
373         // find whether a DSS key blob is public.
374         private static bool IsPublic (byte[] keyBlob) {
375             if (keyBlob == null)
376                 throw new ArgumentNullException("keyBlob");
377             Contract.EndContractBlock();
378
379             // The CAPI DSS public key representation consists of the following sequence:
380             //  - BLOBHEADER
381             //  - DSSPUBKEY
382             //  - rgbP[cbKey]
383             //  - rgbQ[20]
384             //  - rgbG[cbKey]
385             //  - rgbY[cbKey]
386             //  - DSSSEED
387
388             // The first should be PUBLICKEYBLOB and magic should be DSS_MAGIC "DSS1" or DSS_PUB_MAGIC_VER3 "DSS3"
389             if (keyBlob[0] != Constants.PUBLICKEYBLOB)
390                 return false;
391
392             if ((keyBlob[11] != 0x31 && keyBlob[11] != 0x33) || keyBlob[10] != 0x53 || keyBlob[9] != 0x53 || keyBlob[8] != 0x44)
393                 return false;
394
395             return true;
396         }
397     }
398 }