-
Notifications
You must be signed in to change notification settings - Fork 1
/
secret.go
116 lines (100 loc) · 3.82 KB
/
secret.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
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
package secrecy
import (
"fmt"
"reflect"
"runtime"
"unsafe"
)
// SecretLeakedMarker is a marker string returned by Secret[T] when accidentally
// leaking the secret. This value is returned when you try to fmt.Println(secret)
// or serialize it to JSON/XML. This enable you to easily monitor your logs for
// secret leak.
var SecretLeakedMarker = "<!SECRET_LEAKED!>"
// NewSecret wraps given secret and returns *Secret[T]. Wrapped value and inner
// values will be entirely wiped from memory when garbage collected, this
// includes maps, slices pointers, etc. Returned secret owns wrapped value and
// inner value, you must not share underlying data.
func NewSecret[T any](value T) *Secret[T] {
secret := new(Secret[T])
secret.value = value
runtime.SetFinalizer(secret, Zeroize)
return secret
}
// Secret is a wrapper type for values that contains secrets, which attempts to
// limit accidental exposure and ensure secrets are wiped from memory when
// garbage collected. (e.g. passwords, cryptographic keys, access tokens or
// other credentials)
//
// Prefer SecretString over Secret[string] as go string are immutable and can't
// be wiped from memory.
type Secret[T any] struct {
value T
}
// ExposeSecret returns a copy of inner secret.
// Don't store returned value and prefer passing Secret itself if needed.
func (s *Secret[T]) ExposeSecret() T {
return s.value
}
// String implements fmt.Stringer.
// This function returns the [SecretLeakedMarker] marker string so it can be
// easily searched.
func (s *Secret[T]) String() string {
return SecretLeakedMarker
}
// Format implements fmt.GoStringer.
// This function return the [SecretLeakedMarker] marker string and appends
// "Secret[T](******)" as a suffix.
func (s *Secret[T]) GoString() string {
typeName := reflect.TypeOf(s.value).String()
return fmt.Sprintf(SecretLeakedMarker+" Secret[%v](******)", typeName)
}
// MarshalText implements encoding.TextMarshaler
// This function returns the [SecretLeakedMarker] marker byte slice so it can be
// easily searched.
func (s *Secret[T]) MarshalText() ([]byte, error) {
return []byte(SecretLeakedMarker), nil
}
// Disable zeroize on garbage collection for this secret.
func (s *Secret[T]) DisableZeroize() {
runtime.SetFinalizer(s, nil)
}
// Enable zeroize on garbage collection for this secret.
// By default zeroize is enabled, you don't need to call this function if didn't
// call DisableZeroize before.
func (s *Secret[T]) EnableZeroize() {
runtime.SetFinalizer(s, Zeroize)
}
// Zeroize implements Zeroizer.
func (s Secret[T]) Zeroize() {
Zeroize(s.value)
}
// NewSecretString wraps given secret and returns SecretString.
// This function takes a byte slice as parameter so the secret can be wiped
// on garbage collection. Use UnsafeStringToBytes to convert your string to a
// byte slice if needed.
func NewSecretString(secret []byte) SecretString {
return SecretString{NewSecret(secret)}
}
// SecretString is a wrapper around Secret[[]byte] that expose its secret
// as string.
type SecretString struct {
*Secret[[]byte]
}
// ExposeSecret exposes underlying secret as a string using unsafe.
// Don't store returned value and prefer passing SecretString itself if needed.
func (ss SecretString) ExposeSecret() string {
bytes := ss.Secret.ExposeSecret()
return UnsafeBytesToString(bytes)
}
// UnsafeStringToBytes converts given string into a bytes slice using unsafe.
// This function doesn't perform any copy so mutating the byte slice will mutate
// the string.
func UnsafeStringToBytes(str string) []byte {
return unsafe.Slice(unsafe.StringData(str), len(str))
}
// UnsafeStringToBytes converts given string into a bytes slice using unsafe.
// This function doesn't perform any copy so mutating the byte slice will mutate
// the returned string.
func UnsafeBytesToString(bytes []byte) string {
return unsafe.String(unsafe.SliceData(bytes), len(bytes))
}