RFC7518 section 6.3 says that only mandatory parameters are n, e and d.
}
#endif
+/* This function calculates missing parameters. The only required values
+ * are e, n, d. If p_r and q_r are provided, they can be used directly
+ * instead of deriving them. */
+static bool dcrypt_openssl_derive_rsa_param(BIGNUM *e, BIGNUM *n, BIGNUM *d,
+ BIGNUM *p_r, BIGNUM *q_r,
+ BIGNUM *dmp1_r, BIGNUM *dmq1_r,
+ BIGNUM *iqmp_r)
+{
+ BIGNUM *p = NULL, *q = NULL;
+ BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
+ BIGNUM *mphi;
+ BN_CTX *ctx;
+ BIGNUM *two, *a, *limit, *t;
+ BIGNUM *cand, *k, *n1, *tmp;
+
+ bool found = FALSE;
+ bool ret = FALSE;
+
+ ctx = BN_CTX_new();
+ if (ctx == NULL)
+ i_fatal_status(FATAL_OUTOFMEM, "Cannot allocate BN_CTX");
+ p = BN_secure_new();
+ BN_set_flags(p, BN_FLG_CONSTTIME);
+ q = BN_secure_new();
+ BN_set_flags(q, BN_FLG_CONSTTIME);
+ mphi = BN_secure_new();
+ BN_set_flags(mphi, BN_FLG_CONSTTIME);
+ t = BN_secure_new();
+ BN_set_flags(mphi, BN_FLG_CONSTTIME);
+ a = BN_new();
+ BN_set_flags(a, BN_FLG_CONSTTIME);
+ two = BN_new();
+ BN_set_flags(two, BN_FLG_CONSTTIME);
+ limit = BN_new();
+ k = BN_secure_new();
+ BN_set_flags(k, BN_FLG_CONSTTIME);
+ cand = BN_secure_new();
+ BN_set_flags(cand, BN_FLG_CONSTTIME);
+ n1 = BN_new();
+ BN_set_flags(n1, BN_FLG_CONSTTIME);
+ tmp = BN_secure_new();
+ BN_set_flags(tmp, BN_FLG_CONSTTIME);
+
+ BN_dec2bn(&two, "2");
+ BN_copy(a, two);
+ BN_dec2bn(&limit, "100");
+ BN_sub(n1, n, BN_value_one());
+
+ if (BN_is_zero(p_r) != 1 && BN_is_zero(q_r) != 1) {
+ /* Make sure n is actually related to p and q*/
+ if (BN_mul(tmp, p_r, q_r, ctx) != 1 ||
+ BN_cmp(tmp, n) != 0) {
+ ret = FALSE;
+ goto finally;
+ }
+ /* Then check that d is related to p and q */
+ BIGNUM *p_1 = BN_secure_new();
+ BN_set_flags(p_1, BN_FLG_CONSTTIME);
+ BN_sub(p_1, p_r, BN_value_one());
+ BIGNUM *q_1 = BN_secure_new();
+ BN_set_flags(q_1, BN_FLG_CONSTTIME);
+ BN_sub(q_1, q_r, BN_value_one());
+ BN_mul(tmp, p_1, q_1, ctx);
+ BIGNUM *tmp2 = BN_secure_new();
+ BN_set_flags(tmp2, BN_FLG_CONSTTIME);
+ BN_mod_mul(tmp2, d, e, tmp, ctx);
+ ret = BN_cmp(tmp2, BN_value_one()) == 0;
+ BN_free(tmp2);
+ BN_free(p_1);
+ BN_free(q_1);
+ if (!ret)
+ goto finally;
+ BN_copy(p, p_r);
+ BN_copy(q, q_r);
+ goto have_pq;
+ }
+
+ /* calculate e*d - 1 */
+ if (BN_mul(mphi, d, e, ctx) != 1)
+ goto finally;
+ if (BN_sub(mphi, mphi, BN_value_one()) != 1)
+ goto finally;
+
+ /* this is a multiple of phi(n), even */
+ if (BN_copy(t, mphi) == NULL)
+ goto finally;
+
+ while (BN_is_odd(t) != 1)
+ if (BN_rshift1(t, t) != 1)
+ goto finally;
+
+ /* Go through all multiplicative inverses in Zn. */
+ for (; BN_cmp(a, limit) < 0; BN_add(a, a, two)) {
+ if (BN_copy(k, t) == NULL)
+ goto finally;
+ while (BN_cmp(k, mphi) < 0) {
+ if (BN_mod_exp(cand, a, k, n, ctx) != 1)
+ goto finally;
+ if (BN_cmp(cand, BN_value_one()) != 0 &&
+ BN_cmp(cand, n1) != 0) {
+ if (BN_mod_exp(tmp, cand, two, n, ctx) != 1)
+ goto finally;
+ if (BN_cmp(tmp, BN_value_one()) == 0) {
+ if (BN_add(tmp, cand, BN_value_one()) !=
+ 1 ||
+ BN_gcd(q, tmp, n, ctx) != 1 ||
+ BN_div(p, tmp, n, q, ctx) != 1 ||
+ BN_is_zero(tmp) != 1) {
+ ret = FALSE;
+ goto finally;
+ }
+ found = TRUE;
+ break;
+ }
+ }
+ if (BN_lshift1(k, k) != 1)
+ goto finally;
+ }
+ if (found)
+ break;
+ }
+
+ if (!found)
+ goto finally;
+have_pq:
+ dmp1 = BN_secure_new();
+ dmq1 = BN_secure_new();
+ iqmp = BN_secure_new();
+ if (BN_sub(tmp, p, BN_value_one()) == 1 &&
+ BN_mod(dmp1, d, tmp, ctx) == 1 &&
+ BN_sub(tmp, q, BN_value_one()) == 1 &&
+ BN_mod(dmq1, d, tmp, ctx) == 1 &&
+ BN_mod_inverse(iqmp, q, p, ctx) != NULL) {
+ BN_copy(p_r, p);
+ BN_copy(q_r, q);
+ BN_copy(dmp1_r, dmp1);
+ BN_copy(dmq1_r, dmq1);
+ BN_copy(iqmp_r, iqmp);
+ ret = TRUE;
+ }
+ BN_free(dmp1);
+ BN_free(dmq1);
+ BN_free(iqmp);
+finally:
+ BN_free(p);
+ BN_free(q);
+ BN_free(tmp);
+ BN_free(t);
+ BN_free(k);
+ BN_free(a);
+ BN_free(limit);
+ BN_free(cand);
+ BN_free(two);
+ BN_free(n1);
+ BN_free(mphi);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
/* Loads both public and private key */
static bool load_jwk_rsa_key(EVP_PKEY **key_r, bool want_private_key,
const struct json_tree_node *root,
struct dcrypt_private_key *dec_key ATTR_UNUSED,
const char **error_r)
{
- const char *n, *e, *d = NULL, *p = NULL, *q = NULL, *dp = NULL;
- const char *dq = NULL, *qi = NULL;
+ const char *n, *e, *d = NULL, *p = NULL, *q = NULL;
+ const char *dp = NULL, *dq = NULL, *qi = NULL;
const struct json_tree_node *node;
/* n and e must be present */
*error_r = "Missing d parameter";
return FALSE;
}
-
- if ((node = json_tree_node_get_member(root, "p")) == NULL ||
- (p = json_tree_node_get_str(node)) == NULL) {
- *error_r = "Missing p parameter";
+ if ((node = json_tree_node_get_member(root, "p")) != NULL)
+ p = json_tree_node_get_str(node);
+ if ((node = json_tree_node_get_member(root, "q")) != NULL)
+ q = json_tree_node_get_str(node);
+ if ((p != NULL && q == NULL) || (p == NULL && q != NULL)) {
+ *error_r = "p and q have to be both present";
return FALSE;
}
- if ((node = json_tree_node_get_member(root, "q")) == NULL ||
- (q = json_tree_node_get_str(node)) == NULL) {
- *error_r = "Missing q parameter";
- return FALSE;
- }
-
- if ((node = json_tree_node_get_member(root, "dp")) == NULL ||
- (dp = json_tree_node_get_str(node)) == NULL) {
- *error_r = "Missing dp parameter";
- return FALSE;
- }
-
- if ((node = json_tree_node_get_member(root, "dq")) == NULL ||
- (dq = json_tree_node_get_str(node)) == NULL) {
- *error_r = "Missing dq parameter";
- return FALSE;
- }
-
- if ((node = json_tree_node_get_member(root, "qi")) == NULL ||
- (qi = json_tree_node_get_str(node)) == NULL) {
- *error_r = "Missing qi parameter";
+ if ((node = json_tree_node_get_member(root, "dp")) != NULL)
+ dp = json_tree_node_get_str(node);
+ if ((node = json_tree_node_get_member(root, "dq")) != NULL)
+ dq = json_tree_node_get_str(node);
+ if ((node = json_tree_node_get_member(root, "qi")) != NULL)
+ qi = json_tree_node_get_str(node);
+ if ((dq != NULL || dp != NULL || qi != NULL) &&
+ (dp == NULL || dq == NULL || qi == NULL)) {
+ *error_r = "dp, dq, and qi must be present together";
return FALSE;
}
}
/* convert into BIGNUMs */
- BIGNUM *pn, *pe, *pd, *pp, *pq, *pdp, *pdq, *pqi;
+ BIGNUM *pn, *pe, *pd;
+ BIGNUM *pp = NULL, *pq = NULL, *pdp = NULL, *pdq = NULL, *pqi = NULL;
buffer_t *bn = t_base64url_decode_str(n);
buffer_t *be = t_base64url_decode_str(e);
if (want_private_key) {
pd = BN_secure_new();
+ BN_set_flags(pd, BN_FLG_CONSTTIME);
buffer_t *bd = t_base64url_decode_str(d);
if (BN_bin2bn(bd->data, bd->used, pd) == NULL) {
BN_free(pd);
return dcrypt_openssl_error(error_r);
}
+ pp = BN_secure_new();
+ BN_set_flags(pp, BN_FLG_CONSTTIME);
+ pq = BN_secure_new();
+ BN_set_flags(pq, BN_FLG_CONSTTIME);
+ if (p != NULL) {
+ buffer_t *bp = t_base64url_decode_str(p);
+ buffer_t *bq = t_base64url_decode_str(q);
+
+ if (BN_bin2bn(bp->data, bp->used, pp) == NULL ||
+ BN_bin2bn(bq->data, bq->used, pq) == NULL) {
+ BN_free(pd);
+ BN_free(pp);
+ BN_free(pq);
+ return dcrypt_openssl_error(error_r);
+ }
+ }
+ pdp = BN_secure_new();
+ pdq = BN_secure_new();
+ pqi = BN_secure_new();
+ if (dp != NULL) {
+ buffer_t *bdp = t_base64url_decode_str(dp);
+ buffer_t *bdq = t_base64url_decode_str(dq);
+ buffer_t *bqi = t_base64url_decode_str(qi);
+
+ if (BN_bin2bn(bdp->data, bdp->used, pdp) == NULL ||
+ BN_bin2bn(bdq->data, bdq->used, pdq) == NULL ||
+ BN_bin2bn(bqi->data, bqi->used, pqi) == NULL) {
+ BN_free(pd);
+ BN_free(pp);
+ BN_free(pq);
+ BN_free(pdp);
+ BN_free(pdq);
+ BN_free(pqi);
+ return dcrypt_openssl_error(error_r);
+ }
+ }
} else {
pd = NULL;
}
if (BN_bin2bn(bn->data, bn->used, pn) == NULL ||
BN_bin2bn(be->data, be->used, pe) == NULL) {
- if (pd != NULL)
- BN_free(pd);
BN_free(pn);
BN_free(pe);
+ if (want_private_key) {
+ BN_free(pd);
+ BN_free(pp);
+ BN_free(pq);
+ BN_free(pdp);
+ BN_free(pdq);
+ BN_free(pqi);
+ }
return dcrypt_openssl_error(error_r);
}
RSA *rsa_key = RSA_new();
if (rsa_key == NULL) {
- if (pd != NULL)
- BN_free(pd);
BN_free(pn);
BN_free(pe);
+ if (want_private_key) {
+ BN_free(pd);
+ BN_free(pp);
+ BN_free(pq);
+ BN_free(pdp);
+ BN_free(pdq);
+ BN_free(pqi);
+ }
return dcrypt_openssl_error(error_r);
}
if (RSA_set0_key(rsa_key, pn, pe, pd) != 1) {
- if (pd != NULL)
- BN_free(pd);
BN_free(pn);
BN_free(pe);
RSA_free(rsa_key);
+ if (want_private_key) {
+ BN_free(pd);
+ BN_free(pp);
+ BN_free(pq);
+ BN_free(pdp);
+ BN_free(pdq);
+ BN_free(pqi);
+ }
return dcrypt_openssl_error(error_r);
}
if (want_private_key) {
- pp = BN_secure_new();
- pq = BN_secure_new();
- pdp = BN_secure_new();
- pdq = BN_secure_new();
- pqi = BN_secure_new();
-
- buffer_t *bp = t_base64url_decode_str(p);
- buffer_t *bq = t_base64url_decode_str(q);
- buffer_t *bdp = t_base64url_decode_str(dp);
- buffer_t *bdq = t_base64url_decode_str(dq);
- buffer_t *bqi = t_base64url_decode_str(qi);
-
- if (BN_bin2bn(bp->data, bp->used, pp) == NULL ||
- BN_bin2bn(bq->data, bq->used, pq) == NULL ||
- BN_bin2bn(bdp->data, bdp->used, pdp) == NULL ||
- BN_bin2bn(bdq->data, bdq->used, pdq) == NULL ||
- BN_bin2bn(bqi->data, bqi->used, pqi) == NULL ||
- RSA_set0_factors(rsa_key, pp, pq) != 1) {
+ if (dp == NULL &&
+ !dcrypt_openssl_derive_rsa_param(pe, pn, pd, pp, pq, pdp,
+ pdq, pqi)) {
+ *error_r = "Cannot derive rsa primes";
+ BN_free(pp);
+ BN_free(pq);
+ BN_free(pdp);
+ BN_free(pdq);
+ BN_free(pqi);
RSA_free(rsa_key);
+ return FALSE;
+ } else if (RSA_set0_factors(rsa_key, pp, pq) != 1) {
BN_free(pp);
BN_free(pq);
BN_free(pdp);
BN_free(pdq);
BN_free(pqi);
+ RSA_free(rsa_key);
return dcrypt_openssl_error(error_r);
} else if (RSA_set0_crt_params(rsa_key, pdp, pdq, pqi) != 1) {
- RSA_free(rsa_key);
BN_free(pdp);
BN_free(pdq);
BN_free(pqi);
+ RSA_free(rsa_key);
+ return dcrypt_openssl_error(error_r);
+ } else if (RSA_check_key(rsa_key) != 1) {
+ RSA_free(rsa_key);
return dcrypt_openssl_error(error_r);
}
}
const BIGNUM *d = EC_KEY_get0_private_key(ec_key);
if (d == NULL) {
*error_r = "No private key available";
- json_ostream_destroy(&joutput);
+ json_ostream_nfinish_destroy(&joutput);
return FALSE;
}
str_truncate(b64url_temp, 0);
#include <openssl/core.h>
#include <openssl/core_names.h>
#include <openssl/params.h>
+#include <openssl/param_build.h>
#include <openssl/err.h>
/**
return res;
}
+/* This function calculates missing parameters. The only required values
+ * are e, n, d. If p_r and q_r are provided, they can be used directly
+ * instead of deriving them.
+ *
+ * This functionality exists since OpenSSL 3.2, but it is hard to check
+ * in configure, so we implement this here ourselves.
+ */
+static bool dcrypt_openssl_derive_rsa_param(BIGNUM *e, BIGNUM *n, BIGNUM *d,
+ BIGNUM *p_r, BIGNUM *q_r,
+ BIGNUM *dmp1_r, BIGNUM *dmq1_r,
+ BIGNUM *iqmp_r)
+{
+ BIGNUM *p = NULL, *q = NULL;
+ BIGNUM *dmp1 = NULL, *dmq1 = NULL, *iqmp = NULL;
+ BIGNUM *mphi;
+ BN_CTX *ctx;
+ BIGNUM *two, *a, *limit, *t;
+ BIGNUM *cand, *k, *n1, *tmp;
+
+ bool found = FALSE;
+ bool ret = FALSE;
+
+ if ((ctx = BN_CTX_new()) == NULL)
+ return FALSE;
+
+ mphi = BN_secure_new();
+ BN_set_flags(mphi, BN_FLG_CONSTTIME);
+ t = BN_secure_new();
+ BN_set_flags(mphi, BN_FLG_CONSTTIME);
+ a = BN_new();
+ BN_set_flags(a, BN_FLG_CONSTTIME);
+ two = BN_new();
+ BN_set_flags(two, BN_FLG_CONSTTIME);
+ limit = BN_new();
+ k = BN_secure_new();
+ BN_set_flags(k, BN_FLG_CONSTTIME);
+ cand = BN_secure_new();
+ BN_set_flags(cand, BN_FLG_CONSTTIME);
+ n1 = BN_new();
+ BN_set_flags(n1, BN_FLG_CONSTTIME);
+ tmp = BN_secure_new();
+ BN_set_flags(tmp, BN_FLG_CONSTTIME);
+ p = BN_secure_new();
+ BN_set_flags(p, BN_FLG_CONSTTIME);
+ q = BN_secure_new();
+ BN_set_flags(q, BN_FLG_CONSTTIME);
+ BN_dec2bn(&two, "2");
+ BN_copy(a, two);
+ BN_dec2bn(&limit, "100");
+ BN_sub(n1, n, BN_value_one());
+
+ if (BN_is_zero(p_r) != 1 && BN_is_zero(q_r) != 1) {
+ /* Make sure n is actually related to p and q*/
+ if (BN_mul(tmp, p_r, q_r, ctx) != 1 ||
+ BN_cmp(tmp, n) != 0) {
+ ret = FALSE;
+ goto finally;
+ }
+ /* Then check that d is related to p and q */
+ BIGNUM *p_1 = BN_secure_new();
+ BN_set_flags(p_1, BN_FLG_CONSTTIME);
+ BN_sub(p_1, p_r, BN_value_one());
+ BIGNUM *q_1 = BN_secure_new();
+ BN_set_flags(q_1, BN_FLG_CONSTTIME);
+ BN_sub(q_1, q_r, BN_value_one());
+ BN_mul(tmp, p_1, q_1, ctx);
+ BIGNUM *tmp2 = BN_secure_new();
+ BN_set_flags(tmp2, BN_FLG_CONSTTIME);
+ BN_mod_mul(tmp2, d, e, tmp, ctx);
+ ret = BN_cmp(tmp2, BN_value_one()) == 0;
+ BN_free(tmp2);
+ BN_free(p_1);
+ BN_free(q_1);
+ if (!ret)
+ goto finally;
+ BN_copy(p, p_r);
+ BN_copy(q, q_r);
+ goto have_pq;
+ }
+
+ /* calculate e*d - 1 */
+ if (BN_mul(mphi, d, e, ctx) != 1)
+ goto finally;
+ if (BN_sub(mphi, mphi, BN_value_one()) != 1)
+ goto finally;
+
+ /* this is a multiple of phi(n), even */
+ if (BN_copy(t, mphi) == NULL)
+ goto finally;
+
+ while (BN_is_odd(t) != 1)
+ if (BN_rshift1(t, t) != 1)
+ goto finally;
+
+ /* Go through all multiplicative inverses in Zn. */
+ for (; BN_cmp(a, limit) < 0; BN_add(a, a, two)) {
+ if (BN_copy(k, t) == NULL)
+ goto finally;
+ while (BN_cmp(k, mphi) < 0) {
+ if (BN_mod_exp(cand, a, k, n, ctx) != 1)
+ goto finally;
+ if (BN_cmp(cand, BN_value_one()) != 0 &&
+ BN_cmp(cand, n1) != 0) {
+ if (BN_mod_exp(tmp, cand, two, n, ctx) != 1)
+ goto finally;
+ if (BN_cmp(tmp, BN_value_one()) == 0) {
+ if (BN_add(tmp, cand, BN_value_one()) !=
+ 1 ||
+ BN_gcd(q, tmp, n, ctx) != 1 ||
+ BN_div(p, tmp, n, q, ctx) != 1 ||
+ BN_is_zero(tmp) != 1) {
+ ret = FALSE;
+ goto finally;
+ }
+ found = TRUE;
+ break;
+ }
+ }
+ if (BN_lshift1(k, k) != 1)
+ goto finally;
+ }
+ if (found)
+ break;
+ }
+
+ if (!found)
+ goto finally;
+have_pq:
+ dmp1 = BN_secure_new();
+ dmq1 = BN_secure_new();
+ iqmp = BN_secure_new();
+ if (BN_sub(tmp, p, BN_value_one()) == 1 &&
+ BN_mod(dmp1, d, tmp, ctx) == 1 &&
+ BN_sub(tmp, q, BN_value_one()) == 1 &&
+ BN_mod(dmq1, d, tmp, ctx) == 1 &&
+ BN_mod_inverse(iqmp, q, p, ctx) != NULL) {
+ BN_copy(p_r, p);
+ BN_copy(q_r, q);
+ BN_copy(dmp1_r, dmp1);
+ BN_copy(dmq1_r, dmq1);
+ BN_copy(iqmp_r, iqmp);
+ ret = TRUE;
+ }
+ BN_free(dmp1);
+ BN_free(dmq1);
+ BN_free(iqmp);
+finally:
+ BN_free(p);
+ BN_free(q);
+ BN_free(tmp);
+ BN_free(t);
+ BN_free(k);
+ BN_free(a);
+ BN_free(limit);
+ BN_free(cand);
+ BN_free(two);
+ BN_free(n1);
+ BN_free(mphi);
+ BN_CTX_free(ctx);
+ return ret;
+}
+
/* Loads both public and private key */
static bool load_jwk_rsa_key(EVP_PKEY **key_r, bool want_private_key,
const struct json_tree_node *root,
struct dcrypt_private_key *dec_key ATTR_UNUSED,
const char **error_r)
{
- const char *n, *e, *d = NULL, *p = NULL, *q = NULL, *dp = NULL;
- const char *dq = NULL, *qi = NULL;
+ const char *n, *e, *d = NULL, *p = NULL, *q = NULL;
+ const char *dp = NULL, *dq = NULL, *qi = NULL;
const struct json_tree_node *node;
/* n and e must be present */
*error_r = "Missing d parameter";
return FALSE;
}
-
- if ((node = json_tree_node_get_member(root, "p")) == NULL ||
- (p = json_tree_node_get_str(node)) == NULL) {
- *error_r = "Missing p parameter";
- return FALSE;
- }
-
- if ((node = json_tree_node_get_member(root, "q")) == NULL ||
- (q = json_tree_node_get_str(node)) == NULL) {
- *error_r = "Missing q parameter";
- return FALSE;
- }
-
- if ((node = json_tree_node_get_member(root, "dp")) == NULL ||
- (dp = json_tree_node_get_str(node)) == NULL) {
- *error_r = "Missing dp parameter";
+ if ((node = json_tree_node_get_member(root, "p")) != NULL)
+ p = json_tree_node_get_str(node);
+ if ((node = json_tree_node_get_member(root, "q")) != NULL)
+ q = json_tree_node_get_str(node);
+ if ((p != NULL && q == NULL) || (p == NULL && q != NULL)) {
+ *error_r = "p and q have to be both present";
return FALSE;
}
- if ((node = json_tree_node_get_member(root, "dq")) == NULL ||
- (dq = json_tree_node_get_str(node)) == NULL) {
- *error_r = "Missing dq parameter";
- return FALSE;
- }
-
- if ((node = json_tree_node_get_member(root, "qi")) == NULL ||
- (qi = json_tree_node_get_str(node)) == NULL) {
- *error_r = "Missing qi parameter";
+ if ((node = json_tree_node_get_member(root, "dp")) != NULL)
+ dp = json_tree_node_get_str(node);
+ if ((node = json_tree_node_get_member(root, "dq")) != NULL)
+ dq = json_tree_node_get_str(node);
+ if ((node = json_tree_node_get_member(root, "qi")) != NULL)
+ qi = json_tree_node_get_str(node);
+ if ((dq != NULL || dp != NULL || qi != NULL) &&
+ (dp == NULL || dq == NULL || qi == NULL)) {
+ *error_r = "dp, dq, and qi must be present together";
return FALSE;
}
}
/* convert into BIGNUMs */
- BIGNUM *pn = NULL, *pe = NULL, *pd = NULL, *pp = NULL;
- BIGNUM *pq = NULL, *pdp = NULL, *pdq = NULL, *pqi = NULL;
+ BIGNUM *pn, *pe, *pd;
+ BIGNUM *pp = NULL, *pq = NULL, *pdp = NULL, *pdq = NULL, *pqi = NULL;
buffer_t *bn = t_base64url_decode_str(n);
buffer_t *be = t_base64url_decode_str(e);
if (want_private_key) {
pd = BN_secure_new();
+ BN_set_flags(pd, BN_FLG_CONSTTIME);
buffer_t *bd = t_base64url_decode_str(d);
if (BN_bin2bn(bd->data, bd->used, pd) == NULL) {
BN_free(pd);
return dcrypt_openssl_error(error_r);
}
+ pp = BN_secure_new();
+ BN_set_flags(pp, BN_FLG_CONSTTIME);
+ pq = BN_secure_new();
+ BN_set_flags(pq, BN_FLG_CONSTTIME);
+ if (p != NULL) {
+ buffer_t *bp = t_base64url_decode_str(p);
+ buffer_t *bq = t_base64url_decode_str(q);
+
+ if (BN_bin2bn(bp->data, bp->used, pp) == NULL ||
+ BN_bin2bn(bq->data, bq->used, pq) == NULL) {
+ BN_free(pd);
+ BN_free(pp);
+ BN_free(pq);
+ return dcrypt_openssl_error(error_r);
+ }
+ }
+ pdp = BN_secure_new();
+ pdq = BN_secure_new();
+ pqi = BN_secure_new();
+ if (dp != NULL) {
+ buffer_t *bdp = t_base64url_decode_str(dp);
+ buffer_t *bdq = t_base64url_decode_str(dq);
+ buffer_t *bqi = t_base64url_decode_str(qi);
+
+ if (BN_bin2bn(bdp->data, bdp->used, pdp) == NULL ||
+ BN_bin2bn(bdq->data, bdq->used, pdq) == NULL ||
+ BN_bin2bn(bqi->data, bqi->used, pqi) == NULL) {
+ BN_free(pd);
+ BN_free(pp);
+ BN_free(pq);
+ BN_free(pdp);
+ BN_free(pdq);
+ BN_free(pqi);
+ return dcrypt_openssl_error(error_r);
+ }
+ }
} else {
pd = NULL;
}
pn = BN_new();
+ BN_set_flags(pn, BN_FLG_CONSTTIME);
pe = BN_new();
+ BN_set_flags(pe, BN_FLG_CONSTTIME);
if (BN_bin2bn(bn->data, bn->used, pn) == NULL ||
BN_bin2bn(be->data, be->used, pe) == NULL) {
- BN_free(pd);
BN_free(pn);
BN_free(pe);
+ if (want_private_key) {
+ BN_free(pd);
+ BN_free(pp);
+ BN_free(pq);
+ BN_free(pdp);
+ BN_free(pdq);
+ BN_free(pqi);
+ }
return dcrypt_openssl_error(error_r);
}
if (want_private_key) {
- pp = BN_secure_new();
- pq = BN_secure_new();
- pdp = BN_secure_new();
- pdq = BN_secure_new();
- pqi = BN_secure_new();
-
- buffer_t *bp = t_base64url_decode_str(p);
- buffer_t *bq = t_base64url_decode_str(q);
- buffer_t *bdp = t_base64url_decode_str(dp);
- buffer_t *bdq = t_base64url_decode_str(dq);
- buffer_t *bqi = t_base64url_decode_str(qi);
-
- if (BN_bin2bn(bp->data, bp->used, pp) == NULL ||
- BN_bin2bn(bq->data, bq->used, pq) == NULL ||
- BN_bin2bn(bdp->data, bdp->used, pdp) == NULL ||
- BN_bin2bn(bdq->data, bdq->used, pdq) == NULL ||
- BN_bin2bn(bqi->data, bqi->used, pqi) == NULL) {
+ if (dp == NULL &&
+ !dcrypt_openssl_derive_rsa_param(pe, pn, pd, pp, pq, pdp,
+ pdq, pqi)) {
BN_free(pn);
BN_free(pe);
+ BN_free(pd);
BN_free(pp);
BN_free(pq);
BN_free(pdp);
BN_free(pdq);
BN_free(pqi);
- return dcrypt_openssl_error(error_r);
+ *error_r = "Cannot derive rsa primes";
+ return FALSE;
}
}
/* create pkey */
- OSSL_PARAM params[9];
- params[0] = dcrypt_construct_param_BN(OSSL_PKEY_PARAM_RSA_N, pn);
- params[1] = dcrypt_construct_param_BN(OSSL_PKEY_PARAM_RSA_E, pe);
+ OSSL_PARAM_BLD *build = OSSL_PARAM_BLD_new();
+
+ OSSL_PARAM_BLD_push_BN(build, OSSL_PKEY_PARAM_RSA_N, pn);
+ OSSL_PARAM_BLD_push_BN(build, OSSL_PKEY_PARAM_RSA_E, pe);
if (want_private_key) {
- params[2] = dcrypt_construct_param_BN(OSSL_PKEY_PARAM_RSA_FACTOR1, pp);
- params[3] = dcrypt_construct_param_BN(OSSL_PKEY_PARAM_RSA_FACTOR2, pq);
- params[4] = dcrypt_construct_param_BN(OSSL_PKEY_PARAM_RSA_EXPONENT1, pdp);
- params[5] = dcrypt_construct_param_BN(OSSL_PKEY_PARAM_RSA_EXPONENT1, pdq);
- params[6] = dcrypt_construct_param_BN(OSSL_PKEY_PARAM_RSA_COEFFICIENT1, pqi);
- params[7] = OSSL_PARAM_construct_end();
- } else {
- params[2] = OSSL_PARAM_construct_end();
+ OSSL_PARAM_BLD_push_BN(build, OSSL_PKEY_PARAM_RSA_D, pd);
+ OSSL_PARAM_BLD_push_BN(build, OSSL_PKEY_PARAM_RSA_FACTOR1, pp);
+ OSSL_PARAM_BLD_push_BN(build, OSSL_PKEY_PARAM_RSA_FACTOR2, pq);
+ OSSL_PARAM_BLD_push_BN(build, OSSL_PKEY_PARAM_RSA_EXPONENT1,
+ pdp);
+ OSSL_PARAM_BLD_push_BN(build, OSSL_PKEY_PARAM_RSA_EXPONENT2,
+ pdq);
+ OSSL_PARAM_BLD_push_BN(build, OSSL_PKEY_PARAM_RSA_COEFFICIENT1,
+ pqi);
}
+ OSSL_PARAM *params = OSSL_PARAM_BLD_to_param(build);
+
/* then load the key */
EVP_PKEY_CTX *ctx =
EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL);
/* pass */
}
EVP_PKEY_CTX_free(ctx);
+ OSSL_PARAM_BLD_free(build);
BN_free(pn);
BN_free(pe);
BN_free(pdp);
BN_free(pdq);
BN_free(pqi);
+ BN_free(pd);
}
if (ec != 1) {
EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &d);
if (d == NULL) {
*error_r = "No private key available";
- json_ostream_destroy(&joutput);
+ json_ostream_nfinish_destroy(&joutput);
return FALSE;
}
str_truncate(b64url_temp, 0);
.error = "point is not on curve",
.public = FALSE,
},
+ /* JWT RSA: Missing d */
+ {
+ .key = "{" \
+ "\"p\": \"0Il4JCQvWtDxyVEHd18rqxhXzdzIaJ3Xq5311ppIXs-oNCe2G2eTAE-CRiePOE0aQ0rl0fjkEeL8kRZZa17npQ\"," \
+ "\"kty\": \"RSA\"," \
+ "\"q\": \"stp6wLoE3XI3oITZO73DkhpDpuNpZ4uMHCg8GCcj784xhLtlPF_hiPgNMgT7tS4JFHFwn7V5GEG3Rk8ThDVvPQ\"," \
+ "\"e\": \"AQAB\"," \
+ "\"use\": \"sig\"," \
+ "\"n\": \"kbGOl_HS6aYs8Ya2Y-OMlK8YcaGldcLanU6wF8nCI0WnedR_DnzZllDhWr7o8h0J5BKuL7Hop_8rn5zSEva213_Zpy3cE5DdrWtdpGyyz9cTceuhukvFSBfw_D4HOQdigRYwerl8Oq6kqCYDL5ui-TmYDLbL_oVdXshfMsU2vVE\"" \
+ "}",
+ .error = "key is not private",
+ .public = FALSE,
+ },
+ /* JWT RSA: Missing e */
+ {
+ .key = "{" \
+ "\"p\": \"0Il4JCQvWtDxyVEHd18rqxhXzdzIaJ3Xq5311ppIXs-oNCe2G2eTAE-CRiePOE0aQ0rl0fjkEeL8kRZZa17npQ\"," \
+ "\"kty\": \"RSA\"," \
+ "\"q\": \"stp6wLoE3XI3oITZO73DkhpDpuNpZ4uMHCg8GCcj784xhLtlPF_hiPgNMgT7tS4JFHFwn7V5GEG3Rk8ThDVvPQ\"," \
+ "\"d\": \"CqxINudXPRiYWEU3HVAxHz9IeiKOXXcdzsJR8hwsparXnvwrJqOMyQ85ww0TQZFRBS09J29QDOaLipDRbuQ19q0c7k0ek_sIrrzx9iulSCPdrbhdw0LS48HfsJxoD5xFg8E5BtDAjnd0P3eUrtG3R1rZXpvnlMd6-kLW-WAyGnE\"," \
+ "\"use\": \"sig\"," \
+ "\"n\": \"kbGOl_HS6aYs8Ya2Y-OMlK8YcaGldcLanU6wF8nCI0WnedR_DnzZllDhWr7o8h0J5BKuL7Hop_8rn5zSEva213_Zpy3cE5DdrWtdpGyyz9cTceuhukvFSBfw_D4HOQdigRYwerl8Oq6kqCYDL5ui-TmYDLbL_oVdXshfMsU2vVE\"" \
+ "}",
+ .error = "Missing e parameter",
+ .public = FALSE,
+ },
+ /* JWT RSA: Missing n */
+ {
+ .key = "{" \
+ "\"p\": \"0Il4JCQvWtDxyVEHd18rqxhXzdzIaJ3Xq5311ppIXs-oNCe2G2eTAE-CRiePOE0aQ0rl0fjkEeL8kRZZa17npQ\"," \
+ "\"kty\": \"RSA\"," \
+ "\"q\": \"stp6wLoE3XI3oITZO73DkhpDpuNpZ4uMHCg8GCcj784xhLtlPF_hiPgNMgT7tS4JFHFwn7V5GEG3Rk8ThDVvPQ\"," \
+ "\"d\": \"CqxINudXPRiYWEU3HVAxHz9IeiKOXXcdzsJR8hwsparXnvwrJqOMyQ85ww0TQZFRBS09J29QDOaLipDRbuQ19q0c7k0ek_sIrrzx9iulSCPdrbhdw0LS48HfsJxoD5xFg8E5BtDAjnd0P3eUrtG3R1rZXpvnlMd6-kLW-WAyGnE\"," \
+ "\"e\": \"AQAB\"," \
+ "\"use\": \"sig\"" \
+ "}",
+ .error = "Missing n parameter",
+ .public = FALSE,
+ },
+ /* JWT RSA: Mismatch p,q and d */
+ {
+ .key = "{" \
+ "\"p\": \"7v5Z9uz-SES2yQ1lWTvYIqxW7B6avGJcI6W_t5c5JDzLxmT3OQ-kw9oJV7oYvFVagIVgmTgP1ymvbcfYzDSKKQ\"," \
+ "\"kty\": \"RSA\"," \
+ "\"q\": \"sdZWMrIiOa8XJFtRkHKZE5sp6PTvTH1H52Zcr7O14j39zMmR-i9GL1-uI_EQGBA0TK-zxnZ-incUm2cWirwVbw\"," \
+ "\"d\": \"CqxINudXPRiYWEU3HVAxHz9IeiKOXXcdzsJR8hwsparXnvwrJqOMyQ85ww0TQZFRBS09J29QDOaLipDRbuQ19q0c7k0ek_sIrrzx9iulSCPdrbhdw0LS48HfsJxoD5xFg8E5BtDAjnd0P3eUrtG3R1rZXpvnlMd6-kLW-WAyGnE\"," \
+ "\"e\": \"AQAB\"," \
+ "\"use\": \"sig\"," \
+ "\"n\": \"kbGOl_HS6aYs8Ya2Y-OMlK8YcaGldcLanU6wF8nCI0WnedR_DnzZllDhWr7o8h0J5BKuL7Hop_8rn5zSEva213_Zpy3cE5DdrWtdpGyyz9cTceuhukvFSBfw_D4HOQdigRYwerl8Oq6kqCYDL5ui-TmYDLbL_oVdXshfMsU2vVE\"" \
+ "}",
+ .error = "Cannot derive rsa primes",
+ .public = FALSE,
+ },
+ /* JWT RSA: Mismatch n and p,q,d */
+ {
+ .key = "{" \
+ "\"p\": \"0Il4JCQvWtDxyVEHd18rqxhXzdzIaJ3Xq5311ppIXs-oNCe2G2eTAE-CRiePOE0aQ0rl0fjkEeL8kRZZa17npQ\"," \
+ "\"kty\": \"RSA\"," \
+ "\"q\": \"stp6wLoE3XI3oITZO73DkhpDpuNpZ4uMHCg8GCcj784xhLtlPF_hiPgNMgT7tS4JFHFwn7V5GEG3Rk8ThDVvPQ\"," \
+ "\"d\": \"CqxINudXPRiYWEU3HVAxHz9IeiKOXXcdzsJR8hwsparXnvwrJqOMyQ85ww0TQZFRBS09J29QDOaLipDRbuQ19q0c7k0ek_sIrrzx9iulSCPdrbhdw0LS48HfsJxoD5xFg8E5BtDAjnd0P3eUrtG3R1rZXpvnlMd6-kLW-WAyGnE\"," \
+ "\"e\": \"AQAB\"," \
+ "\"use\": \"sig\"," \
+ "\"n\": \"pgX1S7R5QI9c2Y7X6KXgUyJdOLJP3DZWBR-NR7w96rHOayKeAHKPiyg9vyGzV8rB_uePbmrnhBAZ42IDlXW_AieJGhPLYkH34d4FX8cC7mmWWXXjolajZWoW5pCg2Ilkk47R1osXGkS6Ta97ODtAvzw7PAJq1jFFPjYXVmo5RMc\"" \
+ "}",
+ .error = "Cannot derive rsa primes",
+ .public = FALSE,
+ },
+ /* JWT RSA: Mismatch e */
+ {
+ .key = "{" \
+ "\"p\": \"0Il4JCQvWtDxyVEHd18rqxhXzdzIaJ3Xq5311ppIXs-oNCe2G2eTAE-CRiePOE0aQ0rl0fjkEeL8kRZZa17npQ\"," \
+ "\"kty\": \"RSA\"," \
+ "\"q\": \"stp6wLoE3XI3oITZO73DkhpDpuNpZ4uMHCg8GCcj784xhLtlPF_hiPgNMgT7tS4JFHFwn7V5GEG3Rk8ThDVvPQ\"," \
+ "\"d\": \"CqxINudXPRiYWEU3HVAxHz9IeiKOXXcdzsJR8hwsparXnvwrJqOMyQ85ww0TQZFRBS09J29QDOaLipDRbuQ19q0c7k0ek_sIrrzx9iulSCPdrbhdw0LS48HfsJxoD5xFg8E5BtDAjnd0P3eUrtG3R1rZXpvnlMd6-kLW-WAyGnE\"," \
+ "\"e\": \"AQAC\"," \
+ "\"use\": \"sig\"," \
+ "\"n\": \"kbGOl_HS6aYs8Ya2Y-OMlK8YcaGldcLanU6wF8nCI0WnedR_DnzZllDhWr7o8h0J5BKuL7Hop_8rn5zSEva213_Zpy3cE5DdrWtdpGyyz9cTceuhukvFSBfw_D4HOQdigRYwerl8Oq6kqCYDL5ui-TmYDLbL_oVdXshfMsU2vVE\"" \
+ "}",
+ .error = "Cannot derive rsa primes",
+ .public = FALSE,
+ },
+
};
for (size_t i = 0; i < N_ELEMENTS(invalid_keys); i++) {
struct dcrypt_keypair pair;
i_zero(&pair);
bool ret;
- const char *error;
+ const char *error = NULL;
if (invalid_keys[i].public) {
ret = dcrypt_key_load_public(
&pair.pub, invalid_keys[i].key, &error);
}
test_assert_idx(ret == FALSE, i);
test_assert_idx(error != NULL, i);
- test_assert_idx(strstr(error, invalid_keys[i].error) != NULL,
+ test_assert_idx(error != NULL && strstr(error, invalid_keys[i].error) != NULL,
i);
if (ret)
dcrypt_keypair_unref(&pair);
dcrypt_keypair_unref(&pair);
+ /* try loading RSA keys */
+
+ jwk_key_json = "{" \
+"\"p\": \"0Il4JCQvWtDxyVEHd18rqxhXzdzIaJ3Xq5311ppIXs-oNCe2G2eTAE-CRiePOE0aQ0rl0fjkEeL8kRZZa17npQ\"," \
+"\"kty\": \"RSA\"," \
+"\"q\": \"stp6wLoE3XI3oITZO73DkhpDpuNpZ4uMHCg8GCcj784xhLtlPF_hiPgNMgT7tS4JFHFwn7V5GEG3Rk8ThDVvPQ\"," \
+"\"d\": \"CqxINudXPRiYWEU3HVAxHz9IeiKOXXcdzsJR8hwsparXnvwrJqOMyQ85ww0TQZFRBS09J29QDOaLipDRbuQ19q0c7k0ek_sIrrzx9iulSCPdrbhdw0LS48HfsJxoD5xFg8E5BtDAjnd0P3eUrtG3R1rZXpvnlMd6-kLW-WAyGnE\"," \
+"\"e\": \"AQAB\"," \
+"\"use\": \"sig\"," \
+"\"qi\": \"PuEkPmG12QTnyYd0DXcNJTD-aq8CCl3alpSn_ra4V-2p3r__auYVm-Z_DvxFmGQNPWf6ENA7i4ETTXJ29V8O1A\"," \
+"\"dp\": \"fKJ8tJUP3GZe6-RK4xR1Y_fTmo0nFieoW8C3yoBBWrEfpbRKScy4dgoPIWDJX2vtk2RzQPBRV0Njkk9aOCrrpQ\"," \
+"\"dq\": \"Ugph8HHjtWilF56Yvwym3yfpDG6YdQTP-kKCflnF5ERi9o23PGCG5ftSojUi-NLrG1OF49ysdXH_jeLMAwM3yQ\"," \
+"\"n\": \"kbGOl_HS6aYs8Ya2Y-OMlK8YcaGldcLanU6wF8nCI0WnedR_DnzZllDhWr7o8h0J5BKuL7Hop_8rn5zSEva213_Zpy3cE5DdrWtdpGyyz9cTceuhukvFSBfw_D4HOQdigRYwerl8Oq6kqCYDL5ui-TmYDLbL_oVdXshfMsU2vVE\"" \
+"}";
+
+ test_assert(dcrypt_key_load_private(&pair.priv, jwk_key_json, NULL, NULL, &error));
+ dcrypt_keypair_unref(&pair);
+
+ jwk_key_json = "{" \
+"\"kty\": \"RSA\"," \
+"\"d\": \"CqxINudXPRiYWEU3HVAxHz9IeiKOXXcdzsJR8hwsparXnvwrJqOMyQ85ww0TQZFRBS09J29QDOaLipDRbuQ19q0c7k0ek_sIrrzx9iulSCPdrbhdw0LS48HfsJxoD5xFg8E5BtDAjnd0P3eUrtG3R1rZXpvnlMd6-kLW-WAyGnE\"," \
+"\"e\": \"AQAB\"," \
+"\"use\": \"sig\"," \
+"\"n\": \"kbGOl_HS6aYs8Ya2Y-OMlK8YcaGldcLanU6wF8nCI0WnedR_DnzZllDhWr7o8h0J5BKuL7Hop_8rn5zSEva213_Zpy3cE5DdrWtdpGyyz9cTceuhukvFSBfw_D4HOQdigRYwerl8Oq6kqCYDL5ui-TmYDLbL_oVdXshfMsU2vVE\"" \
+"}";
+
+ test_assert(dcrypt_key_load_private(&pair.priv, jwk_key_json, NULL, NULL, &error));
+ jwk_key_json = "{" \
+"\"kty\": \"RSA\"," \
+"\"e\": \"AQAB\"," \
+"\"use\": \"sig\"," \
+"\"n\": \"kbGOl_HS6aYs8Ya2Y-OMlK8YcaGldcLanU6wF8nCI0WnedR_DnzZllDhWr7o8h0J5BKuL7Hop_8rn5zSEva213_Zpy3cE5DdrWtdpGyyz9cTceuhukvFSBfw_D4HOQdigRYwerl8Oq6kqCYDL5ui-TmYDLbL_oVdXshfMsU2vVE\"" \
+"}";
+ test_assert(dcrypt_key_load_public(&pair.pub, jwk_key_json, &error));
+ dcrypt_keypair_unref(&pair);
+
test_end();
}