gpg,ecc: Handle external representation as SOS with opaque MPI.

* g10/pkglue.h (sexp_extract_param_sos): New.
* g10/build-packet.c (sos_write): New.
(do_key, do_pubkey_enc, do_signature): Use sos_write for ECC.
* g10/export.c (cleartext_secret_key_to_openpgp): Use
sexp_extract_param_sos.
(transfer_format_to_openpgp): Use opaque MPI for ECC.
* g10/keygen.c (ecckey_from_sexp): Use sexp_extract_param_sos.
* g10/keyid.c (hash_public_key): Handle opaque MPI for SOS.
* g10/parse-packet.c (sos_read): New.
(parse_pubkeyenc,parse_signature,parse_key): Use sos_read for ECC.
* g10/pkglue.c (sexp_extract_param_sos): New.
(pk_verify): Handle opaque MPI for SOS.
(pk_encrypt): Use sexp_extract_param_sos.
* g10/seskey.c (encode_session_key): Use opaque MPI.
* g10/sign.c (do_sign): Use sexp_extract_param_sos.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka 2020-06-09 10:32:47 +09:00
parent 5c2080f467
commit f5bc945554
9 changed files with 316 additions and 59 deletions

View File

@ -364,7 +364,71 @@ gpg_mpi_write (iobuf_t out, gcry_mpi_t a, unsigned int *r_nwritten)
/*
* Write an opaque MPI to the output stream without length info.
* Write the mpi A to the output stream OUT as "SOS" (Strange Octet
* String). If R_NWRITTEN is not NULL the number of bytes written is
* stored there. To only get the number of bytes which would be
* written, NULL may be passed for OUT.
*/
static gpg_error_t
sos_write (iobuf_t out, gcry_mpi_t a, unsigned int *r_nwritten)
{
gpg_error_t err;
unsigned int nwritten = 0;
if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE))
{
unsigned int nbits;
const unsigned char *p;
unsigned char lenhdr[2];
/* gcry_log_debugmpi ("a", a); */
p = gcry_mpi_get_opaque (a, &nbits);
/* gcry_log_debug (" [%u bit]\n", nbits); */
/* gcry_log_debughex (" ", p, (nbits+7)/8); */
if (p && *p)
{
nbits = ((nbits + 7) / 8) * 8;
if (nbits >= 8 && !(*p & 0x80))
if (--nbits >= 7 && !(*p & 0x40))
if (--nbits >= 6 && !(*p & 0x20))
if (--nbits >= 5 && !(*p & 0x10))
if (--nbits >= 4 && !(*p & 0x08))
if (--nbits >= 3 && !(*p & 0x04))
if (--nbits >= 2 && !(*p & 0x02))
if (--nbits >= 1 && !(*p & 0x01))
--nbits;
}
lenhdr[0] = nbits >> 8;
lenhdr[1] = nbits;
err = out? iobuf_write (out, lenhdr, 2) : 0;
if (!err)
{
nwritten += 2;
if (p)
{
err = out? iobuf_write (out, p, (nbits+7)/8) : 0;
if (!err)
nwritten += (nbits+7)/8;
}
}
}
else
{
log_info ("non-opaque MPI (%u bits) for SOS\n", gcry_mpi_get_nbits (a));
err = gpg_error (GPG_ERR_INV_DATA);
}
if (r_nwritten)
*r_nwritten = nwritten;
return err;
}
/*
* Write an opaque string to the output stream without length info.
*/
gpg_error_t
gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a)
@ -575,6 +639,10 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk)
|| (pk->pubkey_algo == PUBKEY_ALGO_EDDSA && (i == 0))
|| (pk->pubkey_algo == PUBKEY_ALGO_ECDH && (i == 0 || i == 2)))
err = gpg_mpi_write_nohdr (a, pk->pkey[i]);
else if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo == PUBKEY_ALGO_EDDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
err = sos_write (a, pk->pkey[i], NULL);
else
err = gpg_mpi_write (a, pk->pkey[i], NULL);
if (err)
@ -691,8 +759,18 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk)
for (j=i; j < nskey; j++ )
{
if ((err = gpg_mpi_write (NULL, pk->pkey[j], &n)))
goto leave;
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo == PUBKEY_ALGO_EDDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
if ((err = sos_write (NULL, pk->pkey[j], &n)))
goto leave;
}
else
{
if ( (err = gpg_mpi_write (a, pk->pkey[i], NULL)))
goto leave;
}
skbytes += n;
}
@ -700,8 +778,16 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk)
}
for ( ; i < nskey; i++ )
if ( (err = gpg_mpi_write (a, pk->pkey[i], NULL)))
goto leave;
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo == PUBKEY_ALGO_EDDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
if ((err = sos_write (a, pk->pkey[i], NULL)))
goto leave;
}
else
if ((err = gpg_mpi_write (a, pk->pkey[i], NULL)))
goto leave;
write_16 (a, ski->csum );
}
@ -817,6 +903,8 @@ do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc )
{
if (enc->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1)
rc = gpg_mpi_write_nohdr (a, enc->data[i]);
else if (enc->pubkey_algo == PUBKEY_ALGO_ECDH)
rc = sos_write (a, enc->data[i], NULL);
else
rc = gpg_mpi_write (a, enc->data[i], NULL);
}
@ -1696,8 +1784,13 @@ do_signature( IOBUF out, int ctb, PKT_signature *sig )
n = pubkey_get_nsig( sig->pubkey_algo );
if ( !n )
write_fake_data( a, sig->data[0] );
for (i=0; i < n && !rc ; i++ )
rc = gpg_mpi_write (a, sig->data[i], NULL);
if (sig->pubkey_algo == PUBKEY_ALGO_ECDSA
|| sig->pubkey_algo == PUBKEY_ALGO_EDDSA)
for (i=0; i < n && !rc ; i++ )
rc = sos_write (a, sig->data[i], NULL);
else
for (i=0; i < n && !rc ; i++ )
rc = gpg_mpi_write (a, sig->data[i], NULL);
if (!rc)
{

View File

@ -42,6 +42,7 @@
#include "trustdb.h"
#include "call-agent.h"
#include "key-clean.h"
#include "pkglue.h"
/* An object to keep track of subkeys. */
@ -750,10 +751,8 @@ cleartext_secret_key_to_openpgp (gcry_sexp_t s_key, PKT_public_key *pk)
err = match_curve_skey_pk (key, pk);
if (err)
goto leave;
if (!err)
err = gcry_sexp_extract_param (key, NULL, "q",
&pub_params[0],
NULL);
else
err = sexp_extract_param_sos (key, "q", &pub_params[0]);
if (!err && (gcry_mpi_cmp(pk->pkey[1], pub_params[0])))
err = gpg_error (GPG_ERR_BAD_PUBKEY);
@ -764,9 +763,7 @@ cleartext_secret_key_to_openpgp (gcry_sexp_t s_key, PKT_public_key *pk)
{
gcry_mpi_release (pk->pkey[sec_start]);
pk->pkey[sec_start] = NULL;
err = gcry_sexp_extract_param (key, NULL, "d",
&pk->pkey[sec_start],
NULL);
err = sexp_extract_param_sos (key, "d", &pk->pkey[sec_start]);
}
if (!err)
@ -978,15 +975,16 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
value = gcry_sexp_nth_data (list, ++idx, &valuelen);
if (!value || !valuelen)
goto bad_seckey;
if (is_enc)
if (is_enc
|| pk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pk->pubkey_algo == PUBKEY_ALGO_EDDSA
|| pk->pubkey_algo == PUBKEY_ALGO_ECDH)
{
void *p = xtrymalloc (valuelen);
if (!p)
goto outofmem;
memcpy (p, value, valuelen);
skey[skeyidx] = gcry_mpi_set_opaque (NULL, p, valuelen*8);
skey[skeyidx] = gcry_mpi_set_opaque_copy (NULL, value, valuelen*8);
if (!skey[skeyidx])
goto outofmem;
if (is_enc)
gcry_mpi_set_flag (skey[skeyidx], GCRYMPI_FLAG_USER1);
}
else
{
@ -1144,7 +1142,7 @@ transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
/* Check that the first secret key parameter in SKEY is encrypted
and that there are no more secret key parameters. The latter is
guaranteed by the v4 packet format. */
if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE))
if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_USER1))
goto bad_seckey;
if (npkey+1 < DIM (skey) && skey[npkey+1])
goto bad_seckey;

