mirror of
git://git.gnupg.org/gnupg.git
synced 2024-11-10 21:38:50 +01:00
90b0ff23b7
Changed order of some conditional to make to put the special case into the true branch. Indentation changes. Minor other changes to make the ECC code more similar to the rest of our code. It builds but many sefltests still fail. Need to fix that before using it with an ECDH enabled libgcrypt. [/] 2011-01-21 Werner Koch <wk@g10code.com> * configure.ac: Need Libgcrypt 1.4.6 due to AESWRAP. (HAVE_GCRY_PK_ECDH): Add new test. [agent/] 2011-01-21 Werner Koch <wk@g10code.com> * cvt-openpgp.c (GCRY_PK_ECDH) [!HAVE_GCRY_PK_ECDH]: New. [include/] 2011-01-21 Werner Koch <wk@g10code.com> * cipher.h (GCRY_PK_USAGE_CERT): Remove compatibility macros because we now require libgcrypt 1.4.6. (GCRY_PK_ECDH): Add replacement.
421 lines
11 KiB
C
421 lines
11 KiB
C
/* pubkey-enc.c - Process a public key encoded packet.
|
|
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2006, 2009,
|
|
* 2010 Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of GnuPG.
|
|
*
|
|
* GnuPG is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GnuPG is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "gpg.h"
|
|
#include "util.h"
|
|
#include "packet.h"
|
|
#include "keydb.h"
|
|
#include "trustdb.h"
|
|
#include "cipher.h"
|
|
#include "status.h"
|
|
#include "options.h"
|
|
#include "main.h"
|
|
#include "i18n.h"
|
|
#include "pkglue.h"
|
|
#include "call-agent.h"
|
|
|
|
|
|
static gpg_error_t get_it (PKT_pubkey_enc *k,
|
|
DEK *dek, PKT_public_key *sk, u32 *keyid);
|
|
|
|
|
|
/* Check that the given algo is mentioned in one of the valid user-ids. */
|
|
static int
|
|
is_algo_in_prefs (kbnode_t keyblock, preftype_t type, int algo)
|
|
{
|
|
kbnode_t k;
|
|
|
|
for (k = keyblock; k; k = k->next)
|
|
{
|
|
if (k->pkt->pkttype == PKT_USER_ID)
|
|
{
|
|
PKT_user_id *uid = k->pkt->pkt.user_id;
|
|
prefitem_t *prefs = uid->prefs;
|
|
|
|
if (uid->created && prefs && !uid->is_revoked && !uid->is_expired)
|
|
{
|
|
for (; prefs->type; prefs++)
|
|
if (prefs->type == type && prefs->value == algo)
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the session key from a pubkey enc packet and return it in DEK,
|
|
* which should have been allocated in secure memory by the caller.
|
|
*/
|
|
gpg_error_t
|
|
get_session_key (PKT_pubkey_enc * k, DEK * dek)
|
|
{
|
|
PKT_public_key *sk = NULL;
|
|
int rc;
|
|
|
|
rc = openpgp_pk_test_algo2 (k->pubkey_algo, PUBKEY_USAGE_ENC);
|
|
if (rc)
|
|
goto leave;
|
|
|
|
if ((k->keyid[0] || k->keyid[1]) && !opt.try_all_secrets)
|
|
{
|
|
sk = xmalloc_clear (sizeof *sk);
|
|
sk->pubkey_algo = k->pubkey_algo; /* We want a pubkey with this algo. */
|
|
if (!(rc = get_seckey (sk, k->keyid)))
|
|
rc = get_it (k, dek, sk, k->keyid);
|
|
}
|
|
else if (opt.skip_hidden_recipients)
|
|
rc = gpg_error (GPG_ERR_NO_SECKEY);
|
|
else /* Anonymous receiver: Try all available secret keys. */
|
|
{
|
|
void *enum_context = NULL;
|
|
u32 keyid[2];
|
|
|
|
for (;;)
|
|
{
|
|
if (sk)
|
|
free_public_key (sk);
|
|
sk = xmalloc_clear (sizeof *sk);
|
|
rc = enum_secret_keys (&enum_context, sk);
|
|
if (rc)
|
|
{
|
|
rc = G10ERR_NO_SECKEY;
|
|
break;
|
|
}
|
|
if (sk->pubkey_algo != k->pubkey_algo)
|
|
continue;
|
|
if (!(sk->pubkey_usage & PUBKEY_USAGE_ENC))
|
|
continue;
|
|
keyid_from_pk (sk, keyid);
|
|
log_info (_("anonymous recipient; trying secret key %s ...\n"),
|
|
keystr (keyid));
|
|
|
|
rc = get_it (k, dek, sk, keyid);
|
|
if (!rc)
|
|
{
|
|
log_info (_("okay, we are the anonymous recipient.\n"));
|
|
break;
|
|
}
|
|
else if (gpg_err_code (rc) == GPG_ERR_FULLY_CANCELED)
|
|
break; /* Don't try any more secret keys. */
|
|
}
|
|
enum_secret_keys (&enum_context, NULL); /* free context */
|
|
}
|
|
|
|
leave:
|
|
if (sk)
|
|
free_public_key (sk);
|
|
return rc;
|
|
}
|
|
|
|
|
|
static gpg_error_t
|
|
get_it (PKT_pubkey_enc *enc, DEK *dek, PKT_public_key *sk, u32 *keyid)
|
|
{
|
|
gpg_error_t err;
|
|
byte *frame = NULL;
|
|
unsigned int n;
|
|
size_t nframe;
|
|
u16 csum, csum2;
|
|
int card = 0;
|
|
gcry_sexp_t s_data;
|
|
char *desc;
|
|
char *keygrip;
|
|
byte fp[MAX_FINGERPRINT_LEN];
|
|
size_t fpn;
|
|
const int gcry_pkalgo = map_pk_openpgp_to_gcry( sk->pubkey_algo );
|
|
|
|
/* Get the keygrip. */
|
|
err = hexkeygrip_from_pk (sk, &keygrip);
|
|
if (err)
|
|
goto leave;
|
|
|
|
|
|
/* Convert the data to an S-expression. */
|
|
if (gcry_pkalgo == GCRY_PK_ELG ||gcry_pkalgo == GCRY_PK_ELG_E)
|
|
{
|
|
if (!enc->data[0] || !enc->data[1])
|
|
err = gpg_error (GPG_ERR_BAD_MPI);
|
|
else
|
|
err = gcry_sexp_build (&s_data, NULL, "(enc-val(elg(a%m)(b%m)))",
|
|
enc->data[0], enc->data[1]);
|
|
}
|
|
else if (gcry_pkalgo == GCRY_PK_RSA || gcry_pkalgo == GCRY_PK_RSA_E)
|
|
{
|
|
if (!enc->data[0])
|
|
err = gpg_error (GPG_ERR_BAD_MPI);
|
|
else
|
|
err = gcry_sexp_build (&s_data, NULL, "(enc-val(rsa(a%m)))",
|
|
enc->data[0]);
|
|
}
|
|
else if (gcry_pkalgo == GCRY_PK_ECDH )
|
|
{
|
|
if (!enc->data[0] || !enc->data[1])
|
|
err = gpg_error (GPG_ERR_BAD_MPI);
|
|
else
|
|
err = gcry_sexp_build (&s_data, NULL, "(enc-val(ecdh(a%m)(b%m)))",
|
|
enc->data[0], enc->data[1]);
|
|
}
|
|
else
|
|
err = gpg_error (GPG_ERR_BUG);
|
|
|
|
if (err)
|
|
goto leave;
|
|
|
|
fingerprint_from_pk( sk, fp, &fpn );
|
|
assert( fpn == 20 );
|
|
|
|
/* Decrypt. */
|
|
desc = gpg_format_keydesc (sk, 0, 1);
|
|
err = agent_pkdecrypt (NULL, keygrip, desc, s_data, &frame, &nframe);
|
|
xfree (desc);
|
|
gcry_sexp_release (s_data);
|
|
if (err)
|
|
goto leave;
|
|
|
|
/* Now get the DEK (data encryption key) from the frame
|
|
*
|
|
* Old versions encode the DEK in in this format (msb is left):
|
|
*
|
|
* 0 1 DEK(16 bytes) CSUM(2 bytes) 0 RND(n bytes) 2
|
|
*
|
|
* Later versions encode the DEK like this:
|
|
*
|
|
* 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes)
|
|
*
|
|
* (mpi_get_buffer already removed the leading zero).
|
|
*
|
|
* RND are non-zero randow bytes.
|
|
* A is the cipher algorithm
|
|
* DEK is the encryption key (session key) with length k
|
|
* CSUM
|
|
*/
|
|
if (DBG_CIPHER)
|
|
log_printhex ("DEK frame:", frame, nframe);
|
|
n = 0;
|
|
|
|
if (sk->pubkey_algo == PUBKEY_ALGO_ECDH)
|
|
{
|
|
gcry_mpi_t shared_mpi;
|
|
gcry_mpi_t decoded;
|
|
|
|
/* At the beginning the frame are the bytes of shared point MPI. */
|
|
err = gcry_mpi_scan (&shared_mpi, GCRYMPI_FMT_USG, frame, nframe, NULL);
|
|
if (err)
|
|
{
|
|
log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err));
|
|
goto leave;
|
|
}
|
|
|
|
err = pk_ecdh_decrypt (&decoded, fp, enc->data[1]/*encr data as an MPI*/,
|
|
shared_mpi, sk->pkey);
|
|
mpi_release (shared_mpi);
|
|
if(err)
|
|
goto leave;
|
|
|
|
/* Reuse NFRAME, which size is sufficient to include the session key. */
|
|
err = gcry_mpi_print (GCRYMPI_FMT_USG, frame, nframe, &nframe, decoded);
|
|
mpi_release (decoded);
|
|
if (err)
|
|
goto leave;
|
|
|
|
/* Now the frame are the bytes decrypted but padded session key. */
|
|
|
|
/* Allow double padding for the benefit of DEK size concealment.
|
|
Higher than this is wasteful. */
|
|
if (frame[nframe-1] > 8*2 || nframe <= 8)
|
|
{
|
|
err = gpg_error (GPG_ERR_WRONG_SECKEY);
|
|
goto leave;
|
|
}
|
|
nframe -= frame[nframe-1]; /* Remove padding. */
|
|
assert (n); /* (used just below) */
|
|
}
|
|
else
|
|
{
|
|
if (!card)
|
|
{
|
|
if (n + 7 > nframe)
|
|
{
|
|
err = gpg_error (GPG_ERR_WRONG_SECKEY);
|
|
goto leave;
|
|
}
|
|
if (frame[n] == 1 && frame[nframe - 1] == 2)
|
|
{
|
|
log_info (_("old encoding of the DEK is not supported\n"));
|
|
err = gpg_error (GPG_ERR_CIPHER_ALGO);
|
|
goto leave;
|
|
}
|
|
if (frame[n] != 2) /* Something went wrong. */
|
|
{
|
|
err = gpg_error (GPG_ERR_WRONG_SECKEY);
|
|
goto leave;
|
|
}
|
|
for (n++; n < nframe && frame[n]; n++) /* Skip the random bytes. */
|
|
;
|
|
n++; /* Skip the zero byte. */
|
|
}
|
|
}
|
|
|
|
if (n + 4 > nframe)
|
|
{
|
|
err = gpg_error (GPG_ERR_WRONG_SECKEY);
|
|
goto leave;
|
|
}
|
|
|
|
dek->keylen = nframe - (n + 1) - 2;
|
|
dek->algo = frame[n++];
|
|
if (dek->algo == CIPHER_ALGO_IDEA)
|
|
write_status (STATUS_RSA_OR_IDEA);
|
|
err = openpgp_cipher_test_algo (dek->algo);
|
|
if (err)
|
|
{
|
|
if (!opt.quiet && gpg_err_code (err) == GPG_ERR_CIPHER_ALGO)
|
|
{
|
|
log_info (_("cipher algorithm %d%s is unknown or disabled\n"),
|
|
dek->algo,
|
|
dek->algo == CIPHER_ALGO_IDEA ? " (IDEA)" : "");
|
|
if (dek->algo == CIPHER_ALGO_IDEA)
|
|
idea_cipher_warn (0);
|
|
}
|
|
dek->algo = 0;
|
|
goto leave;
|
|
}
|
|
if (dek->keylen != openpgp_cipher_get_algo_keylen (dek->algo))
|
|
{
|
|
err = gpg_error (GPG_ERR_WRONG_SECKEY);
|
|
goto leave;
|
|
}
|
|
|
|
/* Copy the key to DEK and compare the checksum. */
|
|
csum = frame[nframe - 2] << 8;
|
|
csum |= frame[nframe - 1];
|
|
memcpy (dek->key, frame + n, dek->keylen);
|
|
for (csum2 = 0, n = 0; n < dek->keylen; n++)
|
|
csum2 += dek->key[n];
|
|
if (csum != csum2)
|
|
{
|
|
err = gpg_error (GPG_ERR_WRONG_SECKEY);
|
|
goto leave;
|
|
}
|
|
if (DBG_CIPHER)
|
|
log_printhex ("DEK is:", dek->key, dek->keylen);
|
|
|
|
/* Check that the algo is in the preferences and whether it has expired. */
|
|
{
|
|
PKT_public_key *pk = NULL;
|
|
KBNODE pkb = get_pubkeyblock (keyid);
|
|
|
|
if (!pkb)
|
|
{
|
|
err = -1;
|
|
log_error ("oops: public key not found for preference check\n");
|
|
}
|
|
else if (pkb->pkt->pkt.public_key->selfsigversion > 3
|
|
&& dek->algo != CIPHER_ALGO_3DES
|
|
&& !opt.quiet
|
|
&& !is_algo_in_prefs (pkb, PREFTYPE_SYM, dek->algo))
|
|
log_info (_("WARNING: cipher algorithm %s not found in recipient"
|
|
" preferences\n"), openpgp_cipher_algo_name (dek->algo));
|
|
if (!err)
|
|
{
|
|
KBNODE k;
|
|
|
|
for (k = pkb; k; k = k->next)
|
|
{
|
|
if (k->pkt->pkttype == PKT_PUBLIC_KEY
|
|
|| k->pkt->pkttype == PKT_PUBLIC_SUBKEY)
|
|
{
|
|
u32 aki[2];
|
|
keyid_from_pk (k->pkt->pkt.public_key, aki);
|
|
|
|
if (aki[0] == keyid[0] && aki[1] == keyid[1])
|
|
{
|
|
pk = k->pkt->pkt.public_key;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (!pk)
|
|
BUG ();
|
|
if (pk->expiredate && pk->expiredate <= make_timestamp ())
|
|
{
|
|
log_info (_("NOTE: secret key %s expired at %s\n"),
|
|
keystr (keyid), asctimestamp (pk->expiredate));
|
|
}
|
|
}
|
|
|
|
if (pk && pk->flags.revoked)
|
|
{
|
|
log_info (_("NOTE: key has been revoked"));
|
|
log_printf ("\n");
|
|
show_revocation_reason (pk, 1);
|
|
}
|
|
|
|
release_kbnode (pkb);
|
|
err = 0;
|
|
}
|
|
|
|
leave:
|
|
xfree (frame);
|
|
xfree (keygrip);
|
|
return err;
|
|
}
|
|
|
|
|
|
/*
|
|
* Get the session key from the given string.
|
|
* String is supposed to be formatted as this:
|
|
* <algo-id>:<even-number-of-hex-digits>
|
|
*/
|
|
gpg_error_t
|
|
get_override_session_key (DEK *dek, const char *string)
|
|
{
|
|
const char *s;
|
|
int i;
|
|
|
|
if (!string)
|
|
return G10ERR_BAD_KEY;
|
|
dek->algo = atoi (string);
|
|
if (dek->algo < 1)
|
|
return G10ERR_BAD_KEY;
|
|
if (!(s = strchr (string, ':')))
|
|
return G10ERR_BAD_KEY;
|
|
s++;
|
|
for (i = 0; i < DIM (dek->key) && *s; i++, s += 2)
|
|
{
|
|
int c = hextobyte (s);
|
|
if (c == -1)
|
|
return G10ERR_BAD_KEY;
|
|
dek->key[i] = c;
|
|
}
|
|
if (*s)
|
|
return G10ERR_BAD_KEY;
|
|
dek->keylen = i;
|
|
return 0;
|
|
}
|