gpg: Implement a parser for Kyber encrypted packets.

* g10/misc.c (pubkey_get_nenc): Add ky768 and ky1024 values.
* g10/parse-packet.c (read_octet_string): New.
(read_size_body): Rename to ...
(read_sized_octet_string): this and change args to update-able PKTLEN.
(parse_pubkeyenc): Split general parsing loop for easier reading.
Implement parser for the Kyber algorithms.
--

Take care: this has not been tested at all, it merely passes the
regression test for the other algos.

Kyber is also known as ML-KEM in FIPS-203.

The list mode is slighly changed: In case of a parsing error no data
is printed - before that already parsed data was printed.

GnuPG-bug-id: 6815
This commit is contained in:
Werner Koch 2023-11-13 16:10:05 +01:00
parent ec1446f944
commit 6fab7b075a
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 137 additions and 54 deletions

View File

@ -169,11 +169,11 @@ typedef enum
PUBKEY_ALGO_ELGAMAL = 20, /* Elgamal encrypt+sign (legacy). */
/* 21 reserved by OpenPGP. */
PUBKEY_ALGO_EDDSA = 22, /* EdDSA. */
PUBKEY_ALGO_KY768_25519 = 29, /* Kyber768 + X25519 */
PUBKEY_ALGO_KY1024_448 = 30, /* Kyber1024 + X448 */
PUBKEY_ALGO_DIL3_25519 = 35, /* Dilithium3 + Ed25519 */
PUBKEY_ALGO_DIL5_448 = 36, /* Dilithium5 + Ed448 */
PUBKEY_ALGO_SPHINX_SHA2 = 41, /* SPHINX+-simple-SHA2 */
PUBKEY_ALGO_KY768_25519 = 29, /* Kyber768 + X25519 (aka ML-KEM-768) */
PUBKEY_ALGO_KY1024_448 = 30, /* Kyber1024 + X448 (aka ML-KEM-1024) */
PUBKEY_ALGO_DIL3_25519 = 35, /* Dilithium3 + Ed25519 (aka ML-DSA-65) */
PUBKEY_ALGO_DIL5_448 = 36, /* Dilithium5 + Ed448 (aka ML-DSA-87) */
PUBKEY_ALGO_SPHINX_SHA2 = 41, /* SPHINX+-simple-SHA2 (aka SLH-DSA-SHA2) */
PUBKEY_ALGO_PRIVATE10 = 110
}
pubkey_algo_t;
@ -208,7 +208,7 @@ compress_algo_t;
#define OPENPGP_MAX_NPKEY 5 /* Maximum number of public key parameters. */
#define OPENPGP_MAX_NSKEY 7 /* Maximum number of secret key parameters. */
#define OPENPGP_MAX_NSIG 2 /* Maximum number of signature parameters. */
#define OPENPGP_MAX_NENC 2 /* Maximum number of encryption parameters. */
#define OPENPGP_MAX_NENC 4 /* Maximum number of encryption parameters. */
/* Decode an rfc4880 encoded S2K count. */

View File

@ -1783,6 +1783,8 @@ pubkey_get_nenc (pubkey_algo_t algo)
case PUBKEY_ALGO_ECDSA: return 0;
case PUBKEY_ALGO_ELGAMAL: return 2;
case PUBKEY_ALGO_EDDSA: return 0;
case PUBKEY_ALGO_KY768_25519: return 4;
case PUBKEY_ALGO_KY1024_448: return 4;
default: return 0;
}
}

View File

