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, crypto_mech_name_t 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 }