/* return the next key which matches, starting searching at *from */ static const pgp_key_t * getkeybyname(pgp_io_t *io, const pgp_keyring_t *keyring, const char *name, unsigned *from) { const pgp_key_t *kp; uint8_t **uidp; unsigned i = 0; pgp_key_t *keyp; unsigned savedstart; regex_t r; uint8_t keyid[PGP_KEY_ID_SIZE + 1]; size_t len; if (!keyring || !name || !from) { return NULL; } len = strlen(name); if (pgp_get_debug_level(__FILE__)) { (void) fprintf(io->outs, "[%u] name '%s', len %zu\n", *from, name, len); } /* first try name as a keyid */ (void) memset(keyid, 0x0, sizeof(keyid)); str2keyid(name, keyid, sizeof(keyid)); if (pgp_get_debug_level(__FILE__)) { hexdump(io->outs, "keyid", keyid, 4); } savedstart = *from; if ((kp = pgp_getkeybyid(io, keyring, keyid, from, NULL)) != NULL) { return kp; } *from = savedstart; if (pgp_get_debug_level(__FILE__)) { (void) fprintf(io->outs, "regex match '%s' from %u\n", name, *from); } /* match on full name or email address as a NOSUB, ICASE regexp */ (void) regcomp(&r, name, REG_EXTENDED | REG_ICASE); for (keyp = &keyring->keys[*from]; *from < keyring->keyc; *from += 1, keyp++) { uidp = keyp->uids; for (i = 0 ; i < keyp->uidc; i++, uidp++) { if (regexec(&r, (char *)*uidp, 0, NULL, 0) == 0) { if (pgp_get_debug_level(__FILE__)) { (void) fprintf(io->outs, "MATCHED keyid \"%s\" len %" PRIsize "u\n", (char *) *uidp, len); } regfree(&r); return keyp; } } } regfree(&r); return NULL; }
/* print out the successful signature information */ static void resultp(pgp_io_t *io, const char *f, pgp_validation_t *res, pgp_keyring_t *ring) { const pgp_key_t *key; pgp_pubkey_t *sigkey; unsigned from; unsigned i; time_t t; char id[MAX_ID_LENGTH + 1]; for (i = 0; i < res->validc; i++) { (void) fprintf(io->res, "Good signature for %s made %s", (f) ? f : "<stdin>", ctime(&res->valid_sigs[i].birthtime)); if (res->duration > 0) { t = res->birthtime + res->duration; (void) fprintf(io->res, "Valid until %s", ctime(&t)); } (void) fprintf(io->res, "using %s key %s\n", pgp_show_pka(res->valid_sigs[i].key_alg), userid_to_id(res->valid_sigs[i].signer_id, id)); from = 0; key = pgp_getkeybyid(io, ring, (const uint8_t *) res->valid_sigs[i].signer_id, &from, &sigkey); if (sigkey == &key->enckey) { (void) fprintf(io->res, "WARNING: signature for %s made with encryption key\n", (f) ? f : "<stdin>"); } pgp_print_keydata(io, ring, key, "signature ", &key->key.pubkey, 0); } }
int dc_pgp_pk_decrypt( dc_context_t* context, const void* ctext, size_t ctext_bytes, const dc_keyring_t* raw_private_keys_for_decryption, const dc_keyring_t* raw_public_keys_for_validation, int use_armor, void** ret_plain, size_t* ret_plain_bytes, dc_hash_t* ret_signature_fingerprints) { pgp_keyring_t* public_keys = calloc(1, sizeof(pgp_keyring_t)); /*should be 0 after parsing*/ pgp_keyring_t* private_keys = calloc(1, sizeof(pgp_keyring_t)); pgp_keyring_t* dummy_keys = calloc(1, sizeof(pgp_keyring_t)); pgp_validation_t* vresult = calloc(1, sizeof(pgp_validation_t)); key_id_t* recipients_key_ids = NULL; unsigned recipients_cnt = 0; pgp_memory_t* keysmem = pgp_memory_new(); int i = 0; int success = 0; if (context==NULL || ctext==NULL || ctext_bytes==0 || ret_plain==NULL || ret_plain_bytes==NULL || raw_private_keys_for_decryption==NULL || raw_private_keys_for_decryption->count<=0 || vresult==NULL || keysmem==NULL || public_keys==NULL || private_keys==NULL) { goto cleanup; } *ret_plain = NULL; *ret_plain_bytes = 0; /* setup keys (the keys may come from pgp_filter_keys_fileread(), see also pgp_keyring_add(rcpts, key)) */ for (i = 0; i < raw_private_keys_for_decryption->count; i++) { pgp_memory_clear(keysmem); /* a simple concatenate of private binary keys fails (works for public keys, however, we don't do it there either) */ pgp_memory_add(keysmem, raw_private_keys_for_decryption->keys[i]->binary, raw_private_keys_for_decryption->keys[i]->bytes); pgp_filter_keys_from_mem(&s_io, dummy_keys/*should stay empty*/, private_keys, NULL, 0, keysmem); } if (private_keys->keyc<=0) { dc_log_warning(context, 0, "Decryption-keyring contains unexpected data (%i/%i)", public_keys->keyc, private_keys->keyc); goto cleanup; } if (raw_public_keys_for_validation) { for (i = 0; i < raw_public_keys_for_validation->count; i++) { pgp_memory_clear(keysmem); pgp_memory_add(keysmem, raw_public_keys_for_validation->keys[i]->binary, raw_public_keys_for_validation->keys[i]->bytes); pgp_filter_keys_from_mem(&s_io, public_keys, dummy_keys/*should stay empty*/, NULL, 0, keysmem); } } /* decrypt */ { pgp_memory_t* outmem = pgp_decrypt_and_validate_buf(&s_io, vresult, ctext, ctext_bytes, private_keys, public_keys, use_armor, &recipients_key_ids, &recipients_cnt); if (outmem==NULL) { dc_log_warning(context, 0, "Decryption failed."); goto cleanup; } *ret_plain = outmem->buf; *ret_plain_bytes = outmem->length; free(outmem); /* do not use pgp_memory_free() as we took ownership of the buffer */ // collect the keys of the valid signatures if (ret_signature_fingerprints) { for (i = 0; i < vresult->validc; i++) { unsigned from = 0; pgp_key_t* key0 = pgp_getkeybyid(&s_io, public_keys, vresult->valid_sigs[i].signer_id, &from, NULL, NULL, 0, 0); if (key0) { pgp_pubkey_t* pubkey0 = &key0->key.pubkey; if (!pgp_fingerprint(&key0->pubkeyfpr, pubkey0, 0)) { goto cleanup; } char* fingerprint_hex = dc_binary_to_uc_hex(key0->pubkeyfpr.fingerprint, key0->pubkeyfpr.length); if (fingerprint_hex) { dc_hash_insert(ret_signature_fingerprints, fingerprint_hex, strlen(fingerprint_hex), (void*)1); } free(fingerprint_hex); } } } } success = 1; cleanup: if (keysmem) { pgp_memory_free(keysmem); } if (public_keys) { pgp_keyring_purge(public_keys); free(public_keys); } /*pgp_keyring_free() frees the content, not the pointer itself*/ if (private_keys) { pgp_keyring_purge(private_keys); free(private_keys); } if (dummy_keys) { pgp_keyring_purge(dummy_keys); free(dummy_keys); } if (vresult) { pgp_validate_result_free(vresult); } free(recipients_key_ids); return success; }