Skip to content

Commit

Permalink
Derive attestation key and sign rolling witness ID
Browse files Browse the repository at this point in the history
  • Loading branch information
AlCutter committed Mar 7, 2024
1 parent 12cdefc commit 6cf2538
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 9 deletions.
62 changes: 55 additions & 7 deletions trusted_applet/key.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,17 @@ import (
)

var (
witnessPublicKey string
witnessSigningKey string
// attestPublicKey can be used to verify that a given witnessPublicKey
// was derived on a known device.
attestPublicKey string
witnessPublicKey string
witnessSigningKey string
witnessPublicKeyAttestation string
)

// deriveWitnessKey creates this witness' signing identity by deriving a key
// based on the hardware's unique internal secret key.
// based on the hardware's unique internal secret key and a counter stored in the RPMB
// (currently always zero).
//
// TODO(al): The derived key should change if the device is wiped.
//
Expand All @@ -48,21 +53,64 @@ func deriveWitnessKey() {
log.Fatalf("Failed to fetch Status: %v", err)
}

// We should add an obvious prefix to key names when we're running without secure boot
// Add an obvious prefix to key names when we're running without secure boot
prefix := ""
if !status.HAB {
prefix = "DEV:"
}

// TODO(al): We'll switch to using a counter from the RPMB to ensure we get a fresh key
// whenever the device is wiped. For now we'll just use a static diversifier.
witnessSigningKey, witnessPublicKey = deriveNoteSigner(
fmt.Sprintf("%sWitnessKey-test", prefix),
fmt.Sprintf("%sWitnessKey-id:%d", prefix, status.IdentityCounter),
status.Serial,
status.HAB,
func(rnd io.Reader) string {
return fmt.Sprintf("%sArmoredWitness-%s", prefix, randomName(rnd))
})

attestPublicKey, witnessPublicKeyAttestation = attestID(&status, witnessPublicKey)

}

// attestID creates a signer which is forever static for this device, and uses
// that to sign a note which binds the passed in witness ID to this device's
// serial number and current identity counter.
//
// The attestation note contents is formatted like so:
//
// "ArmoredWitness ID attestation v1"
// <Device serial string>
// <Witness identity counter in decimal>
// <Witness identity note verifier string>
//
// Returns the note verifier string which can be used to open the note, and the note containing the witness ID attestation.
func attestID(status *api.Status, pubkey string) (string, string) {
// Add an obvious prefix to key names when we're running without secure boot
prefix := ""
if !status.HAB {
prefix = "DEV:"
}

attestSigner, attestPublicKey := deriveNoteSigner(
fmt.Sprintf("%sID-Attestation", prefix),
status.Serial,
status.HAB,
func(_ io.Reader) string {
return fmt.Sprintf("%sAW-ID-Attestation-%s", prefix, status.Serial)
})

aN := &note.Note{
Text: fmt.Sprintf("ArmoredWitness ID attestation v1\n%s\n%d\n%s\n", status.Serial, status.IdentityCounter, witnessPublicKey),
}
aSigner, err := note.NewSigner(attestSigner)
if err != nil {
panic(fmt.Errorf("failed to create attestation signer: %v", err))
}

attestation, err := note.Sign(aN, aSigner)
if err != nil {
panic(fmt.Errorf("failed to sign witness ID attestation: %v", err))
}
return attestPublicKey, string(attestation)
}

// deriveNoteSigner uses the h/w secret to derive a new note.Signer.
Expand Down
16 changes: 14 additions & 2 deletions trusted_applet/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,14 @@ func main() {
// (Re-)create our witness identity based on the device's internal secret key.
deriveWitnessKey()
// Update our status in OS so custodian can inspect our signing identity even if there's no network.
syscall.Call("RPC.SetWitnessStatus", rpc.WitnessStatus{Identity: witnessPublicKey}, nil)
syscall.Call("RPC.SetWitnessStatus", rpc.WitnessStatus{
Identity: witnessPublicKey,
IDAttestPublicKey: attestPublicKey,
AttestedID: witnessPublicKeyAttestation,
}, nil)

log.Printf("Attestation key:\n%s", attestPublicKey)
log.Printf("Attested identity key:\n%s", witnessPublicKeyAttestation)

go func() {
l := true
Expand Down Expand Up @@ -224,7 +231,12 @@ func runWithNetworking(ctx context.Context) error {
}
log.Printf("TA Version:%s MAC:%s IP:%s GW:%s DNS:%s", Version, iface.NIC.MAC.String(), addr, iface.Stack.GetRouteTable(), net.DefaultNS)
// Update status with latest IP address too.
syscall.Call("RPC.SetWitnessStatus", rpc.WitnessStatus{Identity: witnessPublicKey, IP: addr.Address.String()}, nil)
syscall.Call("RPC.SetWitnessStatus", rpc.WitnessStatus{
Identity: witnessPublicKey,
IDAttestPublicKey: attestPublicKey,
AttestedID: witnessPublicKeyAttestation,
IP: addr.Address.String(),
}, nil)

// Avoid the situation where, at boot, we get a DHCP lease and then immediately update our
// local clock from 1970 to now, whereupon we consider the DHCP lease invalid and have to tear down
Expand Down

0 comments on commit 6cf2538

Please sign in to comment.