// Package verifier implements high-level logic to check information // provided by TPMs in client devices. package verifier import ( "bytes" "crypto/rsa" "fmt" tpb "github.com/google/go-attestation/proto" pb "github.com/google/go-attestation/verifier/proto" tpm1 "github.com/google/go-tpm/tpm" "github.com/google/go-tpm/tpm2" ) const ( tpmGeneratedMagic = 0xff544347 ) // VerifyAIK examines properties of an AIK and a creation attestation, to determine // if it is suitable for use as an attestation key. func VerifyAIK(tpmVersion tpb.TpmVersion, aik *tpb.AikInfo) (*pb.AikVerificationResults, error) { switch tpmVersion { case tpb.TpmVersion_TPM_12: aik12 := aik.GetTpm12() return verifyAIK12(aik12.GetPublicBlob()) case tpb.TpmVersion_TPM_20: aik20 := aik.GetTpm20() return verifyAIK20(aik20.GetPublicBlob(), aik20.GetCreationData(), aik20.GetAttestationData(), aik20.GetSignatureData()) default: return nil, fmt.Errorf("TPM version %d not supported", tpmVersion) } } func verifyAIK12(public []byte) (*pb.AikVerificationResults, error) { pub, err := tpm1.UnmarshalPubRSAPublicKey(public) if err != nil { return nil, err } var out pb.AikVerificationResults out.RocaVulnerableKey = ROCAVulnerableKey(pub) out.Succeeded = !out.RocaVulnerableKey return &out, nil } func verifyAIK20(public, creationData, attestationData, signature []byte) (*pb.AikVerificationResults, error) { if len(signature) < 8 { return nil, fmt.Errorf("signature is too short to be valid: only %d bytes", len(signature)) } pub, err := tpm2.DecodePublic(public) if err != nil { return nil, err } _, err = tpm2.DecodeCreationData(creationData) if err != nil { return nil, err } att, err := tpm2.DecodeAttestationData(attestationData) if err != nil { return nil, err } if att.Type != tpm2.TagAttestCreation { return nil, fmt.Errorf("attestation does apply to creation data, got tag %x", att.Type) } var out pb.AikVerificationResults switch pub.Type { case tpm2.AlgRSA: if pub.RSAParameters.KeyBits < 2048 { out.KeyTooSmall = true } out.RocaVulnerableKey = ROCAVulnerableKey(&rsa.PublicKey{N: pub.RSAParameters.Modulus()}) default: return nil, fmt.Errorf("public key of alg 0x%x not supported", pub.Type) } // Compute & verify that the creation data matches the digest in the // attestation structure. nameHashConstructor, err := pub.NameAlg.HashConstructor() if err != nil { return nil, err } h := nameHashConstructor() h.Write(creationData) out.CreationAttestationMismatch = !bytes.Equal(att.AttestedCreationInfo.OpaqueDigest, h.Sum(nil)) // Make sure the AIK has sane key parameters (Attestation can be faked if an AIK // can be used for arbitrary signatures). // We verify the following: // - Key is TPM backed. // - Key is TPM generated. // - Key is a restricted key (means it cannot do arbitrary signing/decrypt ops). // - Key cannot be duplicated. // - Key was generated by a call to TPM_Create*. out.KeyNotTpmBound = (att.Magic != tpmGeneratedMagic) || ((pub.Attributes & tpm2.FlagFixedTPM) == 0) out.KeyUsageOverlyBroad = ((pub.Attributes & tpm2.FlagRestricted) == 0) || ((pub.Attributes & tpm2.FlagFixedParent) == 0) || ((pub.Attributes & tpm2.FlagSensitiveDataOrigin) == 0) // Verify the attested creation name matches what is computed from // the public key. match, err := att.AttestedCreationInfo.Name.MatchesPublic(pub) if err != nil { return nil, err } out.NameAttestationMismatch = !match // Check the signature over the attestation data verifies correctly. p := rsa.PublicKey{E: int(pub.RSAParameters.Exponent()), N: pub.RSAParameters.Modulus()} signHashConstructor, err := pub.RSAParameters.Sign.Hash.HashConstructor() if err != nil { return nil, err } hsh := signHashConstructor() hsh.Write(attestationData) verifyHash, err := cryptoHash(pub.RSAParameters.Sign.Hash) if err != nil { return nil, err } //TODO(jsonp): Decode to tpm2.Signature & use that, once PR to expose DecodeSignature() is in. out.SignatureMismatch = rsa.VerifyPKCS1v15(&p, verifyHash, hsh.Sum(nil), signature[6:]) != nil out.Succeeded = !(out.CreationAttestationMismatch || out.KeyTooSmall || out.KeyNotTpmBound || out.KeyUsageOverlyBroad || out.NameAttestationMismatch || out.SignatureMismatch) return &out, nil }