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 }