Files
story/cmd/sim-device/firmware_evidence.go
T

106 lines
4.1 KiB
Go

package main
import (
"encoding/json"
"errors"
"fmt"
"os"
"strings"
"time"
"data-terminal-cloud/internal/firmwareconformance"
)
func recordSimulatedFirmwareEvidence(command simulatedDeviceCommand, manifest simulatedFirmwareManifest, phase string, state simulatedFirmwareBootState, resultCode string) error {
path := strings.TrimSpace(os.Getenv("SIM_FIRMWARE_EVIDENCE_FILE"))
if path == "" {
return nil
}
now := time.Now().UTC()
var evidence firmwareconformance.DeviceEvidence
content, err := os.ReadFile(path)
if errors.Is(err, os.ErrNotExist) {
partitionDigest := strings.ToLower(env("SIM_PARTITION_TABLE_SHA256", firmwareconformance.SHA256([]byte(env("SIM_DEVICE_MODEL", "simulated-terminal")+":"+env("SIM_DEVICE_HARDWARE_REVISION", "sim-rev-a")+":A/B"))))
secureBoot, parseErr := simulatedEvidenceBoolean("SIM_SECURE_BOOT_ENABLED", false)
if parseErr != nil {
return parseErr
}
evidence = firmwareconformance.DeviceEvidence{
SchemaVersion: firmwareconformance.DeviceEvidenceSchema, EvidenceSource: "SIMULATOR",
CandidateVersion: env("SIM_CANDIDATE_VERSION", "local-development"), BuildDigest: strings.ToLower(strings.TrimSpace(os.Getenv("SIM_BUILD_DIGEST"))),
DeviceID: env("SIM_DEVICE_ID", "10000000-0000-0000-0000-000000000511"), DeviceModel: env("SIM_DEVICE_MODEL", "simulated-terminal"), HardwareRevision: env("SIM_DEVICE_HARDWARE_REVISION", "sim-rev-a"),
ReleaseID: manifest.ReleaseID, CommandID: command.ID, Scenario: strings.ToUpper(strings.TrimSpace(os.Getenv("SIM_FIRMWARE_EVIDENCE_SCENARIO"))),
BootloaderVersion: env("SIM_BOOTLOADER_VERSION", "sim-bootloader-1"), PartitionTableSHA256: partitionDigest, SecureBootEnabled: secureBoot,
ArtifactSHA256: manifest.Digest, SigningKeyID: manifest.SigningKeyID,
InitialSlot: state.PreviousSlot, TargetSlot: state.TargetSlot, FinalSlot: state.PreviousSlot,
InitialVersion: state.PreviousVersion, TargetVersion: state.TargetVersion, FinalVersion: state.PreviousVersion,
BootAttemptID: state.BootAttemptID, CollectedAt: now, Events: []firmwareconformance.DeviceEvent{},
}
if evidence.BuildDigest == "" || evidence.Scenario == "" {
return errors.New("SIM_BUILD_DIGEST and SIM_FIRMWARE_EVIDENCE_SCENARIO are required when evidence capture is enabled")
}
} else if err != nil {
return fmt.Errorf("read simulated firmware evidence: %w", err)
} else {
evidence, err = firmwareconformance.ParseDeviceEvidence(content)
if err != nil {
return err
}
if evidence.CommandID != command.ID || evidence.ReleaseID != manifest.ReleaseID {
return errors.New("simulated firmware evidence belongs to a different command or release")
}
}
if manifest.SigningKeyID != "" && evidence.SigningKeyID == "" {
evidence.SigningKeyID = manifest.SigningKeyID
}
if state.BootAttemptID != "" {
evidence.BootAttemptID = state.BootAttemptID
}
event := firmwareconformance.DeviceEvent{
Phase: phase, OccurredAt: now, ActiveSlot: state.ActiveSlot, TargetSlot: state.TargetSlot, ResultCode: resultCode, BootAttemptID: state.BootAttemptID,
}
evidence.Events = append(evidence.Events, event)
evidence.CollectedAt = now
switch phase {
case "confirmed":
evidence.FinalSlot = state.ActiveSlot
evidence.FinalVersion = manifest.Version
case "rolled_back":
evidence.FinalSlot = state.ActiveSlot
evidence.FinalVersion = state.TargetVersion
}
encoded, err := json.MarshalIndent(evidence, "", " ")
if err != nil {
return err
}
file, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o600)
if err != nil {
return fmt.Errorf("open simulated firmware evidence: %w", err)
}
if _, err = file.Write(encoded); err == nil {
err = file.Sync()
}
closeErr := file.Close()
if err != nil {
return fmt.Errorf("write simulated firmware evidence: %w", err)
}
if closeErr != nil {
return fmt.Errorf("close simulated firmware evidence: %w", closeErr)
}
return nil
}
func simulatedEvidenceBoolean(key string, fallback bool) (bool, error) {
value := strings.ToLower(strings.TrimSpace(os.Getenv(key)))
if value == "" {
return fallback, nil
}
if value == "true" {
return true, nil
}
if value == "false" {
return false, nil
}
return false, fmt.Errorf("%s must be true or false", key)
}