Skip to content

Commit 0c7e335

Browse files
committed
Check downloaded AppImages signature
1 parent fbd8d2a commit 0c7e335

File tree

4 files changed

+184
-0
lines changed

4 files changed

+184
-0
lines changed

app/commands/install/install.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,14 @@ func (cmd *InstallCmd) Run(*commands.Context) (err error) {
5252

5353
cmd.createDesktopIntegration(targetFilePath)
5454

55+
signingEntity, _ := utils.VerifySignature(targetFilePath)
56+
if signingEntity != nil {
57+
fmt.Println("AppImage signed by:")
58+
for _, v := range signingEntity.Identities {
59+
fmt.Println("\t", v.Name)
60+
}
61+
}
62+
5563
return
5664
}
5765

app/commands/update/command.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,14 @@ func (cmd *UpdateCmd) Run(*commands.Context) (err error) {
6060
continue
6161
}
6262

63+
signingEntity, _ := utils.VerifySignature(result)
64+
if signingEntity != nil {
65+
fmt.Println("AppImage signed by:")
66+
for _, v := range signingEntity.Identities {
67+
fmt.Println("\t", v.Name)
68+
}
69+
}
70+
6371
fmt.Println("Update downloaded to: " + result)
6472
}
6573

app/utils/signature.go

Lines changed: 167 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
package utils
2+
3+
import (
4+
"bytes"
5+
"crypto/sha256"
6+
"debug/elf"
7+
"encoding/hex"
8+
"fmt"
9+
"golang.org/x/crypto/openpgp"
10+
"io"
11+
"os"
12+
"strings"
13+
)
14+
15+
func VerifySignature(target string) (result *openpgp.Entity, err error) {
16+
key, err := readElfSection(target, ".sig_key")
17+
if err != nil {
18+
return nil, err
19+
}
20+
21+
signature, err := readElfSection(target, ".sha256_sig")
22+
if err != nil {
23+
return nil, err
24+
}
25+
26+
file, err := newAppImagePreSignatureReader(target)
27+
if err != nil {
28+
return
29+
}
30+
31+
sha256Hash := sha256.New()
32+
_, err = io.Copy(sha256Hash, file)
33+
34+
if err != nil {
35+
return nil, err
36+
}
37+
38+
verification_target := hex.EncodeToString(sha256Hash.Sum(nil))
39+
40+
keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(key))
41+
if err != nil {
42+
return nil, err
43+
}
44+
45+
entity, err := openpgp.CheckArmoredDetachedSignature(keyring, strings.NewReader(verification_target), bytes.NewReader(signature))
46+
if err != nil {
47+
return nil, err
48+
}
49+
50+
return entity, nil
51+
}
52+
53+
func readElfSection(appImagePath string, sectionName string) ([]byte, error) {
54+
elfFile, err := elf.Open(appImagePath)
55+
if err != nil {
56+
panic("Unable to open target: \"" + appImagePath + "\"." + err.Error())
57+
}
58+
59+
section := elfFile.Section(sectionName)
60+
if section == nil {
61+
return nil, fmt.Errorf("missing " + sectionName + " section on target elf")
62+
}
63+
sectionData, err := section.Data()
64+
65+
if err != nil {
66+
return nil, fmt.Errorf("Unable to parse " + sectionName + " section: " + err.Error())
67+
}
68+
69+
str_end := bytes.Index(sectionData, []byte("\000"))
70+
if str_end == -1 || str_end == 0 {
71+
return nil, nil
72+
}
73+
74+
return sectionData[:str_end], nil
75+
}
76+
77+
func ReadSignature(appImagePath string) ([]byte, error) {
78+
elfFile, err := elf.Open(appImagePath)
79+
if err != nil {
80+
panic("Unable to open target: \"" + appImagePath + "\"." + err.Error())
81+
}
82+
83+
updInfo := elfFile.Section(".sha256_sig")
84+
if updInfo == nil {
85+
panic("Missing .sha256_sig section on target elf ")
86+
}
87+
sectionData, err := updInfo.Data()
88+
89+
if err != nil {
90+
panic("Unable to parse .sha256_sig section: " + err.Error())
91+
}
92+
93+
str_end := bytes.Index(sectionData, []byte("\000"))
94+
if str_end == -1 || str_end == 0 {
95+
return nil, fmt.Errorf("No update information found in: " + appImagePath)
96+
}
97+
98+
return sectionData[:str_end], nil
99+
}
100+
101+
type appImagePreSignatureReader struct {
102+
keySectionOffset uint64
103+
keySectionSize uint64
104+
105+
sigSectionOffset uint64
106+
sigSectionSize uint64
107+
108+
offset uint64
109+
file *os.File
110+
}
111+
112+
func newAppImagePreSignatureReader(target string) (*appImagePreSignatureReader, error) {
113+
elfFile, err := elf.Open(target)
114+
if err != nil {
115+
return nil, err
116+
}
117+
118+
key := elfFile.Section(".sig_key")
119+
if key == nil {
120+
return nil, fmt.Errorf("Missing .sig_key section")
121+
}
122+
123+
signature := elfFile.Section(".sha256_sig")
124+
if signature == nil {
125+
return nil, fmt.Errorf("Missing .sha256_sig section")
126+
}
127+
128+
file, err := os.Open(target)
129+
if err != nil {
130+
return nil, err
131+
}
132+
133+
return &appImagePreSignatureReader{
134+
offset: 0,
135+
file: file,
136+
keySectionOffset: key.Offset,
137+
keySectionSize: key.Size,
138+
sigSectionOffset: signature.Offset,
139+
sigSectionSize: signature.Size,
140+
}, nil
141+
}
142+
143+
func (reader *appImagePreSignatureReader) Read(p []byte) (n int, err error) {
144+
n, err = reader.file.Read(p)
145+
if err != nil {
146+
return
147+
}
148+
149+
oldOffset := reader.offset
150+
reader.offset += uint64(n)
151+
152+
if reader.keySectionOffset >= oldOffset && reader.keySectionOffset < reader.offset {
153+
start := reader.keySectionOffset - oldOffset
154+
for i := start; i < uint64(n) && (i-start) < reader.keySectionSize; i++ {
155+
p[i] = 0
156+
}
157+
}
158+
159+
if reader.sigSectionOffset >= oldOffset && reader.sigSectionOffset < reader.offset {
160+
start := reader.sigSectionOffset - oldOffset
161+
for i := start; i < uint64(n) && (i-start) < reader.sigSectionSize; i++ {
162+
p[i] = 0
163+
}
164+
}
165+
166+
return n, err
167+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@ require (
1515
github.com/schollz/progressbar/v3 v3.3.3
1616
github.com/stretchr/testify v1.6.1
1717
github.com/tidwall/gjson v1.6.0
18+
golang.org/x/crypto v0.0.0-20200604202706-70a84ac30bf9
1819
)

0 commit comments

Comments
 (0)