memguard: github.com/awnumar/memguard Index | Examples | Files | Directories

package memguard

import "github.com/awnumar/memguard"

Package memguard lets you easily handle sensitive values in memory.

package main

import (
    "fmt"

    "github.com/awnumar/memguard"
)

func main() {
    // Tell memguard to listen out for interrupts, and cleanup in case of one.
    memguard.CatchInterrupt(func() {
        fmt.Println("Interrupt signal received. Exiting...")
    })
    // Make sure to destroy all LockedBuffers when returning.
    defer memguard.DestroyAll()

    // Normal code continues from here.
    foo()
}

func foo() {
    // Create a 32 byte, immutable, random key.
    key, err := memguard.NewImmutableRandom(32)
    if err != nil {
        // Oh no, an error. Safely exit.
        fmt.Println(err)
        memguard.SafeExit(1)
    }
    // Remember to destroy this key when the function returns.
    defer key.Destroy()

    // Do something with the key.
    fmt.Printf("This is a %d byte key.\n", key.Size())
    fmt.Printf("This key starts with %x\n", key.Buffer()[0])
}

The number of LockedBuffers that you are able to create is limited by how much memory your system kernel allows each process to mlock/VirtualLock. Therefore you should call Destroy on LockedBuffers that you no longer need, or simply defer a Destroy call after creating a new LockedBuffer.

If a function that you're using requires an array, you can cast the buffer to an array and then pass around a pointer. Make sure that you do not dereference the pointer and pass around the resulting value, as this will leave copies all over the place.

key, err := memguard.NewImmutableRandom(16)
if err != nil {
    fmt.Println(err)
    memguard.SafeExit(1)
}
defer key.Destroy()

// Make sure the size of the array matches the size of the Buffer.
// In this case that size is 16. This is very important.
keyArrayPtr := (*[16]byte)(unsafe.Pointer(&key.Buffer()[0]))

The MemGuard API is thread-safe. You can extend this thread-safety to outside of the API functions by using the Mutex that each LockedBuffer exposes. Don't use the mutex when calling a function that is part of the MemGuard API though, or the process will deadlock.

When terminating your application, care should be taken to securely cleanup everything.

// Start a listener that will wait for interrupt signals and catch them.
memguard.CatchInterrupt(func() {
    // Over here put anything you want executing before program exit.
    fmt.Println("Interrupt signal received. Exiting...")
})

// Defer a DestroyAll() in your main() function.
defer memguard.DestroyAll()

// Use memguard.SafeExit() instead of os.Exit().
memguard.SafeExit(1) // 1 is the exit code.

Index

Examples

Package Files

container.go docs.go errors.go internals.go memguard.go

Variables

var ErrDestroyed = errors.New("memguard.ErrDestroyed: buffer is destroyed")

ErrDestroyed is returned when a function is called on a destroyed LockedBuffer.

var ErrImmutable = errors.New("memguard.ErrImmutable: cannot modify immutable buffer")

ErrImmutable is returned when a function that needs to modify a LockedBuffer is given a LockedBuffer that is immutable.

var ErrInvalidConversion = errors.New("memguard.ErrInvalidConversion: length of buffer must align with target type")

ErrInvalidConversion is returned when attempting to get a slice of a LockedBuffer that is of an inappropriate size for that slice type. For example, attempting to get a []uint16 representation of a LockedBuffer of length 9 bytes would trigger this error, since there would be a byte leftover after the conversion.

var ErrInvalidLength = errors.New("memguard.ErrInvalidLength: length of buffer must be greater than zero")

ErrInvalidLength is returned when a LockedBuffer of smaller than one byte is requested.

func CatchInterrupt Uses

func CatchInterrupt(f func())

CatchInterrupt starts a goroutine that monitors for interrupt signals. It accepts a function of type func() and executes that before calling SafeExit(0).

If CatchInterrupt is called multiple times, only the first call is executed and all subsequent calls are ignored.

Code:

CatchInterrupt(func() {
    fmt.Println("Exiting...")
})

func DestroyAll Uses

func DestroyAll()

