xref: /illumos-gate/usr/src/uts/common/crypto/io/dca_dsa.c (revision dd72704bd9e794056c558153663c739e2012d721)
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 2006 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * Deimos - cryptographic acceleration based upon Broadcom 582x.
29  */
30 
31 #include <sys/types.h>
32 #include <sys/ddi.h>
33 #include <sys/sunddi.h>
34 #include <sys/kmem.h>
35 #include <sys/crypto/spi.h>
36 #include <sys/crypto/dca.h>
37 
38 /*
39  * DSA implementation.
40  */
41 
42 static void dca_dsa_sign_done(dca_request_t *, int);
43 static void dca_dsa_verify_done(dca_request_t *, int);
44 
45 
46 int dca_dsa_sign(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *sig,
47     crypto_req_handle_t req);
48 int dca_dsa_verify(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *sig,
49     crypto_req_handle_t req);
50 int dca_dsainit(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
51     crypto_key_t *key, int kmflag, int mode);
52 
53 
54 int
55 dca_dsa_sign(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *sig,
56     crypto_req_handle_t req)
57 {
58 	dca_request_t	*reqp = ctx->cc_provider_private;
59 	dca_t		*dca = ctx->cc_provider;
60 	int		err;
61 	int		rv = CRYPTO_QUEUED;
62 	caddr_t		kaddr;
63 	size_t		buflen;
64 
65 	buflen = dca_length(data);
66 	if (buflen != SHA1LEN) {
67 		DBG(dca, DWARN, "dca_dsa_sign: data length != %d", SHA1LEN);
68 		rv = CRYPTO_DATA_LEN_RANGE;
69 		goto errout;
70 	}
71 
72 	/* Return length needed to store the output. */
73 	if (dca_length(sig) < DSASIGLEN) {
74 		DBG(dca, DWARN,
75 		    "dca_dsa_sign: output buffer too short (%d < %d)",
76 		    dca_length(sig), DSASIGLEN);
77 		sig->cd_length = DSASIGLEN;
78 		rv = CRYPTO_BUFFER_TOO_SMALL;
79 		goto errout;
80 	}
81 
82 	/*
83 	 * Don't change the data values of the data crypto_data_t structure
84 	 * yet. Only reset the sig cd_length to zero before writing to it.
85 	 */
86 
87 	reqp->dr_job_stat = DS_DSASIGN;
88 	reqp->dr_byte_stat = -1;
89 	reqp->dr_in = data;
90 	reqp->dr_out = sig;
91 	reqp->dr_callback = dca_dsa_sign_done;
92 
93 	reqp->dr_kcf_req = req;
94 	/* dca_gather() increments cd_offset & dec. cd_length by SHA1LEN. */
95 	err = dca_gather(data, reqp->dr_ibuf_kaddr, SHA1LEN, 1);
96 	if (err != CRYPTO_SUCCESS) {
97 		DBG(dca, DWARN, "dca_dsa_sign: dca_gather() failed");
98 		rv = err;
99 		goto errout;
100 	}
101 
102 
103 	/* sync the input buffer */
104 	(void) ddi_dma_sync(reqp->dr_ibuf_dmah, 0, SHA1LEN,
105 		DDI_DMA_SYNC_FORDEV);
106 	if (dca_check_dma_handle(dca, reqp->dr_ibuf_dmah,
107 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
108 		reqp->destroy = TRUE;
109 		rv = CRYPTO_DEVICE_ERROR;
110 		goto errout;
111 	}
112 
113 	reqp->dr_in_paddr = reqp->dr_ibuf_paddr;
114 	reqp->dr_in_next = 0;
115 	reqp->dr_in_len = SHA1LEN;
116 	reqp->dr_pkt_length = buflen;
117 
118 	/*
119 	 * The output requires *two* buffers, r followed by s.
120 	 */
121 	kaddr = reqp->dr_ctx_kaddr + reqp->dr_offset;
122 
123 	/* r */
124 	reqp->dr_out_paddr = reqp->dr_obuf_paddr;
125 	reqp->dr_out_len = DSAPARTLEN;
126 	reqp->dr_out_next = reqp->dr_ctx_paddr + reqp->dr_offset;
127 
128 	/* s */
129 	PUTDESC32(reqp, kaddr, DESC_BUFADDR,
130 	    reqp->dr_obuf_paddr + DSAPARTLEN);
131 	PUTDESC32(reqp, kaddr, DESC_NEXT, 0);
132 	PUTDESC16(reqp, kaddr, DESC_RSVD, 0);
133 	PUTDESC16(reqp, kaddr, DESC_LENGTH, DSAPARTLEN);
134 
135 	/* schedule the work by doing a submit */
136 	rv = dca_start(dca, reqp, MCR2, 1);
137 
138 errout:
139 
140 	if (rv != CRYPTO_QUEUED && rv != CRYPTO_BUFFER_TOO_SMALL)
141 		(void) dca_free_context(ctx);
142 
143 	return (rv);
144 }
145 
146 static void
147 dca_dsa_sign_done(dca_request_t *reqp, int errno)
148 {
149 	if (errno == CRYPTO_SUCCESS) {
150 		(void) ddi_dma_sync(reqp->dr_obuf_dmah, 0, DSASIGLEN,
151 		    DDI_DMA_SYNC_FORKERNEL);
152 		if (dca_check_dma_handle(reqp->dr_dca, reqp->dr_obuf_dmah,
153 		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
154 			reqp->destroy = TRUE;
155 			errno = CRYPTO_DEVICE_ERROR;
156 			goto errout;
157 		}
158 		/*
159 		 * Set the sig cd_length to zero so it's ready to take the
160 		 * signature. Have already confirmed its size is adequate.
161 		 */
162 		reqp->dr_out->cd_length = 0;
163 		errno = dca_scatter(reqp->dr_obuf_kaddr,
164 		    reqp->dr_out, DSAPARTLEN, 1);
165 		if (errno != CRYPTO_SUCCESS) {
166 			DBG(reqp->dr_dca, DWARN,
167 			    "dca_dsa_sign_done: dca_scatter() failed");
168 			goto errout;
169 		}
170 		errno = dca_scatter(reqp->dr_obuf_kaddr+DSAPARTLEN,
171 		    reqp->dr_out, DSAPARTLEN, 1);
172 		if (errno != CRYPTO_SUCCESS) {
173 			DBG(reqp->dr_dca, DWARN,
174 			    "dca_dsa_sign_done: dca_scatter() failed");
175 		}
176 	}
177 errout:
178 	ASSERT(reqp->dr_kcf_req != NULL);
179 
180 	/* notify framework that request is completed */
181 	crypto_op_notification(reqp->dr_kcf_req, errno);
182 	DBG(reqp->dr_dca, DINTR,
183 	    "dca_dsa_sign_done: rtn 0x%x to kef via crypto_op_notification",
184 	    errno);
185 
186 	/*
187 	 * For non-atomic operations, reqp will be freed in the kCF
188 	 * callback function since it may be needed again if
189 	 * CRYPTO_BUFFER_TOO_SMALL is returned to kCF
190 	 */
191 	if (reqp->dr_ctx.atomic) {
192 		crypto_ctx_t ctx;
193 		ctx.cc_provider_private = reqp;
194 		dca_dsactxfree(&ctx);
195 	}
196 }
197 
198 int
199 dca_dsa_verify(crypto_ctx_t *ctx, crypto_data_t *data, crypto_data_t *sig,
200     crypto_req_handle_t req)
201 {
202 	dca_request_t	*reqp = ctx->cc_provider_private;
203 	dca_t		*dca = ctx->cc_provider;
204 	int		err;
205 	int		rv = CRYPTO_QUEUED;
206 	caddr_t		kaddr;
207 
208 	/* Impossible for verify to be an in-place operation. */
209 	if (sig == NULL) {
210 		rv = CRYPTO_ARGUMENTS_BAD;
211 		goto errout;
212 	}
213 
214 	if (dca_length(data) != SHA1LEN) {
215 		DBG(dca, DWARN, "dca_dsa_verify: input length != %d", SHA1LEN);
216 		rv = CRYPTO_DATA_LEN_RANGE;
217 		goto errout;
218 	}
219 
220 	if (dca_length(sig) != DSASIGLEN) {
221 		DBG(dca, DWARN, "dca_dsa_verify: signature length != %d",
222 		    DSASIGLEN);
223 		rv = CRYPTO_SIGNATURE_LEN_RANGE;
224 		goto errout;
225 	}
226 
227 	/* Don't change the data & sig values for verify. */
228 
229 	reqp->dr_job_stat = DS_DSAVERIFY;
230 	reqp->dr_byte_stat = -1;
231 
232 	/*
233 	 * Grab h, r and s.
234 	 */
235 	err = dca_gather(data, reqp->dr_ibuf_kaddr, SHA1LEN, 1);
236 	if (err != CRYPTO_SUCCESS) {
237 		DBG(dca, DWARN,
238 		    "dca_dsa_vrfy: dca_gather() failed for h");
239 		rv = err;
240 		goto errout;
241 	}
242 	err = dca_gather(sig, reqp->dr_ibuf_kaddr+SHA1LEN, DSAPARTLEN, 1);
243 	if (err != CRYPTO_SUCCESS) {
244 		DBG(dca, DWARN,
245 		    "dca_dsa_vrfy: dca_gather() failed for r");
246 		rv = err;
247 		goto errout;
248 	}
249 	err = dca_gather(sig, reqp->dr_ibuf_kaddr+SHA1LEN+DSAPARTLEN,
250 	    DSAPARTLEN, 1);
251 	if (err != CRYPTO_SUCCESS) {
252 		DBG(dca, DWARN,
253 		    "dca_dsa_vrfy: dca_gather() failed for s");
254 		rv = err;
255 		goto errout;
256 	}
257 	/*
258 	 * As dca_gather() increments the cd_offset and decrements
259 	 * the cd_length as it copies the data rewind the values ready for
260 	 * the final compare.
261 	 */
262 	sig->cd_offset -= (DSAPARTLEN * 2);
263 	sig->cd_length += (DSAPARTLEN * 2);
264 	/* sync the input buffer */
265 	(void) ddi_dma_sync(reqp->dr_ibuf_dmah, 0, SHA1LEN + DSAPARTLEN,
266 	    DDI_DMA_SYNC_FORDEV);
267 
268 	if (dca_check_dma_handle(dca, reqp->dr_ibuf_dmah,
269 	    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
270 		reqp->destroy = TRUE;
271 		rv = CRYPTO_DEVICE_ERROR;
272 		goto errout;
273 	}
274 
275 	reqp->dr_in = data;
276 	reqp->dr_out = sig;
277 	reqp->dr_kcf_req = req;
278 	reqp->dr_flags |= DR_SCATTER | DR_GATHER;
279 	reqp->dr_callback = dca_dsa_verify_done;
280 
281 	/*
282 	 * Input requires three buffers.  m, followed by r, followed by s.
283 	 * In order to deal with things cleanly, we reverse the signature
284 	 * into the buffer and then fix up the pointers.
285 	 */
286 	reqp->dr_pkt_length = SHA1LEN;
287 
288 	reqp->dr_in_paddr = reqp->dr_ibuf_paddr;
289 	reqp->dr_in_len = SHA1LEN;
290 	reqp->dr_in_next = reqp->dr_ctx_paddr + reqp->dr_offset;
291 
292 	reqp->dr_out_paddr = reqp->dr_obuf_paddr;
293 	reqp->dr_out_len = DSAPARTLEN;
294 	reqp->dr_out_next = 0;
295 
296 	/* setup 1st chain for r */
297 	kaddr = reqp->dr_ctx_kaddr + reqp->dr_offset;
298 	PUTDESC32(reqp, kaddr, DESC_BUFADDR, reqp->dr_ibuf_paddr + SHA1LEN);
299 	PUTDESC32(reqp, kaddr, DESC_NEXT,
300 	    reqp->dr_ctx_paddr + reqp->dr_offset + DESC_SIZE);
301 	PUTDESC16(reqp, kaddr, DESC_RSVD, 0);
302 	PUTDESC16(reqp, kaddr, DESC_LENGTH, DSAPARTLEN);
303 
304 	/* and 2nd chain for s */
305 	kaddr = reqp->dr_ctx_kaddr + reqp->dr_offset + DESC_SIZE;
306 	PUTDESC32(reqp, kaddr, DESC_BUFADDR, reqp->dr_ibuf_paddr +
307 	    SHA1LEN + DSAPARTLEN);
308 	PUTDESC32(reqp, kaddr, DESC_NEXT, 0);
309 	PUTDESC16(reqp, kaddr, DESC_RSVD, 0);
310 	PUTDESC16(reqp, kaddr, DESC_LENGTH, DSAPARTLEN);
311 
312 	/* schedule the work by doing a submit */
313 	rv = dca_start(dca, reqp, MCR2, 1);
314 
315 errout:
316 	if (rv != CRYPTO_QUEUED && rv != CRYPTO_BUFFER_TOO_SMALL) {
317 		(void) dca_free_context(ctx);
318 	}
319 	return (rv);
320 }
321 
322 static void
323 dca_dsa_verify_done(dca_request_t *reqp, int errno)
324 {
325 	if (errno == CRYPTO_SUCCESS) {
326 		int		count = DSAPARTLEN;
327 		crypto_data_t	*sig = reqp->dr_out;
328 		caddr_t		daddr;
329 
330 		(void) ddi_dma_sync(reqp->dr_obuf_dmah, 0, count,
331 		    DDI_DMA_SYNC_FORKERNEL);
332 		if (dca_check_dma_handle(reqp->dr_dca, reqp->dr_obuf_dmah,
333 		    DCA_FM_ECLASS_NONE) != DDI_SUCCESS) {
334 			reqp->destroy = TRUE;
335 			errno = CRYPTO_DEVICE_ERROR;
336 			goto errout;
337 		}
338 
339 		/* Can only handle a contiguous data buffer currently. */
340 		if (dca_sgcheck(reqp->dr_dca, sig, DCA_SG_CONTIG)) {
341 			errno = CRYPTO_SIGNATURE_INVALID;
342 			goto errout;
343 		}
344 
345 		if ((daddr = dca_bufdaddr(sig)) == NULL) {
346 			errno = CRYPTO_ARGUMENTS_BAD;
347 			goto errout;
348 		}
349 
350 		if (dca_bcmp_reverse(daddr, reqp->dr_obuf_kaddr,
351 		    DSAPARTLEN) != 0) {
352 			/* VERIFY FAILED */
353 			errno = CRYPTO_SIGNATURE_INVALID;
354 		}
355 	}
356 errout:
357 	ASSERT(reqp->dr_kcf_req != NULL);
358 
359 	/* notify framework that request is completed */
360 
361 	crypto_op_notification(reqp->dr_kcf_req, errno);
362 	DBG(reqp->dr_dca, DINTR,
363 	    "dca_dsa_verify_done: rtn 0x%x to kef via crypto_op_notification",
364 	    errno);
365 
366 	/*
367 	 * For non-atomic operations, reqp will be freed in the kCF
368 	 * callback function since it may be needed again if
369 	 * CRYPTO_BUFFER_TOO_SMALL is returned to kCF
370 	 */
371 	if (reqp->dr_ctx.atomic) {
372 		crypto_ctx_t ctx;
373 		ctx.cc_provider_private = reqp;
374 		dca_dsactxfree(&ctx);
375 	}
376 }
377 
378 /* ARGSUSED */
379 int
380 dca_dsainit(crypto_ctx_t *ctx, crypto_mechanism_t *mechanism,
381     crypto_key_t *key, int kmflag, int mode)
382 {
383 	crypto_object_attribute_t	*attr;
384 	unsigned			plen = 0, qlen = 0, glen = 0, xlen = 0;
385 	uchar_t				*p, *q, *g, *x;
386 	dca_request_t			*reqp = NULL;
387 	dca_t				*dca = (dca_t *)ctx->cc_provider;
388 	int				rv = CRYPTO_SUCCESS;
389 	unsigned			pbits, padjlen;
390 	uint16_t			ctxlen;
391 	caddr_t				kaddr;
392 
393 	if ((reqp = dca_getreq(dca, MCR2, 1)) == NULL) {
394 		dca_error(dca,
395 		    "dca_dsainit: unable to allocate request for DSA");
396 		rv = CRYPTO_HOST_MEMORY;
397 		goto errout;
398 	}
399 
400 	ctx->cc_provider_private = reqp;
401 	reqp->dr_ctx.ctx_cm_type = mechanism->cm_type;
402 
403 	if ((attr = dca_get_key_attr(key)) == NULL) {
404 		DBG(NULL, DWARN, "dca_dsainit: key attributes missing");
405 		rv = CRYPTO_KEY_TYPE_INCONSISTENT;
406 		goto errout;
407 	}
408 
409 	/* Prime */
410 	if (dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_PRIME,
411 	    (void *) &p, &plen)) {
412 		DBG(NULL, DWARN, "dca_dsainit: prime key value not present");
413 		rv = CRYPTO_ARGUMENTS_BAD;
414 		goto errout;
415 	}
416 
417 	/* Subprime */
418 	if (dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_SUBPRIME,
419 	    (void *) &q, &qlen)) {
420 		DBG(NULL, DWARN, "dca_dsainit: subprime key value not present");
421 		rv = CRYPTO_ARGUMENTS_BAD;
422 		goto errout;
423 	}
424 
425 	/* Base */
426 	if (dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_BASE,
427 	    (void *) &g, &glen)) {
428 		DBG(NULL, DWARN, "dca_dsainit: base key value not present");
429 		rv = CRYPTO_ARGUMENTS_BAD;
430 		goto errout;
431 	}
432 
433 	/* Value */
434 	if (dca_attr_lookup_uint8_array(attr, key->ck_count, CKA_VALUE,
435 	    (void *) &x, &xlen)) {
436 		DBG(NULL, DWARN, "dca_dsainit: value key not present");
437 		rv = CRYPTO_ARGUMENTS_BAD;
438 		goto errout;
439 	}
440 
441 	if (plen == 0 || qlen == 0 || glen == 0 || xlen == 0) {
442 		rv = CRYPTO_ARGUMENTS_BAD;
443 		goto errout;
444 	}
445 
446 	if (plen > DSA_MAX_KEY_LEN) {
447 		/* maximum 1Kbit key */
448 		DBG(NULL, DWARN, "dca_dsainit: maximum 1Kbit key (%d)", plen);
449 		rv = CRYPTO_KEY_SIZE_RANGE;
450 		goto errout;
451 	}
452 
453 	if (qlen > DSAPARTLEN) {
454 		DBG(NULL, DWARN, "dca_dsainit: q is too long (%d)", qlen);
455 		rv = CRYPTO_KEY_SIZE_RANGE;
456 		goto errout;
457 	}
458 
459 	if (mode == DCA_DSA_SIGN && xlen > DSAPARTLEN) {
460 		DBG(NULL, DWARN,
461 		    "dca_dsainit: private key is too long (%d)", xlen);
462 		rv = CRYPTO_KEY_SIZE_RANGE;
463 		goto errout;
464 	}
465 
466 	/*
467 	 * Setup the key partion of the request.
468 	 */
469 
470 	pbits = dca_bitlen(p, plen);
471 	padjlen = dca_padfull(pbits);
472 
473 	/* accounts for leading context words */
474 	if (mode == DCA_DSA_SIGN) {
475 		ctxlen = CTX_DSABIGNUMS + DSAPARTLEN + (padjlen * 2) +
476 		    DSAPARTLEN;
477 		PUTCTX16(reqp, CTX_CMD, CMD_DSASIGN);
478 	} else {
479 		ctxlen = CTX_DSABIGNUMS + DSAPARTLEN + (padjlen * 3);
480 		PUTCTX16(reqp, CTX_CMD, CMD_DSAVERIFY);
481 	}
482 
483 	PUTCTX16(reqp, CTX_LENGTH, ctxlen);
484 	PUTCTX16(reqp, CTX_DSAMSGTYPE, CTX_DSAMSGTYPE_SHA1);
485 	PUTCTX16(reqp, CTX_DSARSVD, 0);
486 	if (mode == DCA_DSA_SIGN)
487 		PUTCTX16(reqp, CTX_DSARNG, CTX_DSARNG_GEN);
488 	else
489 		PUTCTX16(reqp, CTX_DSARNG, 0);
490 	PUTCTX16(reqp, CTX_DSAPLEN, pbits);
491 
492 	kaddr = reqp->dr_ctx_kaddr + CTX_DSABIGNUMS;
493 
494 	/* store the bignums */
495 	dca_reverse(q, kaddr, qlen, DSAPARTLEN);
496 	kaddr += DSAPARTLEN;
497 
498 	dca_reverse(p, kaddr, plen, padjlen);
499 	kaddr += padjlen;
500 
501 	dca_reverse(g, kaddr, glen, padjlen);
502 	kaddr += padjlen;
503 
504 	if (mode == DCA_DSA_SIGN) {
505 		dca_reverse(x, kaddr, xlen, DSAPARTLEN);
506 		kaddr += DSAPARTLEN;
507 	} else {
508 		dca_reverse(x, kaddr, xlen, padjlen);
509 		kaddr += padjlen;
510 	}
511 
512 	return (CRYPTO_SUCCESS);
513 
514 errout:
515 
516 	dca_dsactxfree(ctx);
517 	return (rv);
518 }
519 
520 void
521 dca_dsactxfree(void *arg)
522 {
523 	crypto_ctx_t	*ctx = (crypto_ctx_t *)arg;
524 	dca_request_t	*reqp = ctx->cc_provider_private;
525 
526 	if (reqp == NULL)
527 		return;
528 
529 	reqp->dr_ctx.ctx_cm_type = 0;
530 	reqp->dr_ctx.atomic = 0;
531 	if (reqp->destroy)
532 		dca_destroyreq(reqp);
533 	else
534 		dca_freereq(reqp);
535 
536 	ctx->cc_provider_private = NULL;
537 }
538 
539 int
540 dca_dsaatomic(crypto_provider_handle_t provider,
541     crypto_session_id_t session_id, crypto_mechanism_t *mechanism,
542     crypto_key_t *key, crypto_data_t *data, crypto_data_t *sig,
543     int kmflag, crypto_req_handle_t req, int mode)
544 {
545 	crypto_ctx_t	ctx;	/* on the stack */
546 	int		rv;
547 
548 	ctx.cc_provider = provider;
549 	ctx.cc_session = session_id;
550 
551 	rv = dca_dsainit(&ctx, mechanism, key, kmflag, mode);
552 	if (rv != CRYPTO_SUCCESS) {
553 		DBG(NULL, DWARN, "dca_dsaatomic: dca_dsainit() failed");
554 		return (rv);
555 	}
556 
557 	/*
558 	 * Set the atomic flag so that the hardware callback function
559 	 * will free the context.
560 	 */
561 	((dca_request_t *)ctx.cc_provider_private)->dr_ctx.atomic = 1;
562 
563 	if (mode == DCA_DSA_SIGN) {
564 		rv = dca_dsa_sign(&ctx, data, sig, req);
565 	} else {
566 		ASSERT(mode == DCA_DSA_VRFY);
567 		rv = dca_dsa_verify(&ctx, data, sig, req);
568 	}
569 
570 	/*
571 	 * The context will be freed in the hardware callback function if it
572 	 * is queued
573 	 */
574 	if (rv != CRYPTO_QUEUED)
575 		dca_dsactxfree(&ctx);
576 
577 	return (rv);
578 }
579