-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update measurements.json to v2 format, and add a helper to load and c…
…ompare (#26) * CLI tool to print verified measurements from an aTLS server * use HTTP GET request, and save to file * cleanup * rename to attested-get * feat(get-measurements): compare against known measurements * docs * backwards-compatible loading of v1 measurements * cleanup * support legacy format * package name: multimeasurements * cleanup
- Loading branch information
Showing
6 changed files
with
280 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,42 +1,50 @@ | ||
{ | ||
"azure-tdx-example": { | ||
"11": { | ||
"expected": "efa43e0beff151b0f251c4abf48152382b1452b4414dbd737b4127de05ca31f7" | ||
}, | ||
"12": { | ||
"expected": "0000000000000000000000000000000000000000000000000000000000000000" | ||
}, | ||
"13": { | ||
"expected": "0000000000000000000000000000000000000000000000000000000000000000" | ||
}, | ||
"15": { | ||
"expected": "0000000000000000000000000000000000000000000000000000000000000000" | ||
}, | ||
"4": { | ||
"expected": "ea92ff762767eae6316794f1641c485d4846bc2b9df2eab6ba7f630ce6f4d66f" | ||
}, | ||
"8": { | ||
"expected": "0000000000000000000000000000000000000000000000000000000000000000" | ||
}, | ||
"9": { | ||
"expected": "c9f429296634072d1063a03fb287bed0b2d177b0a504755ad9194cffd90b2489" | ||
} | ||
}, | ||
"dcap-tdx-example": { | ||
"0": { | ||
"expected": "5d56080eb9ef8ce0bbaf6bdcdadeeb06e7c5b0a4d1ec16be868a85a953babe0c5e54d01c8e050a54fe1ca078372530d2" | ||
}, | ||
"1": { | ||
"expected": "4216e925f796f4e282cfa6e72d4c77a80560987afa29155a61fdc33adb80eab0d4112abd52387e5e25a60deefb8a5287" | ||
}, | ||
"2": { | ||
"expected": "4274fefb79092c164000b571b64ecb432fa2357adb421fd1c77a867168d7d7f7fe82796d1eba092c7bab35cf43f5ec55" | ||
}, | ||
"3": { | ||
"expected": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" | ||
}, | ||
"4": { | ||
"expected": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" | ||
} | ||
} | ||
} | ||
[ | ||
{ | ||
"measurement_id": "azure-tdx-example-01", | ||
"attestation_type": "azure-tdx", | ||
"measurements": { | ||
"4": { | ||
"expected": "ea92ff762767eae6316794f1641c485d4846bc2b9df2eab6ba7f630ce6f4d66f" | ||
}, | ||
"9": { | ||
"expected": "c9f429296634072d1063a03fb287bed0b2d177b0a504755ad9194cffd90b2489" | ||
}, | ||
"11": { | ||
"expected": "efa43e0beff151b0f251c4abf48152382b1452b4414dbd737b4127de05ca31f7" | ||
} | ||
} | ||
}, | ||
{ | ||
"measurement_id": "cvm-image-azure-tdx.rootfs-20241107200854.wic.vhd", | ||
"attestation_type": "azure-tdx", | ||
"measurements": { | ||
"4": { | ||
"expected": "1b8cd655f5ebdf50bedabfb5db6b896a0a7c56de54f318103a2de1e7cea57b6b" | ||
}, | ||
"9": { | ||
"expected": "992465f922102234c196f596fdaba86ea16eaa4c264dc425ec26bc2d1c364472" | ||
} | ||
} | ||
}, | ||
{ | ||
"measurement_id": "dcap-tdx-example-02", | ||
"attestation_type": "dcap-tdx", | ||
"measurements": { | ||
"0": { | ||
"expected": "5d56080eb9ef8ce0bbaf6bdcdadeeb06e7c5b0a4d1ec16be868a85a953babe0c5e54d01c8e050a54fe1ca078372530d2" | ||
}, | ||
"1": { | ||
"expected": "4216e925f796f4e282cfa6e72d4c77a80560987afa29155a61fdc33adb80eab0d4112abd52387e5e25a60deefb8a5287" | ||
}, | ||
"2": { | ||
"expected": "4274fefb79092c164000b571b64ecb432fa2357adb421fd1c77a867168d7d7f7fe82796d1eba092c7bab35cf43f5ec55" | ||
}, | ||
"3": { | ||
"expected": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" | ||
}, | ||
"4": { | ||
"expected": "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" | ||
} | ||
} | ||
} | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// Package multimeasurements contains a helper to load a file with multiple measurements | ||
// and compare provided measurements against them. | ||
// | ||
// Compatible with measurements data schema v2 (see measurements.json) as well as the | ||
// legacy v1 schema. | ||
package multimeasurements | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"io" | ||
"net/http" | ||
"os" | ||
"strings" | ||
|
||
"github.com/flashbots/cvm-reverse-proxy/internal/attestation/measurements" | ||
) | ||
|
||
// MultiMeasurements holds several known measurements, and can check if | ||
// given measurements match known ones. | ||
type MultiMeasurements struct { | ||
Measurements []MeasurementsContainer | ||
} | ||
|
||
type MeasurementsContainer struct { | ||
MeasurementID string `json:"measurement_id"` | ||
AttestationType string `json:"attestation_type"` | ||
Measurements measurements.M `json:"measurements"` | ||
} | ||
|
||
type LegacyMultiMeasurements map[string]measurements.M | ||
|
||
// New returns a MultiMeasurements instance, with the measurements | ||
// loaded from a file or URL. | ||
func New(path string) (m *MultiMeasurements, err error) { | ||
var data []byte | ||
if strings.HasPrefix(path, "http") { | ||
// load from URL | ||
resp, err := http.Get(path) | ||
if err != nil { | ||
return nil, err | ||
} | ||
defer resp.Body.Close() | ||
data, err = io.ReadAll(resp.Body) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} else { | ||
// load from file | ||
data, err = os.ReadFile(path) | ||
if err != nil { | ||
return nil, err | ||
} | ||
} | ||
|
||
m = &MultiMeasurements{} | ||
|
||
// Try to load the v2 data schema, if that fails fall back to legacy v1 schema | ||
if err = json.Unmarshal(data, &m.Measurements); err != nil { | ||
var legacyData LegacyMultiMeasurements | ||
err = json.Unmarshal(data, &legacyData) | ||
for measurementID, measurements := range legacyData { | ||
container := MeasurementsContainer{ | ||
MeasurementID: measurementID, | ||
AttestationType: "azure-tdx", | ||
Measurements: measurements, | ||
} | ||
m.Measurements = append(m.Measurements, container) | ||
} | ||
} | ||
|
||
return m, err | ||
} | ||
|
||
// Contains checks if the provided measurements match one of the known measurements. Any keys in the provided | ||
// measurements which are not in the known measurements are ignored. | ||
func (m *MultiMeasurements) Contains(measurements map[uint32][]byte) (found bool, foundMeasurement *MeasurementsContainer) { | ||
// For every known container, all known measurements match (and additional ones are ignored) | ||
for _, container := range m.Measurements { | ||
allMatch := true | ||
for key, value := range container.Measurements { | ||
if !bytes.Equal(value.Expected, measurements[key]) { | ||
allMatch = false | ||
break | ||
} | ||
} | ||
|
||
if allMatch { | ||
return true, &container | ||
} | ||
} | ||
|
||
return false, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package multimeasurements | ||
|
||
import ( | ||
"encoding/hex" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/require" | ||
) | ||
|
||
// TestMeasurements is kept simple: map[pcr]measurement | ||
type TestMeasurements map[uint32][]byte | ||
|
||
func mustBytesFromHex(hexValue string) []byte { | ||
bytes, err := hex.DecodeString(hexValue) | ||
if err != nil { | ||
panic(err) | ||
} | ||
return bytes | ||
} | ||
|
||
// Measurements V1 (legacy) JSON (from https://github.com/flashbots/cvm-reverse-proxy/blob/837588b9f87ee49d1bb6dca4712a1c2844eb1ecc/measurements.json) | ||
var measurementsV1JSON = []byte(`{"azure-tdx-example":{"11":{"expected":"efa43e0beff151b0f251c4abf48152382b1452b4414dbd737b4127de05ca31f7"},"12":{"expected":"0000000000000000000000000000000000000000000000000000000000000000"},"13":{"expected":"0000000000000000000000000000000000000000000000000000000000000000"},"15":{"expected":"0000000000000000000000000000000000000000000000000000000000000000"},"4":{"expected":"ea92ff762767eae6316794f1641c485d4846bc2b9df2eab6ba7f630ce6f4d66f"},"8":{"expected":"0000000000000000000000000000000000000000000000000000000000000000"},"9":{"expected":"c9f429296634072d1063a03fb287bed0b2d177b0a504755ad9194cffd90b2489"}},"dcap-tdx-example":{"0":{"expected":"5d56080eb9ef8ce0bbaf6bdcdadeeb06e7c5b0a4d1ec16be868a85a953babe0c5e54d01c8e050a54fe1ca078372530d2"},"1":{"expected":"4216e925f796f4e282cfa6e72d4c77a80560987afa29155a61fdc33adb80eab0d4112abd52387e5e25a60deefb8a5287"},"2":{"expected":"4274fefb79092c164000b571b64ecb432fa2357adb421fd1c77a867168d7d7f7fe82796d1eba092c7bab35cf43f5ec55"},"3":{"expected":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"},"4":{"expected":"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}}}`) | ||
|
||
// TestMultiMeasurementsV2 tests the v2 data schema | ||
func TestMultiMeasurementsV2(t *testing.T) { | ||
// Load expected measurements from JSON file (in V2 format) | ||
m, err := New("../measurements.json") | ||
require.NoError(t, err) | ||
require.Len(t, m.Measurements, 3) | ||
|
||
// Setup test measurements (matching cvm-image-azure-tdx.rootfs-20241107200854.wic.vhd) | ||
testMeasurements := TestMeasurements{ | ||
4: mustBytesFromHex("1b8cd655f5ebdf50bedabfb5db6b896a0a7c56de54f318103a2de1e7cea57b6b"), | ||
9: mustBytesFromHex("992465f922102234c196f596fdaba86ea16eaa4c264dc425ec26bc2d1c364472"), | ||
} | ||
|
||
// Ensure matching entries works, and that additional fields are ignored | ||
testMeasurements[11] = testMeasurements[4] | ||
exists, foundMeasurement := m.Contains(testMeasurements) | ||
require.True(t, exists) | ||
require.Equal(t, "cvm-image-azure-tdx.rootfs-20241107200854.wic.vhd", foundMeasurement.MeasurementID) | ||
require.Equal(t, "azure-tdx", foundMeasurement.AttestationType) | ||
|
||
// Ensure check fails with a missing required key | ||
delete(testMeasurements, 4) | ||
exists, _ = m.Contains(testMeasurements) | ||
require.False(t, exists) | ||
|
||
// Double-check it works again | ||
testMeasurements[4] = testMeasurements[11] | ||
exists, _ = m.Contains(testMeasurements) | ||
require.True(t, exists) | ||
|
||
// Any changed value should make it fail | ||
testMeasurements[4] = testMeasurements[9] | ||
exists, _ = m.Contains(testMeasurements) | ||
require.False(t, exists) | ||
|
||
// Check for another set of known measurements (dcap-tdx-example) | ||
testMeasurements = TestMeasurements{ | ||
0: mustBytesFromHex("5d56080eb9ef8ce0bbaf6bdcdadeeb06e7c5b0a4d1ec16be868a85a953babe0c5e54d01c8e050a54fe1ca078372530d2"), | ||
1: mustBytesFromHex("4216e925f796f4e282cfa6e72d4c77a80560987afa29155a61fdc33adb80eab0d4112abd52387e5e25a60deefb8a5287"), | ||
2: mustBytesFromHex("4274fefb79092c164000b571b64ecb432fa2357adb421fd1c77a867168d7d7f7fe82796d1eba092c7bab35cf43f5ec55"), | ||
3: mustBytesFromHex("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), | ||
4: mustBytesFromHex("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), | ||
} | ||
exists, foundMeasurement = m.Contains(testMeasurements) | ||
require.True(t, exists) | ||
require.Equal(t, "dcap-tdx-example-02", foundMeasurement.MeasurementID) | ||
} | ||
|
||
func TestMultiMeasurementsV1(t *testing.T) { | ||
tempDir := t.TempDir() | ||
err := os.WriteFile(filepath.Join(tempDir, "measurements.json"), measurementsV1JSON, 0644) | ||
require.NoError(t, err) | ||
|
||
// Load expected measurements from JSON file | ||
m, err := New(filepath.Join(tempDir, "measurements.json")) | ||
require.NoError(t, err) | ||
require.Len(t, m.Measurements, 2) | ||
|
||
testMeasurements := TestMeasurements{ | ||
0: mustBytesFromHex("5d56080eb9ef8ce0bbaf6bdcdadeeb06e7c5b0a4d1ec16be868a85a953babe0c5e54d01c8e050a54fe1ca078372530d2"), | ||
1: mustBytesFromHex("4216e925f796f4e282cfa6e72d4c77a80560987afa29155a61fdc33adb80eab0d4112abd52387e5e25a60deefb8a5287"), | ||
2: mustBytesFromHex("4274fefb79092c164000b571b64ecb432fa2357adb421fd1c77a867168d7d7f7fe82796d1eba092c7bab35cf43f5ec55"), | ||
3: mustBytesFromHex("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), | ||
4: mustBytesFromHex("000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"), | ||
} | ||
exists, foundMeasurement := m.Contains(testMeasurements) | ||
require.True(t, exists) | ||
require.Equal(t, "dcap-tdx-example", foundMeasurement.MeasurementID) | ||
} |
Oops, something went wrong.