Note
This is a maintained fork of https://github.com/99designs/keyring which seems to be an abandoned project. Contributions are welcome, but keep in mind this is a side project and maintained on best effort basis!
Keyring provides a common interface to a range of secure credential storage services. Originally developed as part of AWS Vault, a command line tool for securely managing AWS access from developer workstations.
Currently Keyring supports the following backends
- macOS Keychain (with TouchID support 🎉)
- Windows Credential Manager
- Windows Hello-gated encrypted Credential Manager backend
- Secret Service (Gnome Keyring, oo7-daemon, KWallet)
- KWallet
- Pass
- Passage
- Encrypted file (JWT)
- KeyCtl
- 1Password Connect
- 1Password Service Accounts
- 1Password Desktop Application Integration
- Proton Pass (Note: experimental)
- oo7 keyring files, encrypted with a passphrase or, in a sandbox, the Secret portal's per-application secret (Linux)
The short version of how to use keyring is shown below.
ring, _ := keyring.Open(keyring.Config{
ServiceName: "example",
})
_ = ring.Set(keyring.Item{
Key: "foo",
Data: []byte("secret-bar"),
})
i, _ := ring.Get("foo")
fmt.Printf("%s", i.Data)To configure TouchId biometrics:
keyring.Config.UseBiometrics = true
keyring.Config.TouchIDAccount = "cc.byteness.aws-vault.biometrics"
keyring.Config.TouchIDService = "aws-vault"KeychainName must also be set — biometrics only apply to a custom keychain, not
the default login keychain. Touch ID is requested only when the OS reports that
keychain as locked; an already-unlocked keychain is not re-prompted.
The Passage backend can select an identities file per keyring instance. If
PassageIdentitiesFile is empty, Passage uses PASSAGE_IDENTITIES_FILE or its
own default.
ring, err := keyring.Open(keyring.Config{
AllowedBackends: []keyring.BackendType{keyring.PassageBackend},
PassDir: "~/.passage/store",
PassPrefix: "example",
PassageIdentitiesFile: "~/.passage/identities",
})
if err != nil {
return err
}The oo7 backend reads and writes oo7 keyring files
(the format behind oo7-daemon, the Secret Service implementation GNOME is moving to) without
going through D-Bus, so no daemon has to be running or able to prompt, and it works over SSH or
inside a sandbox. Items are kept in their own file, $XDG_DATA_HOME/<ServiceName>/keyrings/default.keyring
by default, which oo7-cli -k <file> -s <secret> can also open. The default deliberately stays out of
$XDG_DATA_HOME/keyrings/v1, which oo7-daemon scans on startup and would otherwise expose as one of
its collections. Build with -tags keyring_nooo7 to leave the backend unregistered; the file format
code stays, as it is portable and unit tested on every platform.
ring, err := keyring.Open(keyring.Config{
AllowedBackends: []keyring.BackendType{keyring.OO7Backend},
ServiceName: "example",
OO7KeyringPath: "~/.local/share/example/keyrings/default.keyring", // optional
OO7KeySource: keyring.OO7KeySourceAuto, // auto, portal or passphrase
OO7PasswordFunc: keyring.TerminalPrompt,
})auto uses the Secret portal
inside a Flatpak or Snap sandbox, as oo7's own client does, and the passphrase prompt otherwise.
A keyring created with one key source cannot be opened with the other. The portal hands out one
secret per application, so inside a sandbox everything running in that application can open the keyring.
ServiceName names a folder in the default path and must not contain / or be ...
The winhello backend stores encrypted envelopes in Windows Credential Manager.
This may sound similar to the wincred backend, but the difference is encryption.
Here, we don't store plaintext item data in Credential Manager. It is encrypted
with AES-256-GCM, and the content encryption key is wrapped by a Windows Hello /
Passport KSP key and unwrapped through an interactive private-key operation.
Upon the first use, a new Passport KSP key is created and stored in the user's protected key store. This operation requires user interaction and Windows Hello authentication. Later, whenever an item is accessed, the content encryption key is unwrapped by the Passport KSP key, which requires Windows Hello authentication again. This means that every access to the stored secrets requires user presence and authentication through Windows Hello (using PIN, fingerprint, face ID, etc.).
This protects against silent reads of the stored Credential Manager blob. It does not protect against malware that can read process memory after a successful unlock, inject into an approved process, or steal credentials after they are handed to a caller.
To use the Windows Hello backend on Windows:
ring, err := keyring.Open(keyring.Config{
ServiceName: "example",
AllowedBackends: []keyring.BackendType{
keyring.WinHelloBackend,
},
})
if err != nil {
return err
}For more detail on the API please check the keyring godocs
Experimental. The
proton-passbackend targets Proton's Pass API, which is unofficial and undocumented: it can change without notice and break this backend. Treat it as beta, pin your dependency, and re-test after upgrades. It is gated behind thekeyring_noprotonpassbuild tag (see below) and excluded from opt-out builds.
The proton-pass backend stores each secret as an item in a Proton Pass vault,
authenticated with a Proton Personal Access Token (PAT) of the form pst_<token>::<key>,
so no browser and no pass-cli subprocess are involved. Item content is decrypted
client-side (AES-256-GCM down Proton's key hierarchy). Because the API is reverse
engineered, the client sends app/SDK version headers mirroring the Proton Pass CLI (for
example [email protected]); Proton may change what it accepts, so those pins can need
updating over time.
ring, err := keyring.Open(keyring.Config{
ServiceName: "example",
AllowedBackends: []keyring.BackendType{
keyring.ProtonPassBackend,
},
ProtonPassShareID: "<share-id>",
})
if err != nil {
return err
}The PAT is read from the PROTON_PASS_PERSONAL_ACCESS_TOKEN environment variable (or a
configured token function), never a command-line flag.
The cross-platform backends compile into every build, whether or not you use them, along with their dependency trees. If you know at build time that you don't need a backend, an opt-out build tag excludes it (and its dependencies) from compilation:
| Build tag | Backends removed | Headline dependencies dropped |
|---|---|---|
keyring_no1password |
op, op-connect, op-desktop |
onepassword-sdk-go (incl. the wazero WebAssembly runtime), connect-sdk-go (incl. jaeger-client-go) |
keyring_nofile |
file |
dvsekhvalnov/jose2go |
keyring_nopass |
pass |
none (shells out to pass) |
keyring_nopassage |
passage |
none (shells out to passage) |
keyring_nooo7 |
oo7 |
none (standard library crypto; godbus is shared with secret-service) |
go build -tags keyring_no1password ./...The platform-specific backends (keychain, wincred, winhello,
secret-service, kwallet, keyctl) are already gated by GOOS constraints
and need no tags; oo7 is Linux-only as well but takes a tag because it is
an opt-in backend. Default builds (no tags) are unaffected. An excluded backend
is simply absent from AvailableBackends(), and requesting it explicitly
returns ErrNoAvailImpl — the same behavior as a backend that's unavailable
on the current platform. The BackendType constants and Config fields are
always present, so there is no API change under any tag.
Vagrant is used to create linux and windows test environments.
# Start vagrant
vagrant up
# Run go tests on all platforms
./bin/go-testReport issues/questions/feature requests on in the issues section.
Full contributing guidelines are covered here.