System.Drawing: added email to icon and test file headers
[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 using System.Globalization;
30 using System.Reflection;
31 using System.Runtime.CompilerServices;
32 using System.Runtime.InteropServices;
33 using System.Runtime.ConstrainedExecution;
34 using System.Security.Cryptography;
35 using System.Security.Permissions;
36
37 namespace System.Security {
38
39         [MonoTODO ("work in progress - encryption is missing")]
40         public sealed class SecureString : CriticalFinalizerObject, IDisposable {
41
42                 private const int BlockSize = 16;
43                 private const int MaxSize = 65536;
44
45                 private int length;
46                 private bool disposed;
47                 private bool read_only;
48                 private byte[] data;
49
50                 static SecureString ()
51                 {
52                         // ProtectedMemory has been moved to System.Security.dll
53                         // we use reflection to call it (if available) or we'll 
54                         // throw an exception
55                 }
56
57                 public SecureString ()
58                 {
59                         Alloc (BlockSize >> 1, false);
60                 }
61
62                 [CLSCompliant (false)]
63                 public unsafe SecureString (char* value, int length)
64                 {
65                         if (value == null)
66                                 throw new ArgumentNullException ("value");
67                         if ((length < 0) || (length > MaxSize))
68                                 throw new ArgumentOutOfRangeException ("length", "< 0 || > 65536");
69
70                         this.length = length; // real length
71                         Alloc (length, false);
72                         int n = 0;
73                         for (int i = 0; i < length; i++) {
74                                 char c = *value++;
75                                 data[n++] = (byte) (c >> 8);
76                                 data[n++] = (byte) c;
77                         }
78                         Encrypt ();
79                 }
80
81                 // properties
82
83                 public int Length {
84                         get {
85                                 if (disposed)
86                                         throw new ObjectDisposedException ("SecureString");
87                                 return length;
88                         }
89                 }
90
91                 public void AppendChar (char c)
92                 {
93                         if (disposed)
94                                 throw new ObjectDisposedException ("SecureString");
95                         if (read_only) {
96                                 throw new InvalidOperationException (Locale.GetText (
97                                         "SecureString is read-only."));
98                         }
99                         if (length == MaxSize)
100                                 throw new ArgumentOutOfRangeException ("length", "> 65536");
101
102                         try {
103                                 Decrypt ();
104                                 int n = length * 2;
105                                 Alloc (++length, true);
106                                 data[n++] = (byte) (c >> 8);
107                                 data[n++] = (byte) c;
108                         }
109                         finally {
110                                 Encrypt ();
111                         }
112                 }
113
114                 public void Clear ()
115                 {
116                         if (disposed)
117                                 throw new ObjectDisposedException ("SecureString");
118                         if (read_only) {
119                                 throw new InvalidOperationException (Locale.GetText (
120                                         "SecureString is read-only."));
121                         }
122
123                         Array.Clear (data, 0, data.Length);
124                         length = 0;
125                 }
126
127                 public SecureString Copy () 
128                 {
129                         SecureString ss = new SecureString ();
130                         ss.data = (byte[]) data.Clone ();
131                         ss.length = length;
132                         return ss;
133                 }
134
135                 public void Dispose ()
136                 {
137                         disposed = true;
138                         // don't call clear because we could be either in read-only 
139                         // or already disposed - but DO CLEAR the data
140                         if (data != null) {
141                                 Array.Clear (data, 0, data.Length);
142                                 data = null;
143                         }
144                         length = 0;
145                 }
146
147                 public void InsertAt (int index, char c)
148                 {
149                         if (disposed)
150                                 throw new ObjectDisposedException ("SecureString");
151                         if (read_only) {
152                                 throw new InvalidOperationException (Locale.GetText (
153                                         "SecureString is read-only."));
154                         }
155                         if ((index < 0) || (index > length))
156                                 throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
157                         // insert increments length
158                         if (length >= MaxSize) {
159                                 string msg = Locale.GetText ("Maximum string size is '{0}'.", MaxSize);
160                                 throw new ArgumentOutOfRangeException ("index", msg);
161                         }
162
163                         try {
164                                 Decrypt ();
165                                 Alloc (++length, true);
166                                 int n = index * 2;
167                                 Buffer.BlockCopy (data, n, data, n + 2, data.Length - n - 2);
168                                 data[n++] = (byte) (c >> 8);
169                                 data[n] = (byte) c;
170                         }
171                         finally {
172                                 Encrypt ();
173                         }
174                 }
175
176                 public bool IsReadOnly ()
177                 {
178                         if (disposed)
179                                 throw new ObjectDisposedException ("SecureString");
180                         return read_only;
181                 }
182
183                 public void MakeReadOnly ()
184                 {
185                         read_only = true;
186                 }
187
188                 public void RemoveAt (int index)
189                 {
190                         if (disposed)
191                                 throw new ObjectDisposedException ("SecureString");
192                         if (read_only) {
193                                 throw new InvalidOperationException (Locale.GetText (
194                                         "SecureString is read-only."));
195                         }
196                         if ((index < 0) || (index >= length))
197                                 throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
198
199                         try {
200                                 Decrypt ();
201                                 Buffer.BlockCopy (data, index + 1, data, index, data.Length - index - 1);
202                                 Alloc (--length, true);
203                         }
204                         finally {
205                                 Encrypt ();
206                         }
207                 }
208
209                 public void SetAt (int index, char c)
210                 {
211                         if (disposed)
212                                 throw new ObjectDisposedException ("SecureString");
213                         if (read_only) {
214                                 throw new InvalidOperationException (Locale.GetText (
215                                         "SecureString is read-only."));
216                         }
217                         if ((index < 0) || (index >= length))
218                                 throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
219
220                         try {
221                                 Decrypt ();
222                                 int n = index * 2;
223                                 data[n++] = (byte) (c >> 8);
224                                 data[n] = (byte) c;
225                         }
226                         finally {
227                                 Encrypt ();
228                         }
229                 }
230
231                 // internal/private stuff
232
233 //              [MethodImplAttribute(MethodImplOptions.InternalCall)]
234 //              extern static void EncryptInternal (byte [] data, object scope);
235
236 //              [MethodImplAttribute(MethodImplOptions.InternalCall)]
237 //              extern static void DecryptInternal (byte [] data, object scope);
238
239 //              static readonly object scope = Enum.Parse (
240 //                      Assembly.Load (Consts.AssemblySystem_Security)
241 //                      .GetType ("System.Security.Cryptography.MemoryProtectionScope"), "SameProcess");
242
243                 // Note that ProtectedMemory is not supported on non-Windows environment right now.
244                 private void Encrypt ()
245                 {
246                         if ((data != null) && (data.Length > 0)) {
247                                 // It somehow causes nunit test breakage
248                                 // EncryptInternal (data, scope);
249                         }
250                 }
251
252                 // Note that ProtectedMemory is not supported on non-Windows environment right now.
253                 private void Decrypt ()
254                 {
255                         if ((data != null) && (data.Length > 0)) {
256                                 // It somehow causes nunit test breakage
257                                 // DecryptInternal (data, scope);
258                         }
259                 }
260
261                 // note: realloc only work for bigger buffers. Clear will 
262                 // reset buffers to default (and small) size.
263                 private void Alloc (int length, bool realloc) 
264                 {
265                         if ((length < 0) || (length > MaxSize))
266                                 throw new ArgumentOutOfRangeException ("length", "< 0 || > 65536");
267
268                         // (size / blocksize) + 1 * blocksize
269                         // where size = length * 2 (unicode) and blocksize == 16 (ProtectedMemory)
270                         // length * 2 (unicode) / 16 (blocksize)
271                         int size = (length >> 3) + (((length & 0x7) == 0) ? 0 : 1) << 4;
272
273                         // is re-allocation necessary ? (i.e. grow or shrink 
274                         // but do not re-allocate the same amount of memory)
275                         if (realloc && (data != null) && (size == data.Length))
276                                 return;
277
278                         if (realloc) {
279                                 // copy, then clear
280                                 byte[] newdata = new byte[size];
281                                 Array.Copy (data, 0, newdata, 0, Math.Min (data.Length, newdata.Length));
282                                 Array.Clear (data, 0, data.Length);
283                                 data = newdata;
284                         } else {
285                                 data = new byte[size];
286                         }
287                 }
288
289                 // dangerous method (put a LinkDemand on it)
290                 internal byte[] GetBuffer ()
291                 {
292                         byte[] secret = new byte[length << 1];
293                         try {
294                                 Decrypt ();
295                                 Buffer.BlockCopy (data, 0, secret, 0, secret.Length);
296                         }
297                         finally {
298                                 Encrypt ();
299                         }
300                         // NOTE: CALLER IS RESPONSIBLE TO ZEROIZE THE DATA
301                         return secret;
302                 }
303         }
304 }