DestroyAll calls Destroy on all LockedBuffers that have not already been destroyed.

CatchInterrupt and SafeExit both call DestroyAll before exiting.

func DisableUnixCoreDumps Uses

func DisableUnixCoreDumps()

DisableUnixCoreDumps disables core-dumps.

Since core-dumps are only relevant on Unix systems, if DisableUnixCoreDumps is called on any other system it will do nothing and return immediately.

This function is precautonary as core-dumps are usually disabled by default on most systems.

func Equal Uses

func Equal(a, b *LockedBuffer) (bool, error)

Equal compares the contents of two LockedBuffers in constant time.

func SafeExit Uses

func SafeExit(c int)

SafeExit exits the program with a specified exit-code, but calls DestroyAll first.

func Split Uses

func Split(b *LockedBuffer, offset int) (*LockedBuffer, *LockedBuffer, error)

Split takes a LockedBuffer, splits it at a specified offset, and then returns the two newly created LockedBuffers. The mutability state of the original is preserved in the new LockedBuffers, and the original LockedBuffer is not destroyed.

func WipeBytes Uses

func WipeBytes(b []byte)

WipeBytes zeroes out a given byte slice. It is recommended that you call WipeBytes on slices after utilizing the Copy or CopyAt methods.

Due to the nature of memory allocated by the Go runtime, WipeBytes cannot guarantee that the data does not exist elsewhere in memory. Therefore, your program should aim to (when possible) store sensitive data only in LockedBuffers.

type LockedBuffer Uses

type LockedBuffer struct {
    // contains filtered or unexported fields
}

LockedBuffer is a structure that holds secure values.

The protected memory itself can be accessed with the Buffer() method. The various states can be accessed with the IsDestroyed() and IsMutable() methods, both of which are pretty self-explanatory.

The number of LockedBuffers that you are able to create is limited by how much memory your system kernel allows each process to mlock/VirtualLock. Therefore you should call Destroy on LockedBuffers that you no longer need, or simply defer a Destroy call after creating a new LockedBuffer.

The entire memguard API handles and passes around pointers to LockedBuffers, and so, for both security and convenience, you should refrain from dereferencing a LockedBuffer.

If an API function that needs to edit a LockedBuffer is given one that is immutable, the call will return an ErrImmutable. Similarly, if a function is given a LockedBuffer that has been destroyed, the call will return an ErrDestroyed.

func Concatenate Uses

func Concatenate(a, b *LockedBuffer) (*LockedBuffer, error)

Concatenate takes two LockedBuffers and concatenates them.

If one of the given LockedBuffers is immutable, the resulting LockedBuffer will also be immutable. The original LockedBuffers are not destroyed.

func Duplicate Uses

func Duplicate(b *LockedBuffer) (*LockedBuffer, error)

Duplicate takes a LockedBuffer and creates a new one with the same contents and mutability state as the original.

func NewImmutable Uses

func NewImmutable(size int) (*LockedBuffer, error)

NewImmutable creates a new, immutable LockedBuffer of a specified size.

The mutability can later be toggled with the MakeImmutable and MakeMutable methods.

If the given length is less than one, the call will return an ErrInvalidLength.

func NewImmutableFromBytes Uses

func NewImmutableFromBytes(buf []byte) (*LockedBuffer, error)

NewImmutableFromBytes is identical to NewImmutable but for the fact that the created LockedBuffer is of the same length and has the same contents as a given slice. The slice is wiped after the bytes have been copied over.

If the size of the slice is zero, the call will return an ErrInvalidLength.

func NewImmutableRandom Uses

func NewImmutableRandom(size int) (*LockedBuffer, error)

NewImmutableRandom is identical to NewImmutable but for the fact that the created LockedBuffer is filled with cryptographically-secure pseudo-random bytes instead of zeroes. Therefore a LockedBuffer created with NewImmutableRandom can safely be used as an encryption key.

func NewMutable Uses

func NewMutable(size int) (*LockedBuffer, error)

NewMutable creates a new, mutable LockedBuffer of a specified length.

