pgparmor.c
来自「著名的加密软件的应用于电子邮件中」· C语言 代码 · 共 1,332 行 · 第 1/3 页
C
1,332 行
/*
* pgpArmor.c -- a module to perform Ascii Armor
*
* Copyright (C) 1995-1997 Pretty Good Privacy, Inc. All rights reserved.
*
* Written by: Derek Atkins <warlord@MIT.EDU>
*
* $Id: pgpArmor.c,v 1.6.2.2.2.1 1997/07/16 18:51:41 colin Exp $
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <ctype.h>
#include "pgpDebug.h"
#include "pgpArmor.h"
#include "pgpCRC.h"
#include "pgpPktByte.h"
#include "pgpRadix64.h"
#include "pgpAnnotate.h"
#include "pgpFIFO.h"
#include "pgpHash.h"
#include "pgpJoin.h"
#include "pgpEnv.h"
#include "pgpMem.h"
#include "pgpEnv.h"
#include "pgpPipeline.h"
#include "pgpRndom.h"
#include "pgpSplit.h"
#include "pgpUsuals.h"
#define ARMORMAGIC 0xa4904f11
/* Max length of a line in MIME content-printable encoding */
#define MIMEMAX 76
/*
* PGP-MIME headers. We chose "=-" as boundary since it can't occur in
* a quoted-printable encoding, nor in base64.
*/
#define MIMESEP "=--"
#define MIMESIGHDR "Mime-Version: 1.0\n" \
"Content-Type: multipart/signed; boundary=\"" MIMESEP "\";\n" \
" protocol=\"application/pgp-signature\"; "
#define MIMEENCHDR "Mime-Version: 1.0\n" \
"Content-Type: multipart/encrypted; boundary=\"" MIMESEP "\";\n" \
" protocol=\"application/pgp-encrypted\";\n\n" \
"--" MIMESEP "\nContent-Type: application/pgp-encrypted\n\n" \
"Version: 1\n\n--" MIMESEP "\n" \
"Content-Type: application/octet-stream\n\n"
#define MIMEMIC "micalg=pgp-"
#define MIMEDFLTMIC "md5"
#define MIMETOPBOUND "\n\n--" MIMESEP "\n"
#define MIMEMIDBOUND "\n--" MIMESEP "\n" \
"Content-Type: application/pgp-signature\n\n"
#define MIMEENDBOUND "\n--" MIMESEP "--\n"
#define MIMETOPHDR "Content-Type: text/plain; charset=iso-8859-1\n"\
"Content-Transfer-Encoding: quoted-printable\n\n"
/* Input size for a line of ASCII armor. */
#define ARMOR_LINE_INPUT 48
/*
* Hairy context with lots of flags for the vsrious options.
* The actual radix-64 encoding always takes the input[] array
* as input and the produces a full line in the output[] array as
* output. Then armorFlushOutput() routine writes it out.
*/
struct Context {
struct PgpPipeline *tail;
struct PgpFifoDesc const *fd;
struct PgpFifoContext *fifo;
struct PgpFifoContext *header;
unsigned long crc;
unsigned long armorlines; /* This must be an unsigned type */
unsigned long lineno;
unsigned thispart;
unsigned maxparts;
int scope_depth;
byte input[ARMOR_LINE_INPUT];
#if 0
char output[65]; /* Maximum 63 buyes + \r and/or \n */
#else
char output[MIMEMAX+2]; /* MIME line plus \n plus null */
#endif
char * outptr; /* First unwritten byte in output[] */
char * messageid; /* This is allocated in create */
char const * blocktype;
char const * comment;
char const * charset;
PgpVersion version;
byte inlen; /* valid bytes in input[] */
byte outlen; /* valid bytes in output[] */
byte clearsign; /* boolean, suppresses final footer */
byte pgpmime;
#define PGPMIMESIG 1
#define PGPMIMEENC 2
byte didheader; /* this part's header written to FIFO yet? */
byte didfooter; /* this part's footer written to FIFO yet? */
byte pgpann_pending; /* PGPANN_MULTIARMOR_END needs to be sent */
byte at_eof; /* boolean, have got sizeAdvise(0) */
byte state; /* Used by clearsign quoting */
byte linebuf; /* Used by clearsign quoting */
};
/*
* Armor 3 raw bytes into 4
* If armoring n < 3 bytes, make the trailers zero, and
* then overwrite the trailing 3-n bytes with '='
*/
static void
armorMorsel(byte const raw[3], char armor[4])
{
armor[0] = armorTable[raw[0] >> 2 & 0x3f];
armor[1] = armorTable[(raw[0] << 4 & 0x30) + (raw[1] >> 4 & 0x0f)];
armor[2] = armorTable[(raw[1] << 2 & 0x3c) + (raw[2] >> 6 & 0x03)];
armor[3] = armorTable[raw[2] & 0x3f];
}
/* Classify the data in the input buffer, if it hasn't already been done */
static void
armorWriteClassify (struct Context *context)
{
if (context->blocktype)
return;
if (context->inlen && PKTBYTE_TYPE(context->input[0]) == PKTBYTE_PUBKEY)
context->blocktype = "PUBLIC KEY BLOCK";
else
context->blocktype = "MESSAGE";
}
/* Write out the output buffer (outptr, outlen) */
static int
armorFlushOutput (struct Context *context)
{
int error;
size_t retlen;
pgpAssert (context);
pgpAssert (context->tail);
/* Try to flush anything that we have buffered. */
while (context->outlen) {
retlen = context->tail->write (context->tail,
(byte *)context->outptr,
context->outlen,
&error);
context->outlen -= retlen;
context->outptr += retlen;
if (error)
return error;
}
return 0;
}
/*
* Try to write out all pending output, the output buffer, the header/footer
* FIFO, and a pending PGPANN_MULTIARMOR_END annotation.
*/
static int
armorFlushPending (struct PgpPipeline *myself)
{
struct Context *context = (struct Context *)myself->priv;
byte const *ptr;
unsigned len;
int error;
size_t retlen;
/* Output buffer */
error = armorFlushOutput (context);
if (error)
return error;
/* header/footer FIFO */
ptr = pgpFifoPeek (context->fd, context->header, &len);
while (len) {
retlen = context->tail->write (context->tail, ptr, len,
&error);
pgpFifoSeek (context->fd, context->header, retlen);
if (error)
return error;
ptr = pgpFifoPeek (context->fd, context->header, &len);
}
/* Annotation */
if (context->pgpann_pending) {
error = context->tail->annotate (context->tail, myself,
PGPANN_MULTIARMOR_END, 0, 0);
if (error)
return error;
context->pgpann_pending = 0;
}
return 0;
}
/* Build a header and write it to the header FIFO */
static int
armorMakeHeader (struct PgpPipeline *myself)
{
struct Context *context = (struct Context *)myself->priv;
char temp[20];
/* Write out the opening annotation, if needed */
if (context->maxparts != 1) {
unsigned thispart = context->thispart+1;
int error;
error = context->tail->annotate (context->tail, myself,
PGPANN_MULTIARMOR_BEGIN,
(byte const *)&thispart,
sizeof (thispart));
if (error)
return error;
}
/* Do the goodies */
context->thispart++;
context->lineno = 0;
context->didfooter = 0;
if (context->pgpmime == PGPMIMEENC) {
pgpFifoWrite (context->fd, context->header,
(byte const *)MIMEENCHDR, strlen(MIMEENCHDR));
}
pgpFifoWrite (context->fd, context->header,
(byte const *)"-----BEGIN PGP ", 15);
pgpFifoWrite (context->fd, context->header, (byte *)context->blocktype,
strlen (context->blocktype));
if (context->maxparts != 1) {
sprintf (temp, ", PART %02u", context->thispart);
pgpFifoWrite (context->fd, context->header, (byte const *)temp,
strlen (temp));
if (context->version <= PGPVERSION_2_6 ||
context->thispart == context->maxparts) {
sprintf (temp, "/%02u", context->maxparts);
pgpFifoWrite (context->fd, context->header,
(byte const *)temp, strlen (temp));
}
}
pgpFifoWrite (context->fd, context->header,
(byte const *)"-----\nVersion: ", 15);
pgpFifoWrite (context->fd, context->header,
(byte const *)pgpLibVersionString,
strlen (pgpLibVersionString));
if (context->messageid) {
pgpFifoWrite (context->fd, context->header,
(byte const *)"\nMessageID: ", 12);
pgpFifoWrite (context->fd, context->header,
(byte const *)context->messageid,
strlen (context->messageid));
}
if (context->comment) {
pgpFifoWrite (context->fd, context->header,
(byte const *)"\nComment: ", 10);
pgpFifoWrite (context->fd, context->header,
(byte const *)context->comment,
strlen (context->comment));
}
if (context->charset) {
pgpFifoWrite (context->fd, context->header,
(byte const *)"\nCharset: ", 10);
pgpFifoWrite (context->fd, context->header,
(byte const *)context->charset,
strlen (context->charset));
}
pgpFifoWrite (context->fd, context->header, (byte *)"\n\n", 2);
context->didheader = 1;
return 0; /* Success */
}
/* Build a footer (CRC onwards) and write it to the header FIFO */
static void
armorMakeFooter (struct Context *context)
{
char temp[20];
/* What to do with 0 bytes of data? */
if (!context->didheader)
return;
/* Emit CRC, MSB-first */
temp[0] = '=';
temp[5] = (byte)(context->crc >> 16);
temp[6] = (byte)(context->crc >> 8);
temp[7] = (byte)context->crc;
armorMorsel (temp+5, temp+1);
pgpFifoWrite (context->fd, context->header, (byte const *)temp, 5);
/* Now emit the -----END */
pgpFifoWrite (context->fd, context->header,
(byte const *)"\n-----END PGP ", 14);
pgpFifoWrite (context->fd, context->header,
(byte const *)context->blocktype,
strlen (context->blocktype));
if (context->maxparts != 1) {
sprintf (temp, ", PART %02u", context->thispart);
pgpFifoWrite (context->fd, context->header,
(byte const *)temp, strlen (temp));
if (context->version <= PGPVERSION_2_6 ||
context->thispart == context->maxparts) {
sprintf (temp, "/%02u", context->maxparts);
pgpFifoWrite (context->fd, context->header,
(byte const *)temp, strlen (temp));
}
}
pgpFifoWrite (context->fd, context->header, (byte const *)"-----\n", 6);
if (context->pgpmime) {
pgpFifoWrite (context->fd, context->header,
(byte const *)MIMEENDBOUND, strlen(MIMEENDBOUND));
}
context->didfooter = 1;
if (context->maxparts != 1)
context->pgpann_pending = 1; /* Send annotate afterwards! */
context->crc = CRC_INIT;
}
/*
* The method here to do the armoring is somewhat tricky.
* Most lines just have inlen = 48 which maps to 48*4/3 = 64
* output characters. But the last line has a short inlen.
* This leads to a truncated last group, which looks like one of:
* xx== (if the last group contains 1 byte - 4 bits of padding are zero)
* xxx= (if the last group contains 2 bytes - 2 bits of padding are zero)
* xxxx (if the last group contains 3 bytes)
* To do this, we make sure that we've added an extra 0 byte to the
* end of the input, then encode it in blocks of 3 bytes, then note by
* how much the encoding overshot the input length, len - inlen.
* This is 2, 1, or 0. Overwrite that many trailing characters with '='.
* Then a newline can be appended for output.
*/
static int
armorLine (byte const *in, unsigned inlen, char *out)
{
unsigned len;
int t;
char const *out0 = out;
/* Fill the output buffer from the input buffer */
for (len = 0; len < inlen; len += 3) {
armorMorsel (in, out);
in += 3;
out += 4;
}
/* Now back up and erase any overrun */
t = (int)(inlen - len); /* Zero or negative */
while (t)
out[t++] = '=';
return (out - out0);
}
/*
* Generate a line of output based on the current input buffer.
* Generate a header if required, then a line of output, writing both out.
* This should only be called when the input buffer is full, except for
* the last partial line. If it is called with a partial line, it
* must be terminated with a trailing 0 byte.
*/
static int
armorWriteLine (struct PgpPipeline *myself)
{
struct Context *context = (struct Context *)myself->priv;
int error = 0;
/* Make a header if required */
if (!context->didheader) {
armorWriteClassify (context);
error = armorMakeHeader (myself);
if (error)
return error;
error = armorFlushPending (myself);
if (error)
return error;
}
/* Update CRC */
context->crc = crcUpdate (context->crc, context->input,
context->inlen);
/* Refill the output buffer from the input buffer */
context->outlen = armorLine (context->input, context->inlen,
context->output);
context->output [context->outlen++] = '\n';
context->outptr = context->output;
/* Mark input as processed */
context->inlen = 0;
context->lineno++;
/* Write a footer, if required (note tricky "-1") */
if (context->lineno > context->armorlines - 1) {
if (!context->didfooter)
armorMakeFooter (context);
context->didheader = 0;
}
return armorFlushPending (myself);
}
/*
* Write out a final partial line in the input buffer.
*/
static int
armorWritePartial (struct PgpPipeline *myself)
{
struct Context *context = (struct Context *)myself->priv;
int error = 0;
error = armorFlushPending (myself);
if (error)
return error;
if (context->inlen) {
/* Ensure that padding byte is there! */
if (context->inlen < sizeof(context->input))
context->input[context->inlen] = 0;
error = armorWriteLine (myself);
}
return error;
}
/*
* Write out as many full lines as possible, leaving the rest in the
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?