SensitiveData<T> Class

Represent sensitive data which should be difficult to accidentally disclose, even accounting for some types of application bugs. However, there's no effort to thwart intentional disclosure of these contents, such as through a debugger or memory dump utility.

public sealed class SensitiveData<T> : IDisposable where T : unmanaged

Type parameters

T
The unmanaged type of elements stored in this sensitive data buffer.

Remarks

The protection applied while the contents are at rest depends on the platform:

  • Windows: the buffer is allocated on a private heap and encrypted with CryptProtectMemory.
  • Linux and macOS: the buffer is mapped with mmap and made inaccessible with mprotect(PROT_NONE). It is also locked into physical memory with mlock on a best-effort basis. Locking commonly fails when RLIMIT_MEMLOCK is low, which is the default in many container images; that failure is not reported, and the contents may then be written to swap. The contents are never encrypted on these platforms. On Linux the pages are excluded from core dumps with madvise(MADV_DONTDUMP); macOS has no equivalent, so a core dump taken while the buffer is unprotected contains them in clear. Each instance maps at least one whole page, and each reveal changes the page protection twice, so these platforms suit a moderate number of long-lived secrets rather than many small or frequently revealed ones.
  • Every other platform, which includes Android, iOS, tvOS, Mac Catalyst, FreeBSD and WebAssembly: the buffer is combined with a random key of the same length. The key lives in the same process, so this only guards against casual inspection. Note that OperatingSystem.IsLinux() returns false on Android and OperatingSystem.IsMacOS() returns false on iOS and Mac Catalyst, so those platforms take this path rather than the mmap one above even though they support the syscalls it uses.

Failing to apply or remove the protection throws; the instance is never silently left unprotected.

Example

// Create and use sensitive data
using var secret = SensitiveData.Create("my-secret-password");

// Get the length without revealing the data
int length = secret.GetLength();
Assert.Equal(18, length);

// Reveal data into an existing buffer
char[] buffer = new char[length];
secret.RevealInto(buffer);
Assert.Equal("my-secret-password", new string(buffer));

// Or reveal to a new array
char[] revealed = secret.RevealToArray();
Assert.Equal("my-secret-password", new string(revealed));

// Use the data with a callback to avoid keeping it in memory
secret.RevealAndUse(arg: length, static (span, expectedLength) =>
{
    // Process the sensitive data here
    Assert.HasCount(expectedLength, span);
});
Implements

Methods

NameDescription
Clone()Creates a new copy of this SensitiveData<T> instance.
Dispose()Disposes of this instance, including any unmanaged resources. The contents will no longer be accessible once the instance is disposed.
GetLength()Returns the length (in elements) of this buffer.
RevealAndUse<TArg>(TArg, ReadOnlySpanAction<T, TArg>)Reveals the contents and invokes a callback action with the data.
RevealInto(Span<T>)Copies the contents of this SensitiveData<T> instance to a destination buffer.
RevealToArray()Copies the contents of this SensitiveData<T> instance to a new array.

Creates a new copy of this SensitiveData<T> instance.

public SensitiveData<T> Clone()

Returns

SensitiveData<T> A new SensitiveData<T> instance containing a copy of the data.

Exceptions

ObjectDisposedException
This instance has already been disposed.

Disposes of this instance, including any unmanaged resources. The contents will no longer be accessible once the instance is disposed.

public void Dispose()

GetLength()

SensitiveData.cs

Returns the length (in elements) of this buffer.

public int GetLength()

Returns

int

RevealAndUse<TArg>(TArg, ReadOnlySpanAction<T, TArg>)

SensitiveData.cs

Reveals the contents and invokes a callback action with the data.

public void RevealAndUse<TArg>(TArg arg, ReadOnlySpanAction<T, TArg> spanAction)

Type parameters

TArg
The type of the argument to pass to the callback.

Parameters

arg TArg
The argument to pass to the callback action.
spanAction ReadOnlySpanAction<T, TArg>
The callback action to invoke with the revealed data and argument.

Exceptions

ArgumentNullException
spanAction is null.
ObjectDisposedException
This instance has already been disposed.

RevealInto(Span<T>)

SensitiveData.cs

Copies the contents of this SensitiveData<T> instance to a destination buffer.

public int RevealInto(Span<T> destination)

Parameters

destination Span<T>
The destination buffer which should receive the contents. This buffer must be at least SensitiveData<T>.GetLength elements in length.

Returns

int The number of elements written to destination, which is always SensitiveData<T>.GetLength. A larger destination is left untouched past that point.

Exceptions

ArgumentException
destination's length is smaller than SensitiveData<T>.GetLength.
ObjectDisposedException
This instance has already been disposed.

RevealToArray()

SensitiveData.cs

Copies the contents of this SensitiveData<T> instance to a new array.

public T[] RevealToArray()

Returns

T[]

Exceptions

ObjectDisposedException
This instance has already been disposed.