CVE-2019-11841
Description
A message-forgery issue was discovered in crypto/openpgp/clearsign/clearsign.go in supplementary Go cryptography libraries 2019-03-25. According to the OpenPGP Message Format specification in RFC 4880 chapter 7, a cleartext signed message can contain one or more optional "Hash" Armor Headers. The "Hash" Armor Header specifies the message digest algorithm(s) used for the signature. However, the Go clearsign package ignores the value of this header, which allows an attacker to spoof it. Consequently, an attacker can lead a victim to believe the signature was generated using a different message digest algorithm than what was actually used. Moreover, since the library skips Armor Header parsing in general, an attacker can not only embed arbitrary Armor Headers, but also prepend arbitrary text to cleartext messages without invalidating the signatures.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
The Go clear-signing library ignores the Hash Armor Header and skips Armor Header validation, allowing attackers to spoof message content and digest algorithms.
Vulnerability
Description
A message-forgery issue was discovered in crypto/openpgp/clearsign/clearsign.go in supplementary Go cryptography libraries as of 2019-03-25 [2]. The flaw lies in how the package handles cleartext signed messages according to the OpenPGP Message Format specification (RFC 4880, chapter 7). The specification permits one or more optional "Hash" Armor Headers that indicate the message digest algorithm(s) used for the signature. However, the clearsign package ignores the value of this header, allowing an attacker to spoof it and embed arbitrary Armor Headers or prepend arbitrary text to cleartext messages without invalidating the signatures [2].
Attack
Vector & Exploitation
An attacker can craft a malformed cleartext message by inserting arbitrary Armor Headers or text before the signed content. Since the library skips Armor Header parsing in general, a human user viewing the message in a terminal or email client that renders certain control characters (e.g., vertical tab printed as newline) could be tricked into believing unverified header text is part of the signed message [4]. For example, inserting \x0b (vertical tab) in the header area makes it appear as a legitimate newline to a human reader. The attacker does not need to break the cryptographic signature; they can simply prepend text without invalidating it [2].
Impact
A successful attack leads to a complete spoofing of the cleartext message. The victim can be misled to believe that the signature was generated using a different (potentially weaker) message digest algorithm than what was actually used. Moreover, the attacker can embed arbitrary text that appears to be verified content, enabling phishing, document forgery, or other social engineering attacks without any cryptographic warning [2][4].
Mitigation
The vulnerability was fixed in commit c05e17bb3b2dca130fc919668a96b4bec9eb9442 to the Go crypto repository [4]. The fix implements stricter validation of Armor Headers and rejects malformed messages that could confuse human readers. Users should update to a patched version of the supplementary Go cryptography library (golang.org/x/crypto) to avoid the issue [3]. No workaround is available other than applying the patch.
AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
golang.org/x/cryptoGo | < 0.0.0-20190424203555-c05e17bb3b2d | 0.0.0-20190424203555-c05e17bb3b2d |
Affected products
8- Go/cryptography librariesdescription
- osv-coords7 versionspkg:apk/chainguard/k3dpkg:apk/chainguard/k3d-proxypkg:apk/chainguard/k3d-toolspkg:apk/wolfi/k3dpkg:apk/wolfi/k3d-proxypkg:apk/wolfi/k3d-toolspkg:golang/golang.org/x/crypto
< 5.6.0-r11+ 6 more
- (no CPE)range: < 5.6.0-r11
- (no CPE)range: < 5.6.0-r11
- (no CPE)range: < 5.6.0-r11
- (no CPE)range: < 5.6.0-r11
- (no CPE)range: < 5.6.0-r11
- (no CPE)range: < 5.6.0-r11
- (no CPE)range: < 0.0.0-20190424203555-c05e17bb3b2d
Patches
1c05e17bb3b2dopenpgp/clearsign: reject potentially misleading headers and messages
2 files changed · +85 −22
openpgp/clearsign/clearsign.go+28 −9 modified@@ -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
openpgp/clearsign/clearsign_test.go+57 −13 modified@@ -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)
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
16- github.com/advisories/GHSA-x3jr-pf6g-c48fghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2019-11841ghsaADVISORY
- packetstormsecurity.com/files/152840/Go-Cryptography-Libraries-Cleartext-Message-Spoofing.htmlghsaWEB
- github.com/golang/crypto/commit/c05e17bb3b2dca130fc919668a96b4bec9eb9442ghsaWEB
- github.com/golang/crypto/tree/master/openpgp/clearsignghsaPACKAGE
- go-review.git.corp.google.com/c/crypto/+/173778ghsaWEB
- go.googlesource.com/crypto/+/c05e17bb3b2dca130fc919668a96b4bec9eb9442ghsaWEB
- groups.google.com/d/msg/golang-openpgp/6vdgZoTgbIY/K6bBY9z3DAAJghsaWEB
- lists.debian.org/debian-lts-announce/2019/09/msg00011.htmlghsamailing-listWEB
- lists.debian.org/debian-lts-announce/2020/10/msg00014.htmlghsamailing-listWEB
- lists.debian.org/debian-lts-announce/2023/06/msg00017.htmlghsamailing-listWEB
- pkg.go.dev/vuln/GO-2023-1992ghsaWEB
- web.archive.org/web/20201207161832/https://sec-consult.com/en/blog/advisories/cleartext-message-spoofing-in-go-cryptography-libraries-cve-2019-11841ghsaWEB
- go.googlesource.com/crypto/mitre
- sec-consult.commitre
- sec-consult.com/en/blog/advisories/cleartext-message-spoofing-in-go-cryptography-libraries-cve-2019-11841/mitre
News mentions
0No linked articles in our index yet.