go-attestation/verifier/aik.go

131 lines
4.2 KiB
Go

// 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
}