xref: /openbsd-src/sys/uvm/uvm_swap_encrypt.c (revision 4af3577ff281f32bc2b40d899fa25046271de1d3)
1*4af3577fSjsg /*	$OpenBSD: uvm_swap_encrypt.c,v 1.24 2021/03/12 14:15:49 jsg Exp $	*/
2133306f0Sniklas 
308914706Sprovos /*
408914706Sprovos  * Copyright 1999 Niels Provos <provos@citi.umich.edu>
508914706Sprovos  * All rights reserved.
608914706Sprovos  *
708914706Sprovos  * Redistribution and use in source and binary forms, with or without
808914706Sprovos  * modification, are permitted provided that the following conditions
908914706Sprovos  * are met:
1008914706Sprovos  * 1. Redistributions of source code must retain the above copyright
1108914706Sprovos  *    notice, this list of conditions and the following disclaimer.
1208914706Sprovos  * 2. Redistributions in binary form must reproduce the above copyright
1308914706Sprovos  *    notice, this list of conditions and the following disclaimer in the
1408914706Sprovos  *    documentation and/or other materials provided with the distribution.
1508914706Sprovos  * 3. All advertising materials mentioning features or use of this software
1608914706Sprovos  *    must display the following acknowledgement:
1708914706Sprovos  *      This product includes software developed by Niels Provos.
1808914706Sprovos  * 4. The name of the author may not be used to endorse or promote products
1908914706Sprovos  *    derived from this software without specific prior written permission.
2008914706Sprovos  *
2108914706Sprovos  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
2208914706Sprovos  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
2308914706Sprovos  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
2408914706Sprovos  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
2508914706Sprovos  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2608914706Sprovos  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2708914706Sprovos  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2808914706Sprovos  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2908914706Sprovos  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
3008914706Sprovos  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3108914706Sprovos  */
3208914706Sprovos 
3308914706Sprovos #include <sys/param.h>
3408914706Sprovos #include <sys/systm.h>
356b24b8a0Sprovos #include <sys/kernel.h>
366b24b8a0Sprovos #include <sys/malloc.h>
378a1f2f9eSprovos #include <sys/sysctl.h>
386b24b8a0Sprovos #include <sys/time.h>
396b24b8a0Sprovos #include <crypto/rijndael.h>
4008914706Sprovos 
416b24b8a0Sprovos #include <uvm/uvm.h>
4289602861Sdlg #include <uvm/uvm_swap_encrypt.h>
436b24b8a0Sprovos 
446b24b8a0Sprovos struct swap_key *kcur = NULL;
455076b47dSmarkus rijndael_ctx swap_ctxt;
4608914706Sprovos 
47c4c74878Sderaadt int uvm_doswapencrypt = 1;
486b24b8a0Sprovos u_int uvm_swpkeyscreated = 0;
496b24b8a0Sprovos u_int uvm_swpkeysdeleted = 0;
506b24b8a0Sprovos 
51f92e66c2Snate int swap_encrypt_initialized = 0;
5208914706Sprovos 
538a1f2f9eSprovos int
swap_encrypt_ctl(int * name,u_int namelen,void * oldp,size_t * oldlenp,void * newp,size_t newlen,struct proc * p)542670cea8Shshoexer swap_encrypt_ctl(int *name, u_int namelen, void *oldp, size_t *oldlenp,
552670cea8Shshoexer     void *newp, size_t newlen, struct proc *p)
568a1f2f9eSprovos {
578a1f2f9eSprovos 	/* all sysctl names at this level are terminal */
588a1f2f9eSprovos 	if (namelen != 1)
598a1f2f9eSprovos 		return (ENOTDIR);		/* overloaded */
608a1f2f9eSprovos 
618a1f2f9eSprovos 	switch (name[0]) {
628a1f2f9eSprovos 	case SWPENC_ENABLE: {
638a1f2f9eSprovos 		int doencrypt = uvm_doswapencrypt;
648a1f2f9eSprovos 		int result;
658a1f2f9eSprovos 
660b905610Sgnezdo 		result = sysctl_int_bounded(oldp, oldlenp, newp, newlen,
670b905610Sgnezdo 		    &doencrypt, 0, 1);
688a1f2f9eSprovos 		if (result)
698a1f2f9eSprovos 			return result;
708a1f2f9eSprovos 
712670cea8Shshoexer 		/*
722670cea8Shshoexer 		 * Swap Encryption has been turned on, we need to
73f92e66c2Snate 		 * initialize state for swap devices that have been
742670cea8Shshoexer 		 * added.
758a1f2f9eSprovos 		 */
768a1f2f9eSprovos 		if (doencrypt)
778a1f2f9eSprovos 			uvm_swap_initcrypt_all();
788a1f2f9eSprovos 		uvm_doswapencrypt = doencrypt;
798a1f2f9eSprovos 		return (0);
808a1f2f9eSprovos 	}
818a1f2f9eSprovos 	case SWPENC_CREATED:
828a1f2f9eSprovos 		return (sysctl_rdint(oldp, oldlenp, newp, uvm_swpkeyscreated));
838a1f2f9eSprovos 	case SWPENC_DELETED:
848a1f2f9eSprovos 		return (sysctl_rdint(oldp, oldlenp, newp, uvm_swpkeysdeleted));
858a1f2f9eSprovos 	default:
868a1f2f9eSprovos 		return (EOPNOTSUPP);
878a1f2f9eSprovos 	}
888a1f2f9eSprovos 	/* NOTREACHED */
898a1f2f9eSprovos }
908a1f2f9eSprovos 
9108914706Sprovos void
swap_key_create(struct swap_key * key)9205db9060Sdlg swap_key_create(struct swap_key *key)
9305db9060Sdlg {
9405db9060Sdlg 	arc4random_buf(key->key, sizeof(key->key));
9505db9060Sdlg 	uvm_swpkeyscreated++;
9605db9060Sdlg }
9705db9060Sdlg 
9805db9060Sdlg void
swap_key_delete(struct swap_key * key)996b24b8a0Sprovos swap_key_delete(struct swap_key *key)
1006b24b8a0Sprovos {
1016b24b8a0Sprovos 	/* Make sure that this key gets removed if we just used it */
1026b24b8a0Sprovos 	swap_key_cleanup(key);
1036b24b8a0Sprovos 
104fa2d22afSderaadt 	explicit_bzero(key, sizeof(*key));
1056b24b8a0Sprovos 	uvm_swpkeysdeleted++;
10608914706Sprovos }
10708914706Sprovos 
10808914706Sprovos /*
10908914706Sprovos  * Encrypt the data before it goes to swap, the size should be 64-bit
11008914706Sprovos  * aligned.
11108914706Sprovos  */
11208914706Sprovos 
11308914706Sprovos void
swap_encrypt(struct swap_key * key,caddr_t src,caddr_t dst,u_int64_t block,size_t count)1142670cea8Shshoexer swap_encrypt(struct swap_key *key, caddr_t src, caddr_t dst, u_int64_t block,
1152670cea8Shshoexer     size_t count)
11608914706Sprovos {
11708914706Sprovos 	u_int32_t *dsrc = (u_int32_t *)src;
11808914706Sprovos 	u_int32_t *ddst = (u_int32_t *)dst;
1196b24b8a0Sprovos 	u_int32_t iv[4];
1206b24b8a0Sprovos 	u_int32_t iv1, iv2, iv3, iv4;
12108914706Sprovos 
122f92e66c2Snate 	if (!swap_encrypt_initialized)
123f92e66c2Snate 		swap_encrypt_initialized = 1;
1246b24b8a0Sprovos 
1256b24b8a0Sprovos 	swap_key_prepare(key, 1);
12608914706Sprovos 
12708914706Sprovos 	count /= sizeof(u_int32_t);
12808914706Sprovos 
1296b24b8a0Sprovos 	iv[0] = block >> 32; iv[1] = block; iv[2] = ~iv[0]; iv[3] = ~iv[1];
1305076b47dSmarkus 	rijndael_encrypt(&swap_ctxt, (u_char *)iv, (u_char *)iv);
1316b24b8a0Sprovos 	iv1 = iv[0]; iv2 = iv[1]; iv3 = iv[2]; iv4 = iv[3];
1326b24b8a0Sprovos 
1336b24b8a0Sprovos 	for (; count > 0; count -= 4) {
13408914706Sprovos 		ddst[0] = dsrc[0] ^ iv1;
13508914706Sprovos 		ddst[1] = dsrc[1] ^ iv2;
1366b24b8a0Sprovos 		ddst[2] = dsrc[2] ^ iv3;
1376b24b8a0Sprovos 		ddst[3] = dsrc[3] ^ iv4;
13808914706Sprovos 		/*
139*4af3577fSjsg 		 * Do not worry about endianness, it only needs to decrypt
1402670cea8Shshoexer 		 * on this machine.
14108914706Sprovos 		 */
1425076b47dSmarkus 		rijndael_encrypt(&swap_ctxt, (u_char *)ddst, (u_char *)ddst);
14308914706Sprovos 		iv1 = ddst[0];
14408914706Sprovos 		iv2 = ddst[1];
1456b24b8a0Sprovos 		iv3 = ddst[2];
1466b24b8a0Sprovos 		iv4 = ddst[3];
14708914706Sprovos 
1486b24b8a0Sprovos 		dsrc += 4;
1496b24b8a0Sprovos 		ddst += 4;
15008914706Sprovos 	}
15108914706Sprovos }
15208914706Sprovos 
15308914706Sprovos /*
15408914706Sprovos  * Decrypt the data after we retrieved it from swap, the size should be 64-bit
15508914706Sprovos  * aligned.
15608914706Sprovos  */
15708914706Sprovos 
15808914706Sprovos void
swap_decrypt(struct swap_key * key,caddr_t src,caddr_t dst,u_int64_t block,size_t count)1592670cea8Shshoexer swap_decrypt(struct swap_key *key, caddr_t src, caddr_t dst, u_int64_t block,
1602670cea8Shshoexer     size_t count)
16108914706Sprovos {
16208914706Sprovos 	u_int32_t *dsrc = (u_int32_t *)src;
16308914706Sprovos 	u_int32_t *ddst = (u_int32_t *)dst;
1646b24b8a0Sprovos 	u_int32_t iv[4];
1656b24b8a0Sprovos 	u_int32_t iv1, iv2, iv3, iv4, niv1, niv2, niv3, niv4;
16608914706Sprovos 
167f92e66c2Snate 	if (!swap_encrypt_initialized)
168f92e66c2Snate 		panic("swap_decrypt: key not initialized");
16908914706Sprovos 
1706b24b8a0Sprovos 	swap_key_prepare(key, 0);
1716b24b8a0Sprovos 
17208914706Sprovos 	count /= sizeof(u_int32_t);
17308914706Sprovos 
1746b24b8a0Sprovos 	iv[0] = block >> 32; iv[1] = block; iv[2] = ~iv[0]; iv[3] = ~iv[1];
1755076b47dSmarkus 	rijndael_encrypt(&swap_ctxt, (u_char *)iv, (u_char *)iv);
1766b24b8a0Sprovos 	iv1 = iv[0]; iv2 = iv[1]; iv3 = iv[2]; iv4 = iv[3];
1776b24b8a0Sprovos 
1786b24b8a0Sprovos 	for (; count > 0; count -= 4) {
17908914706Sprovos 		ddst[0] = niv1 = dsrc[0];
18008914706Sprovos 		ddst[1] = niv2 = dsrc[1];
1816b24b8a0Sprovos 		ddst[2] = niv3 = dsrc[2];
1826b24b8a0Sprovos 		ddst[3] = niv4 = dsrc[3];
1835076b47dSmarkus 		rijndael_decrypt(&swap_ctxt, (u_char *)ddst, (u_char *)ddst);
18408914706Sprovos 		ddst[0] ^= iv1;
18508914706Sprovos 		ddst[1] ^= iv2;
1866b24b8a0Sprovos 		ddst[2] ^= iv3;
1876b24b8a0Sprovos 		ddst[3] ^= iv4;
18808914706Sprovos 
18908914706Sprovos 		iv1 = niv1;
19008914706Sprovos 		iv2 = niv2;
1916b24b8a0Sprovos 		iv3 = niv3;
1926b24b8a0Sprovos 		iv4 = niv4;
19308914706Sprovos 
1946b24b8a0Sprovos 		dsrc += 4;
1956b24b8a0Sprovos 		ddst += 4;
19608914706Sprovos 	}
19708914706Sprovos }
1986b24b8a0Sprovos 
1996b24b8a0Sprovos void
swap_key_prepare(struct swap_key * key,int encrypt)2006b24b8a0Sprovos swap_key_prepare(struct swap_key *key, int encrypt)
2016b24b8a0Sprovos {
2022670cea8Shshoexer 	/*
2032670cea8Shshoexer 	 * Check if we have prepared for this key already,
2046b24b8a0Sprovos 	 * if we only have the encryption schedule, we have
2052670cea8Shshoexer 	 * to recompute and get the decryption schedule also.
2066b24b8a0Sprovos 	 */
2075076b47dSmarkus 	if (kcur == key && (encrypt || !swap_ctxt.enc_only))
2086b24b8a0Sprovos 		return;
2096b24b8a0Sprovos 
2105076b47dSmarkus 	if (encrypt)
2115076b47dSmarkus 		rijndael_set_key_enc_only(&swap_ctxt, (u_char *)key->key,
2125076b47dSmarkus 		    sizeof(key->key) * 8);
2135076b47dSmarkus 	else
2145076b47dSmarkus 		rijndael_set_key(&swap_ctxt, (u_char *)key->key,
2155076b47dSmarkus 		    sizeof(key->key) * 8);
2166b24b8a0Sprovos 
2176b24b8a0Sprovos 	kcur = key;
2186b24b8a0Sprovos }
2196b24b8a0Sprovos 
2206b24b8a0Sprovos /*
2216b24b8a0Sprovos  * Make sure that a specific key is no longer available.
2226b24b8a0Sprovos  */
2236b24b8a0Sprovos 
2246b24b8a0Sprovos void
swap_key_cleanup(struct swap_key * key)2256b24b8a0Sprovos swap_key_cleanup(struct swap_key *key)
2266b24b8a0Sprovos {
2276b24b8a0Sprovos 	/* Check if we have a key */
2286b24b8a0Sprovos 	if (kcur == NULL || kcur != key)
2296b24b8a0Sprovos 		return;
2306b24b8a0Sprovos 
2316b24b8a0Sprovos 	/* Zero out the subkeys */
232fa2d22afSderaadt 	explicit_bzero(&swap_ctxt, sizeof(swap_ctxt));
2336b24b8a0Sprovos 
2346b24b8a0Sprovos 	kcur = NULL;
2356b24b8a0Sprovos }
236