// Seal encrypts data so only certain hosted programs can unseal it. Note that // at least some TPMs can only seal up to 149 bytes of data. So, we employ a // hybrid encryption scheme that seals a key and uses the key to encrypt the // data separately. We use the keys infrastructure to perform secure and // flexible encryption. func (tt *TPM2Tao) Seal(data []byte, policy string) ([]byte, error) { rh, err := tt.loadRoot() if err != nil { return nil, err } defer tpm2.FlushContext(tt.rw, rh) sk, policy_digest, err := tt.loadSession() if err != nil { return nil, errors.New("Can't load root key") } defer tpm2.FlushContext(tt.rw, sk) if policy != SealPolicyDefault { return nil, errors.New("tpm-specific policies are not yet implemented") } crypter, err := GenerateCrypter() if err != nil { return nil, err } defer ZeroBytes(crypter.aesKey) defer ZeroBytes(crypter.hmacKey) c, err := crypter.Encrypt(data) if err != nil { return nil, err } ck, err := MarshalCrypterProto(crypter) if err != nil { return nil, err } defer ZeroBytes(ck.Key) ckb, err := proto.Marshal(ck) if err != nil { return nil, err } defer ZeroBytes(ckb) priv, pub, err := tpm2.AssistSeal(tt.rw, rh, ckb, "", tt.password, tt.pcrs, policy_digest) if err != nil { return nil, err } // encode pub and priv s := EncodeTwoBytes(pub, priv) h := &HybridSealedData{ SealedKey: s, EncryptedData: c, } return proto.Marshal(h) }
// Unseal decrypts data that has been sealed by the Seal() operation, but only // if the policy specified during the Seal() operation is satisfied. func (tt *TPM2Tao) Unseal(sealed []byte) (data []byte, policy string, err error) { rh, err := tt.loadRoot() if err != nil { return nil, "", err } defer tpm2.FlushContext(tt.rw, rh) sh, policy_digest, err := tt.loadSession() if err != nil { return nil, "", errors.New("Can't load root key") } defer tpm2.FlushContext(tt.rw, sh) // The sealed data is a HybridSealedData. var h HybridSealedData if err := proto.Unmarshal(sealed, &h); err != nil { return nil, "", err } // Decode buffer containing pub and priv blobs pub, priv := DecodeTwoBytes(h.SealedKey) unsealed, _, err := tpm2.AssistUnseal(tt.rw, sh, rh, pub, priv, "", tt.password, policy_digest) if err != nil { return nil, "", err } defer ZeroBytes(unsealed) var ck CryptoKey if err := proto.Unmarshal(unsealed, &ck); err != nil { return nil, "", err } defer ZeroBytes(ck.Key) crypter, err := UnmarshalCrypterProto(&ck) if err != nil { return nil, "", err } defer ZeroBytes(crypter.aesKey) defer ZeroBytes(crypter.hmacKey) m, err := crypter.Decrypt(h.EncryptedData) if err != nil { return nil, "", err } return m, SealPolicyDefault, nil }
// FinalizeTPM2Tao releases the resources for the TPM2Tao. func FinalizeTPM2Tao(tt *TPM2Tao) { if tt.sessionHandle != 0 { tpm2.FlushContext(tt.rw, tpm2.Handle(tt.sessionHandle)) } tt.sessionHandle = 0 if tt.rootHandle != 0 { tpm2.FlushContext(tt.rw, tpm2.Handle(tt.rootHandle)) } tt.rootHandle = 0 if tt.sealHandle != 0 { tpm2.FlushContext(tt.rw, tpm2.Handle(tt.sealHandle)) } tt.sealHandle = 0 // Release the file handle. tt.rw.Close() }
// Return attest certificate func (tt *TPM2Tao) Tpm2Certify(network, addr string, keyName string) ([]byte, error) { // Establish connection wtih the CA. conn, err := net.Dial(network, addr) if err != nil { return nil, err } defer conn.Close() rk, err := tt.loadRoot() if err != nil { return nil, err } defer tpm2.FlushContext(tt.rw, rk) qh, err := tt.loadQuote() if err != nil { return nil, err } defer tpm2.FlushContext(tt.rw, qh) ms := util.NewMessageStream(conn) programCertMessage, err := Tpm2ConstructClientRequest(tt.rw, tt.rootCert, tt.pcrs, qh, "", tt.password, keyName) _, err = ms.WriteMessage(programCertMessage) if err != nil { return nil, err } var resp tpm2.ProgramCertResponseMessage err = ms.ReadMessage(&resp) if err != nil { return nil, err } attestCert, err := Tpm2ClientDecodeServerResponse(tt.rw, rk, qh, tt.password, resp) return attestCert, nil }
// Attest requests the Tao host seal a statement on behalf of the caller. The // optional issuer, time and expiration will be given default values if nil. func (tt *TPM2Tao) Attest(issuer *auth.Prin, start, expiration *int64, message auth.Form) (*Attestation, error) { fmt.Fprintf(os.Stderr, "About to load the quote key in attest\n") qh, err := tt.loadQuote() if err != nil { return nil, err } defer tpm2.FlushContext(tt.rw, qh) if issuer == nil { issuer = &tt.name } else if !auth.SubprinOrIdentical(*issuer, tt.name) { return nil, errors.New("invalid issuer in statement") } // TODO(tmroeder): we assume here that the PCRs haven't changed (e.g., been // extended) since this TPM2Tao was created. If they have, then the PCRs will // be wrong when we extend the principal here with them as the first // component. This doesn't matter at the moment, since we don't currently // support extending the PCRs or clearing them, but it will need to be // changed when we do. stmt := auth.Says{ Speaker: *issuer, Time: start, Expiration: expiration, Message: message, } // This is done in GenerateAttestation, but the TPM attestation is sealed // differently, so we do the time calculations here. t := time.Now() if stmt.Time == nil { i := t.UnixNano() stmt.Time = &i } if stmt.Expiration == nil { i := t.Add(365 * 24 * time.Hour).UnixNano() stmt.Expiration = &i } ser := auth.Marshal(stmt) var pcrVals [][]byte toQuote, err := tpm2.FormatTpm2Quote(ser, tt.pcrs, pcrVals) if err != nil { return nil, errors.New("Can't format tpm2 Quote") } // TODO(tmroeder): check the pcrVals for sanity once we support extending or // clearing the PCRs. quote_struct, sig, err := tpm2.Quote(tt.rw, qh, "", tt.password, toQuote, tt.pcrs, uint16(tpm2.AlgTPM_ALG_NULL)) if err != nil { return nil, err } fmt.Printf("toQuote: %x\n", toQuote) fmt.Printf("Quote: %x\n", quote_struct) fmt.Printf("sig: %x\n", sig) quoteKey, err := x509.MarshalPKIXPublicKey(tt.verifier) if err != nil { return nil, err } // TODO(kwalsh) remove Tpm2QuoteStructure from Attestation structure a := &Attestation{ SerializedStatement: ser, Signature: sig, SignerType: proto.String("tpm2"), SignerKey: quoteKey, Tpm2QuoteStructure: quote_struct, } return a, nil }
// NewTPM2Tao creates a new TPM2Tao and returns it under the Tao interface. func NewTPM2Tao(tpmPath string, statePath string, pcrNums []int) (Tao, error) { var err error tt := &TPM2Tao{pcrCount: 24, password: ""} tt.rw, err = tpm2.OpenTPM(tpmPath) if err != nil { return nil, err } // Make sure the TPM2Tao releases all its resources runtime.SetFinalizer(tt, FinalizeTPM2Tao) tt.pcrs = pcrNums tt.path = statePath // Create the root key. keySize := uint16(2048) quotePassword := "" //var empty []byte rootSaveContext := path.Join(tt.path, "root_context") _, rootErr := os.Stat(rootSaveContext) quoteSaveContext := path.Join(tt.path, "quote_context") _, quoteErr := os.Stat(quoteSaveContext) sealSaveContext := path.Join(tt.path, "seal_context") _, sealErr := os.Stat(sealSaveContext) if rootErr != nil || quoteErr != nil || sealErr != nil { if err := tpm2.InitTpm2Keys(tt.rw, tt.pcrs, keySize, uint16(tpm2.AlgTPM_ALG_SHA1), quotePassword, rootSaveContext, quoteSaveContext, sealSaveContext); err != nil { return nil, err } } // Read the contexts and public info and use them to load the handles. if tt.rootContext, err = ioutil.ReadFile(rootSaveContext); err != nil { return nil, fmt.Errorf("Could not read the root context from %s: %v", rootSaveContext, err) } if tt.quoteContext, err = ioutil.ReadFile(quoteSaveContext); err != nil { return nil, fmt.Errorf("Could not read the quote context from %s: %v", quoteSaveContext, err) } if tt.sealContext, err = ioutil.ReadFile(sealSaveContext); err != nil { return nil, fmt.Errorf("Could not read the seal context from %s: %v", sealSaveContext, err) } if tt.quoteHandle, err = tt.loadQuote(); err != nil { return nil, err } defer tpm2.FlushContext(tt.rw, tt.quoteHandle) if tt.verifier, err = tpm2.GetRsaKeyFromHandle(tt.rw, tt.quoteHandle); err != nil { return nil, err } fmt.Fprintf(os.Stderr, "Loaded the handles and the verifier\n") // Get the pcr values for the PCR nums. tt.pcrNums = make([]int, len(pcrNums)) for i, v := range pcrNums { tt.pcrNums[i] = v } tt.pcrVals, err = ReadTPM2PCRs(tt.rw, pcrNums) if err != nil { return nil, err } // Create principal. tt.name, err = MakeTPM2Prin(tt.verifier, tt.pcrNums, tt.pcrVals) if err != nil { return nil, err } quoteCertPath := path.Join(tt.path, "quote_cert") if _, quoteCertErr := os.Stat(quoteCertPath); quoteCertErr != nil { tt.quoteCert, err = getQuoteCert(tt.rw, tt.path, tt.quoteHandle, quotePassword, tt.name, tt.verifier) if err != nil { return nil, err } if err := ioutil.WriteFile(quoteCertPath, tt.quoteCert, 0644); err != nil { return nil, err } } else { if tt.quoteCert, err = ioutil.ReadFile(quoteCertPath); err != nil { return nil, err } } fmt.Fprintf(os.Stderr, "Got TPM 2.0 principal name %q\n", tt.name) return tt, nil }
// This program makes the endorsement certificate given the Policy key. func main() { keySize := flag.Int("modulus size", 2048, "Modulus size for keys") keyName := flag.String("Endorsement key name", "JohnsHw", "endorsement key name") endorsementCertFile := flag.String("Endorsement save file", "endorsement.cert.der", "endorsement save file") policyCertFile := flag.String("Policy cert file", "policy.cert.go.der", "cert file") policyKeyFile := flag.String("Policy key file", "policy.go.bin", "policy save file") policyKeyPassword := flag.String("Policy key password", "xxzzy", "policy key password") flag.Parse() fmt.Printf("Policy key password: %s\n", *policyKeyPassword) // TODO pcrs := []int{7} // Open tpm rw, err := tpm2.OpenTPM("/dev/tpm0") if err != nil { fmt.Printf("OpenTPM failed %s\n", err) return } defer rw.Close() // Flushall err = tpm2.Flushall(rw) if err != nil { fmt.Printf("Flushall failed\n") return } var notBefore time.Time notBefore = time.Now() validFor := 365 * 24 * time.Hour notAfter := notBefore.Add(validFor) serializePolicyKey, err := ioutil.ReadFile(*policyKeyFile) if err != nil { fmt.Printf("Can't get serialized policy key\n") return } derPolicyCert, err := ioutil.ReadFile(*policyCertFile) if err != nil { fmt.Printf("Can't get policy cert %s\n", *policyCertFile) return } policyKey, err := tpm2.DeserializeRsaKey(serializePolicyKey) if err != nil { fmt.Printf("Can't get deserialize policy key\n") return } ekHandle, _, err := tpm2.CreateEndorsement(rw, uint16(*keySize), pcrs) if err != nil { fmt.Printf("Can't CreateEndorsement\n") return } defer tpm2.FlushContext(rw, ekHandle) endorsementCert, err := tpm2.GenerateHWCert(rw, ekHandle, *keyName, notBefore, notAfter, tpm2.GetSerialNumber(), derPolicyCert, policyKey) if err != nil { fmt.Printf("Can't create endorsement cert\n") } fmt.Printf("Endorsement cert: %x\n", endorsementCert) ioutil.WriteFile(*endorsementCertFile, endorsementCert, 0644) fmt.Printf("Endorsement cert created") }