int dc_pgp_is_valid_key(dc_context_t* context, const dc_key_t* raw_key) { int key_is_valid = 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 (context==NULL || raw_key==NULL || 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); /* function returns 0 on any error in any packet - this does not mean, we cannot use the key. We check the details below therefore. */ if (raw_key->type==DC_KEY_PUBLIC && public_keys->keyc >= 1) { key_is_valid = 1; } else if (raw_key->type==DC_KEY_PRIVATE && private_keys->keyc >= 1) { key_is_valid = 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 key_is_valid; }
int dc_pgp_symm_decrypt(dc_context_t* context, const char* passphrase, const void* ctext, size_t ctext_bytes, void** ret_plain_text, size_t* ret_plain_bytes) { int success = 0; pgp_io_t io; pgp_memory_t* outmem = NULL; memset(&io, 0, sizeof(pgp_io_t)); io.outs = stdout; io.errs = stderr; io.res = stderr; if ((outmem=pgp_decrypt_buf(&io, ctext, ctext_bytes, NULL, NULL, 0, 0, passphrase))==NULL) { goto cleanup; } *ret_plain_text = outmem->buf; *ret_plain_bytes = outmem->length; free(outmem); /* do not use pgp_memory_free() as we took ownership of the buffer */ outmem = NULL; success = 1; cleanup: if (outmem) { pgp_memory_free(outmem); } return success; }
/** \ingroup Core_Keys \brief Add selfsigned User ID to key \param keydata Key to which to add user ID \param userid Self-signed User ID to add \return 1 if OK; else 0 */ unsigned pgp_add_selfsigned_userid(pgp_key_t *key, uint8_t *userid) { pgp_create_sig_t *sig; pgp_subpacket_t sigpacket; pgp_memory_t *mem_userid = NULL; pgp_output_t *useridoutput = NULL; pgp_memory_t *mem_sig = NULL; pgp_output_t *sigoutput = NULL; /* * create signature packet for this userid */ /* create userid pkt */ pgp_setup_memory_write(&useridoutput, &mem_userid, 128); pgp_write_struct_userid(useridoutput, userid); /* create sig for this pkt */ sig = pgp_create_sig_new(); pgp_sig_start_key_sig(sig, &key->key.seckey.pubkey, userid, PGP_CERT_POSITIVE); pgp_add_time(sig, (int64_t)time(NULL), "birth"); pgp_add_issuer_keyid(sig, key->sigid); pgp_add_primary_userid(sig, 1); pgp_end_hashed_subpkts(sig); pgp_setup_memory_write(&sigoutput, &mem_sig, 128); pgp_write_sig(sigoutput, sig, &key->key.seckey.pubkey, &key->key.seckey); /* add this packet to key */ sigpacket.length = pgp_mem_len(mem_sig); sigpacket.raw = pgp_mem_data(mem_sig); /* add userid to key */ (void) pgp_add_userid(key, userid); (void) pgp_add_subpacket(key, &sigpacket); /* cleanup */ pgp_create_sig_delete(sig); pgp_output_delete(useridoutput); pgp_output_delete(sigoutput); pgp_memory_free(mem_userid); pgp_memory_free(mem_sig); return 1; }
int dc_pgp_split_key(dc_context_t* context, const dc_key_t* private_in, dc_key_t* ret_public_key) { 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(); pgp_memory_t* pubmem = pgp_memory_new(); pgp_output_t* pubout = pgp_output_new(); if (context==NULL || private_in==NULL || ret_public_key==NULL || public_keys==NULL || private_keys==NULL || keysmem==NULL || pubmem==NULL || pubout==NULL) { goto cleanup; } pgp_memory_add(keysmem, private_in->binary, private_in->bytes); pgp_filter_keys_from_mem(&s_io, public_keys, private_keys, NULL, 0, keysmem); if (private_in->type!=DC_KEY_PRIVATE || private_keys->keyc <= 0) { dc_log_warning(context, 0, "Split key: Given key is no private key."); goto cleanup; } if (public_keys->keyc <= 0) { dc_log_warning(context, 0, "Split key: Given key does not contain a public key."); goto cleanup; } pgp_writer_set_memory(pubout, pubmem); if (!pgp_write_xfer_key(pubout, &public_keys->keys[0], 0/*armored*/) || pubmem->buf==NULL || pubmem->length <= 0) { goto cleanup; } dc_key_set_from_binary(ret_public_key, pubmem->buf, pubmem->length, DC_KEY_PUBLIC); success = 1; cleanup: if (pubout) { pgp_output_delete(pubout); } if (pubmem) { pgp_memory_free(pubmem); } 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; }
/** \ingroup HighLevel_Crypto Encrypt a file \param infile Name of file to be encrypted \param outfile Name of file to write to. If NULL, name is constructed from infile \param pubkey Public Key to encrypt file for \param use_armour Write armoured text, if set \param allow_overwrite Allow output file to be overwrwritten if it exists \return 1 if OK; else 0 */ unsigned pgp_encrypt_file(pgp_io_t *io, const char *infile, const char *outfile, const pgp_key_t *key, const unsigned use_armour, const unsigned allow_overwrite, const char *cipher) { pgp_output_t *output; pgp_memory_t *inmem; int fd_out; __PGP_USED(io); inmem = pgp_memory_new(); if (!pgp_mem_readfile(inmem, infile)) { return 0; } fd_out = pgp_setup_file_write(&output, outfile, allow_overwrite); if (fd_out < 0) { pgp_memory_free(inmem); return 0; } /* set armoured/not armoured here */ if (use_armour) { pgp_writer_push_armor_msg(output); } /* Push the encrypted writer */ if (!pgp_push_enc_se_ip(output, key, cipher)) { pgp_memory_free(inmem); return 0; } /* This does the writing */ pgp_write(output, pgp_mem_data(inmem), (unsigned)pgp_mem_len(inmem)); /* tidy up */ pgp_memory_free(inmem); pgp_teardown_file_write(output, fd_out); return 1; }
static void hash_add_key(pgp_hash_t *hash, const pgp_pubkey_t *key) { pgp_memory_t *mem = pgp_memory_new(); const unsigned dontmakepacket = 0; size_t len; pgp_build_pubkey(mem, key, dontmakepacket); len = pgp_mem_len(mem); pgp_hash_add_int(hash, 0x99, 1); pgp_hash_add_int(hash, (unsigned)len, 2); hash->add(hash, pgp_mem_data(mem), (unsigned)len); pgp_memory_free(mem); }
unsigned pgp_fileread_litdata(const char *filename, const pgp_litdata_enum type, pgp_output_t *output) { pgp_memory_t *mem; unsigned ret; int len; mem = pgp_memory_new(); if (!pgp_mem_readfile(mem, filename)) { (void) fprintf(stderr, "pgp_mem_readfile of '%s' failed\n", filename); return 0; } len = (int)pgp_mem_len(mem); ret = pgp_write_litdata(output, pgp_mem_data(mem), len, type); pgp_memory_free(mem); return ret; }
static void add_selfsigned_userid(pgp_key_t *skey, pgp_key_t *pkey, const uint8_t *userid, time_t key_expiry) { /* similar to pgp_add_selfsigned_userid() which, however, uses different key flags */ pgp_create_sig_t* sig = NULL; pgp_subpacket_t sigpacket; pgp_memory_t* mem_sig = NULL; pgp_output_t* sigoutput = NULL; /* create sig for this pkt */ sig = pgp_create_sig_new(); pgp_sig_start_key_sig(sig, &skey->key.seckey.pubkey, NULL, userid, PGP_CERT_POSITIVE); pgp_add_creation_time(sig, time(NULL)); pgp_add_key_expiration_time(sig, key_expiry); pgp_add_primary_userid(sig, 1); pgp_add_key_flags(sig, PGP_KEYFLAG_SIGN_DATA|PGP_KEYFLAG_CERT_KEYS); add_key_prefs(sig); pgp_add_key_features(sig); /* will add 0x01 - modification detection */ pgp_end_hashed_subpkts(sig); pgp_add_issuer_keyid(sig, skey->pubkeyid); /* the issuer keyid is not hashed by definition */ pgp_setup_memory_write(&sigoutput, &mem_sig, 128); pgp_write_sig(sigoutput, sig, &skey->key.seckey.pubkey, &skey->key.seckey); /* add this packet to key */ sigpacket.length = pgp_mem_len(mem_sig); sigpacket.raw = pgp_mem_data(mem_sig); /* add user id and signature to key */ pgp_update_userid(skey, userid, &sigpacket, &sig->sig.info); if(pkey) { pgp_update_userid(pkey, userid, &sigpacket, &sig->sig.info); } /* cleanup */ pgp_create_sig_delete(sig); pgp_output_delete(sigoutput); pgp_memory_free(mem_sig); }
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; }
/** \ingroup HighLevel_Sign \brief Sign a file \param inname Input filename \param outname Output filename. If NULL, a name is constructed from the input filename. \param seckey Secret Key to use for signing \param armored Write armoured text, if set. \param overwrite May overwrite existing file, if set. \return 1 if OK; else 0; */ unsigned pgp_sign_file(pgp_io_t *io, const char *inname, const char *outname, const pgp_seckey_t *seckey, const char *hashname, const int64_t from, const uint64_t duration, const unsigned armored, const unsigned cleartext, const unsigned overwrite) { pgp_create_sig_t *sig; pgp_sig_type_t sig_type; pgp_hash_alg_t hash_alg; pgp_memory_t *infile; pgp_output_t *output; pgp_hash_t *hash; unsigned ret; uint8_t keyid[PGP_KEY_ID_SIZE]; int fd_out; sig = NULL; sig_type = PGP_SIG_BINARY; infile = NULL; output = NULL; hash = NULL; fd_out = 0; /* find the hash algorithm */ hash_alg = pgp_str_to_hash_alg(hashname); if (hash_alg == PGP_HASH_UNKNOWN) { (void) fprintf(io->errs, "pgp_sign_file: unknown hash algorithm: \"%s\"\n", hashname); return 0; } /* read input file into buf */ infile = pgp_memory_new(); if (!pgp_mem_readfile(infile, inname)) { return 0; } /* setup output file */ fd_out = open_output_file(&output, inname, outname, (armored) ? "asc" : "gpg", overwrite); if (fd_out < 0) { pgp_memory_free(infile); return 0; } /* set up signature */ sig = pgp_create_sig_new(); if (!sig) { pgp_memory_free(infile); pgp_teardown_file_write(output, fd_out); return 0; } pgp_start_sig(sig, seckey, hash_alg, sig_type); if (cleartext) { if (pgp_writer_push_clearsigned(output, sig) != 1) { return 0; } /* Do the signing */ pgp_write(output, pgp_mem_data(infile), (unsigned)pgp_mem_len(infile)); pgp_memory_free(infile); /* add signature with subpackets: */ /* - creation time */ /* - key id */ ret = pgp_writer_use_armored_sig(output) && pgp_add_time(sig, (int64_t)from, "birth") && pgp_add_time(sig, (int64_t)duration, "expiration"); if (ret == 0) { pgp_teardown_file_write(output, fd_out); return 0; } pgp_keyid(keyid, PGP_KEY_ID_SIZE, &seckey->pubkey, hash_alg); ret = pgp_add_issuer_keyid(sig, keyid) && pgp_end_hashed_subpkts(sig) && pgp_write_sig(output, sig, &seckey->pubkey, seckey); pgp_teardown_file_write(output, fd_out); if (ret == 0) { PGP_ERROR_1(&output->errors, PGP_E_W, "%s", "Cannot sign file as cleartext"); } } else { /* set armoured/not armoured here */ if (armored) { pgp_writer_push_armor_msg(output); } /* write one_pass_sig */ pgp_write_one_pass_sig(output, seckey, hash_alg, sig_type); /* hash file contents */ hash = pgp_sig_get_hash(sig); hash->add(hash, pgp_mem_data(infile), (unsigned)pgp_mem_len(infile)); #if 1 /* output file contents as Literal Data packet */ pgp_write_litdata(output, pgp_mem_data(infile), (const int)pgp_mem_len(infile), PGP_LDT_BINARY); #else /* XXX - agc - sync with writer.c 1094 for ops_writez */ pgp_setup_memory_write(&litoutput, &litmem, bufsz); pgp_setup_memory_write(&zoutput, &zmem, bufsz); pgp_write_litdata(litoutput, pgp_mem_data(pgp_mem_data(infile), (const int)pgp_mem_len(infile), PGP_LDT_BINARY); pgp_writez(zoutput, pgp_mem_data(litmem), pgp_mem_len(litmem)); #endif /* add creation time to signature */ pgp_add_time(sig, (int64_t)from, "birth"); pgp_add_time(sig, (int64_t)duration, "expiration"); /* add key id to signature */ pgp_keyid(keyid, PGP_KEY_ID_SIZE, &seckey->pubkey, hash_alg); pgp_add_issuer_keyid(sig, keyid); pgp_end_hashed_subpkts(sig); pgp_write_sig(output, sig, &seckey->pubkey, seckey); /* tidy up */ pgp_teardown_file_write(output, fd_out); pgp_create_sig_delete(sig); pgp_memory_free(infile); ret = 1; } return ret; }
unsigned pgp_write_sig(pgp_output_t *output, pgp_create_sig_t *sig, const pgp_pubkey_t *key, const pgp_seckey_t *seckey) { unsigned ret = 0; size_t len = pgp_mem_len(sig->mem); /* check key not decrypted */ switch (seckey->pubkey.alg) { case PGP_PKA_RSA: case PGP_PKA_RSA_ENCRYPT_ONLY: case PGP_PKA_RSA_SIGN_ONLY: if (seckey->key.rsa.d == NULL) { (void) fprintf(stderr, "pgp_write_sig: null rsa.d\n"); return 0; } break; case PGP_PKA_DSA: if (seckey->key.dsa.x == NULL) { (void) fprintf(stderr, "pgp_write_sig: null dsa.x\n"); return 0; } break; default: (void) fprintf(stderr, "Unsupported algorithm %d\n", seckey->pubkey.alg); return 0; } if (sig->hashlen == (unsigned) -1) { (void) fprintf(stderr, "ops_write_sig: bad hashed data len\n"); return 0; } pgp_memory_place_int(sig->mem, sig->unhashoff, (unsigned)(len - sig->unhashoff - 2), 2); /* add the packet from version number to end of hashed subpackets */ if (pgp_get_debug_level(__FILE__)) { (void) fprintf(stderr, "ops_write_sig: hashed packet info\n"); } sig->hash.add(&sig->hash, pgp_mem_data(sig->mem), sig->unhashoff); /* add final trailer */ pgp_hash_add_int(&sig->hash, (unsigned)sig->sig.info.version, 1); pgp_hash_add_int(&sig->hash, 0xff, 1); /* +6 for version, type, pk alg, hash alg, hashed subpacket length */ pgp_hash_add_int(&sig->hash, sig->hashlen + 6, 4); if (pgp_get_debug_level(__FILE__)) { (void) fprintf(stderr, "ops_write_sig: done writing hashed\n"); } /* XXX: technically, we could figure out how big the signature is */ /* and write it directly to the output instead of via memory. */ switch (seckey->pubkey.alg) { case PGP_PKA_RSA: case PGP_PKA_RSA_ENCRYPT_ONLY: case PGP_PKA_RSA_SIGN_ONLY: if (!rsa_sign(&sig->hash, &key->key.rsa, &seckey->key.rsa, sig->output)) { (void) fprintf(stderr, "pgp_write_sig: rsa_sign failure\n"); return 0; } break; case PGP_PKA_DSA: if (!dsa_sign(&sig->hash, &key->key.dsa, &seckey->key.dsa, sig->output)) { (void) fprintf(stderr, "pgp_write_sig: dsa_sign failure\n"); return 0; } break; default: (void) fprintf(stderr, "Unsupported algorithm %d\n", seckey->pubkey.alg); return 0; } ret = pgp_write_ptag(output, PGP_PTAG_CT_SIGNATURE); if (ret) { len = pgp_mem_len(sig->mem); ret = pgp_write_length(output, (unsigned)len) && pgp_write(output, pgp_mem_data(sig->mem), (unsigned)len); } pgp_memory_free(sig->mem); if (ret == 0) { PGP_ERROR_1(&output->errors, PGP_E_W, "%s", "Cannot write signature"); } return ret; }
/* sign a file, and put the signature in a separate file */ int pgp_sign_detached(pgp_io_t *io, const char *f, char *sigfile, pgp_seckey_t *seckey, const char *hash, const int64_t from, const uint64_t duration, const unsigned armored, const unsigned overwrite) { pgp_create_sig_t *sig; pgp_hash_alg_t hash_alg; pgp_output_t *output; pgp_memory_t *mem; uint8_t keyid[PGP_KEY_ID_SIZE]; int fd; /* find out which hash algorithm to use */ hash_alg = pgp_str_to_hash_alg(hash); if (hash_alg == PGP_HASH_UNKNOWN) { (void) fprintf(io->errs,"Unknown hash algorithm: %s\n", hash); return 0; } /* setup output file */ fd = open_output_file(&output, f, sigfile, (armored) ? "asc" : "sig", overwrite); if (fd < 0) { (void) fprintf(io->errs,"Can't open output file: %s\n", f); return 0; } /* create a new signature */ sig = pgp_create_sig_new(); pgp_start_sig(sig, seckey, hash_alg, PGP_SIG_BINARY); /* read the contents of 'f', and add that to the signature */ mem = pgp_memory_new(); if (!pgp_mem_readfile(mem, f)) { pgp_teardown_file_write(output, fd); return 0; } /* set armoured/not armoured here */ if (armored) { pgp_writer_push_armor_msg(output); } pgp_sig_add_data(sig, pgp_mem_data(mem), pgp_mem_len(mem)); pgp_memory_free(mem); /* calculate the signature */ pgp_add_time(sig, from, "birth"); pgp_add_time(sig, (int64_t)duration, "expiration"); pgp_keyid(keyid, sizeof(keyid), &seckey->pubkey, hash_alg); pgp_add_issuer_keyid(sig, keyid); pgp_end_hashed_subpkts(sig); pgp_write_sig(output, sig, &seckey->pubkey, seckey); pgp_teardown_file_write(output, fd); pgp_seckey_free(seckey); return 1; }
int dc_pgp_pk_encrypt( dc_context_t* context, const void* plain_text, size_t plain_bytes, const dc_keyring_t* raw_public_keys_for_encryption, const dc_key_t* raw_private_key_for_signing, int use_armor, void** ret_ctext, size_t* ret_ctext_bytes) { pgp_keyring_t* public_keys = calloc(1, sizeof(pgp_keyring_t)); pgp_keyring_t* private_keys = calloc(1, sizeof(pgp_keyring_t)); pgp_keyring_t* dummy_keys = calloc(1, sizeof(pgp_keyring_t)); pgp_memory_t* keysmem = pgp_memory_new(); pgp_memory_t* signedmem = NULL; int i = 0; int success = 0; if (context==NULL || plain_text==NULL || plain_bytes==0 || ret_ctext==NULL || ret_ctext_bytes==NULL || raw_public_keys_for_encryption==NULL || raw_public_keys_for_encryption->count<=0 || keysmem==NULL || public_keys==NULL || private_keys==NULL || dummy_keys==NULL) { goto cleanup; } *ret_ctext = NULL; *ret_ctext_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_public_keys_for_encryption->count; i++) { pgp_memory_clear(keysmem); pgp_memory_add(keysmem, raw_public_keys_for_encryption->keys[i]->binary, raw_public_keys_for_encryption->keys[i]->bytes); pgp_filter_keys_from_mem(&s_io, public_keys, private_keys/*should stay empty*/, NULL, 0, keysmem); } if (public_keys->keyc <=0 || private_keys->keyc!=0) { dc_log_warning(context, 0, "Encryption-keyring contains unexpected data (%i/%i)", public_keys->keyc, private_keys->keyc); goto cleanup; } /* encrypt */ { const void* signed_text = NULL; size_t signed_bytes = 0; int encrypt_raw_packet = 0; clock_t sign_clocks = 0; clock_t encrypt_clocks = 0; if (raw_private_key_for_signing) { pgp_memory_clear(keysmem); pgp_memory_add(keysmem, raw_private_key_for_signing->binary, raw_private_key_for_signing->bytes); pgp_filter_keys_from_mem(&s_io, dummy_keys, private_keys, NULL, 0, keysmem); if (private_keys->keyc <= 0) { dc_log_warning(context, 0, "No key for signing found."); goto cleanup; } clock_t start = clock(); pgp_key_t* sk0 = &private_keys->keys[0]; signedmem = pgp_sign_buf(&s_io, plain_text, plain_bytes, &sk0->key.seckey, time(NULL)/*birthtime*/, 0/*duration*/, NULL/*hash, defaults to sha256*/, 0/*armored*/, 0/*cleartext*/); sign_clocks = clock()-start; if (signedmem==NULL) { dc_log_warning(context, 0, "Signing failed."); goto cleanup; } signed_text = signedmem->buf; signed_bytes = signedmem->length; encrypt_raw_packet = 1; } else { signed_text = plain_text; signed_bytes = plain_bytes; encrypt_raw_packet = 0; } clock_t start = clock(); pgp_memory_t* outmem = pgp_encrypt_buf(&s_io, signed_text, signed_bytes, public_keys, use_armor, NULL/*cipher*/, encrypt_raw_packet); encrypt_clocks = clock()-start; dc_log_info(context, 0, "Message signed in %.3f ms and encrypted in %.3f ms.", (double)(sign_clocks)*1000.0/CLOCKS_PER_SEC, (double)(encrypt_clocks)*1000.0/CLOCKS_PER_SEC); if (outmem==NULL) { dc_log_warning(context, 0, "Encryption failed."); goto cleanup; } *ret_ctext = outmem->buf; *ret_ctext_bytes = outmem->length; free(outmem); /* do not use pgp_memory_free() as we took ownership of the buffer */ } success = 1; cleanup: if (keysmem) { pgp_memory_free(keysmem); } if (signedmem) { pgp_memory_free(signedmem); } 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); } 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_symm_encrypt(dc_context_t* context, const char* passphrase, const void* plain, size_t plain_bytes, char** ret_ctext_armored) { int success = 0; uint8_t salt[PGP_SALT_SIZE]; pgp_crypt_t crypt_info; uint8_t* key = NULL; pgp_output_t* payload_output = NULL; pgp_memory_t* payload_mem = NULL; pgp_output_t* encr_output = NULL; pgp_memory_t* encr_mem = NULL; if (context==NULL || passphrase==NULL || plain==NULL || plain_bytes==0 || ret_ctext_armored==NULL ) { goto cleanup; } //printf("\n~~~~~~~~~~~~~~~~~~~~SETUP-PAYLOAD~~~~~~~~~~~~~~~~~~~~\n%s~~~~~~~~~~~~~~~~~~~~/SETUP-PAYLOAD~~~~~~~~~~~~~~~~~~~~\n",key_asc); // DEBUG OUTPUT /* put the payload into a literal data packet which will be encrypted then, see RFC 4880, 5.7 : "When it has been decrypted, it contains other packets (usually a literal data packet or compressed data packet, but in theory other Symmetrically Encrypted Data packets or sequences of packets that form whole OpenPGP messages)" */ pgp_setup_memory_write(&payload_output, &payload_mem, 128); pgp_write_litdata(payload_output, (const uint8_t*)plain, plain_bytes, PGP_LDT_BINARY); /* create salt for the key */ pgp_random(salt, PGP_SALT_SIZE); /* S2K */ #define SYMM_ALGO PGP_SA_AES_128 if (!pgp_crypt_any(&crypt_info, SYMM_ALGO)) { goto cleanup; } int s2k_spec = PGP_S2KS_ITERATED_AND_SALTED; // 0=simple, 1=salted, 3=salted+iterated int s2k_iter_id = 96; // 0=1024 iterations, 96=65536 iterations #define HASH_ALG PGP_HASH_SHA256 if ((key = pgp_s2k_do(passphrase, crypt_info.keysize, s2k_spec, HASH_ALG, salt, s2k_iter_id))==NULL) { goto cleanup; } /* encrypt the payload using the key using AES-128 and put it into OpenPGP's "Symmetric-Key Encrypted Session Key" (Tag 3, https://tools.ietf.org/html/rfc4880#section-5.3) followed by OpenPGP's "Symmetrically Encrypted Data Packet" (Tag 18, https://tools.ietf.org/html/rfc4880#section-5.13 , better than Tag 9) */ pgp_setup_memory_write(&encr_output, &encr_mem, 128); pgp_writer_push_armor_msg(encr_output); /* Tag 3 - PGP_PTAG_CT_SK_SESSION_KEY */ pgp_write_ptag (encr_output, PGP_PTAG_CT_SK_SESSION_KEY); pgp_write_length (encr_output, 1/*version*/ + 1/*symm. algo*/ + 1/*s2k_spec*/ + 1/*S2 hash algo*/ + ((s2k_spec==PGP_S2KS_SALTED || s2k_spec==PGP_S2KS_ITERATED_AND_SALTED)? PGP_SALT_SIZE : 0)/*the salt*/ + ((s2k_spec==PGP_S2KS_ITERATED_AND_SALTED)? 1 : 0)/*number of iterations*/); pgp_write_scalar (encr_output, 4, 1); // 1 octet: version pgp_write_scalar (encr_output, SYMM_ALGO, 1); // 1 octet: symm. algo pgp_write_scalar (encr_output, s2k_spec, 1); // 1 octet: s2k_spec pgp_write_scalar (encr_output, HASH_ALG, 1); // 1 octet: S2 hash algo if (s2k_spec==PGP_S2KS_SALTED || s2k_spec==PGP_S2KS_ITERATED_AND_SALTED) { pgp_write (encr_output, salt, PGP_SALT_SIZE); // 8 octets: the salt } if (s2k_spec==PGP_S2KS_ITERATED_AND_SALTED) { pgp_write_scalar (encr_output, s2k_iter_id, 1); // 1 octet: number of iterations } // for(int j=0; j<AES_KEY_LENGTH; j++) { printf("%02x", key[j]); } printf("\n----------------\n"); /* Tag 18 - PGP_PTAG_CT_SE_IP_DATA */ //pgp_write_symm_enc_data((const uint8_t*)payload_mem->buf, payload_mem->length, PGP_SA_AES_128, key, encr_output); //-- would generate Tag 9 { uint8_t* iv = calloc(1, crypt_info.blocksize); if (iv==NULL) { goto cleanup; } crypt_info.set_iv(&crypt_info, iv); free(iv); crypt_info.set_crypt_key(&crypt_info, &key[0]); pgp_encrypt_init(&crypt_info); pgp_write_se_ip_pktset(encr_output, payload_mem->buf, payload_mem->length, &crypt_info); crypt_info.decrypt_finish(&crypt_info); } /* done with symmetric key block */ pgp_writer_close(encr_output); *ret_ctext_armored = dc_null_terminate((const char*)encr_mem->buf, encr_mem->length); //printf("\n~~~~~~~~~~~~~~~~~~~~SYMMETRICALLY ENCRYPTED~~~~~~~~~~~~~~~~~~~~\n%s~~~~~~~~~~~~~~~~~~~~/SYMMETRICALLY ENCRYPTED~~~~~~~~~~~~~~~~~~~~\n",encr_string); // DEBUG OUTPUT success = 1; cleanup: if (payload_output) { pgp_output_delete(payload_output); } if (payload_mem) { pgp_memory_free(payload_mem); } if (encr_output) { pgp_output_delete(encr_output); } if (encr_mem) { pgp_memory_free(encr_mem); } free(key); 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; }