Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1593 lines
28 KiB

  1. /*
  2. interop.c
  3. 6/23/00 dangriff created
  4. */
  5. #include <windows.h>
  6. #include <wincrypt.h>
  7. #include "interop.h"
  8. #include "cspstruc.h"
  9. #include "csptestsuite.h"
  10. //
  11. // Function: IsDataEqual
  12. //
  13. BOOL IsDataEqual(
  14. IN PDATA_BLOB pdb1,
  15. IN PDATA_BLOB pdb2,
  16. IN PTESTCASE ptc)
  17. {
  18. /*
  19. LOGFAILINFO LogFailInfo;
  20. InitFailInfoFromTestCase(ptc, &LogFailInfo);
  21. */
  22. if (pdb1->cbData != pdb2->cbData)
  23. {
  24. /*
  25. LogFailInfo.dwErrorType = ERROR_WRONG_SIZE;
  26. LogFail(&LogFailInfo);
  27. */
  28. LogApiFailure(
  29. API_DATACOMPARE,
  30. ERROR_WRONG_SIZE,
  31. ptc);
  32. return FALSE;
  33. }
  34. if (0 != memcmp(pdb1->pbData, pdb2->pbData, pdb1->cbData))
  35. {
  36. /*
  37. LogFailInfo.dwErrorType = ERROR_BAD_DATA;
  38. LogFail(&LogFailInfo);
  39. */
  40. LogApiFailure(
  41. API_DATACOMPARE,
  42. ERROR_BAD_DATA,
  43. ptc);
  44. return FALSE;
  45. }
  46. return TRUE;
  47. }
  48. //
  49. // Function: ExportPublicKey
  50. //
  51. BOOL ExportPublicKey(IN HCRYPTKEY hSourceKey, OUT PDATA_BLOB pdbKey, IN PTESTCASE ptc)
  52. {
  53. BOOL fSuccess = FALSE;
  54. //
  55. // Export the public key blob from the key handle
  56. //
  57. LOG_TRY(TExportKey(
  58. hSourceKey,
  59. 0,
  60. PUBLICKEYBLOB,
  61. 0,
  62. NULL,
  63. &(pdbKey->cbData),
  64. ptc));
  65. LOG_TRY(TestAlloc(&(pdbKey->pbData), pdbKey->cbData, ptc));
  66. LOG_TRY(TExportKey(
  67. hSourceKey,
  68. 0,
  69. PUBLICKEYBLOB,
  70. 0,
  71. pdbKey->pbData,
  72. &(pdbKey->cbData),
  73. ptc));
  74. fSuccess = TRUE;
  75. Cleanup:
  76. return fSuccess;
  77. }
  78. //
  79. // Function: CreateHashAndAddData
  80. //
  81. BOOL CreateHashAndAddData(
  82. IN HCRYPTPROV hProv,
  83. OUT HCRYPTHASH *phHash,
  84. IN PHASH_INFO pHashInfo,
  85. IN PTESTCASE ptc,
  86. IN HCRYPTKEY hKey /* should be 0 when not doing MAC or HMAC */,
  87. IN PHMAC_INFO pHmacInfo /* should be NULL when not doing HMAC */)
  88. {
  89. BOOL fSuccess = FALSE;
  90. LOG_TRY(TCreateHash(
  91. hProv,
  92. pHashInfo->aiHash,
  93. hKey,
  94. 0,
  95. phHash,
  96. ptc));
  97. //
  98. // This step only applies to the HMAC algorithm.
  99. //
  100. if ( (NULL != pHmacInfo) &&
  101. (CALG_HMAC == pHashInfo->aiHash))
  102. {
  103. LOG_TRY(TSetHash(
  104. *phHash,
  105. HP_HMAC_INFO,
  106. (PBYTE) pHmacInfo,
  107. 0,
  108. ptc));
  109. }
  110. LOG_TRY(THashData(
  111. *phHash,
  112. pHashInfo->dbBaseData.pbData,
  113. pHashInfo->dbBaseData.cbData,
  114. 0,
  115. ptc));
  116. fSuccess = TRUE;
  117. Cleanup:
  118. return fSuccess;
  119. }
  120. //
  121. // Function: ExportPlaintextSessionKey
  122. //
  123. BOOL ExportPlaintextSessionKey(
  124. IN HCRYPTKEY hKey,
  125. IN HCRYPTPROV hProv,
  126. OUT PDATA_BLOB pdbKey,
  127. IN PTESTCASE ptc)
  128. {
  129. BOOL fSuccess = FALSE;
  130. HCRYPTKEY hExchangeKey = 0;
  131. //
  132. // First import the private RSA key with
  133. // exponent of one.
  134. //
  135. LOG_TRY(TImportKey(
  136. hProv,
  137. PrivateKeyWithExponentOfOne,
  138. sizeof(PrivateKeyWithExponentOfOne),
  139. 0,
  140. 0,
  141. &hExchangeKey,
  142. ptc));
  143. //
  144. // Now export "encrypted" session key
  145. //
  146. LOG_TRY(TExportKey(
  147. hKey,
  148. hExchangeKey,
  149. SIMPLEBLOB,
  150. 0,
  151. NULL,
  152. &(pdbKey->cbData),
  153. ptc));
  154. LOG_TRY(TestAlloc(&(pdbKey->pbData), pdbKey->cbData, ptc));
  155. LOG_TRY(TExportKey(
  156. hKey,
  157. hExchangeKey,
  158. SIMPLEBLOB,
  159. 0,
  160. pdbKey->pbData,
  161. &(pdbKey->cbData),
  162. ptc));
  163. fSuccess = TRUE;
  164. Cleanup:
  165. if (hExchangeKey)
  166. {
  167. TDestroyKey(hExchangeKey, ptc);
  168. }
  169. return fSuccess;
  170. }
  171. //
  172. // Function: ImportPlaintextSessionKey
  173. //
  174. BOOL ImportPlaintextSessionKey(
  175. IN PDATA_BLOB pdbKey,
  176. OUT HCRYPTKEY *phKey,
  177. IN HCRYPTPROV hProv,
  178. IN PTESTCASE ptc)
  179. {
  180. BOOL fSuccess = FALSE;
  181. HCRYPTKEY hExchangeKey = 0;
  182. //
  183. // First import the private RSA key with
  184. // exponent of one.
  185. //
  186. LOG_TRY(TImportKey(
  187. hProv,
  188. PrivateKeyWithExponentOfOne,
  189. sizeof(PrivateKeyWithExponentOfOne),
  190. 0,
  191. 0,
  192. &hExchangeKey,
  193. ptc));
  194. //
  195. // Next import the "encrypted" session key
  196. //
  197. LOG_TRY(TImportKey(
  198. hProv,
  199. pdbKey->pbData,
  200. pdbKey->cbData,
  201. hExchangeKey,
  202. 0,
  203. phKey,
  204. ptc));
  205. fSuccess = TRUE;
  206. Cleanup:
  207. if (hExchangeKey)
  208. {
  209. TDestroyKey(hExchangeKey, ptc);
  210. }
  211. return fSuccess;
  212. }
  213. //
  214. // Function: CheckHashedData
  215. //
  216. BOOL CheckHashedData(
  217. IN PHASH_INFO pHashInfo,
  218. IN HCRYPTPROV hProv,
  219. IN PTESTCASE ptc,
  220. IN PTEST_MAC_INFO pTestMacInfo /* Should be NULL when not using MAC alg */)
  221. {
  222. HCRYPTKEY hKey = 0;
  223. BOOL fSuccess = FALSE;
  224. BOOL fUsingMac = FALSE;
  225. HCRYPTHASH hHash = 0;
  226. DATA_BLOB dbHash;
  227. memset(&dbHash, 0, sizeof(dbHash));
  228. if (NULL != pTestMacInfo)
  229. {
  230. fUsingMac = TRUE;
  231. LOG_TRY(ImportPlaintextSessionKey(
  232. &(pTestMacInfo->dbKey),
  233. &hKey,
  234. hProv,
  235. ptc));
  236. }
  237. //
  238. // Create a new hash object of the specified type
  239. // and add the requested data.
  240. //
  241. LOG_TRY(TCreateHash(
  242. hProv,
  243. pHashInfo->aiHash,
  244. hKey,
  245. 0,
  246. &hHash,
  247. ptc));
  248. if ( fUsingMac &&
  249. (CALG_HMAC == pHashInfo->aiHash))
  250. {
  251. LOG_TRY(TSetHash(
  252. hHash,
  253. HP_HMAC_INFO,
  254. (PBYTE) &(pTestMacInfo->HmacInfo),
  255. 0,
  256. ptc));
  257. }
  258. LOG_TRY(THashData(
  259. hHash,
  260. pHashInfo->dbBaseData.pbData,
  261. pHashInfo->dbBaseData.cbData,
  262. 0,
  263. ptc));
  264. //
  265. // Get the resulting hash value and compare it to the
  266. // expected result.
  267. //
  268. LOG_TRY(TGetHash(
  269. hHash,
  270. HP_HASHVAL,
  271. NULL,
  272. &(dbHash.cbData),
  273. 0,
  274. ptc));
  275. LOG_TRY(TestAlloc(&(dbHash.pbData), dbHash.cbData, ptc));
  276. LOG_TRY(TGetHash(
  277. hHash,
  278. HP_HASHVAL,
  279. dbHash.pbData ,
  280. &(dbHash.cbData),
  281. 0,
  282. ptc));
  283. LOG_TRY(IsDataEqual(
  284. &dbHash,
  285. &(pHashInfo->dbHashValue),
  286. ptc));
  287. fSuccess = TRUE;
  288. Cleanup:
  289. if (dbHash.pbData)
  290. {
  291. free(dbHash.pbData);
  292. }
  293. if (hKey)
  294. {
  295. TDestroyKey(hKey, ptc);
  296. }
  297. if (hHash)
  298. {
  299. TDestroyHash(hHash, ptc);
  300. }
  301. return fSuccess;
  302. }
  303. //
  304. // Function: CheckDerivedKey
  305. //
  306. BOOL CheckDerivedKey(
  307. IN PDERIVED_KEY_INFO pDerivedKeyInfo,
  308. IN HCRYPTPROV hProv,
  309. IN PTESTCASE ptc)
  310. {
  311. BOOL fSuccess = FALSE;
  312. HCRYPTKEY hSessionKey = 0;
  313. HCRYPTHASH hHash = 0;
  314. DWORD cbValidData = 0;
  315. DATA_BLOB dbSessionKey;
  316. memset(&dbSessionKey, 0, sizeof(dbSessionKey));
  317. //
  318. // Create a hash and hash the provided data
  319. //
  320. LOG_TRY(CreateHashAndAddData(
  321. hProv,
  322. &hHash,
  323. &(pDerivedKeyInfo->HashInfo),
  324. ptc,
  325. 0, NULL));
  326. // Debugging
  327. /*
  328. pDerivedKeyInfo->cbHB = sizeof(pDerivedKeyInfo->rgbHashValB);
  329. LOG_TRY(CryptGetHashParam(hHash, HP_HASHVAL, pDerivedKeyInfo->rgbHashValB, &(pDerivedKeyInfo->cbHB), 0));
  330. */
  331. //
  332. // Derive a session key from the resulting hash object
  333. //
  334. LOG_TRY(TDeriveKey(
  335. hProv,
  336. pDerivedKeyInfo->aiKey,
  337. hHash,
  338. CRYPT_EXPORTABLE | (pDerivedKeyInfo->dwKeySize) << 16,
  339. &hSessionKey,
  340. ptc));
  341. // Debug
  342. /*
  343. pDerivedKeyInfo->cbCB = 10;
  344. LOG_TRY(CryptEncrypt(hSessionKey, 0, TRUE, 0, pDerivedKeyInfo->rgbCipherB, &(pDerivedKeyInfo->cbCB), sizeof(pDerivedKeyInfo->rgbCipherA)));
  345. */
  346. //
  347. // Export the session key in plaintext form
  348. //
  349. LOG_TRY(ExportPlaintextSessionKey(hSessionKey, hProv, &dbSessionKey, ptc));
  350. // Debug
  351. /*
  352. PrintBytes(L"SessionA", pDerivedKeyInfo->dbKey.pbData, pDerivedKeyInfo->dbKey.cbData);
  353. PrintBytes(L"SessionB", dbSessionKey.pbData, dbSessionKey.cbData);
  354. */
  355. //
  356. // Fudge the data comparison slightly since the RSA cipher text blob
  357. // actually contains mostly random padding in this case (having
  358. // used ExportPlaintextSessionKey(), only the first <key length>
  359. // bytes of the cipher data are interesting).
  360. //
  361. // Therefor, compare the following number of bytes in the blobs. The
  362. // rest of it won't match.
  363. //
  364. // sizeof(BLOBHEADER) + sizeof(ALG_ID) + dwKeySize / 8
  365. //
  366. cbValidData = sizeof(BLOBHEADER) + sizeof(ALG_ID) + pDerivedKeyInfo->dwKeySize / 8;
  367. dbSessionKey.cbData = cbValidData;
  368. pDerivedKeyInfo->dbKey.cbData = cbValidData;
  369. LOG_TRY(IsDataEqual(
  370. &dbSessionKey,
  371. &(pDerivedKeyInfo->dbKey),
  372. ptc));
  373. fSuccess = TRUE;
  374. Cleanup:
  375. if (hSessionKey)
  376. {
  377. TDestroyKey(hSessionKey, ptc);
  378. }
  379. if (hHash)
  380. {
  381. TDestroyHash(hHash, ptc);
  382. }
  383. if (dbSessionKey.pbData)
  384. {
  385. free(dbSessionKey.pbData);
  386. }
  387. return fSuccess;
  388. }
  389. //
  390. // Function: CheckSignedData
  391. //
  392. BOOL CheckSignedData(
  393. IN PSIGNED_DATA_INFO pSignedDataInfo,
  394. IN HCRYPTPROV hProv,
  395. IN PTESTCASE ptc)
  396. {
  397. BOOL fSuccess = FALSE;
  398. HCRYPTHASH hHash = 0;
  399. HCRYPTKEY hPubKey = 0;
  400. //
  401. // Create a hash and hash the provided data
  402. //
  403. if (! CreateHashAndAddData(
  404. hProv,
  405. &hHash,
  406. &(pSignedDataInfo->HashInfo),
  407. ptc,
  408. 0, NULL))
  409. {
  410. goto Cleanup;
  411. }
  412. //
  413. // Import the public key corresponding to the private key
  414. // that was used to sign the hashed data.
  415. //
  416. LOG_TRY(TImportKey(
  417. hProv,
  418. pSignedDataInfo->dbPublicKey.pbData,
  419. pSignedDataInfo->dbPublicKey.cbData,
  420. 0,
  421. 0,
  422. &hPubKey,
  423. ptc));
  424. LOG_TRY(TVerifySign(
  425. hHash,
  426. pSignedDataInfo->dbSignature.pbData,
  427. pSignedDataInfo->dbSignature.cbData,
  428. hPubKey,
  429. NULL,
  430. 0,
  431. ptc));
  432. fSuccess = TRUE;
  433. Cleanup:
  434. if (hHash)
  435. {
  436. TDestroyHash(hHash, ptc);
  437. }
  438. if (hPubKey)
  439. {
  440. TDestroyKey(hPubKey, ptc);
  441. }
  442. return fSuccess;
  443. }
  444. //
  445. // Function: PrepareCipherBuffer
  446. // Purpose: Allocate a buffer to receive encrypted data based
  447. // on the size of the data to encrypt, and based on whether
  448. // the cipher is block or stream.
  449. //
  450. BOOL PrepareCipherBuffer(
  451. OUT PDATA_BLOB pdbTargetBuffer,
  452. IN PDATA_BLOB pdbSourceBuffer,
  453. IN DWORD cbBlockLen,
  454. IN BOOL fIsBlockCipher,
  455. IN PTESTCASE ptc)
  456. {
  457. BOOL fSuccess = FALSE;
  458. if (fIsBlockCipher)
  459. {
  460. //
  461. // Determine the maximum length of the cipher text for
  462. // this block cipher. The length of the cipher text is
  463. // up to block length more than the length of the plaintext.
  464. //
  465. pdbTargetBuffer->cbData =
  466. pdbSourceBuffer->cbData + cbBlockLen - (pdbSourceBuffer->cbData % cbBlockLen);
  467. }
  468. else
  469. {
  470. pdbTargetBuffer->cbData = pdbSourceBuffer->cbData;
  471. }
  472. LOG_TRY(TestAlloc(
  473. &(pdbTargetBuffer->pbData),
  474. pdbTargetBuffer->cbData,
  475. ptc));
  476. memcpy(
  477. pdbTargetBuffer->pbData,
  478. pdbSourceBuffer->pbData,
  479. pdbSourceBuffer->cbData);
  480. fSuccess = TRUE;
  481. Cleanup:
  482. return fSuccess;
  483. }
  484. //
  485. // Function: DoBlockCipherOperation
  486. // Purpose: Perform the block cipher operation indicated in the Op parameter
  487. // on the data stored in the pdbSource parameter. The processed data
  488. // will be in pdbTarget.
  489. //
  490. BOOL DoBlockCipherOperation(
  491. IN HCRYPTKEY hKey,
  492. OUT PDATA_BLOB pdbTarget,
  493. IN PDATA_BLOB pdbSource,
  494. IN DWORD cbBlockLen,
  495. IN CIPHER_OP Op,
  496. IN PTESTCASE ptc)
  497. {
  498. BOOL fSuccess = FALSE;
  499. DWORD cbCurrent = CIPHER_BLOCKS_PER_ROUND * cbBlockLen;
  500. DWORD cbProcessed = 0;
  501. BOOL fFinal = FALSE;
  502. DWORD dwKeyAlg = 0;
  503. DWORD cb = 0;
  504. switch ( Op )
  505. {
  506. case OP_Encrypt:
  507. {
  508. LOG_TRY(PrepareCipherBuffer(
  509. pdbTarget,
  510. pdbSource,
  511. cbBlockLen,
  512. TRUE,
  513. ptc));
  514. while (cbCurrent < pdbSource->cbData)
  515. {
  516. LOG_TRY(TEncrypt(
  517. hKey,
  518. 0,
  519. fFinal,
  520. 0,
  521. pdbTarget->pbData + cbProcessed,
  522. &cbCurrent,
  523. pdbTarget->cbData - cbProcessed,
  524. ptc));
  525. if (fFinal)
  526. {
  527. break;
  528. }
  529. cbProcessed += cbCurrent;
  530. if ((cbProcessed + cbCurrent) >= pdbSource->cbData)
  531. {
  532. cbCurrent = pdbSource->cbData - cbProcessed;
  533. fFinal = TRUE;
  534. }
  535. }
  536. break;
  537. }
  538. case OP_Decrypt:
  539. {
  540. //
  541. // For block decryption, the decrypted data will be no
  542. // larger than the cipher text.
  543. //
  544. pdbTarget->cbData = pdbSource->cbData;
  545. LOG_TRY(TestAlloc(
  546. &(pdbTarget->pbData),
  547. pdbTarget->cbData,
  548. ptc));
  549. memcpy(pdbTarget->pbData, pdbSource->pbData, pdbTarget->cbData);
  550. //
  551. // Known 3DES_112 bug in Windows 2000. Specifying a 112
  552. // bit key size, rather than 128 bits, causes the last two
  553. // bytes of key data to be random.
  554. //
  555. cb = sizeof(dwKeyAlg);
  556. LOG_TRY(TGetKey(
  557. hKey,
  558. KP_ALGID,
  559. (PBYTE) &dwKeyAlg,
  560. &cb,
  561. 0,
  562. ptc));
  563. if (CALG_3DES_112 == dwKeyAlg)
  564. {
  565. ptc->KnownErrorID = KNOWN_TESTDECRYPTPROC_3DES112;
  566. ptc->pwszErrorHelp = L"Inconsistent encryption results when using a 14 byte 3DES_112 key";
  567. }
  568. while (cbCurrent < pdbTarget->cbData)
  569. {
  570. LOG_TRY(TDecrypt(
  571. hKey,
  572. 0,
  573. fFinal,
  574. 0,
  575. pdbTarget->pbData + cbProcessed,
  576. &cbCurrent,
  577. ptc));
  578. if (fFinal)
  579. {
  580. //
  581. // Set the size of the actual resulting plaintext.
  582. //
  583. pdbTarget->cbData = cbProcessed + cbCurrent;
  584. break;
  585. }
  586. cbProcessed += cbCurrent;
  587. if ((cbProcessed + cbCurrent) >= pdbTarget->cbData)
  588. {
  589. cbCurrent = pdbTarget->cbData - cbProcessed;
  590. fFinal = TRUE;
  591. }
  592. }
  593. ptc->KnownErrorID = KNOWN_ERROR_UNKNOWN;
  594. ptc->pwszErrorHelp = NULL;
  595. break;
  596. }
  597. }
  598. fSuccess = TRUE;
  599. Cleanup:
  600. return fSuccess;
  601. }
  602. //
  603. // Function: DoStreamCipherOperation
  604. // Purpose: Perform the stream cipher operation indicated in the Op parameter
  605. // on the data stored in the pdbSource parameter. The processed data
  606. // will be in pdbTarget.
  607. //
  608. BOOL DoStreamCipherOperation(
  609. IN HCRYPTKEY hKey,
  610. OUT PDATA_BLOB pdbTarget,
  611. IN PDATA_BLOB pdbSource,
  612. IN CIPHER_OP Op,
  613. IN PTESTCASE ptc)
  614. {
  615. BOOL fSuccess = FALSE;
  616. DWORD cbData = pdbSource->cbData;
  617. switch ( Op )
  618. {
  619. case OP_Encrypt:
  620. {
  621. LOG_TRY(PrepareCipherBuffer(
  622. pdbTarget,
  623. pdbSource,
  624. 0,
  625. FALSE,
  626. ptc));
  627. LOG_TRY(TEncrypt(
  628. hKey,
  629. 0,
  630. TRUE,
  631. 0,
  632. pdbTarget->pbData,
  633. &cbData,
  634. pdbTarget->cbData,
  635. ptc));
  636. break;
  637. }
  638. case OP_Decrypt:
  639. {
  640. pdbTarget->cbData = pdbSource->cbData;
  641. LOG_TRY(TestAlloc(
  642. &(pdbTarget->pbData),
  643. pdbTarget->cbData,
  644. ptc));
  645. memcpy(pdbTarget->pbData, pdbSource->pbData, pdbTarget->cbData);
  646. LOG_TRY(TDecrypt(
  647. hKey,
  648. 0,
  649. TRUE,
  650. 0,
  651. pdbTarget->pbData,
  652. &cbData,
  653. ptc));
  654. break;
  655. }
  656. }
  657. fSuccess = TRUE;
  658. Cleanup:
  659. return fSuccess;
  660. }
  661. //
  662. // Function: ProcessCipherData
  663. //
  664. BOOL ProcessCipherData(
  665. IN HCRYPTPROV hProvA,
  666. IN OUT PTEST_ENCRYPT_INFO pTestEncryptInfo,
  667. IN PTESTCASE ptc)
  668. {
  669. BOOL fSuccess = FALSE;
  670. HCRYPTKEY hKey = 0;
  671. DWORD cbData = 0;
  672. DWORD cbBlockLen = 0;
  673. //DWORD cbProcessed = 0;
  674. //DWORD cbCurrent = 0;
  675. //BOOL fFinal = FALSE;
  676. //
  677. // Create the key
  678. //
  679. LOG_TRY(TGenKey(
  680. hProvA,
  681. pTestEncryptInfo->aiKeyAlg,
  682. CRYPT_EXPORTABLE | (pTestEncryptInfo->dwKeySize << 16),
  683. &hKey,
  684. ptc));
  685. //
  686. // Generate the salt, if requested
  687. //
  688. if (pTestEncryptInfo->fUseSalt)
  689. {
  690. //
  691. // Microsoft CSP's have a maximum key + salt length of
  692. // 128 bits, but the test will set a long salt value
  693. // regardless of the key size to ensure
  694. // that possible interop issues are exposed.
  695. //
  696. LOG_TRY(TestAlloc(
  697. &(pTestEncryptInfo->dbSalt.pbData),
  698. DEFAULT_SALT_LEN,
  699. ptc));
  700. pTestEncryptInfo->dbSalt.cbData = DEFAULT_SALT_LEN;
  701. LOG_TRY(TSetKey(
  702. hKey,
  703. KP_SALT_EX,
  704. (PBYTE) &(pTestEncryptInfo->dbSalt),
  705. 0,
  706. ptc));
  707. }
  708. //
  709. // Set the cipher mode, if requested
  710. //
  711. if (pTestEncryptInfo->fSetMode)
  712. {
  713. LOG_TRY(TSetKey(
  714. hKey,
  715. KP_MODE,
  716. (PBYTE) &(pTestEncryptInfo->dwMode),
  717. 0,
  718. ptc));
  719. }
  720. //
  721. // Determine cipher block len, if applicable
  722. //
  723. if (ALG_TYPE_BLOCK & pTestEncryptInfo->aiKeyAlg)
  724. {
  725. cbData = sizeof(cbBlockLen);
  726. LOG_TRY(TGetKey(
  727. hKey,
  728. KP_BLOCKLEN,
  729. (PBYTE) &cbBlockLen,
  730. &cbData,
  731. 0,
  732. ptc));
  733. // Block length is returned in bits
  734. cbBlockLen = cbBlockLen / 8;
  735. pTestEncryptInfo->cbBlockLen = cbBlockLen;
  736. }
  737. //
  738. // Set the IV, if requested
  739. //
  740. // If caller has requested an IV for a stream cipher,
  741. // these calls may fail.
  742. //
  743. if (pTestEncryptInfo->fSetIV)
  744. {
  745. //
  746. // Size of IV must be equal to cipher block length
  747. //
  748. LOG_TRY(TestAlloc(
  749. &(pTestEncryptInfo->pbIV),
  750. cbBlockLen,
  751. ptc));
  752. LOG_TRY(TSetKey(
  753. hKey,
  754. KP_IV,
  755. pTestEncryptInfo->pbIV,
  756. 0,
  757. ptc));
  758. }
  759. //
  760. // Generate the base data
  761. //
  762. if (ALG_TYPE_BLOCK & pTestEncryptInfo->aiKeyAlg)
  763. {
  764. //
  765. // To create a "better" block cipher test scenario,
  766. // the base data will not be an exact multiple of the
  767. // block length. This will allow an interesting multi-round
  768. // encryption, with the last round requiring less than a block
  769. // length of padding.
  770. //
  771. // data len = BLOCKS_IN_BASE_DATA * cbBlockLen - 1byte
  772. //
  773. pTestEncryptInfo->dbBaseData.cbData = BLOCKS_IN_BASE_DATA * cbBlockLen - 1;
  774. }
  775. else
  776. {
  777. //
  778. // Set base data len for a stream cipher
  779. //
  780. pTestEncryptInfo->dbBaseData.cbData = STREAM_CIPHER_BASE_DATA_LEN;
  781. }
  782. LOG_TRY(TestAlloc(
  783. &(pTestEncryptInfo->dbBaseData.pbData),
  784. pTestEncryptInfo->dbBaseData.cbData,
  785. ptc));
  786. LOG_TRY(TGenRand(
  787. hProvA,
  788. pTestEncryptInfo->dbBaseData.cbData,
  789. pTestEncryptInfo->dbBaseData.pbData,
  790. ptc));
  791. //
  792. // Call DoBlockCipherOperation or DoStreamCipherOperation,
  793. // depending on the cipher algorithm, to perform the requested
  794. // operation.
  795. //
  796. if (ALG_TYPE_BLOCK & pTestEncryptInfo->aiKeyAlg)
  797. {
  798. LOG_TRY(DoBlockCipherOperation(
  799. hKey,
  800. &(pTestEncryptInfo->dbProcessedData),
  801. &(pTestEncryptInfo->dbBaseData),
  802. cbBlockLen,
  803. pTestEncryptInfo->Operation,
  804. ptc));
  805. }
  806. else
  807. {
  808. LOG_TRY(DoStreamCipherOperation(
  809. hKey,
  810. &(pTestEncryptInfo->dbProcessedData),
  811. &(pTestEncryptInfo->dbBaseData),
  812. pTestEncryptInfo->Operation,
  813. ptc));
  814. }
  815. //
  816. // Export the session key in plain text
  817. //
  818. LOG_TRY(ExportPlaintextSessionKey(
  819. hKey,
  820. hProvA,
  821. &(pTestEncryptInfo->dbKey),
  822. ptc));
  823. fSuccess = TRUE;
  824. Cleanup:
  825. if (hKey)
  826. {
  827. TDestroyKey(hKey, ptc);
  828. }
  829. return fSuccess;
  830. }
  831. //
  832. // Function: VerifyCipherData
  833. //
  834. BOOL VerifyCipherData(
  835. IN HCRYPTPROV hProvB,
  836. IN PTEST_ENCRYPT_INFO pTestEncryptInfo,
  837. IN PTESTCASE ptc)
  838. {
  839. BOOL fSuccess = FALSE;
  840. HCRYPTKEY hKey = 0;
  841. CIPHER_OP Op;
  842. DATA_BLOB dbData;
  843. memset(&dbData, 0, sizeof(dbData));
  844. //
  845. // Import the plaintext session key
  846. //
  847. LOG_TRY(ImportPlaintextSessionKey(
  848. &(pTestEncryptInfo->dbKey),
  849. &hKey,
  850. hProvB,
  851. ptc));
  852. if (pTestEncryptInfo->fUseSalt)
  853. {
  854. LOG_TRY(TSetKey(
  855. hKey,
  856. KP_SALT_EX,
  857. (PBYTE) &(pTestEncryptInfo->dbSalt),
  858. 0,
  859. ptc));
  860. }
  861. //
  862. // Set the salt value, if requested
  863. //
  864. if (pTestEncryptInfo->fUseSalt)
  865. {
  866. LOG_TRY(TSetKey(
  867. hKey,
  868. KP_SALT_EX,
  869. (PBYTE) &(pTestEncryptInfo->dbSalt),
  870. 0,
  871. ptc));
  872. }
  873. //
  874. // Set the cipher mode, if requested
  875. //
  876. if (pTestEncryptInfo->fSetMode)
  877. {
  878. LOG_TRY(TSetKey(
  879. hKey,
  880. KP_MODE,
  881. (PBYTE) &(pTestEncryptInfo->dwMode),
  882. 0,
  883. ptc));
  884. }
  885. //
  886. // Set the IV, if requested
  887. //
  888. if (pTestEncryptInfo->fSetIV)
  889. {
  890. LOG_TRY(TSetKey(
  891. hKey,
  892. KP_IV,
  893. pTestEncryptInfo->pbIV,
  894. 0,
  895. ptc));
  896. }
  897. //
  898. // The verification operation should be the opposite of what
  899. // the caller initially specified (the opposite of the operation
  900. // performed in ProcessCipherData).
  901. //
  902. Op = (OP_Encrypt == pTestEncryptInfo->Operation) ? OP_Decrypt : OP_Encrypt;
  903. //
  904. // Call DoBlockCipherOperation or DoStreamCipherOperation,
  905. // depending on the cipher algorithm, to perform the requested
  906. // operation.
  907. //
  908. if (ALG_TYPE_BLOCK & pTestEncryptInfo->aiKeyAlg)
  909. {
  910. LOG_TRY(DoBlockCipherOperation(
  911. hKey,
  912. &dbData,
  913. &(pTestEncryptInfo->dbProcessedData),
  914. pTestEncryptInfo->cbBlockLen,
  915. Op,
  916. ptc));
  917. }
  918. else
  919. {
  920. LOG_TRY(DoStreamCipherOperation(
  921. hKey,
  922. &dbData,
  923. &(pTestEncryptInfo->dbProcessedData),
  924. Op,
  925. ptc));
  926. }
  927. if (CALG_3DES_112 == pTestEncryptInfo->aiKeyAlg)
  928. {
  929. ptc->KnownErrorID = KNOWN_TESTDECRYPTPROC_3DES112;
  930. }
  931. LOG_TRY(IsDataEqual(&dbData, &(pTestEncryptInfo->dbBaseData), ptc));
  932. ptc->KnownErrorID = KNOWN_ERROR_UNKNOWN;
  933. fSuccess = TRUE;
  934. Cleanup:
  935. if (hKey)
  936. {
  937. TDestroyKey(hKey, ptc);
  938. }
  939. if (dbData.pbData)
  940. {
  941. free(dbData.pbData);
  942. }
  943. if (pTestEncryptInfo->dbBaseData.pbData)
  944. {
  945. free(pTestEncryptInfo->dbBaseData.pbData);
  946. }
  947. if (pTestEncryptInfo->dbProcessedData.pbData)
  948. {
  949. free(pTestEncryptInfo->dbProcessedData.pbData);
  950. }
  951. if (pTestEncryptInfo->dbKey.pbData)
  952. {
  953. free(pTestEncryptInfo->dbKey.pbData);
  954. }
  955. if (pTestEncryptInfo->pbIV)
  956. {
  957. free(pTestEncryptInfo->pbIV);
  958. }
  959. if (pTestEncryptInfo->dbSalt.pbData)
  960. {
  961. free(pTestEncryptInfo->dbSalt.pbData);
  962. }
  963. return fSuccess;
  964. }
  965. //
  966. // Function: GetHashVal
  967. // Purpose: Populate a data blob with the hash value from the
  968. // provided hash handle.
  969. //
  970. BOOL GetHashVal(
  971. IN HCRYPTHASH hHash,
  972. OUT PDATA_BLOB pdb,
  973. IN PTESTCASE ptc)
  974. {
  975. BOOL fSuccess = FALSE;
  976. LOG_TRY(TGetHash(
  977. hHash,
  978. HP_HASHVAL,
  979. NULL,
  980. &(pdb->cbData),
  981. 0,
  982. ptc));
  983. LOG_TRY(TestAlloc(&(pdb->pbData), pdb->cbData, ptc));
  984. LOG_TRY(TGetHash(
  985. hHash,
  986. HP_HASHVAL,
  987. pdb->pbData,
  988. &(pdb->cbData),
  989. 0,
  990. ptc));
  991. fSuccess = TRUE;
  992. Cleanup:
  993. return fSuccess;
  994. }
  995. //
  996. // Function: CreateHashedSessionKey
  997. //
  998. BOOL CreateHashedSessionKey(
  999. IN HCRYPTPROV hProv,
  1000. IN OUT PHASH_SESSION_INFO pHashSessionInfo,
  1001. IN PTESTCASE ptc)
  1002. {
  1003. BOOL fSuccess = FALSE;
  1004. HCRYPTKEY hKey = 0;
  1005. HCRYPTHASH hHash = 0;
  1006. LOG_TRY(TGenKey(
  1007. hProv,
  1008. pHashSessionInfo->aiKey,
  1009. CRYPT_EXPORTABLE | (pHashSessionInfo->dwKeySize << 16),
  1010. &hKey,
  1011. ptc));
  1012. LOG_TRY(TCreateHash(
  1013. hProv,
  1014. pHashSessionInfo->aiHash,
  1015. 0,
  1016. 0,
  1017. &hHash,
  1018. ptc));
  1019. LOG_TRY(THashSession(hHash, hKey, pHashSessionInfo->dwFlags, ptc));
  1020. LOG_TRY(GetHashVal(hHash, &(pHashSessionInfo->dbHash), ptc));
  1021. LOG_TRY(ExportPlaintextSessionKey(
  1022. hKey,
  1023. hProv,
  1024. &(pHashSessionInfo->dbKey),
  1025. ptc));
  1026. fSuccess = TRUE;
  1027. Cleanup:
  1028. if (hKey)
  1029. {
  1030. TDestroyKey(hKey, ptc);
  1031. }
  1032. if (hHash)
  1033. {
  1034. TDestroyHash(hHash, ptc);
  1035. }
  1036. return fSuccess;
  1037. }
  1038. //
  1039. // Function: VerifyHashedSessionKey
  1040. // Purpose: Import the plaintext session key into a separate CSP.
  1041. // Hash the session key with CryptHashSessionKey. Verify
  1042. // the resulting hash value.
  1043. //
  1044. BOOL VerifyHashedSessionKey(
  1045. IN HCRYPTPROV hInteropProv,
  1046. IN PHASH_SESSION_INFO pHashSessionInfo,
  1047. IN PTESTCASE ptc)
  1048. {
  1049. BOOL fSuccess = FALSE;
  1050. HCRYPTKEY hKey = 0;
  1051. HCRYPTHASH hHash = 0;
  1052. DATA_BLOB dbInteropHash;
  1053. memset(&dbInteropHash, 0, sizeof(dbInteropHash));
  1054. LOG_TRY(ImportPlaintextSessionKey(
  1055. &(pHashSessionInfo->dbKey),
  1056. &hKey,
  1057. hInteropProv,
  1058. ptc));
  1059. LOG_TRY(TCreateHash(
  1060. hInteropProv,
  1061. pHashSessionInfo->aiHash,
  1062. 0,
  1063. 0,
  1064. &hHash,
  1065. ptc));
  1066. LOG_TRY(THashSession(hHash, hKey, pHashSessionInfo->dwFlags, ptc));
  1067. LOG_TRY(GetHashVal(hHash, &dbInteropHash, ptc));
  1068. LOG_TRY(IsDataEqual(&(pHashSessionInfo->dbHash), &dbInteropHash, ptc));
  1069. fSuccess = TRUE;
  1070. Cleanup:
  1071. if (hKey)
  1072. {
  1073. TDestroyKey(hKey, ptc);
  1074. }
  1075. if (hHash)
  1076. {
  1077. TDestroyHash(hHash, ptc);
  1078. }
  1079. if (dbInteropHash.pbData)
  1080. {
  1081. free(dbInteropHash.pbData);
  1082. }
  1083. if (pHashSessionInfo->dbHash.pbData)
  1084. {
  1085. free(pHashSessionInfo->dbHash.pbData);
  1086. }
  1087. if (pHashSessionInfo->dbKey.pbData)
  1088. {
  1089. free(pHashSessionInfo->dbKey.pbData);
  1090. }
  1091. return fSuccess;
  1092. }
  1093. //
  1094. // Function: RSA1_CreateKeyPair
  1095. //
  1096. BOOL RSA1_CreateKeyPair(
  1097. IN HCRYPTPROV hProvA,
  1098. IN PKEYEXCHANGE_INFO pKeyExchangeInfo,
  1099. OUT PKEYEXCHANGE_STATE pKeyExchangeState,
  1100. IN PTESTCASE ptc)
  1101. {
  1102. BOOL fSuccess = FALSE;
  1103. HCRYPTKEY hPubKeyA = 0;
  1104. //
  1105. // Create an RSA key exchange key pair and export the public
  1106. // key.
  1107. //
  1108. LOG_TRY(TGenKey(
  1109. hProvA,
  1110. AT_KEYEXCHANGE,
  1111. (pKeyExchangeInfo->dwPubKeySize << 16) | CRYPT_EXPORTABLE,
  1112. &hPubKeyA,
  1113. ptc));
  1114. if (! ExportPublicKey(hPubKeyA, &(pKeyExchangeState->dbPubKeyA), ptc))
  1115. {
  1116. goto Cleanup;
  1117. }
  1118. fSuccess = TRUE;
  1119. Cleanup:
  1120. if (hPubKeyA)
  1121. {
  1122. TDestroyKey(hPubKeyA, ptc);
  1123. }
  1124. return fSuccess;
  1125. }
  1126. //
  1127. // Function: RSA2_EncryptPlainText
  1128. //
  1129. BOOL RSA2_EncryptPlainText(
  1130. IN HCRYPTPROV hProvB,
  1131. IN PKEYEXCHANGE_INFO pKeyExchangeInfo,
  1132. IN OUT PKEYEXCHANGE_STATE pKeyExchangeState,
  1133. IN PTESTCASE ptc)
  1134. {
  1135. BOOL fSuccess = TRUE;
  1136. HCRYPTKEY hSessionKeyB = 0;
  1137. HCRYPTKEY hPubKeyB = 0;
  1138. HCRYPTKEY hPubKeyA = 0;
  1139. HCRYPTHASH hHash = 0;
  1140. //DWORD cbBuffer = 0;
  1141. DWORD cbData = 0;
  1142. //
  1143. // User B creates an RSA signature key pair
  1144. //
  1145. LOG_TRY(TGenKey(
  1146. hProvB,
  1147. AT_SIGNATURE,
  1148. (pKeyExchangeInfo->dwPubKeySize << 16) | CRYPT_EXPORTABLE,
  1149. &hPubKeyB,
  1150. ptc));
  1151. //
  1152. // Create hash and session key
  1153. //
  1154. LOG_TRY(TCreateHash(
  1155. hProvB,
  1156. pKeyExchangeInfo->aiHash,
  1157. 0,
  1158. 0,
  1159. &hHash,
  1160. ptc));
  1161. LOG_TRY(TGenKey(
  1162. hProvB,
  1163. pKeyExchangeInfo->aiSessionKey,
  1164. (pKeyExchangeInfo->dwSessionKeySize << 16) | CRYPT_EXPORTABLE,
  1165. &hSessionKeyB,
  1166. ptc));
  1167. //
  1168. // Encrypt and hash the data simultaneously
  1169. //
  1170. cbData = pKeyExchangeState->dbCipherTextB.cbData = pKeyExchangeInfo->dbPlainText.cbData;
  1171. LOG_TRY(TEncrypt(
  1172. hSessionKeyB,
  1173. 0,
  1174. TRUE,
  1175. 0,
  1176. NULL,
  1177. &(pKeyExchangeState->dbCipherTextB.cbData),
  1178. 0,
  1179. ptc));
  1180. LOG_TRY(TestAlloc(
  1181. &(pKeyExchangeState->dbCipherTextB.pbData),
  1182. pKeyExchangeState->dbCipherTextB.cbData,
  1183. ptc));
  1184. memcpy(
  1185. pKeyExchangeState->dbCipherTextB.pbData,
  1186. pKeyExchangeInfo->dbPlainText.pbData,
  1187. cbData);
  1188. LOG_TRY(TEncrypt(
  1189. hSessionKeyB,
  1190. hHash,
  1191. TRUE,
  1192. 0,
  1193. pKeyExchangeState->dbCipherTextB.pbData,
  1194. &cbData,
  1195. pKeyExchangeState->dbCipherTextB.cbData,
  1196. ptc));
  1197. //
  1198. // Now sign the hashed plain text
  1199. //
  1200. LOG_TRY(TSignHash(
  1201. hHash,
  1202. AT_SIGNATURE,
  1203. NULL,
  1204. 0,
  1205. NULL,
  1206. &(pKeyExchangeState->dbSignatureB.cbData),
  1207. ptc));
  1208. LOG_TRY(TestAlloc(
  1209. &(pKeyExchangeState->dbSignatureB.pbData),
  1210. pKeyExchangeState->dbSignatureB.cbData,
  1211. ptc));
  1212. LOG_TRY(TSignHash(
  1213. hHash,
  1214. AT_SIGNATURE,
  1215. NULL,
  1216. 0,
  1217. pKeyExchangeState->dbSignatureB.pbData,
  1218. &(pKeyExchangeState->dbSignatureB.cbData),
  1219. ptc));
  1220. //
  1221. // Import User A's public key. Then export User B's session key encrypted
  1222. // with User A's public key.
  1223. //
  1224. LOG_TRY(TImportKey(
  1225. hProvB,
  1226. pKeyExchangeState->dbPubKeyA.pbData,
  1227. pKeyExchangeState->dbPubKeyA.cbData,
  1228. 0,
  1229. 0,
  1230. &hPubKeyA,
  1231. ptc));
  1232. LOG_TRY(TExportKey(
  1233. hSessionKeyB,
  1234. hPubKeyA,
  1235. SIMPLEBLOB,
  1236. 0,
  1237. NULL,
  1238. &(pKeyExchangeState->dbEncryptedSessionKeyB.cbData),
  1239. ptc));
  1240. LOG_TRY(TestAlloc(
  1241. &(pKeyExchangeState->dbEncryptedSessionKeyB.pbData),
  1242. pKeyExchangeState->dbEncryptedSessionKeyB.cbData,
  1243. ptc));
  1244. LOG_TRY(TExportKey(
  1245. hSessionKeyB,
  1246. hPubKeyA,
  1247. SIMPLEBLOB,
  1248. 0,
  1249. pKeyExchangeState->dbEncryptedSessionKeyB.pbData,
  1250. &(pKeyExchangeState->dbEncryptedSessionKeyB.cbData),
  1251. ptc));
  1252. //
  1253. // Export User B's public key so that User A can verify the signed data
  1254. //
  1255. if (! ExportPublicKey(hPubKeyB, &(pKeyExchangeState->dbPubKeyB), ptc))
  1256. {
  1257. goto Cleanup;
  1258. }
  1259. fSuccess = TRUE;
  1260. Cleanup:
  1261. if (hSessionKeyB)
  1262. {
  1263. TDestroyKey(hSessionKeyB, ptc);
  1264. }
  1265. if (hPubKeyB)
  1266. {
  1267. TDestroyKey(hPubKeyB, ptc);
  1268. }
  1269. if (hPubKeyA)
  1270. {
  1271. TDestroyKey(hPubKeyA, ptc);
  1272. }
  1273. if (hHash)
  1274. {
  1275. TDestroyHash(hHash, ptc);
  1276. }
  1277. return fSuccess;
  1278. }
  1279. //
  1280. // Function: RSA3_DecryptAndCheck
  1281. //
  1282. BOOL RSA3_DecryptAndCheck(
  1283. IN HCRYPTPROV hProvA,
  1284. IN PKEYEXCHANGE_INFO pKeyExchangeInfo,
  1285. IN PKEYEXCHANGE_STATE pKeyExchangeState,
  1286. IN PTESTCASE ptc)
  1287. {
  1288. BOOL fSuccess = FALSE;
  1289. HCRYPTKEY hPubKeyA = 0;
  1290. HCRYPTKEY hPubKeyB = 0;
  1291. HCRYPTKEY hSessionKey = 0;
  1292. HCRYPTHASH hHash = 0;
  1293. //
  1294. // Get User A's RSA key exchange key handle
  1295. //
  1296. LOG_TRY(TGetUser(
  1297. hProvA,
  1298. AT_KEYEXCHANGE,
  1299. &hPubKeyA,
  1300. ptc));
  1301. //
  1302. // Import and decrypt the session key from User B
  1303. //
  1304. LOG_TRY(TImportKey(
  1305. hProvA,
  1306. pKeyExchangeState->dbEncryptedSessionKeyB.pbData,
  1307. pKeyExchangeState->dbEncryptedSessionKeyB.cbData,
  1308. hPubKeyA,
  1309. 0,
  1310. &hSessionKey,
  1311. ptc));
  1312. //
  1313. // Create a hash. Then simultaneously decrypt the cipher text and
  1314. // hash the resulting plain text.
  1315. //
  1316. LOG_TRY(TCreateHash(
  1317. hProvA,
  1318. pKeyExchangeInfo->aiHash,
  1319. 0,
  1320. 0,
  1321. &hHash,
  1322. ptc));
  1323. LOG_TRY(TDecrypt(
  1324. hSessionKey,
  1325. hHash,
  1326. TRUE,
  1327. 0,
  1328. pKeyExchangeState->dbCipherTextB.pbData,
  1329. &(pKeyExchangeState->dbCipherTextB.cbData),
  1330. ptc));
  1331. //
  1332. // Import User B's signature public key.
  1333. //
  1334. LOG_TRY(TImportKey(
  1335. hProvA,
  1336. pKeyExchangeState->dbPubKeyB.pbData,
  1337. pKeyExchangeState->dbPubKeyB.cbData,
  1338. 0,
  1339. 0,
  1340. &hPubKeyB,
  1341. ptc));
  1342. //
  1343. // Verify the signature blob
  1344. //
  1345. LOG_TRY(TVerifySign(
  1346. hHash,
  1347. pKeyExchangeState->dbSignatureB.pbData,
  1348. pKeyExchangeState->dbSignatureB.cbData,
  1349. hPubKeyB,
  1350. NULL,
  1351. 0,
  1352. ptc));
  1353. fSuccess = TRUE;
  1354. Cleanup:
  1355. if (hSessionKey)
  1356. {
  1357. TDestroyKey(hSessionKey, ptc);
  1358. }
  1359. if (hPubKeyA)
  1360. {
  1361. TDestroyKey(hPubKeyA, ptc);
  1362. }
  1363. if (hPubKeyB)
  1364. {
  1365. TDestroyKey(hPubKeyB, ptc);
  1366. }
  1367. if (hHash)
  1368. {
  1369. TDestroyHash(hHash, ptc);
  1370. }
  1371. if (pKeyExchangeState->dbCipherTextB.pbData)
  1372. {
  1373. free(pKeyExchangeState->dbCipherTextB.pbData);
  1374. }
  1375. if (pKeyExchangeState->dbPubKeyA.pbData)
  1376. {
  1377. free(pKeyExchangeState->dbPubKeyA.pbData);
  1378. }
  1379. if (pKeyExchangeState->dbPubKeyB.pbData)
  1380. {
  1381. free(pKeyExchangeState->dbPubKeyB.pbData);
  1382. }
  1383. if (pKeyExchangeState->dbSignatureB.pbData)
  1384. {
  1385. free(pKeyExchangeState->dbSignatureB.pbData);
  1386. }
  1387. if (pKeyExchangeState->dbEncryptedSessionKeyB.pbData)
  1388. {
  1389. free(pKeyExchangeState->dbEncryptedSessionKeyB.pbData);
  1390. }
  1391. return fSuccess;
  1392. }