From 08806a14444fdae19ea5837decd3eeb2ba02af27 Mon Sep 17 00:00:00 2001 From: Al Cutter Date: Wed, 12 Jun 2024 11:51:58 +0100 Subject: [PATCH] Derive BastionID (#302) --- go.mod | 2 +- go.sum | 4 ++-- trusted_applet/key.go | 48 ++++++++++++++++++++++++++++++++++++++++++ trusted_applet/main.go | 2 ++ 4 files changed, 53 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 2f0dee1..833fe38 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/prometheus/client_golang v1.19.1 github.com/transparency-dev/armored-witness-boot v0.1.0 github.com/transparency-dev/armored-witness-common v0.0.0-20240313170947-0b19d0fb8b95 - github.com/transparency-dev/armored-witness-os v0.1.2 + github.com/transparency-dev/armored-witness-os v0.1.3-0.20240524123036-bdd4b0b96386 github.com/transparency-dev/formats v0.0.0-20231205184308-949529efd6b3 github.com/transparency-dev/serverless-log v0.0.0-20231215122707-66f68a7705f5 github.com/transparency-dev/witness v0.0.0-20240311170858-5de1177dc362 diff --git a/go.sum b/go.sum index e45af2b..ef6bd94 100644 --- a/go.sum +++ b/go.sum @@ -57,8 +57,8 @@ github.com/transparency-dev/armored-witness-boot v0.1.0 h1:OBlz71PCmPju9cDI1VEmy github.com/transparency-dev/armored-witness-boot v0.1.0/go.mod h1:XpiSLWNiDplWVYjtR1FY/wyFqiWK++5pLCiYe3PZZC8= github.com/transparency-dev/armored-witness-common v0.0.0-20240313170947-0b19d0fb8b95 h1:qOh9vp/TLfJ1X46bk756se0wdlKHSV2TrGUMa3kz91w= github.com/transparency-dev/armored-witness-common v0.0.0-20240313170947-0b19d0fb8b95/go.mod h1:cb6aKsLVU2OUk8+UjD8xvzxU84miHXLMHJaxO2gHDus= -github.com/transparency-dev/armored-witness-os v0.1.2 h1:t8IrQQ9n0+lqvhASGfc9NOuEH3bykPSjtcMbWnARMZE= -github.com/transparency-dev/armored-witness-os v0.1.2/go.mod h1:Yt2PelPhnZ5Vc+/XJRPHoFaV5gTBw9xBXcMQJZTwSVM= +github.com/transparency-dev/armored-witness-os v0.1.3-0.20240524123036-bdd4b0b96386 h1:WHmJqV1L7dqAR5WXhOpFDL5jo4Rp2gCzvtmDh5FcX7E= +github.com/transparency-dev/armored-witness-os v0.1.3-0.20240524123036-bdd4b0b96386/go.mod h1:N+hTvqWTb08EeJC+DLEIPvnG4uI9ibnJVIA+oZaOBn4= github.com/transparency-dev/formats v0.0.0-20231205184308-949529efd6b3 h1:Mpx9pqc7bKrx2QQxKL3SPbLIGH4gTBR1ZFrNuKq3CcY= github.com/transparency-dev/formats v0.0.0-20231205184308-949529efd6b3/go.mod h1:tY9Z9oBaYdQt4NWIhsFAtv0altwLk+K9Gg/2tbS0eBQ= github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4= diff --git a/trusted_applet/key.go b/trusted_applet/key.go index 8b658c8..282ada0 100644 --- a/trusted_applet/key.go +++ b/trusted_applet/key.go @@ -16,6 +16,7 @@ package main import ( "crypto/aes" + "crypto/ed25519" "crypto/sha256" "encoding/binary" "fmt" @@ -37,6 +38,8 @@ var ( witnessPublicKey string witnessSigningKey string witnessPublicKeyAttestation string + bastionSigningKey ed25519.PrivateKey + bastionIDAttestation string ) // deriveIdentityKeys creates this witness' signing and attestation identities. @@ -68,6 +71,8 @@ func deriveIdentityKeys() { prefix = "DEV:" } + // Other than via the identity counter, the diversifier and key name in here + // MUST NOT be changed, or we'll break the invariant that this key is static. witnessSigningKey, witnessPublicKey = deriveNoteSigner( fmt.Sprintf("%sWitnessKey-id:%d", prefix, status.IdentityCounter), status.Serial, @@ -89,6 +94,18 @@ func deriveIdentityKeys() { // were derived on a known armored witness unit. witnessPublicKeyAttestation = attestID(&status, witnessPublicKey) + // Other than via the counter, the diversifier and key name in here + // MUST NOT be changed, or we'll break the invariant that this key is static. + bastionSec, bastionPub := deriveEd25519( + fmt.Sprintf("%sBastionKey-id:0", prefix), + status.Serial) + bastionSigningKey = bastionSec + bastionID := fmt.Sprintf("%064x", sha256.Sum256(bastionPub)) + + // Attest to the bastion ID so we can convince others that this ID + // was derived on a known armored witness unit + bastionIDAttestation = attestBastion(&status, bastionID) + } // attestID uses attestSigningKey to sign a note which binds the passed in witness ID to this device's @@ -109,6 +126,24 @@ func attestID(status *api.Status, pubkey string) string { return attestNote(status, aN) } +// attestBastion uses attestSigningKey to sign a note which binds the passed in witness ID to this device's +// serial number and current identity counter. +// +// The witness ID attestation note contents is formatted like so: +// +// "ArmoredWitness BastionID attestation v1" +// +// +// +// +// Returns the note verifier string which can be used to open the note, and the note containing the witness ID attestation. +func attestBastion(status *api.Status, bastionID string) string { + aN := ¬e.Note{ + Text: fmt.Sprintf("ArmoredWitness BastionID attestation v1\n%s\n%d\n%s\n", status.Serial, 0, bastionID), + } + return attestNote(status, aN) +} + func attestNote(status *api.Status, aN *note.Note) string { aSigner, err := note.NewSigner(attestSigningKey) if err != nil { @@ -137,6 +172,19 @@ func deriveNoteSigner(diversifier string, uniqueID string, keyName func(io.Reade return sec, pub } +// deriveEd25519 uses the hardware secret to device a new ed25519 keypair. +// +// diversifier should uniquely specify the key's intended usage, uniqueID should be the +// device's h/w unique identifier. +func deriveEd25519(diversifier string, uniqueID string) (ed25519.PrivateKey, ed25519.PublicKey) { + r := deriveHKDF(diversifier, uniqueID) + pub, priv, err := ed25519.GenerateKey(r) + if err != nil { + log.Fatalf("Failed to generate derived ed25519 key: %v", err) + } + return priv, pub +} + // randomName generates a random human-friendly name. func randomName(rnd io.Reader) string { // Figure out our name diff --git a/trusted_applet/main.go b/trusted_applet/main.go index 0ff96a2..070b21d 100644 --- a/trusted_applet/main.go +++ b/trusted_applet/main.go @@ -184,6 +184,7 @@ func main() { Identity: witnessPublicKey, IDAttestPublicKey: attestPublicKey, AttestedID: witnessPublicKeyAttestation, + AttestedBastionID: bastionIDAttestation, }, nil) klog.Infof("Attestation key:\n%s", attestPublicKey) @@ -281,6 +282,7 @@ func runWithNetworking(ctx context.Context) error { Identity: witnessPublicKey, IDAttestPublicKey: attestPublicKey, AttestedID: witnessPublicKeyAttestation, + AttestedBastionID: bastionIDAttestation, IP: addr.Address.String(), }, nil)