openssl: Add support for RFC4716 keys
[paraslash.git] / openssl.c
index bda5791..9782b5e 100644 (file)
--- a/openssl.c
+++ b/openssl.c
@@ -124,7 +124,7 @@ free_rsa:
        return ret;
 }
 
-static int get_private_key(const char *path, RSA **rsa)
+static int read_pem_private_key(const char *path, RSA **rsa)
 {
        EVP_PKEY *pkey;
        BIO *bio = BIO_new(BIO_s_file());
@@ -144,6 +144,132 @@ bio_free:
        return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
 }
 
+static int read_private_rsa_params(const unsigned char *blob,
+               const unsigned char *end, RSA **result)
+{
+       int ret;
+       RSA *rsa;
+       BN_CTX *ctx;
+       BIGNUM *n, *e, *d, *iqmp, *p, *q; /* stored in the key file */
+       BIGNUM *dmp1, *dmq1; /* these will be computed */
+       BIGNUM *tmp;
+       const unsigned char *cp = blob;
+
+       rsa = RSA_new();
+       if (!rsa)
+               return -E_BIGNUM;
+       ret = -E_BIGNUM;
+       tmp = BN_new();
+       if (!tmp)
+               goto free_rsa;
+       ctx = BN_CTX_new();
+       if (!ctx)
+               goto free_tmp;
+       dmp1 = BN_new();
+       if (!dmp1)
+               goto free_ctx;
+       dmq1 = BN_new();
+       if (!dmq1)
+               goto free_dmp1;
+       ret = read_bignum(cp, end - cp, &n);
+       if (ret < 0)
+               goto free_dmq1;
+       cp += ret;
+       ret = read_bignum(cp, end - cp, &e);
+       if (ret < 0)
+               goto free_n;
+       cp += ret;
+       ret = read_bignum(cp, end - cp, &d);
+       if (ret < 0)
+               goto free_e;
+       cp += ret;
+       ret = read_bignum(cp, end - cp, &iqmp);
+       if (ret < 0)
+               goto free_d;
+       cp += ret;
+       ret = read_bignum(cp, end - cp, &p);
+       if (ret < 0)
+               goto free_iqmp;
+       cp += ret;
+       ret = read_bignum(cp, end - cp, &q);
+       if (ret < 0)
+               goto free_p;
+       ret = -E_BIGNUM;
+       if (!BN_sub(tmp, q, BN_value_one()))
+               goto free_q;
+       if (!BN_mod(dmp1, d, tmp, ctx))
+               goto free_q;
+       if (!BN_sub(tmp, q, BN_value_one()))
+               goto free_q;
+       if (!BN_mod(dmq1, d, tmp, ctx))
+               goto free_q;
+#ifdef HAVE_RSA_SET0_KEY
+       RSA_set0_key(rsa, n, e, d);
+       RSA_set0_factors(rsa, p, q);
+       RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp);
+#else
+       rsa->n = n;
+       rsa->e = e;
+       rsa->d = d;
+       rsa->p = p;
+       rsa->q = q;
+       rsa->dmp1 = dmp1;
+       rsa->dmq1 = dmq1;
+       rsa->iqmp = iqmp;
+#endif
+       *result = rsa;
+       ret = 1;
+       goto free_ctx;
+free_q:
+       BN_clear_free(q);
+free_p:
+       BN_clear_free(p);
+free_iqmp:
+       BN_clear_free(iqmp);
+free_d:
+       BN_clear_free(d);
+free_e:
+       BN_free(e);
+free_n:
+       BN_free(n);
+free_dmq1:
+       BN_clear_free(dmq1);
+free_dmp1:
+       BN_clear_free(dmp1);
+free_ctx:
+       BN_CTX_free(ctx);
+free_tmp:
+       BN_clear_free(tmp);
+free_rsa:
+       if (ret < 0)
+               RSA_free(rsa);
+       return ret;
+}
+
+static int get_private_key(const char *path, RSA **rsa)
+{
+       int ret;
+       unsigned char *blob, *end;
+       size_t blob_size;
+
+       *rsa = NULL;
+       ret = decode_private_key(path, &blob, &blob_size);
+       if (ret < 0)
+               return ret;
+       end = blob + blob_size;
+       if (ret == PKT_OPENSSH) {
+               ret = find_openssh_bignum_offset(blob, blob_size);
+               if (ret < 0)
+                       goto free_blob;
+               PARA_INFO_LOG("reading RSA params at offset %d\n", ret);
+               ret = read_private_rsa_params(blob + ret, end, rsa);
+       } else
+               ret = read_pem_private_key(path, rsa);
+free_blob:
+       free(blob);
+       return ret;
+}
+
 int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
 {
        unsigned char *blob;