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 2017 Nexenta Systems, Inc.  All rights reserved.
14 */
15
16/*
17 * Helper functions for SMB signing using PKCS#11
18 *
19 * There are two implementations of these functions:
20 * This one (for user space) and another for kernel.
21 * See: uts/common/fs/smbclnt/netsmb/smb_sign_kcf.c
22 */
23
24#include <stdlib.h>
25#include <strings.h>
26#include <sys/cmn_err.h>
27#include <netsmb/smb_signing.h>
28#include <security/cryptoki.h>
29#include <security/pkcs11.h>
30
31/*
32 * Common function to see if a mech is available.
33 */
34static int
35find_mech(smb_sign_mech_t *mech, ulong_t mid)
36{
37	CK_SESSION_HANDLE hdl;
38	CK_RV rv;
39
40	rv = SUNW_C_GetMechSession(mid, &hdl);
41	if (rv != CKR_OK) {
42		cmn_err(CE_NOTE, "PKCS#11: no mech 0x%x",
43		    (unsigned int)mid);
44		return (-1);
45	}
46	(void) C_CloseSession(hdl);
47
48	mech->mechanism = mid;
49	mech->pParameter = NULL;
50	mech->ulParameterLen = 0;
51	return (0);
52}
53
54/*
55 * SMB1 signing helpers:
56 * (getmech, init, update, final)
57 */
58
59/*
60 * Find out if we have this mech.
61 */
62int
63smb_md5_getmech(smb_sign_mech_t *mech)
64{
65	return (find_mech(mech, CKM_MD5));
66}
67
68/*
69 * Start PKCS#11 session.
70 */
71int
72smb_md5_init(smb_sign_ctx_t *ctxp, smb_sign_mech_t *mech)
73{
74	CK_RV rv;
75
76	rv = SUNW_C_GetMechSession(mech->mechanism, ctxp);
77	if (rv != CKR_OK)
78		return (-1);
79
80	rv = C_DigestInit(*ctxp, mech);
81
82	return (rv == CKR_OK ? 0 : -1);
83}
84
85/*
86 * Digest one segment
87 */
88int
89smb_md5_update(smb_sign_ctx_t ctx, void *buf, size_t len)
90{
91	CK_RV rv;
92
93	rv = C_DigestUpdate(ctx, buf, len);
94	if (rv != CKR_OK)
95		(void) C_CloseSession(ctx);
96
97	return (rv == CKR_OK ? 0 : -1);
98}
99
100/*
101 * Get the final digest.
102 */
103int
104smb_md5_final(smb_sign_ctx_t ctx, uint8_t *digest16)
105{
106	CK_ULONG len = MD5_DIGEST_LENGTH;
107	CK_RV rv;
108
109	rv = C_DigestFinal(ctx, digest16, &len);
110	(void) C_CloseSession(ctx);
111
112	return (rv == CKR_OK ? 0 : -1);
113}
114
115/*
116 * SMB2 signing helpers:
117 * (getmech, init, update, final)
118 */
119
120/*
121 * Find out if we have this mech.
122 */
123int
124smb2_hmac_getmech(smb_sign_mech_t *mech)
125{
126	return (find_mech(mech, CKM_SHA256_HMAC));
127}
128
129/*
130 * Start PKCS#11 session, load the key.
131 */
132int
133smb2_hmac_init(smb_sign_ctx_t *ctxp, smb_sign_mech_t *mech,
134    uint8_t *key, size_t key_len)
135{
136	CK_OBJECT_HANDLE hkey = 0;
137	CK_RV rv;
138
139	rv = SUNW_C_GetMechSession(mech->mechanism, ctxp);
140	if (rv != CKR_OK)
141		return (-1);
142
143	rv = SUNW_C_KeyToObject(*ctxp, mech->mechanism,
144	    key, key_len, &hkey);
145	if (rv != CKR_OK)
146		return (-1);
147
148	rv = C_SignInit(*ctxp, mech, hkey);
149	(void) C_DestroyObject(*ctxp, hkey);
150
151	return (rv == CKR_OK ? 0 : -1);
152}
153
154/*
155 * Digest one segment
156 */
157int
158smb2_hmac_update(smb_sign_ctx_t ctx, uint8_t *in, size_t len)
159{
160	CK_RV rv;
161
162	rv = C_SignUpdate(ctx, in, len);
163	if (rv != CKR_OK)
164		(void) C_CloseSession(ctx);
165
166	return (rv == CKR_OK ? 0 : -1);
167}
168
169/*
170 * Note, the SMB2 signature is the first 16 bytes of the
171 * 32-byte SHA256 HMAC digest.
172 */
173int
174smb2_hmac_final(smb_sign_ctx_t ctx, uint8_t *digest16)
175{
176	uint8_t full_digest[SHA256_DIGEST_LENGTH];
177	CK_ULONG len = SHA256_DIGEST_LENGTH;
178	CK_RV rv;
179
180	rv = C_SignFinal(ctx, full_digest, &len);
181	if (rv == CKR_OK)
182		bcopy(full_digest, digest16, 16);
183
184	(void) C_CloseSession(ctx);
185
186	return (rv == CKR_OK ? 0 : -1);
187}
188
189/*
190 * SMB3 signing helpers:
191 * (getmech, init, update, final)
192 */
193
194/*
195 * Find out if we have this mech.
196 */
197int
198smb3_cmac_getmech(smb_sign_mech_t *mech)
199{
200	return (find_mech(mech, CKM_AES_CMAC));
201}
202
203/*
204 * Start PKCS#11 session, load the key.
205 */
206int
207smb3_cmac_init(smb_sign_ctx_t *ctxp, smb_sign_mech_t *mech,
208    uint8_t *key, size_t key_len)
209{
210	CK_OBJECT_HANDLE hkey = 0;
211	CK_RV rv;
212
213	rv = SUNW_C_GetMechSession(mech->mechanism, ctxp);
214	if (rv != CKR_OK)
215		return (-1);
216
217	rv = SUNW_C_KeyToObject(*ctxp, mech->mechanism,
218	    key, key_len, &hkey);
219	if (rv != CKR_OK) {
220		(void) C_CloseSession(*ctxp);
221		return (-1);
222	}
223
224	rv = C_SignInit(*ctxp, mech, hkey);
225	(void) C_DestroyObject(*ctxp, hkey);
226	if (rv != CKR_OK) {
227		(void) C_CloseSession(*ctxp);
228		return (-1);
229	}
230
231	return (0);
232}
233
234/*
235 * Digest one segment
236 */
237int
238smb3_cmac_update(smb_sign_ctx_t ctx, uint8_t *in, size_t len)
239{
240	CK_RV rv;
241
242	rv = C_SignUpdate(ctx, in, len);
243	if (rv != CKR_OK)
244		(void) C_CloseSession(ctx);
245
246	return (rv == CKR_OK ? 0 : -1);
247}
248
249/*
250 * Note, the SMB2 signature is just the AES CMAC digest.
251 * (both are 16 bytes long)
252 */
253int
254smb3_cmac_final(smb_sign_ctx_t ctx, uint8_t *digest)
255{
256	CK_ULONG len = SMB2_SIG_SIZE;
257	CK_RV rv;
258
259	rv = C_SignFinal(ctx, digest, &len);
260	(void) C_CloseSession(ctx);
261
262	return (rv == CKR_OK ? 0 : -1);
263}
264