Skip to content
Draft
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
91 changes: 64 additions & 27 deletions tools/cmd/mkcosi/generator/cih.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,16 @@ import (

// cihPartitionDef describes one expected partition in a CIH image.
type cihPartitionDef struct {
Name string // GPT partition name (e.g. "USR-A")
TypeGUID string // Partition type GUID, lowercase
UUID string // Unique partition GUID, lowercase; empty means "don't check"
Name string // GPT partition name (e.g. "USR-A")
TypeGUIDs []string // Acceptable partition type GUIDs, lowercase. Most
// entries have exactly one; ROOT and HASH-A/HASH-B carry both the
// amd64 and arm64 discoverable-partition-spec GUIDs, since a CIH
// image built for either architecture is valid and the image being
// inspected here was not necessarily produced on a host of the same
// architecture (e.g. cross-arch builds, or a build/inspection tool
// running natively rather than under arch-emulation). Do not key
// this off runtime.GOARCH -- see cih.go's isCIHImage doc comment.
UUID string // Unique partition GUID, lowercase; empty means "don't check"
}

// cihRequiredPartitions lists the partitions that must be present (by name and
Expand All @@ -40,20 +47,29 @@ type cihPartitionDef struct {
// partition UUIDs vary across builds and are not checked.
// HASH-A and HASH-B are optional — images without them are still valid CIH.
var cihRequiredPartitions = []cihPartitionDef{
{Name: "EFI-SYSTEM", TypeGUID: "c12a7328-f81f-11d2-ba4b-00a0c93ec93b"},
{Name: "USR-A", TypeGUID: "5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6", UUID: "7130c94a-213a-4e5a-8e26-6cce9662f132"},
{Name: "USR-B", TypeGUID: "5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6", UUID: "e03dd35c-7c2d-4a47-b3fe-27f15780a57c"},
{Name: "OEM", TypeGUID: "0fc63daf-8483-4772-8e79-3d69d8477de4"},
{Name: "ROOT", TypeGUID: "4f68bce3-e8cd-4db1-96e7-fbcaf984b709"},
{Name: "EFI-SYSTEM", TypeGUIDs: []string{"c12a7328-f81f-11d2-ba4b-00a0c93ec93b"}},
{Name: "USR-A", TypeGUIDs: []string{"5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6"}, UUID: "7130c94a-213a-4e5a-8e26-6cce9662f132"},
{Name: "USR-B", TypeGUIDs: []string{"5dfbf5f4-2848-4bac-aa5e-0d9a20b745a6"}, UUID: "e03dd35c-7c2d-4a47-b3fe-27f15780a57c"},
{Name: "OEM", TypeGUIDs: []string{"0fc63daf-8483-4772-8e79-3d69d8477de4"}},
{Name: "ROOT", TypeGUIDs: []string{
string(metadata.PartitionTypeRootAmd64),
string(metadata.PartitionTypeRootArm64),
}},
}

// cihOptionalPartitions lists partitions that may or may not be present.
// When present, both name+typeGUID and UUID must match.
var cihOptionalPartitions = []cihPartitionDef{
{Name: "BIOS-BOOT", TypeGUID: "21686148-6449-6e6f-744e-656564454649"},
{Name: "OEM-CONFIG", TypeGUID: "c95dc21a-df0e-4340-8d7b-26cbfa9a03e0"},
{Name: "HASH-A", TypeGUID: "77ff5f63-e7b6-4633-acf4-1565b864c0e6", UUID: "b736baf1-cdb4-4535-beba-ddaaa30ad7b7"},
{Name: "HASH-B", TypeGUID: "77ff5f63-e7b6-4633-acf4-1565b864c0e6", UUID: "35bdf78b-c453-4661-98e6-f834f534ef5b"},
{Name: "BIOS-BOOT", TypeGUIDs: []string{"21686148-6449-6e6f-744e-656564454649"}},
{Name: "OEM-CONFIG", TypeGUIDs: []string{"c95dc21a-df0e-4340-8d7b-26cbfa9a03e0"}},
{Name: "HASH-A", TypeGUIDs: []string{
string(metadata.PartitionTypeUsrAmd64Verity),
string(metadata.PartitionTypeUsrArm64Verity),
}, UUID: "b736baf1-cdb4-4535-beba-ddaaa30ad7b7"},
{Name: "HASH-B", TypeGUIDs: []string{
string(metadata.PartitionTypeUsrAmd64Verity),
string(metadata.PartitionTypeUsrArm64Verity),
}, UUID: "35bdf78b-c453-4661-98e6-f834f534ef5b"},
}

// cihMountPointByName maps CIH partition names to their logical mount points.
Expand All @@ -72,37 +88,58 @@ var cihMountPointByName = map[string]string{
// name+typeGUID; those with a non-empty UUID are also verified by UUID.
// Optional partitions (HASH-A/HASH-B) are validated when present.
func isCIHImage(parsedGPT *gpt.ParsedGPT) bool {
type partKey struct {
name string
type partVal struct {
typeGUID string
uuid string
}
// Map name+typeGUID -> partition UUID for checking.
partMap := make(map[partKey]string, len(parsedGPT.Partitions))
// Map partition name -> (typeGUID, UUID) for checking. GPT partition
// names are unique within a CIH image, so this does not need to be
// keyed by typeGUID as well.
partMap := make(map[string]partVal, len(parsedGPT.Partitions))
for _, p := range parsedGPT.Partitions {
partMap[partKey{
name: p.GetName(),
partMap[p.GetName()] = partVal{
typeGUID: strings.ToLower(p.PartitionTypeGUID.String()),
}] = strings.ToLower(p.UniquePartitionGUID.String())
uuid: strings.ToLower(p.UniquePartitionGUID.String()),
}
}

// matchesTypeGUID reports whether actual is one of the acceptable type
// GUIDs for a partition definition. Some logical partitions (ROOT,
// HASH-A, HASH-B) accept either the amd64 or arm64 discoverable-
// partition-spec GUID: a CIH image is valid for either architecture,
// and the architecture of the image under inspection is not assumed
// to match the architecture of whatever is running this check.
matchesTypeGUID := func(def cihPartitionDef, actual string) bool {
for _, want := range def.TypeGUIDs {
if actual == want {
return true
}
}
return false
}

// All required partitions must be present with matching name+typeGUID.
// Those with a specified UUID must also match.
for _, req := range cihRequiredPartitions {
key := partKey{name: req.Name, typeGUID: req.TypeGUID}
actualUUID, found := partMap[key]
if !found {
actual, found := partMap[req.Name]
if !found || !matchesTypeGUID(req, actual.typeGUID) {
return false
}
if req.UUID != "" && actualUUID != req.UUID {
if req.UUID != "" && actual.uuid != req.UUID {
return false
}
}

// Optional partitions: if present, their UUID must match.
// Optional partitions: if present, their type GUID and UUID must match.
for _, opt := range cihOptionalPartitions {
key := partKey{name: opt.Name, typeGUID: opt.TypeGUID}
actualUUID, found := partMap[key]
if found && opt.UUID != "" && actualUUID != opt.UUID {
actual, found := partMap[opt.Name]
if !found {
continue
}
if !matchesTypeGUID(opt, actual.typeGUID) {
continue
}
if opt.UUID != "" && actual.uuid != opt.UUID {
return false
}
}
Expand Down
Loading