diff --git a/attestationreport/attestationreport.go b/attestationreport/attestationreport.go index 0bcd06df..1ed5b651 100644 --- a/attestationreport/attestationreport.go +++ b/attestationreport/attestationreport.go @@ -54,6 +54,7 @@ type DriverConfig struct { UseCtr bool CtrPcr int CtrLog string + CtrDriver string } // Serializer is a generic interface providing methods for data serialization and @@ -63,7 +64,7 @@ type Serializer interface { GetPayload(raw []byte) ([]byte, error) Marshal(v any) ([]byte, error) Unmarshal(data []byte, v any) error - Sign(report []byte, signer Driver) ([]byte, error) + Sign(data []byte, signer Driver) ([]byte, error) VerifyToken(data []byte, roots []*x509.Certificate) (TokenResult, []byte, bool) } @@ -81,18 +82,21 @@ type Validity struct { NotAfter string `json:"notAfter" cbor:"1,keyasint"` } -// PcrMeasurement represents the measurements of a single PCR. If the type is 'PCR Summary', -// Sha256 is the final PCR value. If the type is 'PCR Eventlog', Sha256 is a list of the -// extends that leads to the final PCR value. The list is retrieved by the prover -// e.g. from the TPM binary bios measurements list or the IMA runtime measurements list. -type PcrMeasurement struct { - Type string `json:"type" cbor:"0,keyasint"` - Pcr int `json:"pcr" cbor:"1,keyasint"` - Summary HexByte `json:"summary,omitempty" cbor:"2,keyasint,omitempty"` - Events []PcrEvent `json:"events,omitempty" cbor:"3,keyasint,omitempty"` +// Artifact represents the digests of a measurement, e.g., of a single PCR. +// If the type is 'PCR Summary', Summary is the final PCR value. +// If the type is 'PCR Eventlog', Events contains a list of the extends that lead to the final +// PCR value. The list is retrieved by the prover, e.g., from the TPM binary bios measurements +// list or the IMA runtime measurements list. +// If the type is 'SW Eventlog', Events contains a list of digests that have been recorded as +// SW measurements +type Artifact struct { + Type string `json:"type" cbor:"0,keyasint"` // PCR Summary, PCR Eventlog, SW Eventlog + Pcr *int `json:"pcr,omitempty" cbor:"1,keyasint"` + Summary HexByte `json:"summary,omitempty" cbor:"2,keyasint,omitempty"` // Either summary + Events []MeasureEvent `json:"events,omitempty" cbor:"3,keyasint,omitempty"` // Or Events } -type PcrEvent struct { +type MeasureEvent struct { Sha256 HexByte `json:"sha256" cbor:"2,keyasint"` EventName string `json:"eventname,omitempty" cbor:"4,keyasint,omitempty"` EventData *EventData `json:"eventdata,omitempty" cbor:"5,keyasint,omitempty"` @@ -104,17 +108,15 @@ type CtrData struct { RootfsSha256 HexByte `json:"rootfsSha256" cbor:"1,keyasint"` } -// TpmMeasurement represents the attestation report +// Measurement represents the attestation report // elements of type 'TPM Measurement', 'SNP Measurement', 'TDX Measurement', // 'SGX Measurement', 'IAS Measurement' or 'SW Measurement' type Measurement struct { - Type string `json:"type" cbor:"0,keyasint"` - Evidence []byte `json:"evidence" cbor:"1,keyasint"` - Certs [][]byte `json:"certs" cbor:"3,keyasint"` - Signature []byte `json:"signature,omitempty" cbor:"2,keyasint,omitempty"` - Pcrs []PcrMeasurement `json:"pcrs,omitempty" cbor:"4,keyasint,omitempty"` - Sha256 HexByte `json:"sha256,omitempty" cbor:"5,keyasint,omitempty"` - Description string `json:"description,omitempty" cbor:"6,keyasint,omitempty"` + Type string `json:"type" cbor:"0,keyasint"` + Evidence []byte `json:"evidence,omitempty" cbor:"1,keyasint"` + Certs [][]byte `json:"certs,omitempty" cbor:"3,keyasint"` + Signature []byte `json:"signature,omitempty" cbor:"2,keyasint,omitempty"` + Artifacts []Artifact `json:"details,omitempty" cbor:"4,keyasint,omitempty"` } type SnpPolicy struct { @@ -379,10 +381,9 @@ type Metadata struct { type AttestationReport struct { Type string `json:"type" cbor:"0,keyasint"` Measurements []Measurement `json:"measurements,omitempty" cbor:"1,keyasint,omitempty"` - RtmManifest []byte `json:"rtmManifests" cbor:"2,keyasint"` + RtmManifest []byte `json:"rtmManifest" cbor:"2,keyasint"` OsManifest []byte `json:"osManifest" cbor:"3,keyasint"` AppManifests [][]byte `json:"appManifests,omitempty" cbor:"4,keyasint,omitempty"` CompanyDescription []byte `json:"companyDescription,omitempty" cbor:"5,keyasint,omitempty"` DeviceDescription []byte `json:"deviceDescription" cbor:"6,keyasint"` - Nonce []byte `json:"nonce" cbor:"7,keyasint"` } diff --git a/attestationreport/cbor.go b/attestationreport/cbor.go index d4108bde..ff43ba16 100644 --- a/attestationreport/cbor.go +++ b/attestationreport/cbor.go @@ -55,7 +55,7 @@ func (s CborSerializer) Unmarshal(data []byte, v any) error { return cbor.Unmarshal(data, v) } -func (s CborSerializer) Sign(report []byte, signer Driver) ([]byte, error) { +func (s CborSerializer) Sign(data []byte, signer Driver) ([]byte, error) { private, _, err := signer.GetSigningKeys() if err != nil { @@ -90,7 +90,7 @@ func (s CborSerializer) Sign(report []byte, signer Driver) ([]byte, error) { sigHolder.Headers.Unprotected[cose.HeaderLabelX5Chain] = certChainRaw msgToSign := cose.NewSignMessage() - msgToSign.Payload = report + msgToSign.Payload = data msgToSign.Signatures = append(msgToSign.Signatures, sigHolder) // This allows the signer to ensure mutual access for signing, if required @@ -99,7 +99,7 @@ func (s CborSerializer) Sign(report []byte, signer Driver) ([]byte, error) { err = msgToSign.Sign(rand.Reader, nil, coseSigner) if err != nil { - return nil, fmt.Errorf("signing failed: %w. len(report): %v", err, len(report)) + return nil, fmt.Errorf("signing failed: %w. len(data): %v", err, len(data)) } // sign and marshal message @@ -183,7 +183,7 @@ func (s CborSerializer) VerifyToken(data []byte, roots []*x509.Certificate) (Tok continue } - //Store details from (all) validated certificate chain(s) in the report + //Store details from (all) validated certificate chain(s) for _, chain := range x509Chains { chainExtracted := []X509CertExtracted{} for _, cert := range chain { diff --git a/attestationreport/json.go b/attestationreport/json.go index 674c09d7..18e53163 100644 --- a/attestationreport/json.go +++ b/attestationreport/json.go @@ -83,10 +83,10 @@ func (s JsonSerializer) Unmarshal(data []byte, v any) error { return json.Unmarshal(data, v) } -// Sign signs the attestation report with the specified signer 'signer' -func (s JsonSerializer) Sign(report []byte, signer Driver) ([]byte, error) { +// Sign signs data with the specified driver 'signer' (to enale hardware-based signatures) +func (s JsonSerializer) Sign(data []byte, signer Driver) ([]byte, error) { - log.Trace("Signing attestation report") + log.Tracef("Signing data length %v", len(data)) // This allows the signer to ensure mutual access for signing, if required signer.Lock() @@ -135,16 +135,16 @@ func (s JsonSerializer) Sign(report []byte, signer Driver) ([]byte, error) { var joseSigner jose.Signer joseSigner, err = jose.NewSigner(jose.SigningKey{Algorithm: alg, Key: opaqueSigner}, opt.WithHeader("x5c", certsb64)) if err != nil { - return nil, fmt.Errorf("failed to setup signer for the Attestation Report: %w", err) + return nil, fmt.Errorf("failed to setup signer : %w", err) } // sign log.Trace("Performing Sign operation") - obj, err := joseSigner.Sign(report) + obj, err := joseSigner.Sign(data) if err != nil { return nil, err } - log.Trace("Signed attestation report") + log.Trace("Signing finished") // return signature in bytes msg := obj.FullSerialize() @@ -210,7 +210,7 @@ func (s JsonSerializer) VerifyToken(data []byte, roots []*x509.Certificate) (Tok continue } - //Store details from (all) validated certificate chain(s) in the report + //Store details from (all) validated certificate chain(s) for _, chain := range certs { chainExtracted := []X509CertExtracted{} for _, cert := range chain { @@ -282,8 +282,8 @@ func algFromKeyType(pub crypto.PublicKey) (jose.SignatureAlgorithm, error) { } } -// Used for the JOSE Opaque Signer Interface. This enables signing -// the attestation report with hardware-based keys (such as TPM-based keys) +// Used for the JOSE Opaque Signer Interface. This enables signing with +// hardware-based keys (such as TPM-based keys) type hwSigner struct { pk *jose.JSONWebKey signer crypto.PrivateKey @@ -291,19 +291,19 @@ type hwSigner struct { } // Implements the JOSE Opaque Signer Interface. This enables signing -// the attestation report with hardware-based keys (such as TPM-based keys) +// with hardware-based keys (such as TPM-based keys) func (hws *hwSigner) Public() *jose.JSONWebKey { return hws.pk } // Implements the JOSE Opaque Signer Interface. This enables signing -// the attestation report with hardware-based keys (such as TPM-based keys) +// with hardware-based keys (such as TPM-based keys) func (hws *hwSigner) Algs() []jose.SignatureAlgorithm { return []jose.SignatureAlgorithm{hws.alg} } // Implements the JOSE Opaque Signer Interface. This enables signing -// the attestation report with hardware-based keys (such as TPM-based keys) +// with hardware-based keys (such as TPM-based keys) func (hws *hwSigner) SignPayload(payload []byte, alg jose.SignatureAlgorithm) ([]byte, error) { // EC-specific: key size in byte for later padding var keySize int @@ -345,7 +345,7 @@ func (hws *hwSigner) SignPayload(payload []byte, alg jose.SignatureAlgorithm) ([ asn1Sig, err := hws.signer.(crypto.Signer).Sign(rand.Reader, hashed, opts) if err != nil { return nil, fmt.Errorf("signing failed: %w. Additional info: "+ - "opts: %v, keySize: %v, len(report): %v, len(hash): %v", + "opts: %v, keySize: %v, len(data): %v, len(hash): %v", err, opts, keySize, n, len(hashed)) } // Convert from asn1 format (as specified in crypto) to concatenated and padded format for go-jose diff --git a/attestationreport/validationreport.go b/attestationreport/validationreport.go index c9c02602..a1743192 100644 --- a/attestationreport/validationreport.go +++ b/attestationreport/validationreport.go @@ -34,7 +34,6 @@ type VerificationResult struct { Prover string `json:"prover,omitempty"` // Name of the proving device the report was created for Created string `json:"created,omitempty"` // Timestamp the attestation verification was completed SwCertLevel int `json:"swCertLevel"` // Overall certification level for the software stack - FreshnessCheck Result `json:"freshnessCheck"` // Result for comparison of the expected nonce to the one provided in the attestation report Measurements []MeasurementResult `json:"measurements"` ReportSignature []SignatureResult `json:"reportSignatureCheck"` // Result for validation of the overall report signature MetadataResult @@ -102,12 +101,11 @@ type MeasurementResult struct { SnpResult *SnpResult `json:"snpResult,omitempty"` SgxResult *SgxResult `json:"sgxResult,omitempty"` TdxResult *TdxResult `json:"tdxResult,omitempty"` - SwResult *SwResult `json:"swResult,omitempty"` } type TpmResult struct { - PcrMatch []PcrResult `json:"pcrMatch"` - AggPcrQuoteMatch Result `json:"aggPcrQuoteMatch"` + PcrMatch []DigestResult `json:"pcrMatch"` + AggPcrQuoteMatch Result `json:"aggPcrQuoteMatch"` } type SnpResult struct { @@ -133,26 +131,13 @@ type TdxResult struct { XfamCheck AttributesCheck `json:"xfamCheck"` } -type SwResult struct { - MeasName string `json:"measurementName"` // Name associated with the measurement used for validation - VerName string `json:"referenceValueName"` // Name of the reference value information used for validation -} - -// PcrResult represents the results for the recalculation of a specific PCR. -type PcrResult struct { - Pcr int `json:"pcr"` // Number for the PCR which was validated - Calculated string `json:"calculated,omitempty"` // PCR Digest that was recalculated - Measured string `json:"measured,omitempty"` // PCR Digest from the measurement - Success bool `json:"success"` -} - // DigestResult represents a generic result for a digest that was processed -// during attestation +// during attestation or for an entire PCR type DigestResult struct { Pcr *int `json:"pcr,omitempty"` // Number for the PCR if present (TPM) Name string `json:"name,omitempty"` // Name of the software artifact - Digest string `json:"digest"` // Digest that was processed - Description string `json:"description,omitempty"` // Optional description + Digest string `json:"digest,omitempty"` // Reference Digest + Description string `json:"description,omitempty"` // Optional description, measured PCR in case of PCR result Success bool `json:"success"` // Indicates whether match was found Type string `json:"type,omitempty"` // On fail, indicates whether digest is reference or measurement EventData *EventData `json:"eventdata,omitempty"` // data that was included from bioseventlog @@ -429,6 +414,7 @@ const ( VerifyTCBChain VerifyTcbInfo ExtensionsCheck + PcrNotSpecified ) type Result struct { @@ -705,8 +691,6 @@ func (r *VerificationResult) PrintErr() { log.Warnf("Verification failed with error code: %v", r.ErrorCode) } - r.FreshnessCheck.PrintErr("Report freshness check") - for _, m := range r.Measurements { m.Summary.PrintErr("%v", m.Type) m.Freshness.PrintErr("Measurement freshness check") @@ -724,8 +708,8 @@ func (r *VerificationResult) PrintErr() { m.TpmResult.AggPcrQuoteMatch.PrintErr("Aggregated PCR verification") for _, p := range m.TpmResult.PcrMatch { if !p.Success { - log.Warnf("PCR%v calculated: %v, measured: %v", p.Pcr, p.Calculated, - p.Measured) + log.Warnf("PCR%v calculated: %v, measured: %v", *p.Pcr, p.Digest, + p.Description) } } } diff --git a/cmc/cmc.go b/cmc/cmc.go index 803a743e..b7a233cb 100644 --- a/cmc/cmc.go +++ b/cmc/cmc.go @@ -98,6 +98,7 @@ func NewCmc(c *Config) (*Cmc, error) { Serializer: s, CtrPcr: c.CtrPcr, CtrLog: c.CtrLog, + CtrDriver: c.CtrDriver, UseCtr: c.UseCtr, } diff --git a/example-setup/setup-cmc b/example-setup/setup-cmc index b137e8dc..8e495057 100755 --- a/example-setup/setup-cmc +++ b/example-setup/setup-cmc @@ -37,7 +37,7 @@ mkdir -p "${data}" sudo apt install -y moreutils golang-cfssl build-essential zlib1g-dev libssl-dev jq sudo snap install yq -# Intall tpm-pcr-tools +# Install tpm-pcr-tools git clone https://github.com/Fraunhofer-AISEC/tpm-pcr-tools.git "${data}/tpm-pcr-tools" cd "${data}/tpm-pcr-tools" make diff --git a/generate/generate.go b/generate/generate.go index 1181e389..f48f8a7c 100644 --- a/generate/generate.go +++ b/generate/generate.go @@ -45,7 +45,6 @@ func Generate(nonce []byte, metadata [][]byte, measurers []ar.Driver, s ar.Seria if len(nonce) > 32 { return nil, fmt.Errorf("nonce exceeds maximum length of 32 bytes") } - report.Nonce = nonce log.Debug("Adding manifests and descriptions to Attestation Report..") @@ -98,19 +97,19 @@ func Generate(nonce []byte, metadata [][]byte, measurers []ar.Driver, s ar.Seria log.Debug("Added ", numManifests, " manifests to attestation report") } - log.Tracef("Retrieving measurements from %v measurers", len(measurers)) + log.Debugf("Retrieving measurements from %v measurers", len(measurers)) for _, measurer := range measurers { - // This actually collects the measurements. The methods are implemented - // in the respective module (e.g. tpm module) - log.Trace("Getting measurements from measurement interface..") + // Collect the measurements/evidence with the specified nonce from hardware/software. + // The methods are implemented in the respective driver (TPM, SNP, ...) + log.Debugf("Getting measurements from measurement interface..") measurement, err := measurer.Measure(nonce) if err != nil { return nil, fmt.Errorf("failed to get measurements: %v", err) } report.Measurements = append(report.Measurements, measurement) - log.Tracef("Added %v to attestation report", measurement.Type) + log.Debugf("Added %v to attestation report", measurement.Type) } log.Trace("Finished attestation report generation") diff --git a/ima/ima.go b/ima/ima.go index ab2b57b0..eeb366e9 100644 --- a/ima/ima.go +++ b/ima/ima.go @@ -53,7 +53,7 @@ type imaTemplate struct { // GetImaRuntimeDigests returns all hashes extended by the IMA into the TPM // IMA PCR as read from the sysfs -func GetImaRuntimeDigests() ([]ar.PcrEvent, error) { +func GetImaRuntimeDigests() ([]ar.MeasureEvent, error) { file := "/sys/kernel/security/ima/binary_runtime_measurements" data, err := os.ReadFile(file) if err != nil { @@ -68,10 +68,10 @@ func GetImaRuntimeDigests() ([]ar.PcrEvent, error) { return digests, nil } -func parseImaRuntimeDigests(data []byte) ([]ar.PcrEvent, error) { +func parseImaRuntimeDigests(data []byte) ([]ar.MeasureEvent, error) { buf := bytes.NewBuffer(data) - events := make([]ar.PcrEvent, 0) + events := make([]ar.MeasureEvent, 0) for buf.Len() > 0 { header := header{} @@ -128,7 +128,7 @@ func parseImaRuntimeDigests(data []byte) ([]ar.PcrEvent, error) { log.Tracef("Failed to parse additional template data: %v", err) } - event := ar.PcrEvent{ + event := ar.MeasureEvent{ Sha256: digest[:], EventName: eventName, } diff --git a/measure/measure.go b/measure/measure.go index 15936ab2..83e33131 100644 --- a/measure/measure.go +++ b/measure/measure.go @@ -67,13 +67,16 @@ func Measure(name string, configSha256, rootfsSha256 []byte, mc *MeasureConfig) // Generate config entry entry := MeasureEntry{ - Pcr: mc.Pcr, TemplateSha256: hash, Name: name, ConfigSha256: configSha256, RootfsSha256: rootfsSha256, } + if strings.EqualFold(mc.Driver, "tpm") { + entry.Pcr = mc.Pcr + } + log.Tracef("Recording measurement %v: %v", name, hex.EncodeToString(entry.TemplateSha256)) // Read the existing measurement list if it already exists @@ -119,6 +122,7 @@ func Measure(name string, configSha256, rootfsSha256 []byte, mc *MeasureConfig) return fmt.Errorf("failed to write measurement list: %w", err) } + // Write the entry to the specified driver, if any if strings.EqualFold(mc.Driver, "tpm") { addr, err := getTpmAddr() if err != nil { @@ -137,6 +141,10 @@ func Measure(name string, configSha256, rootfsSha256 []byte, mc *MeasureConfig) } log.Tracef("Recorded measurement %v into PCR%v", name, mc.Pcr) + } else if strings.EqualFold(mc.Driver, "sw") { + log.Tracef("Nothing to do for SW driver") + } else { + return fmt.Errorf("unknown driver '%v'", mc.Driver) } return nil diff --git a/sgxdriver/sgxdriver.go b/sgxdriver/sgxdriver.go index f1676395..2876a133 100644 --- a/sgxdriver/sgxdriver.go +++ b/sgxdriver/sgxdriver.go @@ -107,6 +107,8 @@ func (sgx *Sgx) Init(c *ar.DriverConfig) error { // as a plugin during attestation report generation func (sgx *Sgx) Measure(nonce []byte) (ar.Measurement, error) { + log.Trace("Collecting SGX measurements") + if sgx == nil { return ar.Measurement{}, errors.New("internal error: SGX object is nil") } diff --git a/snpdriver/snpdriver.go b/snpdriver/snpdriver.go index 3d8e0632..7064f01a 100644 --- a/snpdriver/snpdriver.go +++ b/snpdriver/snpdriver.go @@ -139,6 +139,8 @@ func (snp *Snp) Init(c *ar.DriverConfig) error { // as a plugin during attestation report generation func (snp *Snp) Measure(nonce []byte) (ar.Measurement, error) { + log.Trace("Collecting SNP measurements") + if snp == nil { return ar.Measurement{}, errors.New("internal error: SNP object is nil") } diff --git a/swdriver/swdriver.go b/swdriver/swdriver.go index 2c332736..d1729aca 100644 --- a/swdriver/swdriver.go +++ b/swdriver/swdriver.go @@ -21,11 +21,16 @@ import ( "crypto/elliptic" "crypto/rand" "crypto/x509" + "encoding/base64" "errors" "fmt" + "os" + "strings" ar "github.com/Fraunhofer-AISEC/cmc/attestationreport" est "github.com/Fraunhofer-AISEC/cmc/est/estclient" + "github.com/Fraunhofer-AISEC/cmc/internal" + m "github.com/Fraunhofer-AISEC/cmc/measure" "github.com/sirupsen/logrus" ) @@ -36,8 +41,12 @@ var ( // Sw is a struct required for implementing the signer and measurer interfaces // of the attestation report to perform software measurements and signing type Sw struct { - certChain []*x509.Certificate - priv crypto.PrivateKey + certChain []*x509.Certificate + priv crypto.PrivateKey + useCtr bool + ctrPcr int + ctrLog string + serializer ar.Serializer } // Init a new object for software-based signing @@ -68,6 +77,11 @@ func (s *Sw) Init(c *ar.DriverConfig) error { return fmt.Errorf("failed to get signing cert chain: %w", err) } + s.useCtr = c.UseCtr && strings.EqualFold(c.CtrDriver, "sw") + s.ctrLog = c.CtrLog + s.ctrPcr = c.CtrPcr + s.serializer = c.Serializer + return nil } @@ -101,7 +115,64 @@ func (s *Sw) GetCertChain() ([]*x509.Certificate, error) { } func (s *Sw) Measure(nonce []byte) (ar.Measurement, error) { - return ar.Measurement{}, errors.New("Measure method not implemented for SW driver") + + log.Trace("Collecting SW measurements") + + if !s.useCtr { + return ar.Measurement{}, errors.New("sw driver specified but use containers equals false") + } + + // For the swdriver, the evidence is simply the signed nonce + evidence, err := s.serializer.Sign(nonce, s) + if err != nil { + return ar.Measurement{}, fmt.Errorf("failed to sign sw evidence: %w", err) + } + + log.Tracef("Reading container measurements") + if _, err := os.Stat(s.ctrLog); err != nil { + log.Trace("No container measurements to read") + return ar.Measurement{Type: "SW Measurement"}, nil + } + + // If CMC container measurements are used, add the list of executed containers + data, err := os.ReadFile(s.ctrLog) + if err != nil { + return ar.Measurement{}, fmt.Errorf("failed to read container measurements: %w", err) + } + + var measureList []m.MeasureEntry + err = s.serializer.Unmarshal(data, &measureList) + if err != nil { + return ar.Measurement{}, fmt.Errorf("failed to unmarshal measurement list: %w", err) + } + + dm := ar.Artifact{ + Type: "SW Eventlog", + } + + for _, ml := range measureList { + log.Tracef("Adding %v container measurement", ml.Name) + event := ar.MeasureEvent{ + Sha256: ml.TemplateSha256, + EventName: ml.Name, + CtrData: &ar.CtrData{ + ConfigSha256: ml.ConfigSha256, + RootfsSha256: ml.RootfsSha256, + }, + } + dm.Events = append(dm.Events, event) + } + + m := ar.Measurement{ + Type: "SW Measurement", + Evidence: evidence, + Artifacts: []ar.Artifact{dm}, + Certs: internal.WriteCertsDer(s.certChain), + } + + log.Warnf("EVI: %v", base64.StdEncoding.EncodeToString(evidence)) + + return m, nil } func getSigningCertChain(priv crypto.PrivateKey, s ar.Serializer, metadata [][]byte, diff --git a/tpmdriver/tpmdriver.go b/tpmdriver/tpmdriver.go index c29efc78..9c88c1ef 100644 --- a/tpmdriver/tpmdriver.go +++ b/tpmdriver/tpmdriver.go @@ -182,7 +182,7 @@ func (t *Tpm) Init(c *ar.DriverConfig) error { t.MeasuringCerts = akchain t.MeasurementLog = c.MeasurementLog t.Serializer = c.Serializer - t.UseCtr = c.UseCtr + t.UseCtr = c.UseCtr && strings.EqualFold(c.CtrDriver, "tpm") t.CtrLog = c.CtrLog t.CtrPcr = c.CtrPcr @@ -193,6 +193,8 @@ func (t *Tpm) Init(c *ar.DriverConfig) error { // as a plugin during attestation report generation func (t *Tpm) Measure(nonce []byte) (ar.Measurement, error) { + log.Trace("Collecting TPM measurements") + if t == nil { return ar.Measurement{}, fmt.Errorf("internal error: tpm object not initialized") } @@ -223,16 +225,16 @@ func (t *Tpm) Measure(nonce []byte) (ar.Measurement, error) { log.Tracef("Collected %v binary bios measurements", len(biosMeasurements)) } - hashChain := make([]ar.PcrMeasurement, len(t.Pcrs)) + hashChain := make([]ar.Artifact, len(t.Pcrs)) for i, num := range t.Pcrs { - events := make([]ar.PcrEvent, 0) + events := make([]ar.MeasureEvent, 0) // Collect detailed measurements from event logs if specified if t.MeasurementLog { for _, digest := range biosMeasurements { if num == *digest.Pcr { - event := ar.PcrEvent{ + event := ar.MeasureEvent{ Sha256: digest.Sha256, EventName: digest.Name, } @@ -246,9 +248,10 @@ func (t *Tpm) Measure(nonce []byte) (ar.Measurement, error) { } } - pcrMeasurement := ar.PcrMeasurement{ - Pcr: num, - } + pcrMeasurement := ar.Artifact{} + pcrMeasurement.Pcr = new(int) + *pcrMeasurement.Pcr = num + if t.MeasurementLog { pcrMeasurement.Type = "PCR Eventlog" pcrMeasurement.Events = events @@ -271,9 +274,12 @@ func (t *Tpm) Measure(nonce []byte) (ar.Measurement, error) { // Find the IMA PCR in the TPM Measurement for i := range hashChain { - if hashChain[i].Pcr == t.ImaPcr { + if hashChain[i].Pcr == nil { + return ar.Measurement{}, errors.New("internal error: pcr is nil") + } + if *hashChain[i].Pcr == t.ImaPcr { log.Tracef("Adding %v IMA events to PCR%v measurement", len(imaEvents), - hashChain[i].Pcr) + *hashChain[i].Pcr) hashChain[i].Events = imaEvents hashChain[i].Summary = nil hashChain[i].Type = "PCR Eventlog" @@ -297,14 +303,17 @@ func (t *Tpm) Measure(nonce []byte) (ar.Measurement, error) { } for i := range hashChain { - if hashChain[i].Pcr == t.CtrPcr { + if hashChain[i].Pcr == nil { + return ar.Measurement{}, errors.New("internal error: pcr is nil") + } + if *hashChain[i].Pcr == t.CtrPcr { log.Tracef("Adding %v container events to PCR%v measurement", len(measureList), - hashChain[i].Pcr) + *hashChain[i].Pcr) hashChain[i].Summary = nil hashChain[i].Type = "PCR Eventlog" for _, ml := range measureList { log.Tracef("Adding %v container measurement", ml.Name) - event := ar.PcrEvent{ + event := ar.MeasureEvent{ Sha256: ml.TemplateSha256, EventName: ml.Name, CtrData: &ar.CtrData{ @@ -328,15 +337,15 @@ func (t *Tpm) Measure(nonce []byte) (ar.Measurement, error) { Evidence: quote.Quote, Signature: quote.Signature, Certs: internal.WriteCertsDer(t.MeasuringCerts), - Pcrs: hashChain, + Artifacts: hashChain, } - for _, elem := range tm.Pcrs { + for _, elem := range tm.Artifacts { if elem.Type == "PCR Summary" { - log.Tracef("PCR%v: %v", elem.Pcr, hex.EncodeToString(elem.Summary)) + log.Tracef("PCR%v: %v", *elem.Pcr, hex.EncodeToString(elem.Summary)) } else if elem.Type == "PCR Eventlog" { for _, event := range elem.Events { - log.Tracef("PCR%v Measured Event: %v", elem.Pcr, hex.EncodeToString(event.Sha256)) + log.Tracef("PCR%v Measured Event: %v", *elem.Pcr, hex.EncodeToString(event.Sha256)) } } } diff --git a/verify/iat.go b/verify/iat.go index ccb59413..e2ff951b 100644 --- a/verify/iat.go +++ b/verify/iat.go @@ -48,8 +48,8 @@ type Iat struct { Vsi string `cbor:"-75010,keyasint,omitempty"` } -func verifyIasMeasurements(iasM ar.Measurement, nonce []byte, referenceValues []ar.ReferenceValue, - cas []*x509.Certificate, +func verifyIasMeasurements(iasM ar.Measurement, nonce []byte, cas []*x509.Certificate, + referenceValues []ar.ReferenceValue, ) (*ar.MeasurementResult, bool) { result := &ar.MeasurementResult{ diff --git a/verify/iat_test.go b/verify/iat_test.go index 0d9f5361..f89008c9 100644 --- a/verify/iat_test.go +++ b/verify/iat_test.go @@ -144,7 +144,7 @@ func Test_verifyIasMeasurements(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - _, got := verifyIasMeasurements(*tt.args.IasM, tt.args.nonce, tt.args.referenceValues, []*x509.Certificate{tt.args.ca}) + _, got := verifyIasMeasurements(*tt.args.IasM, tt.args.nonce, []*x509.Certificate{tt.args.ca}, tt.args.referenceValues) if got != tt.want { t.Errorf("verifyIasMeasurements() error = %v, wantErr %v", got, tt.want) return diff --git a/verify/sgx.go b/verify/sgx.go index 89c06991..f3165071 100644 --- a/verify/sgx.go +++ b/verify/sgx.go @@ -135,7 +135,7 @@ func verifySgxMeasurements(sgxM ar.Measurement, nonce []byte, intelCache string, return result, false } - // Compare Nonce for Freshness (called Report Data in the SNP Attestation Report Structure) + // Compare nonce for freshness (called report data in the SNP attestation report structure) // ReportData contains: nonce in ReportData field nonce64 := make([]byte, 64) copy(nonce64, nonce[:]) diff --git a/verify/snp.go b/verify/snp.go index 85b81175..4d503d54 100644 --- a/verify/snp.go +++ b/verify/snp.go @@ -127,7 +127,7 @@ func verifySnpMeasurements(snpM ar.Measurement, nonce []byte, referenceValues [] return result, false } - // Compare Nonce for Freshness (called Report Data in the SNP Attestation Report Structure) + // Compare nonce for freshness (called report data in the SNP attestation report structure) nonce64 := make([]byte, 64) copy(nonce64, nonce) if cmp := bytes.Compare(s.ReportData[:], nonce64); cmp != 0 { diff --git a/verify/sw.go b/verify/sw.go new file mode 100644 index 00000000..19bb2ad0 --- /dev/null +++ b/verify/sw.go @@ -0,0 +1,123 @@ +// Copyright (c) 2021 Fraunhofer AISEC +// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package verify + +import ( + "bytes" + "crypto/x509" + "encoding/hex" + "strings" + + ar "github.com/Fraunhofer-AISEC/cmc/attestationreport" +) + +func verifySwMeasurements(swMeasurement ar.Measurement, nonce []byte, cas []*x509.Certificate, + s ar.Serializer, refVals []ar.ReferenceValue) (*ar.MeasurementResult, bool, +) { + + log.Trace("Verifying SW measurements") + + result := &ar.MeasurementResult{ + Type: "SW Result", + } + ok := true + + // Verify signature and extract evidence, which is just the nonce for the sw driver + tr, evidenceNonce, ok := s.VerifyToken(swMeasurement.Evidence, cas) + if !ok { + log.Tracef("Failed to verify sw evidence") + result.Summary.SetErr(ar.ParseEvidence) + return result, false + } + result.Signature = tr.SignatureCheck[0] + + // Verify nonce + if res := bytes.Compare(evidenceNonce, nonce); res != 0 { + log.Tracef("Nonces mismatch: supplied nonce: %v, report nonce = %v", + hex.EncodeToString(nonce), hex.EncodeToString(evidenceNonce)) + ok = false + result.Freshness.Success = false + result.Freshness.Expected = hex.EncodeToString(evidenceNonce) + result.Freshness.Got = hex.EncodeToString(nonce) + } else { + result.Freshness.Success = true + } + + // Check that reference values are reflected by mandatory measurements + for _, v := range refVals { + found := false + for _, swm := range swMeasurement.Artifacts { + for _, event := range swm.Events { + if bytes.Equal(event.Sha256, v.Sha256) { + found = true + break + } + } + if found { + break + } + } + if !found && !v.Optional { + log.Tracef("no SW Measurement found for SW Reference Value %v (hash: %v)", v.Name, hex.EncodeToString(v.Sha256)) + r := ar.DigestResult{ + Type: "Reference Value", + Success: false, + Name: v.Name, + Digest: hex.EncodeToString(v.Sha256), + } + result.Artifacts = append(result.Artifacts, r) + ok = false + } + } + + // Check that every measurement is reflected by a reference value + for _, swm := range swMeasurement.Artifacts { + for _, event := range swm.Events { + found := false + for _, ref := range refVals { + if bytes.Equal(event.Sha256, ref.Sha256) { + found = true + nameInfo := ref.Name + if event.EventName != "" && !strings.EqualFold(ref.Name, event.EventName) { + nameInfo += ": " + event.EventName + } + r := ar.DigestResult{ + Success: true, + Name: nameInfo, + Digest: hex.EncodeToString(event.Sha256), + } + result.Artifacts = append(result.Artifacts, r) + break + } + } + if !found { + r := ar.DigestResult{ + Type: "Measurement", + Success: false, + Name: event.EventName, + Digest: hex.EncodeToString(event.Sha256), + } + result.Artifacts = append(result.Artifacts, r) + log.Tracef("no SW Reference Value found for SW Measurement: %v", hex.EncodeToString(event.Sha256)) + ok = false + } + } + } + + result.Summary.Success = ok + + return result, ok +} diff --git a/verify/sw_test.go b/verify/sw_test.go new file mode 100644 index 00000000..90be4f5f --- /dev/null +++ b/verify/sw_test.go @@ -0,0 +1,250 @@ +// Copyright (c) 2021 Fraunhofer AISEC +// Fraunhofer-Gesellschaft zur Foerderung der angewandten Forschung e.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package verify + +import ( + "crypto/x509" + "encoding/base64" + "testing" + + ar "github.com/Fraunhofer-AISEC/cmc/attestationreport" +) + +func Test_verifySwMeasurements(t *testing.T) { + type args struct { + swMeasurement ar.Measurement + nonce []byte + cas []*x509.Certificate + s ar.Serializer + refVals []ar.ReferenceValue + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "Valid SW Measurement", + args: args{ + swMeasurement: ar.Measurement{ + Type: "SW Measurement", + Evidence: validSwEvidence, + Certs: validSwCertChain, + Artifacts: validSwArtifacts, + }, + nonce: validSwNonce, + cas: []*x509.Certificate{validSwCa}, + s: ar.JsonSerializer{}, + refVals: validSwRefVals, + }, + want: true, + }, + { + name: "Invalid Nonce", + args: args{ + swMeasurement: ar.Measurement{ + Type: "SW Measurement", + Evidence: validSwEvidence, + Certs: validSwCertChain, + Artifacts: validSwArtifacts, + }, + nonce: invalidSwNonce, + cas: []*x509.Certificate{validSwCa}, + s: ar.JsonSerializer{}, + refVals: validSwRefVals, + }, + want: false, + }, + { + name: "Invalid Refvals", + args: args{ + swMeasurement: ar.Measurement{ + Type: "SW Measurement", + Evidence: validSwEvidence, + Certs: validSwCertChain, + Artifacts: validSwArtifacts, + }, + nonce: validSwNonce, + cas: []*x509.Certificate{validSwCa}, + s: ar.JsonSerializer{}, + refVals: invalidSwRefVals, + }, + want: false, + }, + { + name: "Invalid Artifacts", + args: args{ + swMeasurement: ar.Measurement{ + Type: "SW Measurement", + Evidence: validSwEvidence, + Certs: validSwCertChain, + Artifacts: invalidSwArtifacts, + }, + nonce: validSwNonce, + cas: []*x509.Certificate{validSwCa}, + s: ar.JsonSerializer{}, + refVals: validSwRefVals, + }, + want: false, + }, + { + name: "Invalid Evidence", + args: args{ + swMeasurement: ar.Measurement{ + Type: "SW Measurement", + Evidence: invalidSwEvidence, + Certs: validSwCertChain, + Artifacts: invalidSwArtifacts, + }, + nonce: validSwNonce, + cas: []*x509.Certificate{validSwCa}, + s: ar.JsonSerializer{}, + refVals: validSwRefVals, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, got := verifySwMeasurements(tt.args.swMeasurement, tt.args.nonce, tt.args.cas, tt.args.s, tt.args.refVals) + if got != tt.want { + t.Errorf("verifySwMeasurements() got = %v, want %v", got, tt.want) + } + }) + } +} + +var ( + validSwArtifacts = []ar.Artifact{ + { + Type: "SW Eventlog", + Events: []ar.MeasureEvent{ + { + Sha256: dec("6dc7a115a4c4a4f02b49cb0866d8f63f33e86cbf933238276411a8f26961e9a6"), + EventName: "07f475b29a92233363d27544683b5ea04f54ca514d742ab5a0b41bdb5914d4b", + CtrData: &ar.CtrData{ + ConfigSha256: dec("333c809d8ed04b01af781b964dd05fc888aea04732edcaff9d940757d2e48be8"), + RootfsSha256: dec("44563228698774cb66bf3fe1432ab3c8ea1adfffd9d6d388bc40d73a68cb533c"), + }, + }, + { + Sha256: dec("715ed62e8fb85decea88ffee33274245d2924e2e961b3cc95be6e09a6ae5a25d"), + EventName: "e4c079503bff631a3433bf34965eb432ecdd6793654a4b83433fa9981ad1833f", + CtrData: &ar.CtrData{ + ConfigSha256: dec("362f6c2fbe2eedb86c96cdd0584e28ccbf1542edb2ec3d52ab1dc5151dfacfd0"), + RootfsSha256: dec("3310d0d0902bc4e5c6c3077910ecd85e303762331fdb5d2f899c30263a5a50c7"), + }, + }, + }, + }, + } + + invalidSwArtifacts = []ar.Artifact{ + { + Type: "SW Eventlog", + Events: []ar.MeasureEvent{ + { + Sha256: dec("6dc7a115a4c4a4f02b49cb0866d8f63f33e86cbf933238276411a8f26961e9ff"), + EventName: "07f475b29a92233363d27544683b5ea04f54ca514d742ab5a0b41bdb5914d4b", + CtrData: &ar.CtrData{ + ConfigSha256: dec("333c809d8ed04b01af781b964dd05fc888aea04732edcaff9d940757d2e48be8"), + RootfsSha256: dec("44563228698774cb66bf3fe1432ab3c8ea1adfffd9d6d388bc40d73a68cb533c"), + }, + }, + { + Sha256: dec("715ed62e8fb85decea88ffee33274245d2924e2e961b3cc95be6e09a6ae5a25d"), + EventName: "e4c079503bff631a3433bf34965eb432ecdd6793654a4b83433fa9981ad1833f", + CtrData: &ar.CtrData{ + ConfigSha256: dec("362f6c2fbe2eedb86c96cdd0584e28ccbf1542edb2ec3d52ab1dc5151dfacfd0"), + RootfsSha256: dec("3310d0d0902bc4e5c6c3077910ecd85e303762331fdb5d2f899c30263a5a50c7"), + }, + }, + }, + }, + } + + validSwRefVals = []ar.ReferenceValue{ + { + Type: "SW Reference Value", + Sha256: dec("715ed62e8fb85decea88ffee33274245d2924e2e961b3cc95be6e09a6ae5a25d"), + Name: "OCI Runtime Bundle Digest: tenzir", + Optional: true, + }, + { + Type: "SW Reference Value", + Sha256: dec("6dc7a115a4c4a4f02b49cb0866d8f63f33e86cbf933238276411a8f26961e9a6"), + Name: "OCI Runtime Bundle Digest: rtkcsm", + Optional: true, + }, + } + + invalidSwRefVals = []ar.ReferenceValue{ + { + Type: "SW Reference Value", + Sha256: dec("ff5ed62e8fb85decea88ffee33274245d2924e2e961b3cc95be6e09a6ae5a25d"), + Name: "OCI Runtime Bundle Digest: tenzir", + Optional: true, + }, + { + Type: "SW Reference Value", + Sha256: dec("6dc7a115a4c4a4f02b49cb0866d8f63f33e86cbf933238276411a8f26961e9a6"), + Name: "OCI Runtime Bundle Digest: rtkcsm", + Optional: true, + }, + } + + validSwNonce = dec("89021a63554e0e58") + + invalidSwNonce = dec("ff021a63554e0e58") + + validSwAk = conv([]byte(`-----BEGIN CERTIFICATE----- +MIICuzCCAmGgAwIBAgIQE3yJJSK718VSl4UNgIqNpzAKBggqhkjOPQQDAjBhMQsw +CQYDVQQGEwJERTESMBAGA1UEBxMJVGVzdCBDaXR5MRUwEwYDVQQKEwxUZXN0IENv +bXBhbnkxEDAOBgNVBAsTB1Jvb3QgQ0ExFTATBgNVBAMTDFRlc3QgUm9vdCBDQTAe +Fw0yNDA4MDYxODAwMzZaFw0yNTAyMDIxODAwMzZaMIGdMQswCQYDVQQGEwJERTEL +MAkGA1UECBMCQlkxDzANBgNVBAcTBk11bmljaDEWMBQGA1UECRMNdGVzdHN0cmVl +dCAxNjEOMAwGA1UEERMFODU3NDgxGjAYBgNVBAoTEVRlc3Qgb3JnYW5pemF0aW9u +MQ8wDQYDVQQLEwZkZXZpY2UxGzAZBgNVBAMTEmRlLnRlc3QuaWsuZGV2aWNlMDBZ +MBMGByqGSM49AgEGCCqGSM49AwEHA0IABPFIprOMyXRjShtt88Mi6dPatlNRlhcv +zQaCuHrMTudDqGBRKZi1e6VAXh7+Ct7B3VDmxGmDjo3QrINCwYZs1S+jgb0wgbow +DgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAM +BgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTTjuK3G2wMFJnszTfAG+lIbTJqdDAfBgNV +HSMEGDAWgBRGAy2mnhOO91nZGITXWKGTsHPJOTA7BgNVHREENDAyggkxMjcuMC4w +LjGCCWxvY2FsaG9zdIIabnVjLTAyLmFpc2VjLmZyYXVuaG9mZXIuZGUwCgYIKoZI +zj0EAwIDSAAwRQIgDEB9QmPWFyyiOO0qa236LEBqG2nJvgAN3uxJMN0El+wCIQDH +x/0jlApG+q4X3ot7AbvKXjgJA9B3wxGg9+QZhbKA2A== +-----END CERTIFICATE-----`)) + + validSwCa = conv([]byte(`-----BEGIN CERTIFICATE----- +MIICCDCCAa6gAwIBAgIUEImN1VPFfruKHN7uf2rR+AbdDFMwCgYIKoZIzj0EAwIw +YTELMAkGA1UEBhMCREUxEjAQBgNVBAcTCVRlc3QgQ2l0eTEVMBMGA1UEChMMVGVz +dCBDb21wYW55MRAwDgYDVQQLEwdSb290IENBMRUwEwYDVQQDEwxUZXN0IFJvb3Qg +Q0EwIBcNMjQwNjI4MTEwNTAwWhgPMjEyNDA2MDQxMTA1MDBaMGExCzAJBgNVBAYT +AkRFMRIwEAYDVQQHEwlUZXN0IENpdHkxFTATBgNVBAoTDFRlc3QgQ29tcGFueTEQ +MA4GA1UECxMHUm9vdCBDQTEVMBMGA1UEAxMMVGVzdCBSb290IENBMFkwEwYHKoZI +zj0CAQYIKoZIzj0DAQcDQgAEXTn3MfowwWQ8vb/MUgHGMitmPly9OW04xmP0FPHS +JfM0/WJBKOIpdPx01GhKD3hgyw1kd8LIqvDx5scb1JU2iqNCMEAwDgYDVR0PAQH/ +BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFEYDLaaeE473WdkYhNdY +oZOwc8k5MAoGCCqGSM49BAMCA0gAMEUCICDteP7WnMoCjgaVkeZzFx1uug2h/Z8G +6HhWfMozwVpaAiEAuOyzkVIveBYCN4wG0WfZZqhyeRd0jwwRwUWWg8KKhyg= +-----END CERTIFICATE-----`)) + + validSwCertChain = [][]byte{validSwAk.Raw, validSwCa.Raw} + + validSwEvidence, _ = base64.StdEncoding.DecodeString("eyJwYXlsb2FkIjoiaVFJYVkxVk9EbGciLCJwcm90ZWN0ZWQiOiJleUpoYkdjaU9pSkZVekkxTmlJc0luZzFZeUk2V3lKTlNVbERkWHBEUTBGdFIyZEJkMGxDUVdkSlVVVXplVXBLVTBzM01UaFdVMncwVlU1blNYRk9jSHBCUzBKblozRm9hMnBQVUZGUlJFRnFRbWhOVVhOM1ExRlpSRlpSVVVkRmQwcEZVbFJGVTAxQ1FVZEJNVlZGUW5oTlNsWkhWbnBrUTBKRVlWaFNOVTFTVlhkRmQxbEVWbEZSUzBWM2VGVmFXRTR3U1VWT2RtSllRbWhpYm10NFJVUkJUMEpuVGxaQ1FYTlVRakZLZG1JelVXZFJNRVY0UmxSQlZFSm5UbFpDUVUxVVJFWlNiR016VVdkVmJUbDJaRU5DUkZGVVFXVkdkekI1VGtSQk5FMUVXWGhQUkVGM1RYcGFZVVozTUhsT1ZFRjVUVVJKZUU5RVFYZE5lbHBoVFVsSFpFMVJjM2REVVZsRVZsRlJSMFYzU2tWU1ZFVk1UVUZyUjBFeFZVVkRRazFEVVd4cmVFUjZRVTVDWjA1V1FrRmpWRUpyTVRGaWJXeHFZVVJGVjAxQ1VVZEJNVlZGUTFKTlRtUkhWbnBrU0U0d1kyMVdiR1JEUVhoT2FrVlBUVUYzUjBFeFZVVkZVazFHVDBSVk0wNUVaM2hIYWtGWlFtZE9Wa0pCYjFSRlZsSnNZek5SWjJJelNtNVpWelZ3WlcxR01HRlhPWFZOVVRoM1JGRlpSRlpSVVV4RmQxcHJXbGhhY0ZreVZYaEhla0ZhUW1kT1ZrSkJUVlJGYlZKc1RHNVNiR016VVhWaFYzTjFXa2RXTW1GWFRteE5SRUphVFVKTlIwSjVjVWRUVFRRNVFXZEZSME5EY1VkVFRUUTVRWGRGU0VFd1NVRkNVRVpKY0hKUFRYbFlVbXBUYUhSME9EaE5hVFprVUdGMGJFNVNiR2hqZG5wUllVTjFTSEpOVkhWa1JIRkhRbEpMV21reFpUWldRVmhvTnl0RGREZENNMVpFYlhoSGJVUnFiek5SY2tsT1EzZFpXbk14VXl0cVoySXdkMmRpYjNkRVoxbEVWbEl3VUVGUlNDOUNRVkZFUVdkWFowMUNNRWRCTVZWa1NsRlJWMDFDVVVkRFEzTkhRVkZWUmtKM1RVSkNaMmR5UW1kRlJrSlJZMFJCYWtGTlFtZE9Wa2hTVFVKQlpqaEZRV3BCUVUxQ01FZEJNVlZrUkdkUlYwSkNWRlJxZFVzelJ6SjNUVVpLYm5ONlZHWkJSeXRzU1dKVVNuRmtSRUZtUW1kT1ZraFRUVVZIUkVGWFowSlNSMEY1TW0xdWFFOVBPVEZ1V2tkSlZGaFhTMGRVYzBoUVNrOVVRVGRDWjA1V1NGSkZSVTVFUVhsbloydDRUV3BqZFUxRE5IZE1ha2REUTFkNGRsa3lSbk5oUnpsNlpFbEpZV0p1Vm1wTVZFRjVURzFHY0dNeVZtcE1iVnA1V1ZoV2RXRkhPVzFhV0VsMVdrZFZkME5uV1VsTGIxcEplbW93UlVGM1NVUlRRVUYzVWxGSlowUkZRamxSYlZCWFJubDVhVTlQTUhGaE1qTTJURVZDY1VjeWJrcDJaMEZPTTNWNFNrMU9NRVZzSzNkRFNWRkVTSGd2TUdwc1FYQkhLM0UwV0ROdmREZEJZblpMV0dwblNrRTVRak4zZUVkbk9TdFJXbWhpUzBFeVFUMDlJaXdpVFVsSlEwTkVRME5CWVRablFYZEpRa0ZuU1ZWRlNXMU9NVlpRUm1aeWRVdElUamQxWmpKeVVpdEJZbVJFUmsxM1EyZFpTVXR2V2tsNmFqQkZRWGRKZDFsVVJVeE5RV3RIUVRGVlJVSm9UVU5TUlZWNFJXcEJVVUpuVGxaQ1FXTlVRMVpTYkdNelVXZFJNbXd3WlZSRlZrMUNUVWRCTVZWRlEyaE5UVlpIVm5wa1EwSkVZakl4ZDFsWE5UVk5Va0YzUkdkWlJGWlJVVXhGZDJSVFlqSTVNRWxGVGtKTlVsVjNSWGRaUkZaUlVVUkZkM2hWV2xoT01FbEdTblppTTFGblVUQkZkMGxDWTA1TmFsRjNUbXBKTkUxVVJYZE9WRUYzVjJoblVFMXFSWGxPUkVFeVRVUlJlRTFVUVRGTlJFSmhUVWRGZUVONlFVcENaMDVXUWtGWlZFRnJVa1pOVWtsM1JVRlpSRlpSVVVoRmQyeFZXbGhPTUVsRlRuQmtTR3Q0UmxSQlZFSm5UbFpDUVc5VVJFWlNiR016VVdkUk1qbDBZMGRHZFdWVVJWRk5RVFJIUVRGVlJVTjRUVWhWYlRsMlpFTkNSRkZVUlZaTlFrMUhRVEZWUlVGNFRVMVdSMVo2WkVOQ1UySXlPVEJKUlU1Q1RVWnJkMFYzV1VoTGIxcEplbW93UTBGUldVbExiMXBKZW1vd1JFRlJZMFJSWjBGRldGUnVNMDFtYjNkM1YxRTRkbUl2VFZWblNFZE5hWFJ0VUd4NU9VOVhNRFI0YlZBd1JsQklVMHBtVFRBdlYwcENTMDlKY0dSUWVEQXhSMmhMUkROb1ozbDNNV3RrT0V4SmNYWkVlRFZ6WTJJeFNsVXlhWEZPUTAxRlFYZEVaMWxFVmxJd1VFRlJTQzlDUVZGRVFXZEZSMDFCT0VkQk1WVmtSWGRGUWk5M1VVWk5RVTFDUVdZNGQwaFJXVVJXVWpCUFFrSlpSVVpGV1VSTVlXRmxSVFEzTTFka2ExbG9UbVJaYjFwUGQyTTRhelZOUVc5SFEwTnhSMU5OTkRsQ1FVMURRVEJuUVUxRlZVTkpRMFIwWlZBM1YyNU5iME5xWjJGV2EyVmFla1o0TVhWMVp6Sm9MMW80UnpaSWFGZG1UVzk2ZDFad1lVRnBSVUYxVDNsNmExWkpkbVZDV1VOT05IZEhNRmRtV2xweGFIbGxVbVF3YW5kM1VuZFZWMWRuT0V0TGFIbG5QU0pkZlEiLCJzaWduYXR1cmUiOiJ3TFpuNmJ4VTJJdXBhaWdBcmVSVTBoSkxsWURkaFlTSlpUeUprRkJmTjdPVkJWWFZyOUhSWjBLUHhTV0dQQU1TaU00b3dKZGZlSVMtMnBDWVNRUzNmZyJ9") + + invalidSwEvidence, _ = base64.StdEncoding.DecodeString("eyJwYXlsb2FkIjoiMktQejBsNUpTNjAiLCJwcm90ZWN0ZWQiOiJleUpoYkdjaU9pSkZVekkxTmlJc0luZzFZeUk2V3lKTlNVbERka1JEUTBGdFMyZEJkMGxDUVdkSlVrRkxka1ZtTmt0YVQwMUxVbEJsU2s4MlIxVkxiazlKZDBObldVbExiMXBKZW1vd1JVRjNTWGRaVkVWTVRVRnJSMEV4VlVWQ2FFMURVa1ZWZUVWcVFWRkNaMDVXUWtGalZFTldVbXhqTTFGblVUSnNNR1ZVUlZaTlFrMUhRVEZWUlVOb1RVMVdSMVo2WkVOQ1JHSXlNWGRaVnpVMVRWSkJkMFJuV1VSV1VWRk1SWGRrVTJJeU9UQkpSVTVDVFZKVmQwVjNXVVJXVVZGRVJYZDRWVnBZVGpCSlJrcDJZak5SWjFFd1JYZElhR05PVFdwUmQwOUVRVEpOVkdjeFRtcEZkMWRvWTA1TmFsVjNUV3BCZVUxVVp6Rk9ha1YzVjJwRFFtNVVSVXhOUVd0SFFURlZSVUpvVFVOU1JWVjRRM3BCU2tKblRsWkNRV2RVUVd0S1drMVJPSGRFVVZsRVZsRlJTRVYzV2s1a1Z6VndXVEpuZUVacVFWVkNaMDVXUWtGclZFUllVbXhqTTFKNlpFaEtiRnBZVVdkTlZGbDRSR3BCVFVKblRsWkNRa1ZVUWxSbk1VNTZVVFJOVW05M1IwRlpSRlpSVVV0RmVFWlZXbGhPTUVsSE9YbGFNa1oxWVZod2FHUkhiSFppYWtWUVRVRXdSMEV4VlVWRGVFMUhXa2RXTW1GWFRteE5Vbk4zUjFGWlJGWlJVVVJGZUVwcldsTTFNRnBZVGpCTWJXeHlURzFTYkdSdGJHcGFWRUYzVjFSQlZFSm5ZM0ZvYTJwUFVGRkpRa0puWjNGb2EycFBVRkZOUWtKM1RrTkJRVlJqTW01QlpXVm1aMVpqV0hkUVdFMDROVGN5UmpWTlNGRlNNVWxsYVhaeVFXRlRVRkZPVWxvNGExTXdjV0p3TkRsd2NVNTFUVUpoUjJzMU5FdFhNR1p0U2pVMVExb3JVbVJOTjI4MGMwWmhVVWRVVHpVM2J6UkhPVTFKUnpaTlFUUkhRVEZWWkVSM1JVSXZkMUZGUVhkSlJtOUVRV1JDWjA1V1NGTlZSVVpxUVZWQ1oyZHlRbWRGUmtKUlkwUkJVVmxKUzNkWlFrSlJWVWhCZDBsM1JFRlpSRlpTTUZSQlVVZ3ZRa0ZKZDBGRVFXUkNaMDVXU0ZFMFJVWm5VVlZ4Um1acE5rbEljbmRCWlhOaldVMTJjR2QyTkVwUGFFbzNkbXQzU0hkWlJGWlNNR3BDUW1kM1JtOUJWVkpuVFhSd2NEUlVhblprV2pKU2FVVXhNV2xvYXpkQ2VubFVhM2RQZDFsRVZsSXdVa0pFVVhkTmIwbEtUVlJKTTB4cVFYVk5RelI0WjJkc2MySXlUbWhpUjJoMll6TlRRMGR0TlRGWmVUQjNUV2sxYUdGWVRteFplVFZ0WTIxR01XSnRhSFphYlZaNVRHMVNiRTFCYjBkRFEzRkhVMDAwT1VKQlRVTkJNR2RCVFVWVlEwbFJSSG96WjBRMmJuWlZXbTQzTWtRdlVHTTBNR1ZoTW5kYWQzSmhObWx4Y1dVNWVXdG1WMGQxSzFBNGFYZEpaMDE0Y1RWd04zRndlRTFUVEZCM09HWmFURFZ4VlZrNGNreE1hVUpVWkhoT1psQTBZamswTVZsUFZ6ZzlJaXdpVFVsSlEwTkVRME5CWVRablFYZEpRa0ZuU1ZWRlNXMU9NVlpRUm1aeWRVdElUamQxWmpKeVVpdEJZbVJFUmsxM1EyZFpTVXR2V2tsNmFqQkZRWGRKZDFsVVJVeE5RV3RIUVRGVlJVSm9UVU5TUlZWNFJXcEJVVUpuVGxaQ1FXTlVRMVpTYkdNelVXZFJNbXd3WlZSRlZrMUNUVWRCTVZWRlEyaE5UVlpIVm5wa1EwSkVZakl4ZDFsWE5UVk5Va0YzUkdkWlJGWlJVVXhGZDJSVFlqSTVNRWxGVGtKTlVsVjNSWGRaUkZaUlVVUkZkM2hWV2xoT01FbEdTblppTTFGblVUQkZkMGxDWTA1TmFsRjNUbXBKTkUxVVJYZE9WRUYzVjJoblVFMXFSWGxPUkVFeVRVUlJlRTFVUVRGTlJFSmhUVWRGZUVONlFVcENaMDVXUWtGWlZFRnJVa1pOVWtsM1JVRlpSRlpSVVVoRmQyeFZXbGhPTUVsRlRuQmtTR3Q0UmxSQlZFSm5UbFpDUVc5VVJFWlNiR016VVdkUk1qbDBZMGRHZFdWVVJWRk5RVFJIUVRGVlJVTjRUVWhWYlRsMlpFTkNSRkZVUlZaTlFrMUhRVEZWUlVGNFRVMVdSMVo2WkVOQ1UySXlPVEJKUlU1Q1RVWnJkMFYzV1VoTGIxcEplbW93UTBGUldVbExiMXBKZW1vd1JFRlJZMFJSWjBGRldGUnVNMDFtYjNkM1YxRTRkbUl2VFZWblNFZE5hWFJ0VUd4NU9VOVhNRFI0YlZBd1JsQklVMHBtVFRBdlYwcENTMDlKY0dSUWVEQXhSMmhMUkROb1ozbDNNV3RrT0V4SmNYWkVlRFZ6WTJJeFNsVXlhWEZPUTAxRlFYZEVaMWxFVmxJd1VFRlJTQzlDUVZGRVFXZEZSMDFCT0VkQk1WVmtSWGRGUWk5M1VVWk5RVTFDUVdZNGQwaFJXVVJXVWpCUFFrSlpSVVpGV1VSTVlXRmxSVFEzTTFka2ExbG9UbVJaYjFwUGQyTTRhelZOUVc5SFEwTnhSMU5OTkRsQ1FVMURRVEJuUVUxRlZVTkpRMFIwWlZBM1YyNU5iME5xWjJGV2EyVmFla1o0TVhWMVp6Sm9MMW80UnpaSWFGZG1UVzk2ZDFad1lVRnBSVUYxVDNsNmExWkpkbVZDV1VOT05IZEhNRmRtV2xweGFIbGxVbVF3YW5kM1VuZFZWMWRuT0V0TGFIbG5QU0pkZlEiLCJzaWduYXR1cmUiOiJQVXU4SmV6aml3ZUl0eTRpS3gtQXZMcktqQlpTRHVWUnpsaFNZYTZyNGVfcjN1Qjc1emdQcDZjbWpPWWpjMFkteVdBOVBUMnZqMWFnOUV5OXEyakNOQSJ9") +) diff --git a/verify/tdx.go b/verify/tdx.go index c455d115..2b850071 100644 --- a/verify/tdx.go +++ b/verify/tdx.go @@ -238,7 +238,7 @@ func verifyTdxMeasurements(tdxM ar.Measurement, nonce []byte, intelCache string, return result, false } - // Compare Nonce for Freshness (called Report Data in the SNP Attestation Report Structure) + // Compare nonce for freshness (called report data in the TDX attestation report structure) nonce64 := make([]byte, 64) copy(nonce64, nonce) if cmp := bytes.Compare(tdxQuote.QuoteBody.ReportData[:], nonce64); cmp != 0 { diff --git a/verify/tpm.go b/verify/tpm.go index fbc8dcd6..08dfcb3a 100644 --- a/verify/tpm.go +++ b/verify/tpm.go @@ -29,7 +29,7 @@ import ( "github.com/google/go-tpm/legacy/tpm2" ) -func verifyTpmMeasurements(tpmM ar.Measurement, nonce []byte, referenceValues []ar.ReferenceValue, cas []*x509.Certificate) (*ar.MeasurementResult, bool) { +func verifyTpmMeasurements(tpmM ar.Measurement, nonce []byte, cas []*x509.Certificate, referenceValues []ar.ReferenceValue) (*ar.MeasurementResult, bool) { result := &ar.MeasurementResult{ Type: "TPM Result", @@ -70,14 +70,21 @@ func verifyTpmMeasurements(tpmM ar.Measurement, nonce []byte, referenceValues [] log.Tracef("Successfully verified nonce %v", hex.EncodeToString(nonce)) // Verify aggregated PCR against TPM Quote PCRDigest: Hash all reference values + // together then compare sum := make([]byte, 0) - for i := range tpmM.Pcrs { - _, ok := calculatedPcrs[int(tpmM.Pcrs[i].Pcr)] + for i := range tpmM.Artifacts { + if tpmM.Artifacts[i].Pcr == nil { + log.Tracef("PCR not specified") + result.Summary.SetErr(ar.PcrNotSpecified) + return result, false + } + pcr := *tpmM.Artifacts[i].Pcr + _, ok := calculatedPcrs[pcr] if !ok { continue } - sum = append(sum, calculatedPcrs[int(tpmM.Pcrs[i].Pcr)]...) + sum = append(sum, calculatedPcrs[pcr]...) } verPcr := sha256.Sum256(sum) if bytes.Equal(verPcr[:], tpmsAttest.AttestedQuoteInfo.PCRDigest) { @@ -131,27 +138,34 @@ func verifyTpmMeasurements(tpmM ar.Measurement, nonce []byte, referenceValues [] return result, ok } -func recalculatePcrs(measurement ar.Measurement, referenceValues []ar.ReferenceValue) (map[int][]byte, []ar.PcrResult, []ar.DigestResult, bool) { +func recalculatePcrs(measurement ar.Measurement, referenceValues []ar.ReferenceValue) (map[int][]byte, []ar.DigestResult, []ar.DigestResult, bool) { ok := true - pcrResults := make([]ar.PcrResult, 0) + pcrResults := make([]ar.DigestResult, 0) detailedResults := make([]ar.DigestResult, 0) calculatedPcrs := make(map[int][]byte) // Iterate over the provided measurement - for _, measuredPcr := range measurement.Pcrs { + for _, measuredPcr := range measurement.Artifacts { - pcrResult := ar.PcrResult{ + pcrResult := ar.DigestResult{ Pcr: measuredPcr.Pcr, Success: true, } + if measuredPcr.Pcr == nil { + log.Trace("PCR not specified") + ok = false + pcrResult.Success = false + continue + } + pcr := *measuredPcr.Pcr + // Initialize calculated PCR if not yet initialized, afterwards extend // reference values - if _, ok := calculatedPcrs[int(measuredPcr.Pcr)]; !ok { - calculatedPcrs[int(measuredPcr.Pcr)] = make([]byte, 32) + if _, ok := calculatedPcrs[pcr]; !ok { + calculatedPcrs[pcr] = make([]byte, 32) } - pcrNum := measuredPcr.Pcr if measuredPcr.Type == "PCR Eventlog" { // measurement contains a detailed measurement list (e.g. retrieved from bios // measurement logs or ima runtime measurement logs) @@ -159,7 +173,7 @@ func recalculatePcrs(measurement ar.Measurement, referenceValues []ar.ReferenceV for _, event := range measuredPcr.Events { //first event could be a TPM_PCR_INIT_VALUE () if event.EventName == "TPM_PCR_INIT_VALUE" { - calculatedPcrs[int(measuredPcr.Pcr)] = event.Sha256 + calculatedPcrs[pcr] = event.Sha256 measuredSummary = event.Sha256 continue } @@ -168,11 +182,11 @@ func recalculatePcrs(measurement ar.Measurement, referenceValues []ar.ReferenceV measuredSummary = extendSha256(measuredSummary, event.Sha256) // Check for every event that a corresponding reference value exists - ref := getReferenceValue(event.Sha256, measuredPcr.Pcr, referenceValues) + ref := getReferenceValue(event.Sha256, pcr, referenceValues) if ref == nil { measResult := ar.DigestResult{ Type: "Measurement", - Pcr: &pcrNum, + Pcr: &pcr, Digest: hex.EncodeToString(event.Sha256), Success: false, Name: event.EventName, @@ -180,14 +194,14 @@ func recalculatePcrs(measurement ar.Measurement, referenceValues []ar.ReferenceV } detailedResults = append(detailedResults, measResult) log.Tracef("Failed to find PCR%v measurement %v: %v in reference values", - measuredPcr.Pcr, event.EventName, hex.EncodeToString(event.Sha256)) + *measuredPcr.Pcr, event.EventName, hex.EncodeToString(event.Sha256)) ok = false pcrResult.Success = false continue } // ...Extent calculated summary only if reference value was found - calculatedPcrs[measuredPcr.Pcr] = extendSha256(calculatedPcrs[measuredPcr.Pcr], + calculatedPcrs[pcr] = extendSha256(calculatedPcrs[pcr], event.Sha256) nameInfo := ref.Name @@ -196,7 +210,7 @@ func recalculatePcrs(measurement ar.Measurement, referenceValues []ar.ReferenceV } measResult := ar.DigestResult{ - Pcr: &pcrNum, + Pcr: &pcr, Digest: hex.EncodeToString(event.Sha256), Success: true, Name: nameInfo, @@ -204,9 +218,9 @@ func recalculatePcrs(measurement ar.Measurement, referenceValues []ar.ReferenceV } detailedResults = append(detailedResults, measResult) } - pcrResult.Calculated = hex.EncodeToString(calculatedPcrs[measuredPcr.Pcr]) - if !bytes.Equal(measuredSummary, calculatedPcrs[measuredPcr.Pcr]) { - pcrResult.Measured = hex.EncodeToString(measuredSummary) + pcrResult.Digest = hex.EncodeToString(calculatedPcrs[pcr]) + if !bytes.Equal(measuredSummary, calculatedPcrs[pcr]) { + pcrResult.Description = hex.EncodeToString(measuredSummary) } } else if measuredPcr.Type == "PCR Summary" { @@ -218,17 +232,17 @@ func recalculatePcrs(measurement ar.Measurement, referenceValues []ar.ReferenceV ok = false continue } - if *ref.Pcr == measuredPcr.Pcr { + if *ref.Pcr == pcr { if ref.Name == "TPM_PCR_INIT_VALUE" { - calculatedPcrs[measuredPcr.Pcr] = ref.Sha256 //the Sha256 should contain the init value - continue //break the loop iteration and continue with the next event + calculatedPcrs[pcr] = ref.Sha256 //the Sha256 should contain the init value + continue //break the loop iteration and continue with the next event } - calculatedPcrs[measuredPcr.Pcr] = extendSha256(calculatedPcrs[measuredPcr.Pcr], ref.Sha256) + calculatedPcrs[pcr] = extendSha256(calculatedPcrs[pcr], ref.Sha256) // As we only have the PCR summary, we will later set all reference values // to true/false depending on whether the calculation matches the PCR summary measResult := ar.DigestResult{ - Pcr: &pcrNum, + Pcr: &pcr, Digest: hex.EncodeToString(ref.Sha256), Name: ref.Name, Description: ref.Description, @@ -238,16 +252,16 @@ func recalculatePcrs(measurement ar.Measurement, referenceValues []ar.ReferenceV } // Then we compare the calculated value with the PCR measurement summary - equal := bytes.Equal(calculatedPcrs[measuredPcr.Pcr], measuredPcr.Summary) + equal := bytes.Equal(calculatedPcrs[pcr], measuredPcr.Summary) if equal { - pcrResult.Calculated = hex.EncodeToString(calculatedPcrs[measuredPcr.Pcr]) + pcrResult.Digest = hex.EncodeToString(calculatedPcrs[pcr]) pcrResult.Success = true } else { - log.Tracef("PCR%v mismatch: measured: %v, calculated: %v", measuredPcr.Pcr, + log.Tracef("PCR%v mismatch: measured: %v, calculated: %v", pcr, hex.EncodeToString(measuredPcr.Summary), - hex.EncodeToString(calculatedPcrs[measuredPcr.Pcr])) - pcrResult.Calculated = hex.EncodeToString(calculatedPcrs[measuredPcr.Pcr]) - pcrResult.Measured = hex.EncodeToString(measuredPcr.Summary) + hex.EncodeToString(calculatedPcrs[pcr])) + pcrResult.Digest = hex.EncodeToString(calculatedPcrs[pcr]) + pcrResult.Description = hex.EncodeToString(measuredPcr.Summary) pcrResult.Success = false ok = false } @@ -255,7 +269,7 @@ func recalculatePcrs(measurement ar.Measurement, referenceValues []ar.ReferenceV // for this PCR to the according value, as we cannot determine which ones // were good or potentially failed for i, elem := range detailedResults { - if *elem.Pcr == measuredPcr.Pcr && measuredPcr.Type != "PCR Eventlog" { + if *elem.Pcr == pcr && measuredPcr.Type != "PCR Eventlog" { detailedResults[i].Success = equal } } @@ -272,7 +286,7 @@ func recalculatePcrs(measurement ar.Measurement, referenceValues []ar.ReferenceV // Sort the results in ascending order sort.Slice(pcrResults, func(i, j int) bool { - return pcrResults[i].Pcr < pcrResults[j].Pcr + return *pcrResults[i].Pcr < *pcrResults[j].Pcr }) // Finally, iterate over the reference values to find reference values with no @@ -296,9 +310,15 @@ func recalculatePcrs(measurement ar.Measurement, referenceValues []ar.ReferenceV // Check if measurement contains the reference value PCR foundPcr := false - for _, measuredPcr := range measurement.Pcrs { + for _, measuredPcr := range measurement.Artifacts { + + if measuredPcr.Pcr == nil { + log.Trace("PCR not specified") + ok = false + continue + } - if measuredPcr.Pcr == *ref.Pcr { + if *measuredPcr.Pcr == *ref.Pcr { foundPcr = true } else { continue diff --git a/verify/tpm_test.go b/verify/tpm_test.go index bdb1ad86..d3591aae 100644 --- a/verify/tpm_test.go +++ b/verify/tpm_test.go @@ -49,7 +49,7 @@ func Test_verifyTpmMeasurements(t *testing.T) { Evidence: validQuote, Signature: validSignature, Certs: validTpmCertChain, - Pcrs: validSummaryHashChain, + Artifacts: validSummaryHashChain, }, nonce: validTpmNonce, referenceValues: validReferenceValues, @@ -66,7 +66,7 @@ func Test_verifyTpmMeasurements(t *testing.T) { Evidence: validQuote, Signature: validSignature, Certs: validTpmCertChain, - Pcrs: validHashChain, + Artifacts: validHashChain, }, nonce: validTpmNonce, referenceValues: validReferenceValues, @@ -83,7 +83,7 @@ func Test_verifyTpmMeasurements(t *testing.T) { Evidence: validQuote, Signature: validSignature, Certs: validTpmCertChain, - Pcrs: validSummaryHashChain, + Artifacts: validSummaryHashChain, }, nonce: invalidTpmNonce, referenceValues: validReferenceValues, @@ -100,7 +100,7 @@ func Test_verifyTpmMeasurements(t *testing.T) { Evidence: validQuote, Signature: invalidSignature, Certs: validTpmCertChain, - Pcrs: validSummaryHashChain, + Artifacts: validSummaryHashChain, }, nonce: validTpmNonce, referenceValues: validReferenceValues, @@ -117,7 +117,7 @@ func Test_verifyTpmMeasurements(t *testing.T) { Evidence: validQuote, Signature: validSignature, Certs: validTpmCertChain, - Pcrs: invalidSummaryHashChain, + Artifacts: invalidSummaryHashChain, }, nonce: validTpmNonce, referenceValues: validReferenceValues, @@ -134,7 +134,7 @@ func Test_verifyTpmMeasurements(t *testing.T) { Evidence: validQuote, Signature: validSignature, Certs: validTpmCertChain, - Pcrs: invalidHashChain, + Artifacts: invalidHashChain, }, nonce: validTpmNonce, referenceValues: validReferenceValues, @@ -151,7 +151,7 @@ func Test_verifyTpmMeasurements(t *testing.T) { Evidence: validQuote, Signature: invalidSignature, Certs: validTpmCertChain, - Pcrs: validSummaryHashChain, + Artifacts: validSummaryHashChain, }, nonce: validTpmNonce, referenceValues: invalidReferenceValues, @@ -168,7 +168,7 @@ func Test_verifyTpmMeasurements(t *testing.T) { Evidence: validQuote, Signature: validSignature, Certs: validTpmCertChain, - Pcrs: validSummaryHashChain, + Artifacts: validSummaryHashChain, }, nonce: validTpmNonce, referenceValues: validReferenceValues, @@ -185,7 +185,7 @@ func Test_verifyTpmMeasurements(t *testing.T) { Evidence: validQuote, Signature: validSignature, Certs: invalidTpmCertChain, - Pcrs: validSummaryHashChain, + Artifacts: validSummaryHashChain, }, nonce: validTpmNonce, referenceValues: validReferenceValues, @@ -200,7 +200,7 @@ func Test_verifyTpmMeasurements(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, got1 := verifyTpmMeasurements(*tt.args.tpmM, tt.args.nonce, tt.args.referenceValues, tt.args.cas) + got, got1 := verifyTpmMeasurements(*tt.args.tpmM, tt.args.nonce, tt.args.cas, tt.args.referenceValues) if got1 != tt.want1 { t.Errorf("verifyTpmMeasurements() --GOT1-- = %v, --WANT1-- %v", got1, tt.want1) } @@ -249,37 +249,37 @@ var ( invalidSignature, _ = hex.DecodeString("0014000b0100740e077a77ff6ac21754d036f751f5f8ec5ec59448aab05bb5fd2b5d81df58bde3550d855ecf16cd25e36b5688122cfaac1a86ab94954b81d49a1b7fc7648ad26b8b808ce846fe7fd49355d2461d049904e97aa687749d55510f09b7c8610b95b6d557ebdaa25a19bfa1663f236419a1a8d974dd05b14de7f28fbce0a54c3ac428a9cf7f0752cc290580ff8d63e33050c0f53582ae24fe4d30792da71d5ef93581e3371147ed4732a0c0c0461489b1b64b1f28dd5153dbc674f04a21e279833433eabec1642cd386fdca6e52b583b2c914ebcd3c7a334214dc5e7c02880b033e321cb261ed6044785e70599d269511f83a20ee45034f0803d623763d461ce763") - validSummaryHashChain = []ar.PcrMeasurement{ + validSummaryHashChain = []ar.Artifact{ { Type: "PCR Summary", - Pcr: 1, + Pcr: ptr(1), Summary: dec("5f96aec0a6b390185495c35bc76dceb9fa6addb4e59b6fc1b3e1992eeb08a5c6"), }, { Type: "PCR Summary", - Pcr: 4, + Pcr: ptr(4), Summary: dec("d3f67dbed9bce9d391a3567edad08971339e4dbabadd5b7eaf082860296e5e72"), }, } - invalidSummaryHashChain = []ar.PcrMeasurement{ + invalidSummaryHashChain = []ar.Artifact{ { Type: "PCR Summary", - Pcr: 1, + Pcr: ptr(1), Summary: dec("2a814d03d22568e2d669595dd8be199fd7b3df2acb8caae38e24e92605e15c80"), }, { Type: "PCR Summary", - Pcr: 4, + Pcr: ptr(4), Summary: dec("1fe8f1a49cf178748a6f6167473bca3cf882ff70b4b4e458e2421c871c9c5bb9"), }, } - validHashChain = []ar.PcrMeasurement{ + validHashChain = []ar.Artifact{ { Type: "PCR Eventlog", - Pcr: 1, - Events: []ar.PcrEvent{ + Pcr: ptr(1), + Events: []ar.MeasureEvent{ {Sha256: dec("ef5631c7bbb8d98ad220e211933fcde16aac6154cf229fea3c728fb0f2c27e39")}, {Sha256: dec("131462b45df65ac00834c7e73356c246037456959674acd24b08357690a03845")}, {Sha256: dec("8574d91b49f1c9a6ecc8b1e8565bd668f819ea8ed73c5f682948141587aecd3b")}, @@ -293,8 +293,8 @@ var ( }, { Type: "PCR Eventlog", - Pcr: 4, - Events: []ar.PcrEvent{ + Pcr: ptr(4), + Events: []ar.MeasureEvent{ {Sha256: dec("3d6772b4f84ed47595d72a2c4c5ffd15f5bb72c7507fe26f2aaee2c69d5633ba")}, {Sha256: dec("df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119")}, {Sha256: dec("dbffd70a2c43fd2c1931f18b8f8c08c5181db15f996f747dfed34def52fad036")}, @@ -304,11 +304,11 @@ var ( }, } - invalidHashChain = []ar.PcrMeasurement{ + invalidHashChain = []ar.Artifact{ { Type: "PCR Eventlog", - Pcr: 1, - Events: []ar.PcrEvent{ + Pcr: ptr(1), + Events: []ar.MeasureEvent{ {Sha256: dec("ff5631c7bbb8d98ad220e211933fcde16aac6154cf229fea3c728fb0f2c27e39")}, {Sha256: dec("131462b45df65ac00834c7e73356c246037456959674acd24b08357690a03845")}, {Sha256: dec("8574d91b49f1c9a6ecc8b1e8565bd668f819ea8ed73c5f682948141587aecd3b")}, @@ -322,8 +322,8 @@ var ( }, { Type: "PCR Eventlog", - Pcr: 4, - Events: []ar.PcrEvent{ + Pcr: ptr(4), + Events: []ar.MeasureEvent{ {Sha256: dec("3d6772b4f84ed47595d72a2c4c5ffd15f5bb72c7507fe26f2aaee2c69d5633ba")}, {Sha256: dec("df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119")}, {Sha256: dec("dbffd70a2c43fd2c1931f18b8f8c08c5181db15f996f747dfed34def52fad036")}, @@ -660,16 +660,16 @@ var ( Signature: validSignatureResult, Artifacts: validArtifacts, TpmResult: &ar.TpmResult{ - PcrMatch: []ar.PcrResult{ + PcrMatch: []ar.DigestResult{ { - Pcr: 1, - Calculated: "5f96aec0a6b390185495c35bc76dceb9fa6addb4e59b6fc1b3e1992eeb08a5c6", - Success: true, + Pcr: ptr(1), + Digest: "5f96aec0a6b390185495c35bc76dceb9fa6addb4e59b6fc1b3e1992eeb08a5c6", + Success: true, }, { - Pcr: 4, - Calculated: "d3f67dbed9bce9d391a3567edad08971339e4dbabadd5b7eaf082860296e5e72", - Success: true, + Pcr: ptr(4), + Digest: "d3f67dbed9bce9d391a3567edad08971339e4dbabadd5b7eaf082860296e5e72", + Success: true, }, }, AggPcrQuoteMatch: validResult, diff --git a/verify/verify.go b/verify/verify.go index 57efaa79..a4ffb87c 100644 --- a/verify/verify.go +++ b/verify/verify.go @@ -100,18 +100,6 @@ func Verify(arRaw, nonce, casPem []byte, policies []byte, polEng PolicyEngineSel } result.MetadataResult = *mr - // Verify nonce - if res := bytes.Compare(report.Nonce, nonce); res != 0 { - log.Tracef("Nonces mismatch: supplied nonce: %v, report nonce = %v", - hex.EncodeToString(nonce), hex.EncodeToString(report.Nonce)) - result.FreshnessCheck.Success = false - result.FreshnessCheck.Expected = hex.EncodeToString(report.Nonce) - result.FreshnessCheck.Got = hex.EncodeToString(nonce) - result.Success = false - } else { - result.FreshnessCheck.Success = true - } - refVals, err := collectReferenceValues(metadata) if err != nil { log.Tracef("Failed to collect reference values: %v", err) @@ -125,7 +113,7 @@ func Verify(arRaw, nonce, casPem []byte, policies []byte, polEng PolicyEngineSel switch mtype := m.Type; mtype { case "TPM Measurement": - r, ok := verifyTpmMeasurements(m, nonce, refVals["TPM Reference Value"], cas) + r, ok := verifyTpmMeasurements(m, nonce, cas, refVals["TPM Reference Value"]) if !ok { result.Success = false } @@ -157,14 +145,20 @@ func Verify(arRaw, nonce, casPem []byte, policies []byte, polEng PolicyEngineSel hwAttest = true case "IAS Measurement": - r, ok := verifyIasMeasurements(m, nonce, - refVals["IAS Reference Value"], cas) + r, ok := verifyIasMeasurements(m, nonce, cas, refVals["IAS Reference Value"]) if !ok { result.Success = false } result.Measurements = append(result.Measurements, *r) hwAttest = true + case "SW Measurement": + r, ok := verifySwMeasurements(m, nonce, cas, s, refVals["SW Reference Value"]) + if !ok { + result.Success = false + } + result.Measurements = append(result.Measurements, *r) + default: log.Tracef("Unsupported measurement type '%v'", mtype) result.Success = false diff --git a/verify/verify_test.go b/verify/verify_test.go index ec27db7d..3743a57c 100644 --- a/verify/verify_test.go +++ b/verify/verify_test.go @@ -313,17 +313,6 @@ func TestVerify(t *testing.T) { }, want: ar.VerificationResult{Success: true}, }, - { - name: "Nonce mismatch", - args: args{ - serializer: ar.JsonSerializer{}, - rtmManifest: validRtmManifest, - osManifest: validOsManifest, - deviceDescription: validDeviceDescription, - nonce: []byte{}, - }, - want: ar.VerificationResult{Success: false}, - }, { // expected aggregated CertificationLevel in Manifests for // empty measurement is max. 1 (here CertificationLevel = 3) @@ -412,8 +401,7 @@ func TestVerify(t *testing.T) { log.Trace("Generating a Sample Report") ar := ar.AttestationReport{ - Type: "Attestation Report", - Nonce: tt.args.nonce, + Type: "Attestation Report", } // Preparation: create signed manifests and deviceDescription