1906Sgm89044
2906Sgm89044 /*
3906Sgm89044 * CDDL HEADER START
4906Sgm89044 *
5906Sgm89044 * The contents of this file are subject to the terms of the
6906Sgm89044 * Common Development and Distribution License (the "License").
7906Sgm89044 * You may not use this file except in compliance with the License.
8906Sgm89044 *
9906Sgm89044 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10906Sgm89044 * or http://www.opensolaris.org/os/licensing.
11906Sgm89044 * See the License for the specific language governing permissions
12906Sgm89044 * and limitations under the License.
13906Sgm89044 *
14906Sgm89044 * When distributing Covered Code, include this CDDL HEADER in each
15906Sgm89044 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16906Sgm89044 * If applicable, add the following below this CDDL HEADER, with the
17906Sgm89044 * fields enclosed by brackets "[]" replaced with your own identifying
18906Sgm89044 * information: Portions Copyright [yyyy] [name of copyright owner]
19906Sgm89044 *
20906Sgm89044 * CDDL HEADER END
21906Sgm89044 */
22906Sgm89044
23906Sgm89044 /*
24*7421SDaniel.Anderson@Sun.COM * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
25906Sgm89044 * Use is subject to license terms.
26906Sgm89044 */
27906Sgm89044
28906Sgm89044 /*
29906Sgm89044 * Deimos - cryptographic acceleration based upon Broadcom 582x.
30906Sgm89044 */
31906Sgm89044
32906Sgm89044 #include <sys/types.h>
33906Sgm89044 #include <sys/ddi.h>
34906Sgm89044 #include <sys/sunddi.h>
35906Sgm89044 #include <sys/kmem.h>
36906Sgm89044 #include <sys/note.h>
37906Sgm89044 #include <sys/crypto/common.h>
38906Sgm89044 #include <sys/crypto/spi.h>
39906Sgm89044 #include <sys/crypto/dca.h>
40906Sgm89044
41*7421SDaniel.Anderson@Sun.COM #if defined(__i386) || defined(__amd64)
42*7421SDaniel.Anderson@Sun.COM #include <sys/byteorder.h>
43*7421SDaniel.Anderson@Sun.COM #define UNALIGNED_POINTERS_PERMITTED
44*7421SDaniel.Anderson@Sun.COM #endif
45*7421SDaniel.Anderson@Sun.COM
46906Sgm89044 /*
47906Sgm89044 * 3DES implementation.
48906Sgm89044 */
49906Sgm89044
50906Sgm89044 static int dca_3desstart(dca_t *, uint32_t, dca_request_t *);
51906Sgm89044 static void dca_3desdone(dca_request_t *, int);
52906Sgm89044
53906Sgm89044
54906Sgm89044 int
dca_3des(crypto_ctx_t * ctx,crypto_data_t * in,crypto_data_t * out,crypto_req_handle_t req,int flags)55906Sgm89044 dca_3des(crypto_ctx_t *ctx, crypto_data_t *in,
56906Sgm89044 crypto_data_t *out, crypto_req_handle_t req, int flags)
57906Sgm89044 {
58906Sgm89044 int len;
59906Sgm89044 int rv;
60906Sgm89044 dca_request_t *reqp = ctx->cc_provider_private;
61906Sgm89044 dca_request_t *des_ctx = ctx->cc_provider_private;
62906Sgm89044 dca_t *dca = ctx->cc_provider;
63906Sgm89044 crypto_data_t *nin = &reqp->dr_ctx.in_dup;
64906Sgm89044
65906Sgm89044 len = dca_length(in);
66906Sgm89044 if (len % DESBLOCK) {
67906Sgm89044 DBG(dca, DWARN, "input not an integral number of DES blocks");
68906Sgm89044 (void) dca_free_context(ctx);
69906Sgm89044 if (flags & DR_DECRYPT) {
70906Sgm89044 return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
71906Sgm89044 } else {
72906Sgm89044 return (CRYPTO_DATA_LEN_RANGE);
73906Sgm89044 }
74906Sgm89044 }
75906Sgm89044
76906Sgm89044 /*
77906Sgm89044 * If cd_miscdata non-null then this contains the IV.
78906Sgm89044 */
79906Sgm89044 if (in->cd_miscdata != NULL) {
80*7421SDaniel.Anderson@Sun.COM #ifdef UNALIGNED_POINTERS_PERMITTED
81*7421SDaniel.Anderson@Sun.COM uint32_t *p = (uint32_t *)in->cd_miscdata;
82*7421SDaniel.Anderson@Sun.COM des_ctx->dr_ctx.iv[0] = htonl(p[0]);
83*7421SDaniel.Anderson@Sun.COM des_ctx->dr_ctx.iv[1] = htonl(p[1]);
84*7421SDaniel.Anderson@Sun.COM #else
85906Sgm89044 uchar_t *p = (uchar_t *)in->cd_miscdata;
86906Sgm89044 des_ctx->dr_ctx.iv[0] = p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3];
87906Sgm89044 des_ctx->dr_ctx.iv[1] = p[4]<<24 | p[5]<<16 | p[6]<<8 | p[7];
88*7421SDaniel.Anderson@Sun.COM #endif /* UNALIGNED_POINTERS_PERMITTED */
89906Sgm89044 }
90906Sgm89044
91906Sgm89044 if (len > dca_length(out)) {
92906Sgm89044 DBG(dca, DWARN, "inadequate output space (need %d, got %d)",
93906Sgm89044 len, dca_length(out));
94906Sgm89044 out->cd_length = len;
95906Sgm89044 /* Do not free the context since the app will call again */
96906Sgm89044 return (CRYPTO_BUFFER_TOO_SMALL);
97906Sgm89044 }
98906Sgm89044
99906Sgm89044 if ((rv = dca_verifyio(in, out)) != CRYPTO_SUCCESS) {
100906Sgm89044 (void) dca_free_context(ctx);
101906Sgm89044 return (rv);
102906Sgm89044 }
103906Sgm89044
104906Sgm89044 /* special handling for null-sized input buffers */
105906Sgm89044 if (len == 0) {
106906Sgm89044 out->cd_length = 0;
107906Sgm89044 (void) dca_free_context(ctx);
108906Sgm89044 return (CRYPTO_SUCCESS);
109906Sgm89044 }
110906Sgm89044
111906Sgm89044 /*
112906Sgm89044 * Make a local copy of the input crypto_data_t structure. This
113906Sgm89044 * allows it to be manipulated locally and for dealing with in-place
114906Sgm89044 * data (ie in == out). Note that "nin" has been pre-allocated,
115906Sgm89044 * and only fields are copied, not actual data.
116906Sgm89044 */
117906Sgm89044 if ((rv = dca_dupcrypto(in, nin)) != CRYPTO_SUCCESS) {
118906Sgm89044 (void) dca_free_context(ctx);
119906Sgm89044 return (rv);
120906Sgm89044 }
121906Sgm89044
122906Sgm89044 /* Set output to zero ready to take the processed data */
123906Sgm89044 out->cd_length = 0;
124906Sgm89044
125906Sgm89044 reqp->dr_kcf_req = req;
126906Sgm89044 reqp->dr_in = nin;
127906Sgm89044 reqp->dr_out = out;
128906Sgm89044 reqp->dr_job_stat = DS_3DESJOBS;
129906Sgm89044 reqp->dr_byte_stat = DS_3DESBYTES;
130906Sgm89044
131906Sgm89044 rv = dca_3desstart(dca, flags, reqp);
132906Sgm89044
133906Sgm89044 /* Context will be freed in the kCF callback function otherwise */
134906Sgm89044 if (rv != CRYPTO_QUEUED && rv != CRYPTO_BUFFER_TOO_SMALL) {
135906Sgm89044 (void) dca_free_context(ctx);
136906Sgm89044 }
137906Sgm89044 return (rv);
138906Sgm89044 }
139906Sgm89044
140906Sgm89044
141906Sgm89044 void
dca_3desctxfree(void * arg)142906Sgm89044 dca_3desctxfree(void *arg)
143906Sgm89044 {
144906Sgm89044 crypto_ctx_t *ctx = (crypto_ctx_t *)arg;
145906Sgm89044 dca_request_t *des_ctx = ctx->cc_provider_private;
146906Sgm89044
147906Sgm89044 if (des_ctx == NULL)
148906Sgm89044 return;
149906Sgm89044
150906Sgm89044 des_ctx->dr_ctx.atomic = 0;
151906Sgm89044 des_ctx->dr_ctx.ctx_cm_type = 0;
152906Sgm89044 ctx->cc_provider_private = NULL;
153906Sgm89044
154906Sgm89044 if (des_ctx->destroy)
155906Sgm89044 dca_destroyreq(des_ctx);
156906Sgm89044 else
157906Sgm89044 /* Return it to the pool */
158906Sgm89044 dca_freereq(des_ctx);
159906Sgm89044 }
160906Sgm89044
161906Sgm89044 int
dca_3desupdate(crypto_ctx_t * ctx,crypto_data_t * in,crypto_data_t * out,crypto_req_handle_t req,int flags)162906Sgm89044 dca_3desupdate(crypto_ctx_t *ctx, crypto_data_t *in,
163906Sgm89044 crypto_data_t *out, crypto_req_handle_t req, int flags)
164906Sgm89044 {
165906Sgm89044 int len;
166906Sgm89044 int rawlen;
167906Sgm89044 int rv;
168906Sgm89044 dca_request_t *reqp = ctx->cc_provider_private;
169906Sgm89044 dca_request_t *des_ctx = ctx->cc_provider_private;
170906Sgm89044 dca_t *dca = ctx->cc_provider;
171906Sgm89044 crypto_data_t *nin = &reqp->dr_ctx.in_dup;
172906Sgm89044
173906Sgm89044 rawlen = dca_length(in) + des_ctx->dr_ctx.residlen;
174906Sgm89044
175906Sgm89044 len = ROUNDDOWN(rawlen, DESBLOCK);
176906Sgm89044 /*
177906Sgm89044 * If cd_miscdata non-null then this contains the IV.
178906Sgm89044 */
179906Sgm89044 if (in->cd_miscdata != NULL) {
180*7421SDaniel.Anderson@Sun.COM #ifdef UNALIGNED_POINTERS_PERMITTED
181*7421SDaniel.Anderson@Sun.COM uint32_t *p = (uint32_t *)in->cd_miscdata;
182*7421SDaniel.Anderson@Sun.COM des_ctx->dr_ctx.iv[0] = htonl(p[0]);
183*7421SDaniel.Anderson@Sun.COM des_ctx->dr_ctx.iv[1] = htonl(p[1]);
184*7421SDaniel.Anderson@Sun.COM #else
185906Sgm89044 uchar_t *p = (uchar_t *)in->cd_miscdata;
186906Sgm89044 des_ctx->dr_ctx.iv[0] = p[0]<<24 | p[1]<<16 | p[2]<<8 | p[3];
187906Sgm89044 des_ctx->dr_ctx.iv[1] = p[4]<<24 | p[5]<<16 | p[6]<<8 | p[7];
188*7421SDaniel.Anderson@Sun.COM #endif /* UNALIGNED_POINTERS_PERMITTED */
189906Sgm89044 }
190906Sgm89044
191906Sgm89044 if (len > dca_length(out)) {
192906Sgm89044 DBG(dca, DWARN, "not enough output space (need %d, got %d)",
193906Sgm89044 len, dca_length(out));
194906Sgm89044 out->cd_length = len;
195906Sgm89044 /* Do not free the context since the app will call again */
196906Sgm89044 return (CRYPTO_BUFFER_TOO_SMALL);
197906Sgm89044 }
198906Sgm89044
199906Sgm89044 if ((rv = dca_verifyio(in, out)) != CRYPTO_SUCCESS) {
200906Sgm89044 (void) dca_free_context(ctx);
201906Sgm89044 return (rv);
202906Sgm89044 }
203906Sgm89044
204906Sgm89044 reqp->dr_kcf_req = req;
205906Sgm89044
206906Sgm89044 /*
207906Sgm89044 * From here on out, we are committed.
208906Sgm89044 */
209906Sgm89044
210906Sgm89044 if (len == 0) {
211906Sgm89044 /*
212906Sgm89044 * No blocks being encrypted, so we just accumulate the
213906Sgm89044 * input for the next pass and return.
214906Sgm89044 */
215906Sgm89044 if ((rv = dca_getbufbytes(in, 0,
216906Sgm89044 (rawlen % DESBLOCK) - des_ctx->dr_ctx.residlen,
217906Sgm89044 des_ctx->dr_ctx.resid + des_ctx->dr_ctx.residlen)) !=
218906Sgm89044 CRYPTO_SUCCESS) {
219906Sgm89044 DBG(dca, DWARN,
220906Sgm89044 "dca_3desupdate: dca_getbufbytes() failed for residual only pass");
221906Sgm89044 dca_freereq(reqp);
222906Sgm89044 return (rv);
223906Sgm89044 }
224906Sgm89044 des_ctx->dr_ctx.residlen = rawlen % DESBLOCK;
225906Sgm89044
226906Sgm89044 out->cd_length = 0;
227906Sgm89044 /*
228906Sgm89044 * Do not free the context here since it will be done
229906Sgm89044 * in the final function
230906Sgm89044 */
231906Sgm89044 return (CRYPTO_SUCCESS);
232906Sgm89044 }
233906Sgm89044
234906Sgm89044 /*
235906Sgm89044 * Set up rbuf for previous residual data.
236906Sgm89044 */
237906Sgm89044 if (des_ctx->dr_ctx.residlen) {
238906Sgm89044 bcopy(des_ctx->dr_ctx.resid, des_ctx->dr_ctx.activeresid,
239906Sgm89044 des_ctx->dr_ctx.residlen);
240906Sgm89044 des_ctx->dr_ctx.activeresidlen = des_ctx->dr_ctx.residlen;
241906Sgm89044 }
242906Sgm89044
243906Sgm89044 /*
244906Sgm89044 * Locate and save residual data for next encrypt_update.
245906Sgm89044 */
246906Sgm89044 if ((rv = dca_getbufbytes(in, len - des_ctx->dr_ctx.residlen,
247906Sgm89044 rawlen % DESBLOCK, des_ctx->dr_ctx.resid)) != CRYPTO_SUCCESS) {
248906Sgm89044 DBG(dca, DWARN, "dca_3desupdate: dca_getbufbytes() failed");
249906Sgm89044 (void) dca_free_context(ctx);
250906Sgm89044 return (rv);
251906Sgm89044 }
252906Sgm89044
253906Sgm89044 /* Calculate new residual length. */
254906Sgm89044 des_ctx->dr_ctx.residlen = rawlen % DESBLOCK;
255906Sgm89044
256906Sgm89044 /*
257906Sgm89044 * Make a local copy of the input crypto_data_t structure. This
258906Sgm89044 * allows it to be manipulated locally and for dealing with in-place
259906Sgm89044 * data (ie in == out).
260906Sgm89044 */
261906Sgm89044 if ((rv = dca_dupcrypto(in, nin)) != CRYPTO_SUCCESS) {
262906Sgm89044 (void) dca_free_context(ctx);
263906Sgm89044 return (rv);
264906Sgm89044 }
265906Sgm89044
266906Sgm89044 /* Set output to zero ready to take the processed data */
267906Sgm89044 out->cd_length = 0;
268906Sgm89044
269906Sgm89044 reqp->dr_in = nin;
270906Sgm89044 reqp->dr_out = out;
271906Sgm89044 reqp->dr_job_stat = DS_3DESJOBS;
272906Sgm89044 reqp->dr_byte_stat = DS_3DESBYTES;
273906Sgm89044
274906Sgm89044 rv = dca_3desstart(dca, flags, reqp);
275906Sgm89044
276906Sgm89044 /*
277906Sgm89044 * As this is multi-part the context is cleared on success
278906Sgm89044 * (CRYPTO_QUEUED) in dca_3desfinal().
279906Sgm89044 */
280906Sgm89044
281906Sgm89044 if (rv != CRYPTO_QUEUED && rv != CRYPTO_BUFFER_TOO_SMALL) {
282906Sgm89044 (void) dca_free_context(ctx);
283906Sgm89044 }
284906Sgm89044 return (rv);
285906Sgm89044 }
286906Sgm89044
287906Sgm89044 int
dca_3desfinal(crypto_ctx_t * ctx,crypto_data_t * out,int mode)288906Sgm89044 dca_3desfinal(crypto_ctx_t *ctx, crypto_data_t *out, int mode)
289906Sgm89044 {
290906Sgm89044 dca_request_t *des_ctx = ctx->cc_provider_private;
291906Sgm89044 dca_t *dca = ctx->cc_provider;
292906Sgm89044 int rv = CRYPTO_SUCCESS;
293906Sgm89044
294906Sgm89044 ASSERT(ctx->cc_provider_private != NULL);
295906Sgm89044 /*
296906Sgm89044 * There must be no unprocessed ciphertext/plaintext.
297906Sgm89044 * This happens if the length of the last data is
298906Sgm89044 * not a multiple of the DES block length.
299906Sgm89044 */
300906Sgm89044 if (des_ctx->dr_ctx.residlen != 0) {
301906Sgm89044 DBG(dca, DWARN, "dca_3desfinal: invalid nonzero residual");
302906Sgm89044 if (mode & DR_DECRYPT) {
303906Sgm89044 rv = CRYPTO_ENCRYPTED_DATA_LEN_RANGE;
304906Sgm89044 } else {
305906Sgm89044 rv = CRYPTO_DATA_LEN_RANGE;
306906Sgm89044 }
307906Sgm89044 }
308906Sgm89044 (void) dca_free_context(ctx);
309906Sgm89044 out->cd_length = 0;
310906Sgm89044 return (rv);
311906Sgm89044 }
312906Sgm89044
313906Sgm89044 int
dca_3desatomic(crypto_provider_handle_t provider,crypto_session_id_t session_id,crypto_mechanism_t * mechanism,crypto_key_t * key,crypto_data_t * input,crypto_data_t * output,int kmflag,crypto_req_handle_t req,int mode)314906Sgm89044 dca_3desatomic(crypto_provider_handle_t provider,
315906Sgm89044 crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
316906Sgm89044 crypto_key_t *key, crypto_data_t *input, crypto_data_t *output,
317906Sgm89044 int kmflag, crypto_req_handle_t req, int mode)
318906Sgm89044 {
319906Sgm89044 crypto_ctx_t ctx; /* on the stack */
320906Sgm89044 int rv;
321906Sgm89044
322906Sgm89044 ctx.cc_provider = provider;
323906Sgm89044 ctx.cc_session = session_id;
324906Sgm89044
325906Sgm89044 /*
326906Sgm89044 * Input must be a multiple of the block size. This test only
327906Sgm89044 * works for non-padded mechanisms when the blocksize is 2^N.
328906Sgm89044 */
329906Sgm89044 if ((dca_length(input) & (DESBLOCK - 1)) != 0) {
330906Sgm89044 DBG(NULL, DWARN, "dca_3desatomic: input not multiple of BS");
331906Sgm89044 if (mode & DR_DECRYPT) {
332906Sgm89044 return (CRYPTO_ENCRYPTED_DATA_LEN_RANGE);
333906Sgm89044 } else {
334906Sgm89044 return (CRYPTO_DATA_LEN_RANGE);
335906Sgm89044 }
336906Sgm89044 }
337906Sgm89044
338906Sgm89044 rv = dca_3desctxinit(&ctx, mechanism, key, kmflag, mode);
339906Sgm89044 if (rv != CRYPTO_SUCCESS) {
340906Sgm89044 DBG(NULL, DWARN, "dca_3desatomic: dca_3desctxinit() failed");
341906Sgm89044 return (rv);
342906Sgm89044 }
343906Sgm89044
344906Sgm89044 /*
345906Sgm89044 * Set the atomic flag so that the hardware callback function
346906Sgm89044 * will free the context.
347906Sgm89044 */
348906Sgm89044 ((dca_request_t *)ctx.cc_provider_private)->dr_ctx.atomic = 1;
349906Sgm89044
3505063Sgm89044 /* check for inplace ops */
3515063Sgm89044 if (input == output) {
3525063Sgm89044 ((dca_request_t *)ctx.cc_provider_private)->dr_flags
3535063Sgm89044 |= DR_INPLACE;
3545063Sgm89044 }
3555063Sgm89044
356906Sgm89044 rv = dca_3des(&ctx, input, output, req, mode);
357906Sgm89044 if ((rv != CRYPTO_QUEUED) && (rv != CRYPTO_SUCCESS)) {
358906Sgm89044 DBG(NULL, DWARN, "dca_3desatomic: dca_3des() failed");
359906Sgm89044 output->cd_length = 0;
360906Sgm89044 }
361906Sgm89044
362906Sgm89044 /*
363906Sgm89044 * The features of dca_3desfinal() are implemented within
364906Sgm89044 * dca_3desdone() due to the asynchronous nature of dca_3des().
365906Sgm89044 */
366906Sgm89044
367906Sgm89044 /*
368906Sgm89044 * The context will be freed in the hardware callback function if it
369906Sgm89044 * is queued
370906Sgm89044 */
371906Sgm89044 if (rv != CRYPTO_QUEUED)
372906Sgm89044 dca_3desctxfree(&ctx);
373906Sgm89044
374906Sgm89044 return (rv);
375906Sgm89044 }
376906Sgm89044
377906Sgm89044 int
dca_3desstart(dca_t * dca,uint32_t flags,dca_request_t * reqp)378906Sgm89044 dca_3desstart(dca_t *dca, uint32_t flags, dca_request_t *reqp)
379906Sgm89044 {
380906Sgm89044 size_t len;
381906Sgm89044 crypto_data_t *in = reqp->dr_in;
382906Sgm89044 int rv;
383906Sgm89044 dca_request_t *ctx = reqp;
384906Sgm89044 uint32_t iv[2];
385906Sgm89044
386906Sgm89044 /*
387906Sgm89044 * Preconditions:
388906Sgm89044 * 1) in and out point to the "right" buffers.
389906Sgm89044 * 2) in->b_bcount - in->b_resid == initial offset
390906Sgm89044 * 3) likewise for out
391906Sgm89044 * 4) there is enough space in the output
392906Sgm89044 * 5) we perform a block for block encrypt
393906Sgm89044 */
394906Sgm89044 len = ctx->dr_ctx.activeresidlen + dca_length(in);
395906Sgm89044 len = ROUNDDOWN(min(len, MAXPACKET), DESBLOCK);
396906Sgm89044 reqp->dr_pkt_length = (uint16_t)len;
397906Sgm89044
398906Sgm89044 /* collect IVs for this pass */
399906Sgm89044 iv[0] = ctx->dr_ctx.iv[0];
400906Sgm89044 iv[1] = ctx->dr_ctx.iv[1];
401906Sgm89044
402906Sgm89044 /*
403906Sgm89044 * And also, for decrypt, collect the IV for the next pass. For
404906Sgm89044 * decrypt, the IV must be collected BEFORE decryption, or else
405906Sgm89044 * we will lose it. (For encrypt, we grab the IV AFTER encryption,
406906Sgm89044 * in dca_3desdone.
407906Sgm89044 */
408906Sgm89044 if (flags & DR_DECRYPT) {
409906Sgm89044 uchar_t ivstore[DESBLOCK];
410*7421SDaniel.Anderson@Sun.COM #ifdef UNALIGNED_POINTERS_PERMITTED
411*7421SDaniel.Anderson@Sun.COM uint32_t *ivp = (uint32_t *)ivstore;
412*7421SDaniel.Anderson@Sun.COM #else
413906Sgm89044 uchar_t *ivp = ivstore;
414*7421SDaniel.Anderson@Sun.COM #endif /* UNALIGNED_POINTERS_PERMITTED */
415906Sgm89044
416906Sgm89044 /* get last 8 bytes of ciphertext for IV of next op */
417906Sgm89044 /*
418906Sgm89044 * If we're processing only a DESBLOCKS worth of data
419906Sgm89044 * and there is active residual present then it will be
420906Sgm89044 * needed for the IV also.
421906Sgm89044 */
422906Sgm89044 if ((len == DESBLOCK) && ctx->dr_ctx.activeresidlen) {
423906Sgm89044 /* Bring the active residual into play */
424906Sgm89044 bcopy(ctx->dr_ctx.activeresid, ivstore,
425906Sgm89044 ctx->dr_ctx.activeresidlen);
426906Sgm89044 rv = dca_getbufbytes(in, 0,
427906Sgm89044 DESBLOCK - ctx->dr_ctx.activeresidlen,
428906Sgm89044 ivstore + ctx->dr_ctx.activeresidlen);
429906Sgm89044 } else {
430906Sgm89044 rv = dca_getbufbytes(in,
431906Sgm89044 len - DESBLOCK - ctx->dr_ctx.activeresidlen,
432906Sgm89044 DESBLOCK, ivstore);
433906Sgm89044 }
434906Sgm89044
435906Sgm89044 if (rv != CRYPTO_SUCCESS) {
436906Sgm89044 DBG(dca, DWARN,
437906Sgm89044 "dca_3desstart: dca_getbufbytes() failed");
438906Sgm89044 return (rv);
439906Sgm89044 }
440906Sgm89044
441906Sgm89044 /* store as a pair of native 32-bit values */
442*7421SDaniel.Anderson@Sun.COM #ifdef UNALIGNED_POINTERS_PERMITTED
443*7421SDaniel.Anderson@Sun.COM ctx->dr_ctx.iv[0] = htonl(ivp[0]);
444*7421SDaniel.Anderson@Sun.COM ctx->dr_ctx.iv[1] = htonl(ivp[1]);
445*7421SDaniel.Anderson@Sun.COM #else
446906Sgm89044 ctx->dr_ctx.iv[0] =
447906Sgm89044 ivp[0]<<24 | ivp[1]<<16 | ivp[2]<<8 | ivp[3];
448906Sgm89044 ctx->dr_ctx.iv[1] =
449906Sgm89044 ivp[4]<<24 | ivp[5]<<16 | ivp[6]<<8 | ivp[7];
450*7421SDaniel.Anderson@Sun.COM #endif /* UNALIGNED_POINTERS_PERMITTED */
451906Sgm89044 }
452906Sgm89044
453906Sgm89044 /* For now we force a pullup. Add direct DMA later. */
454906Sgm89044 reqp->dr_flags &= ~(DR_SCATTER | DR_GATHER);
455906Sgm89044 if ((len < dca_mindma) || (ctx->dr_ctx.activeresidlen > 0) ||
456906Sgm89044 dca_sgcheck(dca, reqp->dr_in, DCA_SG_CONTIG) ||
457906Sgm89044 dca_sgcheck(dca, reqp->dr_out, DCA_SG_WALIGN)) {
458906Sgm89044 reqp->dr_flags |= DR_SCATTER | DR_GATHER;
459906Sgm89044 }
460906Sgm89044
461906Sgm89044 /* Try to do direct DMA. */
462906Sgm89044 if (!(reqp->dr_flags & (DR_SCATTER | DR_GATHER))) {
463906Sgm89044 if (dca_bindchains(reqp, len, len) == DDI_SUCCESS) {
464906Sgm89044 reqp->dr_in->cd_offset += len;
465906Sgm89044 reqp->dr_in->cd_length -= len;
466906Sgm89044 } else {
467906Sgm89044 DBG(dca, DWARN,
468906Sgm89044 "dca_3desstart: dca_bindchains() failed");
469906Sgm89044 return (CRYPTO_DEVICE_ERROR);
470906Sgm89044 }
471906Sgm89044 }
472906Sgm89044
473906Sgm89044 /* gather the data into the device */
474906Sgm89044 if (reqp->dr_flags & DR_GATHER) {
475906Sgm89044 rv = dca_resid_gather(in, (char *)ctx->dr_ctx.activeresid,
476906Sgm89044 &ctx->dr_ctx.activeresidlen, reqp->dr_ibuf_kaddr, len);
477906Sgm89044 if (rv != CRYPTO_SUCCESS) {
478906Sgm89044 DBG(dca, DWARN,
479906Sgm89044 "dca_3desstart: dca_resid_gather() failed");
480906Sgm89044 return (rv);
481906Sgm89044 }
482906Sgm89044 /*
483906Sgm89044 * Setup for scattering the result back out
484906Sgm89044 * The output buffer is a multi-entry chain for x86 and
485906Sgm89044 * a single entry chain for Sparc.
486906Sgm89044 * Use the actual length if the first entry is sufficient.
487906Sgm89044 */
488906Sgm89044 (void) ddi_dma_sync(reqp->dr_ibuf_dmah, 0, len,
4895063Sgm89044 DDI_DMA_SYNC_FORDEV);
490906Sgm89044 if (dca_check_dma_handle(dca, reqp->dr_ibuf_dmah,
491906Sgm89044 DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
492906Sgm89044 reqp->destroy = TRUE;
493906Sgm89044 return (CRYPTO_DEVICE_ERROR);
494906Sgm89044 }
495906Sgm89044
496906Sgm89044 reqp->dr_in_paddr = reqp->dr_ibuf_head.dc_buffer_paddr;
497906Sgm89044 reqp->dr_in_next = reqp->dr_ibuf_head.dc_next_paddr;
498906Sgm89044 if (len > reqp->dr_ibuf_head.dc_buffer_length)
499906Sgm89044 reqp->dr_in_len = reqp->dr_ibuf_head.dc_buffer_length;
500906Sgm89044 else
501906Sgm89044 reqp->dr_in_len = len;
502906Sgm89044 }
503906Sgm89044 /*
504906Sgm89044 * Setup for scattering the result back out
505906Sgm89044 * The output buffer is a multi-entry chain for x86 and
506906Sgm89044 * a single entry chain for Sparc.
507906Sgm89044 * Use the actual length if the first entry is sufficient.
508906Sgm89044 */
509906Sgm89044 if (reqp->dr_flags & DR_SCATTER) {
510906Sgm89044 reqp->dr_out_paddr = reqp->dr_obuf_head.dc_buffer_paddr;
511906Sgm89044 reqp->dr_out_next = reqp->dr_obuf_head.dc_next_paddr;
512906Sgm89044 if (len > reqp->dr_obuf_head.dc_buffer_length)
513906Sgm89044 reqp->dr_out_len = reqp->dr_obuf_head.dc_buffer_length;
514906Sgm89044 else
515906Sgm89044 reqp->dr_out_len = len;
516906Sgm89044 }
517906Sgm89044
518906Sgm89044 reqp->dr_flags |= flags;
519906Sgm89044 reqp->dr_callback = dca_3desdone;
520906Sgm89044
521906Sgm89044 /* write out the context structure */
522906Sgm89044 PUTCTX32(reqp, CTX_3DESIVHI, iv[0]);
523906Sgm89044 PUTCTX32(reqp, CTX_3DESIVLO, iv[1]);
524906Sgm89044
525906Sgm89044 /* schedule the work by doing a submit */
526906Sgm89044 return (dca_start(dca, reqp, MCR1, 1));
527906Sgm89044 }
528906Sgm89044
529906Sgm89044 void
dca_3desdone(dca_request_t * reqp,int errno)530906Sgm89044 dca_3desdone(dca_request_t *reqp, int errno)
531906Sgm89044 {
532906Sgm89044 crypto_data_t *out = reqp->dr_out;
533906Sgm89044 dca_request_t *ctx = reqp;
534906Sgm89044 ASSERT(ctx != NULL);
535906Sgm89044
536906Sgm89044 if (errno == CRYPTO_SUCCESS) {
537906Sgm89044 size_t off;
538906Sgm89044 /*
539906Sgm89044 * Save the offset: this has to be done *before* dca_scatter
540906Sgm89044 * modifies the buffer. We take the initial offset into the
541906Sgm89044 * first buf, and add that to the total packet size to find
542906Sgm89044 * the end of the packet.
543906Sgm89044 */
544906Sgm89044 off = dca_length(out) + reqp->dr_pkt_length - DESBLOCK;
545906Sgm89044
546906Sgm89044 if (reqp->dr_flags & DR_SCATTER) {
547906Sgm89044 (void) ddi_dma_sync(reqp->dr_obuf_dmah, 0,
5485063Sgm89044 reqp->dr_out_len, DDI_DMA_SYNC_FORKERNEL);
549906Sgm89044 if (dca_check_dma_handle(reqp->dr_dca,
550906Sgm89044 reqp->dr_obuf_dmah, DCA_FM_ECLASS_NONE) !=
551906Sgm89044 DDI_SUCCESS) {
552906Sgm89044 reqp->destroy = TRUE;
553906Sgm89044 errno = CRYPTO_DEVICE_ERROR;
554906Sgm89044 goto errout;
555906Sgm89044 }
556906Sgm89044
557906Sgm89044 errno = dca_scatter(reqp->dr_obuf_kaddr,
558906Sgm89044 reqp->dr_out, reqp->dr_out_len, 0);
559906Sgm89044 if (errno != CRYPTO_SUCCESS) {
560906Sgm89044 DBG(NULL, DWARN,
561906Sgm89044 "dca_3desdone: dca_scatter() failed");
562906Sgm89044 goto errout;
563906Sgm89044 }
564906Sgm89044
565906Sgm89044 } else {
566906Sgm89044 /* we've processed some more data */
567906Sgm89044 out->cd_length += reqp->dr_pkt_length;
568906Sgm89044 }
569906Sgm89044
570906Sgm89044
571906Sgm89044 /*
572906Sgm89044 * For encryption only, we have to grab the IV for the
573906Sgm89044 * next pass AFTER encryption.
574906Sgm89044 */
575906Sgm89044 if (reqp->dr_flags & DR_ENCRYPT) {
576906Sgm89044 uchar_t ivstore[DESBLOCK];
577*7421SDaniel.Anderson@Sun.COM #ifdef UNALIGNED_POINTERS_PERMITTED
578*7421SDaniel.Anderson@Sun.COM uint32_t *iv = (uint32_t *)ivstore;
579*7421SDaniel.Anderson@Sun.COM #else
580906Sgm89044 uchar_t *iv = ivstore;
581*7421SDaniel.Anderson@Sun.COM #endif /* UNALIGNED_POINTERS_PERMITTED */
582906Sgm89044
583906Sgm89044 /* get last 8 bytes for IV of next op */
584*7421SDaniel.Anderson@Sun.COM errno = dca_getbufbytes(out, off, DESBLOCK,
585*7421SDaniel.Anderson@Sun.COM (uchar_t *)iv);
586906Sgm89044 if (errno != CRYPTO_SUCCESS) {
587906Sgm89044 DBG(NULL, DWARN,
588906Sgm89044 "dca_3desdone: dca_getbufbytes() failed");
589906Sgm89044 goto errout;
590906Sgm89044 }
591*7421SDaniel.Anderson@Sun.COM
592906Sgm89044 /* store as a pair of native 32-bit values */
593*7421SDaniel.Anderson@Sun.COM #ifdef UNALIGNED_POINTERS_PERMITTED
594*7421SDaniel.Anderson@Sun.COM ctx->dr_ctx.iv[0] = htonl(iv[0]);
595*7421SDaniel.Anderson@Sun.COM ctx->dr_ctx.iv[1] = htonl(iv[1]);
596*7421SDaniel.Anderson@Sun.COM #else
597906Sgm89044 ctx->dr_ctx.iv[0] =
598906Sgm89044 iv[0]<<24 | iv[1]<<16 | iv[2]<<8 | iv[3];
599906Sgm89044 ctx->dr_ctx.iv[1] =
600906Sgm89044 iv[4]<<24 | iv[5]<<16 | iv[6]<<8 | iv[7];
601*7421SDaniel.Anderson@Sun.COM #endif /* UNALIGNED_POINTERS_PERMITTED */
602906Sgm89044 }
603906Sgm89044
604906Sgm89044 /*
605906Sgm89044 * If there is more to do, then reschedule another
606906Sgm89044 * pass.
607906Sgm89044 */
608906Sgm89044 if (dca_length(reqp->dr_in) >= 8) {
609906Sgm89044 errno = dca_3desstart(reqp->dr_dca, reqp->dr_flags,
610906Sgm89044 reqp);
611906Sgm89044 if (errno == CRYPTO_QUEUED) {
612906Sgm89044 return;
613906Sgm89044 }
614906Sgm89044 }
615906Sgm89044 }
616906Sgm89044
617906Sgm89044 errout:
618906Sgm89044
619906Sgm89044 /*
620906Sgm89044 * If this is an atomic operation perform the final function
621906Sgm89044 * tasks (equivalent to to dca_3desfinal()).
622906Sgm89044 */
623906Sgm89044 if (reqp->dr_ctx.atomic) {
624906Sgm89044 if ((errno == CRYPTO_SUCCESS) && (ctx->dr_ctx.residlen != 0)) {
625906Sgm89044 DBG(NULL, DWARN,
626906Sgm89044 "dca_3desdone: invalid nonzero residual");
627906Sgm89044 if (reqp->dr_flags & DR_DECRYPT) {
628906Sgm89044 errno = CRYPTO_ENCRYPTED_DATA_LEN_RANGE;
629906Sgm89044 } else {
630906Sgm89044 errno = CRYPTO_DATA_LEN_RANGE;
631906Sgm89044 }
632906Sgm89044 }
633906Sgm89044 }
634906Sgm89044
635906Sgm89044 ASSERT(reqp->dr_kcf_req != NULL);
636906Sgm89044 /* notify framework that request is completed */
637906Sgm89044 crypto_op_notification(reqp->dr_kcf_req, errno);
638906Sgm89044 DBG(NULL, DINTR,
639906Sgm89044 "dca_3desdone: returning %d to the kef via crypto_op_notification",
640906Sgm89044 errno);
641906Sgm89044
642906Sgm89044 /* This has to be done after notifing the framework */
643906Sgm89044 if (reqp->dr_ctx.atomic) {
644906Sgm89044 reqp->dr_context = NULL;
645906Sgm89044 reqp->dr_ctx.atomic = 0;
646906Sgm89044 reqp->dr_ctx.ctx_cm_type = 0;
647906Sgm89044 if (reqp->destroy)
648906Sgm89044 dca_destroyreq(reqp);
649906Sgm89044 else
650906Sgm89044 dca_freereq(reqp);
651906Sgm89044 }
652906Sgm89044 }
653906Sgm89044
654906Sgm89044 /* ARGSUSED */
655906Sgm89044 int
dca_3desctxinit(crypto_ctx_t * ctx,crypto_mechanism_t * mechanism,crypto_key_t * key,int kmflag,int flags)656906Sgm89044 dca_3desctxinit(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
657906Sgm89044 crypto_key_t *key, int kmflag, int flags)
658906Sgm89044 {
659906Sgm89044 dca_request_t *des_ctx;
660906Sgm89044 dca_t *dca = ctx->cc_provider;
661*7421SDaniel.Anderson@Sun.COM #ifdef UNALIGNED_POINTERS_PERMITTED
662*7421SDaniel.Anderson@Sun.COM uint32_t *param;
663*7421SDaniel.Anderson@Sun.COM uint32_t *value32;
664*7421SDaniel.Anderson@Sun.COM #else
665906Sgm89044 uchar_t *param;
666*7421SDaniel.Anderson@Sun.COM #endif /* UNALIGNED_POINTERS_PERMITTED */
667906Sgm89044 uchar_t *value;
668906Sgm89044 size_t paramsz;
669906Sgm89044 unsigned len;
670906Sgm89044 int i, j;
671906Sgm89044
672906Sgm89044 paramsz = mechanism->cm_param_len;
673*7421SDaniel.Anderson@Sun.COM #ifdef UNALIGNED_POINTERS_PERMITTED
674*7421SDaniel.Anderson@Sun.COM param = (uint32_t *)mechanism->cm_param;
675*7421SDaniel.Anderson@Sun.COM #else
676906Sgm89044 param = (uchar_t *)mechanism->cm_param;
677*7421SDaniel.Anderson@Sun.COM #endif /* UNALIGNED_POINTERS_PERMITTED */
678*7421SDaniel.Anderson@Sun.COM
679906Sgm89044 if ((paramsz != 0) && (paramsz != DES_IV_LEN)) {
680906Sgm89044 DBG(NULL, DWARN,
681906Sgm89044 "dca_3desctxinit: parameter(IV) length not %d (%d)",
682906Sgm89044 DES_IV_LEN, paramsz);
683906Sgm89044 return (CRYPTO_MECHANISM_PARAM_INVALID);
684906Sgm89044 }
685906Sgm89044
686906Sgm89044 if ((des_ctx = dca_getreq(dca, MCR1, 1)) == NULL) {
687906Sgm89044 dca_error(dca, "unable to allocate request for 3DES");
688906Sgm89044 return (CRYPTO_HOST_MEMORY);
689906Sgm89044 }
690906Sgm89044 /*
691906Sgm89044 * Identify and store the IV as a pair of native 32-bit words.
692906Sgm89044 *
693906Sgm89044 * If cm_param == NULL then the IV comes from the cd_miscdata field
694906Sgm89044 * in the crypto_data structure.
695906Sgm89044 */
696906Sgm89044 if (param != NULL) {
697906Sgm89044 ASSERT(paramsz == DES_IV_LEN);
698*7421SDaniel.Anderson@Sun.COM #ifdef UNALIGNED_POINTERS_PERMITTED
699*7421SDaniel.Anderson@Sun.COM des_ctx->dr_ctx.iv[0] = htonl(param[0]);
700*7421SDaniel.Anderson@Sun.COM des_ctx->dr_ctx.iv[1] = htonl(param[1]);
701*7421SDaniel.Anderson@Sun.COM #else
702906Sgm89044 des_ctx->dr_ctx.iv[0] = param[0]<<24 | param[1]<<16 |
703906Sgm89044 param[2]<<8 | param[3];
704906Sgm89044 des_ctx->dr_ctx.iv[1] = param[4]<<24 | param[5]<<16 |
705906Sgm89044 param[6]<<8 | param[7];
706*7421SDaniel.Anderson@Sun.COM #endif /* UNALIGNED_POINTERS_PERMITTED */
707906Sgm89044 }
708906Sgm89044 des_ctx->dr_ctx.residlen = 0;
709906Sgm89044 des_ctx->dr_ctx.activeresidlen = 0;
710906Sgm89044 des_ctx->dr_ctx.ctx_cm_type = mechanism->cm_type;
711906Sgm89044 ctx->cc_provider_private = des_ctx;
712906Sgm89044
713906Sgm89044 if (key->ck_format != CRYPTO_KEY_RAW) {
714906Sgm89044 DBG(NULL, DWARN,
715906Sgm89044 "dca_3desctxinit: only raw crypto key type support with DES/3DES");
716906Sgm89044 dca_3desctxfree(ctx);
717906Sgm89044 return (CRYPTO_KEY_TYPE_INCONSISTENT);
718906Sgm89044 }
719906Sgm89044
720906Sgm89044 len = key->ck_length;
721906Sgm89044 value = (uchar_t *)key->ck_data;
722906Sgm89044
723906Sgm89044 if (flags & DR_TRIPLE) {
724906Sgm89044 /* 3DES */
725906Sgm89044 switch (len) {
726906Sgm89044 case 192:
727906Sgm89044 for (i = 0; i < 6; i++) {
728906Sgm89044 des_ctx->dr_ctx.key[i] = 0;
729906Sgm89044 for (j = 0; j < 4; j++) {
730906Sgm89044 des_ctx->dr_ctx.key[i] <<= 8;
731906Sgm89044 des_ctx->dr_ctx.key[i] |= *value;
732906Sgm89044 value++;
733906Sgm89044 }
734906Sgm89044 }
735906Sgm89044 break;
736906Sgm89044
737906Sgm89044 case 128:
738906Sgm89044 for (i = 0; i < 4; i++) {
739906Sgm89044 des_ctx->dr_ctx.key[i] = 0;
740906Sgm89044 for (j = 0; j < 4; j++) {
741906Sgm89044 des_ctx->dr_ctx.key[i] <<= 8;
742906Sgm89044 des_ctx->dr_ctx.key[i] |= *value;
743906Sgm89044 value++;
744906Sgm89044 }
745906Sgm89044 }
746906Sgm89044 des_ctx->dr_ctx.key[4] = des_ctx->dr_ctx.key[0];
747906Sgm89044 des_ctx->dr_ctx.key[5] = des_ctx->dr_ctx.key[1];
748906Sgm89044 break;
749906Sgm89044
750906Sgm89044 default:
751906Sgm89044 DBG(NULL, DWARN, "Incorrect 3DES keysize (%d)", len);
752906Sgm89044 dca_3desctxfree(ctx);
753906Sgm89044 return (CRYPTO_KEY_SIZE_RANGE);
754906Sgm89044 }
755906Sgm89044 } else {
756906Sgm89044 /* single DES */
757906Sgm89044 if (len != 64) {
758906Sgm89044 DBG(NULL, DWARN, "Incorrect DES keysize (%d)", len);
759906Sgm89044 dca_3desctxfree(ctx);
760906Sgm89044 return (CRYPTO_KEY_SIZE_RANGE);
761906Sgm89044 }
762*7421SDaniel.Anderson@Sun.COM
763*7421SDaniel.Anderson@Sun.COM #ifdef UNALIGNED_POINTERS_PERMITTED
764*7421SDaniel.Anderson@Sun.COM value32 = (uint32_t *)value;
765*7421SDaniel.Anderson@Sun.COM des_ctx->dr_ctx.key[0] = htonl(value32[0]);
766*7421SDaniel.Anderson@Sun.COM des_ctx->dr_ctx.key[1] = htonl(value32[1]);
767*7421SDaniel.Anderson@Sun.COM #else
768906Sgm89044 des_ctx->dr_ctx.key[0] =
769906Sgm89044 value[0]<<24 | value[1]<<16 | value[2]<<8 | value[3];
770906Sgm89044 des_ctx->dr_ctx.key[1] =
771906Sgm89044 value[4]<<24 | value[5]<<16 | value[6]<<8 | value[7];
772*7421SDaniel.Anderson@Sun.COM #endif /* UNALIGNED_POINTERS_PERMITTED */
773*7421SDaniel.Anderson@Sun.COM
774906Sgm89044 /* for single des just repeat des key */
775906Sgm89044 des_ctx->dr_ctx.key[4] =
776906Sgm89044 des_ctx->dr_ctx.key[2] = des_ctx->dr_ctx.key[0];
777906Sgm89044 des_ctx->dr_ctx.key[5] =
778906Sgm89044 des_ctx->dr_ctx.key[3] = des_ctx->dr_ctx.key[1];
779906Sgm89044 }
780906Sgm89044
781906Sgm89044 /*
782906Sgm89044 * Setup the context here so that we do not need to setup it up
783906Sgm89044 * for every update
784906Sgm89044 */
785906Sgm89044 PUTCTX16(des_ctx, CTX_LENGTH, CTX_3DES_LENGTH);
786906Sgm89044 PUTCTX16(des_ctx, CTX_CMD, CMD_3DES);
787906Sgm89044 PUTCTX32(des_ctx, CTX_3DESDIRECTION,
788906Sgm89044 flags & DR_ENCRYPT ? CTX_3DES_ENCRYPT : CTX_3DES_DECRYPT);
789906Sgm89044 PUTCTX32(des_ctx, CTX_3DESKEY1HI, des_ctx->dr_ctx.key[0]);
790906Sgm89044 PUTCTX32(des_ctx, CTX_3DESKEY1LO, des_ctx->dr_ctx.key[1]);
791906Sgm89044 PUTCTX32(des_ctx, CTX_3DESKEY2HI, des_ctx->dr_ctx.key[2]);
792906Sgm89044 PUTCTX32(des_ctx, CTX_3DESKEY2LO, des_ctx->dr_ctx.key[3]);
793906Sgm89044 PUTCTX32(des_ctx, CTX_3DESKEY3HI, des_ctx->dr_ctx.key[4]);
794906Sgm89044 PUTCTX32(des_ctx, CTX_3DESKEY3LO, des_ctx->dr_ctx.key[5]);
795906Sgm89044
796906Sgm89044 return (CRYPTO_SUCCESS);
797906Sgm89044 }
798