// Authors
// Sebastien Pouliot <sebastien@ximian.com>
//
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// Copyright (C) 2004-2005 Novell, Inc (http://www.novell.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-#if NET_2_0
-
-using System;
using System.Globalization;
+using System.Reflection;
+using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.ConstrainedExecution;
using System.Security.Cryptography;
using System.Security.Permissions;
-
-using Mono.Security.Cryptography;
+#if NET_4_0
+using System.Runtime.ExceptionServices;
+#endif
namespace System.Security {
- [MonoTODO ("current version ISN'T encrypted")]
- [ComVisible (false)]
- public sealed class SecureString : CriticalFinalizerObject, IDisposable {
+ [MonoTODO ("work in progress - encryption is missing")]
+ public sealed class SecureString : IDisposable {
- static private SymmetricAlgorithm _cipher;
- static private int _blockSize;
+ private const int BlockSize = 16;
+ private const int MaxSize = 65536;
- private int _length;
- private bool _disposed;
- private bool _readonly;
- private byte[] _enc; // encrypted (permanent buffer)
- private char[] _dec; // decrypted (temporary buffer)
- private byte[] _iv; // initialization vector
+ private int length;
+ private bool disposed;
+ private bool read_only;
+ private byte[] data;
static SecureString ()
{
- _cipher = SymmetricAlgorithm.Create ();
- _cipher.Mode = CipherMode.CBC;
- _cipher.Padding = PaddingMode.PKCS7;
- _blockSize = _cipher.BlockSize << 3; // in bytes
+ // ProtectedMemory has been moved to System.Security.dll
+ // we use reflection to call it (if available) or we'll
+ // throw an exception
}
public SecureString ()
{
- _iv = KeyBuilder.IV (_blockSize);
- // default size
- Alloc (_blockSize >> 1, false);
+ Alloc (BlockSize >> 1, false);
}
[CLSCompliant (false)]
{
if (value == null)
throw new ArgumentNullException ("value");
+ if ((length < 0) || (length > MaxSize))
+ throw new ArgumentOutOfRangeException ("length", "< 0 || > 65536");
- _iv = KeyBuilder.IV (_blockSize);
+ this.length = length; // real length
Alloc (length, false);
- for (_length=1; _length <= length; _length++)
- _dec [_length] = *value++;
+ int n = 0;
+ for (int i = 0; i < length; i++) {
+ char c = *value++;
+ data[n++] = (byte) (c >> 8);
+ data[n++] = (byte) c;
+ }
Encrypt ();
}
- ~SecureString ()
- {
- Dispose ();
- }
-
// properties
public int Length {
get {
- if (_disposed)
+ if (disposed)
throw new ObjectDisposedException ("SecureString");
- return _length;
+ return length;
}
}
+#if NET_4_0
+ [HandleProcessCorruptedStateExceptions]
+#endif
public void AppendChar (char c)
{
- if (_disposed)
+ if (disposed)
throw new ObjectDisposedException ("SecureString");
- if (_readonly) {
+ if (read_only) {
throw new InvalidOperationException (Locale.GetText (
"SecureString is read-only."));
}
- if (_length == 65535)
+ if (length == MaxSize)
throw new ArgumentOutOfRangeException ("length", "> 65536");
try {
Decrypt ();
- if (_length >= _dec.Length) {
- Alloc (_length + 1, true);
- }
- _dec [_length++] = c;
- Encrypt ();
+ int n = length * 2;
+ Alloc (++length, true);
+ data[n++] = (byte) (c >> 8);
+ data[n++] = (byte) c;
}
- catch {
- Array.Clear (_dec, 0, _dec.Length);
- _length = 0;
- throw;
+ finally {
+ Encrypt ();
}
}
public void Clear ()
{
- if (_disposed)
+ if (disposed)
throw new ObjectDisposedException ("SecureString");
- if (_readonly) {
+ if (read_only) {
throw new InvalidOperationException (Locale.GetText (
"SecureString is read-only."));
}
- Array.Clear (_dec, 0, _dec.Length); // should be empty
- Array.Clear (_enc, 0, _enc.Length);
- // return to default sizes
- _dec = new char [_blockSize >> 1];
- _enc = new byte [_blockSize];
- _length = 0;
- // get a new IV
- _iv = KeyBuilder.IV (_blockSize);
+ Array.Clear (data, 0, data.Length);
+ length = 0;
}
public SecureString Copy ()
{
SecureString ss = new SecureString ();
- try {
- Decrypt ();
- ss._dec = (char[]) _dec.Clone ();
- Array.Clear (_dec, 0, _dec.Length);
- ss._enc = new byte [_dec.Length >> 1];
- ss.Encrypt ();
- }
- catch {
- Array.Clear (_dec, 0, _dec.Length);
- if (ss._dec != null)
- Array.Clear (ss._dec, 0, ss._dec.Length);
- }
+ ss.data = (byte[]) data.Clone ();
+ ss.length = length;
return ss;
}
public void Dispose ()
{
- if (_dec != null) {
- Array.Clear (_enc, 0, _enc.Length);
- Array.Clear (_dec, 0, _dec.Length);
- _dec = null;
- _length = 0;
+ disposed = true;
+ // don't call clear because we could be either in read-only
+ // or already disposed - but DO CLEAR the data
+ if (data != null) {
+ Array.Clear (data, 0, data.Length);
+ data = null;
}
- _disposed = true;
+ length = 0;
}
+#if NET_4_0
+ [HandleProcessCorruptedStateExceptions]
+#endif
public void InsertAt (int index, char c)
{
- if (_disposed)
+ if (disposed)
throw new ObjectDisposedException ("SecureString");
- if ((index < 0) || (index >= _length))
- throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
- if (_readonly) {
+ if (read_only) {
throw new InvalidOperationException (Locale.GetText (
"SecureString is read-only."));
}
+ if ((index < 0) || (index > length))
+ throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
+ // insert increments length
+ if (length >= MaxSize) {
+ string msg = Locale.GetText ("Maximum string size is '{0}'.", MaxSize);
+ throw new ArgumentOutOfRangeException ("index", msg);
+ }
try {
Decrypt ();
- // TODO
- Encrypt ();
+ Alloc (++length, true);
+ int n = index * 2;
+ Buffer.BlockCopy (data, n, data, n + 2, data.Length - n - 2);
+ data[n++] = (byte) (c >> 8);
+ data[n] = (byte) c;
}
- catch {
- Array.Clear (_dec, 0, _dec.Length);
- _length = 0;
- throw;
+ finally {
+ Encrypt ();
}
}
public bool IsReadOnly ()
{
- if (_disposed)
+ if (disposed)
throw new ObjectDisposedException ("SecureString");
- return _readonly;
+ return read_only;
}
public void MakeReadOnly ()
{
- _readonly = true;
+ read_only = true;
}
+#if NET_4_0
+ [HandleProcessCorruptedStateExceptions]
+#endif
public void RemoveAt (int index)
{
- if (_disposed)
+ if (disposed)
throw new ObjectDisposedException ("SecureString");
- if ((index < 0) || (index >= _length))
- throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
- if (_readonly) {
+ if (read_only) {
throw new InvalidOperationException (Locale.GetText (
"SecureString is read-only."));
}
+ if ((index < 0) || (index >= length))
+ throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
try {
Decrypt ();
- Buffer.BlockCopy (_dec, index, _dec, index - 1, _dec.Length - index);
- _length--;
- Encrypt ();
+ Buffer.BlockCopy (data, index + 1, data, index, data.Length - index - 1);
+ Alloc (--length, true);
}
- catch {
- Array.Clear (_dec, 0, _dec.Length);
- _length = 0;
- throw;
+ finally {
+ Encrypt ();
}
}
+#if NET_4_0
+ [HandleProcessCorruptedStateExceptions]
+#endif
public void SetAt (int index, char c)
{
- if (_disposed)
+ if (disposed)
throw new ObjectDisposedException ("SecureString");
- if ((index < 0) || (index >= _length))
- throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
- if (_readonly) {
+ if (read_only) {
throw new InvalidOperationException (Locale.GetText (
"SecureString is read-only."));
}
+ if ((index < 0) || (index >= length))
+ throw new ArgumentOutOfRangeException ("index", "< 0 || > length");
try {
Decrypt ();
- _dec [index] = c;
- Encrypt ();
+ int n = index * 2;
+ data[n++] = (byte) (c >> 8);
+ data[n] = (byte) c;
}
- catch {
- Array.Clear (_dec, 0, _dec.Length);
- _length = 0;
- throw;
+ finally {
+ Encrypt ();
}
}
// internal/private stuff
+// [MethodImplAttribute(MethodImplOptions.InternalCall)]
+// extern static void EncryptInternal (byte [] data, object scope);
+
+// [MethodImplAttribute(MethodImplOptions.InternalCall)]
+// extern static void DecryptInternal (byte [] data, object scope);
+
+// static readonly object scope = Enum.Parse (
+// Assembly.Load (Consts.AssemblySystem_Security)
+// .GetType ("System.Security.Cryptography.MemoryProtectionScope"), "SameProcess");
+
+ // Note that ProtectedMemory is not supported on non-Windows environment right now.
+ private void Encrypt ()
+ {
+ if ((data != null) && (data.Length > 0)) {
+ // It somehow causes nunit test breakage
+ // EncryptInternal (data, scope);
+ }
+ }
+
+ // Note that ProtectedMemory is not supported on non-Windows environment right now.
+ private void Decrypt ()
+ {
+ if ((data != null) && (data.Length > 0)) {
+ // It somehow causes nunit test breakage
+ // DecryptInternal (data, scope);
+ }
+ }
+
// note: realloc only work for bigger buffers. Clear will
// reset buffers to default (and small) size.
private void Alloc (int length, bool realloc)
{
- if ((length < 0) || (length > 65536))
+ if ((length < 0) || (length > MaxSize))
throw new ArgumentOutOfRangeException ("length", "< 0 || > 65536");
- int size = (((length * 2) / _blockSize) + 1) * _blockSize;
+ // (size / blocksize) + 1 * blocksize
+ // where size = length * 2 (unicode) and blocksize == 16 (ProtectedMemory)
+ // length * 2 (unicode) / 16 (blocksize)
+ int size = (length >> 3) + (((length & 0x7) == 0) ? 0 : 1) << 4;
- char[] dec = new char [size >> 1];
- byte[] enc = new byte [size];
+ // is re-allocation necessary ? (i.e. grow or shrink
+ // but do not re-allocate the same amount of memory)
+ if (realloc && (data != null) && (size == data.Length))
+ return;
if (realloc) {
// copy, then clear
- Array.Copy (_dec, 0, dec, 0, _dec.Length);
- Array.Clear (_dec, 0, _dec.Length);
- _dec = null;
-
- Array.Copy (_enc, 0, enc, 0, _enc.Length);
- Array.Clear (_enc, 0, _enc.Length);
- _enc = null;
+ byte[] newdata = new byte[size];
+ Array.Copy (data, 0, newdata, 0, Math.Min (data.Length, newdata.Length));
+ Array.Clear (data, 0, data.Length);
+ data = newdata;
+ } else {
+ data = new byte[size];
}
-
- _dec = dec;
- _enc = enc;
}
- [MonoTODO ("no decryption - data is only copied")]
- private void Decrypt ()
- {
- Buffer.BlockCopy (_enc, 0, _dec, 0, _enc.Length);
- }
-
- [MonoTODO ("no encryption - data is only copied")]
- private void Encrypt ()
+ // dangerous method (put a LinkDemand on it)
+ internal byte[] GetBuffer ()
{
- Buffer.BlockCopy (_dec, 0, _enc, 0, _enc.Length);
- Array.Clear (_dec, 0, _dec.Length);
+ byte[] secret = new byte[length << 1];
+ try {
+ Decrypt ();
+ Buffer.BlockCopy (data, 0, secret, 0, secret.Length);
+ }
+ finally {
+ Encrypt ();
+ }
+ // NOTE: CALLER IS RESPONSIBLE TO ZEROIZE THE DATA
+ return secret;
}
}
}
-
-#endif