memguard

package module
v0.3.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 5, 2017 License: Apache-2.0 Imports: 9 Imported by: 0

README

MemGuard

A pure Go library that handles sensitive values in memory.


This is a thread-safe package, designed to allow you to easily handle sensitive values in memory. It supports all major operating systems and is written in pure Go.

Features

  • Memory is allocated using system calls, thereby bypassing the Go runtime and preventing the GC from messing with it.
  • To prevent buffer overflows and underflows, the secure buffer is sandwiched between two protected guard pages. If these pages are accessed, a SIGSEGV violation is triggered.
  • The secure buffer is prepended with a random canary. If this value changes, the process will panic. This is designed to prevent buffer underflows.
  • All pages between the two guards are locked to stop them from being swapped to disk.
  • The secure buffer can be made read-only so that any other action triggers a SIGSEGV violation.
  • When freeing, secure memory is wiped.
  • The API includes functions to disable system core dumps and catch signals.

Some of these features were inspired by libsodium, so credits to them.

Full documentation and a complete overview of the API can be found here.

Installation

Although we do recommend using a release, the simplest way to install the library is to go get it:

$ go get github.com/libeclipse/memguard

If you would prefer a signed release that you can verify and manually compile yourself, download and extract the latest release. Then go ahead and run:

$ go install -v ./

The releases are cryptographically signed with my PGP key, which can be found on keybase. To import it directly into GPG, run:

$ curl https://keybase.io/awn/pgp_keys.asc | gpg --import

Documentation

Overview

Package memguard is designed to allow you to easily handle sensitive values in memory.

The general working cycle is easy to follow:

// Declare a protected slice and move bytes into it.
encryptionKey := memguard.New(32)
encryptionKey.Move(generateRandomBytes(32))

// Use the buffer wherever you need it.
Encrypt(encryptionKey.Buffer, plaintext)

// Destroy it after you're done.
encryptionKey.Destroy()

As you'll have noted, the example above does not append or assign the key to the buffer, but rather it uses the built-in API function `Move()`.

b := memguard.New(32)

b.Move([]byte("...")) // Correct.
b.Copy([]byte("...")) // Less correct; original buffer isn't wiped.

b.Buffer = []byte("...")                   // WRONG
b.Buffer = append(b.Buffer, []byte("...")) // WRONG

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 := memguard.NewFromBytes([]byte("yellow submarine"))

// Make sure that the size of the array matches the size of the Buffer.
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. Do not use the mutex when calling a function that is part of the MemGuard API. For example:

b := New(4)
b.Lock()
copy(b.Buffer, []byte("test"))
b.Unlock()

c := New(4)
c.Lock()
c.Copy([]byte("test")) // This will deadlock.
c.Unlock()

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(0) // 0 is the status code.

Index

Constants

This section is empty.

Variables

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

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

View Source
var ErrZeroLength = errors.New("memguard.Err: length of buffer must be non-zero")

ErrZeroLength is returned when a LockedBuffer of smaller than one bytes is requested.

Functions

func CatchInterrupt

func CatchInterrupt(f ExitFunc)

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

memguard.CatchInterrupt(func() {
	fmt.Println("Interrupt signal received. Exiting...")
})

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

func DestroyAll

func DestroyAll()

DestroyAll calls Destroy on all LockedBuffers. This function can be called even if no LockedBuffers exist.

func DisableCoreDumps

func DisableCoreDumps()

DisableCoreDumps disables core dumps on Unix systems. On windows it is a no-op.

func SafeExit

func SafeExit(c int)

SafeExit exits the program with the specified return code, but calls DestroyAll before doing so.

func WipeBytes

func WipeBytes(buf []byte)

WipeBytes zeroes out a byte slice.

Types

type ExitFunc

type ExitFunc func()

ExitFunc is a function type that takes no arguments and returns no values. It is passed to CatchInterrupt which executes it before terminating the application securely.

type LockedBuffer

type LockedBuffer struct {
	// Exposed mutex for implementing thread-safety
	// both within and outside of the API.
	sync.Mutex

	// Buffer holds the secure values themselves.
	Buffer []byte

	// Holds the current permissions of Buffer.
	// Possible values are `ReadWrite` and `ReadOnly`.
	Permissions string

	// A boolean option indicating whether this
	// LockedBuffer has been destroyed. No API
	// calls succeed on a destroyed buffer.
	Destroyed bool
}

LockedBuffer implements a structure that holds protected values.

func New

func New(length int) (*LockedBuffer, error)

New creates a new *LockedBuffer and returns it. The LockedBuffer's state is `ReadWrite`. Length must be greater than zero.

func NewFromBytes

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

NewFromBytes creates a new *LockedBuffer from a byte slice, attempting to destroy the old value before returning. It is identicle to calling New() followed by Move().

func (*LockedBuffer) Copy

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

Copy copies bytes from a byte slice into a LockedBuffer, preserving the original slice. This is insecure, and so Move() should be favoured unless you have a specific need.

func (*LockedBuffer) Destroy

func (b *LockedBuffer) Destroy()

Destroy verifies that everything went well, wipes the Buffer, and then unlocks and frees all related memory. This function should be called on all LockedBuffers before exiting. If the LockedBuffer has already been destroyed, then nothing happens and the function returns.

func (*LockedBuffer) Move

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

Move copies bytes from a byte slice into a LockedBuffer, wiping the original slice afterwards.

func (*LockedBuffer) ReadOnly

func (b *LockedBuffer) ReadOnly() error

ReadOnly makes the buffer read-only. After setting this, any other action will trigger a SIGSEGV violation.

func (*LockedBuffer) ReadWrite

func (b *LockedBuffer) ReadWrite() error

ReadWrite makes the buffer readable and writable. This is the default state of new LockedBuffers.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL