Files
esengine 579c59da70 ci(release-studio): sign and verify every PE file in the Windows tree
The Windows release signed only the three executables Studio builds. The
installed tree also ships Electron's ffmpeg.dll, dxcompiler.dll,
vk_swiftshader.dll and vulkan-1.dll unsigned, and Smart App Control / WDAC
can refuse to load an unsigned low-reputation DLL, so the app may not start.

Every PE file the Windows bundle may hold is now declared in
scripts/windows-signing-lib.ps1: seven the release signs, and
d3dcompiler_47.dll and dxil.dll, which Electron ships signed by Microsoft
and which are kept that way and verified as Microsoft-signed. A bundle with
an undeclared or missing PE file (.exe, .dll, .node, or any MZ header) fails
until a person updates the list.

windows-sign-payload signs the declared files, verifies all nine, and
records a SHA-256 digest of the whole signed tree as a job output. The
signed tree is the payload artifact; windows-package packages it as it
came. A new job, windows-verify-package, with no secrets and no
environment, checks the payload against that digest, unpacks the zip and
the installer's app-64.7z, requires both trees to equal the payload file
for file (plus resources/elevate.exe, pinned by SHA-256), and outputs the
SHA-256 of both packages. windows-sign-installer only requires the packages
to hash to those values before signing, and afterwards only runs SignTool
verification, so no archive is parsed in a job holding the Certum
credentials and Certum never signs an installer whose contents did not
match.

Signatures are read from the file itself through SignTool and the embedded
signer certificate. Get-AuthenticodeSignature answers from the OS catalog
for d3dcompiler_47.dll and reports a catalog signature whatever the file
carries.

The contract pins the new scripts, and validate now refuses a fingerprint
file that runs a script under scripts/ which is not pinned.
2026-09-27 09:09:45 -07:00

428 lines
13 KiB
Go

