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

package memguard

import "github.com/awnumar/memguard"

Package memguard implements a secure software enclave for the storage of sensitive information in memory.

package main

import (
	"fmt"
	"os"

	"github.com/awnumar/memguard"
)

func main() {
	// Safely terminate in case of an interrupt signal
	memguard.CatchInterrupt()

	// Purge the session when we return
	defer memguard.Purge()

	// Generate a key sealed inside an encrypted container
	key := memguard.NewEnclaveRandom(32)

	// Passing the key off to another function
	key = invert(key)

	// Decrypt the result returned from invert
	keyBuf, err := key.Open()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		return
	}
	defer keyBuf.Destroy()

	// Um output it
	fmt.Println(keyBuf.Bytes())
}

func invert(key *memguard.Enclave) *memguard.Enclave {
	// Decrypt the key into a local copy
	b, err := key.Open()
	if err != nil {
		memguard.SafePanic(err)
	}
	defer b.Destroy() // Destroy the copy when we return

	// Open returns the data in an immutable buffer, so make it mutable
	b.Melt()

	// Set every element to its complement
	for i := range b.Bytes() {
		b.Bytes()[i] = ^b.Bytes()[i]
	}

	// Return the new data in encrypted form
	return b.Seal() // <- sealing also destroys b
}

There are two main container objects exposed in this API. Enclave objects encrypt data and store the ciphertext whereas LockedBuffers are more like guarded memory allocations. There is a limit on the maximum number of LockedBuffer objects that can exist at any one time, imposed by the system's mlock limits. There is no limit on Enclaves.

The general workflow is to store sensitive information in Enclaves when it is not immediately needed and decrypt it when and where it is. After use, the LockedBuffer should be destroyed.

If you need access to the data inside a LockedBuffer in a type not covered by any methods provided by this API, you can type-cast the allocation's memory to whatever type you want.

key := memguard.NewBuffer(32)
keyArrayPtr := (*[32]byte)(unsafe.Pointer(&key.Bytes()[0])) // do not dereference

This is of course an unsafe operation and so care must be taken to ensure that the cast is valid and does not result in memory unsafety. Further examples of code and interesting use-cases can be found in the examples subpackage.

Several functions exist to make the mass purging of data very easy. It is recommended to make use of them when appropriate.

// Start an interrupt handler that will clean up memory before exiting
memguard.CatchInterrupt()

// Purge the session when returning from the main function of your program
defer memguard.Purge()

// Use the safe variants of exit functions provided in the stdlib
memguard.SafeExit(1)
memguard.SafePanic(err)

// Destroy LockedBuffers as soon as possible after using them
b, err := enclave.Open()
if err != nil {
	memguard.SafePanic(err)
}
defer b.Destroy()

Core dumps are disabled by default. If you absolutely require them, you can enable them by using unix.Setrlimit to set RLIMIT_CORE to an appropriate value.

Index

Package Files

buffer.go docs.go enclave.go memguard.go signals.go

func CatchInterrupt Uses

func CatchInterrupt()

CatchInterrupt is a wrapper around CatchSignal that makes it easy to safely handle receiving interrupt signals. If an interrupt is received, the process will wipe sensitive data in memory before terminating.

A subsequent call to CatchSignal will override this call.

func CatchSignal Uses

func CatchSignal(f func(os.Signal), signals ...os.Signal)

CatchSignal assigns a given function to be run in the event of a signal being received by the process. If no signals are provided all signals will be caught.

1. Signal is caught by the process
2. Interrupt handler is executed
3. Secure session state is wiped
4. Process terminates with exit code 1

This function can be called multiple times with the effect that only the last call will have any effect.

func Purge Uses

func Purge()

Purge resets the session key to a fresh value and destroys all existing LockedBuffers. Existing Enclave objects will no longer be decryptable.

func SafeExit Uses

func SafeExit(c int)

SafeExit destroys everything sensitive before exiting with a specified status code.

func SafePanic Uses

func SafePanic(v interface{})

SafePanic wipes all it can before calling panic(v).

func ScrambleBytes Uses

func ScrambleBytes(buf []byte)

ScrambleBytes overwrites an arbitrary buffer with cryptographically-secure random bytes.

func WipeBytes Uses

func WipeBytes(buf []byte)

WipeBytes overwrites an arbitrary buffer with zeroes.

type Enclave Uses

type Enclave struct {
    *core.Enclave
}

Enclave is a sealed and encrypted container for sensitive data.

func NewEnclave Uses

func NewEnclave(src []byte) *Enclave

NewEnclave seals up some data into an encrypted enclave object. The buffer is wiped after the data is copied. If the length of the buffer is zero, the function will return nil.

A LockedBuffer may alternatively be converted into an Enclave object using its Seal method. This will also have the effect of destroying the LockedBuffer.

func NewEnclaveRandom Uses

