1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17 package jsonsign
18
19 import (
20 "bytes"
21 "context"
22 "crypto"
23 "encoding/json"
24 "errors"
25 "fmt"
26 "log"
27 "os"
28 "strings"
29
30 "perkeep.org/pkg/blob"
31 "perkeep.org/pkg/camerrors"
32
33 "golang.org/x/crypto/openpgp/armor"
34 "golang.org/x/crypto/openpgp/packet"
35 )
36
37 const sigSeparator = `,"camliSig":"`
38
39
40
41 func reArmor(line string) string {
42 lastEq := strings.LastIndex(line, "=")
43 if lastEq == -1 {
44 return ""
45 }
46 buf := new(bytes.Buffer)
47 fmt.Fprintf(buf, "-----BEGIN PGP SIGNATURE-----\n\n")
48 payload := line[0:lastEq]
49 crc := line[lastEq:]
50 for len(payload) > 0 {
51 chunkLen := len(payload)
52 if chunkLen > 60 {
53 chunkLen = 60
54 }
55 fmt.Fprintf(buf, "%s\n", payload[0:chunkLen])
56 payload = payload[chunkLen:]
57 }
58 fmt.Fprintf(buf, "%s\n-----END PGP SIGNATURE-----\n", crc)
59 return buf.String()
60 }
61
62
63
64 type VerifyRequest struct {
65 fetcher blob.Fetcher
66
67 ba []byte
68 bp []byte
69 bpj []byte
70 bs []byte
71
72 CamliSigner blob.Ref
73 CamliSig string
74 PublicKeyPacket *packet.PublicKey
75
76
77 PayloadMap map[string]interface{}
78 SignerKeyId string
79
80 Err error
81 }
82
83 func (vr *VerifyRequest) fail(msg string) bool {
84 vr.Err = errors.New("jsonsign: " + msg)
85 return false
86 }
87
88 func (vr *VerifyRequest) ParseSigMap() bool {
89 sigMap := make(map[string]interface{})
90 if err := json.Unmarshal(vr.bs, &sigMap); err != nil {
91 return vr.fail("invalid JSON in signature")
92 }
93
94 if len(sigMap) != 1 {
95 return vr.fail("signature JSON didn't have exactly 1 key")
96 }
97
98 sigVal, hasCamliSig := sigMap["camliSig"]
99 if !hasCamliSig {
100 return vr.fail("no 'camliSig' key in signature")
101 }
102
103 var ok bool
104 vr.CamliSig, ok = sigVal.(string)
105 if !ok {
106 return vr.fail("camliSig not a string")
107 }
108
109 return true
110 }
111
112 func (vr *VerifyRequest) ParsePayloadMap() bool {
113 vr.PayloadMap = make(map[string]interface{})
114 pm := vr.PayloadMap
115
116 if err := json.Unmarshal(vr.bpj, &pm); err != nil {
117 return vr.fail("parse error; payload JSON is invalid")
118 }
119
120 if _, hasVersion := pm["camliVersion"]; !hasVersion {
121 return vr.fail("missing 'camliVersion' in the JSON payload")
122 }
123
124 signer, hasSigner := pm["camliSigner"]
125 if !hasSigner {
126 return vr.fail("missing 'camliSigner' in the JSON payload")
127 }
128
129 if _, ok := signer.(string); !ok {
130 return vr.fail("invalid 'camliSigner' in the JSON payload")
131 }
132
133 var ok bool
134 vr.CamliSigner, ok = blob.Parse(signer.(string))
135 if !ok {
136 return vr.fail("malformed 'camliSigner' blobref in the JSON payload")
137 }
138 return true
139 }
140
141 func (vr *VerifyRequest) FindAndParsePublicKeyBlob(ctx context.Context) error {
142 reader, _, err := vr.fetcher.Fetch(ctx, vr.CamliSigner)
143 if err != nil {
144 if err == os.ErrNotExist {
145 return camerrors.ErrMissingKeyBlob
146 }
147 log.Printf("error fetching public key blob %v: %v", vr.CamliSigner, err)
148 return err
149 }
150 defer reader.Close()
151 pk, err := openArmoredPublicKeyFile(reader)
152 if err != nil {
153 return fmt.Errorf("error opening public key file: %v", err)
154 }
155 vr.PublicKeyPacket = pk
156 return nil
157 }
158
159 func (vr *VerifyRequest) VerifySignature() bool {
160 armorData := reArmor(vr.CamliSig)
161 block, _ := armor.Decode(bytes.NewBufferString(armorData))
162 if block == nil {
163 return vr.fail("can't parse camliSig armor")
164 }
165 var p packet.Packet
166 var err error
167 p, err = packet.Read(block.Body)
168 if err != nil {
169 return vr.fail("error reading PGP packet from camliSig: " + err.Error())
170 }
171 sig, ok := p.(*packet.Signature)
172 if !ok {
173 return vr.fail("PGP packet isn't a signature packet")
174 }
175 if sig.Hash != crypto.SHA1 && sig.Hash != crypto.SHA256 {
176 return vr.fail("I can only verify SHA1 or SHA256 signatures")
177 }
178 if sig.SigType != packet.SigTypeBinary {
179 return vr.fail("I can only verify binary signatures")
180 }
181 hash := sig.Hash.New()
182 hash.Write(vr.bp)
183 err = vr.PublicKeyPacket.VerifySignature(hash, sig)
184 if err != nil {
185 return vr.fail(fmt.Sprintf("bad signature: %s", err))
186 }
187 vr.SignerKeyId = vr.PublicKeyPacket.KeyIdString()
188 return true
189 }
190
191 func NewVerificationRequest(sjson string, fetcher blob.Fetcher) (vr *VerifyRequest) {
192 if fetcher == nil {
193 panic("NewVerificationRequest fetcher is nil")
194 }
195 vr = new(VerifyRequest)
196 vr.ba = []byte(sjson)
197 vr.fetcher = fetcher
198
199 sigIndex := bytes.LastIndex(vr.ba, []byte(sigSeparator))
200 if sigIndex == -1 {
201 vr.Err = errors.New("jsonsign: no 13-byte camliSig separator found in sjson")
202 return
203 }
204
205
206 vr.bp = vr.ba[:sigIndex]
207
208
209
210
211 vr.bpj = vr.ba[:sigIndex+1]
212 vr.bpj[sigIndex] = '}'
213 vr.bs = []byte("{" + sjson[sigIndex+1:])
214 return
215 }
216
217 type VerifiedSignature struct {
218
219 }
220
221 func (vr *VerifyRequest) Verify(ctx context.Context) (info VerifiedSignature, err error) {
222 if vr.Err != nil {
223 return VerifiedSignature{}, vr.Err
224 }
225 defer func() {
226 if err != nil {
227
228
229 vr.PayloadMap = nil
230 if vr.Err == nil {
231
232
233 vr.Err = err
234 }
235 }
236 }()
237
238 if !vr.ParseSigMap() {
239 return VerifiedSignature{}, errors.New("parsing signature map failed")
240 }
241 if !vr.ParsePayloadMap() {
242 return VerifiedSignature{}, errors.New("parsing payload map failed")
243 }
244 if err := vr.FindAndParsePublicKeyBlob(ctx); err != nil {
245 return VerifiedSignature{}, err
246 }
247 if !vr.VerifySignature() {
248 return VerifiedSignature{}, errors.New("signature verification failed")
249 }
250
251 return VerifiedSignature{
252
253 }, nil
254 }