1 // <OWNER>Microsoft</OWNER>
2 namespace System.Security {
3 using System.Security.Cryptography;
4 using System.Runtime.InteropServices;
5 #if FEATURE_CORRUPTING_EXCEPTIONS
6 using System.Runtime.ExceptionServices;
7 #endif // FEATURE_CORRUPTING_EXCEPTIONS
10 using System.Runtime.CompilerServices;
11 using System.Security.Permissions;
12 using System.Runtime.ConstrainedExecution;
13 using System.Runtime.Versioning;
14 using Microsoft.Win32.SafeHandles;
15 using System.Diagnostics.Contracts;
17 public sealed class SecureString: IDisposable {
18 [System.Security.SecurityCritical] // auto-generated
19 private SafeBSTRHandle m_buffer;
20 [ContractPublicPropertyName("Length")]
22 private bool m_readOnly;
23 private bool m_encrypted;
25 static bool supportedOnCurrentPlatform = EncryptionSupported();
27 const int BlockSize = (int)Win32Native.CRYPTPROTECTMEMORY_BLOCK_SIZE /2; // a char is two bytes
28 const int MaxLength = 65536;
29 const uint ProtectionScope = Win32Native.CRYPTPROTECTMEMORY_SAME_PROCESS;
31 [System.Security.SecuritySafeCritical] // auto-generated
36 [System.Security.SecurityCritical] // auto-generated
37 unsafe static bool EncryptionSupported() {
38 // check if the enrypt/decrypt function is supported on current OS
39 bool supported = true;
41 Win32Native.SystemFunction041(
42 SafeBSTRHandle.Allocate(null , (int)Win32Native.CRYPTPROTECTMEMORY_BLOCK_SIZE),
43 Win32Native.CRYPTPROTECTMEMORY_BLOCK_SIZE,
44 Win32Native.CRYPTPROTECTMEMORY_SAME_PROCESS);
46 catch (EntryPointNotFoundException) {
52 [System.Security.SecurityCritical] // auto-generated
53 internal SecureString(SecureString str) {
54 AllocateBuffer(str.BufferLength);
55 SafeBSTRHandle.Copy(str.m_buffer, this.m_buffer);
56 m_length = str.m_length;
57 m_encrypted = str.m_encrypted;
61 [System.Security.SecuritySafeCritical] // auto-generated
62 public SecureString() {
63 CheckSupportedOnCurrentPlatform();
65 // allocate the minimum block size for calling protectMemory
66 AllocateBuffer(BlockSize);
71 [System.Security.SecurityCritical] // auto-generated
72 #if FEATURE_CORRUPTING_EXCEPTIONS
73 [HandleProcessCorruptedStateExceptions] //
74 #endif // FEATURE_CORRUPTING_EXCEPTIONS
75 private unsafe void InitializeSecureString(char* value, int length)
77 CheckSupportedOnCurrentPlatform();
79 AllocateBuffer(length);
82 byte* bufferPtr = null;
83 RuntimeHelpers.PrepareConstrainedRegions();
86 m_buffer.AcquirePointer(ref bufferPtr);
87 Buffer.Memcpy(bufferPtr, (byte*)value, length * 2);
95 if (bufferPtr != null)
96 m_buffer.ReleasePointer();
102 [System.Security.SecurityCritical] // auto-generated
103 [CLSCompliant(false)]
104 public unsafe SecureString(char* value, int length) {
106 throw new ArgumentNullException("value");
110 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_NeedNonNegNum"));
113 if( length > MaxLength) {
114 throw new ArgumentOutOfRangeException("length", Environment.GetResourceString("ArgumentOutOfRange_Length"));
116 Contract.EndContractBlock();
118 // Refactored since HandleProcessCorruptedStateExceptionsAttribute applies to methods only (yet).
119 InitializeSecureString(value, length);
123 [System.Security.SecuritySafeCritical] // auto-generated
124 [MethodImplAttribute(MethodImplOptions.Synchronized)]
131 [System.Security.SecuritySafeCritical] // auto-generated
132 [MethodImplAttribute(MethodImplOptions.Synchronized)]
133 #if FEATURE_CORRUPTING_EXCEPTIONS
134 [HandleProcessCorruptedStateExceptions] //
135 #endif // FEATURE_CORRUPTING_EXCEPTIONS
136 public void AppendChar(char c) {
140 EnsureCapacity(m_length + 1);
142 RuntimeHelpers.PrepareConstrainedRegions();
145 m_buffer.Write<char>((uint)m_length * sizeof(char), c);
157 // clears the current contents. Only available if writable
158 [System.Security.SecuritySafeCritical] // auto-generated
159 [MethodImplAttribute(MethodImplOptions.Synchronized)]
160 public void Clear() {
165 m_buffer.ClearBuffer();
169 // Do a deep-copy of the SecureString
170 [System.Security.SecuritySafeCritical] // auto-generated
171 [MethodImplAttribute(MethodImplOptions.Synchronized)]
172 public SecureString Copy() {
174 return new SecureString(this);
177 [System.Security.SecuritySafeCritical] // auto-generated
178 [MethodImplAttribute(MethodImplOptions.Synchronized)]
179 public void Dispose() {
180 if(m_buffer != null && !m_buffer.IsInvalid) {
186 [System.Security.SecuritySafeCritical] // auto-generated
187 [MethodImplAttribute(MethodImplOptions.Synchronized)]
188 #if FEATURE_CORRUPTING_EXCEPTIONS
189 [HandleProcessCorruptedStateExceptions] //
190 #endif // FEATURE_CORRUPTING_EXCEPTIONS
191 public void InsertAt( int index, char c ) {
192 if( index < 0 || index > m_length) {
193 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_IndexString"));
195 Contract.EndContractBlock();
200 EnsureCapacity(m_length + 1);
203 byte* bufferPtr = null;
204 RuntimeHelpers.PrepareConstrainedRegions();
207 m_buffer.AcquirePointer(ref bufferPtr);
208 char* pBuffer = (char*)bufferPtr;
210 for (int i = m_length; i > index; i--) {
211 pBuffer[i] = pBuffer[i - 1];
222 if (bufferPtr != null)
223 m_buffer.ReleasePointer();
228 [System.Security.SecuritySafeCritical] // auto-generated
229 [MethodImplAttribute(MethodImplOptions.Synchronized)]
230 public bool IsReadOnly() {
235 [System.Security.SecuritySafeCritical] // auto-generated
236 [MethodImplAttribute(MethodImplOptions.Synchronized)]
237 public void MakeReadOnly() {
242 [System.Security.SecuritySafeCritical] // auto-generated
243 [MethodImplAttribute(MethodImplOptions.Synchronized)]
244 #if FEATURE_CORRUPTING_EXCEPTIONS
245 [HandleProcessCorruptedStateExceptions] //
246 #endif // FEATURE_CORRUPTING_EXCEPTIONS
247 public void RemoveAt( int index ) {
251 if( index < 0 || index >= m_length) {
252 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_IndexString"));
257 byte* bufferPtr = null;
258 RuntimeHelpers.PrepareConstrainedRegions();
262 m_buffer.AcquirePointer(ref bufferPtr);
263 char* pBuffer = (char*)bufferPtr;
265 for (int i = index; i < m_length - 1; i++)
267 pBuffer[i] = pBuffer[i + 1];
269 pBuffer[--m_length] = (char)0;
278 if (bufferPtr != null)
279 m_buffer.ReleasePointer();
284 [System.Security.SecuritySafeCritical] // auto-generated
285 [MethodImplAttribute(MethodImplOptions.Synchronized)]
286 #if FEATURE_CORRUPTING_EXCEPTIONS
287 [HandleProcessCorruptedStateExceptions] //
288 #endif // FEATURE_CORRUPTING_EXCEPTIONS
289 public void SetAt( int index, char c ) {
290 if( index < 0 || index >= m_length) {
291 throw new ArgumentOutOfRangeException("index", Environment.GetResourceString("ArgumentOutOfRange_IndexString"));
293 Contract.EndContractBlock();
294 Contract.Assert(index <= Int32.MaxValue / sizeof(char));
299 RuntimeHelpers.PrepareConstrainedRegions();
302 m_buffer.Write<char>((uint)index * sizeof(char), c);
313 private int BufferLength {
314 [System.Security.SecurityCritical] // auto-generated
316 Contract.Assert(m_buffer != null, "Buffer is not initialized!");
317 return m_buffer.Length;
321 [System.Security.SecurityCritical] // auto-generated
322 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
323 private void AllocateBuffer(int size) {
324 uint alignedSize = GetAlignedSize(size);
326 m_buffer = SafeBSTRHandle.Allocate(null, alignedSize);
327 if (m_buffer.IsInvalid) {
328 throw new OutOfMemoryException();
332 private void CheckSupportedOnCurrentPlatform() {
333 if( !supportedOnCurrentPlatform) {
334 throw new NotSupportedException(Environment.GetResourceString("Arg_PlatformSecureString"));
336 Contract.EndContractBlock();
339 [System.Security.SecurityCritical] // auto-generated
340 private void EnsureCapacity(int capacity) {
341 if( capacity > MaxLength) {
342 throw new ArgumentOutOfRangeException("capacity", Environment.GetResourceString("ArgumentOutOfRange_Capacity"));
344 Contract.EndContractBlock();
346 if( capacity <= m_buffer.Length) {
350 SafeBSTRHandle newBuffer = SafeBSTRHandle.Allocate(null, GetAlignedSize(capacity));
352 if (newBuffer.IsInvalid) {
353 throw new OutOfMemoryException();
356 SafeBSTRHandle.Copy(m_buffer, newBuffer);
358 m_buffer = newBuffer;
361 [System.Security.SecurityCritical] // auto-generated
362 private void EnsureNotDisposed() {
363 if( m_buffer == null) {
364 throw new ObjectDisposedException(null);
366 Contract.EndContractBlock();
369 private void EnsureNotReadOnly() {
371 throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_ReadOnly"));
373 Contract.EndContractBlock();
376 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
377 private static uint GetAlignedSize( int size) {
378 Contract.Assert(size >= 0, "size must be non-negative");
380 uint alignedSize = ((uint)size / BlockSize) * BlockSize;
381 if( (size % BlockSize != 0) || size == 0) { // if size is 0, set allocated size to blocksize
382 alignedSize += BlockSize;
387 [System.Security.SecurityCritical] // auto-generated
388 private unsafe int GetAnsiByteCount() {
389 const uint CP_ACP = 0;
390 const uint WC_NO_BEST_FIT_CHARS = 0x00000400;
392 uint flgs = WC_NO_BEST_FIT_CHARS;
393 uint DefaultCharUsed = (uint)'?';
395 byte* bufferPtr = null;
396 RuntimeHelpers.PrepareConstrainedRegions();
398 m_buffer.AcquirePointer(ref bufferPtr);
400 return Win32Native.WideCharToMultiByte(
408 new IntPtr((void*)&DefaultCharUsed));
411 if (bufferPtr != null)
412 m_buffer.ReleasePointer();
416 [System.Security.SecurityCritical] // auto-generated
417 private unsafe void GetAnsiBytes( byte * ansiStrPtr, int byteCount) {
418 const uint CP_ACP = 0;
419 const uint WC_NO_BEST_FIT_CHARS = 0x00000400;
421 uint flgs = WC_NO_BEST_FIT_CHARS;
422 uint DefaultCharUsed = (uint)'?';
424 byte* bufferPtr = null;
425 RuntimeHelpers.PrepareConstrainedRegions();
427 m_buffer.AcquirePointer(ref bufferPtr);
429 Win32Native.WideCharToMultiByte(
437 new IntPtr((void*)&DefaultCharUsed));
439 *(ansiStrPtr + byteCount - 1) = (byte)0;
442 if (bufferPtr != null)
443 m_buffer.ReleasePointer();
447 [System.Security.SecurityCritical] // auto-generated
448 [ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
449 private void ProtectMemory() {
450 Contract.Assert(!m_buffer.IsInvalid && m_buffer.Length != 0, "Invalid buffer!");
451 Contract.Assert(m_buffer.Length % BlockSize == 0, "buffer length must be multiple of blocksize!");
453 if( m_length == 0 || m_encrypted) {
457 RuntimeHelpers.PrepareConstrainedRegions();
461 // RtlEncryptMemory return an NTSTATUS
462 int status = Win32Native.SystemFunction040(m_buffer, (uint)m_buffer.Length * 2, ProtectionScope);
463 if (status < 0) { // non-negative numbers indicate success
465 throw new CryptographicException(Win32Native.RtlNtStatusToDosError(status));
467 throw new CryptographicException(Win32Native.LsaNtStatusToWinError(status));
474 [System.Security.SecurityCritical] // auto-generated
475 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
476 [MethodImplAttribute(MethodImplOptions.Synchronized)]
477 #if FEATURE_CORRUPTING_EXCEPTIONS
478 [HandleProcessCorruptedStateExceptions] //
479 #endif // FEATURE_CORRUPTING_EXCEPTIONS
480 internal unsafe IntPtr ToBSTR() {
482 int length = m_length;
483 IntPtr ptr = IntPtr.Zero;
484 IntPtr result = IntPtr.Zero;
485 byte* bufferPtr = null;
487 RuntimeHelpers.PrepareConstrainedRegions();
489 RuntimeHelpers.PrepareConstrainedRegions();
493 ptr = Win32Native.SysAllocStringLen(null, length);
496 if (ptr == IntPtr.Zero) {
497 throw new OutOfMemoryException();
501 m_buffer.AcquirePointer(ref bufferPtr);
502 Buffer.Memcpy((byte*) ptr.ToPointer(), bufferPtr, length *2);
511 if( result == IntPtr.Zero) {
512 // If we failed for any reason, free the new buffer
513 if (ptr != IntPtr.Zero) {
514 Win32Native.ZeroMemory(ptr, (UIntPtr)(length * 2));
515 Win32Native.SysFreeString(ptr);
518 if (bufferPtr != null)
519 m_buffer.ReleasePointer();
524 [System.Security.SecurityCritical] // auto-generated
525 [MethodImplAttribute(MethodImplOptions.Synchronized)]
526 #if FEATURE_CORRUPTING_EXCEPTIONS
527 [HandleProcessCorruptedStateExceptions] //
528 #endif // FEATURE_CORRUPTING_EXCEPTIONS
529 internal unsafe IntPtr ToUniStr(bool allocateFromHeap) {
531 int length = m_length;
532 IntPtr ptr = IntPtr.Zero;
533 IntPtr result = IntPtr.Zero;
534 byte* bufferPtr = null;
536 RuntimeHelpers.PrepareConstrainedRegions();
538 RuntimeHelpers.PrepareConstrainedRegions();
542 if( allocateFromHeap) {
543 ptr = Marshal.AllocHGlobal((length + 1) * 2);
546 ptr = Marshal.AllocCoTaskMem((length + 1) * 2);
550 if (ptr == IntPtr.Zero) {
551 throw new OutOfMemoryException();
555 m_buffer.AcquirePointer(ref bufferPtr);
556 Buffer.Memcpy((byte*) ptr.ToPointer(), bufferPtr, length *2);
557 char * endptr = (char *) ptr.ToPointer();
558 *(endptr + length) = '\0';
568 if( result == IntPtr.Zero) {
569 // If we failed for any reason, free the new buffer
570 if (ptr != IntPtr.Zero) {
571 Win32Native.ZeroMemory(ptr, (UIntPtr)(length * 2));
572 if( allocateFromHeap) {
573 Marshal.FreeHGlobal(ptr);
576 Marshal.FreeCoTaskMem(ptr);
581 if (bufferPtr != null)
582 m_buffer.ReleasePointer();
587 [System.Security.SecurityCritical] // auto-generated
588 [MethodImplAttribute(MethodImplOptions.Synchronized)]
589 #if FEATURE_CORRUPTING_EXCEPTIONS
590 [HandleProcessCorruptedStateExceptions] //
591 #endif // FEATURE_CORRUPTING_EXCEPTIONS
592 internal unsafe IntPtr ToAnsiStr(bool allocateFromHeap) {
595 IntPtr ptr = IntPtr.Zero;
596 IntPtr result = IntPtr.Zero;
598 RuntimeHelpers.PrepareConstrainedRegions();
600 // GetAnsiByteCount uses the string data, so the calculation must happen after we are decrypted.
603 // allocating an extra char for terminating zero
604 byteCount = GetAnsiByteCount() + 1;
606 RuntimeHelpers.PrepareConstrainedRegions();
610 if( allocateFromHeap) {
611 ptr = Marshal.AllocHGlobal(byteCount);
614 ptr = Marshal.AllocCoTaskMem(byteCount);
618 if (ptr == IntPtr.Zero) {
619 throw new OutOfMemoryException();
622 GetAnsiBytes((byte *)ptr.ToPointer(), byteCount);
631 if( result == IntPtr.Zero) {
632 // If we failed for any reason, free the new buffer
633 if (ptr != IntPtr.Zero) {
634 Win32Native.ZeroMemory(ptr, (UIntPtr)byteCount);
635 if( allocateFromHeap) {
636 Marshal.FreeHGlobal(ptr);
639 Marshal.FreeCoTaskMem(ptr);
648 [System.Security.SecurityCritical] // auto-generated
649 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
650 private void UnProtectMemory() {
651 Contract.Assert(!m_buffer.IsInvalid && m_buffer.Length != 0, "Invalid buffer!");
652 Contract.Assert(m_buffer.Length % BlockSize == 0, "buffer length must be multiple of blocksize!");
658 RuntimeHelpers.PrepareConstrainedRegions();
663 // RtlEncryptMemory return an NTSTATUS
664 int status = Win32Native.SystemFunction041(m_buffer, (uint)m_buffer.Length * 2, ProtectionScope);
666 { // non-negative numbers indicate success
668 throw new CryptographicException(Win32Native.RtlNtStatusToDosError(status));
670 throw new CryptographicException(Win32Native.LsaNtStatusToWinError(status));
679 [System.Security.SecurityCritical] // auto-generated
680 [SuppressUnmanagedCodeSecurityAttribute()]
681 internal sealed class SafeBSTRHandle : SafeBuffer {
682 internal SafeBSTRHandle () : base(true) {}
684 internal static SafeBSTRHandle Allocate(String src, uint len)
686 SafeBSTRHandle bstr = SysAllocStringLen(src, len);
687 bstr.Initialize(len * sizeof(char));
691 [ResourceExposure(ResourceScope.None)]
692 [DllImport(Win32Native.OLEAUT32, CharSet = CharSet.Unicode)]
693 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
694 private static extern SafeBSTRHandle SysAllocStringLen(String src, uint len); // BSTR
696 [System.Security.SecurityCritical]
697 override protected bool ReleaseHandle()
699 Win32Native.ZeroMemory(handle, (UIntPtr) (Win32Native.SysStringLen(handle) * 2));
700 Win32Native.SysFreeString(handle);
704 [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
705 internal unsafe void ClearBuffer() {
706 byte* bufferPtr = null;
707 RuntimeHelpers.PrepareConstrainedRegions();
710 AcquirePointer(ref bufferPtr);
711 Win32Native.ZeroMemory((IntPtr)bufferPtr, (UIntPtr) (Win32Native.SysStringLen((IntPtr)bufferPtr) * 2));
715 if (bufferPtr != null)
721 internal unsafe int Length {
723 return (int) Win32Native.SysStringLen(this);
727 internal unsafe static void Copy(SafeBSTRHandle source, SafeBSTRHandle target) {
728 byte* sourcePtr = null, targetPtr = null;
729 RuntimeHelpers.PrepareConstrainedRegions();
732 source.AcquirePointer(ref sourcePtr);
733 target.AcquirePointer(ref targetPtr);
735 Contract.Assert(Win32Native.SysStringLen((IntPtr)targetPtr) >= Win32Native.SysStringLen((IntPtr)sourcePtr), "Target buffer is not large enough!");
737 Buffer.Memcpy(targetPtr, sourcePtr, (int) Win32Native.SysStringLen((IntPtr)sourcePtr) * 2);
741 if (sourcePtr != null)
742 source.ReleasePointer();
743 if (targetPtr != null)
744 target.ReleasePointer();