/* webid-oidc, implementation of the Solid specification Copyright (C) 2020, 2021 Vivien Kraus This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ #include #define _(s) dgettext (PACKAGE, s) void webid_oidc_random (size_t len, uint8_t * dst); SCM webidoidc_hash_g (SCM alg, SCM payload); /* 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; } SCM_DEFINE (webidoidc_kty_g, "kty", 1, 0, 0, (SCM key), "Return the type of @var{key}, @code{'EC} or @code{'RSA}. Return @code{#f} if this is not a JWK.") { SCM crv = scm_assq_ref (key, kcrv); const struct ecc_curve *c_crv = NULL; struct ecc_point c_point; struct ecc_scalar c_scalar; struct rsa_public_key c_pub; struct rsa_private_key c_key; SCM ret = SCM_BOOL_F; if (scm_is_true (crv)) { c_crv = do_ecc_curve_load (crv, 0); } scm_dynwind_begin (0); if (c_crv) { 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); } 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); if (c_crv && (do_ecc_point_load (&c_point, key) || do_ecc_scalar_load (&c_scalar, key))) { ret = kty_ec; } else if (do_rsa_public_key_load (&c_pub, key) || do_rsa_private_key_load (&c_key, key)) { ret = kty_rsa; } scm_dynwind_end (); return ret; } SCM_DEFINE (webidoidc_strip_key_g, "strip-key", 1, 0, 0, (SCM key), "Strip a JWK, keeping only the parts that are required for a public key. The fields are sorted aphabetically.") { SCM kty = webidoidc_kty_g (key); SCM crv = scm_assq_ref (key, kcrv); SCM x = scm_assq_ref (key, kx); SCM y = scm_assq_ref (key, ky); SCM n = scm_assq_ref (key, kn); SCM e = scm_assq_ref (key, ke); if (scm_is_eq (kty, kty_ec)) { return scm_list_4 (scm_cons (kcrv, crv), scm_cons (kkty, kty_ec), scm_cons (kx, x), scm_cons (ky, y)); } if (scm_is_eq (kty, kty_rsa)) { return scm_list_3 (scm_cons (ke, e), scm_cons (kkty, kty_rsa), scm_cons (kn, n)); } scm_throw (unsupported_kty, scm_list_1 (key)); } static SCM scm_to_json_string; SCM_SYMBOL (sha256, "SHA-256"); SCM_DEFINE (webidoidc_jkt_g, "jkt", 1, 0, 0, (SCM key), "Hash a public key.") { SCM stripped = webidoidc_strip_key_g (key); SCM as_json = scm_call_1 (scm_to_json_string, stripped); SCM as_bytevector = scm_string_to_utf8 (as_json); return webidoidc_hash_g (sha256, as_bytevector); } void init_webidoidc_jwk (void) { scm_to_json_string = scm_c_public_ref ("json", "scm->json-string"); #ifndef SCM_MAGIC_SNARFER #include "libwebidoidc-jwk.x" #endif /* not SCM_MAGIC_SNARFER */ }