Skip to main content

signer

import "github.com/populist/protocol/signer"

Interface

type Signer interface {
Sign(ctx context.Context, payload []byte) ([]byte, error)
PublicKeyDER() []byte
}

The Signer interface decouples the state machine from any particular key management backend. The state machine calls Sign exactly once per poll close, with the canonical tally payload:

SHA-256(VoteMerkleRoot ‖ VoterMerkleRoot ‖ TotalVotes ‖ sorted-counts)

The returned signature and PublicKeyDER() are stored in types.Tally for independent verification.

Built-in signing backends

Four production implementations ship in services/:

// AWS KMS (services/kms)
import "github.com/ShywareLLC/community/services/kms"
s, err := kms.NewSigner(ctx, os.Getenv("SIGNING_KEY_ID"))
// Every Sign call is CloudTrail-audited. FIPS 140-3 L3 (CMVP #4884).

// GCP Cloud KMS (services/gcp)
import "github.com/ShywareLLC/community/services/gcp"
s, err := gcp.NewSigner(ctx, os.Getenv("SIGNING_KEY_ID")) // full resource name
// Every Sign call is recorded in GCP Cloud Audit Logs.

// Azure Key Vault (services/azure)
import "github.com/ShywareLLC/community/services/azure"
s, err := azure.NewSigner(ctx) // reads SIGNING_KEY_ID or AZURE_VAULT_NAME + AZURE_KEY_NAME
// Auth: DefaultAzureCredential (managed identity, env vars, Azure CLI).
// Every Sign call is recorded in Azure Monitor Key Vault audit logs.
// Azure returns P1363 (raw r||s); the signer converts to ASN.1 DER for consistency.

// HashiCorp Vault Transit (services/vault)
import "github.com/ShywareLLC/community/services/vault"
s, err := vault.NewSigner(ctx) // reads VAULT_ADDR, VAULT_TOKEN, SIGNING_KEY_ID
// Every Sign call is recorded in the Vault audit log.

All four expose VerifyDER(payload, sigDER) for auditor-side verification without cloud credentials. Public key is cached at construction time; no remote call is made for PublicKeyDER().

Signer resolution order in app.New:

  1. cfg.Signer — caller-supplied signer.Signer (BYOL: use gcp.NewSigner, azure.NewSigner, vault.NewSigner, or any custom implementation)
  2. cfg.KMSKeyID — convenience: constructs an AWS KMS signer automatically
  3. Neither set — SHA-256 stub (degraded; dev only)

Leave both empty in local development; the state machine falls back to the stub.

Bringing your own signer

Implement the two-method interface and pass it via app.Config.Signer:

type VaultSigner struct { ... }

func (v *VaultSigner) Sign(ctx context.Context, payload []byte) ([]byte, error) {
// call Vault Transit, GCP KMS, Azure Key Vault, or any other signing backend
}

func (v *VaultSigner) PublicKeyDER() []byte {
return v.cachedDER
}
app.New(ctx, app.Config{
ChainID: "my-chain-1",
Signer: &VaultSigner{ /* ... */ },
Verifier: identity.DiditVerifier{ /* ... */ },
// KMSKeyID is ignored when Signer is set
}, logger)

shyshares deployments use cfg.VotingSigner for the same injection into the voting sub-state machine.

The important invariant is not a specific cloud vendor — it is that the signing key never touches validator disk storage, and every signing invocation produces an immutable audit record (CloudTrail, Vault audit log, GCP Cloud Audit Logs, etc.).