yubihsm

package module
v0.0.0-...-9cd8256 Latest Latest
Warning

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

Go to latest
Published: Mar 13, 2024 License: MIT Imports: 19 Imported by: 0

README

Idiomatic Go bindings to the YubiHSM2.

Documentation

Overview

Package yubihsm provides to a YubiHSM2 via idiomatic Go crypto APIs

The YubiHSM2 is the big-sibling to the YubiKey PIV dongle. See [What is YubiHSM 2] for Yubico's documentation on their HSM.

Connecting to a YubiHSM2

This package does not directly provide access to a YubiHSM2. Instead, you must run a separate _connector_ to provide access via USB. The most common option is Yubico's yubihsm-connector. This can either installed via source, your distribution's packages, or via Yubico's yubihsm2-sdk.

Alternatives to the yubihsm-connector include the yubihsm.rs example HTTP connector or its mockhsm.

Each connector provides a simple HTTP POST interface to the YubiHSM2, providing binary a command-response interface. The connector listens at localhost:12345 by default, but it is possible to connect to a remote instance of the connector:

conn := NewHTTPConnector(WithConnectorURL("http://1.2.3.4:5678/connector/api"))

YubiHSM2 Sessions

All meaningful commands on a YubiHSM2 are sent within the context of a YubiHSM2 session. Each session is encrypted and authenticated via a symmetric authentication key.

An out-of-box YubiHSM2 is configured with a default authentication key derived from the password "password". You _must_ replace the default password and set a random key prior to using the HSM!

Up to 16 sessions may be active concurrently on the HSM. Each session has a 30-second inactivity timeout before the session expires. This package does not currently support keepalives; long-running processes should implement this via the Session.Ping method:

var session Session
timer := time.NewTimer(20*time.Second)
for _ := range timer.C {
	_, err := session.Echo(ctx, conn, 0xff)
	if err != nil {
		return err
	}
}

YubiHSM2 Keys

Keys can be loaded from a Session. This package currently does not support generating the keys, you can use an external tool such as yubihsm-shell instead.

The returned key object generally conforms the standard crypto key APIs, and can be used wherever a crypto.Signer or crypto.Deriver is used.

Supported key algorithms

Only asymmetric key pairs (RSA, ECDSA, Ed25519) are supported. Any of these may be used to generate a signature. Only an RSA key can be used to decrypt a message.

Ed25519ph is not supported by the YubiHSM2, only plain Ed25519 works.

ECDH is not supported. (The crypto/ecdh API is closed from external extension; there is no way to implement a crypto/ecdh.PrivateKey in a third-party module.)

Index

Constants

