/* $NetBSD: cgd_crypto.c,v 1.27 2020/07/25 22:14:35 riastradh Exp $ */ /*- * Copyright (c) 2002 The NetBSD Foundation, Inc. * All rights reserved. * * This code is derived from software contributed to The NetBSD Foundation * by Roland C. Dowdeswell. * * 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. */ /* * Crypto Framework For cgd.c * * This framework is temporary and awaits a more complete * kernel wide crypto implementation. */ #include __KERNEL_RCSID(0, "$NetBSD: cgd_crypto.c,v 1.27 2020/07/25 22:14:35 riastradh Exp $"); #include #include #include #include #include #include #include #include #include #include /* * The general framework provides only one generic function. * It takes the name of an algorithm and returns a struct cryptfuncs * * for it. It is up to the initialisation routines of the algorithm * to check key size and block size. */ static cfunc_init cgd_cipher_aes_cbc_init; static cfunc_destroy cgd_cipher_aes_cbc_destroy; static cfunc_cipher cgd_cipher_aes_cbc; static cfunc_init cgd_cipher_aes_xts_init; static cfunc_destroy cgd_cipher_aes_xts_destroy; static cfunc_cipher cgd_cipher_aes_xts; static cfunc_init cgd_cipher_3des_init; static cfunc_destroy cgd_cipher_3des_destroy; static cfunc_cipher cgd_cipher_3des_cbc; static cfunc_init cgd_cipher_bf_init; static cfunc_destroy cgd_cipher_bf_destroy; static cfunc_cipher cgd_cipher_bf_cbc; static cfunc_init cgd_cipher_adiantum_init; static cfunc_destroy cgd_cipher_adiantum_destroy; static cfunc_cipher cgd_cipher_adiantum_crypt; static const struct cryptfuncs cf[] = { { .cf_name = "aes-xts", .cf_init = cgd_cipher_aes_xts_init, .cf_destroy = cgd_cipher_aes_xts_destroy, .cf_cipher = cgd_cipher_aes_xts, }, { .cf_name = "aes-cbc", .cf_init = cgd_cipher_aes_cbc_init, .cf_destroy = cgd_cipher_aes_cbc_destroy, .cf_cipher = cgd_cipher_aes_cbc, }, { .cf_name = "3des-cbc", .cf_init = cgd_cipher_3des_init, .cf_destroy = cgd_cipher_3des_destroy, .cf_cipher = cgd_cipher_3des_cbc, }, { .cf_name = "blowfish-cbc", .cf_init = cgd_cipher_bf_init, .cf_destroy = cgd_cipher_bf_destroy, .cf_cipher = cgd_cipher_bf_cbc, }, { .cf_name = "adiantum", .cf_init = cgd_cipher_adiantum_init, .cf_destroy = cgd_cipher_adiantum_destroy, .cf_cipher = cgd_cipher_adiantum_crypt, }, }; const struct cryptfuncs * cryptfuncs_find(const char *alg) { for (size_t i = 0; i < __arraycount(cf); i++) if (strcmp(cf[i].cf_name, alg) == 0) return &cf[i]; return NULL; } /* * AES Framework */ struct aes_privdata { struct aesenc ap_enckey; struct aesdec ap_deckey; uint32_t ap_nrounds; }; static void * cgd_cipher_aes_cbc_init(size_t keylen, const void *key, size_t *blocksize) { struct aes_privdata *ap; if (!blocksize) return NULL; if (keylen != 128 && keylen != 192 && keylen != 256) return NULL; if (*blocksize == (size_t)-1) *blocksize = 128; if (*blocksize != 128) return NULL; ap = kmem_zalloc(sizeof(*ap), KM_SLEEP); switch (keylen) { case 128: aes_setenckey128(&ap->ap_enckey, key); aes_setdeckey128(&ap->ap_deckey, key); ap->ap_nrounds = AES_128_NROUNDS; break; case 192: aes_setenckey192(&ap->ap_enckey, key); aes_setdeckey192(&ap->ap_deckey, key); ap->ap_nrounds = AES_192_NROUNDS; break; case 256: aes_setenckey256(&ap->ap_enckey, key); aes_setdeckey256(&ap->ap_deckey, key); ap->ap_nrounds = AES_256_NROUNDS; break; } return ap; } static void cgd_cipher_aes_cbc_destroy(void *data) { struct aes_privdata *apd = data; explicit_memset(apd, 0, sizeof(*apd)); kmem_free(apd, sizeof(*apd)); } static void cgd_cipher_aes_cbc(void *privdata, void *dst, const void *src, size_t nbytes, const void *blkno, int dir) { struct aes_privdata *apd = privdata; uint8_t iv[CGD_AES_BLOCK_SIZE] __aligned(CGD_AES_BLOCK_SIZE) = {0}; /* Compute the CBC IV as AES_k(blkno). */ aes_enc(&apd->ap_enckey, blkno, iv, apd->ap_nrounds); switch (dir) { case CGD_CIPHER_ENCRYPT: aes_cbc_enc(&apd->ap_enckey, src, dst, nbytes, iv, apd->ap_nrounds); break; case CGD_CIPHER_DECRYPT: aes_cbc_dec(&apd->ap_deckey, src, dst, nbytes, iv, apd->ap_nrounds); break; default: panic("%s: unrecognised direction %d", __func__, dir); } } /* * AES-XTS */ struct aesxts { struct aesenc ax_enckey; struct aesdec ax_deckey; struct aesenc ax_tweakkey; uint32_t ax_nrounds; }; static void * cgd_cipher_aes_xts_init(size_t keylen, const void *xtskey, size_t *blocksize) { struct aesxts *ax; const char *key, *key2; /* XTS key is made of two AES keys. */ if (!blocksize) return NULL; if (keylen != 256 && keylen != 512) return NULL; if (*blocksize == (size_t)-1) *blocksize = 128; if (*blocksize != 128) return NULL; ax = kmem_zalloc(sizeof(*ax), KM_SLEEP); keylen /= 2; key = xtskey; key2 = key + keylen / CHAR_BIT; switch (keylen) { case 128: aes_setenckey128(&ax->ax_enckey, key); aes_setdeckey128(&ax->ax_deckey, key); aes_setenckey128(&ax->ax_tweakkey, key2); ax->ax_nrounds = AES_128_NROUNDS; break; case 256: aes_setenckey256(&ax->ax_enckey, key); aes_setdeckey256(&ax->ax_deckey, key); aes_setenckey256(&ax->ax_tweakkey, key2); ax->ax_nrounds = AES_256_NROUNDS; break; } return ax; } static void cgd_cipher_aes_xts_destroy(void *cookie) { struct aesxts *ax = cookie; explicit_memset(ax, 0, sizeof(*ax)); kmem_free(ax, sizeof(*ax)); } static void cgd_cipher_aes_xts(void *cookie, void *dst, const void *src, size_t nbytes, const void *blkno, int dir) { struct aesxts *ax = cookie; uint8_t tweak[CGD_AES_BLOCK_SIZE]; /* Compute the initial tweak as AES_k(blkno). */ aes_enc(&ax->ax_tweakkey, blkno, tweak, ax->ax_nrounds); switch (dir) { case CGD_CIPHER_ENCRYPT: aes_xts_enc(&ax->ax_enckey, src, dst, nbytes, tweak, ax->ax_nrounds); break; case CGD_CIPHER_DECRYPT: aes_xts_dec(&ax->ax_deckey, src, dst, nbytes, tweak, ax->ax_nrounds); break; default: panic("%s: unrecognised direction %d", __func__, dir); } } /* * 3DES Framework */ struct c3des_privdata { des_key_schedule cp_key1; des_key_schedule cp_key2; des_key_schedule cp_key3; }; static void * cgd_cipher_3des_init(size_t keylen, const void *key, size_t *blocksize) { struct c3des_privdata *cp; int error = 0; des_cblock *block; if (!blocksize) return NULL; if (*blocksize == (size_t)-1) *blocksize = 64; if (keylen != (DES_KEY_SZ * 3 * 8) || *blocksize != 64) return NULL; cp = kmem_zalloc(sizeof(*cp), KM_SLEEP); block = __UNCONST(key); error = des_key_sched(block, cp->cp_key1); error |= des_key_sched(block + 1, cp->cp_key2); error |= des_key_sched(block + 2, cp->cp_key3); if (error) { explicit_memset(cp, 0, sizeof(*cp)); kmem_free(cp, sizeof(*cp)); return NULL; } return cp; } static void cgd_cipher_3des_destroy(void *data) { struct c3des_privdata *cp = data; explicit_memset(cp, 0, sizeof(*cp)); kmem_free(cp, sizeof(*cp)); } static void cgd_cipher_3des_cbc(void *privdata, void *dst, const void *src, size_t nbytes, const void *blkno, int dir) { struct c3des_privdata *cp = privdata; des_cblock zero; uint8_t iv[CGD_3DES_BLOCK_SIZE]; /* Compute the CBC IV as 3DES_k(blkno) = 3DES-CBC_k(iv=blkno, 0). */ memset(&zero, 0, sizeof(zero)); des_ede3_cbc_encrypt(blkno, iv, CGD_3DES_BLOCK_SIZE, cp->cp_key1, cp->cp_key2, cp->cp_key3, &zero, /*encrypt*/1); switch (dir) { case CGD_CIPHER_ENCRYPT: des_ede3_cbc_encrypt(src, dst, nbytes, cp->cp_key1, cp->cp_key2, cp->cp_key3, (des_cblock *)iv, /*encrypt*/1); break; case CGD_CIPHER_DECRYPT: des_ede3_cbc_encrypt(src, dst, nbytes, cp->cp_key1, cp->cp_key2, cp->cp_key3, (des_cblock *)iv, /*encrypt*/0); break; default: panic("%s: unrecognised direction %d", __func__, dir); } } /* * Blowfish Framework */ struct bf_privdata { BF_KEY bp_key; }; struct bf_encdata { BF_KEY *be_key; uint8_t be_iv[CGD_BF_BLOCK_SIZE]; }; static void * cgd_cipher_bf_init(size_t keylen, const void *key, size_t *blocksize) { struct bf_privdata *bp; if (!blocksize) return NULL; if (keylen < 40 || keylen > 448 || (keylen % 8 != 0)) return NULL; if (*blocksize == (size_t)-1) *blocksize = 64; if (*blocksize != 64) return NULL; bp = kmem_zalloc(sizeof(*bp), KM_SLEEP); if (!bp) return NULL; BF_set_key(&bp->bp_key, keylen / 8, key); return bp; } static void cgd_cipher_bf_destroy(void *data) { struct bf_privdata *bp = data; explicit_memset(bp, 0, sizeof(*bp)); kmem_free(bp, sizeof(*bp)); } static void cgd_cipher_bf_cbc(void *privdata, void *dst, const void *src, size_t nbytes, const void *blkno, int dir) { struct bf_privdata *bp = privdata; uint8_t zero[CGD_BF_BLOCK_SIZE], iv[CGD_BF_BLOCK_SIZE]; /* Compute the CBC IV as Blowfish_k(blkno) = BF_CBC_k(blkno, 0). */ memset(zero, 0, sizeof(zero)); BF_cbc_encrypt(blkno, iv, CGD_BF_BLOCK_SIZE, &bp->bp_key, zero, /*encrypt*/1); switch (dir) { case CGD_CIPHER_ENCRYPT: BF_cbc_encrypt(src, dst, nbytes, &bp->bp_key, iv, /*encrypt*/1); break; case CGD_CIPHER_DECRYPT: BF_cbc_encrypt(src, dst, nbytes, &bp->bp_key, iv, /*encrypt*/0); break; default: panic("%s: unrecognised direction %d", __func__, dir); } } /* * Adiantum */ static void * cgd_cipher_adiantum_init(size_t keylen, const void *key, size_t *blocksize) { struct adiantum *A; if (!blocksize) return NULL; if (keylen != 256) return NULL; if (*blocksize == (size_t)-1) *blocksize = 128; if (*blocksize != 128) return NULL; A = kmem_zalloc(sizeof(*A), KM_SLEEP); adiantum_init(A, key); return A; } static void cgd_cipher_adiantum_destroy(void *cookie) { struct adiantum *A = cookie; explicit_memset(A, 0, sizeof(*A)); kmem_free(A, sizeof(*A)); } static void cgd_cipher_adiantum_crypt(void *cookie, void *dst, const void *src, size_t nbytes, const void *blkno, int dir) { /* * Treat the block number as a 128-bit block. This is more * than twice as big as the largest number of reasonable * blocks, but it doesn't hurt (it would be rounded up to a * 128-bit input anyway). */ const unsigned tweaklen = 16; struct adiantum *A = cookie; switch (dir) { case CGD_CIPHER_ENCRYPT: adiantum_enc(dst, src, nbytes, blkno, tweaklen, A); break; case CGD_CIPHER_DECRYPT: adiantum_dec(dst, src, nbytes, blkno, tweaklen, A); break; default: panic("%s: unrecognised direction %d", __func__, dir); } }