From 94b652ecb006c29fa2ffb1badc9f02b758581737 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 9 Oct 2013 15:05:26 +0200 Subject: pubkey: Move sexp parsing for gcry_pk_verify to the modules. * cipher/rsa.c (rsa_verify): Revamp. * cipher/dsa.c (dsa_verify): Revamp. * cipher/elgamal.c (elg_verify): Revamp. * cipher/ecc.c (ecc_verify): Revamp. * cipher/pubkey.c (sexp_to_sig): Remove. (pss_verify_cmp): Move to pubkey-util.c (sexp_data_to_mpi): Ditto. (init_encoding_ctx): Ditto. (gcry_pk_verify): Simplify. * cipher/pubkey-util.c (_gcry_pk_util_init_encoding_ctx): Add. Take from pubkey.c (get_hash_algo): Ditto. (_gcry_pk_util_data_to_mpi): Ditto. (pss_verify_cmp): Ditto. (_gcry_pk_util_extract_mpis): New. (_gcry_pk_util_preparse_sigval): New. (_gcry_pk_util_free_encoding_ctx): New. * cipher/ecc-curves.c (_gcry_ecc_fill_in_curve): Make curve init optional. * src/g10lib.h (GCC_ATTR_SENTINEL): New. * tests/basic.c (check_pubkey_sign): Print the algo name. (main): Add option --pubkey. Signed-off-by: Werner Koch --- cipher/pubkey-util.c | 767 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 767 insertions(+) (limited to 'cipher/pubkey-util.c') diff --git a/cipher/pubkey-util.c b/cipher/pubkey-util.c index ef0ef441..debe6ca6 100644 --- a/cipher/pubkey-util.c +++ b/cipher/pubkey-util.c @@ -30,6 +30,21 @@ #include "pubkey-internal.h" +/* Callback for the pubkey algorithm code to verify PSS signatures. + OPAQUE is the data provided by the actual caller. The meaning of + TMP depends on the actual algorithm (but there is only RSA); now + for RSA it is the output of running the public key function on the + input. */ +static int +pss_verify_cmp (void *opaque, gcry_mpi_t tmp) +{ + struct pk_encoding_ctx *ctx = opaque; + gcry_mpi_t hash = ctx->verify_arg; + + return _gcry_rsa_pss_verify (hash, tmp, ctx->nbits - 1, + ctx->hash_algo, ctx->saltlen); +} + /* Get the "nbits" parameter from an s-expression of the format: @@ -125,3 +140,755 @@ _gcry_pk_util_get_rsa_use_e (gcry_sexp_t list, unsigned long *r_e) gcry_sexp_release (list); return 0; } + + +/* Extract MPIs from an s-expression using a list of one letter + * parameters. The names of these parameters are given by the string + * LIST. Some special characters may be given to control the + * conversion: + * + * + :: Switch to unsigned integer format (default). + * - :: Switch to standard signed format. + * / :: Switch to opaque format. + * ? :: The previous parameter is optional. + * + * For each parameter name a pointer to an MPI variable is expected + * and finally a NULL is expected. Example: + * + * _gcry_pk_util_extract_mpis (key, "n/x+ed", &mpi_n, &mpi_x, &mpi_e, NULL) + * + * This stores the parameter "N" from KEY as an unsigned MPI into + * MPI_N, the parameter "X" as an opaque MPI into MPI_X, and the + * parameter "E" again as an unsigned MPI into MPI_E. + * + * The function returns NULL on success. On error an error code is + * returned and and the passed MPIs have no defined value. + */ +gpg_err_code_t +_gcry_pk_util_extract_mpis (gcry_sexp_t sexp, const char *list, ...) +{ + va_list arg_ptr; + const char *s; + gcry_mpi_t *array[10]; + int idx; + gcry_sexp_t l1; + enum gcry_mpi_format mpifmt = GCRYMPI_FMT_USG; + + /* First copy all the args into an array. This is required so that + we are able to release already allocated MPIs if later an error + was found. */ + va_start (arg_ptr, list) ; + for (s=list, idx=0; *s && idx < DIM (array); s++) + { + if (*s == '+' || *s == '-' || *s == '/' || *s == '?') + ; + else + { + array[idx] = va_arg (arg_ptr, gcry_mpi_t *); + if (!array[idx]) + { + va_end (arg_ptr); + return GPG_ERR_INTERNAL; /* NULL pointer given. */ + } + idx++; + } + } + if (*s) + { + va_end (arg_ptr); + return GPG_ERR_INTERNAL; /* Too many list elements. */ + } + if (va_arg (arg_ptr, gcry_mpi_t *)) + { + va_end (arg_ptr); + return GPG_ERR_INTERNAL; /* Not enough list elemends. */ + } + va_end (arg_ptr); + + /* Now extract all parameters. */ + for (s=list, idx=0; *s; s++) + { + if (*s == '+') + mpifmt = GCRYMPI_FMT_USG; + else if (*s == '-') + mpifmt = GCRYMPI_FMT_STD; + else if (*s == '/') + mpifmt = GCRYMPI_FMT_HEX; /* Used to indicate opaque. */ + else if (*s == '?') + ; /* Only used via lookahead. */ + else + { + l1 = gcry_sexp_find_token (sexp, s, 1); + if (!l1 && s[1] == '?') + *array[idx] = NULL; /* Optional element not found. */ + else if (!l1) + { + while (idx--) + gcry_mpi_release (*array[idx]); + return GPG_ERR_NO_OBJ; /* List element not found. */ + } + else + { + if (mpifmt == GCRYMPI_FMT_HEX) + *array[idx] = _gcry_sexp_nth_opaque_mpi (l1, 1); + else + *array[idx] = gcry_sexp_nth_mpi (l1, 1, mpifmt); + gcry_sexp_release (l1); + if (!*array[idx]) + { + while (idx--) + gcry_mpi_release (*array[idx]); + return GPG_ERR_INV_OBJ; /* Conversion failed. */ + } + } + idx++; + } + } + + return 0; +} + + +/* Parse a "sig_val s-expression and store the inner parameter list at + R_PARMS. ALGO_NAMES is used to verify that the algorithm in + "sig-val" is valid. Returns 0 on success and stores a new list at + R_PARMS which must be freed by the caller. On error R_PARMS is set + to NULL and an error code returned. If R_ECCFLAGS is not NULL flag + values are set into it; as of now they are only used with ecc + algorithms. */ +gpg_err_code_t +_gcry_pk_util_preparse_sigval (gcry_sexp_t s_sig, const char **algo_names, + gcry_sexp_t *r_parms, int *r_eccflags) +{ + gpg_err_code_t rc; + gcry_sexp_t l1 = NULL; + gcry_sexp_t l2 = NULL; + char *name = NULL; + int i; + + *r_parms = NULL; + if (r_eccflags) + *r_eccflags = 0; + + /* Extract the signature value. */ + l1 = gcry_sexp_find_token (s_sig, "sig-val", 0); + if (!l1) + { + rc = GPG_ERR_INV_OBJ; /* Does not contain a signature value object. */ + goto leave; + } + + l2 = gcry_sexp_nth (l1, 1); + if (!l2) + { + rc = GPG_ERR_NO_OBJ; /* No cadr for the sig object. */ + goto leave; + } + name = _gcry_sexp_nth_string (l2, 0); + if (!name) + { + rc = GPG_ERR_INV_OBJ; /* Invalid structure of object. */ + goto leave; + } + else if (!strcmp (name, "flags")) + { + /* Skip a "flags" parameter and look again for the algorithm + name. This is not used but here just for the sake of + consistent S-expressions we need to handle it. */ + gcry_sexp_release (l2); + l2 = gcry_sexp_nth (l1, 2); + if (!l2) + { + rc = GPG_ERR_INV_OBJ; + goto leave; + } + gcry_free (name); + name = _gcry_sexp_nth_string (l2, 0); + if (!name) + { + rc = GPG_ERR_INV_OBJ; /* Invalid structure of object. */ + goto leave; + } + } + + for (i=0; algo_names[i]; i++) + if (!stricmp (name, algo_names[i])) + break; + if (!algo_names[i]) + { + rc = GPG_ERR_CONFLICT; /* "sig-val" uses an unexpected algo. */ + goto leave; + } + if (r_eccflags) + { + if (!strcmp (name, "eddsa")) + *r_eccflags = PUBKEY_FLAG_EDDSA; + } + + *r_parms = l2; + l2 = NULL; + rc = 0; + + leave: + gcry_free (name); + gcry_sexp_release (l2); + gcry_sexp_release (l1); + return rc; +} + +/* Initialize an encoding context. */ +void +_gcry_pk_util_init_encoding_ctx (struct pk_encoding_ctx *ctx, + enum pk_operation op, + unsigned int nbits) +{ + ctx->op = op; + ctx->nbits = nbits; + ctx->encoding = PUBKEY_ENC_UNKNOWN; + ctx->flags = 0; + ctx->hash_algo = GCRY_MD_SHA1; + ctx->label = NULL; + ctx->labellen = 0; + ctx->saltlen = 20; + ctx->verify_cmp = NULL; + ctx->verify_arg = NULL; +} + +/* Free a context initialzied by _gcry_pk_util_init_encoding_ctx. */ +void +_gcry_pk_util_free_encoding_ctx (struct pk_encoding_ctx *ctx) +{ + gcry_free (ctx->label); +} + + +static inline int +get_hash_algo (const char *s, size_t n) +{ + static const struct { const char *name; int algo; } hashnames[] = { + { "sha1", GCRY_MD_SHA1 }, + { "md5", GCRY_MD_MD5 }, + { "sha256", GCRY_MD_SHA256 }, + { "ripemd160", GCRY_MD_RMD160 }, + { "rmd160", GCRY_MD_RMD160 }, + { "sha384", GCRY_MD_SHA384 }, + { "sha512", GCRY_MD_SHA512 }, + { "sha224", GCRY_MD_SHA224 }, + { "md2", GCRY_MD_MD2 }, + { "md4", GCRY_MD_MD4 }, + { "tiger", GCRY_MD_TIGER }, + { "haval", GCRY_MD_HAVAL }, + { NULL, 0 } + }; + int algo; + int i; + + for (i=0; hashnames[i].name; i++) + { + if ( strlen (hashnames[i].name) == n + && !memcmp (hashnames[i].name, s, n)) + break; + } + if (hashnames[i].name) + algo = hashnames[i].algo; + else + { + /* In case of not listed or dynamically allocated hash + algorithm we fall back to this somewhat slower + method. Further, it also allows to use OIDs as + algorithm names. */ + char *tmpname; + + tmpname = gcry_malloc (n+1); + if (!tmpname) + algo = 0; /* Out of core - silently give up. */ + else + { + memcpy (tmpname, s, n); + tmpname[n] = 0; + algo = gcry_md_map_name (tmpname); + gcry_free (tmpname); + } + } + return algo; +} + + +/* 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. + + () + or + (data + [(flags [raw, direct, pkcs1, oaep, pss, no-blinding, rfc6979, eddsa])] + [(hash )] + [(value )] + [(hash-algo )] + [(label