diff options
author | Damien Miller <djm@mindrot.org> | 2013-11-21 14:12:23 +1100 |
---|---|---|
committer | Damien Miller <djm@mindrot.org> | 2013-11-21 14:12:23 +1100 |
commit | 0fde8acdad78a4d20cadae974376cc0165f645ee (patch) | |
tree | 6e6aa82b73163bcb412920050d98f82ca9f4e86e /cipher.c | |
parent | fdb2306acdc3eb2bc46b6dfdaaf6005c650af22a (diff) | |
download | openssh-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.c | 65 |
1 files changed, 57 insertions, 8 deletions
@@ -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: |