openpgp/armor: allow armored PGP signature without a CRC

RFC 4800, Section 6 specifies that the CRC at the end of the
armor is optional, so do not fail to decode signatures missing
the CRC.

Credit: armor.go patch from engineer at Google

Change-Id: I39b04e0afaaafdf7aa86577fe4a35c50e7cf0b2b
Reviewed-on: https://go-review.googlesource.com/c/crypto/+/215022
Run-TryBot: Katie Hockman <katie@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
diff --git a/openpgp/armor/armor.go b/openpgp/armor/armor.go
index 592d186..36a6804 100644
--- a/openpgp/armor/armor.go
+++ b/openpgp/armor/armor.go
@@ -62,10 +62,11 @@
 // lineReader wraps a line based reader. It watches for the end of an armor
 // block and records the expected CRC value.
 type lineReader struct {
-	in  *bufio.Reader
-	buf []byte
-	eof bool
-	crc uint32
+	in     *bufio.Reader
+	buf    []byte
+	eof    bool
+	crc    uint32
+	crcSet bool
 }
 
 func (l *lineReader) Read(p []byte) (n int, err error) {
@@ -87,6 +88,11 @@
 		return 0, ArmorCorrupt
 	}
 
+	if bytes.HasPrefix(line, armorEnd) {
+		l.eof = true
+		return 0, io.EOF
+	}
+
 	if len(line) == 5 && line[0] == '=' {
 		// This is the checksum line
 		var expectedBytes [3]byte
@@ -108,6 +114,7 @@
 		}
 
 		l.eof = true
+		l.crcSet = true
 		return 0, io.EOF
 	}
 
@@ -141,10 +148,8 @@
 	n, err = r.b64Reader.Read(p)
 	r.currentCRC = crc24(r.currentCRC, p[:n])
 
-	if err == io.EOF {
-		if r.lReader.crc != uint32(r.currentCRC&crc24Mask) {
-			return 0, ArmorCorrupt
-		}
+	if err == io.EOF && r.lReader.crcSet && r.lReader.crc != uint32(r.currentCRC&crc24Mask) {
+		return 0, ArmorCorrupt
 	}
 
 	return
diff --git a/openpgp/clearsign/clearsign_test.go b/openpgp/clearsign/clearsign_test.go
index 737f41f..051b8f1 100644
--- a/openpgp/clearsign/clearsign_test.go
+++ b/openpgp/clearsign/clearsign_test.go
@@ -7,6 +7,7 @@
 import (
 	"bytes"
 	"fmt"
+	"io"
 	"testing"
 
 	"golang.org/x/crypto/openpgp"
@@ -191,6 +192,22 @@
 	}
 }
 
+func TestDecodeMissingCRC(t *testing.T) {
+	block, rest := Decode(clearsignInput3)
+	if block == nil {
+		t.Fatal("failed to decode PGP signature missing a CRC")
+	}
+	if len(rest) > 0 {
+		t.Fatalf("Decode should not have any remaining data left: %s", rest)
+	}
+	if _, err := packet.Read(block.ArmoredSignature.Body); err != nil {
+		t.Error(err)
+	}
+	if _, err := packet.Read(block.ArmoredSignature.Body); err != io.EOF {
+		t.Error(err)
+	}
+}
+
 const signatureBlock = `
 -----BEGIN PGP SIGNATURE-----
 Version: OpenPrivacy 0.99
@@ -286,6 +303,65 @@
 
 trailing`)
 
