static int generate_subkey( cdk_keygen_ctx_t hd ) { gcry_sexp_t s_params = NULL, s_key; size_t n = hd->key[1].len; int rc; if( !hd ) return CDK_Inv_Value; if( is_DSA( hd->key[1].algo) ) rc = gcry_sexp_build( &s_params, NULL, "(genkey(dsa(nbits %d)))", n ); else if( is_ELG( hd->key[1].algo) ) rc = gcry_sexp_build( &s_params, NULL, "(genkey(elg(nbits %d)))", n ); else if( is_RSA( hd->key[1].algo) ) rc = gcry_sexp_build( &s_params, NULL, "(genkey(rsa(nbits %d)))", n ); else rc = CDK_Inv_Algo; if( !rc ) rc = gcry_pk_genkey( &s_key, s_params ); gcry_sexp_release( s_params ); if( !rc ) { if( is_DSA( hd->key[1].algo) ) rc = read_dsa_key( s_key, hd->key[1].resarr ); else if( is_ELG( hd->key[1].algo) ) rc = read_elg_key( s_key, hd->key[1].resarr ); else if( is_RSA( hd->key[1].algo) ) rc = read_rsa_key( s_key, hd->key[1].resarr ); } hd->key[1].n = cdk_pk_get_npkey( hd->key[1].algo ); gcry_sexp_release( s_key ); return rc; }
/** * cdk_pk_get_keyid: * @pk: the public key * @keyid: buffer to store the key ID * * Calculate the key ID of the given public key. **/ u32 cdk_pk_get_keyid (cdk_pubkey_t pk, u32 *keyid) { u32 lowbits = 0; byte buf[24]; if (pk && (!pk->keyid[0] || !pk->keyid[1])) { if (pk->version < 4 && is_RSA (pk->pubkey_algo)) { byte p[MAX_MPI_BYTES]; size_t n; gcry_mpi_print (GCRYMPI_FMT_USG, p, MAX_MPI_BYTES, &n, pk->mpi[0]); pk->keyid[0] = p[n-8] << 24 | p[n-7] << 16 | p[n-6] << 8 | p[n-5]; pk->keyid[1] = p[n-4] << 24 | p[n-3] << 16 | p[n-2] << 8 | p[n-1]; } else if (pk->version == 4) { cdk_pk_get_fingerprint (pk, buf); pk->keyid[0] = _cdk_buftou32 (buf + 12); pk->keyid[1] = _cdk_buftou32 (buf + 16); } } lowbits = pk ? pk->keyid[1] : 0; if (keyid && pk) { keyid[0] = pk->keyid[0]; keyid[1] = pk->keyid[1]; } return lowbits; }
/** * cdk_pk_get_fingerprint: * @pk: the public key * @fpr: the buffer to hold the fingerprint * * Return the fingerprint of the given public key. * The buffer must be at least 20 octets. * This function should be considered deprecated and * the new cdk_pk_to_fingerprint() should be used whenever * possible to avoid overflows. **/ cdk_error_t cdk_pk_get_fingerprint (cdk_pubkey_t pk, byte * fpr) { digest_hd_st hd; int md_algo; int dlen = 0; int err; if (!pk || !fpr) return CDK_Inv_Value; if (pk->version < 4 && is_RSA (pk->pubkey_algo)) md_algo = GNUTLS_DIG_MD5; /* special */ else md_algo = GNUTLS_DIG_SHA1; dlen = _gnutls_hash_get_algo_len (md_algo); err = _gnutls_hash_init (&hd, md_algo); if (err < 0) { gnutls_assert (); return map_gnutls_error (err); } _cdk_hash_pubkey (pk, &hd, 1); _gnutls_hash_deinit (&hd, fpr); if (dlen == 16) memset (fpr + 16, 0, 4); return 0; }
/* This function gets the signature parameters and encodes * them into a way for _gnutls_pk_verify to use. */ static cdk_error_t sig_to_datum (gnutls_datum_t * r_sig, cdk_pkt_signature_t sig) { int err; cdk_error_t rc; if (!r_sig || !sig) return CDK_Inv_Value; rc = 0; if (is_RSA (sig->pubkey_algo)) { err = _gnutls_mpi_dprint (sig->mpi[0], r_sig); if (err < 0) rc = map_gnutls_error (err); } else if (is_DSA (sig->pubkey_algo)) { err = _gnutls_encode_ber_rs (r_sig, sig->mpi[0], sig->mpi[1]); if (err < 0) rc = map_gnutls_error (err); } else rc = CDK_Inv_Algo; return rc; }
/** * gnutls_openpgp_privkey_get_subkey_pk_algorithm: * @key: is an OpenPGP key * @idx: is the subkey index * @bits: if bits is non null it will hold the size of the parameters' in bits * * This function will return the public key algorithm of a subkey of an OpenPGP * certificate. * * If bits is non null, it should have enough size to hold the parameters * size in bits. For RSA the bits returned is the modulus. * For DSA the bits returned are of the public exponent. * * Returns: a member of the #gnutls_pk_algorithm_t enumeration on * success, or a negative error code on error. * * Since: 2.4.0 **/ gnutls_pk_algorithm_t gnutls_openpgp_privkey_get_subkey_pk_algorithm(gnutls_openpgp_privkey_t key, unsigned int idx, unsigned int *bits) { cdk_packet_t pkt; int algo; if (!key) { gnutls_assert(); return GNUTLS_PK_UNKNOWN; } if (idx == GNUTLS_OPENPGP_MASTER_KEYID_IDX) return gnutls_openpgp_privkey_get_pk_algorithm(key, bits); pkt = _get_secret_subkey(key, idx); algo = 0; if (pkt) { if (bits) *bits = cdk_pk_get_nbits(pkt->pkt.secret_key->pk); algo = pkt->pkt.secret_key->pubkey_algo; if (is_RSA(algo)) algo = GNUTLS_PK_RSA; else if (is_DSA(algo)) algo = GNUTLS_PK_DSA; else algo = GNUTLS_E_UNKNOWN_PK_ALGORITHM; } return algo; }
/** * gnutls_openpgp_privkey_get_fingerprint: * @key: the raw data that contains the OpenPGP secret key. * @fpr: the buffer to save the fingerprint, must hold at least 20 bytes. * @fprlen: the integer to save the length of the fingerprint. * * Get the fingerprint of the OpenPGP key. Depends on the * algorithm, the fingerprint can be 16 or 20 bytes. * * Returns: On success, 0 is returned, or an error code. * * Since: 2.4.0 **/ int gnutls_openpgp_privkey_get_fingerprint(gnutls_openpgp_privkey_t key, void *fpr, size_t * fprlen) { cdk_packet_t pkt; cdk_pkt_pubkey_t pk = NULL; if (!fpr || !fprlen) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } *fprlen = 0; pkt = cdk_kbnode_find_packet(key->knode, CDK_PKT_SECRET_KEY); if (!pkt) { gnutls_assert(); return GNUTLS_E_OPENPGP_GETKEY_FAILED; } pk = pkt->pkt.secret_key->pk; *fprlen = 20; if (is_RSA(pk->pubkey_algo) && pk->version < 4) *fprlen = 16; cdk_pk_get_fingerprint(pk, fpr); return 0; }
static cdk_error_t pubenc_to_sexp (gcry_sexp_t * r_sexp, cdk_pkt_pubkey_enc_t enc) { gcry_sexp_t sexp = NULL; gcry_error_t err; const char *fmt; if (!r_sexp || !enc) return CDK_Inv_Value; *r_sexp = NULL; if (is_RSA (enc->pubkey_algo)) { fmt = "(enc-val(openpgp-rsa((a%m))))"; err = gcry_sexp_build (&sexp, NULL, fmt, enc->mpi[0]); } else if (is_ELG (enc->pubkey_algo)) { fmt = "(enc-val(openpgp-elg((a%m)(b%m))))"; err = gcry_sexp_build (&sexp, NULL, fmt, enc->mpi[0], enc->mpi[1]); } else return CDK_Inv_Algo; if (err) return map_gcry_error (err); *r_sexp = sexp; return 0; }
/** * gnutls_openpgp_privkey_get_subkey_fingerprint: * @key: the raw data that contains the OpenPGP secret key. * @idx: the subkey index * @fpr: the buffer to save the fingerprint, must hold at least 20 bytes. * @fprlen: the integer to save the length of the fingerprint. * * Get the fingerprint of an OpenPGP subkey. Depends on the * algorithm, the fingerprint can be 16 or 20 bytes. * * Returns: On success, 0 is returned, or an error code. * * Since: 2.4.0 **/ int gnutls_openpgp_privkey_get_subkey_fingerprint(gnutls_openpgp_privkey_t key, unsigned int idx, void *fpr, size_t * fprlen) { cdk_packet_t pkt; cdk_pkt_pubkey_t pk = NULL; if (!fpr || !fprlen) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } if (idx == GNUTLS_OPENPGP_MASTER_KEYID_IDX) return gnutls_openpgp_privkey_get_fingerprint(key, fpr, fprlen); *fprlen = 0; pkt = _get_secret_subkey(key, idx); if (!pkt) return GNUTLS_E_OPENPGP_GETKEY_FAILED; pk = pkt->pkt.secret_key->pk; *fprlen = 20; if (is_RSA(pk->pubkey_algo) && pk->version < 4) *fprlen = 16; cdk_pk_get_fingerprint(pk, fpr); return 0; }
static cdk_error_t write_v3_sig (cdk_stream_t out, cdk_pkt_signature_t sig, int nsig) { size_t size; cdk_error_t rc; size = 19 + calc_mpisize (sig->mpi, nsig); if (is_RSA (sig->pubkey_algo)) rc = pkt_write_head2 (out, size, CDK_PKT_SIGNATURE); else rc = pkt_write_head (out, 1, size, CDK_PKT_SIGNATURE); if (!rc) rc = stream_putc (out, sig->version); if (!rc) rc = stream_putc (out, 5); if (!rc) rc = stream_putc (out, sig->sig_class); if (!rc) rc = write_32 (out, sig->timestamp); if (!rc) rc = write_32 (out, sig->keyid[0]); if (!rc) rc = write_32 (out, sig->keyid[1]); if (!rc) rc = stream_putc (out, _cdk_pub_algo_to_pgp (sig->pubkey_algo)); if (!rc) rc = stream_putc (out, _gnutls_hash_algo_to_pgp (sig->digest_algo)); if (!rc) rc = stream_putc (out, sig->digest_start[0]); if (!rc) rc = stream_putc (out, sig->digest_start[1]); if (!rc) rc = write_mpibuf (out, sig->mpi, nsig); return rc; }
static cdk_error_t sig_to_sexp (gcry_sexp_t *r_sig_sexp, cdk_pkt_signature_t sig) { gcry_error_t err; cdk_error_t rc; const char *fmt; if (!r_sig_sexp || !sig) return CDK_Inv_Value; if (is_ELG (sig->pubkey_algo)) return CDK_Not_Implemented; rc = 0; if (is_RSA (sig->pubkey_algo)) { fmt = "(sig-val(openpgp-rsa(s%m)))"; err = gcry_sexp_build (r_sig_sexp, NULL, fmt, sig->mpi[0]); if (err) rc = map_gcry_error (err); } else if (is_DSA (sig->pubkey_algo)) { fmt = "(sig-val(openpgp-dsa(r%m)(s%m)))"; err = gcry_sexp_build (r_sig_sexp, NULL, fmt, sig->mpi[0], sig->mpi[1]); if (err) rc = map_gcry_error (err); } else rc = CDK_Inv_Algo; return rc; }
static cdk_error_t sexp_to_sig (cdk_pkt_signature_t sig, gcry_sexp_t sexp) { if (!sig || !sexp) return CDK_Inv_Value; /* ElGamal signatures are not supported any longer. */ if (is_ELG (sig->pubkey_algo)) { _cdk_log_debug ("sexp_to_sig: unsupported signature type (ElGamal)\n"); return CDK_Not_Implemented; } if (is_RSA (sig->pubkey_algo)) return sexp_to_mpi (sexp, "s", &sig->mpi[0]); else if (is_DSA (sig->pubkey_algo)) { cdk_error_t rc; rc = sexp_to_mpi (sexp, "r", &sig->mpi[0]); if (!rc) rc = sexp_to_mpi (sexp, "s", &sig->mpi[1]); return rc; } return CDK_Inv_Algo; }
/** * gnutls_openpgp_key_get_pk_algorithm - This function returns the key's PublicKey algorithm * @key: is an OpenPGP key * @bits: if bits is non null it will hold the size of the parameters' in bits * * This function will return the public key algorithm of an OpenPGP * certificate. * * If bits is non null, it should have enough size to hold the parameters * size in bits. For RSA the bits returned is the modulus. * For DSA the bits returned are of the public exponent. * * Returns a member of the GNUTLS_PKAlgorithm enumeration on success, * or a negative value on error. * **/ int gnutls_openpgp_key_get_pk_algorithm (gnutls_openpgp_key_t key, unsigned int *bits) { cdk_packet_t pkt; int algo = 0; if (!key) return GNUTLS_E_INVALID_REQUEST; pkt = cdk_kbnode_find_packet (key->knode, CDK_PKT_PUBLIC_KEY); if (pkt && pkt->pkttype == CDK_PKT_PUBLIC_KEY) { if (bits) *bits = cdk_pk_get_nbits (pkt->pkt.public_key); algo = pkt->pkt.public_key->pubkey_algo; if (is_RSA (algo)) algo = GNUTLS_PK_RSA; else if (is_DSA (algo)) algo = GNUTLS_PK_DSA; else algo = GNUTLS_E_UNKNOWN_PK_ALGORITHM; } return algo; }
/** * cdk_pk_get_fingerprint: * @pk: the public key * @fpr: the buffer to hold the fingerprint * * Return the fingerprint of the given public key. * The buffer must be at least 20 octets. * This function should be considered deprecated and * the new cdk_pk_to_fingerprint() should be used whenever * possible to avoid overflows. **/ cdk_error_t cdk_pk_get_fingerprint (cdk_pubkey_t pk, byte *fpr) { gcry_md_hd_t hd; int md_algo; int dlen = 0; gcry_error_t err; if (!pk || !fpr) return CDK_Inv_Value; if (pk->version < 4 && is_RSA (pk->pubkey_algo)) md_algo = GCRY_MD_MD5; /* special */ else md_algo = GCRY_MD_SHA1; dlen = gcry_md_get_algo_dlen (md_algo); err = gcry_md_open (&hd, md_algo, 0); if (err) return map_gcry_error (err); _cdk_hash_pubkey (pk, hd, 1); gcry_md_final (hd); memcpy (fpr, gcry_md_read (hd, md_algo), dlen); gcry_md_close (hd); if (dlen == 16) memset (fpr + 16, 0, 4); return 0; }
/** * cdk_keygen_start: kick off the key generation * @hd: the keygen object * **/ cdk_error_t cdk_keygen_start( cdk_keygen_ctx_t hd ) { gcry_sexp_t s_params = NULL, s_key = NULL; size_t n; int rc = 0; if( !hd || !hd->user_id ) return CDK_Inv_Value; if( is_ELG( hd->key[0].algo ) ) return CDK_Inv_Mode; if( !hd->key[0].len ) hd->key[0].len = 1024; n = hd->key[0].len; if( !hd->sym_prefs ) cdk_keygen_set_prefs( hd, CDK_PREFTYPE_SYM, NULL, 0 ); if( !hd->hash_prefs ) cdk_keygen_set_prefs( hd, CDK_PREFTYPE_HASH, NULL, 0 ); if( !hd->zip_prefs ) cdk_keygen_set_prefs( hd, CDK_PREFTYPE_ZIP, NULL, 0 ); if( is_DSA( hd->key[0].algo ) ) rc = gcry_sexp_build( &s_params, NULL, "(genkey(dsa(nbits %d)))", n ); else if( is_RSA( hd->key[0].algo ) ) rc = gcry_sexp_build( &s_params, NULL, "(genkey(rsa(nbits %d)))", n ); else rc = CDK_Inv_Algo; if( !rc ) rc = gcry_pk_genkey( &s_key, s_params ); gcry_sexp_release( s_params ); if( !rc ) { if( is_DSA( hd->key[0].algo ) ) rc = read_dsa_key( s_key, hd->key[0].resarr ); else if( is_RSA( hd->key[0].algo ) ) rc = read_rsa_key( s_key, hd->key[0].resarr ); hd->key[0].n = cdk_pk_get_npkey( hd->key[0].algo ); } gcry_sexp_release( s_key ); if( !rc ) { if( hd->key[1].algo && hd->key[1].len ) rc = generate_subkey( hd ); } return rc; }
/** * cdk_pk_get_nbits: * @algo: the public key algorithm * * Return the number of MPIs a signature consists of. **/ int cdk_pk_get_nsig(int algo) { if (is_RSA(algo)) return 1; else if (is_DSA(algo)) return 2; else return 0; }
/** * cdk_pk_get_nenc: * @algo: the public key algorithm * * Return the number of MPI's the encrypted data consists of. **/ int cdk_pk_get_nenc(int algo) { if (is_RSA(algo)) return 1; else if (is_ELG(algo)) return 2; else return 0; }
/**************** * Return the number of encryption material numbers */ int pubkey_get_nenc( int algo ) { int i; do { for(i=0; pubkey_table[i].name; i++ ) if( pubkey_table[i].algo == algo ) return pubkey_table[i].nenc; } while( load_pubkey_modules() ); if( is_RSA(algo) ) /* special hack, so that we are able to */ return 1; /* see the RSA keyids */ return 0; }
/** * cdk_pk_get_npkey: * @algo: The public key algorithm. * * Return the number of multiprecison integer forming an public * key with the given algorithm. */ int cdk_pk_get_npkey(int algo) { if (is_RSA(algo)) return RSA_PUBLIC_PARAMS; else if (is_DSA(algo)) return DSA_PUBLIC_PARAMS; else if (is_ELG(algo)) return 3; else { gnutls_assert(); return 0; } }
/**************** * Get the number of nbits from the public key */ unsigned pubkey_nbits( int algo, MPI *pkey ) { int i; do { for(i=0; pubkey_table[i].name; i++ ) if( pubkey_table[i].algo == algo ) return (*pubkey_table[i].get_nbits)( algo, pkey ); } while( load_pubkey_modules() ); if( is_RSA(algo) ) /* we always wanna see the length of a key :-) */ return mpi_get_nbits( pkey[0] ); return 0; }
int _gnutls_openpgp_get_algo(int cdk_algo) { int algo; if (is_RSA(cdk_algo)) algo = GNUTLS_PK_RSA; else if (is_DSA(cdk_algo)) algo = GNUTLS_PK_DSA; else { _gnutls_debug_log("Unknown OpenPGP algorithm %d\n", cdk_algo); algo = GNUTLS_PK_UNKNOWN; } return algo; }
static cdk_error_t sexp_to_pubenc (cdk_pkt_pubkey_enc_t enc, gcry_sexp_t sexp) { if (!sexp || !enc) return CDK_Inv_Value; if (is_RSA (enc->pubkey_algo)) return sexp_to_mpi (sexp, "a", &enc->mpi[0]); else if (is_ELG (enc->pubkey_algo)) { cdk_error_t rc = sexp_to_mpi (sexp, "a", &enc->mpi[0]); if (!rc) rc = sexp_to_mpi (sexp, "b", &enc->mpi[1]); return rc; } return CDK_Inv_Algo; }
/** * cdk_pk_get_nskey: * @algo: the public key algorithm * * Return the number of multiprecision integers forming an * secret key with the given algorithm. **/ int cdk_pk_get_nskey(int algo) { int ret; if (is_RSA(algo)) ret = RSA_PRIVATE_PARAMS - 2; /* we don't have exp1 and exp2 */ else if (is_DSA(algo)) ret = DSA_PRIVATE_PARAMS; else if (is_ELG(algo)) ret = 4; else { gnutls_assert(); return 0; } ret -= cdk_pk_get_npkey(algo); return ret; }
static int xml_add_key_mpi (gnutls_string * xmlkey, cdk_pkt_pubkey_t pk) { const char *s = " <KEY ENCODING=\"HEX\"/>\n"; int rc = 0; if (!xmlkey || !pk) { gnutls_assert (); return GNUTLS_E_INVALID_REQUEST; } _gnutls_string_append_str (xmlkey, s); if (is_RSA (pk->pubkey_algo)) { rc = xml_add_mpi (xmlkey, pk, 0, "RSA-N"); if (!rc) rc = xml_add_mpi (xmlkey, pk, 1, "RSA-E"); } else if (is_DSA (pk->pubkey_algo)) { rc = xml_add_mpi (xmlkey, pk, 0, "DSA-P"); if (!rc) rc = xml_add_mpi (xmlkey, pk, 1, "DSA-Q"); if (!rc) rc = xml_add_mpi (xmlkey, pk, 2, "DSA-G"); if (!rc) rc = xml_add_mpi (xmlkey, pk, 3, "DSA-Y"); } else if (is_ELG (pk->pubkey_algo)) { rc = xml_add_mpi (xmlkey, pk, 0, "ELG-P"); if (!rc) rc = xml_add_mpi (xmlkey, pk, 1, "ELG-G"); if (!rc) rc = xml_add_mpi (xmlkey, pk, 2, "ELG-Y"); } else return GNUTLS_E_UNWANTED_ALGORITHM; return 0; }
/* Convert the given secret key into a gcrypt SEXP object. */ static int seckey_to_sexp (gcry_sexp_t *r_skey, cdk_seckey_t sk) { gcry_sexp_t sexp = NULL; gcry_mpi_t *mpk = NULL, *msk = NULL; gcry_error_t err; cdk_pubkey_t pk; const char *fmt; if (!r_skey || !sk || !sk->pk) return CDK_Inv_Value; pk = sk->pk; mpk = pk->mpi; msk = sk->mpi; *r_skey = NULL; if (is_RSA (sk->pubkey_algo)) { fmt = "(private-key(openpgp-rsa(n%m)(e%m)(d%m)(p%m)(q%m)(u%m)))"; err = gcry_sexp_build (&sexp, NULL, fmt, mpk[0], mpk[1], msk[0], msk[1], msk[2], msk[3]); } else if (is_ELG (sk->pubkey_algo)) { fmt = "(private-key(openpgp-elg(p%m)(g%m)(y%m)(x%m)))"; err = gcry_sexp_build (&sexp, NULL, fmt, mpk[0], mpk[1], mpk[2], msk[0]); } else if (is_DSA (sk->pubkey_algo)) { fmt = "(private-key(openpgp-dsa(p%m)(q%m)(g%m)(y%m)(x%m)))"; err = gcry_sexp_build (&sexp, NULL, fmt, mpk[0], mpk[1], mpk[2], mpk[3], msk[0]); } else return CDK_Inv_Algo; if (err) return map_gcry_error (err); *r_skey = sexp; return 0; }
/** * cdk_pk_get_keyid: * @pk: the public key * @keyid: buffer to store the key ID * * Calculate the key ID of the given public key. **/ u32 cdk_pk_get_keyid(cdk_pubkey_t pk, u32 * keyid) { u32 lowbits = 0; byte buf[24]; int rc; if (pk && (!pk->keyid[0] || !pk->keyid[1])) { if (pk->version < 4 && is_RSA(pk->pubkey_algo)) { byte p[MAX_MPI_BYTES]; size_t n; n = MAX_MPI_BYTES; rc = _gnutls_mpi_print(pk->mpi[0], p, &n); if (rc < 0 || n < 8) { keyid[0] = keyid[1] = (u32)-1; return (u32)-1; } pk->keyid[0] = p[n - 8] << 24 | p[n - 7] << 16 | p[n - 6] << 8 | p[n - 5]; pk->keyid[1] = p[n - 4] << 24 | p[n - 3] << 16 | p[n - 2] << 8 | p[n - 1]; } else if (pk->version == 4) { cdk_pk_get_fingerprint(pk, buf); pk->keyid[0] = _cdk_buftou32(buf + 12); pk->keyid[1] = _cdk_buftou32(buf + 16); } } lowbits = pk ? pk->keyid[1] : 0; if (keyid && pk) { keyid[0] = pk->keyid[0]; keyid[1] = pk->keyid[1]; } return lowbits; }
/* Hash an entire public key PK with the given message digest context MD. The @usefpr param is only valid for version 3 keys because of the different way to calculate the fingerprint. */ cdk_error_t _cdk_hash_pubkey (cdk_pubkey_t pk, digest_hd_st * md, int usefpr) { byte buf[12]; size_t i, n, npkey; if (!pk || !md) return CDK_Inv_Value; if (usefpr && pk->version < 4 && is_RSA (pk->pubkey_algo)) return hash_mpibuf (pk, md, 1); /* The version 4 public key packet does not have the 2 octets for the expiration date. */ n = pk->version < 4 ? 8 : 6; npkey = cdk_pk_get_npkey (pk->pubkey_algo); for (i = 0; i < npkey; i++) n = n + (_gnutls_mpi_get_nbits (pk->mpi[i]) + 7) / 8 + 2; i = 0; buf[i++] = 0x99; buf[i++] = n >> 8; buf[i++] = n >> 0; buf[i++] = pk->version; buf[i++] = pk->timestamp >> 24; buf[i++] = pk->timestamp >> 16; buf[i++] = pk->timestamp >> 8; buf[i++] = pk->timestamp >> 0; if (pk->version < 4) { u16 a = 0; /* Convert the expiration date into days. */ if (pk->expiredate) a = (u16) ((pk->expiredate - pk->timestamp) / 86400L); buf[i++] = a >> 8; buf[i++] = a; }
/* Convert the given public key to a gcrypt SEXP object. */ static cdk_error_t pubkey_to_sexp (gcry_sexp_t *r_key_sexp, cdk_pubkey_t pk) { gcry_mpi_t *m; gcry_error_t err; const char *fmt = NULL; cdk_error_t rc = 0; if (!r_key_sexp || !pk) return CDK_Inv_Value; m = pk->mpi; if (is_RSA (pk->pubkey_algo)) { fmt = "(public-key(openpgp-rsa(n%m)(e%m)))"; err = gcry_sexp_build (r_key_sexp, NULL, fmt, m[0], m[1]); if (err) rc = map_gcry_error (err); } else if (is_ELG (pk->pubkey_algo)) { fmt = "(public-key(openpgp-elg(p%m)(g%m)(y%m)))"; err = gcry_sexp_build (r_key_sexp, NULL, fmt, m[0], m[1], m[2]); if (err) rc = map_gcry_error (err); } else if (is_DSA (pk->pubkey_algo)) { fmt = "(public-key(openpgp-dsa(p%m)(q%m)(g%m)(y%m)))"; err = gcry_sexp_build (r_key_sexp, NULL, fmt, m[0], m[1], m[2], m[3]); if (err) rc = map_gcry_error (err); } else rc = CDK_Inv_Algo; return rc; }
static int do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk ) { int rc = 0; int i, nskey, npkey; IOBUF a = iobuf_temp(); /* Build in a self-enlarging buffer. */ /* Write the version number - if none is specified, use 3 */ if ( !sk->version ) iobuf_put ( a, 3 ); else iobuf_put ( a, sk->version ); write_32 (a, sk->timestamp ); /* v3 needs the expiration time. */ if ( sk->version < 4 ) { u16 ndays; if ( sk->expiredate ) ndays = (u16)((sk->expiredate - sk->timestamp) / 86400L); else ndays = 0; write_16(a, ndays); } iobuf_put (a, sk->pubkey_algo ); /* Get number of secret and public parameters. They are held in one array first the public ones, then the secret ones. */ nskey = pubkey_get_nskey ( sk->pubkey_algo ); npkey = pubkey_get_npkey ( sk->pubkey_algo ); /* If we don't have any public parameters - which is the case if we don't know the algorithm used - the parameters are stored as one blob in a faked (opaque) MPI. */ if ( !npkey ) { write_fake_data( a, sk->skey[0] ); goto leave; } assert ( npkey < nskey ); /* Writing the public parameters is easy. */ for (i=0; i < npkey; i++ ) if ((rc = mpi_write (a, sk->skey[i]))) goto leave; /* Build the header for protected (encrypted) secret parameters. */ if ( sk->is_protected ) { if ( is_RSA(sk->pubkey_algo) && sk->version < 4 && !sk->protect.s2k.mode ) { /* The simple rfc1991 (v3) way. */ iobuf_put (a, sk->protect.algo ); iobuf_write (a, sk->protect.iv, sk->protect.ivlen ); } else { /* OpenPGP protection according to rfc2440. */ iobuf_put(a, sk->protect.sha1chk? 0xfe : 0xff ); iobuf_put(a, sk->protect.algo ); if ( sk->protect.s2k.mode >= 1000 ) { /* These modes are not possible in OpenPGP, we use them to implement our extensions, 101 can be seen as a private/experimental extension (this is not specified in rfc2440 but the same scheme is used for all other algorithm identifiers) */ iobuf_put(a, 101 ); iobuf_put(a, sk->protect.s2k.hash_algo ); iobuf_write(a, "GNU", 3 ); iobuf_put(a, sk->protect.s2k.mode - 1000 ); } else { iobuf_put(a, sk->protect.s2k.mode ); iobuf_put(a, sk->protect.s2k.hash_algo ); } if ( sk->protect.s2k.mode == 1 || sk->protect.s2k.mode == 3 ) iobuf_write (a, sk->protect.s2k.salt, 8 ); if ( sk->protect.s2k.mode == 3 ) iobuf_put (a, sk->protect.s2k.count ); /* For our special modes 1001, 1002 we do not need an IV. */ if ( sk->protect.s2k.mode != 1001 && sk->protect.s2k.mode != 1002 ) iobuf_write (a, sk->protect.iv, sk->protect.ivlen ); } } else iobuf_put (a, 0 ); if ( sk->protect.s2k.mode == 1001 ) ; /* GnuPG extension - don't write a secret key at all. */ else if ( sk->protect.s2k.mode == 1002 ) { /* GnuPG extension - divert to OpenPGP smartcard. */ iobuf_put(a, sk->protect.ivlen ); /* Length of the serial number or 0 for no serial number. */ /* The serial number gets stored in the IV field. */ iobuf_write(a, sk->protect.iv, sk->protect.ivlen); } else if ( sk->is_protected && sk->version >= 4 ) { /* The secret key is protected - write it out as it is. */ byte *p; unsigned int ndatabits; assert (gcry_mpi_get_flag (sk->skey[npkey], GCRYMPI_FLAG_OPAQUE)); p = gcry_mpi_get_opaque (sk->skey[npkey], &ndatabits ); iobuf_write (a, p, (ndatabits+7)/8 ); } else if ( sk->is_protected ) { /* The secret key is protected the old v4 way. */ for ( ; i < nskey; i++ ) { byte *p; unsigned int ndatabits; assert (gcry_mpi_get_flag (sk->skey[i], GCRYMPI_FLAG_OPAQUE)); p = gcry_mpi_get_opaque (sk->skey[i], &ndatabits); iobuf_write (a, p, (ndatabits+7)/8); } write_16(a, sk->csum ); } else { /* Non-protected key. */ for ( ; i < nskey; i++ ) if ( (rc = mpi_write (a, sk->skey[i]))) goto leave; write_16 (a, sk->csum ); } leave: if (!rc) { /* Build the header of the packet - which we must do after writing all the other stuff, so that we know the length of the packet */ write_header2(out, ctb, iobuf_get_temp_length(a), sk->hdrbytes); /* And finally write it out the real stream */ rc = iobuf_write_temp( out, a ); } iobuf_close(a); /* Close the remporary buffer */ return rc; }
static cdk_error_t write_secret_key (cdk_stream_t out, cdk_pkt_seckey_t sk, int is_subkey, int old_ctb) { cdk_pkt_pubkey_t pk = NULL; size_t size = 6, npkey, nskey; int pkttype, s2k_mode; cdk_error_t rc; assert (out); assert (sk); if (!sk->pk) return CDK_Inv_Value; pk = sk->pk; if (pk->version < 2 || pk->version > 4) return CDK_Inv_Packet; if (DEBUG_PKT) _cdk_log_debug ("write_secret_key:\n"); npkey = cdk_pk_get_npkey (pk->pubkey_algo); nskey = cdk_pk_get_nskey (pk->pubkey_algo); if (!npkey || !nskey) { gnutls_assert (); return CDK_Inv_Algo; } if (pk->version < 4) size += 2; /* If the key is unprotected, the 1 extra byte: 1 octet - cipher algorithm byte (0x00) the other bytes depend on the mode: a) simple checksum - 2 octets b) sha-1 checksum - 20 octets */ size = !sk->is_protected ? size + 1 : size + 1 + calc_s2ksize (sk); size += calc_mpisize (pk->mpi, npkey); if (sk->version == 3 || !sk->is_protected) { if (sk->version == 3) { size += 2; /* force simple checksum */ sk->protect.sha1chk = 0; } else size += sk->protect.sha1chk ? 20 : 2; size += calc_mpisize (sk->mpi, nskey); } else /* We do not know anything about the encrypted mpi's so we treat the data as opaque. */ size += sk->enclen; pkttype = is_subkey ? CDK_PKT_SECRET_SUBKEY : CDK_PKT_SECRET_KEY; rc = pkt_write_head (out, old_ctb, size, pkttype); if (!rc) rc = stream_putc (out, pk->version); if (!rc) rc = write_32 (out, pk->timestamp); if (!rc && pk->version < 4) { u16 ndays = 0; if (pk->expiredate) ndays = (u16) ((pk->expiredate - pk->timestamp) / 86400L); rc = write_16 (out, ndays); } if (!rc) rc = stream_putc (out, _cdk_pub_algo_to_pgp (pk->pubkey_algo)); if (!rc) rc = write_mpibuf (out, pk->mpi, npkey); if (!rc) { if (sk->is_protected == 0) rc = stream_putc (out, 0x00); else { if (is_RSA (pk->pubkey_algo) && pk->version < 4) rc = stream_putc (out, _gnutls_cipher_to_pgp (sk->protect.algo)); else if (sk->protect.s2k) { s2k_mode = sk->protect.s2k->mode; rc = stream_putc (out, sk->protect.sha1chk ? 0xFE : 0xFF); if (!rc) rc = stream_putc (out, _gnutls_cipher_to_pgp (sk->protect.algo)); if (!rc) rc = stream_putc (out, sk->protect.s2k->mode); if (!rc) rc = stream_putc (out, sk->protect.s2k->hash_algo); if (!rc && (s2k_mode == 1 || s2k_mode == 3)) { rc = stream_write (out, sk->protect.s2k->salt, 8); if (!rc && s2k_mode == 3) rc = stream_putc (out, sk->protect.s2k->count); } } else return CDK_Inv_Value; if (!rc) rc = stream_write (out, sk->protect.iv, sk->protect.ivlen); } } if (!rc && sk->is_protected && pk->version == 4) { if (sk->encdata && sk->enclen) rc = stream_write (out, sk->encdata, sk->enclen); } else { if (!rc) rc = write_mpibuf (out, sk->mpi, nskey); if (!rc) { if (!sk->csum) sk->csum = _cdk_sk_get_csum (sk); rc = write_16 (out, sk->csum); } } return rc; }
/**************** * Encrypt the file with the given userids (or ask if none * is supplied). */ int encode_crypt( const char *filename, STRLIST remusr, int use_symkey ) { IOBUF inp = NULL, out = NULL; PACKET pkt; PKT_plaintext *pt = NULL; DEK *symkey_dek = NULL; STRING2KEY *symkey_s2k = NULL; int rc = 0, rc2 = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t afx; compress_filter_context_t zfx; text_filter_context_t tfx; progress_filter_context_t pfx; PK_LIST pk_list,work_list; int do_compress = opt.compress_algo && !RFC1991; memset( &cfx, 0, sizeof cfx); memset( &afx, 0, sizeof afx); memset( &zfx, 0, sizeof zfx); memset( &tfx, 0, sizeof tfx); init_packet(&pkt); if(use_symkey && (rc=setup_symkey(&symkey_s2k,&symkey_dek))) return rc; if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) ) return rc; if(PGP2) { for(work_list=pk_list; work_list; work_list=work_list->next) if(!(is_RSA(work_list->pk->pubkey_algo) && nbits_from_pk(work_list->pk)<=2048)) { log_info(_("you can only encrypt to RSA keys of 2048 bits or " "less in --pgp2 mode\n")); compliance_failure(); break; } } /* prepare iobufs */ inp = iobuf_open(filename); if (inp) iobuf_ioctl (inp,3,1,NULL); /* disable fd caching */ if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; errno = EPERM; } if( !inp ) { log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]", strerror(errno) ); rc = G10ERR_OPEN_FILE; goto leave; } else if( opt.verbose ) log_info(_("reading from `%s'\n"), filename? filename: "[stdin]"); handle_progress (&pfx, inp, filename); if( opt.textmode ) iobuf_push_filter( inp, text_filter, &tfx ); if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) goto leave; if( opt.armor ) iobuf_push_filter( out, armor_filter, &afx ); /* create a session key */ cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek); if( !opt.def_cipher_algo ) { /* try to get it from the prefs */ cfx.dek->algo = select_algo_from_prefs(pk_list,PREFTYPE_SYM,-1,NULL); /* The only way select_algo_from_prefs can fail here is when mixing v3 and v4 keys, as v4 keys have an implicit preference entry for 3DES, and the pk_list cannot be empty. In this case, use 3DES anyway as it's the safest choice - perhaps the v3 key is being used in an OpenPGP implementation and we know that the implementation behind any v4 key can handle 3DES. */ if( cfx.dek->algo == -1 ) { cfx.dek->algo = CIPHER_ALGO_3DES; if( PGP2 ) { log_info(_("unable to use the IDEA cipher for all of the keys " "you are encrypting to.\n")); compliance_failure(); } } } else { if(!opt.expert && select_algo_from_prefs(pk_list,PREFTYPE_SYM, opt.def_cipher_algo,NULL)!=opt.def_cipher_algo) log_info(_("WARNING: forcing symmetric cipher %s (%d)" " violates recipient preferences\n"), cipher_algo_to_string(opt.def_cipher_algo), opt.def_cipher_algo); cfx.dek->algo = opt.def_cipher_algo; } cfx.dek->use_mdc=use_mdc(pk_list,cfx.dek->algo); /* Only do the is-file-already-compressed check if we are using a MDC. This forces compressed files to be re-compressed if we do not have a MDC to give some protection against chosen ciphertext attacks. */ if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2) ) { if (opt.verbose) log_info(_("`%s' already compressed\n"), filename); do_compress = 0; } if (rc2) { rc = rc2; goto leave; } make_session_key( cfx.dek ); if( DBG_CIPHER ) log_hexdump("DEK is: ", cfx.dek->key, cfx.dek->keylen ); rc = write_pubkey_enc_from_list( pk_list, cfx.dek, out ); if( rc ) goto leave; /* We put the passphrase (if any) after any public keys as this seems to be the most useful on the recipient side - there is no point in prompting a user for a passphrase if they have the secret key needed to decrypt. */ if(use_symkey && (rc=write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out))) goto leave; if (!opt.no_literal) { /* setup the inner packet */ if( filename || opt.set_filename ) { char *s = make_basename( opt.set_filename ? opt.set_filename : filename, iobuf_get_real_fname( inp ) ); pt = xmalloc( sizeof *pt + strlen(s) - 1 ); pt->namelen = strlen(s); memcpy(pt->name, s, pt->namelen ); xfree(s); } else { /* no filename */ pt = xmalloc( sizeof *pt - 1 ); pt->namelen = 0; } } if (!iobuf_is_pipe_filename (filename) && *filename && !opt.textmode ) { off_t tmpsize; int overflow; if ( !(tmpsize = iobuf_get_filelength(inp, &overflow)) && !overflow ) log_info(_("WARNING: `%s' is an empty file\n"), filename ); /* We can't encode the length of very large files because OpenPGP uses only 32 bit for file sizes. So if the the size of a file is larger than 2^32 minus some bytes for packet headers, we switch to partial length encoding. */ if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) ) filesize = tmpsize; else filesize = 0; } else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { pt->timestamp = make_timestamp(); pt->mode = opt.textmode ? 't' : 'b'; pt->len = filesize; pt->new_ctb = !pt->len && !RFC1991; pt->buf = inp; pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0; } else cfx.datalen = filesize && !do_compress ? filesize : 0; /* register the cipher filter */ iobuf_push_filter( out, cipher_filter, &cfx ); /* register the compress filter */ if( do_compress ) { int compr_algo = opt.compress_algo; if(compr_algo==-1) { if((compr_algo= select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1) compr_algo=DEFAULT_COMPRESS_ALGO; /* Theoretically impossible to get here since uncompressed is implicit. */ } else if(!opt.expert && select_algo_from_prefs(pk_list,PREFTYPE_ZIP, compr_algo,NULL)!=compr_algo) log_info(_("WARNING: forcing compression algorithm %s (%d)" " violates recipient preferences\n"), compress_algo_to_string(compr_algo),compr_algo); /* algo 0 means no compression */ if( compr_algo ) { if (cfx.dek && cfx.dek->use_mdc) zfx.new_ctb = 1; push_compress_filter(out,&zfx,compr_algo); } } /* do the work */ if (!opt.no_literal) { if( (rc = build_packet( out, &pkt )) ) log_error("build_packet failed: %s\n", g10_errstr(rc) ); } else { /* user requested not to create a literal packet, so we copy the plain data */ byte copy_buffer[4096]; int bytes_copied; while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1) if (iobuf_write(out, copy_buffer, bytes_copied) == -1) { rc = G10ERR_WRITE_FILE; log_error("copying input to output failed: %s\n", g10_errstr(rc) ); break; } wipememory(copy_buffer, 4096); /* burn buffer */ } /* finish the stuff */ leave: iobuf_close(inp); if( rc ) iobuf_cancel(out); else { iobuf_close(out); /* fixme: check returncode */ write_status( STATUS_END_ENCRYPTION ); } if( pt ) pt->buf = NULL; free_packet(&pkt); xfree(cfx.dek); xfree(symkey_dek); xfree(symkey_s2k); release_pk_list( pk_list ); return rc; }