View Source
const (
	// ErrNotAuthenticated is returned if a command is sent over an
	// unauthenticated [Session].
	ErrNotAuthenticated sessionError = "cannot send message over unauthenticated session"

	// ErrReauthenticationRequired is returned when the maximum number
	// of commands have been sent over an encrypted [Session]. The
	// session must be reauthenticated by calling [Session.Authenticate].
	ErrReauthenticationRequired sessionError = "maximum messages sent; session must reauthenticate"

	// ErrIncorrectMAC is returned when a response from the YubiHSM2
	// has an inccorect MAC.
	ErrIncorrectMAC sessionError = "session message MAC failed"

	// ErrInvalidMessage is returned when a response message cannot
	// be processed; generally indicating the length is incorrect.
	ErrInvalidMessage sessionError = "invalid response message"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type AuthenticationOption

type AuthenticationOption func(*Session, *authConfig) error

AuthenticationOption configures an HSM Session.

func WithAuthenticationKeyID

func WithAuthenticationKeyID(keyID ObjectID) AuthenticationOption

WithAuthenticationKeyID sets the authentication key ID of a session. If left unspecified the default HSM ID 1 is used.

func WithAuthenticationKeys

func WithAuthenticationKeys(encryptionKey, macKey SessionKey) AuthenticationOption

WithAuthenticationKeys sets the authentication key of a session. If left unspecified the session uses keys derived from the default HSM password.

At most one of WithPassword or WithAuthenticationKeys may be used.

func WithPassword

func WithPassword(password string) AuthenticationOption

WithPassword sets the authentication password of a session. If left unspecified the session uses the default HSM password.

At most one of WithPassword or WithAuthenticationKeys may be used.

type Connector

type Connector interface {
	SendCommand(ctx context.Context, command []byte) ([]byte, error)
}

Connector allows sending commands to a YubiHSM2.

[command] is a fully serialized HSM command. The YubiHSM2 has a maximum command length of 2028 bytes per the documentation of the Put Opaque command; therefore a connector must support sending a message this long.

type DeviceInfo

type DeviceInfo struct {
	// Version string. Received from the HSM.
	Version string
	// Serial number. Received from the HSM.
	Serial uint32
	// LogStore size expressed in number of log entries. Received from
	// the HSM.
	LogStore uint8
	// LogLines used. Received from the HSM.
	LogLines uint8
	// Algorithms supported by the device. Received from the HSM.
	Algorithms uint64
	// Trusted is set to [true] if and only if the information was
	// received via an authenticated and encrypted [Session].
	Trusted bool
}

DeviceInfo contains information about the HSM.

https://developers.yubico.com/YubiHSM2/Commands/Device_Info.html

type HTTPConnector

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

HTTPConnector is a Connector which provides access to a YubiHSM2 through the yubihsm-connector HTTP interface.

The zero value HTTPConnector is valid to use and connects to the yubihsm-connector at http://localhost:12345/connector/api using net/http.DefaultClient. This behavior can be customized with NewHTTPConnector.

func NewHTTPConnector

func NewHTTPConnector(options ...HTTPOption) HTTPConnector

NewHTTPConnector creates an HTTPConnector using the provided configuration options.

func (*HTTPConnector) SendCommand

func (h *HTTPConnector) SendCommand(ctx context.Context, cmd []byte) ([]byte, error)

SendCommand transmits the command and returns the YubiHSM2's response.

type HTTPOption

type HTTPOption func(*httpConnector)

HTTPOption configures the behavior of the HTTPConnector created by NewHTTPConnector.

func WithConnectorURL

func WithConnectorURL(url string) HTTPOption

WithConnectorURL configures the HTTPConnector to issue HTTP requests to the yubihsm-connector at the provided URL.

If not specified this defaults to "http://localhost:12345/connector/api".

NewHTTPConnector(WithConnectorURL("http://1.2.3.4:5678/connector/api"))

func WithHTTPClient

func WithHTTPClient(client *http.Client) HTTPOption

WithHTTPClient configures the HTTPConnector to make HTTP requests using the provided HTTP client.

If not specified this defaults to http.DefaultClient.

type KeyPair

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

KeyPair manages either an RSA, ECDSA, or Ed25519 key on a YubiHSM2.

A KeyPair is a crypto.PrivateKey, but it does not directly implement either crypto.Signer or crypto.Decrypter. This is because invoking a command on an HSM requires a context.Context parameter, which is not supported by the crypto API.

Instead, use the KeyPair.Sign function directly, or used the KeyPair.AsCryptoSigner to wrap a (KeyPair, context) pair into a signing key object. The equivalent KeyPair.Decrypt and KeyPair.AsCryptoDecrypter are used to obtain a decryption key.

func (*KeyPair) AsCryptoDecrypter

func (k *KeyPair) AsCryptoDecrypter(ctx context.Context, conn Connector, session *Session) crypto.Decrypter

AsCryptoDecrypter wraps the keypair into a type which can be used with the Go standard library's crypto.Decrypter. The returned key also defines an Equal() method to implement crypto.PrivateKey.

It does this by embedding the provided [ctx], [conn], and [session] into the returned object. This is non-idiomatic; particularly wrapping a context.Context into a returned structure contradicts standard practice.

However, this is the only approach which matches the API of crypto.Decrypter. Use of KeyPair.Sign should be preferred whenever compatibility with the standard library isn't needed.

This does not confirm that the HSM key is compatible with signing, nor whether the Effective Capabilities are sufficient.

func (*KeyPair) AsCryptoSigner

func (k *KeyPair) AsCryptoSigner(ctx context.Context, conn Connector, session *Session) crypto.Signer

AsCryptoSigner wraps the keypair into a type which can be used with the Go standard library's crypto.Signer. The returned key also defines an Equal() method to implement crypto.PrivateKey.

It does this by embedding the provided [ctx], [conn], and [session] into the returned object. This is non-idiomatic; particularly wrapping a context.Context into a returned structure contradicts standard practice.

However, this is the only approach which matches the API of crypto.Signer. Use of KeyPair.Sign should be preferred whenever compatibility with the standard library isn't needed.

This does not confirm that the HSM key is compatible with signing, nor whether the Effective Capabilities are sufficient.

func (*KeyPair) Decrypt

func (k *KeyPair) Decrypt(ctx context.Context, conn Connector, session *Session, ciphertext []byte, opts crypto.DecrypterOpts) ([]byte, error)

Decrypt the [message] in the YubiHSM and return the plaintext.

This mimics the semantics of crypto.Signer.Sign, in particular the value of [opt]. See the details of rsa.PrivateKey.Decrypt.

This function will fail if the HSM key type is incompatible with decryption or if the Effective Capabilities are insufficient.

func (*KeyPair) Equal

func (k *KeyPair) Equal(x crypto.PrivateKey) bool

Equal checks if two private keys are equal. It implements crypto.PrivateKey.

This checks for logical equivalency of the keys; not if they are the exact same objects on the same YubiHSM2. As an example, a private key imported to multiple HSMs would compare equal, even if object IDs did not match.

func (*KeyPair) Public

func (k *KeyPair) Public() crypto.PublicKey

Public returns the public key. It implements crypto.PrivateKey.

It will be either an rsa.PublicKey, ecdsa.PublicKey, or ed25519.PublicKey depending upon the type of the key in the YubiHSM.

func (*KeyPair) Sign

func (k *KeyPair) Sign(ctx context.Context, conn Connector, session *Session, digest []byte, opts crypto.SignerOpts) ([]byte, error)

Sign the message [digest] in the YubiHSM and return the signature.

This mimics the semantics of crypto.Signer.Sign, in particular the value of [opt]. See the details of rsa.PrivateKey.Sign and ecdsa.PrivateKey.Sign for additional details. Both PKCS1v1.5 and PSS signatures are supported with RSA keys. Only basic Ed25519 signatures are supported; the YubiHSM2 supports neither the Ed25519ph or Ed25519ctx variants.

This function will fail if the HSM key type is incompatible with decryption or if the Effective Capabilities are insufficient.

type ObjectID

type ObjectID = yubihsm.ObjectID

ObjectID identifies a key or other object stored on a YubiHSM2.

type Session

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

Session is an encrypted and authenticated communication channel to an HSM. It can be used to exchange commands and responses to the HSM.

The zero Session is valid to use.

var session Session
err := session.Authenticate(ctx, conn)

func (*Session) Authenticate

func (s *Session) Authenticate(ctx context.Context, conn Connector, options ...AuthenticationOption) error

Authenticate performs the cryptographic exchange to authenticate with the YubiHSM2 and establish an encrypted communication channel.

func (*Session) Close

func (s *Session) Close(ctx context.Context, conn Connector) error

Close cleanly shuts the session down. A closed Session cannot be reused. After closing a session any [KeyPair]s loaded will no longer work.

This does not implement the standard io.Closer interface since a context.Context and Connector must be provided to send a close message to the HSM.

func (*Session) GetDeviceInfo

func (s *Session) GetDeviceInfo(ctx context.Context, conn Connector) (DeviceInfo, error)

GetDeviceInfo retrieves the HSM's status information.

This is the only command other than Session.Authenticate which can be called on an unauthenticated session, and the only command which can be called on either an authenticated _or_ unauthenticated session.

If the session isn't authenticated then the returned device information itself is neither encrypted not authenticated. It therefore should not be trusted; but this can be useful sometimes to e.g. lookup an HSM's configuration by its serial number prior to establishing a session.

If untrusted device information is used then it should be confirmed after authenticating the session by requesting the device info again and confirming against trusted values:

var session Session
untrustedDevInfo, _ := session.GetDeviceInfo(ctx, conn)
authKey, _ := keys[untrustedDevInfo.Serial]
_ = session.Authenticate(ctx, conn, WithAuthenticationKeys(authKey))
trustedDevInfo, _ := session.GetDeviceInfo(ctx, conn)
if trustedDevInfo.Serial != untrustedDevInfo.Serial {
	println("Lies!")
}

func (*Session) LoadKeyPair

func (s *Session) LoadKeyPair(ctx context.Context, conn Connector, label string) (*KeyPair, error)

LoadKeyPair looks up the asymmetric keypair in the HSM using the provided [label] and returns a KeyPair which can be used to sign messages or decrypt ciphertext.

The returned key's public will be one of an *ecdsa.PublicKey, ed25519.PublicKey, or an *rsa.PublicKey. Dependent upon the key's type and the Effective Capabilities KeyPair.Sign and/or KeyPair.Decrypt will work.

func (*Session) Ping

func (s *Session) Ping(ctx context.Context, conn Connector, data ...byte) error

Ping sends a [ping] message to the YubiHSM2 and returns the received [pong] response. It uses the Echo command to send and receive data.

The most common use of the echo command is to implement a session keepalive heartbeat; to mimic the yubihsm-shell's behavior use:

err = session.Ping(ctx, conn, 0xff)

type SessionKey

type SessionKey [sessionKeyLen]byte

SessionKey is a random key used to authenticate and encrypt a YubiHSM2 session.

These should always be randomly generated.

Directories

Path Synopsis
Package yubihsm implements core datatypes and serialization/deserialization the YubiHSM2 command protocol.
Package yubihsm implements core datatypes and serialization/deserialization the YubiHSM2 command protocol.

Jump to

Keyboard shortcuts

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