New test.
[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 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                         return ss;
132                 }
133
134                 public void Dispose ()
135                 {
136                         disposed = true;
137                         // don't call clear because we could be either in read-only 
138                         // or already disposed - but DO CLEAR the data
139                         if (data != null) {
140                                 Array.Clear (data, 0, data.Length);
141                                 data = null;
142                         }
143                         length = 0;
144                 }
145
146                 public void InsertAt (int index, char c)
147                 {
148                         if (disposed)
149                                 throw new ObjectDisposedException ("SecureString");
150                         if (read_only) {
151                                 throw new InvalidOperationException (Locale.GetText (
152                                         "SecureString is read-only."));
153                         }
154                         if ((index < 0) || (index > length))
155                                 throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
156                         // insert increments length
157                         if (length >= MaxSize) {
158                                 string msg = Locale.GetText ("Maximum string size is '{0}'.", MaxSize);
159                                 throw new ArgumentOutOfRangeException ("index", msg);
160                         }
161
162                         try {
163                                 Decrypt ();
164                                 Alloc (++length, true);
165                                 int n = index * 2;
166                                 Buffer.BlockCopy (data, n, data, n + 2, data.Length - 2);
167                                 data[n++] = (byte) (c >> 8);
168                                 data[n] = (byte) c;
169                         }
170                         finally {
171                                 Encrypt ();
172                         }
173                 }
174
175                 public bool IsReadOnly ()
176                 {
177                         if (disposed)
178                                 throw new ObjectDisposedException ("SecureString");
179                         return read_only;
180                 }
181
182                 public void MakeReadOnly ()
183                 {
184                         read_only = true;
185                 }
186
187                 public void RemoveAt (int index)
188                 {
189                         if (disposed)
190                                 throw new ObjectDisposedException ("SecureString");
191                         if (read_only) {
192                                 throw new InvalidOperationException (Locale.GetText (
193                                         "SecureString is read-only."));
194                         }
195                         if ((index < 0) || (index >= length))
196                                 throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
197
198                         try {
199                                 Decrypt ();
200                                 Buffer.BlockCopy (data, index + 1, data, index, data.Length - index - 1);
201                                 Alloc (--length, true);
202                         }
203                         finally {
204                                 Encrypt ();
205                         }
206                 }
207
208                 public void SetAt (int index, char c)
209                 {
210                         if (disposed)
211                                 throw new ObjectDisposedException ("SecureString");
212                         if (read_only) {
213                                 throw new InvalidOperationException (Locale.GetText (
214                                         "SecureString is read-only."));
215                         }
216                         if ((index < 0) || (index >= length))
217                                 throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
218
219                         try {
220                                 Decrypt ();
221                                 int n = index * 2;
222                                 data[n++] = (byte) (c >> 8);
223                                 data[n] = (byte) c;
224                         }
225                         finally {
226                                 Encrypt ();
227                         }
228                 }
229
230                 // internal/private stuff
231
232                 [MonoTODO ("ProtectedMemory is in System.Security.dll - move this into the runtime/icall")]
233                 private void Encrypt ()
234                 {
235                         if ((data != null) && (data.Length > 0)) {
236 //                              ProtectedMemory.Protect (data, MemoryProtectionScope.SameProcess);
237                         }
238                 }
239
240                 [MonoTODO ("ProtectedMemory is in System.Security.dll - move this into the runtime/icall")]
241                 private void Decrypt ()
242                 {
243                         if ((data != null) && (data.Length > 0)) {
244 //                              ProtectedMemory.Unprotect (data, MemoryProtectionScope.SameProcess);
245                         }
246                 }
247
248                 // note: realloc only work for bigger buffers. Clear will 
249                 // reset buffers to default (and small) size.
250                 private void Alloc (int length, bool realloc) 
251                 {
252                         if ((length < 0) || (length > MaxSize))
253                                 throw new ArgumentOutOfRangeException ("length", "< 0 || > 65536");
254
255                         // (size / blocksize) + 1 * blocksize
256                         // where size = length * 2 (unicode) and blocksize == 16 (ProtectedMemory)
257                         // length * 2 (unicode) / 16 (blocksize)
258                         int size = (length >> 3) + (((length & 0x7) == 0) ? 0 : 1) << 4;
259
260                         // is re-allocation necessary ? (i.e. grow or shrink 
261                         // but do not re-allocate the same amount of memory)
262                         if (realloc && (data != null) && (size == data.Length))
263                                 return;
264
265                         if (realloc) {
266                                 // copy, then clear
267                                 byte[] newdata = new byte[size];
268                                 Array.Copy (data, 0, newdata, 0, Math.Min (data.Length, newdata.Length));
269                                 Array.Clear (data, 0, data.Length);
270                                 data = newdata;
271                         } else {
272                                 data = new byte[size];
273                         }
274                 }
275
276                 // dangerous method (put a LinkDemand on it)
277                 internal byte[] GetBuffer ()
278                 {
279                         byte[] secret = new byte[length << 1];
280                         try {
281                                 Decrypt ();
282                                 Buffer.BlockCopy (data, 0, secret, 0, secret.Length);
283                         }
284                         finally {
285                                 Encrypt ();
286                         }
287                         // NOTE: CALLER IS RESPONSIBLE TO ZEROIZE THE DATA
288                         return secret;
289                 }
290         }
291 }
292
293 #endif