Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F106115485
D32652.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
29 KB
Referenced Files
None
Subscribers
None
D32652.diff
View Options
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -12,6 +12,7 @@
ATF_TESTS_C+= kern_descrip_test
ATF_TESTS_C+= fdgrowtable_test
ATF_TESTS_C+= kill_zombie
+ATF_TESTS_C+= ktls_test
ATF_TESTS_C+= ptrace_test
TEST_METADATA.ptrace_test+= timeout="15"
ATF_TESTS_C+= reaper
@@ -46,6 +47,7 @@
LIBADD.ptrace_test+= pthread
LIBADD.unix_seqpacket_test+= pthread
LIBADD.kcov+= pthread
+LIBADD.ktls_test+= crypto
LIBADD.sendfile_helper+= pthread
LIBADD.fdgrowtable_test+= util pthread kvm procstat
diff --git a/tests/sys/kern/ktls_test.c b/tests/sys/kern/ktls_test.c
new file mode 100644
--- /dev/null
+++ b/tests/sys/kern/ktls_test.c
@@ -0,0 +1,1033 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2021 Netflix Inc.
+ * Written by: John Baldwin <jhb@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ */
+
+#include <sys/types.h>
+#include <sys/endian.h>
+#include <sys/event.h>
+#include <sys/ktls.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <crypto/cryptodev.h>
+#include <assert.h>
+#include <err.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <atf-c.h>
+
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+
+static void
+require_ktls(void)
+{
+ size_t len;
+ bool enable;
+
+ len = sizeof(enable);
+ if (sysctlbyname("kern.ipc.tls.enable", &enable, &len, NULL, 0) == -1) {
+ if (errno == ENOENT)
+ atf_tc_skip("kernel does not support TLS offload");
+ atf_libc_error(errno, "Failed to read kern.ipc.tls.enable");
+ }
+
+ if (!enable)
+ atf_tc_skip("Kernel TLS is disabled");
+}
+
+#define ATF_REQUIRE_KTLS() require_ktls()
+
+static char
+rdigit(void)
+{
+ /* ASCII printable values between 0x20 and 0x7e */
+ return (0x20 + random() % (0x7f - 0x20));
+}
+
+static char *
+alloc_buffer(size_t len)
+{
+ char *buf;
+ size_t i;
+
+ if (len == 0)
+ return (NULL);
+ buf = malloc(len);
+ for (i = 0; i < len; i++)
+ buf[i] = rdigit();
+ return (buf);
+}
+
+static bool
+socketpair_tcp(int *sv)
+{
+ struct pollfd pfd;
+ struct sockaddr_in sin;
+ socklen_t len;
+ int as, cs, ls;
+
+ ls = socket(PF_INET, SOCK_STREAM, 0);
+ if (ls == -1) {
+ warn("socket() for listen");
+ return (false);
+ }
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ if (bind(ls, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
+ warn("bind");
+ close(ls);
+ return (false);
+ }
+
+ if (listen(ls, 1) == -1) {
+ warn("listen");
+ close(ls);
+ return (false);
+ }
+
+ len = sizeof(sin);
+ if (getsockname(ls, (struct sockaddr *)&sin, &len) == -1) {
+ warn("getsockname");
+ close(ls);
+ return (false);
+ }
+
+ cs = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ if (cs == -1) {
+ warn("socket() for connect");
+ close(ls);
+ return (false);
+ }
+
+ if (connect(cs, (struct sockaddr *)&sin, sizeof(sin)) == -1) {
+ if (errno != EINPROGRESS) {
+ warn("connect");
+ close(ls);
+ close(cs);
+ return (false);
+ }
+ }
+
+ as = accept4(ls, NULL, NULL, SOCK_NONBLOCK);
+ if (as == -1) {
+ warn("accept4");
+ close(ls);
+ close(cs);
+ return (false);
+ }
+
+ close(ls);
+
+ pfd.fd = cs;
+ pfd.events = POLLOUT;
+ pfd.revents = 0;
+ ATF_REQUIRE(poll(&pfd, 1, INFTIM) == 1);
+ ATF_REQUIRE(pfd.revents == POLLOUT);
+
+ sv[0] = cs;
+ sv[1] = as;
+ return (true);
+}
+
+static void
+fd_set_blocking(int fd)
+{
+ int flags;
+
+ ATF_REQUIRE((flags = fcntl(fd, F_GETFL)) != -1);
+ flags &= ~O_NONBLOCK;
+ ATF_REQUIRE(fcntl(fd, F_SETFL, flags) != -1);
+}
+
+static bool
+cbc_decrypt(const EVP_CIPHER *cipher, const char *key, const char *iv,
+ const char *input, char *output, size_t size)
+{
+ EVP_CIPHER_CTX *ctx;
+ int outl, total;
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL) {
+ warnx("EVP_CIPHER_CTX_new failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (false);
+ }
+ if (EVP_CipherInit_ex(ctx, cipher, NULL, (const u_char *)key,
+ (const u_char *)iv, 0) != 1) {
+ warnx("EVP_CipherInit_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+ if (EVP_CipherUpdate(ctx, (u_char *)output, &outl,
+ (const u_char *)input, size) != 1) {
+ warnx("EVP_CipherUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ total = outl;
+ if (EVP_CipherFinal_ex(ctx, (u_char *)output + outl, &outl) != 1) {
+ warnx("EVP_CipherFinal_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ total += outl;
+ if ((size_t)total != size) {
+ warnx("decrypt size mismatch: %zu vs %d", size, total);
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ EVP_CIPHER_CTX_free(ctx);
+ return (true);
+}
+
+static bool
+verify_hash(const EVP_MD *md, const void *key, size_t key_len, const void *aad,
+ size_t aad_len, const void *buffer, size_t len, const void *digest)
+{
+ HMAC_CTX *ctx;
+ unsigned char digest2[EVP_MAX_MD_SIZE];
+ u_int digest_len;
+
+ ctx = HMAC_CTX_new();
+ if (ctx == NULL) {
+ warnx("HMAC_CTX_new failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (false);
+ }
+ if (HMAC_Init_ex(ctx, key, key_len, md, NULL) != 1) {
+ warnx("HMAC_Init_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ HMAC_CTX_free(ctx);
+ return (false);
+ }
+ if (HMAC_Update(ctx, aad, aad_len) != 1) {
+ warnx("HMAC_Update (aad) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ HMAC_CTX_free(ctx);
+ return (false);
+ }
+ if (HMAC_Update(ctx, buffer, len) != 1) {
+ warnx("HMAC_Update (payload) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ HMAC_CTX_free(ctx);
+ return (false);
+ }
+ if (HMAC_Final(ctx, digest2, &digest_len) != 1) {
+ warnx("HMAC_Final failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ HMAC_CTX_free(ctx);
+ return (false);
+ }
+ HMAC_CTX_free(ctx);
+ if (memcmp(digest, digest2, digest_len) != 0) {
+ warnx("HMAC mismatch");
+ return (false);
+ }
+ return (true);
+}
+
+static bool
+aead_decrypt(const EVP_CIPHER *cipher, const char *key, const char *nonce,
+ const void *aad, size_t aad_len, const char *input, char *output,
+ size_t size, const char *tag, size_t tag_len)
+{
+ EVP_CIPHER_CTX *ctx;
+ int outl, total;
+ bool valid;
+
+ ctx = EVP_CIPHER_CTX_new();
+ if (ctx == NULL) {
+ warnx("EVP_CIPHER_CTX_new failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ return (false);
+ }
+ if (EVP_DecryptInit_ex(ctx, cipher, NULL, (const u_char *)key,
+ (const u_char *)nonce) != 1) {
+ warnx("EVP_DecryptInit_ex failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ EVP_CIPHER_CTX_set_padding(ctx, 0);
+ if (aad != NULL) {
+ if (EVP_DecryptUpdate(ctx, NULL, &outl, (const u_char *)aad,
+ aad_len) != 1) {
+ warnx("EVP_DecryptUpdate for AAD failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ }
+ if (EVP_DecryptUpdate(ctx, (u_char *)output, &outl,
+ (const u_char *)input, size) != 1) {
+ warnx("EVP_DecryptUpdate failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ total = outl;
+ if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, tag_len,
+ __DECONST(char *, tag)) != 1) {
+ warnx("EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_SET_TAG) failed: %s",
+ ERR_error_string(ERR_get_error(), NULL));
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ valid = (EVP_DecryptFinal_ex(ctx, (u_char *)output + outl, &outl) == 1);
+ total += outl;
+ if ((size_t)total != size) {
+ warnx("decrypt size mismatch: %zu vs %d", size, total);
+ EVP_CIPHER_CTX_free(ctx);
+ return (false);
+ }
+ if (!valid)
+ warnx("tag mismatch");
+ EVP_CIPHER_CTX_free(ctx);
+ return (valid);
+}
+
+static void
+build_tls_enable(int cipher_alg, size_t cipher_key_len, int auth_alg,
+ int minor, uint64_t seqno, struct tls_enable *en)
+{
+ u_int auth_key_len, iv_len;
+
+ memset(en, 0, sizeof(*en));
+
+ switch (cipher_alg) {
+ case CRYPTO_AES_CBC:
+ if (minor == TLS_MINOR_VER_ZERO)
+ iv_len = AES_BLOCK_LEN;
+ else
+ iv_len = 0;
+ break;
+ case CRYPTO_AES_NIST_GCM_16:
+ if (minor == TLS_MINOR_VER_TWO)
+ iv_len = TLS_AEAD_GCM_LEN;
+ else
+ iv_len = TLS_1_3_GCM_IV_LEN;
+ break;
+ case CRYPTO_CHACHA20_POLY1305:
+ iv_len = TLS_CHACHA20_IV_LEN;
+ break;
+ default:
+ iv_len = 0;
+ break;
+ }
+ switch (auth_alg) {
+ case CRYPTO_SHA1_HMAC:
+ auth_key_len = SHA1_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_256_HMAC:
+ auth_key_len = SHA2_256_HASH_LEN;
+ break;
+ case CRYPTO_SHA2_384_HMAC:
+ auth_key_len = SHA2_384_HASH_LEN;
+ break;
+ default:
+ auth_key_len = 0;
+ break;
+ }
+ en->cipher_key = alloc_buffer(cipher_key_len);
+ en->iv = alloc_buffer(iv_len);
+ en->auth_key = alloc_buffer(auth_key_len);
+ en->cipher_algorithm = cipher_alg;
+ en->cipher_key_len = cipher_key_len;
+ en->iv_len = iv_len;
+ en->auth_algorithm = auth_alg;
+ en->auth_key_len = auth_key_len;
+ en->tls_vmajor = TLS_MAJOR_VER_ONE;
+ en->tls_vminor = minor;
+ be64enc(en->rec_seq, seqno);
+}
+
+static void
+free_tls_enable(struct tls_enable *en)
+{
+ free(__DECONST(void *, en->cipher_key));
+ free(__DECONST(void *, en->iv));
+ free(__DECONST(void *, en->auth_key));
+}
+
+static const EVP_CIPHER *
+tls_EVP_CIPHER(const struct tls_enable *en)
+{
+ switch (en->cipher_algorithm) {
+ case CRYPTO_AES_CBC:
+ switch (en->cipher_key_len) {
+ case 128 / 8:
+ return (EVP_aes_128_cbc());
+ case 256 / 8:
+ return (EVP_aes_256_cbc());
+ default:
+ return (NULL);
+ }
+ break;
+ case CRYPTO_AES_NIST_GCM_16:
+ switch (en->cipher_key_len) {
+ case 128 / 8:
+ return (EVP_aes_128_gcm());
+ case 256 / 8:
+ return (EVP_aes_256_gcm());
+ default:
+ return (NULL);
+ }
+ break;
+ case CRYPTO_CHACHA20_POLY1305:
+ return (EVP_chacha20_poly1305());
+ default:
+ return (NULL);
+ }
+}
+
+static const EVP_MD *
+tls_EVP_MD(const struct tls_enable *en)
+{
+ switch (en->auth_algorithm) {
+ case CRYPTO_SHA1_HMAC:
+ return (EVP_sha1());
+ case CRYPTO_SHA2_256_HMAC:
+ return (EVP_sha256());
+ case CRYPTO_SHA2_384_HMAC:
+ return (EVP_sha384());
+ default:
+ return (NULL);
+ }
+}
+
+static size_t
+tls_header_len(struct tls_enable *en)
+{
+ size_t len;
+
+ len = sizeof(struct tls_record_layer);
+ switch (en->cipher_algorithm) {
+ case CRYPTO_AES_CBC:
+ if (en->tls_vminor != TLS_MINOR_VER_ZERO)
+ len += AES_BLOCK_LEN;
+ return (len);
+ case CRYPTO_AES_NIST_GCM_16:
+ if (en->tls_vminor == TLS_MINOR_VER_TWO)
+ len += sizeof(uint64_t);
+ return (len);
+ case CRYPTO_CHACHA20_POLY1305:
+ return (len);
+ default:
+ return (0);
+ }
+}
+
+static size_t
+tls_mac_len(struct tls_enable *en)
+{
+ switch (en->cipher_algorithm) {
+ case CRYPTO_AES_CBC:
+ switch (en->auth_algorithm) {
+ case CRYPTO_SHA1_HMAC:
+ return (SHA1_HASH_LEN);
+ case CRYPTO_SHA2_256_HMAC:
+ return (SHA2_256_HASH_LEN);
+ case CRYPTO_SHA2_384_HMAC:
+ return (SHA2_384_HASH_LEN);
+ default:
+ return (0);
+ }
+ case CRYPTO_AES_NIST_GCM_16:
+ return (AES_GMAC_HASH_LEN);
+ case CRYPTO_CHACHA20_POLY1305:
+ return (POLY1305_HASH_LEN);
+ default:
+ return (0);
+ }
+}
+
+/* Includes maximum padding for MTE. */
+static size_t
+tls_trailer_len(struct tls_enable *en)
+{
+ size_t len;
+
+ len = tls_mac_len(en);
+ if (en->cipher_algorithm == CRYPTO_AES_CBC)
+ len += AES_BLOCK_LEN;
+ if (en->tls_vminor == TLS_MINOR_VER_THREE)
+ len++;
+ return (len);
+}
+
+/* 'len' is the length of the payload application data. */
+static void
+tls_mte_aad(struct tls_enable *en, size_t len,
+ const struct tls_record_layer *hdr, uint64_t seqno, struct tls_mac_data *ad)
+{
+ ad->seq = htobe64(seqno);
+ ad->type = hdr->tls_type;
+ ad->tls_vmajor = hdr->tls_vmajor;
+ ad->tls_vminor = hdr->tls_vminor;
+ ad->tls_length = htons(len);
+}
+
+static void
+tls_12_aead_aad(struct tls_enable *en, size_t len,
+ const struct tls_record_layer *hdr, uint64_t seqno,
+ struct tls_aead_data *ad)
+{
+ ad->seq = htobe64(seqno);
+ ad->type = hdr->tls_type;
+ ad->tls_vmajor = hdr->tls_vmajor;
+ ad->tls_vminor = hdr->tls_vminor;
+ ad->tls_length = htons(len);
+}
+
+static void
+tls_13_aad(struct tls_enable *en, const struct tls_record_layer *hdr,
+ uint64_t seqno, struct tls_aead_data_13 *ad)
+{
+ ad->type = hdr->tls_type;
+ ad->tls_vmajor = hdr->tls_vmajor;
+ ad->tls_vminor = hdr->tls_vminor;
+ ad->tls_length = hdr->tls_length;
+}
+
+static void
+tls_12_gcm_nonce(struct tls_enable *en, const struct tls_record_layer *hdr,
+ char *nonce)
+{
+ memcpy(nonce, en->iv, TLS_AEAD_GCM_LEN);
+ memcpy(nonce + TLS_AEAD_GCM_LEN, hdr + 1, sizeof(uint64_t));
+}
+
+static void
+tls_13_nonce(struct tls_enable *en, uint64_t seqno, char *nonce)
+{
+ static_assert(TLS_1_3_GCM_IV_LEN == TLS_CHACHA20_IV_LEN,
+ "TLS 1.3 nonce length mismatch");
+ memcpy(nonce, en->iv, TLS_1_3_GCM_IV_LEN);
+ *(uint64_t *)(nonce + 4) ^= htobe64(seqno);
+}
+
+/*
+ * Decrypt a TLS record 'len' bytes long at 'src' and store the result at
+ * 'dst'. If the TLS record header length doesn't match or 'dst' doesn't
+ * have sufficient room ('avail'), fail the test.
+ */
+static size_t
+decrypt_tls_aes_cbc_mte(struct tls_enable *en, uint64_t seqno, const void *src,
+ size_t len, void *dst, size_t avail, uint8_t *record_type)
+{
+ const struct tls_record_layer *hdr;
+ struct tls_mac_data aad;
+ const char *iv;
+ char *buf;
+ size_t hdr_len, mac_len, payload_len;
+ int padding;
+
+ hdr = src;
+ hdr_len = tls_header_len(en);
+ mac_len = tls_mac_len(en);
+ ATF_REQUIRE(hdr->tls_vmajor == TLS_MAJOR_VER_ONE);
+ ATF_REQUIRE(hdr->tls_vminor == en->tls_vminor);
+
+ /* First, decrypt the outer payload into a temporary buffer. */
+ payload_len = len - hdr_len;
+ buf = malloc(payload_len);
+ if (en->tls_vminor == TLS_MINOR_VER_ZERO)
+ iv = en->iv;
+ else
+ iv = (void *)(hdr + 1);
+ ATF_REQUIRE(cbc_decrypt(tls_EVP_CIPHER(en), en->cipher_key, iv,
+ (const u_char *)src + hdr_len, buf, payload_len));
+
+ /*
+ * Copy the last encrypted block to use as the IV for the next
+ * record for TLS 1.0.
+ */
+ if (en->tls_vminor == TLS_MINOR_VER_ZERO)
+ memcpy(__DECONST(uint8_t *, en->iv), (const u_char *)src +
+ (len - AES_BLOCK_LEN), AES_BLOCK_LEN);
+
+ /*
+ * Verify trailing padding and strip.
+ *
+ * The kernel always generates the smallest amount of padding.
+ */
+ padding = buf[payload_len - 1] + 1;
+ ATF_REQUIRE(padding > 0 && padding <= AES_BLOCK_LEN);
+ ATF_REQUIRE(payload_len >= mac_len + padding);
+ payload_len -= padding;
+
+ /* Verify HMAC. */
+ payload_len -= mac_len;
+ tls_mte_aad(en, payload_len, hdr, seqno, &aad);
+ ATF_REQUIRE(verify_hash(tls_EVP_MD(en), en->auth_key, en->auth_key_len,
+ &aad, sizeof(aad), buf, payload_len, buf + payload_len));
+
+ ATF_REQUIRE(payload_len <= avail);
+ memcpy(dst, buf, payload_len);
+ *record_type = hdr->tls_type;
+ return (payload_len);
+}
+
+static size_t
+decrypt_tls_12_aead(struct tls_enable *en, uint64_t seqno, const void *src,
+ size_t len, void *dst, uint8_t *record_type)
+{
+ const struct tls_record_layer *hdr;
+ struct tls_aead_data aad;
+ char nonce[12];
+ size_t hdr_len, mac_len, payload_len;
+
+ hdr = src;
+
+ hdr_len = tls_header_len(en);
+ mac_len = tls_mac_len(en);
+ payload_len = len - (hdr_len + mac_len);
+ ATF_REQUIRE(hdr->tls_vmajor == TLS_MAJOR_VER_ONE);
+ ATF_REQUIRE(hdr->tls_vminor == TLS_MINOR_VER_TWO);
+
+ tls_12_aead_aad(en, payload_len, hdr, seqno, &aad);
+ if (en->cipher_algorithm == CRYPTO_AES_NIST_GCM_16)
+ tls_12_gcm_nonce(en, hdr, nonce);
+ else
+ tls_13_nonce(en, seqno, nonce);
+
+ ATF_REQUIRE(aead_decrypt(tls_EVP_CIPHER(en), en->cipher_key, nonce,
+ &aad, sizeof(aad), (const char *)src + hdr_len, dst, payload_len,
+ (const char *)src + hdr_len + payload_len, mac_len));
+
+ *record_type = hdr->tls_type;
+ return (payload_len);
+}
+
+static size_t
+decrypt_tls_13_aead(struct tls_enable *en, uint64_t seqno, const void *src,
+ size_t len, void *dst, uint8_t *record_type)
+{
+ const struct tls_record_layer *hdr;
+ struct tls_aead_data_13 aad;
+ char nonce[12];
+ char *buf;
+ size_t hdr_len, mac_len, payload_len;
+
+ hdr = src;
+
+ hdr_len = tls_header_len(en);
+ mac_len = tls_mac_len(en);
+ payload_len = len - (hdr_len + mac_len);
+ ATF_REQUIRE(payload_len >= 1);
+ ATF_REQUIRE(hdr->tls_type == TLS_RLTYPE_APP);
+ ATF_REQUIRE(hdr->tls_vmajor == TLS_MAJOR_VER_ONE);
+ ATF_REQUIRE(hdr->tls_vminor == TLS_MINOR_VER_TWO);
+
+ tls_13_aad(en, hdr, seqno, &aad);
+ tls_13_nonce(en, seqno, nonce);
+
+ /*
+ * Have to use a temporary buffer for the output due to the
+ * record type as the last byte of the trailer.
+ */
+ buf = malloc(payload_len);
+
+ ATF_REQUIRE(aead_decrypt(tls_EVP_CIPHER(en), en->cipher_key, nonce,
+ &aad, sizeof(aad), (const char *)src + hdr_len, buf, payload_len,
+ (const char *)src + hdr_len + payload_len, mac_len));
+
+ /* Trim record type. */
+ *record_type = buf[payload_len - 1];
+ payload_len--;
+
+ memcpy(dst, buf, payload_len);
+ free(buf);
+
+ return (payload_len);
+}
+
+static size_t
+decrypt_tls_aead(struct tls_enable *en, uint64_t seqno, const void *src,
+ size_t len, void *dst, size_t avail, uint8_t *record_type)
+{
+ const struct tls_record_layer *hdr;
+ size_t payload_len;
+
+ hdr = src;
+ ATF_REQUIRE(ntohs(hdr->tls_length) + sizeof(*hdr) == len);
+
+ payload_len = len - (tls_header_len(en) + tls_trailer_len(en));
+ ATF_REQUIRE(payload_len <= avail);
+
+ if (en->tls_vminor == TLS_MINOR_VER_TWO) {
+ ATF_REQUIRE(decrypt_tls_12_aead(en, seqno, src, len, dst,
+ record_type) == payload_len);
+ } else {
+ ATF_REQUIRE(decrypt_tls_13_aead(en, seqno, src, len, dst,
+ record_type) == payload_len);
+ }
+
+ return (payload_len);
+}
+
+static size_t
+decrypt_tls_record(struct tls_enable *en, uint64_t seqno, const void *src,
+ size_t len, void *dst, size_t avail, uint8_t *record_type)
+{
+ if (en->cipher_algorithm == CRYPTO_AES_CBC)
+ return (decrypt_tls_aes_cbc_mte(en, seqno, src, len, dst, avail,
+ record_type));
+ else
+ return (decrypt_tls_aead(en, seqno, src, len, dst, avail,
+ record_type));
+}
+
+static void
+test_ktls_transmit_app_data(struct tls_enable *en, uint64_t seqno, size_t len)
+{
+ struct kevent ev;
+ struct tls_record_layer *hdr;
+ char *plaintext, *decrypted, *outbuf;
+ size_t decrypted_len, outbuf_len, outbuf_cap, record_len, written;
+ ssize_t rv;
+ int kq, sockets[2];
+ uint8_t record_type;
+
+ plaintext = alloc_buffer(len);
+ decrypted = malloc(len);
+ outbuf_cap = tls_header_len(en) + TLS_MAX_MSG_SIZE_V10_2 +
+ tls_trailer_len(en);
+ outbuf = malloc(outbuf_cap);
+ hdr = (struct tls_record_layer *)outbuf;
+
+ ATF_REQUIRE((kq = kqueue()) != -1);
+
+ ATF_REQUIRE_MSG(socketpair_tcp(sockets), "failed to create sockets");
+
+ ATF_REQUIRE(setsockopt(sockets[1], IPPROTO_TCP, TCP_TXTLS_ENABLE, en,
+ sizeof(*en)) == 0);
+
+ EV_SET(&ev, sockets[0], EVFILT_READ, EV_ADD, 0, 0, NULL);
+ ATF_REQUIRE(kevent(kq, &ev, 1, NULL, 0, NULL) == 0);
+ EV_SET(&ev, sockets[1], EVFILT_WRITE, EV_ADD, 0, 0, NULL);
+ ATF_REQUIRE(kevent(kq, &ev, 1, NULL, 0, NULL) == 0);
+
+ decrypted_len = 0;
+ outbuf_len = 0;
+ written = 0;
+
+ while (decrypted_len != len) {
+ ATF_REQUIRE(kevent(kq, NULL, 0, &ev, 1, NULL) == 1);
+
+ switch (ev.filter) {
+ case EVFILT_WRITE:
+ /* Try to write any remaining data. */
+ rv = write(ev.ident, plaintext + written,
+ len - written);
+ ATF_REQUIRE_MSG(rv > 0,
+ "failed to write to socket");
+ written += rv;
+ if (written == len) {
+ ev.flags = EV_DISABLE;
+ ATF_REQUIRE(kevent(kq, &ev, 1, NULL, 0,
+ NULL) == 0);
+ }
+ break;
+
+ case EVFILT_READ:
+ ATF_REQUIRE((ev.flags & EV_EOF) == 0);
+
+ /*
+ * Try to read data for the next TLS record
+ * into outbuf. Start by reading the header
+ * to determine how much additional data to
+ * read.
+ */
+ if (outbuf_len < sizeof(struct tls_record_layer)) {
+ rv = read(ev.ident, outbuf + outbuf_len,
+ sizeof(struct tls_record_layer) -
+ outbuf_len);
+ ATF_REQUIRE_MSG(rv > 0,
+ "failed to read from socket");
+ outbuf_len += rv;
+ }
+
+ if (outbuf_len < sizeof(struct tls_record_layer))
+ break;
+
+ record_len = sizeof(struct tls_record_layer) +
+ ntohs(hdr->tls_length);
+ assert(record_len <= outbuf_cap);
+ assert(record_len > outbuf_len);
+ rv = read(ev.ident, outbuf + outbuf_len,
+ record_len - outbuf_len);
+ if (rv == -1 && errno == EAGAIN)
+ break;
+ ATF_REQUIRE_MSG(rv > 0, "failed to read from socket");
+
+ outbuf_len += rv;
+ if (outbuf_len == record_len) {
+ decrypted_len += decrypt_tls_record(en, seqno,
+ outbuf, outbuf_len,
+ decrypted + decrypted_len,
+ len - decrypted_len, &record_type);
+ ATF_REQUIRE(record_type == TLS_RLTYPE_APP);
+
+ seqno++;
+ outbuf_len = 0;
+ }
+ break;
+ }
+ }
+
+ ATF_REQUIRE_MSG(written == decrypted_len,
+ "read %zu decrypted bytes, but wrote %zu", decrypted_len, written);
+
+ ATF_REQUIRE(memcmp(plaintext, decrypted, len) == 0);
+
+ free(outbuf);
+ free(decrypted);
+ free(plaintext);
+
+ close(sockets[1]);
+ close(sockets[0]);
+ close(kq);
+}
+
+static void
+ktls_send_control_message(int fd, uint8_t type, void *data, size_t len)
+{
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ char cbuf[CMSG_SPACE(sizeof(type))];
+ struct iovec iov;
+
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = IPPROTO_TCP;
+ cmsg->cmsg_type = TLS_SET_RECORD_TYPE;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(type));
+ *(uint8_t *)CMSG_DATA(cmsg) = type;
+
+ iov.iov_base = data;
+ iov.iov_len = len;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ ATF_REQUIRE(sendmsg(fd, &msg, 0) == (ssize_t)len);
+}
+
+static void
+test_ktls_transmit_control(struct tls_enable *en, uint64_t seqno, uint8_t type,
+ size_t len)
+{
+ struct tls_record_layer *hdr;
+ char *plaintext, *decrypted, *outbuf;
+ size_t outbuf_cap, payload_len, record_len;
+ ssize_t rv;
+ int sockets[2];
+ uint8_t record_type;
+
+ ATF_REQUIRE(len <= TLS_MAX_MSG_SIZE_V10_2);
+
+ plaintext = alloc_buffer(len);
+ decrypted = malloc(len);
+ outbuf_cap = tls_header_len(en) + len + tls_trailer_len(en);
+ outbuf = malloc(outbuf_cap);
+ hdr = (struct tls_record_layer *)outbuf;
+
+ ATF_REQUIRE_MSG(socketpair_tcp(sockets), "failed to create sockets");
+
+ ATF_REQUIRE(setsockopt(sockets[1], IPPROTO_TCP, TCP_TXTLS_ENABLE, en,
+ sizeof(*en)) == 0);
+
+ fd_set_blocking(sockets[0]);
+ fd_set_blocking(sockets[1]);
+
+ ktls_send_control_message(sockets[1], type, plaintext, len);
+
+ /*
+ * First read the header to determine how much additional data
+ * to read.
+ */
+ rv = read(sockets[0], outbuf, sizeof(struct tls_record_layer));
+ ATF_REQUIRE(rv == sizeof(struct tls_record_layer));
+ payload_len = ntohs(hdr->tls_length);
+ record_len = payload_len + sizeof(struct tls_record_layer);
+ assert(record_len <= outbuf_cap);
+ rv = read(sockets[0], outbuf + sizeof(struct tls_record_layer),
+ payload_len);
+ ATF_REQUIRE(rv == (ssize_t)payload_len);
+
+ rv = decrypt_tls_record(en, seqno, outbuf, record_len, decrypted, len,
+ &record_type);
+
+ ATF_REQUIRE_MSG((ssize_t)len == rv,
+ "read %zd decrypted bytes, but wrote %zu", rv, len);
+ ATF_REQUIRE(record_type == type);
+
+ ATF_REQUIRE(memcmp(plaintext, decrypted, len) == 0);
+
+ free(outbuf);
+ free(decrypted);
+ free(plaintext);
+
+ close(sockets[1]);
+ close(sockets[0]);
+}
+
+#define AES_CBC_TESTS(M) \
+ M(aes128_cbc_1_0_sha1, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_ZERO) \
+ M(aes256_cbc_1_0_sha1, CRYPTO_AES_CBC, 256 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_ZERO) \
+ M(aes128_cbc_1_1_sha1, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_ONE) \
+ M(aes256_cbc_1_1_sha1, CRYPTO_AES_CBC, 256 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_ONE) \
+ M(aes128_cbc_1_2_sha1, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_TWO) \
+ M(aes256_cbc_1_2_sha1, CRYPTO_AES_CBC, 256 / 8, \
+ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_TWO) \
+ M(aes128_cbc_1_2_sha256, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA2_256_HMAC, TLS_MINOR_VER_TWO) \
+ M(aes256_cbc_1_2_sha256, CRYPTO_AES_CBC, 256 / 8, \
+ CRYPTO_SHA2_256_HMAC, TLS_MINOR_VER_TWO) \
+ M(aes128_cbc_1_2_sha384, CRYPTO_AES_CBC, 128 / 8, \
+ CRYPTO_SHA2_384_HMAC, TLS_MINOR_VER_TWO) \
+ M(aes256_cbc_1_2_sha384, CRYPTO_AES_CBC, 256 / 8, \
+ CRYPTO_SHA2_384_HMAC, TLS_MINOR_VER_TWO) \
+
+#define AES_GCM_TESTS(M) \
+ M(aes128_gcm_1_2, CRYPTO_AES_NIST_GCM_16, 128 / 8, 0, \
+ TLS_MINOR_VER_TWO) \
+ M(aes256_gcm_1_2, CRYPTO_AES_NIST_GCM_16, 256 / 8, 0, \
+ TLS_MINOR_VER_TWO) \
+ M(aes128_gcm_1_3, CRYPTO_AES_NIST_GCM_16, 128 / 8, 0, \
+ TLS_MINOR_VER_THREE) \
+ M(aes256_gcm_1_3, CRYPTO_AES_NIST_GCM_16, 256 / 8, 0, \
+ TLS_MINOR_VER_THREE)
+
+#define CHACHA20_TESTS(M) \
+ M(chacha20_poly1305_1_2, CRYPTO_CHACHA20_POLY1305, 256 / 8, 0, \
+ TLS_MINOR_VER_TWO) \
+ M(chacha20_poly1305_1_3, CRYPTO_CHACHA20_POLY1305, 256 / 8, 0, \
+ TLS_MINOR_VER_THREE)
+
+#define GEN_TRANSMIT_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, name, len) \
+ATF_TC_WITHOUT_HEAD(ktls_transmit_##cipher_name##_##name); \
+ATF_TC_BODY(ktls_transmit_##cipher_name##_##name, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(cipher_alg, key_size, auth_alg, minor, seqno, \
+ &en); \
+ test_ktls_transmit_app_data(&en, seqno, len); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_TRANSMIT_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, name) \
+ ATF_TP_ADD_TC(tp, ktls_transmit_##cipher_name##_##name);
+
+#define GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, type, len) \
+ATF_TC_WITHOUT_HEAD(ktls_transmit_##cipher_name##_control); \
+ATF_TC_BODY(ktls_transmit_##cipher_name##_control, tc) \
+{ \
+ struct tls_enable en; \
+ uint64_t seqno; \
+ \
+ ATF_REQUIRE_KTLS(); \
+ seqno = random(); \
+ build_tls_enable(cipher_alg, key_size, auth_alg, minor, seqno, \
+ &en); \
+ test_ktls_transmit_control(&en, seqno, type, len); \
+ free_tls_enable(&en); \
+}
+
+#define ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor) \
+ ATF_TP_ADD_TC(tp, ktls_transmit_##cipher_name##_control);
+
+#define GEN_TRANSMIT_TESTS(cipher_name, cipher_alg, key_size, auth_alg, \
+ minor) \
+ GEN_TRANSMIT_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, short, 64) \
+ GEN_TRANSMIT_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, long, 64 * 1024) \
+ GEN_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, 0x21 /* Alert */, 32)
+
+#define ADD_TRANSMIT_TESTS(cipher_name, cipher_alg, key_size, auth_alg, \
+ minor) \
+ ADD_TRANSMIT_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, short) \
+ ADD_TRANSMIT_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor, long) \
+ ADD_TRANSMIT_CONTROL_TEST(cipher_name, cipher_alg, key_size, \
+ auth_alg, minor)
+
+/*
+ * For each supported cipher suite, run three transmit tests:
+ *
+ * - a short test which sends 64 bytes of application data (likely as
+ * a single TLS record)
+ *
+ * - a long test which sends 64KB of application data (split across
+ * multiple TLS records)
+ *
+ * - a control test which sends a single record with a specific
+ * content type via sendmsg()
+ */
+AES_CBC_TESTS(GEN_TRANSMIT_TESTS);
+AES_GCM_TESTS(GEN_TRANSMIT_TESTS);
+CHACHA20_TESTS(GEN_TRANSMIT_TESTS);
+
+ATF_TP_ADD_TCS(tp)
+{
+ AES_CBC_TESTS(ADD_TRANSMIT_TESTS);
+ AES_GCM_TESTS(ADD_TRANSMIT_TESTS);
+ CHACHA20_TESTS(ADD_TRANSMIT_TESTS);
+
+ return (atf_no_error());
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, Dec 26, 3:49 PM (11 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15605813
Default Alt Text
D32652.diff (29 KB)
Attached To
Mode
D32652: ktls: Add simple transmit tests of kernel TLS.
Attached
Detach File
Event Timeline
Log In to Comment