ebd6ef6309792854bf0aa6fb713118141439eda4
[mono.git] / mcs / class / referencesource / mscorlib / system / security / cryptography / cryptoapitransform.cs
1 // ==++==
2 // 
3 //   Copyright (c) Microsoft Corporation.  All rights reserved.
4 // 
5 // ==--==
6 // <OWNER>Microsoft</OWNER>
7 // 
8
9 //
10 // CryptoAPITransform.cs
11 //
12
13 namespace System.Security.Cryptography {
14
15     using System.Security.AccessControl;
16     using System.Security.Permissions;
17     using System.Runtime.InteropServices;
18     using System.Runtime.Versioning;
19     using System.Diagnostics.Contracts;
20
21 #if FEATURE_MACL && FEATURE_CRYPTO
22
23     [Serializable]
24     internal enum CryptoAPITransformMode {
25         Encrypt = 0,
26         Decrypt = 1
27     }
28
29 [System.Runtime.InteropServices.ComVisible(true)]
30     public sealed class CryptoAPITransform : ICryptoTransform {
31         private int BlockSizeValue;
32         private byte[] IVValue;
33         private CipherMode ModeValue;
34         private PaddingMode PaddingValue;
35         private CryptoAPITransformMode encryptOrDecrypt;
36         private byte[] _rgbKey;
37         private byte[] _depadBuffer = null;
38         [System.Security.SecurityCritical] // auto-generated
39         private SafeKeyHandle _safeKeyHandle;
40         [System.Security.SecurityCritical] // auto-generated
41         private SafeProvHandle _safeProvHandle;
42
43         private CryptoAPITransform () {}
44         [System.Security.SecurityCritical]  // auto-generated
45         internal CryptoAPITransform(int algid, int cArgs, int[] rgArgIds,
46                                     Object[] rgArgValues, byte[] rgbKey, PaddingMode padding, 
47                                     CipherMode cipherChainingMode, int blockSize,
48                                     int feedbackSize, bool useSalt,
49                                     CryptoAPITransformMode encDecMode) {
50             int dwValue;
51             byte[] rgbValue;
52
53             BlockSizeValue = blockSize;
54             ModeValue = cipherChainingMode;
55             PaddingValue = padding;
56             encryptOrDecrypt = encDecMode;
57
58             // Copy the input args
59             int _cArgs = cArgs;
60             int[] _rgArgIds = new int[rgArgIds.Length];
61             Array.Copy(rgArgIds, _rgArgIds, rgArgIds.Length);
62             _rgbKey = new byte[rgbKey.Length];
63             Array.Copy(rgbKey, _rgbKey, rgbKey.Length);
64             Object[] _rgArgValues = new Object[rgArgValues.Length];
65             // an element of rgArgValues can only be an int or a byte[]
66             for (int j = 0; j < rgArgValues.Length; j++) {
67                 if (rgArgValues[j] is byte[]) {
68                     byte[] rgbOrig = (byte[]) rgArgValues[j];
69                     byte[] rgbNew = new byte[rgbOrig.Length];
70                     Array.Copy(rgbOrig, rgbNew, rgbOrig.Length);
71                     _rgArgValues[j] = rgbNew;
72                     continue;
73                 }
74                 if (rgArgValues[j] is int) {
75                     _rgArgValues[j] = (int) rgArgValues[j];
76                     continue;
77                 }
78                 if (rgArgValues[j] is CipherMode) {
79                     _rgArgValues[j] = (int) rgArgValues[j];
80                     continue;
81                 }
82             }
83
84             _safeProvHandle = Utils.AcquireProvHandle(new CspParameters(Utils.DefaultRsaProviderType));
85
86             SafeKeyHandle safeKeyHandle = SafeKeyHandle.InvalidHandle;
87             // _ImportBulkKey will check for failures and throw an exception
88             Utils._ImportBulkKey(_safeProvHandle, algid, useSalt, _rgbKey, ref safeKeyHandle);
89             _safeKeyHandle = safeKeyHandle;
90
91             for (int i=0; i<cArgs; i++) {
92                 switch (rgArgIds[i]) {
93                 case Constants.KP_IV:
94                     IVValue = (byte[]) _rgArgValues[i];
95                     rgbValue = IVValue;
96                     Utils.SetKeyParamRgb(_safeKeyHandle, _rgArgIds[i], rgbValue, rgbValue.Length);
97                     break;
98
99                 case Constants.KP_MODE:
100                     ModeValue = (CipherMode) _rgArgValues[i];
101                     dwValue = (Int32) _rgArgValues[i];
102                 SetAsDWord:
103                     Utils.SetKeyParamDw(_safeKeyHandle, _rgArgIds[i], dwValue);
104                     break;
105
106                 case Constants.KP_MODE_BITS:
107                     dwValue = (Int32) _rgArgValues[i];
108                     goto SetAsDWord;
109
110                 case Constants.KP_EFFECTIVE_KEYLEN:
111                     dwValue = (Int32) _rgArgValues[i];
112                     goto SetAsDWord;
113
114                 default:
115                     throw new CryptographicException(Environment.GetResourceString("Cryptography_InvalidKeyParameter"), "_rgArgIds[i]");
116                 }
117             }
118         }
119
120         public void Dispose() {
121             Clear();
122         }
123
124         [System.Security.SecuritySafeCritical]  // auto-generated
125         public void Clear() {
126             Dispose(true);
127             GC.SuppressFinalize(this);
128         }
129
130         [System.Security.SecurityCritical]  // auto-generated
131         private void Dispose(bool disposing) {
132             if (disposing) {
133                 // We need to always zeroize the following fields because they contain sensitive data
134                 if (_rgbKey != null) {
135                     Array.Clear(_rgbKey,0,_rgbKey.Length);
136                     _rgbKey = null;
137                 }
138                 if (IVValue != null) {
139                     Array.Clear(IVValue,0,IVValue.Length);
140                     IVValue = null;
141                 }
142                 if (_depadBuffer != null) {
143                     Array.Clear(_depadBuffer, 0, _depadBuffer.Length);
144                     _depadBuffer = null;
145                 }
146
147                 if (_safeKeyHandle != null && !_safeKeyHandle.IsClosed) {
148                 _safeKeyHandle.Dispose();
149                 }
150                 if (_safeProvHandle != null && !_safeProvHandle.IsClosed) {
151                 _safeProvHandle.Dispose();
152         }
153             }
154         }
155
156         //
157         // public properties
158         //
159
160         public IntPtr KeyHandle {
161             [System.Security.SecuritySafeCritical]  // auto-generated
162             [SecurityPermissionAttribute(SecurityAction.Demand, Flags=SecurityPermissionFlag.UnmanagedCode)]
163             get { return _safeKeyHandle.DangerousGetHandle(); }
164         }
165
166         public int InputBlockSize {
167             get { return(BlockSizeValue/8); }
168         }
169
170         public int OutputBlockSize {
171             get { return(BlockSizeValue/8); }
172         }
173
174         public bool CanTransformMultipleBlocks {
175             get { return(true); }
176         }
177
178         public bool CanReuseTransform {
179             get { return(true); }
180         }
181
182         //
183         // public methods
184         //
185
186         // This routine resets the internal state of the CryptoAPITransform 
187         [System.Security.SecuritySafeCritical]  // auto-generated
188         [System.Runtime.InteropServices.ComVisible(false)]
189         public void Reset() {
190             _depadBuffer = null;
191             // just ensure we've called CryptEncrypt with the true flag
192             byte[] temp = null;
193             Utils._EncryptData(_safeKeyHandle, EmptyArray<Byte>.Value, 0, 0, ref temp, 0, PaddingValue, true);
194         }
195
196         [System.Security.SecuritySafeCritical]  // auto-generated
197         public int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset) {
198             // Note: special handling required if decrypting & using padding because the padding adds to the end of the last
199             // block, we have to buffer an entire block's worth of bytes in case what I just transformed turns out to be 
200             // the last block Then in TransformFinalBlock we strip off the padding.
201
202             if (inputBuffer == null) throw new ArgumentNullException("inputBuffer");
203             if (outputBuffer == null) throw new ArgumentNullException("outputBuffer");
204             if (inputOffset < 0) throw new ArgumentOutOfRangeException("inputOffset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
205             if ((inputCount <= 0) || (inputCount % InputBlockSize != 0) || (inputCount > inputBuffer.Length)) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidValue"));
206             if ((inputBuffer.Length - inputCount) < inputOffset) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
207             Contract.EndContractBlock();
208
209             if (encryptOrDecrypt == CryptoAPITransformMode.Encrypt) {
210                 // if we're encrypting we can always push out the bytes because no padding mode
211                 // removes bytes during encryption
212                 return Utils._EncryptData(_safeKeyHandle, inputBuffer, inputOffset, inputCount, ref outputBuffer, outputOffset, PaddingValue, false);
213             } else {
214                 if (PaddingValue == PaddingMode.Zeros || PaddingValue == PaddingMode.None) {
215                     // like encryption, if we're using None or Zeros padding on decrypt we can write out all
216                     // the bytes.  Note that we cannot depad a block partially padded with Zeros because
217                     // we can't tell if those zeros are plaintext or pad.
218                     return Utils._DecryptData(_safeKeyHandle, inputBuffer, inputOffset, inputCount, ref outputBuffer, outputOffset, PaddingValue, false);
219                 } else {
220                     // OK, now we're in the special case.  Check to see if this is the *first* block we've seen
221                     // If so, buffer it and return null zero bytes
222                     if (_depadBuffer == null) {
223                         _depadBuffer = new byte[InputBlockSize];
224                         // copy the last InputBlockSize bytes to _depadBuffer everything else gets processed and returned
225                         int inputToProcess = inputCount - InputBlockSize;
226                         Buffer.InternalBlockCopy(inputBuffer, inputOffset+inputToProcess, _depadBuffer, 0, InputBlockSize);
227                         return Utils._DecryptData(_safeKeyHandle, inputBuffer, inputOffset, inputToProcess, ref outputBuffer, outputOffset, PaddingValue, false);
228                     } else {
229                         // we already have a depad buffer, so we need to decrypt that info first & copy it out
230                         int r = Utils._DecryptData(_safeKeyHandle, _depadBuffer, 0, _depadBuffer.Length, ref outputBuffer, outputOffset, PaddingValue, false);
231                         outputOffset += OutputBlockSize;
232                         int inputToProcess = inputCount - InputBlockSize;
233                         Buffer.InternalBlockCopy(inputBuffer, inputOffset+inputToProcess, _depadBuffer, 0, InputBlockSize);
234                         r = Utils._DecryptData(_safeKeyHandle, inputBuffer, inputOffset, inputToProcess, ref outputBuffer, outputOffset, PaddingValue, false);
235                         return (OutputBlockSize + r);
236                     }
237                 }
238             }
239         }
240
241         [System.Security.SecuritySafeCritical]  // auto-generated
242         public byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount) { 
243             if (inputBuffer == null) throw new ArgumentNullException("inputBuffer");
244             if (inputOffset < 0) throw new ArgumentOutOfRangeException("inputOffset", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
245             if ((inputCount < 0) || (inputCount > inputBuffer.Length)) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidValue"));
246             if ((inputBuffer.Length - inputCount) < inputOffset) throw new ArgumentException(Environment.GetResourceString("Argument_InvalidOffLen"));
247             Contract.EndContractBlock();
248
249             if (encryptOrDecrypt == CryptoAPITransformMode.Encrypt) {
250                 // If we're encrypting we can always return what we compute because there's no _depadBuffer
251                 byte[] transformedBytes = null;
252                 Utils._EncryptData(_safeKeyHandle, inputBuffer, inputOffset, inputCount, ref transformedBytes, 0, PaddingValue, true);
253                 Reset();
254                 return transformedBytes;
255             } else {
256                 if (inputCount%InputBlockSize != 0)
257                     throw new CryptographicException(Environment.GetResourceString("Cryptography_SSD_InvalidDataSize"));
258
259                 if (_depadBuffer == null) {
260                     byte[] transformedBytes = null;
261                     Utils._DecryptData(_safeKeyHandle, inputBuffer, inputOffset, inputCount, ref transformedBytes, 0, PaddingValue, true);
262                     Reset();
263                     return transformedBytes;
264                 } else {
265                     byte[] temp = new byte[_depadBuffer.Length + inputCount];
266                     Buffer.InternalBlockCopy(_depadBuffer, 0, temp, 0, _depadBuffer.Length);
267                     Buffer.InternalBlockCopy(inputBuffer, inputOffset, temp, _depadBuffer.Length, inputCount);
268                     byte[] transformedBytes = null;
269                     Utils._DecryptData(_safeKeyHandle, temp, 0, temp.Length, ref transformedBytes, 0, PaddingValue, true);
270                     Reset();
271                     return transformedBytes;
272                 }
273             }
274         }
275     }
276 #endif // FEATURE_MACL && FEATURE_CRYPTO
277
278     [Serializable]
279     [System.Runtime.InteropServices.ComVisible(true)]
280     [Flags]
281     public enum CspProviderFlags {
282         NoFlags                 = 0x0000,
283         UseMachineKeyStore      = 0x0001,
284         UseDefaultKeyContainer  = 0x0002,
285         UseNonExportableKey     = 0x0004,
286         UseExistingKey          = 0x0008,
287         UseArchivableKey        = 0x0010,
288         UseUserProtectedKey     = 0x0020,
289         NoPrompt                = 0x0040,
290         CreateEphemeralKey      = 0x0080
291     }
292
293     [System.Runtime.InteropServices.ComVisible(true)]
294     public sealed class CspParameters
295     {
296         public int          ProviderType;
297         public string       ProviderName;
298         [ResourceExposure(ResourceScope.Machine)]
299         public string       KeyContainerName;
300         public int          KeyNumber;
301
302         private int m_flags;
303         public CspProviderFlags Flags {
304             get { return (CspProviderFlags) m_flags; }
305             set { 
306                 int allFlags = 0x00FF; // this should change if more values are added to CspProviderFlags
307                 Contract.Assert((CspProviderFlags.UseMachineKeyStore |
308                                 CspProviderFlags.UseDefaultKeyContainer |
309                                 CspProviderFlags.UseNonExportableKey |
310                                 CspProviderFlags.UseExistingKey |
311                                 CspProviderFlags.UseArchivableKey |
312                                 CspProviderFlags.UseUserProtectedKey |
313                                 CspProviderFlags.NoPrompt |
314                                 CspProviderFlags.CreateEphemeralKey) == (CspProviderFlags)allFlags, "allFlags does not match all CspProviderFlags");
315                 
316                 int flags = (int) value;
317                 if ((flags & ~allFlags) != 0)
318                     throw new ArgumentException(Environment.GetResourceString("Arg_EnumIllegalVal", (int)value), "value");
319                 m_flags = flags;
320             }
321         }
322
323 #if FEATURE_MACL || MONO
324         private CryptoKeySecurity m_cryptoKeySecurity;
325         public CryptoKeySecurity CryptoKeySecurity {
326             get {
327                 return m_cryptoKeySecurity;
328             }
329             set {
330                 m_cryptoKeySecurity = value;
331             }
332         }
333 #endif
334
335 #if (FEATURE_CRYPTO && FEATURE_X509_SECURESTRINGS) || FEATURE_CORECLR
336         private SecureString m_keyPassword;
337         public SecureString KeyPassword {
338             get {
339                 return m_keyPassword;
340             }
341             set {
342                 m_keyPassword = value;
343                 // Parent handle and PIN are mutually exclusive.
344                 m_parentWindowHandle = IntPtr.Zero;
345             }
346         }
347
348         private IntPtr m_parentWindowHandle;
349         public IntPtr ParentWindowHandle {
350             [ResourceExposure(ResourceScope.Machine)]
351             get {
352                 return m_parentWindowHandle;
353             }
354             [ResourceExposure(ResourceScope.Machine)]
355             set {
356                 m_parentWindowHandle = value;
357                 // Parent handle and PIN are mutually exclusive.
358                 m_keyPassword = null;
359             }
360         }
361 #endif
362
363         [ResourceExposure(ResourceScope.None)]
364         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
365         public CspParameters () : this(Utils.DefaultRsaProviderType, null, null) {}
366
367         [ResourceExposure(ResourceScope.None)]
368         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
369         public CspParameters (int dwTypeIn) : this(dwTypeIn, null, null) {}
370
371         [ResourceExposure(ResourceScope.None)]
372         [ResourceConsumption(ResourceScope.Machine, ResourceScope.Machine)]
373         public CspParameters (int dwTypeIn, string strProviderNameIn) : this(dwTypeIn, strProviderNameIn, null) {}
374
375         [ResourceExposure(ResourceScope.Machine)]
376         [ResourceConsumption(ResourceScope.Machine)]
377         public CspParameters (int dwTypeIn, string strProviderNameIn, string strContainerNameIn) :
378             this (dwTypeIn, strProviderNameIn, strContainerNameIn, CspProviderFlags.NoFlags) {}
379
380 #if MONO || (FEATURE_MACL && FEATURE_CRYPTO && FEATURE_X509_SECURESTRINGS)
381         [ResourceExposure(ResourceScope.Machine)]
382         [ResourceConsumption(ResourceScope.Machine)]
383         public CspParameters (int providerType, string providerName, string keyContainerName,
384                               CryptoKeySecurity cryptoKeySecurity, SecureString keyPassword)
385             : this (providerType, providerName, keyContainerName) {
386             m_cryptoKeySecurity = cryptoKeySecurity;
387             m_keyPassword = keyPassword;
388         }
389
390         [ResourceExposure(ResourceScope.Machine)]
391         [ResourceConsumption(ResourceScope.Machine)]
392         public CspParameters (int providerType, string providerName, string keyContainerName,
393                               CryptoKeySecurity cryptoKeySecurity, IntPtr parentWindowHandle)
394             : this (providerType, providerName, keyContainerName) {
395             m_cryptoKeySecurity = cryptoKeySecurity;
396             m_parentWindowHandle = parentWindowHandle;
397         }
398 #endif // #if FEATURE_MACL && FEATURE_CRYPTO && FEATURE_X509_SECURESTRINGS
399
400         [ResourceExposure(ResourceScope.Machine)]
401         internal CspParameters (int providerType, string providerName, string keyContainerName, CspProviderFlags flags) {
402             ProviderType = providerType;
403             ProviderName = providerName;
404             KeyContainerName = keyContainerName;
405             KeyNumber = -1;
406             Flags = flags;
407         }
408
409         // copy constructor
410         internal CspParameters (CspParameters parameters) {
411             ProviderType = parameters.ProviderType;
412             ProviderName = parameters.ProviderName;
413             KeyContainerName = parameters.KeyContainerName;
414             KeyNumber = parameters.KeyNumber;
415             Flags = parameters.Flags;
416 #if FEATURE_MACL || MONO
417             m_cryptoKeySecurity = parameters.m_cryptoKeySecurity;
418 #endif // FEATURE_MACL
419 #if FEATURE_CRYPTO && FEATURE_X509_SECURESTRINGS
420             m_keyPassword = parameters.m_keyPassword;
421             m_parentWindowHandle = parameters.m_parentWindowHandle;
422 #endif // FEATURE_CRYPTO && FEATURE_X509_SECURESTRINGS
423         }
424     }
425 }