Documentation
¶
Overview ¶
Package keyring implements self-contained encrypted storage for a versioned collection of versioned byte strings, typically cryptographic keys or access tokens.
Outline ¶
To create a new keyring, call New. You must provide an access key and the first key version to initialize the ring:
r, err := keyring.New(keyring.Config{
AccessKey: accessKey,
InitialKey: []byte("hunter2"},
})
The access key must be exactly AccessKeyLen bytes. The keys stored in the keyring may be of any length, but must not be empty.
Each key version in the keyring is identified by a unique ID. At any time, one version is identified as the "active" version. Call Ring.Active to obtain the active ID.
active := r.Active()
To add a new key version, use Ring.Add or Ring.AddRandom:
id := r.Add([]byte("hunter2 hunter2"))
Adding a new key does not change the active version. Use Ring.Activate to change the current active version:
r.Activate(id)
To retrieve a key version, use Ring.Get:
var buf []byte buf = r.Get(id, buf)
This method will panic if the provided ID is unknown. Use Ring.Has to test whether a given version is present:
if r.Has(id) {
log.Printf("Key id %v is present", id)
}
Use Ring.GetActive get the id and content of the active version:
id, buf := r.GetActive(buf)
Storage ¶
To write a keyring to persistent storage, use Ring.WriteTo:
f, err := os.Create("key.ring")
// ...
nw, err := r.WriteTo(f)
To read a keyring from persistent storage, use Read:
f, err := os.Open("key.ring")
// ...
r, err := keyring.Read(f, accessKeyFunc)
The AccessKeyFunc returns the access key used to decrypt the stored ring.
The binary encoding format of a Ring is defined in the packet package.
Access Keys ¶
An access key is a 32-byte symmetric encryption key. It must be provided to create a new keyring, or to read a saved keyring. In order to allow access keys generated by a key-derivation function, a Ring may optionally store an access-key generation salt provided by the user.
For example, if accessKey is generated by combining a root key with salt and other context, you may provide that context when constructing the ring:
r, err := keyring.New(keyring.Config{
InitialKey: []byte("hunter2"),
AccessKey: accessKey, // derived from baseKey + keySalt
AccessKeySalt: keySalt,
})
The keyring will persist keySalt in storage, and will pass it to the AccessKeyFunc provided when reading it:
r, err := keyring.Read(f, func(keySalt []byte) []byte {
return someKDF(baseKey, keySalt, ...)
})
If you do not use a key-derivation function, use StaticKey. The PassphraseKey and AccessKeyFromPassphrase helpers may also be useful if you want to derive an access key from a user-provided low-entropy passphrase.
Read-only usage ¶
To use a keyring with an API that does not need the ability to modify the contents of the keyring, call Ring.View to obtain a read-only view. The View type allows reading all the existing key versions at the time it is created, but stores no other cryptographic material and cannot be modified or written to storage.
Once a View is created, further changes to the Ring from which it was derived do not affect the view. While a Ring is not safe for concurrent access by multiple goroutines without separate synchronization, a View can be shared among multiple goroutines safely.
Deletion ¶
A Ring intentionally does not support explicit deletion of keys. Once a key version has been used to encrypt data, deleting it would render those data unreadable. A newly-added and unused key could be safely removed, but the API omits this functionality to avoid accidental misuse. To remove a new and unused key, create a new keyring and copy over the key material.
Example ¶
package main
import (
"bytes"
"fmt"
"log"
"github.com/creachadair/keyring"
)
func main() {
key, salt := keyring.AccessKeyFromPassphrase("hunter2")
r, err := keyring.New(keyring.Config{
AccessKey: key,
AccessKeySalt: salt,
InitialKey: []byte("too many secrets"),
})
if err != nil {
log.Fatalf("New failed: %v", err)
}
// Print the currently-active key.
fmt.Printf("Key %d: %q\n", r.Active(), r.Get(r.Active(), nil))
// Add another key and print that too.
id := r.Add([]byte("no more secrets"))
fmt.Printf("Key %d: %q\n", id, r.Get(id, nil))
// Note that the active key ID doesn't change until we say so.
fmt.Printf("Active ID before: %d\n", r.Active())
r.Activate(id)
fmt.Printf("Active ID after: %d\n", r.Active())
var buf bytes.Buffer
nw, err := r.WriteTo(&buf)
if err != nil {
log.Fatalf("Write failed: %v", err)
}
fmt.Printf("Encoded keyring is %d bytes\n\n", nw)
// Read the keyring back in from "storage" (buf).
r2, err := keyring.Read(&buf, keyring.PassphraseKey("hunter2"))
if err != nil {
log.Fatalf("Read failed: %v", err)
}
fmt.Println("(reloaded)")
id, akey := r2.GetActive(nil)
fmt.Printf("Key %d: %q\n", id, akey)
}
Output: Key 1: "too many secrets" Key 2: "no more secrets" Active ID before: 1 Active ID after: 2 Encoded keyring is 199 bytes (reloaded) Key 2: "no more secrets"
Index ¶
- Constants
- func AccessKeyFromPassphrase(passphrase string) (key, salt []byte)
- func RandomKey(n int) []byte
- type AccessKeyFunc
- type Config
- type ID
- type Ring
- func (r *Ring) Activate(id ID)
- func (r *Ring) Active() ID
- func (r *Ring) Add(key []byte) ID
- func (r *Ring) AddRandom(n int) ID
- func (r *Ring) Get(id ID, buf []byte) []byte
- func (r *Ring) GetActive(buf []byte) (ID, []byte)
- func (r *Ring) Has(id ID) bool
- func (r *Ring) Len() int
- func (r *Ring) Rekey(accessKey, accessKeySalt []byte) error
- func (r *Ring) View() *View
- func (r *Ring) WriteTo(w io.Writer) (int64, error)
- type View
Examples ¶
Constants ¶
const AccessKeyLen = cipher.KeyLen // 32 bytes
AccessKeyLen is the length in bytes of an access key.
Variables ¶
This section is empty.
Functions ¶
func AccessKeyFromPassphrase ¶
AccessKeyFromPassphrase generates a key from the specified passphrase using argon2id and a random salt. It returns the key and the salt.
Types ¶
type AccessKeyFunc ¶
AccessKeyFunc is a function that generates an access key from a generation salt. The implementation is not required to use the salt. It must return a slice of exactly AccessKeyLen bytes.
func PassphraseKey ¶
func PassphraseKey(passphrase string) AccessKeyFunc
PassphraseKey returns an access key generation function generates an access key using argon2id on the provided passphrase and the stored salt.
func StaticKey ¶
func StaticKey(key []byte) AccessKeyFunc
StaticKey returns an access key generation function that ignores the key generation salt and returns the provided key.
type Config ¶
type Config struct {
// The initial active key for the ring. This field must be non-empty.
InitialKey []byte
// The secret key to decrypt the data encryption key.
// This must be exactly [AccessKeyLen] bytes.
AccessKey []byte
// An optional key-generation salt for the access key. If provided, this
// value will be passed to the accessKey callback of [Read] when reading the
// keyring from storage. This may be empty or nil.
AccessKeySalt []byte
}
Config carries the settings for a Ring.
type ID ¶
type ID = int
An ID identifies a particular version of a key stored in a Ring. A valid ID value is positive. ID is defined is an alias for legibility, rather than a type, so that callers can use the methods of a Ring or View without a direct dependency on this package.
type Ring ¶
type Ring struct {
// contains filtered or unexported fields
}
A Ring is a versioned collection of byte strings, typically cryptographic keys or access tokens. The contents of a ring can be serialized in binary format for persistent storage, and are symmetrically encrypted under a randomly-generated data storage key.
In storage, the data storage key is itself symmetrically encrypted with a user-provided access key and stored alongside the encrypted keyring contents. When a keyring is read, the access key is used to recover the data storage key, which can then be used to decrypt and re-encrypt the contents of the keyring without further need of the access key.
func New ¶
New constructs a new Ring from c. At minimum, a non-empty initial key and an access key must be provided. It reports an error if any required options are unset or invalid, or if a data encryption key could not be generated.
func Read ¶
func Read(r io.Reader, accessKey AccessKeyFunc) (*Ring, error)
Read parses, and decrypts the binary representation of a Ring from r. It fully consumes the contents of r.
The accessKey function is called to obtain the encryption key for the ring itself. If the ring has a key generation salt, it is passed to the accessKey function; otherwise the salt argument is nil.
func (*Ring) Activate ¶
Activate activates the specified key ID in r. It has no effect if the given key ID is already active. It panics if id does not exist in r.
func (*Ring) Add ¶
Add adds the specified non-empty key to r and returns its new ID. If r is empty, the The added key is not marked active; use Ring.Activate to make it active. It panics if len(key) == 0.
func (*Ring) AddRandom ¶
AddRandom adds a new randomly-generated n-byte key to r, and returns its ID. It is shorthand for calling Ring.Add with a randomly-generated key. It will panic if n ≤ 0.
func (*Ring) Get ¶ added in v0.1.0
Get appends the contents of the specified key to buf, and returns the resulting slice. It panics if id does not exist in r.
func (*Ring) GetActive ¶ added in v0.1.0
GetActive appends the contents of the active key to buf, and returns active ID and the updated slice.
func (*Ring) Rekey ¶
Rekey generates a new data storage key for r, and changes the access key to the provided value. If an error occurs, the current state of r is unchanged. The accessKey must be exactly AccessKeyLen bytes; the salt may be empty or nil.
type View ¶
type View struct {
// contains filtered or unexported fields
}
A View is a read-only view of a Ring. A View contains no cryptographic material, Keys cannot be added to it, the active key ID cannot be changed, and it cannot be written to storage.
func SingleKeyView ¶
SingleKeyView constructs a View that exports the single provided key as its only version with ID 1. It will panic if singleKey is empty.
func (*View) Get ¶ added in v0.1.0
Get appends the contents of the specified key to buf, and returns the resulting slice. It panics if id does not exist in r.
func (*View) GetActive ¶ added in v0.1.0
GetActive appends the contents of the active key to buf, and returns active ID and the updated slice.
Directories
¶
| Path | Synopsis |
|---|---|
|
cmd
|
|
|
keyring
command
Program keyring is a command-line tool to manipulate keyring files.
|
Program keyring is a command-line tool to manipulate keyring files. |
|
internal
|
|
|
cipher
Package cipher implements symmetric encryption helpers for keyrings.
|
Package cipher implements symmetric encryption helpers for keyrings. |
|
packet
Packet packet defines the binary storage representation of a keyring as defined by the parent package.
|
Packet packet defines the binary storage representation of a keyring as defined by the parent package. |