xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb3_encrypt_kcf.c (revision eba274b9392a53f991f6ca04d624533172d46fd4)
11160dcf7SMatt Barden /*
21160dcf7SMatt Barden  * This file and its contents are supplied under the terms of the
31160dcf7SMatt Barden  * Common Development and Distribution License ("CDDL"), version 1.0.
41160dcf7SMatt Barden  * You may only use this file in accordance with the terms of version
51160dcf7SMatt Barden  * 1.0 of the CDDL.
61160dcf7SMatt Barden  *
71160dcf7SMatt Barden  * A full copy of the text of the CDDL should have accompanied this
81160dcf7SMatt Barden  * source.  A copy of the CDDL is also available via the Internet at
91160dcf7SMatt Barden  * http://www.illumos.org/license/CDDL.
101160dcf7SMatt Barden  */
111160dcf7SMatt Barden 
121160dcf7SMatt Barden /*
131160dcf7SMatt Barden  * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
144e065a9fSAlexander Stetsenko  * Copyright 2020 RackTop Systems, Inc.
151160dcf7SMatt Barden  */
161160dcf7SMatt Barden 
171160dcf7SMatt Barden /*
181160dcf7SMatt Barden  * Helper functions for SMB3 encryption using the
191160dcf7SMatt Barden  * Kernel Cryptographic Framework (KCF)
201160dcf7SMatt Barden  *
211160dcf7SMatt Barden  * There are two implementations of these functions:
221160dcf7SMatt Barden  * This one (for kernel) and another for user space:
231160dcf7SMatt Barden  * See: lib/smbsrv/libfksmbsrv/common/fksmb_encrypt_pkcs.c
241160dcf7SMatt Barden  */
251160dcf7SMatt Barden 
261160dcf7SMatt Barden #include <sys/crypto/api.h>
271160dcf7SMatt Barden #include <smbsrv/smb_kcrypt.h>
281160dcf7SMatt Barden #include <smbsrv/smb2_kproto.h>
291160dcf7SMatt Barden #include <sys/cmn_err.h>
301160dcf7SMatt Barden 
311160dcf7SMatt Barden /*
324e065a9fSAlexander Stetsenko  * Common function to see if a mech is available.
334e065a9fSAlexander Stetsenko  */
344e065a9fSAlexander Stetsenko static int
35*eba274b9SToomas Soome find_mech(smb_crypto_mech_t *mech, const char *name)
364e065a9fSAlexander Stetsenko {
374e065a9fSAlexander Stetsenko 	crypto_mech_type_t t;
384e065a9fSAlexander Stetsenko 
394e065a9fSAlexander Stetsenko 	t = crypto_mech2id(name);
404e065a9fSAlexander Stetsenko 	if (t == CRYPTO_MECH_INVALID) {
414e065a9fSAlexander Stetsenko 		cmn_err(CE_NOTE, "smb: no kcf mech: %s", name);
424e065a9fSAlexander Stetsenko 		return (-1);
434e065a9fSAlexander Stetsenko 	}
444e065a9fSAlexander Stetsenko 	mech->cm_type = t;
454e065a9fSAlexander Stetsenko 	return (0);
464e065a9fSAlexander Stetsenko }
474e065a9fSAlexander Stetsenko 
484e065a9fSAlexander Stetsenko /*
491160dcf7SMatt Barden  * SMB3 encryption helpers:
501160dcf7SMatt Barden  * (getmech, init, update, final)
511160dcf7SMatt Barden  */
521160dcf7SMatt Barden 
531160dcf7SMatt Barden int
544e065a9fSAlexander Stetsenko smb3_aes_ccm_getmech(smb_crypto_mech_t *mech)
551160dcf7SMatt Barden {
564e065a9fSAlexander Stetsenko 	return (find_mech(mech, SUN_CKM_AES_CCM));
571160dcf7SMatt Barden }
581160dcf7SMatt Barden 
594e065a9fSAlexander Stetsenko int
604e065a9fSAlexander Stetsenko smb3_aes_gcm_getmech(smb_crypto_mech_t *mech)
614e065a9fSAlexander Stetsenko {
624e065a9fSAlexander Stetsenko 	return (find_mech(mech, SUN_CKM_AES_GCM));
631160dcf7SMatt Barden }
641160dcf7SMatt Barden 
651160dcf7SMatt Barden void
664e065a9fSAlexander Stetsenko smb3_crypto_init_ccm_param(smb3_crypto_param_t *param,
671160dcf7SMatt Barden     uint8_t *nonce, size_t noncesize, uint8_t *auth, size_t authsize,
681160dcf7SMatt Barden     size_t datasize)
691160dcf7SMatt Barden {
704e065a9fSAlexander Stetsenko 	param->ccm.ulMACSize = SMB2_SIG_SIZE;
714e065a9fSAlexander Stetsenko 	param->ccm.ulNonceSize = noncesize;
724e065a9fSAlexander Stetsenko 	param->ccm.nonce = nonce;
734e065a9fSAlexander Stetsenko 	param->ccm.ulDataSize = datasize;
744e065a9fSAlexander Stetsenko 	param->ccm.ulAuthDataSize = authsize;
754e065a9fSAlexander Stetsenko 	param->ccm.authData = auth;
764e065a9fSAlexander Stetsenko }
774e065a9fSAlexander Stetsenko 
784e065a9fSAlexander Stetsenko void
794e065a9fSAlexander Stetsenko smb3_crypto_init_gcm_param(smb3_crypto_param_t *param,
804e065a9fSAlexander Stetsenko     uint8_t *nonce, size_t noncesize, uint8_t *auth, size_t authsize)
814e065a9fSAlexander Stetsenko {
824e065a9fSAlexander Stetsenko 	ASSERT3U(noncesize, ==, 12);
834e065a9fSAlexander Stetsenko 	param->gcm.pIv = nonce;
844e065a9fSAlexander Stetsenko 	param->gcm.ulIvLen = noncesize;		/* should be 12 bytes */
854e065a9fSAlexander Stetsenko 	/* tform hdr size - (protcolo id + signing) == 32 bytes */
864e065a9fSAlexander Stetsenko 	param->gcm.ulTagBits = SMB2_SIG_SIZE << 3; /* convert bytes to bits */
874e065a9fSAlexander Stetsenko 	param->gcm.pAAD = auth;			/* auth data */
884e065a9fSAlexander Stetsenko 	param->gcm.ulAADLen = authsize;		/* auth data len */
891160dcf7SMatt Barden }
901160dcf7SMatt Barden 
911160dcf7SMatt Barden /*
921160dcf7SMatt Barden  * Start the KCF session, load the key
931160dcf7SMatt Barden  */
941160dcf7SMatt Barden static int
951160dcf7SMatt Barden smb3_crypto_init(smb3_enc_ctx_t *ctxp, smb_crypto_mech_t *mech,
961160dcf7SMatt Barden     uint8_t *key, size_t key_len, smb3_crypto_param_t *param,
971160dcf7SMatt Barden     boolean_t is_encrypt)
981160dcf7SMatt Barden {
991160dcf7SMatt Barden 	crypto_key_t ckey;
1001160dcf7SMatt Barden 	int rv;
1011160dcf7SMatt Barden 
1021160dcf7SMatt Barden 	bzero(&ckey, sizeof (ckey));
1031160dcf7SMatt Barden 	ckey.ck_format = CRYPTO_KEY_RAW;
1041160dcf7SMatt Barden 	ckey.ck_data = key;
1051160dcf7SMatt Barden 	ckey.ck_length = key_len * 8; /* in bits */
1061160dcf7SMatt Barden 
1071160dcf7SMatt Barden 	mech->cm_param = (caddr_t)param;
1081160dcf7SMatt Barden 	mech->cm_param_len = sizeof (*param);
1091160dcf7SMatt Barden 
1101160dcf7SMatt Barden 	if (is_encrypt)
1111160dcf7SMatt Barden 		rv = crypto_encrypt_init(mech, &ckey, NULL, &ctxp->ctx, NULL);
1121160dcf7SMatt Barden 	else
1131160dcf7SMatt Barden 		rv = crypto_decrypt_init(mech, &ckey, NULL, &ctxp->ctx, NULL);
1141160dcf7SMatt Barden 
1151160dcf7SMatt Barden 	if (rv != CRYPTO_SUCCESS) {
1161160dcf7SMatt Barden 		if (is_encrypt)
1171160dcf7SMatt Barden 			cmn_err(CE_WARN,
1181160dcf7SMatt Barden 			    "crypto_encrypt_init failed: 0x%x", rv);
1191160dcf7SMatt Barden 		else
1201160dcf7SMatt Barden 			cmn_err(CE_WARN,
1211160dcf7SMatt Barden 			    "crypto_decrypt_init failed: 0x%x", rv);
1221160dcf7SMatt Barden 	}
1231160dcf7SMatt Barden 
1241160dcf7SMatt Barden 	return (rv == CRYPTO_SUCCESS ? 0 : -1);
1251160dcf7SMatt Barden }
1261160dcf7SMatt Barden 
1271160dcf7SMatt Barden int
1281160dcf7SMatt Barden smb3_encrypt_init(smb3_enc_ctx_t *ctxp, smb_crypto_mech_t *mech,
1291160dcf7SMatt Barden     smb3_crypto_param_t *param, uint8_t *key, size_t keylen,
1301160dcf7SMatt Barden     uint8_t *buf, size_t buflen)
1311160dcf7SMatt Barden {
1321160dcf7SMatt Barden 
1331160dcf7SMatt Barden 	bzero(&ctxp->output, sizeof (ctxp->output));
1341160dcf7SMatt Barden 	ctxp->output.cd_format = CRYPTO_DATA_RAW;
1351160dcf7SMatt Barden 	ctxp->output.cd_length = buflen;
1361160dcf7SMatt Barden 	ctxp->output.cd_raw.iov_len = buflen;
1371160dcf7SMatt Barden 	ctxp->output.cd_raw.iov_base = (void *)buf;
1381160dcf7SMatt Barden 
1391160dcf7SMatt Barden 	return (smb3_crypto_init(ctxp, mech, key, keylen,
1401160dcf7SMatt Barden 	    param, B_TRUE));
1411160dcf7SMatt Barden }
1421160dcf7SMatt Barden 
1431160dcf7SMatt Barden int
1441160dcf7SMatt Barden smb3_decrypt_init(smb3_enc_ctx_t *ctxp, smb_crypto_mech_t *mech,
1451160dcf7SMatt Barden     smb3_crypto_param_t *param, uint8_t *key, size_t keylen)
1461160dcf7SMatt Barden {
1471160dcf7SMatt Barden 	return (smb3_crypto_init(ctxp, mech, key, keylen,
1481160dcf7SMatt Barden 	    param, B_FALSE));
1491160dcf7SMatt Barden }
1501160dcf7SMatt Barden 
1511160dcf7SMatt Barden /*
1521160dcf7SMatt Barden  * Digest one segment
1531160dcf7SMatt Barden  */
1541160dcf7SMatt Barden int
1551160dcf7SMatt Barden smb3_encrypt_update(smb3_enc_ctx_t *ctxp, uint8_t *in, size_t len)
1561160dcf7SMatt Barden {
1571160dcf7SMatt Barden 	crypto_data_t data;
1581160dcf7SMatt Barden 	int rv;
1591160dcf7SMatt Barden 
1601160dcf7SMatt Barden 	bzero(&data, sizeof (data));
1611160dcf7SMatt Barden 	data.cd_format = CRYPTO_DATA_RAW;
1621160dcf7SMatt Barden 	data.cd_length = len;
1631160dcf7SMatt Barden 	data.cd_raw.iov_base = (void *)in;
1641160dcf7SMatt Barden 	data.cd_raw.iov_len = len;
1651160dcf7SMatt Barden 
1661160dcf7SMatt Barden 	rv = crypto_encrypt_update(ctxp->ctx, &data, &ctxp->output, NULL);
1671160dcf7SMatt Barden 
1681160dcf7SMatt Barden 	if (rv != CRYPTO_SUCCESS) {
1691160dcf7SMatt Barden 		cmn_err(CE_WARN, "crypto_encrypt_update failed: 0x%x", rv);
1701160dcf7SMatt Barden 		crypto_cancel_ctx(ctxp->ctx);
1711160dcf7SMatt Barden 		return (-1);
1721160dcf7SMatt Barden 	}
1731160dcf7SMatt Barden 
1741160dcf7SMatt Barden 	len = ctxp->output.cd_length;
1751160dcf7SMatt Barden 	ctxp->len -= len;
1761160dcf7SMatt Barden 	ctxp->output.cd_offset += len;
1771160dcf7SMatt Barden 	ctxp->output.cd_length = ctxp->len;
1781160dcf7SMatt Barden 
1791160dcf7SMatt Barden 	return (0);
1801160dcf7SMatt Barden }
1811160dcf7SMatt Barden 
1821160dcf7SMatt Barden int
1831160dcf7SMatt Barden smb3_decrypt_update(smb3_enc_ctx_t *ctxp, uint8_t *in, size_t len)
1841160dcf7SMatt Barden {
1851160dcf7SMatt Barden 	crypto_data_t data;
1861160dcf7SMatt Barden 	int rv;
1871160dcf7SMatt Barden 
1881160dcf7SMatt Barden 	bzero(&data, sizeof (data));
1891160dcf7SMatt Barden 	data.cd_format = CRYPTO_DATA_RAW;
1901160dcf7SMatt Barden 	data.cd_length = len;
1911160dcf7SMatt Barden 	data.cd_raw.iov_base = (void *)in;
1921160dcf7SMatt Barden 	data.cd_raw.iov_len = len;
1931160dcf7SMatt Barden 
1941160dcf7SMatt Barden 	/*
1951160dcf7SMatt Barden 	 * AES_CCM does not output data until decrypt_final,
1961160dcf7SMatt Barden 	 * and only does so if the signature matches.
1971160dcf7SMatt Barden 	 */
1981160dcf7SMatt Barden 	rv = crypto_decrypt_update(ctxp->ctx, &data, NULL, NULL);
1991160dcf7SMatt Barden 
2001160dcf7SMatt Barden 	if (rv != CRYPTO_SUCCESS) {
2011160dcf7SMatt Barden 		cmn_err(CE_WARN, "crypto_decrypt_update failed: 0x%x", rv);
2021160dcf7SMatt Barden 		crypto_cancel_ctx(ctxp->ctx);
2031160dcf7SMatt Barden 		return (-1);
2041160dcf7SMatt Barden 	}
2051160dcf7SMatt Barden 
2061160dcf7SMatt Barden 	return (0);
2071160dcf7SMatt Barden }
2081160dcf7SMatt Barden 
2091160dcf7SMatt Barden int
2101160dcf7SMatt Barden smb3_encrypt_final(smb3_enc_ctx_t *ctxp, uint8_t *digest16)
2111160dcf7SMatt Barden {
2121160dcf7SMatt Barden 	crypto_data_t out;
2131160dcf7SMatt Barden 	int rv;
2141160dcf7SMatt Barden 	uint8_t buf[SMB2_SIG_SIZE + 16] = {0};
2151160dcf7SMatt Barden 	size_t outlen;
2161160dcf7SMatt Barden 
2171160dcf7SMatt Barden 	bzero(&out, sizeof (out));
2181160dcf7SMatt Barden 	out.cd_format = CRYPTO_DATA_RAW;
2191160dcf7SMatt Barden 	out.cd_length = sizeof (buf);
2201160dcf7SMatt Barden 	out.cd_raw.iov_len = sizeof (buf);
2211160dcf7SMatt Barden 	out.cd_raw.iov_base = (void *)buf;
2221160dcf7SMatt Barden 
2231160dcf7SMatt Barden 	rv = crypto_encrypt_final(ctxp->ctx, &out, 0);
2241160dcf7SMatt Barden 
2251160dcf7SMatt Barden 	if (rv != CRYPTO_SUCCESS) {
2261160dcf7SMatt Barden 		cmn_err(CE_WARN, "crypto_encrypt_final failed: 0x%x", rv);
2271160dcf7SMatt Barden 		return (-1);
2281160dcf7SMatt Barden 	}
2291160dcf7SMatt Barden 
2304e065a9fSAlexander Stetsenko 	/*
2314e065a9fSAlexander Stetsenko 	 * For some reason AES module processes ccm_encrypt_final and
2324e065a9fSAlexander Stetsenko 	 * gcm_encrypt_final differently.
2334e065a9fSAlexander Stetsenko 	 * For GCM it restores original offset (which is 0) and updates
2344e065a9fSAlexander Stetsenko 	 * cd_length to size of residual data + mac len.
2354e065a9fSAlexander Stetsenko 	 * For CCM it does nothing, what means offset is updated and cd_length
2364e065a9fSAlexander Stetsenko 	 * is decreased by size of residual data + mac len.
2374e065a9fSAlexander Stetsenko 	 */
2384e065a9fSAlexander Stetsenko 	if (out.cd_offset == 0) {
2394e065a9fSAlexander Stetsenko 		/* GCM */
2404e065a9fSAlexander Stetsenko 		outlen = out.cd_length - SMB2_SIG_SIZE;
2414e065a9fSAlexander Stetsenko 	} else {
2424e065a9fSAlexander Stetsenko 		/* CCM */
2431160dcf7SMatt Barden 		outlen = out.cd_offset - SMB2_SIG_SIZE;
2444e065a9fSAlexander Stetsenko 	}
2454e065a9fSAlexander Stetsenko 
2461160dcf7SMatt Barden 	if (outlen > 0)
2471160dcf7SMatt Barden 		bcopy(buf, ctxp->output.cd_raw.iov_base +
2481160dcf7SMatt Barden 		    ctxp->output.cd_offset, outlen);
2491160dcf7SMatt Barden 	bcopy(buf + outlen, digest16, SMB2_SIG_SIZE);
2501160dcf7SMatt Barden 
2511160dcf7SMatt Barden 	return (0);
2521160dcf7SMatt Barden }
2531160dcf7SMatt Barden 
2541160dcf7SMatt Barden int
2551160dcf7SMatt Barden smb3_decrypt_final(smb3_enc_ctx_t *ctxp, uint8_t *buf, size_t buflen)
2561160dcf7SMatt Barden {
2571160dcf7SMatt Barden 	crypto_data_t out;
2581160dcf7SMatt Barden 	int rv;
2591160dcf7SMatt Barden 
2601160dcf7SMatt Barden 	bzero(&out, sizeof (out));
2611160dcf7SMatt Barden 	out.cd_format = CRYPTO_DATA_RAW;
2621160dcf7SMatt Barden 	out.cd_length = buflen;
2631160dcf7SMatt Barden 	out.cd_raw.iov_len = buflen;
2641160dcf7SMatt Barden 	out.cd_raw.iov_base = (void *)buf;
2651160dcf7SMatt Barden 
2661160dcf7SMatt Barden 	rv = crypto_decrypt_final(ctxp->ctx, &out, NULL);
2671160dcf7SMatt Barden 
2681160dcf7SMatt Barden 	if (rv != CRYPTO_SUCCESS)
2691160dcf7SMatt Barden 		cmn_err(CE_WARN, "crypto_decrypt_final failed: 0x%x", rv);
2701160dcf7SMatt Barden 
2711160dcf7SMatt Barden 	return (rv == CRYPTO_SUCCESS ? 0 : -1);
2721160dcf7SMatt Barden }
2731160dcf7SMatt Barden 
2741160dcf7SMatt Barden void
2751160dcf7SMatt Barden smb3_encrypt_cancel(smb3_enc_ctx_t *ctxp)
2761160dcf7SMatt Barden {
2771160dcf7SMatt Barden 	crypto_cancel_ctx(ctxp->ctx);
2781160dcf7SMatt Barden }
279