]> git.tue.mpg.de Git - paraslash.git/commitdiff
openssl: Add support for RFC4716 keys
authorAndre Noll <maan@tuebingen.mpg.de>
Thu, 30 Aug 2018 13:32:34 +0000 (15:32 +0200)
committerAndre Noll <maan@tuebingen.mpg.de>
Tue, 25 Dec 2018 16:45:25 +0000 (17:45 +0100)
The decode_private_key() helper of gcrypt.c base64-decodes the key
but does not read the RSA bignums. It is thus independent of gcrypt
and openssl. To add support for RFC4716 keys to the openssl backend,
we need to move it to crypt_common.c, making it a crypo backend
function. That is, non-static and declared in crypt_backend.h. The
patch also documents the function using doxgen comments.

With decode_private_key() and find_openssh_bignum_offset() to
our disposal, supporting RFC4716 keys is simple. We only need to
initialize the openssl-specific rsa structure with the six bignums
stored in a private RFC4716 key. This is implemented in the new
read_private_rsa_params() which calls the existing read_bignum()
six times.

The fields of the rsa structure are exposed to applications in
openssl-1.0, but the structure was made opaque in openssl-1.1. We
use the existing HAVE_RSA_SET0_KEY macro to decide whether or not
we must use the accessor functions of openssl-1.1 to initialize the
rsa structure.

crypt_backend.h
crypt_common.c
gcrypt.c
openssl.c

index ae01fc14733a1068b0513afbd8f9e5b9bc791827..b0998d8513f35303243ccd632abc3c0aa46f6377 100644 (file)
@@ -9,5 +9,11 @@
 
 int decode_public_key(const char *filename, unsigned char **blob,
                size_t *decoded_size);
+int decode_private_key(const char *key_file, unsigned char **result,
+               size_t *blob_size);
+/** Legacy PEM keys (openssh-7.7 and earlier, paraslash.0.6.2 and earlier) */
+#define PKT_PEM (0)
+/** OPENSSH keys (since openssh-7.8, paraslash.0.6.3) */
+#define PKT_OPENSSH (1)
 int check_private_key_file(const char *file);
 int find_openssh_bignum_offset(const unsigned char *data, int len);
index 277897f2b47a3c9587d4bb08a5b634b7d4c53f46..c1e40d920ff425ccfdcc536c5fb69cd0422472ca 100644 (file)
@@ -230,3 +230,90 @@ int find_openssh_bignum_offset(const unsigned char *data, int len)
        return p - data;
 }
 
+/** Private PEM keys (legacy format) start with this header. */
+#define PRIVATE_PEM_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----"
+/** Private OPENSSH keys (RFC4716) start with this header. */
+#define PRIVATE_OPENSSH_KEY_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----"
+/** Private PEM keys (legacy format) end with this footer. */
+#define PRIVATE_PEM_KEY_FOOTER "-----END RSA PRIVATE KEY-----"
+/** Private OPENSSH keys (RFC4716) end with this footer. */
+#define PRIVATE_OPENSSH_KEY_FOOTER "-----END OPENSSH PRIVATE KEY-----"
+
+/**
+ * Decode an openssh-v1 (aka RFC4716) or PEM (aka ASN.1) private key.
+ *
+ * \param key_file The private key file (usually id_rsa).
+ * \param result Pointer to base64-decoded blob is returned here.
+ * \param blob_size The size of the decoded blob.
+ *
+ * This only checks header and footer and base64-decodes the part in between.
+ * No attempt to read the decoded part is made.
+ *
+ * \return Negative on errors, PKT_PEM or PKT_OPENSSH on success, indicating
+ * the type of key.
+ */
+int decode_private_key(const char *key_file, unsigned char **result,
+               size_t *blob_size)
+{
+       int ret, ret2, i, j, key_type;
+       void *map;
+       size_t map_size, key_size;
+       unsigned char *blob = NULL;
+       char *begin, *footer, *key;
+
+       ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
+       if (ret < 0)
+               goto out;
+       ret = -E_KEY_MARKER;
+       if (strncmp(map, PRIVATE_PEM_KEY_HEADER,
+                       strlen(PRIVATE_PEM_KEY_HEADER)) == 0) {
+               key_type = PKT_PEM;
+               begin = map + strlen(PRIVATE_PEM_KEY_HEADER);
+               footer = strstr(map, PRIVATE_PEM_KEY_FOOTER);
+               PARA_INFO_LOG("detected legacy PEM key %s\n", key_file);
+       } else if (strncmp(map, PRIVATE_OPENSSH_KEY_HEADER,
+                       strlen(PRIVATE_OPENSSH_KEY_HEADER)) == 0) {
+               key_type = PKT_OPENSSH;
+               begin = map + strlen(PRIVATE_OPENSSH_KEY_HEADER);
+               footer = strstr(map, PRIVATE_OPENSSH_KEY_FOOTER);
+               PARA_INFO_LOG("detected openssh key %s\n", key_file);
+       } else
+               goto unmap;
+       if (!footer)
+               goto unmap;
+       /* skip whitespace at the beginning */
+       for (; begin < footer; begin++) {
+               if (para_isspace(*begin))
+                       continue;
+               break;
+       }
+       ret = -E_KEY_MARKER;
+       if (begin >= footer)
+               goto unmap;
+
+       key_size = footer - begin;
+       key = para_malloc(key_size + 1);
+       for (i = 0, j = 0; begin + i < footer; i++) {
+               if (para_isspace(begin[i]))
+                       continue;
+               key[j++] = begin[i];
+       }
+       key[j] = '\0';
+       ret = base64_decode(key, j, (char **)&blob, blob_size);
+       free(key);
+       if (ret < 0)
+               goto unmap;
+       ret = key_type;
+unmap:
+       ret2 = para_munmap(map, map_size);
+       if (ret >= 0 && ret2 < 0)
+               ret = ret2;
+       if (ret < 0) {
+               free(blob);
+               blob = NULL;
+       }
+out:
+       *result = blob;
+       return ret;
+}
+
index 69c2c34f0b4e87fd57b0c22f2a6d7725eaaaa043..dbe4900862fef83a552d9c60d9ac21d4c8253d5e 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -12,7 +12,6 @@
 #include "crypt_backend.h"
 #include "fd.h"
 #include "base64.h"
