mirror of
https://github.com/lightninglabs/aperture.git
synced 2025-12-17 09:04:19 +01:00
93 lines
2.6 KiB
Go
93 lines
2.6 KiB
Go
package aperture
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/lightninglabs/aperture/lsat"
|
|
"github.com/lightninglabs/aperture/mint"
|
|
"go.etcd.io/etcd/clientv3"
|
|
)
|
|
|
|
var (
|
|
// secretsPrefix is the key we'll use to prefix all LSAT identifiers
|
|
// with when storing secrets in an etcd cluster.
|
|
secretsPrefix = "secrets"
|
|
)
|
|
|
|
// idKey returns the full key to store in the database for an LSAT identifier.
|
|
// The identifier is hex-encoded in order to prevent conflicts with the etcd key
|
|
// delimeter.
|
|
//
|
|
// The resulting path of the identifier bff4ee83 within etcd would look like:
|
|
// lsat/proxy/secrets/bff4ee83
|
|
func idKey(id [sha256.Size]byte) string {
|
|
return strings.Join(
|
|
[]string{topLevelKey, secretsPrefix, hex.EncodeToString(id[:])},
|
|
etcdKeyDelimeter,
|
|
)
|
|
}
|
|
|
|
// secretStore is a store of LSAT secrets backed by an etcd cluster.
|
|
type secretStore struct {
|
|
*clientv3.Client
|
|
}
|
|
|
|
// A compile-time constraint to ensure secretStore implements mint.SecretStore.
|
|
var _ mint.SecretStore = (*secretStore)(nil)
|
|
|
|
// newSecretStore instantiates a new LSAT secrets store backed by an etcd
|
|
// cluster.
|
|
func newSecretStore(client *clientv3.Client) *secretStore {
|
|
return &secretStore{Client: client}
|
|
}
|
|
|
|
// NewSecret creates a new cryptographically random secret which is keyed by the
|
|
// given hash.
|
|
func (s *secretStore) NewSecret(ctx context.Context,
|
|
id [sha256.Size]byte) ([lsat.SecretSize]byte, error) {
|
|
|
|
var secret [lsat.SecretSize]byte
|
|
if _, err := rand.Read(secret[:]); err != nil {
|
|
return secret, err
|
|
}
|
|
|
|
_, err := s.Put(ctx, idKey(id), string(secret[:]))
|
|
return secret, err
|
|
}
|
|
|
|
// GetSecret returns the cryptographically random secret that corresponds to the
|
|
// given hash. If there is no secret, then mint.ErrSecretNotFound is returned.
|
|
func (s *secretStore) GetSecret(ctx context.Context,
|
|
id [sha256.Size]byte) ([lsat.SecretSize]byte, error) {
|
|
|
|
resp, err := s.Get(ctx, idKey(id))
|
|
if err != nil {
|
|
return [lsat.SecretSize]byte{}, err
|
|
}
|
|
if len(resp.Kvs) == 0 {
|
|
return [lsat.SecretSize]byte{}, mint.ErrSecretNotFound
|
|
}
|
|
if len(resp.Kvs[0].Value) != lsat.SecretSize {
|
|
return [lsat.SecretSize]byte{}, fmt.Errorf("invalid secret "+
|
|
"size %v", len(resp.Kvs[0].Value))
|
|
}
|
|
|
|
var secret [lsat.SecretSize]byte
|
|
copy(secret[:], resp.Kvs[0].Value)
|
|
return secret, nil
|
|
}
|
|
|
|
// RevokeSecret removes the cryptographically random secret that corresponds to
|
|
// the given hash. This acts as a NOP if the secret does not exist.
|
|
func (s *secretStore) RevokeSecret(ctx context.Context,
|
|
id [sha256.Size]byte) error {
|
|
|
|
_, err := s.Delete(ctx, idKey(id))
|
|
return err
|
|
}
|