]> git.tuebingen.mpg.de Git - paraslash.git/commitdiff
Merge branch 'refs/heads/t/ssh'
authorAndre Noll <maan@tuebingen.mpg.de>
Sat, 23 Nov 2019 17:20:21 +0000 (18:20 +0100)
committerAndre Noll <maan@tuebingen.mpg.de>
Sat, 23 Nov 2019 17:21:32 +0000 (18:21 +0100)
A medium sized series which teaches both crypto backends about the
RFC4716 key format, which has become the default for ssh-keygen as
of openssh-7.8.

Was cooking for almost a year.

* refs/heads/t/ssh:
  manual: Instruct the user to create RFC4716 keys.
  openssl: Add support for RFC4716 keys
  openssl: Move get_private_key() down.
  gcrypt: Add support for RFC4716 private keys.
  crypt: Rename decoding functions.
  gcrypt: Introduce read_openssh_bignum().
  gcrypt: Factor out read_pem_rsa_params().
  gcrypt: Let read_bignum() return bits, not bytes.
  gcrypt: Let decode_key() return blob size through additional argument.
  gcrypt: Drop unnecessary arguments of decode_key().

NEWS.md
crypt_backend.h
crypt_common.c
error.h
gcrypt.c
openssl.c
web/manual.md

diff --git a/NEWS.md b/NEWS.md
index eda654a6a155e2f4c484bfa3013d7edc7d278aec..fd587fab054dea33124918870b24351b20586fc6 100644 (file)
--- a/NEWS.md
+++ b/NEWS.md
@@ -19,7 +19,9 @@ NEWS
 - We now build the tree using the .ONESHELL feature of GNU make,
   which results in a significant speedup.
 - Two robustness fixes for FreeBSD.
-
+- para_client now supports RFC4716 private keys as generated with
+  ssh-keygen -m RFC4716. In fact, this key format has been made the
+  default, and the former PEM keys will be depreciated at some point.
 
 --------------------------------------
 0.6.2 (2018-06-30) "elastic diversity"
index 175a6881c3d6567137513043055f548700c6ab65..b0998d8513f35303243ccd632abc3c0aa46f6377 100644 (file)
@@ -7,6 +7,13 @@
 /** AES block size in bytes. */
 #define AES_CRT128_BLOCK_SIZE 16
 
