diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/ChangeLog | 5 | ||||
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/jwk/ChangeLog | 8 | ||||
-rw-r--r-- | src/jwk/Makefile.am | 13 | ||||
-rw-r--r-- | src/jwk/generate-key.c | 59 | ||||
-rw-r--r-- | src/jwk/libwebidoidc-jwk.c | 118 | ||||
-rw-r--r-- | src/libwebidoidc.c | 2 | ||||
-rw-r--r-- | src/scm/webid-oidc/stubs.scm | 3 | ||||
-rw-r--r-- | src/utilities.h | 285 |
9 files changed, 493 insertions, 1 deletions
diff --git a/src/ChangeLog b/src/ChangeLog index e2e9bea..3b99888 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,6 +1,11 @@ 2020-11-25 Vivien Kraus <vivien@planete-kraus.eu> + * utilities.h: Add functions for the jwk submodule. + + * libwebidoidc.c (init_webidoidc): Initialize the jwk submodule. + * Makefile.am (%canon_reldir%_libwebidoidc_la_SOURCES): the common code is considered a source. + Build the "jwk" submodule. * utilities.h: Put the common code in that header. diff --git a/src/Makefile.am b/src/Makefile.am index 732a941..1d5f61c 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -25,6 +25,7 @@ install_mod_targets = install-webidoidcmodDATA install-dist_webidoidcmodDATA include %reldir%/base64/Makefile.am include %reldir%/random/Makefile.am +include %reldir%/jwk/Makefile.am include %reldir%/pre-inst/Makefile.am include %reldir%/inst/Makefile.am include %reldir%/scm/Makefile.am diff --git a/src/jwk/ChangeLog b/src/jwk/ChangeLog new file mode 100644 index 0000000..10e590f --- /dev/null +++ b/src/jwk/ChangeLog @@ -0,0 +1,8 @@ +2020-11-25 Vivien Kraus <vivien@planete-kraus.eu> + + * libwebidoidc-jwk.c: new file. + + * generate-key.c: new file. + + * Makefile.am: new file. + diff --git a/src/jwk/Makefile.am b/src/jwk/Makefile.am new file mode 100644 index 0000000..686f450 --- /dev/null +++ b/src/jwk/Makefile.am @@ -0,0 +1,13 @@ +noinst_LTLIBRARIES += %reldir%/libwebidoidc-jwk.la +noinst_PROGRAMS += %reldir%/generate-key +EXTRA_DIST += %reldir%/libwebidoidc-jwk.x +BUILT_SOURCES += %reldir%/libwebidoidc-jwk.x + +%canon_reldir%_libwebidoidc_jwk_la_LIBADD = $(GUILE_LIBS) $(NETTLE_LIBS) $(HOGWEED_LIBS) +%canon_reldir%_generate_key_LDADD = src/libwebidoidc.la $(GUILE_LIBS) + +AM_CFLAGS += -I %reldir% -I $(srcdir)/%reldir% + +INDENTED += %reldir%/libwebidoidc-jwk.c %reldir%/generate-key.c + +%reldir%/libwebidoidc-jwk.o: %reldir%/libwebidoidc-jwk.x diff --git a/src/jwk/generate-key.c b/src/jwk/generate-key.c new file mode 100644 index 0000000..ec8ce76 --- /dev/null +++ b/src/jwk/generate-key.c @@ -0,0 +1,59 @@ +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <stdio.h> +#include <stdlib.h> +#include <libguile.h> +#include <gettext.h> + +#define _(s) gettext (s) + +SCM webidoidc_generate_key_g (SCM args); + +extern int init_webidoidc (void); + +static void +run (void *params, int argc, char *argv[]) +{ + SCM data; + char *end; + size_t n_size = 0; + (void) params; + setlocale (LC_ALL, ""); + bindtextdomain (PACKAGE, LOCALEDIR); + textdomain (PACKAGE); + if (argc != 2 + || (strcmp (argv[1], "P-256") != 0 + && strcmp (argv[1], "P-384") != 0 + && strcmp (argv[1], "P-521") != 0 + && ((n_size = strtoull (argv[1], &end, 10)) == 0 || *end != '\0'))) + { + fprintf (stderr, _("Usage: generate-key [NUMBER OF BITS | CURVE]\n")); + exit (1); + } + init_webidoidc (); + if (strcmp (argv[1], "P-256") == 0 + || strcmp (argv[1], "P-384") == 0 || strcmp (argv[1], "P-521") == 0) + { + data = + webidoidc_generate_key_g (scm_list_2 + (scm_from_utf8_keyword ("crv"), + scm_from_utf8_symbol (argv[1]))); + } + else + { + data = + webidoidc_generate_key_g (scm_list_2 + (scm_from_utf8_keyword ("n-size"), + scm_from_size_t (n_size))); + } + scm_display (data, scm_current_output_port ()); +} + +int +main (int argc, char *argv[]) +{ + scm_boot_guile (argc, argv, run, NULL); + return 0; +} diff --git a/src/jwk/libwebidoidc-jwk.c b/src/jwk/libwebidoidc-jwk.c new file mode 100644 index 0000000..85386fb --- /dev/null +++ b/src/jwk/libwebidoidc-jwk.c @@ -0,0 +1,118 @@ +#include <utilities.h> + +#define _(s) dgettext (PACKAGE, s) + +void webid_oidc_random (size_t len, uint8_t * dst); + +/* Register "generate-key", a guile function to generate a keypair. */ +void init_webidoidc_jwk (void); + +SCM_KEYWORD (kw_crv, "crv"); +SCM_KEYWORD (kw_e, "e"); +SCM_KEYWORD (kw_n_size, "n-size"); +SCM_KEYWORD (kw_e_size, "e-size"); + +SCM_SYMBOL (kty_ec, "EC"); +SCM_SYMBOL (kty_rsa, "RSA"); + +SCM_SYMBOL (unsupported_kty, "unsupported-kty"); +SCM_SYMBOL (unsupported_crv, "unsupported-crv"); +SCM_SYMBOL (invalid_parameters, "invalid-parameters"); +SCM_SYMBOL (base64_decoding_error, "base64-decoding-error"); + +/* Required for utilities.h */ +SCM_SYMBOL (p256, "P-256"); +SCM_SYMBOL (p384, "P-384"); +SCM_SYMBOL (p521, "P-521"); +SCM_SYMBOL (kcrv, "crv"); +SCM_SYMBOL (kx, "x"); +SCM_SYMBOL (ky, "y"); +SCM_SYMBOL (kd, "d"); +SCM_SYMBOL (kn, "n"); +SCM_SYMBOL (ke, "e"); +SCM_SYMBOL (kp, "p"); +SCM_SYMBOL (kq, "q"); +SCM_SYMBOL (kdp, "dp"); +SCM_SYMBOL (kdq, "dq"); +SCM_SYMBOL (kqi, "qi"); + +SCM_SYMBOL (kkty, "kty"); + +static void +generate_random (void *unused, size_t len, uint8_t * dst) +{ + (void) unused; + webid_oidc_random (len, dst); +} + +SCM_DEFINE (webidoidc_generate_key_g, "generate-key", 0, 0, 1, (SCM rest), + "Generate a key pair. The parameters are passed as keyword arguments: @code{#:crv}, @code{#:n-size}, @code{#:e} or @code{#:e-size}. Return an alist of the parameters. If @code{#:crv} is specified, generate an EC key pair using the curve, @code{'P-256}, @code{'P-384} or @code{'P-521}. If @code{#:n-size} is specified, generate an RSA key pair that many bits long. Otherwise, this is an error. If @code{#:e} is set, use that for the exponent instead of AQAB. If @code{#:e-size} is set, generate the exponent that many bits long.") +{ + SCM crv = SCM_UNDEFINED; + SCM n_size = SCM_UNDEFINED; + SCM e_size = scm_from_int (0); + SCM e = scm_from_utf8_string ("AQAB"); + const struct ecc_curve *c_crv = NULL; + SCM ret = SCM_EOL; + scm_c_bind_keyword_arguments ("generate-key", rest, 0, + kw_crv, &crv, + kw_n_size, &n_size, + kw_e_size, &e_size, kw_e, &e, SCM_UNDEFINED); + if (!SCM_UNBNDP (crv)) + { + c_crv = do_ecc_curve_load (crv, 1); + } + else if (!SCM_UNBNDP (n_size)) + { + /* This is a RSA key pair */ + } + else + { + scm_throw (unsupported_kty, SCM_EOL); + } + if (c_crv) + { + struct ecc_point c_point; + struct ecc_scalar c_scalar; + scm_dynwind_begin (0); + ecc_point_init (&c_point, c_crv); + dynwind_ecc_point_clear (&c_point); + ecc_scalar_init (&c_scalar, c_crv); + dynwind_ecc_scalar_clear (&c_scalar); + ecdsa_generate_keypair (&c_point, &c_scalar, NULL, generate_random); + ret = wrap_ecc_key_pair (c_crv, &c_point, &c_scalar); + scm_dynwind_end (); + } + else + { + struct rsa_public_key c_pub; + struct rsa_private_key c_key; + scm_dynwind_begin (0); + rsa_public_key_init (&c_pub); + dynwind_rsa_public_key_clear (&c_pub); + rsa_private_key_init (&c_key); + dynwind_rsa_private_key_clear (&c_key); + do_mpz_t_load (c_pub.e, e, 1); + if (rsa_generate_keypair (&c_pub, &c_key, + NULL, &generate_random, + NULL, NULL, + scm_to_uint (n_size), scm_to_uint (e_size))) + { + ret = wrap_rsa_key_pair (&c_pub, &c_key);; + } + else + { + scm_throw (invalid_parameters, scm_list_3 (n_size, e_size, e)); + } + scm_dynwind_end (); + } + return ret; +} + +void +init_webidoidc_jwk (void) +{ +#ifndef SCM_MAGIC_SNARFER +#include "libwebidoidc-jwk.x" +#endif /* not SCM_MAGIC_SNARFER */ +} diff --git a/src/libwebidoidc.c b/src/libwebidoidc.c index af26ae0..514a51c 100644 --- a/src/libwebidoidc.c +++ b/src/libwebidoidc.c @@ -1,6 +1,7 @@ #define N_(s) void init_webidoidc_base64 (void); void init_webidoidc_random (void); +void init_webidoidc_jwk (void); void init_webidoidc (void) @@ -8,4 +9,5 @@ init_webidoidc (void) N_("This is the main function."); init_webidoidc_base64 (); init_webidoidc_random (); + init_webidoidc_jwk (); } diff --git a/src/scm/webid-oidc/stubs.scm b/src/scm/webid-oidc/stubs.scm index b022ef2..5858986 100644 --- a/src/scm/webid-oidc/stubs.scm +++ b/src/scm/webid-oidc/stubs.scm @@ -17,4 +17,5 @@ base64-encode (fix-base64-decode . base64-decode) random - random-init!) + random-init! + generate-key) diff --git a/src/utilities.h b/src/utilities.h index b5b91f9..d17f647 100644 --- a/src/utilities.h +++ b/src/utilities.h @@ -19,12 +19,58 @@ #include <stdio.h> #include <unistd.h> +/* The symbols are used as parameter names for crypto keys */ +static SCM p256; +static SCM p384; +static SCM p521; +static SCM kcrv; +static SCM kx; +static SCM ky; +static SCM kd; +static SCM kn; +static SCM ke; +static SCM kp; +static SCM kq; +static SCM kdp; +static SCM kdq; +static SCM kqi; + /* Return a base64 encoding of some raw data. */ static SCM wrap_bytevector (size_t length, uint8_t * data); +/* Return a base64 encoding of the bigint z. */ +static SCM wrap_mpz_t (mpz_t z); + +/* Return an alist for a key */ +static SCM wrap_ecc_point (const struct ecc_curve *crv, + const struct ecc_point *x); +static SCM wrap_ecc_scalar (const struct ecc_curve *crv, + const struct ecc_scalar *x); +static SCM wrap_ecc_key_pair (const struct ecc_curve *crv, + const struct ecc_point *point, + const struct ecc_scalar *scalar); +static SCM wrap_rsa_public_key (struct rsa_public_key *x); +static SCM wrap_rsa_private_key (struct rsa_private_key *x); +static SCM wrap_rsa_key_pair (struct rsa_public_key *pub, + struct rsa_private_key *key); + /* Decode a base64 of binary data. */ static uint8_t *get_as_bytevector (SCM data, size_t *size, int throw_if_fail); +/* Parse a bigint (data) as base64 and store it in x. */ +static int do_mpz_t_load (mpz_t x, SCM data, int throw_if_fail); + +/* Parse an ECC curve */ +static const struct ecc_curve *do_ecc_curve_load (SCM data, + int throw_if_fail); + +/* Register x to be destroyed at the end of the dynamic wind. */ +static void dynwind_mpz_t_clear (mpz_t x); +static void dynwind_ecc_point_clear (struct ecc_point *x); +static void dynwind_ecc_scalar_clear (struct ecc_scalar *x); +static void dynwind_rsa_public_key_clear (struct rsa_public_key *x); +static void dynwind_rsa_private_key_clear (struct rsa_private_key *x); + static inline SCM wrap_bytevector (size_t length, uint8_t * data) { @@ -52,6 +98,129 @@ wrap_bytevector (size_t length, uint8_t * data) return ret; } +static uint8_t * +export_mpz_t (mpz_t data, size_t *length) +{ + size_t check_length; + uint8_t *ret = NULL; + *length = (mpz_sizeinbase (data, 2) + 7) / 8; + ret = scm_malloc (*length); + mpz_export (ret, &check_length, 1, 1, 1, 0, data); + assert (*length == check_length); + return ret; +} + +static inline SCM +wrap_mpz_t (mpz_t data) +{ + size_t length; + uint8_t *c_data; + SCM ret; + scm_dynwind_begin (0); + c_data = export_mpz_t (data, &length); + scm_dynwind_free (c_data); + ret = wrap_bytevector (length, c_data); + scm_dynwind_end (); + return ret; +} + +static SCM +wrap_ecc_curve (const struct ecc_curve *crv) +{ + static const struct ecc_curve *p_256 = NULL; + static const struct ecc_curve *p_384 = NULL; + static const struct ecc_curve *p_521 = NULL; + static int init = 0; + if (!init) + { + init = 1; + p_256 = nettle_get_secp_256r1 (); + p_384 = nettle_get_secp_384r1 (); + p_521 = nettle_get_secp_521r1 (); + } + if (crv == p_256) + { + return p256; /* the symbol */ + } + if (crv == p_384) + { + return p384; + } + if (crv == p_521) + { + return p521; + } + abort (); + return SCM_UNDEFINED; +} + +static inline SCM +wrap_ecc_point (const struct ecc_curve *crv, const struct ecc_point *point) +{ + mpz_t x, y; + SCM ret; + scm_dynwind_begin (0); + mpz_init (x); + dynwind_mpz_t_clear (x); + mpz_init (y); + dynwind_mpz_t_clear (y); + ecc_point_get (point, x, y); + ret = + scm_list_3 (scm_cons (kcrv, wrap_ecc_curve (crv)), + scm_cons (kx, wrap_mpz_t (x)), scm_cons (ky, wrap_mpz_t (y))); + scm_dynwind_end (); + return ret; +} + +static inline SCM +wrap_ecc_scalar (const struct ecc_curve *crv, const struct ecc_scalar *scalar) +{ + mpz_t z; + SCM ret; + scm_dynwind_begin (0); + mpz_init (z); + dynwind_mpz_t_clear (z); + ecc_scalar_get (scalar, z); + ret = + scm_list_2 (scm_cons (kcrv, wrap_ecc_curve (crv)), + scm_cons (kd, wrap_mpz_t (z))); + scm_dynwind_end (); + return ret; +} + +static inline SCM +wrap_ecc_key_pair (const struct ecc_curve *crv, const struct ecc_point *point, + const struct ecc_scalar *scalar) +{ + return scm_append (scm_list_2 (wrap_ecc_point (crv, point), + wrap_ecc_scalar (crv, scalar))); +} + +static inline SCM +wrap_rsa_public_key (struct rsa_public_key *x) +{ + return scm_list_2 (scm_cons (kn, wrap_mpz_t (x->n)), + scm_cons (ke, wrap_mpz_t (x->e))); +} + +static inline SCM +wrap_rsa_private_key (struct rsa_private_key *x) +{ + return scm_list_n (scm_cons (kd, wrap_mpz_t (x->d)), + scm_cons (kp, wrap_mpz_t (x->p)), + scm_cons (kq, wrap_mpz_t (x->q)), + scm_cons (kdp, wrap_mpz_t (x->a)), + scm_cons (kdq, wrap_mpz_t (x->b)), + scm_cons (kqi, wrap_mpz_t (x->c)), SCM_UNDEFINED); +} + +static inline SCM +wrap_rsa_key_pair (struct rsa_public_key *pub, struct rsa_private_key *key) +{ + return scm_append (scm_list_2 (wrap_rsa_public_key (pub), + wrap_rsa_private_key (key))); +} + static inline uint8_t * get_as_bytevector (SCM data, size_t *size, int throw_if_fail) { @@ -85,4 +254,120 @@ get_as_bytevector (SCM data, size_t *size, int throw_if_fail) return ret; } +static inline int +do_mpz_t_load (mpz_t x, SCM data, int throw_if_fail) +{ + size_t size; + uint8_t *c_data; + int ret = 1; + scm_dynwind_begin (0); + c_data = get_as_bytevector (data, &size, throw_if_fail); + if (c_data) + { + scm_dynwind_free (c_data); + mpz_import (x, size, 1, 1, 1, 0, c_data); + } + else + { + ret = 0; + } + scm_dynwind_end (); + return ret; +} + +static inline const struct ecc_curve * +do_ecc_curve_load (SCM crv, int throw_if_fail) +{ + if (scm_is_string (crv)) + { + return do_ecc_curve_load (scm_string_to_symbol (crv), throw_if_fail); + } + if (scm_is_eq (crv, p256)) + { + return nettle_get_secp_256r1 (); + } + if (scm_is_eq (crv, p384)) + { + return nettle_get_secp_384r1 (); + } + if (scm_is_eq (crv, p521)) + { + return nettle_get_secp_521r1 (); + } + if (throw_if_fail) + { + scm_throw (scm_from_utf8_symbol ("unsupported-crv"), scm_list_1 (crv)); + } + return NULL; +} + +static void +do_mpz_t_clear (void *ptr) +{ + mpz_t *z = ptr; + mpz_clear (*z); +} + +static void +do_rsa_public_key_clear (void *ptr) +{ + struct rsa_public_key *pub = ptr; + rsa_public_key_clear (pub); +} + +static void +do_rsa_private_key_clear (void *ptr) +{ + struct rsa_private_key *key = ptr; + rsa_private_key_clear (key); +} + +static void +do_ecc_point_clear (void *ptr) +{ + struct ecc_point *point = ptr; + ecc_point_clear (point); +} + +static void +do_ecc_scalar_clear (void *ptr) +{ + struct ecc_scalar *scalar = ptr; + ecc_scalar_clear (scalar); +} + +static inline void +dynwind_mpz_t_clear (mpz_t z) +{ + scm_dynwind_unwind_handler (do_mpz_t_clear, &z, SCM_F_WIND_EXPLICITLY); +} + +static inline void +dynwind_rsa_public_key_clear (struct rsa_public_key *pub) +{ + scm_dynwind_unwind_handler (do_rsa_public_key_clear, pub, + SCM_F_WIND_EXPLICITLY); +} + +static inline void +dynwind_rsa_private_key_clear (struct rsa_private_key *key) +{ + scm_dynwind_unwind_handler (do_rsa_private_key_clear, key, + SCM_F_WIND_EXPLICITLY); +} + +static inline void +dynwind_ecc_point_clear (struct ecc_point *point) +{ + scm_dynwind_unwind_handler (do_ecc_point_clear, point, + SCM_F_WIND_EXPLICITLY); +} + +static inline void +dynwind_ecc_scalar_clear (struct ecc_scalar *scalar) +{ + scm_dynwind_unwind_handler (do_ecc_scalar_clear, scalar, + SCM_F_WIND_EXPLICITLY); +} + #endif /* not H_UTILITIES_INCLUDED */ |