package secret import ( "encoding/base64" "fmt" "io/ioutil" "sync" ) const numSecretBytes = 32 type sec struct { path string bytes []byte sync.RWMutex } var ( theSecret = &sec{} ) func SetPath(path string) { theSecret.Lock() defer theSecret.Unlock() theSecret.path = path theSecret.bytes = nil } // Lazy access to the HMAC secret key. We must be lazy because if the key // is not already there, it will be generated by gitlab-rails, and // gitlab-rails is slow. func Bytes() ([]byte, error) { if bytes := getBytes(); bytes != nil { return copyBytes(bytes), nil } return setBytes() } func getBytes() []byte { theSecret.RLock() defer theSecret.RUnlock() return theSecret.bytes } func copyBytes(bytes []byte) []byte { out := make([]byte, len(bytes)) copy(out, bytes) return out } func setBytes() ([]byte, error) { theSecret.Lock() defer theSecret.Unlock() if theSecret.bytes != nil { return theSecret.bytes, nil } base64Bytes, err := ioutil.ReadFile(theSecret.path) if err != nil { return nil, fmt.Errorf("secret.setBytes: read %q: %v", theSecret.path, err) } secretBytes := make([]byte, base64.StdEncoding.DecodedLen(len(base64Bytes))) n, err := base64.StdEncoding.Decode(secretBytes, base64Bytes) if err != nil { return nil, fmt.Errorf("secret.setBytes: decode secret: %v", err) } if n != numSecretBytes { return nil, fmt.Errorf("secret.setBytes: expected %d secretBytes in %s, found %d", numSecretBytes, theSecret.path, n) } theSecret.bytes = secretBytes return copyBytes(theSecret.bytes), nil }