View File

@ -1329,19 +1329,10 @@ ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo)
if (err)
goto leave;
l2 = gcry_sexp_find_token (list, "q", 0);
if (!l2)
{
err = gpg_error (GPG_ERR_NO_OBJ);
goto leave;
}
array[1] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
gcry_sexp_release (l2);
if (!array[1])
{
err = gpg_error (GPG_ERR_INV_OBJ);
goto leave;
}
err = sexp_extract_param_sos (list, "q", &array[1]);
if (err)
goto leave;
gcry_sexp_release (list);
if (algo == PUBKEY_ALGO_ECDH)

View File

@ -181,14 +181,23 @@ hash_public_key (gcry_md_hd_t md, PKT_public_key *pk)
else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE))
{
const void *p;
int is_sos = 0;
if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER2))
is_sos = 2;
p = gcry_mpi_get_opaque (pk->pkey[i], &nbits);
pp[i] = xmalloc ((nbits+7)/8);
pp[i] = xmalloc ((nbits+7)/8 + is_sos);
if (p)
memcpy (pp[i], p, (nbits+7)/8);
memcpy (pp[i] + is_sos, p, (nbits+7)/8);
else
pp[i] = NULL;
nn[i] = (nbits+7)/8;
if (is_sos)
{
pp[i][0] = (nbits >> 8);
pp[i][1] = nbits;
}
nn[i] = (nbits+7)/8 + is_sos;
n += nn[i];
}
else

