/** * \ingroup Core_WritePackets * \brief Writes a User Id packet * \param id * \param output * \return 1 if OK, otherwise 0 */ unsigned pgp_write_struct_userid(pgp_output_t *output, const uint8_t *id) { return pgp_write_ptag(output, PGP_PTAG_CT_USER_ID) && pgp_write_length(output, (unsigned)strlen((const char *) id)) && pgp_write(output, id, (unsigned)strlen((const char *) id)); }
/** * \ingroup Core_WritePackets * \brief Writes a Public Key packet * \param key * \param output * \return 1 if OK, otherwise 0 */ static unsigned write_struct_pubkey(pgp_output_t *output, const pgp_pubkey_t *key) { return pgp_write_ptag(output, PGP_PTAG_CT_PUBLIC_KEY) && pgp_write_length(output, 1 + 4 + 1 + pubkey_length(key)) && write_pubkey_body(key, output); }
/** \ingroup Core_WritePackets \brief Write Symmetrically Encrypted packet \param data Data to encrypt \param len Length of data \param output Write settings \return 1 if OK; else 0 \note Hard-coded to use AES256 */ unsigned pgp_write_symm_enc_data(const uint8_t *data, const int len, pgp_output_t * output) { pgp_crypt_t crypt_info; uint8_t *encrypted = (uint8_t *) NULL; size_t encrypted_sz; int done = 0; /* \todo assume AES256 for now */ pgp_crypt_any(&crypt_info, PGP_SA_AES_256); pgp_encrypt_init(&crypt_info); encrypted_sz = (size_t)(len + crypt_info.blocksize + 2); if ((encrypted = calloc(1, encrypted_sz)) == NULL) { (void) fprintf(stderr, "can't allocate %" PRIsize "d\n", encrypted_sz); return 0; } done = (int)pgp_encrypt_se(&crypt_info, encrypted, data, (unsigned)len); if (done != len) { (void) fprintf(stderr, "pgp_write_symm_enc_data: done != len\n"); return 0; } return pgp_write_ptag(output, PGP_PTAG_CT_SE_DATA) && pgp_write_length(output, (unsigned)(1 + encrypted_sz)) && pgp_write(output, data, (unsigned)len); }
unsigned pgp_write_mdc(pgp_output_t *output, const uint8_t *hashed) { /* write it out */ return pgp_write_ptag(output, PGP_PTAG_CT_MDC) && pgp_write_length(output, PGP_SHA1_HASH_SIZE) && pgp_write(output, hashed, PGP_SHA1_HASH_SIZE); }
unsigned pgp_write_ss_header(pgp_output_t *output, unsigned length, pgp_content_enum type) { return pgp_write_length(output, length) && pgp_write_scalar(output, (unsigned)(type - (unsigned)PGP_PTAG_SIG_SUBPKT_BASE), 1); }
/** \ingroup Core_WritePackets \brief Writes Public Key Session Key packet \param info Write settings \param pksk Public Key Session Key to write out \return 1 if OK; else 0 */ unsigned pgp_write_pk_sesskey(pgp_output_t *output, pgp_pk_sesskey_t *pksk) { /* XXX - Flexelint - Pointer parameter 'pksk' (line 1076) could be declared as pointing to const */ if (pksk == NULL) { (void) fprintf(stderr, "pgp_write_pk_sesskey: NULL pksk\n"); return 0; } switch (pksk->alg) { case PGP_PKA_RSA: return pgp_write_ptag(output, PGP_PTAG_CT_PK_SESSION_KEY) && pgp_write_length(output, (unsigned)(1 + 8 + 1 + BN_num_bytes(pksk->params.rsa.encrypted_m) + 2)) && pgp_write_scalar(output, (unsigned)pksk->version, 1) && pgp_write(output, pksk->key_id, 8) && pgp_write_scalar(output, (unsigned)pksk->alg, 1) && pgp_write_mpi(output, pksk->params.rsa.encrypted_m) /* ?? && pgp_write_scalar(output, 0, 2); */ ; case PGP_PKA_DSA: case PGP_PKA_ELGAMAL: return pgp_write_ptag(output, PGP_PTAG_CT_PK_SESSION_KEY) && pgp_write_length(output, (unsigned)(1 + 8 + 1 + BN_num_bytes(pksk->params.elgamal.g_to_k) + 2 + BN_num_bytes(pksk->params.elgamal.encrypted_m) + 2)) && pgp_write_scalar(output, (unsigned)pksk->version, 1) && pgp_write(output, pksk->key_id, 8) && pgp_write_scalar(output, (unsigned)pksk->alg, 1) && pgp_write_mpi(output, pksk->params.elgamal.g_to_k) && pgp_write_mpi(output, pksk->params.elgamal.encrypted_m) /* ?? && pgp_write_scalar(output, 0, 2); */ ; default: (void) fprintf(stderr, "pgp_write_pk_sesskey: bad algorithm\n"); return 0; } }
/** \ingroup Core_WritePackets \brief Write a One Pass Signature packet \param seckey Secret Key to use \param hash_alg Hash Algorithm to use \param sig_type Signature type \param output Write settings \return 1 if OK; else 0 */ unsigned pgp_write_one_pass_sig(pgp_output_t *output, const pgp_seckey_t *seckey, const pgp_hash_alg_t hash_alg, const pgp_sig_type_t sig_type) { uint8_t keyid[PGP_KEY_ID_SIZE]; pgp_keyid(keyid, PGP_KEY_ID_SIZE, &seckey->pubkey, PGP_HASH_SHA1); /* XXX - hardcoded */ return pgp_write_ptag(output, PGP_PTAG_CT_1_PASS_SIG) && pgp_write_length(output, 1 + 1 + 1 + 1 + 8 + 1) && pgp_write_scalar(output, 3, 1) /* version */ && pgp_write_scalar(output, (unsigned)sig_type, 1) && pgp_write_scalar(output, (unsigned)hash_alg, 1) && pgp_write_scalar(output, (unsigned)seckey->pubkey.alg, 1) && pgp_write(output, keyid, 8) && pgp_write_scalar(output, 1, 1); }
/** \ingroup Core_WritePackets \brief Writes Literal Data packet from buffer \param data Buffer to write out \param maxlen Max length of buffer \param type Literal Data Type \param output Write settings \return 1 if OK; else 0 */ unsigned pgp_write_litdata(pgp_output_t *output, const uint8_t *data, const int maxlen, const pgp_litdata_enum type) { /* * RFC4880 does not specify a meaning for filename or date. * It is implementation-dependent. * We will not implement them. */ /* \todo do we need to check text data for <cr><lf> line endings ? */ return pgp_write_ptag(output, PGP_PTAG_CT_LITDATA) && pgp_write_length(output, (unsigned)(1 + 1 + 4 + maxlen)) && pgp_write_scalar(output, (unsigned)type, 1) && pgp_write_scalar(output, 0, 1) && pgp_write_scalar(output, 0, 4) && pgp_write(output, data, (unsigned)maxlen); }
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; }
/** * \ingroup Core_WritePackets * \brief Writes a Secret Key packet. * \param key The secret key * \param passphrase The passphrase * \param pplen Length of passphrase * \param output * \return 1 if OK; else 0 */ unsigned pgp_write_struct_seckey(const pgp_seckey_t *key, const uint8_t *passphrase, const size_t pplen, pgp_output_t *output) { int length = 0; if (key->pubkey.version != 4) { (void) fprintf(stderr, "pgp_write_struct_seckey: public key version\n"); return 0; } /* Ref: RFC4880 Section 5.5.3 */ /* pubkey, excluding MPIs */ length += 1 + 4 + 1 + 1; /* s2k usage */ length += 1; switch (key->s2k_usage) { case PGP_S2KU_NONE: /* nothing to add */ break; case PGP_S2KU_ENCRYPTED_AND_HASHED: /* 254 */ case PGP_S2KU_ENCRYPTED: /* 255 */ /* Ref: RFC4880 Section 3.7 */ length += 1; /* s2k_specifier */ switch (key->s2k_specifier) { case PGP_S2KS_SIMPLE: length += 1; /* hash algorithm */ break; case PGP_S2KS_SALTED: length += 1 + 8; /* hash algorithm + salt */ break; case PGP_S2KS_ITERATED_AND_SALTED: length += 1 + 8 + 1; /* hash algorithm, salt + * count */ break; default: (void) fprintf(stderr, "pgp_write_struct_seckey: s2k spec\n"); return 0; } break; default: (void) fprintf(stderr, "pgp_write_struct_seckey: s2k usage\n"); return 0; } /* IV */ if (key->s2k_usage) { length += pgp_block_size(key->alg); } /* checksum or hash */ switch (key->s2k_usage) { case PGP_S2KU_NONE: case PGP_S2KU_ENCRYPTED: length += 2; break; case PGP_S2KU_ENCRYPTED_AND_HASHED: length += PGP_CHECKHASH_SIZE; break; default: (void) fprintf(stderr, "pgp_write_struct_seckey: s2k cksum usage\n"); return 0; } /* secret key and public key MPIs */ length += (unsigned)seckey_length(key); return pgp_write_ptag(output, PGP_PTAG_CT_SECRET_KEY) && /* pgp_write_length(output,1+4+1+1+seckey_length(key)+2) && */ pgp_write_length(output, (unsigned)length) && write_seckey_body(key, passphrase, pplen, output); }
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; }
unsigned pgp_writez(pgp_output_t *out, const uint8_t *data, const unsigned len) { compress_t *zip; size_t sz_in; size_t sz_out; int ret; int r = 0; /* compress the data */ const int level = Z_DEFAULT_COMPRESSION; /* \todo allow varying * levels */ if ((zip = calloc(1, sizeof(*zip))) == NULL) { (void) fprintf(stderr, "pgp_writez: bad alloc\n"); return 0; } zip->stream.zalloc = Z_NULL; zip->stream.zfree = Z_NULL; zip->stream.opaque = NULL; /* all other fields set to zero by use of calloc */ /* LINTED */ /* this is a lint problem in zlib.h header */ if ((int)deflateInit(&zip->stream, level) != Z_OK) { (void) fprintf(stderr, "pgp_writez: can't initialise\n"); return 0; } /* do necessary transformation */ /* copy input to maintain const'ness of src */ if (zip->src != NULL || zip->dst != NULL) { (void) fprintf(stderr, "pgp_writez: non-null streams\n"); return 0; } sz_in = len * sizeof(uint8_t); sz_out = ((101 * sz_in) / 100) + 12; /* from zlib webpage */ if ((zip->src = calloc(1, sz_in)) == NULL) { free(zip); (void) fprintf(stderr, "pgp_writez: bad alloc2\n"); return 0; } if ((zip->dst = calloc(1, sz_out)) == NULL) { free(zip->src); free(zip); (void) fprintf(stderr, "pgp_writez: bad alloc3\n"); return 0; } (void) memcpy(zip->src, data, len); /* setup stream */ zip->stream.next_in = zip->src; zip->stream.avail_in = (unsigned)sz_in; zip->stream.total_in = 0; zip->stream.next_out = zip->dst; zip->stream.avail_out = (unsigned)sz_out; zip->stream.total_out = 0; do { r = deflate(&zip->stream, Z_FINISH); } while (r != Z_STREAM_END); /* write it out */ ret = pgp_write_ptag(out, PGP_PTAG_CT_COMPRESSED) && pgp_write_length(out, (unsigned)(zip->stream.total_out + 1))&& pgp_write_scalar(out, PGP_C_ZLIB, 1) && pgp_write(out, zip->dst, (unsigned)zip->stream.total_out); free(zip->src); free(zip->dst); free(zip); return ret; }