keyring

package module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 10, 2025 License: MIT Imports: 18 Imported by: 0

README

keyring

A lightweight and secure keyring Golang library for managing secrets, passwords, and tokens across platforms.

Features

  • It uses Shamir's secret sharing for keyring initialization.
  • Also supports an extensible auto-unlock feature. Easily pluggable to AWS CloudHSM or Azure Dedicated HSM.
  • Supports multiple encryption engines (currently AES-GCM is implemented).
  • Supports multiple encryption keys. New data is stored with the newest key.
  • Supports root key rotation.
  • Supports a pluggable storage engine used to store keyring data.

Usage

Create a keyring object using the New function.

Then use Initialize method to initialize the keyring. It will create the initial root and encryption keys and return the necessary information required to unlock the keyring in the future.

NOTES:
  • It is safe to call Initialize in an already initialized keyring, the proper error (ErrAlreadyInitialized) will be returned.
  • On a successful call to Initialize, the keyring is left unlocked. See the locking section below for details.

At this point you can use Encrypt and Decrypt to encrypt and decrypt data.

Locking

When an already initialized keyring object is created, the keyring remains in a locked state.

To unlock the keyring, use the Unlock method and pass the proper parameter options depending if manual or automatic unlock mechanism is being used.

Once the keyring is unlocked, you can, again, use Encrypt and Decrypt to encrypt and decrypt data.

Manual lock

In manual lock/unlock mode, the Initialize method returns a set of keys that SHOULD be distributed among several parties. Every call to Unlock needs a key until the configured threshold is reached and the keyring will be unlocked.

Automatic lock

In automatic lock/unlock mode, an encryption/decryption callback function is passed as the parameter. The engine will call them to encrypt or decrypt the root key.

This approach is mainly intended to be used with third-party Hardware Security Modules (HSM) like AWS CloudHSM and Azure KeyVault, but you can also use your custom solution.

Because this method does not involves different parties having just one piece of the whole key, BE VERY CAREFUL about how has access and how the chosen engine works to avoid unauthorized access.

Bugs and enhancements

Don't hesitate to open a GitHub issue if you find any bug or want to share any improvement.

NOTES:
  • Access to HSM engines in the cloud and databases are not and won't be part of this library.

  • Multiple instances of an application using the same storage for the keyring should work without problems. The Status method can be called from time to time to detect if the keyring object should be recreated on a given instance due to a change. Because existing encryption keys are never deleted, an instance can still work if it is "outdated."

  • If several instances are running but, each one, with their own storage and they are synchronized through, for example, some consensus mechanism like Raft. In this case, the recommended approach would to capture all the changes between the BeginStorageTransactionFunc call and its commit, and group them as a single operation.

    Even a root key rotation involving rewriting hundreds of stored encryption keys, the whole operation should take a few kilobytes of data.

LICENSE

Copyright © 2025 Mauro H. Leggieri

MIT

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrAlreadyInitialized should be considered as non-fatal. It is returned in the call to `Initialize`
	// when the keyring is already initialized.
	ErrAlreadyInitialized = errors.New("already initialized")

	// ErrMoreKeysRequired is returned by `Unlock` when the keyring is locked and the user needs to provide
	// more keys to unlock it.
	ErrMoreKeysRequired = errors.New("more keys required")

	// ErrAlreadyUnlocked is returned by `Unlock` when the keyring is already unlocked.
	ErrAlreadyUnlocked = errors.New("already unlocked")

	ErrNotInitialized        = errors.New("not initialized")
	ErrLocked                = errors.New("locked")
	ErrEncryptionKeyNotFound = errors.New("encryption key not found")
	ErrInvalidStoredData     = errors.New("invalid stored data")
	ErrNotFound              = errors.New("not found")
	ErrUnlockFailed          = errors.New("unlock failed")

	ErrKeyringDataHasChanged = errors.New("keyring data has changed")
)

Functions

func IsKeyringKey added in v0.2.0

func IsKeyringKey(key string) bool

IsKeyringKey returns true if the given key is a keyring key.

Types

type AutoLockOptions added in v0.2.0

type AutoLockOptions struct {
	// Encrypt function to call when the keyring manager needs to encrypt the root key using the
	// external secure encryption engine like AWS CloudHSM or Azure Dedicated HSM.
	Encrypt func(ctx context.Context, plaintext []byte) ([]byte, error)
}

AutoLockOptions defines the keyring auto-lock feature options.

type AutoUnlockOptions added in v0.2.0

type AutoUnlockOptions struct {
	// Function to call when the keyring manager needs to decrypt the root key.
	Decrypt func(ctx context.Context, ciphertext []byte) ([]byte, error)
}

AutoUnlockOptions establishes the options to use when the auto-locking feature is used.

type BeginStorageTransactionFunc

type BeginStorageTransactionFunc func(ctx context.Context, readOnly bool) (StorageTx, error)

BeginStorageTransactionFunc defines a function that creates a transaction in the underlying storage.

type InitializeOptions added in v0.2.0

type InitializeOptions struct {
	// Encryption engine to use for the initial encryption key.
	Engine string

	// Encryption engine to use for the root key. If not defined, the same engine for encryption keys will be used.
	RootKeyEngine string

	ManualLock *ManualLockOptions
	AutoLock   *AutoLockOptions
}