View File

@ -188,6 +188,76 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
}
/* Read an external representation of an SOS and return the opaque MPI
with GCRYMPI_FLAG_USER2. The external format is a 16-bit unsigned
value stored in network byte order giving information for the
following octets.
The caller must set *RET_NREAD to the maximum number of bytes to
read from the pipeline INP. This function sets *RET_NREAD to be
the number of bytes actually read from the pipeline.
If SECURE is true, the integer is stored in secure memory
(allocated using gcry_xmalloc_secure). */
static gcry_mpi_t
sos_read (iobuf_t inp, unsigned int *ret_nread, int secure)
{
int c, c1, c2, i;
unsigned int nmax = *ret_nread;
unsigned int nbits, nbytes;
size_t nread = 0;
gcry_mpi_t a = NULL;
byte *buf = NULL;
byte *p;
if (!nmax)
goto overflow;
if ((c = c1 = iobuf_get (inp)) == -1)
goto leave;
if (++nread == nmax)
goto overflow;
nbits = c << 8;
if ((c = c2 = iobuf_get (inp)) == -1)
goto leave;
++nread;
nbits |= c;
if (nbits > MAX_EXTERN_MPI_BITS)
{
log_error ("mpi too large (%u bits)\n", nbits);
goto leave;
}
nbytes = (nbits + 7) / 8;
buf = secure ? gcry_xmalloc_secure (nbytes) : gcry_xmalloc (nbytes);
p = buf;
for (i = 0; i < nbytes; i++)
{
if (nread == nmax)
goto overflow;
c = iobuf_get (inp);
if (c == -1)
goto leave;
p[i] = c;
nread ++;
}
a = gcry_mpi_set_opaque (NULL, buf, nbits);
gcry_mpi_set_flag (a, GCRYMPI_FLAG_USER2);
*ret_nread = nread;
return a;
overflow:
log_error ("mpi larger than indicated length (%u bits)\n", 8*nmax);
leave:
*ret_nread = nread;
gcry_free(buf);
return a;
}
/* Register STRING as a known critical notation name. */
void
register_known_notation (const char *string)
@ -1328,11 +1398,22 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
{
for (i = 0; i < ndata; i++)
{
if (k->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1)
if (k->pubkey_algo == PUBKEY_ALGO_ECDH)
{
size_t n;
rc = read_size_body (inp, pktlen, &n, k->data+i);
pktlen -= n;
if (i == 1)
{
size_t n;
rc = read_size_body (inp, pktlen, &n, k->data+i);
pktlen -= n;
}
else
{
int n = pktlen;
k->data[i] = sos_read (inp, &n, 0);
pktlen -= n;
if (!k->data[i])
rc = gpg_error (GPG_ERR_INV_PACKET);
}
}
else
{
@ -2282,7 +2363,11 @@ parse_signature (IOBUF inp, int pkttype, unsigned long pktlen,
for (i = 0; i < ndata; i++)
{
n = pktlen;
sig->data[i] = mpi_read (inp, &n, 0);
if (sig->pubkey_algo == PUBKEY_ALGO_ECDSA
|| sig->pubkey_algo == PUBKEY_ALGO_EDDSA)
sig->data[i] = sos_read (inp, &n, 0);
else
sig->data[i] = mpi_read (inp, &n, 0);
pktlen -= n;
if (list_mode)
{
@ -2510,7 +2595,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
|| (algorithm == PUBKEY_ALGO_EDDSA && (i == 0))
|| (algorithm == PUBKEY_ALGO_ECDH && (i == 0 || i == 2)))
{
/* Read the OID (i==1) or the KDF params (i==2). */
/* Read the OID (i==0) or the KDF params (i==2). */
size_t n;
err = read_size_body (inp, pktlen, &n, pk->pkey+i);
pktlen -= n;
@ -2518,7 +2603,12 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
else
{
unsigned int n = pktlen;
pk->pkey[i] = mpi_read (inp, &n, 0);
if (algorithm == PUBKEY_ALGO_ECDSA
|| algorithm == PUBKEY_ALGO_EDDSA
|| algorithm == PUBKEY_ALGO_ECDH)
pk->pkey[i] = sos_read (inp, &n, 0);
else
pk->pkey[i] = mpi_read (inp, &n, 0);
pktlen -= n;
if (!pk->pkey[i])
err = gpg_error (GPG_ERR_INV_PACKET);
@ -2830,7 +2920,12 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
goto leave;
}
n = pktlen;
pk->pkey[i] = mpi_read (inp, &n, 0);
if (algorithm == PUBKEY_ALGO_ECDSA
|| algorithm == PUBKEY_ALGO_EDDSA
|| algorithm == PUBKEY_ALGO_ECDH)
pk->pkey[i] = sos_read (inp, &n, 0);
else
pk->pkey[i] = mpi_read (inp, &n, 0);
pktlen -= n;
if (list_mode)
{

View File

@ -47,6 +47,56 @@ get_mpi_from_sexp (gcry_sexp_t sexp, const char *item, int mpifmt)
}
/* Extract SOS representation from SEXP for PARAM, return the result
in R_SOS. */
gpg_error_t
sexp_extract_param_sos (gcry_sexp_t sexp, const char *param, gcry_mpi_t *r_sos)
{
gpg_error_t err;
gcry_sexp_t l2 = gcry_sexp_find_token (sexp, param, 0);
*r_sos = NULL;
if (!l2)
err = gpg_error (GPG_ERR_NO_OBJ);
else
{
size_t buflen;
void *p0 = gcry_sexp_nth_buffer (l2, 1, &buflen);
if (!p0)
err = gpg_error_from_syserror ();
else
{
gcry_mpi_t sos;
unsigned int nbits = buflen*8;
unsigned char *p = p0;
if (nbits >= 8 && !(*p & 0x80))
if (--nbits >= 7 && !(*p & 0x40))
if (--nbits >= 6 && !(*p & 0x20))
if (--nbits >= 5 && !(*p & 0x10))
if (--nbits >= 4 && !(*p & 0x08))
if (--nbits >= 3 && !(*p & 0x04))
if (--nbits >= 2 && !(*p & 0x02))
if (--nbits >= 1 && !(*p & 0x01))
--nbits;
sos = gcry_mpi_set_opaque (NULL, p0, nbits);
if (sos)
{
gcry_mpi_set_flag (sos, GCRYMPI_FLAG_USER2);
*r_sos = sos;
err = 0;
}
else
err = gpg_error_from_syserror ();
}
gcry_sexp_release (l2);
}
return err;
}
/****************
* Emulate our old PK interface here - sometime in the future we might
@ -152,6 +202,7 @@ pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash,
gcry_mpi_t s = data[1];
size_t rlen, slen, n; /* (bytes) */
char buf[64];
unsigned int nbits;
log_assert (neededfixedlen <= sizeof buf);
@ -182,6 +233,17 @@ pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash,
memset (buf, 0, neededfixedlen - n);
r = gcry_mpi_set_opaque_copy (NULL, buf, neededfixedlen * 8);
}
else if (rlen < neededfixedlen
&& gcry_mpi_get_flag (r, GCRYMPI_FLAG_OPAQUE))
{
const unsigned char *p;
p = gcry_mpi_get_opaque (r, &nbits);
n = (nbits+7)/8;
memcpy (buf + (neededfixedlen - n), p, n);
memset (buf, 0, neededfixedlen - n);
gcry_mpi_set_opaque_copy (r, buf, neededfixedlen * 8);
}
if (slen < neededfixedlen
&& !gcry_mpi_get_flag (s, GCRYMPI_FLAG_OPAQUE)
&& !(rc=gcry_mpi_print (GCRYMPI_FMT_USG, buf, sizeof buf, &n, s)))
@ -191,6 +253,17 @@ pk_verify (pubkey_algo_t pkalgo, gcry_mpi_t hash,
memset (buf, 0, neededfixedlen - n);
s = gcry_mpi_set_opaque_copy (NULL, buf, neededfixedlen * 8);
}
else if (slen < neededfixedlen
&& gcry_mpi_get_flag (s, GCRYMPI_FLAG_OPAQUE))
{
const unsigned char *p;
p = gcry_mpi_get_opaque (s, &nbits);
n = (nbits+7)/8;
memcpy (buf + (neededfixedlen - n), p, n);
memset (buf, 0, neededfixedlen - n);
gcry_mpi_set_opaque_copy (s, buf, neededfixedlen * 8);
}
if (!rc)
rc = gcry_sexp_build (&s_sig, NULL,
@ -315,7 +388,7 @@ pk_encrypt (pubkey_algo_t algo, gcry_mpi_t *resarr, gcry_mpi_t data,
/* Get the shared point and the ephemeral public key. */
shared = get_mpi_from_sexp (s_ciph, "s", GCRYMPI_FMT_USG);
public = get_mpi_from_sexp (s_ciph, "e", GCRYMPI_FMT_USG);
rc = sexp_extract_param_sos (s_ciph, "e", &public);
gcry_sexp_release (s_ciph);
s_ciph = NULL;
if (DBG_CRYPTO)
@ -329,7 +402,8 @@ pk_encrypt (pubkey_algo_t algo, gcry_mpi_t *resarr, gcry_mpi_t data,
fingerprint_from_pk (pk, fp, &fpn);
if (fpn != 20)
rc = gpg_error (GPG_ERR_INV_LENGTH);
else
if (!rc)
rc = pk_ecdh_encrypt_with_shared_point (shared,
fp, data, pkey, &result);
gcry_mpi_release (shared);

View File

@ -24,6 +24,8 @@
/*-- pkglue.c --*/
gcry_mpi_t get_mpi_from_sexp (gcry_sexp_t sexp, const char *item, int mpifmt);
gpg_error_t sexp_extract_param_sos (gcry_sexp_t sexp, const char *param,
gcry_mpi_t *r_sos);
int pk_verify (pubkey_algo_t algo, gcry_mpi_t hash, gcry_mpi_t *data,
gcry_mpi_t *pkey);

View File

@ -82,7 +82,6 @@ encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits)
byte *frame;
int i,n;
u16 csum;
gcry_mpi_t a;
if (DBG_CRYPTO)
log_debug ("encode_session_key: encoding %d byte DEK", dek->keylen);
@ -124,10 +123,7 @@ encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits)
(int) nframe, frame[0], frame[1], frame[2],
frame[nframe-3], frame[nframe-2], frame[nframe-1]);
if (gcry_mpi_scan (&a, GCRYMPI_FMT_USG, frame, nframe, &nframe))
BUG();
xfree(frame);
return a;
return gcry_mpi_set_opaque (NULL, frame, 8*nframe);
}
/* The current limitation is that we can only use a session key
@ -195,10 +191,7 @@ encode_session_key (int openpgp_pk_algo, DEK *dek, unsigned int nbits)
frame[n++] = csum >>8;
frame[n++] = csum;
log_assert (n == nframe);
if (gcry_mpi_scan( &a, GCRYMPI_FMT_USG, frame, n, &nframe))
BUG();
xfree (frame);
return a;
return gcry_mpi_set_opaque (NULL, frame, 8*n);
}

View File

@ -505,10 +505,12 @@ do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig,
else if (pksk->pubkey_algo == GCRY_PK_RSA
|| pksk->pubkey_algo == GCRY_PK_RSA_S)
sig->data[0] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG);
else if (openpgp_oid_is_ed25519 (pksk->pkey[0]))
else if (pksk->pubkey_algo == PUBKEY_ALGO_ECDSA
|| pksk->pubkey_algo == PUBKEY_ALGO_EDDSA)
{
sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_OPAQUE);
sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_OPAQUE);
err = sexp_extract_param_sos (s_sigval, "r", &sig->data[0]);
if (!err)
err = sexp_extract_param_sos (s_sigval, "s", &sig->data[1]);
}
else
{