mirror of
https://github.com/awnumar/memguard.git
synced 2026-02-06 17:59:49 +00:00
Memcall is not touched yet. Otherwise this replaces sentinel error values with IsX exported predicate functions. This enables more effective error handling by the users and leaves much more freedom for changing the implementation in the future without breaking API. Updates #111
168 lines
3.7 KiB
Go
168 lines
3.7 KiB
Go
package core
|
|
|
|
import (
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"gitlab.com/NebulousLabs/fastrand"
|
|
)
|
|
|
|
var (
|
|
// Static allocation for fast random bytes reading.
|
|
buf32, _ = NewBuffer(32)
|
|
)
|
|
|
|
// Interval of time between each verify & re-key cycle.
|
|
var Interval uint64 = 8 // milliseconds
|
|
|
|
// SetInterval changes the interval in milliseconds between re-key cycles. The default is 8 milliseconds and a value below one second is recommended.
|
|
func SetInterval(interval uint64) {
|
|
atomic.StoreUint64(&Interval, interval)
|
|
}
|
|
|
|
/*
|
|
Coffer is a specialized container for securing highly-sensitive, 32 byte values.
|
|
*/
|
|
type Coffer struct {
|
|
sync.RWMutex
|
|
|
|
left *Buffer // Left partition.
|
|
right *Buffer // Right partition.
|
|
}
|
|
|
|
// NewCoffer is a raw constructor for the *Coffer object.
|
|
func NewCoffer() *Coffer {
|
|
// Create a new Coffer object.
|
|
s := new(Coffer)
|
|
|
|
// Allocate the partitions.
|
|
s.left, _ = NewBuffer(32)
|
|
s.right, _ = NewBuffer(32)
|
|
|
|
// Initialise with a random 32 byte value.
|
|
s.Initialise()
|
|
|
|
go func(s *Coffer) {
|
|
for {
|
|
// Sleep for the specified interval.
|
|
time.Sleep(time.Duration(atomic.LoadUint64(&Interval)) * time.Millisecond)
|
|
|
|
// Re-key the contents, exiting the routine if object destroyed.
|
|
if err := s.Rekey(); err != nil {
|
|
break
|
|
}
|
|
}
|
|
}(s)
|
|
|
|
return s
|
|
}
|
|
|
|
/*
|
|
Initialise is used to reset the value stored inside a Coffer to a new random 32 byte value, overwriting the old.
|
|
*/
|
|
func (s *Coffer) Initialise() error {
|
|
// Check if it has been destroyed.
|
|
if s.Destroyed() {
|
|
return errors[errCodeCofferExpired]
|
|
}
|
|
|
|
// Attain the mutex.
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
|
|
// Overwrite the old value with fresh random bytes.
|
|
Scramble(s.left.Data())
|
|
Scramble(s.right.Data())
|
|
|
|
// left = left XOR hash(right)
|
|
hr := Hash(s.right.Data())
|
|
for i := range hr {
|
|
s.left.Data()[i] ^= hr[i]
|
|
}
|
|
Wipe(hr)
|
|
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
View returns a snapshot of the contents of a Coffer inside a Buffer. As usual the Buffer should be destroyed as soon as possible after use by calling the Destroy method.
|
|
*/
|
|
func (s *Coffer) View() (*Buffer, error) {
|
|
// Attain a read-only lock.
|
|
s.RLock()
|
|
defer s.RUnlock()
|
|
|
|
// Check if it's destroyed.
|
|
if s.Destroyed() {
|
|
return nil, errors[errCodeCofferExpired]
|
|
}
|
|
|
|
// Create a new Buffer for the data.
|
|
b, _ := NewBuffer(32)
|
|
|
|
// data = hash(right) XOR left
|
|
h := Hash(s.right.Data())
|
|
for i := range b.Data() {
|
|
b.Data()[i] = h[i] ^ s.left.Data()[i]
|
|
}
|
|
Wipe(h)
|
|
|
|
// Return the view.
|
|
return b, nil
|
|
}
|
|
|
|
/*
|
|
Rekey is used to re-key a Coffer. Ideally this should be done at short, regular intervals.
|
|
*/
|
|
func (s *Coffer) Rekey() error {
|
|
// Check if it has been destroyed.
|
|
if s.Destroyed() {
|
|
return errors[errCodeCofferExpired]
|
|
}
|
|
|
|
// Attain the mutex.
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
|
|
// Attain 32 bytes of fresh cryptographic buf32.
|
|
fastrand.Read(buf32.Data())
|
|
|
|
// Hash the current right partition for later.
|
|
hashRightCurrent := Hash(s.right.Data())
|
|
|
|
// new_right = current_right XOR buf32
|
|
for i := range s.right.Data() {
|
|
s.right.Data()[i] ^= buf32.Data()[i]
|
|
}
|
|
|
|
// new_left = current_left XOR hash(current_right) XOR hash(new_right)
|
|
hashRightNew := Hash(s.right.Data())
|
|
for i := range s.left.Data() {
|
|
s.left.Data()[i] ^= hashRightCurrent[i] ^ hashRightNew[i]
|
|
}
|
|
Wipe(hashRightNew)
|
|
|
|
return nil
|
|
}
|
|
|
|
/*
|
|
Destroy wipes and cleans up all memory related to a Coffer object. Once this method has been called, the Coffer can no longer be used and a new one should be created instead.
|
|
*/
|
|
func (s *Coffer) Destroy() {
|
|
// Attain the mutex.
|
|
s.Lock()
|
|
defer s.Unlock()
|
|
|
|
// Destroy the partitions.
|
|
s.left.Destroy()
|
|
s.right.Destroy()
|
|
}
|
|
|
|
// Destroyed returns a boolean value indicating if a Coffer has been destroyed.
|
|
func (s *Coffer) Destroyed() bool {
|
|
s.RLock()
|
|
defer s.RUnlock()
|
|
|
|
return (!s.left.Alive()) && (!s.right.Alive())
|
|
}
|