1 /* $NetBSD: evp-wincng.c,v 1.4 2023/06/19 21:41:43 christos Exp $ */ 2 3 /* 4 * Copyright (c) 2015, Secure Endpoints Inc. 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 11 * - Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 14 * - Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in 16 * the documentation and/or other materials provided with the 17 * distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 22 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 23 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 24 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 28 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 30 * OF THE POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 /* Windows CNG provider */ 34 35 #include <config.h> 36 #include <krb5/roken.h> 37 #include <assert.h> 38 39 #include <evp.h> 40 #include <evp-wincng.h> 41 42 #include <bcrypt.h> 43 44 /* 45 * CNG cipher provider 46 */ 47 48 struct wincng_key { 49 BCRYPT_KEY_HANDLE hKey; 50 UCHAR rgbKeyObject[1]; 51 }; 52 53 #define WINCNG_KEY_OBJECT_SIZE(ctx) \ 54 ((ctx)->cipher->ctx_size - sizeof(struct wincng_key) + 1) 55 56 static int 57 wincng_do_cipher(EVP_CIPHER_CTX *ctx, 58 unsigned char *out, 59 const unsigned char *in, 60 unsigned int size) 61 { 62 struct wincng_key *cng = ctx->cipher_data; 63 NTSTATUS status; 64 ULONG cbResult; 65 66 assert(EVP_CIPHER_CTX_mode(ctx) == EVP_CIPH_STREAM_CIPHER || 67 (size % ctx->cipher->block_size) == 0); 68 69 if (ctx->encrypt) { 70 status = BCryptEncrypt(cng->hKey, 71 (PUCHAR)in, 72 size, 73 NULL, /* pPaddingInfo */ 74 ctx->cipher->iv_len ? ctx->iv : NULL, 75 ctx->cipher->iv_len, 76 out, 77 size, 78 &cbResult, 79 0); 80 } else { 81 status = BCryptDecrypt(cng->hKey, 82 (PUCHAR)in, 83 size, 84 NULL, /* pPaddingInfo */ 85 ctx->cipher->iv_len ? ctx->iv : NULL, 86 ctx->cipher->iv_len, 87 out, 88 size, 89 &cbResult, 90 0); 91 } 92 93 return BCRYPT_SUCCESS(status) && cbResult == size; 94 } 95 96 static int 97 wincng_cleanup(EVP_CIPHER_CTX *ctx) 98 { 99 struct wincng_key *cng = ctx->cipher_data; 100 101 if (cng->hKey) 102 BCryptDestroyKey(cng->hKey); 103 SecureZeroMemory(cng->rgbKeyObject, WINCNG_KEY_OBJECT_SIZE(ctx)); 104 105 return 1; 106 } 107 108 static int 109 wincng_cipher_algorithm_init(EVP_CIPHER *cipher, 110 LPWSTR pszAlgId) 111 { 112 BCRYPT_ALG_HANDLE hAlgorithm = NULL; 113 NTSTATUS status; 114 LPCWSTR pszChainingMode; 115 ULONG cbKeyObject, cbChainingMode, cbData; 116 117 if (cipher->app_data) 118 return 1; 119 120 status = BCryptOpenAlgorithmProvider(&hAlgorithm, 121 pszAlgId, 122 NULL, 123 0); 124 if (!BCRYPT_SUCCESS(status)) 125 return 0; 126 127 status = BCryptGetProperty(hAlgorithm, 128 BCRYPT_OBJECT_LENGTH, 129 (PUCHAR)&cbKeyObject, 130 sizeof(ULONG), 131 &cbData, 132 0); 133 if (!BCRYPT_SUCCESS(status)) { 134 BCryptCloseAlgorithmProvider(hAlgorithm, 0); 135 return 0; 136 } 137 138 cipher->ctx_size = sizeof(struct wincng_key) + cbKeyObject - 1; 139 140 switch (cipher->flags & EVP_CIPH_MODE) { 141 case EVP_CIPH_CBC_MODE: 142 pszChainingMode = BCRYPT_CHAIN_MODE_CBC; 143 cbChainingMode = sizeof(BCRYPT_CHAIN_MODE_CBC); 144 break; 145 case EVP_CIPH_CFB8_MODE: 146 pszChainingMode = BCRYPT_CHAIN_MODE_CFB; 147 cbChainingMode = sizeof(BCRYPT_CHAIN_MODE_CFB); 148 break; 149 default: 150 pszChainingMode = NULL; 151 cbChainingMode = 0; 152 break; 153 } 154 155 if (cbChainingMode) { 156 status = BCryptSetProperty(hAlgorithm, 157 BCRYPT_CHAINING_MODE, 158 (PUCHAR)pszChainingMode, 159 cbChainingMode, 160 0); 161 if (!BCRYPT_SUCCESS(status)) { 162 BCryptCloseAlgorithmProvider(hAlgorithm, 0); 163 return 0; 164 } 165 } 166 167 if (wcscmp(pszAlgId, BCRYPT_RC2_ALGORITHM) == 0) { 168 ULONG cbEffectiveKeyLength = EVP_CIPHER_key_length(cipher) * 8; 169 170 status = BCryptSetProperty(hAlgorithm, 171 BCRYPT_EFFECTIVE_KEY_LENGTH, 172 (PUCHAR)&cbEffectiveKeyLength, 173 sizeof(cbEffectiveKeyLength), 174 0); 175 if (!BCRYPT_SUCCESS(status)) { 176 BCryptCloseAlgorithmProvider(hAlgorithm, 0); 177 return 0; 178 } 179 } 180 181 InterlockedCompareExchangePointerRelease(&cipher->app_data, 182 hAlgorithm, NULL); 183 return 1; 184 } 185 186 static int 187 wincng_key_init(EVP_CIPHER_CTX *ctx, 188 const unsigned char *key, 189 const unsigned char *iv, 190 int encp) 191 { 192 struct wincng_key *cng = ctx->cipher_data; 193 NTSTATUS status; 194 195 assert(cng != NULL); 196 assert(ctx->cipher != NULL); 197 198 if (ctx->cipher->app_data == NULL) 199 return 0; 200 201 if (cng->hKey) { 202 BCryptDestroyKey(cng->hKey); /* allow reinitialization */ 203 cng->hKey = (BCRYPT_KEY_HANDLE)0; 204 } 205 206 /* 207 * Note: ctx->key_len not EVP_CIPHER_CTX_key_length() for 208 * variable length key support. 209 */ 210 status = BCryptGenerateSymmetricKey(ctx->cipher->app_data, 211 &cng->hKey, 212 cng->rgbKeyObject, 213 WINCNG_KEY_OBJECT_SIZE(ctx), 214 (PUCHAR)key, 215 ctx->key_len, 216 0); 217 218 return BCRYPT_SUCCESS(status); 219 } 220 221 #define WINCNG_CIPHER_ALGORITHM(name, alg_id, block_size, key_len, \ 222 iv_len, flags) \ 223 \ 224 static EVP_CIPHER \ 225 wincng_##name = { \ 226 0, \ 227 block_size, \ 228 key_len, \ 229 iv_len, \ 230 flags, \ 231 wincng_key_init, \ 232 wincng_do_cipher, \ 233 wincng_cleanup, \ 234 0, \ 235 NULL, \ 236 NULL, \ 237 NULL, \ 238 NULL \ 239 }; \ 240 \ 241 const EVP_CIPHER * \ 242 hc_EVP_wincng_##name(void) \ 243 { \ 244 wincng_cipher_algorithm_init(&wincng_##name, alg_id); \ 245 return wincng_##name.app_data ? &wincng_##name : NULL; \ 246 } 247 248 #define WINCNG_CIPHER_ALGORITHM_CLEANUP(name) do { \ 249 if (wincng_##name.app_data) { \ 250 BCryptCloseAlgorithmProvider(wincng_##name.app_data, 0); \ 251 wincng_##name.app_data = NULL; \ 252 } \ 253 } while (0) 254 255 #define WINCNG_CIPHER_ALGORITHM_UNAVAILABLE(name) \ 256 \ 257 const EVP_CIPHER * \ 258 hc_EVP_wincng_##name(void) \ 259 { \ 260 return NULL; \ 261 } 262 263 /** 264 * The triple DES cipher type (Windows CNG provider) 265 * 266 * @return the DES-EDE3-CBC EVP_CIPHER pointer. 267 * 268 * @ingroup hcrypto_evp 269 */ 270 271 WINCNG_CIPHER_ALGORITHM(des_ede3_cbc, 272 BCRYPT_3DES_ALGORITHM, 273 8, 274 24, 275 8, 276 EVP_CIPH_CBC_MODE); 277 278 /** 279 * The DES cipher type (Windows CNG provider) 280 * 281 * @return the DES-CBC EVP_CIPHER pointer. 282 * 283 * @ingroup hcrypto_evp 284 */ 285 286 WINCNG_CIPHER_ALGORITHM(des_cbc, 287 BCRYPT_DES_ALGORITHM, 288 8, 289 8, 290 8, 291 EVP_CIPH_CBC_MODE); 292 293 /** 294 * The AES-128 cipher type (Windows CNG provider) 295 * 296 * @return the AES-128-CBC EVP_CIPHER pointer. 297 * 298 * @ingroup hcrypto_evp 299 */ 300 301 WINCNG_CIPHER_ALGORITHM(aes_128_cbc, 302 BCRYPT_AES_ALGORITHM, 303 16, 304 16, 305 16, 306 EVP_CIPH_CBC_MODE); 307 308 /** 309 * The AES-192 cipher type (Windows CNG provider) 310 * 311 * @return the AES-192-CBC EVP_CIPHER pointer. 312 * 313 * @ingroup hcrypto_evp 314 */ 315 316 WINCNG_CIPHER_ALGORITHM(aes_192_cbc, 317 BCRYPT_AES_ALGORITHM, 318 16, 319 24, 320 16, 321 EVP_CIPH_CBC_MODE); 322 323 /** 324 * The AES-256 cipher type (Windows CNG provider) 325 * 326 * @return the AES-256-CBC EVP_CIPHER pointer. 327 * 328 * @ingroup hcrypto_evp 329 */ 330 331 WINCNG_CIPHER_ALGORITHM(aes_256_cbc, 332 BCRYPT_AES_ALGORITHM, 333 16, 334 32, 335 16, 336 EVP_CIPH_CBC_MODE); 337 338 /** 339 * The AES-128 CFB8 cipher type (Windows CNG provider) 340 * 341 * @return the AES-128-CFB8 EVP_CIPHER pointer. 342 * 343 * @ingroup hcrypto_evp 344 */ 345 346 WINCNG_CIPHER_ALGORITHM(aes_128_cfb8, 347 BCRYPT_AES_ALGORITHM, 348 16, 349 16, 350 16, 351 EVP_CIPH_CFB8_MODE); 352 353 /** 354 * The AES-192 CFB8 cipher type (Windows CNG provider) 355 * 356 * @return the AES-192-CFB8 EVP_CIPHER pointer. 357 * 358 * @ingroup hcrypto_evp 359 */ 360 361 WINCNG_CIPHER_ALGORITHM(aes_192_cfb8, 362 BCRYPT_AES_ALGORITHM, 363 16, 364 24, 365 16, 366 EVP_CIPH_CFB8_MODE); 367 368 /** 369 * The AES-256 CFB8 cipher type (Windows CNG provider) 370 * 371 * @return the AES-256-CFB8 EVP_CIPHER pointer. 372 * 373 * @ingroup hcrypto_evp 374 */ 375 376 WINCNG_CIPHER_ALGORITHM(aes_256_cfb8, 377 BCRYPT_AES_ALGORITHM, 378 16, 379 32, 380 16, 381 EVP_CIPH_CFB8_MODE); 382 383 /** 384 * The RC2 cipher type - Windows CNG 385 * 386 * @return the RC2 EVP_CIPHER pointer. 387 * 388 * @ingroup hcrypto_evp 389 */ 390 391 WINCNG_CIPHER_ALGORITHM(rc2_cbc, 392 BCRYPT_RC2_ALGORITHM, 393 8, 394 16, 395 8, 396 EVP_CIPH_CBC_MODE); 397 398 /** 399 * The RC2-40 cipher type - Windows CNG 400 * 401 * @return the RC2-40 EVP_CIPHER pointer. 402 * 403 * @ingroup hcrypto_evp 404 */ 405 406 WINCNG_CIPHER_ALGORITHM(rc2_40_cbc, 407 BCRYPT_RC2_ALGORITHM, 408 8, 409 5, 410 8, 411 EVP_CIPH_CBC_MODE); 412 413 /** 414 * The RC2-64 cipher type - Windows CNG 415 * 416 * @return the RC2-64 EVP_CIPHER pointer. 417 * 418 * @ingroup hcrypto_evp 419 */ 420 421 WINCNG_CIPHER_ALGORITHM(rc2_64_cbc, 422 BCRYPT_RC2_ALGORITHM, 423 8, 424 8, 425 8, 426 EVP_CIPH_CBC_MODE); 427 428 /** 429 * The Camellia-128 cipher type - CommonCrypto 430 * 431 * @return the Camellia-128 EVP_CIPHER pointer. 432 * 433 * @ingroup hcrypto_evp 434 */ 435 436 WINCNG_CIPHER_ALGORITHM_UNAVAILABLE(camellia_128_cbc); 437 438 /** 439 * The Camellia-198 cipher type - CommonCrypto 440 * 441 * @return the Camellia-198 EVP_CIPHER pointer. 442 * 443 * @ingroup hcrypto_evp 444 */ 445 446 WINCNG_CIPHER_ALGORITHM_UNAVAILABLE(camellia_192_cbc); 447 448 /** 449 * The Camellia-256 cipher type - CommonCrypto 450 * 451 * @return the Camellia-256 EVP_CIPHER pointer. 452 * 453 * @ingroup hcrypto_evp 454 */ 455 456 WINCNG_CIPHER_ALGORITHM_UNAVAILABLE(camellia_256_cbc); 457 458 /** 459 * The RC4 cipher type (Windows CNG provider) 460 * 461 * @return the RC4 EVP_CIPHER pointer. 462 * 463 * @ingroup hcrypto_evp 464 */ 465 466 WINCNG_CIPHER_ALGORITHM(rc4, 467 BCRYPT_RC4_ALGORITHM, 468 1, 469 16, 470 0, 471 EVP_CIPH_STREAM_CIPHER | EVP_CIPH_VARIABLE_LENGTH); 472 473 /** 474 * The RC4-40 cipher type (Windows CNG provider) 475 * 476 * @return the RC4 EVP_CIPHER pointer. 477 * 478 * @ingroup hcrypto_evp 479 */ 480 481 WINCNG_CIPHER_ALGORITHM(rc4_40, 482 BCRYPT_RC4_ALGORITHM, 483 1, 484 5, 485 0, 486 EVP_CIPH_STREAM_CIPHER | EVP_CIPH_VARIABLE_LENGTH); 487 488 static void 489 wincng_cipher_algorithm_cleanup(void) 490 { 491 WINCNG_CIPHER_ALGORITHM_CLEANUP(des_ede3_cbc); 492 WINCNG_CIPHER_ALGORITHM_CLEANUP(des_cbc); 493 WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_128_cbc); 494 WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_192_cbc); 495 WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_256_cbc); 496 WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_128_cfb8); 497 WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_192_cfb8); 498 WINCNG_CIPHER_ALGORITHM_CLEANUP(aes_256_cfb8); 499 WINCNG_CIPHER_ALGORITHM_CLEANUP(rc2_cbc); 500 WINCNG_CIPHER_ALGORITHM_CLEANUP(rc2_40_cbc); 501 WINCNG_CIPHER_ALGORITHM_CLEANUP(rc2_64_cbc); 502 WINCNG_CIPHER_ALGORITHM_CLEANUP(rc4); 503 WINCNG_CIPHER_ALGORITHM_CLEANUP(rc4_40); 504 } 505 506 /* 507 * CNG digest provider 508 */ 509 510 struct wincng_md_ctx { 511 BCRYPT_HASH_HANDLE hHash; 512 ULONG cbHashObject; 513 UCHAR rgbHashObject[1]; 514 }; 515 516 static BCRYPT_ALG_HANDLE 517 wincng_md_algorithm_init(EVP_MD *md, 518 LPCWSTR pszAlgId) 519 { 520 BCRYPT_ALG_HANDLE hAlgorithm; 521 NTSTATUS status; 522 ULONG cbHashObject, cbData; 523 ULONG cbHash = 0, cbBlock = 0; 524 525 status = BCryptOpenAlgorithmProvider(&hAlgorithm, 526 pszAlgId, 527 NULL, 528 0); 529 if (!BCRYPT_SUCCESS(status)) 530 return NULL; 531 532 status = BCryptGetProperty(hAlgorithm, 533 BCRYPT_HASH_LENGTH, 534 (PUCHAR)&cbHash, 535 sizeof(ULONG), 536 &cbData, 537 0); 538 if (!BCRYPT_SUCCESS(status)) { 539 BCryptCloseAlgorithmProvider(hAlgorithm, 0); 540 return NULL; 541 } 542 543 status = BCryptGetProperty(hAlgorithm, 544 BCRYPT_HASH_BLOCK_LENGTH, 545 (PUCHAR)&cbBlock, 546 sizeof(ULONG), 547 &cbData, 548 0); 549 if (!BCRYPT_SUCCESS(status)) { 550 BCryptCloseAlgorithmProvider(hAlgorithm, 0); 551 return NULL; 552 } 553 554 status = BCryptGetProperty(hAlgorithm, 555 BCRYPT_OBJECT_LENGTH, 556 (PUCHAR)&cbHashObject, 557 sizeof(ULONG), 558 &cbData, 559 0); 560 if (!BCRYPT_SUCCESS(status)) { 561 BCryptCloseAlgorithmProvider(hAlgorithm, 0); 562 return NULL; 563 } 564 565 md->hash_size = cbHash; 566 md->block_size = cbBlock; 567 md->ctx_size = sizeof(struct wincng_md_ctx) + cbHashObject - 1; 568 569 return hAlgorithm; 570 } 571 572 static int 573 wincng_md_hash_init(BCRYPT_ALG_HANDLE hAlgorithm, 574 EVP_MD_CTX *ctx) 575 { 576 struct wincng_md_ctx *cng = (struct wincng_md_ctx *)ctx; 577 NTSTATUS status; 578 ULONG cbData; 579 580 if (cng->hHash) { 581 BCryptDestroyHash(cng->hHash); /* allow reinitialization */ 582 cng->hHash = (BCRYPT_HASH_HANDLE)0; 583 } 584 585 status = BCryptGetProperty(hAlgorithm, 586 BCRYPT_OBJECT_LENGTH, 587 (PUCHAR)&cng->cbHashObject, 588 sizeof(ULONG), 589 &cbData, 590 0); 591 if (!BCRYPT_SUCCESS(status)) 592 return 0; 593 594 status = BCryptCreateHash(hAlgorithm, 595 &cng->hHash, 596 cng->rgbHashObject, 597 cng->cbHashObject, 598 NULL, 599 0, 600 0); 601 602 return BCRYPT_SUCCESS(status); 603 } 604 605 static int 606 wincng_md_update(EVP_MD_CTX *ctx, 607 const void *data, 608 size_t length) 609 { 610 struct wincng_md_ctx *cng = (struct wincng_md_ctx *)ctx; 611 NTSTATUS status; 612 613 status = BCryptHashData(cng->hHash, (PUCHAR)data, length, 0); 614 615 return BCRYPT_SUCCESS(status); 616 } 617 618 static int 619 wincng_md_final(void *digest, 620 EVP_MD_CTX *ctx) 621 { 622 struct wincng_md_ctx *cng = (struct wincng_md_ctx *)ctx; 623 NTSTATUS status; 624 ULONG cbHash, cbData; 625 626 status = BCryptGetProperty(cng->hHash, 627 BCRYPT_HASH_LENGTH, 628 (PUCHAR)&cbHash, 629 sizeof(DWORD), 630 &cbData, 631 0); 632 if (!BCRYPT_SUCCESS(status)) 633 return 0; 634 635 status = BCryptFinishHash(cng->hHash, 636 digest, 637 cbHash, 638 0); 639 640 return BCRYPT_SUCCESS(status); 641 } 642 643 static int 644 wincng_md_cleanup(EVP_MD_CTX *ctx) 645 { 646 struct wincng_md_ctx *cng = (struct wincng_md_ctx *)ctx; 647 648 if (cng->hHash) 649 BCryptDestroyHash(cng->hHash); 650 SecureZeroMemory(cng->rgbHashObject, cng->cbHashObject); 651 652 return 1; 653 } 654 655 #define WINCNG_MD_ALGORITHM(name, alg_id) \ 656 \ 657 static BCRYPT_ALG_HANDLE wincng_hAlgorithm_##name; \ 658 \ 659 static int wincng_##name##_init(EVP_MD_CTX *ctx) \ 660 { \ 661 return wincng_md_hash_init(wincng_hAlgorithm_##name, ctx); \ 662 } \ 663 \ 664 const EVP_MD * \ 665 hc_EVP_wincng_##name(void) \ 666 { \ 667 static struct hc_evp_md name = { \ 668 0, \ 669 0, \ 670 0, \ 671 wincng_##name##_init, \ 672 wincng_md_update, \ 673 wincng_md_final, \ 674 wincng_md_cleanup \ 675 }; \ 676 \ 677 if (wincng_hAlgorithm_##name == NULL) { \ 678 BCRYPT_ALG_HANDLE hAlgorithm = \ 679 wincng_md_algorithm_init(&name, alg_id); \ 680 InterlockedCompareExchangePointerRelease( \ 681 &wincng_hAlgorithm_##name, hAlgorithm, NULL); \ 682 } \ 683 return wincng_hAlgorithm_##name ? &name : NULL; \ 684 } 685 686 #define WINCNG_MD_ALGORITHM_CLEANUP(name) do { \ 687 if (wincng_hAlgorithm_##name) { \ 688 BCryptCloseAlgorithmProvider(wincng_hAlgorithm_##name, 0); \ 689 wincng_hAlgorithm_##name = NULL; \ 690 } \ 691 } while (0) 692 693 WINCNG_MD_ALGORITHM(md4, BCRYPT_MD4_ALGORITHM); 694 WINCNG_MD_ALGORITHM(md5, BCRYPT_MD5_ALGORITHM); 695 WINCNG_MD_ALGORITHM(sha1, BCRYPT_SHA1_ALGORITHM); 696 WINCNG_MD_ALGORITHM(sha256, BCRYPT_SHA256_ALGORITHM); 697 WINCNG_MD_ALGORITHM(sha384, BCRYPT_SHA384_ALGORITHM); 698 WINCNG_MD_ALGORITHM(sha512, BCRYPT_SHA512_ALGORITHM); 699 700 static void 701 wincng_md_algorithm_cleanup(void) 702 { 703 WINCNG_MD_ALGORITHM_CLEANUP(md4); 704 WINCNG_MD_ALGORITHM_CLEANUP(md5); 705 WINCNG_MD_ALGORITHM_CLEANUP(sha1); 706 WINCNG_MD_ALGORITHM_CLEANUP(sha256); 707 WINCNG_MD_ALGORITHM_CLEANUP(sha384); 708 WINCNG_MD_ALGORITHM_CLEANUP(sha512); 709 } 710 711 void _hc_wincng_cleanup(void) 712 { 713 wincng_md_algorithm_cleanup(); 714 wincng_cipher_algorithm_cleanup(); 715 } 716