Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sw measurements #196

Merged
merged 6 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 22 additions & 21 deletions attestationreport/attestationreport.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}

Expand All @@ -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"`
Expand All @@ -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 {
Expand Down Expand Up @@ -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"`
}
8 changes: 4 additions & 4 deletions attestationreport/cbor.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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 {
Expand Down
26 changes: 13 additions & 13 deletions attestationreport/json.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -282,28 +282,28 @@ 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
alg jose.SignatureAlgorithm
}

// 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
Expand Down Expand Up @@ -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
Expand Down
32 changes: 8 additions & 24 deletions attestationreport/validationreport.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 {
Expand All @@ -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
Expand Down Expand Up @@ -429,6 +414,7 @@ const (
VerifyTCBChain
VerifyTcbInfo
ExtensionsCheck
PcrNotSpecified
)

type Result struct {
Expand Down Expand Up @@ -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")
Expand All @@ -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)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions cmc/cmc.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ func NewCmc(c *Config) (*Cmc, error) {
Serializer: s,
CtrPcr: c.CtrPcr,
CtrLog: c.CtrLog,
CtrDriver: c.CtrDriver,
UseCtr: c.UseCtr,
}

Expand Down
2 changes: 1 addition & 1 deletion example-setup/setup-cmc
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
11 changes: 5 additions & 6 deletions generate/generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -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..")

Expand Down Expand Up @@ -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")
Expand Down
8 changes: 4 additions & 4 deletions ima/ima.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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{}
Expand Down Expand Up @@ -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,
}
Expand Down
Loading
Loading