1 /* 2 * Copyright (c) 2005-2010 Pawel Jakub Dawidek <pjd@FreeBSD.org> 3 * Copyright (c) 2018 Sean Eric Fagan <sef@ixsystems.com> 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * Portions of this file are derived from sys/geom/eli/g_eli_hmac.c 28 */ 29 30 #include <sys/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/types.h> 34 #include <sys/errno.h> 35 36 #ifdef _KERNEL 37 #include <sys/libkern.h> 38 #include <sys/malloc.h> 39 #include <sys/sysctl.h> 40 #include <opencrypto/cryptodev.h> 41 #include <opencrypto/xform.h> 42 #endif 43 44 #include <sys/zio_crypt.h> 45 #include <sys/fs/zfs.h> 46 #include <sys/zio.h> 47 48 #include <sys/freebsd_crypto.h> 49 50 #define SHA512_HMAC_BLOCK_SIZE 128 51 52 static int crypt_sessions = 0; 53 SYSCTL_DECL(_vfs_zfs); 54 SYSCTL_INT(_vfs_zfs, OID_AUTO, crypt_sessions, CTLFLAG_RD, 55 &crypt_sessions, 0, "Number of cryptographic sessions created"); 56 57 void 58 crypto_mac_init(struct hmac_ctx *ctx, const crypto_key_t *c_key) 59 { 60 uint8_t k_ipad[SHA512_HMAC_BLOCK_SIZE], 61 k_opad[SHA512_HMAC_BLOCK_SIZE], 62 key[SHA512_HMAC_BLOCK_SIZE]; 63 SHA512_CTX lctx; 64 int i; 65 size_t cl_bytes = CRYPTO_BITS2BYTES(c_key->ck_length); 66 67 /* 68 * This code is based on the similar code in geom/eli/g_eli_hmac.c 69 */ 70 memset(key, 0, sizeof (key)); 71 if (c_key->ck_length == 0) 72 /* do nothing */; 73 else if (cl_bytes <= SHA512_HMAC_BLOCK_SIZE) 74 memcpy(key, c_key->ck_data, cl_bytes); 75 else { 76 /* 77 * If key is longer than 128 bytes reset it to 78 * key = SHA512(key). 79 */ 80 SHA512_Init(&lctx); 81 SHA512_Update(&lctx, c_key->ck_data, cl_bytes); 82 SHA512_Final(key, &lctx); 83 } 84 85 /* XOR key with ipad and opad values. */ 86 for (i = 0; i < sizeof (key); i++) { 87 k_ipad[i] = key[i] ^ 0x36; 88 k_opad[i] = key[i] ^ 0x5c; 89 } 90 memset(key, 0, sizeof (key)); 91 92 /* Start inner SHA512. */ 93 SHA512_Init(&ctx->innerctx); 94 SHA512_Update(&ctx->innerctx, k_ipad, sizeof (k_ipad)); 95 memset(k_ipad, 0, sizeof (k_ipad)); 96 /* Start outer SHA512. */ 97 SHA512_Init(&ctx->outerctx); 98 SHA512_Update(&ctx->outerctx, k_opad, sizeof (k_opad)); 99 memset(k_opad, 0, sizeof (k_opad)); 100 } 101 102 void 103 crypto_mac_update(struct hmac_ctx *ctx, const void *data, size_t datasize) 104 { 105 SHA512_Update(&ctx->innerctx, data, datasize); 106 } 107 108 void 109 crypto_mac_final(struct hmac_ctx *ctx, void *md, size_t mdsize) 110 { 111 uint8_t digest[SHA512_DIGEST_LENGTH]; 112 113 /* Complete inner hash */ 114 SHA512_Final(digest, &ctx->innerctx); 115 116 /* Complete outer hash */ 117 SHA512_Update(&ctx->outerctx, digest, sizeof (digest)); 118 SHA512_Final(digest, &ctx->outerctx); 119 120 memset(ctx, 0, sizeof (*ctx)); 121 /* mdsize == 0 means "Give me the whole hash!" */ 122 if (mdsize == 0) 123 mdsize = SHA512_DIGEST_LENGTH; 124 memcpy(md, digest, mdsize); 125 memset(digest, 0, sizeof (digest)); 126 } 127 128 void 129 crypto_mac(const crypto_key_t *key, const void *in_data, size_t in_data_size, 130 void *out_data, size_t out_data_size) 131 { 132 struct hmac_ctx ctx; 133 134 crypto_mac_init(&ctx, key); 135 crypto_mac_update(&ctx, in_data, in_data_size); 136 crypto_mac_final(&ctx, out_data, out_data_size); 137 } 138 139 static int 140 freebsd_zfs_crypt_done(struct cryptop *crp) 141 { 142 freebsd_crypt_session_t *ses; 143 144 ses = crp->crp_opaque; 145 mtx_lock(&ses->fs_lock); 146 ses->fs_done = true; 147 mtx_unlock(&ses->fs_lock); 148 wakeup(crp); 149 return (0); 150 } 151 152 static int 153 freebsd_zfs_crypt_done_sync(struct cryptop *crp) 154 { 155 156 return (0); 157 } 158 159 void 160 freebsd_crypt_freesession(freebsd_crypt_session_t *sess) 161 { 162 mtx_destroy(&sess->fs_lock); 163 crypto_freesession(sess->fs_sid); 164 memset(sess, 0, sizeof (*sess)); 165 } 166 167 static int 168 zfs_crypto_dispatch(freebsd_crypt_session_t *session, struct cryptop *crp) 169 { 170 int error; 171 172 crp->crp_opaque = session; 173 for (;;) { 174 #if __FreeBSD_version < 1400004 175 boolean_t async = ((crypto_ses2caps(crp->crp_session) & 176 CRYPTOCAP_F_SYNC) == 0); 177 #else 178 boolean_t async = !CRYPTO_SESS_SYNC(crp->crp_session); 179 #endif 180 crp->crp_callback = async ? freebsd_zfs_crypt_done : 181 freebsd_zfs_crypt_done_sync; 182 error = crypto_dispatch(crp); 183 if (error == 0) { 184 if (async) { 185 mtx_lock(&session->fs_lock); 186 while (session->fs_done == false) { 187 msleep(crp, &session->fs_lock, 0, 188 "zfs_crypto", 0); 189 } 190 mtx_unlock(&session->fs_lock); 191 } 192 error = crp->crp_etype; 193 } 194 195 if (error == ENOMEM) { 196 pause("zcrnomem", 1); 197 } else if (error != EAGAIN) { 198 break; 199 } 200 crp->crp_etype = 0; 201 crp->crp_flags &= ~CRYPTO_F_DONE; 202 session->fs_done = false; 203 #if __FreeBSD_version < 1300087 204 /* 205 * Session ID changed, so we should record that, 206 * and try again 207 */ 208 session->fs_sid = crp->crp_session; 209 #endif 210 } 211 return (error); 212 } 213 static void 214 freebsd_crypt_uio_debug_log(boolean_t encrypt, 215 freebsd_crypt_session_t *input_sessionp, 216 const struct zio_crypt_info *c_info, 217 zfs_uio_t *data_uio, 218 crypto_key_t *key, 219 uint8_t *ivbuf, 220 size_t datalen, 221 size_t auth_len) 222 { 223 #ifdef FCRYPTO_DEBUG 224 struct cryptodesc *crd; 225 uint8_t *p = NULL; 226 size_t total = 0; 227 228 printf("%s(%s, %p, { %s, %d, %d, %s }, %p, { %p, %u }, " 229 "%p, %u, %u)\n", 230 __FUNCTION__, encrypt ? "encrypt" : "decrypt", input_sessionp, 231 c_info->ci_algname, c_info->ci_crypt_type, 232 (unsigned int)c_info->ci_keylen, c_info->ci_name, 233 data_uio, key->ck_data, 234 (unsigned int)key->ck_length, 235 ivbuf, (unsigned int)datalen, (unsigned int)auth_len); 236 printf("\tkey = { "); 237 for (int i = 0; i < key->ck_length / 8; i++) { 238 uint8_t *b = (uint8_t *)key->ck_data; 239 printf("%02x ", b[i]); 240 } 241 printf("}\n"); 242 for (int i = 0; i < zfs_uio_iovcnt(data_uio); i++) { 243 printf("\tiovec #%d: <%p, %u>\n", i, 244 zfs_uio_iovbase(data_uio, i), 245 (unsigned int)zfs_uio_iovlen(data_uio, i)); 246 total += zfs_uio_iovlen(data_uio, i); 247 } 248 zfs_uio_resid(data_uio) = total; 249 #endif 250 } 251 /* 252 * Create a new cryptographic session. This should 253 * happen every time the key changes (including when 254 * it's first loaded). 255 */ 256 #if __FreeBSD_version >= 1300087 257 int 258 freebsd_crypt_newsession(freebsd_crypt_session_t *sessp, 259 const struct zio_crypt_info *c_info, crypto_key_t *key) 260 { 261 struct crypto_session_params csp = {0}; 262 int error = 0; 263 264 #ifdef FCRYPTO_DEBUG 265 printf("%s(%p, { %s, %d, %d, %s }, { %p, %u })\n", 266 __FUNCTION__, sessp, 267 c_info->ci_algname, c_info->ci_crypt_type, 268 (unsigned int)c_info->ci_keylen, c_info->ci_name, 269 key->ck_data, (unsigned int)key->ck_length); 270 printf("\tkey = { "); 271 for (int i = 0; i < key->ck_length / 8; i++) { 272 uint8_t *b = (uint8_t *)key->ck_data; 273 printf("%02x ", b[i]); 274 } 275 printf("}\n"); 276 #endif 277 csp.csp_mode = CSP_MODE_AEAD; 278 csp.csp_cipher_key = key->ck_data; 279 csp.csp_cipher_klen = key->ck_length / 8; 280 switch (c_info->ci_crypt_type) { 281 case ZC_TYPE_GCM: 282 csp.csp_cipher_alg = CRYPTO_AES_NIST_GCM_16; 283 csp.csp_ivlen = AES_GCM_IV_LEN; 284 switch (key->ck_length/8) { 285 case AES_128_GMAC_KEY_LEN: 286 case AES_192_GMAC_KEY_LEN: 287 case AES_256_GMAC_KEY_LEN: 288 break; 289 default: 290 error = EINVAL; 291 goto bad; 292 } 293 break; 294 case ZC_TYPE_CCM: 295 csp.csp_cipher_alg = CRYPTO_AES_CCM_16; 296 csp.csp_ivlen = AES_CCM_IV_LEN; 297 switch (key->ck_length/8) { 298 case AES_128_CBC_MAC_KEY_LEN: 299 case AES_192_CBC_MAC_KEY_LEN: 300 case AES_256_CBC_MAC_KEY_LEN: 301 break; 302 default: 303 error = EINVAL; 304 goto bad; 305 break; 306 } 307 break; 308 default: 309 error = ENOTSUP; 310 goto bad; 311 } 312 313 /* 314 * Disable the use of hardware drivers on FreeBSD 13 and later since 315 * common crypto offload drivers impose constraints on AES-GCM AAD 316 * lengths that make them unusable for ZFS, and we currently do not have 317 * a mechanism to fall back to a software driver for requests not 318 * handled by a hardware driver. 319 * 320 * On 12 we continue to permit the use of hardware drivers since 321 * CPU-accelerated drivers such as aesni(4) register themselves as 322 * hardware drivers. 323 */ 324 error = crypto_newsession(&sessp->fs_sid, &csp, CRYPTOCAP_F_SOFTWARE); 325 mtx_init(&sessp->fs_lock, "FreeBSD Cryptographic Session Lock", 326 NULL, MTX_DEF); 327 crypt_sessions++; 328 bad: 329 #ifdef FCRYPTO_DEBUG 330 if (error) 331 printf("%s: returning error %d\n", __FUNCTION__, error); 332 #endif 333 return (error); 334 } 335 336 int 337 freebsd_crypt_uio(boolean_t encrypt, 338 freebsd_crypt_session_t *input_sessionp, 339 const struct zio_crypt_info *c_info, 340 zfs_uio_t *data_uio, 341 crypto_key_t *key, 342 uint8_t *ivbuf, 343 size_t datalen, 344 size_t auth_len) 345 { 346 struct cryptop *crp; 347 freebsd_crypt_session_t *session = NULL; 348 int error = 0; 349 size_t total = 0; 350 351 freebsd_crypt_uio_debug_log(encrypt, input_sessionp, c_info, data_uio, 352 key, ivbuf, datalen, auth_len); 353 for (int i = 0; i < zfs_uio_iovcnt(data_uio); i++) 354 total += zfs_uio_iovlen(data_uio, i); 355 zfs_uio_resid(data_uio) = total; 356 if (input_sessionp == NULL) { 357 session = kmem_zalloc(sizeof (*session), KM_SLEEP); 358 error = freebsd_crypt_newsession(session, c_info, key); 359 if (error) 360 goto out; 361 } else 362 session = input_sessionp; 363 364 crp = crypto_getreq(session->fs_sid, M_WAITOK); 365 if (encrypt) { 366 crp->crp_op = CRYPTO_OP_ENCRYPT | 367 CRYPTO_OP_COMPUTE_DIGEST; 368 } else { 369 crp->crp_op = CRYPTO_OP_DECRYPT | 370 CRYPTO_OP_VERIFY_DIGEST; 371 } 372 crp->crp_flags = CRYPTO_F_CBIFSYNC | CRYPTO_F_IV_SEPARATE; 373 crypto_use_uio(crp, GET_UIO_STRUCT(data_uio)); 374 375 crp->crp_aad_start = 0; 376 crp->crp_aad_length = auth_len; 377 crp->crp_payload_start = auth_len; 378 crp->crp_payload_length = datalen; 379 crp->crp_digest_start = auth_len + datalen; 380 381 memcpy(crp->crp_iv, ivbuf, ZIO_DATA_IV_LEN); 382 error = zfs_crypto_dispatch(session, crp); 383 crypto_freereq(crp); 384 out: 385 #ifdef FCRYPTO_DEBUG 386 if (error) 387 printf("%s: returning error %d\n", __FUNCTION__, error); 388 #endif 389 if (input_sessionp == NULL) { 390 freebsd_crypt_freesession(session); 391 kmem_free(session, sizeof (*session)); 392 } 393 return (error); 394 } 395 396 #else 397 int 398 freebsd_crypt_newsession(freebsd_crypt_session_t *sessp, 399 const struct zio_crypt_info *c_info, crypto_key_t *key) 400 { 401 struct cryptoini cria = {0}, crie = {0}, *crip; 402 struct enc_xform *xform; 403 struct auth_hash *xauth; 404 int error = 0; 405 crypto_session_t sid; 406 407 #ifdef FCRYPTO_DEBUG 408 printf("%s(%p, { %s, %d, %d, %s }, { %p, %u })\n", 409 __FUNCTION__, sessp, 410 c_info->ci_algname, c_info->ci_crypt_type, 411 (unsigned int)c_info->ci_keylen, c_info->ci_name, 412 key->ck_data, (unsigned int)key->ck_length); 413 printf("\tkey = { "); 414 for (int i = 0; i < key->ck_length / 8; i++) { 415 uint8_t *b = (uint8_t *)key->ck_data; 416 printf("%02x ", b[i]); 417 } 418 printf("}\n"); 419 #endif 420 switch (c_info->ci_crypt_type) { 421 case ZC_TYPE_GCM: 422 xform = &enc_xform_aes_nist_gcm; 423 switch (key->ck_length/8) { 424 case AES_128_GMAC_KEY_LEN: 425 xauth = &auth_hash_nist_gmac_aes_128; 426 break; 427 case AES_192_GMAC_KEY_LEN: 428 xauth = &auth_hash_nist_gmac_aes_192; 429 break; 430 case AES_256_GMAC_KEY_LEN: 431 xauth = &auth_hash_nist_gmac_aes_256; 432 break; 433 default: 434 error = EINVAL; 435 goto bad; 436 } 437 break; 438 case ZC_TYPE_CCM: 439 xform = &enc_xform_ccm; 440 switch (key->ck_length/8) { 441 case AES_128_CBC_MAC_KEY_LEN: 442 xauth = &auth_hash_ccm_cbc_mac_128; 443 break; 444 case AES_192_CBC_MAC_KEY_LEN: 445 xauth = &auth_hash_ccm_cbc_mac_192; 446 break; 447 case AES_256_CBC_MAC_KEY_LEN: 448 xauth = &auth_hash_ccm_cbc_mac_256; 449 break; 450 default: 451 error = EINVAL; 452 goto bad; 453 break; 454 } 455 break; 456 default: 457 error = ENOTSUP; 458 goto bad; 459 } 460 #ifdef FCRYPTO_DEBUG 461 printf("%s(%d): Using crypt %s (key length %u [%u bytes]), " 462 "auth %s (key length %d)\n", 463 __FUNCTION__, __LINE__, 464 xform->name, (unsigned int)key->ck_length, 465 (unsigned int)key->ck_length/8, 466 xauth->name, xauth->keysize); 467 #endif 468 469 crie.cri_alg = xform->type; 470 crie.cri_key = key->ck_data; 471 crie.cri_klen = key->ck_length; 472 473 cria.cri_alg = xauth->type; 474 cria.cri_key = key->ck_data; 475 cria.cri_klen = key->ck_length; 476 477 cria.cri_next = &crie; 478 crie.cri_next = NULL; 479 crip = &cria; 480 // Everything else is zero-initialised 481 482 error = crypto_newsession(&sid, crip, 483 CRYPTOCAP_F_HARDWARE | CRYPTOCAP_F_SOFTWARE); 484 if (error != 0) { 485 printf("%s(%d): crypto_newsession failed with %d\n", 486 __FUNCTION__, __LINE__, error); 487 goto bad; 488 } 489 sessp->fs_sid = sid; 490 mtx_init(&sessp->fs_lock, "FreeBSD Cryptographic Session Lock", 491 NULL, MTX_DEF); 492 crypt_sessions++; 493 bad: 494 return (error); 495 } 496 497 /* 498 * The meat of encryption/decryption. 499 * If sessp is NULL, then it will create a 500 * temporary cryptographic session, and release 501 * it when done. 502 */ 503 int 504 freebsd_crypt_uio(boolean_t encrypt, 505 freebsd_crypt_session_t *input_sessionp, 506 const struct zio_crypt_info *c_info, 507 zfs_uio_t *data_uio, 508 crypto_key_t *key, 509 uint8_t *ivbuf, 510 size_t datalen, 511 size_t auth_len) 512 { 513 struct cryptop *crp; 514 struct cryptodesc *enc_desc, *auth_desc; 515 struct enc_xform *xform; 516 struct auth_hash *xauth; 517 freebsd_crypt_session_t *session = NULL; 518 int error; 519 520 freebsd_crypt_uio_debug_log(encrypt, input_sessionp, c_info, data_uio, 521 key, ivbuf, datalen, auth_len); 522 switch (c_info->ci_crypt_type) { 523 case ZC_TYPE_GCM: 524 xform = &enc_xform_aes_nist_gcm; 525 switch (key->ck_length/8) { 526 case AES_128_GMAC_KEY_LEN: 527 xauth = &auth_hash_nist_gmac_aes_128; 528 break; 529 case AES_192_GMAC_KEY_LEN: 530 xauth = &auth_hash_nist_gmac_aes_192; 531 break; 532 case AES_256_GMAC_KEY_LEN: 533 xauth = &auth_hash_nist_gmac_aes_256; 534 break; 535 default: 536 error = EINVAL; 537 goto bad; 538 } 539 break; 540 case ZC_TYPE_CCM: 541 xform = &enc_xform_ccm; 542 switch (key->ck_length/8) { 543 case AES_128_CBC_MAC_KEY_LEN: 544 xauth = &auth_hash_ccm_cbc_mac_128; 545 break; 546 case AES_192_CBC_MAC_KEY_LEN: 547 xauth = &auth_hash_ccm_cbc_mac_192; 548 break; 549 case AES_256_CBC_MAC_KEY_LEN: 550 xauth = &auth_hash_ccm_cbc_mac_256; 551 break; 552 default: 553 error = EINVAL; 554 goto bad; 555 break; 556 } 557 break; 558 default: 559 error = ENOTSUP; 560 goto bad; 561 } 562 563 #ifdef FCRYPTO_DEBUG 564 printf("%s(%d): Using crypt %s (key length %u [%u bytes]), " 565 "auth %s (key length %d)\n", 566 __FUNCTION__, __LINE__, 567 xform->name, (unsigned int)key->ck_length, 568 (unsigned int)key->ck_length/8, 569 xauth->name, xauth->keysize); 570 #endif 571 572 if (input_sessionp == NULL) { 573 session = kmem_zalloc(sizeof (*session), KM_SLEEP); 574 error = freebsd_crypt_newsession(session, c_info, key); 575 if (error) 576 goto out; 577 } else 578 session = input_sessionp; 579 580 crp = crypto_getreq(2); 581 if (crp == NULL) { 582 error = ENOMEM; 583 goto bad; 584 } 585 586 auth_desc = crp->crp_desc; 587 enc_desc = auth_desc->crd_next; 588 589 crp->crp_session = session->fs_sid; 590 crp->crp_ilen = auth_len + datalen; 591 crp->crp_buf = (void*)GET_UIO_STRUCT(data_uio); 592 crp->crp_flags = CRYPTO_F_IOV | CRYPTO_F_CBIFSYNC; 593 594 auth_desc->crd_skip = 0; 595 auth_desc->crd_len = auth_len; 596 auth_desc->crd_inject = auth_len + datalen; 597 auth_desc->crd_alg = xauth->type; 598 #ifdef FCRYPTO_DEBUG 599 printf("%s: auth: skip = %u, len = %u, inject = %u\n", 600 __FUNCTION__, auth_desc->crd_skip, auth_desc->crd_len, 601 auth_desc->crd_inject); 602 #endif 603 604 enc_desc->crd_skip = auth_len; 605 enc_desc->crd_len = datalen; 606 enc_desc->crd_inject = auth_len; 607 enc_desc->crd_alg = xform->type; 608 enc_desc->crd_flags = CRD_F_IV_EXPLICIT | CRD_F_IV_PRESENT; 609 memcpy(enc_desc->crd_iv, ivbuf, ZIO_DATA_IV_LEN); 610 enc_desc->crd_next = NULL; 611 612 #ifdef FCRYPTO_DEBUG 613 printf("%s: enc: skip = %u, len = %u, inject = %u\n", 614 __FUNCTION__, enc_desc->crd_skip, enc_desc->crd_len, 615 enc_desc->crd_inject); 616 #endif 617 618 if (encrypt) 619 enc_desc->crd_flags |= CRD_F_ENCRYPT; 620 621 error = zfs_crypto_dispatch(session, crp); 622 crypto_freereq(crp); 623 out: 624 if (input_sessionp == NULL) { 625 freebsd_crypt_freesession(session); 626 kmem_free(session, sizeof (*session)); 627 } 628 bad: 629 #ifdef FCRYPTO_DEBUG 630 if (error) 631 printf("%s: returning error %d\n", __FUNCTION__, error); 632 #endif 633 return (error); 634 } 635 #endif 636