package truststores import ( "errors" "fmt" "github.com/zalando/go-keyring" "github.com/safing/jess" ) const ( keyringServiceNamePrefix = "jess:" keyringSelfcheckKey = "_selfcheck" keyringSelfcheckValue = "!selfcheck" ) // KeyringTrustStore is a trust store that uses the system keyring. // It does not support listing entries, so it cannot be easily managed. type KeyringTrustStore struct { serviceName string } // NewKeyringTrustStore returns a new keyring trust store with the given service name. // The effect of the service name depends on the operating system. // Read more at https://pkg.go.dev/github.com/zalando/go-keyring func NewKeyringTrustStore(serviceName string) (*KeyringTrustStore, error) { krts := &KeyringTrustStore{ serviceName: keyringServiceNamePrefix + serviceName, } // Run a self-check. err := keyring.Set(krts.serviceName, keyringSelfcheckKey, keyringSelfcheckValue) if err != nil { return nil, err } selfcheckReturn, err := keyring.Get(krts.serviceName, keyringSelfcheckKey) if err != nil { return nil, err } if selfcheckReturn != keyringSelfcheckValue { return nil, errors.New("keyring is faulty") } return krts, nil } // GetSignet returns the Signet with the given ID. func (krts *KeyringTrustStore) GetSignet(id string, recipient bool) (*jess.Signet, error) { // Build ID. if recipient { id += recipientSuffix } else { id += signetSuffix } // Get data from keyring. data, err := keyring.Get(krts.serviceName, id) if err != nil { return nil, fmt.Errorf("%w: %w", jess.ErrSignetNotFound, err) } // Parse and return. return jess.SignetFromBase58(data) } // StoreSignet stores a Signet. func (krts *KeyringTrustStore) StoreSignet(signet *jess.Signet) error { // Build ID. var id string if signet.Public { id = signet.ID + recipientSuffix } else { id = signet.ID + signetSuffix } // Serialize. data, err := signet.ToBase58() if err != nil { return err } // Save to keyring. return keyring.Set(krts.serviceName, id, data) } // DeleteSignet deletes the Signet or Recipient with the given ID. func (krts *KeyringTrustStore) DeleteSignet(id string, recipient bool) error { // Build ID. if recipient { id += recipientSuffix } else { id += signetSuffix } // Delete from keyring. return keyring.Delete(krts.serviceName, id) } // SelectSignets returns a selection of the signets in the trust store. Results are filtered by tool/algorithm and whether it you're looking for a signet (private key) or a recipient (public key). func (krts *KeyringTrustStore) SelectSignets(filter uint8, schemes ...string) ([]*jess.Signet, error) { return nil, ErrNotSupportedByTrustStore } // GetEnvelope returns the Envelope with the given name. func (krts *KeyringTrustStore) GetEnvelope(name string) (*jess.Envelope, error) { // Build ID. name += envelopeSuffix // Get data from keyring. data, err := keyring.Get(krts.serviceName, name) if err != nil { return nil, fmt.Errorf("%w: %w", jess.ErrEnvelopeNotFound, err) } // Parse and return. return jess.EnvelopeFromBase58(data) } // StoreEnvelope stores an Envelope. func (krts *KeyringTrustStore) StoreEnvelope(envelope *jess.Envelope) error { // Build ID. name := envelope.Name + envelopeSuffix // Serialize. data, err := envelope.ToBase58() if err != nil { return err } // Save to keyring. return keyring.Set(krts.serviceName, name, data) } // DeleteEnvelope deletes the Envelope with the given name. func (krts *KeyringTrustStore) DeleteEnvelope(name string) error { // Build ID. name += envelopeSuffix // Delete from keyring. return keyring.Delete(krts.serviceName, name) } // AllEnvelopes returns all envelopes. func (krts *KeyringTrustStore) AllEnvelopes() ([]*jess.Envelope, error) { return nil, ErrNotSupportedByTrustStore }