version 1.101, 2015/12/10 17:08:40 |
version 1.102, 2016/08/03 05:41:57 |
|
|
extern int ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); |
extern int ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); |
#endif |
#endif |
|
|
|
struct sshcipher_ctx { |
|
int plaintext; |
|
int encrypt; |
|
EVP_CIPHER_CTX *evp; |
|
struct chachapoly_ctx cp_ctx; /* XXX union with evp? */ |
|
struct aesctr_ctx ac_ctx; /* XXX union with evp? */ |
|
const struct sshcipher *cipher; |
|
}; |
|
|
struct sshcipher { |
struct sshcipher { |
char *name; |
char *name; |
int number; /* for ssh1 only */ |
int number; /* for ssh1 only */ |
|
|
} |
} |
|
|
u_int |
u_int |
|
cipher_ctx_is_plaintext(struct sshcipher_ctx *cc) |
|
{ |
|
return cc->plaintext; |
|
} |
|
|
|
u_int |
|
cipher_ctx_get_number(struct sshcipher_ctx *cc) |
|
{ |
|
return cc->cipher->number; |
|
} |
|
|
|
u_int |
cipher_mask_ssh1(int client) |
cipher_mask_ssh1(int client) |
{ |
{ |
u_int mask = 0; |
u_int mask = 0; |
|
|
} |
} |
|
|
int |
int |
cipher_init(struct sshcipher_ctx *cc, const struct sshcipher *cipher, |
cipher_init(struct sshcipher_ctx **ccp, const struct sshcipher *cipher, |
const u_char *key, u_int keylen, const u_char *iv, u_int ivlen, |
const u_char *key, u_int keylen, const u_char *iv, u_int ivlen, |
int do_encrypt) |
int do_encrypt) |
{ |
{ |
#ifdef WITH_OPENSSL |
struct sshcipher_ctx *cc = NULL; |
int ret = SSH_ERR_INTERNAL_ERROR; |
int ret = SSH_ERR_INTERNAL_ERROR; |
|
#ifdef WITH_OPENSSL |
const EVP_CIPHER *type; |
const EVP_CIPHER *type; |
int klen; |
int klen; |
u_char *junk, *discard; |
u_char *junk, *discard; |
|
#endif |
|
|
|
*ccp = NULL; |
|
if ((cc = calloc(sizeof(*cc), 1)) == NULL) |
|
return SSH_ERR_ALLOC_FAIL; |
|
|
if (cipher->number == SSH_CIPHER_DES) { |
if (cipher->number == SSH_CIPHER_DES) { |
if (keylen > 8) |
if (keylen > 8) |
keylen = 8; |
keylen = 8; |
} |
} |
#endif |
|
cc->plaintext = (cipher->number == SSH_CIPHER_NONE); |
cc->plaintext = (cipher->number == SSH_CIPHER_NONE); |
cc->encrypt = do_encrypt; |
cc->encrypt = do_encrypt; |
|
|
if (keylen < cipher->key_len || |
if (keylen < cipher->key_len || |
(iv != NULL && ivlen < cipher_ivlen(cipher))) |
(iv != NULL && ivlen < cipher_ivlen(cipher))) { |
return SSH_ERR_INVALID_ARGUMENT; |
ret = SSH_ERR_INVALID_ARGUMENT; |
|
goto out; |
|
} |
|
|
cc->cipher = cipher; |
cc->cipher = cipher; |
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) { |
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) { |
return chachapoly_init(&cc->cp_ctx, key, keylen); |
ret = chachapoly_init(&cc->cp_ctx, key, keylen); |
|
goto out; |
} |
} |
#ifndef WITH_OPENSSL |
#ifndef WITH_OPENSSL |
if ((cc->cipher->flags & CFLAG_AESCTR) != 0) { |
if ((cc->cipher->flags & CFLAG_AESCTR) != 0) { |
aesctr_keysetup(&cc->ac_ctx, key, 8 * keylen, 8 * ivlen); |
aesctr_keysetup(&cc->ac_ctx, key, 8 * keylen, 8 * ivlen); |
aesctr_ivsetup(&cc->ac_ctx, iv); |
aesctr_ivsetup(&cc->ac_ctx, iv); |
return 0; |
ret = 0; |
|
goto out; |
} |
} |
if ((cc->cipher->flags & CFLAG_NONE) != 0) |
if ((cc->cipher->flags & CFLAG_NONE) != 0) { |
return 0; |
ret = 0; |
return SSH_ERR_INVALID_ARGUMENT; |
goto out; |
#else |
} |
|
ret = SSH_ERR_INVALID_ARGUMENT; |
|
goto out; |
|
#else /* WITH_OPENSSL */ |
type = (*cipher->evptype)(); |
type = (*cipher->evptype)(); |
EVP_CIPHER_CTX_init(&cc->evp); |
if ((cc->evp = EVP_CIPHER_CTX_new()) == NULL) { |
if (EVP_CipherInit(&cc->evp, type, NULL, (u_char *)iv, |
ret = SSH_ERR_ALLOC_FAIL; |
|
goto out; |
|
} |
|
if (EVP_CipherInit(cc->evp, type, NULL, (u_char *)iv, |
(do_encrypt == CIPHER_ENCRYPT)) == 0) { |
(do_encrypt == CIPHER_ENCRYPT)) == 0) { |
ret = SSH_ERR_LIBCRYPTO_ERROR; |
ret = SSH_ERR_LIBCRYPTO_ERROR; |
goto bad; |
goto out; |
} |
} |
if (cipher_authlen(cipher) && |
if (cipher_authlen(cipher) && |
!EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_SET_IV_FIXED, |
!EVP_CIPHER_CTX_ctrl(cc->evp, EVP_CTRL_GCM_SET_IV_FIXED, |
-1, (u_char *)iv)) { |
-1, (u_char *)iv)) { |
ret = SSH_ERR_LIBCRYPTO_ERROR; |
ret = SSH_ERR_LIBCRYPTO_ERROR; |
goto bad; |
goto out; |
} |
} |
klen = EVP_CIPHER_CTX_key_length(&cc->evp); |
klen = EVP_CIPHER_CTX_key_length(cc->evp); |
if (klen > 0 && keylen != (u_int)klen) { |
if (klen > 0 && keylen != (u_int)klen) { |
if (EVP_CIPHER_CTX_set_key_length(&cc->evp, keylen) == 0) { |
if (EVP_CIPHER_CTX_set_key_length(cc->evp, keylen) == 0) { |
ret = SSH_ERR_LIBCRYPTO_ERROR; |
ret = SSH_ERR_LIBCRYPTO_ERROR; |
goto bad; |
goto out; |
} |
} |
} |
} |
if (EVP_CipherInit(&cc->evp, NULL, (u_char *)key, NULL, -1) == 0) { |
if (EVP_CipherInit(cc->evp, NULL, (u_char *)key, NULL, -1) == 0) { |
ret = SSH_ERR_LIBCRYPTO_ERROR; |
ret = SSH_ERR_LIBCRYPTO_ERROR; |
goto bad; |
goto out; |
} |
} |
|
|
if (cipher->discard_len > 0) { |
if (cipher->discard_len > 0) { |
|
|
(discard = malloc(cipher->discard_len)) == NULL) { |
(discard = malloc(cipher->discard_len)) == NULL) { |
free(junk); |
free(junk); |
ret = SSH_ERR_ALLOC_FAIL; |
ret = SSH_ERR_ALLOC_FAIL; |
goto bad; |
goto out; |
} |
} |
ret = EVP_Cipher(&cc->evp, discard, junk, cipher->discard_len); |
ret = EVP_Cipher(cc->evp, discard, junk, cipher->discard_len); |
explicit_bzero(discard, cipher->discard_len); |
explicit_bzero(discard, cipher->discard_len); |
free(junk); |
free(junk); |
free(discard); |
free(discard); |
if (ret != 1) { |
if (ret != 1) { |
ret = SSH_ERR_LIBCRYPTO_ERROR; |
ret = SSH_ERR_LIBCRYPTO_ERROR; |
bad: |
goto out; |
EVP_CIPHER_CTX_cleanup(&cc->evp); |
|
return ret; |
|
} |
} |
} |
} |
#endif |
ret = 0; |
return 0; |
#endif /* WITH_OPENSSL */ |
|
out: |
|
if (ret == 0) { |
|
/* success */ |
|
*ccp = cc; |
|
} else { |
|
if (cc != NULL) { |
|
#ifdef WITH_OPENSSL |
|
if (cc->evp != NULL) |
|
EVP_CIPHER_CTX_free(cc->evp); |
|
#endif /* WITH_OPENSSL */ |
|
explicit_bzero(cc, sizeof(*cc)); |
|
free(cc); |
|
} |
|
} |
|
return ret; |
} |
} |
|
|
/* |
/* |
|
|
if (authlen != cipher_authlen(cc->cipher)) |
if (authlen != cipher_authlen(cc->cipher)) |
return SSH_ERR_INVALID_ARGUMENT; |
return SSH_ERR_INVALID_ARGUMENT; |
/* increment IV */ |
/* increment IV */ |
if (!EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_IV_GEN, |
if (!EVP_CIPHER_CTX_ctrl(cc->evp, EVP_CTRL_GCM_IV_GEN, |
1, lastiv)) |
1, lastiv)) |
return SSH_ERR_LIBCRYPTO_ERROR; |
return SSH_ERR_LIBCRYPTO_ERROR; |
/* set tag on decyption */ |
/* set tag on decyption */ |
if (!cc->encrypt && |
if (!cc->encrypt && |
!EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_SET_TAG, |
!EVP_CIPHER_CTX_ctrl(cc->evp, EVP_CTRL_GCM_SET_TAG, |
authlen, (u_char *)src + aadlen + len)) |
authlen, (u_char *)src + aadlen + len)) |
return SSH_ERR_LIBCRYPTO_ERROR; |
return SSH_ERR_LIBCRYPTO_ERROR; |
} |
} |
if (aadlen) { |
if (aadlen) { |
if (authlen && |
if (authlen && |
EVP_Cipher(&cc->evp, NULL, (u_char *)src, aadlen) < 0) |
EVP_Cipher(cc->evp, NULL, (u_char *)src, aadlen) < 0) |
return SSH_ERR_LIBCRYPTO_ERROR; |
return SSH_ERR_LIBCRYPTO_ERROR; |
memcpy(dest, src, aadlen); |
memcpy(dest, src, aadlen); |
} |
} |
if (len % cc->cipher->block_size) |
if (len % cc->cipher->block_size) |
return SSH_ERR_INVALID_ARGUMENT; |
return SSH_ERR_INVALID_ARGUMENT; |
if (EVP_Cipher(&cc->evp, dest + aadlen, (u_char *)src + aadlen, |
if (EVP_Cipher(cc->evp, dest + aadlen, (u_char *)src + aadlen, |
len) < 0) |
len) < 0) |
return SSH_ERR_LIBCRYPTO_ERROR; |
return SSH_ERR_LIBCRYPTO_ERROR; |
if (authlen) { |
if (authlen) { |
/* compute tag (on encrypt) or verify tag (on decrypt) */ |
/* compute tag (on encrypt) or verify tag (on decrypt) */ |
if (EVP_Cipher(&cc->evp, NULL, NULL, 0) < 0) |
if (EVP_Cipher(cc->evp, NULL, NULL, 0) < 0) |
return cc->encrypt ? |
return cc->encrypt ? |
SSH_ERR_LIBCRYPTO_ERROR : SSH_ERR_MAC_INVALID; |
SSH_ERR_LIBCRYPTO_ERROR : SSH_ERR_MAC_INVALID; |
if (cc->encrypt && |
if (cc->encrypt && |
!EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_GET_TAG, |
!EVP_CIPHER_CTX_ctrl(cc->evp, EVP_CTRL_GCM_GET_TAG, |
authlen, dest + aadlen + len)) |
authlen, dest + aadlen + len)) |
return SSH_ERR_LIBCRYPTO_ERROR; |
return SSH_ERR_LIBCRYPTO_ERROR; |
} |
} |
|
|
return 0; |
return 0; |
} |
} |
|
|
int |
void |
cipher_cleanup(struct sshcipher_ctx *cc) |
cipher_free(struct sshcipher_ctx *cc) |
{ |
{ |
if (cc == NULL || cc->cipher == NULL) |
if (cc == NULL) |
return 0; |
return; |
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) |
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) |
explicit_bzero(&cc->cp_ctx, sizeof(cc->cp_ctx)); |
explicit_bzero(&cc->cp_ctx, sizeof(cc->cp_ctx)); |
else if ((cc->cipher->flags & CFLAG_AESCTR) != 0) |
else if ((cc->cipher->flags & CFLAG_AESCTR) != 0) |
explicit_bzero(&cc->ac_ctx, sizeof(cc->ac_ctx)); |
explicit_bzero(&cc->ac_ctx, sizeof(cc->ac_ctx)); |
#ifdef WITH_OPENSSL |
#ifdef WITH_OPENSSL |
else if (EVP_CIPHER_CTX_cleanup(&cc->evp) == 0) |
if (cc->evp != NULL) { |
return SSH_ERR_LIBCRYPTO_ERROR; |
EVP_CIPHER_CTX_free(cc->evp); |
|
cc->evp = NULL; |
|
} |
#endif |
#endif |
return 0; |
explicit_bzero(cc, sizeof(*cc)); |
|
free(cc); |
} |
} |
|
|
/* |
/* |
|
|
* passphrase and using the resulting 16 bytes as the key. |
* passphrase and using the resulting 16 bytes as the key. |
*/ |
*/ |
int |
int |
cipher_set_key_string(struct sshcipher_ctx *cc, const struct sshcipher *cipher, |
cipher_set_key_string(struct sshcipher_ctx **ccp, |
const char *passphrase, int do_encrypt) |
const struct sshcipher *cipher, const char *passphrase, int do_encrypt) |
{ |
{ |
u_char digest[16]; |
u_char digest[16]; |
int r = SSH_ERR_INTERNAL_ERROR; |
int r = SSH_ERR_INTERNAL_ERROR; |
|
|
digest, sizeof(digest))) != 0) |
digest, sizeof(digest))) != 0) |
goto out; |
goto out; |
|
|
r = cipher_init(cc, cipher, digest, 16, NULL, 0, do_encrypt); |
r = cipher_init(ccp, cipher, digest, 16, NULL, 0, do_encrypt); |
out: |
out: |
explicit_bzero(digest, sizeof(digest)); |
explicit_bzero(digest, sizeof(digest)); |
return r; |
return r; |
|
|
ivlen = sizeof(cc->ac_ctx.ctr); |
ivlen = sizeof(cc->ac_ctx.ctr); |
#ifdef WITH_OPENSSL |
#ifdef WITH_OPENSSL |
else |
else |
ivlen = EVP_CIPHER_CTX_iv_length(&cc->evp); |
ivlen = EVP_CIPHER_CTX_iv_length(cc->evp); |
#endif /* WITH_OPENSSL */ |
#endif /* WITH_OPENSSL */ |
return (ivlen); |
return (ivlen); |
} |
} |
|
|
case SSH_CIPHER_SSH2: |
case SSH_CIPHER_SSH2: |
case SSH_CIPHER_DES: |
case SSH_CIPHER_DES: |
case SSH_CIPHER_BLOWFISH: |
case SSH_CIPHER_BLOWFISH: |
evplen = EVP_CIPHER_CTX_iv_length(&cc->evp); |
evplen = EVP_CIPHER_CTX_iv_length(cc->evp); |
if (evplen == 0) |
if (evplen == 0) |
return 0; |
return 0; |
else if (evplen < 0) |
else if (evplen < 0) |
|
|
if ((u_int)evplen != len) |
if ((u_int)evplen != len) |
return SSH_ERR_INVALID_ARGUMENT; |
return SSH_ERR_INVALID_ARGUMENT; |
if (cipher_authlen(c)) { |
if (cipher_authlen(c)) { |
if (!EVP_CIPHER_CTX_ctrl(&cc->evp, EVP_CTRL_GCM_IV_GEN, |
if (!EVP_CIPHER_CTX_ctrl(cc->evp, EVP_CTRL_GCM_IV_GEN, |
len, iv)) |
len, iv)) |
return SSH_ERR_LIBCRYPTO_ERROR; |
return SSH_ERR_LIBCRYPTO_ERROR; |
} else |
} else |
memcpy(iv, cc->evp.iv, len); |
memcpy(iv, cc->evp->iv, len); |
break; |
break; |
#endif |
#endif |
#ifdef WITH_SSH1 |
#ifdef WITH_SSH1 |
case SSH_CIPHER_3DES: |
case SSH_CIPHER_3DES: |
return ssh1_3des_iv(&cc->evp, 0, iv, 24); |
return ssh1_3des_iv(cc->evp, 0, iv, 24); |
#endif |
#endif |
default: |
default: |
return SSH_ERR_INVALID_ARGUMENT; |
return SSH_ERR_INVALID_ARGUMENT; |
|
|
case SSH_CIPHER_SSH2: |
case SSH_CIPHER_SSH2: |
case SSH_CIPHER_DES: |
case SSH_CIPHER_DES: |
case SSH_CIPHER_BLOWFISH: |
case SSH_CIPHER_BLOWFISH: |
evplen = EVP_CIPHER_CTX_iv_length(&cc->evp); |
evplen = EVP_CIPHER_CTX_iv_length(cc->evp); |
if (evplen <= 0) |
if (evplen <= 0) |
return SSH_ERR_LIBCRYPTO_ERROR; |
return SSH_ERR_LIBCRYPTO_ERROR; |
if (cipher_authlen(c)) { |
if (cipher_authlen(c)) { |
/* XXX iv arg is const, but EVP_CIPHER_CTX_ctrl isn't */ |
/* XXX iv arg is const, but EVP_CIPHER_CTX_ctrl isn't */ |
if (!EVP_CIPHER_CTX_ctrl(&cc->evp, |
if (!EVP_CIPHER_CTX_ctrl(cc->evp, |
EVP_CTRL_GCM_SET_IV_FIXED, -1, (void *)iv)) |
EVP_CTRL_GCM_SET_IV_FIXED, -1, (void *)iv)) |
return SSH_ERR_LIBCRYPTO_ERROR; |
return SSH_ERR_LIBCRYPTO_ERROR; |
} else |
} else |
memcpy(cc->evp.iv, iv, evplen); |
memcpy(cc->evp->iv, iv, evplen); |
break; |
break; |
#endif |
#endif |
#ifdef WITH_SSH1 |
#ifdef WITH_SSH1 |
case SSH_CIPHER_3DES: |
case SSH_CIPHER_3DES: |
return ssh1_3des_iv(&cc->evp, 1, (u_char *)iv, 24); |
return ssh1_3des_iv(cc->evp, 1, (u_char *)iv, 24); |
#endif |
#endif |
default: |
default: |
return SSH_ERR_INVALID_ARGUMENT; |
return SSH_ERR_INVALID_ARGUMENT; |
|
|
} |
} |
|
|
#ifdef WITH_OPENSSL |
#ifdef WITH_OPENSSL |
#define EVP_X_STATE(evp) (evp).cipher_data |
#define EVP_X_STATE(evp) (evp)->cipher_data |
#define EVP_X_STATE_LEN(evp) (evp).cipher->ctx_size |
#define EVP_X_STATE_LEN(evp) (evp)->cipher->ctx_size |
#endif |
#endif |
|
|
int |
int |