Browse Source

2005-10-20 Sebastien Pouliot <[email protected]>

	* CryptographicAttributeCollection.cs: Fixed Add and Remove methods.
	* ProtectedMemory.cs: Implemented (unmanaged) for Windows only.
	* ProtectedData.cs: Implemented for everything except Windows ;-) by
	using the new ManagedProtection class.


svn path=/trunk/mcs/; revision=52000
Sebastien Pouliot 20 years ago
parent
commit
de26591ab9

+ 7 - 0
mcs/class/System.Security/System.Security.Cryptography/ChangeLog

@@ -1,3 +1,10 @@
+2005-10-20  Sebastien Pouliot  <[email protected]>
+
+	* CryptographicAttributeCollection.cs: Fixed Add and Remove methods.
+	* ProtectedMemory.cs: Implemented (unmanaged) for Windows only.
+	* ProtectedData.cs: Implemented for everything except Windows ;-) by
+	using the new ManagedProtection class.
+
 2005-09-26  Sebastien Pouliot  <[email protected]>
 
 	* Asn*.cs, Oid*.cs: Moved to System.dll

+ 30 - 10
mcs/class/System.Security/System.Security.Cryptography/CryptographicAttributeCollection.cs

@@ -38,13 +38,13 @@ namespace System.Security.Cryptography {
 		private ArrayList _list;
 
 		public CryptographicAttributeObjectCollection () 
-		{
+		{
 			_list = new ArrayList ();
 		}
 
 		public CryptographicAttributeObjectCollection (CryptographicAttributeObject attribute)
 			: this ()
-		{
+		{
 			_list.Add (attribute);
 		}
 
@@ -63,7 +63,7 @@ namespace System.Security.Cryptography {
 		}
 
 		public object SyncRoot {
-			get { return _list.SyncRoot; }
+			get { return this; }
 		}
 
 		// methods
@@ -71,17 +71,34 @@ namespace System.Security.Cryptography {
 		public int Add (AsnEncodedData asnEncodedData)
 		{
 			if (asnEncodedData == null)
-				throw new ArgumentNullException ("asnEncodedData");
-
-			return _list.Add (asnEncodedData);
+				throw new ArgumentNullException ("asnEncodedData");
+
+			AsnEncodedDataCollection coll = new AsnEncodedDataCollection (asnEncodedData);
+			return Add (new CryptographicAttributeObject (asnEncodedData.Oid, coll));
 		}
 
 		public int Add (CryptographicAttributeObject attribute)
 		{
 			if (attribute == null)
-				throw new ArgumentNullException ("attribute");
-
-			return _list.Add (attribute);
+				throw new ArgumentNullException ("attribute");
+
+			int existing = -1;
+			string oid = attribute.Oid.Value;
+			for (int i=0; i < _list.Count; i++) {
+				if ((_list[i] as CryptographicAttributeObject).Oid.Value == oid) {
+					existing = i;
+					break;
+				}
+			}
+			if (existing >= 0) {
+				CryptographicAttributeObject cao = this[existing];
+				foreach (AsnEncodedData value in attribute.Values) {
+					cao.Values.Add (value);
+				}
+				return existing;
+			} else {
+				return _list.Add (attribute);
+			}
 		}
 
 		public void CopyTo (CryptographicAttributeObject[] array, int index)
@@ -105,7 +122,10 @@ namespace System.Security.Cryptography {
 		}
 
 		public void Remove (CryptographicAttributeObject attribute) 
-		{
+		{
+			if (attribute == null)
+				throw new ArgumentNullException ("attribute");
+
 			_list.Remove (attribute);
 		}
 	}

+ 98 - 18
mcs/class/System.Security/System.Security.Cryptography/ProtectedData.cs

@@ -1,11 +1,11 @@
 //
 // ProtectedData.cs: Protect (encrypt) data without (user involved) key management
 //
-// Author:
-//	Sebastien Pouliot ([email protected])
+// Author:
+//	Sebastien Pouliot  <[email protected]>
 //
-// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
-// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
+// (C) 2003 Motus Technologies Inc. (http://www.motus.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
@@ -27,9 +27,12 @@
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
 
-#if NET_2_0
+#if NET_2_0
+
+using System.Runtime.InteropServices;
+using System.Security.Permissions;
 
-using System;
+using Mono.Security.Cryptography;
 
 namespace System.Security.Cryptography {
 
@@ -43,29 +46,106 @@ namespace System.Security.Cryptography {
 		{
 		}
 
-		// FIXME: interop could be important under windows - if one application protect some data using
-		// mono and another one unprotects it using ms.net
-
-		[MonoTODO ("interop with MS implementation ?")]
+		[MonoTODO ("not (yet) supported on Windows")]
+// FIXME	[DataProtectionPermission (SecurityAction.Demand, ProtectData = true)]
 		public static byte[] Protect (byte[] userData, byte[] optionalEntropy, DataProtectionScope scope) 
 		{
 			if (userData == null)
 				throw new ArgumentNullException ("userData");
 
-			// on Windows this is supported only under 2000 and later OS
-			throw new PlatformNotSupportedException ();
-		}
-
-		[MonoTODO ("interop with MS implementation ?")]
+			// on Windows this is supported only under 2000 and later OS
+			Check (scope);
+
+			switch (impl) {
+			case DataProtectionImplementation.ManagedProtection:
+				try {
+					return ManagedProtection.Protect (userData, optionalEntropy, scope);
+				}
+				catch (Exception e) {
+					string msg = Locale.GetText ("Data protection failed.");
+					throw new CryptographicException (msg, e);
+				}
+			case DataProtectionImplementation.Win32CryptoProtect:
+			default:
+				throw new PlatformNotSupportedException ();
+			}
+		}
+
+		[MonoTODO ("not (yet) supported on Windows")]
+// FIXME	[DataProtectionPermission (SecurityAction.Demand, UnprotectData = true)]
 		public static byte[] Unprotect (byte[] encryptedData, byte[] optionalEntropy, DataProtectionScope scope) 
 		{
 			if (encryptedData == null)
 				throw new ArgumentNullException ("encryptedData");
 
-			// on Windows this is supported only under 2000 and later OS
-			throw new PlatformNotSupportedException ();
+			// on Windows this is supported only under 2000 and later OS
+			Check (scope);
+
+			switch (impl) {
+			case DataProtectionImplementation.ManagedProtection:
+				try {
+					return ManagedProtection.Unprotect (encryptedData, optionalEntropy, scope);
+				}
+				catch (Exception e) {
+					string msg = Locale.GetText ("Data unprotection failed.");
+					throw new CryptographicException (msg, e);
+				}
+			case DataProtectionImplementation.Win32CryptoProtect:
+			default:
+				throw new PlatformNotSupportedException ();
+			}
 		}
- 	} 
+
+		// private stuff
+
+		enum DataProtectionImplementation {
+			Unknown,
+			Win32CryptoProtect,
+			ManagedProtection,
+			Unsupported = Int32.MinValue
+		}
+
+		private static DataProtectionImplementation impl;
+
+		private static void Detect ()
+		{
+			OperatingSystem os = Environment.OSVersion;
+			switch (os.Platform) {
+			case PlatformID.Win32NT:
+				Version v = os.Version;
+				if (v.Major < 5) {
+					impl = DataProtectionImplementation.Unsupported;
+				} else {
+					// Windows 2000 (5.0) and later
+					impl = DataProtectionImplementation.Win32CryptoProtect;
+				}
+				break;
+			case PlatformID.Unix:
+				impl = DataProtectionImplementation.ManagedProtection;
+				break;
+			default:
+				impl = DataProtectionImplementation.Unsupported;
+				break;
+			}
+		}
+
+		private static void Check (DataProtectionScope scope)
+		{
+			if ((scope < DataProtectionScope.CurrentUser) || (scope > DataProtectionScope.LocalMachine)) {
+				string msg = Locale.GetText ("Invalid enum value '{0}' for '{1}'.", 
+					scope, "DataProtectionScope");
+				throw new ArgumentException (msg, "scope");
+			}
+
+			switch (impl) {
+			case DataProtectionImplementation.Unknown:
+				Detect ();
+				break;
+			case DataProtectionImplementation.Unsupported:
+				throw new PlatformNotSupportedException ();
+			}
+		}
+	}
 }
 
 #endif

+ 151 - 19
mcs/class/System.Security/System.Security.Cryptography/ProtectedMemory.cs

@@ -2,10 +2,10 @@
 // ProtectedMemory.cs: Protect (encrypt) memory without (user involved) key management
 //
 // Author:
-//	Sebastien Pouliot ([email protected])
+//	Sebastien Pouliot  <[email protected]>
 //
 // (C) 2003 Motus Technologies Inc. (http://www.motus.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
@@ -29,7 +29,9 @@
 
 #if NET_2_0
 
-using System;
+using System.Runtime.InteropServices;
+using System.Security;
+using System.Security.Permissions;
 
 namespace System.Security.Cryptography {
 
@@ -43,29 +45,159 @@ namespace System.Security.Cryptography {
 		{
 		}
 
-		[MonoTODO]
+		[MonoTODO ("only supported on Windows 2000 SP3 and later")]
+// FIXME	[DataProtectionPermission (SecurityAction.Demand, ProtectMemory = true)]
 		public static void Protect (byte[] userData, MemoryProtectionScope scope) 
 		{
 			if (userData == null)
-				throw new ArgumentNullException ("userData");
-			if (userData.Length % 16 != 0)
-				throw new CryptographicException ("not a multiple of 16 bytes");
-
-			// on Windows this is supported only under XP and later OS
-			throw new PlatformNotSupportedException ();
-		}
-
-		[MonoTODO]
+				throw new ArgumentNullException ("userData");
+
+			Check (userData.Length, scope);
+			try {
+				uint flags = (uint) scope;
+				uint length = (uint) userData.Length;
+
+				switch (impl) {
+				case MemoryProtectionImplementation.Win32RtlEncryptMemory:
+					int err = RtlEncryptMemory (userData, length, flags);
+					if (err < 0) {
+						string msg = Locale.GetText ("Error. NTSTATUS = {0}.", err);
+						throw new CryptographicException (msg);
+					}
+					break;
+				case MemoryProtectionImplementation.Win32CryptoProtect:
+					bool result = CryptProtectMemory (userData, length, flags);
+					if (!result)
+						throw new CryptographicException (Marshal.GetLastWin32Error ());
+					break;
+				default:
+					throw new PlatformNotSupportedException ();
+				}
+			}
+			catch {
+				// Windows 2000 before SP3 will throw
+				impl = MemoryProtectionImplementation.Unsupported;
+				throw new PlatformNotSupportedException ();
+			}
+		}
+
+		[MonoTODO ("only supported on Windows 2000 SP3 and later")]
+// FIXME	[DataProtectionPermission (SecurityAction.Demand, UnprotectMemory = true)]
 		public static void Unprotect (byte[] encryptedData, MemoryProtectionScope scope) 
 		{
 			if (encryptedData == null)
-				throw new ArgumentNullException ("encryptedData");
-			if (encryptedData.Length % 16 != 0)
-				throw new CryptographicException ("not a multiple of 16 bytes");
-
-			// on Windows this is supported only under XP and later OS
-			throw new PlatformNotSupportedException ();
+				throw new ArgumentNullException ("encryptedData");
+
+			Check (encryptedData.Length, scope);
+			try {
+				uint flags = (uint) scope;
+				uint length = (uint) encryptedData.Length;
+
+				switch (impl) {
+				case MemoryProtectionImplementation.Win32RtlEncryptMemory:
+					int err = RtlDecryptMemory (encryptedData, length, flags);
+					if (err < 0) {
+						string msg = Locale.GetText ("Error. NTSTATUS = {0}.", err);
+						throw new CryptographicException (msg);
+					}
+					break;
+				case MemoryProtectionImplementation.Win32CryptoProtect:
+					bool result = CryptUnprotectMemory (encryptedData, length, flags);
+					if (!result)
+						throw new CryptographicException (Marshal.GetLastWin32Error ());
+					break;
+				default:
+					throw new PlatformNotSupportedException ();
+				}
+			}
+			catch {
+				// Windows 2000 before SP3 will throw
+				impl = MemoryProtectionImplementation.Unsupported;
+				throw new PlatformNotSupportedException ();
+			}
 		}
+
+		// private stuff
+
+		private const int BlockSize = 16;
+
+		enum MemoryProtectionImplementation {
+			Unknown,
+			Win32RtlEncryptMemory,
+			Win32CryptoProtect,
+			Unsupported = Int32.MinValue
+		}
+
+		private static MemoryProtectionImplementation impl;
+
+		private static void Detect ()
+		{
+			OperatingSystem os = Environment.OSVersion;
+			switch (os.Platform) {
+			case PlatformID.Win32NT:
+				Version v = os.Version;
+				if (v.Major < 5) {
+					impl = MemoryProtectionImplementation.Unsupported;
+				} else if (v.Major == 5) {
+					if (v.Minor < 2) {
+						// 2000 (5.0) Service Pack 3 and XP (5.1)
+						impl = MemoryProtectionImplementation.Win32RtlEncryptMemory;
+					} else {
+						impl = MemoryProtectionImplementation.Win32CryptoProtect;
+					}
+				} else {
+					// vista (6.0) and later
+					impl = MemoryProtectionImplementation.Win32CryptoProtect;
+				}
+				break;
+			default:
+				impl = MemoryProtectionImplementation.Unsupported;
+				break;
+			}
+		}
+
+		private static void Check (int size, MemoryProtectionScope scope)
+		{
+			if (size % BlockSize != 0) {
+				string msg = Locale.GetText ("Not a multiple of {0} bytes.", BlockSize);
+				throw new CryptographicException (msg);
+			}
+
+			if ((scope < MemoryProtectionScope.SameProcess) || (scope > MemoryProtectionScope.SameLogon)) {
+				string msg = Locale.GetText ("Invalid enum value for '{0}'.", "MemoryProtectionScope");
+				throw new ArgumentException (msg, "scope");
+			}
+
+			switch (impl) {
+			case MemoryProtectionImplementation.Unknown:
+				Detect ();
+				break;
+			case MemoryProtectionImplementation.Unsupported:
+				throw new PlatformNotSupportedException ();
+			}
+		}
+
+		// http://msdn.microsoft.com/library/en-us/dncode/html/secure06122003.asp
+		// Summary: CryptProtectMemory and CryptUnprotectMemory exists only in Windows 2003 +
+		// but they are available in advapi32.dll as RtlEncryptMemory (SystemFunction040) and
+		// RtlDecryptMemory (SystemFunction041) since Windows 2000 SP 3. Sadly both can disappear
+		// anytime with newer OS so we include support for Crypt[Unp|P]rotectMemory too.
+
+		[SuppressUnmanagedCodeSecurity]
+		[DllImport ("advapi32.dll", EntryPoint="SystemFunction040", SetLastError = true, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
+		private static extern int RtlEncryptMemory (byte[] pData, uint cbData, uint dwFlags);
+
+		[SuppressUnmanagedCodeSecurity]
+		[DllImport ("advapi32.dll", EntryPoint = "SystemFunction041", SetLastError = true, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
+		private static extern int RtlDecryptMemory (byte[] pData, uint cbData, uint dwFlags);
+
+		[SuppressUnmanagedCodeSecurity]
+		[DllImport ("crypt32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
+		private static extern bool CryptProtectMemory (byte[] pData, uint cbData, uint dwFlags);
+
+		[SuppressUnmanagedCodeSecurity]
+		[DllImport ("crypt32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Auto)]
+		private static extern bool CryptUnprotectMemory (byte[] pData, uint cbData, uint dwFlags);
 	} 
 }