// AddEndorsements reads the SerializedEndorsements in an attestation and adds // the ones that are predicates signed by a guard's policy key. func AddEndorsements(guard Guard, a *Attestation, v *Verifier) error { // Before validating against the guard, check to see if there are any // predicates endorsed by the policy key. This allows truncated principals // to get the Tao CA to sign a statement of the form // TrustedHash(ext.Program(...)). for _, e := range a.SerializedEndorsements { var ea Attestation if err := proto.Unmarshal(e, &ea); err != nil { return err } f, err := auth.UnmarshalForm(ea.SerializedStatement) if err != nil { return err } says, ok := f.(auth.Says) if !ok { return fmt.Errorf("a serialized endorsement must be an auth.Says") } // TODO(tmroeder): check that this endorsement hasn't expired. pred, ok := says.Message.(auth.Pred) if !ok { return fmt.Errorf("the message in an endorsement must be a predicate") } signerPrin := auth.NewPrin(*ea.SignerType, ea.SignerKey) if !signerPrin.Identical(says.Speaker) { return fmt.Errorf("the speaker of an endorsement must be the signer: %v vs %v", signerPrin, says.Speaker) } if !v.ToPrincipal().Identical(signerPrin) { return fmt.Errorf("the signer of an endorsement must be the guard's policy key") } if ok, err := v.Verify(ea.SerializedStatement, AttestationSigningContext, ea.Signature); (err != nil) || !ok { return fmt.Errorf("the signature on an endorsement didn't pass verification") } return guard.AddRule(pred.String()) } return nil }
// Checks the following: // (1) the endorsement attestation is valid // (2) the key being endorsed is kPrin // (3) the subject being endorsed is a trusted host // Finally the function returns the key principal signing the endorsement func validateEndorsementAttestation(attestation *tao.Attestation, guard tao.Guard, kPrin *auth.Prin) (*auth.Prin, error) { saysStatement, err := attestation.Validate() if err != nil { return nil, err } _, key, host, err := parseSaysStatement(&saysStatement) if err != nil { return nil, err } if !key.Identical(kPrin) { return nil, errors.New("endorsement does not endorse signer of (previous) attestaton") } if !guard.IsAuthorized(*host, "Host", []string{}) { return nil, errors.New("endorsement host not authorized to run in this domain") } signerType := attestation.SignerType if signerType == nil { return nil, errors.New("endorsement chain has attestation with missing SignerType") } signerPrin := auth.NewPrin(*signerType, attestation.SignerKey) return &signerPrin, nil }
// ValidSigner checks the signature on an attestation and, if so, returns the // principal name for the signer. func (a *Attestation) ValidSigner() (auth.Prin, error) { signer := auth.NewPrin(*a.SignerType, a.SignerKey) switch *a.SignerType { case "tpm": // The PCRs are contained in the Speaker of an auth.Says statement that // makes up the a.SerializedStatement. f, err := auth.UnmarshalForm(a.SerializedStatement) if err != nil { return auth.Prin{}, newError("tao: couldn't unmarshal the statement: %s", err) } // A TPM attestation must be an auth.Says. says, ok := f.(auth.Says) if !ok { return auth.Prin{}, newError("tao: the attestation statement was not an auth.Says statement") } // Signer is tpm; use tpm-specific signature verification. Extract the // PCRs from the issuer name, unmarshal the key as an RSA key, and call // tpm.VerifyQuote(). speaker, ok := says.Speaker.(auth.Prin) if !ok { return auth.Prin{}, newError("tao: the speaker of an attestation must be an auth.Prin") } pcrNums, pcrVals, err := extractPCRs(speaker) if err != nil { return auth.Prin{}, newError("tao: couldn't extract TPM PCRs from attestation: %s", err) } pk, err := extractTPMKey(a.SignerKey) if err != nil { return auth.Prin{}, newError("tao: couldn't extract TPM key from attestation: %s", err) } if err := tpm.VerifyQuote(pk, a.SerializedStatement, a.Signature, pcrNums, pcrVals); err != nil { return auth.Prin{}, newError("tao: TPM quote failed verification: %s", err) } return signer, nil case "tpm2": // TODO -- tpm2 // The PCRs are contained in the Speaker of an auth.Says statement that // makes up the a.SerializedStatement. f, err := auth.UnmarshalForm(a.SerializedStatement) if err != nil { return auth.Prin{}, newError("tao: couldn't unmarshal the statement: %s", err) } // put this back in // A TPM attestation must be an auth.Says. says, ok := f.(auth.Says) if !ok { return auth.Prin{}, newError("tao: the attestation statement was not an auth.Says statement") } // Signer is tpm; use tpm-specific signature verification. Extract the // PCRs from the issuer name, unmarshal the key as an RSA key, and call // tpm2.VerifyQuote(). speaker, ok := says.Speaker.(auth.Prin) if !ok { return auth.Prin{}, newError("tao: the speaker of an attestation must be an auth.Prin") } key, err := extractTPM2Key(a.SignerKey) if err != nil { return auth.Prin{}, newError("tao: couldn't extract TPM key from attestation: %s", err) } pcrNums, pcrVal, err := extractTpm2PCRs(speaker) if err != nil { return auth.Prin{}, newError("tao: couldn't extract TPM PCRs from attestation: %s", err) } ok, err = tpm2.VerifyTpm2Quote(a.SerializedStatement, pcrNums, pcrVal, a.Tpm2QuoteStructure, a.Signature, key) if err != nil { return auth.Prin{}, newError("tao: TPM quote verification error") } if !ok { return auth.Prin{}, newError("tao: TPM quote failed verification") } return signer, nil case "key": // Signer is ECDSA key, use Tao signature verification. v, err := UnmarshalKey(a.SignerKey) if err != nil { return auth.Prin{}, err } ok, err := v.Verify(a.SerializedStatement, AttestationSigningContext, a.Signature) if err != nil { return auth.Prin{}, err } if !ok { return auth.Prin{}, newError("tao: attestation signature invalid") } return signer, nil default: return auth.Prin{}, newError("tao: attestation signer principal unrecognized: %s", signer.String()) } }
// This function makes the following checks // (1) Checks if the attestation signature is valid and the statement is of the form // 'Speaker says Key speaks for Program'. // (2) Checks that 'Program' in the above statement is allowed to Execute in the domain policy. // In particular, the policy should allow the predicate: // Authorized(ProgramTaoName, "Execute") // (3) Checks that 'Speaker' in the above statement is a key principal endorsed by the policy key, // or rootCerts, via an endorsement chain. Each endorsement in this chain endorses the key // signing the previous endorsement (starting with the 'Speaker' key). // // An endorsement endorses either a host key, in which case it is an attestation, // or the root hardware key, in which case it is certificate. // This function also checks that each host or root hardware encoutered along this endorsement // chain is allowed as per domain policy. In particular the policy should allow the predicates // Authorized(HostTaoName, "Host") and Authorized(EncodedMachineInformation, "Root") // // A valid attestation chain must either end in a attestation signed by the policy key // or a certificate signed by one of the rootCerts. // // If all above checks go through, the function returns the principals: Speaker, Key, Program. func VerifyHostAttestation(serializedHostAttestation []byte, domain *tao.Domain, rootCerts *x509.CertPool) (*auth.Prin, *auth.Prin, *auth.Prin, error) { var hostAttestation tao.Attestation err := proto.Unmarshal(serializedHostAttestation, &hostAttestation) if err != nil { return nil, nil, nil, errors.New( "domain_service: error deserialiaizng host attestation: " + err.Error()) } // First check if attestation is valid. statement, err := hostAttestation.Validate() if err != nil { return nil, nil, nil, errors.New( "host attestation fails validation check: " + err.Error()) } // Next, check if SpeaksFor delegator is authorized to execute (i.e. the program is allowed to // run as per policy). speaker, key, prog, err := parseSaysStatement(&statement) if err != nil { return nil, nil, nil, err } if !domain.Guard.IsAuthorized(*prog, "Execute", []string{}) { return nil, nil, nil, errors.New( "program not authorized to run in this domain") } // Look for endorsement cert(s), rooted in the policy key, that ultimately certify the // key of the signer. signingKey := hostAttestation.GetSignerKey() if hostAttestation.SignerType == nil { return nil, nil, nil, errors.New("host attestation missing SignerType field") } signingPrin := auth.NewPrin(*hostAttestation.SignerType, signingKey) if !speaker.Identical(signingPrin) { // TODO: endorsement endorses speaker or signer? } // Look for endorsement(s) of signer, rooted in policy key. serializedEndorsements := hostAttestation.GetSerializedEndorsements() var realErr error var kPrin *auth.Prin kPrin = &signingPrin for _, serializedEndorsement := range serializedEndorsements { // serializedEndorsement could be X.509 certificate or Tao attestation. var attestation tao.Attestation err := proto.Unmarshal(serializedEndorsement, &attestation) if err == nil { kPrin, realErr = validateEndorsementAttestation(&attestation, domain.Guard, kPrin) if realErr != nil { return nil, nil, nil, realErr } } else if cert, err1 := x509.ParseCertificate(serializedEndorsement); err1 == nil { realErr = validateEndorsementCertificate(cert, domain.Guard, kPrin, rootCerts) if realErr != nil { return nil, nil, nil, realErr } else { // Endorsement certs are the root of the endorsement chain. // If they are valid, then no more checking is required. return speaker, key, prog, nil } } else { return nil, nil, nil, errors.New("error parsing host endorsement") } } if domain.Keys.SigningKey.ToPrincipal().Identical(*kPrin) { return speaker, key, prog, nil } return nil, nil, nil, errors.New("endorsement chain does not terminate in policy key") }