// GenerateAttestation uses the signing key to generate an attestation for this // statement. func GenerateAttestation(s *Signer, delegation []byte, stmt auth.Says) (*Attestation, error) { signer := s.ToPrincipal() 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) sig, err := s.Sign(ser, AttestationSigningContext) if err != nil { return nil, err } a := &Attestation{ SerializedStatement: ser, Signature: sig, Signer: auth.Marshal(signer), } if len(delegation) > 0 { a.SerializedDelegation = delegation } return a, nil }
// GenerateAttestation uses the signing key to generate an attestation for this // statement. func GenerateAttestation(s *Signer, delegation []byte, stmt auth.Says) (*Attestation, error) { 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) sig, err := s.Sign(ser, AttestationSigningContext) if err != nil { return nil, err } a := &Attestation{ SerializedStatement: ser, Signature: sig, SignerType: proto.String("key"), SignerKey: s.GetVerifier().MarshalKey(), } if len(delegation) > 0 { a.SerializedDelegation = delegation } return a, nil }
// Attest requests the Tao host sign a statement on behalf of the caller. The // optional issuer, time and expiration will be given default values if nil. func (tt *TPMTao) Attest(issuer *auth.Prin, start, expiration *int64, message auth.Form) (*Attestation, error) { 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 TPMTao 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 signed // 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) // TODO(tmroeder): check the pcrVals for sanity once we support extending or // clearing the PCRs. sig, _, err := tpm.Quote(tt.tpmfile, tt.aikHandle, ser, tt.pcrNums, tt.srkAuth[:]) if err != nil { return nil, err } // Pull off the extensions from the name to get the bare TPM key for the // signer. signer := auth.Prin{ Type: tt.name.Type, Key: tt.name.Key, } a := &Attestation{ SerializedStatement: ser, Signature: sig, Signer: auth.Marshal(signer), } return a, nil }
// Validate checks whether an attestation is valid and, if so, it returns the // statement conveyed by the attestation. func (a *Attestation) Validate() (auth.Says, error) { signer, err := a.ValidSigner() if err != nil { return auth.Says{}, err } f, err := auth.UnmarshalForm(a.SerializedStatement) if err != nil { return auth.Says{}, err } var stmt *auth.Says if ptr, ok := f.(*auth.Says); ok { stmt = ptr } else if val, ok := f.(auth.Says); ok { stmt = &val } else { return auth.Says{}, newError("tao: attestation statement has wrong type: %T", f) } if a.SerializedDelegation == nil { // Case (1), no delegation present. // Require that stmt.Speaker be a subprincipal of (or identical to) a.signer. if !auth.SubprinOrIdentical(stmt.Speaker, signer) { return auth.Says{}, newError("tao: attestation statement signer (%v) does not evidently speak for issuer (%v)", signer, stmt.Speaker) } } else { // Case (2), delegation present. // Require that: // - delegation conveys delegator says delegate speaksfor delegator, // - a.signer speaks for delegate // - and delegator speaks for s.Speaker var da Attestation if err := proto.Unmarshal(a.SerializedDelegation, &da); err != nil { return auth.Says{}, err } delegationStatement, err := da.Validate() if err != nil { return auth.Says{}, err } var delegation *auth.Speaksfor if ptr, ok := delegationStatement.Message.(*auth.Speaksfor); ok { delegation = ptr } else if val, ok := delegationStatement.Message.(auth.Speaksfor); ok { delegation = &val } else { return auth.Says{}, newError("tao: attestation delegation is wrong type") } if !delegationStatement.Speaker.Identical(delegation.Delegator) { return auth.Says{}, newError("tao: attestation delegation is invalid") } if !auth.SubprinOrIdentical(delegation.Delegate, signer) { return auth.Says{}, newError("tao: attestation delegation irrelevant to signer") } if !auth.SubprinOrIdentical(stmt.Speaker, delegation.Delegator) { return auth.Says{}, newError("tao: attestation delegation irrelevant to issuer") } if stmt.Time == nil { stmt.Time = delegationStatement.Time } else if delegationStatement.Time != nil && *stmt.Time < *delegationStatement.Time { stmt.Time = delegationStatement.Time } if stmt.Expiration == nil { stmt.Expiration = delegationStatement.Expiration } else if delegationStatement.Expiration != nil && *stmt.Expiration > *delegationStatement.Expiration { stmt.Expiration = delegationStatement.Expiration } } return *stmt, 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 }
// Create a response to a request. func respond(req *CARequest, s *Signer, guard Guard) (resp *CAResponse) { resp = new(CAResponse) resp.Type = CAType_ERROR.Enum() if *req.Type == CAType_ATTESTATION && req.Attestation != nil { truncSays, pe, err := TruncateAttestation(s.ToPrincipal(), req.Attestation) if err != nil { fmt.Fprintln(os.Stderr, "Couldn't truncate the attestation:", err) return } // TODO(tmroeder): fix this to check the time and make sure we're not // signing an unbounded attestation to this program. ra, err := GenerateAttestation(s, nil, truncSays) if err != nil { fmt.Fprintln(os.Stderr, "Couldn't attest to the new says statement:", err) return } // Add an endorsement to this PrinExt Program hash so the receiver can check // it successfully against policy. endorsement := auth.Says{ Speaker: s.ToPrincipal(), Message: auth.Pred{ Name: "TrustedProgramHash", Arg: []auth.Term{auth.PrinTail{Ext: []auth.PrinExt{pe}}}, }, } if truncSays.Time != nil { i := *truncSays.Time endorsement.Time = &i } if truncSays.Expiration != nil { i := *truncSays.Expiration endorsement.Expiration = &i } ea, err := GenerateAttestation(s, nil, endorsement) if err != nil { fmt.Fprintln(os.Stderr, "Couldn't generate an endorsement for this program:", err) return } eab, err := proto.Marshal(ea) if err != nil { fmt.Fprintln(os.Stderr, "Couldn't marshal an endorsement:", err) return } ra.SerializedEndorsements = [][]byte{eab} resp.Type = CAType_ATTESTATION.Enum() resp.Attestation = ra } else if *req.Type == CAType_DATALOG_POLICY { dg, ok := guard.(*DatalogGuard) if !ok { fmt.Fprintln(os.Stderr, "Requested wrong type") return } sdb, err := dg.GetSignedDatalogRules(s) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't get signed datalog rules: %s", err) return } resp.Type = CAType_DATALOG_POLICY.Enum() resp.SignedDatalogRules = sdb } else if *req.Type == CAType_ACL_POLICY { ac, ok := guard.(*ACLGuard) if !ok { fmt.Fprintln(os.Stderr, "Requested wrong type") return } sdb, err := ac.GetSignedACLSet(s) if err != nil { fmt.Fprintf(os.Stderr, "Couldn't get signed ACL set: %s", err) return } resp.Type = CAType_ACL_POLICY.Enum() resp.SignedAclSet = sdb } else { resp.Type = CAType_UNDEFINED.Enum() } return }