InitializeOptions is a set of options to use to initialize the keyring.

type InitializeResult added in v0.2.0

type InitializeResult struct {
	ManualLock ManualLockResult
}

InitializeResult is returned as a result of the keyring initialization process.

type Keyring

type Keyring struct {
	// contains filtered or unexported fields
}

Keyring implements a secure store and management of encryption keys.

func New

func New(opts Options) (*Keyring, error)

New creates a new keyring manager.

func (*Keyring) AddEncryptionKey

func (kr *Keyring) AddEncryptionKey(ctx context.Context, engine string) error

AddEncryptionKey adds a new encryption key to the keyring. Later encryption will use this new key.

func (*Keyring) CancelUnlock

func (kr *Keyring) CancelUnlock()

CancelUnlock cancels an active keyring unlock process.

func (*Keyring) Decrypt

func (kr *Keyring) Decrypt(ciphertext []byte) ([]byte, error)

Decrypt decrypts the given cipher text with the available encryption keys.

func (*Keyring) Destroy

func (kr *Keyring) Destroy()

Destroy destroys (not physically a keyring). All memory is zeroed.

func (*Keyring) Encrypt

func (kr *Keyring) Encrypt(plaintext []byte) ([]byte, error)

Encrypt encrypts the given plain text with the current active encryption key.

func (*Keyring) Initialize

func (kr *Keyring) Initialize(ctx context.Context, opts InitializeOptions) (InitializeResult, error)

Initialize initializes an uninitialized keyring. NOTE: If initialization succeeds, the keyring remains unlocked.

func (*Keyring) IsLocked

func (kr *Keyring) IsLocked() bool

IsLocked returns if the keyring is locked or not.

func (*Keyring) Lock

func (kr *Keyring) Lock()

Lock locks access until unlocked again. NOTE: If you lock an auto-unlock keyring, you will need to create a new keyring object based on the same

storage and auto-unlock interface to unlock it.

func (*Keyring) RotateRootKey

func (kr *Keyring) RotateRootKey(ctx context.Context, opts RotateRootKeyOptions) (RotateRootKeyResult, error)

RotateRootKey changes the root key. It also allows to change from manual to auto-locking and vice versa.

func (*Keyring) Status added in v0.2.0

func (kr *Keyring) Status(ctx context.Context) error

Status returns the current status of the keyring. It can be used to check if the keyring is initialized, unlocked or if another instance using the same database changed any keyring configuration.

ErrLocked is returned if the keyring is locked.

ErrKeyringDataHasChanged is returned if an encryption key was added or the root key or a parameter was changed.

func (*Keyring) Unlock

func (kr *Keyring) Unlock(ctx context.Context, opts UnlockOptions) error

Unlock tries to unlock the root key.

type ManualLockOptions added in v0.2.0

type ManualLockOptions struct {
	// Threshold defines the minimum number of keys required to unlock the keyring.
	Threshold int

	// Shares is the number of splits the root key will have.
	Shares int
}

ManualLockOptions defines the keyring manual-lock feature options.

type ManualLockResult added in v0.2.0

type ManualLockResult struct {
	// SplitRootKey will hold the split shamir root key.
	SplitRootKey [][]byte
}

ManualLockResult contains the result of a manual-lock keyring.

type ManualUnlockOptions added in v0.2.0

type ManualUnlockOptions struct {
	// One of the split keys to unlock the keyring.
	Key []byte
}

ManualUnlockOptions establishes the options to use when manual-locking is used.

type Options

type Options struct {
	// A transactional-enabled storage that holds keyring data.
	BeginStorageTX BeginStorageTransactionFunc

	// An optional random number generator reader. If nil, the keyring will use crypto/rand.Reader.
	RandomGeneratorReader io.Reader
}

Options configure the keyring base options.

type RotateRootKeyOptions added in v0.2.0

type RotateRootKeyOptions struct {
	// Encryption engine to use for the root key.
	Engine string

	ManualLock *ManualLockOptions
	AutoLock   *AutoLockOptions
}

RotateRootKeyOptions is a set of options to use to rotate the root key of the keyring.

type RotateRootKeyResult added in v0.2.0

type RotateRootKeyResult struct {
	ManualLock ManualLockResult
}

RotateRootKeyResult is returned as a result of the root key rotation process.

type StorageTx

type StorageTx interface {
	// Get retrieves the value of the given key. Returns nil and no error if the key is not found.
	// Also, the implementation must return a copy of the value if the underlying implementation
	// overwrites its contents.
	Get(ctx context.Context, key string) ([]byte, error)

	// Put saves the given value under the provided key. The implementation MUST make a copy of the
	// value parameter if it needs to keep it until the commit call.
	Put(ctx context.Context, key string, value []byte) error

	// Delete removes the given key from the database. Don't return an error if the key is not found.
	Delete(ctx context.Context, key string) error

	// Commit saves all changes into the storage.
	Commit(ctx context.Context) error

	// Rollback discards pending changes.
	Rollback(ctx context.Context)
}

StorageTx is an interface that represents a storage transaction.

type UnlockOptions added in v0.2.0

type UnlockOptions struct {
	ManualUnlock *ManualUnlockOptions
	AutoUnlock   *AutoUnlockOptions
}

UnlockOptions is a set of options used to unlock the keyring.

Directories

Path Synopsis
crypto

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL