📄 pgpkeymisc.c
字号:
for (;;) {
bnExtractBigBytes(bn, buf, start, sizeof(buf));
i = sizeof(buf);
start += i;
do {
if (buf[--i] == 0) {
memset(buf, 0, sizeof(buf));
return start - i - 1;
}
} while (i);
}
/*NOTREACHED*/
}
/*
* Performs a PKCS unpack operation. Returns a prefix of the unwrapped
* data in the given buf. Returns the length of the untruncated
* data, which may exceed "len". Returns <0 on error.
*
* For the constant-padding (signature checking) case,
* it recongizes the PGP 2.2 format, but not in all its generality;
* only the one case (framing byte = 1, length = 16) which was ever
* generated. It fakes the DER prefix in that case.
*/
int
pgpPKCSUnpack(byte *buf, unsigned len, struct BigNum *bn, byte padtype,
unsigned bytes)
{
byte tmp[2];
bnExtractBigBytes(bn, tmp, bytes-2, 2);
if (tmp[0] != 0) {
memset(tmp, 0, 2);
return PGPERR_PK_CORRUPT;
}
if (padtype == PKCS_PAD_ENCRYPTED) {
/* Decryption case, random padding */
/* Special case: PGP <= 2.2 hack */
bnExtractBigBytes(bn, tmp, 0, 1);
if (tmp[1] == 1 && tmp[0] == padtype &&
bnSearchZeroFromLow(bn, 1) == bytes-1-1-16-2-1)
{
/* Aha, it's PGP <= 2.2 */
if (len > 1+16+2)
len = 1+16+2;
bnExtractBigBytes(bn, buf, bytes-1-len, len);
return 1+16+2;
}
/* Okay, assume it's PKCS.1 */
if (tmp[1] != 2) {
memset(tmp, 0, 2);
return PGPERR_PK_CORRUPT;
}
memset(tmp, 0, 2);
bytes = bnSearchZeroFromHigh(bn, bytes-2);
if (bytes-- == 0)
return PGPERR_PK_CORRUPT;
} else {
pgpAssert (padtype==PKCS_PAD_SIGNED);
/* Signature check, constant padding */
if (tmp[1] != padtype) {
memset(tmp, 0, sizeof(tmp));
return PGPERR_PK_CORRUPT;
}
/*
* Special-case hack: is is PGP <= 2.2 format? This is
* identified by a least significant byte of 1, a byte
* of 0 between the 16-byte MD5 hash and the padding,
* and all ones padding.
*/
bnExtractBigBytes(bn, tmp, 0, 1); /* Should be 1 if <= 2.2 */
bnExtractBigBytes(bn, tmp+1, bytes-19, 1); /* zero if <= 2.2 */
if (tmp[0] == padtype && tmp[1] == 0 &&
bnSearchNonOneFromLow(bn, 1) == bytes-19)
{
/* Aha, it's PGP <= 2.2 - fake up the DER prefix */
if (len <= sizeof(MD5_prefix)) {
memcpy(buf, MD5_prefix, len);
} else {
memcpy(buf, MD5_prefix, sizeof(MD5_prefix));
buf += sizeof(MD5_prefix);
len -= sizeof(MD5_prefix);
if (len > 16)
len = 16;
bnExtractBigBytes(bn, buf, bytes-2-len, len);
}
return 16 + sizeof(MD5_prefix);
}
/* Okay, assume it's PKCS format thing. */
bytes = bnSearchNonOneFromHigh(bn, bytes-2);
if (bytes < 1)
return PGPERR_PK_CORRUPT;
bytes--;
tmp[1] = 0;
bnExtractBigBytes(bn, tmp, bytes, 1);
if (tmp[0] != 0) {
memset(tmp, 0, sizeof(tmp));
return PGPERR_PK_CORRUPT;
}
/* Note: tmp isn't secret any more because it's a constant! */
}
/* Success! Return the data */
if (len > bytes)
len = bytes;
bnExtractBigBytes(bn, buf, bytes-len, len);
return bytes;
}
/*
* Convert a big-endian byte buffer (with bit-count prefix) to an MPI.
* Returns number of bytes read from buffer, or <= 0 on error.
* (Returns 0 if the buffer is too short.)
*/
int
pgpBnGetPlain(struct BigNum *bn, byte const *buf, unsigned size)
{
unsigned t;
if (size < 2)
return 0;
t = ((unsigned)buf[0] << 8) + buf[1];
t = (t+7)/8;
if (size < t+2)
return 0;
if (bnInsertBigBytes(bn, buf+2, 0, t) < 0)
return PGPERR_NOMEM;
return (int)t+2;
}
/*
* Helper function for key unlocking.
* Convert a big-endian byte buffer to an MPI, with optional decryption and
* checksums. Accepts cfb == NULL to mean "unencrypted".
* Returns number of bytes read from buffer, or <= 0 on error.
* (Returns 0 if the buffer is too short.)
*
* If "old" is true, this does it in the PGP 2.x way, where the length
* of the data is unencrypted and the CFB is resynced each step.
* If "old" is false, this assumes that just everything is encrypted
* and
*/
int
pgpBnGet(struct BigNum *bn, byte const *buf, unsigned size,
struct PgpCfbContext *cfb, unsigned *checksump, int old)
{
byte tmp[64]; /* This can be any (non-zero) size >= 2 */
unsigned t, l;
if (!cfb) {
int i;
i = pgpBnGetPlain(bn, buf, size);
if (i >= 0 && checksump) {
for (t = 0; t < (unsigned)i; t++)
*checksump += buf[t];
}
return i;
}
if (size < 2)
return 0;
if (old) {
/* Length it bits is not encrypted */
pgpCfbSync(cfb);
if (checksump)
*checksump += (unsigned)buf[0] + buf[1];
t = ((unsigned)buf[0] << 8) + buf[1];
} else {
/* Length in bits is encrypted */
pgpCfbDecrypt(cfb, buf, tmp, 2);
if (checksump)
*checksump += (unsigned)tmp[0] + tmp[1];
t = ((unsigned)tmp[0] << 8) + tmp[1];
}
buf += 2;
t = (t+7)/8;
if (size < t+2)
return 0;
/*
* Descrypt and convert in pieces. It's done from the
* most-significnt end to force allocation of the result
* number all at once rather than reallocating it bit by bit.
*/
size = t;
while (size) {
l = size < sizeof(tmp) ? size : sizeof(tmp);
pgpCfbDecrypt(cfb, buf, tmp, l);
size -= l;
buf += l;
if (bnInsertBigBytes(bn, tmp, size, l) < 0) {
memset(tmp, 0, sizeof(tmp));
return PGPERR_NOMEM;
}
/* Checksum */
if (checksump) {
do {
*checksump += tmp[--l];
} while (l);
}
}
memset(tmp, 0, sizeof(tmp));
return (int)t+2;
}
/*
* Read the 2-byte simple checksum (as computed above) from the
* buffer for comparison. Old-style is unencrypted, new-style is
* encrypted.
*/
unsigned
pgpChecksumGet(byte const *buf, struct PgpCfbContext *cfb, int old)
{
byte tmp[2];
unsigned checksum;
if (old || !cfb) {
checksum = ((unsigned)buf[0] << 8) + buf[1];
} else {
pgpCfbDecrypt(cfb, buf, tmp, 2);
checksum = ((unsigned)tmp[0] << 8) + tmp[1];
memset(tmp, 0, 2);
}
return checksum;
}
/*
* Convert an MPI to a big-endian byte buffer, with a length prefix.
* Returns number of bytes put into buffer.
*/
unsigned
pgpBnPutPlain(struct BigNum const *bn, byte *buf)
{
unsigned t;
t = bnBits(bn);
buf[0] = (byte)(t>>8 & 255);
buf[1] = (byte)(t & 255);
t = (t+7)/8;
bnExtractBigBytes(bn, buf+2, 0, t);
return t+2;
}
/*
* Helper function for ChangeLock.
* Convert an MPI to a big-endian byte buffer, with optional encryption and
* checksums. Accepts cfb == NULL to mean "unencrypted".
* Returns number of bytes put into buffer.
*/
unsigned
pgpBnPut(struct BigNum const *bn, byte *buf,
struct PgpCfbContext *cfb, unsigned *checksump, int old)
{
unsigned t, u;
t = pgpBnPutPlain(bn, buf);
if (checksump)
for (u = 0; u < t; u++)
*checksump += buf[u];
if (cfb) {
if (old) {
pgpCfbSync(cfb);
pgpCfbEncrypt(cfb, buf+2, buf+2, t-2);
} else {
pgpCfbEncrypt(cfb, buf, buf, t);
}
}
return t;
}
/*
* Write the 2-byte simple checksum (as computed above) to the
* buffer for comparison. Old-style is unencrypted, new-style is
* encrypted.
*/
void
pgpChecksumPut(unsigned checksum, byte *buf, struct PgpCfbContext *cfb,
int old)
{
buf[0] = (byte)(checksum>>8 & 255);
buf[1] = (byte)(checksum & 255);
if (cfb && !old)
pgpCfbEncrypt(cfb, buf, buf, 2);
}
/*
* Generate a random bignum of the specified length, with the given
* high and low 8 bits. "High" is merged into the high 8 bits of the
* number. For example, set it to 0x80 to ensure that the number is
* exactly "bits" bits long (i.e. 2^(bits-1) <= bn < 2^bits).
* "Low" is merged into the low 8 bits. For example, set it to
* 1 to ensure that you generate an odd number.
*/
int
pgpBnGenRand(struct BigNum *bn, struct PgpRandomContext const *rc,
unsigned bits, byte high, byte low, unsigned effective)
{
unsigned char buf[64];
unsigned bytes;
unsigned l;
int err;
bnSetQ(bn, 0);
/* Get high random bits */
bytes = (bits+7) / 8;
l = bytes < sizeof(buf) ? bytes : sizeof(buf);
pgpRandomGetBytesEntropy(rc, buf, l, effective);
/* Mask off excess high bits */
buf[0] &= 255 >> (-bits & 7);
/* Merge in specified high bits */
buf[0] |= high >> (-bits & 7);
if (bits & 7)
buf[1] |= high << (bits & 7);
for (;;) {
bytes -= l;
if (!bytes) /* Last word - merge in low bits */
buf[l-1] |= low;
err = bnInsertBigBytes(bn, buf, bytes, l);
if (!bytes || err < 0)
break;
l = bytes < sizeof(buf) ? bytes : sizeof(buf);
pgpRandomGetBytesEntropy(rc, buf, l, 0);
}
/* Burn and return */
memset(buf, 0, sizeof(buf));
return err;
}
/*
* Parse a buffer containing n mpi format numbers (two bytes of length in bits,
* followed by data). Make sure data is well formed and doesn't exceed
* buffer length. Take n pointers to offsets where the n numbers start
* (pointers may be null but must not be left off arg list).
* Return offset past last value, or negative for error.
*/
int
pgpBnParse(byte const *buf, unsigned size, int n, ...)
{
va_list ap;
unsigned nb;
unsigned off;
unsigned *poff;
va_start (ap, n);
if (size < 2U*n)
return PGPERR_KEY_SHORT;
off = 0;
while (n--) {
poff = va_arg(ap, unsigned *);
nb = ((unsigned)buf[0+off] << 8) + buf[1+off];
if (!nb || buf[2+off] >> ((nb-1) & 7) != 1)
return PGPERR_KEY_MPI; /* Bad bit length */
nb = (nb+7)/8;
/* Need nb+2 bytes for this, plus 2*n for remainder */
if (size-off < nb + 2 + 2*n)
return PGPERR_KEY_SHORT;
if (poff)
*poff = off;
off += nb+2;
}
va_end (ap);
return off;
}
/*
* Given a cipher algorithm descriptor in (buf,len) and a passphrase,
* initialize the passed-in PgpCfbContext pointer and return the
* number of bytes of descriptor used, or <0 on error. (In which
* case *cfbp is NULL.)
*/
int
pgpCipherSetup(byte const *buf, unsigned len, char const *phrase, size_t plen,
struct PgpEnv const *env, struct PgpCfbContext **cfbp)
{
struct PgpCipher const *cipher;
struct PgpStringToKey *s2k;
unsigned alg;
int alglen;
byte key[PGP_CIPHER_MAXKEYSIZE];
/* First things first, in case of error... */
*cfbp = NULL;
if (len < 1)
return PGPERR_KEY_SHORT;
alg = buf[0] & 255;
if (!alg) /* The key isn't encrypted; just read it in */
return 1;
if (alg == 255) {
/* New style, with a separate string-to-key */
if (len == 1)
return PGPERR_KEY_SHORT;
alg = buf[1];
alglen = pgpS2Kdecode(&s2k, env, buf+2, len-2);
if (alglen < 0)
return alglen;
alglen += 2;
if (len < (unsigned)alglen)
return PGPERR_KEY_SHORT;
} else {
/* Old-style string-to-key */
s2k = pgpS2Ksimple(env, pgpHashByNumber(PGP_HASH_MD5));
if (!s2k)
return PGPERR_NOMEM;
alglen = 1;
}
/* Okay now, do the conversion */
cipher = pgpCipherByNumber(alg);
if (!cipher) {
pgpS2Kdestroy(s2k);
return PGPERR_BAD_CIPHERNUM;
}
if (len < alglen + cipher->blocksize) {
pgpS2Kdestroy(s2k);
return PGPERR_KEY_SHORT;
}
*cfbp = pgpCfbCreate(cipher);
if (!*cfbp) {
pgpS2Kdestroy(s2k);
return PGPERR_NOMEM;
}
pgpAssert(cipher->keysize <= sizeof(key));
pgpStringToKey(s2k, phrase, plen, key, cipher->keysize);
pgpCfbInit(*cfbp, key, buf + alglen);
memset(key, 0, sizeof(key));
pgpS2Kdestroy(s2k);
return alglen + cipher->blocksize;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -