/* Helper to extract an MPI from key parameters. */ static gpg_err_code_t mpi_from_keyparam (gcry_mpi_t *r_a, gcry_sexp_t keyparam, const char *name) { gcry_err_code_t ec = 0; gcry_sexp_t l1; l1 = sexp_find_token (keyparam, name, 0); if (l1) { *r_a = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_USG); sexp_release (l1); if (!*r_a) ec = GPG_ERR_INV_OBJ; } return ec; }
/* Take the hash value and convert into an MPI, suitable for passing to the low level functions. We currently support the old style way of passing just a MPI and the modern interface which allows to pass flags so that we can choose between raw and pkcs1 padding - may be more padding options later. (<mpi>) or (data [(flags [raw, direct, pkcs1, oaep, pss, no-blinding, rfc6979, eddsa])] [(hash <algo> <value>)] [(value <text>)] [(hash-algo <algo>)] [(label <label>)] [(salt-length <length>)] [(random-override <data>)] ) Either the VALUE or the HASH element must be present for use with signatures. VALUE is used for encryption. HASH-ALGO is specific to OAEP and EDDSA. LABEL is specific to OAEP. SALT-LENGTH is for PSS. RANDOM-OVERRIDE is used to replace random nonces for regression testing. */ gcry_err_code_t _gcry_pk_util_data_to_mpi (gcry_sexp_t input, gcry_mpi_t *ret_mpi, struct pk_encoding_ctx *ctx) { gcry_err_code_t rc = 0; gcry_sexp_t ldata, lhash, lvalue; size_t n; const char *s; int unknown_flag = 0; int parsed_flags = 0; *ret_mpi = NULL; ldata = sexp_find_token (input, "data", 0); if (!ldata) { /* assume old style */ *ret_mpi = sexp_nth_mpi (input, 0, 0); return *ret_mpi ? GPG_ERR_NO_ERROR : GPG_ERR_INV_OBJ; } /* See whether there is a flags list. */ { gcry_sexp_t lflags = sexp_find_token (ldata, "flags", 0); if (lflags) { if (_gcry_pk_util_parse_flaglist (lflags, &parsed_flags, &ctx->encoding)) unknown_flag = 1; sexp_release (lflags); } } if (ctx->encoding == PUBKEY_ENC_UNKNOWN) ctx->encoding = PUBKEY_ENC_RAW; /* default to raw */ /* Get HASH or MPI */ lhash = sexp_find_token (ldata, "hash", 0); lvalue = lhash? NULL : sexp_find_token (ldata, "value", 0); if (!(!lhash ^ !lvalue)) rc = GPG_ERR_INV_OBJ; /* none or both given */ else if (unknown_flag) rc = GPG_ERR_INV_FLAG; else if (ctx->encoding == PUBKEY_ENC_RAW && (parsed_flags & PUBKEY_FLAG_EDDSA)) { /* Prepare for EdDSA. */ gcry_sexp_t list; void *value; size_t valuelen; if (!lvalue) { rc = GPG_ERR_INV_OBJ; goto leave; } /* Get HASH-ALGO. */ list = sexp_find_token (ldata, "hash-algo", 0); if (list) { s = sexp_nth_data (list, 1, &n); if (!s) rc = GPG_ERR_NO_OBJ; else { ctx->hash_algo = get_hash_algo (s, n); if (!ctx->hash_algo) rc = GPG_ERR_DIGEST_ALGO; } sexp_release (list); } else rc = GPG_ERR_INV_OBJ; if (rc) goto leave; /* Get VALUE. */ value = sexp_nth_buffer (lvalue, 1, &valuelen); if (!value) { /* We assume that a zero length message is meant by "(value)". This is commonly used by test vectors. Note that S-expression do not allow zero length items. */ valuelen = 0; value = xtrymalloc (1); if (!value) rc = gpg_err_code_from_syserror (); } else if ((valuelen * 8) < valuelen) { xfree (value); rc = GPG_ERR_TOO_LARGE; } if (rc) goto leave; /* Note that mpi_set_opaque takes ownership of VALUE. */ *ret_mpi = mpi_set_opaque (NULL, value, valuelen*8); } else if (ctx->encoding == PUBKEY_ENC_RAW && lhash && ((parsed_flags & PUBKEY_FLAG_RAW_FLAG) || (parsed_flags & PUBKEY_FLAG_RFC6979))) { /* Raw encoding along with a hash element. This is commonly used for DSA. For better backward error compatibility we allow this only if either the rfc6979 flag has been given or the raw flags was explicitly given. */ if (sexp_length (lhash) != 3) rc = GPG_ERR_INV_OBJ; else if ( !(s=sexp_nth_data (lhash, 1, &n)) || !n ) rc = GPG_ERR_INV_OBJ; else { void *value; size_t valuelen; ctx->hash_algo = get_hash_algo (s, n); if (!ctx->hash_algo) rc = GPG_ERR_DIGEST_ALGO; else if (!(value=sexp_nth_buffer (lhash, 2, &valuelen))) rc = GPG_ERR_INV_OBJ; else if ((valuelen * 8) < valuelen) { xfree (value); rc = GPG_ERR_TOO_LARGE; } else *ret_mpi = mpi_set_opaque (NULL, value, valuelen*8); } } else if (ctx->encoding == PUBKEY_ENC_RAW && lvalue) { /* RFC6969 may only be used with the a hash value and not the MPI based value. */ if (parsed_flags & PUBKEY_FLAG_RFC6979) { rc = GPG_ERR_CONFLICT; goto leave; } /* Get the value */ *ret_mpi = sexp_nth_mpi (lvalue, 1, GCRYMPI_FMT_USG); if (!*ret_mpi) rc = GPG_ERR_INV_OBJ; } else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lvalue && ctx->op == PUBKEY_OP_ENCRYPT) { const void * value; size_t valuelen; gcry_sexp_t list; void *random_override = NULL; size_t random_override_len = 0; if ( !(value=sexp_nth_data (lvalue, 1, &valuelen)) || !valuelen ) rc = GPG_ERR_INV_OBJ; else { /* Get optional RANDOM-OVERRIDE. */ list = sexp_find_token (ldata, "random-override", 0); if (list) { s = sexp_nth_data (list, 1, &n); if (!s) rc = GPG_ERR_NO_OBJ; else if (n > 0) { random_override = xtrymalloc (n); if (!random_override) rc = gpg_err_code_from_syserror (); else { memcpy (random_override, s, n); random_override_len = n; } } sexp_release (list); if (rc) goto leave; } rc = _gcry_rsa_pkcs1_encode_for_enc (ret_mpi, ctx->nbits, value, valuelen, random_override, random_override_len); xfree (random_override); } } else if (ctx->encoding == PUBKEY_ENC_PKCS1 && lhash && (ctx->op == PUBKEY_OP_SIGN || ctx->op == PUBKEY_OP_VERIFY)) { if (sexp_length (lhash) != 3) rc = GPG_ERR_INV_OBJ; else if ( !(s=sexp_nth_data (lhash, 1, &n)) || !n ) rc = GPG_ERR_INV_OBJ; else { const void * value; size_t valuelen; ctx->hash_algo = get_hash_algo (s, n); if (!ctx->hash_algo) rc = GPG_ERR_DIGEST_ALGO; else if ( !(value=sexp_nth_data (lhash, 2, &valuelen)) || !valuelen ) rc = GPG_ERR_INV_OBJ; else rc = _gcry_rsa_pkcs1_encode_for_sig (ret_mpi, ctx->nbits, value, valuelen, ctx->hash_algo); } } else if (ctx->encoding == PUBKEY_ENC_OAEP && lvalue && ctx->op == PUBKEY_OP_ENCRYPT) { const void * value; size_t valuelen; if ( !(value=sexp_nth_data (lvalue, 1, &valuelen)) || !valuelen ) rc = GPG_ERR_INV_OBJ; else { gcry_sexp_t list; void *random_override = NULL; size_t random_override_len = 0; /* Get HASH-ALGO. */ list = sexp_find_token (ldata, "hash-algo", 0); if (list) { s = sexp_nth_data (list, 1, &n); if (!s) rc = GPG_ERR_NO_OBJ; else { ctx->hash_algo = get_hash_algo (s, n); if (!ctx->hash_algo) rc = GPG_ERR_DIGEST_ALGO; } sexp_release (list); if (rc) goto leave; } /* Get LABEL. */ list = sexp_find_token (ldata, "label", 0); if (list) { s = sexp_nth_data (list, 1, &n); if (!s) rc = GPG_ERR_NO_OBJ; else if (n > 0) { ctx->label = xtrymalloc (n); if (!ctx->label) rc = gpg_err_code_from_syserror (); else { memcpy (ctx->label, s, n); ctx->labellen = n; } } sexp_release (list); if (rc) goto leave; } /* Get optional RANDOM-OVERRIDE. */ list = sexp_find_token (ldata, "random-override", 0); if (list) { s = sexp_nth_data (list, 1, &n); if (!s) rc = GPG_ERR_NO_OBJ; else if (n > 0) { random_override = xtrymalloc (n); if (!random_override) rc = gpg_err_code_from_syserror (); else { memcpy (random_override, s, n); random_override_len = n; } } sexp_release (list); if (rc) goto leave; } rc = _gcry_rsa_oaep_encode (ret_mpi, ctx->nbits, ctx->hash_algo, value, valuelen, ctx->label, ctx->labellen, random_override, random_override_len); xfree (random_override); } } else if (ctx->encoding == PUBKEY_ENC_PSS && lhash && ctx->op == PUBKEY_OP_SIGN) { if (sexp_length (lhash) != 3) rc = GPG_ERR_INV_OBJ; else if ( !(s=sexp_nth_data (lhash, 1, &n)) || !n ) rc = GPG_ERR_INV_OBJ; else { const void * value; size_t valuelen; void *random_override = NULL; size_t random_override_len = 0; ctx->hash_algo = get_hash_algo (s, n); if (!ctx->hash_algo) rc = GPG_ERR_DIGEST_ALGO; else if ( !(value=sexp_nth_data (lhash, 2, &valuelen)) || !valuelen ) rc = GPG_ERR_INV_OBJ; else { gcry_sexp_t list; /* Get SALT-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); } /* Get optional RANDOM-OVERRIDE. */ list = sexp_find_token (ldata, "random-override", 0); if (list) { s = sexp_nth_data (list, 1, &n); if (!s) rc = GPG_ERR_NO_OBJ; else if (n > 0) { random_override = xtrymalloc (n); if (!random_override) rc = gpg_err_code_from_syserror (); else { memcpy (random_override, s, n); random_override_len = n; } } sexp_release (list); if (rc) goto leave; } /* Encode the data. (NBITS-1 is due to 8.1.1, step 1.) */ rc = _gcry_rsa_pss_encode (ret_mpi, ctx->nbits - 1, ctx->hash_algo, value, valuelen, ctx->saltlen, random_override, random_override_len); xfree (random_override); } } } else if (ctx->encoding == PUBKEY_ENC_PSS && lhash && ctx->op == PUBKEY_OP_VERIFY) { if (sexp_length (lhash) != 3) rc = GPG_ERR_INV_OBJ; else if ( !(s=sexp_nth_data (lhash, 1, &n)) || !n ) rc = GPG_ERR_INV_OBJ; else { ctx->hash_algo = get_hash_algo (s, n); if (!ctx->hash_algo) rc = GPG_ERR_DIGEST_ALGO; else { *ret_mpi = sexp_nth_mpi (lhash, 2, GCRYMPI_FMT_USG); if (!*ret_mpi) rc = GPG_ERR_INV_OBJ; ctx->verify_cmp = pss_verify_cmp; ctx->verify_arg = *ret_mpi; } } } else rc = GPG_ERR_CONFLICT; leave: sexp_release (ldata); sexp_release (lhash); sexp_release (lvalue); if (!rc) ctx->flags = parsed_flags; else { xfree (ctx->label); ctx->label = NULL; } return rc; }
/* Helper to extract a point from key parameters. If no parameter with NAME is found, the functions tries to find a non-encoded point by appending ".x", ".y" and ".z" to NAME. ".z" is in this case optional and defaults to 1. EC is the context which at this point may not be fully initialized. */ static gpg_err_code_t point_from_keyparam (gcry_mpi_point_t *r_a, gcry_sexp_t keyparam, const char *name, mpi_ec_t ec) { gcry_err_code_t rc; gcry_sexp_t l1; gcry_mpi_point_t point; l1 = sexp_find_token (keyparam, name, 0); if (l1) { gcry_mpi_t a; a = sexp_nth_mpi (l1, 1, GCRYMPI_FMT_OPAQUE); sexp_release (l1); if (!a) return GPG_ERR_INV_OBJ; point = mpi_point_new (0); if (ec && ec->dialect == ECC_DIALECT_ED25519) rc = _gcry_ecc_eddsa_decodepoint (a, ec, point, NULL, NULL); else rc = _gcry_ecc_os2ec (point, a); mpi_free (a); if (rc) { mpi_point_release (point); return rc; } } else { char *tmpname; gcry_mpi_t x = NULL; gcry_mpi_t y = NULL; gcry_mpi_t z = NULL; tmpname = xtrymalloc (strlen (name) + 2 + 1); if (!tmpname) return gpg_err_code_from_syserror (); strcpy (stpcpy (tmpname, name), ".x"); rc = mpi_from_keyparam (&x, keyparam, tmpname); if (rc) { xfree (tmpname); return rc; } strcpy (stpcpy (tmpname, name), ".y"); rc = mpi_from_keyparam (&y, keyparam, tmpname); if (rc) { mpi_free (x); xfree (tmpname); return rc; } strcpy (stpcpy (tmpname, name), ".z"); rc = mpi_from_keyparam (&z, keyparam, tmpname); if (rc) { mpi_free (y); mpi_free (x); xfree (tmpname); return rc; } if (!z) z = mpi_set_ui (NULL, 1); if (x && y) point = mpi_point_snatch_set (NULL, x, y, z); else { mpi_free (x); mpi_free (y); mpi_free (z); point = NULL; } xfree (tmpname); } if (point) *r_a = point; return 0; }