gpg: Handle leading-zeros private key for Ed25519.

* g10/parse-packet.c (mpi_read_detect_0_removal): New.
(parse_key): Use mpi_read_detect_0_removal for PUBKEY_ALGO_EDDSA
to tweak the checksum.

--

GnuPG-bug-id: 5120
Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
This commit is contained in:
NIIBE Yutaka 2022-04-28 15:12:01 +09:00
parent 3192939a10
commit 3fcef73714
1 changed files with 94 additions and 1 deletions

View File

@ -188,6 +188,92 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
}
/* Read an external representation (which is possibly an SOS) and
return the MPI. 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
mpi_read_detect_0_removal (iobuf_t inp, unsigned int *ret_nread, int secure,
u16 *r_csum_tweak)
{
int c, c1, c2, i;
unsigned int nmax = *ret_nread;
unsigned int nbits, nbits1, 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 + 2) : gcry_xmalloc (nbytes + 2);
p = buf;
p[0] = c1;
p[1] = c2;
for (i = 0; i < nbytes; i++)
{
if (nread == nmax)
goto overflow;
c = iobuf_get (inp);
if (c == -1)
goto leave;
p[i + 2] = c;
nread ++;
}
if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread))
a = NULL;
/* Possibly, it has leading zeros. */
nbits1 = gcry_mpi_get_nbits (a);
if (nbits > nbits1)
{
*r_csum_tweak -= (nbits >> 8);
*r_csum_tweak -= (nbits & 0xff);
*r_csum_tweak += (nbits1 >> 8);
*r_csum_tweak += (nbits1 & 0xff);
}
*ret_nread = nread;
gcry_free(buf);
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)
@ -2724,6 +2810,8 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
}
else
{
u16 csum_tweak = 0;
/* Not encrypted. */
for (i = npkey; i < nskey; i++)
{
@ -2735,7 +2823,11 @@ 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_EDDSA)
pk->pkey[i] = mpi_read_detect_0_removal (inp, &n, 0,
&csum_tweak);
else
pk->pkey[i] = mpi_read (inp, &n, 0);
pktlen -= n;
if (list_mode)
{
@ -2756,6 +2848,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
goto leave;
}
ski->csum = read_16 (inp);
ski->csum += csum_tweak;
pktlen -= 2;
if (list_mode)
es_fprintf (listfp, "\tchecksum: %04hx\n", ski->csum);