static int set_cipher_key(struct sftp_cipher *cipher, const EVP_MD *hash, const unsigned char *k, uint32_t klen, const char *h, uint32_t hlen, char *letter, const unsigned char *id, uint32_t id_len) { EVP_MD_CTX *ctx; unsigned char *key = NULL; size_t key_sz = 0; uint32_t key_len = 0; if (strncmp(cipher->algo, "none", 5) == 0) { cipher->key = key; cipher->key_len = key_len; return 0; } key_sz = sftp_crypto_get_size(cipher->key_len > 0 ? cipher->key_len : EVP_CIPHER_key_length(cipher->cipher), EVP_MD_size(hash)); if (key_sz == 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unable to determine key length for cipher '%s'", cipher->algo); errno = EINVAL; return -1; } key = malloc(key_sz); if (key == NULL) { pr_log_pri(PR_LOG_ALERT, MOD_SFTP_VERSION ": Out of memory!"); _exit(1); } ctx = EVP_MD_CTX_create(); EVP_DigestInit(ctx, hash); EVP_DigestUpdate(ctx, k, klen); EVP_DigestUpdate(ctx, h, hlen); EVP_DigestUpdate(ctx, letter, sizeof(char)); EVP_DigestUpdate(ctx, (char *) id, id_len); EVP_DigestFinal(ctx, key, &key_len); EVP_MD_CTX_destroy(ctx); /* If we need more, keep hashing, as per RFC, until we have enough * material. */ while (key_sz > key_len) { uint32_t len = key_len; pr_signals_handle(); ctx = EVP_MD_CTX_create(); EVP_DigestInit(ctx, hash); EVP_DigestUpdate(ctx, k, klen); EVP_DigestUpdate(ctx, h, hlen); EVP_DigestUpdate(ctx, key, len); EVP_DigestFinal(ctx, key + len, &len); EVP_MD_CTX_destroy(ctx); key_len += len; } cipher->key = key; cipher->key_len = key_len; return 0; }
static int set_mac_key(struct sftp_mac *mac, const EVP_MD *hash, const char *k, uint32_t klen, const char *h, uint32_t hlen, char *letter, const unsigned char *id, uint32_t id_len) { EVP_MD_CTX ctx; unsigned char *key = NULL; size_t key_sz; uint32_t key_len = 0; key_sz = sftp_crypto_get_size(EVP_MD_block_size(mac->digest), EVP_MD_size(hash)); if (key_sz == 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unable to determine key length for MAC '%s'", mac->algo); errno = EINVAL; return -1; } key = malloc(key_sz); if (key == NULL) { pr_log_pri(PR_LOG_CRIT, MOD_SFTP_VERSION ": Out of memory!"); _exit(1); } /* In OpenSSL 0.9.6, many of the EVP_Digest* functions returned void, not * int. Without these ugly OpenSSL version preprocessor checks, the * compiler will error out with "void value not ignored as it ought to be". */ #if OPENSSL_VERSION_NUMBER >= 0x000907000L if (EVP_DigestInit(&ctx, hash) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error initializing message digest: %s", sftp_crypto_get_errors()); free(key); return -1; } #else EVP_DigestInit(&ctx, hash); #endif #if OPENSSL_VERSION_NUMBER >= 0x000907000L if (EVP_DigestUpdate(&ctx, k, klen) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error updating message digest with K: %s", sftp_crypto_get_errors()); free(key); return -1; } #else EVP_DigestUpdate(&ctx, k, klen); #endif #if OPENSSL_VERSION_NUMBER >= 0x000907000L if (EVP_DigestUpdate(&ctx, h, hlen) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error updating message digest with H: %s", sftp_crypto_get_errors()); free(key); return -1; } #else EVP_DigestUpdate(&ctx, h, hlen); #endif #if OPENSSL_VERSION_NUMBER >= 0x000907000L if (EVP_DigestUpdate(&ctx, letter, sizeof(char)) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error updating message digest with '%c': %s", *letter, sftp_crypto_get_errors()); free(key); return -1; } #else EVP_DigestUpdate(&ctx, letter, sizeof(char)); #endif #if OPENSSL_VERSION_NUMBER >= 0x000907000L if (EVP_DigestUpdate(&ctx, (char *) id, id_len) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error updating message digest with ID: %s", sftp_crypto_get_errors()); free(key); return -1; } #else EVP_DigestUpdate(&ctx, (char *) id, id_len); #endif #if OPENSSL_VERSION_NUMBER >= 0x000907000L if (EVP_DigestFinal(&ctx, key, &key_len) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error finalizing message digest: %s", sftp_crypto_get_errors()); pr_memscrub(key, key_sz); free(key); return -1; } #else EVP_DigestFinal(&ctx, key, &key_len); #endif /* If we need more, keep hashing, as per RFC, until we have enough * material. */ while (key_sz > key_len) { uint32_t len = key_len; pr_signals_handle(); #if OPENSSL_VERSION_NUMBER >= 0x000907000L if (EVP_DigestInit(&ctx, hash) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error initializing message digest: %s", sftp_crypto_get_errors()); pr_memscrub(key, key_sz); free(key); return -1; } #else EVP_DigestInit(&ctx, hash); #endif #if OPENSSL_VERSION_NUMBER >= 0x000907000L if (EVP_DigestUpdate(&ctx, k, klen) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error updating message digest with K: %s", sftp_crypto_get_errors()); pr_memscrub(key, key_sz); free(key); return -1; } #else EVP_DigestUpdate(&ctx, k, klen); #endif #if OPENSSL_VERSION_NUMBER >= 0x000907000L if (EVP_DigestUpdate(&ctx, h, hlen) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error updating message digest with H: %s", sftp_crypto_get_errors()); pr_memscrub(key, key_sz); free(key); return -1; } #else EVP_DigestUpdate(&ctx, h, hlen); #endif #if OPENSSL_VERSION_NUMBER >= 0x000907000L if (EVP_DigestUpdate(&ctx, key, len) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error updating message digest with data: %s", sftp_crypto_get_errors()); pr_memscrub(key, key_sz); free(key); return -1; } #else EVP_DigestUpdate(&ctx, key, len); #endif #if OPENSSL_VERSION_NUMBER >= 0x000907000L if (EVP_DigestFinal(&ctx, key + len, &len) != 1) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "error finalizing message digest: %s", sftp_crypto_get_errors()); pr_memscrub(key, key_sz); free(key); return -1; } #else EVP_DigestFinal(&ctx, key + len, &len); #endif key_len += len; } mac->key = key; mac->keysz = key_sz; mac->key_len = EVP_MD_size(mac->digest); if (!sftp_interop_supports_feature(SFTP_SSH2_FEAT_MAC_LEN)) { mac->key_len = 16; } return 0; }
static int set_cipher_iv(struct sftp_cipher *cipher, const EVP_MD *hash, const unsigned char *k, uint32_t klen, const char *h, uint32_t hlen, char *letter, const unsigned char *id, uint32_t id_len) { EVP_MD_CTX *ctx; unsigned char *iv = NULL; size_t cipher_iv_len = 0, iv_sz = 0; uint32_t iv_len = 0; if (strncmp(cipher->algo, "none", 5) == 0) { cipher->iv = iv; cipher->iv_len = iv_len; return 0; } /* Some ciphers do not use IVs; handle this case. */ cipher_iv_len = EVP_CIPHER_iv_length(cipher->cipher); if (cipher_iv_len != 0) { iv_sz = sftp_crypto_get_size(cipher_iv_len, EVP_MD_size(hash)); } else { iv_sz = EVP_MD_size(hash); } if (iv_sz == 0) { (void) pr_log_writefile(sftp_logfd, MOD_SFTP_VERSION, "unable to determine IV length for cipher '%s'", cipher->algo); errno = EINVAL; return -1; } iv = malloc(iv_sz); if (iv == NULL) { pr_log_pri(PR_LOG_ALERT, MOD_SFTP_VERSION ": Out of memory!"); _exit(1); } ctx = EVP_MD_CTX_create(); EVP_DigestInit(ctx, hash); if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_CIPHER_USE_K)) { EVP_DigestUpdate(ctx, k, klen); } EVP_DigestUpdate(ctx, h, hlen); EVP_DigestUpdate(ctx, letter, sizeof(char)); EVP_DigestUpdate(ctx, (char *) id, id_len); EVP_DigestFinal(ctx, iv, &iv_len); EVP_MD_CTX_destroy(ctx); /* If we need more, keep hashing, as per RFC, until we have enough * material. */ while (iv_sz > iv_len) { uint32_t len = iv_len; pr_signals_handle(); ctx = EVP_MD_CTX_create(); EVP_DigestInit(ctx, hash); if (sftp_interop_supports_feature(SFTP_SSH2_FEAT_CIPHER_USE_K)) { EVP_DigestUpdate(ctx, k, klen); } EVP_DigestUpdate(ctx, h, hlen); EVP_DigestUpdate(ctx, iv, len); EVP_DigestFinal(ctx, iv + len, &len); EVP_MD_CTX_destroy(ctx); iv_len += len; } cipher->iv = iv; cipher->iv_len = iv_len; return 0; }