From d689d1d7940b2122787aa3db8288b0e5d3c4110d Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 24 Feb 2019 22:15:08 +0200 Subject: [PATCH] chacha20 poly1305 --- config_in.h | 24 +- configure | 93 ++++- configure.ac | 8 +- crypto/cipher/chacha20_poly1305_nss.c | 510 +++++++++++++++++++++++++ crypto/cipher/chacha20_poly1305_ossl.c | 475 +++++++++++++++++++++++ crypto/cipher/cipher.c | 18 +- crypto/include/chacha20_poly1305.h | 86 +++++ crypto/include/cipher_types.h | 6 +- crypto/include/crypto_types.h | 7 + crypto/kernel/crypto_kernel.c | 7 + crypto/test/kernel_driver.c | 24 +- include/srtp.h | 12 + srtp/srtp.c | 73 +++- 13 files changed, 1313 insertions(+), 30 deletions(-) create mode 100644 crypto/cipher/chacha20_poly1305_nss.c create mode 100644 crypto/cipher/chacha20_poly1305_ossl.c create mode 100644 crypto/include/chacha20_poly1305.h diff --git a/config_in.h b/config_in.h index 32b7c1c1a..f10592eed 100644 --- a/config_in.h +++ b/config_in.h @@ -3,6 +3,9 @@ /* Define if building universal (internal helper macro) */ #undef AC_APPLE_UNIVERSAL_BUILD +/* Define this to use CHACHA20-POLY1305. */ +#undef CHAPOLY + /* Define if building for a CISC machine (e.g. Intel). */ #undef CPU_CISC @@ -18,6 +21,9 @@ /* Define to redirect logging to stdout. */ #undef ERR_REPORTING_STDOUT +/* Define this to use AES-GCM. */ +#undef GCM + /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_INET_H @@ -42,6 +48,12 @@ /* Define to 1 if you have the `dl' library (-ldl). */ #undef HAVE_LIBDL +/* Define to 1 if you have the `nspr4' library (-lnspr4). */ +#undef HAVE_LIBNSPR4 + +/* Define to 1 if you have the `nss3' library (-lnss3). */ +#undef HAVE_LIBNSS3 + /* Define to 1 if you have the `socket' library (-lsocket). */ #undef HAVE_LIBSOCKET @@ -57,6 +69,9 @@ /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IN_H +/* Define to 1 if you have the header file. */ +#undef HAVE_NSS_H + /* Define to 1 if you have the `winpcap' library (-lwpcap) */ #undef HAVE_PCAP @@ -120,6 +135,9 @@ /* Define to use X86 inlined assembly code */ #undef HAVE_X86 +/* Define this to use NSS crypto. */ +#undef NSS + /* Define this to use OpenSSL crypto. */ #undef OPENSSL @@ -129,12 +147,6 @@ /* Define this to use OpenSSL KDF for SRTP. */ #undef OPENSSL_KDF -/* Define this to use NSS crypto. */ -#undef NSS - -/* Define this to use AES-GCM. Requires OPENSSL or NSS */ -#undef GCM - /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT diff --git a/configure b/configure index b537b166a..61dcb4ba4 100755 --- a/configure +++ b/configure @@ -682,6 +682,7 @@ infodir docdir oldincludedir includedir +runstatedir localstatedir sharedstatedir sysconfdir @@ -768,6 +769,7 @@ datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' @@ -1020,6 +1022,15 @@ do | -silent | --silent | --silen | --sile | --sil) silent=yes ;; + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ @@ -1157,7 +1168,7 @@ fi for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ - libdir localedir mandir + libdir localedir mandir runstatedir do eval ac_val=\$$ac_var # Remove trailing slashes. @@ -1310,6 +1321,7 @@ Fine tuning of the installation directories: --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] @@ -5196,8 +5208,8 @@ fi if test "x$PKG_CONFIG" != "x"; then pkg_failed=no -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libcrypto >= 1.0.1" >&5 -$as_echo_n "checking for libcrypto >= 1.0.1... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for crypto" >&5 +$as_echo_n "checking for crypto... " >&6; } if test -n "$crypto_CFLAGS"; then pkg_cv_crypto_CFLAGS="$crypto_CFLAGS" @@ -5237,7 +5249,7 @@ fi if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then @@ -5264,7 +5276,7 @@ Alternatively, you may set the environment variables crypto_CFLAGS and crypto_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} @@ -5568,6 +5580,66 @@ as_fn_error $? "can't find openssl >= 1.0.1 crypto lib See \`config.log' for more details" "$LINENO" 5; } fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing EVP_chacha20_poly1305" >&5 +$as_echo_n "checking for library containing EVP_chacha20_poly1305... " >&6; } +if ${ac_cv_search_EVP_chacha20_poly1305+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char EVP_chacha20_poly1305 (); +int +main () +{ +return EVP_chacha20_poly1305 (); + ; + return 0; +} +_ACEOF +for ac_lib in '' crypto; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_EVP_chacha20_poly1305=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_EVP_chacha20_poly1305+:} false; then : + break +fi +done +if ${ac_cv_search_EVP_chacha20_poly1305+:} false; then : + +else + ac_cv_search_EVP_chacha20_poly1305=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_EVP_chacha20_poly1305" >&5 +$as_echo "$ac_cv_search_EVP_chacha20_poly1305" >&6; } +ac_res=$ac_cv_search_EVP_chacha20_poly1305 +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + + +$as_echo "#define CHAPOLY 1" >>confdefs.h + + CHAPOLY_OBJ="crypto/cipher/chacha20_poly1305_ossl.o" +fi + $as_echo "#define GCM 1" >>confdefs.h @@ -5575,7 +5647,7 @@ $as_echo "#define GCM 1" >>confdefs.h $as_echo "#define OPENSSL 1" >>confdefs.h - AES_ICM_OBJS="crypto/cipher/aes_icm_ossl.o crypto/cipher/aes_gcm_ossl.o" + AES_ICM_OBJS="crypto/cipher/aes_icm_ossl.o crypto/cipher/aes_gcm_ossl.o $CHAPOLY_OBJ" HMAC_OBJS=crypto/hash/hmac_ossl.o USE_EXTERNAL_CRYPTO=1 @@ -5796,7 +5868,7 @@ fi if test $pkg_failed = yes; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then @@ -5823,7 +5895,7 @@ Alternatively, you may set the environment variables nss_CFLAGS and nss_LIBS to avoid the need to call pkg-config. See the pkg-config man page for more details." "$LINENO" 5 elif test $pkg_failed = untried; then - { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} @@ -5968,9 +6040,12 @@ fi $as_echo "#define GCM 1" >>confdefs.h +$as_echo "#define CHAPOLY 1" >>confdefs.h + + $as_echo "#define NSS 1" >>confdefs.h - AES_ICM_OBJS="crypto/cipher/aes_icm_nss.o crypto/cipher/aes_gcm_nss.o" + AES_ICM_OBJS="crypto/cipher/aes_icm_nss.o crypto/cipher/aes_gcm_nss.o crypto/cipher/chacha20_poly1305_nss.o" # TODO(RLB): Use NSS for HMAC HMAC_OBJS="crypto/hash/hmac.o crypto/hash/sha1.o" diff --git a/configure.ac b/configure.ac index bed7a8716..d8f9cf60c 100644 --- a/configure.ac +++ b/configure.ac @@ -255,10 +255,13 @@ if test "$enable_openssl" = "yes"; then [], [AC_MSG_FAILURE([can't find openssl >= 1.0.1 crypto lib])]) AC_SEARCH_LIBS([EVP_aes_128_gcm], [crypto], [], [AC_MSG_FAILURE([can't find openssl >= 1.0.1 crypto lib])]) + AC_SEARCH_LIBS([EVP_chacha20_poly1305], [crypto], [ + AC_DEFINE([CHAPOLY], [1], [Define this to use CHACHA20-POLY1305.]) + CHAPOLY_OBJ="crypto/cipher/chacha20_poly1305_ossl.o"]) AC_DEFINE([GCM], [1], [Define this to use AES-GCM.]) AC_DEFINE([OPENSSL], [1], [Define this to use OpenSSL crypto.]) - AES_ICM_OBJS="crypto/cipher/aes_icm_ossl.o crypto/cipher/aes_gcm_ossl.o" + AES_ICM_OBJS="crypto/cipher/aes_icm_ossl.o crypto/cipher/aes_gcm_ossl.o $CHAPOLY_OBJ" HMAC_OBJS=crypto/hash/hmac_ossl.o AC_SUBST([USE_EXTERNAL_CRYPTO], [1]) @@ -343,8 +346,9 @@ elif test "$enable_nss" = "yes"; then fi AC_DEFINE([GCM], [1], [Define this to use AES-GCM.]) + AC_DEFINE([CHAPOLY], [1], [Define this to use CHACHA20-POLY1305.]) AC_DEFINE([NSS], [1], [Define this to use NSS crypto.]) - AES_ICM_OBJS="crypto/cipher/aes_icm_nss.o crypto/cipher/aes_gcm_nss.o" + AES_ICM_OBJS="crypto/cipher/aes_icm_nss.o crypto/cipher/aes_gcm_nss.o crypto/cipher/chacha20_poly1305_nss.o" # TODO(RLB): Use NSS for HMAC HMAC_OBJS="crypto/hash/hmac.o crypto/hash/sha1.o" diff --git a/crypto/cipher/chacha20_poly1305_nss.c b/crypto/cipher/chacha20_poly1305_nss.c new file mode 100644 index 000000000..42d34bf49 --- /dev/null +++ b/crypto/cipher/chacha20_poly1305_nss.c @@ -0,0 +1,510 @@ +/* + * chacha20_poly1305_nss.c + * + * CHACHA20 POLY1305 + * + * + */ + +/* + * + * Copyright (c) 2013-2017, Cisco Systems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * Neither the name of the Cisco Systems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "chacha20_poly1305.h" +#include "alloc.h" +#include "err.h" /* for srtp_debug */ +#include "crypto_types.h" +#include "cipher_types.h" +#include +#include + +srtp_debug_module_t srtp_mod_chacha20_poly1305 = { + 0, /* debugging is off by default */ + "chacha20 poly1305 nss" /* printable module name */ +}; + +/* + * For now we only support 8 and 16 octet tags. The spec allows for + * optional 12 byte tag, which may be supported in the future. + */ +#define AEAD_IV_LEN 12 +#define AEAD_AUTH_TAG_LEN 16 +#define AEAD_AUTH_TAG_LEN_8 8 + +/* + * This function allocates a new instance of this crypto engine. + * The key_len parameter should be 44 for + * CHACHA20 POLY1305 respectively. Note that the + * key length includes the 14 byte salt value that is used when + * initializing the KDF. + */ +static srtp_err_status_t srtp_chacha20_poly1305_nss_alloc(srtp_cipher_t **c, + int key_len, + int tlen) +{ + srtp_chacha20_poly1305_ctx_t *gcm; + NSSInitContext *nss; + + debug_print(srtp_mod_chacha20_poly1305, + "allocating cipher with key length %d", key_len); + debug_print(srtp_mod_chacha20_poly1305, + "allocating cipher with tag length %d", tlen); + + /* + * Verify the key_len is valid for one of: CHACHA20 POLY1305 + */ + if (key_len != SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT) { + return (srtp_err_status_bad_param); + } + + if (tlen != AEAD_AUTH_TAG_LEN && tlen != AEAD_AUTH_TAG_LEN_8) { + return (srtp_err_status_bad_param); + } + + /* Initialize NSS equiv of NSS_NoDB_Init(NULL) */ + nss = NSS_InitContext("", "", "", "", NULL, + NSS_INIT_READONLY | NSS_INIT_NOCERTDB | + NSS_INIT_NOMODDB | NSS_INIT_FORCEOPEN | + NSS_INIT_OPTIMIZESPACE); + if (!nss) { + return (srtp_err_status_cipher_fail); + } + + /* allocate memory a cipher of type chacha20_poly1305 */ + *c = (srtp_cipher_t *)srtp_crypto_alloc(sizeof(srtp_cipher_t)); + if (*c == NULL) { + NSS_ShutdownContext(nss); + return (srtp_err_status_alloc_fail); + } + + gcm = (srtp_chacha20_poly1305_ctx_t *)srtp_crypto_alloc( + sizeof(srtp_chacha20_poly1305_ctx_t)); + if (gcm == NULL) { + NSS_ShutdownContext(nss); + srtp_crypto_free(*c); + *c = NULL; + return (srtp_err_status_alloc_fail); + } + + gcm->nss = nss; + + /* set pointers */ + (*c)->state = gcm; + + /* setup cipher attributes */ + switch (key_len) { + case SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT: + (*c)->type = &srtp_chacha20_poly1305; + (*c)->algorithm = SRTP_CHACHA20_POLY1305; + gcm->key_size = SRTP_CHACHA20_POLY1305_KEY_LEN; + gcm->tag_size = tlen; + gcm->params.ulTagLen = tlen; + break; + default: + /* this should never hit, but to be sure... */ + return (srtp_err_status_bad_param); + } + + /* set key size and tag size*/ + (*c)->key_len = key_len; + + return (srtp_err_status_ok); +} + +/* + * This function deallocates a CHACHA20 POLY1305 session + */ +static srtp_err_status_t srtp_chacha20_poly1305_nss_dealloc(srtp_cipher_t *c) +{ + srtp_chacha20_poly1305_ctx_t *ctx; + + ctx = (srtp_chacha20_poly1305_ctx_t *)c->state; + if (ctx) { + /* release NSS resources */ + if (ctx->key) { + PK11_FreeSymKey(ctx->key); + } + + if (ctx->nss) { + NSS_ShutdownContext(ctx->nss); + ctx->nss = NULL; + } + + /* zeroize the key material */ + octet_string_set_to_zero(ctx, sizeof(srtp_chacha20_poly1305_ctx_t)); + srtp_crypto_free(ctx); + } + + /* free memory */ + srtp_crypto_free(c); + + return (srtp_err_status_ok); +} + +/* + * chacha20_poly1305_nss_context_init(...) initializes the + * chacha20_poly1305_context + * using the value in key[]. + * + * the key is the secret key + */ +static srtp_err_status_t srtp_chacha20_poly1305_nss_context_init( + void *cv, + const uint8_t *key) +{ + srtp_chacha20_poly1305_ctx_t *c = (srtp_chacha20_poly1305_ctx_t *)cv; + + c->dir = srtp_direction_any; + + debug_print(srtp_mod_chacha20_poly1305, "key: %s", + srtp_octet_string_hex_string(key, c->key_size)); + + if (c->key) { + PK11_FreeSymKey(c->key); + c->key = NULL; + } + + PK11SlotInfo *slot = PK11_GetBestSlot(CKM_NSS_CHACHA20_POLY1305, NULL); + if (!slot) { + return (srtp_err_status_cipher_fail); + } + + SECItem key_item = { siBuffer, (unsigned char *)key, c->key_size }; + c->key = PK11_ImportSymKey(slot, CKM_NSS_CHACHA20_POLY1305, + PK11_OriginUnwrap, CKA_ENCRYPT, &key_item, NULL); + PK11_FreeSlot(slot); + + if (!c->key) { + return (srtp_err_status_cipher_fail); + } + + return (srtp_err_status_ok); +} + +/* + * chacha20_poly1305_nss_set_iv(c, iv) sets the counter value to the exor of iv + * with + * the offset + */ +static srtp_err_status_t srtp_chacha20_poly1305_nss_set_iv( + void *cv, + uint8_t *iv, + srtp_cipher_direction_t direction) +{ + srtp_chacha20_poly1305_ctx_t *c = (srtp_chacha20_poly1305_ctx_t *)cv; + + if (direction != srtp_direction_encrypt && + direction != srtp_direction_decrypt) { + return (srtp_err_status_bad_param); + } + c->dir = direction; + + debug_print(srtp_mod_chacha20_poly1305, "setting iv: %s", + srtp_octet_string_hex_string(iv, AEAD_IV_LEN)); + + memcpy(c->iv, iv, AEAD_IV_LEN); + + return (srtp_err_status_ok); +} + +/* + * This function processes the AAD + * + * Parameters: + * c Crypto context + * aad Additional data to process for AEAD cipher suites + * aad_len length of aad buffer + */ +static srtp_err_status_t srtp_chacha20_poly1305_nss_set_aad(void *cv, + const uint8_t *aad, + uint32_t aad_len) +{ + srtp_chacha20_poly1305_ctx_t *c = (srtp_chacha20_poly1305_ctx_t *)cv; + + debug_print(srtp_mod_chacha20_poly1305, "setting AAD: %s", + srtp_octet_string_hex_string(aad, aad_len)); + + if (aad_len + c->aad_size > MAX_AD_SIZE) { + return srtp_err_status_bad_param; + } + + memcpy(c->aad + c->aad_size, aad, aad_len); + c->aad_size += aad_len; + + return (srtp_err_status_ok); +} + +static srtp_err_status_t srtp_chacha20_poly1305_nss_do_crypto( + void *cv, + int encrypt, + unsigned char *buf, + unsigned int *enc_len) +{ + srtp_chacha20_poly1305_ctx_t *c = (srtp_chacha20_poly1305_ctx_t *)cv; + + c->params.pNonce = c->iv; + c->params.ulNonceLen = AEAD_IV_LEN; + c->params.pAAD = c->aad; + c->params.ulAADLen = c->aad_size; + + // Reset AAD + c->aad_size = 0; + + int rv; + SECItem param = { siBuffer, (unsigned char *)&c->params, + sizeof(CK_NSS_AEAD_PARAMS) }; + if (encrypt) { + rv = PK11_Encrypt(c->key, CKM_NSS_CHACHA20_POLY1305, ¶m, buf, + enc_len, *enc_len + 16, buf, *enc_len); + } else { + rv = PK11_Decrypt(c->key, CKM_NSS_CHACHA20_POLY1305, ¶m, buf, + enc_len, *enc_len, buf, *enc_len); + } + + srtp_err_status_t status = (srtp_err_status_ok); + if (rv != SECSuccess) { + status = (srtp_err_status_cipher_fail); + } + + return status; +} + +/* + * This function encrypts a buffer using CHACHA20 POLY1305 mode + * + * XXX(rlb@ipv.sx): We're required to break off and cache the tag + * here, because the get_tag() method is separate and the tests expect + * encrypt() not to change the size of the plaintext. It might be + * good to update the calling API so that this is cleaner. + * + * Parameters: + * c Crypto context + * buf data to encrypt + * enc_len length of encrypt buffer + */ +static srtp_err_status_t srtp_chacha20_poly1305_nss_encrypt( + void *cv, + unsigned char *buf, + unsigned int *enc_len) +{ + srtp_chacha20_poly1305_ctx_t *c = (srtp_chacha20_poly1305_ctx_t *)cv; + + // When we get a non-NULL buffer, we know that the caller is + // prepared to also take the tag. When we get a NULL buffer, + // even though there's no data, we need to give NSS a buffer + // where it can write the tag. We can't just use c->tag because + // memcpy has undefined behavior on overlapping ranges. + unsigned char tagbuf[16]; + unsigned char *non_null_buf = buf; + if (!non_null_buf && (*enc_len == 0)) { + non_null_buf = tagbuf; + } else if (!non_null_buf) { + return srtp_err_status_bad_param; + } + + srtp_err_status_t status = + srtp_chacha20_poly1305_nss_do_crypto(cv, 1, non_null_buf, enc_len); + if (status != srtp_err_status_ok) { + return status; + } + + memcpy(c->tag, non_null_buf + (*enc_len - c->tag_size), c->tag_size); + *enc_len -= c->tag_size; + return srtp_err_status_ok; +} + +/* + * This function calculates and returns the AEAD tag for a given context. + * This should be called after encrypting the data. The *len value + * is increased by the tag size. The caller must ensure that *buf has + * enough room to accept the appended tag. + * + * Parameters: + * c Crypto context + * buf data to encrypt + * len length of encrypt buffer + */ +static srtp_err_status_t srtp_chacha20_poly1305_nss_get_tag(void *cv, + uint8_t *buf, + uint32_t *len) +{ + srtp_chacha20_poly1305_ctx_t *c = (srtp_chacha20_poly1305_ctx_t *)cv; + *len = c->tag_size; + memcpy(buf, c->tag, c->tag_size); + return (srtp_err_status_ok); +} + +/* + * This function decrypts a buffer using CHACHA20 POLY1305 mode + * + * Parameters: + * c Crypto context + * buf data to encrypt + * enc_len length of encrypt buffer + */ +static srtp_err_status_t srtp_chacha20_poly1305_nss_decrypt( + void *cv, + unsigned char *buf, + unsigned int *enc_len) +{ + srtp_err_status_t status = + srtp_chacha20_poly1305_nss_do_crypto(cv, 0, buf, enc_len); + if (status != srtp_err_status_ok) { + int err = PR_GetError(); + if (err == SEC_ERROR_BAD_DATA) { + status = srtp_err_status_auth_fail; + } + } + + return status; +} + +/* + * Name of this crypto engine + */ +static const char srtp_chacha20_poly1305_nss_description[] = + "CHACHA20 POLY1305 using NSS"; + +/* + * KAT values for CHACHA20 POLY1305 self-test. These + * values we're derived from independent test code + * using OpenSSL. + */ + +/* clang-format off */ +static const uint8_t srtp_chacha20_poly1305_test_case_1_key[SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT] = { + 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0xa5, 0x59, 0x09, 0xc5, 0x54, 0x66, 0x93, 0x1c, + 0xaf, 0xf5, 0x26, 0x9a, 0x21, 0xd5, 0x14, 0xb2, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, +}; +/* clang-format on */ + +/* clang-format off */ +static uint8_t srtp_chacha20_poly1305_test_case_1_iv[12] = { + 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, + 0xde, 0xca, 0xf8, 0x88 +}; +/* clang-format on */ + +/* clang-format off */ +static const uint8_t srtp_chacha20_poly1305_test_case_1_plaintext[60] = { + 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, + 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, + 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, + 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, + 0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, + 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, + 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, + 0xba, 0x63, 0x7b, 0x39 +}; +/* clang-format on */ + +/* clang-format off */ +static const uint8_t srtp_chacha20_poly1305_test_case_1_aad[20] = { + 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, + 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, + 0xab, 0xad, 0xda, 0xd2 +}; +/* clang-format on */ + +/* clang-format off */ +static const uint8_t srtp_chacha20_poly1305_test_case_1_ciphertext[76] = { + 0xf1, 0xe9, 0x8f, 0xa5, 0x30, 0xce, 0x2c, 0x53, + 0x3c, 0x54, 0x31, 0xdb, 0x1f, 0xe4, 0x5b, 0xcb, + 0x88, 0xfd, 0x48, 0x7c, 0xd2, 0x28, 0xab, 0x0b, + 0x2f, 0x62, 0xd6, 0xc8, 0xaa, 0xc4, 0x6f, 0xd1, + 0x7f, 0xbf, 0xca, 0x17, 0xf9, 0x0d, 0x2c, 0x86, + 0x85, 0x1d, 0xf1, 0x7a, 0xfc, 0x10, 0xef, 0xa7, + 0x87, 0x92, 0x41, 0x66, 0x79, 0x63, 0xde, 0x6f, + 0x15, 0xc3, 0x39, 0x9b, + /* the last 16 bytes are the tag */ + 0x55, 0xc9, 0xa5, 0x8e, 0xe4, 0x4a, 0xc3, 0xfe, + 0x86, 0x5c, 0xb5, 0xe3, 0x2e, 0x25, 0x16, 0xf9, +}; +/* clang-format on */ + +static const srtp_cipher_test_case_t srtp_chacha20_poly1305_test_case_1a = { + SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT, /* octets in key */ + srtp_chacha20_poly1305_test_case_1_key, /* key */ + srtp_chacha20_poly1305_test_case_1_iv, /* packet index */ + 60, /* octets in plaintext */ + srtp_chacha20_poly1305_test_case_1_plaintext, /* plaintext */ + 68, /* octets in ciphertext */ + srtp_chacha20_poly1305_test_case_1_ciphertext, /* ciphertext + tag */ + 20, /* octets in AAD */ + srtp_chacha20_poly1305_test_case_1_aad, /* AAD */ + AEAD_AUTH_TAG_LEN_8, /* */ + NULL /* pointer to next testcase */ +}; + +static const srtp_cipher_test_case_t srtp_chacha20_poly1305_test_case_1 = { + SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT, /* octets in key */ + srtp_chacha20_poly1305_test_case_1_key, /* key */ + srtp_chacha20_poly1305_test_case_1_iv, /* packet index */ + 60, /* octets in plaintext */ + srtp_chacha20_poly1305_test_case_1_plaintext, /* plaintext */ + 76, /* octets in ciphertext */ + srtp_chacha20_poly1305_test_case_1_ciphertext, /* ciphertext + tag */ + 20, /* octets in AAD */ + srtp_chacha20_poly1305_test_case_1_aad, /* AAD */ + AEAD_AUTH_TAG_LEN, /* */ + &srtp_chacha20_poly1305_test_case_1a /* pointer to next testcase */ +}; + +/* + * This is the vector function table for this crypto engine. + */ +/* clang-format off */ +const srtp_cipher_type_t srtp_chacha20_poly1305 = { + srtp_chacha20_poly1305_nss_alloc, + srtp_chacha20_poly1305_nss_dealloc, + srtp_chacha20_poly1305_nss_context_init, + srtp_chacha20_poly1305_nss_set_aad, + srtp_chacha20_poly1305_nss_encrypt, + srtp_chacha20_poly1305_nss_decrypt, + srtp_chacha20_poly1305_nss_set_iv, + srtp_chacha20_poly1305_nss_get_tag, + srtp_chacha20_poly1305_nss_description, + &srtp_chacha20_poly1305_test_case_1, + SRTP_CHACHA20_POLY1305 +}; +/* clang-format on */ diff --git a/crypto/cipher/chacha20_poly1305_ossl.c b/crypto/cipher/chacha20_poly1305_ossl.c new file mode 100644 index 000000000..cbee50c63 --- /dev/null +++ b/crypto/cipher/chacha20_poly1305_ossl.c @@ -0,0 +1,475 @@ +/* + * chacha20_poly1305_ossl.c + * + * CHACHA20 POLY1305 + * + * + */ + +/* + * + * Copyright (c) 2013-2017, Cisco Systems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * Neither the name of the Cisco Systems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include "chacha20_poly1305.h" +#include "alloc.h" +#include "err.h" /* for srtp_debug */ +#include "crypto_types.h" +#include "cipher_types.h" + +srtp_debug_module_t srtp_mod_chacha20_poly1305 = { + 0, /* debugging is off by default */ + "chacha20 poly1305" /* printable module name */ +}; + +/* + * For now we only support 8 and 16 octet tags. The spec allows for + * optional 12 byte tag, which may be supported in the future. + */ +#define AEAD_AUTH_TAG_LEN 16 +#define AEAD_AUTH_TAG_LEN_8 8 + +/* + * This function allocates a new instance of this crypto engine. + * The key_len parameter should be one 44 for + * CHACHA20 POLY1305 respectively. Note that the + * key length includes the 14 byte salt value that is used when + * initializing the KDF. + */ +static srtp_err_status_t srtp_chacha20_poly1305_openssl_alloc(srtp_cipher_t **c, + int key_len, + int tlen) +{ + srtp_chacha20_poly1305_ctx_t *gcm; + + debug_print(srtp_mod_chacha20_poly1305, + "allocating cipher with key length %d", key_len); + debug_print(srtp_mod_chacha20_poly1305, + "allocating cipher with tag length %d", tlen); + + /* + * Verify the key_len is valid for one of: CHACHA20-POLY1305 + */ + if (key_len != SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT) { + return (srtp_err_status_bad_param); + } + + if (tlen != AEAD_AUTH_TAG_LEN && tlen != AEAD_AUTH_TAG_LEN_8) { + return (srtp_err_status_bad_param); + } + + /* allocate memory a cipher of type chacha20_poly1305 */ + *c = (srtp_cipher_t *)srtp_crypto_alloc(sizeof(srtp_cipher_t)); + if (*c == NULL) { + return (srtp_err_status_alloc_fail); + } + + gcm = (srtp_chacha20_poly1305_ctx_t *)srtp_crypto_alloc( + sizeof(srtp_chacha20_poly1305_ctx_t)); + if (gcm == NULL) { + srtp_crypto_free(*c); + *c = NULL; + return (srtp_err_status_alloc_fail); + } + + gcm->ctx = EVP_CIPHER_CTX_new(); + if (gcm->ctx == NULL) { + srtp_crypto_free(gcm); + srtp_crypto_free(*c); + *c = NULL; + return srtp_err_status_alloc_fail; + } + + /* set pointers */ + (*c)->state = gcm; + + /* setup cipher attributes */ + switch (key_len) { + case SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT: + (*c)->type = &srtp_chacha20_poly1305; + (*c)->algorithm = SRTP_CHACHA20_POLY1305; + gcm->key_size = SRTP_CHACHA20_POLY1305_KEY_LEN; + gcm->tag_len = tlen; + break; + } + + /* set key size */ + (*c)->key_len = key_len; + + return (srtp_err_status_ok); +} + +/* + * This function deallocates a CHACHA20 POLY1305 session + */ +static srtp_err_status_t srtp_chacha20_poly1305_openssl_dealloc( + srtp_cipher_t *c) +{ + srtp_chacha20_poly1305_ctx_t *ctx; + + ctx = (srtp_chacha20_poly1305_ctx_t *)c->state; + if (ctx) { + EVP_CIPHER_CTX_free(ctx->ctx); + /* zeroize the key material */ + octet_string_set_to_zero(ctx, sizeof(srtp_chacha20_poly1305_ctx_t)); + srtp_crypto_free(ctx); + } + + /* free memory */ + srtp_crypto_free(c); + + return (srtp_err_status_ok); +} + +/* + * chacha20_poly1305_openssl_context_init(...) initializes the + * chacha20_poly1305_context + * using the value in key[]. + * + * the key is the secret key + */ +static srtp_err_status_t srtp_chacha20_poly1305_openssl_context_init( + void *cv, + const uint8_t *key) +{ + srtp_chacha20_poly1305_ctx_t *c = (srtp_chacha20_poly1305_ctx_t *)cv; + const EVP_CIPHER *evp; + + c->dir = srtp_direction_any; + + debug_print(srtp_mod_chacha20_poly1305, "key: %s", + srtp_octet_string_hex_string(key, c->key_size)); + + switch (c->key_size) { + case SRTP_CHACHA20_POLY1305_KEY_LEN: + evp = EVP_chacha20_poly1305(); + break; + default: + return (srtp_err_status_bad_param); + break; + } + + if (!EVP_CipherInit_ex(c->ctx, evp, NULL, key, NULL, 0)) { + return (srtp_err_status_init_fail); + } + + return (srtp_err_status_ok); +} + +/* + * chacha20_poly1305_openssl_set_iv(c, iv) sets the counter value to the exor of + * iv with + * the offset + */ +static srtp_err_status_t srtp_chacha20_poly1305_openssl_set_iv( + void *cv, + uint8_t *iv, + srtp_cipher_direction_t direction) +{ + srtp_chacha20_poly1305_ctx_t *c = (srtp_chacha20_poly1305_ctx_t *)cv; + + if (direction != srtp_direction_encrypt && + direction != srtp_direction_decrypt) { + return (srtp_err_status_bad_param); + } + c->dir = direction; + + debug_print(srtp_mod_chacha20_poly1305, "setting iv: %s", + srtp_octet_string_hex_string(iv, 12)); + + if (!EVP_CIPHER_CTX_ctrl(c->ctx, EVP_CTRL_AEAD_SET_IVLEN, 12, 0)) { + return (srtp_err_status_init_fail); + } + + if (!EVP_CipherInit_ex(c->ctx, NULL, NULL, NULL, iv, + (c->dir == srtp_direction_encrypt ? 1 : 0))) { + return (srtp_err_status_init_fail); + } + + return (srtp_err_status_ok); +} + +/* + * This function processes the AAD + * + * Parameters: + * c Crypto context + * aad Additional data to process for AEAD cipher suites + * aad_len length of aad buffer + */ +static srtp_err_status_t srtp_chacha20_poly1305_openssl_set_aad( + void *cv, + const uint8_t *aad, + uint32_t aad_len) +{ + srtp_chacha20_poly1305_ctx_t *c = (srtp_chacha20_poly1305_ctx_t *)cv; + int rv; + + debug_print(srtp_mod_chacha20_poly1305, "setting AAD: %s", + srtp_octet_string_hex_string(aad, aad_len)); + + /* + * Set dummy tag, OpenSSL requires the Tag to be set before + * processing AAD + */ + + /* + * OpenSSL never write to address pointed by the last parameter of + * EVP_CIPHER_CTX_ctrl while EVP_CTRL_AEAD_SET_TAG (in reality, + * OpenSSL copy its content to the context), so we can make + * aad read-only in this function and all its wrappers. + */ + unsigned char dummy_tag[AEAD_AUTH_TAG_LEN]; + memset(dummy_tag, 0x0, AEAD_AUTH_TAG_LEN); + EVP_CIPHER_CTX_ctrl(c->ctx, EVP_CTRL_AEAD_SET_TAG, c->tag_len, &dummy_tag); + + rv = EVP_Cipher(c->ctx, NULL, aad, aad_len); + if (rv != aad_len) { + return (srtp_err_status_algo_fail); + } else { + return (srtp_err_status_ok); + } +} + +/* + * This function encrypts a buffer using CHACHA20 POLY1305 mode + * + * Parameters: + * c Crypto context + * buf data to encrypt + * enc_len length of encrypt buffer + */ +static srtp_err_status_t srtp_chacha20_poly1305_openssl_encrypt( + void *cv, + unsigned char *buf, + unsigned int *enc_len) +{ + srtp_chacha20_poly1305_ctx_t *c = (srtp_chacha20_poly1305_ctx_t *)cv; + if (c->dir != srtp_direction_encrypt && c->dir != srtp_direction_decrypt) { + return (srtp_err_status_bad_param); + } + + /* + * Encrypt the data + */ + EVP_Cipher(c->ctx, buf, buf, *enc_len); + + return (srtp_err_status_ok); +} + +/* + * This function calculates and returns the AEAD tag for a given context. + * This should be called after encrypting the data. The *len value + * is increased by the tag size. The caller must ensure that *buf has + * enough room to accept the appended tag. + * + * Parameters: + * c Crypto context + * buf data to encrypt + * len length of encrypt buffer + */ +static srtp_err_status_t srtp_chacha20_poly1305_openssl_get_tag(void *cv, + uint8_t *buf, + uint32_t *len) +{ + srtp_chacha20_poly1305_ctx_t *c = (srtp_chacha20_poly1305_ctx_t *)cv; + /* + * Calculate the tag + */ + EVP_Cipher(c->ctx, NULL, NULL, 0); + + /* + * Retreive the tag + */ + EVP_CIPHER_CTX_ctrl(c->ctx, EVP_CTRL_AEAD_GET_TAG, c->tag_len, buf); + + /* + * Increase encryption length by desired tag size + */ + *len = c->tag_len; + + return (srtp_err_status_ok); +} + +/* + * This function decrypts a buffer using CHACHA20 POLY1305 mode + * + * Parameters: + * c Crypto context + * buf data to encrypt + * enc_len length of encrypt buffer + */ +static srtp_err_status_t srtp_chacha20_poly1305_openssl_decrypt( + void *cv, + unsigned char *buf, + unsigned int *enc_len) +{ + srtp_chacha20_poly1305_ctx_t *c = (srtp_chacha20_poly1305_ctx_t *)cv; + if (c->dir != srtp_direction_encrypt && c->dir != srtp_direction_decrypt) { + return (srtp_err_status_bad_param); + } + + /* + * Set the tag before decrypting + */ + EVP_CIPHER_CTX_ctrl(c->ctx, EVP_CTRL_AEAD_SET_TAG, c->tag_len, + buf + (*enc_len - c->tag_len)); + EVP_Cipher(c->ctx, buf, buf, *enc_len - c->tag_len); + + /* + * Check the tag + */ + if (EVP_Cipher(c->ctx, NULL, NULL, 0)) { + return (srtp_err_status_auth_fail); + } + + /* + * Reduce the buffer size by the tag length since the tag + * is not part of the original payload + */ + *enc_len -= c->tag_len; + + return (srtp_err_status_ok); +} + +/* + * Name of this crypto engine + */ +static const char srtp_chacha20_poly1305_openssl_description[] = + "CHACHA20 POLY1305 using openssl"; + +/* clang-format off */ +static const uint8_t srtp_chacha20_poly1305_test_case_1_key[SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT] = { + 0xfe, 0xff, 0xe9, 0x92, 0x86, 0x65, 0x73, 0x1c, + 0xa5, 0x59, 0x09, 0xc5, 0x54, 0x66, 0x93, 0x1c, + 0xaf, 0xf5, 0x26, 0x9a, 0x21, 0xd5, 0x14, 0xb2, + 0x6d, 0x6a, 0x8f, 0x94, 0x67, 0x30, 0x83, 0x08, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, +}; +/* clang-format on */ + +/* clang-format off */ +static uint8_t srtp_chacha20_poly1305_test_case_1_iv[12] = { + 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad, + 0xde, 0xca, 0xf8, 0x88 +}; +/* clang-format on */ + +/* clang-format off */ +static const uint8_t srtp_chacha20_poly1305_test_case_1_plaintext[60] = { + 0xd9, 0x31, 0x32, 0x25, 0xf8, 0x84, 0x06, 0xe5, + 0xa5, 0x59, 0x09, 0xc5, 0xaf, 0xf5, 0x26, 0x9a, + 0x86, 0xa7, 0xa9, 0x53, 0x15, 0x34, 0xf7, 0xda, + 0x2e, 0x4c, 0x30, 0x3d, 0x8a, 0x31, 0x8a, 0x72, + 0x1c, 0x3c, 0x0c, 0x95, 0x95, 0x68, 0x09, 0x53, + 0x2f, 0xcf, 0x0e, 0x24, 0x49, 0xa6, 0xb5, 0x25, + 0xb1, 0x6a, 0xed, 0xf5, 0xaa, 0x0d, 0xe6, 0x57, + 0xba, 0x63, 0x7b, 0x39 +}; +/* clang-format on */ + +/* clang-format off */ +static const uint8_t srtp_chacha20_poly1305_test_case_1_aad[20] = { + 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, + 0xfe, 0xed, 0xfa, 0xce, 0xde, 0xad, 0xbe, 0xef, + 0xab, 0xad, 0xda, 0xd2 +}; +/* clang-format on */ + +/* clang-format off */ +static const uint8_t srtp_chacha20_poly1305_test_case_1_ciphertext[76] = { + 0xf1, 0xe9, 0x8f, 0xa5, 0x30, 0xce, 0x2c, 0x53, + 0x3c, 0x54, 0x31, 0xdb, 0x1f, 0xe4, 0x5b, 0xcb, + 0x88, 0xfd, 0x48, 0x7c, 0xd2, 0x28, 0xab, 0x0b, + 0x2f, 0x62, 0xd6, 0xc8, 0xaa, 0xc4, 0x6f, 0xd1, + 0x7f, 0xbf, 0xca, 0x17, 0xf9, 0x0d, 0x2c, 0x86, + 0x85, 0x1d, 0xf1, 0x7a, 0xfc, 0x10, 0xef, 0xa7, + 0x87, 0x92, 0x41, 0x66, 0x79, 0x63, 0xde, 0x6f, + 0x15, 0xc3, 0x39, 0x9b, + /* the last 16 bytes are the tag */ + 0x55, 0xc9, 0xa5, 0x8e, 0xe4, 0x4a, 0xc3, 0xfe, + 0x86, 0x5c, 0xb5, 0xe3, 0x2e, 0x25, 0x16, 0xf9, +}; +/* clang-format on */ + +static const srtp_cipher_test_case_t srtp_chacha20_poly1305_test_case_1a = { + SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT, /* octets in key */ + srtp_chacha20_poly1305_test_case_1_key, /* key */ + srtp_chacha20_poly1305_test_case_1_iv, /* packet index */ + 60, /* octets in plaintext */ + srtp_chacha20_poly1305_test_case_1_plaintext, /* plaintext */ + 68, /* octets in ciphertext */ + srtp_chacha20_poly1305_test_case_1_ciphertext, /* ciphertext + tag */ + 20, /* octets in AAD */ + srtp_chacha20_poly1305_test_case_1_aad, /* AAD */ + AEAD_AUTH_TAG_LEN_8, /* */ + NULL /* pointer to next testcase */ +}; + +static const srtp_cipher_test_case_t srtp_chacha20_poly1305_test_case_1 = { + SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT, /* octets in key */ + srtp_chacha20_poly1305_test_case_1_key, /* key */ + srtp_chacha20_poly1305_test_case_1_iv, /* packet index */ + 60, /* octets in plaintext */ + srtp_chacha20_poly1305_test_case_1_plaintext, /* plaintext */ + 76, /* octets in ciphertext */ + srtp_chacha20_poly1305_test_case_1_ciphertext, /* ciphertext + tag */ + 20, /* octets in AAD */ + srtp_chacha20_poly1305_test_case_1_aad, /* AAD */ + AEAD_AUTH_TAG_LEN, /* */ + &srtp_chacha20_poly1305_test_case_1a /* pointer to next testcase */ +}; + +/* + * This is the vector function table for this crypto engine. + */ +const srtp_cipher_type_t srtp_chacha20_poly1305 = { + srtp_chacha20_poly1305_openssl_alloc, + srtp_chacha20_poly1305_openssl_dealloc, + srtp_chacha20_poly1305_openssl_context_init, + srtp_chacha20_poly1305_openssl_set_aad, + srtp_chacha20_poly1305_openssl_encrypt, + srtp_chacha20_poly1305_openssl_decrypt, + srtp_chacha20_poly1305_openssl_set_iv, + srtp_chacha20_poly1305_openssl_get_tag, + srtp_chacha20_poly1305_openssl_description, + &srtp_chacha20_poly1305_test_case_1, + SRTP_CHACHA20_POLY1305 +}; diff --git a/crypto/cipher/cipher.c b/crypto/cipher/cipher.c index 88a4a7199..96809da89 100644 --- a/crypto/cipher/cipher.c +++ b/crypto/cipher/cipher.c @@ -288,7 +288,8 @@ srtp_err_status_t srtp_cipher_type_test( } if (c->algorithm == SRTP_AES_GCM_128 || - c->algorithm == SRTP_AES_GCM_256) { + c->algorithm == SRTP_AES_GCM_256 || + c->algorithm == SRTP_CHACHA20_POLY1305) { debug_print(srtp_mod_cipher, "IV: %s", srtp_octet_string_hex_string(test_case->idx, 12)); @@ -315,7 +316,8 @@ srtp_err_status_t srtp_cipher_type_test( } if (c->algorithm == SRTP_AES_GCM_128 || - c->algorithm == SRTP_AES_GCM_256) { + c->algorithm == SRTP_AES_GCM_256 || + c->algorithm == SRTP_CHACHA20_POLY1305) { /* * Get the GCM tag */ @@ -392,7 +394,8 @@ srtp_err_status_t srtp_cipher_type_test( } if (c->algorithm == SRTP_AES_GCM_128 || - c->algorithm == SRTP_AES_GCM_256) { + c->algorithm == SRTP_AES_GCM_256 || + c->algorithm == SRTP_CHACHA20_POLY1305) { /* * Set the AAD */ @@ -514,7 +517,8 @@ srtp_err_status_t srtp_cipher_type_test( } if (c->algorithm == SRTP_AES_GCM_128 || - c->algorithm == SRTP_AES_GCM_256) { + c->algorithm == SRTP_AES_GCM_256 || + c->algorithm == SRTP_CHACHA20_POLY1305) { /* * Set the AAD */ @@ -537,7 +541,8 @@ srtp_err_status_t srtp_cipher_type_test( return status; } if (c->algorithm == SRTP_AES_GCM_128 || - c->algorithm == SRTP_AES_GCM_256) { + c->algorithm == SRTP_AES_GCM_256 || + c->algorithm == SRTP_CHACHA20_POLY1305) { /* * Get the GCM tag */ @@ -567,7 +572,8 @@ srtp_err_status_t srtp_cipher_type_test( return status; } if (c->algorithm == SRTP_AES_GCM_128 || - c->algorithm == SRTP_AES_GCM_256) { + c->algorithm == SRTP_AES_GCM_256 || + c->algorithm == SRTP_CHACHA20_POLY1305) { /* * Set the AAD */ diff --git a/crypto/include/chacha20_poly1305.h b/crypto/include/chacha20_poly1305.h new file mode 100644 index 000000000..f5c5cc2fa --- /dev/null +++ b/crypto/include/chacha20_poly1305.h @@ -0,0 +1,86 @@ +/* + * chacha20_poly1305.h + * + * Header for CHACHA20 POLY1305. + * + * + */ +/* + * + * Copyright (c) 2013-2017, Cisco Systems, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * + * Neither the name of the Cisco Systems, Inc. nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED + * OF THE POSSIBILITY OF SUCH DAMAGE. + * + */ + +#ifndef CHACHA20_POLY1305_H +#define CHACHA20_POLY1305_H + +#include "cipher.h" +#include "srtp.h" +#include "datatypes.h" + +#ifdef OPENSSL + +#include + +typedef struct { + int key_size; + int tag_len; + EVP_CIPHER_CTX *ctx; + srtp_cipher_direction_t dir; +} srtp_chacha20_poly1305_ctx_t; + +#endif /* OPENSSL */ + +#ifdef NSS + +#include +#include + +#define MAX_AD_SIZE 2048 + +typedef struct { + int key_size; + int tag_size; + srtp_cipher_direction_t dir; + NSSInitContext *nss; + PK11SymKey *key; + uint8_t iv[12]; + uint8_t aad[MAX_AD_SIZE]; + int aad_size; + CK_NSS_AEAD_PARAMS params; + uint8_t tag[16]; +} srtp_chacha20_poly1305_ctx_t; + +#endif /* NSS */ + +#endif /* CHACHA20_POLY1305_H */ diff --git a/crypto/include/cipher_types.h b/crypto/include/cipher_types.h index 18f0328fb..80b5a97ef 100644 --- a/crypto/include/cipher_types.h +++ b/crypto/include/cipher_types.h @@ -52,7 +52,9 @@ extern const srtp_cipher_type_t srtp_aes_icm_192; extern const srtp_cipher_type_t srtp_aes_gcm_128; extern const srtp_cipher_type_t srtp_aes_gcm_256; #endif - +#ifdef CHAPOLY +extern const srtp_cipher_type_t srtp_chacha20_poly1305; +#endif /* * auth func types that can be included in the kernel */ @@ -73,9 +75,11 @@ extern srtp_debug_module_t srtp_mod_alloc; extern srtp_debug_module_t srtp_mod_aes_icm; #ifdef OPENSSL extern srtp_debug_module_t srtp_mod_aes_gcm; +extern srtp_debug_module_t srtp_mod_chacha20_poly1305; #endif #ifdef NSS extern srtp_debug_module_t srtp_mod_aes_gcm; +extern srtp_debug_module_t srtp_mod_chacha20_poly1305; #endif /* debug modules for auth types */ diff --git a/crypto/include/crypto_types.h b/crypto/include/crypto_types.h index 7fd3178b0..56bbbc11d 100644 --- a/crypto/include/crypto_types.h +++ b/crypto/include/crypto_types.h @@ -97,6 +97,13 @@ */ #define SRTP_AES_GCM_256 7 +/* + * CHACHA20-POLY1305 + * + * This cipher uses a 32-octet key. + */ +#define SRTP_CHACHA20_POLY1305 8 + /* * The null authentication function performs no authentication. * diff --git a/crypto/kernel/crypto_kernel.c b/crypto/kernel/crypto_kernel.c index df6af7da8..58fa6a2ca 100644 --- a/crypto/kernel/crypto_kernel.c +++ b/crypto/kernel/crypto_kernel.c @@ -151,6 +151,13 @@ srtp_err_status_t srtp_crypto_kernel_init() return status; } #endif +#ifdef CHAPOLY + status = srtp_crypto_kernel_load_cipher_type(&srtp_chacha20_poly1305, + SRTP_CHACHA20_POLY1305); + if (status) { + return status; + } +#endif /* load auth func types */ status = srtp_crypto_kernel_load_auth_type(&srtp_null_auth, SRTP_NULL_AUTH); diff --git a/crypto/test/kernel_driver.c b/crypto/test/kernel_driver.c index d29405a97..793721f41 100644 --- a/crypto/test/kernel_driver.c +++ b/crypto/test/kernel_driver.c @@ -50,6 +50,26 @@ #include "getopt_s.h" #include "crypto_kernel.h" +void log_handler(srtp_log_level_t level, const char *msg, void *data) +{ + char level_char = '?'; + switch (level) { + case srtp_log_level_error: + level_char = 'e'; + break; + case srtp_log_level_warning: + level_char = 'w'; + break; + case srtp_log_level_info: + level_char = 'i'; + break; + case srtp_log_level_debug: + level_char = 'd'; + break; + } + printf("SRTP-LOG [%c]: %s\n", level_char, msg); +} + void usage(char *prog_name) { printf("usage: %s [ -v ][ -d debug_module ]*\n", prog_name); @@ -65,10 +85,12 @@ int main(int argc, char *argv[]) if (argc == 1) usage(argv[0]); + status = srtp_install_log_handler(log_handler, NULL); + /* initialize kernel - we need to do this before anything else */ status = srtp_crypto_kernel_init(); if (status) { - printf("error: srtp_crypto_kernel init failed\n"); + printf("error: srtp_crypto_kernel init failed %d\n", status); exit(1); } printf("srtp_crypto_kernel successfully initalized\n"); diff --git a/include/srtp.h b/include/srtp.h index 1fdd6c3af..0a552b27e 100644 --- a/include/srtp.h +++ b/include/srtp.h @@ -113,6 +113,8 @@ extern "C" { #define SRTP_AES_192_KEY_LEN 24 #define SRTP_AES_256_KEY_LEN 32 +#define SRTP_CHACHA20_POLY1305_KEY_LEN 32 + #define SRTP_AES_ICM_128_KEY_LEN_WSALT (SRTP_SALT_LEN + SRTP_AES_128_KEY_LEN) #define SRTP_AES_ICM_192_KEY_LEN_WSALT (SRTP_SALT_LEN + SRTP_AES_192_KEY_LEN) #define SRTP_AES_ICM_256_KEY_LEN_WSALT (SRTP_SALT_LEN + SRTP_AES_256_KEY_LEN) @@ -124,6 +126,9 @@ extern "C" { #define SRTP_AES_GCM_256_KEY_LEN_WSALT \ (SRTP_AEAD_SALT_LEN + SRTP_AES_256_KEY_LEN) +#define SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT \ + (SRTP_AEAD_SALT_LEN + SRTP_CHACHA20_POLY1305_KEY_LEN) + /** * @brief A srtp_cipher_type_id_t is an identifier for a particular cipher * type. @@ -1147,6 +1152,11 @@ void srtp_crypto_policy_set_aes_gcm_128_16_auth(srtp_crypto_policy_t *p); */ void srtp_crypto_policy_set_aes_gcm_256_16_auth(srtp_crypto_policy_t *p); +void srtp_crypto_policy_set_chacha20_poly1305_8_auth(srtp_crypto_policy_t *p); +void srtp_crypto_policy_set_chacha20_poly1305_8_only_auth( + srtp_crypto_policy_t *p); +void srtp_crypto_policy_set_chacha20_poly1305_16_auth(srtp_crypto_policy_t *p); + /** * @brief srtp_dealloc() deallocates storage for an SRTP session * context. @@ -1180,6 +1190,8 @@ typedef enum { srtp_profile_null_sha1_32 = 6, srtp_profile_aead_aes_128_gcm = 7, srtp_profile_aead_aes_256_gcm = 8, + + srtp_profile_aead_chacha20_poly1305 = 16, // TODO unregister } srtp_profile_t; /** diff --git a/srtp/srtp.c b/srtp/srtp.c index 5d9da7b2e..26adb66d2 100644 --- a/srtp/srtp.c +++ b/srtp/srtp.c @@ -55,6 +55,10 @@ #include "aes_gcm.h" /* for AES GCM mode */ #endif +#ifdef CHAPOLY +#include "chacha20_poly1305.h" +#endif + #ifdef OPENSSL_KDF #include #include "aes_icm_ext.h" @@ -408,6 +412,7 @@ srtp_err_status_t srtp_stream_alloc(srtp_stream_ctx_t **str_ptr, enc_xtn_hdr_cipher_type = SRTP_AES_ICM_256; enc_xtn_hdr_cipher_key_len = SRTP_AES_ICM_256_KEY_LEN_WSALT; break; + case SRTP_CHACHA20_POLY1305: // TODO ??? default: enc_xtn_hdr_cipher_type = p->rtp.cipher_type; enc_xtn_hdr_cipher_key_len = p->rtp.cipher_key_len; @@ -767,6 +772,9 @@ static inline int base_key_length(const srtp_cipher_type_t *cipher, case SRTP_AES_GCM_256: return key_length - SRTP_AEAD_SALT_LEN; break; + case SRTP_CHACHA20_POLY1305: + return key_length - SRTP_AEAD_SALT_LEN; + break; default: return key_length; break; @@ -1019,6 +1027,7 @@ srtp_err_status_t srtp_stream_init_keys(srtp_stream_ctx_t *srtp, */ rtp_xtn_hdr_salt_len = rtp_salt_len; break; + case SRTP_CHACHA20_POLY1305: // TODO ??? default: /* zeroize temp buffer */ octet_string_set_to_zero(tmp_key, MAX_SRTP_KEY_LEN); @@ -1545,7 +1554,9 @@ srtp_session_keys_t *srtp_get_session_keys(srtp_stream_ctx_t *stream, // Determine the authentication tag size if (stream->session_keys[0].rtp_cipher->algorithm == SRTP_AES_GCM_128 || - stream->session_keys[0].rtp_cipher->algorithm == SRTP_AES_GCM_256) { + stream->session_keys[0].rtp_cipher->algorithm == SRTP_AES_GCM_256 || + stream->session_keys[0].rtp_cipher->algorithm == + SRTP_CHACHA20_POLY1305) { tag_len = 0; } else { tag_len = srtp_auth_get_tag_length(stream->session_keys[0].rtp_auth); @@ -2133,7 +2144,8 @@ srtp_err_status_t srtp_protect_mki(srtp_ctx_t *ctx, * the request to our AEAD handler. */ if (session_keys->rtp_cipher->algorithm == SRTP_AES_GCM_128 || - session_keys->rtp_cipher->algorithm == SRTP_AES_GCM_256) { + session_keys->rtp_cipher->algorithm == SRTP_AES_GCM_256 || + session_keys->rtp_cipher->algorithm == SRTP_CHACHA20_POLY1305) { return srtp_protect_aead(ctx, stream, rtp_hdr, (unsigned int *)pkt_octet_len, session_keys, use_mki); @@ -2480,7 +2492,8 @@ srtp_err_status_t srtp_unprotect_mki(srtp_ctx_t *ctx, * the request to our AEAD handler. */ if (session_keys->rtp_cipher->algorithm == SRTP_AES_GCM_128 || - session_keys->rtp_cipher->algorithm == SRTP_AES_GCM_256) { + session_keys->rtp_cipher->algorithm == SRTP_AES_GCM_256 || + session_keys->rtp_cipher->algorithm == SRTP_CHACHA20_POLY1305) { return srtp_unprotect_aead(ctx, stream, delta, est, srtp_hdr, (unsigned int *)pkt_octet_len, session_keys, mki_size); @@ -3411,6 +3424,41 @@ void srtp_crypto_policy_set_aes_gcm_256_16_auth(srtp_crypto_policy_t *p) #endif +#ifdef CHAPOLY + +void srtp_crypto_policy_set_chacha20_poly1305_8_auth(srtp_crypto_policy_t *p) +{ + p->cipher_type = SRTP_CHACHA20_POLY1305; + p->cipher_key_len = SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT; + p->auth_type = SRTP_NULL_AUTH; /* handles the auth for us */ + p->auth_key_len = 0; + p->auth_tag_len = 8; /* 8 octet tag length */ + p->sec_serv = sec_serv_conf_and_auth; +} + +void srtp_crypto_policy_set_chacha20_poly1305_8_only_auth( + srtp_crypto_policy_t *p) +{ + p->cipher_type = SRTP_CHACHA20_POLY1305; + p->cipher_key_len = SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT; + p->auth_type = SRTP_NULL_AUTH; /* handles the auth for us */ + p->auth_key_len = 0; + p->auth_tag_len = 8; /* 8 octet tag length */ + p->sec_serv = sec_serv_auth; /* This only applies to RTCP */ +} + +void srtp_crypto_policy_set_chacha20_poly1305_16_auth(srtp_crypto_policy_t *p) +{ + p->cipher_type = SRTP_CHACHA20_POLY1305; + p->cipher_key_len = SRTP_CHACHA20_POLY1305_KEY_LEN_WSALT; + p->auth_type = SRTP_NULL_AUTH; /* handles the auth for us */ + p->auth_key_len = 0; + p->auth_tag_len = 16; /* 16 octet tag length */ + p->sec_serv = sec_serv_conf_and_auth; +} + +#endif + /* * secure rtcp functions */ @@ -3940,7 +3988,8 @@ srtp_err_status_t srtp_protect_rtcp_mki(srtp_t ctx, * the request to our AEAD handler. */ if (session_keys->rtp_cipher->algorithm == SRTP_AES_GCM_128 || - session_keys->rtp_cipher->algorithm == SRTP_AES_GCM_256) { + session_keys->rtp_cipher->algorithm == SRTP_AES_GCM_256 || + session_keys->rtp_cipher->algorithm == SRTP_CHACHA20_POLY1305) { return srtp_protect_rtcp_aead(ctx, stream, rtcp_hdr, (unsigned int *)pkt_octet_len, session_keys, use_mki); @@ -4196,7 +4245,8 @@ srtp_err_status_t srtp_unprotect_rtcp_mki(srtp_t ctx, * the request to our AEAD handler. */ if (session_keys->rtp_cipher->algorithm == SRTP_AES_GCM_128 || - session_keys->rtp_cipher->algorithm == SRTP_AES_GCM_256) { + session_keys->rtp_cipher->algorithm == SRTP_AES_GCM_256 || + session_keys->rtp_cipher->algorithm == SRTP_CHACHA20_POLY1305) { return srtp_unprotect_rtcp_aead(ctx, stream, srtcp_hdr, (unsigned int *)pkt_octet_len, session_keys, mki_size); @@ -4443,6 +4493,11 @@ srtp_err_status_t srtp_crypto_policy_set_from_profile_for_rtp( case srtp_profile_aead_aes_256_gcm: srtp_crypto_policy_set_aes_gcm_256_16_auth(policy); break; +#endif +#ifdef CHAPOLY + case srtp_profile_aead_chacha20_poly1305: + srtp_crypto_policy_set_chacha20_poly1305_16_auth(policy); + break; #endif /* the following profiles are not (yet) supported */ case srtp_profile_null_sha1_32: @@ -4477,6 +4532,11 @@ srtp_err_status_t srtp_crypto_policy_set_from_profile_for_rtcp( case srtp_profile_aead_aes_256_gcm: srtp_crypto_policy_set_aes_gcm_256_16_auth(policy); break; +#endif +#ifdef CHAPOLY + case srtp_profile_aead_chacha20_poly1305: + srtp_crypto_policy_set_chacha20_poly1305_16_auth(policy); + break; #endif /* the following profiles are not (yet) supported */ case srtp_profile_null_sha1_32: @@ -4513,6 +4573,9 @@ unsigned int srtp_profile_get_master_key_length(srtp_profile_t profile) case srtp_profile_aead_aes_256_gcm: return SRTP_AES_256_KEY_LEN; break; + case srtp_profile_aead_chacha20_poly1305: + return SRTP_CHACHA20_POLY1305_KEY_LEN; + break; /* the following profiles are not (yet) supported */ case srtp_profile_null_sha1_32: default: