/
opgp.go
157 lines (132 loc) · 4.13 KB
/
opgp.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// TODO: Discuss with AppImage team whether we can switch from GPG to RSA
// and whether this would simplify things and reduce dependencies
// https://socketloop.com/tutorials/golang-saving-private-and-public-key-to-files
package helpers
import (
"bytes"
"encoding/base64"
"errors"
"fmt"
"log"
"strings"
"time"
"golang.org/x/crypto/openpgp/packet"
"os"
"github.com/alokmenghrajani/gpgeez"
"golang.org/x/crypto/openpgp"
)
func CreateAndValidateKeyPair() {
createKeyPair()
b, err := os.ReadFile("privkey")
if err != nil {
fmt.Println(err)
return
}
hexstring, _ := readPGP(b)
if err != nil {
log.Fatal(err)
}
err = validate(hexstring)
if err != nil {
log.Fatal(err)
}
}
func readPGP(armoredKey []byte) (string, error) {
keyReader := bytes.NewReader(armoredKey)
entityList, err := openpgp.ReadArmoredKeyRing(keyReader)
if err != nil {
log.Fatalf("error reading armored key %s", err)
}
serializedEntity := bytes.NewBuffer(nil)
err = entityList[0].Serialize(serializedEntity)
if err != nil {
return "", fmt.Errorf("error serializing entity for file %s", err)
}
return base64.StdEncoding.EncodeToString(serializedEntity.Bytes()), nil
}
func validate(keystring string) error {
data, err := base64.StdEncoding.DecodeString(keystring)
if err != nil {
return err
}
_, err = openpgp.ReadEntity(packet.NewReader(bytes.NewBuffer(data)))
if err != nil {
return err
}
fmt.Println("PASSED")
return nil
}
func createKeyPair() {
config := gpgeez.Config{Expiry: 0 * time.Hour}
config.RSABits = 4096
key, err := gpgeez.CreateKey("Signing key", "", "", &config) // TODO: Better name, comment, email
if err != nil {
fmt.Printf("Something went wrong while creating key pair: %v", err)
return
}
pubkeyascdata, err := key.Armor()
if err != nil {
fmt.Printf("Something went wrong while armoding public key: %v", err)
return
}
privkeyascdata, err := key.ArmorPrivate(&config)
if err != nil {
fmt.Printf("Something went wrong while armoding private key: %v", err)
return
}
os.WriteFile(PubkeyFileName, []byte(pubkeyascdata), 0666)
os.WriteFile(PrivkeyFileName, []byte(privkeyascdata), 0600)
}
// CheckSignature checks the signature embedded in an AppImage at path,
// returns the entity that has signed the AppImage and error
// based on https://stackoverflow.com/a/34008326
func CheckSignature(path string) (*openpgp.Entity, error) {
var ent *openpgp.Entity
err := errors.New("could not verify AppImage signature") // Be pessimistic by default, unless we can positively verify the signature
pubkeybytes, err := GetSectionData(path, ".sig_key")
keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewReader(pubkeybytes))
if err != nil {
return ent, err
}
sigbytes, err := GetSectionData(path, ".sha256_sig")
ent, err = openpgp.CheckArmoredDetachedSignature(keyring, strings.NewReader(CalculateSHA256Digest(path)), bytes.NewReader(sigbytes))
if err != nil {
return ent, err
}
return ent, nil
}
// SignAppImage signs an AppImage, returns error
// Based on https://gist.github.com/eliquious/9e96017f47d9bd43cdf9
func SignAppImage(path string, digest string) error {
// Read in public key
pubkeyFileBuffer, _ := os.Open(PubkeyFileName)
defer pubkeyFileBuffer.Close()
_, err := openpgp.ReadArmoredKeyRing(pubkeyFileBuffer)
if err != nil {
fmt.Println("openpgp.ReadArmoredKeyRing error while reading public key:", err)
return err
}
// Read in private key
privkeyFileBuffer, _ := os.Open(PrivkeyFileName)
defer privkeyFileBuffer.Close()
entityList, err := openpgp.ReadArmoredKeyRing(privkeyFileBuffer)
if err != nil {
fmt.Println("openpgp.ReadArmoredKeyRing error while reading private key:", err)
return err
}
buf := new(bytes.Buffer)
// Get the digest we want to sign into an io.Reader
// FIXME: Use the digest we have already calculated earlier on (let's not do it twice)
whatToSignReader := strings.NewReader(digest)
err = openpgp.ArmoredDetachSign(buf, entityList[0], whatToSignReader, nil)
if err != nil {
fmt.Println("Error signing input:", err)
return err
}
err = EmbedStringInSegment(path, ".sha256_sig", buf.String())
if err != nil {
PrintError("EmbedStringInSegment", err)
return err
}
return nil
}