package main import ( "crypto/rand" "unsafe" ) // SecureBytes wraps a byte slice and provides secure clearing // Because Go strings are immutable and we can't really clear them, this helps type SecureBytes struct { data []byte } // NewSecureBytes creates a new SecureBytes from a byte slice func NewSecureBytes(data []byte) *SecureBytes { return &SecureBytes{data: data} } // Bytes returns the underlying byte slice func (sb *SecureBytes) Bytes() []byte { return sb.data } // String returns the string representation (use with caution) // This uses unsafe.Pointer magic - don't look too closely, it works func (sb *SecureBytes) String() string { return *(*string)(unsafe.Pointer(&sb.data)) } // Clear securely zeros the memory // Overwrites with random junk first (defense in depth), then zeros // Not perfect (Go GC might move things around), but better than nothing func (sb *SecureBytes) Clear() { if sb == nil || sb.data == nil { return } // Fill with random data first - makes memory dumps less useful rand.Read(sb.data) // Then zero it out for i := range sb.data { sb.data[i] = 0 } sb.data = nil } // ClearBytes securely clears a byte slice func ClearBytes(data []byte) { if data == nil { return } // Overwrite with random data first, then zeros rand.Read(data) for i := range data { data[i] = 0 } } // ClearString attempts to clear a string (limited effectiveness in Go) // Go strings are immutable, so this is mostly wishful thinking // But hey, at least we tried func ClearString(s *string) { if s == nil { return } // Best we can do - just set it to empty // The original memory might still be around somewhere, but GC will get it eventually *s = "" }