cipher/ecc.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++-- cipher/pubkey-util.c | 23 ++++++++++++++++- src/cipher.h | 2 +- 3 files changed, 94 insertions(+), 4 deletions(-) diff --git a/cipher/ecc.c b/cipher/ecc.c index 3f221a2..ce4db8f 100644 --- a/cipher/ecc.c +++ b/cipher/ecc.c @@ -1264,6 +1264,7 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) gcry_mpi_t mpi_s = NULL; gcry_mpi_t mpi_e = NULL; gcry_mpi_t data = NULL; + gcry_mpi_t salt = NULL; ECC_public_key pk; mpi_ec_t ec = NULL; int flags = 0; @@ -1345,6 +1346,29 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) mpi_clear_bit (data, i); mpi_set_highbit (data, mpi_get_nbits (pk.E.p) - 1); } + + /* For GOST extract the UKM value. If its length is unspecified + take 64 bits as default. */ + if ((flags & PUBKEY_FLAG_GOST)) + { + unsigned int ukm_blen = ctx.saltlen ? ctx.saltlen : 64; + if (_gcry_mpi_get_nbits (data) < ukm_blen) + { + rc = GPG_ERR_TOO_SHORT; + goto leave; + } + salt = _gcry_mpi_copy (data); + if (!salt) + { + rc = gpg_error_from_syserror (); + goto leave; + } + _gcry_mpi_clear_highbit (salt, ukm_blen); + if (DBG_CIPHER) + log_printmpi ("UKM: ", salt); + _gcry_mpi_rshift (data, data, ukm_blen); + } + if (DBG_CIPHER) log_mpidump ("ecc_encrypt data", data); @@ -1404,6 +1428,10 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) /* R = kQ <=> R = kdG */ _gcry_mpi_ec_mul_point (&R, data, &pk.Q, ec); + /* Multiply the resulting point by a salt value if any. */ + if (salt) + _gcry_mpi_ec_mul_point (&R, salt, &R, ec); + if (_gcry_mpi_ec_get_affine (x, y, &R, ec)) { /* @@ -1471,7 +1499,14 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) } if (!rc) - rc = sexp_build (r_ciph, NULL, "(enc-val(ecdh(s%m)(e%m)))", mpi_s, mpi_e); + { + if (DBG_CIPHER) + { + log_printmpi ("ecc_encrypt res", mpi_s); + log_printmpi ("ecc_encrypt public key e", mpi_e); + } + rc = sexp_build (r_ciph, NULL, "(enc-val(ecdh(s%m)(e%m)))", mpi_s, mpi_e); + } leave: _gcry_mpi_release (pk.E.p); @@ -1490,6 +1525,7 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms) sexp_release (l1); _gcry_mpi_ec_free (ec); _gcry_pk_util_free_encoding_ctx (&ctx); + _gcry_mpi_release (salt); if (DBG_CIPHER) log_debug ("ecc_encrypt => %s\n", gpg_strerror (rc)); return rc; @@ -1518,6 +1554,7 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) mpi_point_struct kG; mpi_point_struct R; gcry_mpi_t r = NULL; + gcry_mpi_t salt = NULL; int flags = 0; memset (&sk, 0, sizeof sk); @@ -1613,10 +1650,35 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) goto leave; } - ec = _gcry_mpi_ec_p_internal_new (sk.E.model, sk.E.dialect, flags, sk.E.p, sk.E.a, sk.E.b); + /* For GOST extract the UKM value. */ + if ((flags & PUBKEY_FLAG_GOST) || 0 == strncmp ("GOST", sk.E.name, 4)) + { + // FIXME: Expect an uncompressed point format 0x04... + int key_len = 2*nbits/8 + 1; + int data_len = (_gcry_mpi_get_nbits (data_e)+7)/8; + int ukm_blen = (data_len - key_len) * 8; + if (ukm_blen < 64) + { + rc = GPG_ERR_TOO_SHORT; + goto leave; + } + salt = _gcry_mpi_copy (data_e); + if (!salt) + { + rc = gpg_error_from_syserror (); + goto leave; + } + _gcry_mpi_clear_highbit (salt, ukm_blen); + if (DBG_CIPHER) + log_printmpi ("UKM: ", salt); + _gcry_mpi_rshift (data_e, data_e, ukm_blen); + if (DBG_CIPHER) + log_printmpi ("ecc_decrypt d_e", data_e); + } + /* * Compute the plaintext. */ @@ -1630,6 +1692,7 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) if (DBG_CIPHER) log_printpnt ("ecc_decrypt kG", &kG, NULL); + if ((flags & PUBKEY_FLAG_DJB_TWEAK)) { /* For X25519, by its definition, validation should not be done. */ @@ -1651,9 +1714,14 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) goto leave; } + /* R = dkG */ _gcry_mpi_ec_mul_point (&R, sk.d, &kG, ec); + /* Multiply the resulting point by a salt value if any. */ + if (salt) + _gcry_mpi_ec_mul_point (&R, salt, &R, ec); + /* The following is false: assert( mpi_cmp_ui( R.x, 1 )==0 );, so: */ { gcry_mpi_t x, y; @@ -1743,6 +1811,7 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms) sexp_release (l1); _gcry_mpi_ec_free (ec); _gcry_pk_util_free_encoding_ctx (&ctx); + _gcry_mpi_release (salt); if (DBG_CIPHER) log_debug ("ecc_decrypt => %s\n", gpg_strerror (rc)); return rc; diff --git a/cipher/pubkey-util.c b/cipher/pubkey-util.c index c40ef97..66278b6 100644 --- a/cipher/pubkey-util.c +++ b/cipher/pubkey-util.c @@ -649,7 +649,7 @@ _gcry_pk_util_free_encoding_ctx (struct pk_encoding_ctx *ctx) () or (data - [(flags [raw, direct, pkcs1, oaep, pss, no-blinding, rfc6979, eddsa])] + [(flags [raw, direct, pkcs1, oaep, pss, no-blinding, rfc6979, eddsa, gost])] [(hash )] [(value )] [(hash-algo )] @@ -666,6 +666,7 @@ _gcry_pk_util_free_encoding_ctx (struct pk_encoding_ctx *ctx) LABEL is specific to OAEP. SALT-LENGTH is for PSS it is limited to 16384 bytes. + For GOST a SALT-LENGTH means the length of UKM in bits. RANDOM-OVERRIDE is used to replace random nonces for regression testing. */ @@ -812,6 +813,26 @@ _gcry_pk_util_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi, *ret_mpi = sexp_nth_mpi (lvalue, 1, GCRYMPI_FMT_USG); if (!*ret_mpi) rc = GPG_ERR_INV_OBJ; + + if (parsed_flags & PUBKEY_FLAG_GOST) + { + gcry_sexp_t list; + /* Get SALT-LENGTH (UKM length). */ + list = sexp_find_token (ldata, "salt-length", 0); + if (list) + { + s = sexp_nth_data (list, 1, &n); + if (!s) + { + rc = GPG_ERR_NO_OBJ; + goto leave; + } + ctx->saltlen = (unsigned int)strtoul (s, NULL, 10); + sexp_release (list); + } + else + ctx->saltlen = 0; + } } else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lvalue && ctx->op == PUBKEY_OP_ENCRYPT) diff --git a/src/cipher.h b/src/cipher.h index f2acb55..197dabc 100644 --- a/src/cipher.h +++ b/src/cipher.h @@ -76,7 +76,7 @@ struct pk_encoding_ctx unsigned char *label; size_t labellen; - /* for PSS */ + /* for PSS or GOST (UKM length in bits)*/ size_t saltlen; int (* verify_cmp) (void *opaque, gcry_mpi_t tmp);