Documentation ¶
Overview ¶
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 ¶
- Variables
- func CatchInterrupt()
- func CatchSignal(f func(os.Signal), signals ...os.Signal)
- func Purge()
- func SafeExit(c int)
- func SafePanic(v interface{})
- func ScrambleBytes(buf []byte)
- func WipeBytes(buf []byte)
- type Enclave
- type LockedBuffer
- func NewBuffer(size int) *LockedBuffer
- func NewBufferFromBytes(src []byte) *LockedBuffer
- func NewBufferFromEntireReader(r io.Reader) (*LockedBuffer, error)
- func NewBufferFromReader(r io.Reader, size int) (*LockedBuffer, error)
- func NewBufferFromReaderUntil(r io.Reader, delim byte) (*LockedBuffer, error)
- func NewBufferRandom(size int) *LockedBuffer
- func (b *LockedBuffer) ByteArray16() *[16]byte
- func (b *LockedBuffer) ByteArray32() *[32]byte
- func (b *LockedBuffer) ByteArray64() *[64]byte
- func (b *LockedBuffer) ByteArray8() *[8]byte
- func (b *LockedBuffer) Bytes() []byte
- func (b *LockedBuffer) Copy(src []byte)
- func (b *LockedBuffer) CopyAt(offset int, src []byte)
- func (b *LockedBuffer) Destroy()
- func (b *LockedBuffer) EqualTo(buf []byte) bool
- func (b *LockedBuffer) Freeze()
- func (b *LockedBuffer) Int16() []int16
- func (b *LockedBuffer) Int32() []int32
- func (b *LockedBuffer) Int64() []int64
- func (b *LockedBuffer) Int8() []int8
- func (b *LockedBuffer) IsAlive() bool
- func (b *LockedBuffer) IsMutable() bool
- func (b *LockedBuffer) Melt()
- func (b *LockedBuffer) Move(src []byte)
- func (b *LockedBuffer) MoveAt(offset int, src []byte)
- func (b *LockedBuffer) Reader() *bytes.Reader
- func (b *LockedBuffer) Scramble()
- func (b *LockedBuffer) Seal() *Enclave
- func (b *LockedBuffer) Size() int
- func (b *LockedBuffer) String() string
- func (b *LockedBuffer) Uint16() []uint16
- func (b *LockedBuffer) Uint32() []uint32
- func (b *LockedBuffer) Uint64() []uint64
- func (b *LockedBuffer) Wipe()
- type Stream
Constants ¶
This section is empty.
Variables ¶
var ( // StreamChunkSize is the maximum amount of data that is locked into memory at a time. // If you get error allocating memory, increase your system's mlock limits. // Use 'ulimit -l' to see mlock limit on unix systems. StreamChunkSize = c )
Functions ¶
func CatchInterrupt ¶
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 ¶ added in v0.16.0
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.
- Signal is caught by the process
- Interrupt handler is executed
- Secure session state is wiped
- 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 ¶ added in v0.16.0
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 ¶
func SafeExit(c int)
SafeExit destroys everything sensitive before exiting with a specified status code.
func SafePanic ¶ added in v0.16.0
func SafePanic(v interface{})
SafePanic wipes all it can before calling panic(v).
func ScrambleBytes ¶ added in v0.16.0
func ScrambleBytes(buf []byte)
ScrambleBytes overwrites an arbitrary buffer with cryptographically-secure random bytes.
Types ¶
type Enclave ¶ added in v0.16.0
Enclave is a sealed and encrypted container for sensitive data.
func NewEnclave ¶ added in v0.16.0
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 ¶ added in v0.16.0
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 ¶ added in v0.16.0
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 ¶
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 ¶ added in v0.16.0
func NewBuffer(size int) *LockedBuffer
NewBuffer creates a mutable data container of the specified size.
func NewBufferFromBytes ¶ added in v0.16.0
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 ¶ added in v0.17.3
func NewBufferFromEntireReader(r io.Reader) (*LockedBuffer, error)
NewBufferFromEntireReader reads from an io.Reader into an immutable buffer. It will continue reading until EOF.
A nil error is returned precisely when we managed to read all the way until EOF. Any data read is returned in either case.
func NewBufferFromReader ¶ added in v0.16.3
func NewBufferFromReader(r io.Reader, size int) (*LockedBuffer, error)
NewBufferFromReader reads some number of bytes from an io.Reader into an immutable LockedBuffer.
An error is returned precisely when the number of bytes read is less than the requested amount. Any data read is returned in either case.
func NewBufferFromReaderUntil ¶ added in v0.16.3
func NewBufferFromReaderUntil(r io.Reader, delim byte) (*LockedBuffer, error)
NewBufferFromReaderUntil constructs an immutable buffer containing data sourced from an io.Reader object.
If an error is encountered before the delimiter value, the error will be returned along with the data read up until that point.
func NewBufferRandom ¶ added in v0.16.0
func NewBufferRandom(size int) *LockedBuffer
NewBufferRandom constructs an immutable buffer filled with cryptographically-secure random bytes.
func (*LockedBuffer) ByteArray16 ¶ added in v0.16.0
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 ¶ added in v0.16.0
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 ¶ added in v0.16.0
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 ¶ added in v0.16.0
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 ¶ added in v0.16.0
func (b *LockedBuffer) Bytes() []byte
Bytes returns a byte slice referencing the protected region of memory.
func (*LockedBuffer) Copy ¶
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 ¶ added in v0.4.0
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 ¶
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 ¶ added in v0.4.0
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 ¶ added in v0.16.0
func (b *LockedBuffer) Freeze()
Freeze makes a LockedBuffer's memory immutable. The call can be reversed with Melt.
func (*LockedBuffer) Int16 ¶ added in v0.14.0
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 ¶ added in v0.14.0
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 ¶ added in v0.14.0
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 ¶ added in v0.14.0
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 ¶ added in v0.16.0
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 ¶ added in v0.13.0
func (b *LockedBuffer) IsMutable() bool
IsMutable returns a boolean value indicating if a LockedBuffer is mutable.
func (*LockedBuffer) Melt ¶ added in v0.16.0
func (b *LockedBuffer) Melt()
Melt makes a LockedBuffer's memory mutable. The call can be reversed with Freeze.
func (*LockedBuffer) Move ¶
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 ¶ added in v0.4.0
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 ¶ added in v0.16.4
func (b *LockedBuffer) Reader() *bytes.Reader
Reader returns a Reader object referencing the protected region of memory.
func (*LockedBuffer) Scramble ¶ added in v0.16.0
func (b *LockedBuffer) Scramble()
Scramble attempts to overwrite the data with cryptographically-secure random bytes.
func (*LockedBuffer) Seal ¶ added in v0.16.0
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 ¶ added in v0.13.0
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 ¶ added in v0.17.0
func (b *LockedBuffer) String() string
String returns a string representation of the protected region of memory.
func (*LockedBuffer) Uint16 ¶ added in v0.14.0
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 ¶ added in v0.14.0
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 ¶ added in v0.14.0
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 ¶ added in v0.13.0
func (b *LockedBuffer) Wipe()
Wipe attempts to overwrite the data with zeros.
type Stream ¶ added in v0.21.0
Stream is an in-memory encrypted container implementing the reader and writer interfaces.
It is most useful when you need to store lots of data in memory and are able to work on it in chunks.
func NewStream ¶ added in v0.21.0
func NewStream() *Stream
NewStream initialises a new empty Stream object.
func (*Stream) Flush ¶ added in v0.22.0
func (s *Stream) Flush() (*LockedBuffer, error)
Flush reads all of the data from a Stream and returns it inside a LockedBuffer. If an error is encountered before all the data could be read, it is returned along with any data read up until that point.
func (*Stream) Next ¶ added in v0.22.0
func (s *Stream) Next() (*LockedBuffer, error)
Next grabs the next chunk of data from the Stream and returns it decrypted inside a LockedBuffer. Any error from the stream is forwarded.
func (*Stream) Read ¶ added in v0.21.0
Read decrypts and places some data from a Stream object into a provided buffer.
If there is no data, the call will return an io.EOF error. If the caller provides a buffer that is too small to hold the next chunk of data, the remaining bytes are re-encrypted and added to the front of the queue to be returned in the next call.
To be performant, have
func (*Stream) Size ¶ added in v0.21.0
Size returns the number of bytes of data currently stored within a Stream object.