From: Andre Noll Date: Sat, 23 Nov 2019 17:20:21 +0000 (+0100) Subject: Merge branch 'refs/heads/t/ssh' X-Git-Tag: v0.6.3~33 X-Git-Url: http://git.tuebingen.mpg.de/?a=commitdiff_plain;h=0d1918752a18755bf701b82cf57fad79d9b18bc9;hp=d6b25bf854c164021550dfa8ff9fb4cfb104582e;p=paraslash.git Merge branch 'refs/heads/t/ssh' 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(). --- diff --git a/NEWS.md b/NEWS.md index eda654a6..fd587fab 100644 --- 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" diff --git a/crypt_backend.h b/crypt_backend.h index 175a6881..b0998d85 100644 --- a/crypt_backend.h +++ b/crypt_backend.h @@ -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); diff --git a/crypt_common.c b/crypt_common.c index 235b8b8d..c1e40d92 100644 --- a/crypt_common.c +++ b/crypt_common.c @@ -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 14e5b072..8ef9e37d 100644 --- a/error.h +++ b/error.h @@ -167,6 +167,7 @@ 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"), \ diff --git a/gcrypt.c b/gcrypt.c index 694c0adb..dbe49008 100644 --- 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, ¶ms); + else + ret = read_openssh_rsa_params(blob + ret, end, ¶ms); + 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(p, q) > 0) { - gcry_mpi_swap(p, q); - 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: diff --git a/openssl.c b/openssl.c index d11b1049..5f04c845 100644 --- 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 , 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); diff --git a/web/manual.md b/web/manual.md index 2c806614..44799e91 100644 --- a/web/manual.md +++ b/web/manual.md @@ -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,