diff --git a/src/crypt.c b/src/crypt.c index 931ae8b8..853682dc 100644 --- a/src/crypt.c +++ b/src/crypt.c @@ -53,6 +53,7 @@ crypt_none_crypt(LIBSSH2_SESSION * session, unsigned char *buf, static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_none = { "none", + "DEK-Info: NONE" 8, /* blocksize (SSH2 defines minimum blocksize as 8) */ 0, /* iv_len */ 0, /* secret_len */ @@ -119,6 +120,7 @@ crypt_dtor(LIBSSH2_SESSION * session, void **abstract) #if LIBSSH2_AES_CTR static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_ctr = { "aes128-ctr", + "", 16, /* blocksize */ 16, /* initial value length */ 16, /* secret length -- 16*8 == 128bit */ @@ -131,6 +133,7 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_ctr = { static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_ctr = { "aes192-ctr", + "", 16, /* blocksize */ 16, /* initial value length */ 24, /* secret length -- 24*8 == 192bit */ @@ -143,6 +146,7 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_ctr = { static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_ctr = { "aes256-ctr", + "", 16, /* blocksize */ 16, /* initial value length */ 32, /* secret length -- 32*8 == 256bit */ @@ -157,6 +161,7 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_ctr = { #if LIBSSH2_AES static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_cbc = { "aes128-cbc", + "DEK-Info: AES-128-CBC", 16, /* blocksize */ 16, /* initial value length */ 16, /* secret length -- 16*8 == 128bit */ @@ -169,6 +174,7 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes128_cbc = { static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_cbc = { "aes192-cbc", + "DEK-Info: AES-192-CBC", 16, /* blocksize */ 16, /* initial value length */ 24, /* secret length -- 24*8 == 192bit */ @@ -181,6 +187,7 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes192_cbc = { static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_cbc = { "aes256-cbc", + "DEK-Info: AES-256-CBC", 16, /* blocksize */ 16, /* initial value length */ 32, /* secret length -- 32*8 == 256bit */ @@ -195,6 +202,7 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_aes256_cbc = { static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_rijndael_cbc_lysator_liu_se = { "rijndael-cbc@lysator.liu.se", + "DEK-Info: AES-256-CBC", 16, /* blocksize */ 16, /* initial value length */ 32, /* secret length -- 32*8 == 256bit */ @@ -209,6 +217,7 @@ static const LIBSSH2_CRYPT_METHOD #if LIBSSH2_BLOWFISH static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_blowfish_cbc = { "blowfish-cbc", + "", 8, /* blocksize */ 8, /* initial value length */ 16, /* secret length */ @@ -223,6 +232,7 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_blowfish_cbc = { #if LIBSSH2_RC4 static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour = { "arcfour", + "DEK-Info: RC4", 8, /* blocksize */ 8, /* initial value length */ 16, /* secret length */ @@ -258,6 +268,7 @@ crypt_init_arcfour128(LIBSSH2_SESSION * session, static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour128 = { "arcfour128", + "", 8, /* blocksize */ 8, /* initial value length */ 16, /* secret length */ @@ -272,6 +283,7 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_arcfour128 = { #if LIBSSH2_CAST static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_cast128_cbc = { "cast128-cbc", + "", 8, /* blocksize */ 8, /* initial value length */ 16, /* secret length */ @@ -286,6 +298,7 @@ static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_cast128_cbc = { #if LIBSSH2_3DES static const LIBSSH2_CRYPT_METHOD libssh2_crypt_method_3des_cbc = { "3des-cbc", + "DEK-Info: DES-EDE3-CBC", 8, /* blocksize */ 8, /* initial value length */ 24, /* secret length */ diff --git a/src/libgcrypt.c b/src/libgcrypt.c index 6ba13a14..5429566a 100644 --- a/src/libgcrypt.c +++ b/src/libgcrypt.c @@ -172,8 +172,6 @@ _libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, unsigned char *n, *e, *d, *p, *q, *e1, *e2, *coeff; unsigned int nlen, elen, dlen, plen, qlen, e1len, e2len, coefflen; - (void) passphrase; - fp = fopen(filename, "r"); if (!fp) { return -1; @@ -182,6 +180,7 @@ _libssh2_rsa_new_private(libssh2_rsa_ctx ** rsa, ret = _libssh2_pem_parse(session, "-----BEGIN RSA PRIVATE KEY-----", "-----END RSA PRIVATE KEY-----", + passphrase, fp, &data, &datalen); fclose(fp); if (ret) { @@ -285,8 +284,6 @@ _libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, unsigned char *p, *q, *g, *y, *x; unsigned int plen, qlen, glen, ylen, xlen; - (void) passphrase; - fp = fopen(filename, "r"); if (!fp) { return -1; @@ -295,6 +292,7 @@ _libssh2_dsa_new_private(libssh2_dsa_ctx ** dsa, ret = _libssh2_pem_parse(session, "-----BEGIN DSA PRIVATE KEY-----", "-----END DSA PRIVATE KEY-----", + passphrase, fp, &data, &datalen); fclose(fp); if (ret) { diff --git a/src/libssh2_priv.h b/src/libssh2_priv.h index 0dab7357..0114331c 100644 --- a/src/libssh2_priv.h +++ b/src/libssh2_priv.h @@ -884,6 +884,7 @@ struct _LIBSSH2_HOSTKEY_METHOD struct _LIBSSH2_CRYPT_METHOD { const char *name; + const char *pem_annotation; int blocksize; @@ -1041,6 +1042,7 @@ const LIBSSH2_HOSTKEY_METHOD **libssh2_hostkey_methods(void); int _libssh2_pem_parse(LIBSSH2_SESSION * session, const char *headerbegin, const char *headerend, + const unsigned char *passphrase, FILE * fp, unsigned char **data, unsigned int *datalen); int _libssh2_pem_parse_memory(LIBSSH2_SESSION * session, const char *headerbegin, diff --git a/src/os400qc3.c b/src/os400qc3.c index 74fe64b4..0bdf5388 100644 --- a/src/os400qc3.c +++ b/src/os400qc3.c @@ -2005,6 +2005,7 @@ try_pem_load(LIBSSH2_SESSION *session, FILE *fp, fseek(fp, 0L, SEEK_SET); for (;;) { ret = _libssh2_pem_parse(session, header, trailer, + NULL, fp, &data, &datalen); if (!ret) { diff --git a/src/pem.c b/src/pem.c index 9f51bba3..523d9c53 100644 --- a/src/pem.c +++ b/src/pem.c @@ -37,6 +37,7 @@ */ #include "libssh2_priv.h" +#include static int readline(char *line, int line_size, FILE * fp) @@ -96,16 +97,26 @@ readline_memory(char *line, size_t line_size, #define LINE_SIZE 128 +const char *crypt_annotation = "Proc-Type: 4,ENCRYPTED"; + +static unsigned char hex_decode(char digit) +{ + return (digit >= 'A') ? 0xA + (digit - 'A') : (digit - '0'); +} + int _libssh2_pem_parse(LIBSSH2_SESSION * session, const char *headerbegin, const char *headerend, + const unsigned char *passphrase, FILE * fp, unsigned char **data, unsigned int *datalen) { char line[LINE_SIZE]; + unsigned char iv[LINE_SIZE]; char *b64data = NULL; unsigned int b64datalen = 0; int ret; + const LIBSSH2_CRYPT_METHOD *method = NULL; do { *line = '\0'; @@ -116,7 +127,45 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session, } while (strcmp(line, headerbegin) != 0); - *line = '\0'; + if (readline(line, LINE_SIZE, fp)) { + return -1; + } + + if (passphrase && + memcmp(line, crypt_annotation, strlen(crypt_annotation)) == 0) { + const LIBSSH2_CRYPT_METHOD **all_methods, *cur_method; + int i; + + if (readline(line, LINE_SIZE, fp)) { + ret = -1; + goto out; + } + all_methods = libssh2_crypt_methods(); + while ((cur_method = *all_methods++)) { + if (*cur_method->pem_annotation && + memcmp(line, cur_method->pem_annotation, strlen(cur_method->pem_annotation)) == 0) { + method = cur_method; + memcpy(iv, line+strlen(method->pem_annotation)+1, 2*method->iv_len); + } + } + + /* None of the available crypt methods were able to decrypt this key */ + if (method == NULL) + return -1; + + /* Decode IV from hex */ + for (i = 0; i < method->iv_len; ++i) + { + iv[i] = hex_decode(iv[2*i]) << 4; + iv[i] |= hex_decode(iv[2*i+1]); + } + + /* skip to the next line */ + if (readline(line, LINE_SIZE, fp)) { + ret = -1; + goto out; + } + } do { if (*line) { @@ -152,6 +201,72 @@ _libssh2_pem_parse(LIBSSH2_SESSION * session, goto out; } + if (method) { + /* Set up decryption */ + int free_iv = 0, free_secret = 0, len_decrypted = 0, padding = 0; + int blocksize = method->blocksize; + void *abstract; + unsigned char secret[2*MD5_DIGEST_LENGTH]; + libssh2_md5_ctx fingerprint_ctx; + + /* Perform key derivation (PBKDF1/MD5) */ + if (!libssh2_md5_init(&fingerprint_ctx)) { + ret = -1; + goto out; + } + libssh2_md5_update(fingerprint_ctx, passphrase, strlen((char*)passphrase)); + libssh2_md5_update(fingerprint_ctx, iv, 8); + libssh2_md5_final(fingerprint_ctx, secret); + if (method->secret_len > MD5_DIGEST_LENGTH) { + if (!libssh2_md5_init(&fingerprint_ctx)) { + ret = -1; + goto out; + } + libssh2_md5_update(fingerprint_ctx, secret, MD5_DIGEST_LENGTH); + libssh2_md5_update(fingerprint_ctx, passphrase, strlen((char*)passphrase)); + libssh2_md5_update(fingerprint_ctx, iv, 8); + libssh2_md5_final(fingerprint_ctx, secret + MD5_DIGEST_LENGTH); + } + + /* Initialize the decryption */ + if (method->init(session, method, iv, &free_iv, secret, + &free_secret, 0, &abstract)) { + memset((char*)secret, 0, sizeof(secret)); + LIBSSH2_FREE(session, data); + ret = -1; + goto out; + } + + if (free_secret) { + memset((char*)secret, 0, sizeof(secret)); + } + + /* Do the actual decryption */ + assert((*datalen % blocksize) == 0); + + while (len_decrypted <= *datalen - blocksize) { + if (method->crypt(session, *data + len_decrypted, blocksize, + &abstract)) { + ret = LIBSSH2_ERROR_DECRYPT; + method->dtor(session, &abstract); + memset(*data, 0, *datalen); + LIBSSH2_FREE(session, *data); + goto out; + } + + len_decrypted += blocksize; + } + + /* Account for padding */ + padding = (*data)[*datalen - 1]; + memset(&(*data)[*datalen-padding],0,padding); + *datalen -= padding; + + /* Clean up */ + memset((char*)secret, 0, sizeof(secret)); + method->dtor(session, &abstract); + } + ret = 0; out: if (b64data) { diff --git a/src/wincng.c b/src/wincng.c index c4b658ce..e58e6ec9 100755 --- a/src/wincng.c +++ b/src/wincng.c @@ -549,14 +549,13 @@ _libssh2_wincng_load_pem(LIBSSH2_SESSION *session, FILE *fp; int ret; - (void)passphrase; - fp = fopen(filename, "r"); if (!fp) { return -1; } ret = _libssh2_pem_parse(session, headerbegin, headerend, + passphrase, fp, data, datalen); fclose(fp);