diff --git a/trusted_applet/key.go b/trusted_applet/key.go index d6c8bee..30ac071 100644 --- a/trusted_applet/key.go +++ b/trusted_applet/key.go @@ -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. // @@ -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" +// +// +// +// +// 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 := ¬e.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. diff --git a/trusted_applet/main.go b/trusted_applet/main.go index 6210515..ff4d7f0 100644 --- a/trusted_applet/main.go +++ b/trusted_applet/main.go @@ -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 @@ -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