1
2
3
4
5 package clearsign
6
7 import (
8 "bytes"
9 "fmt"
10 "github.com/ProtonMail/go-crypto/openpgp"
11 "github.com/ProtonMail/go-crypto/openpgp/packet"
12 "io"
13 "testing"
14 )
15
16 func testParse(t *testing.T, input []byte, expected, expectedPlaintext string) {
17 b, rest := Decode(input)
18 if b == nil {
19 t.Fatal("failed to decode clearsign message")
20 }
21 if !bytes.Equal(rest, []byte("trailing")) {
22 t.Errorf("unexpected remaining bytes returned: %s", string(rest))
23 }
24 if b.ArmoredSignature.Type != "PGP SIGNATURE" {
25 t.Errorf("bad armor type, got:%s, want:PGP SIGNATURE", b.ArmoredSignature.Type)
26 }
27 if !bytes.Equal(b.Bytes, []byte(expected)) {
28 t.Errorf("bad body, got:%x want:%x", b.Bytes, expected)
29 }
30
31 if !bytes.Equal(b.Plaintext, []byte(expectedPlaintext)) {
32 t.Errorf("bad plaintext, got:%x want:%x", b.Plaintext, expectedPlaintext)
33 }
34
35 keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(signingKey))
36 if err != nil {
37 t.Errorf("failed to parse public key: %s", err)
38 }
39
40 config := &packet.Config{}
41 if _, err := openpgp.CheckDetachedSignature(keyring, bytes.NewBuffer(b.Bytes), b.ArmoredSignature.Body, config); err != nil {
42 t.Errorf("failed to check signature: %s", err)
43 }
44
45 b, _ = Decode(input)
46 if _, err := b.VerifySignature(keyring, config); err != nil {
47 t.Errorf("failed to check signature: %s", err)
48 }
49 }
50
51 func TestParse(t *testing.T) {
52 testParse(t, clearsignInput, "Hello world\r\nline 2", "Hello world\nline 2\n")
53 testParse(t, clearsignInput2, "\r\n\r\n(This message has a couple of blank lines at the start and end.)\r\n\r\n", "\n\n(This message has a couple of blank lines at the start and end.)\n\n\n")
54 }
55
56 func TestParseWithNoNewlineAtEnd(t *testing.T) {
57 input := clearsignInput
58 input = input[:len(input)-len("trailing")-1]
59 b, rest := Decode(input)
60 if b == nil {
61 t.Fatal("failed to decode clearsign message")
62 }
63 if len(rest) > 0 {
64 t.Errorf("unexpected remaining bytes returned: %s", string(rest))
65 }
66 }
67
68 var signingTests = []struct {
69 in, signed, plaintext string
70 }{
71 {"", "", ""},
72 {"a", "a", "a\n"},
73 {"a\n", "a", "a\n"},
74 {"-a\n", "-a", "-a\n"},
75 {"--a\nb", "--a\r\nb", "--a\nb\n"},
76
77 {" a\n", " a", " a\n"},
78 {" a\n", " a", " a\n"},
79
80 {"a \n", "a", "a\n"},
81 {"a ", "a", "a\n"},
82
83 {" \n", "", "\n"},
84 {" ", "", "\n"},
85 {"a\n \n \nb\n", "a\r\n\r\n\r\nb", "a\n\n\nb\n"},
86 }
87
88 func TestSigning(t *testing.T) {
89 keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewBufferString(signingKey))
90 if err != nil {
91 t.Errorf("failed to parse public key: %s", err)
92 }
93
94 for i, test := range signingTests {
95 var buf bytes.Buffer
96
97 plaintext, err := Encode(&buf, keyring[0].PrivateKey, nil)
98 if err != nil {
99 t.Errorf("#%d: error from Encode: %s", i, err)
100 continue
101 }
102 if _, err := plaintext.Write([]byte(test.in)); err != nil {
103 t.Errorf("#%d: error from Write: %s", i, err)
104 continue
105 }
106 if err := plaintext.Close(); err != nil {
107 t.Fatalf("#%d: error from Close: %s", i, err)
108 continue
109 }
110
111 b, _ := Decode(buf.Bytes())
112 if b == nil {
113 t.Errorf("#%d: failed to decode clearsign message", i)
114 continue
115 }
116 if !bytes.Equal(b.Bytes, []byte(test.signed)) {
117 t.Errorf("#%d: bad result, got:%x, want:%x", i, b.Bytes, test.signed)
118 continue
119 }
120 if !bytes.Equal(b.Plaintext, []byte(test.plaintext)) {
121 t.Errorf("#%d: bad result, got:%x, want:%x", i, b.Plaintext, test.plaintext)
122 continue
123 }
124
125 config := &packet.Config{}
126 if _, err := openpgp.CheckDetachedSignature(keyring, bytes.NewBuffer(b.Bytes), b.ArmoredSignature.Body, config); err != nil {
127 t.Errorf("#%d: failed to check signature: %s", i, err)
128 }
129 }
130 }
131
132
133 type quickRand byte
134
135 func (qr *quickRand) Read(p []byte) (int, error) {
136 for i := range p {
137 p[i] = byte(*qr)
138 }
139 *qr++
140 return len(p), nil
141 }
142
143 func TestMultiSign(t *testing.T) {
144 if testing.Short() {
145 t.Skip("skipping long test in -short mode")
146 }
147
148 zero := quickRand(0)
149 config := packet.Config{Rand: &zero}
150
151 for nKeys := 0; nKeys < 4; nKeys++ {
152 nextTest:
153 for nExtra := 0; nExtra < 4; nExtra++ {
154 var signKeys []*packet.PrivateKey
155 var verifyKeys openpgp.EntityList
156
157 desc := fmt.Sprintf("%d keys; %d of which will be used to verify", nKeys+nExtra, nKeys)
158 for i := 0; i < nKeys+nExtra; i++ {
159 e, err := openpgp.NewEntity("name", "comment", "email", &config)
160 if err != nil {
161 t.Errorf("cannot create key: %v", err)
162 continue nextTest
163 }
164 if i < nKeys {
165 verifyKeys = append(verifyKeys, e)
166 }
167 signKeys = append(signKeys, e.PrivateKey)
168 }
169
170 input := []byte("this is random text\r\n4 17")
171 var output bytes.Buffer
172 w, err := EncodeMulti(&output, signKeys, nil)
173 if err != nil {
174 t.Errorf("EncodeMulti (%s) failed: %v", desc, err)
175 }
176 if _, err := w.Write(input); err != nil {
177 t.Errorf("Write(%q) to signer (%s) failed: %v", string(input), desc, err)
178 }
179 if err := w.Close(); err != nil {
180 t.Errorf("Close() of signer (%s) failed: %v", desc, err)
181 }
182
183 block, _ := Decode(output.Bytes())
184 if string(block.Bytes) != string(input) {
185 t.Errorf("Inline data didn't match original; got %q want %q", string(block.Bytes), string(input))
186 }
187 config := &packet.Config{}
188 _, err = openpgp.CheckDetachedSignature(verifyKeys, bytes.NewReader(block.Bytes), block.ArmoredSignature.Body, config)
189 if nKeys == 0 {
190 if err == nil {
191 t.Errorf("verifying inline (%s) succeeded; want failure", desc)
192 }
193 } else {
194 if err != nil {
195 t.Errorf("verifying inline (%s) failed (%v); want success", desc, err)
196 }
197 }
198 }
199 }
200 }
201
202 func TestDecodeMissingCRC(t *testing.T) {
203 block, rest := Decode(clearsignInput3)
204 if block == nil {
205 t.Fatal("failed to decode PGP signature missing a CRC")
206 }
207 if len(rest) > 0 {
208 t.Fatalf("Decode should not have any remaining data left: %s", rest)
209 }
210 if _, err := packet.Read(block.ArmoredSignature.Body); err != nil {
211 t.Error(err)
212 }
213 if _, err := packet.Read(block.ArmoredSignature.Body); err != io.EOF {
214 t.Error(err)
215 }
216 }
217
218 const signatureBlock = `
219 -----BEGIN PGP SIGNATURE-----
220 Version: OpenPrivacy 0.99
221
222 yDgBO22WxBHv7O8X7O/jygAEzol56iUKiXmV+XmpCtmpqQUKiQrFqclFqUDBovzS
223 vBSFjNSiVHsuAA==
224 =njUN
225 -----END PGP SIGNATURE-----
226 `
227
228 var invalidInputs = []string{
229 `
230 -----BEGIN PGP SIGNED MESSAGE-----
231 Hash: SHA256
232
233 (This message was truncated.)
234 `,
235 `
236 -----BEGIN PGP SIGNED MESSAGE-----garbage
237 Hash: SHA256
238
239 _o/
240 ` + signatureBlock,
241 `
242 garbage-----BEGIN PGP SIGNED MESSAGE-----
243 Hash: SHA256
244
245 _o/
246 ` + signatureBlock,
247 `
248 -----BEGIN PGP SIGNED MESSAGE-----
249 Hash: SHA` + "\x0b\x0b" + `256
250
251 _o/
252 ` + signatureBlock,
253 `
254 -----BEGIN PGP SIGNED MESSAGE-----
255 NotHash: SHA256
256
257 _o/
258 ` + signatureBlock,
259 }
260
261 func TestParseInvalid(t *testing.T) {
262 for i, input := range invalidInputs {
263 if b, rest := Decode([]byte(input)); b != nil {
264 t.Errorf("#%d: decoded a bad clearsigned message without any error", i)
265 } else if string(rest) != input {
266 t.Errorf("#%d: did not return all data with a bad message", i)
267 }
268 }
269 }
270
271 var clearsignInput = []byte(`
272 ;lasjlkfdsa
273
274 -----BEGIN PGP SIGNED MESSAGE-----
275 Hash: SHA1
276
277 Hello world
278 line 2
279 -----BEGIN PGP SIGNATURE-----
280 Version: GnuPG v1.4.10 (GNU/Linux)
281
282 iJwEAQECAAYFAk8kMuEACgkQO9o98PRieSpMsAQAhmY/vwmNpflrPgmfWsYhk5O8
283 pjnBUzZwqTDoDeINjZEoPDSpQAHGhjFjgaDx/Gj4fAl0dM4D0wuUEBb6QOrwflog
284 2A2k9kfSOMOtk0IH/H5VuFN1Mie9L/erYXjTQIptv9t9J7NoRBMU0QOOaFU0JaO9
285 MyTpno24AjIAGb+mH1U=
286 =hIJ6
287 -----END PGP SIGNATURE-----
288 trailing`)
289
290 var clearsignInput2 = []byte(`
291 asdlfkjasdlkfjsadf
292
293 -----BEGIN PGP SIGNED MESSAGE-----
294 Hash: SHA256
295
296
297
298 (This message has a couple of blank lines at the start and end.)
299
300
301 -----BEGIN PGP SIGNATURE-----
302 Version: GnuPG v1.4.11 (GNU/Linux)
303
304 iJwEAQEIAAYFAlPpSREACgkQO9o98PRieSpZTAP+M8QUoCt/7Rf3YbXPcdzIL32v
305 pt1I+cMNeopzfLy0u4ioEFi8s5VkwpL1AFmirvgViCwlf82inoRxzZRiW05JQ5LI
306 ESEzeCoy2LIdRCQ2hcrG8pIUPzUO4TqO5D/dMbdHwNH4h5nNmGJUAEG6FpURlPm+
307 qZg6BaTvOxepqOxnhVU=
308 =e+C6
309 -----END PGP SIGNATURE-----
310
311 trailing`)
312
313 var clearsignInput3 = []byte(`-----BEGIN PGP SIGNED MESSAGE-----
314 Hash: SHA256
315
316 Origin: vscode stable
317 Label: vscode stable
318 Suite: stable
319 Codename: stable
320 Date: Mon, 13 Jan 2020 08:41:45 UTC
321 Architectures: amd64
322 Components: main
323 Description: Generated by aptly
324 MD5Sum:
325 66437152b3082616d8053e52c4bafafb 5821166 Contents-amd64
326 8024662ed51109946a517754bbafdd33 286298 Contents-amd64.gz
327 66437152b3082616d8053e52c4bafafb 5821166 main/Contents-amd64
328 8024662ed51109946a517754bbafdd33 286298 main/Contents-amd64.gz
329 3062a08b3eca94a65d6d17ba1dafcf3e 1088265 main/binary-amd64/Packages
330 b8ee22200fba8fa3be56c1ff946cdd24 159344 main/binary-amd64/Packages.bz2
331 f89c47c81ebd25caf287c8e6dda16c1a 169456 main/binary-amd64/Packages.gz
332 4c9ca25b556f111a5536c78df885ad82 95 main/binary-amd64/Release
333 SHA1:
334 2b62d0e322746b7d094878278f49993ca4314bf7 5821166 Contents-amd64
335 aafe35cce12e03d8b1939e403ddf5c0958c6e9bd 286298 Contents-amd64.gz
336 2b62d0e322746b7d094878278f49993ca4314bf7 5821166 main/Contents-amd64
337 aafe35cce12e03d8b1939e403ddf5c0958c6e9bd 286298 main/Contents-amd64.gz
338 30316ac5d4ce3b472a96a797eeb0a2a82d43ed3e 1088265 main/binary-amd64/Packages
339 6507e0b4da8194fd1048fcbb74c6e7433edaf3d6 159344 main/binary-amd64/Packages.bz2
340 ec9d39c39567c74001221e4900fb5d11ec11b833 169456 main/binary-amd64/Packages.gz
341 58bf20987a91d35936f18efce75ea233d43dbf8b 95 main/binary-amd64/Release
342 SHA256:
343 deff9ebfc44bf482e10a6ea10f608c6bb0fdc8373bf86b88cad9d99879ae3c39 5821166 Contents-amd64
344 f163bc65c7666ef58e0be3336e8c846ae2b7b388fbb2d7db0bcdc3fd1abae462 286298 Contents-amd64.gz
345 deff9ebfc44bf482e10a6ea10f608c6bb0fdc8373bf86b88cad9d99879ae3c39 5821166 main/Contents-amd64
346 f163bc65c7666ef58e0be3336e8c846ae2b7b388fbb2d7db0bcdc3fd1abae462 286298 main/Contents-amd64.gz
347 0fba50799ef72d0c2b354d0bcbbc8c623f6dae5a7fd7c218a54ea44dd8a49d5e 1088265 main/binary-amd64/Packages
348 69382470a88b67acde80fe45ab223016adebc445713ff0aa3272902581d21f13 159344 main/binary-amd64/Packages.bz2
349 1724b8ace5bd8882943e9463d8525006f33ca704480da0186fd47937451dc216 169456 main/binary-amd64/Packages.gz
350 0f509a0cb07e0ab433176fa47a21dccccc6b519f25f640cc58561104c11de6c2 95 main/binary-amd64/Release
351 SHA512:
352 f69f09c6180ceb6625a84b5f7123ad27972983146979dcfd9c38b2990459b52b4975716f85374511486bb5ad5852ebb1ef8265176df7134fc15b17ada3ba596c 5821166 Contents-amd64
353 46031bf89166188989368957d20cdcaac6eec72bab3f9839c9704bb08cbee3174ca6da11e290b0eab0e6b5754c1e7feb06d18ec9c5a0c955029cef53235e0a3a 286298 Contents-amd64.gz
354 f69f09c6180ceb6625a84b5f7123ad27972983146979dcfd9c38b2990459b52b4975716f85374511486bb5ad5852ebb1ef8265176df7134fc15b17ada3ba596c 5821166 main/Contents-amd64
355 46031bf89166188989368957d20cdcaac6eec72bab3f9839c9704bb08cbee3174ca6da11e290b0eab0e6b5754c1e7feb06d18ec9c5a0c955029cef53235e0a3a 286298 main/Contents-amd64.gz
356 3f78baf5adbaf0100996555b154807c794622fd0b5879b568ae0b6560e988fbfabed8d97db5a703d1a58514b9690fc6b60f9ad2eeece473d86ab257becd0ae41 1088265 main/binary-amd64/Packages
357 18f26df90beff29192662ca40525367c3c04f4581d59d2e9ab1cd0700a145b6a292a1609ca33ebe1c211f13718a8eee751f41fd8189cf93d52aa3e0851542dfc 159344 main/binary-amd64/Packages.bz2
358 6a6d917229e0cf06c493e174a87d76e815717676f2c70bcbd3bc689a80bd3c5489ea97db83b8f74cba8e70f374f9d9974f22b1ed2687a4ba1dacd22fdef7e14d 169456 main/binary-amd64/Packages.gz
359 e1a4378ad266c13c2edf8a0e590fa4d11973ab99ce79f15af005cb838f1600f66f3dc6da8976fa8b474da9073c118039c27623ab3360c6df115071497fe4f50c 95 main/binary-amd64/Release
360
361 -----BEGIN PGP SIGNATURE-----
362 Version: BSN Pgp v1.0.0.0
363
364 iQEcBAEBCAAGBQJeHC1bAAoJEOs+lK2+EinPAg8H/1rrhcgfm1HYL+Vmr9Ns6ton
365 LWQ8r13ADN66UTRa3XsO9V+q1fYowTqpXq6EZt2Gmlby/cpDf7mFPM5IteOXWLl7
366 QcWxPKHcdPIUi+h5F7BkFW65imP9GyX+V5Pxx5X544op7hYKaI0gAQ1oYtWDb3HE
367 4D27fju6icbj8w6E8TePcrDn82UvWAcaI5WSLboyhXCt2DxS3PNGFlyaP58zKJ8F
368 9cbBzksuMgMaTPAAMrU0zrFGfGeQz0Yo6nV/gRGiQaL9pSeIJWSKLNCMG/nIGmv2
369 xHVNFqTEetREY6UcQmuhwOn4HezyigH6XCBVp/Uez1izXiNdwBOet34SSvnkuJ4=
370 -----END PGP SIGNATURE-----`)
371
372 var signingKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
373 Version: GnuPG v1.4.10 (GNU/Linux)
374
375 lQHYBE2rFNoBBADFwqWQIW/DSqcB4yCQqnAFTJ27qS5AnB46ccAdw3u4Greeu3Bp
376 idpoHdjULy7zSKlwR1EA873dO/k/e11Ml3dlAFUinWeejWaK2ugFP6JjiieSsrKn
377 vWNicdCS4HTWn0X4sjl0ZiAygw6GNhqEQ3cpLeL0g8E9hnYzJKQ0LWJa0QARAQAB
378 AAP/TB81EIo2VYNmTq0pK1ZXwUpxCrvAAIG3hwKjEzHcbQznsjNvPUihZ+NZQ6+X
379 0HCfPAdPkGDCLCb6NavcSW+iNnLTrdDnSI6+3BbIONqWWdRDYJhqZCkqmG6zqSfL
380 IdkJgCw94taUg5BWP/AAeQrhzjChvpMQTVKQL5mnuZbUCeMCAN5qrYMP2S9iKdnk
381 VANIFj7656ARKt/nf4CBzxcpHTyB8+d2CtPDKCmlJP6vL8t58Jmih+kHJMvC0dzn
382 gr5f5+sCAOOe5gt9e0am7AvQWhdbHVfJU0TQJx+m2OiCJAqGTB1nvtBLHdJnfdC9
383 TnXXQ6ZXibqLyBies/xeY2sCKL5qtTMCAKnX9+9d/5yQxRyrQUHt1NYhaXZnJbHx
384 q4ytu0eWz+5i68IYUSK69jJ1NWPM0T6SkqpB3KCAIv68VFm9PxqG1KmhSrQIVGVz
385 dCBLZXmIuAQTAQIAIgUCTasU2gIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgECF4AA
386 CgkQO9o98PRieSoLhgQAkLEZex02Qt7vGhZzMwuN0R22w3VwyYyjBx+fM3JFETy1
387 ut4xcLJoJfIaF5ZS38UplgakHG0FQ+b49i8dMij0aZmDqGxrew1m4kBfjXw9B/v+
388 eIqpODryb6cOSwyQFH0lQkXC040pjq9YqDsO5w0WYNXYKDnzRV0p4H1pweo2VDid
389 AdgETasU2gEEAN46UPeWRqKHvA99arOxee38fBt2CI08iiWyI8T3J6ivtFGixSqV
390 bRcPxYO/qLpVe5l84Nb3X71GfVXlc9hyv7CD6tcowL59hg1E/DC5ydI8K8iEpUmK
391 /UnHdIY5h8/kqgGxkY/T/hgp5fRQgW1ZoZxLajVlMRZ8W4tFtT0DeA+JABEBAAEA
392 A/0bE1jaaZKj6ndqcw86jd+QtD1SF+Cf21CWRNeLKnUds4FRRvclzTyUMuWPkUeX
393 TaNNsUOFqBsf6QQ2oHUBBK4VCHffHCW4ZEX2cd6umz7mpHW6XzN4DECEzOVksXtc
394 lUC1j4UB91DC/RNQqwX1IV2QLSwssVotPMPqhOi0ZLNY7wIA3n7DWKInxYZZ4K+6
395 rQ+POsz6brEoRHwr8x6XlHenq1Oki855pSa1yXIARoTrSJkBtn5oI+f8AzrnN0BN
396 oyeQAwIA/7E++3HDi5aweWrViiul9cd3rcsS0dEnksPhvS0ozCJiHsq/6GFmy7J8
397 QSHZPteedBnZyNp5jR+H7cIfVN3KgwH/Skq4PsuPhDq5TKK6i8Pc1WW8MA6DXTdU
398 nLkX7RGmMwjC0DBf7KWAlPjFaONAX3a8ndnz//fy1q7u2l9AZwrj1qa1iJ8EGAEC
399 AAkFAk2rFNoCGwwACgkQO9o98PRieSo2/QP/WTzr4ioINVsvN1akKuekmEMI3LAp
400 BfHwatufxxP1U+3Si/6YIk7kuPB9Hs+pRqCXzbvPRrI8NHZBmc8qIGthishdCYad
401 AHcVnXjtxrULkQFGbGvhKURLvS9WnzD/m1K2zzwxzkPTzT9/Yf06O6Mal5AdugPL
402 VrM0m72/jnpKo04=
403 =zNCn
404 -----END PGP PRIVATE KEY BLOCK-----
405 `
406
View as plain text