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; }
/* Hash all multi precision integers of the key PK with the given message digest context MD. */ static int hash_mpibuf (cdk_pubkey_t pk, digest_hd_st * md, int usefpr) { byte buf[MAX_MPI_BYTES]; /* FIXME: do not use hardcoded length. */ size_t nbytes; size_t i, npkey; int err; /* We have to differ between two modes for v3 keys. To form the fingerprint, we hash the MPI values without the length prefix. But if we calculate the hash for verifying/signing we use all data. */ npkey = cdk_pk_get_npkey (pk->pubkey_algo); for (i = 0; i < npkey; i++) { nbytes = MAX_MPI_BYTES; err = _gnutls_mpi_print_pgp (pk->mpi[i], buf, &nbytes); if (err < 0) return map_gnutls_error (err); if (!usefpr || pk->version == 4) _gnutls_hash (md, buf, nbytes); else /* without the prefix. */ _gnutls_hash (md, buf + 2, nbytes - 2); } return 0; }
/** * cdk_pk_get_mpi: * @pk: public key * @idx: index of the MPI to retrieve * @buf: buffer to hold the raw data * @r_nwritten: output how large the raw data is * @r_nbits: size of the MPI in bits. * * Return the MPI with the given index of the public key. **/ cdk_error_t cdk_pk_get_mpi (cdk_pubkey_t pk, size_t idx, byte *buf, size_t buflen, size_t *r_nwritten, size_t *r_nbits) { if (!pk || !r_nwritten) return CDK_Inv_Value; if (idx > cdk_pk_get_npkey (pk->pubkey_algo)) return CDK_Inv_Value; return mpi_to_buffer (pk->mpi[idx], buf, buflen, r_nwritten, r_nbits); }
/** * 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) { size_t bytes; if (gcry_pk_algo_info (algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &bytes)) return 0; bytes -= cdk_pk_get_npkey (algo); return bytes; }
cdk_error_t _cdk_pubkey_compare(cdk_pkt_pubkey_t a, cdk_pkt_pubkey_t b) { int na, nb, i; if (a->timestamp != b->timestamp || a->pubkey_algo != b->pubkey_algo) return -1; if (a->version < 4 && a->expiredate != b->expiredate) return -1; na = cdk_pk_get_npkey(a->pubkey_algo); nb = cdk_pk_get_npkey(b->pubkey_algo); if (na != nb) return -1; for (i = 0; i < na; i++) { if (_gnutls_mpi_cmp(a->mpi[i], b->mpi[i])) return -1; } return 0; }
static void hash_mpibuf( cdk_pkt_pubkey_t pk, cdk_md_hd_t md ) { cdk_mpi_t a; int i, npkey; npkey = cdk_pk_get_npkey( pk->pubkey_algo ); for( i = 0; i < npkey; i++ ) { a = pk->mpi[i]; if( pk->version == 4 ) { cdk_md_putc( md, a->bits >> 8 ); cdk_md_putc( md, a->bits ); } cdk_md_write( md, a->data + 2, a->bytes ); }
void cdk_pk_release(cdk_pubkey_t pk) { size_t npkey; if (!pk) return; npkey = cdk_pk_get_npkey(pk->pubkey_algo); _cdk_free_userid(pk->uid); pk->uid = NULL; cdk_free(pk->prefs); pk->prefs = NULL; _cdk_free_mpibuf(npkey, pk->mpi); cdk_free(pk); }
static cdk_error_t write_public_key (cdk_stream_t out, cdk_pkt_pubkey_t pk, int is_subkey, int old_ctb) { int pkttype, ndays = 0; size_t npkey = 0, size = 6; cdk_error_t rc; assert (out); assert (pk); if (pk->version < 2 || pk->version > 4) return CDK_Inv_Packet; if (DEBUG_PKT) _cdk_log_debug ("write_public_key: subkey=%d\n", is_subkey); pkttype = is_subkey ? CDK_PKT_PUBLIC_SUBKEY : CDK_PKT_PUBLIC_KEY; npkey = cdk_pk_get_npkey (pk->pubkey_algo); if (!npkey) return CDK_Inv_Algo; if (pk->version < 4) size += 2; /* expire date */ if (is_subkey) old_ctb = 0; size += calc_mpisize (pk->mpi, npkey); if (old_ctb) rc = pkt_write_head2 (out, size, pkttype); else 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) { 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); return rc; }
static cdk_error_t read_public_key (cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_t pk) { size_t i, ndays, npkey; if (!inp || !pk) return CDK_Inv_Value; if (DEBUG_PKT) _cdk_log_debug ("read_public_key: %d octets\n", pktlen); pk->is_invalid = 1; /* default to detect missing self signatures */ pk->is_revoked = 0; pk->has_expired = 0; pk->version = cdk_stream_getc (inp); if (pk->version < 2 || pk->version > 4) return CDK_Inv_Packet_Ver; pk->timestamp = read_32 (inp); if (pk->version < 4) { ndays = read_16 (inp); if (ndays) pk->expiredate = pk->timestamp + ndays * 86400L; } pk->pubkey_algo = _pgp_pub_algo_to_cdk (cdk_stream_getc (inp)); npkey = cdk_pk_get_npkey (pk->pubkey_algo); if (!npkey) { gnutls_assert (); _cdk_log_debug ("invalid public key algorithm %d\n", pk->pubkey_algo); return CDK_Inv_Algo; } for (i = 0; i < npkey; i++) { cdk_error_t rc = read_mpi (inp, &pk->mpi[i], 0); if (rc) return rc; } /* This value is just for the first run and will be replaced with the actual key flags from the self signature. */ pk->pubkey_usage = 0; 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_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 gcry_mpi_to_native( cdk_keygen_ctx_t hd, size_t nkey, int type, cdk_pkt_pubkey_t pk, cdk_pkt_seckey_t sk ) { gcry_mpi_t * resarr; cdk_mpi_t a = NULL; size_t nbytes; int i = 0, j = 0, nbits; int rc = 0; if( !hd ) return CDK_Inv_Value; if( !pk && !sk ) return CDK_Inv_Value; if( type < 0 || type > 1 ) return CDK_Inv_Value; resarr = hd->key[type].resarr; if( sk ) i += cdk_pk_get_npkey( sk->pubkey_algo ); while( j != nkey ) { nbits = gcry_mpi_get_nbits( resarr[i] ); if( pk ) a = cdk_calloc( 1, sizeof * a + (nbits + 7) / 8 + 2 + 1 ); else if( sk ) a = cdk_salloc( sizeof * a + (nbits + 7) / 8 + 2 + 1, 1 ); a->bits = nbits; a->bytes = ( nbits + 7 ) / 8; nbytes = a->bytes; a->data[0] = nbits >> 8; a->data[1] = nbits; rc = gcry_mpi_print( GCRYMPI_FMT_USG, a->data+2, nbytes, &nbytes, resarr[i] ); if( rc ) break; if( pk ) pk->mpi[j++] = a; else if( sk ) sk->mpi[j++] = a; i++; } return rc; }
/* 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; }
cdk_error_t _cdk_copy_pubkey(cdk_pkt_pubkey_t * dst, cdk_pkt_pubkey_t src) { cdk_pkt_pubkey_t k; int i; if (!dst || !src) return CDK_Inv_Value; *dst = NULL; k = cdk_calloc(1, sizeof *k); if (!k) return CDK_Out_Of_Core; memcpy(k, src, sizeof *k); if (src->uid) _cdk_copy_userid(&k->uid, src->uid); if (src->prefs) k->prefs = _cdk_copy_prefs(src->prefs); for (i = 0; i < cdk_pk_get_npkey(src->pubkey_algo); i++) k->mpi[i] = _gnutls_mpi_copy(src->mpi[i]); *dst = k; return 0; }
static cdk_pkt_pubkey_t pk_create( cdk_keygen_ctx_t hd, int type ) { cdk_pkt_pubkey_t pk; int rc = 0, npkey = 0; if( type < 0 || type > 1 ) return NULL; pk = cdk_calloc( 1, sizeof * pk ); if( !pk ) return NULL; pk->version = 4; pk->pubkey_algo = hd->key[type].algo; pk->timestamp = _cdk_timestamp( ); if( hd->key[type].expire_date ) pk->expiredate = pk->timestamp + hd->key[type].expire_date; npkey = cdk_pk_get_npkey( pk->pubkey_algo ); rc = gcry_mpi_to_native( hd, npkey, type, pk, NULL ); if( rc ) { cdk_free( pk ); pk = NULL; } return pk; }
/** * cdk_pk_verify: * @pk: the public key * @sig: signature * @md: the message digest * * Verify the signature in @sig and compare it with the message digest in @md. **/ cdk_error_t cdk_pk_verify (cdk_pubkey_t pk, cdk_pkt_signature_t sig, const byte * md) { gnutls_datum_t s_sig; byte *encmd = NULL; size_t enclen; cdk_error_t rc; int ret, algo; unsigned int i; gnutls_datum_t data; gnutls_pk_params_st params; if (!pk || !sig || !md) { gnutls_assert (); return CDK_Inv_Value; } if (is_DSA (pk->pubkey_algo)) algo = GNUTLS_PK_DSA; else if (is_RSA (pk->pubkey_algo)) algo = GNUTLS_PK_RSA; else { gnutls_assert (); return CDK_Inv_Value; } rc = sig_to_datum (&s_sig, sig); if (rc) { gnutls_assert (); goto leave; } rc = _cdk_digest_encode_pkcs1 (&encmd, &enclen, pk->pubkey_algo, md, sig->digest_algo, cdk_pk_get_nbits (pk)); if (rc) { gnutls_assert (); goto leave; } data.data = encmd; data.size = enclen; params.params_nr = cdk_pk_get_npkey (pk->pubkey_algo); for (i = 0; i < params.params_nr; i++) params.params[i] = pk->mpi[i]; params.flags = 0; ret = _gnutls_pk_verify (algo, &data, &s_sig, ¶ms); if (ret < 0) { gnutls_assert (); rc = map_gnutls_error (ret); goto leave; } rc = 0; leave: _gnutls_free_datum (&s_sig); cdk_free (encmd); 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; }
/** * cdk_pk_verify: * @pk: the public key * @sig: signature * @md: the message digest * * Verify the signature in @sig and compare it with the message digest in @md. **/ cdk_error_t cdk_pk_verify(cdk_pubkey_t pk, cdk_pkt_signature_t sig, const byte * md) { gnutls_datum_t s_sig = { NULL, 0 }, di = { NULL, 0}; byte *encmd = NULL; cdk_error_t rc; int ret, algo; unsigned int i; gnutls_pk_params_st params; const mac_entry_st *me; if (!pk || !sig || !md) { gnutls_assert(); return CDK_Inv_Value; } if (is_DSA(pk->pubkey_algo)) algo = GNUTLS_PK_DSA; else if (is_RSA(pk->pubkey_algo)) algo = GNUTLS_PK_RSA; else { gnutls_assert(); return CDK_Inv_Value; } rc = sig_to_datum(&s_sig, sig); if (rc) { gnutls_assert(); goto leave; } me = mac_to_entry(sig->digest_algo); rc = _gnutls_set_datum(&di, md, _gnutls_hash_get_algo_len(me)); if (rc < 0) { rc = gnutls_assert_val(CDK_Out_Of_Core); goto leave; } rc = pk_prepare_hash(algo, me, &di); if (rc < 0) { rc = gnutls_assert_val(CDK_General_Error); goto leave; } params.params_nr = cdk_pk_get_npkey(pk->pubkey_algo); for (i = 0; i < params.params_nr; i++) params.params[i] = pk->mpi[i]; params.flags = 0; ret = _gnutls_pk_verify(algo, &di, &s_sig, ¶ms); if (ret < 0) { gnutls_assert(); rc = map_gnutls_error(ret); goto leave; } rc = 0; leave: _gnutls_free_datum(&s_sig); _gnutls_free_datum(&di); cdk_free(encmd); return rc; }