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