返回 DeepSeek-Reasonix
windows_payload.go
根目录 / desktop / internal / update / windows_payload.go
1 package update
2
3 import (
4 "bytes"
5 "crypto/sha256"
6 "encoding/hex"
7 "encoding/json"
8 "fmt"
9 "io"
10 "strings"
11 )
12
13 const (
14 WindowsPayloadManifestSchemaVersion = 1
15 WindowsPayloadManifestName = "reasonix-payload.json"
16 WindowsPayloadSignatureName = WindowsPayloadManifestName + ".minisig"
17 )
18
19 var windowsPayloadFileNames = [...]string{
20 "reasonix-desktop.exe",
21 "reasonix-guard.exe",
22 "reasonix-launcher.exe",
23 "reasonix-update-helper.exe",
24 "reasonix-cli.exe",
25 }
26
27 type WindowsPayloadManifest struct {
28 SchemaVersion int `json:"schemaVersion"`
29 Version string `json:"version"`
30 Files []WindowsPayloadManifestFile `json:"files"`
31 }
32
33 type WindowsPayloadManifestFile struct {
34 Name string `json:"name"`
35 SHA256 string `json:"sha256"`
36 }
37
38 func WindowsPayloadFileNames() []string {
39 return append([]string(nil), windowsPayloadFileNames[:]...)
40 }
41
42 func EncodeWindowsPayloadManifest(version string, hashes map[string]string) ([]byte, error) {
43 version = strings.TrimSpace(version)
44 if version == "" {
45 return nil, fmt.Errorf("Windows payload manifest version is empty")
46 }
47 manifest := WindowsPayloadManifest{
48 SchemaVersion: WindowsPayloadManifestSchemaVersion,
49 Version: version,
50 Files: make([]WindowsPayloadManifestFile, 0, len(windowsPayloadFileNames)),
51 }
52 for _, name := range windowsPayloadFileNames {
53 hash := strings.ToLower(strings.TrimSpace(hashes[name]))
54 if !validWindowsPayloadSHA256(hash) {
55 return nil, fmt.Errorf("Windows payload manifest hash for %s is invalid", name)
56 }
57 manifest.Files = append(manifest.Files, WindowsPayloadManifestFile{
58 Name: name,
59 SHA256: hash,
60 })
61 }
62 if len(hashes) != len(manifest.Files) {
63 return nil, fmt.Errorf("Windows payload manifest contains unexpected files")
64 }
65 b, err := json.MarshalIndent(manifest, "", " ")
66 if err != nil {
67 return nil, err
68 }
69 return append(b, '\n'), nil
70 }
71
72 func DecodeWindowsPayloadManifest(data []byte, expectedVersion string) (map[string]string, error) {
73 var manifest WindowsPayloadManifest
74 dec := json.NewDecoder(bytes.NewReader(data))
75 dec.DisallowUnknownFields()
76 if err := dec.Decode(&manifest); err != nil {
77 return nil, fmt.Errorf("decode Windows payload manifest: %w", err)
78 }
79 var trailing any
80 if err := dec.Decode(&trailing); err != io.EOF {
81 if err == nil {
82 return nil, fmt.Errorf("decode Windows payload manifest: trailing JSON value")
83 }
84 return nil, fmt.Errorf("decode Windows payload manifest: %w", err)
85 }
86 expectedVersion = strings.TrimSpace(expectedVersion)
87 if manifest.SchemaVersion != WindowsPayloadManifestSchemaVersion ||
88 expectedVersion == "" ||
89 manifest.Version != expectedVersion {
90 return nil, fmt.Errorf("Windows payload manifest identity does not match the pending update")
91 }
92 expected := make(map[string]struct{}, len(windowsPayloadFileNames))
93 for _, name := range windowsPayloadFileNames {
94 expected[name] = struct{}{}
95 }
96 hashes := make(map[string]string, len(manifest.Files))
97 for _, file := range manifest.Files {
98 name := file.Name
99 hash := file.SHA256
100 if _, ok := expected[name]; !ok || !validWindowsPayloadSHA256(hash) {
101 return nil, fmt.Errorf("Windows payload manifest member is invalid")
102 }
103 if _, duplicate := hashes[name]; duplicate {
104 return nil, fmt.Errorf("Windows payload manifest contains duplicate members")
105 }
106 hashes[name] = hash
107 }
108 if len(hashes) != len(expected) {
109 return nil, fmt.Errorf("Windows payload manifest is incomplete")
110 }
111 return hashes, nil
112 }
113
114 func WindowsPayloadSHA256(data []byte) string {
115 sum := sha256.Sum256(data)
116 return hex.EncodeToString(sum[:])
117 }
118
119 func validWindowsPayloadSHA256(value string) bool {
120 if len(value) != sha256.Size*2 {
121 return false
122 }
123 if value != strings.ToLower(value) {
124 return false
125 }
126 _, err := hex.DecodeString(value)
127 return err == nil
128 }
129
129 lines GO