From owner-svn-src-all@freebsd.org Fri Mar 23 04:31:20 2018 Return-Path: Delivered-To: svn-src-all@mailman.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mailman.ysv.freebsd.org (Postfix) with ESMTP id 3403BF6A7E4; Fri, 23 Mar 2018 04:31:20 +0000 (UTC) (envelope-from cem@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client CN "mxrelay.nyi.freebsd.org", Issuer "Let's Encrypt Authority X3" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id D71D67F5E1; Fri, 23 Mar 2018 04:31:19 +0000 (UTC) (envelope-from cem@FreeBSD.org) Received: from repo.freebsd.org (repo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:0]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (Client did not present a certificate) by mxrelay.nyi.freebsd.org (Postfix) with ESMTPS id D1BFD1B0C0; Fri, 23 Mar 2018 04:31:19 +0000 (UTC) (envelope-from cem@FreeBSD.org) Received: from repo.freebsd.org ([127.0.1.37]) by repo.freebsd.org (8.15.2/8.15.2) with ESMTP id w2N4VJ86039717; Fri, 23 Mar 2018 04:31:19 GMT (envelope-from cem@FreeBSD.org) Received: (from cem@localhost) by repo.freebsd.org (8.15.2/8.15.2/Submit) id w2N4VJV4039715; Fri, 23 Mar 2018 04:31:19 GMT (envelope-from cem@FreeBSD.org) Message-Id: <201803230431.w2N4VJV4039715@repo.freebsd.org> X-Authentication-Warning: repo.freebsd.org: cem set sender to cem@FreeBSD.org using -f From: Conrad Meyer Date: Fri, 23 Mar 2018 04:31:19 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org Subject: svn commit: r331417 - head/tools/tools/crypto X-SVN-Group: head X-SVN-Commit-Author: cem X-SVN-Commit-Paths: head/tools/tools/crypto X-SVN-Commit-Revision: 331417 X-SVN-Commit-Repository: base MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.25 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 23 Mar 2018 04:31:20 -0000 Author: cem Date: Fri Mar 23 04:31:19 2018 New Revision: 331417 URL: https://svnweb.freebsd.org/changeset/base/331417 Log: Bring in JHB's cryptocheck tool It can be used to validate basic algorithm correctness on a variety of inputs, by comarison to openssl. While here, add some sanity to the crypto/Makefile. The tool may not be perfect, but getting it in tree where collaboration can happen is a nice first step. The pace of development outside of svn seems to have slowed down mid-2017. Obtained from: github bsdjhb/freebsd:cryptocheck Sponsored by: Dell EMC Isilon Added: head/tools/tools/crypto/cryptocheck.c (contents, props changed) Modified: head/tools/tools/crypto/Makefile Modified: head/tools/tools/crypto/Makefile ============================================================================== --- head/tools/tools/crypto/Makefile Fri Mar 23 03:48:45 2018 (r331416) +++ head/tools/tools/crypto/Makefile Fri Mar 23 04:31:19 2018 (r331417) @@ -1,5 +1,6 @@ # $FreeBSD$ # +# Copyright (c) 2018 Conrad Meyer # Copyright (c) 2002, 2003 Sam Leffler, Errno Consulting # All rights reserved. # @@ -25,40 +26,23 @@ # SUCH DAMAGE. # -ALL= cryptotest cryptokeytest cryptostats \ +PROGS= cryptocheck cryptotest cryptokeytest cryptostats \ ubsecstats hifnstats ipsecstats safestats -BINDIR= /usr/local/bin +MAN= +BINDIR?= /usr/local/bin -all: ${ALL} +# cryptocheck: test symmetric crypto functions +LIBADD.cryptocheck+= crypto ssl util -# program to test asymmetric crypto functions -cryptokeytest: cryptokeytest.c - ${CC} -o cryptokeytest cryptokeytest.c -lcrypto +# cryptokeytest: test asymmetric crypto functions +LIBADD.cryptokeytest+= crypto -# program to dump statistics kept by the core crypto code -cryptostats: cryptostats.c - ${CC} -o cryptostats cryptostats.c +# cryptostats: dump statistics kept by the core crypto code +# ubsecstats: print statistics kept by the Broadcom driver +# hifnstats: print statistics kept by the HIFN driver +# safestats: statistics kept by the SafeNet driver +# ipsecstats: print statistics kept by fast ipsec -# program to print statistics kept by the Broadcom driver -ubsecstats: ubsecstats.c - ${CC} -o ubsecstats ubsecstats.c +CLEANFILES+= core a.out -# program to print statistics kept by the HIFN driver -hifnstats: hifnstats.c - ${CC} -o hifnstats hifnstats.c - -# program to print statistics kept by the SafeNet driver -safestats: safestats.c - ${CC} -o safestats safestats.c - -# program to print statistics kept by fast ipsec -ipsecstats: ipsecstats.c - ${CC} -o ipsecstats ipsecstats.c - -clean: - rm -f ${ALL} core a.out - -install: ${ALL} - for i in ${ALL}; do \ - install $$i ${DESTDIR}${BINDIR}; \ - done +.include Added: head/tools/tools/crypto/cryptocheck.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/tools/tools/crypto/cryptocheck.c Fri Mar 23 04:31:19 2018 (r331417) @@ -0,0 +1,1168 @@ +/*- + * Copyright (c) 2017 John Baldwin, + * Copyright (c) 2004 Sam Leffler, Errno Consulting + * All rights reserved. + * + * 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, + * without modification. + * 2. Redistributions in binary form must reproduce at minimum a disclaimer + * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any + * redistribution must be conditioned upon including a substantially + * similar Disclaimer requirement for further binary redistribution. + * 3. Neither the names of the above-listed copyright holders nor the names + * of any contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * NO WARRANTY + * 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 NONINFRINGEMENT, MERCHANTIBILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR 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 DAMAGES. + * + * $FreeBSD$ + */ + +/* + * A different tool for checking hardware crypto support. Whereas + * cryptotest is focused on simple performance numbers, this tool is + * focused on correctness. For each crypto operation, it performs the + * operation once in software via OpenSSL and a second time via + * OpenCrypto and compares the results. + * + * cryptocheck [-vz] [-A aad length] [-a algorithm] [-d dev] [size ...] + * + * Options: + * -v Verbose. + * -z Run all algorithms on a variety of buffer sizes. + * + * Supported algorithms: + * all Run all tests + * hmac Run all hmac tests + * blkcipher Run all block cipher tests + * authenc Run all authenticated encryption tests + * aead Run all authenticated encryption with associated data + * tests + * + * HMACs: + * sha1 sha1 hmac + * sha256 256-bit sha2 hmac + * sha384 384-bit sha2 hmac + * sha512 512-bit sha2 hmac + * + * Block Ciphers: + * aes-cbc 128-bit aes cbc + * aes-cbc192 192-bit aes cbc + * aes-cbc256 256-bit aes cbc + * aes-ctr 128-bit aes ctr + * aes-ctr192 192-bit aes ctr + * aes-ctr256 256-bit aes ctr + * aes-xts 128-bit aes xts + * aes-xts256 256-bit aes xts + * + * Authenticated Encryption: + * + + * + * Authenticated Encryption with Associated Data: + * aes-gcm 128-bit aes gcm + * aes-gcm192 192-bit aes gcm + * aes-gcm256 256-bit aes gcm + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* XXX: Temporary hack */ +#ifndef COP_F_CIPHER_FIRST +#define COP_F_CIPHER_FIRST 0x0001 /* Cipher before MAC. */ +#endif + +struct alg { + const char *name; + int cipher; + int mac; + enum { T_HMAC, T_BLKCIPHER, T_AUTHENC, T_GCM } type; + const EVP_CIPHER *(*evp_cipher)(void); + const EVP_MD *(*evp_md)(void); +} algs[] = { + { .name = "sha1", .mac = CRYPTO_SHA1_HMAC, .type = T_HMAC, + .evp_md = EVP_sha1 }, + { .name = "sha256", .mac = CRYPTO_SHA2_256_HMAC, .type = T_HMAC, + .evp_md = EVP_sha256 }, + { .name = "sha384", .mac = CRYPTO_SHA2_384_HMAC, .type = T_HMAC, + .evp_md = EVP_sha384 }, + { .name = "sha512", .mac = CRYPTO_SHA2_512_HMAC, .type = T_HMAC, + .evp_md = EVP_sha512 }, + { .name = "aes-cbc", .cipher = CRYPTO_AES_CBC, .type = T_BLKCIPHER, + .evp_cipher = EVP_aes_128_cbc }, + { .name = "aes-cbc192", .cipher = CRYPTO_AES_CBC, .type = T_BLKCIPHER, + .evp_cipher = EVP_aes_192_cbc }, + { .name = "aes-cbc256", .cipher = CRYPTO_AES_CBC, .type = T_BLKCIPHER, + .evp_cipher = EVP_aes_256_cbc }, + { .name = "aes-ctr", .cipher = CRYPTO_AES_ICM, .type = T_BLKCIPHER, + .evp_cipher = EVP_aes_128_ctr }, + { .name = "aes-ctr192", .cipher = CRYPTO_AES_ICM, .type = T_BLKCIPHER, + .evp_cipher = EVP_aes_192_ctr }, + { .name = "aes-ctr256", .cipher = CRYPTO_AES_ICM, .type = T_BLKCIPHER, + .evp_cipher = EVP_aes_256_ctr }, + { .name = "aes-xts", .cipher = CRYPTO_AES_XTS, .type = T_BLKCIPHER, + .evp_cipher = EVP_aes_128_xts }, + { .name = "aes-xts256", .cipher = CRYPTO_AES_XTS, .type = T_BLKCIPHER, + .evp_cipher = EVP_aes_256_xts }, + { .name = "aes-gcm", .cipher = CRYPTO_AES_NIST_GCM_16, + .mac = CRYPTO_AES_128_NIST_GMAC, .type = T_GCM, + .evp_cipher = EVP_aes_128_gcm }, + { .name = "aes-gcm192", .cipher = CRYPTO_AES_NIST_GCM_16, + .mac = CRYPTO_AES_192_NIST_GMAC, .type = T_GCM, + .evp_cipher = EVP_aes_192_gcm }, + { .name = "aes-gcm256", .cipher = CRYPTO_AES_NIST_GCM_16, + .mac = CRYPTO_AES_256_NIST_GMAC, .type = T_GCM, + .evp_cipher = EVP_aes_256_gcm }, +}; + +static bool verbose; +static int crid; +static size_t aad_len; + +static void +usage(void) +{ + fprintf(stderr, + "usage: cryptocheck [-z] [-a algorithm] [-d dev] [size ...]\n"); + exit(1); +} + +static struct alg * +find_alg(const char *name) +{ + u_int i; + + for (i = 0; i < nitems(algs); i++) + if (strcasecmp(algs[i].name, name) == 0) + return (&algs[i]); + return (NULL); +} + +static struct alg * +build_authenc(struct alg *cipher, struct alg *hmac) +{ + static struct alg authenc; + char *name; + + assert(cipher->type == T_BLKCIPHER); + assert(hmac->type == T_HMAC); + memset(&authenc, 0, sizeof(authenc)); + asprintf(&name, "%s+%s", cipher->name, hmac->name); + authenc.name = name; + authenc.cipher = cipher->cipher; + authenc.mac = hmac->mac; + authenc.type = T_AUTHENC; + authenc.evp_cipher = cipher->evp_cipher; + authenc.evp_md = hmac->evp_md; + return (&authenc); +} + +static struct alg * +build_authenc_name(const char *name) +{ + struct alg *cipher, *hmac; + const char *hmac_name; + char *cp, *cipher_name; + + cp = strchr(name, '+'); + cipher_name = strndup(name, cp - name); + hmac_name = cp + 1; + cipher = find_alg(cipher_name); + free(cipher_name); + if (cipher == NULL) + errx(1, "Invalid cipher %s", cipher_name); + hmac = find_alg(hmac_name); + if (hmac == NULL) + errx(1, "Invalid hash %s", hmac_name); + return (build_authenc(cipher, hmac)); +} + +static int +devcrypto(void) +{ + static int fd = -1; + + if (fd < 0) { + fd = open("/dev/crypto", O_RDWR | O_CLOEXEC, 0); + if (fd < 0) + err(1, "/dev/crypto"); + } + return (fd); +} + +static int +crlookup(const char *devname) +{ + struct crypt_find_op find; + + if (strncmp(devname, "soft", 4) == 0) + return CRYPTO_FLAG_SOFTWARE; + + find.crid = -1; + strlcpy(find.name, devname, sizeof(find.name)); + if (ioctl(devcrypto(), CIOCFINDDEV, &find) == -1) + err(1, "ioctl(CIOCFINDDEV)"); + return (find.crid); +} + +const char * +crfind(int crid) +{ + static struct crypt_find_op find; + + if (crid == CRYPTO_FLAG_SOFTWARE) + return ("soft"); + else if (crid == CRYPTO_FLAG_HARDWARE) + return ("unknown"); + + bzero(&find, sizeof(find)); + find.crid = crid; + if (ioctl(devcrypto(), CRIOFINDDEV, &find) == -1) + err(1, "ioctl(CIOCFINDDEV): crid %d", crid); + return (find.name); +} + +static int +crget(void) +{ + int fd; + + if (ioctl(devcrypto(), CRIOGET, &fd) == -1) + err(1, "ioctl(CRIOGET)"); + if (fcntl(fd, F_SETFD, 1) == -1) + err(1, "fcntl(F_SETFD) (crget)"); + return fd; +} + +static char +rdigit(void) +{ + const char a[] = { + 0x10,0x54,0x11,0x48,0x45,0x12,0x4f,0x13,0x49,0x53,0x14,0x41, + 0x15,0x16,0x4e,0x55,0x54,0x17,0x18,0x4a,0x4f,0x42,0x19,0x01 + }; + return 0x20+a[random()%nitems(a)]; +} + +static char * +alloc_buffer(size_t len) +{ + char *buf; + size_t i; + + buf = malloc(len); + for (i = 0; i < len; i++) + buf[i] = rdigit(); + return (buf); +} + +static char * +generate_iv(size_t len, struct alg *alg) +{ + char *iv; + + iv = alloc_buffer(len); + switch (alg->cipher) { + case CRYPTO_AES_ICM: + /* Clear the low 32 bits of the IV to hold the counter. */ + iv[len - 4] = 0; + iv[len - 3] = 0; + iv[len - 2] = 0; + iv[len - 1] = 0; + break; + case CRYPTO_AES_XTS: + /* + * Clear the low 64-bits to only store a 64-bit block + * number. + */ + iv[len - 8] = 0; + iv[len - 7] = 0; + iv[len - 6] = 0; + iv[len - 5] = 0; + iv[len - 4] = 0; + iv[len - 3] = 0; + iv[len - 2] = 0; + iv[len - 1] = 0; + break; + } + return (iv); +} + +static bool +ocf_hmac(struct alg *alg, const char *buffer, size_t size, const char *key, + size_t key_len, char *digest, int *cridp) +{ + struct session2_op sop; + struct crypt_op cop; + int fd; + + memset(&sop, 0, sizeof(sop)); + memset(&cop, 0, sizeof(cop)); + sop.crid = crid; + sop.mackeylen = key_len; + sop.mackey = (char *)key; + sop.mac = alg->mac; + fd = crget(); + if (ioctl(fd, CIOCGSESSION2, &sop) < 0) { + warn("cryptodev %s HMAC not supported for device %s", + alg->name, crfind(crid)); + close(fd); + return (false); + } + + cop.ses = sop.ses; + cop.op = 0; + cop.len = size; + cop.src = (char *)buffer; + cop.dst = NULL; + cop.mac = digest; + cop.iv = NULL; + + if (ioctl(fd, CIOCCRYPT, &cop) < 0) { + warn("cryptodev %s (%zu) HMAC failed for device %s", alg->name, + size, crfind(crid)); + close(fd); + return (false); + } + + if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0) + warn("ioctl(CIOCFSESSION)"); + + close(fd); + *cridp = sop.crid; + return (true); +} + +static void +run_hmac_test(struct alg *alg, size_t size) +{ + const EVP_MD *md; + char *key, *buffer; + u_int key_len, digest_len; + int crid; + char control_digest[EVP_MAX_MD_SIZE], test_digest[EVP_MAX_MD_SIZE]; + + memset(control_digest, 0x3c, sizeof(control_digest)); + memset(test_digest, 0x3c, sizeof(test_digest)); + + md = alg->evp_md(); + key_len = EVP_MD_size(md); + assert(EVP_MD_size(md) <= sizeof(control_digest)); + + key = alloc_buffer(key_len); + buffer = alloc_buffer(size); + + /* OpenSSL HMAC. */ + digest_len = sizeof(control_digest); + if (HMAC(md, key, key_len, (u_char *)buffer, size, + (u_char *)control_digest, &digest_len) == NULL) + errx(1, "OpenSSL %s (%zu) HMAC failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + + /* cryptodev HMAC. */ + if (!ocf_hmac(alg, buffer, size, key, key_len, test_digest, &crid)) + goto out; + if (memcmp(control_digest, test_digest, sizeof(control_digest)) != 0) { + if (memcmp(control_digest, test_digest, EVP_MD_size(md)) == 0) + printf("%s (%zu) mismatch in trailer:\n", + alg->name, size); + else + printf("%s (%zu) mismatch:\n", alg->name, size); + printf("control:\n"); + hexdump(control_digest, sizeof(control_digest), NULL, 0); + printf("test (cryptodev device %s):\n", crfind(crid)); + hexdump(test_digest, sizeof(test_digest), NULL, 0); + goto out; + } + + if (verbose) + printf("%s (%zu) matched (cryptodev device %s)\n", + alg->name, size, crfind(crid)); + +out: + free(buffer); + free(key); +} + +static void +openssl_cipher(struct alg *alg, const EVP_CIPHER *cipher, const char *key, + const char *iv, const char *input, char *output, size_t size, int enc) +{ + EVP_CIPHER_CTX *ctx; + int outl, total; + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + errx(1, "OpenSSL %s (%zu) ctx new failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + if (EVP_CipherInit_ex(ctx, cipher, NULL, (const u_char *)key, + (const u_char *)iv, enc) != 1) + errx(1, "OpenSSL %s (%zu) ctx init failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_set_padding(ctx, 0); + if (EVP_CipherUpdate(ctx, (u_char *)output, &outl, + (const u_char *)input, size) != 1) + errx(1, "OpenSSL %s (%zu) cipher update failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + total = outl; + if (EVP_CipherFinal_ex(ctx, (u_char *)output + outl, &outl) != 1) + errx(1, "OpenSSL %s (%zu) cipher final failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + total += outl; + if (total != size) + errx(1, "OpenSSL %s (%zu) cipher size mismatch: %d", alg->name, + size, total); + EVP_CIPHER_CTX_free(ctx); +} + +static bool +ocf_cipher(struct alg *alg, const char *key, size_t key_len, + const char *iv, const char *input, char *output, size_t size, int enc, + int *cridp) +{ + struct session2_op sop; + struct crypt_op cop; + int fd; + + memset(&sop, 0, sizeof(sop)); + memset(&cop, 0, sizeof(cop)); + sop.crid = crid; + sop.keylen = key_len; + sop.key = (char *)key; + sop.cipher = alg->cipher; + fd = crget(); + if (ioctl(fd, CIOCGSESSION2, &sop) < 0) { + warn("cryptodev %s block cipher not supported for device %s", + alg->name, crfind(crid)); + close(fd); + return (false); + } + + cop.ses = sop.ses; + cop.op = enc ? COP_ENCRYPT : COP_DECRYPT; + cop.len = size; + cop.src = (char *)input; + cop.dst = output; + cop.mac = NULL; + cop.iv = (char *)iv; + + if (ioctl(fd, CIOCCRYPT, &cop) < 0) { + warn("cryptodev %s (%zu) block cipher failed for device %s", + alg->name, size, crfind(crid)); + close(fd); + return (false); + } + + if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0) + warn("ioctl(CIOCFSESSION)"); + + close(fd); + *cridp = sop.crid; + return (true); +} + +static void +run_blkcipher_test(struct alg *alg, size_t size) +{ + const EVP_CIPHER *cipher; + char *buffer, *cleartext, *ciphertext; + char *iv, *key; + u_int iv_len, key_len; + int crid; + + cipher = alg->evp_cipher(); + if (size % EVP_CIPHER_block_size(cipher) != 0) { + if (verbose) + printf( + "%s (%zu): invalid buffer size (block size %d)\n", + alg->name, size, EVP_CIPHER_block_size(cipher)); + return; + } + + key_len = EVP_CIPHER_key_length(cipher); + iv_len = EVP_CIPHER_iv_length(cipher); + + key = alloc_buffer(key_len); + iv = generate_iv(iv_len, alg); + cleartext = alloc_buffer(size); + buffer = malloc(size); + ciphertext = malloc(size); + + /* OpenSSL cipher. */ + openssl_cipher(alg, cipher, key, iv, cleartext, ciphertext, size, 1); + if (size > 0 && memcmp(cleartext, ciphertext, size) == 0) + errx(1, "OpenSSL %s (%zu): cipher text unchanged", alg->name, + size); + openssl_cipher(alg, cipher, key, iv, ciphertext, buffer, size, 0); + if (memcmp(cleartext, buffer, size) != 0) { + printf("OpenSSL %s (%zu): cipher mismatch:", alg->name, size); + printf("original:\n"); + hexdump(cleartext, size, NULL, 0); + printf("decrypted:\n"); + hexdump(buffer, size, NULL, 0); + exit(1); + } + + /* OCF encrypt. */ + if (!ocf_cipher(alg, key, key_len, iv, cleartext, buffer, size, 1, + &crid)) + goto out; + if (memcmp(ciphertext, buffer, size) != 0) { + printf("%s (%zu) encryption mismatch:\n", alg->name, size); + printf("control:\n"); + hexdump(ciphertext, size, NULL, 0); + printf("test (cryptodev device %s):\n", crfind(crid)); + hexdump(buffer, size, NULL, 0); + goto out; + } + + /* OCF decrypt. */ + if (!ocf_cipher(alg, key, key_len, iv, ciphertext, buffer, size, 0, + &crid)) + goto out; + if (memcmp(cleartext, buffer, size) != 0) { + printf("%s (%zu) decryption mismatch:\n", alg->name, size); + printf("control:\n"); + hexdump(cleartext, size, NULL, 0); + printf("test (cryptodev device %s):\n", crfind(crid)); + hexdump(buffer, size, NULL, 0); + goto out; + } + + if (verbose) + printf("%s (%zu) matched (cryptodev device %s)\n", + alg->name, size, crfind(crid)); + +out: + free(ciphertext); + free(buffer); + free(cleartext); + free(iv); + free(key); +} + +static bool +ocf_authenc(struct alg *alg, const char *cipher_key, size_t cipher_key_len, + const char *iv, size_t iv_len, const char *auth_key, size_t auth_key_len, + const char *aad, size_t aad_len, const char *input, char *output, + size_t size, char *digest, int enc, int *cridp) +{ + struct session2_op sop; + int fd; + + memset(&sop, 0, sizeof(sop)); + sop.crid = crid; + sop.keylen = cipher_key_len; + sop.key = (char *)cipher_key; + sop.cipher = alg->cipher; + sop.mackeylen = auth_key_len; + sop.mackey = (char *)auth_key; + sop.mac = alg->mac; + fd = crget(); + if (ioctl(fd, CIOCGSESSION2, &sop) < 0) { + warn("cryptodev %s AUTHENC not supported for device %s", + alg->name, crfind(crid)); + close(fd); + return (false); + } + + if (aad_len != 0) { + struct crypt_aead caead; + + memset(&caead, 0, sizeof(caead)); + caead.ses = sop.ses; + caead.op = enc ? COP_ENCRYPT : COP_DECRYPT; + caead.flags = enc ? COP_F_CIPHER_FIRST : 0; + caead.len = size; + caead.aadlen = aad_len; + caead.ivlen = iv_len; + caead.src = (char *)input; + caead.dst = output; + caead.aad = (char *)aad; + caead.tag = digest; + caead.iv = (char *)iv; + + if (ioctl(fd, CIOCCRYPTAEAD, &caead) < 0) { + warn("cryptodev %s (%zu) failed for device %s", + alg->name, size, crfind(crid)); + close(fd); + return (false); + } + } else { + struct crypt_op cop; + + memset(&cop, 0, sizeof(cop)); + cop.ses = sop.ses; + cop.op = enc ? COP_ENCRYPT : COP_DECRYPT; + cop.flags = enc ? COP_F_CIPHER_FIRST : 0; + cop.len = size; + cop.src = (char *)input; + cop.dst = output; + cop.mac = digest; + cop.iv = (char *)iv; + + if (ioctl(fd, CIOCCRYPT, &cop) < 0) { + warn("cryptodev %s (%zu) AUTHENC failed for device %s", + alg->name, size, crfind(crid)); + close(fd); + return (false); + } + } + + if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0) + warn("ioctl(CIOCFSESSION)"); + + close(fd); + *cridp = sop.crid; + return (true); +} + +static void +run_authenc_test(struct alg *alg, size_t size) +{ + const EVP_CIPHER *cipher; + const EVP_MD *md; + char *aad, *buffer, *cleartext, *ciphertext; + char *iv, *auth_key, *cipher_key; + u_int iv_len, auth_key_len, cipher_key_len, digest_len; + int crid; + char control_digest[EVP_MAX_MD_SIZE], test_digest[EVP_MAX_MD_SIZE]; + + cipher = alg->evp_cipher(); + if (size % EVP_CIPHER_block_size(cipher) != 0) { + if (verbose) + printf( + "%s (%zu): invalid buffer size (block size %d)\n", + alg->name, size, EVP_CIPHER_block_size(cipher)); + return; + } + + memset(control_digest, 0x3c, sizeof(control_digest)); + memset(test_digest, 0x3c, sizeof(test_digest)); + + md = alg->evp_md(); + + cipher_key_len = EVP_CIPHER_key_length(cipher); + iv_len = EVP_CIPHER_iv_length(cipher); + auth_key_len = EVP_MD_size(md); + + cipher_key = alloc_buffer(cipher_key_len); + iv = generate_iv(iv_len, alg); + auth_key = alloc_buffer(auth_key_len); + cleartext = alloc_buffer(aad_len + size); + buffer = malloc(aad_len + size); + ciphertext = malloc(aad_len + size); + + /* OpenSSL encrypt + HMAC. */ + if (aad_len != 0) + memcpy(ciphertext, cleartext, aad_len); + openssl_cipher(alg, cipher, cipher_key, iv, cleartext + aad_len, + ciphertext + aad_len, size, 1); + if (size > 0 && memcmp(cleartext + aad_len, ciphertext + aad_len, + size) == 0) + errx(1, "OpenSSL %s (%zu): cipher text unchanged", alg->name, + size); + digest_len = sizeof(control_digest); + if (HMAC(md, auth_key, auth_key_len, (u_char *)ciphertext, + aad_len + size, (u_char *)control_digest, &digest_len) == NULL) + errx(1, "OpenSSL %s (%zu) HMAC failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + + /* OCF encrypt + HMAC. */ + if (!ocf_authenc(alg, cipher_key, cipher_key_len, iv, iv_len, auth_key, + auth_key_len, aad_len != 0 ? cleartext : NULL, aad_len, + cleartext + aad_len, buffer + aad_len, size, test_digest, 1, &crid)) + goto out; + if (memcmp(ciphertext + aad_len, buffer + aad_len, size) != 0) { + printf("%s (%zu) encryption mismatch:\n", alg->name, size); + printf("control:\n"); + hexdump(ciphertext + aad_len, size, NULL, 0); + printf("test (cryptodev device %s):\n", crfind(crid)); + hexdump(buffer + aad_len, size, NULL, 0); + goto out; + } + if (memcmp(control_digest, test_digest, sizeof(control_digest)) != 0) { + if (memcmp(control_digest, test_digest, EVP_MD_size(md)) == 0) + printf("%s (%zu) enc hash mismatch in trailer:\n", + alg->name, size); + else + printf("%s (%zu) enc hash mismatch:\n", alg->name, + size); + printf("control:\n"); + hexdump(control_digest, sizeof(control_digest), NULL, 0); + printf("test (cryptodev device %s):\n", crfind(crid)); + hexdump(test_digest, sizeof(test_digest), NULL, 0); + goto out; + } + + /* OCF HMAC + decrypt. */ + memset(test_digest, 0x3c, sizeof(test_digest)); + if (!ocf_authenc(alg, cipher_key, cipher_key_len, iv, iv_len, auth_key, + auth_key_len, aad_len != 0 ? ciphertext : NULL, aad_len, + ciphertext + aad_len, buffer + aad_len, size, test_digest, 0, + &crid)) + goto out; + if (memcmp(control_digest, test_digest, sizeof(control_digest)) != 0) { + if (memcmp(control_digest, test_digest, EVP_MD_size(md)) == 0) + printf("%s (%zu) dec hash mismatch in trailer:\n", + alg->name, size); + else + printf("%s (%zu) dec hash mismatch:\n", alg->name, + size); + printf("control:\n"); + hexdump(control_digest, sizeof(control_digest), NULL, 0); + printf("test (cryptodev device %s):\n", crfind(crid)); + hexdump(test_digest, sizeof(test_digest), NULL, 0); + goto out; + } + if (memcmp(cleartext + aad_len, buffer + aad_len, size) != 0) { + printf("%s (%zu) decryption mismatch:\n", alg->name, size); + printf("control:\n"); + hexdump(cleartext, size, NULL, 0); + printf("test (cryptodev device %s):\n", crfind(crid)); + hexdump(buffer, size, NULL, 0); + goto out; + } + + if (verbose) + printf("%s (%zu) matched (cryptodev device %s)\n", + alg->name, size, crfind(crid)); + +out: + free(ciphertext); + free(buffer); + free(cleartext); + free(auth_key); + free(iv); + free(cipher_key); +} + +static void +openssl_gcm_encrypt(struct alg *alg, const EVP_CIPHER *cipher, const char *key, + const char *iv, const char *aad, size_t aad_len, const char *input, + char *output, size_t size, char *tag) +{ + EVP_CIPHER_CTX *ctx; + int outl, total; + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + errx(1, "OpenSSL %s (%zu) ctx new failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + if (EVP_EncryptInit_ex(ctx, cipher, NULL, (const u_char *)key, + (const u_char *)iv) != 1) + errx(1, "OpenSSL %s (%zu) ctx init failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_set_padding(ctx, 0); + if (aad != NULL) { + if (EVP_EncryptUpdate(ctx, NULL, &outl, (const u_char *)aad, + aad_len) != 1) + errx(1, "OpenSSL %s (%zu) aad update failed: %s", + alg->name, size, + ERR_error_string(ERR_get_error(), NULL)); + } + if (EVP_EncryptUpdate(ctx, (u_char *)output, &outl, + (const u_char *)input, size) != 1) + errx(1, "OpenSSL %s (%zu) encrypt update failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + total = outl; + if (EVP_EncryptFinal_ex(ctx, (u_char *)output + outl, &outl) != 1) + errx(1, "OpenSSL %s (%zu) encrypt final failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + total += outl; + if (total != size) + errx(1, "OpenSSL %s (%zu) encrypt size mismatch: %d", alg->name, + size, total); + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, AES_GMAC_HASH_LEN, + tag) != 1) + errx(1, "OpenSSL %s (%zu) get tag failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_free(ctx); +} + +static bool +ocf_gcm(struct alg *alg, const char *key, size_t key_len, const char *iv, + size_t iv_len, const char *aad, size_t aad_len, const char *input, + char *output, size_t size, char *tag, int enc, int *cridp) +{ + struct session2_op sop; + struct crypt_aead caead; + int fd; + + memset(&sop, 0, sizeof(sop)); + memset(&caead, 0, sizeof(caead)); + sop.crid = crid; + sop.keylen = key_len; + sop.key = (char *)key; + sop.cipher = alg->cipher; + sop.mackeylen = key_len; + sop.mackey = (char *)key; + sop.mac = alg->mac; + fd = crget(); + if (ioctl(fd, CIOCGSESSION2, &sop) < 0) { + warn("cryptodev %s not supported for device %s", + alg->name, crfind(crid)); + close(fd); + return (false); + } + + caead.ses = sop.ses; + caead.op = enc ? COP_ENCRYPT : COP_DECRYPT; + caead.len = size; + caead.aadlen = aad_len; + caead.ivlen = iv_len; + caead.src = (char *)input; + caead.dst = output; + caead.aad = (char *)aad; + caead.tag = tag; + caead.iv = (char *)iv; + + if (ioctl(fd, CIOCCRYPTAEAD, &caead) < 0) { + warn("cryptodev %s (%zu) failed for device %s", + alg->name, size, crfind(crid)); + close(fd); + return (false); + } + + if (ioctl(fd, CIOCFSESSION, &sop.ses) < 0) + warn("ioctl(CIOCFSESSION)"); + + close(fd); + *cridp = sop.crid; + return (true); +} + +#ifdef notused +static bool +openssl_gcm_decrypt(struct alg *alg, const EVP_CIPHER *cipher, const char *key, + const char *iv, const char *aad, size_t aad_len, const char *input, + char *output, size_t size, char *tag) +{ + EVP_CIPHER_CTX *ctx; + int outl, total; + bool valid; + + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) + errx(1, "OpenSSL %s (%zu) ctx new failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + if (EVP_DecryptInit_ex(ctx, cipher, NULL, (const u_char *)key, + (const u_char *)iv) != 1) + errx(1, "OpenSSL %s (%zu) ctx init failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + EVP_CIPHER_CTX_set_padding(ctx, 0); + if (aad != NULL) { + if (EVP_DecryptUpdate(ctx, NULL, &outl, (const u_char *)aad, + aad_len) != 1) + errx(1, "OpenSSL %s (%zu) aad update failed: %s", + alg->name, size, + ERR_error_string(ERR_get_error(), NULL)); + } + if (EVP_DecryptUpdate(ctx, (u_char *)output, &outl, + (const u_char *)input, size) != 1) + errx(1, "OpenSSL %s (%zu) decrypt update failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + total = outl; + if (EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, AES_GMAC_HASH_LEN, + tag) != 1) + errx(1, "OpenSSL %s (%zu) get tag failed: %s", alg->name, + size, ERR_error_string(ERR_get_error(), NULL)); + valid = (EVP_DecryptFinal_ex(ctx, (u_char *)output + outl, &outl) != 1); + total += outl; + if (total != size) + errx(1, "OpenSSL %s (%zu) decrypt size mismatch: %d", alg->name, + size, total); + EVP_CIPHER_CTX_free(ctx); + return (valid); +} +#endif + +static void +run_gcm_test(struct alg *alg, size_t size) +{ + const EVP_CIPHER *cipher; + char *aad, *buffer, *cleartext, *ciphertext; + char *iv, *key; + u_int iv_len, key_len; + int crid; + char control_tag[AES_GMAC_HASH_LEN], test_tag[AES_GMAC_HASH_LEN]; + + cipher = alg->evp_cipher(); + if (size % EVP_CIPHER_block_size(cipher) != 0) { + if (verbose) + printf( + "%s (%zu): invalid buffer size (block size %d)\n", + alg->name, size, EVP_CIPHER_block_size(cipher)); + return; + } + + memset(control_tag, 0x3c, sizeof(control_tag)); + memset(test_tag, 0x3c, sizeof(test_tag)); + + key_len = EVP_CIPHER_key_length(cipher); + iv_len = EVP_CIPHER_iv_length(cipher); + + key = alloc_buffer(key_len); + iv = generate_iv(iv_len, alg); *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***