diff --git a/openpgp/clearsign/clearsign.go b/openpgp/clearsign/clearsign.go index a9437dc..c360460 100644 --- a/openpgp/clearsign/clearsign.go +++ b/openpgp/clearsign/clearsign.go @@ -18,6 +18,7 @@ import ( "io" "net/textproto" "strconv" + "strings" "golang.org/x/crypto/openpgp/armor" "golang.org/x/crypto/openpgp/errors" @@ -27,7 +28,7 @@ import ( // A Block represents a clearsigned message. A signature on a Block can // be checked by passing Bytes into openpgp.CheckDetachedSignature. type Block struct { - Headers textproto.MIMEHeader // Optional message headers + Headers textproto.MIMEHeader // Optional unverified Hash headers Plaintext []byte // The original message text Bytes []byte // The signed message ArmoredSignature *armor.Block // The signature block @@ -69,8 +70,13 @@ func getLine(data []byte) (line, rest []byte) { return data[0:i], data[j:] } -// Decode finds the first clearsigned message in data and returns it, as well -// as the suffix of data which remains after the message. +// Decode finds the first clearsigned message in data and returns it, as well as +// the suffix of data which remains after the message. Any prefix data is +// discarded. +// +// If no message is found, or if the message is invalid, Decode returns nil and +// the whole data slice. The only allowed header type is Hash, and it is not +// verified against the signature hash. func Decode(data []byte) (b *Block, rest []byte) { // start begins with a newline. However, at the very beginning of // the byte array, we'll accept the start string without it. @@ -83,8 +89,11 @@ func Decode(data []byte) (b *Block, rest []byte) { return nil, data } - // Consume the start line. - _, rest = getLine(rest) + // Consume the start line and check it does not have a suffix. + suffix, rest := getLine(rest) + if len(suffix) != 0 { + return nil, data + } var line []byte b = &Block{ @@ -103,15 +112,25 @@ func Decode(data []byte) (b *Block, rest []byte) { break } + // Reject headers with control or Unicode characters. + if i := bytes.IndexFunc(line, func(r rune) bool { + return r < 0x20 || r > 0x7e + }); i != -1 { + return nil, data + } + i := bytes.Index(line, []byte{':'}) if i == -1 { return nil, data } - key, val := line[0:i], line[i+1:] - key = bytes.TrimSpace(key) - val = bytes.TrimSpace(val) - b.Headers.Add(string(key), string(val)) + key, val := string(line[0:i]), string(line[i+1:]) + key = strings.TrimSpace(key) + if key != "Hash" { + return nil, data + } + val = strings.TrimSpace(val) + b.Headers.Add(key, val) } firstLine := true diff --git a/openpgp/clearsign/clearsign_test.go b/openpgp/clearsign/clearsign_test.go index 96f5d78..737f41f 100644 --- a/openpgp/clearsign/clearsign_test.go +++ b/openpgp/clearsign/clearsign_test.go @@ -47,12 +47,6 @@ func TestParse(t *testing.T) { 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") } -func TestParseInvalid(t *testing.T) { - if b, _ := Decode(clearsignInput3); b != nil { - t.Fatal("decoded a bad clearsigned message without any error") - } -} - func TestParseWithNoNewlineAtEnd(t *testing.T) { input := clearsignInput input = input[:len(input)-len("trailing")-1] @@ -140,6 +134,10 @@ func (qr *quickRand) Read(p []byte) (int, error) { } func TestMultiSign(t *testing.T) { + if testing.Short() { + t.Skip("skipping long test in -short mode") + } + zero := quickRand(0) config := packet.Config{Rand: &zero} @@ -193,6 +191,59 @@ func TestMultiSign(t *testing.T) { } } +const signatureBlock = ` +-----BEGIN PGP SIGNATURE----- +Version: OpenPrivacy 0.99 + +yDgBO22WxBHv7O8X7O/jygAEzol56iUKiXmV+XmpCtmpqQUKiQrFqclFqUDBovzS +vBSFjNSiVHsuAA== +=njUN +-----END PGP SIGNATURE----- +` + +var invalidInputs = []string{ + ` +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +(This message was truncated.) +`, + ` +-----BEGIN PGP SIGNED MESSAGE-----garbage +Hash: SHA256 + +_o/ +` + signatureBlock, + ` +garbage-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA256 + +_o/ +` + signatureBlock, + ` +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA` + "\x0b\x0b" + `256 + +_o/ +` + signatureBlock, + ` +-----BEGIN PGP SIGNED MESSAGE----- +NotHash: SHA256 + +_o/ +` + signatureBlock, +} + +func TestParseInvalid(t *testing.T) { + for i, input := range invalidInputs { + if b, rest := Decode([]byte(input)); b != nil { + t.Errorf("#%d: decoded a bad clearsigned message without any error", i) + } else if string(rest) != input { + t.Errorf("#%d: did not return all data with a bad message", i) + } + } +} + var clearsignInput = []byte(` ;lasjlkfdsa @@ -235,13 +286,6 @@ qZg6BaTvOxepqOxnhVU= trailing`) -var clearsignInput3 = []byte(` ------BEGIN PGP SIGNED MESSAGE----- -Hash: SHA256 - -(This message was truncated.) -`) - var signingKey = `-----BEGIN PGP PRIVATE KEY BLOCK----- Version: GnuPG v1.4.10 (GNU/Linux)