func NewEnclaveRandom(size int) *Enclave

NewEnclaveRandom generates and seals arbitrary amounts of cryptographically-secure random bytes into an encrypted enclave object. If size is not strictly positive the function will return nil.

func (*Enclave) Open Uses

func (e *Enclave) Open() (*LockedBuffer, error)

Open decrypts an Enclave object and places its contents into an immutable LockedBuffer. An error will be returned if decryption failed.

type LockedBuffer Uses

type LockedBuffer struct {
    *core.Buffer
    // contains filtered or unexported fields
}

LockedBuffer is a structure that holds raw sensitive data.

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

func NewBuffer Uses

func NewBuffer(size int) *LockedBuffer

NewBuffer creates a mutable data container of the specified size.

func NewBufferFromBytes Uses

func NewBufferFromBytes(src []byte) *LockedBuffer

NewBufferFromBytes constructs an immutable buffer from a byte slice. The source buffer is wiped after the value has been copied over to the created container.

func NewBufferFromEntireReader Uses

func NewBufferFromEntireReader(r io.Reader) *LockedBuffer

NewBufferFromEntireReader reads from an io.Reader into an immutable buffer. It will continue reading until EOF or any other error.

func NewBufferFromReader Uses

func NewBufferFromReader(r io.Reader, size int) *LockedBuffer

NewBufferFromReader reads some number of bytes from an io.Reader into an immutable LockedBuffer.

If an error is encountered before size bytes are read, they will be returned. The number of bytes read can be inferred using the Size method.

func NewBufferFromReaderUntil Uses

func NewBufferFromReaderUntil(r io.Reader, delim byte) *LockedBuffer

NewBufferFromReaderUntil constructs an immutable buffer containing data sourced from an io.Reader object.

It will continue reading until it encounters the delimiter value, or an error occurs. The delimiter will not be included in the returned data.

func NewBufferRandom Uses

func NewBufferRandom(size int) *LockedBuffer

NewBufferRandom constructs an immutable buffer filled with cryptographically-secure random bytes.

func (*LockedBuffer) ByteArray16 Uses

func (b *LockedBuffer) ByteArray16() *[16]byte

ByteArray16 returns a pointer to some 16 byte array. Care must be taken not to dereference the pointer and instead pass it around as-is.

The length of the buffer must be at least 16 bytes in size and the LockedBuffer should not be destroyed. In either of these cases a nil value is returned.

func (*LockedBuffer) ByteArray32 Uses

func (b *LockedBuffer) ByteArray32() *[32]byte

ByteArray32 returns a pointer to some 32 byte array. Care must be taken not to dereference the pointer and instead pass it around as-is.

The length of the buffer must be at least 32 bytes in size and the LockedBuffer should not be destroyed. In either of these cases a nil value is returned.

func (*LockedBuffer) ByteArray64 Uses

func (b *LockedBuffer) ByteArray64() *[64]byte

ByteArray64 returns a pointer to some 64 byte array. Care must be taken not to dereference the pointer and instead pass it around as-is.

The length of the buffer must be at least 64 bytes in size and the LockedBuffer should not be destroyed. In either of these cases a nil value is returned.

func (*LockedBuffer) ByteArray8 Uses

func (b *LockedBuffer) ByteArray8() *[8]byte

ByteArray8 returns a pointer to some 8 byte array. Care must be taken not to dereference the pointer and instead pass it around as-is.

The length of the buffer must be at least 8 bytes in size and the LockedBuffer should not be destroyed. In either of these cases a nil value is returned.

func (*LockedBuffer) Bytes Uses

func (b *LockedBuffer) Bytes() []byte

Bytes returns a byte slice referencing the protected region of memory.

func (*LockedBuffer) Copy Uses

func (b *LockedBuffer) Copy(src []byte)

Copy performs a time-constant copy into a LockedBuffer. Move is preferred if the source is not also a LockedBuffer or if the source is no longer needed.

func (*LockedBuffer) CopyAt Uses

func (b *LockedBuffer) CopyAt(offset int, src []byte)

CopyAt performs a time-constant copy into a LockedBuffer at an offset. Move is preferred if the source is not also a LockedBuffer or if the source is no longer needed.

func (*LockedBuffer) Destroy Uses

func (b *LockedBuffer) Destroy()

Destroy wipes and frees the underlying memory of a LockedBuffer. The LockedBuffer will not be accessible or usable after this calls is made.

func (*LockedBuffer) EqualTo Uses

func (b *LockedBuffer) EqualTo(buf []byte) bool

EqualTo performs a time-constant comparison on the contents of a LockedBuffer with a given buffer. A destroyed LockedBuffer will always return false.

func (*LockedBuffer) Freeze Uses

func (b *LockedBuffer) Freeze()

Freeze makes a LockedBuffer's memory immutable. The call can be reversed with Melt.

