memguard

repository·master·Indexed 25 days ago

https://github.com/awnumar/memguard

A pure Go software enclave designed to protect sensitive information in memory. It provides security features including XSalsa20Poly1305 encryption and authentication, memory locking to prevent swapping, kernel-level immutability, and guard pages to detect overflows. The library allows for the creation of LockedBuffers and Enclaves to securely store data, bypassing the Go runtime and garbage collector for allocation to reduce the risk of data exposure.

Tokens
2.4K
Snippets
1
Records
33
Agent score
83%

What's inside memguard

  1. Overview of MemGuard features

    master

    MemGuard is a software enclave designed for the secure storage of sensitive information in memory. Key security features include:

    • Encryption & Authentication: Sensitive data is encrypted and authenticated in memory using XSalsa20Poly1305, providing defense against cold-boot attacks.
    • Runtime Bypass: Uses system calls to bypass the Go language runtime and garbage collector for memory allocation.
    • Buffer Fortification: Plaintext buffers are protected with guard pages and canary values to detect overflows or spurious accesses.
    • Anti-Persistence: Prevents sensitive data from touching the disk by locking memory (preventing swapping) and handling core dumps.
    • Kernel-level Immutability: Protected regions are made immutable at the kernel level, causing access violations upon attempted modification.
    • Safe Termination: Provides session purging, safe termination capabilities, and signal handling to prevent remnant data from being left behind.
    • Side-channel Mitigation: Uses constant-time operations for data copying and comparison to mitigate side-channel attacks.
  2. Install MemGuard

    master

    Install the MemGuard package using the Go toolchain. Note that the API is currently experimental and may undergo unstable changes; it is recommended to pin your version using Go modules.

    $ go get github.com/awnumar/memguard
  3. Add a new example package

    master

    To contribute a new example to the repository, follow these steps:

    1. Create a new directory within examples/ and populate it with your Go code.
    2. Include test code and benchmarks within your directory.
    3. Run your code using go test -v -race ./examples/your_module_name to ensure it works correctly.
    4. Add your package to the list in the examples/README.md file.

    Note: Packages within the examples/ directory are able to import one another, allowing you to build complex systems from existing modules.

  4. Configure StreamChunkSize and mlock limits

    master

    The StreamChunkSize variable determines the maximum amount of data locked into memory at a time.

    If you encounter memory allocation errors, you may need to increase your system's mlock limits. On Unix systems, you can check your current limit using ulimit -l.

  5. Handle interrupt signals with CatchInterrupt

    master

    Use CatchInterrupt as a convenience wrapper to handle os.Interrupt signals. When an interrupt is received, the process will automatically wipe sensitive data in memory before terminating.

    Calling CatchSignal after CatchInterrupt will override the interrupt handler.

  6. Create a mutable LockedBuffer

    master

    Use NewBuffer(size int) to create a new, mutable LockedBuffer of a specific size. This container holds raw sensitive data in protected memory.

    Note: The number of LockedBuffers you can create is limited by your system's kernel mlock/VirtualLock limits. Always call Destroy() on buffers you no longer need to free memory.

  7. Get the next chunk from a Stream with Next()

    master
    The Next() method retrieves the next chunk of data from the Stream and returns it decrypted inside a LockedBuffer. This is an alternative to Read when you want to work with the full decrypted chunk directly rather than copying it into a pre-allocated buffer.
  8. Access LockedBuffer data as different types

    master

    A LockedBuffer can be accessed as various data types via its protected memory region. Note that many of these methods return slices pointing to the protected memory.

    Available representations:

    • Bytes() []byte: Returns the raw byte slice.
    • String() string: Returns a string representation.
    • Reader() *bytes.Reader: Returns an io.Reader for the buffer.
    • Integer Slices: Uint16(), Uint32(), Uint64(), Int8(), Int16(), Int32(), Int64().
    • Fixed-size Byte Arrays: ByteArray8(), ByteArray16(), ByteArray32(), ByteArray64().

    Warning: For ByteArrayN methods, do not dereference the returned pointer; pass it around as-is. These methods return nil if the buffer is destroyed or too small.

  9. Create an immutable LockedBuffer from an entire reader

    master

    Use NewBufferFromEntireReader(r io.Reader) to read all data from an io.Reader until EOF into an immutable LockedBuffer.

    A nil error is returned only if the reader reached EOF successfully. Any data read is returned in the buffer.