forked from openssh/openssh-portable
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
small refactor of cipher.c: make ciphercontext opaque to callers feedback and ok markus@ Upstream-ID: 094849f8be68c3bdad2c0f3dee551ecf7be87f6f
- Loading branch information
Showing
5 changed files
with
179 additions
and
139 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
/* $OpenBSD: cipher.c,v 1.101 2015/12/10 17:08:40 mmcc Exp $ */ | ||
/* $OpenBSD: cipher.c,v 1.102 2016/08/03 05:41:57 djm Exp $ */ | ||
/* | ||
* Author: Tatu Ylonen <[email protected]> | ||
* Copyright (c) 1995 Tatu Ylonen <[email protected]>, Espoo, Finland | ||
|
@@ -57,6 +57,15 @@ extern const EVP_CIPHER *evp_ssh1_3des(void); | |
extern int ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); | ||
#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 { | ||
char *name; | ||
int number; /* for ssh1 only */ | ||
|
@@ -205,6 +214,18 @@ cipher_is_cbc(const struct sshcipher *c) | |
return (c->flags & CFLAG_CBC) != 0; | ||
} | ||
|
||
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) | ||
{ | ||
|
@@ -297,87 +318,116 @@ cipher_warning_message(const struct sshcipher_ctx *cc) | |
} | ||
|
||
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, | ||
int do_encrypt) | ||
{ | ||
#ifdef WITH_OPENSSL | ||
struct sshcipher_ctx *cc = NULL; | ||
int ret = SSH_ERR_INTERNAL_ERROR; | ||
#ifdef WITH_OPENSSL | ||
const EVP_CIPHER *type; | ||
int klen; | ||
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 (keylen > 8) | ||
keylen = 8; | ||
} | ||
#endif | ||
|
||
cc->plaintext = (cipher->number == SSH_CIPHER_NONE); | ||
cc->encrypt = do_encrypt; | ||
|
||
if (keylen < cipher->key_len || | ||
(iv != NULL && ivlen < cipher_ivlen(cipher))) | ||
return SSH_ERR_INVALID_ARGUMENT; | ||
(iv != NULL && ivlen < cipher_ivlen(cipher))) { | ||
ret = SSH_ERR_INVALID_ARGUMENT; | ||
goto out; | ||
} | ||
|
||
cc->cipher = cipher; | ||
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 | ||
if ((cc->cipher->flags & CFLAG_AESCTR) != 0) { | ||
aesctr_keysetup(&cc->ac_ctx, key, 8 * keylen, 8 * ivlen); | ||
aesctr_ivsetup(&cc->ac_ctx, iv); | ||
return 0; | ||
ret = 0; | ||
goto out; | ||
} | ||
if ((cc->cipher->flags & CFLAG_NONE) != 0) | ||
return 0; | ||
return SSH_ERR_INVALID_ARGUMENT; | ||
#else | ||
if ((cc->cipher->flags & CFLAG_NONE) != 0) { | ||
ret = 0; | ||
goto out; | ||
} | ||
ret = SSH_ERR_INVALID_ARGUMENT; | ||
goto out; | ||
#else /* WITH_OPENSSL */ | ||
type = (*cipher->evptype)(); | ||
EVP_CIPHER_CTX_init(&cc->evp); | ||
if (EVP_CipherInit(&cc->evp, type, NULL, (u_char *)iv, | ||
if ((cc->evp = EVP_CIPHER_CTX_new()) == NULL) { | ||
ret = SSH_ERR_ALLOC_FAIL; | ||
goto out; | ||
} | ||
if (EVP_CipherInit(cc->evp, type, NULL, (u_char *)iv, | ||
(do_encrypt == CIPHER_ENCRYPT)) == 0) { | ||
ret = SSH_ERR_LIBCRYPTO_ERROR; | ||
goto bad; | ||
goto out; | ||
} | ||
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)) { | ||
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 (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; | ||
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; | ||
goto bad; | ||
goto out; | ||
} | ||
|
||
if (cipher->discard_len > 0) { | ||
if ((junk = malloc(cipher->discard_len)) == NULL || | ||
(discard = malloc(cipher->discard_len)) == NULL) { | ||
free(junk); | ||
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); | ||
free(junk); | ||
free(discard); | ||
if (ret != 1) { | ||
ret = SSH_ERR_LIBCRYPTO_ERROR; | ||
bad: | ||
EVP_CIPHER_CTX_cleanup(&cc->evp); | ||
return ret; | ||
goto out; | ||
} | ||
} | ||
#endif | ||
return 0; | ||
ret = 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; | ||
} | ||
|
||
/* | ||
|
@@ -418,33 +468,33 @@ cipher_crypt(struct sshcipher_ctx *cc, u_int seqnr, u_char *dest, | |
if (authlen != cipher_authlen(cc->cipher)) | ||
return SSH_ERR_INVALID_ARGUMENT; | ||
/* 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)) | ||
return SSH_ERR_LIBCRYPTO_ERROR; | ||
/* set tag on decyption */ | ||
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)) | ||
return SSH_ERR_LIBCRYPTO_ERROR; | ||
} | ||
if (aadlen) { | ||
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; | ||
memcpy(dest, src, aadlen); | ||
} | ||
if (len % cc->cipher->block_size) | ||
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) | ||
return SSH_ERR_LIBCRYPTO_ERROR; | ||
if (authlen) { | ||
/* 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 ? | ||
SSH_ERR_LIBCRYPTO_ERROR : SSH_ERR_MAC_INVALID; | ||
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)) | ||
return SSH_ERR_LIBCRYPTO_ERROR; | ||
} | ||
|
@@ -466,29 +516,32 @@ cipher_get_length(struct sshcipher_ctx *cc, u_int *plenp, u_int seqnr, | |
return 0; | ||
} | ||
|
||
int | ||
cipher_cleanup(struct sshcipher_ctx *cc) | ||
void | ||
cipher_free(struct sshcipher_ctx *cc) | ||
{ | ||
if (cc == NULL || cc->cipher == NULL) | ||
return 0; | ||
if (cc == NULL) | ||
return; | ||
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) | ||
explicit_bzero(&cc->cp_ctx, sizeof(cc->cp_ctx)); | ||
else if ((cc->cipher->flags & CFLAG_AESCTR) != 0) | ||
explicit_bzero(&cc->ac_ctx, sizeof(cc->ac_ctx)); | ||
#ifdef WITH_OPENSSL | ||
else if (EVP_CIPHER_CTX_cleanup(&cc->evp) == 0) | ||
return SSH_ERR_LIBCRYPTO_ERROR; | ||
if (cc->evp != NULL) { | ||
EVP_CIPHER_CTX_free(cc->evp); | ||
cc->evp = NULL; | ||
} | ||
#endif | ||
return 0; | ||
explicit_bzero(cc, sizeof(*cc)); | ||
free(cc); | ||
} | ||
|
||
/* | ||
* Selects the cipher, and keys if by computing the MD5 checksum of the | ||
* passphrase and using the resulting 16 bytes as the key. | ||
*/ | ||
int | ||
cipher_set_key_string(struct sshcipher_ctx *cc, const struct sshcipher *cipher, | ||
const char *passphrase, int do_encrypt) | ||
cipher_set_key_string(struct sshcipher_ctx **ccp, | ||
const struct sshcipher *cipher, const char *passphrase, int do_encrypt) | ||
{ | ||
u_char digest[16]; | ||
int r = SSH_ERR_INTERNAL_ERROR; | ||
|
@@ -498,7 +551,7 @@ cipher_set_key_string(struct sshcipher_ctx *cc, const struct sshcipher *cipher, | |
digest, sizeof(digest))) != 0) | ||
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: | ||
explicit_bzero(digest, sizeof(digest)); | ||
return r; | ||
|
@@ -523,7 +576,7 @@ cipher_get_keyiv_len(const struct sshcipher_ctx *cc) | |
ivlen = sizeof(cc->ac_ctx.ctr); | ||
#ifdef WITH_OPENSSL | ||
else | ||
ivlen = EVP_CIPHER_CTX_iv_length(&cc->evp); | ||
ivlen = EVP_CIPHER_CTX_iv_length(cc->evp); | ||
#endif /* WITH_OPENSSL */ | ||
return (ivlen); | ||
} | ||
|
@@ -555,7 +608,7 @@ cipher_get_keyiv(struct sshcipher_ctx *cc, u_char *iv, u_int len) | |
case SSH_CIPHER_SSH2: | ||
case SSH_CIPHER_DES: | ||
case SSH_CIPHER_BLOWFISH: | ||
evplen = EVP_CIPHER_CTX_iv_length(&cc->evp); | ||
evplen = EVP_CIPHER_CTX_iv_length(cc->evp); | ||
if (evplen == 0) | ||
return 0; | ||
else if (evplen < 0) | ||
|
@@ -568,16 +621,16 @@ cipher_get_keyiv(struct sshcipher_ctx *cc, u_char *iv, u_int len) | |
else | ||
#endif | ||
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)) | ||
return SSH_ERR_LIBCRYPTO_ERROR; | ||
} else | ||
memcpy(iv, cc->evp.iv, len); | ||
memcpy(iv, cc->evp->iv, len); | ||
break; | ||
#endif | ||
#ifdef WITH_SSH1 | ||
case SSH_CIPHER_3DES: | ||
return ssh1_3des_iv(&cc->evp, 0, iv, 24); | ||
return ssh1_3des_iv(cc->evp, 0, iv, 24); | ||
#endif | ||
default: | ||
return SSH_ERR_INVALID_ARGUMENT; | ||
|
@@ -603,21 +656,21 @@ cipher_set_keyiv(struct sshcipher_ctx *cc, const u_char *iv) | |
case SSH_CIPHER_SSH2: | ||
case SSH_CIPHER_DES: | ||
case SSH_CIPHER_BLOWFISH: | ||
evplen = EVP_CIPHER_CTX_iv_length(&cc->evp); | ||
evplen = EVP_CIPHER_CTX_iv_length(cc->evp); | ||
if (evplen <= 0) | ||
return SSH_ERR_LIBCRYPTO_ERROR; | ||
if (cipher_authlen(c)) { | ||
/* 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)) | ||
return SSH_ERR_LIBCRYPTO_ERROR; | ||
} else | ||
memcpy(cc->evp.iv, iv, evplen); | ||
memcpy(cc->evp->iv, iv, evplen); | ||
break; | ||
#endif | ||
#ifdef WITH_SSH1 | ||
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 | ||
default: | ||
return SSH_ERR_INVALID_ARGUMENT; | ||
|
@@ -626,8 +679,8 @@ cipher_set_keyiv(struct sshcipher_ctx *cc, const u_char *iv) | |
} | ||
|
||
#ifdef WITH_OPENSSL | ||
#define EVP_X_STATE(evp) (evp).cipher_data | ||
#define EVP_X_STATE_LEN(evp) (evp).cipher->ctx_size | ||
#define EVP_X_STATE(evp) (evp)->cipher_data | ||
#define EVP_X_STATE_LEN(evp) (evp)->cipher->ctx_size | ||
#endif | ||
|
||
int | ||
|
Oops, something went wrong.