static void add_subkey_binding_signature(pgp_subkeysig_t* p, pgp_key_t* primarykey, pgp_key_t* subkey, pgp_key_t* seckey) { /*add "0x18: Subkey Binding Signature" packet, PGP_SIG_SUBKEY */ pgp_create_sig_t* sig = NULL; pgp_output_t* sigoutput = NULL; pgp_memory_t* mem_sig = NULL; sig = pgp_create_sig_new(); pgp_sig_start_key_sig(sig, &primarykey->key.pubkey, &subkey->key.pubkey, NULL, PGP_SIG_SUBKEY); pgp_add_creation_time(sig, time(NULL)); pgp_add_key_expiration_time(sig, 0); pgp_add_key_flags(sig, PGP_KEYFLAG_ENC_STORAGE|PGP_KEYFLAG_ENC_COMM); /* NB: algo/hash/compression preferences are not added to subkeys */ pgp_end_hashed_subpkts(sig); pgp_add_issuer_keyid(sig, seckey->pubkeyid); /* the issuer keyid is not hashed by definition */ pgp_setup_memory_write(&sigoutput, &mem_sig, 128); pgp_write_sig(sigoutput, sig, &seckey->key.seckey.pubkey, &seckey->key.seckey); p->subkey = primarykey->subkeyc-1; /* index of subkey in array */ p->packet.length = mem_sig->length; p->packet.raw = mem_sig->buf; mem_sig->buf = NULL; /* move ownership to packet */ copy_sig_info(&p->siginfo, &sig->sig.info); /* not sure, if this is okay, however, siginfo should be set up, otherwise we get "bad info-type" errors */ pgp_create_sig_delete(sig); pgp_output_delete(sigoutput); free(mem_sig); /* do not use pgp_memory_free() as this would also free mem_sig->buf which is owned by the packet */ }
/** \ingroup Core_Signature Free signature and memory associated with it \param sig struct to free \sa pgp_create_sig_new() */ void pgp_create_sig_delete(pgp_create_sig_t *sig) { pgp_output_delete(sig->output); sig->output = NULL; free(sig); }
/** \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; }
void pgp_build_pubkey(pgp_memory_t *out, const pgp_pubkey_t *key, unsigned make_packet) { pgp_output_t *output; output = pgp_output_new(); pgp_memory_init(out, 128); pgp_writer_set_memory(output, out); write_pubkey_body(key, output); if (make_packet) { pgp_memory_make_packet(out, PGP_PTAG_CT_PUBLIC_KEY); } pgp_output_delete(output); }
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; }
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); }
/* encrypt the contents of the input buffer, and return the mem structure */ pgp_memory_t * pgp_encrypt_buf(pgp_io_t *io, const void *input, const size_t insize, const pgp_key_t *pubkey, const unsigned use_armour, const char *cipher) { pgp_output_t *output; pgp_memory_t *outmem; __PGP_USED(io); if (input == NULL) { (void) fprintf(io->errs, "pgp_encrypt_buf: null memory\n"); return 0; } pgp_setup_memory_write(&output, &outmem, insize); /* set armoured/not armoured here */ if (use_armour) { pgp_writer_push_armor_msg(output); } /* Push the encrypted writer */ pgp_push_enc_se_ip(output, pubkey, cipher); /* This does the writing */ pgp_write(output, input, (unsigned)insize); /* tidy up */ pgp_writer_close(output); pgp_output_delete(output); return outmem; }
/** \ingroup HighLevel_Sign \brief Signs a buffer \param input Input text to be signed \param input_len Length of input text \param sig_type Signature type \param seckey Secret Key \param armored Write armoured text, if set \return New pgp_memory_t struct containing signed text \note It is the caller's responsibility to call pgp_memory_free(me) */ pgp_memory_t * pgp_sign_buf(pgp_io_t *io, const void *input, const size_t insize, const pgp_seckey_t *seckey, const int64_t from, const uint64_t duration, const char *hashname, const unsigned armored, const unsigned cleartext) { pgp_litdata_enum ld_type; pgp_create_sig_t *sig; pgp_sig_type_t sig_type; pgp_hash_alg_t hash_alg; pgp_output_t *output; pgp_memory_t *mem; uint8_t keyid[PGP_KEY_ID_SIZE]; pgp_hash_t *hash; unsigned ret; sig = NULL; sig_type = PGP_SIG_BINARY; output = NULL; mem = pgp_memory_new(); hash = NULL; ret = 0; hash_alg = pgp_str_to_hash_alg(hashname); if (hash_alg == PGP_HASH_UNKNOWN) { (void) fprintf(io->errs, "pgp_sign_buf: unknown hash algorithm: \"%s\"\n", hashname); return NULL; } /* setup literal data packet type */ ld_type = (cleartext) ? PGP_LDT_TEXT : PGP_LDT_BINARY; if (input == NULL) { (void) fprintf(io->errs, "pgp_sign_buf: null input\n"); return NULL; } /* set up signature */ if ((sig = pgp_create_sig_new()) == NULL) { return NULL; } pgp_start_sig(sig, seckey, hash_alg, sig_type); /* setup writer */ pgp_setup_memory_write(&output, &mem, insize); if (cleartext) { /* Do the signing */ /* add signature with subpackets: */ /* - creation time */ /* - key id */ ret = pgp_writer_push_clearsigned(output, sig) && pgp_write(output, input, (unsigned)insize) && pgp_writer_use_armored_sig(output) && pgp_add_time(sig, from, "birth") && pgp_add_time(sig, (int64_t)duration, "expiration"); if (ret == 0) { return NULL; } pgp_output_delete(output); } else { /* set armoured/not armoured here */ if (armored) { pgp_writer_push_armor_msg(output); } if (pgp_get_debug_level(__FILE__)) { fprintf(io->errs, "** Writing out one pass sig\n"); } /* write one_pass_sig */ pgp_write_one_pass_sig(output, seckey, hash_alg, sig_type); /* hash memory */ hash = pgp_sig_get_hash(sig); hash->add(hash, input, (unsigned)insize); /* output file contents as Literal Data packet */ if (pgp_get_debug_level(__FILE__)) { (void) fprintf(stderr, "** Writing out data now\n"); } pgp_write_litdata(output, input, (const int)insize, ld_type); if (pgp_get_debug_level(__FILE__)) { fprintf(stderr, "** After Writing out data now\n"); } /* add creation time to signature */ pgp_add_time(sig, 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); /* write out sig */ pgp_write_sig(output, sig, &seckey->pubkey, seckey); /* tidy up */ pgp_writer_close(output); pgp_create_sig_delete(sig); } return mem; }
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; }
/* decrypt an area of memory */ pgp_memory_t * pgp_decrypt_buf(pgp_io_t *io, const void *input, const size_t insize, pgp_keyring_t *secring, pgp_keyring_t *pubring, const unsigned use_armour, const unsigned sshkeys, void *passfp, int numtries, pgp_cbfunc_t *getpassfunc) { pgp_stream_t *parse = NULL; pgp_memory_t *outmem; pgp_memory_t *inmem; const int printerrors = 1; if (input == NULL) { (void) fprintf(io->errs, "pgp_encrypt_buf: null memory\n"); return 0; } inmem = pgp_memory_new(); pgp_memory_add(inmem, input, insize); /* set up to read from memory */ pgp_setup_memory_read(io, &parse, inmem, NULL, write_parsed_cb, 0); /* setup for writing decrypted contents to given output file */ pgp_setup_memory_write(&parse->cbinfo.output, &outmem, insize); /* setup keyring and passphrase callback */ parse->cbinfo.cryptinfo.secring = secring; parse->cbinfo.cryptinfo.pubring = pubring; parse->cbinfo.passfp = passfp; parse->cbinfo.cryptinfo.getpassphrase = getpassfunc; parse->cbinfo.sshseckey = (sshkeys) ? &secring->keys[0].key.seckey : NULL; parse->cbinfo.numtries = numtries; /* Set up armour/passphrase options */ if (use_armour) { pgp_reader_push_dearmour(parse); } /* Do it */ pgp_parse(parse, printerrors); /* Unsetup */ if (use_armour) { pgp_reader_pop_dearmour(parse); } /* tidy up */ pgp_teardown_memory_read(parse, inmem); pgp_writer_close(parse->cbinfo.output); pgp_output_delete(parse->cbinfo.output); /* if we didn't get the passphrase, return NULL */ return (parse->cbinfo.gotpass) ? outmem : NULL; }