/* Mixmaster version 2.9 -- (C) 1999 - 2003 Anonymizer Inc. and others. Mixmaster may be redistributed and modified under certain conditions. This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the file COPYRIGHT for details. Read OpenPGP packets $Id: pgpget.c 665 2003-11-09 01:47:32Z rabbi $ */ #include "mix.h" #include "mix3.h" #ifdef USE_PGP #include "pgp.h" #include "crypto.h" #include #include #include int pgp_getmsg(BUFFER *in, BUFFER *key, BUFFER *sig, char *pubring, char *secring) { BUFFER *p; BUFFER *out; int type, algo = 0; int err = PGP_NOMSG; pgpsig signature = {0, NULL, 0, 0, {0,} }; p = buf_new(); out = buf_new(); if (sig) signature.userid = buf_new(); while ((type = pgp_getpacket(in, p)) > 0) switch (type) { case PGP_LITERAL: pgp_getliteral(p); buf_move(out, p); err = 0; break; case PGP_COMPRESSED: err = pgp_uncompress(p); if (err == 0) err = pgp_getmsg(p, key, sig, pubring, secring); if (err != PGP_ERR && err != PGP_PASS) buf_move(out, p); break; case PGP_ENCRYPTED: case PGP_ENCRYPTEDMDC: if (!key) { err = -1; break; } if (/*key->length > 0 &&*/ algo == 0) { algo = PGP_K_IDEA; digest_md5(key, key); } if (key->length > 0) err = pgp_getsymmetric(p, key, algo, type==PGP_ENCRYPTEDMDC); else err = -1; if (err == 0) err = pgp_getmsg(p, NULL, sig, pubring, secring); if (err != PGP_ERR) buf_move(out, p); break; case PGP_SESKEY: if (!key) { err = -1; break; } err = pgp_getsessionkey(p, key, secring); if (err >= 0) { algo = err; err = 0; buf_set(key, p); } break; case PGP_SYMSESKEY: if (!key) { err = -1; break; } err = pgp_getsymsessionkey(p, key); if (err >= 0) { algo = err; err = 0; if (key) buf_set(key, p); } break; case PGP_MARKER: err = 0; break; /* ignore per RFC2440 */ case PGP_SIG: pgp_getsig(p, &signature, pubring); /* fallthru */ default: if (err == PGP_NOMSG) err = PGP_NODATA; } if (signature.ok == PGP_SIGVRFY) pgp_verify(out, sig, &signature); if (signature.ok == PGP_SIGOK) { char line[LINELEN]; time_t t; struct tm *tc; t = signature.sigtime; tc = localtime(&t); #if 0 strftime(line, LINELEN, "[%Y-%m-%d %H:%M:%S]", tc); #else /* end of 0 */ strftime(line, LINELEN, "[%a %b %d %H:%M:%S %Y]", tc); #endif /* else if not 0 */ if (sig) { buf_cat(sig, signature.userid); buf_appendc(sig, ' '); buf_appends(sig, line); } } if (sig) { if (signature.ok == PGP_SIGNKEY) buf_appendf(sig, "%02X%02X%02X%02X", signature.userid->data[4], signature.userid->data[5], signature.userid->data[6], signature.userid->data[7]); buf_free(signature.userid); } if ((err == 0 || err == PGP_NODATA) && signature.ok != 0) err = signature.ok; buf_move(in, out); buf_free(out); buf_free(p); return (err); } int pgp_ispacket(BUFFER *b) { return ((b->data[b->ptr] >> 6) == 2 || (b->data[b->ptr] >> 6) == 3); } int pgp_packettype(BUFFER *b, long *len, int *partial) { int ctb; ctb = buf_getc(b); switch (ctb >> 6) { case 2: /* old packet type */ switch (ctb & 3) { case 0: *len = buf_getc(b); break; case 1: *len = buf_geti(b); break; case 2: *len = buf_getl(b); break; case 3: *len = b->length - b->ptr; break; } *partial = 0; return (ctb >> 2) & 15; case 3: case 1: /* in GnuPG secret key ring */ /* new packet type */ *len = buf_getc(b); if (*len >= 192 && *len <= 223) *len = (*len - 192) * 256 + buf_getc(b) + 192; else if (*len == 255) *len = buf_getl(b); else if (*len > 223) { *len = 1 <<(*len & 0x1f); *partial = 1; } return (ctb & 63); } return (-1); } int pgp_packetpartial(BUFFER *b, long *len, int *partial) { *partial = 0; *len = buf_getc(b); if (*len >= 192 && *len <= 223) *len = (*len - 192) * 256 + buf_getc(b) + 192; else if (*len == 255) *len = buf_getl(b); else if (*len > 223) { *len = 1 <<(*len & 0x1f); *partial = 1; } return 1; } int pgp_isconventional(BUFFER *b) { int type; BUFFER *p; p = buf_new(); type = pgp_getpacket(b, p); if (type == PGP_MARKER) type = pgp_getpacket(b, p); buf_rewind(b); buf_free(p); return (type == PGP_ENCRYPTED || type == PGP_SYMSESKEY); } int pgp_getpacket(BUFFER *in, BUFFER *p) /* returns <0 = error, >0 = packet type */ { int type; long len; int partial = 0; BUFFER *tmp; tmp = buf_new(); type = pgp_packettype(in, &len, &partial); if (type > 0 && len > 0) { buf_clear(p); while(partial && len > 0) { buf_get(in, tmp, len); buf_cat(p, tmp); pgp_packetpartial(in, &len, &partial); } if (len > 0) { buf_get(in, tmp, len); buf_cat(p, tmp); } } buf_free(tmp); return (type); } int pgp_getsig(BUFFER *p, pgpsig *sig, char *pubring) { BUFFER *sigkey, *id, *i; int algo, hashalgo; int hash; sigkey = buf_new(); id = buf_new(); i = buf_new(); sig->ok = PGP_SIGBAD; if (buf_getc(p) > 3) goto end; if (buf_getc(p) != 5) goto end; sig->sigtype = buf_getc(p); sig->sigtime = buf_getl(p); buf_get(p, id, 8); algo = buf_getc(p); hashalgo = buf_getc(p); if (hashalgo != PGP_H_MD5) goto end; hash = buf_geti(p); if (pgpdb_getkey(PK_VERIFY, algo, NULL, NULL, sigkey, NULL, sig->userid, id, pubring, NULL) < 0) { sig->ok = PGP_SIGNKEY; if (sig->userid) buf_set(sig->userid, id); goto end; } switch (algo) { #ifdef USE_RSA case PGP_ES_RSA: mpi_get(p, i); if (pgp_rsa(i, sigkey, PK_VERIFY) == -1 || memcmp(i->data, MD5PREFIX, sizeof(MD5PREFIX) - 1) != 0) goto end; memcpy(sig->hash, i->data + sizeof(MD5PREFIX) - 1, 16); if (sig->hash[0] * 256 + sig->hash[1] != hash) goto end; sig->ok = PGP_SIGVRFY; break; #endif /* USE_RSA */ default: break; } end: buf_free(sigkey); buf_free(id); buf_free(i); return (sig->ok); } void pgp_verify(BUFFER *msg, BUFFER *detached, pgpsig *sig) { MD5_CTX c; BUFFER *t; byte md[16]; t = buf_new(); sig->ok = PGP_SIGBAD; if (msg->length == 0) { /* detached signature */ if (detached && detached->length) { buf_move(msg, detached); if (sig->sigtype == PGP_SIG_CANONIC) pgp_sigcanonic(msg); /* for cleartext signatures */ } else sig->ok = PGP_NODATA; } MD5_Init(&c); switch (sig->sigtype) { case PGP_SIG_BINARY: MD5_Update(&c, msg->data, msg->length); break; case PGP_SIG_CANONIC: while (buf_getline(msg, t) != -1) { #if 0 pgp_sigcanonic(t); /* according to OpenPGP */ #else /* end of 0 */ buf_appends(t, "\r\n"); #endif /* else if not 0 */ MD5_Update(&c, t->data, t->length); } break; default: sig->ok = PGP_SIGBAD; } MD5_Update(&c, &(sig->sigtype), 1); buf_appendl(t, sig->sigtime); MD5_Update(&c, t->data, 4); MD5_Final(md, &c); if (memcmp(md, sig->hash, 16) == 0) sig->ok = PGP_SIGOK; buf_free(t); } #ifdef USE_IDEA static int pgp_ideadecrypt(BUFFER *in, BUFFER *out, BUFFER *key, int mdc) { int err = 0; byte iv[8]; byte hdr[10]; int i, n; IDEA_KEY_SCHEDULE ks; SHA_CTX c; char md[20]; /* we could make hdr 20 bytes long and reuse it for md */ if (key->length != 16 || in->length <= (mdc?(1+10+22):10)) return (-1); if (mdc) { mdc = 1; if (in->data[0] != 1) return (-1); } buf_prepare(out, in->length - 10 - mdc); for (i = 0; i < 8; i++) iv[i] = 0; idea_set_encrypt_key(key->data, &ks); n = 0; idea_cfb64_encrypt(in->data + mdc, hdr, 10, &ks, iv, &n, IDEA_DECRYPT); if (n != 2 || hdr[8] != hdr[6] || hdr[9] != hdr[7]) { err = -1; goto end; } if (mdc) { SHA1_Init(&c); SHA1_Update(&c, hdr, 10); } else { iv[6] = iv[0], iv[7] = iv[1]; memcpy(iv, in->data + 2, 6); n = 0; } idea_cfb64_encrypt(in->data + 10 + mdc, out->data, in->length - 10 - mdc, &ks, iv, &n, IDEA_DECRYPT); if (mdc) { if (out->length > 22) { out->length -= 22; if (out->data[out->length] == 0xD3 && out->data[out->length + 1] == 0x14) { SHA1_Update(&c, out->data, out->length + 2); SHA1_Final(md, &c); if (memcmp(out->data + out->length + 2, md, 20)) err = -1; } else err = -1; } else err = -1; } end: return (err); } #endif /* USE_IDEA */ static int pgp_3desdecrypt(BUFFER *in, BUFFER *out, BUFFER *key, int mdc) { int err = 0; des_cblock iv; byte hdr[10]; int i, n; des_key_schedule ks1; des_key_schedule ks2; des_key_schedule ks3; SHA_CTX c; char md[20]; /* we could make hdr 20 bytes long and reuse it for md */ if (key->length != 24 || in->length <= (mdc?(1+10+22):10)) return (-1); if (mdc) { mdc = 1; if (in->data[0] != 1) return (-1); } buf_prepare(out, in->length - 10 - mdc); for (i = 0; i < 8; i++) iv[i] = 0; des_set_key((const_des_cblock *) key->data, ks1); des_set_key((const_des_cblock *) (key->data + 8), ks2); des_set_key((const_des_cblock *) (key->data+ 16), ks3); n = 0; des_ede3_cfb64_encrypt(in->data + mdc, hdr, 10, ks1, ks2, ks3, &iv, &n, DECRYPT); if (n != 2 || hdr[8] != hdr[6] || hdr[9] != hdr[7]) { err = -1; goto end; } if (mdc) { SHA1_Init(&c); SHA1_Update(&c, hdr, 10); } else { iv[6] = iv[0], iv[7] = iv[1]; memcpy(iv, in->data + 2, 6); n = 0; } des_ede3_cfb64_encrypt(in->data + 10 + mdc, out->data, in->length - 10 + mdc, ks1, ks2, ks3, &iv, &n, DECRYPT); if (mdc) { if (out->length > 22) { out->length -= 22; if (out->data[out->length] == 0xD3 && out->data[out->length + 1] == 0x14) { SHA1_Update(&c, out->data, out->length + 2); SHA1_Final(md, &c); if (memcmp(out->data + out->length + 2, md, 20)) err = -1; } else err = -1; } else err = -1; } end: return (err); } static int pgp_castdecrypt(BUFFER *in, BUFFER *out, BUFFER *key, int mdc) { int err = 0; byte iv[8]; byte hdr[10]; int i, n; SHA_CTX c; char md[20]; /* we could make hdr 20 bytes long and reuse it for md */ CAST_KEY ks; if (key->length != 16 || in->length <= (mdc?(1+10+22):10)) return (-1); if (mdc) { mdc = 1; if (in->data[0] != 1) return (-1); } buf_prepare(out, in->length - 10 - mdc); for (i = 0; i < 8; i++) iv[i] = 0; CAST_set_key(&ks, 16, key->data); n = 0; CAST_cfb64_encrypt(in->data + mdc, hdr, 10, &ks, iv, &n, CAST_DECRYPT); if (n != 2 || hdr[8] != hdr[6] || hdr[9] != hdr[7]) { err = -1; goto end; } if (mdc) { SHA1_Init(&c); SHA1_Update(&c, hdr, 10); } else { iv[6] = iv[0], iv[7] = iv[1]; memcpy(iv, in->data + 2, 6); n = 0; } CAST_cfb64_encrypt(in->data + 10 + mdc, out->data, in->length - 10 - mdc, &ks, iv, &n, CAST_DECRYPT); if (mdc) { if (out->length > 22) { out->length -= 22; if (out->data[out->length] == 0xD3 && out->data[out->length + 1] == 0x14) { SHA1_Update(&c, out->data, out->length + 2); SHA1_Final(md, &c); if (memcmp(out->data + out->length + 2, md, 20)) err = -1; } else err = -1; } else err = -1; } end: return (err); } static int pgp_bfdecrypt(BUFFER *in, BUFFER *out, BUFFER *key, int mdc) { int err = 0; byte iv[8]; byte hdr[10]; int i, n; SHA_CTX c; char md[20]; /* we could make hdr 20 bytes long and reuse it for md */ BF_KEY ks; if (key->length != 16 || in->length <= (mdc?(1+10+22):10)) return (-1); if (mdc) { mdc = 1; if (in->data[0] != 1) return (-1); } buf_prepare(out, in->length - 10 - mdc); for (i = 0; i < 8; i++) iv[i] = 0; BF_set_key(&ks, 16, key->data); n = 0; BF_cfb64_encrypt(in->data + mdc, hdr, 10, &ks, iv, &n, BF_DECRYPT); if (n != 2 || hdr[8] != hdr[6] || hdr[9] != hdr[7]) { err = -1; goto end; } if (mdc) { SHA1_Init(&c); SHA1_Update(&c, hdr, 10); } else { iv[6] = iv[0], iv[7] = iv[1]; memcpy(iv, in->data + 2, 6); n = 0; } BF_cfb64_encrypt(in->data + 10 + mdc, out->data, in->length - 10 - mdc, &ks, iv, &n, BF_DECRYPT); if (mdc) { if (out->length > 22) { out->length -= 22; if (out->data[out->length] == 0xD3 && out->data[out->length + 1] == 0x14) { SHA1_Update(&c, out->data, out->length + 2); SHA1_Final(md, &c); if (memcmp(out->data + out->length + 2, md, 20)) err = -1; } else err = -1; } else err = -1; } end: return (err); } #ifdef USE_AES static int pgp_aesdecrypt(BUFFER *in, BUFFER *out, BUFFER *key, int mdc) { int err = 0; byte iv[16]; byte hdr[18]; int i, n; SHA_CTX c; char md[20]; /* we could make hdr 20 bytes long and reuse it for md */ AES_KEY ks; if ((key->length != 16 && key->length != 24 && key->length != 32) || in->length <= (mdc?(1+18+22):18)) return (-1); if (mdc) { mdc = 1; if (in->data[0] != 1) return (-1); } buf_prepare(out, in->length - 18 - mdc); for (i = 0; i < 16; i++) iv[i] = 0; AES_set_encrypt_key(key->data, key->length<<3, &ks); n = 0; AES_cfb128_encrypt(in->data + mdc, hdr, 18, &ks, iv, &n, AES_DECRYPT); if (n != 2 || hdr[16] != hdr[14] || hdr[17] != hdr[15]) { err = -1; goto end; } if (mdc) { SHA1_Init(&c); SHA1_Update(&c, hdr, 18); } else { iv[14] = iv[0], iv[15] = iv[1]; memcpy(iv, in->data + 2, 14); n = 0; } AES_cfb128_encrypt(in->data + 18 + mdc, out->data, in->length - 18 - mdc, &ks, iv, &n, AES_DECRYPT); if (mdc) { if (out->length > 22) { out->length -= 22; if (out->data[out->length] == 0xD3 && out->data[out->length + 1] == 0x14) { SHA1_Update(&c, out->data, out->length + 2); SHA1_Final(md, &c); if (memcmp(out->data + out->length + 2, md, 20)) err = -1; } else err = -1; } else err = -1; } end: return (err); } #endif /* USE_AES */ int pgp_getsymmetric(BUFFER *in, BUFFER *key, int algo, int mdc) { int err = -1; BUFFER *out; out = buf_new(); switch (algo) { #ifdef USE_AES case PGP_K_AES128: case PGP_K_AES192: case PGP_K_AES256: err = pgp_aesdecrypt(in, out, key, mdc); break; #endif /* USE_AES */ #ifdef USE_IDEA case PGP_K_IDEA: err = pgp_ideadecrypt(in, out, key, mdc); break; #endif /* USE_IDEA */ case PGP_K_3DES: err = pgp_3desdecrypt(in, out, key, mdc); break; case PGP_K_CAST5: err = pgp_castdecrypt(in, out, key, mdc); break; case PGP_K_BF: err = pgp_bfdecrypt(in, out, key, mdc); break; } if (err < 0) errlog(ERRORMSG, "PGP decryption failed.\n"); buf_move(in, out); buf_free(out); return (err); } int pgp_getliteral(BUFFER *in) { long fnlen; int err = 0; int mode; BUFFER *out; BUFFER *line; out = buf_new(); line = buf_new(); mode = buf_getc(in); fnlen = buf_getc(in); in->ptr += fnlen; /* skip filename */ if (in->ptr + 4 > in->length) err = -1; else { buf_getl(in); /* timestamp */ if (mode == 't') while (buf_getline(in, line) != -1) { buf_cat(out, line); buf_nl(out); } else buf_rest(out, in); } buf_move(in, out); buf_free(line); buf_free(out); return (err); } int pgp_uncompress(BUFFER *in) { int err = -1; switch(buf_getc(in)) { case 0: err = 0; break; case 1: err = buf_unzip(in, 0); break; case 2: err = buf_unzip(in, 1); break; default: err = -1; break; } return (err); } int pgp_getsessionkey(BUFFER *in, BUFFER *pass, char *secring) { BUFFER *out; BUFFER *key; BUFFER *keyid; int type; int i, csum = 0; int algo = 0; int err = -1; out = buf_new(); key = buf_new(); keyid = buf_new(); type = buf_getc(in); /* packet type */ if (type < 2 || type > 3) goto end; buf_get(in, keyid, 8); algo = buf_getc(in); err = pgpdb_getkey(PK_DECRYPT, algo, NULL, NULL, key, NULL, NULL, keyid, secring, pass); if (err < 0) goto end; switch (algo) { #ifdef USE_RSA case PGP_ES_RSA: mpi_get(in, out); err = pgp_rsa(out, key, PK_DECRYPT); break; #endif /* USE_RSA */ case PGP_E_ELG: buf_rest(out, in); err = pgp_elgdecrypt(out, key); break; default: err = -1; } if (err == 0 && out->length > 3) { algo = buf_getc(out); buf_get(out, in, out->length - 3); /* return recovered key */ csum = buf_geti(out); for (i = 0; i < in->length; i++) csum = (csum - in->data[i]) % 65536; if (csum != 0) err = -1; } else err = -1; end: buf_free(out); buf_free(key); buf_free(keyid); return (err == 0 ? algo : err); } void pgp_iteratedsk(BUFFER *out, BUFFER *salt, BUFFER *pass, byte c) { int count; BUFFER *temp; temp = buf_new(); count = (16l + (c & 15)) << ((c >> 4) + 6); while (temp->length < count) { buf_cat(temp, salt); buf_cat(temp, pass); } buf_get(temp, out, count); buf_free(temp); } int pgp_getsk(BUFFER *p, BUFFER *pass, BUFFER *key) { int skalgo, skspecifier, hashalgo; BUFFER *salted; /* passphrase with salt */ if (!pass) return(-1); salted = buf_new(); skalgo = buf_getc(p); skspecifier = buf_getc(p); hashalgo = buf_getc(p); switch (skspecifier) { case 0: buf_set(salted, pass); break; case 1: buf_get(p, salted, 8); /* salt */ buf_cat(salted, pass); break; case 3: buf_get(p, salted, 8); /* salt */ pgp_iteratedsk(salted, salted, pass, buf_getc(p)); break; default: skalgo = -1; goto end; } if (pgp_expandsk(key, skalgo, hashalgo, salted) == -1) skalgo = -1; end: buf_free(salted); return (skalgo); } int pgp_getsymsessionkey(BUFFER *in, BUFFER *pass) { BUFFER *temp, *key, *iv; int algo = -1; temp = buf_new(); key = buf_new(); iv = buf_new(); if (buf_getc(in) == 4) { /* version */ algo = pgp_getsk(in, pass, key); buf_rest(temp, in); if (temp->length) { /* encrypted session key present */ buf_appendzero(iv, pgp_blocklen(algo)); skcrypt(temp, algo, key, iv, DECRYPT); algo = buf_getc(temp); buf_rest(in, temp); } else buf_set(in, key); } buf_free(temp); buf_free(key); buf_free(iv); return (algo); } #endif /* USE_PGP */