/* add a key to a secret keyring */ int pgp_add_to_secring(pgp_keyring_t *keyring, const pgp_seckey_t *seckey) { const pgp_pubkey_t *pubkey; pgp_key_t *key; if (pgp_get_debug_level(__FILE__)) { fprintf(stderr, "pgp_add_to_secring\n"); } if (keyring->keyc > 0) { key = &keyring->keys[keyring->keyc - 1]; if (pgp_get_debug_level(__FILE__) && key->key.pubkey.alg == PGP_PKA_DSA && seckey->pubkey.alg == PGP_PKA_ELGAMAL) { fprintf(stderr, "pgp_add_to_secring: found elgamal seckey\n"); } } EXPAND_ARRAY(keyring, key); key = &keyring->keys[keyring->keyc++]; (void) memset(key, 0x0, sizeof(*key)); pubkey = &seckey->pubkey; pgp_keyid(key->sigid, PGP_KEY_ID_SIZE, pubkey, keyring->hashtype); pgp_fingerprint(&key->sigfingerprint, pubkey, keyring->hashtype); key->type = PGP_PTAG_CT_SECRET_KEY; key->key.seckey = *seckey; if (pgp_get_debug_level(__FILE__)) { fprintf(stderr, "pgp_add_to_secring: keyc %u\n", keyring->keyc); } return 1; }
/* add a key to a public keyring */ int pgp_add_to_pubring(pgp_keyring_t *keyring, const pgp_pubkey_t *pubkey, pgp_content_enum tag) { pgp_key_t *key; time_t duration; if (pgp_get_debug_level(__FILE__)) { fprintf(stderr, "pgp_add_to_pubring (type %u)\n", tag); } switch(tag) { case PGP_PTAG_CT_PUBLIC_KEY: EXPAND_ARRAY(keyring, key); key = &keyring->keys[keyring->keyc++]; duration = key->key.pubkey.duration; (void) memset(key, 0x0, sizeof(*key)); key->type = tag; pgp_keyid(key->sigid, PGP_KEY_ID_SIZE, pubkey, keyring->hashtype); pgp_fingerprint(&key->sigfingerprint, pubkey, keyring->hashtype); key->key.pubkey = *pubkey; key->key.pubkey.duration = duration; return 1; case PGP_PTAG_CT_PUBLIC_SUBKEY: /* subkey is not the first */ key = &keyring->keys[keyring->keyc - 1]; pgp_keyid(key->encid, PGP_KEY_ID_SIZE, pubkey, keyring->hashtype); duration = key->key.pubkey.duration; (void) memcpy(&key->enckey, pubkey, sizeof(key->enckey)); key->enckey.duration = duration; return 1; default: return 0; } }
int dc_pgp_calc_fingerprint(const dc_key_t* raw_key, uint8_t** ret_fingerprint, size_t* ret_fingerprint_bytes) { int success = 0; pgp_keyring_t* public_keys = calloc(1, sizeof(pgp_keyring_t)); pgp_keyring_t* private_keys = calloc(1, sizeof(pgp_keyring_t)); pgp_memory_t* keysmem = pgp_memory_new(); if (raw_key==NULL || ret_fingerprint==NULL || *ret_fingerprint!=NULL || ret_fingerprint_bytes==NULL || *ret_fingerprint_bytes!=0 || raw_key->binary==NULL || raw_key->bytes <= 0 || public_keys==NULL || private_keys==NULL || keysmem==NULL) { goto cleanup; } pgp_memory_add(keysmem, raw_key->binary, raw_key->bytes); pgp_filter_keys_from_mem(&s_io, public_keys, private_keys, NULL, 0, keysmem); if (raw_key->type != DC_KEY_PUBLIC || public_keys->keyc <= 0) { goto cleanup; } pgp_key_t* key0 = &public_keys->keys[0]; pgp_pubkey_t* pubkey0 = &key0->key.pubkey; if (!pgp_fingerprint(&key0->pubkeyfpr, pubkey0, 0)) { goto cleanup; } *ret_fingerprint_bytes = key0->pubkeyfpr.length; *ret_fingerprint = malloc(*ret_fingerprint_bytes); memcpy(*ret_fingerprint, key0->pubkeyfpr.fingerprint, *ret_fingerprint_bytes); 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); } return success; }
int dc_pgp_create_keypair(dc_context_t* context, const char* addr, dc_key_t* ret_public_key, dc_key_t* ret_private_key) { int success = 0; pgp_key_t seckey; pgp_key_t pubkey; pgp_key_t subkey; uint8_t subkeyid[PGP_KEY_ID_SIZE]; uint8_t* user_id = NULL; pgp_memory_t* pubmem = pgp_memory_new(); pgp_memory_t* secmem = pgp_memory_new(); pgp_output_t* pubout = pgp_output_new(); pgp_output_t* secout = pgp_output_new(); memset(&seckey, 0, sizeof(pgp_key_t)); memset(&pubkey, 0, sizeof(pgp_key_t)); memset(&subkey, 0, sizeof(pgp_key_t)); if (context==NULL || addr==NULL || ret_public_key==NULL || ret_private_key==NULL || pubmem==NULL || secmem==NULL || pubout==NULL || secout==NULL) { goto cleanup; } /* Generate User ID. By convention, this is the e-mail-address in angle brackets. As the user-id is only decorative in Autocrypt and not needed for Delta Chat, so we _could_ just use sth. that looks like an e-mail-address. This would protect the user's privacy if someone else uploads the keys to keyservers. However, as eg. Enigmail displayes the user-id in "Good signature from <user-id>, for now, we decided to leave the address in the user-id */ #if 0 user_id = (uint8_t*)dc_mprintf("<%08X@%08X.org>", (int)random(), (int)random()); #else user_id = (uint8_t*)dc_mprintf("<%s>", addr); #endif /* generate two keypairs */ if (!pgp_rsa_generate_keypair(&seckey, DC_KEYGEN_BITS, DC_KEYGEN_E, NULL, NULL, NULL, 0) || !pgp_rsa_generate_keypair(&subkey, DC_KEYGEN_BITS, DC_KEYGEN_E, NULL, NULL, NULL, 0)) { goto cleanup; } /* Create public key, bind public subkey to public key ------------------------------------------------------------------------ */ pubkey.type = PGP_PTAG_CT_PUBLIC_KEY; pgp_pubkey_dup(&pubkey.key.pubkey, &seckey.key.pubkey); memcpy(pubkey.pubkeyid, seckey.pubkeyid, PGP_KEY_ID_SIZE); pgp_fingerprint(&pubkey.pubkeyfpr, &seckey.key.pubkey, 0); add_selfsigned_userid(&seckey, &pubkey, (const uint8_t*)user_id, 0/*never expire*/); EXPAND_ARRAY((&pubkey), subkey); { pgp_subkey_t* p = &pubkey.subkeys[pubkey.subkeyc++]; pgp_pubkey_dup(&p->key.pubkey, &subkey.key.pubkey); pgp_keyid(subkeyid, PGP_KEY_ID_SIZE, &pubkey.key.pubkey, PGP_HASH_SHA1); memcpy(p->id, subkeyid, PGP_KEY_ID_SIZE); } EXPAND_ARRAY((&pubkey), subkeysig); add_subkey_binding_signature(&pubkey.subkeysigs[pubkey.subkeysigc++], &pubkey, &subkey, &seckey); /* Create secret key, bind secret subkey to secret key ------------------------------------------------------------------------ */ EXPAND_ARRAY((&seckey), subkey); { pgp_subkey_t* p = &seckey.subkeys[seckey.subkeyc++]; pgp_seckey_dup(&p->key.seckey, &subkey.key.seckey); pgp_keyid(subkeyid, PGP_KEY_ID_SIZE, &seckey.key.pubkey, PGP_HASH_SHA1); memcpy(p->id, subkeyid, PGP_KEY_ID_SIZE); } EXPAND_ARRAY((&seckey), subkeysig); add_subkey_binding_signature(&seckey.subkeysigs[seckey.subkeysigc++], &seckey, &subkey, &seckey); /* Done with key generation, write binary keys to memory ------------------------------------------------------------------------ */ pgp_writer_set_memory(pubout, pubmem); if (!pgp_write_xfer_key(pubout, &pubkey, 0/*armored*/) || pubmem->buf==NULL || pubmem->length <= 0) { goto cleanup; } pgp_writer_set_memory(secout, secmem); if (!pgp_write_xfer_key(secout, &seckey, 0/*armored*/) || secmem->buf==NULL || secmem->length <= 0) { goto cleanup; } dc_key_set_from_binary(ret_public_key, pubmem->buf, pubmem->length, DC_KEY_PUBLIC); dc_key_set_from_binary(ret_private_key, secmem->buf, secmem->length, DC_KEY_PRIVATE); success = 1; cleanup: if (pubout) { pgp_output_delete(pubout); } if (secout) { pgp_output_delete(secout); } if (pubmem) { pgp_memory_free(pubmem); } if (secmem) { pgp_memory_free(secmem); } pgp_key_free(&seckey); /* not: pgp_keydata_free() which will also free the pointer itself (we created it on the stack) */ pgp_key_free(&pubkey); pgp_key_free(&subkey); free(user_id); return success; }
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; }
/** \ingroup HighLevel_KeyGenerate \brief Generates an RSA keypair \param numbits Modulus size \param e Public Exponent \param keydata Pointer to keydata struct to hold new key \return 1 if key generated successfully; otherwise 0 \note It is the caller's responsibility to call pgp_keydata_free(keydata) */ static unsigned rsa_generate_keypair(pgp_key_t *keydata, const int numbits, const unsigned long e, const char *hashalg, const char *cipher) { pgp_seckey_t *seckey; RSA *rsa; BN_CTX *ctx; pgp_output_t *output; pgp_memory_t *mem; ctx = BN_CTX_new(); pgp_keydata_init(keydata, PGP_PTAG_CT_SECRET_KEY); seckey = pgp_get_writable_seckey(keydata); /* generate the key pair */ rsa = RSA_generate_key(numbits, e, NULL, NULL); /* populate pgp key from ssl key */ seckey->pubkey.version = PGP_V4; seckey->pubkey.birthtime = time(NULL); seckey->pubkey.days_valid = 0; seckey->pubkey.alg = PGP_PKA_RSA; seckey->pubkey.key.rsa.n = BN_dup(rsa->n); seckey->pubkey.key.rsa.e = BN_dup(rsa->e); seckey->s2k_usage = PGP_S2KU_ENCRYPTED_AND_HASHED; seckey->s2k_specifier = PGP_S2KS_SALTED; /* seckey->s2k_specifier=PGP_S2KS_SIMPLE; */ if ((seckey->hash_alg = pgp_str_to_hash_alg(hashalg)) == PGP_HASH_UNKNOWN) { seckey->hash_alg = PGP_HASH_SHA1; } seckey->alg = pgp_str_to_cipher(cipher); seckey->octetc = 0; seckey->checksum = 0; seckey->key.rsa.d = BN_dup(rsa->d); seckey->key.rsa.p = BN_dup(rsa->p); seckey->key.rsa.q = BN_dup(rsa->q); seckey->key.rsa.u = BN_mod_inverse(NULL, rsa->p, rsa->q, ctx); if (seckey->key.rsa.u == NULL) { (void) fprintf(stderr, "seckey->key.rsa.u is NULL\n"); return 0; } BN_CTX_free(ctx); RSA_free(rsa); pgp_keyid(keydata->sigid, PGP_KEY_ID_SIZE, &keydata->key.seckey.pubkey, seckey->hash_alg); pgp_fingerprint(&keydata->sigfingerprint, &keydata->key.seckey.pubkey, seckey->hash_alg); /* Generate checksum */ output = NULL; mem = NULL; pgp_setup_memory_write(&output, &mem, 128); pgp_push_checksum_writer(output, seckey); switch (seckey->pubkey.alg) { case PGP_PKA_DSA: return pgp_write_mpi(output, seckey->key.dsa.x); case PGP_PKA_RSA: case PGP_PKA_RSA_ENCRYPT_ONLY: case PGP_PKA_RSA_SIGN_ONLY: if (!pgp_write_mpi(output, seckey->key.rsa.d) || !pgp_write_mpi(output, seckey->key.rsa.p) || !pgp_write_mpi(output, seckey->key.rsa.q) || !pgp_write_mpi(output, seckey->key.rsa.u)) { return 0; } break; case PGP_PKA_ELGAMAL: return pgp_write_mpi(output, seckey->key.elgamal.x); default: (void) fprintf(stderr, "Bad seckey->pubkey.alg\n"); return 0; } /* close rather than pop, since its the only one on the stack */ pgp_writer_close(output); pgp_teardown_memory_write(output, mem); /* should now have checksum in seckey struct */ /* test */ if (pgp_get_debug_level(__FILE__)) { test_seckey(seckey); } return 1; }