Cleartext Signature Plaintext Truncated for Hash Calculation
An attacker can extend certain singed messages with arbitrary data in a way that still passed signature verification in GnuPG.
Impact
If an attacker obtains the signature S and plaintext P of a message where the message plaintext contains ‘\f\n’ (or in other words, has a line ending in ‘\f’), the attacker can craft a signature plaintext pair (S,P’) where P’ has attacker controlled inserts at those occurences in the plaintext and still successfully verifies.
Practically this this is applicable to the following scenario:
- An attacker obtains (P, S’) and
Details
GnuPG truncates plaintext lines to 20000 characters minus padding:
#define MAX_LINELEN 20000
// ...
/* read the next line */
maxlen = MAX_LINELEN;
afx->buffer_pos = 0;
afx->buffer_len = iobuf_read_line(a, &afx->buffer,
&afx->buffer_size, &maxlen);
if (!afx->buffer_len) {
rc = -1; /* eof (should not happen) */
continue;
}
if (!maxlen) {
afx->truncated++;
this_truncated = 1;
} else this_truncated = 0;
// ...
/* Now handle the end-of-line canonicalization */
if (!afx->not_dash_escaped || this_truncated) {
int crlf = n > 1 && p[n - 2] == '\r' && p[n - 1] == '\n';
afx->buffer_len =
trim_trailing_chars(&p[afx->buffer_pos], n - afx->buffer_pos,
" \t\r\n");
afx->buffer_len += afx->buffer_pos;
/* the buffer is always allocated with enough space to append
* the removed [CR], LF and a Nul
* The reason for this complicated procedure is to keep at least
* the original type of lineending - handling of the removed
* trailing spaces seems to be impossible in our method
* of faking a packet; either we have to use a temporary file
* or calculate the hash here in this module and somehow find
* a way to send the hash down the processing line (well, a special
* faked packet could do the job).
*
* To make sure that a truncated line triggers a bad
* signature error we replace a removed LF by a FF or
* append a FF. Right, this is a hack but better than a
* global variable and way easier than to introduce a new
* control packet or insert a line like "[truncated]\n"
* into the filter output.
*/
if (crlf) afx->buffer[afx->buffer_len++] = '\r';
afx->buffer[afx->buffer_len++] = this_truncated ? '' : '\n';
afx->buffer[afx->buffer_len] = '