-int decode_ssh_key(const char *filename, unsigned char **blob,
+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 235b8b8d3e398551fc28bfbc746349c5dc8f6f1c..c1e40d920ff425ccfdcc536c5fb69cd0422472ca 100644 (file)
@@ -85,7 +85,7 @@ static int check_ssh_key_header(const unsigned char *blob, int blen)
  *
  * \sa \ref uudecode().
  */
-int decode_ssh_key(const char *filename, unsigned char **blob,
+int decode_public_key(const char *filename, unsigned char **blob,
                size_t *decoded_size)
 {
        int ret, ret2;
@@ -159,3 +159,161 @@ int hash_compare(unsigned char *h1, unsigned char *h2)
        }
        return 0;
 }
+
+/**
+ * Check header of an openssh private key and compute bignum offset.
+ *
+ * \param data The base64-decoded key.
+ * \param len The size of the decoded key.
+ *
+ * Several assumptions are made about the key. Most notably, we only support
+ * single unencrypted keys without comments.
+ *
+ * \return The offset at which the first bignum of the private key (the public
+ * exponent n) starts. Negative error code on failure.
+ */
+int find_openssh_bignum_offset(const unsigned char *data, int len)
+{
+       /*
+        * Unencrypted keys without comments always start with the below byte
+        * sequence. See PROTOCOL.key of the openssh package.
+        */
+       static const unsigned char valid_openssh_header[] = {
+               /* string "openssh-key-v1" */
+               0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2d, 0x6b, 0x65,
+                       0x79, 0x2d, 0x76, 0x31,
+               /* length of the cipher name */
+               0x00, 0x00, 0x00, 0x00, 0x04,
+               /* cipher name: "none" */
+               0x6e, 0x6f, 0x6e, 0x65,
+               /* length of the kdfname (only used for encrypted keys) */
+               0x00, 0x00, 0x00, 0x04,
+               /* kdfname: "none" */
+               0x6e, 0x6f, 0x6e, 0x65,
+               /* length of kdfoptions */
+               0x00, 0x00, 0x00, 0x00,
+               /* number of keys */
+               0x00, 0x00, 0x00, 0x01,
+       };
+       uint32_t val;
+       const unsigned char *p, *end = data + len;
+
+       if (len <= sizeof(valid_openssh_header) + 4)
+               return -E_OPENSSH_PARSE;
+       if (memcmp(data, valid_openssh_header, sizeof(valid_openssh_header)))
+               return -E_OPENSSH_PARSE;
+       p = data + sizeof(valid_openssh_header);
+       /* length of public key */
+       val = read_u32_be(p);
+       if (val > end - p - 4)
+               return -E_OPENSSH_PARSE;
+       p += val + 4;
+       /* length of private key */
+       val = read_u32_be(p);
+       if (val > end - p - 4)
+               return -E_OPENSSH_PARSE;
+       p += 4;
+       /* two equal random integers ("checkint") */
+       if (p + 8 > end)
+               return -E_OPENSSH_PARSE;
+       if (read_u32_be(p) != read_u32_be(p + 4))
+               return -E_OPENSSH_PARSE;
+       p += 8;
+       /* length of name of key type "ssh-rsa" */
+       if (p + 11 > end)
+               return -E_OPENSSH_PARSE;
+       if (read_u32_be(p) != 7)
+               return -E_OPENSSH_PARSE;
+       if (memcmp(p + 4, "ssh-rsa", 7))
+               return -E_OPENSSH_PARSE;
+       p += 11;
+       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;
+}
+
diff --git a/error.h b/error.h
index 14e5b0726e11e92e80d9ddbcf346504c4b6199c3..8ef9e37d9cc4630178641b59146e0e77f4c0567e 100644 (file)
--- a/error.h
+++ b/error.h
        PARA_ERROR(OGG_PACKET_IN, "ogg_stream_packetin() failed"), \
        PARA_ERROR(OGG_STREAM_FLUSH, "ogg_stream_flush() failed"), \
        PARA_ERROR(OGG_SYNC, "internal ogg storage overflow"), \
+       PARA_ERROR(OPENSSH_PARSE, "could not parse openssh private key"), \
        PARA_ERROR(OPUS_COMMENT, "invalid or corrupted opus comment"), \
        PARA_ERROR(OPUS_DECODE, "opus decode error"), \
        PARA_ERROR(OPUS_HEADER, "invalid opus header"), \
index 694c0adb8454b73dc0e5b67876fc7a03617b61d0..dbe4900862fef83a552d9c60d9ac21d4c8253d5e 100644 (file)
--- a/gcrypt.c
+++ b/gcrypt.c
@@ -106,66 +106,6 @@ static const char *gcrypt_strerror(gcry_error_t gret)
        return gcry_strerror(gcry_err_code(gret));
 }
 
-static int decode_key(const char *key_file, const char *header_str,
-               const char *footer_str, unsigned char **result)
-{
-       int ret, ret2, i, j;
-       void *map;
-       size_t map_size, key_size, blob_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, header_str, strlen(header_str)))
-               goto unmap;
-       footer = strstr(map, footer_str);
-       ret = -E_KEY_MARKER;
-       if (!footer)
-               goto unmap;
-       begin = map + strlen(header_str);
-       /* 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 free_unmap;
-       ret = blob_size;
-       goto unmap;
-free_unmap:
-       free(blob);
-       blob = NULL;
-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. */
@@ -207,11 +147,11 @@ static inline int get_long_form_num_length_bytes(unsigned char c)
 
 /*
  * Returns: Number of bytes scanned. This may differ from the value returned via
- * bn_bytes because the latter does not include the ASN.1 prefix and a leading
- * zero is not considered as an additional byte for bn_bytes.
+ * bitsp because the latter does not include the ASN.1 prefix and a leading
+ * zero is not considered as an additional byte for the number of bits.
  */
-static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn,
-               int *bn_bytes)
+static int read_pem_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn,
+               unsigned *bitsp)
 {
        int i, bn_size;
        gcry_error_t gret;
@@ -249,8 +189,8 @@ static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn,
                cp++;
                bn_size--;
        }
-       if (bn_bytes)
-               *bn_bytes = bn_size;
+       if (bitsp)
+               *bitsp = bn_size * 8;
        cp += bn_size;
 //     unsigned char *buf;
 //     gcry_mpi_aprint(GCRYMPI_FMT_HEX, &buf, NULL, *bn);
@@ -258,7 +198,55 @@ static int read_bignum(unsigned char *start, unsigned char *end, gcry_mpi_t *bn,
        return cp - start;
 }
 
-static int find_privkey_bignum_offset(const unsigned char *data, int len)
+struct rsa_params {
+       gcry_mpi_t n, e, d, p, q, u;
+};
+
+static int read_pem_rsa_params(unsigned char *start, unsigned char *end,
+               struct rsa_params *p)
+{
+       unsigned char *cp = start;
+       unsigned bits;
+       int ret;
+
+       ret = read_pem_bignum(cp, end, &p->n, &bits);
+       if (ret < 0)
+               return ret;
+       cp += ret;
+       ret = read_pem_bignum(cp, end, &p->e, NULL);
+       if (ret < 0)
+               goto release_n;
+       cp += ret;
+       ret = read_pem_bignum(cp, end, &p->d, NULL);
+       if (ret < 0)
+               goto release_e;
+       cp += ret;
+       ret = read_pem_bignum(cp, end, &p->p, NULL);
+       if (ret < 0)
+               goto release_d;
+       cp += ret;
+       ret = read_pem_bignum(cp, end, &p->q, NULL);
+       if (ret < 0)
+               goto release_p;
+       cp += ret;
+       ret = read_pem_bignum(cp, end, &p->u, NULL);
+       if (ret < 0)
+               goto release_q;
+       return bits;
+release_q:
+       gcry_mpi_release(p->q);
+release_p:
+       gcry_mpi_release(p->p);
+release_d:
+       gcry_mpi_release(p->d);
+release_e:
+       gcry_mpi_release(p->e);
+release_n:
+       gcry_mpi_release(p->n);
+       return ret;
+}
+
+static int find_pem_bignum_offset(const unsigned char *data, int len)
 {
        const unsigned char *p = data, *end = data + len;
 
@@ -290,96 +278,131 @@ static int find_privkey_bignum_offset(const unsigned char *data, int len)
        return p - data;
 }
 
-/** Private keys start with this header. */
-#define PRIVATE_KEY_HEADER "-----BEGIN RSA PRIVATE KEY-----"
-/** Private keys end with this footer. */
-#define PRIVATE_KEY_FOOTER "-----END RSA PRIVATE KEY-----"
-
-static int get_private_key(const char *key_file, struct asymmetric_key **result)
+static int read_openssh_bignum(unsigned char *start, unsigned char *end,
+               gcry_mpi_t *bn, unsigned *bitsp)
 {
-       gcry_mpi_t n = NULL, e = NULL, d = NULL, p = NULL, q = NULL,
-               u = NULL;
-       unsigned char *blob, *cp, *end;
-       int blob_size, ret, n_size;
        gcry_error_t gret;
-       size_t erroff;
-       gcry_sexp_t sexp;
-       struct asymmetric_key *key;
+       size_t nscanned;
+       unsigned bits;
 
-       *result = NULL;
-       ret = decode_key(key_file, PRIVATE_KEY_HEADER, PRIVATE_KEY_FOOTER,
-               &blob);
-       if (ret < 0)
-               return ret;
-       blob_size = ret;
-       end = blob + blob_size;
-       ret = find_privkey_bignum_offset(blob, blob_size);
-       if (ret < 0)
-               goto free_blob;
-       PARA_INFO_LOG("reading RSA params at offset %d\n", ret);
-       cp = blob + ret;
+       gret = gcry_mpi_scan(bn, GCRYMPI_FMT_SSH, start, end - start, &nscanned);
+       if (gret) {
+               PARA_ERROR_LOG("gcry_mpi_scan: %s\n",
+                       gcry_strerror(gcry_err_code(gret)));
+               return -E_MPI_SCAN;
+       }
+       bits = (nscanned - 4 - (start[4] == '\0')) * 8;
+       if (bitsp)
+               *bitsp = bits;
+       PARA_DEBUG_LOG("scanned %u-bit bignum\n", bits);
+       return nscanned;
+}
 
-       ret = read_bignum(cp, end, &n, &n_size);
+static int read_openssh_rsa_params(unsigned char *start, unsigned char *end,
+               struct rsa_params *p)
+{
+       unsigned char *cp = start;
+       unsigned bits;
+       int ret;
+
+       ret = read_openssh_bignum(cp, end, &p->n, &bits);
        if (ret < 0)
-               goto free_blob;
+               return ret;
        cp += ret;
-
-       ret = read_bignum(cp, end, &e, NULL);
+       ret = read_openssh_bignum(cp, end, &p->e, NULL);
        if (ret < 0)
                goto release_n;
        cp += ret;
-
-       ret = read_bignum(cp, end, &d, NULL);
+       ret = read_openssh_bignum(cp, end, &p->d, NULL);
        if (ret < 0)
                goto release_e;
        cp += ret;
-
-       ret = read_bignum(cp, end, &p, NULL);
+       ret = read_openssh_bignum(cp, end, &p->u, NULL);
        if (ret < 0)
                goto release_d;
        cp += ret;
-
-       ret = read_bignum(cp, end, &q, NULL);
+       ret = read_openssh_bignum(cp, end, &p->p, NULL);
        if (ret < 0)
-               goto release_p;
+               goto release_u;
        cp += ret;
-       ret = read_bignum(cp, end, &u, NULL);
+       ret = read_openssh_bignum(cp, end, &p->q, NULL);
        if (ret < 0)
-               goto release_q;
+               goto release_p;
+       return bits;
+release_p:
+       gcry_mpi_release(p->p);
+release_u:
+       gcry_mpi_release(p->u);
+release_d:
+       gcry_mpi_release(p->d);
+release_e:
+       gcry_mpi_release(p->e);
+release_n:
+       gcry_mpi_release(p->n);
+       return ret;
+}
+
+static int get_private_key(const char *key_file, struct asymmetric_key **result)
+{
+       struct rsa_params params;
+       unsigned char *blob, *end;
+       unsigned bits;
+       int ret, key_type;
+       gcry_error_t gret;
+       size_t erroff, blob_size;
+       gcry_sexp_t sexp;
+       struct asymmetric_key *key;
+
+       *result = NULL;
+       ret = decode_private_key(key_file, &blob, &blob_size);
+       if (ret < 0)
+               return ret;
+       key_type = ret;
+       end = blob + blob_size;
+       if (key_type == PKT_PEM)
+               ret = find_pem_bignum_offset(blob, blob_size);
+       else
+               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);
+       if (key_type == PKT_PEM)
+               ret = read_pem_rsa_params(blob + ret, end, &params);
+       else
+               ret = read_openssh_rsa_params(blob + ret, end, &params);
+       if (ret < 0)
+               goto free_blob;
+       bits = ret;
        /*
         * OpenSSL uses slightly different parameters than gcrypt. To use these
         * parameters we need to swap the values of p and q and recompute u.
         */
