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