Make a copy of the old ZipLib
[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")]
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                 }
79
80                 // properties
81
82                 public int Length {
83                         get {
84                                 if (disposed)
85                                         throw new ObjectDisposedException ("SecureString");
86                                 return length;
87                         }
88                 }
89
90                 public void AppendChar (char c)
91                 {
92                         if (disposed)
93                                 throw new ObjectDisposedException ("SecureString");
94                         if (read_only) {
95                                 throw new InvalidOperationException (Locale.GetText (
96                                         "SecureString is read-only."));
97                         }
98                         if (length == MaxSize)
99                                 throw new ArgumentOutOfRangeException ("length", "> 65536");
100
101                         try {
102                                 Decrypt ();
103                                 if (length >= data.Length) {
104                                         Alloc (length + 1, true);
105                                 }
106                                 int n = length * 2;
107                                 data[n++] = (byte) (c >> 8);
108                                 data[n++] = (byte) c;
109                                 length++;
110                         }
111                         finally {
112                                 Encrypt ();
113                         }
114                 }
115
116                 public void Clear ()
117                 {
118                         if (disposed)
119                                 throw new ObjectDisposedException ("SecureString");
120                         if (read_only) {
121                                 throw new InvalidOperationException (Locale.GetText (
122                                         "SecureString is read-only."));
123                         }
124
125                         Array.Clear (data, 0, data.Length);
126                         length = 0;
127                 }
128
129                 public SecureString Copy () 
130                 {
131                         SecureString ss = new SecureString ();
132                         ss.data = (byte[]) data.Clone ();
133                         return ss;
134                 }
135
136                 public void Dispose ()
137                 {
138                         disposed = true;
139                         // don't call clear because we could be either in read-only 
140                         // or already disposed - but DO CLEAR the data
141                         if (data != null) {
142                                 Array.Clear (data, 0, data.Length);
143                                 data = null;
144                         }
145                         length = 0;
146                 }
147
148                 public void InsertAt (int index, char c)
149                 {
150                         if (disposed)
151                                 throw new ObjectDisposedException ("SecureString");
152                         if (read_only) {
153                                 throw new InvalidOperationException (Locale.GetText (
154                                         "SecureString is read-only."));
155                         }
156                         if ((index < 0) || (index > length))
157                                 throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
158                         // insert increments length
159                         if (length >= MaxSize) {
160                                 string msg = Locale.GetText ("Maximum string size is '{0}'.", MaxSize);
161                                 throw new ArgumentOutOfRangeException ("index", msg);
162                         }
163
164                         try {
165                                 Decrypt ();
166                                 // TODO
167                         }
168                         finally {
169                                 Encrypt ();
170                         }
171                 }
172
173                 public bool IsReadOnly ()
174                 {
175                         if (disposed)
176                                 throw new ObjectDisposedException ("SecureString");
177                         return read_only;
178                 }
179
180                 public void MakeReadOnly ()
181                 {
182                         read_only = true;
183                 }
184
185                 public void RemoveAt (int index)
186                 {
187                         if (disposed)
188                                 throw new ObjectDisposedException ("SecureString");
189                         if (read_only) {
190                                 throw new InvalidOperationException (Locale.GetText (
191                                         "SecureString is read-only."));
192                         }
193                         if ((index < 0) || (index >= length))
194                                 throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
195
196                         try {
197                                 Decrypt ();
198                                 Buffer.BlockCopy (data, index, data, index - 1, data.Length - index);
199                                 length--;
200                         }
201                         finally {
202                                 Encrypt ();
203                         }
204                 }
205
206                 public void SetAt (int index, char c)
207                 {
208                         if (disposed)
209                                 throw new ObjectDisposedException ("SecureString");
210                         if (read_only) {
211                                 throw new InvalidOperationException (Locale.GetText (
212                                         "SecureString is read-only."));
213                         }
214                         if ((index < 0) || (index >= length))
215                                 throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
216
217                         try {
218                                 Decrypt ();
219                                 int n = index * 2;
220                                 data[n++] = (byte) (c >> 8);
221                                 data[n] = (byte) c;
222                         }
223                         finally {
224                                 Encrypt ();
225                         }
226                 }
227
228                 // internal/private stuff
229
230                 private void Encrypt ()
231                 {
232                         throw new NotSupportedException ();
233 // ProtectedMemory was moved into System.Security.dll
234 //                      ProtectedMemory.Protect (data, MemoryProtectionScope.SameProcess);
235                 }
236
237                 private void Decrypt ()
238                 {
239                         throw new NotSupportedException ();
240 // ProtectedMemory was moved into System.Security.dll
241 //                      ProtectedMemory.Unprotect (data, MemoryProtectionScope.SameProcess);
242                 }
243
244                 // note: realloc only work for bigger buffers. Clear will 
245                 // reset buffers to default (and small) size.
246                 private void Alloc (int length, bool realloc) 
247                 {
248                         if ((length < 0) || (length > MaxSize))
249                                 throw new ArgumentOutOfRangeException ("length", "< 0 || > 65536");
250
251                         // (size / blocksize) + 1 * blocksize
252                         // where size = length * 2 (unicode) and blocksize == 16 (ProtectedMemory)
253                         // length * 2 (unicode) / 16 (blocksize)
254                         int size = (length >> 3) + (((length & 0x7) == 0) ? 0 : 1) << 4;
255                         if (realloc) {
256                                 // copy, then clear
257                                 byte[] newdata = new byte[size];
258                                 Array.Copy (data, 0, newdata, 0, data.Length);
259                                 Array.Clear (data, 0, data.Length);
260                                 data = newdata;
261                         } else {
262                                 data = new byte[size];
263                         }
264                 }
265
266                 // dangerous method (put a LinkDemand on it)
267                 internal byte[] GetBuffer ()
268                 {
269                         byte[] secret = null;
270                         try {
271                                 Decrypt ();
272                                 secret = (byte[]) data.Clone ();
273                         }
274                         finally {
275                                 Encrypt ();
276                         }
277                         // NOTE: CALLER IS RESPONSIBLE TO ZEROIZE THE DATA
278                         return secret;
279                 }
280         }
281 }
282
283 #endif