// 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", "aik.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 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-aik": k, err := tpm.NewAIK(nil) if err != nil { return fmt.Errorf("failed to mint an AIK: %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.LoadAIK(b) if err != nil { return fmt.Errorf("failed to load AIK: %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) 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.NewAIK(nil) if err != nil { return nil, fmt.Errorf("failed to mint an AIK: %v", err) } defer k.Close(tpm) out.AIK = 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 }