xref: /illumos-gate/usr/src/lib/pkcs11/pkcs11_kernel/common/kernelEmulate.c (revision 598f4ceed9327d2d6c2325dd67cae3aa06f7fea6)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #include <errno.h>
28 #include <stdio.h>
29 #include <strings.h>
30 #include <sys/crypto/ioctl.h>
31 #include <security/cryptoki.h>
32 #include "kernelGlobal.h"
33 #include "kernelSession.h"
34 #include "kernelEmulate.h"
35 
36 /*
37  * Helper routine to know if this is a HMAC. We can't just check
38  * the CKF_SIGN mech flag as it is set for non-HMAC mechs too.
39  */
40 boolean_t
41 is_hmac(CK_MECHANISM_TYPE mechanism)
42 {
43 	switch (mechanism) {
44 	case CKM_SSL3_MD5_MAC:
45 	case CKM_SSL3_SHA1_MAC:
46 	case CKM_MD5_HMAC_GENERAL:
47 	case CKM_MD5_HMAC:
48 	case CKM_SHA_1_HMAC_GENERAL:
49 	case CKM_SHA_1_HMAC:
50 	case CKM_SHA256_HMAC_GENERAL:
51 	case CKM_SHA256_HMAC:
52 	case CKM_SHA384_HMAC_GENERAL:
53 	case CKM_SHA384_HMAC:
54 	case CKM_SHA512_HMAC_GENERAL:
55 	case CKM_SHA512_HMAC:
56 		return (B_TRUE);
57 
58 	default:
59 		return (B_FALSE);
60 	}
61 }
62 
63 /*
64  * Helper routine to allocate an emulation structure for the session.
65  * buflen indicates the size of the scratch buffer to be allocated.
66  */
67 CK_RV
68 emulate_buf_init(kernel_session_t *session_p, int buflen, int opflag)
69 {
70 	digest_buf_t *bufp;
71 	crypto_active_op_t *opp;
72 
73 	opp = (opflag & OP_DIGEST) ? &(session_p->digest) : \
74 	    ((opflag & OP_SIGN) ? &(session_p->sign) : &(session_p->verify));
75 
76 	bufp = opp->context;
77 
78 	if (bufp != NULL) {
79 		bufp->indata_len = 0;
80 		/*
81 		 * We can reuse the context structure, digest_buf_t.
82 		 * See if we can reuse the scratch buffer in the context too.
83 		 */
84 		if (buflen > bufp->buf_len) {
85 			free(bufp->buf);
86 			bufp->buf = NULL;
87 		}
88 	} else {
89 		bufp = opp->context = calloc(1, sizeof (digest_buf_t));
90 		if (bufp == NULL) {
91 			return (CKR_HOST_MEMORY);
92 		}
93 	}
94 
95 	if (bufp->buf == NULL) {
96 		bufp->buf = malloc(buflen);
97 		if (bufp->buf == NULL) {
98 			free(bufp);
99 			opp->context = NULL;
100 			return (CKR_HOST_MEMORY);
101 		}
102 		bufp->buf_len = buflen;
103 	}
104 
105 	return (CKR_OK);
106 }
107 
108 /*
109  * Setup the support necessary to do this operation in a
110  * single part. We allocate a buffer to accumulate the
111  * input data from later calls. We also get ready for
112  * the case where we have to do it in software by initializing
113  * a standby context. The opflag tells if this is a sign or verify.
114  */
115 CK_RV
116 emulate_init(kernel_session_t *session_p, CK_MECHANISM_PTR pMechanism,
117     crypto_key_t *keyp, int opflag)
118 {
119 	CK_RV rv;
120 	crypto_active_op_t *opp;
121 
122 	if ((rv = emulate_buf_init(session_p, EDIGEST_LENGTH, opflag)) !=
123 	    CKR_OK)
124 		return (rv);
125 
126 	opp = (opflag & OP_SIGN) ? &(session_p->sign) : &(session_p->verify);
127 
128 	opflag |= OP_INIT;
129 	rv = do_soft_hmac_init(get_spp(opp), pMechanism, keyp->ck_data,
130 	    keyp->ck_length >> 3, opflag);
131 
132 	return (rv);
133 }
134 
135 #define	DO_SOFT_UPDATE(opp, pPart, ulPartLen, opflag)		\
136 	if ((opflag) & OP_DIGEST) {				\
137 		rv = do_soft_digest(get_spp(opp), NULL, pPart,	\
138 		    ulPartLen, NULL, NULL, opflag);		\
139 	} else {						\
140 		rv = do_soft_hmac_update(get_spp(opp), pPart,	\
141 		    ulPartLen, opflag);				\
142 	}
143 
144 /*
145  * Accumulate the input data in the buffer, allocating a bigger
146  * buffer if needed. If we reach the maximum input data size
147  * that can be accumulated, start using the software from then on.
148  * The opflag tells if this is a digest, sign or verify.
149  */
150 CK_RV
151 emulate_update(kernel_session_t *session_p, CK_BYTE_PTR pPart,
152     CK_ULONG ulPartLen, int opflag)
153 {
154 	CK_RV rv;
155 	int maxlen;
156 	digest_buf_t *bufp;
157 	boolean_t use_soft = B_FALSE;
158 	crypto_active_op_t *opp;
159 
160 	if (opflag & OP_DIGEST) {
161 		opp = &(session_p->digest);
162 		if (!SLOT_HAS_LIMITED_HASH(session_p))
163 			return (CKR_ARGUMENTS_BAD);
164 		maxlen =  SLOT_HASH_MAX_INDATA_LEN(session_p);
165 	} else if (opflag & (OP_SIGN | OP_VERIFY)) {
166 		opp = (opflag & OP_SIGN) ?
167 		    &(session_p->sign) : &(session_p->verify);
168 		if (!SLOT_HAS_LIMITED_HMAC(session_p))
169 			return (CKR_ARGUMENTS_BAD);
170 		maxlen =  SLOT_HMAC_MAX_INDATA_LEN(session_p);
171 	} else
172 		return (CKR_ARGUMENTS_BAD);
173 
174 	if (opp->flags & CRYPTO_EMULATE_USING_SW) {
175 		opflag |= OP_UPDATE;
176 		DO_SOFT_UPDATE(opp, pPart, ulPartLen, opflag);
177 		opp->flags |= CRYPTO_EMULATE_UPDATE_DONE;
178 		return (rv);
179 	}
180 
181 	bufp = opp->context;
182 	if (bufp == NULL) {
183 		return (CKR_FUNCTION_FAILED);
184 	}
185 
186 	/* Did we exceed the maximum allowed? */
187 	if (bufp->indata_len + ulPartLen > maxlen) {
188 		use_soft = B_TRUE;
189 	} else if (ulPartLen > (bufp->buf_len - bufp->indata_len))  {
190 		int siz = ulPartLen < bufp->buf_len ?
191 		    bufp->buf_len * 2 : bufp->buf_len + ulPartLen;
192 		uint8_t *old = bufp->buf;
193 
194 		bufp->buf = realloc(bufp->buf, siz);
195 		if (bufp->buf == NULL) {
196 			/* Try harder rather than failing */
197 			bufp->buf =  old;
198 			use_soft = B_TRUE;
199 		} else
200 			bufp->buf_len = siz;
201 	}
202 
203 	if (use_soft) {
204 		opp->flags |= CRYPTO_EMULATE_USING_SW;
205 
206 		if (opflag & OP_DIGEST) {
207 			CK_MECHANISM_PTR pMechanism;
208 
209 			pMechanism = &(opp->mech);
210 			rv = do_soft_digest(get_spp(opp), pMechanism, NULL, 0,
211 			    NULL, NULL, OP_INIT);
212 			if (rv != CKR_OK)
213 				return (rv);
214 		}
215 
216 		opflag |= OP_UPDATE;
217 		DO_SOFT_UPDATE(opp, bufp->buf, bufp->indata_len, opflag);
218 		opp->flags |= CRYPTO_EMULATE_UPDATE_DONE;
219 		if (rv == CKR_OK) {
220 			DO_SOFT_UPDATE(opp, pPart, ulPartLen, opflag);
221 		}
222 
223 		return (rv);
224 	}
225 
226 	/* accumulate the update data */
227 	bcopy(pPart, bufp->buf + bufp->indata_len, ulPartLen);
228 	bufp->indata_len += ulPartLen;
229 
230 	return (CKR_OK);
231 }
232