📄 pgparmor.c
字号:
/*
* pgpArmor.c -- a module to perform Ascii Armor
*
* Written by: Derek Atkins <warlord@MIT.EDU>
*
* $Id: pgpArmor.c,v 1.46 1999/03/25 18:22:52 melkins Exp $
*/
#include "pgpConfig.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "pgpDebug.h"
#include "pgpArmor.h"
#include "pgpCRC.h"
#include "pgpPktByte.h"
#include "pgpRadix64.h"
#include "pgpAnnotate.h"
#include "pgpFIFO.h"
#include "pgpHashPriv.h"
#include "pgpJoin.h"
#include "pgpEnv.h"
#include "pgpMem.h"
#include "pgpEnv.h"
#include "pgpPipeline.h"
#include "pgpRandomX9_17.h"
#include "pgpSplit.h"
#include "pgpUsuals.h"
#include "pgpFeatures.h"
#include "pgpContext.h"
#define ARMORMAGIC 0xa4904f11
/* Max length of a line in MIME content-printable encoding */
#define MIMEMAX 76
/* Number characters in our random MIME separator */
#define NSEPCHARS 48
/*
* PGP-MIME headers. We chose "=-" as boundary since it can't occur in
* a quoted-printable encoding, nor in base64.
*/
#define MIMEENCSEP "=--"
#define MIMESIGHDR1a "Mime-Version: 1.0\n"
#define MIMESIGHDR1b \
"Content-Type: multipart/signed;\n boundary=\""
#define MIMESIGHDR2 "\";\n" \
" protocol=\"application/pgp-signature\"; "
#define MIMESIGHDRLINESa 1
#define MIMESIGHDRLINESb 2
#define MIMEENCHDR1a "Mime-Version: 1.0\n"
#define MIMEENCHDR1b \
"Content-Type: multipart/encrypted; boundary=\"" MIMEENCSEP "\";\n" \
" protocol=\"application/pgp-encrypted\"\n\n"
#define MIMEENCHDR2 "--" MIMEENCSEP "\n" \
"Content-Type: application/pgp-encrypted\n\n" \
"Version: 1\n\n--" MIMEENCSEP "\n" \
"Content-Type: application/octet-stream\n\n"
#define MIMEENCHDRLINES1a 1
#define MIMEENCHDRLINES1b 3
#define MIMEMIC "micalg=pgp-"
#define MIMEDFLTMIC "pgp-md5"
#define MIMEMIDBOUND "Content-Type: application/pgp-signature\n\n"
typedef struct ArmorContext
{
/* by placing this here, we can just allocate everything at once */
PGPPipeline pipe;
PGPPipeline *tail;
PGPFifoDesc const *fd;
PGPFifoContext *fifo;
PGPFifoContext *header;
PGPContextRef cdkContext;
unsigned long crc;
unsigned long armorlines;
unsigned long lineno;
unsigned thispart;
unsigned maxparts;
int scope_depth;
PGPByte input[48]; /* Maximum 48 bytes */
#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 mimesigsep[NSEPCHARS+1];
char * outptr;
PGPBoolean needmessageid;
char * messageid;
char const * blocktype;
char const * comment;
char const * charset;
char const * versionString;
PGPSize mimebodyoff; /* Offset to start of body (1 char/nl) */
PGPUInt32 mimeheaderlines; /* # lines in mime header */
PgpVersion version;
PGPByte inlen;
PGPByte outlen;
PGPByte clearsign;
PGPByte pgpmime;
#define PGPMIMESIG 1
#define PGPMIMEENC 2
PGPByte pgpmimeversionline;
PGPByte didheader;
PGPByte didfooter;
PGPByte sizevalid;
PGPByte state;
PGPByte linebuf;
DEBUG_STRUCT_CONSTRUCTOR( ArmorContext )
} ArmorContext;
/* Forward declarations */
static int
armorLine (PGPByte const *in, unsigned inlen, char *out);
/*
* 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(PGPByte 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];
}
static void
armorWriteClassify (ArmorContext *context, PGPByte const *buf)
{
if (context->blocktype)
return;
if (PKTBYTE_TYPE(*buf) == PKTBYTE_PUBKEY)
context->blocktype = "PUBLIC KEY BLOCK";
else if (PKTBYTE_TYPE(*buf) == PKTBYTE_SECKEY)
context->blocktype = "PRIVATE KEY BLOCK";
else
context->blocktype = "MESSAGE";
}
/* Message ID is derived by hashing the beginning of the message data */
static void
armorDeriveMessageID (ArmorContext *context)
{
PGPHashVTBL const *h;
PGPHashContext *hc;
PGPMemoryMgrRef memoryMgr;
PGPByte const *p;
PGPUInt32 len;
PGPByte hashdata[24]; /* Produces 32 chars of messageid */
memoryMgr = PGPGetContextMemoryMgr( context->cdkContext );
pgpAssert (IsntNull (memoryMgr) );
h = pgpHashByNumber (kPGPHashAlgorithm_SHA);
if (!h)
goto error;
hc = pgpHashCreate (memoryMgr, h);
if (!hc)
goto error;
PGPContinueHash (hc, context->input, context->inlen);
p = (PGPByte *) pgpHashFinal (hc);
pgpClearMemory (hashdata, sizeof(hashdata));
pgpCopyMemory (p, hashdata, pgpMin (h->hashsize, sizeof(hashdata)));
PGPFreeHashContext (hc);
context->messageid = (char *)PGPNewData (memoryMgr,
sizeof(hashdata)*4/3 + 1, 0);
if (!context->messageid)
return;
len = armorLine (hashdata, sizeof(hashdata), context->messageid);
context->messageid[len] = '\0';
return;
error:
/* Create an empty messageid */
context->messageid = (char *)PGPNewData ( memoryMgr, 1, 0 );
context->messageid[0] = '\0';
return;
}
static PGPError
armorFlushHeader (ArmorContext *context)
{
PGPByte const *ptr;
PGPSize len;
PGPError error;
size_t retlen;
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);
}
return kPGPError_NoErr;
}
static void
armorMakeHeader (ArmorContext *context)
{
char temp[20];
if (context->pgpmime == PGPMIMEENC) {
context->mimebodyoff = 0;
context->mimeheaderlines = 0;
if( context->pgpmimeversionline ) {
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)MIMEENCHDR1a, strlen(MIMEENCHDR1a));
context->mimebodyoff += strlen(MIMEENCHDR1a);
context->mimeheaderlines += MIMEENCHDRLINES1a;
}
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)MIMEENCHDR1b, strlen(MIMEENCHDR1b));
context->mimebodyoff += strlen(MIMEENCHDR1b);
context->mimeheaderlines += MIMEENCHDRLINES1b;
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)MIMEENCHDR2, strlen(MIMEENCHDR2));
}
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)"-----BEGIN PGP ", 15);
pgpFifoWrite (context->fd, context->header, (PGPByte *)context->blocktype,
strlen (context->blocktype));
if (context->maxparts) {
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)", PART ", 7);
sprintf (temp, "%02u", context->thispart);
pgpFifoWrite (context->fd, context->header, (PGPByte const *)temp,
strlen (temp));
if (context->version <= PGPVERSION_3 ||
context->thispart == context->maxparts) {
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)"/", 1);
sprintf (temp, "%02u", context->maxparts);
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)temp, strlen (temp));
}
}
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)"-----\nVersion: ", 15);
if( IsntNull( context->versionString ) ) {
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)context->versionString,
strlen (context->versionString));
} else {
char sVersionString[ 256 ];
PGPGetSDKString( sVersionString );
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)sVersionString, strlen (sVersionString));
}
if (context->needmessageid && context->maxparts) {
if (!context->messageid) {
armorDeriveMessageID (context);
}
if (context->messageid) {
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)"\nMessageID: ", 12);
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)context->messageid,
strlen (context->messageid));
}
}
/* Don't do comment if empty string. This is more convenient for
* callers who want no comment, in some cases. */
if (context->comment && context->comment[0]) {
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)"\nComment: ", 10);
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)context->comment,
strlen (context->comment));
}
if (context->charset) {
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)"\nCharset: ", 10);
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)context->charset,
strlen (context->charset));
}
pgpFifoWrite (context->fd, context->header, (PGPByte *)"\n\n", 2);
context->didheader = 1;
}
static void
armorMakeFooter (ArmorContext *context)
{
char temp[20];
PGPByte crc[3];
/* Emit CRC, MSB-first */
crc[0] = (PGPByte)(context->crc >> 16);
crc[1] = (PGPByte)(context->crc >> 8);
crc[2] = (PGPByte)context->crc;
armorMorsel (crc, temp);
pgpFifoWrite (context->fd, context->header, (PGPByte const *)"=", 1);
pgpFifoWrite (context->fd, context->header, (PGPByte const *)temp, 4);
/* Now emit the end */
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)"\n-----END PGP ", 14);
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)context->blocktype,
strlen (context->blocktype));
if (context->maxparts) {
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)", PART ", 7);
sprintf (temp, "%02u", context->thispart);
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)temp, strlen (temp));
if (context->version <= PGPVERSION_3 ||
context->thispart == context->maxparts) {
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)"/", 1);
sprintf (temp, "%02u", context->maxparts);
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)temp, strlen (temp));
}
}
pgpFifoWrite (context->fd, context->header,
(PGPByte const *)"-----\n", 6);
if (context->pgpmime) {
pgpFifoWrite (context->fd, context->header, (PGPByte *)"\n--", 3);
if (context->pgpmime == PGPMIMEENC) {
pgpFifoWrite (context->fd, context->header,
(PGPByte *)MIMEENCSEP, strlen(MIMEENCSEP));
} else {
pgpFifoWrite (context->fd, context->header,
(PGPByte *)context->mimesigsep, NSEPCHARS);
}
pgpFifoWrite (context->fd, context->header, (PGPByte *)"--\n", 3);
}
context->didfooter = 1;
context->crc = CRC_INIT;
}
static PGPError
armorNewFile (PGPPipeline *myself)
{
ArmorContext *context = (ArmorContext *)myself->priv;
PGPError error;
PGPSize thispart;
/*
* First, if we're already in a part, then close off the last
* one.
*/
if (context->thispart) {
if (!context->didfooter)
armorMakeFooter (context);
error = armorFlushHeader (context);
if (error)
return error;
error = context->tail->sizeAdvise (context->tail, 0);
if (error)
return error;
error = context->tail->annotate (context->tail, myself,
PGPANN_MULTIARMOR_END, 0, 0);
if (error)
return error;
}
thispart = context->thispart+1;
error = context->tail->annotate (context->tail, myself,
PGPANN_MULTIARMOR_BEGIN,
(PGPByte const *)&thispart,
sizeof (thispart));
if (error)
return error;
context->thispart++;
context->didheader = 0;
context->didfooter = 0;
context->lineno = 0;
/* And give it an appropriate header */
armorMakeHeader (context);
return kPGPError_NoErr;
}
/*
* 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 (PGPByte 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);
}
static void
armorProcessLine (ArmorContext *context)
{
/* Update CRC */
context->crc = crcUpdate (context->crc, context->input,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -