static gboolean read_public_dsa (EggBuffer *req, gsize *offset, gcry_sexp_t *sexp) { gcry_mpi_t p, q, g, y; int gcry; if (!read_mpi (req, offset, &p) || !read_mpi (req, offset, &q) || !read_mpi (req, offset, &g) || !read_mpi (req, offset, &y)) return FALSE; gcry = gcry_sexp_build (sexp, NULL, SEXP_PUBLIC_DSA, p, q, g, y); if (gcry) { g_warning ("couldn't parse incoming public DSA key: %s", gcry_strerror (gcry)); return FALSE; } gcry_mpi_release (p); gcry_mpi_release (q); gcry_mpi_release (g); gcry_mpi_release (y); return TRUE; }
static gboolean parse_v4_algo_bits (const guchar **at, const guchar *end, guint8 algo, guint16 *bits) { switch (algo) { case GCR_OPENPGP_ALGO_RSA: case GCR_OPENPGP_ALGO_RSA_E: case GCR_OPENPGP_ALGO_RSA_S: if (!read_mpi (at, end, bits, NULL) || !read_mpi (at, end, NULL, NULL)) return FALSE; return TRUE; case GCR_OPENPGP_ALGO_DSA: if (!read_mpi (at, end, bits, NULL) || !read_mpi (at, end, NULL, NULL) || !read_mpi (at, end, NULL, NULL) || !read_mpi (at, end, NULL, NULL)) return FALSE; return TRUE; case GCR_OPENPGP_ALGO_ELG_E: if (!read_mpi (at, end, bits, NULL) || !read_mpi (at, end, NULL, NULL) || !read_mpi (at, end, NULL, NULL)) return FALSE; return TRUE; default: /* Unsupported key */ return FALSE; } }
/* Parse a Key Exchange Message into a newly-allocated KeyExchMsg structure */ KeyExchMsg parse_keyexch(const char *msg) { KeyExchMsg kem = NULL; size_t lenp; unsigned char *raw = decode(msg, &lenp); unsigned char *bufp = raw; if (!raw) goto inv; kem = calloc(1, sizeof(struct s_KeyExchMsg)); if (!kem) { free(raw); goto inv; } kem->raw = raw; kem->sigstart = bufp; require_len(3); if (memcmp(bufp, "\x00\x01\x0a", 3)) goto inv; bufp += 3; lenp -= 3; require_len(1); kem->reply = *bufp; bufp += 1; lenp -= 1; read_mpi(kem->p); read_mpi(kem->q); read_mpi(kem->g); read_mpi(kem->e); read_int(kem->keyid); read_mpi(kem->y); kem->sigend = bufp; require_len(40); gcry_mpi_scan(&kem->r, GCRYMPI_FMT_USG, bufp, 20, NULL); gcry_mpi_scan(&kem->s, GCRYMPI_FMT_USG, bufp+20, 20, NULL); bufp += 40; lenp -= 40; if (lenp != 0) goto inv; return kem; inv: free_keyexch(kem); return NULL; }
static cdk_error_t read_pubkey_enc (cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_enc_t pke) { size_t i, nenc; if (!inp || !pke) return CDK_Inv_Value; if (DEBUG_PKT) _cdk_log_debug ("read_pubkey_enc: %d octets\n", pktlen); if (pktlen < 12) return CDK_Inv_Packet; pke->version = cdk_stream_getc (inp); if (pke->version < 2 || pke->version > 3) return CDK_Inv_Packet; pke->keyid[0] = read_32 (inp); pke->keyid[1] = read_32 (inp); if (!pke->keyid[0] && !pke->keyid[1]) pke->throw_keyid = 1; /* RFC2440 "speculative" keyID */ pke->pubkey_algo = _pgp_pub_algo_to_cdk (cdk_stream_getc (inp)); nenc = cdk_pk_get_nenc (pke->pubkey_algo); if (!nenc) return CDK_Inv_Algo; for (i = 0; i < nenc; i++) { cdk_error_t rc = read_mpi (inp, &pke->mpi[i], 0); if (rc) return rc; } return 0; }
static gboolean parse_v3_rsa_bits_and_keyid (const guchar **at, const guchar *end, guint16 *bits, gchar **keyid) { guchar *n; gsize bytes; g_assert (bits); g_assert (keyid); /* Read in the modulus */ if (!read_mpi (at, end, bits, &n)) return FALSE; /* Last 64-bits of modulus are keyid */ bytes = (*bits + 7) / 8; if (bytes < 8) { g_free (n); return FALSE; } *keyid = egg_hex_encode_full (n + (bytes - 8), 8, TRUE, 0, 0); return TRUE; }
static gboolean skip_signature_mpis (const guchar **at, const guchar *end, guint8 algo) { switch (algo) { /* RSA signature value */ case GCR_OPENPGP_ALGO_RSA: return read_mpi (at, end, NULL, NULL); /* DSA values r and s */ case GCR_OPENPGP_ALGO_DSA: return read_mpi (at, end, NULL, NULL) && read_mpi (at, end, NULL, NULL); default: return FALSE; } }
static gboolean read_public_rsa (EggBuffer *req, gsize *offset, gcry_sexp_t *sexp) { gcry_mpi_t n, e; int gcry; if (!read_mpi (req, offset, &e) || !read_mpi (req, offset, &n)) return FALSE; gcry = gcry_sexp_build (sexp, NULL, SEXP_PUBLIC_RSA, n, e); if (gcry) { g_warning ("couldn't parse incoming public RSA key: %s", gcry_strerror (gcry)); return FALSE; } gcry_mpi_release (n); gcry_mpi_release (e); return TRUE; }
static cdk_error_t read_public_key (cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_t pk) { size_t i, ndays, npkey; if (!inp || !pk) return CDK_Inv_Value; if (DEBUG_PKT) _cdk_log_debug ("read_public_key: %d octets\n", pktlen); pk->is_invalid = 1; /* default to detect missing self signatures */ pk->is_revoked = 0; pk->has_expired = 0; pk->version = cdk_stream_getc (inp); if (pk->version < 2 || pk->version > 4) return CDK_Inv_Packet_Ver; pk->timestamp = read_32 (inp); if (pk->version < 4) { ndays = read_16 (inp); if (ndays) pk->expiredate = pk->timestamp + ndays * 86400L; } pk->pubkey_algo = _pgp_pub_algo_to_cdk (cdk_stream_getc (inp)); npkey = cdk_pk_get_npkey (pk->pubkey_algo); if (!npkey) { gnutls_assert (); _cdk_log_debug ("invalid public key algorithm %d\n", pk->pubkey_algo); return CDK_Inv_Algo; } for (i = 0; i < npkey; i++) { cdk_error_t rc = read_mpi (inp, &pk->mpi[i], 0); if (rc) return rc; } /* This value is just for the first run and will be replaced with the actual key flags from the self signature. */ pk->pubkey_usage = 0; return 0; }
/* Parse a D-H Key Message into a newly-allocated KeyMsg structure */ KeyMsg parse_key(const char *msg) { KeyMsg kmsg = NULL; size_t lenp; unsigned char *raw = decode(msg, &lenp); unsigned char *bufp = raw; if (!raw) goto inv; kmsg = calloc(1, sizeof(struct s_KeyMsg)); if (!kmsg) { free(raw); goto inv; } kmsg->raw = raw; require_len(3); kmsg->version = bufp[1]; if (!memcmp(bufp, "\x00\x03\x0a", 3)) { bufp += 3; lenp -= 3; read_int(kmsg->sender_instance); read_int(kmsg->receiver_instance); } else if (!memcmp(bufp, "\x00\x02\x0a", 3)) { bufp += 3; lenp -= 3; kmsg->sender_instance = 0; kmsg->receiver_instance = 0; } else goto inv; read_mpi(kmsg->y); if (lenp != 0) goto inv; return kmsg; inv: free_key(kmsg); return NULL; }
/* Accept an OTR Data Message in datamsg. Decrypt it and put the * plaintext into *plaintextp, and any TLVs into tlvsp. Put any * received flags into *flagsp (if non-NULL). */ gcry_error_t otrl_proto_accept_data(char **plaintextp, OtrlTLV **tlvsp, ConnContext *context, const char *datamsg, unsigned char *flagsp) { char *otrtag, *endtag; gcry_error_t err; unsigned char *rawmsg = NULL; size_t msglen, rawlen, lenp; unsigned char *macstart, *macend; unsigned char *bufp; unsigned int sender_keyid, recipient_keyid; gcry_mpi_t sender_next_y = NULL; unsigned char ctr[8]; unsigned int datalen, reveallen; unsigned char *data = NULL; unsigned char *nul = NULL; unsigned char givenmac[20]; DH_sesskeys *sess; unsigned char version; *plaintextp = NULL; *tlvsp = NULL; if (flagsp) *flagsp = 0; otrtag = strstr(datamsg, "?OTR:"); if (!otrtag) { goto invval; } endtag = strchr(otrtag, '.'); if (endtag) { msglen = endtag-otrtag; } else { msglen = strlen(otrtag); } /* Base64-decode the message */ rawlen = ((msglen-5) / 4) * 3; /* maximum possible */ rawmsg = malloc(rawlen); if (!rawmsg && rawlen > 0) { err = gcry_error(GPG_ERR_ENOMEM); goto err; } rawlen = otrl_base64_decode(rawmsg, otrtag+5, msglen-5); /* actual size */ bufp = rawmsg; lenp = rawlen; macstart = bufp; require_len(3); if (memcmp(bufp, "\x00\x01\x03", 3) && memcmp(bufp, "\x00\x02\x03", 3)) { /* Invalid header */ goto invval; } version = bufp[1]; bufp += 3; lenp -= 3; if (version == 2) { require_len(1); if (flagsp) *flagsp = bufp[0]; bufp += 1; lenp -= 1; } read_int(sender_keyid); read_int(recipient_keyid); read_mpi(sender_next_y); require_len(8); memmove(ctr, bufp, 8); bufp += 8; lenp -= 8; read_int(datalen); require_len(datalen); data = malloc(datalen+1); if (!data) { err = gcry_error(GPG_ERR_ENOMEM); goto err; } memmove(data, bufp, datalen); data[datalen] = '\0'; bufp += datalen; lenp -= datalen; macend = bufp; require_len(20); memmove(givenmac, bufp, 20); bufp += 20; lenp -= 20; read_int(reveallen); require_len(reveallen); /* Just skip over the revealed MAC keys, which we don't need. They * were published for deniability of transcripts. */ bufp += reveallen; lenp -= reveallen; /* That should be everything */ if (lenp != 0) goto invval; /* We don't take any action on this message (especially rotating * keys) until we've verified the MAC on this message. To that end, * we need to know which keys this message is claiming to use. */ if (context->their_keyid == 0 || (sender_keyid != context->their_keyid && sender_keyid != context->their_keyid - 1) || (recipient_keyid != context->our_keyid && recipient_keyid != context->our_keyid - 1) || sender_keyid == 0 || recipient_keyid == 0) { goto conflict; } if (sender_keyid == context->their_keyid - 1 && context->their_old_y == NULL) { goto conflict; } /* These are the session keys this message is claiming to use. */ sess = &(context->sesskeys [context->our_keyid - recipient_keyid] [context->their_keyid - sender_keyid]); gcry_md_reset(sess->rcvmac); gcry_md_write(sess->rcvmac, macstart, macend-macstart); if (memcmp(givenmac, gcry_md_read(sess->rcvmac, GCRY_MD_SHA1), 20)) { /* The MACs didn't match! */ goto conflict; } sess->rcvmacused = 1; /* Check to see that the counter is increasing; i.e. that this isn't * a replay. */ if (otrl_dh_cmpctr(ctr, sess->rcvctr) <= 0) { goto conflict; } /* Decrypt the message */ memmove(sess->rcvctr, ctr, 8); err = gcry_cipher_reset(sess->rcvenc); if (err) goto err; err = gcry_cipher_setctr(sess->rcvenc, sess->rcvctr, 16); if (err) goto err; err = gcry_cipher_decrypt(sess->rcvenc, data, datalen, NULL, 0); if (err) goto err; /* See if either set of keys needs rotating */ if (recipient_keyid == context->our_keyid) { /* They're using our most recent key, so generate a new one */ err = rotate_dh_keys(context); if (err) goto err; } if (sender_keyid == context->their_keyid) { /* They've sent us a new public key */ err = rotate_y_keys(context, sender_next_y); if (err) goto err; } gcry_mpi_release(sender_next_y); *plaintextp = (char *)data; /* See if there are TLVs */ nul = data; while (nul < data+datalen && *nul) ++nul; /* If we stopped before the end, skip the NUL we stopped at */ if (nul < data+datalen) ++nul; *tlvsp = otrl_tlv_parse(nul, (data+datalen)-nul); free(rawmsg); return gcry_error(GPG_ERR_NO_ERROR); invval: err = gcry_error(GPG_ERR_INV_VALUE); goto err; conflict: err = gcry_error(GPG_ERR_CONFLICT); goto err; err: gcry_mpi_release(sender_next_y); free(data); free(rawmsg); return err; }
static cdk_error_t read_signature (cdk_stream_t inp, size_t pktlen, cdk_pkt_signature_t sig) { size_t nbytes; size_t i, size, nsig; cdk_error_t rc; if (!inp || !sig) return CDK_Inv_Value; if (DEBUG_PKT) _cdk_log_debug ("read_signature: %d octets\n", pktlen); if (pktlen < 16) return CDK_Inv_Packet; sig->version = cdk_stream_getc (inp); if (sig->version < 2 || sig->version > 4) return CDK_Inv_Packet_Ver; sig->flags.exportable = 1; sig->flags.revocable = 1; if (sig->version < 4) { if (cdk_stream_getc (inp) != 5) return CDK_Inv_Packet; sig->sig_class = cdk_stream_getc (inp); sig->timestamp = read_32 (inp); sig->keyid[0] = read_32 (inp); sig->keyid[1] = read_32 (inp); sig->pubkey_algo = _pgp_pub_algo_to_cdk (cdk_stream_getc (inp)); sig->digest_algo = _pgp_hash_algo_to_gnutls (cdk_stream_getc (inp)); sig->digest_start[0] = cdk_stream_getc (inp); sig->digest_start[1] = cdk_stream_getc (inp); nsig = cdk_pk_get_nsig (sig->pubkey_algo); if (!nsig) return CDK_Inv_Algo; for (i = 0; i < nsig; i++) { rc = read_mpi (inp, &sig->mpi[i], 0); if (rc) return rc; } } else { sig->sig_class = cdk_stream_getc (inp); sig->pubkey_algo = _pgp_pub_algo_to_cdk (cdk_stream_getc (inp)); sig->digest_algo = _pgp_hash_algo_to_gnutls (cdk_stream_getc (inp)); sig->hashed_size = read_16 (inp); size = sig->hashed_size; sig->hashed = NULL; while (size > 0) { rc = read_subpkt (inp, &sig->hashed, &nbytes); if (rc) return rc; size -= nbytes; } sig->unhashed_size = read_16 (inp); size = sig->unhashed_size; sig->unhashed = NULL; while (size > 0) { rc = read_subpkt (inp, &sig->unhashed, &nbytes); if (rc) return rc; size -= nbytes; } rc = parse_sig_subpackets (sig); if (rc) return rc; sig->digest_start[0] = cdk_stream_getc (inp); sig->digest_start[1] = cdk_stream_getc (inp); nsig = cdk_pk_get_nsig (sig->pubkey_algo); if (!nsig) return CDK_Inv_Algo; for (i = 0; i < nsig; i++) { rc = read_mpi (inp, &sig->mpi[i], 0); if (rc) return rc; } } return 0; }
static cdk_error_t read_secret_key (cdk_stream_t inp, size_t pktlen, cdk_pkt_seckey_t sk) { size_t p1, p2, nread; int i, nskey; int rc; if (!inp || !sk || !sk->pk) return CDK_Inv_Value; if (DEBUG_PKT) _cdk_log_debug ("read_secret_key: %d octets\n", pktlen); p1 = cdk_stream_tell (inp); rc = read_public_key (inp, pktlen, sk->pk); if (rc) return rc; sk->s2k_usage = cdk_stream_getc (inp); sk->protect.sha1chk = 0; if (sk->s2k_usage == 254 || sk->s2k_usage == 255) { sk->protect.sha1chk = (sk->s2k_usage == 254); sk->protect.algo = _pgp_cipher_to_gnutls (cdk_stream_getc (inp)); sk->protect.s2k = cdk_calloc (1, sizeof *sk->protect.s2k); if (!sk->protect.s2k) return CDK_Out_Of_Core; rc = read_s2k (inp, sk->protect.s2k); if (rc) return rc; /* refer to --export-secret-subkeys in gpg(1) */ if (sk->protect.s2k->mode == CDK_S2K_GNU_EXT) sk->protect.ivlen = 0; else { sk->protect.ivlen = _gnutls_cipher_get_block_size (sk->protect.algo); if (!sk->protect.ivlen) return CDK_Inv_Packet; rc = stream_read (inp, sk->protect.iv, sk->protect.ivlen, &nread); if (rc) return rc; if (nread != sk->protect.ivlen) return CDK_Inv_Packet; } } else sk->protect.algo = _pgp_cipher_to_gnutls (sk->s2k_usage); if (sk->protect.algo == GNUTLS_CIPHER_NULL) { sk->csum = 0; nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo); if (!nskey) { gnutls_assert (); return CDK_Inv_Algo; } for (i = 0; i < nskey; i++) { rc = read_mpi (inp, &sk->mpi[i], 1); if (rc) return rc; } sk->csum = read_16 (inp); sk->is_protected = 0; } else if (sk->pk->version < 4) { /* The length of each multiprecision integer is stored in plaintext. */ nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo); if (!nskey) { gnutls_assert (); return CDK_Inv_Algo; } for (i = 0; i < nskey; i++) { rc = read_mpi (inp, &sk->mpi[i], 1); if (rc) return rc; } sk->csum = read_16 (inp); sk->is_protected = 1; } else { /* We need to read the rest of the packet because we do not have any information how long the encrypted mpi's are */ p2 = cdk_stream_tell (inp); p2 -= p1; sk->enclen = pktlen - p2; if (sk->enclen < 2) return CDK_Inv_Packet; /* at least 16 bits for the checksum! */ sk->encdata = cdk_calloc (1, sk->enclen + 1); if (!sk->encdata) return CDK_Out_Of_Core; if (stream_read (inp, sk->encdata, sk->enclen, &nread)) return CDK_Inv_Packet; /* Handle the GNU S2K extensions we know (just gnu-dummy right now): */ if (sk->protect.s2k->mode == CDK_S2K_GNU_EXT) { unsigned char gnumode; if ((sk->enclen < strlen ("GNU") + 1) || (0 != memcmp ("GNU", sk->encdata, strlen ("GNU")))) return CDK_Inv_Packet; gnumode = sk->encdata[strlen ("GNU")]; /* we only handle gnu-dummy (mode 1). mode 2 should refer to external smart cards. */ if (gnumode != 1) return CDK_Inv_Packet; /* gnu-dummy should have no more data */ if (sk->enclen != strlen ("GNU") + 1) return CDK_Inv_Packet; } nskey = cdk_pk_get_nskey (sk->pk->pubkey_algo); if (!nskey) { gnutls_assert (); return CDK_Inv_Algo; } /* We mark each MPI entry with NULL to indicate a protected key. */ for (i = 0; i < nskey; i++) sk->mpi[i] = NULL; sk->is_protected = 1; } sk->is_primary = 1; _cdk_copy_pk_to_sk (sk->pk, sk); return 0; }
/* Parse a Data Message into a newly-allocated DataMsg structure */ DataMsg parse_datamsg(const char *msg) { DataMsg datam = NULL; size_t lenp; unsigned char *raw = decode(msg, &lenp); unsigned char *bufp = raw; unsigned char version; if (!raw) goto inv; datam = calloc(1, sizeof(struct s_DataMsg)); if (!datam) { free(raw); goto inv; } datam->raw = raw; datam->rawlen = lenp; datam->macstart = bufp; require_len(3); if (memcmp(bufp, "\x00\x01\x03", 3) && memcmp(bufp, "\x00\x03\x03", 3) && memcmp(bufp, "\x00\x02\x03", 3)) goto inv; version = bufp[1]; datam->sender_instance = 0; datam->receiver_instance = 0; datam->version = version; datam->flags = -1; bufp += 3; lenp -= 3; if (version == 3) { read_int(datam->sender_instance); read_int(datam->receiver_instance); } if (version == 2 || version == 3) { require_len(1); datam->flags = bufp[0]; bufp += 1; lenp -= 1; } read_int(datam->sender_keyid); read_int(datam->rcpt_keyid); read_mpi(datam->y); read_raw(datam->ctr, 8); read_int(datam->encmsglen); datam->encmsg = malloc(datam->encmsglen); if (!datam->encmsg && datam->encmsglen > 0) goto inv; read_raw(datam->encmsg, datam->encmsglen); datam->macend = bufp; read_raw(datam->mac, 20); read_int(datam->mackeyslen); datam->mackeys = malloc(datam->mackeyslen); if (!datam->mackeys && datam->mackeyslen > 0) goto inv; read_raw(datam->mackeys, datam->mackeyslen); if (lenp != 0) goto inv; return datam; inv: free_datamsg(datam); return NULL; }