// Binary attest-tool performs attestation operations on the local system. package main import ( "bytes" "crypto/ecdsa" "crypto/rand" "crypto/rsa" "encoding/hex" "encoding/json" "encoding/pem" "errors" "flag" "fmt" "io/ioutil" "os" "github.com/google/certificate-transparency-go/x509" "github.com/google/go-attestation/attest" "github.com/google/go-attestation/attest/attest-tool/internal" ) var ( keyPath = flag.String("key", "ak.json", "Path to the key file") nonceHex = flag.String("nonce", "", "Hex string to use as nonce when quoting") randomNonce = flag.Bool("random-nonce", false, "Generate a random nonce instead of using one provided") useSHA256 = flag.Bool("sha256", false, "Use SHA256 for quote operatons") ) func main() { flag.Parse() if *randomNonce { n := make([]byte, 8) rand.Read(n) *nonceHex = hex.EncodeToString(n) } tpm, err := attest.OpenTPM(nil) if err != nil { fmt.Fprintf(os.Stderr, "Error opening the TPM: %v\n", err) os.Exit(1) } err = runCommand(tpm) tpm.Close() if err != nil { fmt.Fprintf(os.Stderr, "Error: %v\n", err) os.Exit(1) } } func selftestCredentialActivation(tpm *attest.TPM, ak *attest.AK) error { eks, err := tpm.EKs() if err != nil { return fmt.Errorf("EKs() failed: %v", err) } if len(eks) == 0 { return errors.New("no EK present") } ek := eks[0].Public // Test credential activation. ap := attest.ActivationParameters{ TPMVersion: tpm.Version(), EK: ek, AK: ak.AttestationParameters(), } secret, ec, err := ap.Generate() if err != nil { return fmt.Errorf("failed to generate activation challenge: %v", err) } decryptedSecret, err := ak.ActivateCredential(tpm, *ec) if err != nil { return fmt.Errorf("failed to generate activate credential: %v", err) } if !bytes.Equal(secret, decryptedSecret) { return errors.New("credential activation produced incorrect secret") } return nil } func selftestAttest(tpm *attest.TPM, ak *attest.AK) error { // This nonce is used in generating the quote. As this is a selftest, // it's set to an arbitrary value. nonce := []byte{1, 2, 3, 4, 5, 6, 7, 8, 1, 2, 3, 4, 5, 6, 7, 8} pub, err := attest.ParseAKPublic(tpm.Version(), ak.AttestationParameters().Public) if err != nil { return fmt.Errorf("failed to parse ak public: %v", err) } if _, err := tpm.MeasurementLog(); err != nil { return fmt.Errorf("no event log available: %v", err) } attestation, err := tpm.AttestPlatform(ak, nonce, nil) if err != nil { return fmt.Errorf("failed to attest: %v", err) } for i, quote := range attestation.Quotes { if err := pub.Verify(quote, attestation.PCRs, nonce); err != nil { return fmt.Errorf("failed to verify quote[%d]: %v", i, err) } } el, err := attest.ParseEventLog(attestation.EventLog) if err != nil { return fmt.Errorf("failed to parse event log: %v", err) } if _, err := el.Verify(attestation.PCRs); err != nil { return fmt.Errorf("event log failed to verify: %v", err) } return nil } func selftest(tpm *attest.TPM) error { ak, err := tpm.NewAK(nil) if err != nil { return fmt.Errorf("NewAK() failed: %v", err) } defer ak.Close(tpm) if err := selftestCredentialActivation(tpm, ak); err != nil { return fmt.Errorf("credential activation failed: %v", err) } if err := selftestAttest(tpm, ak); err != nil { return fmt.Errorf("state attestation failed: %v", err) } return nil } func runCommand(tpm *attest.TPM) error { switch flag.Arg(0) { case "info": info, err := tpm.Info() if err != nil { return err } fmt.Printf("Version: %d\n", info.Version) fmt.Printf("Interface: %d\n", info.Interface) fmt.Printf("VendorInfo: %x\n", info.VendorInfo) fmt.Printf("Manufactorer: %v\n", info.Manufacturer) case "make-ak", "make-aik": k, err := tpm.NewAK(nil) if err != nil { return fmt.Errorf("failed to mint an AK: %v", err) } defer k.Close(tpm) b, err := k.Marshal() if err != nil { return err } return ioutil.WriteFile(*keyPath, b, 0644) case "quote": b, err := ioutil.ReadFile(*keyPath) if err != nil { return err } k, err := tpm.LoadAK(b) if err != nil { return fmt.Errorf("failed to load AK: %v", err) } defer k.Close(tpm) nonce, err := hex.DecodeString(*nonceHex) if err != nil { return err } alg := attest.HashSHA1 if *useSHA256 { alg = attest.HashSHA256 } q, err := k.Quote(tpm, nonce, alg) if err != nil { return fmt.Errorf("failed to generate quote: %v", err) } fmt.Printf("Quote: %x\n", q.Quote) fmt.Printf("Signature: %x\n", q.Signature) case "list-eks": eks, err := tpm.EKs() if err != nil { return fmt.Errorf("failed to read EKs: %v", err) } for _, ek := range eks { data, err := encodeEK(ek) if err != nil { return fmt.Errorf("encoding ek: %v", err) } fmt.Printf("%s\n", data) } case "list-pcrs": alg := attest.HashSHA1 if *useSHA256 { alg = attest.HashSHA256 } pcrs, err := tpm.PCRs(alg) if err != nil { return fmt.Errorf("failed to read PCRs: %v", err) } for _, pcr := range pcrs { fmt.Printf("PCR[%d]: %x\n", pcr.Index, pcr.Digest) } case "measurement-log": b, err := tpm.MeasurementLog() if err != nil { return fmt.Errorf("failed to read the measurement log: %v", err) } fmt.Printf("%x\n", b) case "dump": dumpData, err := runDump(tpm) if err != nil { return err } return json.NewEncoder(os.Stdout).Encode(dumpData) case "self-test": err := selftest(tpm) if err != nil { fmt.Println("FAIL") return err } else { fmt.Println("PASS") } default: return fmt.Errorf("no such command %q", flag.Arg(0)) } return nil } func encodeEK(ek attest.EK) ([]byte, error) { if ek.Certificate != nil { return pem.EncodeToMemory(&pem.Block{ Type: "CERTIFICATE", Bytes: ek.Certificate.Raw, }), nil } switch pub := ek.Public.(type) { case *ecdsa.PublicKey: data, err := x509.MarshalPKIXPublicKey(pub) if err != nil { return nil, fmt.Errorf("marshaling ec public key: %v", err) } return pem.EncodeToMemory(&pem.Block{ Type: "EC PUBLIC KEY", Bytes: data, }), nil case *rsa.PublicKey: return pem.EncodeToMemory(&pem.Block{ Type: "RSA PUBLIC KEY", Bytes: x509.MarshalPKCS1PublicKey(pub), }), nil default: return nil, fmt.Errorf("unsupported public key type %T", pub) } } func runDump(tpm *attest.TPM) (*internal.Dump, error) { var ( out internal.Dump err error ) out.Static.TPMVersion = tpm.Version() if out.Static.EKPem, err = rsaEKPEM(tpm); err != nil { return nil, err } k, err := tpm.NewAK(nil) if err != nil { return nil, fmt.Errorf("failed to mint an AK: %v", err) } defer k.Close(tpm) out.AK = k.AttestationParameters() // Get a quote. if out.Quote.Nonce, err = hex.DecodeString(*nonceHex); err != nil { return nil, fmt.Errorf("failed decoding nonce hex: %v", err) } out.Quote.Alg = attest.HashSHA1 if *useSHA256 { out.Quote.Alg = attest.HashSHA256 } q, err := k.Quote(tpm, out.Quote.Nonce, out.Quote.Alg) if err != nil { return nil, fmt.Errorf("failed to generate quote: %v", err) } out.Quote.Quote = q.Quote out.Quote.Signature = q.Signature // Get log information. if out.Log.Raw, err = tpm.MeasurementLog(); err != nil { return nil, fmt.Errorf("failed to read measurement log: %v", err) } // Get PCR values. if out.Log.PCRs, err = tpm.PCRs(out.Quote.Alg); err != nil { return nil, fmt.Errorf("failed to read PCRs: %v", err) } return &out, nil } func rsaEKPEM(tpm *attest.TPM) ([]byte, error) { eks, err := tpm.EKs() if err != nil { return nil, fmt.Errorf("failed to read EKs: %v", err) } var ( pk *rsa.PublicKey buf bytes.Buffer ) for _, ek := range eks { if pub, ok := ek.Public.(*rsa.PublicKey); ok { pk = pub break } } if pk == nil { return nil, errors.New("no EK available") } if err := pem.Encode(&buf, &pem.Block{Type: "RSA PUBLIC KEY", Bytes: x509.MarshalPKCS1PublicKey(pk)}); err != nil { return nil, fmt.Errorf("failed to PEM encode: %v", err) } return buf.Bytes(), nil }