From nobody Tue Nov 23 23:13:09 2021 X-Original-To: dev-commits-src-all@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id B6B1818A2326; Tue, 23 Nov 2021 23:13:12 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4HzKgV3D0gz3vqv; Tue, 23 Nov 2021 23:13:10 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id 34DA31F14B; Tue, 23 Nov 2021 23:13:09 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from gitrepo.freebsd.org ([127.0.1.44]) by gitrepo.freebsd.org (8.16.1/8.16.1) with ESMTP id 1ANND9hS038017; Tue, 23 Nov 2021 23:13:09 GMT (envelope-from git@gitrepo.freebsd.org) Received: (from git@localhost) by gitrepo.freebsd.org (8.16.1/8.16.1/Submit) id 1ANND92I038016; Tue, 23 Nov 2021 23:13:09 GMT (envelope-from git) Date: Tue, 23 Nov 2021 23:13:09 GMT Message-Id: <202111232313.1ANND92I038016@gitrepo.freebsd.org> To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: John Baldwin Subject: git: 38b44748ab2f - stable/13 - ktls: Add simple receive tests of kernel TLS. List-Id: Commit messages for all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: Sender: owner-dev-commits-src-all@freebsd.org X-BeenThere: dev-commits-src-all@freebsd.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: jhb X-Git-Repository: src X-Git-Refname: refs/heads/stable/13 X-Git-Reftype: branch X-Git-Commit: 38b44748ab2f42c23d1e84cb5b82be322701cc29 Auto-Submitted: auto-generated ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1637709190; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=ELb+VDCcDKUE7GMKXqhCqR3i32tgeO+diYb++iEWvtQ=; b=ck/DyO1Rn+oQDti00RXiA81c4H/oTKfplySvqA3XZmKyWKxGX3vew/SUs7KRn1PWK2bPcY aohd5f+hiNRUyihgrr+cBgVKPFPlroF5i+NOVjX6tDNTLS/bTygxXNTeSTvtaSeJfgiaHM OI/VA6aX2NTL8UvfIkBR6YTeeTcZJEix6yZHwdsUtrkf9iIMeOZS2SmAc2tF7lgjYBPGhS e94D3cK6Md6/XLzRRrdURZqWLt0h2qsr1wwNZvkrCFwI0JnZnyxu0WmRuXKZAIC4cPxuyA RniFt8ppvZRzzfrsEIG4m52nn4QoZmbMhMeRg969bVxs6WCbtGvhWI41565W3Q== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1637709190; a=rsa-sha256; cv=none; b=TiB1cjjyTuSZPBkc3QXbqHlJr6AZAvGM8pP+K4L2IObCPL44OJkVGWLMsaFTX8tLSXkpf7 Ss1ZG3cJXsMkOb1VWb+fqacZgSMKGb2vCyhZv733kimLG0ueryiKMa3ZB2twmhFGmOdMnX od/rEmUObE9P/vdb1/XMuqI6c3TOiIKjX/aMEgxn/x4llfO3zC9fzo3xXL5Fw75pVVWDk7 PKZZGvjP2XkYYQ/S6PbZMFvP8rsS/wTfgh3bjkhg7tw6mCEPdWISpV/MZclbo6JroBJhCS 5/JFifnTXicBQLufeVk5B5YYaRCIg7zEGQHKusW/SY3dqNSkfV9Y37r/QVzKJg== ARC-Authentication-Results: i=1; mx1.freebsd.org; none X-ThisMailContainsUnwantedMimeParts: N The branch stable/13 has been updated by jhb: URL: https://cgit.FreeBSD.org/src/commit/?id=38b44748ab2f42c23d1e84cb5b82be322701cc29 commit 38b44748ab2f42c23d1e84cb5b82be322701cc29 Author: John Baldwin AuthorDate: 2021-11-15 19:31:16 +0000 Commit: John Baldwin CommitDate: 2021-11-23 23:11:53 +0000 ktls: Add simple receive tests of kernel TLS. Similar to the simple transmit tests added in a10482ea7476d68d1ab028145ae6d97cef747b49, these tests test the kernel TLS functionality directly by manually encrypting TLS records using randomly generated keys and writing them to a socket to be processed by the kernel. Reviewed by: markj Sponsored by: Netflix Differential Revision: https://reviews.freebsd.org/D32980 (cherry picked from commit 3e7f8a8da254cfa97af90c3aae1cb827da55fc9d) --- tests/sys/kern/ktls_test.c | 319 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 319 insertions(+) diff --git a/tests/sys/kern/ktls_test.c b/tests/sys/kern/ktls_test.c index ef0f7405b3fd..bdd2e62a546c 100644 --- a/tests/sys/kern/ktls_test.c +++ b/tests/sys/kern/ktls_test.c @@ -262,6 +262,68 @@ verify_hash(const EVP_MD *md, const void *key, size_t key_len, const void *aad, return (true); } +static bool +aead_encrypt(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, char *tag, size_t tag_len) +{ + 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_EncryptInit_ex(ctx, cipher, NULL, (const u_char *)key, + (const u_char *)nonce) != 1) { + warnx("EVP_EncryptInit_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_EncryptUpdate(ctx, NULL, &outl, (const u_char *)aad, + aad_len) != 1) { + warnx("EVP_EncryptUpdate for AAD failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_free(ctx); + return (false); + } + } + if (EVP_EncryptUpdate(ctx, (u_char *)output, &outl, + (const u_char *)input, size) != 1) { + warnx("EVP_EncryptUpdate failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_free(ctx); + return (false); + } + total = outl; + if (EVP_EncryptFinal_ex(ctx, (u_char *)output + outl, &outl) != 1) { + warnx("EVP_EncryptFinal_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("encrypt size mismatch: %zu vs %d", size, total); + EVP_CIPHER_CTX_free(ctx); + return (false); + } + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, tag_len, tag) != + 1) { + warnx("EVP_CIPHER_CTX_ctrl(EVP_CTRL_AEAD_GET_TAG) failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_free(ctx); + return (false); + } + EVP_CIPHER_CTX_free(ctx); + 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, @@ -714,6 +776,68 @@ decrypt_tls_record(struct tls_enable *en, uint64_t seqno, const void *src, record_type)); } +/* + * Encrypt a TLS record of type 'record_type' with payload 'len' bytes + * long at 'src' and store the result at 'dst'. If 'dst' doesn't have + * sufficient room ('avail'), fail the test. + */ +static size_t +encrypt_tls_12_aead(struct tls_enable *en, uint8_t record_type, uint64_t seqno, + const void *src, size_t len, void *dst) +{ + struct tls_record_layer *hdr; + struct tls_aead_data aad; + char nonce[12]; + size_t hdr_len, mac_len, record_len; + + hdr = dst; + + hdr_len = tls_header_len(en); + mac_len = tls_mac_len(en); + record_len = hdr_len + len + mac_len; + + hdr->tls_type = record_type; + hdr->tls_vmajor = TLS_MAJOR_VER_ONE; + hdr->tls_vminor = TLS_MINOR_VER_TWO; + hdr->tls_length = htons(record_len - sizeof(*hdr)); + if (en->cipher_algorithm == CRYPTO_AES_NIST_GCM_16) + memcpy(hdr + 1, &seqno, sizeof(seqno)); + + tls_12_aead_aad(en, 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_encrypt(tls_EVP_CIPHER(en), en->cipher_key, nonce, + &aad, sizeof(aad), src, (char *)dst + hdr_len, len, + (char *)dst + hdr_len + len, mac_len)); + + return (record_len); +} + +static size_t +encrypt_tls_aead(struct tls_enable *en, uint8_t record_type, uint64_t seqno, + const void *src, size_t len, void *dst, size_t avail) +{ + size_t record_len; + + record_len = tls_header_len(en) + len + tls_trailer_len(en); + ATF_REQUIRE(record_len <= avail); + + ATF_REQUIRE(encrypt_tls_12_aead(en, record_type, seqno, src, len, + dst) == record_len); + + return (record_len); +} + +static size_t +encrypt_tls_record(struct tls_enable *en, uint8_t record_type, uint64_t seqno, + const void *src, size_t len, void *dst, size_t avail) +{ + return (encrypt_tls_aead(en, record_type, seqno, src, len, dst, avail)); +} + static void test_ktls_transmit_app_data(struct tls_enable *en, uint64_t seqno, size_t len) { @@ -963,12 +1087,156 @@ test_ktls_transmit_empty_fragment(struct tls_enable *en, uint64_t seqno) close(sockets[0]); } +static size_t +ktls_receive_tls_record(struct tls_enable *en, int fd, uint8_t record_type, + void *data, size_t len) +{ + struct msghdr msg; + struct cmsghdr *cmsg; + struct tls_get_record *tgr; + char cbuf[CMSG_SPACE(sizeof(*tgr))]; + struct iovec iov; + ssize_t rv; + + memset(&msg, 0, sizeof(msg)); + + msg.msg_control = cbuf; + msg.msg_controllen = sizeof(cbuf); + + iov.iov_base = data; + iov.iov_len = len; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + + ATF_REQUIRE((rv = recvmsg(fd, &msg, 0)) > 0); + + ATF_REQUIRE((msg.msg_flags & (MSG_EOR | MSG_CTRUNC)) == MSG_EOR); + + cmsg = CMSG_FIRSTHDR(&msg); + ATF_REQUIRE(cmsg != NULL); + ATF_REQUIRE(cmsg->cmsg_level == IPPROTO_TCP); + ATF_REQUIRE(cmsg->cmsg_type == TLS_GET_RECORD); + ATF_REQUIRE(cmsg->cmsg_len == CMSG_LEN(sizeof(*tgr))); + + tgr = (struct tls_get_record *)CMSG_DATA(cmsg); + ATF_REQUIRE(tgr->tls_type == record_type); + ATF_REQUIRE(tgr->tls_vmajor == en->tls_vmajor); + ATF_REQUIRE(tgr->tls_vminor == en->tls_vminor); + ATF_REQUIRE(tgr->tls_length == htons(rv)); + + return (rv); +} + +static void +test_ktls_receive_app_data(struct tls_enable *en, uint64_t seqno, size_t len) +{ + struct kevent ev; + char *plaintext, *received, *outbuf; + size_t outbuf_cap, outbuf_len, outbuf_sent, received_len, todo, written; + ssize_t rv; + int kq, sockets[2]; + + plaintext = alloc_buffer(len); + received = malloc(len); + outbuf_cap = tls_header_len(en) + TLS_MAX_MSG_SIZE_V10_2 + + tls_trailer_len(en); + outbuf = malloc(outbuf_cap); + + ATF_REQUIRE((kq = kqueue()) != -1); + + ATF_REQUIRE_MSG(socketpair_tcp(sockets), "failed to create sockets"); + + ATF_REQUIRE(setsockopt(sockets[0], IPPROTO_TCP, TCP_RXTLS_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); + + received_len = 0; + outbuf_len = 0; + written = 0; + + while (received_len != len) { + ATF_REQUIRE(kevent(kq, NULL, 0, &ev, 1, NULL) == 1); + + switch (ev.filter) { + case EVFILT_WRITE: + /* + * Compose the next TLS record to send. + */ + if (outbuf_len == 0) { + ATF_REQUIRE(written < len); + todo = len - written; + if (todo > TLS_MAX_MSG_SIZE_V10_2) + todo = TLS_MAX_MSG_SIZE_V10_2; + outbuf_len = encrypt_tls_record(en, + TLS_RLTYPE_APP, seqno, plaintext + written, + todo, outbuf, outbuf_cap); + outbuf_sent = 0; + written += todo; + seqno++; + } + + /* + * Try to write the remainder of the current + * TLS record. + */ + rv = write(ev.ident, outbuf + outbuf_sent, + outbuf_len - outbuf_sent); + ATF_REQUIRE_MSG(rv > 0, + "failed to write to socket"); + outbuf_sent += rv; + if (outbuf_sent == outbuf_len) { + outbuf_len = 0; + 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); + + rv = ktls_receive_tls_record(en, ev.ident, + TLS_RLTYPE_APP, received + received_len, + len - received_len); + received_len += rv; + break; + } + } + + ATF_REQUIRE_MSG(written == received_len, + "read %zu decrypted bytes, but wrote %zu", received_len, written); + + ATF_REQUIRE(memcmp(plaintext, received, len) == 0); + + free(outbuf); + free(received); + free(plaintext); + + close(sockets[1]); + close(sockets[0]); + close(kq); +} + #define TLS_10_TESTS(M) \ M(aes128_cbc_1_0_sha1, CRYPTO_AES_CBC, 128 / 8, \ CRYPTO_SHA1_HMAC) \ M(aes256_cbc_1_0_sha1, CRYPTO_AES_CBC, 256 / 8, \ CRYPTO_SHA1_HMAC) +#define TLS_12_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(chacha20_poly1305_1_2, CRYPTO_CHACHA20_POLY1305, 256 / 8, 0, \ + TLS_MINOR_VER_TWO) + #define AES_CBC_TESTS(M) \ M(aes128_cbc_1_0_sha1, CRYPTO_AES_CBC, 128 / 8, \ CRYPTO_SHA1_HMAC, TLS_MINOR_VER_ZERO) \ @@ -1251,8 +1519,57 @@ ATF_TC_BODY(ktls_transmit_invalid_##name, tc) \ */ INVALID_CIPHER_SUITES(GEN_INVALID_TRANSMIT_TEST); +#define GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \ + auth_alg, minor, name, len) \ +ATF_TC_WITHOUT_HEAD(ktls_receive_##cipher_name##_##name); \ +ATF_TC_BODY(ktls_receive_##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_receive_app_data(&en, seqno, len); \ + free_tls_enable(&en); \ +} + +#define ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \ + auth_alg, minor, name) \ + ATF_TP_ADD_TC(tp, ktls_receive_##cipher_name##_##name); + +#define GEN_RECEIVE_TESTS(cipher_name, cipher_alg, key_size, auth_alg, \ + minor) \ + GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \ + auth_alg, minor, short, 64) \ + GEN_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \ + auth_alg, minor, long, 64 * 1024) + +#define ADD_RECEIVE_TESTS(cipher_name, cipher_alg, key_size, auth_alg, \ + minor) \ + ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \ + auth_alg, minor, short) \ + ADD_RECEIVE_APP_DATA_TEST(cipher_name, cipher_alg, key_size, \ + auth_alg, minor, long) + +/* + * For each supported cipher suite, run two receive 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) + * + * Note that receive is currently only supported for TLS 1.2 AEAD + * cipher suites. + */ +TLS_12_TESTS(GEN_RECEIVE_TESTS); + ATF_TP_ADD_TCS(tp) { + /* Transmit tests */ AES_CBC_TESTS(ADD_TRANSMIT_TESTS); AES_GCM_TESTS(ADD_TRANSMIT_TESTS); CHACHA20_TESTS(ADD_TRANSMIT_TESTS); @@ -1260,5 +1577,7 @@ ATF_TP_ADD_TCS(tp) TLS_10_TESTS(ADD_TRANSMIT_EMPTY_FRAGMENT_TEST); INVALID_CIPHER_SUITES(ADD_INVALID_TRANSMIT_TEST); + /* Receive tests */ + TLS_12_TESTS(ADD_RECEIVE_TESTS); return (atf_no_error()); }