2005-06-08 Sebastien Pouliot <sebastien@ximian.com>
[mono.git] / mcs / class / corlib / System.Security / SecureString.cs
1 //
2 // System.Security.SecureString class
3 //
4 // Authors
5 //      Sebastien Pouliot  <sebastien@ximian.com>
6 //
7 // Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29 #if NET_2_0
30
31 using System.Globalization;
32 using System.Runtime.InteropServices;
33 using System.Runtime.ConstrainedExecution;
34 using System.Security.Cryptography;
35 using System.Security.Permissions;
36
37 using Mono.Security.Cryptography;
38
39 namespace System.Security {
40
41         [MonoTODO ("current version ISN'T encrypted")]
42         public sealed class SecureString : CriticalFinalizerObject, IDisposable {
43
44                 static private SymmetricAlgorithm _cipher;
45                 static private int _blockSize;
46
47                 private int _length;
48                 private bool _disposed;
49                 private bool _readonly;
50                 private byte[] _enc;    // encrypted (permanent buffer)
51                 private char[] _dec;    // decrypted (temporary buffer)
52                 private byte[] _iv;     // initialization vector
53
54                 static SecureString ()
55                 {
56                         _cipher = SymmetricAlgorithm.Create ();
57                         _cipher.Mode = CipherMode.CBC;
58                         _cipher.Padding = PaddingMode.PKCS7;
59                         _blockSize = _cipher.BlockSize << 3; // in bytes
60                 }
61
62                 public SecureString ()
63                 {
64                         _iv = KeyBuilder.IV (_blockSize);
65                         // default size
66                         Alloc (_blockSize >> 1, false);
67                 }
68
69                 [CLSCompliant (false)]
70                 public unsafe SecureString (char* value, int length)
71                 {
72                         if (value == null)
73                                 throw new ArgumentNullException ("value");
74
75                         _iv = KeyBuilder.IV (_blockSize);
76                         Alloc (length, false);
77                         for (_length=1; _length <= length; _length++)
78                                 _dec [_length] = *value++;
79                         Encrypt ();
80                 }
81
82                 // properties
83
84                 public int Length {
85                         get {
86                                 if (_disposed)
87                                         throw new ObjectDisposedException ("SecureString");
88                                 return _length;
89                         }
90                 }
91
92                 public void AppendChar (char c)
93                 {
94                         if (_disposed)
95                                 throw new ObjectDisposedException ("SecureString");
96                         if (_readonly) {
97                                 throw new InvalidOperationException (Locale.GetText (
98                                         "SecureString is read-only."));
99                         }
100                         if (_length == 65535)
101                                 throw new ArgumentOutOfRangeException ("length", "> 65536");
102
103                         try {
104                                 Decrypt ();
105                                 if (_length >= _dec.Length) {
106                                         Alloc (_length + 1, true);
107                                 }
108                                 _dec [_length++] = c;
109                                 Encrypt ();
110                         }
111                         catch {
112                                 Array.Clear (_dec, 0, _dec.Length);
113                                 _length = 0;
114                                 throw;
115                         }
116                 }
117
118                 public void Clear ()
119                 {
120                         if (_disposed)
121                                 throw new ObjectDisposedException ("SecureString");
122                         if (_readonly) {
123                                 throw new InvalidOperationException (Locale.GetText (
124                                         "SecureString is read-only."));
125                         }
126
127                         Array.Clear (_dec, 0, _dec.Length);     // should be empty
128                         Array.Clear (_enc, 0, _enc.Length);
129                         // return to default sizes
130                         _dec = new char [_blockSize >> 1];
131                         _enc = new byte [_blockSize];
132                         _length = 0;
133                         // get a new IV
134                         _iv = KeyBuilder.IV (_blockSize);
135                 }
136
137                 public SecureString Copy () 
138                 {
139                         SecureString ss = new SecureString ();
140                         try {
141                                 Decrypt ();
142                                 ss._dec = (char[]) _dec.Clone ();
143                                 Array.Clear (_dec, 0, _dec.Length);
144                                 ss._enc = new byte [_dec.Length >> 1];
145                                 ss.Encrypt ();
146                         }
147                         catch {
148                                 Array.Clear (_dec, 0, _dec.Length);
149                                 if (ss._dec != null)
150                                         Array.Clear (ss._dec, 0, ss._dec.Length);
151                         }
152                         return ss;
153                 }
154
155                 public void Dispose ()
156                 {
157                         if (_dec != null) {
158                                 Array.Clear (_enc, 0, _enc.Length);
159                                 Array.Clear (_dec, 0, _dec.Length);
160                                 _dec = null;
161                                 _length = 0;
162                         }
163                         _disposed = true;
164                 }
165
166                 public void InsertAt (int index, char c)
167                 {
168                         if (_disposed)
169                                 throw new ObjectDisposedException ("SecureString");
170                         if ((index < 0) || (index >= _length))
171                                 throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
172                         if (_readonly) {
173                                 throw new InvalidOperationException (Locale.GetText (
174                                         "SecureString is read-only."));
175                         }
176
177                         try {
178                                 Decrypt ();
179                                 // TODO
180                                 Encrypt ();
181                         }
182                         catch {
183                                 Array.Clear (_dec, 0, _dec.Length);
184                                 _length = 0;
185                                 throw;
186                         }
187                 }
188
189                 public bool IsReadOnly ()
190                 {
191                         if (_disposed)
192                                 throw new ObjectDisposedException ("SecureString");
193                         return _readonly;
194                 }
195
196                 public void MakeReadOnly ()
197                 {
198                         _readonly = true;
199                 }
200
201                 public void RemoveAt (int index)
202                 {
203                         if (_disposed)
204                                 throw new ObjectDisposedException ("SecureString");
205                         if ((index < 0) || (index >= _length))
206                                 throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
207                         if (_readonly) {
208                                 throw new InvalidOperationException (Locale.GetText (
209                                         "SecureString is read-only."));
210                         }
211
212                         try {
213                                 Decrypt ();
214                                 Buffer.BlockCopy (_dec, index, _dec, index - 1, _dec.Length - index);
215                                 _length--;
216                                 Encrypt ();
217                         }
218                         catch {
219                                 Array.Clear (_dec, 0, _dec.Length);
220                                 _length = 0;
221                                 throw;
222                         }
223                 }
224
225                 public void SetAt (int index, char c)
226                 {
227                         if (_disposed)
228                                 throw new ObjectDisposedException ("SecureString");
229                         if ((index < 0) || (index >= _length))
230                                 throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
231                         if (_readonly) {
232                                 throw new InvalidOperationException (Locale.GetText (
233                                         "SecureString is read-only."));
234                         }
235
236                         try {
237                                 Decrypt ();
238                                 _dec [index] = c;
239                                 Encrypt ();
240                         }
241                         catch {
242                                 Array.Clear (_dec, 0, _dec.Length);
243                                 _length = 0;
244                                 throw;
245                         }
246                 }
247
248                 // internal/private stuff
249
250                 // note: realloc only work for bigger buffers. Clear will 
251                 // reset buffers to default (and small) size.
252                 private void Alloc (int length, bool realloc) 
253                 {
254                         if ((length < 0) || (length > 65536))
255                                 throw new ArgumentOutOfRangeException ("length", "< 0 || > 65536");
256
257                         int size = (((length * 2) / _blockSize) + 1) * _blockSize;
258
259                         char[] dec = new char [size >> 1];
260                         byte[] enc = new byte [size];
261
262                         if (realloc) {
263                                 // copy, then clear
264                                 Array.Copy (_dec, 0, dec, 0, _dec.Length);
265                                 Array.Clear (_dec, 0, _dec.Length);
266                                 _dec = null;
267
268                                 Array.Copy (_enc, 0, enc, 0, _enc.Length);
269                                 Array.Clear (_enc, 0, _enc.Length);
270                                 _enc = null;
271                         }
272
273                         _dec = dec;
274                         _enc = enc;
275                 }
276
277                 [MonoTODO ("no decryption - data is only copied")]
278                 private void Decrypt ()
279                 {
280                         Buffer.BlockCopy (_enc, 0, _dec, 0, _enc.Length);
281                 }
282
283                 [MonoTODO ("no encryption - data is only copied")]
284                 private void Encrypt ()
285                 {
286                         Buffer.BlockCopy (_dec, 0, _enc, 0, _enc.Length);
287                         Array.Clear (_dec, 0, _dec.Length);
288                 }
289
290                 // dangerous method (put a LinkDemand on it)
291                 internal byte[] GetBuffer ()
292                 {
293                         byte[] secret = null;
294                         try {
295                                 Decrypt ();
296                                 secret = (byte[]) _dec.Clone ();
297                         }
298                         finally {
299                                 Array.Clear (_dec, 0, _dec.Length);
300                         }
301                         // NOTE: CALLER IS RESPONSIBLE TO ZEROIZE THE DATA
302                         return secret;
303                 }
304         }
305 }
306
307 #endif