merge: mpc-bump-hsm-airgap-2026-04-28

This commit is contained in:
2025-12-28 20:10:00 -08:00
4 changed files with 1144 additions and 12 deletions
+13 -9
View File
@@ -21,12 +21,14 @@ import (
// surface for working with airgapped hardware wallets (Coldcard,
// Foundation Passport, Keystone Pro, NGRAVE Zero).
//
// The signing ceremony is file-mediated: mpcd writes the challenge to a
// working directory, the operator hand-carries it to the offline device
// via microSD or QR, and the signed response is dropped back into the
// same directory. This file owns the ceremony orchestration; in-process
// Signer construction for these devices is gated on a luxfi/hsm release
// that exports the airgap factory cases.
// The signing ceremony is file-mediated by design: Coldcard's canonical
// product flow uses microSD, Foundation Passport uses BBQr-encoded QR,
// and Keystone/NGRAVE use UR-encoded animated QR — all of which surface
// as challenge bytes the host writes to disk for the operator to ferry
// to the offline device. The response file is signed bytes the operator
// drops back into the same directory. mpcd owns the ceremony I/O; the
// per-vendor envelope semantics (PSBT vs UR vs BBQr) are owned by the
// luxfi/hsm Signer, which is constructible since v1.1.3 via NewSigner.
//
// The subcommands here are intentionally narrow:
//
@@ -49,9 +51,11 @@ func airgapCommand() *cli.Command {
}
// airgapProviders is the canonical set of airgap-capable provider names
// reserved by luxfi/hsm. Adding entries here is a contract change — they
// must round-trip through hsm.NewSigner once the airgap surface is
// published downstream.
// reserved by luxfi/hsm. Adding entries here is a contract change —
// every name MUST round-trip through hsm.NewSigner. Coverage lives in
// airgap_command_test.go so the dispatch table is exercised by `go test`
// rather than crashing the daemon at boot when an operator probes with
// an unconfigured provider.
func airgapProviders() []string {
return []string{"coldcard", "foundation", "keystone", "ngrave"}
}
+44
View File
@@ -0,0 +1,44 @@
// Copyright (C) 2020-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package main
import (
"testing"
"github.com/luxfi/hsm"
)
// TestAirgapProvidersRoundTripFactory enforces the contract documented
// on airgapProviders(): every reserved provider name MUST be a known
// factory case in luxfi/hsm. We supply each constructor's required
// fields so we exercise the dispatch table and the per-signer
// validators — the goal is to catch missing factory cases or rename
// drift, not to validate operational config.
func TestAirgapProvidersRoundTripFactory(t *testing.T) {
t.Parallel()
cases := map[string]map[string]string{
"coldcard": {"device_id": "test"},
"foundation": {"device_id": "test"},
"keystone": {"device_id": "test", "ur_type": "eth-sign-request"},
"ngrave": {"device_id": "test", "ur_type": "eth-sign-request"},
}
for _, p := range airgapProviders() {
cfg, ok := cases[p]
if !ok {
t.Fatalf("airgap_command_test: missing test config for provider %q — extend cases", p)
}
signer, err := hsm.NewSigner(p, cfg)
if err != nil {
t.Fatalf("hsm.NewSigner(%q): %v", p, err)
}
if signer == nil {
t.Fatalf("hsm.NewSigner(%q): nil signer with no error", p)
}
if got := signer.Provider(); got != p {
t.Fatalf("hsm.NewSigner(%q).Provider() = %q", p, got)
}
}
}
+9 -1
View File
@@ -31,7 +31,7 @@ require (
github.com/luxfi/crypto v1.17.45
github.com/luxfi/database v1.17.44
github.com/luxfi/fhe v1.7.7
github.com/luxfi/hsm v1.1.2
github.com/luxfi/hsm v1.1.3
github.com/luxfi/log v1.4.1
github.com/luxfi/metric v1.5.1
github.com/luxfi/threshold v1.6.0
@@ -70,6 +70,8 @@ require (
github.com/ChainSafe/go-schnorrkel v1.1.0 // indirect
github.com/ProjectZKM/Ziren/crates/go-runtime/zkvm_runtime v0.0.0-20260311194731-d5b7577c683d // indirect
github.com/agl/ed25519 v0.0.0-20200225211852-fd4d107ace12 // indirect
github.com/ansel1/merry v1.8.1 // indirect
github.com/ansel1/merry/v2 v2.2.2 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.41.5 // indirect
@@ -101,6 +103,8 @@ require (
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.13 // indirect
github.com/ganigeorgiev/fexpr v0.5.0 // indirect
github.com/gemalto/flume v1.0.0 // indirect
github.com/gemalto/kmip-go v0.1.0 // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
@@ -158,7 +162,9 @@ require (
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/miekg/dns v1.1.72 // indirect
github.com/miekg/pkcs11 v1.1.1 // indirect
github.com/mimoo/StrobeGo v0.0.0-20220103164710-9a04d6ca976b // indirect
github.com/minio/crc64nvme v1.1.1 // indirect
github.com/minio/md5-simd v1.1.2 // indirect
@@ -202,6 +208,8 @@ require (
go.temporal.io/sdk v1.41.1 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/mock v0.6.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.1 // indirect
go.yaml.in/yaml/v2 v2.4.4 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/exp v0.0.0-20260312153236-7ab1446f8b90 // indirect
+1078 -2
View File
File diff suppressed because it is too large Load Diff