The mutability can later be toggled with the MakeImmutable and MakeMutable methods.

If the given length is less than one, the call will return an ErrInvalidLength.

func NewMutableFromBytes Uses

func NewMutableFromBytes(buf []byte) (*LockedBuffer, error)

NewMutableFromBytes is identical to NewMutable but for the fact that the created LockedBuffer is of the same length and has the same contents as a given slice. The slice is wiped after the bytes have been copied over.

If the size of the slice is zero, the call will return an ErrInvalidLength.

func NewMutableRandom Uses

func NewMutableRandom(size int) (*LockedBuffer, error)

NewMutableRandom is identical to NewMutable but for the fact that the created LockedBuffer is filled with cryptographically-secure pseudo-random bytes instead of zeroes. Therefore a LockedBuffer created with NewMutableRandom can safely be used as an encryption key.

func Trim Uses

func Trim(b *LockedBuffer, offset, size int) (*LockedBuffer, error)

Trim shortens a LockedBuffer according to the given specifications. The mutability state of the original is preserved in the new LockedBuffer, and the original LockedBuffer is not destroyed.

Trim takes an offset and a size as arguments. The resulting LockedBuffer starts at index [offset] and ends at index [offset+size].

func (LockedBuffer) Buffer Uses

func (b LockedBuffer) Buffer() []byte

Buffer returns a slice that references the secure, protected portion of memory.

If the LockedBuffer that you call Buffer on has been destroyed, the returned slice will be nil (it will have a length and capacity of zero).

If a function that you're using requires an array, you can cast the buffer to an array and then pass around a pointer:

// Make sure the size of the array matches the size of the buffer.
// In this case that size is 16. This is *very* important.
keyArrayPtr := (*[16]byte)(unsafe.Pointer(&b.Buffer()[0]))

Make sure that you do not dereference the pointer and pass around the resulting value, as this will leave copies all over the place.

func (LockedBuffer) Copy Uses

func (b LockedBuffer) Copy(buf []byte) error

Copy copies bytes from a byte slice into a LockedBuffer in constant-time. Just like Golang's built-in copy function, Copy only copies up to the smallest of the two buffers.

It does not wipe the original slice so using Copy is less secure than using Move. Therefore Move should be favoured unless you have a good reason.

You should aim to call WipeBytes on the original slice as soon as possible.

If the LockedBuffer is marked as read-only, the call will fail and return an ErrReadOnly.

func (LockedBuffer) CopyAt Uses

func (b LockedBuffer) CopyAt(buf []byte, offset int) error

CopyAt is identical to Copy but it copies into the LockedBuffer at a specified offset.

func (LockedBuffer) Destroy Uses

func (b LockedBuffer) Destroy()

Destroy verifies that no buffer underflows occurred and then wipes, unlocks, and frees all related memory. If a buffer underflow is detected, the process panics.

This function must be called on all LockedBuffers before exiting. DestroyAll is designed for this purpose, as is CatchInterrupt and SafeExit. We recommend using all of them together.

If the LockedBuffer has already been destroyed then the call makes no changes.

func (LockedBuffer) EqualBytes Uses

func (b LockedBuffer) EqualBytes(buf []byte) (bool, error)

EqualBytes compares a LockedBuffer to a byte slice in constant time.

func (LockedBuffer) FillRandomBytes Uses

func (b LockedBuffer) FillRandomBytes() error

FillRandomBytes fills a LockedBuffer with cryptographically-secure pseudo-random bytes.

func (LockedBuffer) FillRandomBytesAt Uses

func (b LockedBuffer) FillRandomBytesAt(offset, length int) error

FillRandomBytesAt fills a LockedBuffer with cryptographically-secure pseudo-random bytes, starting at an offset and ending after a given number of bytes.

func (LockedBuffer) Int16 Uses

func (b LockedBuffer) Int16() ([]int16, error)

Int16 returns a slice (of type []int16) that references the secure, protected portion of memory.

The LockedBuffer must be a multiple of 2 bytes in length, or else an error will be returned.

func (LockedBuffer) Int32 Uses

