[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH 2/2] libgcrypt: Implement chacha20-poly1305@xxxxxxxxxxx cipher using libgcrypt
[Thread Prev] | [Thread Next]
- Subject: [PATCH 2/2] libgcrypt: Implement chacha20-poly1305@xxxxxxxxxxx cipher using libgcrypt
- From: Jussi Kivilinna <jussi.kivilinna@xxxxxx>
- Reply-to: libssh@xxxxxxxxxx
- Date: Sat, 7 Dec 2019 19:17:57 +0200
- To: libssh@xxxxxxxxxx
- Cc: Jussi Kivilinna <jussi.kivilinna@xxxxxx>
Libgcrypt has supported ChaCha20 and Poly1305 since 1.7.0 version and
provides fast assembler implementations.
Signed-off-by: Jussi Kivilinna <jussi.kivilinna@xxxxxx>
---
ConfigureChecks.cmake | 3 +
config.h.cmake | 3 +
src/libgcrypt.c | 252 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 258 insertions(+)
diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake
index c8bb2aa0..a99278f7 100644
--- a/ConfigureChecks.cmake
+++ b/ConfigureChecks.cmake
@@ -278,6 +278,9 @@ if (GCRYPT_FOUND)
set(HAVE_GCRYPT_ECC 1)
set(HAVE_ECC 1)
endif (GCRYPT_VERSION VERSION_GREATER "1.4.6")
+ if (NOT GCRYPT_VERSION VERSION_LESS "1.7.0")
+ set(HAVE_GCRYPT_CHACHA_POLY 1)
+ endif (NOT GCRYPT_VERSION VERSION_LESS "1.7.0")
endif (GCRYPT_FOUND)
if (MBEDTLS_FOUND)
diff --git a/config.h.cmake b/config.h.cmake
index 98a72f65..847fc579 100644
--- a/config.h.cmake
+++ b/config.h.cmake
@@ -103,6 +103,9 @@
/* Define to 1 if you have OpenSSL with X25519 support */
#cmakedefine HAVE_OPENSSL_X25519 1
+/* Define to 1 if you have gcrypt with ChaCha20/Poly1305 support */
+#cmakedefine HAVE_GCRYPT_CHACHA_POLY 1
+
/*************************** FUNCTIONS ***************************/
/* Define to 1 if you have the `EVP_aes128_ctr' function. */
diff --git a/src/libgcrypt.c b/src/libgcrypt.c
index df192392..72f6661c 100644
--- a/src/libgcrypt.c
+++ b/src/libgcrypt.c
@@ -36,6 +36,34 @@
#ifdef HAVE_LIBGCRYPT
#include <gcrypt.h>
+#ifdef HAVE_GCRYPT_CHACHA_POLY
+
+#define CHACHA20_BLOCKSIZE 64
+#define CHACHA20_KEYLEN 32
+
+#define POLY1305_TAGLEN 16
+#define POLY1305_KEYLEN 32
+
+struct chacha20_poly1305_keysched {
+ int initialized;
+ /* cipher handle used for encrypting the length field */
+ gcry_cipher_hd_t main_hd;
+ /* cipher handle used for encrypting the packets */
+ gcry_cipher_hd_t header_hd;
+ /* mac handle used for authenticating the packets */
+ gcry_mac_hd_t mac_hd;
+};
+
+#pragma pack(push, 1)
+struct ssh_packet_header {
+ uint32_t length;
+ uint8_t payload[];
+};
+#pragma pack(pop)
+
+static const uint8_t zero_block[CHACHA20_BLOCKSIZE] = {0};
+#endif /* HAVE_GCRYPT_CHACHA_POLY */
+
static int libgcrypt_initialized = 0;
static int alloc_key(struct ssh_cipher_struct *cipher) {
@@ -558,6 +586,210 @@ static void des3_decrypt(struct ssh_cipher_struct *cipher, void *in,
gcry_cipher_decrypt(cipher->key[0], out, len, in, len);
}
+#ifdef HAVE_GCRYPT_CHACHA_POLY
+static void chacha20_cleanup(struct ssh_cipher_struct *cipher) {
+ struct chacha20_poly1305_keysched *ctx;
+
+ if (cipher->chacha20_schedule == NULL)
+ return;
+
+ ctx = cipher->chacha20_schedule;
+
+ if (ctx->initialized) {
+ gcry_cipher_close(ctx->main_hd);
+ gcry_cipher_close(ctx->header_hd);
+ gcry_mac_close(ctx->mac_hd);
+ ctx->initialized = 0;
+ }
+
+ SAFE_FREE(cipher->chacha20_schedule);
+}
+
+static int chacha20_set_encrypt_key(struct ssh_cipher_struct *cipher,
+ void *key,
+ void *IV)
+{
+ struct chacha20_poly1305_keysched *ctx;
+ uint8_t *u8key = key;
+ (void)IV;
+
+ if (cipher->chacha20_schedule == NULL) {
+ ctx = malloc(sizeof *ctx);
+ if (ctx == NULL){
+ return -1;
+ }
+ memset(ctx, 0, sizeof *ctx);
+ cipher->chacha20_schedule = ctx;
+ } else {
+ ctx = cipher->chacha20_schedule;
+ }
+
+ if (!ctx->initialized) {
+ /* Open cipher/mac handles. */
+ if (gcry_cipher_open(&ctx->main_hd, GCRY_CIPHER_CHACHA20,
+ GCRY_CIPHER_MODE_STREAM, 0)) {
+ SAFE_FREE(cipher->chacha20_schedule);
+ return -1;
+ }
+ if (gcry_cipher_open(&ctx->header_hd, GCRY_CIPHER_CHACHA20,
+ GCRY_CIPHER_MODE_STREAM, 0)) {
+ gcry_cipher_close(ctx->main_hd);
+ SAFE_FREE(cipher->chacha20_schedule);
+ return -1;
+ }
+ if (gcry_mac_open(&ctx->mac_hd, GCRY_MAC_POLY1305, 0, NULL)) {
+ gcry_cipher_close(ctx->main_hd);
+ gcry_cipher_close(ctx->header_hd);
+ SAFE_FREE(cipher->chacha20_schedule);
+ return -1;
+ }
+
+ ctx->initialized = 1;
+ }
+
+ if (gcry_cipher_setkey(ctx->main_hd, u8key, CHACHA20_KEYLEN) ||
+ gcry_cipher_setkey(ctx->header_hd, u8key + CHACHA20_KEYLEN,
+ CHACHA20_KEYLEN)) {
+ chacha20_cleanup(cipher);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void chacha20_poly1305_aead_encrypt(struct ssh_cipher_struct *cipher,
+ void *in,
+ void *out,
+ size_t len,
+ uint8_t *tag,
+ uint64_t seq)
+{
+ struct ssh_packet_header *in_packet = in, *out_packet = out;
+ struct chacha20_poly1305_keysched *ctx = cipher->chacha20_schedule;
+ uint8_t poly_key[CHACHA20_BLOCKSIZE];
+ size_t taglen = POLY1305_TAGLEN;
+
+ seq = htonll(seq);
+ /* step 1, prepare the poly1305 key */
+ if (gcry_cipher_setiv(ctx->main_hd, (uint8_t *)&seq, sizeof(seq)))
+ goto out;
+ /* Output full ChaCha block so that counter increases by one for
+ * payload encryption step. */
+ if (gcry_cipher_encrypt(ctx->main_hd,
+ poly_key,
+ sizeof(poly_key),
+ zero_block,
+ sizeof(zero_block)))
+ goto out;
+ if (gcry_mac_setkey(ctx->mac_hd, poly_key, POLY1305_KEYLEN))
+ goto out;
+
+ /* step 2, encrypt length field */
+ if (gcry_cipher_setiv(ctx->header_hd, (uint8_t *)&seq, sizeof(seq)))
+ goto out;
+ if (gcry_cipher_encrypt(ctx->header_hd,
+ (uint8_t *)&out_packet->length,
+ sizeof(uint32_t),
+ (uint8_t *)&in_packet->length,
+ sizeof(uint32_t)))
+ goto out;
+
+ /* step 3, encrypt packet payload (main_hd counter == 1) */
+ if (gcry_cipher_encrypt(ctx->main_hd,
+ out_packet->payload,
+ len - sizeof(uint32_t),
+ in_packet->payload,
+ len - sizeof(uint32_t)))
+ goto out;
+
+ /* step 4, compute the MAC */
+ if (gcry_mac_write(ctx->mac_hd, (uint8_t *)out_packet, len))
+ goto out;
+ if (gcry_mac_read(ctx->mac_hd, tag, &taglen))
+ goto out;
+
+out:
+ explicit_bzero(poly_key, sizeof(poly_key));
+}
+
+static int chacha20_poly1305_aead_decrypt_length(
+ struct ssh_cipher_struct *cipher,
+ void *in,
+ uint8_t *out,
+ size_t len,
+ uint64_t seq)
+{
+ struct chacha20_poly1305_keysched *ctx = cipher->chacha20_schedule;
+
+ if (len < sizeof(uint32_t)) {
+ return SSH_ERROR;
+ }
+ seq = htonll(seq);
+
+ if (gcry_cipher_setiv(ctx->header_hd, (uint8_t *)&seq, sizeof(seq)))
+ return SSH_ERROR;
+ if (gcry_cipher_decrypt(ctx->header_hd,
+ out,
+ sizeof(uint32_t),
+ in,
+ sizeof(uint32_t)))
+ return SSH_ERROR;
+
+ return SSH_OK;
+}
+
+static int chacha20_poly1305_aead_decrypt(struct ssh_cipher_struct *cipher,
+ void *complete_packet,
+ uint8_t *out,
+ size_t encrypted_size,
+ uint64_t seq)
+{
+ struct chacha20_poly1305_keysched *ctx = cipher->chacha20_schedule;
+ uint8_t *mac = (uint8_t *)complete_packet + sizeof(uint32_t) +
+ encrypted_size;
+ uint8_t poly_key[CHACHA20_BLOCKSIZE];
+ int ret = SSH_ERROR;
+
+ seq = htonll(seq);
+ /* step 1, prepare the poly1305 key */
+ if (gcry_cipher_setiv(ctx->main_hd, (uint8_t *)&seq, sizeof(seq)))
+ goto out;
+ /* Output full ChaCha block so that counter increases by one for
+ * decryption step. */
+ if (gcry_cipher_encrypt(ctx->main_hd,
+ poly_key,
+ sizeof(poly_key),
+ zero_block,
+ sizeof(zero_block)))
+ goto out;
+ if (gcry_mac_setkey(ctx->mac_hd, poly_key, POLY1305_KEYLEN))
+ goto out;
+
+ /* step 2, check MAC */
+ if (gcry_mac_write(ctx->mac_hd, (uint8_t *)complete_packet,
+ encrypted_size + sizeof(uint32_t)))
+ goto out;
+ if (gcry_mac_verify(ctx->mac_hd, mac, POLY1305_TAGLEN)) {
+ SSH_LOG(SSH_LOG_PACKET,"poly1305 verify error");
+ goto out;
+ }
+
+ /* step 3, decrypt packet payload (main_hd counter == 1) */
+ if (gcry_cipher_decrypt(ctx->main_hd,
+ out,
+ encrypted_size,
+ (uint8_t *)complete_packet + sizeof(uint32_t),
+ encrypted_size))
+ goto out;
+
+ ret = SSH_OK;
+
+out:
+ explicit_bzero(poly_key, sizeof(poly_key));
+ return ret;
+}
+#endif /* HAVE_GCRYPT_CHACHA_POLY */
+
/* the table of supported ciphers */
static struct ssh_cipher_struct ssh_ciphertab[] = {
#ifdef WITH_BLOWFISH_CIPHER
@@ -679,7 +911,23 @@ static struct ssh_cipher_struct ssh_ciphertab[] = {
.decrypt = des3_decrypt
},
{
+#ifdef HAVE_GCRYPT_CHACHA_POLY
+ .ciphertype = SSH_AEAD_CHACHA20_POLY1305,
+ .name = "chacha20-poly1305@xxxxxxxxxxx",
+ .blocksize = 8,
+ .lenfield_blocksize = 4,
+ .keylen = sizeof(struct chacha20_poly1305_keysched),
+ .keysize = 2 * CHACHA20_KEYLEN * 8,
+ .tag_size = POLY1305_TAGLEN,
+ .set_encrypt_key = chacha20_set_encrypt_key,
+ .set_decrypt_key = chacha20_set_encrypt_key,
+ .aead_encrypt = chacha20_poly1305_aead_encrypt,
+ .aead_decrypt_length = chacha20_poly1305_aead_decrypt_length,
+ .aead_decrypt = chacha20_poly1305_aead_decrypt,
+ .cleanup = chacha20_cleanup
+#else
.name = "chacha20-poly1305@xxxxxxxxxxx"
+#endif
},
{
.name = NULL,
@@ -759,6 +1007,8 @@ int ssh_crypto_init(void)
{
size_t i;
+ (void)i;
+
if (libgcrypt_initialized) {
return SSH_OK;
}
@@ -776,6 +1026,7 @@ int ssh_crypto_init(void)
/* Re-enable warning */
gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
+#ifndef HAVE_GCRYPT_CHACHA_POLY
for (i = 0; ssh_ciphertab[i].name != NULL; i++) {
int cmp;
cmp = strcmp(ssh_ciphertab[i].name, "chacha20-poly1305@xxxxxxxxxxx");
@@ -786,6 +1037,7 @@ int ssh_crypto_init(void)
break;
}
}
+#endif
libgcrypt_initialized = 1;
--
2.20.1
| Re: [PATCH 2/2] libgcrypt: Implement chacha20-poly1305@xxxxxxxxxxx cipher using libgcrypt | Jakub Jelen <jjelen@xxxxxxxxxx> |
| [PATCH 1/2] tests: add crypto unittest for chacha20poly1305 | Jussi Kivilinna <jussi.kivilinna@xxxxxx> |