package main
import (
"crypto/sha256"
"encoding/binary"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"regexp"
"slices"
"strings"
"gopkg.in/yaml.v3"
)
const (
contractPath = ".signpath/contracts/release-signing.yml"
projectSlug = "DeepSeek-Reasonix"
policySlug = "release-signing"
repository = "https://github.com/esengine/DeepSeek-Reasonix.git"
)
// The studio line has its own tag namespace and its own protected branch, so
// the origin a signing request must carry is this one and not the desktop
// line's. Pinned here rather than read from the contract: this guard exists to
// catch the contract being widened, and a guard that reads its own answer out
// of the file it is checking cannot.
var expectedBranches = []string{"studio"}
type releaseSigningContract struct {
Version int `yaml:"version"`
ProjectSlug string `yaml:"project_slug"`
SigningPolicySlug string `yaml:"signing_policy_slug"`
RepositoryURL string `yaml:"repository_url"`
AllowedBranchNames []string `yaml:"allowed_branch_names"`
AllowedBuildDefinitions []string `yaml:"allowed_build_definitions"`
FingerprintFiles []string `yaml:"fingerprint_files"`
}
type workflowInfo struct {
externallyTriggered bool
directSigning bool
calls []string
}
func main() {
if len(os.Args) < 2 || len(os.Args) > 3 {
fatalf("usage: signpath-contract <validate|fingerprint> [repository-root]")
}
root := "."
if len(os.Args) == 3 {
root = os.Args[2]
}
contract, err := loadAndValidate(root)
if err != nil {
fatalf("%v", err)
}
switch os.Args[1] {
case "validate":
fmt.Println("SignPath release signing contract is valid")
case "fingerprint":
fingerprint, err := contractFingerprint(root, contract)
if err != nil {
fatalf("fingerprint contract: %v", err)
}
fmt.Printf("v1:%x\n", fingerprint)
default:
fatalf("unknown command %q", os.Args[1])
}
}
func fatalf(format string, args ...any) {
fmt.Fprintf(os.Stderr, "signpath-contract: "+format+"\n", args...)
os.Exit(1)
}
func loadAndValidate(root string) (releaseSigningContract, error) {
data, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(contractPath)))
if err != nil {
return releaseSigningContract{}, fmt.Errorf("read %s: %w", contractPath, err)
}
var contract releaseSigningContract
if err := yaml.Unmarshal(data, &contract); err != nil {
return releaseSigningContract{}, fmt.Errorf("parse %s: %w", contractPath, err)
}
if err := validateContract(root, contract); err != nil {
return releaseSigningContract{}, err
}
return contract, nil
}
func validateContract(root string, contract releaseSigningContract) error {
if contract.Version != 1 {
return fmt.Errorf("%s version is %d, want 1", contractPath, contract.Version)
}
if contract.ProjectSlug != projectSlug {
return fmt.Errorf("project_slug is %q, want %q", contract.ProjectSlug, projectSlug)
}
if contract.SigningPolicySlug != policySlug {
return fmt.Errorf("signing_policy_slug is %q, want %q", contract.SigningPolicySlug, policySlug)
}
if contract.RepositoryURL != repository {
return fmt.Errorf("repository_url is %q, want %q", contract.RepositoryURL, repository)
}
if err := requireExactSet("allowed_branch_names", contract.AllowedBranchNames, expectedBranches); err != nil {
return err
}
if len(contract.AllowedBuildDefinitions) == 0 {
return errors.New("allowed_build_definitions must not be empty")
}
for _, value := range append(append([]string{}, contract.AllowedBranchNames...), contract.AllowedBuildDefinitions...) {
if strings.ContainsAny(value, "*?[") {
return fmt.Errorf("SignPath allowlists must not contain wildcard %q", value)
}
}
for _, name := range contract.AllowedBuildDefinitions {
if err := validateRepositoryPath(name); err != nil {
return fmt.Errorf("invalid allowed build definition %q: %w", name, err)
}
if !strings.HasPrefix(name, ".github/workflows/") {
return fmt.Errorf("allowed build definition %q is outside .github/workflows", name)
}
}
if err := requireUnique("allowed_build_definitions", contract.AllowedBuildDefinitions); err != nil {
return err
}
if len(contract.FingerprintFiles) == 0 {
return errors.New("fingerprint_files must not be empty")
}
seen := make(map[string]bool, len(contract.FingerprintFiles))
for _, name := range contract.FingerprintFiles {
if err := validateRepositoryPath(name); err != nil {
return fmt.Errorf("invalid fingerprint file %q: %w", name, err)
}
if seen[name] {
return fmt.Errorf("duplicate fingerprint file %q", name)
}
seen[name] = true
info, err := os.Stat(filepath.Join(root, filepath.FromSlash(name)))
if err != nil {
return fmt.Errorf("fingerprint file %q: %w", name, err)
}
if !info.Mode().IsRegular() {
return fmt.Errorf("fingerprint file %q is not a regular file", name)
}
}
if err := requireScriptClosure(root, contract.FingerprintFiles); err != nil {
return err
}
reachable, err := discoverTopLevelSigningWorkflows(root)
if err != nil {
return err
}
if err := requireExactSet("top-level workflows that reach SignPath", reachable, contract.AllowedBuildDefinitions); err != nil {
return fmt.Errorf("SignPath build-definition drift: %w", err)
}
return nil
}
var scriptReference = regexp.MustCompile(`[A-Za-z0-9_.-]+\.ps1\b`)
// A pinned file that runs a script under scripts/ pins nothing unless that
// script is pinned too, so every one a fingerprint file names must be listed.
func requireScriptClosure(root string, fingerprintFiles []string) error {
pinned := make(map[string]bool, len(fingerprintFiles))
for _, name := range fingerprintFiles {
pinned[name] = true
}
for _, name := range fingerprintFiles {
data, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(name)))
if err != nil {
return fmt.Errorf("read fingerprint file %q: %w", name, err)
}
for _, leaf := range scriptReference.FindAllString(string(data), -1) {
script := "scripts/" + leaf
info, err := os.Stat(filepath.Join(root, filepath.FromSlash(script)))
if err != nil || !info.Mode().IsRegular() {
continue
}
if !pinned[script] {
return fmt.Errorf("fingerprint file %q runs %q, which fingerprint_files does not list", name, script)
}
}
}
return nil
}
func requireExactSet(name string, got, want []string) error {
gotCopy := append([]string(nil), got...)
wantCopy := append([]string(nil), want...)
slices.Sort(gotCopy)
slices.Sort(wantCopy)
if len(gotCopy) != len(wantCopy) {
return fmt.Errorf("%s is %v, want %v", name, got, want)
}
for i := range gotCopy {
if gotCopy[i] != wantCopy[i] {
return fmt.Errorf("%s is %v, want %v", name, got, want)
}
if i > 0 && gotCopy[i] == gotCopy[i-1] {
return fmt.Errorf("%s contains duplicate %q", name, gotCopy[i])
}
}
return nil
}
func requireUnique(name string, values []string) error {
seen := make(map[string]bool, len(values))
for _, value := range values {
if seen[value] {
return fmt.Errorf("%s contains duplicate %q", name, value)
}
seen[value] = true
}
return nil
}
func validateRepositoryPath(name string) error {
if name == "" || filepath.IsAbs(filepath.FromSlash(name)) {
return errors.New("path must be non-empty and repository-relative")
}
clean := filepath.Clean(filepath.FromSlash(name))
if clean == "." || clean == ".." || strings.HasPrefix(clean, ".."+string(filepath.Separator)) {
return errors.New("path escapes the repository")
}
if filepath.ToSlash(clean) != name {
return errors.New("path must be normalized with forward slashes")
}
return nil
}
func discoverTopLevelSigningWorkflows(root string) ([]string, error) {
paths, err := filepath.Glob(filepath.Join(root, ".github", "workflows", "*.y*ml"))
if err != nil {
return nil, fmt.Errorf("list workflows: %w", err)
}
workflows := make(map[string]workflowInfo, len(paths))
for _, path := range paths {
data, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("read workflow %s: %w", path, err)
}
name, err := filepath.Rel(root, path)
if err != nil {
return nil, err
}
name = filepath.ToSlash(name)
info, err := parseWorkflow(data)
if err != nil {
return nil, fmt.Errorf("parse workflow %s: %w", name, err)
}
workflows[name] = info
}
state := make(map[string]uint8, len(workflows))
memo := make(map[string]bool, len(workflows))
var reachesSigning func(string) (bool, error)
reachesSigning = func(name string) (bool, error) {
if state[name] == 2 {
return memo[name], nil
}
if state[name] == 1 {
return false, fmt.Errorf("reusable workflow cycle includes %s", name)
}
info, ok := workflows[name]
if !ok {
return false, fmt.Errorf("workflow calls missing local workflow %s", name)
}
state[name] = 1
reachable := info.directSigning
for _, called := range info.calls {
calledReachable, err := reachesSigning(called)
if err != nil {
return false, err
}
reachable = reachable || calledReachable
}
state[name] = 2
memo[name] = reachable
return reachable, nil
}
var result []string
for name, info := range workflows {
if !info.externallyTriggered {
continue
}
reachable, err := reachesSigning(name)
if err != nil {
return nil, err
}
if reachable {
result = append(result, name)
}
}
slices.Sort(result)
return result, nil
}
func parseWorkflow(data []byte) (workflowInfo, error) {
var document yaml.Node
if err := yaml.Unmarshal(data, &document); err != nil {
return workflowInfo{}, err
}
if len(document.Content) != 1 || document.Content[0].Kind != yaml.MappingNode {
return workflowInfo{}, errors.New("workflow root must be a mapping")
}
root := document.Content[0]
on := mappingValue(root, "on")
jobs := mappingValue(root, "jobs")
if on == nil || jobs == nil || jobs.Kind != yaml.MappingNode {
return workflowInfo{}, errors.New("workflow must contain on and jobs mappings")
}
info := workflowInfo{externallyTriggered: hasExternalTrigger(on)}
for i := 1; i < len(jobs.Content); i += 2 {
job := jobs.Content[i]
if job.Kind != yaml.MappingNode {
continue
}
if uses := mappingScalar(job, "uses"); strings.HasPrefix(uses, "./.github/workflows/") {
info.calls = append(info.calls, strings.TrimPrefix(uses, "./"))
}
if nodeContains(job, "secrets.SIGNPATH_API_TOKEN") || nodeContains(job, "secrets.CERTUM_OTP_URI") {
info.directSigning = true
}
steps := mappingValue(job, "steps")
if steps == nil || steps.Kind != yaml.SequenceNode {
continue
}
for _, step := range steps.Content {
if step.Kind != yaml.MappingNode {
continue
}
if strings.HasPrefix(mappingScalar(step, "uses"), "signpath/github-action-submit-signing-request@") {
info.directSigning = true
}
}
}
slices.Sort(info.calls)
return info, nil
}
func nodeContains(node *yaml.Node, text string) bool {
if node == nil {
return false
}
if node.Kind == yaml.ScalarNode && strings.Contains(node.Value, text) {
return true
}
for _, child := range node.Content {
if nodeContains(child, text) {
return true
}
}
return false
}
func hasExternalTrigger(node *yaml.Node) bool {
switch node.Kind {
case yaml.ScalarNode:
return node.Value != "workflow_call"
case yaml.SequenceNode:
for _, child := range node.Content {
if child.Value != "workflow_call" {
return true
}
}
case yaml.MappingNode:
for i := 0; i < len(node.Content); i += 2 {
if node.Content[i].Value != "workflow_call" {
return true
}
}
}
return false
}
func mappingValue(node *yaml.Node, key string) *yaml.Node {
if node == nil || node.Kind != yaml.MappingNode {
return nil
}
for i := 0; i < len(node.Content); i += 2 {
if node.Content[i].Value == key {
return node.Content[i+1]
}
}
return nil
}
func mappingScalar(node *yaml.Node, key string) string {
value := mappingValue(node, key)
if value == nil || value.Kind != yaml.ScalarNode {
return ""
}
return value.Value
}
func contractFingerprint(root string, contract releaseSigningContract) ([sha256.Size]byte, error) {
names := append([]string{contractPath}, contract.FingerprintFiles...)
slices.Sort(names)
hash := sha256.New()
_, _ = io.WriteString(hash, "reasonix-signpath-release-contract-v1\x00")
for _, name := range names {
data, err := os.ReadFile(filepath.Join(root, filepath.FromSlash(name)))
if err != nil {
return [sha256.Size]byte{}, fmt.Errorf("read %s: %w", name, err)
}
writeLengthPrefixed(hash, []byte(name))
writeLengthPrefixed(hash, data)
}
var result [sha256.Size]byte
copy(result[:], hash.Sum(nil))
return result, nil
}
func writeLengthPrefixed(writer io.Writer, data []byte) {
var length [8]byte
binary.BigEndian.PutUint64(length[:], uint64(len(data)))
_, _ = writer.Write(length[:])
_, _ = writer.Write(data)
}