-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsecurekey.go
More file actions
66 lines (55 loc) · 1.64 KB
/
securekey.go
File metadata and controls
66 lines (55 loc) · 1.64 KB
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
package main
import (
"fmt"
"os"
"unsafe"
"golang.org/x/sys/unix"
)
// secureAlloc allocates a page-aligned memory region outside the Go heap,
// locks it into RAM (no swap), and returns a slice backed by that region.
// The caller must call secureFree when done.
func secureAlloc(size int) ([]byte, error) {
pageSize := os.Getpagesize()
// Round up to page boundary
pages := (size + pageSize - 1) / pageSize
allocSize := pages * pageSize
mem, err := unix.Mmap(-1, 0, allocSize,
unix.PROT_READ|unix.PROT_WRITE,
unix.MAP_ANON|unix.MAP_PRIVATE)
if err != nil {
return nil, fmt.Errorf("mmap: %w", err)
}
if err := unix.Mlock(mem); err != nil {
unix.Munmap(mem)
return nil, fmt.Errorf("mlock: %w", err)
}
return mem[:size], nil
}
// secureReadOnly marks a secure allocation as read-only.
// Any subsequent write to this memory will cause a SIGSEGV.
func secureReadOnly(mem []byte) error {
pageSize := os.Getpagesize()
pages := (len(mem) + pageSize - 1) / pageSize
allocSize := pages * pageSize
// Recover the full page-aligned slice for mprotect
fullMem := unsafe.Slice(&mem[0], allocSize)
return unix.Mprotect(fullMem, unix.PROT_READ)
}
// secureFree zeros, unlocks, and unmaps a secure allocation.
func secureFree(mem []byte) {
if len(mem) == 0 {
return
}
pageSize := os.Getpagesize()
pages := (len(mem) + pageSize - 1) / pageSize
allocSize := pages * pageSize
fullMem := unsafe.Slice(&mem[0], allocSize)
// Make writable again so we can zero it
_ = unix.Mprotect(fullMem, unix.PROT_READ|unix.PROT_WRITE)
// Zero the memory
for i := range fullMem {
fullMem[i] = 0
}
_ = unix.Munlock(fullMem)
_ = unix.Munmap(fullMem)
}