Documentation
¶
Overview ¶
Package hostdelivery is the public wire contract for sealed host delivery of one input to the StackKit node that owns it. A delivering authority such as Techstack or the kombify Wallet seals the value to a recipient key that the node generated and proved; only that node opens it into its own custody. The authority and every transport between them carry ciphertext and opaque references only.
The sealing scheme is HPKE (RFC 9180): DHKEM(X25519, HKDF-SHA256) with HKDF-SHA256 and ChaCha20-Poly1305. The canonical Context is the HPKE info, so a ciphertext opens only under the exact delivery, tenant, server, binding generation, operation, recipient key and expiry it was sealed for.
HPKE base mode does not authenticate the sender: anyone holding the public recipient key can seal to it. Operations whose effect is integrity-critical (Wallet items, SSH CA trust and host certificates) therefore also carry an Ed25519 authority signature over the whole envelope, made with a key the node pinned at enrollment and echoes in its recipient statement. Each operation declares its own plaintext limit and whether that signature is required; see LookupOperation.
Index ¶
- Constants
- func Fingerprint(public []byte) string
- func Operations() []string
- func Seal(recipientPublic []byte, context Context, plaintext []byte, now time.Time) ([]byte, error)
- func SignEnvelope(raw []byte, authority ed25519.PrivateKey, keyID string) ([]byte, error)
- func StatementDigest(payload []byte) []byte
- func VerifyStatement(statement Statement, pinnedOwnerPublic ed25519.PublicKey, challenge string, ...) error
- func VerifyStatementAuthority(statement Statement, authority ed25519.PublicKey, keyID string) error
- type AuthoritySignature
- type Context
- type Envelope
- type OperationSpec
- type SSHCARevoke
- type SSHCATrust
- type SSHHostCertificate
- type Signature
- type Statement
- type WalletItemRemove
Constants ¶
const ( EnvelopeAPIVersion = "stackkit.sealed-host-delivery/v1" StatementAPIVersion = "stackkit.host-recipient-statement/v1" // OperationBackupTargetImport delivers the owner S3 target material that // `stackkit backup target import` writes into encrypted local custody. OperationBackupTargetImport = "backup-target-import" // OperationBackupTargetRebind renews Plan authority over identical custody // when a node-issued binding has expired. OperationBackupTargetRebind = "backup-target-rebind" // OperationWalletItemImport delivers one Wallet item version (for example a // stack secret input) into the node's encrypted local custody. The // plaintext is the item value itself; DeliveryRef names the item and // ItemVersion its immutable version. OperationWalletItemImport = "wallet-item-import" // OperationWalletItemRotate delivers a newer version of an item already in // custody; the node removes every older version once it holds the new one. OperationWalletItemRotate = "wallet-item-rotate" // OperationWalletItemRemove tells the node to drop every version of the // item DeliveryRef names from its local custody (WalletItemRemove). It // carries no value; the Wallet sends it when the owner takes the item off // the server or the server is withdrawn. OperationWalletItemRemove = "wallet-item-remove" // OperationSSHCATrust delivers the owner's SSH user CA public keys and the // principals each local user accepts (SSHCATrust). An empty key list // revokes the trust like OperationSSHCARevoke. OperationSSHCATrust = "ssh-ca-trust" // OperationSSHCARevoke removes the owner's SSH CA trust (SSHCARevoke). OperationSSHCARevoke = "ssh-ca-revoke" // OperationSSHHostCertificate delivers an SSH host certificate for one of // this server's host keys (SSHHostCertificate). OperationSSHHostCertificate = "ssh-host-certificate" // MaxLifetime bounds how long one sealed delivery stays openable. MaxLifetime = 15 * time.Minute // MaxPlaintextBytes bounds the sealed material of the backup-target and SSH // operations. MaxPlaintextBytes = 16 << 10 // MaxWalletItemPlaintextBytes bounds one delivered Wallet item value. MaxWalletItemPlaintextBytes = 64 << 10 // MaxEnvelopeBytes bounds one encoded envelope, the largest operation's // sealed material and its context included. MaxEnvelopeBytes = 128 << 10 )
Variables ¶
This section is empty.
Functions ¶
func Operations ¶ added in v0.53.1
func Operations() []string
Operations lists every known operation in a stable order.
func Seal ¶
Seal encrypts plaintext to a recipient public key under the exact context. The plaintext must fit the operation's limit. An AuthoritySigned operation additionally needs SignEnvelope before it is delivered.
func SignEnvelope ¶ added in v0.53.1
SignEnvelope adds the delivering authority's signature to a sealed envelope.
func StatementDigest ¶
StatementDigest is the digest the owner key signs for a statement payload.
func VerifyStatement ¶
func VerifyStatement(statement Statement, pinnedOwnerPublic ed25519.PublicKey, challenge string, now time.Time, maxAge time.Duration) error
VerifyStatement checks the owner signature, key binding and freshness of a statement against the owner public key the caller pinned at enrollment. It proves the recipient key belongs to the holder of that owner key; it does not establish that the pinned owner key is genuine.
func VerifyStatementAuthority ¶ added in v0.53.1
VerifyStatementAuthority checks that a statement, already verified with VerifyStatement, names exactly the authority key the caller signs with. An authority seals AuthoritySigned operations only to a node that pinned it.
Types ¶
type AuthoritySignature ¶ added in v0.53.1
AuthoritySignature is an Ed25519 signature by the delivering authority over the envelope without this field.
type Context ¶
type Context struct {
TenantRef string `json:"tenantRef"`
DeliveryRef string `json:"deliveryRef"`
ItemVersion uint64 `json:"itemVersion"`
SiteRef string `json:"siteRef"`
NodeRef string `json:"nodeRef"`
BindingGeneration uint64 `json:"bindingGeneration"`
Operation string `json:"operation"`
RecipientFingerprint string `json:"recipientFingerprint"`
OperationID string `json:"operationId"`
IssuedAt string `json:"issuedAt"`
NotAfter string `json:"notAfter"`
}
Context is the exact scope a delivery is sealed for. Every field is bound into the ciphertext; none is secret.
func Open ¶
Open decrypts an envelope with the recipient private key. Callers must have checked the context, and for an AuthoritySigned operation the authority signature (Envelope.VerifyAuthority), against their own expectations first; Open re-binds the context cryptographically, so a changed field fails to decrypt.
type Envelope ¶
type Envelope struct {
APIVersion string `json:"apiVersion"`
Context Context `json:"context"`
Sealed []byte `json:"sealed"`
// Authority is the delivering authority's signature over the envelope. It
// is required for operations whose spec is AuthoritySigned.
Authority *AuthoritySignature `json:"authority,omitempty"`
}
Envelope is the only thing that crosses the control plane and transport.
func ParseEnvelope ¶ added in v0.53.1
ParseEnvelope decodes one envelope strictly and checks its bounds. It does not open the envelope or judge its context.
type OperationSpec ¶ added in v0.53.1
type OperationSpec struct {
// MaxPlaintextBytes bounds the sealed material of this operation.
MaxPlaintextBytes int
// AuthoritySigned requires an envelope signature by the delivering
// authority key the node pinned for its recipient generation.
AuthoritySigned bool
// Secret marks material the node must treat as a delivered secret: it is
// listed for rotation when the node is detached.
Secret bool
}
OperationSpec is what the contract fixes for one operation.
func LookupOperation ¶ added in v0.53.1
func LookupOperation(name string) (OperationSpec, bool)
LookupOperation returns the contract of a known operation. An unknown operation is neither sealed nor opened.
type SSHCARevoke ¶ added in v0.53.1
type SSHCARevoke struct{}
SSHCARevoke is the plaintext of OperationSSHCARevoke, encoded as {}.
type SSHCATrust ¶ added in v0.53.1
type SSHCATrust struct {
CAKeys []string `json:"caKeys"`
Principals map[string][]string `json:"principals"`
}
SSHCATrust is the plaintext of OperationSSHCATrust. CAKeys are OpenSSH authorized-key lines of the owner's user CA (several during a CA rotation); Principals maps each local user to the certificate principals it accepts.
type SSHHostCertificate ¶ added in v0.53.1
type SSHHostCertificate struct {
Certificate string `json:"certificate"`
}
SSHHostCertificate is the plaintext of OperationSSHHostCertificate: one OpenSSH host certificate line for a key this server already holds.
type Signature ¶
type Signature struct {
OwnerRef string `json:"ownerRef"`
KeyID string `json:"keyId"`
Value string `json:"value"`
}
Signature is the node owner's signature over a recipient statement.
type Statement ¶
type Statement struct {
APIVersion string `json:"apiVersion"`
SiteRef string `json:"siteRef"`
NodeRef string `json:"nodeRef"`
RecipientKey string `json:"recipientKey"`
Fingerprint string `json:"fingerprint"`
Generation uint64 `json:"generation"`
Challenge string `json:"challenge"`
IssuedAt string `json:"issuedAt"`
OwnerKeyID string `json:"ownerKeyId"`
OwnerPublic string `json:"ownerPublicKey"`
// AuthorityKeyID and AuthorityFingerprint name the delivering authority
// key the node pinned for this generation. They are empty until one is
// pinned, so a statement without them is byte-identical to a v1 statement
// from before authority signatures existed.
AuthorityKeyID string `json:"authorityKeyId,omitempty"`
AuthorityFingerprint string `json:"authorityFingerprint,omitempty"`
Signature Signature `json:"signature"`
}
Statement is the node's proof that a recipient key belongs to it. The Challenge makes it fresh: the delivering authority supplies it and rejects a statement that does not echo it.
func (Statement) SigningBytes ¶
SigningBytes is the canonical payload the owner signs, without the signature.
type WalletItemRemove ¶ added in v0.53.9
type WalletItemRemove struct{}
WalletItemRemove is the plaintext of OperationWalletItemRemove, encoded as {}.