+var clearsignInput3 = []byte(`-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA256
+
+Origin: vscode stable
+Label: vscode stable
+Suite: stable
+Codename: stable
+Date: Mon, 13 Jan 2020 08:41:45 UTC
+Architectures: amd64
+Components: main
+Description: Generated by aptly
+MD5Sum:
+ 66437152b3082616d8053e52c4bafafb  5821166 Contents-amd64
+ 8024662ed51109946a517754bbafdd33   286298 Contents-amd64.gz
+ 66437152b3082616d8053e52c4bafafb  5821166 main/Contents-amd64
+ 8024662ed51109946a517754bbafdd33   286298 main/Contents-amd64.gz
+ 3062a08b3eca94a65d6d17ba1dafcf3e  1088265 main/binary-amd64/Packages
+ b8ee22200fba8fa3be56c1ff946cdd24   159344 main/binary-amd64/Packages.bz2
+ f89c47c81ebd25caf287c8e6dda16c1a   169456 main/binary-amd64/Packages.gz
+ 4c9ca25b556f111a5536c78df885ad82       95 main/binary-amd64/Release
+SHA1:
+ 2b62d0e322746b7d094878278f49993ca4314bf7  5821166 Contents-amd64
+ aafe35cce12e03d8b1939e403ddf5c0958c6e9bd   286298 Contents-amd64.gz
+ 2b62d0e322746b7d094878278f49993ca4314bf7  5821166 main/Contents-amd64
+ aafe35cce12e03d8b1939e403ddf5c0958c6e9bd   286298 main/Contents-amd64.gz
+ 30316ac5d4ce3b472a96a797eeb0a2a82d43ed3e  1088265 main/binary-amd64/Packages
+ 6507e0b4da8194fd1048fcbb74c6e7433edaf3d6   159344 main/binary-amd64/Packages.bz2
+ ec9d39c39567c74001221e4900fb5d11ec11b833   169456 main/binary-amd64/Packages.gz
+ 58bf20987a91d35936f18efce75ea233d43dbf8b       95 main/binary-amd64/Release
+SHA256:
+ deff9ebfc44bf482e10a6ea10f608c6bb0fdc8373bf86b88cad9d99879ae3c39  5821166 Contents-amd64
+ f163bc65c7666ef58e0be3336e8c846ae2b7b388fbb2d7db0bcdc3fd1abae462   286298 Contents-amd64.gz
+ deff9ebfc44bf482e10a6ea10f608c6bb0fdc8373bf86b88cad9d99879ae3c39  5821166 main/Contents-amd64
+ f163bc65c7666ef58e0be3336e8c846ae2b7b388fbb2d7db0bcdc3fd1abae462   286298 main/Contents-amd64.gz
+ 0fba50799ef72d0c2b354d0bcbbc8c623f6dae5a7fd7c218a54ea44dd8a49d5e  1088265 main/binary-amd64/Packages
+ 69382470a88b67acde80fe45ab223016adebc445713ff0aa3272902581d21f13   159344 main/binary-amd64/Packages.bz2
+ 1724b8ace5bd8882943e9463d8525006f33ca704480da0186fd47937451dc216   169456 main/binary-amd64/Packages.gz
+ 0f509a0cb07e0ab433176fa47a21dccccc6b519f25f640cc58561104c11de6c2       95 main/binary-amd64/Release
+SHA512:
+ f69f09c6180ceb6625a84b5f7123ad27972983146979dcfd9c38b2990459b52b4975716f85374511486bb5ad5852ebb1ef8265176df7134fc15b17ada3ba596c  5821166 Contents-amd64
+ 46031bf89166188989368957d20cdcaac6eec72bab3f9839c9704bb08cbee3174ca6da11e290b0eab0e6b5754c1e7feb06d18ec9c5a0c955029cef53235e0a3a   286298 Contents-amd64.gz
+ f69f09c6180ceb6625a84b5f7123ad27972983146979dcfd9c38b2990459b52b4975716f85374511486bb5ad5852ebb1ef8265176df7134fc15b17ada3ba596c  5821166 main/Contents-amd64
+ 46031bf89166188989368957d20cdcaac6eec72bab3f9839c9704bb08cbee3174ca6da11e290b0eab0e6b5754c1e7feb06d18ec9c5a0c955029cef53235e0a3a   286298 main/Contents-amd64.gz
+ 3f78baf5adbaf0100996555b154807c794622fd0b5879b568ae0b6560e988fbfabed8d97db5a703d1a58514b9690fc6b60f9ad2eeece473d86ab257becd0ae41  1088265 main/binary-amd64/Packages
+ 18f26df90beff29192662ca40525367c3c04f4581d59d2e9ab1cd0700a145b6a292a1609ca33ebe1c211f13718a8eee751f41fd8189cf93d52aa3e0851542dfc   159344 main/binary-amd64/Packages.bz2
+ 6a6d917229e0cf06c493e174a87d76e815717676f2c70bcbd3bc689a80bd3c5489ea97db83b8f74cba8e70f374f9d9974f22b1ed2687a4ba1dacd22fdef7e14d   169456 main/binary-amd64/Packages.gz
+ e1a4378ad266c13c2edf8a0e590fa4d11973ab99ce79f15af005cb838f1600f66f3dc6da8976fa8b474da9073c118039c27623ab3360c6df115071497fe4f50c       95 main/binary-amd64/Release
+
+-----BEGIN PGP SIGNATURE-----
+Version: BSN Pgp v1.0.0.0
+
+iQEcBAEBCAAGBQJeHC1bAAoJEOs+lK2+EinPAg8H/1rrhcgfm1HYL+Vmr9Ns6ton
+LWQ8r13ADN66UTRa3XsO9V+q1fYowTqpXq6EZt2Gmlby/cpDf7mFPM5IteOXWLl7
+QcWxPKHcdPIUi+h5F7BkFW65imP9GyX+V5Pxx5X544op7hYKaI0gAQ1oYtWDb3HE
+4D27fju6icbj8w6E8TePcrDn82UvWAcaI5WSLboyhXCt2DxS3PNGFlyaP58zKJ8F
+9cbBzksuMgMaTPAAMrU0zrFGfGeQz0Yo6nV/gRGiQaL9pSeIJWSKLNCMG/nIGmv2
+xHVNFqTEetREY6UcQmuhwOn4HezyigH6XCBVp/Uez1izXiNdwBOet34SSvnkuJ4=
+-----END PGP SIGNATURE-----`)
+
 var signingKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
 Version: GnuPG v1.4.10 (GNU/Linux)