139 lines
3.4 KiB
Go
139 lines
3.4 KiB
Go
package secret
|
|
|
|
import (
|
|
"context"
|
|
"encoding/base64"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// Provider for secrets.
|
|
type Provider interface {
|
|
// GetSecret loads a secret by named key.
|
|
GetSecret(key string) (value []byte, err error)
|
|
}
|
|
|
|
// Crypter for in-transit encrypted secrets.
|
|
type Crypter interface {
|
|
// Encrypt a plaintext using the key specified in keyID.
|
|
Encrypt(ctx context.Context, keyID string, plaintext []byte) (ciphertext []byte, err error)
|
|
|
|
// Decrypt a ciphertext using the key specified in keyID.
|
|
Decrypt(ctx context.Context, keyID string, ciphertext []byte) (plaintext []byte, err error)
|
|
}
|
|
|
|
func Encrypt(ctx context.Context, p Provider, keyID string, plaintext []byte) (ciphertext []byte, err error) {
|
|
if c, ok := p.(Crypter); ok {
|
|
return c.Encrypt(ctx, keyID, plaintext)
|
|
}
|
|
return nil, fmt.Errorf("secret: %T doesn't implement Crypter", p)
|
|
}
|
|
|
|
func Decrypt(ctx context.Context, p Provider, keyID string, ciphertext []byte) (plaintext []byte, err error) {
|
|
if c, ok := p.(Crypter); ok {
|
|
return c.Decrypt(ctx, keyID, ciphertext)
|
|
}
|
|
return nil, fmt.Errorf("secret: %T doesn't implement Crypter", p)
|
|
}
|
|
|
|
// AmbiguousKey is an error incdicating that the secret doesn't resolve to exactly one item.
|
|
type AmbiguousKey struct {
|
|
Key string
|
|
}
|
|
|
|
func (err AmbiguousKey) Error() string {
|
|
return fmt.Sprintf("secret: ambigious secret key %q", err.Key)
|
|
}
|
|
|
|
// NotFound is an error indicating the secret can not be found.
|
|
type NotFound struct {
|
|
Key string
|
|
}
|
|
|
|
func (err NotFound) Error() string {
|
|
if err.Key == "" {
|
|
return "secret: not found"
|
|
}
|
|
return fmt.Sprintf("secret: %q not found", err.Key)
|
|
}
|
|
|
|
// ToBinary converts a string to []bytes.
|
|
//
|
|
// There are special prefixes for binary encoded formats:
|
|
// - hex: for hexadecimal encoded strings
|
|
// - b64: for base-64 encoded strings (raw encoding)
|
|
//
|
|
// If a special prefix is found, the appropriate codec will be used to decode to string to []byte,
|
|
// when there is an error decoding, it may result in an empty value.
|
|
//
|
|
// All other strings will be converted to []byte as-is.
|
|
func ToBinary(s string) (bytes []byte) {
|
|
switch {
|
|
case strings.HasPrefix(s, "hex:"):
|
|
bytes, _ = hex.DecodeString(s[4:])
|
|
case strings.HasPrefix(s, "b64:"):
|
|
bytes, _ = base64.RawStdEncoding.DecodeString(s[4:])
|
|
default:
|
|
bytes = []byte(s)
|
|
}
|
|
return
|
|
}
|
|
|
|
type providerOptions struct {
|
|
timeout time.Duration
|
|
apiKey string
|
|
credentials string
|
|
clientID string
|
|
secretID string
|
|
region string
|
|
}
|
|
|
|
// Option for providers.
|
|
type Option func(*providerOptions)
|
|
|
|
func newProviderOptions(opts ...Option) *providerOptions {
|
|
options := new(providerOptions)
|
|
for _, opt := range opts {
|
|
opt(options)
|
|
}
|
|
return options
|
|
}
|
|
|
|
// WithAPIKey sets the provider API key.
|
|
func WithAPIKey(key string) Option {
|
|
return func(p *providerOptions) {
|
|
p.apiKey = key
|
|
}
|
|
}
|
|
|
|
// WithCredentials specifies the API credentials.
|
|
func WithCredentials(credentials string) Option {
|
|
return func(p *providerOptions) {
|
|
p.credentials = credentials
|
|
}
|
|
}
|
|
|
|
// WithOAuth sets the provider client ID & secret options.
|
|
func WithOAuth(clientID, secret string) Option {
|
|
return func(p *providerOptions) {
|
|
p.clientID = clientID
|
|
p.secretID = secret
|
|
}
|
|
}
|
|
|
|
// WithTimeout sets the provider timeout option.
|
|
func WithTimeout(timeout time.Duration) Option {
|
|
return func(p *providerOptions) {
|
|
p.timeout = timeout
|
|
}
|
|
}
|
|
|
|
// WithRegion sets the provider (cloud) region.
|
|
func WithRegion(region string) Option {
|
|
return func(p *providerOptions) {
|
|
p.region = region
|
|
}
|
|
}
|