version 1.97, 2014/02/07 06:55:54 |
version 1.98, 2014/04/29 18:01:49 |
|
|
#include "buffer.h" |
#include "buffer.h" |
#include "digest.h" |
#include "digest.h" |
|
|
|
#ifdef WITH_SSH1 |
extern const EVP_CIPHER *evp_ssh1_bf(void); |
extern const EVP_CIPHER *evp_ssh1_bf(void); |
extern const EVP_CIPHER *evp_ssh1_3des(void); |
extern const EVP_CIPHER *evp_ssh1_3des(void); |
extern void ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); |
extern void ssh1_3des_iv(EVP_CIPHER_CTX *, int, u_char *, int); |
|
#endif |
|
|
struct Cipher { |
struct Cipher { |
char *name; |
char *name; |
|
|
u_int flags; |
u_int flags; |
#define CFLAG_CBC (1<<0) |
#define CFLAG_CBC (1<<0) |
#define CFLAG_CHACHAPOLY (1<<1) |
#define CFLAG_CHACHAPOLY (1<<1) |
|
#define CFLAG_AESCTR (1<<2) |
|
#define CFLAG_NONE (1<<3) |
|
#ifdef WITH_OPENSSL |
const EVP_CIPHER *(*evptype)(void); |
const EVP_CIPHER *(*evptype)(void); |
|
#else |
|
void *ignored; |
|
#endif |
}; |
}; |
|
|
static const struct Cipher ciphers[] = { |
static const struct Cipher ciphers[] = { |
{ "none", SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null }, |
#ifdef WITH_SSH1 |
{ "des", SSH_CIPHER_DES, 8, 8, 0, 0, 0, 1, EVP_des_cbc }, |
{ "des", SSH_CIPHER_DES, 8, 8, 0, 0, 0, 1, EVP_des_cbc }, |
{ "3des", SSH_CIPHER_3DES, 8, 16, 0, 0, 0, 1, evp_ssh1_3des }, |
{ "3des", SSH_CIPHER_3DES, 8, 16, 0, 0, 0, 1, evp_ssh1_3des }, |
{ "blowfish", SSH_CIPHER_BLOWFISH, 8, 32, 0, 0, 0, 1, evp_ssh1_bf }, |
{ "blowfish", SSH_CIPHER_BLOWFISH, 8, 32, 0, 0, 0, 1, evp_ssh1_bf }, |
|
#endif |
|
#ifdef WITH_OPENSSL |
|
{ "none", SSH_CIPHER_NONE, 8, 0, 0, 0, 0, 0, EVP_enc_null }, |
{ "3des-cbc", SSH_CIPHER_SSH2, 8, 24, 0, 0, 0, 1, EVP_des_ede3_cbc }, |
{ "3des-cbc", SSH_CIPHER_SSH2, 8, 24, 0, 0, 0, 1, EVP_des_ede3_cbc }, |
{ "blowfish-cbc", |
{ "blowfish-cbc", |
SSH_CIPHER_SSH2, 8, 16, 0, 0, 0, 1, EVP_bf_cbc }, |
SSH_CIPHER_SSH2, 8, 16, 0, 0, 0, 1, EVP_bf_cbc }, |
|
|
SSH_CIPHER_SSH2, 16, 16, 12, 16, 0, 0, EVP_aes_128_gcm }, |
SSH_CIPHER_SSH2, 16, 16, 12, 16, 0, 0, EVP_aes_128_gcm }, |
{ "aes256-gcm@openssh.com", |
{ "aes256-gcm@openssh.com", |
SSH_CIPHER_SSH2, 16, 32, 12, 16, 0, 0, EVP_aes_256_gcm }, |
SSH_CIPHER_SSH2, 16, 32, 12, 16, 0, 0, EVP_aes_256_gcm }, |
|
#else |
|
{ "aes128-ctr", SSH_CIPHER_SSH2, 16, 16, 0, 0, 0, CFLAG_AESCTR, NULL }, |
|
{ "aes192-ctr", SSH_CIPHER_SSH2, 16, 24, 0, 0, 0, CFLAG_AESCTR, NULL }, |
|
{ "aes256-ctr", SSH_CIPHER_SSH2, 16, 32, 0, 0, 0, CFLAG_AESCTR, NULL }, |
|
{ "none", SSH_CIPHER_NONE, 8, 0, 0, 0, 0, CFLAG_NONE, NULL }, |
|
#endif |
{ "chacha20-poly1305@openssh.com", |
{ "chacha20-poly1305@openssh.com", |
SSH_CIPHER_SSH2, 8, 64, 0, 16, 0, CFLAG_CHACHAPOLY, NULL }, |
SSH_CIPHER_SSH2, 8, 64, 0, 16, 0, CFLAG_CHACHAPOLY, NULL }, |
|
|
|
|
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 |
static int dowarn = 1; |
static int dowarn = 1; |
const EVP_CIPHER *type; |
const EVP_CIPHER *type; |
int klen; |
int klen; |
|
|
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; |
|
|
|
|
chachapoly_init(&cc->cp_ctx, key, keylen); |
chachapoly_init(&cc->cp_ctx, key, keylen); |
return; |
return; |
} |
} |
|
#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; |
|
} |
|
if ((cc->cipher->flags & CFLAG_NONE) != 0) |
|
return; |
|
fatal("unsupported cipher"); |
|
#else |
type = (*cipher->evptype)(); |
type = (*cipher->evptype)(); |
EVP_CIPHER_CTX_init(&cc->evp); |
EVP_CIPHER_CTX_init(&cc->evp); |
if (EVP_CipherInit(&cc->evp, type, NULL, (u_char *)iv, |
if (EVP_CipherInit(&cc->evp, type, NULL, (u_char *)iv, |
|
|
free(junk); |
free(junk); |
free(discard); |
free(discard); |
} |
} |
|
#endif |
} |
} |
|
|
/* |
/* |
|
|
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) |
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) |
return chachapoly_crypt(&cc->cp_ctx, seqnr, dest, src, len, |
return chachapoly_crypt(&cc->cp_ctx, seqnr, dest, src, len, |
aadlen, authlen, cc->encrypt); |
aadlen, authlen, cc->encrypt); |
|
#ifndef WITH_OPENSSL |
|
if ((cc->cipher->flags & CFLAG_AESCTR) != 0) { |
|
if (aadlen) |
|
memcpy(dest, src, aadlen); |
|
aesctr_encrypt_bytes(&cc->ac_ctx, src + aadlen, |
|
dest + aadlen, len); |
|
return 0; |
|
} |
|
if ((cc->cipher->flags & CFLAG_NONE) != 0) { |
|
memcpy(dest, src, aadlen + len); |
|
return 0; |
|
} |
|
fatal("unsupported cipher"); |
|
#else |
if (authlen) { |
if (authlen) { |
u_char lastiv[1]; |
u_char lastiv[1]; |
|
|
|
|
fatal("%s: EVP_CTRL_GCM_GET_TAG", __func__); |
fatal("%s: EVP_CTRL_GCM_GET_TAG", __func__); |
} |
} |
return 0; |
return 0; |
|
#endif |
} |
} |
|
|
/* Extract the packet length, including any decryption necessary beforehand */ |
/* Extract the packet length, including any decryption necessary beforehand */ |
|
|
{ |
{ |
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) |
|
explicit_bzero(&cc->ac_ctx, sizeof(cc->ac_ctx)); |
|
#ifdef WITH_OPENSSL |
else if (EVP_CIPHER_CTX_cleanup(&cc->evp) == 0) |
else if (EVP_CIPHER_CTX_cleanup(&cc->evp) == 0) |
error("cipher_cleanup: EVP_CIPHER_CTX_cleanup failed"); |
error("cipher_cleanup: EVP_CIPHER_CTX_cleanup failed"); |
|
#endif |
} |
} |
|
|
/* |
/* |
|
|
cipher_get_keyiv_len(const CipherContext *cc) |
cipher_get_keyiv_len(const CipherContext *cc) |
{ |
{ |
const Cipher *c = cc->cipher; |
const Cipher *c = cc->cipher; |
int ivlen; |
int ivlen = 0; |
|
|
if (c->number == SSH_CIPHER_3DES) |
if (c->number == SSH_CIPHER_3DES) |
ivlen = 24; |
ivlen = 24; |
else if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) |
else if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) |
ivlen = 0; |
ivlen = 0; |
|
#ifdef WITH_OPENSSL |
else |
else |
ivlen = EVP_CIPHER_CTX_iv_length(&cc->evp); |
ivlen = EVP_CIPHER_CTX_iv_length(&cc->evp); |
|
#endif |
return (ivlen); |
return (ivlen); |
} |
} |
|
|
|
|
cipher_get_keyiv(CipherContext *cc, u_char *iv, u_int len) |
cipher_get_keyiv(CipherContext *cc, u_char *iv, u_int len) |
{ |
{ |
const Cipher *c = cc->cipher; |
const Cipher *c = cc->cipher; |
|
#ifdef WITH_OPENSSL |
int evplen; |
int evplen; |
|
#endif |
|
|
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) { |
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) { |
if (len != 0) |
if (len != 0) |
fatal("%s: wrong iv length %d != %d", __func__, len, 0); |
fatal("%s: wrong iv length %d != %d", __func__, len, 0); |
return; |
return; |
} |
} |
|
if ((cc->cipher->flags & CFLAG_NONE) != 0) |
|
return; |
|
|
switch (c->number) { |
switch (c->number) { |
|
#ifdef WITH_OPENSSL |
case SSH_CIPHER_SSH2: |
case SSH_CIPHER_SSH2: |
case SSH_CIPHER_DES: |
case SSH_CIPHER_DES: |
case SSH_CIPHER_BLOWFISH: |
case SSH_CIPHER_BLOWFISH: |
|
|
} else |
} else |
memcpy(iv, cc->evp.iv, len); |
memcpy(iv, cc->evp.iv, len); |
break; |
break; |
|
#endif |
|
#ifdef WITH_SSH1 |
case SSH_CIPHER_3DES: |
case SSH_CIPHER_3DES: |
ssh1_3des_iv(&cc->evp, 0, iv, 24); |
ssh1_3des_iv(&cc->evp, 0, iv, 24); |
break; |
break; |
|
#endif |
default: |
default: |
fatal("%s: bad cipher %d", __func__, c->number); |
fatal("%s: bad cipher %d", __func__, c->number); |
} |
} |
|
|
cipher_set_keyiv(CipherContext *cc, u_char *iv) |
cipher_set_keyiv(CipherContext *cc, u_char *iv) |
{ |
{ |
const Cipher *c = cc->cipher; |
const Cipher *c = cc->cipher; |
|
#ifdef WITH_OPENSSL |
int evplen = 0; |
int evplen = 0; |
|
#endif |
|
|
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) |
if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) |
return; |
return; |
|
if ((cc->cipher->flags & CFLAG_NONE) != 0) |
|
return; |
|
|
switch (c->number) { |
switch (c->number) { |
|
#ifdef WITH_OPENSSL |
case SSH_CIPHER_SSH2: |
case SSH_CIPHER_SSH2: |
case SSH_CIPHER_DES: |
case SSH_CIPHER_DES: |
case SSH_CIPHER_BLOWFISH: |
case SSH_CIPHER_BLOWFISH: |
|
|
} else |
} else |
memcpy(cc->evp.iv, iv, evplen); |
memcpy(cc->evp.iv, iv, evplen); |
break; |
break; |
|
#endif |
|
#ifdef WITH_SSH1 |
case SSH_CIPHER_3DES: |
case SSH_CIPHER_3DES: |
ssh1_3des_iv(&cc->evp, 1, iv, 24); |
ssh1_3des_iv(&cc->evp, 1, iv, 24); |
break; |
break; |
|
#endif |
default: |
default: |
fatal("%s: bad cipher %d", __func__, c->number); |
fatal("%s: bad cipher %d", __func__, c->number); |
} |
} |
} |
} |
|
|
|
#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 |
|
|
int |
int |
cipher_get_keycontext(const CipherContext *cc, u_char *dat) |
cipher_get_keycontext(const CipherContext *cc, u_char *dat) |
{ |
{ |
|
#ifdef WITH_OPENSSL |
const Cipher *c = cc->cipher; |
const Cipher *c = cc->cipher; |
int plen = 0; |
int plen = 0; |
|
|
|
|
memcpy(dat, EVP_X_STATE(cc->evp), plen); |
memcpy(dat, EVP_X_STATE(cc->evp), plen); |
} |
} |
return (plen); |
return (plen); |
|
#else |
|
return (0); |
|
#endif |
} |
} |
|
|
void |
void |
cipher_set_keycontext(CipherContext *cc, u_char *dat) |
cipher_set_keycontext(CipherContext *cc, u_char *dat) |
{ |
{ |
|
#ifdef WITH_OPENSSL |
const Cipher *c = cc->cipher; |
const Cipher *c = cc->cipher; |
int plen; |
int plen; |
|
|
|
|
plen = EVP_X_STATE_LEN(cc->evp); |
plen = EVP_X_STATE_LEN(cc->evp); |
memcpy(EVP_X_STATE(cc->evp), dat, plen); |
memcpy(EVP_X_STATE(cc->evp), dat, plen); |
} |
} |
|
#endif |
} |
} |