@ -188,6 +188,65 @@ mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
}
/* Read an octet string of length NBYTES from INP and return it at
* R_DATA. On error return an error code and store NULL at R_DATA.
* PKTLEN shall give the current lenhgth of the packt and is updated
* with each read. If SECURE is true, the integer is stored in secure
* memory (allocated using gcry_xmalloc_secure). */
static gpg_error_t
read_octet_string (iobuf_t inp, unsigned long *pktlen, unsigned int nbytes,
int secure, gcry_mpi_t *r_data)
{
gpg_error_t err;
int c, i;
byte *buf = NULL;
byte *p;
*r_data = NULL;
if (nbytes*8 > MAX_EXTERN_MPI_BITS)
{
log_error ("octet string too large (%u octets)\n", nbytes);
err = gpg_error (GPG_ERR_TOO_LARGE);
goto leave;
}
if (nbytes > *pktlen)
{
log_error ("octet string larger than packet (%u octets)\n", nbytes);
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
buf = secure ? gcry_malloc_secure (nbytes) : gcry_malloc (nbytes);
if (!buf)
{
err = gpg_error_from_syserror ();
goto leave;
}
p = buf;
for (i = 0; i < nbytes; i++)
{
c = iobuf_get (inp);
if (c == -1)
{
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
p[i] = c;
--*pktlen;
}
*r_data = gcry_mpi_set_opaque (NULL, buf, nbytes*8);
gcry_mpi_set_flag (*r_data, GCRYMPI_FLAG_USER2);
return 0;
leave:
gcry_free (buf);
return err;
}
/* 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
@ -1102,32 +1161,29 @@ read_rest (IOBUF inp, size_t pktlen)
/* Read a special size+body from INP. On success store an opaque MPI
with it at R_DATA. On error return an error code and store NULL at
R_DATA. Even in the error case store the number of read bytes at
R_NREAD. The caller shall pass the remaining size of the packet in
PKTLEN. */
* with it at R_DATA. The caller shall store the remaining size of
* the packet at PKTLEN. On error return an error code and store NULL
* at R_DATA. Even in the error case store the number of read bytes
* at PKTLEN is updated. */
static gpg_error_t
read_size_body (iobuf_t inp, int pktlen, size_t *r_nread,
gcry_mpi_t *r_data)
read_sized_octet_string (iobuf_t inp, unsigned long *pktlen, gcry_mpi_t *r_data)
{
char buffer[256];
char *tmpbuf;
int i, c, nbytes;
*r_nread = 0;
*r_data = NULL;
if (!pktlen)
if (!*pktlen)
return gpg_error (GPG_ERR_INV_PACKET);
c = iobuf_readbyte (inp);
if (c < 0)
return gpg_error (GPG_ERR_INV_PACKET);
pktlen--;
++*r_nread;
--*pktlen;
nbytes = c;
if (nbytes < 2 || nbytes > 254)
return gpg_error (GPG_ERR_INV_PACKET);
if (nbytes > pktlen)
if (nbytes > *pktlen)
return gpg_error (GPG_ERR_INV_PACKET);
buffer[0] = nbytes;
@ -1137,7 +1193,7 @@ read_size_body (iobuf_t inp, int pktlen, size_t *r_nread,
c = iobuf_get (inp);
if (c < 0)
return gpg_error (GPG_ERR_INV_PACKET);
++*r_nread;
--*pktlen;
buffer[1+i] = c;
}
@ -1350,7 +1406,9 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
{
int rc = 0;
int i, ndata;
unsigned int n;
PKT_pubkey_enc *k;
int is_ky1024 = 0;
k = packet->pkt.pubkey_enc = xmalloc_clear (sizeof *packet->pkt.pubkey_enc);
if (pktlen < 12)
@ -1392,46 +1450,71 @@ parse_pubkeyenc (IOBUF inp, int pkttype, unsigned long pktlen,
unknown_pubkey_warning (k->pubkey_algo);
k->data[0] = NULL; /* No need to store the encrypted data. */
}
else if (k->pubkey_algo == PUBKEY_ALGO_ECDH)
{
log_assert (ndata == 2);
/* Get the ephemeral public key. */
n = pktlen;
k->data[0] = sos_read (inp, &n, 0);
pktlen -= n;
if (!k->data[0])
{
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
/* Get the wrapped symmetric key. */
rc = read_sized_octet_string (inp, &pktlen, k->data + 1);
if (rc)
goto leave;
}
else if (k->pubkey_algo == PUBKEY_ALGO_KY768_25519
|| (is_ky1024 = (k->pubkey_algo == PUBKEY_ALGO_KY1024_448)))
{
log_assert (ndata == 4);
/* Get the ephemeral public key. */
n = is_ky1024? 56 : 32;
rc = read_octet_string (inp, &pktlen, n, 0, k->data + 0);
if (rc)
goto leave;
/* Get the Kyber ciphertext. */
n = is_ky1024? 1568 : 1088;
rc = read_octet_string (inp, &pktlen, n, 0, k->data + 1);
if (rc)
goto leave;
/* Get the algorithm id. */
n = 1;
rc = read_octet_string (inp, &pktlen, n, 0, k->data + 2);
if (rc)
goto leave;
/* Get the wrapped symmetric key. */
rc = read_sized_octet_string (inp, &pktlen, k->data + 3);
if (rc)
goto leave;
}
else
{
for (i = 0; i < ndata; i++)
{
if (k->pubkey_algo == PUBKEY_ALGO_ECDH)
{
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
{
int n = pktlen;
k->data[i] = mpi_read (inp, &n, 0);
pktlen -= n;
if (!k->data[i])
rc = gpg_error (GPG_ERR_INV_PACKET);
}
if (rc)
goto leave;
if (list_mode)
{
es_fprintf (listfp, "\tdata: ");
mpi_print (listfp, k->data[i], mpi_print_mode);
es_putc ('\n', listfp);
}
n = pktlen;
k->data[i] = mpi_read (inp, &n, 0);
pktlen -= n;
if (!k->data[i])
rc = gpg_error (GPG_ERR_INV_PACKET);
}
if (rc)
goto leave;
}
if (list_mode)
{
for (i = 0; i < ndata; i++)
{
es_fprintf (listfp, "\tdata: ");
mpi_print (listfp, k->data[i], mpi_print_mode);
es_putc ('\n', listfp);
}
}
leave:
iobuf_skip_rest (inp, pktlen, 0);
return rc;
@ -2601,9 +2684,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen,
|| (algorithm == PUBKEY_ALGO_ECDH && (i == 0 || 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;
err = read_sized_octet_string (inp, &pktlen, pk->pkey+i);
}
else
{