forked from awnumar/memguard
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcontainer.go
95 lines (71 loc) · 3.33 KB
/
container.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package memguard
import (
"crypto/subtle"
"runtime"
"sync"
"unsafe"
"github.com/awnumar/memguard/memcall"
)
/*
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.
*/
type LockedBuffer struct {
*container // Import all the container fields.
*littleBird // Monitor this for auto-destruction.
}
// container implements the actual data container.
type container struct {
sync.Mutex // Local mutex lock.
buffer []byte // Slice that references the protected memory.
mutable bool // Is this LockedBuffer mutable?
}
// littleBird is a value that we monitor instead of the LockedBuffer
// itself. It allows us to tell the GC to auto-destroy LockedBuffers.
type littleBird [16]byte
// Global internal function used to create new secure containers.
func newContainer(size int, mutable bool) (*LockedBuffer, error) {
// Return an error if length < 1.
if size < 1 {
return nil, ErrInvalidLength
}
// Allocate a new LockedBuffer.
ib := new(container)
b := &LockedBuffer{ib, new(littleBird)}
// Round length + 32 bytes for the canary to a multiple of the page size..
roundedLength := roundToPageSize(size + 32)
// Calculate the total size of memory including the guard pages.
totalSize := (2 * pageSize) + roundedLength
// Allocate it all.
memory := memcall.Alloc(totalSize)
// Make the guard pages inaccessible.
memcall.Protect(memory[:pageSize], false, false)
memcall.Protect(memory[pageSize+roundedLength:], false, false)
// Lock the pages that will hold the sensitive data.
memcall.Lock(memory[pageSize : pageSize+roundedLength])
// Set the canary.
subtle.ConstantTimeCopy(1, memory[pageSize+roundedLength-size-32:pageSize+roundedLength-size], canary)
// Set Buffer to a byte slice that describes the reigon of memory that is protected.
b.buffer = getBytes(uintptr(unsafe.Pointer(&memory[pageSize+roundedLength-size])), size)
// The buffer is filled with weird bytes so let's wipe it.
wipeBytes(b.buffer)
// Set appropriate mutability state.
b.mutable = true
if !mutable {
b.MakeImmutable()
}
// Use a finalizer to make sure the buffer gets destroyed if forgotten.
runtime.SetFinalizer(b.littleBird, func(_ *littleBird) {
go ib.Destroy()
})
// Append the container to allLockedBuffers. We have to add container
// instead of LockedBuffer so that littleBird can become unreachable.
allLockedBuffersMutex.Lock()
allLockedBuffers = append(allLockedBuffers, ib)
allLockedBuffersMutex.Unlock()
// Return a pointer to the LockedBuffer.
return b, nil
}