Secure Memory And Guarded Heap allocations - Chewhern/ASodium Wiki

Secure Memory generally have these corresponding functions which are zeroing memory which clears an array of data through mutating it, locking/unlocking memory and Guarded Heap allocations which their corresponding functionalities have been described at official libsodium documentation page.

Note: To understand on mutable and immutable data type, kindly refers to computerphile's video or the following layman's video made by me(https://www.youtube.com/watch?v=g2d3uCirWQg&list=PLCCjQamH_8r1MGV-zrpMvh7TPWCu4HtMD&index=22)

Critical: Please avoid using any functions that has String/string ** or ** char/Char ** in them as the corresponding string and char data type have not properly define their length**

Zeroing memory (Initial function)

public static void MemZero(IntPtr intPtr, long Length)

C# have a constraints on the length of array. C# only allows long data type as their array length. You will need to first convert the data regardless of its data type into an IntPtr data type which is equivalent to C's variable**. As pointer data type don't have a specified length like an array, you will have to know the length of the array before converting it into IntPtr data type.

The following is an example of using this method/function.

Byte[] RandomByte = new Byte[32];
Byte[] ZeroByte = new Byte[32];
RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
rngCsp.GetBytes(RandomByte);
//Assume the RandomByte variable is extremely sensitive so you should
//clear it from memory is best option
//but we have to do it securely
//==GCHandle==
GCHandle MyGeneralGCHandle = GCHandle.Alloc(RandomByte, GCHandleType.Pinned);
SodiumSecureMemory.MemZero(MyGeneralGCHandle.AddrOfPinnedObject(), RandomByte.Length);
MyGeneralGCHandle.Free();

Similarly we can also do this on other datatypes array but it's not recommended as you need to know the size of the data type being used and you can't guarantee that they are mutable data type

Zeroing Memory(Helpers)

If the initial zeroing memory is too troublesome for developers, there're a few functions which helps to simplify the works needed.

public static void SecureClearBytes(Byte[] Source)
public static void SecureClearString(String Source)
public static void SecureClearCharArray(Char[] Source)

Locking/Unlocking Memory (Initial Function)

The official function descriptions can be found through libsodium documentation.

The functions

public static void MemLock(IntPtr intPtr, long Length)
public static void MemUnlock(IntPtr intPtr, long Length)

Here's an example of using the function.

Byte[] RandomByte = new Byte[32];
Byte[] ZeroByte = new Byte[32];
RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
rngCsp.GetBytes(RandomByte);
//Assume the RandomByte variable is extremely sensitive so you should
//clear it from memory is best option
//but we have to do it securely
//==GCHandle==
GCHandle MyGeneralGCHandle = GCHandle.Alloc(RandomByte, GCHandleType.Pinned);
SodiumSecureMemory.MemLock(MyGeneralGCHandle.AddrOfPinnedObject(), RandomByte.Length);
SodiumSecureMemory.MemUnlock(MyGeneralGCHandle.AddrOfPinnedObject(), RandomByte.Length);
MessageBox.Show(RandomByte.SequenceEqual(ZeroByte).ToString()); //Compare 2 byte arrays after locking and unlocking the array from memory
MyGeneralGCHandle.Free();
//Most of the times we use MemLock and MemUnlock in a sequence because by not doing so, you may have encounter memory lock/unlock limits..
//MemLock and MemUnlock can be used but they are not as general purpose as Sodium MemZero in my opinion..
//MemUnlock will also call "sodium_memzero()" function.

Locking/Unlocking Memory (Helpers)

List of helper functions

public static void SecureMemoryLockBytes(Byte[] Source)
public static void SecureMemoryLockString(String Source)
public static void SecureMemoryLockCharArray(Char[] Source)
public static void SecureMemoryUnlockBytes(Byte[] Source)
public static void SecureMemoryUnlockString(String Source)
public static void SecureMemoryUnlockCharArray(Char[] Source)

Guarded Heap Allocations (Initial Functions)

public static IntPtr Sodium_Malloc(ref Boolean IsZero,long Size)
public static IntPtr Sodium_AllocArray(ref Boolean IsZero, long ArrayLength, long ArrayElementSizeInBytes)
public static void Sodium_Free(IntPtr intPtr)

As stated in libsodium documentation, it's uncertain whether the pointer which points to an address can be allocated or can't be allocated. Hence we need a boolean data type to let developer determine whether the pointer is an allocated pointer or a null pointer.

Before you use these functions, make sure that you initialize libsodium library through sodium_init() or SodiumInit.Init().

Even though I have written the example code for Sodium_Free() function, I have no idea what it really does and whether is it working?

Here's the example for 2 of these functions

Boolean IsZero = true;
IntPtr MyIntPtr = SodiumGuardedHeapAllocation.Sodium_Malloc(ref IsZero, 32);
if (IsZero == false) 
{
    MessageBox.Show("Successfully allocate space for IntPtr");
    //Allocated pointer
}
else 
{
    MessageBox.Show("Failed to allocate space for IntPtr");
    //Null pointer
}
//If the pointer is not zero/null then you can actually use Marshal to put data into the sodium allocated pointer memory
Boolean IsZero = true;
IntPtr MyIntPtr = SodiumGuardedHeapAllocation.Sodium_AllocArray(ref IsZero,16,1);
//Since you're allocating for a memory address, you need to decide that memory address what data type
//was being stored in that particular address
//For example, is it String? is it Byte[] ? is it Int[] ? is it Char[], each of the data type have
//their own corresponding size. I am allocating memory for a byte array that has the length of 16.
if (IsZero == false)
{
    MessageBox.Show("Successfully allocate space for IntPtr");
}
else
{
    MessageBox.Show("Failed to allocate space for IntPtr");
}
//If the pointer is not zero/null then you can actually use Marshal to put data into the sodium allocated pointer memory

Guarded Heap Allocations - Memory Protection Through Permission (Initial Functions)

public static void Sodium_MProtect_NoAccess(IntPtr intPtr) 
public static void Sodium_MProtect_ReadOnly(IntPtr intPtr)
public static void Sodium_MProtect_ReadWrite(IntPtr intPtr)

Before you use these functions, make sure that you initialize libsodium library through sodium_init() or SodiumInit.Init(). The other prerequisite is that the pointer you are putting into these functions parameters must be allocated through libsodium library.

Here's the example for these functions

Boolean IsZero = true;
IntPtr MyIntPtr = SodiumGuardedHeapAllocation.Sodium_Malloc(ref IsZero, 32);
Byte[] RandomByte = new Byte[32];
Byte[] TestByte = new Byte[32];
RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
rngCsp.GetBytes(RandomByte);
if (IsZero == false)
{
    //Any attempt to write/read bytes into/from IntPtr is no longer possible through Marshal after Sodium mark the region as no access..
    SodiumGuardedHeapAllocation.Sodium_MProtect_NoAccess(MyIntPtr);
    //Marshal.Copy(RandomByte, 0, MyIntPtr, 32);
}
Boolean IsZero = true;
IntPtr MyIntPtr = SodiumGuardedHeapAllocation.Sodium_Malloc(ref IsZero, 32);
Byte[] RandomByte = new Byte[32];
Byte[] TestByte = new Byte[32];
RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
rngCsp.GetBytes(RandomByte);
if (IsZero == false)
{
    Marshal.Copy(RandomByte, 0, MyIntPtr, 32);
    //Any attempt to write bytes into IntPtr is no longer possible through Marshal after Sodium mark the region as read only..
    SodiumGuardedHeapAllocation.Sodium_MProtect_ReadOnly(MyIntPtr);
    //Marshal.Copy(RandomByte, 0, MyIntPtr, 32);
}
Boolean IsZero = true;
IntPtr MyIntPtr = SodiumGuardedHeapAllocation.Sodium_Malloc(ref IsZero, 32);
Byte[] RandomByte = new Byte[32];
RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider();
rngCsp.GetBytes(RandomByte);
if (IsZero == false)
{
    Marshal.Copy(RandomByte, 0, MyIntPtr, 32);
    SodiumGuardedHeapAllocation.Sodium_MProtect_ReadWrite(MyIntPtr);
    //This allows the IntPtr to be able to read and write again..
}