summaryrefslogtreecommitdiff
path: root/cipher.c
diff options
context:
space:
mode:
authorDamien Miller <djm@mindrot.org>2013-11-21 14:12:23 +1100
committerDamien Miller <djm@mindrot.org>2013-11-21 14:12:23 +1100
commit0fde8acdad78a4d20cadae974376cc0165f645ee (patch)
tree6e6aa82b73163bcb412920050d98f82ca9f4e86e /cipher.c
parentfdb2306acdc3eb2bc46b6dfdaaf6005c650af22a (diff)
downloadopenssh-git-0fde8acdad78a4d20cadae974376cc0165f645ee.tar.gz
- djm@cvs.openbsd.org 2013/11/21 00:45:44
[Makefile.in PROTOCOL PROTOCOL.chacha20poly1305 authfile.c chacha.c] [chacha.h cipher-chachapoly.c cipher-chachapoly.h cipher.c cipher.h] [dh.c myproposal.h packet.c poly1305.c poly1305.h servconf.c ssh.1] [ssh.c ssh_config.5 sshd_config.5] Add a new protocol 2 transport cipher "chacha20-poly1305@openssh.com" that combines Daniel Bernstein's ChaCha20 stream cipher and Poly1305 MAC to build an authenticated encryption mode. Inspired by and similar to Adam Langley's proposal for TLS: http://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-03 but differs in layout used for the MAC calculation and the use of a second ChaCha20 instance to separately encrypt packet lengths. Details are in the PROTOCOL.chacha20poly1305 file. Feedback markus@, naddy@; manpage bits Loganden Velvindron @ AfriNIC ok markus@ naddy@
Diffstat (limited to 'cipher.c')
-rw-r--r--cipher.c65
1 files changed, 57 insertions, 8 deletions
diff --git a/cipher.c b/cipher.c
index 54315f48..c4aec392 100644
--- a/cipher.c
+++ b/cipher.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: cipher.c,v 1.90 2013/11/07 11:58:27 dtucker Exp $ */
+/* $OpenBSD: cipher.c,v 1.91 2013/11/21 00:45:44 djm Exp $ */
/*
* Author: Tatu Ylonen <ylo@cs.hut.fi>
* Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo, Finland
@@ -43,9 +43,11 @@
#include <string.h>
#include <stdarg.h>
+#include <stdio.h>
#include "xmalloc.h"
#include "log.h"
+#include "misc.h"
#include "cipher.h"
/* compatibility with old or broken OpenSSL versions */
@@ -63,7 +65,9 @@ struct Cipher {
u_int iv_len; /* defaults to block_size */
u_int auth_len;
u_int discard_len;
- u_int cbc_mode;
+ u_int flags;
+#define CFLAG_CBC (1<<0)
+#define CFLAG_CHACHAPOLY (1<<1)
const EVP_CIPHER *(*evptype)(void);
};
@@ -95,6 +99,8 @@ static const struct Cipher ciphers[] = {
{ "aes256-gcm@openssh.com",
SSH_CIPHER_SSH2, 16, 32, 12, 16, 0, 0, EVP_aes_256_gcm },
#endif
+ { "chacha20-poly1305@openssh.com",
+ SSH_CIPHER_SSH2, 8, 64, 0, 16, 0, CFLAG_CHACHAPOLY, NULL },
{ NULL, SSH_CIPHER_INVALID, 0, 0, 0, 0, 0, 0, NULL }
};
@@ -102,7 +108,7 @@ static const struct Cipher ciphers[] = {
/* Returns a list of supported ciphers separated by the specified char. */
char *
-cipher_alg_list(char sep)
+cipher_alg_list(char sep, int auth_only)
{
char *ret = NULL;
size_t nlen, rlen = 0;
@@ -111,6 +117,8 @@ cipher_alg_list(char sep)
for (c = ciphers; c->name != NULL; c++) {
if (c->number != SSH_CIPHER_SSH2)
continue;
+ if (auth_only && c->auth_len == 0)
+ continue;
if (ret != NULL)
ret[rlen++] = sep;
nlen = strlen(c->name);
@@ -142,7 +150,12 @@ cipher_authlen(const Cipher *c)
u_int
cipher_ivlen(const Cipher *c)
{
- return (c->iv_len ? c->iv_len : c->block_size);
+ /*
+ * Default is cipher block size, except for chacha20+poly1305 that
+ * needs no IV. XXX make iv_len == -1 default?
+ */
+ return (c->iv_len != 0 || (c->flags & CFLAG_CHACHAPOLY) != 0) ?
+ c->iv_len : c->block_size;
}
u_int
@@ -154,7 +167,7 @@ cipher_get_number(const Cipher *c)
u_int
cipher_is_cbc(const Cipher *c)
{
- return (c->cbc_mode);
+ return (c->flags & CFLAG_CBC) != 0;
}
u_int
@@ -274,8 +287,11 @@ cipher_init(CipherContext *cc, const Cipher *cipher,
ivlen, cipher->name);
cc->cipher = cipher;
+ if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) {
+ chachapoly_init(&cc->cp_ctx, key, keylen);
+ return;
+ }
type = (*cipher->evptype)();
-
EVP_CIPHER_CTX_init(&cc->evp);
#ifdef SSH_OLD_EVP
if (type->key_len > 0 && type->key_len != keylen) {
@@ -330,9 +346,15 @@ cipher_init(CipherContext *cc, const Cipher *cipher,
* Both 'aadlen' and 'authlen' can be set to 0.
*/
void
-cipher_crypt(CipherContext *cc, u_char *dest, const u_char *src,
+cipher_crypt(CipherContext *cc, u_int seqnr, u_char *dest, const u_char *src,
u_int len, u_int aadlen, u_int authlen)
{
+ if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) {
+ if (chachapoly_crypt(&cc->cp_ctx, seqnr, dest, src, len, aadlen,
+ authlen, cc->encrypt) != 0)
+ fatal("Decryption integrity check failed");
+ return;
+ }
if (authlen) {
u_char lastiv[1];
@@ -374,10 +396,26 @@ cipher_crypt(CipherContext *cc, u_char *dest, const u_char *src,
}
}
+/* Extract the packet length, including any decryption necessary beforehand */
+int
+cipher_get_length(CipherContext *cc, u_int *plenp, u_int seqnr,
+ const u_char *cp, u_int len)
+{
+ if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0)
+ return chachapoly_get_length(&cc->cp_ctx, plenp, seqnr,
+ cp, len);
+ if (len < 4)
+ return -1;
+ *plenp = get_u32(cp);
+ return 0;
+}
+
void
cipher_cleanup(CipherContext *cc)
{
- if (EVP_CIPHER_CTX_cleanup(&cc->evp) == 0)
+ if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0)
+ bzero(&cc->cp_ctx, sizeof(&cc->cp_ctx));
+ else if (EVP_CIPHER_CTX_cleanup(&cc->evp) == 0)
error("cipher_cleanup: EVP_CIPHER_CTX_cleanup failed");
}
@@ -417,6 +455,8 @@ cipher_get_keyiv_len(const CipherContext *cc)
if (c->number == SSH_CIPHER_3DES)
ivlen = 24;
+ else if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0)
+ ivlen = 0;
else
ivlen = EVP_CIPHER_CTX_iv_length(&cc->evp);
return (ivlen);
@@ -428,6 +468,12 @@ cipher_get_keyiv(CipherContext *cc, u_char *iv, u_int len)
const Cipher *c = cc->cipher;
int evplen;
+ if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0) {
+ if (len != 0)
+ fatal("%s: wrong iv length %d != %d", __func__, len, 0);
+ return;
+ }
+
switch (c->number) {
case SSH_CIPHER_SSH2:
case SSH_CIPHER_DES:
@@ -464,6 +510,9 @@ cipher_set_keyiv(CipherContext *cc, u_char *iv)
const Cipher *c = cc->cipher;
int evplen = 0;
+ if ((cc->cipher->flags & CFLAG_CHACHAPOLY) != 0)
+ return;
+
switch (c->number) {
case SSH_CIPHER_SSH2:
case SSH_CIPHER_DES: