forked from stakater/Reloader
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsecure_token.go
More file actions
183 lines (148 loc) · 3.54 KB
/
secure_token.go
File metadata and controls
183 lines (148 loc) · 3.54 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
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
package handler
import (
"crypto/rand"
"encoding/base64"
"sync"
)
// SecureToken provides a wrapper around sensitive token data with automatic zeroing
type SecureToken struct {
data []byte
mu sync.RWMutex
}
// NewSecureToken creates a new secure token from a string
func NewSecureToken(token string) *SecureToken {
if token == "" {
return &SecureToken{data: nil}
}
return &SecureToken{
data: []byte(token),
}
}
// Get returns the token value as a string (creates a copy)
func (st *SecureToken) Get() string {
if st == nil {
return ""
}
st.mu.RLock()
defer st.mu.RUnlock()
if st.data == nil {
return ""
}
// Return a copy to prevent external modification
return string(st.data)
}
// Zero securely erases the token from memory
func (st *SecureToken) Zero() {
if st == nil {
return
}
st.mu.Lock()
defer st.mu.Unlock()
if st.data == nil {
return
}
// Overwrite with random data first
_, _ = rand.Read(st.data)
// Then zero it
for i := range st.data {
st.data[i] = 0
}
st.data = nil
}
// IsEmpty checks if the token is empty
func (st *SecureToken) IsEmpty() bool {
if st == nil {
return true
}
st.mu.RLock()
defer st.mu.RUnlock()
return len(st.data) == 0
}
// MaskedString returns a masked version for logging
func (st *SecureToken) MaskedString() string {
if st == nil || st.IsEmpty() {
return "[empty]"
}
st.mu.RLock()
defer st.mu.RUnlock()
length := len(st.data)
if length <= 8 {
return "****"
}
// Show first 4 and last 4 characters
return string(st.data[:4]) + "..." + string(st.data[length-4:])
}
// CompareConstantTime performs constant-time comparison to prevent timing attacks
func (st *SecureToken) CompareConstantTime(other string) bool {
if st == nil || st.IsEmpty() {
return other == ""
}
st.mu.RLock()
defer st.mu.RUnlock()
otherBytes := []byte(other)
// Constant-time length check
if len(st.data) != len(otherBytes) {
return false
}
// Constant-time byte comparison
var result byte
for i := range st.data {
result |= st.data[i] ^ otherBytes[i]
}
return result == 0
}
// TokenCache provides a simple cache for tokens with expiration
type TokenCache struct {
tokens map[string]*SecureToken
mu sync.RWMutex
}
// NewTokenCache creates a new token cache
func NewTokenCache() *TokenCache {
return &TokenCache{
tokens: make(map[string]*SecureToken),
}
}
// Set stores a token in the cache
func (tc *TokenCache) Set(key string, token *SecureToken) {
tc.mu.Lock()
defer tc.mu.Unlock()
// Zero old token if it exists
if old, exists := tc.tokens[key]; exists {
old.Zero()
}
tc.tokens[key] = token
}
// Get retrieves a token from the cache
func (tc *TokenCache) Get(key string) *SecureToken {
tc.mu.RLock()
defer tc.mu.RUnlock()
return tc.tokens[key]
}
// Clear removes all tokens and zeros them
func (tc *TokenCache) Clear() {
tc.mu.Lock()
defer tc.mu.Unlock()
for _, token := range tc.tokens {
token.Zero()
}
tc.tokens = make(map[string]*SecureToken)
}
// obfuscateForLog masks sensitive data for logging
func obfuscateForLog(data string, showChars int) string {
if data == "" {
return "[empty]"
}
if len(data) <= showChars*2 {
return "****"
}
return data[:showChars] + "..." + data[len(data)-showChars:]
}
// HashToken creates a one-way hash of a token for comparison purposes
func HashToken(token string) string {
if token == "" {
return ""
}
// Use base64 encoding of the token for a simple non-reversible representation
// In production, you might want to use a proper hash function like SHA256
return base64.StdEncoding.EncodeToString([]byte(token))
}