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 <pthread.h> 28 #include <errno.h> 29 #include <sys/crypto/ioctl.h> 30 #include <security/cryptoki.h> 31 #include "kernelGlobal.h" 32 #include "kernelSession.h" 33 #include "kernelEmulate.h" 34 35 static CK_RV 36 common_digest_init(CK_SESSION_HANDLE hSession, 37 CK_MECHANISM_PTR pMechanism, boolean_t is_external_caller) 38 { 39 CK_RV rv; 40 kernel_session_t *session_p; 41 boolean_t ses_lock_held = B_FALSE; 42 crypto_digest_init_t digest_init; 43 crypto_mech_type_t k_mech_type; 44 int r; 45 46 if (!kernel_initialized) 47 return (CKR_CRYPTOKI_NOT_INITIALIZED); 48 49 if (pMechanism == NULL) 50 return (CKR_ARGUMENTS_BAD); 51 52 /* 53 * Get the kernel's internal mechanism number. 54 */ 55 rv = kernel_mech(pMechanism->mechanism, &k_mech_type); 56 if (rv != CKR_OK) 57 return (rv); 58 59 /* 60 * Obtain the session pointer. Also, increment the session 61 * reference count. 62 */ 63 rv = handle2session(hSession, &session_p); 64 if (rv != CKR_OK) 65 return (rv); 66 67 /* Acquire the session lock */ 68 (void) pthread_mutex_lock(&session_p->session_mutex); 69 ses_lock_held = B_TRUE; 70 71 /* 72 * This active flag will remain ON until application calls either 73 * C_Digest or C_DigestFinal to actually obtain the value of 74 * the message digest. 75 */ 76 session_p->digest.flags |= CRYPTO_OPERATION_ACTIVE; 77 78 if (SLOT_HAS_LIMITED_HASH(session_p) && is_external_caller) { 79 session_p->digest.mech.mechanism = pMechanism->mechanism; 80 session_p->digest.mech.pParameter = NULL; 81 session_p->digest.mech.ulParameterLen = 0; 82 session_p->digest.flags |= CRYPTO_EMULATE; 83 rv = emulate_buf_init(session_p, EDIGEST_LENGTH, OP_DIGEST); 84 REFRELE(session_p, ses_lock_held); 85 return (rv); 86 } 87 88 digest_init.di_session = session_p->k_session; 89 (void) pthread_mutex_unlock(&session_p->session_mutex); 90 ses_lock_held = B_FALSE; 91 digest_init.di_mech.cm_type = k_mech_type; 92 digest_init.di_mech.cm_param = pMechanism->pParameter; 93 94 /* 95 * If pParameter is NULL, set cm_param_len to be 0, so that ioctl call 96 * will have a clean input data. 97 */ 98 if (pMechanism->pParameter != NULL) 99 digest_init.di_mech.cm_param_len = pMechanism->ulParameterLen; 100 else 101 digest_init.di_mech.cm_param_len = 0; 102 103 while ((r = ioctl(kernel_fd, CRYPTO_DIGEST_INIT, &digest_init)) < 0) { 104 if (errno != EINTR) 105 break; 106 } 107 if (r < 0) { 108 rv = CKR_FUNCTION_FAILED; 109 } else { 110 rv = crypto2pkcs11_error_number(digest_init.di_return_value); 111 } 112 113 if (rv != CKR_OK) { 114 (void) pthread_mutex_lock(&session_p->session_mutex); 115 ses_lock_held = B_TRUE; 116 session_p->digest.flags &= ~CRYPTO_OPERATION_ACTIVE; 117 /* 118 * Decrement the session reference count. 119 * We hold the session lock, and REFRELE() 120 * will release the session lock for us. 121 */ 122 REFRELE(session_p, ses_lock_held); 123 return (rv); 124 } 125 126 /* 127 * Decrement the session reference count. 128 * We do not hold the session lock. 129 */ 130 REFRELE(session_p, ses_lock_held); 131 return (rv); 132 } 133 134 CK_RV 135 C_DigestInit(CK_SESSION_HANDLE hSession, CK_MECHANISM_PTR pMechanism) 136 { 137 return (common_digest_init(hSession, pMechanism, B_TRUE)); 138 } 139 140 CK_RV 141 C_Digest(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pData, CK_ULONG ulDataLen, 142 CK_BYTE_PTR pDigest, CK_ULONG_PTR pulDigestLen) 143 { 144 CK_RV rv; 145 kernel_session_t *session_p; 146 boolean_t ses_lock_held = B_FALSE; 147 crypto_digest_t digest; 148 int r; 149 150 if (!kernel_initialized) 151 return (CKR_CRYPTOKI_NOT_INITIALIZED); 152 153 /* 154 * Obtain the session pointer. Also, increment the session 155 * reference count. 156 */ 157 rv = handle2session(hSession, &session_p); 158 if (rv != CKR_OK) 159 return (rv); 160 161 if (pData == NULL || pulDigestLen == NULL) { 162 rv = CKR_ARGUMENTS_BAD; 163 goto clean_exit; 164 } 165 166 /* Acquire the session lock */ 167 (void) pthread_mutex_lock(&session_p->session_mutex); 168 ses_lock_held = B_TRUE; 169 170 /* Application must call C_DigestInit before calling C_Digest */ 171 if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { 172 /* 173 * Decrement the session reference count. 174 * We hold the session lock, and REFRELE() 175 * will release the session lock for us. 176 */ 177 REFRELE(session_p, ses_lock_held); 178 return (CKR_OPERATION_NOT_INITIALIZED); 179 } 180 181 /* 182 * C_Digest must be called without intervening C_DigestUpdate 183 * calls. 184 */ 185 if (session_p->digest.flags & CRYPTO_OPERATION_UPDATE) { 186 /* 187 * C_Digest can not be used to terminate a multi-part 188 * operation, so we'll leave the active digest operation 189 * flag on and let the application continue with the 190 * digest update operation. 191 * 192 * Decrement the session reference count. 193 * We hold the session lock, and REFRELE() 194 * will release the session lock for us. 195 */ 196 REFRELE(session_p, ses_lock_held); 197 return (CKR_FUNCTION_FAILED); 198 } 199 200 if (session_p->digest.flags & CRYPTO_EMULATE) { 201 crypto_active_op_t *opp; 202 CK_MECHANISM_PTR pMechanism; 203 204 opp = &(session_p->digest); 205 if (opp->context == NULL) { 206 REFRELE(session_p, ses_lock_held); 207 return (CKR_ARGUMENTS_BAD); 208 } 209 pMechanism = &(opp->mech); 210 211 if ((ulDataLen < SLOT_THRESHOLD(session_p)) || 212 (ulDataLen > SLOT_HASH_MAX_INDATA_LEN(session_p))) { 213 session_p->digest.flags |= CRYPTO_EMULATE_USING_SW; 214 (void) pthread_mutex_unlock(&session_p->session_mutex); 215 ses_lock_held = B_FALSE; 216 217 rv = do_soft_digest(get_spp(opp), pMechanism, 218 pData, ulDataLen, pDigest, pulDigestLen, 219 OP_INIT | OP_SINGLE); 220 goto done; 221 } else if (!(session_p->digest.flags & 222 CRYPTO_EMULATE_INIT_DONE)) { 223 session_p->digest.flags |= CRYPTO_EMULATE_INIT_DONE; 224 (void) pthread_mutex_unlock(&session_p->session_mutex); 225 ses_lock_held = B_FALSE; 226 227 rv = common_digest_init(hSession, pMechanism, B_FALSE); 228 if (rv != CKR_OK) 229 goto clean_exit; 230 (void) pthread_mutex_lock(&session_p->session_mutex); 231 ses_lock_held = B_TRUE; 232 } 233 } 234 235 digest.cd_session = session_p->k_session; 236 (void) pthread_mutex_unlock(&session_p->session_mutex); 237 ses_lock_held = B_FALSE; 238 digest.cd_datalen = ulDataLen; 239 digest.cd_databuf = (char *)pData; 240 digest.cd_digestbuf = (char *)pDigest; 241 digest.cd_digestlen = *pulDigestLen; 242 243 while ((r = ioctl(kernel_fd, CRYPTO_DIGEST, &digest)) < 0) { 244 if (errno != EINTR) 245 break; 246 } 247 if (r < 0) { 248 rv = CKR_FUNCTION_FAILED; 249 } else { 250 rv = crypto2pkcs11_error_number(digest.cd_return_value); 251 } 252 253 if ((rv == CKR_OK) || (rv == CKR_BUFFER_TOO_SMALL)) 254 *pulDigestLen = digest.cd_digestlen; 255 256 done: 257 if ((rv == CKR_BUFFER_TOO_SMALL) || 258 (rv == CKR_OK && pDigest == NULL)) { 259 /* 260 * We will not terminate the active digest operation flag, 261 * when the application-supplied buffer is too small, or 262 * the application asks for the length of buffer to hold 263 * the message digest. 264 * 265 * Decrement the session reference count. 266 * We do not hold the session lock. 267 */ 268 REFRELE(session_p, ses_lock_held); 269 return (rv); 270 } 271 272 clean_exit: 273 /* 274 * Terminates the active digest operation. 275 * Application needs to call C_DigestInit again for next 276 * digest operation. 277 */ 278 (void) pthread_mutex_lock(&session_p->session_mutex); 279 ses_lock_held = B_TRUE; 280 281 REINIT_OPBUF(&session_p->digest); 282 session_p->digest.flags = 0; 283 284 /* 285 * Decrement the session reference count. 286 * We hold the session lock, and REFRELE() 287 * will release the session lock for us. 288 */ 289 REFRELE(session_p, ses_lock_held); 290 291 return (rv); 292 } 293 294 CK_RV 295 C_DigestUpdate(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pPart, 296 CK_ULONG ulPartLen) 297 { 298 299 CK_RV rv; 300 kernel_session_t *session_p; 301 boolean_t ses_lock_held = B_FALSE; 302 crypto_digest_update_t digest_update; 303 int r; 304 305 if (!kernel_initialized) 306 return (CKR_CRYPTOKI_NOT_INITIALIZED); 307 308 /* 309 * Obtain the session pointer. Also, increment the session 310 * reference count. 311 */ 312 rv = handle2session(hSession, &session_p); 313 if (rv != CKR_OK) 314 return (rv); 315 316 if (pPart == NULL) { 317 rv = CKR_ARGUMENTS_BAD; 318 goto clean_exit; 319 } 320 321 /* Acquire the session lock */ 322 (void) pthread_mutex_lock(&session_p->session_mutex); 323 ses_lock_held = B_TRUE; 324 325 /* 326 * Application must call C_DigestInit before calling 327 * C_DigestUpdate. 328 */ 329 if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { 330 /* 331 * Decrement the session reference count. 332 * We hold the session lock, and REFRELE() 333 * will release the session lock for us. 334 */ 335 REFRELE(session_p, ses_lock_held); 336 return (CKR_OPERATION_NOT_INITIALIZED); 337 } 338 339 /* Set update flag to protect C_Digest */ 340 session_p->digest.flags |= CRYPTO_OPERATION_UPDATE; 341 342 if (session_p->digest.flags & CRYPTO_EMULATE) { 343 (void) pthread_mutex_unlock(&session_p->session_mutex); 344 ses_lock_held = B_FALSE; 345 rv = emulate_update(session_p, pPart, ulPartLen, OP_DIGEST); 346 goto done; 347 } 348 349 digest_update.du_session = session_p->k_session; 350 (void) pthread_mutex_unlock(&session_p->session_mutex); 351 ses_lock_held = B_FALSE; 352 digest_update.du_datalen = ulPartLen; 353 digest_update.du_databuf = (char *)pPart; 354 355 while ((r = ioctl(kernel_fd, CRYPTO_DIGEST_UPDATE, 356 &digest_update)) < 0) { 357 if (errno != EINTR) 358 break; 359 } 360 if (r < 0) { 361 rv = CKR_FUNCTION_FAILED; 362 } else { 363 rv = crypto2pkcs11_error_number(digest_update.du_return_value); 364 } 365 366 done: 367 if (rv == CKR_OK) { 368 /* 369 * Decrement the session reference count. 370 * We do not hold the session lock. 371 */ 372 REFRELE(session_p, ses_lock_held); 373 return (CKR_OK); 374 } 375 376 clean_exit: 377 /* 378 * After an error occurred, terminate the current digest 379 * operation by resetting the active and update flags. 380 */ 381 (void) pthread_mutex_lock(&session_p->session_mutex); 382 ses_lock_held = B_TRUE; 383 REINIT_OPBUF(&session_p->digest); 384 session_p->digest.flags = 0; 385 386 /* 387 * Decrement the session reference count. 388 * We hold the session lock, and REFRELE() 389 * will release the session lock for us. 390 */ 391 REFRELE(session_p, ses_lock_held); 392 393 return (rv); 394 } 395 396 397 CK_RV 398 C_DigestKey(CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hKey) 399 { 400 401 CK_RV rv; 402 kernel_session_t *session_p; 403 kernel_object_t *key_p; 404 boolean_t ses_lock_held = B_FALSE; 405 CK_BYTE_PTR pPart; 406 CK_ULONG ulPartLen; 407 crypto_digest_key_t digest_key; 408 crypto_digest_update_t digest_update; 409 int r; 410 411 if (!kernel_initialized) 412 return (CKR_CRYPTOKI_NOT_INITIALIZED); 413 414 /* 415 * Obtain the session pointer. Also, increment the session 416 * reference count. 417 */ 418 rv = handle2session(hSession, &session_p); 419 if (rv != CKR_OK) 420 return (rv); 421 422 /* Obtain the object pointer. */ 423 HANDLE2OBJECT(hKey, key_p, rv); 424 if (rv != CKR_OK) { 425 (void) pthread_mutex_lock(&session_p->session_mutex); 426 ses_lock_held = B_TRUE; 427 REINIT_OPBUF(&session_p->digest); 428 session_p->digest.flags = 0; 429 REFRELE(session_p, ses_lock_held); 430 return (rv); 431 } 432 433 /* Check the key type */ 434 if (key_p->is_lib_obj && (key_p->class != CKO_SECRET_KEY)) { 435 rv = CKR_KEY_INDIGESTIBLE; 436 goto clean_exit; 437 } 438 439 /* 440 * Application must call C_DigestInit before calling 441 * C_DigestKey. 442 */ 443 (void) pthread_mutex_lock(&session_p->session_mutex); 444 ses_lock_held = B_TRUE; 445 446 if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { 447 /* 448 * Decrement the session reference count. 449 * We hold the session lock, and REFRELE() 450 * will release the session lock for us. 451 */ 452 OBJ_REFRELE(key_p); 453 REFRELE(session_p, ses_lock_held); 454 return (CKR_OPERATION_NOT_INITIALIZED); 455 } 456 session_p->digest.flags |= CRYPTO_OPERATION_UPDATE; 457 458 /* 459 * If the key object is from the HW provider, call CRYPTO_DIGEST_KEY 460 * ioctl. Otherwise, call CRYPTO_DIGEST_UPDATE ioctl and pass the key 461 * by value. 462 */ 463 if (key_p->is_lib_obj) { 464 digest_update.du_session = session_p->k_session; 465 } else { 466 digest_key.dk_session = session_p->k_session; 467 } 468 (void) pthread_mutex_unlock(&session_p->session_mutex); 469 ses_lock_held = B_FALSE; 470 471 if (!key_p->is_lib_obj) { 472 if (session_p->digest.flags & CRYPTO_EMULATE) { 473 rv = CKR_FUNCTION_NOT_SUPPORTED; 474 goto clean_exit; 475 } 476 digest_key.dk_key.ck_format = CRYPTO_KEY_REFERENCE; 477 digest_key.dk_key.ck_obj_id = key_p->k_handle; 478 while ((r = ioctl(kernel_fd, CRYPTO_DIGEST_KEY, 479 &digest_key)) < 0) { 480 if (errno != EINTR) 481 break; 482 } 483 if (r < 0) { 484 rv = CKR_FUNCTION_FAILED; 485 } else { 486 rv = crypto2pkcs11_error_number( 487 digest_key.dk_return_value); 488 } 489 } else { 490 ulPartLen = OBJ_SEC_VALUE_LEN(key_p); 491 if (ulPartLen == 0) { 492 rv = CKR_KEY_SIZE_RANGE; 493 goto clean_exit; 494 } 495 496 pPart = (CK_BYTE_PTR) OBJ_SEC_VALUE(key_p); 497 if (pPart == NULL) { 498 rv = CKR_KEY_HANDLE_INVALID; 499 goto clean_exit; 500 } 501 502 (void) pthread_mutex_lock(&session_p->session_mutex); 503 ses_lock_held = B_TRUE; 504 if (session_p->digest.flags & CRYPTO_EMULATE) { 505 (void) pthread_mutex_unlock(&session_p->session_mutex); 506 ses_lock_held = B_FALSE; 507 rv = emulate_update(session_p, pPart, 508 ulPartLen, OP_DIGEST); 509 goto done; 510 } 511 (void) pthread_mutex_unlock(&session_p->session_mutex); 512 ses_lock_held = B_FALSE; 513 514 digest_update.du_datalen = ulPartLen; 515 digest_update.du_databuf = (char *)pPart; 516 517 while ((r = ioctl(kernel_fd, CRYPTO_DIGEST_UPDATE, 518 &digest_update)) < 0) { 519 if (errno != EINTR) 520 break; 521 } 522 if (r < 0) { 523 rv = CKR_FUNCTION_FAILED; 524 } else { 525 rv = crypto2pkcs11_error_number( 526 digest_update.du_return_value); 527 } 528 } 529 530 done: 531 if (rv == CKR_OK) { 532 /* 533 * Decrement the session reference count. 534 * We do not hold the session lock. 535 */ 536 OBJ_REFRELE(key_p); 537 REFRELE(session_p, ses_lock_held); 538 return (CKR_OK); 539 } 540 541 clean_exit: 542 OBJ_REFRELE(key_p); 543 /* 544 * After an error occurred, terminate the current digest 545 * operation by resetting the active and update flags. 546 */ 547 (void) pthread_mutex_lock(&session_p->session_mutex); 548 ses_lock_held = B_TRUE; 549 REINIT_OPBUF(&session_p->digest); 550 session_p->digest.flags = 0; 551 552 /* 553 * Decrement the session reference count. 554 * We hold the session lock, and REFRELE() 555 * will release the session lock for us. 556 */ 557 REFRELE(session_p, ses_lock_held); 558 return (rv); 559 } 560 561 562 CK_RV 563 C_DigestFinal(CK_SESSION_HANDLE hSession, CK_BYTE_PTR pDigest, 564 CK_ULONG_PTR pulDigestLen) 565 { 566 567 CK_RV rv; 568 kernel_session_t *session_p; 569 boolean_t ses_lock_held = B_FALSE; 570 crypto_digest_final_t digest_final; 571 int r; 572 573 if (!kernel_initialized) 574 return (CKR_CRYPTOKI_NOT_INITIALIZED); 575 576 /* 577 * Obtain the session pointer. Also, increment the session 578 * reference count. 579 */ 580 rv = handle2session(hSession, &session_p); 581 if (rv != CKR_OK) 582 return (rv); 583 584 if (pulDigestLen == NULL) { 585 rv = CKR_ARGUMENTS_BAD; 586 goto clean_exit; 587 } 588 589 /* Acquire the session lock */ 590 (void) pthread_mutex_lock(&session_p->session_mutex); 591 ses_lock_held = B_TRUE; 592 593 /* 594 * Application must call C_DigestInit before calling 595 * C_DigestFinal. 596 */ 597 if (!(session_p->digest.flags & CRYPTO_OPERATION_ACTIVE)) { 598 /* 599 * Decrement the session reference count. 600 * We hold the session lock, and REFRELE() 601 * will release the session lock for us. 602 */ 603 REFRELE(session_p, ses_lock_held); 604 return (CKR_OPERATION_NOT_INITIALIZED); 605 } 606 607 /* The order of checks is important here */ 608 if (session_p->digest.flags & CRYPTO_EMULATE_USING_SW) { 609 if (session_p->digest.flags & CRYPTO_EMULATE_UPDATE_DONE) { 610 (void) pthread_mutex_unlock(&session_p->session_mutex); 611 ses_lock_held = B_FALSE; 612 rv = do_soft_digest(get_spp(&session_p->digest), 613 NULL, NULL, NULL, pDigest, pulDigestLen, OP_FINAL); 614 } else { 615 /* 616 * We end up here if an earlier C_DigestFinal() call 617 * took the C_Digest() path and it had returned 618 * CKR_BUFFER_TOO_SMALL. 619 */ 620 digest_buf_t *bufp = session_p->digest.context; 621 (void) pthread_mutex_unlock(&session_p->session_mutex); 622 ses_lock_held = B_FALSE; 623 if (bufp == NULL || bufp->buf == NULL) { 624 rv = CKR_ARGUMENTS_BAD; 625 goto clean_exit; 626 } 627 rv = do_soft_digest(get_spp(&session_p->digest), 628 NULL, bufp->buf, bufp->indata_len, 629 pDigest, pulDigestLen, OP_SINGLE); 630 } 631 goto done; 632 } else if (session_p->digest.flags & CRYPTO_EMULATE) { 633 digest_buf_t *bufp = session_p->digest.context; 634 635 /* 636 * We are emulating a single-part operation now. 637 * So, clear the flag. 638 */ 639 session_p->digest.flags &= ~CRYPTO_OPERATION_UPDATE; 640 if (bufp == NULL || bufp->buf == NULL) { 641 rv = CKR_ARGUMENTS_BAD; 642 goto clean_exit; 643 } 644 REFRELE(session_p, ses_lock_held); 645 rv = C_Digest(hSession, bufp->buf, bufp->indata_len, 646 pDigest, pulDigestLen); 647 return (rv); 648 } 649 650 digest_final.df_session = session_p->k_session; 651 (void) pthread_mutex_unlock(&session_p->session_mutex); 652 ses_lock_held = B_FALSE; 653 digest_final.df_digestlen = *pulDigestLen; 654 digest_final.df_digestbuf = (char *)pDigest; 655 656 while ((r = ioctl(kernel_fd, CRYPTO_DIGEST_FINAL, &digest_final)) < 0) { 657 if (errno != EINTR) 658 break; 659 } 660 if (r < 0) { 661 rv = CKR_FUNCTION_FAILED; 662 } else { 663 rv = crypto2pkcs11_error_number(digest_final.df_return_value); 664 } 665 666 if ((rv == CKR_OK) || (rv == CKR_BUFFER_TOO_SMALL)) 667 *pulDigestLen = digest_final.df_digestlen; 668 669 done: 670 if ((rv == CKR_BUFFER_TOO_SMALL) || 671 (rv == CKR_OK && pDigest == NULL)) { 672 /* 673 * We will not terminate the active digest operation flag, 674 * when the application-supplied buffer is too small, or 675 * the application asks for the length of buffer to hold 676 * the message digest. 677 * 678 * Decrement the session reference count. 679 * We do not hold the session lock. 680 */ 681 REFRELE(session_p, ses_lock_held); 682 return (rv); 683 } 684 685 clean_exit: 686 /* Terminates the active digest operation */ 687 (void) pthread_mutex_lock(&session_p->session_mutex); 688 ses_lock_held = B_TRUE; 689 REINIT_OPBUF(&session_p->digest); 690 session_p->digest.flags = 0; 691 692 /* 693 * Decrement the session reference count. 694 * We hold the session lock, and REFRELE() 695 * will release the session lock for us. 696 */ 697 REFRELE(session_p, ses_lock_held); 698 699 return (rv); 700 } 701