mirror of
https://github.com/ultravioletrs/cocos.git
synced 2026-06-23 04:10:25 +00:00
NOISSUE - Add Rust script for fetching platform data (#133)
* add rust program for fetching platform data * fix new line error * add CLI options to add the measurement to platform_info.json file * add documentation for platform info testing * add explanation for sev-snp-measure * delete excess space * fix minor errors * fix minor errors * add file permision constant
This commit is contained in:
committed by
GitHub
parent
2ce112cc1b
commit
0574abc228
@@ -1,5 +1,6 @@
|
||||
BUILD_DIR = build
|
||||
SERVICES = manager agent cli
|
||||
PLATFORM_INFO = platform_info
|
||||
CGO_ENABLED ?= 0
|
||||
GOARCH ?= amd64
|
||||
VERSION ?= $(shell git describe --abbrev=0 --tags --always)
|
||||
@@ -17,13 +18,16 @@ define compile_service
|
||||
-o ${BUILD_DIR}/cocos-$(1) cmd/$(1)/main.go
|
||||
endef
|
||||
|
||||
.PHONY: all $(SERVICES)
|
||||
.PHONY: all $(SERVICES) $(PLATFORM_INFO)
|
||||
|
||||
all: $(SERVICES)
|
||||
|
||||
$(SERVICES):
|
||||
$(call compile_service,$(@))
|
||||
|
||||
$(PLATFORM_INFO):
|
||||
$(MAKE) -C ./scripts/platform_info
|
||||
|
||||
protoc:
|
||||
protoc -I. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative agent/agent.proto
|
||||
protoc -I. --go_out=./pkg --go_opt=paths=source_relative --go-grpc_out=./pkg --go-grpc_opt=paths=source_relative manager/manager.proto
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
// Copyright (c) Ultraviolet
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
package cli
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/google/go-sev-guest/proto/check"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
const filePermision = 0o755
|
||||
|
||||
type AttestationConfiguration struct {
|
||||
SNPPolicy *check.Policy `json:"snp_policy,omitempty"`
|
||||
RootOFTrust *check.RootOfTrust `json:"root_of_trust,omitempty"`
|
||||
}
|
||||
|
||||
func (cli *CLI) NewAddMeasurementCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "measurement",
|
||||
Short: "Add measurement to the platform info file. The value should be in base64. The second parameter is platform_info.json file",
|
||||
Example: "measurement <measurement> <platform_info.json>",
|
||||
Args: cobra.ExactArgs(2),
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
measurement, err := base64.StdEncoding.DecodeString(args[0])
|
||||
if err != nil {
|
||||
log.Fatalf("Error could not decode base64: %v", err)
|
||||
}
|
||||
|
||||
attestationConfiguration := AttestationConfiguration{}
|
||||
|
||||
manifest, err := os.OpenFile(args[1], os.O_RDWR, filePermision)
|
||||
if err != nil {
|
||||
log.Fatalf("Error opening the platform information file: %v", err)
|
||||
}
|
||||
defer manifest.Close()
|
||||
|
||||
decoder := json.NewDecoder(manifest)
|
||||
err = decoder.Decode(&attestationConfiguration)
|
||||
if err != nil {
|
||||
log.Fatalf("Error decoding the platform information file: %v", err)
|
||||
}
|
||||
|
||||
attestationConfiguration.SNPPolicy.Measurement = measurement
|
||||
if err = manifest.Truncate(0); err != nil {
|
||||
log.Fatalf("Error could not truncate platform information JSON file: %v", err)
|
||||
}
|
||||
|
||||
fileJson, err := json.MarshalIndent(attestationConfiguration, "", " ")
|
||||
if err != nil {
|
||||
log.Fatalf("Error marshaling the platform information JSON: %v", err)
|
||||
}
|
||||
if err = os.WriteFile(manifest.Name(), fileJson, filePermision); err != nil {
|
||||
log.Fatalf("Error writing into platform information JSON file: %v", err)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -90,7 +90,7 @@ func main() {
|
||||
attestaionCmd := cliSVC.NewAttestationCmd()
|
||||
rootCmd.AddCommand(attestaionCmd)
|
||||
rootCmd.AddCommand(cliSVC.NewFileHashCmd())
|
||||
rootCmd.AddCommand(cliSVC.NewKeysCmd())
|
||||
rootCmd.AddCommand(cliSVC.NewAddMeasurementCmd())
|
||||
|
||||
// Attestation commands
|
||||
attestaionCmd.AddCommand(cliSVC.NewGetAttestationCmd())
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
[package]
|
||||
name = "platform_info"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.0", features = ["derive"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
sev = "3.1.1"
|
||||
sysinfo = "0.30.12"
|
||||
regex = "1.10.4"
|
||||
@@ -0,0 +1,14 @@
|
||||
CARGO = cargo
|
||||
TARGET = target
|
||||
BUILD_DIR = $(TARGET)/release
|
||||
BIN_NAME = platform_info
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
$(CARGO) build --release
|
||||
|
||||
clean:
|
||||
$(CARGO) clean
|
||||
|
||||
.PHONY: all build clean
|
||||
@@ -0,0 +1,19 @@
|
||||
# Rust project for fetching platform info
|
||||
This rust project fetches information from the host system needed for validation of the attestation report. It outputs a JSON file that contains the said information.
|
||||
The JSON file is in a format that can be used with the [go-sev-guest](https://github.com/google/go-sev-guest) library.
|
||||
|
||||
## Usage
|
||||
Clone `cocos` repository:
|
||||
```bash
|
||||
git clone git@github.com:ultravioletrs/cocos.git
|
||||
cd ./cocos/scripts/platform_info
|
||||
make
|
||||
```
|
||||
|
||||
Then run the binary. Keep in mind that you have to specify the policy of the Guest VM:
|
||||
```bash
|
||||
cd ./target/releas
|
||||
|
||||
# Run with option --policy (policy is 64 bit number)
|
||||
./platform_info --policy 196608
|
||||
```
|
||||
@@ -0,0 +1,137 @@
|
||||
use clap::{Arg, Command, value_parser};
|
||||
use serde::Serialize;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use sysinfo::System;
|
||||
use regex::Regex;
|
||||
use sev::firmware::host::*;
|
||||
|
||||
const PLATFORM_INFO_JSON : &str = "platform_info.json";
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Vmpl {
|
||||
value : u32,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct SnpPolicy {
|
||||
policy: u64,
|
||||
family_id: Vec<u8>,
|
||||
image_id: Vec<u8>,
|
||||
vmpl: Vmpl,
|
||||
minimum_tcb: u64,
|
||||
minimum_launch_tcb: u64,
|
||||
require_author_key: bool,
|
||||
measurement: Vec<u8>,
|
||||
host_data: Vec<u8>,
|
||||
report_id_ma: Vec<u8>,
|
||||
chip_id: Vec<u8>,
|
||||
minimum_build: u32,
|
||||
minimum_version: String,
|
||||
permit_provisional_firmware: bool,
|
||||
require_id_block: bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct RootOfTrust {
|
||||
product: String,
|
||||
check_crl : bool,
|
||||
disallow_network : bool,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Computation {
|
||||
snp_policy: SnpPolicy,
|
||||
root_of_trust: RootOfTrust,
|
||||
}
|
||||
|
||||
fn get_product_name() -> String {
|
||||
let mut sys = System::new_all();
|
||||
sys.refresh_all();
|
||||
|
||||
let re = Regex::new(r"EPYC.*7..3.*").unwrap();
|
||||
|
||||
for cpu in sys.cpus() {
|
||||
if re.is_match(cpu.brand()) {
|
||||
return "Milan".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
"Unknown".to_string()
|
||||
}
|
||||
|
||||
fn get_uint64_from_tcb(tcb_version : &TcbVersion) -> u64 {
|
||||
let microcode = (tcb_version.microcode as u64) << 56;
|
||||
let snp = (tcb_version.snp as u64) << 48;
|
||||
let tee = (tcb_version.tee as u64) << 8;
|
||||
let bootloader = tcb_version.bootloader as u64;
|
||||
|
||||
microcode | snp | tee | bootloader
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let matches = Command::new("Platform info")
|
||||
.about("Processes command line options and outputs a JSON file for Attestation verification")
|
||||
.arg(Arg::new("policy")
|
||||
.long("policy")
|
||||
.value_name("INT")
|
||||
.help("Sets the policy integer")
|
||||
.required(true)
|
||||
.value_parser(value_parser!(u64)))
|
||||
.get_matches();
|
||||
|
||||
let mut firmware: Firmware = Firmware::open().unwrap();
|
||||
let status: SnpPlatformStatus = firmware.snp_platform_status().unwrap();
|
||||
|
||||
let policy: u64 = *matches.get_one::<u64>("policy").unwrap();
|
||||
let family_id = vec![0];
|
||||
let image_id = vec![0];
|
||||
let vmpl = Vmpl { value: 0};
|
||||
let minimum_tcb = get_uint64_from_tcb(&status.platform_tcb_version);
|
||||
let minimum_launch_tcb = get_uint64_from_tcb(&status.platform_tcb_version);
|
||||
let require_author_key = false;
|
||||
let measurement = vec![0];
|
||||
let host_data = vec![0];
|
||||
let report_id_ma = vec![0];
|
||||
let cpu_id: Identifier = firmware.get_identifier().unwrap();
|
||||
let chip_id: Vec<u8> = cpu_id.0;
|
||||
let minimum_build = status.build_id;
|
||||
let minimum_version = status.version.to_string();
|
||||
let permit_provisional_firmware = false;
|
||||
let require_id_block = false;
|
||||
|
||||
let snp_policy = SnpPolicy {
|
||||
policy,
|
||||
family_id,
|
||||
image_id,
|
||||
vmpl,
|
||||
minimum_tcb,
|
||||
minimum_launch_tcb,
|
||||
require_author_key,
|
||||
measurement,
|
||||
host_data,
|
||||
report_id_ma,
|
||||
chip_id,
|
||||
minimum_build,
|
||||
minimum_version,
|
||||
permit_provisional_firmware,
|
||||
require_id_block,
|
||||
};
|
||||
|
||||
let root_of_trust = RootOfTrust {
|
||||
product : get_product_name(),
|
||||
check_crl : true,
|
||||
disallow_network : false,
|
||||
};
|
||||
|
||||
let computation = Computation {
|
||||
snp_policy,
|
||||
root_of_trust,
|
||||
};
|
||||
|
||||
let json = serde_json::to_string_pretty(&computation).expect("Failed to serialize to JSON");
|
||||
let mut file = File::create(PLATFORM_INFO_JSON).expect("Failed to create file");
|
||||
file.write_all(json.as_bytes()).expect("Failed to write to file");
|
||||
|
||||
println!("Computation JSON has been written to {}", PLATFORM_INFO_JSON);
|
||||
}
|
||||
+27
-3
@@ -17,13 +17,37 @@ The algorithm program should return the results to a socket and an example can b
|
||||
|
||||
Agent is started automatically in the VM when launched but requires configuration and manifest to be passed by manager. Alternatively you can pass configuration using this [simplified script](./agent-config/main.go)
|
||||
|
||||
Open console on the host, and run
|
||||
For attested TLS, you will have to calculate the VM's measurement, which can be done using a tool [sev-snp-measure](https://pypi.org/project/sev-snp-measure/).
|
||||
|
||||
```bash
|
||||
# Define the path to the OVMF, KERNEL, INITRD and CMD Kernel line arguments.
|
||||
OVMF_CODE="/home/cocosai/ovmf/Build/AmdSev/DEBUG_GCC5/FV/OVMF.fd"
|
||||
INITRD="/home/cocosai/initramfs.cpio.gz"
|
||||
KERNEL="/home/cocosai/bzImage"
|
||||
LINE="earlyprintk=serial console=ttyS0"
|
||||
|
||||
# Call sev-snp-measure
|
||||
sev-snp-measure --mode snp --vcpus 4 --vcpu-type EPYC-v4 --ovmf $OVMF_CODE --kernel $KERNEL --initrd $INITRD --append "$LINE" --output-format base64
|
||||
```
|
||||
|
||||
```sh
|
||||
export AGENT_GRPC_URL=localhost:7002
|
||||
|
||||
# For attested TLS, also define the path to the computation.json that contains reference values for the fields of the attestation report
|
||||
export AGENT_GRPC_MANIFEST=./test/manual/computation/computation.json
|
||||
# For attested TLS, the CLI should also be aware of the VM measurement. To
|
||||
# add the measurement to the .json file that contains the information about
|
||||
# the platform, run CLI with the measurement in base64 format and the path
|
||||
# of the platform_info.json file.:
|
||||
go run cmd/cli/main.go measurement '<measurement>' '<platform_info.json>'
|
||||
|
||||
# The platform_info.json file can be generated using Rust by running:
|
||||
cd scripts/platform_info
|
||||
make
|
||||
sudo ./target/release/platform_info --policy 196608 # Default value of the policy should be 196608
|
||||
# The output file platform_info.json will be generated in the directory from which the executable has been called.
|
||||
cd ../..
|
||||
|
||||
# For attested TLS, also define the path to the platform_info.json that contains reference values for the fields of the attestation report
|
||||
export AGENT_GRPC_MANIFEST=./scripts/platform_info/platform_info.json
|
||||
export AGENT_GRPC_ATTESTED_TLS=true
|
||||
|
||||
# Retieve Attestation
|
||||
|
||||
Reference in New Issue
Block a user