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