Documentation
¶
Overview ¶
Package pkcs11 provides the same repository-style cryptographic API as the local package, backed by a PKCS#11 token (a hardware or network HSM) when a RFC 7512 "pkcs11:" key URI is supplied.
The package supports provider-backed symmetric encryption, HMAC, RSA-OAEP, RSA signing, Ed25519 signing, and ECDH key agreement through the vendor PKCS#11 library, while still routing explicit local key material to the local implementation. Operations whose mechanism the token does not report in C_GetMechanismList fail with a descriptive error instead of silently falling back to software, so a caller can never believe an operation was hardware-backed when it was not.
Unlike the cloud backends, this package requires cgo and is only compiled when the "pkcs11" build tag is set:
go build -tags pkcs11 ./...
Without that tag a stub implementation is compiled instead and every constructor returns ErrUnavailable, which keeps CGO_ENABLED=0 builds and cross-compilation working for consumers that do not need HSM support.
Two token-side requirements are worth knowing before deploying. HMAC needs a CKK_GENERIC_SECRET key carrying CKA_SIGN, which GenerateSymetrycKeys does not produce -- it creates a CKK_AES key for EncryptAES -- so HMAC keys are provisioned separately, as they are with the cloud backends. And RSA-OAEP is pinned to SHA-256 with MGF1-SHA256 to stay readable by the other backends; a token that only implements OAEP with SHA-1, as SoftHSM2 does, reports ErrOAEPHashUnsupported rather than silently weakening the parameters.
Configuration is read from viper using "encrypt.vault.pkcs11.module", "encrypt.vault.pkcs11.token-label", "encrypt.vault.pkcs11.slot" and "encrypt.vault.pkcs11.max-sessions", and every value can be overridden through functional options. The token PIN is never read from configuration: it is supplied by the caller through a PinProvider.
Index ¶
- Variables
- func Shutdown(ctx context.Context) error
- type AsymmetricRepository
- type HashRepository
- type KeyRepository
- type Option
- func WithAllowSecretExtraction(allow bool) Option
- func WithDeactivateDestroys(destroy bool) Option
- func WithKeyURI(uri string) Option
- func WithMaxSessions(n int) Option
- func WithModulePath(path string) Option
- func WithPinProvider(provider PinProvider) Option
- func WithRotateDisablesPrevious(disable bool) Option
- func WithSlotID(id uint64) Option
- func WithTokenLabel(label string) Option
- type PinProvider
- type Repository
- type SignatureRepository
- type SymmetricRepository
Constants ¶
This section is empty.
Variables ¶
var ( // built without the "pkcs11" build tag or without cgo. ErrUnavailable = errors.New("pkcs11: support is not compiled in; rebuild with -tags pkcs11 and CGO_ENABLED=1") // ErrModuleRequired is returned when no PKCS#11 library path was supplied // through options or configuration. ErrModuleRequired = errors.New("pkcs11: module path is required") // ErrTokenRequired is returned when neither a token label nor a slot id // was supplied through options or configuration. ErrTokenRequired = errors.New("pkcs11: token label or slot id is required") // ErrPinRequired is returned when no PinProvider was supplied. The PIN is // never read from configuration. ErrPinRequired = errors.New("pkcs11: pin provider is required") // ErrKeyURIRequired is returned when an operation needs a key reference and // neither the request nor the configuration supplied one. ErrKeyURIRequired = errors.New("pkcs11: key uri is required") // ErrKeyNotFound is returned when no object on the token matches the // supplied key URI. ErrKeyNotFound = errors.New("pkcs11: no object matches the key uri") // ErrOAEPHashUnsupported is returned when the token rejects the SHA-256 // OAEP parameters this package pins. // // The parameters are not negotiable: every other backend encrypts RSA-OAEP // with SHA-256 and MGF1-SHA256, so a token-side downgrade to SHA-1 would // produce ciphertext the local backend cannot read, besides being weaker. // SoftHSM2 is the common case, as it hardcodes OAEP to SHA-1. ErrOAEPHashUnsupported = errors.New("pkcs11: token does not accept RSA-OAEP with SHA-256; the parameters are fixed for cross-backend compatibility") // ErrSecretNotExtractable is returned when the token policy forbids reading // the derived shared secret, which makes the interoperable ECDH payload // format impossible to produce. See the ECDH_Decode documentation. ErrSecretNotExtractable = errors.New("pkcs11: token policy forbids extracting the derived secret; ECDH is unavailable on this token") )
Functions ¶
func Shutdown ¶
Shutdown finalizes every PKCS#11 library this process loaded and clears the registry.
It is separate from Close because C_Finalize tears down state shared by every user of the library in the process. Call it once, on the way out, when no repository is in use. Most services never need it: leaving a library initialized until the process exits is harmless.
Types ¶
type AsymmetricRepository ¶
type AsymmetricRepository interface {
// GenerateRSAKeys creates an RSA key pair on the token, with a
// non-extractable private half, and returns its public key plus reference.
GenerateRSAKeys(ctx context.Context, input models.GenerateRSAKeyRequest) (*models.KeyData, error)
// GenerateECDHCurveKeys creates an ECC key pair on the token for the
// requested curve and returns its public key plus reference.
GenerateECDHCurveKeys(ctx context.Context, input models.GenerateECDHCurveKeyRequest) (*models.KeyData, error)
// RSA_OAEP_Encode encrypts plaintext with a token key reference or a Base64
// RSA public key. Encryption needs no secret, so the token public key is
// fetched and the operation completed locally.
RSA_OAEP_Encode(ctx context.Context, input models.RSAOAEPEncodeRequest) (string, error)
// RSA_OAEP_Decode decrypts ciphertext produced by RSA_OAEP_Encode using a
// token key reference or a Base64 RSA private key.
RSA_OAEP_Decode(ctx context.Context, input models.RSAOAEPDecodeRequest) (string, error)
// ECDH_Encode encrypts plaintext with a token key reference or a local
// Base64 ECC public key.
ECDH_Encode(ctx context.Context, input models.ECDHEncodeRequest) (string, error)
// ECDH_Decode decrypts ciphertext produced by ECDH_Encode using a token key
// reference or a local Base64 ECC private key.
ECDH_Decode(ctx context.Context, input models.ECDHDecodeRequest) (string, error)
}
func NewAsymmetricRepository ¶
func NewAsymmetricRepository(opts ...Option) AsymmetricRepository
type HashRepository ¶
type HashRepository interface {
// HMAC generates an HMAC-SHA256 value on the token when secretKey is a
// "pkcs11:" URI, or locally otherwise.
HMAC(ctx context.Context, secretKey, message string) string
// Sha256Hex returns the SHA-256 digest encoded as hexadecimal.
Sha256Hex(ctx context.Context, message string) string
// Blake3 returns the BLAKE3 digest encoded as Base64.
Blake3(ctx context.Context, message string) string
}
func NewHashRepository ¶
func NewHashRepository(opts ...Option) HashRepository
type KeyRepository ¶
type KeyRepository interface {
// RotateKey creates a new provider-backed key version or key material by key id.
RotateKey(ctx context.Context, input models.RotateKeyRequest) (*models.KeyData, error)
// GetKey returns the provider metadata and public material available for key id.
GetKey(ctx context.Context, input models.GetKeyRequest) (*models.KeyData, error)
// DeactivateKey disables a provider-backed key or key version by key id.
DeactivateKey(ctx context.Context, input models.DeactivateKeyRequest) error
}
func NewKeyRepository ¶
func NewKeyRepository(opts ...Option) KeyRepository
type Option ¶
type Option func(*config)
Option customises the backend. Every value also has a viper key, so NewRepository() with no options behaves like the other backends.
func WithAllowSecretExtraction ¶
WithAllowSecretExtraction controls whether ECDH_Decode may fall back to reading the derived shared secret out of the token when the token does not implement CKM_HKDF_DERIVE.
It defaults to true. What leaves the token in that path is an ephemeral per-message secret, never long-term key material, and it is exactly what the aws-kms and azure-key-vault backends already do. Set it to false on a token whose policy must guarantee that nothing derived ever leaves the hardware, accepting that ECDH_Decode then fails on tokens without CKM_HKDF_DERIVE.
func WithDeactivateDestroys ¶
WithDeactivateDestroys makes DeactivateKey destroy the object when the token refuses to clear its usage attributes.
It defaults to false: failing is safer than destroying a key the caller only asked to disable.
func WithKeyURI ¶
WithKeyURI sets the default RFC 7512 key URI used when a request does not carry one, mirroring encrypt.vault.aws-kms.arn in the AWS backend.
func WithMaxSessions ¶
WithMaxSessions bounds concurrent token sessions. Values below one are ignored.
func WithModulePath ¶
WithModulePath sets the path to the vendor PKCS#11 shared library.
func WithPinProvider ¶
func WithPinProvider(provider PinProvider) Option
WithPinProvider supplies the token PIN.
func WithRotateDisablesPrevious ¶
WithRotateDisablesPrevious makes RotateKey disable the previous key after generating its replacement. It defaults to false so that rotation stays non-destructive and old ciphertext remains decryptable.
func WithSlotID ¶
WithSlotID selects the token by slot id. Prefer WithTokenLabel: slot ids are reassigned when tokens are added or removed.
func WithTokenLabel ¶
WithTokenLabel selects the token by its label, which is stable across reboots unlike the slot id.
type PinProvider ¶
PinProvider returns the token PIN. It is called at most once per token, when the first session logs in, and the returned string is wiped from C memory as soon as C_Login returns.
Supplying it as a function rather than a configuration value lets the PIN come from wherever the deployment keeps it — a file with restricted permissions, a secrets manager, Dragon CMK — without this package taking a dependency on any of them.
type Repository ¶
type Repository struct {
SymmetricRepository
AsymmetricRepository
KeyRepository
SignatureRepository
HashRepository
// contains filtered or unexported fields
}
func NewRepository ¶
func NewRepository(opts ...Option) *Repository
NewRepository returns the composite token-backed repository. Called with no options it reads everything from viper, like the other backends; the PIN, which has no configuration key, must always come from WithPinProvider.
func (*Repository) Close ¶
func (repository *Repository) Close() error
Close releases this repository's share of the PKCS#11 library and closes its idle sessions.
It is deliberately not part of any of the five interfaces: adding it there would force a no-op Close on the local and cloud backends, and encrypt.NewRepository erases the concrete type anyway, so a caller that needs it keeps its own *Repository. Close does not call C_Finalize, which tears down state shared by every user of the library in the process; Shutdown does that explicitly.
type SignatureRepository ¶
type SignatureRepository interface {
// GenerateEd255Keys creates an Ed25519 signing key on the token.
GenerateEd255Keys(ctx context.Context) (*models.KeyData, error)
// SignEd25519 signs text with a token key reference or a Base64 Ed25519
// private key.
SignEd25519(ctx context.Context, privateKey, text string) (string, error)
// VerifyEd25519 verifies a Base64 Ed25519 signature with a token key
// reference or a Base64 Ed25519 public key.
VerifyEd25519(ctx context.Context, publicKey, text, signature string) error
// SignRSAPSS signs text with a token RSA key reference or a Base64 RSA
// private key.
SignRSAPSS(ctx context.Context, privateKey, text string) (string, error)
// VerifyRSAPSS verifies a Base64 RSA-PSS signature with a token key
// reference or a Base64 RSA public key.
VerifyRSAPSS(ctx context.Context, publicKey, text, signature string) error
// Sign_RSA_PKCS1v15_SHA256 signs data with RSA PKCS#1 v1.5 using the token
// when privateKey is a "pkcs11:" URI, or a local Base64 RSA private key.
Sign_RSA_PKCS1v15_SHA256(ctx context.Context, privateKey, data string) (string, error)
// Verify_RSA_PKCS1v15_SHA256 verifies an RSA PKCS#1 v1.5 SHA-256 signature
// with the token key's public half, or a local Base64 RSA public key.
Verify_RSA_PKCS1v15_SHA256(ctx context.Context, data, publicKey string, signature string) error
}
func NewSignatureRepository ¶
func NewSignatureRepository(opts ...Option) SignatureRepository
type SymmetricRepository ¶
type SymmetricRepository interface {
// GenerateSymetrycKeys creates a non-extractable AES key on the token and
// returns its RFC 7512 reference.
GenerateSymetrycKeys(ctx context.Context, input models.GenerateSymmetricKeyRequest) (*models.KeyData, error)
// EncryptAES encrypts plaintext with a token AES key referenced by a
// "pkcs11:" URI, or falls back to local AES-GCM when secretKey is a Base64
// AES key. Both paths produce the same wire format.
EncryptAES(ctx context.Context, input models.EncryptAESRequest) (string, error)
// DecryptAES decrypts ciphertext produced by EncryptAES using the token or
// a local Base64 AES key.
DecryptAES(ctx context.Context, input models.DecryptAESRequest) (string, error)
}
func NewSymmetricRepository ¶
func NewSymmetricRepository(opts ...Option) SymmetricRepository