xref: /illumos-gate/usr/src/uts/common/fs/smbsrv/smb3_encrypt_kcf.c (revision dd72704bd9e794056c558153663c739e2012d721)
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