diff --git a/g10/build-packet.c b/g10/build-packet.c --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -364,7 +364,71 @@ /* - * 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 @@ || (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 @@ 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 @@ } 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 @@ { 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 @@ 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) { diff --git a/g10/export.c b/g10/export.c --- a/g10/export.c +++ b/g10/export.c @@ -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 @@ 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 @@ { 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 @@ 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 @@ /* 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; diff --git a/g10/keygen.c b/g10/keygen.c --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1329,19 +1329,10 @@ 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) diff --git a/g10/keyid.c b/g10/keyid.c --- a/g10/keyid.c +++ b/g10/keyid.c @@ -181,14 +181,23 @@ 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 diff --git a/g10/parse-packet.c b/g10/parse-packet.c --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -188,6 +188,76 @@ } +/* 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 @@ { 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 @@ 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 @@ || (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 @@ 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 @@ 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) { diff --git a/g10/pkglue.h b/g10/pkglue.h --- a/g10/pkglue.h +++ b/g10/pkglue.h @@ -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); diff --git a/g10/pkglue.c b/g10/pkglue.c --- a/g10/pkglue.c +++ b/g10/pkglue.c @@ -47,6 +47,56 @@ } +/* 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 @@ 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 @@ 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 @@ 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 @@ /* 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 @@ 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); diff --git a/g10/seskey.c b/g10/seskey.c --- a/g10/seskey.c +++ b/g10/seskey.c @@ -82,7 +82,6 @@ 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 @@ (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 @@ 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); } diff --git a/g10/sign.c b/g10/sign.c --- a/g10/sign.c +++ b/g10/sign.c @@ -505,10 +505,12 @@ 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 {