/** \ingroup Core_Create \brief implementation of EME-PKCS1-v1_5-ENCODE, as defined in OpenPGP RFC \param M \param mLen \param pubkey \param EM \return 1 if OK; else 0 */ unsigned encode_m_buf(const uint8_t *M, size_t mLen, const pgp_pubkey_t * pubkey, uint8_t *EM) { unsigned k; unsigned i; /* implementation of EME-PKCS1-v1_5-ENCODE, as defined in OpenPGP RFC */ switch (pubkey->alg) { case PGP_PKA_RSA: k = (unsigned)BN_num_bytes(pubkey->key.rsa.n); if (mLen > k - 11) { (void) fprintf(stderr, "encode_m_buf: message too long\n"); return 0; } break; case PGP_PKA_DSA: case PGP_PKA_ELGAMAL: k = (unsigned)BN_num_bytes(pubkey->key.elgamal.p); if (mLen > k - 11) { (void) fprintf(stderr, "encode_m_buf: message too long\n"); return 0; } break; default: (void) fprintf(stderr, "encode_m_buf: pubkey algorithm\n"); return 0; } /* these two bytes defined by RFC */ EM[0] = 0x00; EM[1] = 0x02; /* add non-zero random bytes of length k - mLen -3 */ for (i = 2; i < (k - mLen) - 1; ++i) { do { pgp_random(EM + i, 1); } while (EM[i] == 0); } if (i < 8 + 2) { (void) fprintf(stderr, "encode_m_buf: bad i len\n"); return 0; } EM[i++] = 0; (void) memcpy(EM + i, M, mLen); if (pgp_get_debug_level(__FILE__)) { hexdump(stderr, "Encoded Message:", EM, mLen); } return 1; }
/** \ingroup Core_Create \brief Creates an pgp_pk_sesskey_t struct from keydata \param key Keydata to use \return pgp_pk_sesskey_t struct \note It is the caller's responsiblity to free the returned pointer \note Currently hard-coded to use CAST5 \note Currently hard-coded to use RSA */ pgp_pk_sesskey_t * pgp_create_pk_sesskey(const pgp_key_t *key, const char *ciphername) { /* * Creates a random session key and encrypts it for the given key * * Encryption used is PK, * can be any, we're hardcoding RSA for now */ const pgp_pubkey_t *pubkey; pgp_pk_sesskey_t *sesskey; pgp_symm_alg_t cipher; const uint8_t *id; pgp_crypt_t cipherinfo; uint8_t *unencoded_m_buf; uint8_t *encoded_m_buf; size_t sz_encoded_m_buf; if (memcmp(key->encid, "\0\0\0\0\0\0\0\0", 8) == 0) { pubkey = pgp_get_pubkey(key); id = key->sigid; } else { pubkey = &key->enckey; id = key->encid; } /* allocate unencoded_m_buf here */ (void) memset(&cipherinfo, 0x0, sizeof(cipherinfo)); pgp_crypt_any(&cipherinfo, cipher = pgp_str_to_cipher((ciphername) ? ciphername : "cast5")); unencoded_m_buf = calloc(1, cipherinfo.keysize + 1 + 2); if (unencoded_m_buf == NULL) { (void) fprintf(stderr, "pgp_create_pk_sesskey: can't allocate\n"); return NULL; } switch(pubkey->alg) { case PGP_PKA_RSA: sz_encoded_m_buf = BN_num_bytes(pubkey->key.rsa.n); break; case PGP_PKA_DSA: case PGP_PKA_ELGAMAL: sz_encoded_m_buf = BN_num_bytes(pubkey->key.elgamal.p); break; default: sz_encoded_m_buf = 0; break; } if ((encoded_m_buf = calloc(1, sz_encoded_m_buf)) == NULL) { (void) fprintf(stderr, "pgp_create_pk_sesskey: can't allocate\n"); free(unencoded_m_buf); return NULL; } if ((sesskey = calloc(1, sizeof(*sesskey))) == NULL) { (void) fprintf(stderr, "pgp_create_pk_sesskey: can't allocate\n"); free(unencoded_m_buf); free(encoded_m_buf); return NULL; } if (key->type != PGP_PTAG_CT_PUBLIC_KEY) { (void) fprintf(stderr, "pgp_create_pk_sesskey: bad type\n"); free(unencoded_m_buf); free(encoded_m_buf); free(sesskey); return NULL; } sesskey->version = PGP_PKSK_V3; (void) memcpy(sesskey->key_id, id, sizeof(sesskey->key_id)); if (pgp_get_debug_level(__FILE__)) { hexdump(stderr, "Encrypting for keyid", id, sizeof(sesskey->key_id)); } switch (pubkey->alg) { case PGP_PKA_RSA: case PGP_PKA_DSA: case PGP_PKA_ELGAMAL: break; default: (void) fprintf(stderr, "pgp_create_pk_sesskey: bad pubkey algorithm\n"); free(unencoded_m_buf); free(encoded_m_buf); free(sesskey); return NULL; } sesskey->alg = pubkey->alg; sesskey->symm_alg = cipher; pgp_random(sesskey->key, cipherinfo.keysize); if (pgp_get_debug_level(__FILE__)) { hexdump(stderr, "sesskey created", sesskey->key, cipherinfo.keysize + 1 + 2); } if (create_unencoded_m_buf(sesskey, &cipherinfo, &unencoded_m_buf[0]) == 0) { free(unencoded_m_buf); free(encoded_m_buf); free(sesskey); return NULL; } if (pgp_get_debug_level(__FILE__)) { hexdump(stderr, "uuencoded m buf", unencoded_m_buf, cipherinfo.keysize + 1 + 2); } encode_m_buf(unencoded_m_buf, cipherinfo.keysize + 1 + 2, pubkey, encoded_m_buf); /* and encrypt it */ switch (key->key.pubkey.alg) { case PGP_PKA_RSA: if (!pgp_rsa_encrypt_mpi(encoded_m_buf, sz_encoded_m_buf, pubkey, &sesskey->params)) { free(unencoded_m_buf); free(encoded_m_buf); free(sesskey); return NULL; } break; case PGP_PKA_DSA: case PGP_PKA_ELGAMAL: if (!pgp_elgamal_encrypt_mpi(encoded_m_buf, sz_encoded_m_buf, pubkey, &sesskey->params)) { free(unencoded_m_buf); free(encoded_m_buf); free(sesskey); return NULL; } break; default: /* will not get here - for lint only */ break; } free(unencoded_m_buf); free(encoded_m_buf); return sesskey; }
/* * Note that we support v3 keys here because they're needed for * verification. */ static unsigned write_seckey_body(const pgp_seckey_t *key, const uint8_t *passphrase, const size_t pplen, pgp_output_t *output) { /* RFC4880 Section 5.5.3 Secret-Key Packet Formats */ pgp_crypt_t crypted; pgp_hash_t hash; unsigned done = 0; unsigned i = 0; uint8_t *hashed; uint8_t sesskey[CAST_KEY_LENGTH]; if (!write_pubkey_body(&key->pubkey, output)) { return 0; } if (key->s2k_usage != PGP_S2KU_ENCRYPTED_AND_HASHED) { (void) fprintf(stderr, "write_seckey_body: s2k usage\n"); return 0; } if (!pgp_write_scalar(output, (unsigned)key->s2k_usage, 1)) { return 0; } if (key->alg != PGP_SA_CAST5) { (void) fprintf(stderr, "write_seckey_body: algorithm\n"); return 0; } if (!pgp_write_scalar(output, (unsigned)key->alg, 1)) { return 0; } if (key->s2k_specifier != PGP_S2KS_SIMPLE && key->s2k_specifier != PGP_S2KS_SALTED) { /* = 1 \todo could also be iterated-and-salted */ (void) fprintf(stderr, "write_seckey_body: s2k spec\n"); return 0; } if (!pgp_write_scalar(output, (unsigned)key->s2k_specifier, 1)) { return 0; } if (!pgp_write_scalar(output, (unsigned)key->hash_alg, 1)) { return 0; } switch (key->s2k_specifier) { case PGP_S2KS_SIMPLE: /* nothing more to do */ break; case PGP_S2KS_SALTED: /* 8-octet salt value */ pgp_random(__UNCONST(&key->salt[0]), PGP_SALT_SIZE); if (!pgp_write(output, key->salt, PGP_SALT_SIZE)) { return 0; } break; /* * \todo case PGP_S2KS_ITERATED_AND_SALTED: // 8-octet salt * value // 1-octet count break; */ default: (void) fprintf(stderr, "invalid/unsupported s2k specifier %d\n", key->s2k_specifier); return 0; } if (!pgp_write(output, &key->iv[0], pgp_block_size(key->alg))) { return 0; } /* * create the session key for encrypting the algorithm-specific * fields */ switch (key->s2k_specifier) { case PGP_S2KS_SIMPLE: case PGP_S2KS_SALTED: /* RFC4880: section 3.7.1.1 and 3.7.1.2 */ for (done = 0, i = 0; done < CAST_KEY_LENGTH; i++) { unsigned hashsize; unsigned j; unsigned needed; unsigned size; uint8_t zero = 0; /* Hard-coded SHA1 for session key */ pgp_hash_any(&hash, PGP_HASH_SHA1); hashsize = pgp_hash_size(key->hash_alg); needed = CAST_KEY_LENGTH - done; size = MIN(needed, hashsize); if ((hashed = calloc(1, hashsize)) == NULL) { (void) fprintf(stderr, "write_seckey_body: bad alloc\n"); return 0; } if (!hash.init(&hash)) { (void) fprintf(stderr, "write_seckey_body: bad alloc\n"); return 0; } /* preload if iterating */ for (j = 0; j < i; j++) { /* * Coverity shows a DEADCODE error on this * line. This is expected since the hardcoded * use of SHA1 and CAST5 means that it will * not used. This will change however when * other algorithms are supported. */ hash.add(&hash, &zero, 1); } if (key->s2k_specifier == PGP_S2KS_SALTED) { hash.add(&hash, key->salt, PGP_SALT_SIZE); } hash.add(&hash, passphrase, (unsigned)pplen); hash.finish(&hash, hashed); /* * if more in hash than is needed by session key, use * the leftmost octets */ (void) memcpy(&sesskey[i * hashsize], hashed, (unsigned)size); done += (unsigned)size; if (done > CAST_KEY_LENGTH) { (void) fprintf(stderr, "write_seckey_body: short add\n"); return 0; } } break; /* * \todo case PGP_S2KS_ITERATED_AND_SALTED: * 8-octet salt * value * 1-octet count break; */ default: (void) fprintf(stderr, "invalid/unsupported s2k specifier %d\n", key->s2k_specifier); return 0; } /* use this session key to encrypt */ pgp_crypt_any(&crypted, key->alg); crypted.set_iv(&crypted, key->iv); crypted.set_crypt_key(&crypted, sesskey); pgp_encrypt_init(&crypted); if (pgp_get_debug_level(__FILE__)) { hexdump(stderr, "writing: iv=", key->iv, pgp_block_size(key->alg)); hexdump(stderr, "key= ", sesskey, CAST_KEY_LENGTH); (void) fprintf(stderr, "\nturning encryption on...\n"); } pgp_push_enc_crypt(output, &crypted); switch (key->pubkey.alg) { case PGP_PKA_RSA: case PGP_PKA_RSA_ENCRYPT_ONLY: case PGP_PKA_RSA_SIGN_ONLY: if (!pgp_write_mpi(output, key->key.rsa.d) || !pgp_write_mpi(output, key->key.rsa.p) || !pgp_write_mpi(output, key->key.rsa.q) || !pgp_write_mpi(output, key->key.rsa.u)) { if (pgp_get_debug_level(__FILE__)) { (void) fprintf(stderr, "4 x mpi not written - problem\n"); } return 0; } break; case PGP_PKA_DSA: return pgp_write_mpi(output, key->key.dsa.x); case PGP_PKA_ELGAMAL: return pgp_write_mpi(output, key->key.elgamal.x); default: return 0; } if (!pgp_write(output, key->checkhash, PGP_CHECKHASH_SIZE)) { return 0; } pgp_writer_pop(output); return 1; }
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; }