diff --git a/tools/flavor-convert/Makefile b/tools/flavor-convert/Makefile new file mode 100644 index 00000000..4f83f1bc --- /dev/null +++ b/tools/flavor-convert/Makefile @@ -0,0 +1,5 @@ +GITTAG := $(shell git describe --tags --abbrev=0 2> /dev/null) +VERSION := $(or ${GITTAG}, v0.0.0) + +all: + go build -ldflags "-X main.BuildVersion=$(VERSION)" -o flavor-convert-$(VERSION) diff --git a/tools/flavor-convert/flavortemplates/default-esxi-tpm12.json b/tools/flavor-convert/flavortemplates/default-esxi-tpm12.json new file mode 100644 index 00000000..4f7cff18 --- /dev/null +++ b/tools/flavor-convert/flavortemplates/default-esxi-tpm12.json @@ -0,0 +1,79 @@ +{ + "id": "f7e3ee4c-944b-4030-9d32-94d22778954d", + "label": "default-esxi-tpm12", + "condition": [ + "//host_info/os_name//*[text()='VMware ESXi']", + "//host_info/hardware_features/TPM/meta/tpm_version//*[text()='1.2']" + ], + "flavor_parts": { + "PLATFORM": { + "meta": { + "tpm_version": "1.2" + }, + "pcr_rules": [ + { + "pcr": { + "index": 0, + "bank": "SHA1" + }, + "pcr_matches": true + }, + { + "pcr": { + "index": 17, + "bank": "SHA1" + }, + "pcr_matches": true + } + ] + }, + "OS": { + "meta": { + "tpm_version": "1.2" + }, + "pcr_rules": [ + { + "pcr": { + "index": 18, + "bank": "SHA1" + }, + "pcr_matches": true + }, + { + "pcr": { + "index": 19, + "bank": "SHA1" + }, + "eventlog_equals": { + "excluding_tags": [ + "commandLine." + ] + } + }, + { + "pcr": { + "index": 20, + "bank": "SHA1" + }, + "pcr_matches": true + } + ] + }, + "HOST_UNIQUE": { + "meta": { + "tpm_version": "1.2" + }, + "pcr_rules": [ + { + "pcr": { + "index": 19, + "bank": "SHA1" + }, + "eventlog_includes": [ + "commandLine." + ] + } + ] + } + } +} \ No newline at end of file diff --git a/tools/flavor-convert/flavortemplates/default-esxi-tpm20.json b/tools/flavor-convert/flavortemplates/default-esxi-tpm20.json new file mode 100644 index 00000000..78b5935a --- /dev/null +++ b/tools/flavor-convert/flavortemplates/default-esxi-tpm20.json @@ -0,0 +1,104 @@ +{ + "label": "default-esxi-tpm20", + "condition": [ + "//host_info/os_name//*[text()='VMware ESXi']", + "//host_info/hardware_features/TPM/meta/tpm_version//*[text()='2.0']" + ], + "flavor_parts": { + "PLATFORM": { + "meta": { + "tpm_version": "2.0" + }, + "pcr_rules": [ + { + "pcr": { + "index": 0, + "bank": "SHA256" + }, + "pcr_matches": true, + "eventlog_equals": {} + }, + { + "pcr": { + "index": 17, + "bank": "SHA256" + }, + "pcr_matches": true, + "eventlog_equals": {} + }, + { + "pcr": { + "index": 18, + "bank": "SHA256" + }, + "pcr_matches": true, + "eventlog_equals": {} + } + ] + }, + "OS": { + "meta": { + "tpm_version": "2.0" + }, + "pcr_rules": [ + { + "pcr": { + "index": 19, + "bank": "SHA256" + }, + "pcr_matches": true, + "eventlog_equals": {} + }, + { + "pcr": { + "index": 20, + "bank": "SHA256" + }, + "eventlog_equals": { + "excluding_tags": [ + "componentName.imgdb.tgz", + "componentName.onetime.tgz" + ] + } + }, + { + "pcr": { + "index": 21, + "bank": "SHA256" + }, + "eventlog_equals": { + "excluding_tags": [ + "commandLine." + ] + } + } + ] + }, + "HOST_UNIQUE": { + "meta": { + "tpm_version": "2.0" + }, + "pcr_rules": [ + { + "pcr": { + "index": 20, + "bank": "SHA256" + }, + "eventlog_includes": [ + "componentName.imgdb.tgz", + "componentName.onetime.tgz" + ] + }, + { + "pcr": { + "index": 21, + "bank": "SHA256" + }, + "eventlog_includes": [ + "commandLine." + ] + } + ] + } + } +} \ No newline at end of file diff --git a/tools/flavor-convert/flavortemplates/linux_tpm20_cbnt.template.json b/tools/flavor-convert/flavortemplates/linux_tpm20_cbnt.template.json new file mode 100644 index 00000000..e05d7012 --- /dev/null +++ b/tools/flavor-convert/flavortemplates/linux_tpm20_cbnt.template.json @@ -0,0 +1,33 @@ +{ + "id": "f7e3ee4c-944b-4030-9d32-94d22773573d", + "label": "linux-tpm20-cbnt", + "condition": [ + "//host_info/os_name//*[text()='RedHatEnterprise']", + "//host_info/hardware_features/TPM/meta/tpm_version//*[text()='2.0']", + "//host_info/hardware_features/CBNT/enabled//*[text()='true']" + ], + "flavor_parts": { + "PLATFORM": { + "meta": { + "tpm_version": "2.0", + "cbnt_enabled": true + }, + "pcr_rules": [ + { + "pcr": { + "index": 0, + "bank": "SHA256" + }, + "pcr_matches": true + }, + { + "pcr": { + "index": 7, + "bank": "SHA256" + }, + "pcr_matches": true + } + ] + } + } +} \ No newline at end of file diff --git a/tools/flavor-convert/flavortemplates/linux_tpm20_suefi.template.json b/tools/flavor-convert/flavortemplates/linux_tpm20_suefi.template.json new file mode 100644 index 00000000..05d222a7 --- /dev/null +++ b/tools/flavor-convert/flavortemplates/linux_tpm20_suefi.template.json @@ -0,0 +1,75 @@ +{ + "id": "aa30e267-1925-4790-bc90-ef28c6d21516", + "label": "linux-tpm20-suefi", + "condition": [ + "//host_info/os_name//*[text()='RedHatEnterprise']", + "//host_info/hardware_features/TPM/meta/tpm_version//*[text()='2.0']", + "//host_info/hardware_features/UEFI/meta/secure_boot_enabled//*[text()='true']" + ], + "flavor_parts": { + "PLATFORM": { + "meta": { + "tpm_version": "2.0", + "cbnt_enabled": true + }, + "pcr_rules": [ + { + "pcr": { + "index": 0, + "bank": "SHA256" + }, + "pcr_matches": true + }, + { + "pcr": { + "index": 1, + "bank": "SHA256" + }, + "pcr_matches": true + }, + { + "pcr": { + "index": 2, + "bank": "SHA256" + }, + "pcr_matches": true + }, + { + "pcr": { + "index": 3, + "bank": "SHA256" + }, + "pcr_matches": true + }, + { + "pcr": { + "index": 4, + "bank": "SHA256" + }, + "pcr_matches": true + }, + { + "pcr": { + "index": 5, + "bank": "SHA256" + }, + "pcr_matches": true + }, + { + "pcr": { + "index": 6, + "bank": "SHA256" + }, + "pcr_matches": true + }, + { + "pcr": { + "index": 7, + "bank": "SHA256" + }, + "pcr_matches": true + } + ] + } + } +} \ No newline at end of file diff --git a/tools/flavor-convert/flavortemplates/linux_tpm20_tboot.template.json b/tools/flavor-convert/flavortemplates/linux_tpm20_tboot.template.json new file mode 100644 index 00000000..3f5dcb3e --- /dev/null +++ b/tools/flavor-convert/flavortemplates/linux_tpm20_tboot.template.json @@ -0,0 +1,101 @@ +{ + "id": "ab2a4f35-6d6e-4fd3-bcb8-1ae57576e3c7", + "label": "linux-tpm20-tboot", + "condition": [ + "//host_info/os_name//*[text()='RedHatEnterprise']", + "//host_info/hardware_features/TPM/meta/tpm_version//*[text()='2.0']", + "//host_info/tboot_installed//*[text()='true']" + ], + "flavor_parts": { + "PLATFORM": { + "meta": { + "tpm_version": "2.0", + "tboot_installed": true + }, + "pcr_rules": [ + { + "pcr": { + "index": 0, + "bank": "SHA256" + }, + "pcr_matches": true + }, + { + "pcr": { + "index": 17, + "bank": "SHA256" + }, + "pcr_matches": true, + "eventlog_equals": { + "excluding_tags": [ + "LCP_CONTROL_HASH", + "initrd", + "vmlinuz" + ] + } + }, + { + "pcr": { + "index": 18, + "bank": "SHA256" + }, + "pcr_matches": true, + "eventlog_equals": { + "excluding_tags": [ + "LCP_CONTROL_HASH", + "initrd", + "vmlinuz" + ] + } + } + ] + }, + "OS": { + "meta": { + "tpm_version": "2.0", + "tboot_installed": true + }, + "pcr_rules": [ + { + "pcr": { + "index": 17, + "bank": "SHA256" + }, + "pcr_matches": true, + "eventlog_includes": [ + "vmlinuz" + ] + } + ] + }, + "HOST_UNIQUE": { + "meta": { + "tpm_version": "2.0", + "tboot_installed": true + }, + "pcr_rules": [ + { + "pcr": { + "index": 17, + "bank": "SHA256" + }, + "pcr_matches": true, + "eventlog_includes": [ + "LCP_CONTROL_HASH", + "initrd" + ] + }, + { + "pcr": { + "index": 18, + "bank": "SHA256" + }, + "pcr_matches": true, + "eventlog_includes": [ + "LCP_CONTROL_HASH" + ] + } + ] + } + } +} \ No newline at end of file diff --git a/tools/flavor-convert/go.mod b/tools/flavor-convert/go.mod new file mode 100644 index 00000000..31d9c10a --- /dev/null +++ b/tools/flavor-convert/go.mod @@ -0,0 +1,13 @@ +module github.com/flavor-convert + +require ( + github.com/antchfx/jsonquery v1.1.4 + github.com/google/uuid v1.2.0 + github.com/intel-secl/intel-secl/v3 v3.5.0 + github.com/stretchr/testify v1.7.0 // indirect + gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect + github.com/vmware/govmomi v0.22.2 +) + +replace github.com/intel-secl/intel-secl/v3 => github.com/pkumarex/intel-secl/v3 v3.5.1-0.20210331131052-c048288e5d78 +replace github.com/vmware/govmomi => github.com/arijit8972/govmomi fix-tpm-attestation-output diff --git a/tools/flavor-convert/main.go b/tools/flavor-convert/main.go new file mode 100644 index 00000000..cb736f82 --- /dev/null +++ b/tools/flavor-convert/main.go @@ -0,0 +1,446 @@ +/* + * Copyright (C) 2021 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + */ + +package main + +import ( + "encoding/json" + "flag" + "fmt" + "io/ioutil" + "os" + "path/filepath" + "reflect" + "strconv" + "strings" + + "github.com/antchfx/jsonquery" + connector "github.com/intel-secl/intel-secl/v3/pkg/lib/host-connector" + "github.com/intel-secl/intel-secl/v3/pkg/lib/host-connector/types" + "github.com/intel-secl/intel-secl/v3/pkg/model/hvs" +) + +// EventIDList - define map for event id +var eventIDList = map[string]string{ + "PCR_MAPPING": "0x401", + "HASH_START": "0x402", + "COMBINED_HASH": "0x403", + "MLE_HASH": "0x404", + "BIOSAC_REG_DATA": "0x40a", + "CPU_SCRTM_STAT": "0x40b", + "LCP_CONTROL_HASH": "0x40c", + "ELEMENTS_HASH": "0x40d", + "STM_HASH": "0x40e", + "OSSINITDATA_CAP_HASH": "0x40f", + "SINIT_PUBKEY_HASH": "0x410", + "LCP_HASH": "0x411", + "LCP_DETAILS_HASH": "0x412", + "LCP_AUTHORITIES_HASH": "0x413", + "NV_INFO_HASH": "0x414", + "EVTYPE_KM_HASH": "0x416", + "EVTYPE_BPM_HASH": "0x417", + "EVTYPE_KM_INFO_HASH": "0x418", + "EVTYPE_BPM_INFO_HASH": "0x419", + "EVTYPE_BOOT_POL_HASH": "0x41a", + "CAP_VALUE": "0x4ff", + "tb_policy": "0x501", + "vmlinuz": "0x501", + "initrd": "0x501", + "asset-tag": "0x501", +} + +const ( + intelVendor = "INTEL" + vmwareVendor = "VMWARE" + + platformFlavor = "PLATFORM" + osFlavor = "OS" + hostUniqueFlavor = "HOST_UNIQUE" +) + +var BuildVersion string + +const helpStr = `Usage: +flavor-convert [argument] + +Available Command: + -o To provide old flavor part json filepath + -n To provide new flavor part json filepath + -h|--help Show this help message + -version Print the current version +` + +//To map the conditions in the flavor template with old flavor part +var flavorTemplateConditions = map[string]string{"//host_info/tboot_installed//*[text()='true']": "//meta/description/tboot_installed//*[text()='true']", + "//host_info/hardware_features/UEFI/meta/secure_boot_enabled//*[text()='true']": "//hardware/feature/SUEFI/enabled//*[text()='true']", + "//host_info/hardware_features/CBNT/enabled//*[text()='true']": "//hardware/feature/CBNT/enabled//*[text()='true']", + "//host_info/os_name//*[text()='RedHatEnterprise']": "//meta/vendor//*[text()='INTEL']", + "//host_info/os_name//*[text()='VMware ESXi']": "//meta/vendor//*[text()='VMWARE']", + "//host_info/hardware_features/TPM/meta/tpm_version//*[text()='2.0']": "//meta/description/tpm_version//*[text()='2.0']", + "//host_info/hardware_features/TPM/meta/tpm_version//*[text()='1.2']": "//meta/description/tpm_version//*[text()='1.2']"} + +var flavorTemplatePath = "./flavortemplates" + +//getFlavorTemplates method is used to get the flavor templates based on old flavor part file +func getFlavorTemplates(body []byte) ([]hvs.FlavorTemplate, error) { + var defaultFlavorTemplates []string + + //read the flavor template file + templates, err := ioutil.ReadDir(flavorTemplatePath) + if err != nil { + return nil, fmt.Errorf("Error in reading flavor template files") + } + for _, template := range templates { + path := flavorTemplatePath + "/" + template.Name() + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("Error in reading the template file - ", template.Name()) + } + defaultFlavorTemplates = append(defaultFlavorTemplates, string(data)) + } + + // finding the correct template to apply + filteredTemplate, err := findTemplatesToApply(body, defaultFlavorTemplates) + if err != nil { + return nil, fmt.Errorf("Error in getting the template file based on old flavorpart") + } + + return filteredTemplate, nil +} + +//findTemplatesToApply method is used to find the correct templates to apply to convert flavor part +func findTemplatesToApply(oldFlavorPart []byte, defaultFlavorTemplates []string) ([]hvs.FlavorTemplate, error) { + var filteredTemplates []hvs.FlavorTemplate + var conditionEval bool + + oldFlavorPartJson, err := jsonquery.Parse(strings.NewReader(string(oldFlavorPart))) + if err != nil { + return nil, fmt.Errorf("Error in parsing the old flavor part json") + } + + for _, template := range defaultFlavorTemplates { + flavorTemplate := hvs.FlavorTemplate{} + + err := json.Unmarshal([]byte(template), &flavorTemplate) + if err != nil { + return nil, fmt.Errorf("Error in unmarshaling the flavor template") + } + if flavorTemplate.Label == "" { + continue + } + conditionEval = false + + for _, condition := range flavorTemplate.Condition { + conditionEval = true + flavorPartCondition := flavorTemplateConditions[condition] + expectedData, _ := jsonquery.Query(oldFlavorPartJson, flavorPartCondition) + if expectedData == nil { + conditionEval = false + break + } + } + if conditionEval == true { + filteredTemplates = append(filteredTemplates, flavorTemplate) + } + } + + return filteredTemplates, nil +} + +//checkIfValidFile method is used to check if the given input file path is valid or not +func checkIfValidFile(filename string) (bool, error) { + // Checking if the input file is json + if fileExtension := filepath.Ext(filename); fileExtension != ".json" { + return false, fmt.Errorf("File '%s' is not json", filename) + } + + // Checking if filepath entered belongs to an existing file + if _, err := os.Stat(filename); err != nil && os.IsNotExist(err) { + return false, fmt.Errorf("File %s does not exist", filename) + } + + // returns true if this is a valid file + return true, nil +} + +//main method implements migration of old format of flavor part to new format +func main() { + oldFlavorPartFilePath := flag.String("o", "", "old flavor part json file") + versionFlag := flag.Bool("version", false, "Print the current version and exit") + newFlavorPartFilePath := flag.String("n", "", "new flavor part json file") + + // Showing useful information when the user enters the -h|--help option + flag.Usage = func() { + if len(os.Args) <= 2 && !*versionFlag && *oldFlavorPartFilePath == "" { + fmt.Println(helpStr) + } else { + fmt.Println("Invalid Command Usage") + fmt.Printf(helpStr) + } + } + flag.Parse() + + // Show the current version when the user enters the -version option + if *versionFlag && *oldFlavorPartFilePath != "" { + fmt.Println("Invalid Command Usage") + fmt.Printf(helpStr) + os.Exit(1) + } else if *versionFlag && *oldFlavorPartFilePath == "" { + fmt.Println("Current build version: ", BuildVersion) + os.Exit(1) + } else if *oldFlavorPartFilePath == "" { + // Checks for the file data that was entered by the user + fmt.Println("Error: Old flavor part json file path is missing") + fmt.Printf(helpStr) + os.Exit(1) + } + + // Validating the old flavor part file path entered + if valid, err := checkIfValidFile(*oldFlavorPartFilePath); err != nil && !valid { + fmt.Println("Error in validating the input file path - ", err) + os.Exit(1) + } + + //reading the data from oldFlavorPartFilePath + body, err := ioutil.ReadFile(*oldFlavorPartFilePath) + if err != nil { + fmt.Println("Error in reading the old flavor part file data") + os.Exit(1) + } + + //get the flavor template based on old flavor part file + templates, err := getFlavorTemplates(body) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + var oldFlavorPart OldFlavorPart + + //unmarshaling the old flavor part file into OldFlavorPart struct + err = json.Unmarshal(body, &oldFlavorPart) + if err != nil { + fmt.Println("Error in unmarshaling the old flavor part file", err) + os.Exit(1) + } + + for flavorIndex, flavor := range oldFlavorPart.SignedFlavor { + //Updating meta section + if flavor.Flavor.Hardware != nil && flavor.Flavor.Hardware.Feature.CBNT.Enabled != nil && flavor.Flavor.Hardware.Feature.CBNT.Enabled.(bool) { + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Meta.Description.CbntEnabled = true + } else if flavor.Flavor.Hardware != nil && flavor.Flavor.Hardware.Feature.SUEFI != nil && flavor.Flavor.Hardware.Feature.SUEFI.Enabled { + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Meta.Description.SuefiEnabled = true + } + + //Updating hardware section + if flavor.Flavor.Hardware != nil { + //TXT + if flavor.Flavor.Hardware.Feature.TXT.Enabled != nil { + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.TXT.Enabled = strconv.FormatBool(flavor.Flavor.Hardware.Feature.TXT.Enabled.(bool)) + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.TXT.Supported = oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.TXT.Enabled.(string) + } else { + //if the TXT section not present in oldflavorpart json,assign false to it + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.TXT.Enabled = "false" + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.TXT.Supported = "false" + } + + //TPM + if flavor.Flavor.Hardware.Feature.TPM.Enabled != nil { + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.TPM.Enabled = strconv.FormatBool(flavor.Flavor.Hardware.Feature.TPM.Enabled.(bool)) + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.TPM.Supported = oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.TPM.Enabled.(string) + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.TPM.Meta.TPMVersion = flavor.Flavor.Hardware.Feature.TPM.Version + flavor.Flavor.Hardware.Feature.TPM.Version = "" + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.TPM.Meta.PCRBanks = flavor.Flavor.Hardware.Feature.TPM.PcrBanks + flavor.Flavor.Hardware.Feature.TPM.PcrBanks = nil + } else { + //if the TPM section not present in oldflavorpart json,assign false to it + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.TPM.Enabled = "false" + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.TPM.Supported = "false" + } + + //CBNT + if flavor.Flavor.Hardware.Feature.CBNT.Enabled != nil { + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.CBNT.Enabled = strconv.FormatBool(flavor.Flavor.Hardware.Feature.CBNT.Enabled.(bool)) + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.CBNT.Supported = oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.CBNT.Enabled.(string) + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.CBNT.Meta.Profile = flavor.Flavor.Hardware.Feature.CBNT.Profile + flavor.Flavor.Hardware.Feature.CBNT.Profile = "" + } else { + //if the CBNT section not present in oldflavorpart json,assign false to it + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.CBNT.Enabled = "false" + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.CBNT.Supported = "false" + } + + //UEFI + if flavor.Flavor.Hardware.Feature.SUEFI != nil { + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.UEFI.Enabled = flavor.Flavor.Hardware.Feature.SUEFI.Enabled + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.UEFI.Supported = flavor.Flavor.Hardware.Feature.SUEFI.Enabled + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Hardware.Feature.UEFI.Meta.SecureBootEnabled = flavor.Flavor.Hardware.Feature.SUEFI.Enabled + flavor.Flavor.Hardware.Feature.SUEFI = nil + } + } + + //removing the signature from the flavors + //since the final flavor part file is not a signed flavor(only the flavor collection) + oldFlavorPart.SignedFlavor[flavorIndex].Signature = "" + + // Copying the pcrs sections from old flavor part to new flavor part + if flavor.Flavor.Pcrs == nil { + continue + } + + for _, template := range templates { + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Meta.Description.FlavorTemplateIds = append(oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Meta.Description.FlavorTemplateIds, template.ID) + flavorname := flavor.Flavor.Meta.Description.FlavorPart + rules, pcrsmap := getPcrRules(flavorname, template) + if rules != nil && pcrsmap != nil { + //Update PCR section + flavor.Flavor.PcrLogs = updatePcrSection(flavor.Flavor.Pcrs, rules, pcrsmap, flavor.Flavor.Meta.Vendor) + } else { + continue + } + } + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.Pcrs = nil + oldFlavorPart.SignedFlavor[flavorIndex].Flavor.PcrLogs = flavor.Flavor.PcrLogs + } + + //getting the final data + finalFlavorPart, err := json.Marshal(oldFlavorPart.SignedFlavor) + if err != nil { + fmt.Println("Error in marshaling the final flavor part file") + os.Exit(1) + } + + //Printing the final flavor part file in console + fmt.Println("New flavor part json:\n", string(finalFlavorPart)) + + //writing the new flavor part into the local file + if *newFlavorPartFilePath != "" { + // Checking if the output file path is json + if fileExtension := filepath.Ext(*newFlavorPartFilePath); fileExtension != ".json" { + fmt.Println("\nError in validating the new flavor part file path - the file '%s' is not json: ", *newFlavorPartFilePath) + os.Exit(1) + } + data := []byte(finalFlavorPart) + err = ioutil.WriteFile(*newFlavorPartFilePath, data, 0644) + if err != nil { + fmt.Println("Error in writing the new flavor part json in local file") + os.Exit(1) + } + } +} + +//updatePcrSection method is used to update the pcr section in new flavor part +func updatePcrSection(Pcrs map[string]map[string]PcrEx, rules []hvs.PcrRules, pcrsmap map[int]string, vendor string) []types.FlavorPcrs { + var newFlavorPcrs []types.FlavorPcrs + newFlavorPcrs = make([]types.FlavorPcrs, len(pcrsmap)) + + for bank, pcrMap := range Pcrs { + index := 0 + for mapIndex, templateBank := range pcrsmap { + pcrIndex := types.PcrIndex(mapIndex) + + if types.SHAAlgorithm(bank) != types.SHAAlgorithm(templateBank) { + break + } + if expectedPcrEx, ok := pcrMap[pcrIndex.String()]; ok { + newFlavorPcrs[index].Pcr.Index = mapIndex + newFlavorPcrs[index].Pcr.Bank = bank + newFlavorPcrs[index].Measurement = expectedPcrEx.Value + if rules[index].PcrMatches != nil { + newFlavorPcrs[index].PCRMatches = *rules[index].PcrMatches + } + + var newTpmEvents []types.EventLog + + if rules[index].Pcr.Index == newFlavorPcrs[index].Pcr.Index && + rules[index].EventlogEquals != nil && expectedPcrEx.Event != nil && !reflect.ValueOf(rules[index].EventlogEquals).IsZero() { + newFlavorPcrs[index].EventlogEqual = new(types.EventLogEqual) + if rules[index].EventlogEquals.ExcludingTags != nil { + newFlavorPcrs[index].EventlogEqual.ExcludeTags = rules[index].EventlogEquals.ExcludingTags + } + + newTpmEvents = make([]types.EventLog, len(expectedPcrEx.Event)) + newTpmEvents = updateTpmEvents(expectedPcrEx.Event, newTpmEvents, vendor) + newFlavorPcrs[index].EventlogEqual.Events = newTpmEvents + newTpmEvents = nil + } + + if rules[index].Pcr.Index == newFlavorPcrs[index].Pcr.Index && rules[index].EventlogIncludes != nil && expectedPcrEx.Event != nil && !reflect.ValueOf(rules[index].EventlogIncludes).IsZero() { + newTpmEvents = make([]types.EventLog, len(expectedPcrEx.Event)) + newTpmEvents = updateTpmEvents(expectedPcrEx.Event, newTpmEvents, vendor) + newFlavorPcrs[index].EventlogIncludes = newTpmEvents + newTpmEvents = nil + } + } + index++ + } + } + + return newFlavorPcrs +} + +//getPcrRules method is used to get the pcr rules defined in the flavor template +func getPcrRules(flavorName string, template hvs.FlavorTemplate) ([]hvs.PcrRules, map[int]string) { + pcrsmap := make(map[int]string) + var rules []hvs.PcrRules + + if flavorName == platformFlavor && template.FlavorParts.Platform != nil { + for _, rules := range template.FlavorParts.Platform.PcrRules { + pcrsmap[rules.Pcr.Index] = rules.Pcr.Bank + } + rules = template.FlavorParts.Platform.PcrRules + return rules, pcrsmap + } else if flavorName == osFlavor && template.FlavorParts.OS != nil { + for _, rules := range template.FlavorParts.OS.PcrRules { + pcrsmap[rules.Pcr.Index] = rules.Pcr.Bank + } + rules = template.FlavorParts.OS.PcrRules + return rules, pcrsmap + } else if flavorName == hostUniqueFlavor && template.FlavorParts.HostUnique != nil { + for _, rules := range template.FlavorParts.HostUnique.PcrRules { + pcrsmap[rules.Pcr.Index] = rules.Pcr.Bank + } + rules = template.FlavorParts.HostUnique.PcrRules + return rules, pcrsmap + } + return nil, nil +} + +//updateTpmEvents This method is used to update the tpm events +func updateTpmEvents(expectedPcrEvent []EventLog, newTpmEvents []types.EventLog, vendor string) []types.EventLog { + //Updating the old event format into new event format + for eventIndex, oldEvents := range expectedPcrEvent { + if vendor == intelVendor { + newTpmEvents[eventIndex].TypeName = oldEvents.Label + newTpmEvents[eventIndex].Tags = append(newTpmEvents[eventIndex].Tags, oldEvents.Label) + newTpmEvents[eventIndex].Measurement = oldEvents.Value + newTpmEvents[eventIndex].TypeID = eventIDList[oldEvents.Label] + } else if vendor == vmwareVendor { + if oldEvents.Info["PackageName"] != "" { + newTpmEvents[eventIndex].Tags = append(newTpmEvents[eventIndex].Tags, oldEvents.Info["ComponentName"], oldEvents.Info["EventName"]+"_"+oldEvents.Info["PackageName"]+"_"+oldEvents.Info["PackageVendor"]) + } else { + newTpmEvents[eventIndex].Tags = append(newTpmEvents[eventIndex].Tags, oldEvents.Info["ComponentName"], oldEvents.Info["EventName"]) + } + newTpmEvents[eventIndex].TypeName = oldEvents.Label + newTpmEvents[eventIndex].Measurement = oldEvents.Value + + switch oldEvents.Info["EventType"] { + case connector.TPM_SOFTWARE_COMPONENT_EVENT_TYPE: + newTpmEvents[eventIndex].TypeID = connector.VIB_NAME_TYPE_ID + case connector.TPM_COMMAND_EVENT_TYPE: + newTpmEvents[eventIndex].TypeID = connector.COMMANDLINE_TYPE_ID + case connector.TPM_OPTION_EVENT_TYPE: + newTpmEvents[eventIndex].TypeID = connector.OPTIONS_FILE_NAME_TYPE_ID + case connector.TPM_BOOT_SECURITY_OPTION_EVENT_TYPE: + newTpmEvents[eventIndex].TypeID = connector.BOOT_SECURITY_OPTION_TYPE_ID + } + } else { + fmt.Println("UNKNOWN VENDOR - unable to update tpm events") + os.Exit(1) + } + } + + return newTpmEvents +} diff --git a/tools/flavor-convert/old_flavorpart.go b/tools/flavor-convert/old_flavorpart.go new file mode 100644 index 00000000..1cbf6c31 --- /dev/null +++ b/tools/flavor-convert/old_flavorpart.go @@ -0,0 +1,212 @@ +/* + * Copyright (C) 2020 Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + */ +package main + +import ( + "time" + + uuid "github.com/google/uuid" + "github.com/intel-secl/intel-secl/v3/pkg/lib/host-connector/types" +) + +type OldFlavorPart struct { + SignedFlavor []SignedFlavors `json:"signed_flavors"` +} + +type SignedFlavors struct { + Flavor Flavor `json:"flavor,omitempty"` + Signature string `json:"signature,omitempty"` +} + +type Flavor struct { + // Meta section is mandatory for all Flavor types + Meta Meta `json:"meta"` + Bios *Bios `json:"bios,omitempty"` + // Hardware section is unique to Platform Flavor type + Hardware *Hardware `json:"hardware,omitempty"` + Pcrs map[string]map[string]PcrEx `json:"pcrs,omitempty"` + PcrLogs []types.FlavorPcrs `json:"pcr_logs,omitempty"` + // External section is unique to AssetTag Flavor type + External *External `json:"external,omitempty"` + Software *Software `json:"software,omitempty"` +} + +type Meta struct { + Schema *Schema `json:"schema,omitempty"` + ID uuid.UUID `json:"id"` + Realm string `json:"realm,omitempty"` + Description Description `json:"description,omitempty"` + Vendor string `json:"vendor,omitempty"` +} + +type PcrEx struct { + Value string `json:"value"` + Event []EventLog `json:"event,omitempty"` +} + +type Bios struct { + BiosName string `json:"bios_name,omitempty"` + BiosVersion string `json:"bios_version,omitempty"` +} + +type Hardware struct { + Vendor string `json:"vendor,omitempty"` + ProcessorInfo string `json:"processor_info,omitempty"` + ProcessorFlags string `json:"processor_flags,omitempty"` + Feature *Feature `json:"feature,omitempty"` +} + +type External struct { + AssetTag AssetTag `json:"asset_tag,omitempty"` +} + +type Software struct { + Measurements map[string]FlavorMeasurement `json:"measurements,omitempty"` + CumulativeHash string `json:"cumulative_hash,omitempty"` +} + +type FlavorMeasurement struct { + Type MeasurementType `json:"type"` + Value string `json:"value"` + Path string `json:"Path"` + Include string `json:"Include,omitempty"` + Exclude string `json:"Exclude,omitempty"` + SearchType string `json:"SearchType,omitempty"` + FilterType string `json:"FilterType,omitempty"` +} + +type MeasurementType string + +const ( + MeasurementTypeFile MeasurementType = "fileMeasurementType" + MeasurementTypeDir MeasurementType = "directoryMeasurementType" + MeasurementTypeSymlink MeasurementType = "symlinkMeasurementType" +) + +type Schema struct { + Uri string `json:"uri,omitempty"` +} + +type Description struct { + FlavorPart string `json:"flavor_part,omitempty"` + Source string `json:"source,omitempty"` + Label string `json:"label,omitempty"` + IPAddress string `json:"ip_address,omitempty"` + BiosName string `json:"bios_name,omitempty"` + BiosVersion string `json:"bios_version,omitempty"` + OsName string `json:"os_name,omitempty"` + OsVersion string `json:"os_version,omitempty"` + VmmName string `json:"vmm_name,omitempty"` + VmmVersion string `json:"vmm_version,omitempty"` + TpmVersion string `json:"tpm_version,omitempty"` + HardwareUUID *uuid.UUID `json:"hardware_uuid,omitempty"` + Comment string `json:"comment,omitempty"` + TbootInstalled bool `json:"tboot_installed,string,omitempty"` + CbntEnabled bool `json:"cbnt_enabled,string,omitempty"` + SuefiEnabled bool `json:"suefi_enabled,string,omitempty"` + DigestAlgorithm string `json:"digest_algorithm,omitempty"` + FlavorTemplateIds []uuid.UUID `json:"flavor_template_ids,omitempty"` + Vendor string `json:"vendor,omitempty"` +} + +type EventLog struct { + DigestType string `json:"digest_type"` + Value string `json:"value"` + Label string `json:"label"` + Info map[string]string `json:"info"` +} + +type Feature struct { + AES_NI *AES_NI `json:"AES_NI,omitempty"` + SUEFI *SUEFI `json:"SUEFI,omitempty"` + TXT TXT `json:"TXT"` + TPM TPM `json:"TPM"` + CBNT CBNT `json:"CBNT"` + UEFI UEFI `json:"UEFI"` + PFR HardwareFeature `json:"PFR"` + BMC HardwareFeature `json:"BMC"` +} + +type AES_NI struct { + Supported bool `json:"supported,omitempty"` + Enabled bool `json:"enabled,omitempty"` +} + +type TXT struct { + Supported string `json:"supported"` + Enabled interface{} `json:"enabled"` +} + +type TPM struct { + Supported string `json:"supported"` + Enabled interface{} `json:"enabled"` + Version string `json:"version,omitempty"` + PcrBanks []string `json:"pcr_banks,omitempty"` + Meta struct { + TPMVersion string `json:"tpm_version"` + PCRBanks []string `json:"pcr_banks"` + } `json:"meta"` +} + +type CBNT struct { + Supported string `json:"supported"` + Enabled interface{} `json:"enabled"` + Profile string `json:"profile,omitempty"` + Meta struct { + Profile string `json:"profile"` + MSR string `json:"msr"` + } `json:"meta"` +} + +type UEFI struct { + HardwareFeature + Meta struct { + SecureBootEnabled bool `json:"secure_boot_enabled"` + } `json:"meta"` +} + +type SUEFI struct { + Supported bool `json:"supported,omitempty"` + Enabled bool `json:"enabled,omitempty"` +} + +type HardwareFeature struct { + Supported bool `json:"supported,string"` + Enabled bool `json:"enabled,string"` +} + +type AssetTag struct { + TagCertificate X509AttributeCertificate `json:"tag_certificate"` +} + +// X509AttributeCertificate holds a subset of x509.Certificate that has relevant information for Flavor +type X509AttributeCertificate struct { + Encoded []byte `json:"encoded"` + Issuer string `json:"issuer"` + SerialNumber int64 `json:"serial_number"` + Subject string `json:"subject"` + NotBefore time.Time `json:"not_before"` + NotAfter time.Time `json:"not_after"` + Attributes []Attribute `json:"attribute,omitempty"` + FingerprintSha384 string `json:"fingerprint_sha384"` +} + +// Attribute is used to store the custom Asset Tags embedded in the tag certificate +type Attribute struct { + AttrType struct { + ID string `json:"id"` + } `json:"attr_type"` + AttributeValues []AttrObjects `json:"attribute_values,omitempty"` +} + +// AttrObject holds the individual TagKeyValue Pair - TagKVAttribute which is decoded from ASN.1 values +type AttrObjects struct { + KVPair TagKvAttribute `json:"objects"` +} + +type TagKvAttribute struct { + Key string `json:"name"` + Value string `json:"value"` +}