-#include "portable_io.h"
 
 //#define GCRYPT_DEBUG 1
 
@@ -107,84 +106,6 @@ static const char *gcrypt_strerror(gcry_error_t gret)
        return gcry_strerror(gcry_err_code(gret));
 }
 
-/** Private PEM keys (legacy format) start with this header. */
-#define PRIVATE_PEM_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----"
-/** Private OPENSSH keys (RFC4716) start with this header. */
-#define PRIVATE_OPENSSH_KEY_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----"
-/** Private PEM keys (legacy format) end with this footer. */
-#define PRIVATE_PEM_KEY_FOOTER "-----END RSA PRIVATE KEY-----"
-/** Private OPENSSH keys (RFC4716) end with this footer. */
-#define PRIVATE_OPENSSH_KEY_FOOTER "-----END OPENSSH PRIVATE KEY-----"
-/** Legacy PEM keys (openssh-7.7 and earlier, paraslash.0.6.2 and earlier) */
-#define PKT_PEM (0)
-/** OPENSSH keys (since openssh-7.8, paraslash.0.6.3) */
-#define PKT_OPENSSH (1)
-
-static int decode_private_key(const char *key_file, unsigned char **result,
-               size_t *blob_size)
-{
-       int ret, ret2, i, j, key_type;
-       void *map;
-       size_t map_size, key_size;
-       unsigned char *blob = NULL;
-       char *begin, *footer, *key;
-
-       ret = mmap_full_file(key_file, O_RDONLY, &map, &map_size, NULL);
-       if (ret < 0)
-               goto out;
-       ret = -E_KEY_MARKER;
-       if (strncmp(map, PRIVATE_PEM_KEY_HEADER,
-                       strlen(PRIVATE_PEM_KEY_HEADER)) == 0) {
-               key_type = PKT_PEM;
-               begin = map + strlen(PRIVATE_PEM_KEY_HEADER);
-               footer = strstr(map, PRIVATE_PEM_KEY_FOOTER);
-               PARA_INFO_LOG("detected legacy PEM key %s\n", key_file);
-       } else if (strncmp(map, PRIVATE_OPENSSH_KEY_HEADER,
-                       strlen(PRIVATE_OPENSSH_KEY_HEADER)) == 0) {
-               key_type = PKT_OPENSSH;
-               begin = map + strlen(PRIVATE_OPENSSH_KEY_HEADER);
-               footer = strstr(map, PRIVATE_OPENSSH_KEY_FOOTER);
-               PARA_INFO_LOG("detected openssh key %s\n", key_file);
-       } else
-               goto unmap;
-       if (!footer)
-               goto unmap;
-       /* skip whitespace at the beginning */
-       for (; begin < footer; begin++) {
-               if (para_isspace(*begin))
-                       continue;
-               break;
-       }
-       ret = -E_KEY_MARKER;
-       if (begin >= footer)
-               goto unmap;
-
-       key_size = footer - begin;
-       key = para_malloc(key_size + 1);
-       for (i = 0, j = 0; begin + i < footer; i++) {
-               if (para_isspace(begin[i]))
-                       continue;
-               key[j++] = begin[i];
-       }
-       key[j] = '\0';
-       ret = base64_decode(key, j, (char **)&blob, blob_size);
-       free(key);
-       if (ret < 0)
-               goto unmap;
-       ret = key_type;
-unmap:
-       ret2 = para_munmap(map, map_size);
-       if (ret >= 0 && ret2 < 0)
-               ret = ret2;
-       if (ret < 0) {
-               free(blob);
-               blob = NULL;
-       }
-out:
-       *result = blob;
-       return ret;
-}
-
 /** ASN Types and their code. */
 enum asn1_types {
        /** The next object is an integer. */
index bda5791fc51d1095a6115ecd5c0476852f517a5e..9782b5e23cd0530d521f098d2683dc83230b0c78 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;