1 /* 2 * This file and its contents are supplied under the terms of the 3 * Common Development and Distribution License ("CDDL"), version 1.0. 4 * You may only use this file in accordance with the terms of version 5 * 1.0 of the CDDL. 6 * 7 * A full copy of the text of the CDDL should have accompanied this 8 * source. A copy of the CDDL is also available via the Internet at 9 * http://www.illumos.org/license/CDDL. 10 */ 11 12 /* 13 * Copyright 2018 Nexenta Systems, Inc. All rights reserved. 14 * Copyright 2020 RackTop Systems, Inc. 15 */ 16 17 /* 18 * Helper functions for SMB3 encryption using the 19 * Kernel Cryptographic Framework (KCF) 20 * 21 * There are two implementations of these functions: 22 * This one (for kernel) and another for user space: 23 * See: lib/smbsrv/libfksmbsrv/common/fksmb_encrypt_pkcs.c 24 */ 25 26 #include <sys/crypto/api.h> 27 #include <smbsrv/smb_kcrypt.h> 28 #include <smbsrv/smb2_kproto.h> 29 #include <sys/cmn_err.h> 30 31 /* 32 * Common function to see if a mech is available. 33 */ 34 static int 35 find_mech(smb_crypto_mech_t *mech, const char *name) 36 { 37 crypto_mech_type_t t; 38 39 t = crypto_mech2id(name); 40 if (t == CRYPTO_MECH_INVALID) { 41 cmn_err(CE_NOTE, "smb: no kcf mech: %s", name); 42 return (-1); 43 } 44 mech->cm_type = t; 45 return (0); 46 } 47 48 /* 49 * SMB3 encryption helpers: 50 * (getmech, init, update, final) 51 */ 52 53 int 54 smb3_aes_ccm_getmech(smb_crypto_mech_t *mech) 55 { 56 return (find_mech(mech, SUN_CKM_AES_CCM)); 57 } 58 59 int 60 smb3_aes_gcm_getmech(smb_crypto_mech_t *mech) 61 { 62 return (find_mech(mech, SUN_CKM_AES_GCM)); 63 } 64 65 void 66 smb3_crypto_init_ccm_param(smb3_crypto_param_t *param, 67 uint8_t *nonce, size_t noncesize, uint8_t *auth, size_t authsize, 68 size_t datasize) 69 { 70 param->ccm.ulMACSize = SMB2_SIG_SIZE; 71 param->ccm.ulNonceSize = noncesize; 72 param->ccm.nonce = nonce; 73 param->ccm.ulDataSize = datasize; 74 param->ccm.ulAuthDataSize = authsize; 75 param->ccm.authData = auth; 76 } 77 78 void 79 smb3_crypto_init_gcm_param(smb3_crypto_param_t *param, 80 uint8_t *nonce, size_t noncesize, uint8_t *auth, size_t authsize) 81 { 82 ASSERT3U(noncesize, ==, 12); 83 param->gcm.pIv = nonce; 84 param->gcm.ulIvLen = noncesize; /* should be 12 bytes */ 85 /* tform hdr size - (protcolo id + signing) == 32 bytes */ 86 param->gcm.ulTagBits = SMB2_SIG_SIZE << 3; /* convert bytes to bits */ 87 param->gcm.pAAD = auth; /* auth data */ 88 param->gcm.ulAADLen = authsize; /* auth data len */ 89 } 90 91 /* 92 * Start the KCF session, load the key 93 */ 94 static int 95 smb3_crypto_init(smb3_enc_ctx_t *ctxp, smb_crypto_mech_t *mech, 96 uint8_t *key, size_t key_len, smb3_crypto_param_t *param, 97 boolean_t is_encrypt) 98 { 99 crypto_key_t ckey; 100 int rv; 101 102 bzero(&ckey, sizeof (ckey)); 103 ckey.ck_format = CRYPTO_KEY_RAW; 104 ckey.ck_data = key; 105 ckey.ck_length = key_len * 8; /* in bits */ 106 107 mech->cm_param = (caddr_t)param; 108 mech->cm_param_len = sizeof (*param); 109 110 if (is_encrypt) 111 rv = crypto_encrypt_init(mech, &ckey, NULL, &ctxp->ctx, NULL); 112 else 113 rv = crypto_decrypt_init(mech, &ckey, NULL, &ctxp->ctx, NULL); 114 115 if (rv != CRYPTO_SUCCESS) { 116 if (is_encrypt) 117 cmn_err(CE_WARN, 118 "crypto_encrypt_init failed: 0x%x", rv); 119 else 120 cmn_err(CE_WARN, 121 "crypto_decrypt_init failed: 0x%x", rv); 122 } 123 124 return (rv == CRYPTO_SUCCESS ? 0 : -1); 125 } 126 127 int 128 smb3_encrypt_init(smb3_enc_ctx_t *ctxp, smb_crypto_mech_t *mech, 129 smb3_crypto_param_t *param, uint8_t *key, size_t keylen, 130 uint8_t *buf, size_t buflen) 131 { 132 133 bzero(&ctxp->output, sizeof (ctxp->output)); 134 ctxp->output.cd_format = CRYPTO_DATA_RAW; 135 ctxp->output.cd_length = buflen; 136 ctxp->output.cd_raw.iov_len = buflen; 137 ctxp->output.cd_raw.iov_base = (void *)buf; 138 139 return (smb3_crypto_init(ctxp, mech, key, keylen, 140 param, B_TRUE)); 141 } 142 143 int 144 smb3_decrypt_init(smb3_enc_ctx_t *ctxp, smb_crypto_mech_t *mech, 145 smb3_crypto_param_t *param, uint8_t *key, size_t keylen) 146 { 147 return (smb3_crypto_init(ctxp, mech, key, keylen, 148 param, B_FALSE)); 149 } 150 151 /* 152 * Digest one segment 153 */ 154 int 155 smb3_encrypt_update(smb3_enc_ctx_t *ctxp, uint8_t *in, size_t len) 156 { 157 crypto_data_t data; 158 int rv; 159 160 bzero(&data, sizeof (data)); 161 data.cd_format = CRYPTO_DATA_RAW; 162 data.cd_length = len; 163 data.cd_raw.iov_base = (void *)in; 164 data.cd_raw.iov_len = len; 165 166 rv = crypto_encrypt_update(ctxp->ctx, &data, &ctxp->output, NULL); 167 168 if (rv != CRYPTO_SUCCESS) { 169 cmn_err(CE_WARN, "crypto_encrypt_update failed: 0x%x", rv); 170 crypto_cancel_ctx(ctxp->ctx); 171 return (-1); 172 } 173 174 len = ctxp->output.cd_length; 175 ctxp->len -= len; 176 ctxp->output.cd_offset += len; 177 ctxp->output.cd_length = ctxp->len; 178 179 return (0); 180 } 181 182 int 183 smb3_decrypt_update(smb3_enc_ctx_t *ctxp, uint8_t *in, size_t len) 184 { 185 crypto_data_t data; 186 int rv; 187 188 bzero(&data, sizeof (data)); 189 data.cd_format = CRYPTO_DATA_RAW; 190 data.cd_length = len; 191 data.cd_raw.iov_base = (void *)in; 192 data.cd_raw.iov_len = len; 193 194 /* 195 * AES_CCM does not output data until decrypt_final, 196 * and only does so if the signature matches. 197 */ 198 rv = crypto_decrypt_update(ctxp->ctx, &data, NULL, NULL); 199 200 if (rv != CRYPTO_SUCCESS) { 201 cmn_err(CE_WARN, "crypto_decrypt_update failed: 0x%x", rv); 202 crypto_cancel_ctx(ctxp->ctx); 203 return (-1); 204 } 205 206 return (0); 207 } 208 209 int 210 smb3_encrypt_final(smb3_enc_ctx_t *ctxp, uint8_t *digest16) 211 { 212 crypto_data_t out; 213 int rv; 214 uint8_t buf[SMB2_SIG_SIZE + 16] = {0}; 215 size_t outlen; 216 217 bzero(&out, sizeof (out)); 218 out.cd_format = CRYPTO_DATA_RAW; 219 out.cd_length = sizeof (buf); 220 out.cd_raw.iov_len = sizeof (buf); 221 out.cd_raw.iov_base = (void *)buf; 222 223 rv = crypto_encrypt_final(ctxp->ctx, &out, 0); 224 225 if (rv != CRYPTO_SUCCESS) { 226 cmn_err(CE_WARN, "crypto_encrypt_final failed: 0x%x", rv); 227 return (-1); 228 } 229 230 /* 231 * For some reason AES module processes ccm_encrypt_final and 232 * gcm_encrypt_final differently. 233 * For GCM it restores original offset (which is 0) and updates 234 * cd_length to size of residual data + mac len. 235 * For CCM it does nothing, what means offset is updated and cd_length 236 * is decreased by size of residual data + mac len. 237 */ 238 if (out.cd_offset == 0) { 239 /* GCM */ 240 outlen = out.cd_length - SMB2_SIG_SIZE; 241 } else { 242 /* CCM */ 243 outlen = out.cd_offset - SMB2_SIG_SIZE; 244 } 245 246 if (outlen > 0) 247 bcopy(buf, ctxp->output.cd_raw.iov_base + 248 ctxp->output.cd_offset, outlen); 249 bcopy(buf + outlen, digest16, SMB2_SIG_SIZE); 250 251 return (0); 252 } 253 254 int 255 smb3_decrypt_final(smb3_enc_ctx_t *ctxp, uint8_t *buf, size_t buflen) 256 { 257 crypto_data_t out; 258 int rv; 259 260 bzero(&out, sizeof (out)); 261 out.cd_format = CRYPTO_DATA_RAW; 262 out.cd_length = buflen; 263 out.cd_raw.iov_len = buflen; 264 out.cd_raw.iov_base = (void *)buf; 265 266 rv = crypto_decrypt_final(ctxp->ctx, &out, NULL); 267 268 if (rv != CRYPTO_SUCCESS) 269 cmn_err(CE_WARN, "crypto_decrypt_final failed: 0x%x", rv); 270 271 return (rv == CRYPTO_SUCCESS ? 0 : -1); 272 } 273 274 void 275 smb3_encrypt_cancel(smb3_enc_ctx_t *ctxp) 276 { 277 crypto_cancel_ctx(ctxp->ctx); 278 } 279