pgpprsasc.c
来自「著名的加密软件的应用于电子邮件中」· C语言 代码 · 共 2,441 行 · 第 1/5 页
C
2,441 行
text->teardown (text);
sighead->teardown (sighead);
return PGPERR_NOMEM;
}
/* Now put it all together in this message */
part->text = text;
part->sig = sighead;
return 0;
}
/*
* Build up the Ascii Parser Table in order to dearmor data. For all
* values, set it to -1 if it is not a valid armor character and then
* set the valid characters out of the armorTable. Inited == 0 implies
* we need to init; Inited == 1 implies we are processing or done.
*/
static void
parseAscInit (void)
{
int i;
if (inited++)
return;
for (i = 0; i < 256; i++)
dearmorTable[i] = -1;
for (i = 0; i < (int)(sizeof(armorTable) - 1); i++)
dearmorTable[armorTable[i] & 0xff] = (char)i;
inited = 1;
return;
}
/*
* Convert input bytes to output bytes. Returns the number of
* input bytes successfully converted. The number of output
* bytes available is one less than this number.
*/
static int
dearmorMorsel (byte const in[4], byte out[3])
{
signed char c0, c1;
c0 = dearmorTable[in[0] & 255];
if (c0 < 0)
return 0;
c1 = dearmorTable[in[1] & 255];
if (c1 < 0)
return 1;
out[0] = (c0 & 63u) << 2 | (c1 & 63u) >> 4;
c0 = dearmorTable[in[2] & 255];
if (c0 < 0)
return 2;
out[1] = (c1 & 63u) << 4 | (c0 & 63u) >> 2;
c1 = dearmorTable[in[3] & 255];
if (c1 < 0)
return 3;
out[2] = (c0 & 63u) << 6 | (c1 & 63u);
return 4;
}
/*
* Given a line of a certain length, convert to binary and
* return the number of binary bytes that result, or -1 on error.
*
* This is very fussy about trailing junk and whatnot.
* There is some complexity due to accepting "=3D" in place
* of a normal "=". This is to allow MIME-encapsulated messages
* to be input directly, without having MIME unencapsulate them
* first.
*
* This will ignore trailing white space and give an error if there
* is too much data on the line.
*/
static int
dearmorLine (struct Context *ctx, unsigned inlen)
{
byte const *in = ctx->armorline;
byte *out = ctx->databuf;
int outlen = 0;
int t;
while ((t = dearmorMorsel (in, out)) == 4) {
in += 4;
out += 3;
inlen -= 4;
outlen += 3;
if (inlen < 4)
return inlen ? -1 : outlen;
}
switch (t) {
case 2:
if (inlen == 4) {
if (in[2] == '=' && in[3] == '=')
return outlen + 1;
} else if (inlen == 8) {
if (in[2] == '=' && in[3] == '3' && in[4] == 'D' &&
in[5] == '=' && in[6] == '3' && in[7] == 'D')
return outlen + 1;
}
break;
case 3:
if (inlen == 4) {
if (in[3] == '=')
return outlen + 2;
} else if (inlen == 6) {
if (in[3] == '=' && in[3] == '3' && in[4] == 'D')
return outlen + 2;
}
break;
}
/* None of the above - we have an error */
return -1;
}
/*
* Given a line thought to contain a CRC, this returns the 24-bit
* CRC, or -1 on error. Handles possible MIME expansion
* of "=" to "=3D".
*/
static long
dearmorCrc (struct Context *ctx)
{
byte const *in = ctx->armorline;
unsigned inlen = ctx->armorlen;
byte buf[3];
/* skip trailing white space */
while (inlen && isspace (in[inlen - 1]))
inlen--;
if (*in != '=')
return -1;
if (inlen == 5)
if (dearmorMorsel (in + 1, buf) != 4)
return -1;
else if (inlen == 7)
if (in[1] != '3' || in[2] != 'D' ||
dearmorMorsel (in + 3, buf) != 4)
return -1;
else
return -1;
/*
* Welcome to the famous ANSI C glitch. ANSI C
* promotes to signed values where possible when preserving
* the value. Thus, buf[1]<<8 is promoted to signed, then
* shifted, then promoted to long (on a 16-bit int machine,
* this causes sign-extension!), and merged with the other
* values. Not Good.
* I'm beginning to see why people preferred the K&R unsigned-
* preserving rules. Sigh.
*/
return (long)buf[0] << 16 | (unsigned)(buf[1] << 8 | buf[2]);
}
/*
* try to fill up ctx->armorline up to LINE_LEN-1 bytes or a newline,
* whichever comes first.
*
* This will set ctx->eol when an EOL condition occurs. eol == 1
* means armorline is ready for processing. eol == 2 means that
* readLine needs to be called with more data (looking for \n).
* In doublespace mode, we use two more values. eol==3 means that
* we saw one line ending (LF or CRLF) and now need more data to see
* another. eol==4 means that we saw CR-LF-CR and are now looking for
* the LF. (doublespace means we skip a blank line after every non-blank
* line. A third party application accidentally munges the files to that
* form.)
*
* The return value is the number of bytes used. If all the bytes are
* used and EOL is not set, then more data is required and readLine
* should be called with more data.
*/
static size_t
readLine (struct Context *ctx, byte const *buf, size_t size)
{
size_t size0 = size;
unsigned t, retlen;
byte eol = ctx->eol;
byte *ptr = ctx->armorline + ctx->armorlen;
if (!size)
return 0;
/*
* This test is needed in case the '\r' and '\n' come in
* different write calls.
*/
if (eol >= 2)
goto lineend;
/* try to fill the input buffer with a line */
t = (unsigned)min (LINE_LEN-1 - ctx->armorlen, size);
for (eol = 0, retlen = 0; retlen < t && !eol; retlen++) {
*ptr++ = *buf;
if (*buf == '\r' || *buf == '\n')
eol = (*buf == '\r' ? 2 : 1);
buf++;
}
size -= retlen;
ctx->armorlen += retlen;
/* Set EOB if we hit the end of the armorline buffer */
if (ctx->armorlen >= LINE_LEN-1)
ctx->eob = 1;
/*
* At this point we are either at EOL or not. If not, then we
* either ran out of input data or ran out of buffer space. If
* we are at EOL and eol==2 and we have more data, check if its a
* \n or something else. If so, copy that in and set eol=1.
* Then do the doublespace logic. If we get here with eol > 2 that
* can only happen from entry, and we use kludgy goto's to fall into
* the right place in the eol logic below.
*/
lineend:
if (eol) {
if (eol == 3)
goto eol3;
if (eol == 4)
goto eol4;
ctx->crlf = (eol == 2 ? PGP_TEXTFILT_CR : PGP_TEXTFILT_LF);
if (eol == 2 && size) {
if (*buf == '\n') {
*ptr++ = *buf++;
ctx->armorlen++;
ctx->crlf = PGP_TEXTFILT_CRLF;
size--;
}
eol = 1;
}
/*
* Don't do the doublespace transformation to blank lines!
* This is ad hoc but that is how the failing case presents
* itself. Unfortunately it is hard to know here that we are
* looking at a blank line. The calling code parses lines as soon
* as we return with eol > 0. Then later it calls us to "clean up"
* (get to eol==1),
* but it has reset the armorlen index to 0. It becomes hard to
* distinguish between a CR-LF at the end of the line, where we got
* split between them, and a CR-LF at the beginning. In each case
* our eol > 0 call looks much the same.
* To fix this, we have fixed the caller in the relevant portions
* (parsing of ascii armored messages/keys) to call us with an
* armorlen index of 1 while cleaning up. This lets us peek at
* the original first char of the line and determine whether we
* are looking at a blank line.
*/
if (ctx->doublespace && eol==1 && ctx->armorline[0]!='\n' &&
ctx->armorline[0]!='\r') {
/* Ignore any following blank line */
eol3:
if (!size) {
/* LF or CR-LF and no more data. Remember state 3 */
eol = 3;
} else if (*buf == '\n') {
/* Got a blank line, just ignore it */
size--;
eol = 1;
} else if (*buf == '\r') {
size--;
buf++;
if (ctx->crlf == PGP_TEXTFILT_CR) {
/* Got a blank line, just ignore it */
eol = 1;
} else {
/* Want to see CR-LF-CR-LF, got the first three... */
eol4:
if (!size) {
/* CR-LF-CR and no more data. Remember state 4 */
eol = 4;
} else if (*buf == '\n') {
/* OK, got all four, just ignore the line */
eol = 1;
size--;
} else {
/* CR-LF-CR-XX, turn off doublespace */
ctx->doublespace = FALSE;
size++; /* Give back CR we took */
eol = 1;
}
}
} else {
/* LF or CR-LF then XX */
ctx->doublespace = FALSE;
eol = 1;
}
}
}
/* Save off the EOL and return the number of bytes used */
ctx->eol = eol;
return size0-size;
}
/* Work like memcmp but ignore material in angle brackets for 1st arg */
/* On a match (return of 0) put number of removed chars in *removelen */
static int
bracketcmp(const char *orig, size_t origlen, const char *test, size_t testlen,
size_t *removelen)
{
char buf[LINE_LEN];
int inbracket = 0;
int rtn;
unsigned i, j;
/* Copy orig to buf, removing bracketed material */
pgpAssert (origlen <= sizeof(buf));
for (i=0,j=0; i<origlen && j < testlen; ++i) {
if (inbracket && orig[i]=='>')
inbracket = 0;
else if (!inbracket && orig[i]=='<')
inbracket = 1;
else if (!inbracket)
buf[j++] = orig[i];
}
rtn = memcmp(buf, test, testlen);
if (!rtn && removelen)
*removelen = i - j;
return rtn;
}
/*
* Contained in buf (of length size) should be a Header-line. Parse
* it appropriately, and store it as necessary.
*
* A headerline must fit in the armorline buffer (I.e., it must be
* less that LINE_LEN-1 bytes long), it must start with an Alpha
* character and then have up to 64 AlphaNumeric characters or dashes,
* followed by a colon and then a space. A header that does not
* conform to this is an error.
*
* Returns 0 on success, or a HEADER_ error code.
*/
#define HEADER_TOOLONG -1
#define HEADER_INVALID -2
static int
parseHeader (struct Context *ctx, byte const *buf, size_t size)
{
int i;
char const *ptr = (char const *)buf;
static char const messageid[] = "MessageID:";
char c;
(void)size; /* So, why do I have this arg? */
/* Make sure this is the WHOLE line -- if not, it is an error */
if (!ctx->eol)
return HEADER_TOOLONG;
/* The first character must be an Alpha character */
if (!isalpha (*ptr++))
return HEADER_INVALID;
i = 1;
for (;;) {
c = *ptr++;
/* header ends in a colon */
if (c == ':')
break;
/* Arbitrary label length restriction */
if (++i > 64)
return HEADER_TOOLONG; /* Error */
/* Following chars much be alphanumeric or '-' */
if (!isalnum(c) && c != '-')
return HEADER_INVALID; /* Error */
}
/* Must be a space after the ':' */
if (*ptr++ != ' ')
return HEADER_INVALID;
/*
* Now process the header
*
* ptr points to the value; buf points to the key (of length
* (ptr - buf - 2), plus a colon)
*/
i = ptr - (char const *)buf - 1;
/* Process message ID header */
if (i == sizeof (messageid) - 1 && !memcmp (buf, messageid, i))
memcpy (ctx->messageid, ptr,
min ((strlen (ptr) + 1), sizeof (ctx->messageid)));
/* XXX: signal an unknown header? */
return 0;
}
/* Write data to be dearmored.
*
* The main FSM to control dearmoring. The actual FSM is kind of
* complicated so I'll try to explain it by comments in the code.
* Suffice it to say that there are two main parts of the machine.
* The first part deals with armor'ed data, and the second part deals
* with out-of-armored text. There is a little glue between them, and
* there you have it...
*
* Here are the states, categorized into general phases of activity:
*
* 0 Start state, outside PGP data
*
* 1 - 5 Waiting for start of PGP data
*
* 9 - 10 Process -----BEGIN PGP line
*
* 11 - 13 Process headers after -----BEGIN PGP
*
* 14 - 16 Setup to begin processing armored data
*
* 17 - 20 Process PGP armored data
*
* 21 - 23 Process CRC at end of armored data
*
* 24 - 27 Process -----END PGP line, finish up, back to 0
*
* 30 - 33 Clearsigned message, process Hash: lines if any
*
* 34 - 36 Setup to begin processing clearsigned message
*
* 39 - 43 Process clearsigned message body, then to 9 for sig
*
* 50 - 53 Process binary message, to 26 to finish up when done
*
* 60 - 63 Scan MIME headers, get boundary string
*
* 65 - 68 Look for initial MIME boundary string, to 34 when found
*
* 70 - 72 Check apparent initial MIME separator, to 34 if looks OK
*
* 75 - 77 Look for start of PGP sig in MIME clearsign sig subpart, to 9
*
* 80 - 83 Look for final MIME clearsign boundary after PGP sig
*
*/
static size_t
Write (struct PgpPipeline *myself, byte const *buf, size_t size, int *error)
{
struct Context *ctx;
struct Message *msg;
struct MsgPart *part;
byte *ptr, *num;
static char const prefix1[] = "-----BEGIN PGP ";
static char const prefix2[] = ", PART ";
static char const prefix3[] = "-----END PGP ";
static char const suffix[] = "-----"; /* With trailing nul */
static char const signedmsg[] = "SIGNED MESSAGE-----";
static char const sigprefix[] = "-----BEGIN PGP SIGNATURE-----";
#if MIMEPARSE
byte *ptr2;
static char prefix_mimesig2[] = "-----BEGIN PGP MESSAGE-----";
static char prefix_mime1[] = "content-type: multipart/signed;";
static char prefix_mime2[] = "protocol=\"application/pgp-signature\"";
static char prefix_mime3[] = "boundary=";
#if 0
static char prefix_mime4[] = "micalg=";
static char prefix_mime5[] = "pgp-";
#endif /* 0 */
#endif /* MIMEPARSE */
int i, thispart, maxparts, lineused;
size_t retval, written = 0;
unsigned inlen;
size_t bracketlen = 0;
long crc;
char temp_c;
pgpAssert (myself);
pgpAssert (myself->magic == DEARMORMAGIC);
pgpAssert (error);
ctx = (struct Context *)myself->priv;
pgpAssert (ctx);
pgpAssert (ctx->tail);
switch (ctx->state) {
case 0:
case_0:
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?