func (b LockedBuffer) Int32() ([]int32, error)

Int32 returns a slice (of type []int32) that references the secure, protected portion of memory.

The LockedBuffer must be a multiple of 4 bytes in length, or else an error will be returned.

func (LockedBuffer) Int64 Uses

func (b LockedBuffer) Int64() ([]int64, error)

Int64 returns a slice (of type []int64) that references the secure, protected portion of memory.

The LockedBuffer must be a multiple of 8 bytes in length, or else an error will be returned.

func (LockedBuffer) Int8 Uses

func (b LockedBuffer) Int8() ([]int8, error)

Int8 returns a slice (of type []int8) that references the secure, protected portion of memory.

func (LockedBuffer) IsDestroyed Uses

func (b LockedBuffer) IsDestroyed() bool

IsDestroyed returns a boolean value indicating if a LockedBuffer has been destroyed.

func (LockedBuffer) IsMutable Uses

func (b LockedBuffer) IsMutable() bool

IsMutable returns a boolean value indicating if a LockedBuffer is marked read-only.

func (LockedBuffer) MakeImmutable Uses

func (b LockedBuffer) MakeImmutable() error

MakeImmutable asks the kernel to mark the LockedBuffer's memory as immutable. Any subsequent attempts to modify this memory will result in the process crashing with a SIGSEGV memory violation.

To make the memory mutable again, MakeMutable is called.

func (LockedBuffer) MakeMutable Uses

func (b LockedBuffer) MakeMutable() error

MakeMutable asks the kernel to mark the LockedBuffer's memory as mutable.

To make the memory immutable again, MakeImmutable is called.

func (LockedBuffer) Move Uses

func (b LockedBuffer) Move(buf []byte) error

Move moves bytes from a byte slice into a LockedBuffer in constant-time. Just like Golang's built-in copy function, Move only moves up to the smallest of the two buffers.

Unlike Copy, Move wipes the entire original slice after copying the appropriate number of bytes over, and so it should be favoured unless you have a good reason.

If the LockedBuffer is marked as read-only, the call will fail and return an ErrReadOnly.

func (LockedBuffer) MoveAt Uses

func (b LockedBuffer) MoveAt(buf []byte, offset int) error

MoveAt is identical to Move but it copies into the LockedBuffer at a specified offset.

func (LockedBuffer) Size Uses

func (b LockedBuffer) Size() int

Size returns an integer representing the total length, in bytes, of a LockedBuffer.

If this size is zero, it is safe to assume that the LockedBuffer has been destroyed.

func (LockedBuffer) Uint16 Uses

func (b LockedBuffer) Uint16() ([]uint16, error)

Uint16 returns a slice (of type []uint16) that references the secure, protected portion of memory.

The LockedBuffer must be a multiple of 2 bytes in length, or else an error will be returned.

func (LockedBuffer) Uint32 Uses

func (b LockedBuffer) Uint32() ([]uint32, error)

Uint32 returns a slice (of type []uint32) that references the secure, protected portion of memory.

The LockedBuffer must be a multiple of 4 bytes in length, or else an error will be returned.

func (LockedBuffer) Uint64 Uses

func (b LockedBuffer) Uint64() ([]uint64, error)

Uint64 returns a slice (of type []uint64) that references the secure, protected portion of memory.

The LockedBuffer must be a multiple of 8 bytes in length, or else an error will be returned.

func (LockedBuffer) Uint8 Uses

func (b LockedBuffer) Uint8() ([]uint8, error)

Uint8 returns a slice (of type []uint8) that references the secure, protected portion of memory.

Uint8 is practically identical to Buffer, but it has been added for completeness' sake. Buffer will usually be the faster and easier option.

func (LockedBuffer) Wipe Uses

func (b LockedBuffer) Wipe() error

Wipe wipes a LockedBuffer's contents by overwriting the buffer with zeroes.

Directories

PathSynopsis
memcall

Package memguard imports 11 packages (graph) and is imported by 17 packages. Updated 2018-10-12. Refresh now. Tools for package owners.