-       if (gcry_mpi_cmp(pq) > 0) {
-               gcry_mpi_swap(pq);
-               gcry_mpi_invm(u, p, q);
+       if (gcry_mpi_cmp(params.p, params.q) > 0) {
+               gcry_mpi_swap(params.p, params.q);
+               gcry_mpi_invm(params.u, params.p, params.q);
        }
-       gret = gcry_sexp_build(&sexp, &erroff, RSA_PRIVKEY_SEXP,
-               n, e, d, p, q, u);
+       gret = gcry_sexp_build(&sexp, &erroff, RSA_PRIVKEY_SEXP, params.n,
+               params.e, params.d, params.p, params.q, params.u);
 
        if (gret) {
                PARA_ERROR_LOG("offset %zu: %s\n", erroff,
                        gcry_strerror(gcry_err_code(gret)));
                ret = -E_SEXP_BUILD;
-               goto release_u;
+               goto free_params;
        }
        key = para_malloc(sizeof(*key));
        key->sexp = sexp;
        *result = key;
-       ret = n_size * 8;
+       ret = bits;
        PARA_INFO_LOG("succesfully read %d bit private key\n", ret);
-release_u:
-       gcry_mpi_release(u);
-release_q:
-       gcry_mpi_release(q);
-release_p:
-       gcry_mpi_release(p);
-release_d:
-       gcry_mpi_release(d);
-release_e:
-       gcry_mpi_release(e);
-release_n:
-       gcry_mpi_release(n);
+free_params:
+       gcry_mpi_release(params.n);
+       gcry_mpi_release(params.e);
+       gcry_mpi_release(params.d);
+       gcry_mpi_release(params.u);
+       gcry_mpi_release(params.p);
+       gcry_mpi_release(params.q);
+
 free_blob:
        free(blob);
        return ret;
@@ -390,34 +413,25 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
        unsigned char *blob, *p, *end;
        int ret;
        gcry_error_t gret;
-       size_t nr_scanned, erroff, decoded_size;
+       size_t erroff, decoded_size;
        gcry_mpi_t e, n;
        gcry_sexp_t sexp;
        struct asymmetric_key *key;
+       unsigned bits;
 
-       ret = decode_ssh_key(key_file, &blob, &decoded_size);
+       ret = decode_public_key(key_file, &blob, &decoded_size);
        if (ret < 0)
                return ret;
        p = blob + ret;
        end = blob + decoded_size;
        PARA_DEBUG_LOG("scanning modulus and public exponent\n");
-       gret = gcry_mpi_scan(&e, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned);
-       if (gret) {
-               ret = -E_MPI_SCAN;
-               PARA_CRIT_LOG("%s\n", gcry_strerror(gcry_err_code(gret)));
+       ret = read_openssh_bignum(p, end, &e, NULL);
+       if (ret < 0)
                goto free_blob;
-       }
-       PARA_DEBUG_LOG("scanned e (%zu bytes)\n", nr_scanned);
-       p += nr_scanned;
-       if (p >= end)
-               goto release_e;
-       gret = gcry_mpi_scan(&n, GCRYMPI_FMT_SSH, p, end - p, &nr_scanned);
-       if (gret) {
-               ret = -E_MPI_SCAN;
-               PARA_ERROR_LOG("%s\n", gcry_strerror(gcry_err_code(gret)));
+       p += ret;
+       ret = read_openssh_bignum(p, end, &n, &bits);
+       if (ret < 0)
                goto release_e;
-       }
-       PARA_DEBUG_LOG("scanned n (%zu bytes)\n", nr_scanned);
        gret = gcry_sexp_build(&sexp, &erroff, RSA_PUBKEY_SEXP, n, e);
        if (gret) {
                PARA_ERROR_LOG("offset %zu: %s\n", erroff,
@@ -425,12 +439,12 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
                ret = -E_SEXP_BUILD;
                goto release_n;
        }
-       ret = ROUND_DOWN(nr_scanned, 32);
-       PARA_INFO_LOG("successfully read %d bit ssh public key\n", ret * 8);
+       PARA_INFO_LOG("successfully read %u bit ssh public key\n", bits);
        key = para_malloc(sizeof(*key));
        key->num_bytes = ret;
        key->sexp = sexp;
        *result = key;
+       ret = bits;
 release_n:
        gcry_mpi_release(n);
 release_e:
index d11b1049d2a321905ab79ce2af6c1e86fe7b7991..5f04c845c6ec1e8b5d837f827233df425033fad4 100644 (file)
--- a/openssl.c
+++ b/openssl.c
@@ -62,26 +62,6 @@ void crypt_shutdown(void)
 #endif
 }
 
-static int get_private_key(const char *path, RSA **rsa)
-{
-       EVP_PKEY *pkey;
-       BIO *bio = BIO_new(BIO_s_file());
-
-       *rsa = NULL;
-       if (!bio)
-               return -E_PRIVATE_KEY;
-       if (BIO_read_filename(bio, path) <= 0)
-               goto bio_free;
-       pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
-       if (!pkey)
-               goto bio_free;
-       *rsa = EVP_PKEY_get1_RSA(pkey);
-       EVP_PKEY_free(pkey);
-bio_free:
-       BIO_free(bio);
-       return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY;
-}
-
 /*
  * The public key loading functions below were inspired by corresponding code
  * of openssh-5.2p1, Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo,
@@ -146,6 +126,152 @@ free_rsa:
        return ret;
 }
 
+static int read_pem_private_key(const char *path, RSA **rsa)
+{
+       EVP_PKEY *pkey;
+       BIO *bio = BIO_new(BIO_s_file());
+
+       *rsa = NULL;
+       if (!bio)
+               return -E_PRIVATE_KEY;
+       if (BIO_read_filename(bio, path) <= 0)
+               goto bio_free;
+       pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
+       if (!pkey)
+               goto bio_free;
+       *rsa = EVP_PKEY_get1_RSA(pkey);
+       EVP_PKEY_free(pkey);
+bio_free:
+       BIO_free(bio);
+       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;
@@ -153,7 +279,7 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
        int ret;
        struct asymmetric_key *key = para_malloc(sizeof(*key));
 
-       ret = decode_ssh_key(key_file, &blob, &decoded_size);
+       ret = decode_public_key(key_file, &blob, &decoded_size);
        if (ret < 0)
                goto out;
        ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa);
index 2c806614d1e4ea001f990949d817dd5929e39a00..44799e910fc8c041667599fcff8669a1a89608c1 100644 (file)
@@ -446,7 +446,7 @@ following commands:
 Next, change to the "bar" account on client_host and generate the
 key pair with the commands
 
-       ssh-keygen -q -t rsa -b 2048 -N '' -m PEM
+       ssh-keygen -q -t rsa -b 2048 -N '' -m RFC4716
 
 This generates the two files id_rsa and id_rsa.pub in ~/.ssh.  Note
 that para_server won't accept keys shorter than 2048 bits. Moreover,