func (*LockedBuffer) Int16 Uses

func (b *LockedBuffer) Int16() []int16

Int16 returns a slice pointing to the protected region of memory with the data represented as a sequence of signed 16 bit integers. Its length will be half that of the byte slice, excluding any remaining part that doesn't form a complete int16 value.

If called on a destroyed LockedBuffer, a nil slice will be returned.

func (*LockedBuffer) Int32 Uses

func (b *LockedBuffer) Int32() []int32

Int32 returns a slice pointing to the protected region of memory with the data represented as a sequence of signed 32 bit integers. Its length will be one quarter that of the byte slice, excluding any remaining part that doesn't form a complete int32 value.

If called on a destroyed LockedBuffer, a nil slice will be returned.

func (*LockedBuffer) Int64 Uses

func (b *LockedBuffer) Int64() []int64

Int64 returns a slice pointing to the protected region of memory with the data represented as a sequence of signed 64 bit integers. Its length will be one eighth that of the byte slice, excluding any remaining part that doesn't form a complete int64 value.

If called on a destroyed LockedBuffer, a nil slice will be returned.

func (*LockedBuffer) Int8 Uses

func (b *LockedBuffer) Int8() []int8

Int8 returns a slice pointing to the protected region of memory with the data represented as a sequence of signed 8 bit integers. If called on a destroyed LockedBuffer, a nil slice will be returned.

func (*LockedBuffer) IsAlive Uses

func (b *LockedBuffer) IsAlive() bool

IsAlive returns a boolean value indicating if a LockedBuffer is alive, i.e. that it has not been destroyed.

func (*LockedBuffer) IsMutable Uses

func (b *LockedBuffer) IsMutable() bool

IsMutable returns a boolean value indicating if a LockedBuffer is mutable.

func (*LockedBuffer) Melt Uses

func (b *LockedBuffer) Melt()

Melt makes a LockedBuffer's memory mutable. The call can be reversed with Freeze.

func (*LockedBuffer) Move Uses

func (b *LockedBuffer) Move(src []byte)

Move performs a time-constant move into a LockedBuffer. The source is wiped after the bytes are copied.

func (*LockedBuffer) MoveAt Uses

func (b *LockedBuffer) MoveAt(offset int, src []byte)

MoveAt performs a time-constant move into a LockedBuffer at an offset. The source is wiped after the bytes are copied.

func (*LockedBuffer) Reader Uses

func (b *LockedBuffer) Reader() *bytes.Reader

Reader returns a Reader object referencing the protected region of memory.

func (*LockedBuffer) Scramble Uses

func (b *LockedBuffer) Scramble()

Scramble attempts to overwrite the data with cryptographically-secure random bytes.

func (*LockedBuffer) Seal Uses

func (b *LockedBuffer) Seal() *Enclave

Seal takes a LockedBuffer object and returns its contents encrypted inside a sealed Enclave object. The LockedBuffer is subsequently destroyed and its contents wiped.

If Seal is called on a destroyed buffer, a nil enclave is returned.

func (*LockedBuffer) Size Uses

func (b *LockedBuffer) Size() int

Size gives you the length of a given LockedBuffer's data segment. A destroyed LockedBuffer will have a size of zero.

func (*LockedBuffer) String Uses

func (b *LockedBuffer) String() string

String returns a string representation of the protected region of memory.

func (*LockedBuffer) Uint16 Uses

func (b *LockedBuffer) Uint16() []uint16

Uint16 returns a slice pointing to the protected region of memory with the data represented as a sequence of unsigned 16 bit integers. Its length will be half that of the byte slice, excluding any remaining part that doesn't form a complete uint16 value.

If called on a destroyed LockedBuffer, a nil slice will be returned.

func (*LockedBuffer) Uint32 Uses

func (b *LockedBuffer) Uint32() []uint32

Uint32 returns a slice pointing to the protected region of memory with the data represented as a sequence of unsigned 32 bit integers. Its length will be one quarter that of the byte slice, excluding any remaining part that doesn't form a complete uint32 value.

If called on a destroyed LockedBuffer, a nil slice will be returned.

func (*LockedBuffer) Uint64 Uses

func (b *LockedBuffer) Uint64() []uint64

Uint64 returns a slice pointing to the protected region of memory with the data represented as a sequence of unsigned 64 bit integers. Its length will be one eighth that of the byte slice, excluding any remaining part that doesn't form a complete uint64 value.

If called on a destroyed LockedBuffer, a nil slice will be returned.

func (*LockedBuffer) Wipe Uses

func (b *LockedBuffer) Wipe()

Wipe attempts to overwrite the data with zeros.

Directories

PathSynopsis
core

Package memguard imports 8 packages (graph) and is imported by 43 packages. Updated 2019-08-19. Refresh now. Tools for package owners.