Fwd: Donation of source code for testing Windows CSP / KSP vs real implementation
Frank Morgner <[email protected]> Wed, 3 Jun 2026 11:59:37 +0200
| Newsgroups | gmane.comp.encryption.opensc.devel |
|---|---|
| Message-ID | <[email protected]> |
This is a multi-part message in MIME format. --------------CMpnZc4mniyJIb4rYpAh0MRF Content-Type: multipart/alternative; boundary="------------sDCQ0TKKR5Rkkuu0Tz4q9ivA" --------------sDCQ0TKKR5Rkkuu0Tz4q9ivA Content-Type: text/plain; charset=UTF-8; format=flowed Content-Transfer-Encoding: 8bit Hi all! please find below a message from our friend Vincent Le Toux, who has difficulties accessing the mailing list. Best regards, Frank. ------------------------------------------------------------------------ *Von:* Vincent Le Toux <[email protected]> *Gesendet:* Freitag, 15. Mai 2026 15:43 *Betreff:* Fwd: Donation of source code for testing Windows CSP / KSP vs real implementation Hi, Maybe you can forward to the opensc mailing as I'm not allowed (anymore ?) to post on it. (still receiving the messages) br Vincent LE TOUX ---------- Forwarded message --------- De : *Vincent Le Toux* <[email protected]> Date: ven. 15 mai 2026 à  15:40 Subject: Donation of source code for testing Windows CSP / KSP vs real implementation To: OpenSC-devel <[email protected]> Hello, Testing an implementation of a smart card in a windows environment is quite difficult. Note: Franck has shared in the past an implementation of java cards applet using jcardsim. While I'm slowly recovering from some health issues, I've tested one of my GIDS implementations with the windows KSP / CSP. I'm calling in my unit tests the Windows Base CSP & KSP which then calls my hooked implementation of SCard* functions which I redirect them to my implementation. This is far easier than copy-pasting the APDU in unit tests. I don't think OpenSC can directly benefit from it, but you never know. What I'm sharing: - how to hook and unhook SCard function in the Windows Base CSP / KSP and minidriver (import table and delay library) - the SCard Implementation (Initially written by IA but tested by me manually) And also ... How to provide the PIN at container creation without a dialog (you can pass the PIN to sign / decrypt but not for container creation) => I'm hooking CredUIPromptForCredentials to provide my hardcoded PIN No IA knows how to do that properly. Maybe this can be adapted to test the OpenSC minidriver out of the usual cmck.exe test program. Just replace the dll name in HookGetCardTypeProviderNameW for that purpose and map SCardTransmit to a smart card implementation. Also I noted a pain point during my unit tests: set a different cardid for each test to avoid any cache issues. Hope that'll help someone in the future. br -- --- Vincent --------------sDCQ0TKKR5Rkkuu0Tz4q9ivA Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: 8bit <!DOCTYPE html> <html> <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> </head> <body dir="ltr"> <div class="moz-forward-container">Hi all!</div> <div class="moz-forward-container"><br> </div> <div class="moz-forward-container">please find below a message from our friend Vincent Le Toux, who has difficulties accessing the mailing list.</div> <div class="moz-forward-container"><br> </div> <div class="moz-forward-container">Best regards,</div> <div class="moz-forward-container">Frank.<br> <style type="text/css" style="display:none;">P {margin-top:0;margin-bottom:0;}</style> <div style="font-family: Aptos, Aptos_EmbeddedFont, Aptos_MSFontService, Calibri, Helvetica, sans-serif; font-size: 12pt; color: rgb(0, 0, 0);" class="elementToProof"> <br> </div> <hr style="display:inline-block;width:98%" tabindex="-1"> <div id="divRplyFwdMsg" dir="ltr"><font face="Calibri, sans-serif" style="font-size:11pt" color="#000000"><b>Von:</b> Vincent Le Toux <a class="moz-txt-link-rfc2396E" href="mailto:[email protected]"><[email protected]></a><br> <b>Gesendet:</b> Freitag, 15. Mai 2026 15:43<br> <b>Betreff:</b> Fwd: Donation of source code for testing Windows CSP / KSP vs real implementation</font> <div> </div> </div> <div> <div dir="ltr"> <div>Hi,</div> <div><br> </div> <div>Maybe you can forward to the opensc mailing as I'm not allowed (anymore ?) to post on it.</div> <div>(still receiving the messages)</div> <div><br> </div> <div>br</div> <div>Vincent LE TOUX</div> <div dir="ltr"><br> <div class="x_gmail_quote"> <div dir="ltr" class="x_gmail_attr">---------- Forwarded message ---------<br> De : <b class="x_gmail_sendername" dir="auto">Vincent Le Toux</b> <span dir="auto"> <<a href="mailto:[email protected]" target="_blank" moz-do-not-send="true" class="moz-txt-link-freetext">[email protected]</a>></span><br> Date: ven. 15 mai 2026 à  15:40<br> Subject: Donation of source code for testing Windows CSP / KSP vs real implementation<br> To: OpenSC-devel <<a href="mailto:[email protected]" target="_blank" moz-do-not-send="true" class="moz-txt-link-freetext">[email protected]</a>><br> </div> <br> <br> <div dir="ltr"> <div>Hello,</div> <div><br> </div> <div>Testing an implementation of a smart card in a windows environment is quite difficult.</div> <div>Note: Franck has shared in the past an implementation of java cards applet using jcardsim.</div> <div><br> </div> <div>While I'm slowly recovering from some health issues, I've tested one of my GIDS implementations with the windows KSP / CSP.</div> <div>I'm calling in my unit tests the Windows Base CSP & KSP which then calls my hooked implementation of SCard* functions which I redirect them to my implementation.</div> <div>This is far easier than copy-pasting the APDU in unit tests.</div> <div><br> </div> <div>I don't think OpenSC can directly benefit from it, but you never know.</div> <div><br> </div> <div>What I'm sharing:</div> <div>- how to hook and unhook SCard function in the Windows Base CSP / KSP and minidriver</div> <div>(import table and delay library)</div> <div>- the SCard Implementation (Initially written by IA but tested by me manually)</div> <div>And also ...</div> <div>How to provide the PIN at container creation without a dialog</div> <div>(you can pass the PIN to sign / decrypt but not for container creation)</div> <div>=> I'm hooking CredUIPromptForCredentials to provide my hardcoded PIN</div> <div>No IA knows how to do that properly.</div> <div><br> </div> <div>Maybe this can be adapted to test the OpenSC minidriver out of the usual cmck.exe test program. Just replace the dll name in HookGetCardTypeProviderNameW for that purpose and map SCardTransmit to a smart card implementation.</div> <div><br> </div> <div>Also I noted a pain point during my unit tests: set a different cardid for each test to avoid any cache issues.</div> <div><br> </div> <div>Hope that'll help someone in the future.</div> <div><br> </div> br<br> <div><span class="x_gmail_signature_prefix">-- </span><br> <div dir="ltr" class="x_gmail_signature" data-smartmail="gmail_signature">---<br> Vincent</div> </div> </div> </div> </div> </div> </div> </div> </body> </html> --------------sDCQ0TKKR5Rkkuu0Tz4q9ivA-- --------------CMpnZc4mniyJIb4rYpAh0MRF Content-Type: text/x-c-code; charset=windows-1252; name="KSPTests.cpp" Content-Disposition: attachment; filename="KSPTests.cpp" Content-Transfer-Encoding: 8bit #include "pch.h" #include <ncrypt.h> #include <CppUnitTest.h> #include <vector> #include <algorithm> #include "MsCryptoBase.h" #pragma comment(lib, "ncrypt") using namespace Microsoft::VisualStudio::CppUnitTestFramework; // Configuration des tests #define TEST_PIN L"12345678" #ifndef SCARD_PROVIDER_CARD_MODULE #define SCARD_PROVIDER_CARD_MODULE 0x80000001 #endif namespace GidsTests { TEST_CLASS(KspTests), public MsCryptoBase { TEST_METHOD_INITIALIZE(SetUp) { Trace(TRACE_LEVEL_INFO, L"--- [SetUp] setup KSP tests ---"); TraceOff(); setUp(); createcard(); setUpHook(); TraceOn(); } TEST_METHOD_CLEANUP(TearDown) { Trace(TRACE_LEVEL_INFO, L"--- [TearDown] Cleaning KSP tests ---"); TraceOff(); tearDownHook(); tearDown(); TraceOn(); } public: // 1. ENUMERATE EMPTY CARD TEST_METHOD(Test_KSP_1_EmptyEnumerate) { Trace(TRACE_LEVEL_INFO, L"[Test] 1. Enumerate Keys"); NCRYPT_PROV_HANDLE hProv = NULL; SECURITY_STATUS status = NCryptOpenStorageProvider(&hProv, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] NCryptOpenStorageProvider failed: 0x%08X", status); Assert::Fail(L"Échec ouverture Provider."); } NCryptKeyName* pKey = NULL; PVOID pState = NULL; Trace(TRACE_LEVEL_INFO, L" [Info] Début énumération..."); while ((status = NCryptEnumKeys(hProv, NULL, &pKey, &pState, 0)) == ERROR_SUCCESS) { Trace(TRACE_LEVEL_INFO, L" [Key Found] %s", pKey->pszName); NCryptFreeBuffer(pKey); } if (status != (SECURITY_STATUS)NTE_NO_MORE_ITEMS) { Trace(TRACE_LEVEL_ERROR, L" [!] NCryptEnumKeys erreur inattendue: 0x%08X", status); if (pState) NCryptFreeBuffer(pState); NCryptFreeObject(hProv); Assert::Fail(L"Échec durant l'énumération."); } Trace(TRACE_LEVEL_INFO, L" [OK] Fin d'énumération nominale."); if (pState) NCryptFreeBuffer(pState); NCryptFreeObject(hProv); } // 2. CAPABILITIES (DYNAMIC LENGTHS) TEST_METHOD(Test_KSP_2_Capabilities) { Trace(TRACE_LEVEL_INFO, L"[Test] 2. KSP Capabilities"); NCRYPT_PROV_HANDLE hProv = NULL; NCRYPT_KEY_HANDLE hTempKey = 0; SECURITY_STATUS status = NCryptOpenStorageProvider(&hProv, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Open failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } // not persisted ! status = NCryptCreatePersistedKey(hProv, &hTempKey, NCRYPT_RSA_ALGORITHM, NULL, 0, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] NCryptCreatePersistedKey failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } NCRYPT_SUPPORTED_LENGTHS len; DWORD cb; status = NCryptGetProperty(hTempKey, NCRYPT_LENGTHS_PROPERTY, (BYTE*)&len, sizeof(len), &cb, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] GetProperty(LENGTHS) failed: 0x%08X", status); NCryptFreeObject(hTempKey); NCryptFreeObject(hProv); Assert::AreEqual(ERROR_SUCCESS, status); } Trace(TRACE_LEVEL_INFO, L" [Caps] RSA: Min=%d, Max=%d, Step=%d", len.dwMinLength, len.dwMaxLength, len.dwIncrement); NCryptFreeObject(hTempKey); NCryptFreeObject(hProv); } // 3. RSA MATRIX (All Detected Sizes) TEST_METHOD(Test_KSP_3_RSAMatrix) { Trace(TRACE_LEVEL_INFO, L"[Test] 3. RSA Matrix"); NCRYPT_PROV_HANDLE hProv = NULL; NCRYPT_KEY_HANDLE hTempKey = 0; SECURITY_STATUS status = NCryptOpenStorageProvider(&hProv, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Open failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } // not persisted ! status = NCryptCreatePersistedKey(hProv, &hTempKey, NCRYPT_RSA_ALGORITHM, NULL, 0, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] NCryptCreatePersistedKey failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } NCRYPT_SUPPORTED_LENGTHS len; DWORD cb; status = NCryptGetProperty(hTempKey, NCRYPT_LENGTHS_PROPERTY, (BYTE*)&len, sizeof(len), &cb, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] GetProperty(LENGTHS) failed: 0x%08X", status); NCryptFreeObject(hTempKey); NCryptFreeObject(hProv); Assert::AreEqual(ERROR_SUCCESS, status); } NCryptFreeObject(hTempKey); for (DWORD dwSize = 1024; dwSize <= 2048; dwSize += 1024) { NCRYPT_KEY_HANDLE hKey = NULL; Trace(TRACE_LEVEL_INFO, L" [Action] Création clé RSA %d bits", dwSize); status = NCryptCreatePersistedKey(hProv, &hKey, BCRYPT_RSA_ALGORITHM, L"KspMatrix", 0, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Create failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } DWORD dwKeySize = dwSize; status = NCryptSetProperty(hKey, NCRYPT_LENGTH_PROPERTY, (BYTE*)&dwKeySize, sizeof(dwKeySize), 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] KeySize set failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } Trace(TRACE_LEVEL_INFO, L" [Action] Injection PIN..."); status = NCryptSetProperty(hKey, NCRYPT_PIN_PROPERTY, (BYTE*)TEST_PIN, (DWORD)(wcslen(TEST_PIN) + 1) * sizeof(wchar_t), 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] PIN set failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } status = NCryptFinalizeKey(hKey, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Finalize failed (%d bits): 0x%08X", dwSize, status); NCryptFreeObject(hKey); Assert::AreEqual(ERROR_SUCCESS, status); } Trace(TRACE_LEVEL_INFO, L" [Action] Suppression clé %d bits", dwSize); status = NCryptDeleteKey(hKey, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Delete failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } } NCryptFreeObject(hProv); } // 4. SIGNATURE MATRIX (All Hash Algos + PSS/PKCS1) TEST_METHOD(Test_KSP_4_SignatureMatrix) { Trace(TRACE_LEVEL_INFO, L"[Test] 4. Signature Matrix"); struct HashInfo { const wchar_t* pszAlg; DWORD cbHash; }; static const HashInfo hashes[] = { { BCRYPT_SHA1_ALGORITHM, 20 }, { BCRYPT_SHA256_ALGORITHM, 32 }, { BCRYPT_SHA384_ALGORITHM, 48 }, { BCRYPT_SHA512_ALGORITHM, 64 }, }; NCRYPT_PROV_HANDLE hP = NULL; NCRYPT_KEY_HANDLE hK = NULL; SECURITY_STATUS status; status = NCryptOpenStorageProvider(&hP, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0); if (status != ERROR_SUCCESS) { Assert::AreEqual(ERROR_SUCCESS, status); } status = NCryptCreatePersistedKey(hP, &hK, BCRYPT_RSA_ALGORITHM, L"KspSignMatrix", 0, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Create failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } DWORD dwKeySize = 2048; status = NCryptSetProperty(hK, NCRYPT_LENGTH_PROPERTY, (BYTE*)&dwKeySize, sizeof(dwKeySize), 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] KeySize set failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } Trace(TRACE_LEVEL_INFO, L" [Action] Injection PIN..."); status = NCryptSetProperty(hK, NCRYPT_PIN_PROPERTY, (BYTE*)TEST_PIN, (DWORD)(wcslen(TEST_PIN) + 1) * sizeof(wchar_t), 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] PIN set failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } status = NCryptFinalizeKey(hK, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Finalize failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } for (auto& h : hashes) { BYTE hash[64] = {}; memset(hash, 0x01, h.cbHash); // fake hash de la bonne taille BYTE sig[512] = {}; DWORD cbSig = 0; Trace(TRACE_LEVEL_INFO, L" [Sign] Hash: %s (%d bytes)", h.pszAlg, h.cbHash); // PKCS1 BCRYPT_PKCS1_PADDING_INFO pkcs1 = { h.pszAlg }; status = NCryptSignHash(hK, &pkcs1, hash, h.cbHash, sig, sizeof(sig), &cbSig, NCRYPT_PAD_PKCS1_FLAG); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] PKCS1 %s failed: 0x%08X", h.pszAlg, status); Assert::AreEqual(ERROR_SUCCESS, status); } status = NCryptVerifySignature(hK, &pkcs1, hash, h.cbHash, sig, cbSig, NCRYPT_PAD_PKCS1_FLAG); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] PKCS1 %s failed: 0x%08X", h.pszAlg, status); Assert::AreEqual(ERROR_SUCCESS, status); } Trace(TRACE_LEVEL_INFO, L" [OK] PKCS1 sig: %d bytes", cbSig); /*BCRYPT_OAEP_PADDING_INFO oaep = { h.pszAlg, NULL, NULL }; status = NCryptSignHash(hK, &oaep, hash, h.cbHash, sig, sizeof(sig), &cbSig, NCRYPT_PAD_OAEP_FLAG); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] OEAP %s failed: 0x%08X", h.pszAlg, status); Assert::AreEqual(ERROR_SUCCESS, status); } status = NCryptVerifySignature(hK, &oaep, hash, h.cbHash, sig, cbSig, NCRYPT_PAD_OAEP_FLAG); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] OEAP %s failed: 0x%08X", h.pszAlg, status); Assert::AreEqual(ERROR_SUCCESS, status); } Trace(TRACE_LEVEL_INFO, L" [OK] OEAP sig: %d bytes", cbSig);*/ } NCryptDeleteKey(hK, 0); NCryptFreeObject(hP); } //// 5. MODE RAW (NCRYPT_NO_PADDING_FLAG) //TEST_METHOD(Test_KSP_5_RawSign) { // Trace(TRACE_LEVEL_INFO, L"[Test] 5. RAW Sign"); // NCRYPT_PROV_HANDLE hP = NULL; NCRYPT_KEY_HANDLE hK = NULL; // SECURITY_STATUS status = NCryptOpenStorageProvider(&hP, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0); // if (status != ERROR_SUCCESS) { // Trace(TRACE_LEVEL_ERROR, L" [!] Open failed: 0x%08X", status); // Assert::AreEqual(ERROR_SUCCESS, status); // } // status = NCryptCreatePersistedKey(hP, &hK, BCRYPT_RSA_ALGORITHM, L"KspRaw", 0, 0); // if (status != ERROR_SUCCESS) { // Trace(TRACE_LEVEL_ERROR, L" [!] Create failed: 0x%08X", status); // Assert::AreEqual(ERROR_SUCCESS, status); // } // DWORD dwKeySize = 2048; // status = NCryptSetProperty(hK, NCRYPT_LENGTH_PROPERTY, // (BYTE*)&dwKeySize, sizeof(dwKeySize), 0); // if (status != ERROR_SUCCESS) { // Trace(TRACE_LEVEL_ERROR, L" [!] KeySize set failed: 0x%08X", status); // Assert::AreEqual(ERROR_SUCCESS, status); // } // status = NCryptFinalizeKey(hK, 0); // if (status != ERROR_SUCCESS) // { // Trace(TRACE_LEVEL_ERROR, L" [!] Finalize failed: 0x%08X", status); // Assert::AreEqual(ERROR_SUCCESS, status); // } // BYTE data[32] = { 0xEE }, sig[2048/8]; // DWORD cbSig = sizeof(sig); // Trace(TRACE_LEVEL_INFO, L" [Action] Sign RAW..."); // status = NCryptSignHash(hK, NULL, data, sizeof(data), sig, cbSig, &cbSig, NCRYPT_NO_PADDING_FLAG); // if (status != ERROR_SUCCESS) // { // Trace(TRACE_LEVEL_ERROR, L" [!] RAW Sign failed: 0x%08X", status); // Assert::AreEqual(ERROR_SUCCESS, status); // } // status = NCryptVerifySignature(hK, NULL, data, sizeof(data), sig, cbSig, NCRYPT_NO_PADDING_FLAG); // if (status != ERROR_SUCCESS) { // Trace(TRACE_LEVEL_ERROR, L" [!] RAW Verify failed: 0x%08X", status); // Assert::AreEqual(ERROR_SUCCESS, status); // } // NCryptDeleteKey(hK, 0); // NCryptFreeObject(hP); //} // 6. CRYPT / DECRYPT TEST_METHOD(Test_KSP_6_CryptDecrypt) { Trace(TRACE_LEVEL_INFO, L"[Test] 6. Crypt/Decrypt"); NCRYPT_PROV_HANDLE hP = NULL; NCRYPT_KEY_HANDLE hK = NULL; SECURITY_STATUS status = NCryptOpenStorageProvider(&hP, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Open failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } status = NCryptCreatePersistedKey(hP, &hK, BCRYPT_RSA_ALGORITHM, L"KspCrypt", 0, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Create failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } DWORD dwKeySize = 2048; status = NCryptSetProperty(hK, NCRYPT_LENGTH_PROPERTY, (BYTE*)&dwKeySize, sizeof(dwKeySize), 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] KeySize set failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } status = NCryptFinalizeKey(hK, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Finalize failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } BYTE plain[32] = { 0x55 }, cipher[512], output[512]; DWORD cbC, cbO; Trace(TRACE_LEVEL_INFO, L" [Action] Encryption..."); status = NCryptEncrypt(hK, plain, 32, NULL, cipher, 512, &cbC, NCRYPT_PAD_PKCS1_FLAG); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Encrypt failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } Trace(TRACE_LEVEL_INFO, L" [Action] Decryption..."); status = NCryptDecrypt(hK, cipher, cbC, NULL, output, 512, &cbO, NCRYPT_PAD_PKCS1_FLAG); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Decrypt failed: 0x%08X", status); NCryptDeleteKey(hK, 0); Assert::AreEqual(ERROR_SUCCESS, status); } NCryptDeleteKey(hK, 0); NCryptFreeObject(hP); } // 7. CMAPFILE STRESS (Permutations) TEST_METHOD(Test_KSP_7_CMapStress) { Trace(TRACE_LEVEL_INFO, L"[Test] 7. CMap Stress"); NCRYPT_PROV_HANDLE hP = NULL; SECURITY_STATUS status = NCryptOpenStorageProvider(&hP, MS_SMART_CARD_KEY_STORAGE_PROVIDER, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Open failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } std::vector<std::wstring> names = { L"K1", L"K2", L"K3" }; std::vector<int> p = { 0, 1, 2 }; int iter = 0; do { iter++; Trace(TRACE_LEVEL_INFO, L" [Stress] Iteration %d", iter); for (int i : p) { NCRYPT_KEY_HANDLE k = NULL; status = NCryptCreatePersistedKey(hP, &k, BCRYPT_RSA_ALGORITHM, names[i].c_str(), 0, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Create failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } status = NCryptFinalizeKey(k, 0); if (status != ERROR_SUCCESS) { Trace(TRACE_LEVEL_ERROR, L" [!] Finalize failed: 0x%08X", status); Assert::AreEqual(ERROR_SUCCESS, status); } NCryptFreeObject(k); } for (int i : p) { NCRYPT_KEY_HANDLE k = NULL; status = NCryptOpenKey(hP, &k, names[i].c_str(), 0, 0); if (status == ERROR_SUCCESS) { NCryptDeleteKey(k, 0); } else { Trace(TRACE_LEVEL_ERROR, L" [!] Failed to re-open %s: 0x%08X", names[i].c_str(), status); Assert::AreEqual(ERROR_SUCCESS, status); } } } while (std::next_permutation(p.begin(), p.end())); NCryptFreeObject(hP); Trace(TRACE_LEVEL_INFO, L" [OK] Stress test terminé."); } }; } --------------CMpnZc4mniyJIb4rYpAh0MRF Content-Type: text/x-c-code; charset=windows-1252; name="CSPTests.cpp" Content-Disposition: attachment; filename="CSPTests.cpp" Content-Transfer-Encoding: 8bit #include "pch.h" #include <wincrypt.h> #include <ncrypt.h> #include <CppUnitTest.h> #include <vector> #include <algorithm> #include "mscryptobase.h" #pragma comment(lib, "crypt32") #pragma comment(lib, "ncrypt") using namespace Microsoft::VisualStudio::CppUnitTestFramework; #define TEST_PIN L"123456078" namespace GidsTests { TEST_CLASS(CspTests), public MsCryptoBase { private: /** * @brief Génère un conteneur sur une carte à puce via le BaseCSP. * * @param phProv [Out] Handle vers le fournisseur de services cryptographiques. * @param pszCont [In] Nom du conteneur (peut être NULL pour le conteneur par défaut). * @param dwKeySize [In] Taille de la clé (ex: 2048). * @return BOOL TRUE si succès, FALSE sinon. */ BOOL GenerateContainerWithPin(HCRYPTPROV * phProv, LPCWSTR pszCont, DWORD dwKeySize) { DWORD dwFlags = CRYPT_NEWKEYSET; // Création d'un nouveau conteneur // 1. Acquisition du contexte // On utilise MS_SMART_CARD_PROV pour forcer l'usage de la carte à puce via le BaseCSP if (!CryptAcquireContextW( phProv, pszCont, MS_SCARD_PROV, PROV_RSA_FULL, dwFlags)) { DWORD err = GetLastError(); // Si le conteneur existe déjà, on tente de l'ouvrir sans le flag NEWKEYSET if (err == NTE_EXISTS) { Trace(TRACE_LEVEL_WARNING, L"Conteneur existant, ouverture simple..."); if (!CryptAcquireContextW(phProv, pszCont, MS_SCARD_PROV, PROV_RSA_FULL, 0)) { Trace(TRACE_LEVEL_ERROR, L"Erreur CryptAcquireContext (Open): 0x%08X", GetLastError()); return FALSE; } } else { Trace(TRACE_LEVEL_ERROR, L"Erreur CryptAcquireContext (New): 0x%08X", err); return FALSE; } } Trace(TRACE_LEVEL_INFO, L"Contexte acquis pour le conteneur: %s", pszCont ? pszCont : L"Default"); // 2. Génération de la paire de clés (RSA) // Le flag CRYPT_EXPORTABLE est souvent ignoré par les Smart Cards pour des raisons de sécurité. // dwKeySize est passé dans les 16 bits de poids fort. DWORD dwGenFlags = (dwKeySize << 16); HCRYPTKEY hKey = NULL; if (!CryptGenKey(*phProv, AT_KEYEXCHANGE, dwGenFlags, &hKey)) { DWORD err = GetLastError(); Trace(TRACE_LEVEL_ERROR, L"Erreur CryptGenKey: 0x%08X", err); // Nettoyage en cas d'échec CryptReleaseContext(*phProv, 0); *phProv = NULL; return FALSE; } Trace(TRACE_LEVEL_INFO, L"Paire de clés de %u bits générée avec succès.", dwKeySize); // 3. Nettoyage du handle de clé (on garde le handle du provider phProv) CryptDestroyKey(hKey); return TRUE; } TEST_METHOD_INITIALIZE(SetUp) { Trace(TRACE_LEVEL_INFO, L"--- [SetUp] setup CSP tests ---"); //TraceOff(); setUp(); createcard(); setUpHook(); TraceOn(); } TEST_METHOD_CLEANUP(TearDown) { Trace(TRACE_LEVEL_INFO, L"--- [TearDown] Cleaning CSP test ---"); //TraceOff(); tearDownHook(); tearDown(); TraceOn(); } // 1. ENUMERATE EMPTY CARD TEST_METHOD(Test_CSP_1_EmptyEnumerate) { HCRYPTPROV hProv = NULL; char szContainer[MAX_PATH]; DWORD cb = MAX_PATH, dwFlags = CRYPT_FIRST, dwCount = 0; if (!CryptAcquireContextW(&hProv, NULL, MS_SCARD_PROV, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { Trace(TRACE_LEVEL_ERROR, L" [!] Acquire Verify failed: 0x%08X", GetLastError()); Assert::Fail(); } while (CryptGetProvParam(hProv, PP_ENUMCONTAINERS, (BYTE*)szContainer, &cb, dwFlags)) { dwFlags = 0; cb = MAX_PATH; dwCount++; } CryptReleaseContext(hProv, 0); } // 3. RSA MATRIX TEST_METHOD(Test_CSP_3_RSAMatrix) { const wchar_t* pszCont = L"RSA_Matrix_Test"; std::vector<DWORD> sizes = { 1024, 2048 }; for (DWORD dwLen : sizes) { HCRYPTPROV hCont = NULL; HCRYPTKEY hKey = NULL; Trace(TRACE_LEVEL_INFO, L"--- Testing RSA size: %d bits ---", dwLen); if (!GenerateContainerWithPin(&hCont, pszCont, dwLen)) { Assert::Fail(L"Generation failed."); } CERT_NAME_BLOB sib = { 0 }; LPCWSTR pszSubject = L"CN=Test"; if (!CertStrToNameW(X509_ASN_ENCODING, pszSubject, CERT_X500_NAME_STR, NULL, NULL, &sib.cbData, NULL)) { Trace(TRACE_LEVEL_ERROR, L" [!] CertStrToName size failed: 0x%08X", GetLastError()); CryptReleaseContext(hCont, 0); Assert::Fail(); } sib.pbData = (BYTE*)malloc(sib.cbData); if (!CertStrToNameW(X509_ASN_ENCODING, pszSubject, CERT_X500_NAME_STR, NULL, sib.pbData, &sib.cbData, NULL)) { Trace(TRACE_LEVEL_ERROR, L" [!] CertStrToName encode failed: 0x%08X", GetLastError()); free(sib.pbData); CryptReleaseContext(hCont, 0); Assert::Fail(); } CRYPT_KEY_PROV_INFO KeyProvInfo = { 0 }; KeyProvInfo.pwszContainerName = (PWSTR)pszCont; KeyProvInfo.pwszProvName = (LPWSTR)MS_SCARD_PROV; // Ou MS_SMART_CARD_PROV KeyProvInfo.dwProvType = PROV_RSA_FULL; KeyProvInfo.dwFlags = 0; KeyProvInfo.cProvParam = 0; KeyProvInfo.rgProvParam = NULL; KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE; PCCERT_CONTEXT pCert = CertCreateSelfSignCertificate(hCont, &sib, 0, &KeyProvInfo, NULL, NULL, NULL, NULL); free(sib.pbData); if (!pCert) { DWORD dwErr = GetLastError(); Trace(TRACE_LEVEL_ERROR, L" [!] SelfSign failed: 0x%08X", dwErr); CryptReleaseContext(hCont, 0); Assert::Fail(); } if (!CryptGetUserKey(hCont, AT_KEYEXCHANGE, &hKey)) { DWORD dwErr = GetLastError(); Trace(TRACE_LEVEL_ERROR, L" [!] CryptGetUserKey failed: 0x%08X", dwErr); CryptReleaseContext(hCont, 0); Assert::Fail(); } if (!CryptSetKeyParam( hKey, KP_CERTIFICATE, pCert->pbCertEncoded, 0)) { DWORD err = GetLastError(); Trace(TRACE_LEVEL_ERROR, L"Erreur CryptSetKeyParam(KP_CERTIFICATE): 0x%08X", err); } CertFreeCertificateContext(pCert); CryptDestroyKey(hKey); CryptReleaseContext(hCont, 0); CryptAcquireContextW(&hCont, pszCont, MS_SCARD_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET); } } // 4. SIGNATURE MATRIX TEST_METHOD(Test_CSP_4_SignatureMatrix) { HCRYPTPROV hProv = NULL; ALG_ID hashAlgs[] = { CALG_SHA1, CALG_SHA_256, CALG_SHA_512 }; const wchar_t* pszCont = L"Sign_Matrix_Test"; if (!GenerateContainerWithPin(&hProv, pszCont, 2048)) { Assert::Fail(); } for (ALG_ID algId : hashAlgs) { HCRYPTHASH hHash = NULL; if (!CryptCreateHash(hProv, algId, 0, 0, &hHash)) { Trace(TRACE_LEVEL_ERROR, L" [!] Hash init failed: 0x%08X", GetLastError()); Assert::Fail(); } BYTE sig[256]; DWORD cbSig = sizeof(sig); if (!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, 0, sig, &cbSig)) { DWORD dwErr = GetLastError(); Trace(TRACE_LEVEL_ERROR, L" [!] Sign failed for 0x%X: 0x%08X", algId, dwErr); CryptDestroyHash(hHash); Assert::Fail(); } Trace(TRACE_LEVEL_INFO, L" [OK] Signature success (0x%X)", algId); CryptDestroyHash(hHash); } CryptReleaseContext(hProv, 0); CryptAcquireContextW(&hProv, pszCont, MS_SCARD_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET); } // 5. MODE RAW TEST_METHOD(Test_CSP_5_RawSign) { HCRYPTPROV hProv = NULL; const wchar_t* pszCont = L"Raw_Test"; HCRYPTKEY hKey = NULL; if (!GenerateContainerWithPin(&hProv, pszCont, 2048)) { Assert::Fail(); } if (!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKey)) { Trace(TRACE_LEVEL_ERROR, L" [!] CryptGetUserKey failed: 0x%08X", GetLastError()); Assert::Fail(); } for (DWORD dwI = 0; dwI < 3; dwI++) { HCRYPTHASH hHash = NULL; if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash)) { Trace(TRACE_LEVEL_ERROR, L" [!] CryptCreateHash failed: 0x%08X", GetLastError()); Assert::Fail(); } // 3. Injection d'une valeur de hash factice (32 octets pour SHA-256) BYTE mockHash[32] = { 0xAA, 0xBB, 0xCC, (BYTE) dwI }; // Le reste à 0 if (!CryptSetHashParam(hHash, HP_HASHVAL, mockHash, 0)) { Trace(TRACE_LEVEL_ERROR, L" [!] CryptSetHashParam failed: 0x%08X", GetLastError()); Assert::Fail(); } DWORD cbSig = 0; if (!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, CRYPT_NOHASHOID, NULL, &cbSig)) { Trace(TRACE_LEVEL_INFO, L" [Info] RAW probe: 0x%08X, size: %d", GetLastError(), cbSig); Assert::Fail(); } std::vector<BYTE> signature(cbSig); if (!CryptSignHash(hHash, AT_KEYEXCHANGE, NULL, CRYPT_NOHASHOID, signature.data(), &cbSig)) { Trace(TRACE_LEVEL_ERROR, L" [!] CryptSignHash (sign) failed: 0x%08X", GetLastError()); Assert::Fail(); } if (!CryptVerifySignature(hHash, signature.data(), cbSig, hKey, NULL, CRYPT_NOHASHOID)) { Trace(TRACE_LEVEL_ERROR, L" [!] CryptVerifySignature (sign) failed: 0x%08X", GetLastError()); Assert::Fail(); } if (hHash) CryptDestroyHash(hHash); } if (hKey) CryptDestroyKey(hKey); CryptReleaseContext(hProv, 0); CryptAcquireContextW(&hProv, pszCont, MS_SCARD_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET); } // 6. CRYPT / DECRYPT TEST_METHOD(Test_CSP_6_CryptDecrypt) { HCRYPTPROV hProv = NULL; HCRYPTKEY hKey = NULL; const wchar_t* pszCont = L"Crypt_Test"; if (!GenerateContainerWithPin(&hProv, pszCont, 2048)) { Assert::Fail(); } if (!CryptGetUserKey(hProv, AT_KEYEXCHANGE, &hKey)) { Trace(TRACE_LEVEL_ERROR, L" [!] GetUserKey failed: 0x%08X", GetLastError()); Assert::Fail(); } BYTE data[256] = { 0x11, 0x22, 0x33, 0x44 }; DWORD cbData = 4; DWORD dwBufLen = 256; if (!CryptEncrypt(hKey, 0, TRUE, 0, data, &cbData, dwBufLen)) { Trace(TRACE_LEVEL_ERROR, L" [!] Encrypt failed: 0x%08X", GetLastError()); Assert::Fail(); } if (!CryptDecrypt(hKey, 0, TRUE, 0, data, &cbData)) { Trace(TRACE_LEVEL_ERROR, L" [!] Decrypt failed: 0x%08X", GetLastError()); Assert::Fail(); } Assert::AreEqual((DWORD)4, cbData); Assert::AreEqual((BYTE)0x11, data[0]); CryptDestroyKey(hKey); CryptReleaseContext(hProv, 0); CryptAcquireContextW(&hProv, pszCont, MS_SCARD_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET); } // 7. CMAPFILE STRESS TEST_METHOD(Test_CSP_7_CMapStress) { std::vector<std::wstring> names = { L"ST1", L"ST2", L"ST3", L"ST4" }; HCRYPTPROV h = NULL; for (auto& name : names) { if (!GenerateContainerWithPin(&h, name.c_str(), 1024)) Assert::Fail(); CryptReleaseContext(h, 0); } for (auto& name : names) { if (!CryptAcquireContextW(&h, name.c_str(), MS_SCARD_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET)) { Trace(TRACE_LEVEL_ERROR, L" [!] Delete failed: 0x%08X", GetLastError()); Assert::Fail(); } } } }; } --------------CMpnZc4mniyJIb4rYpAh0MRF Content-Type: text/x-c-code; charset=UTF-8; name="MsCryptoBase.cpp" Content-Disposition: attachment; filename="MsCryptoBase.cpp" Content-Transfer-Encoding: base64 77u/I2luY2x1ZGUgInBjaC5oIg0KI2luY2x1ZGUgIk1zQ3J5cHRvQmFzZS5oIg0KDQojaW5j bHVkZSA8bWFwPg0KI2luY2x1ZGUgPHZlY3Rvcj4NCiNpbmNsdWRlIDxzdHJpbmc+DQojaW5j bHVkZSA8bnRzZWNhcGkuaD4NCiNpbmNsdWRlIDxPYmpiYXNlLmg+DQojaW5jbHVkZSAiLi4v RUlEVmlydHVhbFNtYXJ0Q2FyZE1pbmlkcml2ZXJUZXN0L2NhcmRtb2QuaCINCg0KLy8gU3Ry dWN0dXJlIHBvdXIgc3RvY2tlciBsZXMgZG9ubsOpZXMgZHUgY2FjaGUNCnN0cnVjdCBDYWNo ZUVudHJ5IHsNCglzdGQ6OnZlY3RvcjxCWVRFPiBkYXRhOw0KfTsNCg0KLy8gU2ltdWxhdGlv biBkdSBjYWNoZSA6IENsw6kgPSBzekxvb2t1cE5hbWUgKHBvdXIgbGEgc2ltcGxpY2l0w6kg ZHUgdGVzdCkNCnN0YXRpYyBzdGQ6Om1hcDxzdGQ6OndzdHJpbmcsIENhY2hlRW50cnk+IGdf U2ltdWxhdGVkQ2FjaGU7DQoNCiNkZWZpbmUgVEhFQVRSIHsweDNCLDB4OEMsMHgwMSwweDRE LDB4NzksMHg1MywweDZELDB4NjEsMHg3MiwweDc0LDB4NEMsMHg2RiwweDY3LDB4NkYsMHg2 RSwweEE1fQ0KI2RlZmluZSBTQ0FSRF9QUk9WSURFUl9DQVJEX01PRFVMRSAweDgwMDAwMDAx DQoNCk1zQ3J5cHRvQmFzZSogZ19wU3RhdGljSW5zdGFuY2UgPSBudWxscHRyOw0KDQpIb29r RW50cnkgTXNDcnlwdG9CYXNlOjpzX0hvb2tzW10gPSB7DQoJeyJXaW5zY2FyZC5kbGwiLCAi U0NhcmRUcmFuc21pdCIsIChQUk9DKU1zQ3J5cHRvQmFzZTo6SG9va1RyYW5zbWl0LCBOVUxM LCBmYWxzZX0sDQoJeyJXaW5zY2FyZC5kbGwiLCAiU0NhcmRDb25uZWN0VyIsIChQUk9DKU1z Q3J5cHRvQmFzZTo6SG9va0Nvbm5lY3RXLCBOVUxMLCBmYWxzZX0sDQoJeyJXaW5zY2FyZC5k bGwiLCAiU0NhcmREaXNjb25uZWN0IiwgKFBST0MpTXNDcnlwdG9CYXNlOjpIb29rRGlzY29u bmVjdCwgTlVMTCwgZmFsc2V9LA0KCXsiV2luc2NhcmQuZGxsIiwgIlNDYXJkU3RhdHVzVyIs IChQUk9DKU1zQ3J5cHRvQmFzZTo6SG9va1N0YXR1c1csIE5VTEwsIGZhbHNlfSwNCgl7Ildp bnNjYXJkLmRsbCIsICJTQ2FyZExpc3RSZWFkZXJzVyIsIChQUk9DKU1zQ3J5cHRvQmFzZTo6 SG9va0xpc3RSZWFkZXJzVywgTlVMTCwgZmFsc2V9LA0KCXsiV2luc2NhcmQuZGxsIiwgIlND YXJkQmVnaW5UcmFuc2FjdGlvbiIsIChQUk9DKU1zQ3J5cHRvQmFzZTo6SG9va0JlZ2luVHJh bnMsIE5VTEwsIGZhbHNlfSwNCgl7IldpbnNjYXJkLmRsbCIsICJTQ2FyZEVuZFRyYW5zYWN0 aW9uIiwgKFBST0MpTXNDcnlwdG9CYXNlOjpIb29rRW5kVHJhbnMsIE5VTEwsIGZhbHNlfSwN Cgl7IldpbnNjYXJkLmRsbCIsICJTQ2FyZENvbnRyb2wiLCAoUFJPQylNc0NyeXB0b0Jhc2U6 Okhvb2tDb250cm9sLCBOVUxMLCBmYWxzZX0sDQoJeyJXaW5zY2FyZC5kbGwiLCAiU0NhcmRF c3RhYmxpc2hDb250ZXh0IiwgKFBST0MpTXNDcnlwdG9CYXNlOjpIb29rRXN0YWJsaXNoQ29u dGV4dCwgTlVMTCwgZmFsc2V9LA0KCXsiV2luc2NhcmQuZGxsIiwgIlNDYXJkUmVsZWFzZUNv bnRleHQiLCAoUFJPQylNc0NyeXB0b0Jhc2U6Okhvb2tSZWxlYXNlQ29udGV4dCwgTlVMTCwg ZmFsc2V9LA0KCXsiV2luc2NhcmQuZGxsIiwgIlNDYXJkR2V0U3RhdHVzQ2hhbmdlVyIsIChQ Uk9DKU1zQ3J5cHRvQmFzZTo6SG9va0dldFN0YXR1c0NoYW5nZVcsIE5VTEwsIGZhbHNlfSwN Cgl7IldpbnNjYXJkLmRsbCIsICJTQ2FyZEZyZWVNZW1vcnkiLCAoUFJPQylNc0NyeXB0b0Jh c2U6Okhvb2tGcmVlTWVtb3J5LCBOVUxMLCBmYWxzZX0sDQoJeyJXaW5zY2FyZC5kbGwiLCAi U0NhcmRDYW5jZWwiLCAoUFJPQylNc0NyeXB0b0Jhc2U6Okhvb2tDYW5jZWwsIE5VTEwsIGZh bHNlfSwNCgl7IldpbnNjYXJkLmRsbCIsICJTQ2FyZFJlY29ubmVjdCIsIChQUk9DKU1zQ3J5 cHRvQmFzZTo6SG9va1JlY29ubmVjdCwgTlVMTCwgZmFsc2V9LA0KCXsiV2luc2NhcmQuZGxs IiwgIlNDYXJkQXVkaXQiLCAoUFJPQylNc0NyeXB0b0Jhc2U6Okhvb2tBdWRpdCwgTlVMTCwg ZmFsc2V9LA0KCXsiV2luc2NhcmQuZGxsIiwgIlNDYXJkTGlzdENhcmRzVyIsIChQUk9DKU1z Q3J5cHRvQmFzZTo6SG9va0xpc3RDYXJkc1csIE5VTEwsIGZhbHNlfSwNCgl7IldpbnNjYXJk LmRsbCIsICJTQ2FyZFJlYWRDYWNoZVciLCAoUFJPQylNc0NyeXB0b0Jhc2U6Okhvb2tSZWFk Q2FjaGVXLCBOVUxMLCBmYWxzZX0sDQoJeyJXaW5zY2FyZC5kbGwiLCAiU0NhcmRXcml0ZUNh Y2hlVyIsIChQUk9DKU1zQ3J5cHRvQmFzZTo6SG9va1dyaXRlQ2FjaGVXLCBOVUxMLCBmYWxz ZX0sDQoJeyJXaW5zY2FyZC5kbGwiLCAiU0NhcmRHZXRDYXJkVHlwZVByb3ZpZGVyTmFtZVci LCAoUFJPQylNc0NyeXB0b0Jhc2U6Okhvb2tHZXRDYXJkVHlwZVByb3ZpZGVyTmFtZVcsIE5V TEwsIGZhbHNlfSwNCgl7IldpbnNjYXJkLmRsbCIsICJTQ2FyZEdldFJlYWRlckljb25XIiwg KFBST0MpTXNDcnlwdG9CYXNlOjpIb29rR2V0UmVhZGVySWNvblcsIE5VTEwsIGZhbHNlfSwN Cgl7IldpbnNjYXJkLmRsbCIsICJTQ2FyZEdldERldmljZVR5cGVJZFciLCAoUFJPQylNc0Ny eXB0b0Jhc2U6Okhvb2tHZXREZXZpY2VUeXBlSWRXLCBOVUxMLCBmYWxzZX0sDQoJeyJXaW5z Y2FyZC5kbGwiLCAiU0NhcmRBY2Nlc3NTdGFydGVkRXZlbnQiLCAoUFJPQylNc0NyeXB0b0Jh c2U6Okhvb2tBY2Nlc3NTdGFydGVkRXZlbnQsIE5VTEwsIGZhbHNlfSwNCgl7IldpbnNjYXJk LmRsbCIsICJTQ2FyZFJlbGVhc2VTdGFydGVkRXZlbnQiLCAoUFJPQylNc0NyeXB0b0Jhc2U6 Okhvb2tSZWxlYXNlU3RhcnRlZEV2ZW50LCBOVUxMLCBmYWxzZX0sDQoJeyJXaW5zY2FyZC5k bGwiLCAiU0NhcmRHZXRSZWFkZXJEZXZpY2VJbnN0YW5jZUlkVyIsIChQUk9DKU1zQ3J5cHRv QmFzZTo6SG9va0dldFJlYWRlckRldmljZUluc3RhbmNlSWRXLCBOVUxMLCBmYWxzZX0sDQoJ eyJXaW5zY2FyZC5kbGwiLCAiZ19yZ1NDYXJkVDBQY2kiLCBOVUxMLCBOVUxMLCB0cnVlfSwN Cgl7IldpbnNjYXJkLmRsbCIsICJnX3JnU0NhcmRUMVBjaSIsIE5VTEwsIE5VTEwsIHRydWV9 LA0KCXsiY3JlZHVpLmRsbCIsICAgIkNyZWRVSVByb21wdEZvcldpbmRvd3NDcmVkZW50aWFs c1ciLCAgKFBST0MpTXNDcnlwdG9CYXNlOjpIb29rQ3JlZFVJUHJvbXB0Rm9yV2luZG93c0Ny ZWRlbnRpYWxzLCBOVUxMLCBmYWxzZX0sDQp9Ow0KDQpNc0NyeXB0b0Jhc2UqIE1zQ3J5cHRv QmFzZTo6RnJvbUhhbmRsZShTQ0FSREhBTkRMRSBoKSB7DQoJDQoJTXNDcnlwdG9CYXNlKiBw b2ludGVyID0gcmVpbnRlcnByZXRfY2FzdDxNc0NyeXB0b0Jhc2UqPihoKTsNCglpZiAocG9p bnRlciAhPSBnX3BTdGF0aWNJbnN0YW5jZSkNCgl7DQoJCXJldHVybiBOVUxMOw0KCX0NCgly ZXR1cm4gcG9pbnRlcjsNCn0NCg0Kdm9pZCBNc0NyeXB0b0Jhc2U6OnNldFVwSG9vaygpIHsN CglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIj09PSBbTXNDcnlwdG9CYXNlXSBEw6lidXQg U0VUVVAgPT09Iik7DQoNCglmb3IgKGF1dG8mIGVudHJ5IDogc19Ib29rcykgew0KCQlITU9E VUxFIGhNb2QgPSBHZXRNb2R1bGVIYW5kbGVBKGVudHJ5LnBzekRsbE5hbWUpOw0KCQlpZiAo IWhNb2QpIGhNb2QgPSBMb2FkTGlicmFyeUEoZW50cnkucHN6RGxsTmFtZSk7DQoJCWlmICgh aE1vZCkgew0KCQkJVHJhY2UoVFJBQ0VfTEVWRUxfV0FSTklORywgTCIgICBbIV0gRExMIG5v biB0cm91dsOpZSA6ICVTIiwgZW50cnkucHN6RGxsTmFtZSk7DQoJCQljb250aW51ZTsNCgkJ fQ0KCQllbnRyeS5wZm5PcmlnaW5hbCA9IEdldFByb2NBZGRyZXNzKGhNb2QsIGVudHJ5LnBz ekZ1bmNOYW1lKTsNCgkJaWYgKCFlbnRyeS5wZm5PcmlnaW5hbCkNCgkJCVRyYWNlKFRSQUNF X0xFVkVMX1dBUk5JTkcsIEwiICAgWyFdIFN5bWJvbGUgbm9uIHRyb3V2w6kgOiAlUyElUyIs DQoJCQkJZW50cnkucHN6RGxsTmFtZSwgZW50cnkucHN6RnVuY05hbWUpOw0KCX0NCg0KCW1f aE1pbmlkcml2ZXIgPSBMb2FkTGlicmFyeShMIm1zY2xtZC5kbGwiKTsNCgltX2hCYXNlQ3Nw ID0gTG9hZExpYnJhcnkoTCJiYXNlY3NwLmRsbCIpOw0KCW1faEtzcCA9IExvYWRMaWJyYXJ5 KEwic2Nrc3AuZGxsIik7DQoNCglQYXRjaElBVChtX2hNaW5pZHJpdmVyLCBmYWxzZSk7DQoJ UGF0Y2hJQVQobV9oS3NwLCBmYWxzZSk7DQoJUGF0Y2hJQVQobV9oQmFzZUNzcCwgZmFsc2Up Ow0KDQoNCglnX3BTdGF0aWNJbnN0YW5jZSA9IHRoaXM7DQoJVHJhY2UoVFJBQ0VfTEVWRUxf SU5GTywgTCI9PT0gW01zQ3J5cHRvQmFzZV0gRmluIFNFVFVQID09PSIpOw0KfQ0KDQp2b2lk IE1zQ3J5cHRvQmFzZTo6dGVhckRvd25Ib29rKCkgew0KCVRyYWNlKFRSQUNFX0xFVkVMX0lO Rk8sIEwiPT09IFtNc0NyeXB0b0Jhc2VdIETDqWJ1dCBURUFSRE9XTiA9PT0iKTsNCglQYXRj aElBVChtX2hNaW5pZHJpdmVyLCB0cnVlKTsNCglQYXRjaElBVChtX2hCYXNlQ3NwLCB0cnVl KTsNCglQYXRjaElBVChtX2hLc3AsIHRydWUpOw0KCWdfcFN0YXRpY0luc3RhbmNlID0gbnVs bHB0cjsNCglnX1NpbXVsYXRlZENhY2hlLmNsZWFyKCk7DQoJLy8gVGVjaG5pcXVlIGRlICJz d2FwIiBwb3VyIGZvcmNlciBsYSBkw6lzYWxsb2NhdGlvbiBkZSBsYSBjYXBhY2l0w6kgZGUg bGEgbWFwDQoJc3RkOjptYXA8c3RkOjp3c3RyaW5nLCBDYWNoZUVudHJ5PigpLnN3YXAoZ19T aW11bGF0ZWRDYWNoZSk7DQp9DQoNCnZvaWQgTXNDcnlwdG9CYXNlOjpQYXRjaElBVChITU9E VUxFIGhUYXJnZXREbGwsIGJvb2wgcmVzdG9yZSkgew0KCWlmICghaFRhcmdldERsbCkgcmV0 dXJuOw0KDQoJV0NIQVIgc3pQYXRoW01BWF9QQVRIXTsNCglHZXRNb2R1bGVGaWxlTmFtZSho VGFyZ2V0RGxsLCBzelBhdGgsIE1BWF9QQVRIKTsNCglzdGQ6OndzdHJpbmcgZGxsTmFtZSA9 IHN6UGF0aDsNCglkbGxOYW1lID0gZGxsTmFtZS5zdWJzdHIoZGxsTmFtZS5maW5kX2xhc3Rf b2YoTCJcXCIpICsgMSk7DQoNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIi0tLSBBbmFs eXNlIElBVCA6ICVzIC0tLSIsIGRsbE5hbWUuY19zdHIoKSk7DQoNCglQSU1BR0VfRE9TX0hF QURFUiBwRG9zSGVhZGVyID0gKFBJTUFHRV9ET1NfSEVBREVSKWhUYXJnZXREbGw7DQoJUElN QUdFX05UX0hFQURFUlMgcE50SGVhZGVycyA9IChQSU1BR0VfTlRfSEVBREVSUykoKEJZVEUq KWhUYXJnZXREbGwgKyBwRG9zSGVhZGVyLT5lX2xmYW5ldyk7DQoNCgkvLyAxLiBTdGFuZGFy ZCBJbXBvcnRzDQoJRFdPUkQgaW1wb3J0QWRkciA9IHBOdEhlYWRlcnMtPk9wdGlvbmFsSGVh ZGVyLkRhdGFEaXJlY3RvcnlbSU1BR0VfRElSRUNUT1JZX0VOVFJZX0lNUE9SVF0uVmlydHVh bEFkZHJlc3M7DQoJaWYgKGltcG9ydEFkZHIpIHsNCgkJUElNQUdFX0lNUE9SVF9ERVNDUklQ VE9SIHBJbXBvcnREZXNjID0gKFBJTUFHRV9JTVBPUlRfREVTQ1JJUFRPUikoKEJZVEUqKWhU YXJnZXREbGwgKyBpbXBvcnRBZGRyKTsNCgkJZm9yICg7IHBJbXBvcnREZXNjLT5OYW1lOyBw SW1wb3J0RGVzYysrKSB7DQoJCQlMUENTVFIgc3pNb2ROYW1lID0gKExQQ1NUUikoKEJZVEUq KWhUYXJnZXREbGwgKyBwSW1wb3J0RGVzYy0+TmFtZSk7DQoJCQlib29sIGJNYXRjaCA9IGZh bHNlOw0KCQkJZm9yIChhdXRvJiBlbnRyeSA6IHNfSG9va3MpDQoJCQkJaWYgKF9zdHJpY21w KHN6TW9kTmFtZSwgZW50cnkucHN6RGxsTmFtZSkgPT0gMCkgeyBiTWF0Y2ggPSB0cnVlOyBi cmVhazsgfQ0KCQkJaWYgKGJNYXRjaCkgew0KCQkJCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8s IEwiICAgW1NlY3Rpb25dICVTIFN0YW5kYXJkIEltcG9ydHMgZMOpdGVjdMOpcy4iLCBzek1v ZE5hbWUpOw0KCQkJCUFwcGx5UGF0Y2hTdGFuZGFyZChoVGFyZ2V0RGxsLCBwSW1wb3J0RGVz YywgcmVzdG9yZSwgZGxsTmFtZS5jX3N0cigpKTsNCgkJCX0NCgkJfQ0KCX0NCg0KCS8vIDIu IERlbGF5LUxvYWQgSW1wb3J0cw0KCURXT1JEIGRlbGF5QWRkciA9IHBOdEhlYWRlcnMtPk9w dGlvbmFsSGVhZGVyLkRhdGFEaXJlY3RvcnlbSU1BR0VfRElSRUNUT1JZX0VOVFJZX0RFTEFZ X0lNUE9SVF0uVmlydHVhbEFkZHJlc3M7DQoJaWYgKGRlbGF5QWRkcikgew0KCQlQSU1BR0Vf REVMQVlMT0FEX0RFU0NSSVBUT1IgcERlbGF5RGVzYyA9IChQSU1BR0VfREVMQVlMT0FEX0RF U0NSSVBUT1IpKChCWVRFKiloVGFyZ2V0RGxsICsgZGVsYXlBZGRyKTsNCgkJZm9yICg7IHBE ZWxheURlc2MtPkRsbE5hbWVSVkE7IHBEZWxheURlc2MrKykgew0KCQkJTFBDU1RSIHN6TW9k TmFtZSA9IChMUENTVFIpKChCWVRFKiloVGFyZ2V0RGxsICsgcERlbGF5RGVzYy0+RGxsTmFt ZVJWQSk7DQoJCQlib29sIGJNYXRjaCA9IGZhbHNlOw0KCQkJZm9yIChhdXRvJiBlbnRyeSA6 IHNfSG9va3MpDQoJCQkJaWYgKF9zdHJpY21wKHN6TW9kTmFtZSwgZW50cnkucHN6RGxsTmFt ZSkgPT0gMCkgeyBiTWF0Y2ggPSB0cnVlOyBicmVhazsgfQ0KCQkJaWYgKGJNYXRjaCkgew0K CQkJCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiICAgW1NlY3Rpb25dICVTIERlbGF5LUxv YWQgSW1wb3J0cyBkw6l0ZWN0w6lzLiIsIHN6TW9kTmFtZSk7DQoJCQkJQXBwbHlQYXRjaERl bGF5TG9hZChoVGFyZ2V0RGxsLCBwRGVsYXlEZXNjLCByZXN0b3JlLCBkbGxOYW1lLmNfc3Ry KCkpOw0KCQkJfQ0KCQl9DQoJfQ0KfQ0KDQp2b2lkIE1zQ3J5cHRvQmFzZTo6QXBwbHlQYXRj aFN0YW5kYXJkKEhNT0RVTEUgaFRhcmdldERsbCwgUElNQUdFX0lNUE9SVF9ERVNDUklQVE9S IHBJbXBvcnREZXNjLCBib29sIHJlc3RvcmUsIGNvbnN0IHdjaGFyX3QqIGRsbE5hbWUpIHsN CglQSU1BR0VfVEhVTktfREFUQSBwVGh1bmsgPSAoUElNQUdFX1RIVU5LX0RBVEEpKChCWVRF KiloVGFyZ2V0RGxsICsgcEltcG9ydERlc2MtPkZpcnN0VGh1bmspOw0KCVBJTUFHRV9USFVO S19EQVRBIHBPcmlnVGh1bmsgPSAoUElNQUdFX1RIVU5LX0RBVEEpKChCWVRFKiloVGFyZ2V0 RGxsICsgcEltcG9ydERlc2MtPk9yaWdpbmFsRmlyc3RUaHVuayk7DQoJaW50IGNvdW50ID0g MDsNCglmb3IgKDsgcFRodW5rLT51MS5GdW5jdGlvbjsgcFRodW5rKyssIHBPcmlnVGh1bmsr Kykgew0KCQlQUk9DKiBwcGZuID0gKFBST0MqKSZwVGh1bmstPnUxLkZ1bmN0aW9uOw0KDQoJ CS8vIFLDqWN1cMOpcmVyIGxlIG5vbSBkZSBsYSBmb25jdGlvbiBpbXBvcnTDqWUgdmlhIE9y aWdpbmFsRmlyc3RUaHVuaw0KCQlMUENTVFIgcHN6RnVuY05hbWUgPSBudWxscHRyOw0KCQlp ZiAocE9yaWdUaHVuayAmJiAhKHBPcmlnVGh1bmstPnUxLk9yZGluYWwgJiBJTUFHRV9PUkRJ TkFMX0ZMQUcpKSB7DQoJCQlQSU1BR0VfSU1QT1JUX0JZX05BTUUgcElibiA9IChQSU1BR0Vf SU1QT1JUX0JZX05BTUUpKChCWVRFKiloVGFyZ2V0RGxsICsgcE9yaWdUaHVuay0+dTEuQWRk cmVzc09mRGF0YSk7DQoJCQlwc3pGdW5jTmFtZSA9IHBJYm4tPk5hbWU7DQoJCX0NCg0KCQli b29sIGJIb29rZWQgPSBmYWxzZTsNCgkJZm9yIChhdXRvJiBlbnRyeSA6IHNfSG9va3MpIHsN CgkJCWlmIChlbnRyeS5pc0RhdGEpIHsNCgkJCQlpZiAoKnBwZm4gPT0gZW50cnkucGZuT3Jp Z2luYWwpDQoJCQkJCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiICAgICAgW0RBVEFdICVT IGlkZW50aWZpZWQgJXAiLCBlbnRyeS5wc3pGdW5jTmFtZSwgKnBwZm4pOw0KCQkJCWNvbnRp bnVlOw0KCQkJfQ0KCQkJUFJPQyBwZm5Ub0ZpbmQgPSByZXN0b3JlID8gZW50cnkucGZuSG9v ayA6IGVudHJ5LnBmbk9yaWdpbmFsOw0KCQkJUFJPQyBwZm5Ub1JlcGxhY2UgPSByZXN0b3Jl ID8gZW50cnkucGZuT3JpZ2luYWwgOiBlbnRyeS5wZm5Ib29rOw0KCQkJaWYgKCpwcGZuID09 IHBmblRvRmluZCAmJiBwZm5Ub0ZpbmQgIT0gTlVMTCkgew0KCQkJCURXT1JEIG9sZDsNCgkJ CQlWaXJ0dWFsUHJvdGVjdChwcGZuLCBzaXplb2YoUFJPQyksIFBBR0VfRVhFQ1VURV9SRUFE V1JJVEUsICZvbGQpOw0KCQkJCSpwcGZuID0gcGZuVG9SZXBsYWNlOw0KCQkJCVZpcnR1YWxQ cm90ZWN0KHBwZm4sIHNpemVvZihQUk9DKSwgb2xkLCAmb2xkKTsNCgkJCQlUcmFjZShUUkFD RV9MRVZFTF9JTkZPLCBMIiAgICAgIFtPSy1TVERdICVTICVzIiwgZW50cnkucHN6RnVuY05h bWUsIHJlc3RvcmUgPyBMInJlc3RvcmVkIiA6IEwicGF0Y2hlZCIpOw0KCQkJCWNvdW50Kys7 DQoJCQkJYkhvb2tlZCA9IHRydWU7DQoJCQkJYnJlYWs7DQoJCQl9DQoJCX0NCg0KCQkvLyDi hpAgTk9VVkVBVSA6IHNpZ25hbGVyIHRvdXQgaW1wb3J0IFdpbnNjYXJkIG5vbiBob29rw6kN CgkJaWYgKCFiSG9va2VkICYmICFyZXN0b3JlICYmIHBzekZ1bmNOYW1lKSB7DQoJCQlUcmFj ZShUUkFDRV9MRVZFTF9XQVJOSU5HLCBMIiAgICAgIFshXSBJbXBvcnQgTk9UIEhPT0tFRCA6 ICVTIOKGkiByZWFsIFdpbnNjYXJkIGNhbGwgcG9zc2libGUgISIsIHBzekZ1bmNOYW1lKTsN CgkJfQ0KCX0NCglpZiAoY291bnQgPT0gMCkNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywg TCIgICAgICBbSW5mb10gTm8gYWRkcmVzcyBtYXRjaCBpbiB0aGlzIFN0YW5kYXJkIHNlY3Rp b24uIik7DQp9DQoNCnZvaWQgTXNDcnlwdG9CYXNlOjpBcHBseVBhdGNoRGVsYXlMb2FkKEhN T0RVTEUgaFRhcmdldERsbCwgUElNQUdFX0RFTEFZTE9BRF9ERVNDUklQVE9SIHBEZWxheURl c2MsIGJvb2wgcmVzdG9yZSwgY29uc3Qgd2NoYXJfdCogZGxsTmFtZSkgew0KCS8vIENhbGN1 bCBkZSBsYSBiYXNlIGR1IG1vZHVsZSBwb3VyIHRyYW5zZm9ybWVyIGxlcyBSVkEgZW4gYWRy ZXNzZXMgcsOpZWxsZXMNCglCWVRFKiBwQmFzZSA9IChCWVRFKiloVGFyZ2V0RGxsOw0KDQoJ Ly8gUsOpY3Vww6lyYXRpb24gZGVzIHRhYmxlcyB2aWEgUlZBIChSZWxhdGl2ZSBWaXJ0dWFs IEFkZHJlc3MpDQoJLy8gSU5UOiBUYWJsZSBkZXMgbm9tcyAoSW1wb3J0IE5hbWUgVGFibGUp DQoJLy8gSUFUOiBUYWJsZSBkZXMgYWRyZXNzZXMgKEltcG9ydCBBZGRyZXNzVGFibGUpIC0g Q29udGllbnQgbGVzIFN0dWJzIGF2YW50IHLDqXNvbHV0aW9uDQoJUElNQUdFX1RIVU5LX0RB VEEgcE5hbWVUaHVuayA9IChQSU1BR0VfVEhVTktfREFUQSkocEJhc2UgKyBwRGVsYXlEZXNj LT5JbXBvcnROYW1lVGFibGVSVkEpOw0KCVBJTUFHRV9USFVOS19EQVRBIHBBZGRyVGh1bmsg PSAoUElNQUdFX1RIVU5LX0RBVEEpKHBCYXNlICsgcERlbGF5RGVzYy0+SW1wb3J0QWRkcmVz c1RhYmxlUlZBKTsNCg0KCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiICAgICAgW0RlbGF5 TG9hZC1TY2FuXSBBbmFseXNlIGRlICVzIChCYXNlOiAlcCwgSU5UIFJWQTogMHglWCkiLA0K CQlkbGxOYW1lLCBwQmFzZSwgcERlbGF5RGVzYy0+SW1wb3J0TmFtZVRhYmxlUlZBKTsNCg0K CWludCBwYXRjaENvdW50ID0gMDsNCg0KCS8vIE9uIHBhcmNvdXJ0IGxhIHRhYmxlIGRlcyBu b21zIChJTlQpDQoJZm9yICg7IHBOYW1lVGh1bmstPnUxLkFkZHJlc3NPZkRhdGE7IHBOYW1l VGh1bmsrKywgcEFkZHJUaHVuaysrKSB7DQoNCgkJLy8gU2tpcCBzaSBpbXBvcnQgcGFyIE9y ZGluYWwgKGxlIGJpdCBkZSBwb2lkcyBmb3J0IGVzdCDDoCAxKQ0KCQlpZiAoSU1BR0VfU05B UF9CWV9PUkRJTkFMKHBOYW1lVGh1bmstPnUxLk9yZGluYWwpKSB7DQoJCQljb250aW51ZTsN CgkJfQ0KDQoJCS8vIEFjY8OocyBhdSBub20gZGUgbGEgZm9uY3Rpb24gdmlhIGxhIFJWQSBB ZGRyZXNzT2ZEYXRhDQoJCVBJTUFHRV9JTVBPUlRfQllfTkFNRSBwSW1wb3J0TmFtZSA9IChQ SU1BR0VfSU1QT1JUX0JZX05BTUUpKHBCYXNlICsgcE5hbWVUaHVuay0+dTEuQWRkcmVzc09m RGF0YSk7DQoJCUxQQ1NUUiBwc3pGdW5jTmFtZSA9IChMUENTVFIpcEltcG9ydE5hbWUtPk5h bWU7DQoNCgkJLy8gcHBmbiBwb2ludGUgc3VyIGwnZW50csOpZSBkZSBsJ0lBVCAoNCBvY3Rl dHMgZW4geDg2LCA4IG9jdGV0cyBlbiB4NjQpDQoJCVBST0MqIHBwZm4gPSAoUFJPQyopJnBB ZGRyVGh1bmstPnUxLkZ1bmN0aW9uOw0KDQoJCS8vIENvbXBhcmFpc29uIGF2ZWMgbm90cmUg dGFibGUgZGUgaG9va3MNCgkJZm9yIChhdXRvJiBlbnRyeSA6IHNfSG9va3MpIHsNCgkJCWlm ICghZW50cnkuaXNEYXRhICYmIF9zdHJpY21wKHBzekZ1bmNOYW1lLCBlbnRyeS5wc3pGdW5j TmFtZSkgPT0gMCkgew0KDQoJCQkJUFJPQyBwZm5Ub1JlcGxhY2UgPSByZXN0b3JlID8gZW50 cnkucGZuT3JpZ2luYWwgOiBlbnRyeS5wZm5Ib29rOw0KCQkJCWlmICghcGZuVG9SZXBsYWNl KSB7DQoJCQkJCVRyYWNlKFRSQUNFX0xFVkVMX1dBUk5JTkcsIEwiICAgICAgICAgWyFdICVT IDogcGZuVG9SZXBsYWNlIGVzdCBOVUxMIiwgcHN6RnVuY05hbWUpOw0KCQkJCQljb250aW51 ZTsNCgkJCQl9DQoNCgkJCQkvLyBWw6lyaWZpY2F0aW9uIGQnw6l2aXRlbWVudCA6IHNpIGTD qWrDoCBwYXRjaMOpIChwb3VyIMOpdml0ZXIgbGVzIMOpY3JpdHVyZXMgaW51dGlsZXMpDQoJ CQkJaWYgKCpwcGZuID09IHBmblRvUmVwbGFjZSkgew0KCQkJCQljb250aW51ZTsNCgkJCQl9 DQoNCgkJCQkvLyAtLS0gUEFUQ0ggTcOJTU9JUkUgLS0tDQoJCQkJRFdPUkQgb2xkUHJvdGVj dDsNCgkJCQkvLyBPbiB1dGlsaXNlIHNpemVvZihQUk9DKSBwb3VyIMOqdHJlIGFnbm9zdGlx dWUgeDg2ICg0KSAvIHg2NCAoOCkNCgkJCQlpZiAoVmlydHVhbFByb3RlY3QocHBmbiwgc2l6 ZW9mKFBST0MpLCBQQUdFX0VYRUNVVEVfUkVBRFdSSVRFLCAmb2xkUHJvdGVjdCkpIHsNCg0K CQkJCQlUcmFjZShUUkFDRV9MRVZFTF9WRVJCT1NFLCBMIiAgICAgICAgIFtERUJVR10gJVMg OiBBbmNpZW5uZSB2YWxldXIgPSAlcCwgTm91dmVsbGUgPSAlcCIsDQoJCQkJCQlwc3pGdW5j TmFtZSwgKnBwZm4sIHBmblRvUmVwbGFjZSk7DQoNCgkJCQkJKnBwZm4gPSBwZm5Ub1JlcGxh Y2U7DQoNCgkJCQkJVmlydHVhbFByb3RlY3QocHBmbiwgc2l6ZW9mKFBST0MpLCBvbGRQcm90 ZWN0LCAmb2xkUHJvdGVjdCk7DQoNCgkJCQkJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCIg ICAgICAgICBbT0stREVMQVldICVTICVzIGRhbnMgJXMiLA0KCQkJCQkJcHN6RnVuY05hbWUs IHJlc3RvcmUgPyBMInJlc3RhdXLDqWUiIDogTCJwYXRjaMOpZSIsIGRsbE5hbWUpOw0KDQoJ CQkJCXBhdGNoQ291bnQrKzsNCgkJCQl9DQoJCQkJZWxzZSB7DQoJCQkJCVRyYWNlKFRSQUNF X0xFVkVMX0VSUk9SLCBMIiAgICAgICAgIFtFUlJFVVJdIFZpcnR1YWxQcm90ZWN0IMOpY2hv dcOpIHBvdXIgJVMgKEVycm9yOiAlZCkiLA0KCQkJCQkJcHN6RnVuY05hbWUsIEdldExhc3RF cnJvcigpKTsNCgkJCQl9DQoJCQl9DQoJCX0NCgl9DQoNCglpZiAocGF0Y2hDb3VudCA+IDAp IHsNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCIgICAgICBbQmlsYW5dICVkIGZvbmN0 aW9ucyBwYXRjaMOpZXMgZGFucyBsYSBzZWN0aW9uIERlbGF5LUxvYWQgZGUgJXMiLCBwYXRj aENvdW50LCBkbGxOYW1lKTsNCgl9DQp9DQovLyAtLS0gTE9HSVFVRSBERVMgSE9PS1MgQVZF QyBUUkFDRSAtLS0NCg0KTE9ORyBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rQ29ubmVjdFco DQoJX0luXyBTQ0FSRENPTlRFWFQgaENvbnRleHQsDQoJX0luXyBMUENXU1RSIHN6UmVhZGVy LA0KCV9Jbl8gRFdPUkQgZHdTaGFyZU1vZGUsDQoJX0luXyBEV09SRCBkd1ByZWZlcnJlZFBy b3RvY29scywNCglfT3V0XyBMUFNDQVJESEFORExFIHBoQ2FyZCwNCglfT3V0XyBMUERXT1JE IHBkd0FjdGl2ZVByb3RvY29sKQ0Kew0KCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiLT4g W0hvb2tdIFNDYXJkQ29ubmVjdFcgJXAgc3VyICVzIiwgaENvbnRleHQsIHN6UmVhZGVyKTsN CglpZiAocGhDYXJkKSAqcGhDYXJkID0gcmVpbnRlcnByZXRfY2FzdDxTQ0FSREhBTkRMRT4o Z19wU3RhdGljSW5zdGFuY2UpOw0KCWlmIChwZHdBY3RpdmVQcm90b2NvbCkgKnBkd0FjdGl2 ZVByb3RvY29sID0gU0NBUkRfUFJPVE9DT0xfVDE7DQoJRnJvbUhhbmRsZSgqcGhDYXJkKTsN CglyZXR1cm4gU0NBUkRfU19TVUNDRVNTOw0KfQ0KDQpMT05HIFdJTkFQSSBNc0NyeXB0b0Jh c2U6Okhvb2tUcmFuc21pdCgNCglfSW5fIFNDQVJESEFORExFIGhDYXJkLA0KCV9Jbl8gTFBD U0NBUkRfSU9fUkVRVUVTVCBwaW9TZW5kUGNpLA0KCV9Jbl9yZWFkc18oY2JTZW5kTGVuZ3Ro KSBMUENCWVRFIHBiU2VuZEJ1ZmZlciwNCglfSW5fIERXT1JEIGNiU2VuZExlbmd0aCwNCglf SW5vdXRfb3B0XyBMUENTQ0FSRF9JT19SRVFVRVNUIHBpb1JlY3ZQY2ksDQoJX091dF93cml0 ZXNfYnl0ZXNfdG9fb3B0XygqcGNiUmVjdkxlbmd0aCwgKnBjYlJlY3ZMZW5ndGgpIExQQllU RSBwYlJlY3ZCdWZmZXIsDQoJX0lub3V0XyBMUERXT1JEIHBjYlJlY3ZMZW5ndGgpDQp7DQoJ VHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gU0NhcmRUcmFuc21pdCAlcCAo VGFpbGxlIDogJWQpIiwgaENhcmQsIGNiU2VuZExlbmd0aCk7DQoJaWYgKCFGcm9tSGFuZGxl KGhDYXJkKSkgcmV0dXJuIFNDQVJEX0VfQ09NTV9EQVRBX0xPU1Q7DQoNCglCT09MIHJldCA9 IEZyb21IYW5kbGUoaENhcmQpLT5leGVjdXRlUmF3KChQQllURSlwYlNlbmRCdWZmZXIsIGNi U2VuZExlbmd0aCwgcGJSZWN2QnVmZmVyLCBwY2JSZWN2TGVuZ3RoLCBGQUxTRSwgMHg5MDAw KTsNCglpZiAoIXJldCkNCgl7DQoJCUFzc2VydDo6RmFpbCgpOw0KCQlyZXR1cm4gU0NBUkRf RV9DT01NX0RBVEFfTE9TVDsNCgl9DQoJcmV0dXJuICBTQ0FSRF9TX1NVQ0NFU1M7DQp9DQoN CkxPTkcgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va1N0YXR1c1coDQoJX0luXyBTQ0FSREhB TkRMRSBoQ2FyZCwNCglfT3V0X3dyaXRlc19vcHRfKCpwY2NoUmVhZGVyTGVuKSBMUFdTVFIg bXN6UmVhZGVyTmFtZXMsDQoJX0lub3V0X29wdF8gTFBEV09SRCBwY2NoUmVhZGVyTGVuLA0K CV9PdXRfb3B0XyBMUERXT1JEIHBkd1N0YXRlLA0KCV9PdXRfb3B0XyBMUERXT1JEIHBkd1By b3RvY29sLA0KCV9PdXRfd3JpdGVzX29wdF8oKnBjYkF0ckxlbikgTFBCWVRFIHBiQXRyLA0K CV9Jbm91dF9vcHRfIExQRFdPUkQgcGNiQXRyTGVuKQ0Kew0KCVRyYWNlKFRSQUNFX0xFVkVM X0lORk8sIEwiLT4gW0hvb2tdIFNDYXJkU3RhdHVzVyAlcCIsIGhDYXJkKTsNCglpZiAoIUZy b21IYW5kbGUoaENhcmQpKSByZXR1cm4gU0NBUkRfRV9DT01NX0RBVEFfTE9TVDsNCgkvLyAx LiBEw6lmaW5pdGlvbiBkZXMgY29uc3RhbnRlcyBkZSByZXRvdXIgKGNvaMOpcmVudGVzIGF2 ZWMgTGlzdFJlYWRlcnMgZXQgbCdBVFIgZMOpZmluaSkNCgljb25zdCB3Y2hhcl90KiBzelJl YWRlck5hbWUgPSBMIkdJRFNfRW11bGF0b3JfMFwwIjsNCglEV09SRCBkd1JlYWRlckxlbiA9 IChEV09SRCl3Y3NsZW4oc3pSZWFkZXJOYW1lKSArIDE7DQoJQllURSBhdHJbXSA9IFRIRUFU UjsNCglEV09SRCBkd0F0ckxlbiA9IHNpemVvZihhdHIpOw0KDQoJLy8gMi4gR2VzdGlvbiBk dSBub20gZHUgbGVjdGV1ciAoc3pSZHIgLyBwY2NoKQ0KCWlmIChwY2NoUmVhZGVyTGVuKSB7 DQoJCWlmIChtc3pSZWFkZXJOYW1lcyA9PSBOVUxMKSB7DQoJCQkqcGNjaFJlYWRlckxlbiA9 IGR3UmVhZGVyTGVuOw0KCQl9DQoJCWVsc2UgaWYgKCpwY2NoUmVhZGVyTGVuID09IFNDQVJE X0FVVE9BTExPQ0FURSkgew0KCQkJTFBXU1RSKiBwcHdzeiA9IHJlaW50ZXJwcmV0X2Nhc3Q8 TFBXU1RSKj4obXN6UmVhZGVyTmFtZXMpOw0KCQkJKnBwd3N6ID0gKExQV1NUUilIZWFwQWxs b2MoR2V0UHJvY2Vzc0hlYXAoKSwgSEVBUF9aRVJPX01FTU9SWSwgZHdSZWFkZXJMZW4gKiBz aXplb2Yod2NoYXJfdCkpOw0KCQkJVHJhY2UoVFJBQ0VfTEVWRUxfVkVSQk9TRSwgTCIgICBb RGVidWddIFNDYXJkU3RhdHVzVyA6IEFsbG9jIG9mICVwICVkIGJ5dGVzIiwgKnBwd3N6LCBk d1JlYWRlckxlbiAqIHNpemVvZih3Y2hhcl90KSk7DQoJCQlpZiAoKnBwd3N6KSB7DQoJCQkJ bWVtY3B5KCpwcHdzeiwgc3pSZWFkZXJOYW1lLCBkd1JlYWRlckxlbiAqIHNpemVvZih3Y2hh cl90KSk7DQoJCQkJKnBjY2hSZWFkZXJMZW4gPSBkd1JlYWRlckxlbjsNCgkJCX0NCgkJfQ0K CQllbHNlIGlmICgqcGNjaFJlYWRlckxlbiA+PSBkd1JlYWRlckxlbikgew0KCQkJbWVtY3B5 KG1zelJlYWRlck5hbWVzLCBzelJlYWRlck5hbWUsIGR3UmVhZGVyTGVuICogc2l6ZW9mKHdj aGFyX3QpKTsNCgkJCSpwY2NoUmVhZGVyTGVuID0gZHdSZWFkZXJMZW47DQoJCX0NCgkJZWxz ZSB7DQoJCQkqcGNjaFJlYWRlckxlbiA9IGR3UmVhZGVyTGVuOw0KCQkJcmV0dXJuIFNDQVJE X0VfSU5TVUZGSUNJRU5UX0JVRkZFUjsNCgkJfQ0KCX0NCg0KCS8vIDMuIEdlc3Rpb24gZGUg bCdBVFIgKHBiQXRyIC8gcGNiQXRyKQ0KCWlmIChwY2JBdHJMZW4pIHsNCgkJaWYgKHBiQXRy ID09IE5VTEwpIHsNCgkJCSpwY2JBdHJMZW4gPSBkd0F0ckxlbjsNCgkJfQ0KCQllbHNlIGlm ICgqcGNiQXRyTGVuID09IFNDQVJEX0FVVE9BTExPQ0FURSkgew0KCQkJTFBCWVRFKiBwcEF0 ciA9IHJlaW50ZXJwcmV0X2Nhc3Q8TFBCWVRFKj4ocGJBdHIpOw0KCQkJKnBwQXRyID0gKExQ QllURSlIZWFwQWxsb2MoR2V0UHJvY2Vzc0hlYXAoKSwgSEVBUF9aRVJPX01FTU9SWSwgZHdB dHJMZW4pOw0KCQkJVHJhY2UoVFJBQ0VfTEVWRUxfVkVSQk9TRSwgTCIgICBbRGVidWddIFND YXJkU3RhdHVzVyA6IEFsbG9jIG9mICVwICVkIGJ5dGVzIiwgKnBwQXRyLCBkd0F0ckxlbik7 DQoJCQlpZiAoKnBwQXRyKSB7DQoJCQkJbWVtY3B5KCpwcEF0ciwgYXRyLCBkd0F0ckxlbik7 DQoJCQkJKnBjYkF0ckxlbiA9IGR3QXRyTGVuOw0KCQkJfQ0KCQl9DQoJCWVsc2UgaWYgKCpw Y2JBdHJMZW4gPj0gZHdBdHJMZW4pIHsNCgkJCW1lbWNweShwYkF0ciwgYXRyLCBkd0F0ckxl bik7DQoJCQkqcGNiQXRyTGVuID0gZHdBdHJMZW47DQoJCX0NCgkJZWxzZSB7DQoJCQkqcGNi QXRyTGVuID0gZHdBdHJMZW47DQoJCQlyZXR1cm4gU0NBUkRfRV9JTlNVRkZJQ0lFTlRfQlVG RkVSOw0KCQl9DQoJfQ0KDQoJLy8gNC4gw4l0YXRzIGV0IFByb3RvY29sZXMNCglpZiAocGR3 U3RhdGUpIHsNCgkJLy8gU0NBUkRfU1BFQ0lGSUMgaW5kaXF1ZSBxdWUgbGEgY2FydGUgZXN0 IHByw6p0ZSBwb3VyIGRlcyBBUERVIChNaW5pZHJpdmVyIGNoYXJnw6kpDQoJCSpwZHdTdGF0 ZSA9IFNDQVJEX1NQRUNJRklDOw0KCX0NCg0KCWlmIChwZHdQcm90b2NvbCkgew0KCQkqcGR3 UHJvdG9jb2wgPSBTQ0FSRF9QUk9UT0NPTF9UMTsNCgl9DQoNCglyZXR1cm4gU0NBUkRfU19T VUNDRVNTOw0KfQ0KDQpMT05HIFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tHZXRTdGF0dXND aGFuZ2VXKA0KCV9Jbl8gU0NBUkRDT05URVhUIGhDb250ZXh0LA0KCV9Jbl8gRFdPUkQgZHdU aW1lb3V0LA0KCV9Jbm91dF91cGRhdGVzXyhjUmVhZGVycykgTFBTQ0FSRF9SRUFERVJTVEFU RVcgcmdSZWFkZXJTdGF0ZXMsDQoJX0luXyBEV09SRCBjUmVhZGVycykNCnsNCglUcmFjZShU UkFDRV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBTQ2FyZEdldFN0YXR1c0NoYW5nZVcgJXAg KE5iOiAlZCkiLCBoQ29udGV4dCwgY1JlYWRlcnMpOw0KDQoJY29uc3Qgd2NoYXJfdCogc3pN eVJlYWRlciA9IEwiR0lEU19FbXVsYXRvcl8wIjsNCglCWVRFIGF0cltdID0gVEhFQVRSOw0K DQoJZm9yIChEV09SRCBpID0gMDsgaSA8IGNSZWFkZXJzOyBpKyspDQoJew0KCQkvLyBPbiB2 w6lyaWZpZSBzaSBjJ2VzdCBub3RyZSBsZWN0ZXVyIMOpbXVsw6kNCgkJaWYgKHJnUmVhZGVy U3RhdGVzW2ldLnN6UmVhZGVyICYmIHdjc3N0cihyZ1JlYWRlclN0YXRlc1tpXS5zelJlYWRl ciwgc3pNeVJlYWRlcikpDQoJCXsNCgkJCS8vIEwnw6l0YXQgInLDqWVsIiBkYW5zIG5vdHJl IMOpbXVsYXRldXIgZXN0IDogUHLDqXNlbnQgKyBNdXRlDQoJCQlEV09SRCBkd1JlYWxTdGF0 ZSA9IChTQ0FSRF9TVEFURV9QUkVTRU5UIHwgU0NBUkRfU1RBVEVfTVVURSkgKyAweDEwMDAw Ow0KDQoJCQkvLyBPbiBjb21wYXJlIGF2ZWMgY2UgcXVlIGwnYXBwbGljYXRpb24gcGVuc2Ug YXZvaXIgKGR3Q3VycmVudFN0YXRlKQ0KCQkJLy8gU2kgbCdhcHBsaWNhdGlvbiBuZSBzYWl0 IHJpZW4gKFNDQVJEX1NUQVRFX1VOQVdBUkUpLCBvbiBmb3JjZSBsZSBjaGFuZ2VtZW50Lg0K CQkJaWYgKHJnUmVhZGVyU3RhdGVzW2ldLmR3Q3VycmVudFN0YXRlID09IFNDQVJEX1NUQVRF X1VOQVdBUkUgfHwNCgkJCQkocmdSZWFkZXJTdGF0ZXNbaV0uZHdDdXJyZW50U3RhdGUgJiBT Q0FSRF9TVEFURV9QUkVTRU5UKSA9PSAwKQ0KCQkJew0KCQkJCXJnUmVhZGVyU3RhdGVzW2ld LmR3RXZlbnRTdGF0ZSA9IFNDQVJEX1NUQVRFX0NIQU5HRUQgfCBkd1JlYWxTdGF0ZTsNCgkJ CQlUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIiAgIFtTdGF0dXNdIFByZW1pZXIgc2lnbmFs ZW1lbnQgOiBDYXJ0ZSBwcsOpc2VudGUuIik7DQoJCQl9DQoJCQllbHNlDQoJCQl7DQoJCQkJ Ly8gTCdhcHBsaWNhdGlvbiBzYWl0IGTDqWrDoCBxdWUgbGEgY2FydGUgZXN0IGzDoCwgb24g bmUgbWV0IFBBUyBsZSBiaXQgQ0hBTkdFRA0KCQkJCXJnUmVhZGVyU3RhdGVzW2ldLmR3RXZl bnRTdGF0ZSA9IGR3UmVhbFN0YXRlOw0KCQkJfQ0KDQoJCQkvLyBPbiByZW1wbGl0IHN5c3TD qW1hdGlxdWVtZW50IGwnQVRSIGV0IGxlIG5vbWJyZSBkJ29jdGV0cw0KCQkJcmdSZWFkZXJT dGF0ZXNbaV0uY2JBdHIgPSBzaXplb2YoYXRyKTsNCgkJCW1lbWNweShyZ1JlYWRlclN0YXRl c1tpXS5yZ2JBdHIsIGF0ciwgc2l6ZW9mKGF0cikpOw0KCQl9DQoJCWVsc2UNCgkJew0KCQkJ Ly8gR2VzdGlvbiBkZXMgYXV0cmVzIGxlY3RldXJzIChQblAsIGV0Yy4pIHBvdXIgw6l2aXRl ciBkZSBibG9xdWVyIGxlIHN5c3TDqG1lDQoJCQlyZ1JlYWRlclN0YXRlc1tpXS5kd0V2ZW50 U3RhdGUgPSBTQ0FSRF9TVEFURV9FTVBUWTsNCgkJfQ0KCX0NCg0KCXJldHVybiBTQ0FSRF9T X1NVQ0NFU1M7DQp9DQoNCi8vI2RlZmluZSBDTV9JT0NUTF9HRVRfRkVBVFVSRV9SRVFVRVNU IFNDQVJEX0NUTF9DT0RFKDM1MDApDQpMT05HIFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tD b250cm9sKA0KCV9Jbl8gU0NBUkRIQU5ETEUgaENhcmQsDQoJX0luXyBEV09SRCBkd0NvbnRy b2xDb2RlLA0KCV9Jbl9yZWFkc19ieXRlc19vcHRfKGNiSW5CdWZmZXJTaXplKSBMUENWT0lE IGxwSW5CdWZmZXIsDQoJX0luXyBEV09SRCBjYkluQnVmZmVyU2l6ZSwNCglfT3V0X3dyaXRl c19ieXRlc190b19vcHRfKGNiT3V0QnVmZmVyU2l6ZSwgKmxwQnl0ZXNSZXR1cm5lZCkgTFBW T0lEIGxwT3V0QnVmZmVyLA0KCV9Jbl8gRFdPUkQgY2JPdXRCdWZmZXJTaXplLA0KCV9PdXRf IExQRFdPUkQgbHBCeXRlc1JldHVybmVkKQ0Kew0KCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8s IEwiLT4gW0hvb2tdIFNDYXJkQ29udHJvbCAlcCAoQ29kZSA6IDB4JTA4WCkiLCBoQ2FyZCwg ZHdDb250cm9sQ29kZSk7DQoJaWYgKCFGcm9tSGFuZGxlKGhDYXJkKSkgcmV0dXJuIFNDQVJE X0VfQ09NTV9EQVRBX0xPU1Q7DQoNCglpZiAobHBCeXRlc1JldHVybmVkKSAqbHBCeXRlc1Jl dHVybmVkID0gMDsNCg0KCXN3aXRjaCAoZHdDb250cm9sQ29kZSkNCgl7DQoJY2FzZSBJT0NU TF9TTUFSVENBUkRfR0VUX0ZFQVRVUkVfUkVRVUVTVDoNCgl7DQoJCVRyYWNlKFRSQUNFX0xF VkVMX0lORk8sIEwiSU9DVExfU01BUlRDQVJEX0dFVF9GRUFUVVJFX1JFUVVFU1QiKTsNCg0K CQkvLyBTdHJ1Y3R1cmUgZCd1bmUgZW50csOpZSBkYW5zIGxhIHLDqXBvbnNlIEdFVF9GRUFU VVJFX1JFUVVFU1QNCgkJLy8gQ2hhcXVlIGZlYXR1cmUgZXN0IGVuY29kw6llIHNlbG9uIFBD L1NDIFBhcnQgMTAgOg0KCQkvLyAgIDEgb2N0ZXQgIDogdGFnIChmZWF0dXJlIGNvZGUpDQoJ CS8vICAgMSBvY3RldCAgOiBsZW5ndGggKHRvdWpvdXJzIDQpDQoJCS8vICAgNCBvY3RldHMg OiBJT0NUTCBhc3NvY2nDqSAoYmlnLWVuZGlhbikNCiNwcmFnbWEgcGFjayhwdXNoLCAxKQ0K CQlzdHJ1Y3QgRkVBVFVSRV9FTlRSWSB7DQoJCQlCWVRFICB0YWc7DQoJCQlCWVRFICBsZW5n dGg7ICAgLy8gdG91am91cnMgMHgwNA0KCQkJRFdPUkQgaW9jdGw7ICAgIC8vIGJpZy1lbmRp YW4gIQ0KCQl9Ow0KI3ByYWdtYSBwYWNrKHBvcCkNCg0KCQkvLyBGZWF0dXJlcyByZXRvdXJu w6llcyA6IHVuaXF1ZW1lbnQgbGVzIGZlYXR1cmVzIGRlIGJhc2UNCgkJLy8gc2FucyBQSU5Q QUQgKHBhcyBkZSBGRUFUVVJFX1ZFUklGWV9QSU5fU1RBUlQvRklOSVNILCBldGMuKQ0KCQkv LyBPbiBleHBvc2Ugc2V1bGVtZW50IEZFQVRVUkVfRVhFQ1VURV9QQUNFIHNpIGJlc29pbiwN CgkJLy8gb3UgcmllbiBkdSB0b3V0IHNpIGxhIGNhcnRlIG4nZW4gYSBwYXMgYmVzb2luLg0K CQkvLw0KCQkvLyBJY2kgb24gcmV0b3VybmUgdW5lIGxpc3RlIHZpZGUgKGFjY8OocyBkaXJl Y3QgY2FydGUsIHBhcyBkZSBmZWF0dXJlIFBJTlBBRCkNCgkJLy8gPT4gcsOpcG9uc2UgdmFs aWRlIG1haXMgdmlkZSA6IGxwQnl0ZXNSZXR1cm5lZCA9IDAsIFNDQVJEX1NfU1VDQ0VTUw0K CQkvLw0KCQkvLyBTaSB0dSB2ZXV4IGV4cG9zZXIgZGVzIGZlYXR1cmVzIHNww6ljaWZpcXVl cywgZMOpY29tbWVudGUgbGVzIGVudHLDqWVzIHZvdWx1ZXMuDQoNCgkJLy9zdGF0aWMgY29u c3QgRkVBVFVSRV9FTlRSWSBmZWF0dXJlc1tdID0gew0KCQkvLyAgICAvLyBFeGVtcGxlIDog RkVBVFVSRV9HRVRfVExWX1BST1BFUlRJRVMgPSAweDEyDQoJCS8vICAgIC8vIHsgMHgxMiwg MHgwNCwgX2J5dGVzd2FwX3Vsb25nKElPQ1RMX0ZFQVRVUkVfR0VUX1RMVl9QUk9QRVJUSUVT KSB9LA0KDQoJCS8vICAgIC8vIEV4ZW1wbGUgOiBGRUFUVVJFX0VYRUNVVEVfUEFDRSA9IDB4 MjANCgkJLy8gICAgLy8geyAweDIwLCAweDA0LCBfYnl0ZXN3YXBfdWxvbmcoSU9DVExfRkVB VFVSRV9FWEVDVVRFX1BBQ0UpIH0sDQoJCS8vfTsNCg0KCQlEV09SRCBjYk5lZWRlZCA9IChE V09SRCkoMC8qc2l6ZW9mKGZlYXR1cmVzKSovKTsNCg0KCQkvLyBDYXMgOiBsZSBjYWxsZXIg c29uZGUgbGEgdGFpbGxlIChscE91dEJ1ZmZlciA9PSBOVUxMIG91IGNiT3V0QnVmZmVyU2l6 ZSA9PSAwKQ0KCQlpZiAobHBPdXRCdWZmZXIgPT0gTlVMTCB8fCBjYk91dEJ1ZmZlclNpemUg PT0gMCkNCgkJew0KCQkJaWYgKGxwQnl0ZXNSZXR1cm5lZCkgKmxwQnl0ZXNSZXR1cm5lZCA9 IGNiTmVlZGVkOw0KCQkJcmV0dXJuIFNDQVJEX1NfU1VDQ0VTUzsNCgkJfQ0KDQoJCS8vIEJ1 ZmZlciB0cm9wIHBldGl0DQoJCWlmIChjYk91dEJ1ZmZlclNpemUgPCBjYk5lZWRlZCkNCgkJ ew0KCQkJaWYgKGxwQnl0ZXNSZXR1cm5lZCkgKmxwQnl0ZXNSZXR1cm5lZCA9IGNiTmVlZGVk Ow0KCQkJcmV0dXJuIFNDQVJEX0VfSU5TVUZGSUNJRU5UX0JVRkZFUjsNCgkJfQ0KDQoJCS8v IENvcGllIGRlIGxhIHLDqXBvbnNlDQoJICAgLyogaWYgKGNiTmVlZGVkID4gMCkNCgkJCW1l bWNweShscE91dEJ1ZmZlciwgZmVhdHVyZXMsIGNiTmVlZGVkKTsqLw0KDQoJCWlmIChscEJ5 dGVzUmV0dXJuZWQpICpscEJ5dGVzUmV0dXJuZWQgPSBjYk5lZWRlZDsNCgkJcmV0dXJuIFND QVJEX1NfU1VDQ0VTUzsNCgl9DQoNCgljYXNlIElPQ1RMX1NNQVJUQ0FSRF9HRVRfQVRUUklC VVRFOg0KCQlUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIklPQ1RMX1NNQVJUQ0FSRF9HRVRf QVRUUklCVVRFIik7DQoJCS8vIMOAIGltcGzDqW1lbnRlciBzZWxvbiB0ZXMgYmVzb2lucw0K CQlyZXR1cm4gU0NBUkRfRV9OT1RfUkVBRFk7DQoNCglkZWZhdWx0Og0KCQlUcmFjZShUUkFD RV9MRVZFTF9XQVJOSU5HLCBMIltIb29rXSBTQ2FyZENvbnRyb2wgOiBjb2RlIGluY29ubnUg MHglMDhYIiwgZHdDb250cm9sQ29kZSk7DQoJCXJldHVybiBTQ0FSRF9FX05PVF9SRUFEWTsN Cgl9DQp9DQoNCkxPTkcgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va0xpc3RSZWFkZXJzVygN CglfSW5fIFNDQVJEQ09OVEVYVCBoQ29udGV4dCwNCglfSW5fb3B0XyBMUENXU1RSIG1zekdy b3VwcywNCglfT3V0X3dyaXRlc19vcHRfKCpwY2NoUmVhZGVycykgTFBXU1RSIG1zelJlYWRl cnMsDQoJX0lub3V0XyBMUERXT1JEIHBjY2hSZWFkZXJzKQ0Kew0KCVRyYWNlKFRSQUNFX0xF VkVMX0lORk8sIEwiLT4gW0hvb2tdIFNDYXJkTGlzdFJlYWRlcnNXICVwIiwgaENvbnRleHQp Ow0KDQoJY29uc3Qgd2NoYXJfdCogcmVhZGVycyA9IEwiR0lEU19FbXVsYXRvcl8wXDAiOw0K CS8vIFRhaWxsZSBlbiBjYXJhY3TDqHJlcyBpbmNsdWFudCBsZSBkb3VibGUgbnVsIGZpbmFs DQoJRFdPUkQgbGVuID0gKERXT1JEKXdjc2xlbihyZWFkZXJzKSArIDI7DQoNCglpZiAocGNj aFJlYWRlcnMgPT0gTlVMTCkNCgl7DQoJCXJldHVybiBTQ0FSRF9FX0lOVkFMSURfUEFSQU1F VEVSOw0KCX0NCg0KCS8vIENhcyAxIDogTCd1dGlsaXNhdGV1ciB2ZXV0IGp1c3RlIGNvbm5h w650cmUgbGEgdGFpbGxlIG7DqWNlc3NhaXJlDQoJaWYgKG1zelJlYWRlcnMgPT0gTlVMTCkN Cgl7DQoJCSpwY2NoUmVhZGVycyA9IGxlbjsNCgkJcmV0dXJuIFNDQVJEX1NfU1VDQ0VTUzsN Cgl9DQoNCgkvLyBDYXMgMiA6IFNDQVJEX0FVVE9BTExPQ0FURSAobXN6UmRyIGVzdCBlbiBm YWl0IHVuIHBvaW50ZXVyIHZlcnMgdW4gTFBXU1RSKQ0KCWlmICgqcGNjaFJlYWRlcnMgPT0g U0NBUkRfQVVUT0FMTE9DQVRFKQ0KCXsNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCIg ICBbSW5mb10gU0NhcmRMaXN0UmVhZGVyc1cgOiBBbGxvY2F0aW9uIGF1dG9tYXRpcXVlIGRl bWFuZMOpZSIpOw0KDQoJCS8vIE9uIGRvaXQgYWxsb3VlciB2aWEgU0NhcmRGcmVlTWVtb3J5 IGNvbXBhdGlibGUgKHNvdXZlbnQgSGVhcEFsbG9jIHN1ciBsZSB0YXMgcGFyIGTDqWZhdXQp DQoJCS8vIE1haXMgcG91ciDDqnRyZSByaWdvdXJldXgsIG9uIGFsbG91ZSB1bmUgem9uZSBx dWUgbGUgQ1NQIHBvdXJyYSBsaWLDqXJlci4NCgkJTFBXU1RSKiBwcG1zelJkciA9IHJlaW50 ZXJwcmV0X2Nhc3Q8TFBXU1RSKj4obXN6UmVhZGVycyk7DQoJCSpwcG1zelJkciA9IChMUFdT VFIpSGVhcEFsbG9jKEdldFByb2Nlc3NIZWFwKCksIEhFQVBfWkVST19NRU1PUlksIGxlbiAq IHNpemVvZih3Y2hhcl90KSk7DQoJCVRyYWNlKFRSQUNFX0xFVkVMX1ZFUkJPU0UsIEwiICAg W0RlYnVnXSBTQ2FyZExpc3RSZWFkZXJzVyA6IEFsbG9jIG9mICVwICVkIGJ5dGVzIiwgKnBw bXN6UmRyLCBsZW4gKiBzaXplb2Yod2NoYXJfdCkpOw0KDQoJCWlmICgqcHBtc3pSZHIgPT0g TlVMTCkNCgkJew0KCQkJcmV0dXJuIFNDQVJEX0VfTk9fTUVNT1JZOw0KCQl9DQoNCgkJbWVt Y3B5KCpwcG1zelJkciwgcmVhZGVycywgbGVuICogc2l6ZW9mKHdjaGFyX3QpKTsNCgkJKnBj Y2hSZWFkZXJzID0gbGVuOw0KCQlyZXR1cm4gU0NBUkRfU19TVUNDRVNTOw0KCX0NCg0KCS8v IENhcyAzIDogQnVmZmVyIGZvdXJuaSBwYXIgbCd1dGlsaXNhdGV1cg0KCWlmICgqcGNjaFJl YWRlcnMgPCBsZW4pDQoJew0KCQkqcGNjaFJlYWRlcnMgPSBsZW47DQoJCXJldHVybiBTQ0FS RF9FX0lOU1VGRklDSUVOVF9CVUZGRVI7DQoJfQ0KDQoJbWVtY3B5KG1zelJlYWRlcnMsIHJl YWRlcnMsIGxlbiAqIHNpemVvZih3Y2hhcl90KSk7DQoJKnBjY2hSZWFkZXJzID0gbGVuOw0K DQoJcmV0dXJuIFNDQVJEX1NfU1VDQ0VTUzsNCn0NCg0KLy8gLS0tIEhvb2tzICJwYXNzLXRo cm91Z2giIGZvcm1hdMOpcyBwb3VyIGxlIERlYnVnZ2luZyAtLS0NCg0KTE9ORyBXSU5BUEkg TXNDcnlwdG9CYXNlOjpIb29rRXN0YWJsaXNoQ29udGV4dCgNCglfSW5fIERXT1JEIGR3U2Nv cGUsDQoJX1Jlc2VydmVkXyBMUENWT0lEIHB2UmVzZXJ2ZWQxLA0KCV9SZXNlcnZlZF8gTFBD Vk9JRCBwdlJlc2VydmVkMiwNCglfT3V0XyBMUFNDQVJEQ09OVEVYVCBwaENvbnRleHQpDQp7 DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gU0NhcmRFc3RhYmxpc2hD b250ZXh0Iik7DQoJaWYgKHBoQ29udGV4dCkNCgl7DQoJCVVMT05HX1BUUiB1bmlxdWVJZCA9 IChVTE9OR19QVFIpR2V0VGlja0NvdW50NjQoKTsNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfVkVS Qk9TRSwgTCJjb250ZXh0OiAlcCIsIHVuaXF1ZUlkKTsNCgkJKnBoQ29udGV4dCA9IChTQ0FS RENPTlRFWFQpdW5pcXVlSWQ7DQoJfQ0KCXJldHVybiBTQ0FSRF9TX1NVQ0NFU1M7DQp9DQoN CkxPTkcgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va1JlYWRDYWNoZVcoDQoJX0luXyBTQ0FS RENPTlRFWFQgaENvbnRleHQsDQoJX0luXyBVVUlEKiBwQ2FyZElkZW50aWZpZXIsDQoJX0lu XyBEV09SRCBGcmVzaG5lc3NDb3VudGVyLA0KCV9Jbl8gTFBXU1RSIHN6TG9va3VwTmFtZSwN CglfT3V0X3dyaXRlc19ieXRlc18oKnBjYkRhdGFMZW4pIFBCWVRFIHBiRGF0YSwNCglfSW5v dXRfIExQRFdPUkQgcGNiRGF0YUxlbikNCnsNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBM IiAgIFtEZWJ1Z10gU0NhcmRSZWFkQ2FjaGVXICVwOiBLZXk9JXMgcGNiRGF0YUxlbj0ldSAl UyIsIGhDb250ZXh0LCBzekxvb2t1cE5hbWUsICpwY2JEYXRhTGVuLCAqcGNiRGF0YUxlbiA9 PSBTQ0FSRF9BVVRPQUxMT0NBVEUgPyAiU0NBUkRfQVVUT0FMTE9DQVRFIiA6ICIiKTsNCg0K CWlmICghc3pMb29rdXBOYW1lIHx8ICFwY2JEYXRhTGVuKQ0KCQlyZXR1cm4gU0NBUkRfRV9J TlZBTElEX1BBUkFNRVRFUjsNCg0KCS8vIDEuIFJlY2hlcmNoZSBkYW5zIHZvdHJlIHNpbXVs YXRldXIgKGNmLiByw6lwb25zZSBwcsOpY8OpZGVudGUpDQoJYXV0byBpdCA9IGdfU2ltdWxh dGVkQ2FjaGUuZmluZChzekxvb2t1cE5hbWUpOw0KCWlmIChpdCA9PSBnX1NpbXVsYXRlZENh Y2hlLmVuZCgpKQ0KCXsNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCJTQ0FSRF9XX0NB Q0hFX0lURU1fTk9UX0ZPVU5EIik7DQoJCXJldHVybiBTQ0FSRF9XX0NBQ0hFX0lURU1fTk9U X0ZPVU5EOw0KCX0NCg0KCWNvbnN0IGF1dG8mIGVudHJ5ID0gaXQtPnNlY29uZDsNCglEV09S RCBpbnRlcm5hbFNpemUgPSBzdGF0aWNfY2FzdDxEV09SRD4oZW50cnkuZGF0YS5zaXplKCkp Ow0KDQoJLy8gMi4gR2VzdGlvbiBkZSBsJ0FVVE9BTExPQ0FURQ0KCWlmICgqcGNiRGF0YUxl biA9PSBTQ0FSRF9BVVRPQUxMT0NBVEUpDQoJew0KCQlUcmFjZShUUkFDRV9MRVZFTF9JTkZP LCBMIlNDQVJEX0FVVE9BTExPQ0FURSIpOw0KCQkvLyBPbiBhbGxvdWUgbGEgbcOpbW9pcmUg KHNpbXVsZSBsZSBjb21wb3J0ZW1lbnQgZHUgc2VydmljZSBTbWFydCBDYXJkKQ0KCQkvLyBO b3RlOiBEYW5zIHVuIHZyYWkgaG9vaywgb24gZGV2cmFpdCB1dGlsaXNlciBsYSBtw6ptZSBo ZWFwIHF1ZSBTQ2FyZEZyZWVNZW1vcnkNCgkJRFdPUkQgYWxpZ25lZFNpemUgPSAoaW50ZXJu YWxTaXplICsgMTUpICYgfjE1OyAvLyBBbGlnbsOpIHN1ciAxNiBvY3RldHMNCgkJUEJZVEUg cEFsbG9jYXRlZCA9IChQQllURSlIZWFwQWxsb2MoR2V0UHJvY2Vzc0hlYXAoKSwgSEVBUF9a RVJPX01FTU9SWSwgYWxpZ25lZFNpemUpOw0KCQlUcmFjZShUUkFDRV9MRVZFTF9WRVJCT1NF LCBMIiAgIFtEZWJ1Z10gU0NhcmRSZWFkQ2FjaGVXIDogQWxsb2Mgb2YgJXAgJWQgYnl0ZXMi LCBwQWxsb2NhdGVkLCBhbGlnbmVkU2l6ZSk7DQoJCWlmICghcEFsbG9jYXRlZCkNCgkJew0K CQkJcmV0dXJuIFNDQVJEX0VfTk9fTUVNT1JZOw0KCQl9DQoNCgkJbWVtY3B5KHBBbGxvY2F0 ZWQsIGVudHJ5LmRhdGEuZGF0YSgpLCBpbnRlcm5hbFNpemUpOw0KDQoJCS8vIE9uIMOpY3Jp dCBsZSBwb2ludGV1ciBhbGxvdcOpIMOgIGwnYWRyZXNzZSBwb2ludMOpZSBwYXIgcGJEYXRh DQoJCS8vIHBiRGF0YSBlc3QgZW4gZmFpdCB1biBQQllURSogZMOpZ3Vpc8OpIGVuIFBCWVRF DQoJCSooKFBCWVRFKilwYkRhdGEpID0gcEFsbG9jYXRlZDsNCgkJKnBjYkRhdGFMZW4gPSBp bnRlcm5hbFNpemU7DQoNCgkJcmV0dXJuIFNDQVJEX1NfU1VDQ0VTUzsNCgl9DQoNCgkvLyAz LiBDYXMgY2xhc3NpcXVlIChCdWZmZXIgTlVMTCBwb3VyIHRhaWxsZSBvdSBCdWZmZXIgcHLD qS1hbGxvdcOpKQ0KCWlmIChwYkRhdGEgPT0gTlVMTCkNCgl7DQoJCSpwY2JEYXRhTGVuID0g aW50ZXJuYWxTaXplOw0KCQlUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMInNpemUgb25seSIp Ow0KCQlyZXR1cm4gU0NBUkRfU19TVUNDRVNTOw0KCX0NCg0KCWlmICgqcGNiRGF0YUxlbiA8 IGludGVybmFsU2l6ZSkNCgl7DQoJCSpwY2JEYXRhTGVuID0gaW50ZXJuYWxTaXplOw0KCQlU cmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIlNDQVJEX0VfSU5TVUZGSUNJRU5UX0JVRkZFUiIp Ow0KCQlyZXR1cm4gU0NBUkRfRV9JTlNVRkZJQ0lFTlRfQlVGRkVSOw0KCX0NCg0KCW1lbWNw eShwYkRhdGEsIGVudHJ5LmRhdGEuZGF0YSgpLCBpbnRlcm5hbFNpemUpOw0KCSpwY2JEYXRh TGVuID0gaW50ZXJuYWxTaXplOw0KCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiU0NBUkRf U19TVUNDRVNTIFNpemUgPSAlZCIsIGludGVybmFsU2l6ZSk7DQoNCglyZXR1cm4gU0NBUkRf U19TVUNDRVNTOw0KfQ0KDQoNCg0KTE9ORyBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rV3Jp dGVDYWNoZVcoDQoJX0luXyBTQ0FSRENPTlRFWFQgaENvbnRleHQsDQoJX0luXyBVVUlEKiBw Q2FyZElkZW50aWZpZXIsDQoJX0luXyBEV09SRCBGcmVzaG5lc3NDb3VudGVyLA0KCV9Jbl8g TFBXU1RSIHN6TG9va3VwTmFtZSwNCglfSW5fcmVhZHNfYnl0ZXNfKGNiRGF0YUxlbikgUEJZ VEUgcGJEYXRhLA0KCV9Jbl8gRFdPUkQgY2JEYXRhTGVuKQ0Kew0KCVRyYWNlKFRSQUNFX0xF VkVMX0lORk8sIEwiLT4gW0hvb2tdIFNDYXJkV3JpdGVDYWNoZVcgJXAgKE5hbWU6ICVzLCBT aXplOiAldSkiLCBoQ29udGV4dCwgc3pMb29rdXBOYW1lLCBjYkRhdGFMZW4pOw0KDQoJaWYg KCFzekxvb2t1cE5hbWUgfHwgKCFwYkRhdGEgJiYgY2JEYXRhTGVuID4gMCkpIHsNCgkJcmV0 dXJuIFNDQVJEX0VfSU5WQUxJRF9QQVJBTUVURVI7DQoJfQ0KDQoJLy8gT24gc3RvY2tlIGxl cyBkb25uw6llcyBkYW5zIG5vdHJlIG1hcA0KCUNhY2hlRW50cnkgZW50cnk7DQoJZW50cnku ZGF0YS5hc3NpZ24ocGJEYXRhLCBwYkRhdGEgKyBjYkRhdGFMZW4pOw0KCWdfU2ltdWxhdGVk Q2FjaGVbc3pMb29rdXBOYW1lXSA9IGVudHJ5Ow0KDQoJcmV0dXJuIFNDQVJEX1NfU1VDQ0VT UzsNCn0NCg0KTE9ORyBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rRGlzY29ubmVjdCgNCglf SW5fIFNDQVJESEFORExFIGhDYXJkLA0KCV9Jbl8gRFdPUkQgZHdEaXNwb3NpdGlvbikNCnsN CglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBTQ2FyZERpc2Nvbm5lY3Qg JXAiLCBoQ2FyZCk7DQoJaWYgKCFGcm9tSGFuZGxlKGhDYXJkKSkgcmV0dXJuIFNDQVJEX0Vf Q09NTV9EQVRBX0xPU1Q7DQoJcmV0dXJuIFNDQVJEX1NfU1VDQ0VTUzsNCn0NCg0KTE9ORyBX SU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rQmVnaW5UcmFucygNCglfSW5fIFNDQVJESEFORExF IGhDYXJkKQ0Kew0KCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiLT4gW0hvb2tdIFNDYXJk QmVnaW5UcmFuc2FjdGlvbiAlcCIsIGhDYXJkKTsNCglpZiAoIUZyb21IYW5kbGUoaENhcmQp KSByZXR1cm4gU0NBUkRfRV9DT01NX0RBVEFfTE9TVDsNCglyZXR1cm4gU0NBUkRfU19TVUND RVNTOw0KfQ0KDQpMT05HIFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tFbmRUcmFucygNCglf SW5fIFNDQVJESEFORExFIGhDYXJkLA0KCV9Jbl8gRFdPUkQgZHdEaXNwb3NpdGlvbikNCnsN CglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBTQ2FyZEVuZFRyYW5zYWN0 aW9uICVwIiwgaENhcmQpOw0KCWlmICghRnJvbUhhbmRsZShoQ2FyZCkpIHJldHVybiBTQ0FS RF9FX0NPTU1fREFUQV9MT1NUOw0KCXJldHVybiBTQ0FSRF9TX1NVQ0NFU1M7DQp9DQoNCkxP TkcgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va1JlbGVhc2VDb250ZXh0KA0KCV9Jbl8gU0NB UkRDT05URVhUIGhDb250ZXh0KQ0Kew0KCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiLT4g W0hvb2tdIFNDYXJkUmVsZWFzZUNvbnRleHQgJXAiLCBoQ29udGV4dCk7DQoJcmV0dXJuIFND QVJEX1NfU1VDQ0VTUzsNCn0NCg0KTE9ORyBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rRnJl ZU1lbW9yeSgNCglfSW5fIFNDQVJEQ09OVEVYVCBoQ29udGV4dCwNCglfSW5fIExQQ1ZPSUQg cHZNZW0pDQp7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gU0NhcmRG cmVlTWVtb3J5ICVwICVwIiwgaENvbnRleHQsIHB2TWVtKTsNCglIZWFwRnJlZShHZXRQcm9j ZXNzSGVhcCgpLCAwLCAoTFBWT0lEKXB2TWVtKTsNCglyZXR1cm4gU0NBUkRfU19TVUNDRVNT Ow0KfQ0KDQpMT05HIFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tDYW5jZWwoDQoJX0luXyBT Q0FSRENPTlRFWFQgaENvbnRleHQpDQp7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCIt PiBbSG9va10gU0NhcmRDYW5jZWwgJXAiLCBoQ29udGV4dCk7DQoJcmV0dXJuIFNDQVJEX1Nf U1VDQ0VTUzsNCn0NCg0KTE9ORyBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rUmVjb25uZWN0 KA0KCV9Jbl8gU0NBUkRIQU5ETEUgaENhcmQsDQoJX0luXyBEV09SRCBkd1NoYXJlTW9kZSwN CglfSW5fIERXT1JEIGR3UHJlZmVycmVkUHJvdG9jb2xzLA0KCV9Jbl8gRFdPUkQgZHdJbml0 aWFsaXphdGlvbiwNCglfT3V0X29wdF8gTFBEV09SRCBwZHdBY3RpdmVQcm90b2NvbCkNCnsN CglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBTQ2FyZFJlY29ubmVjdCAl cCIsIGhDYXJkKTsNCglpZiAoIUZyb21IYW5kbGUoaENhcmQpKSByZXR1cm4gU0NBUkRfRV9D T01NX0RBVEFfTE9TVDsNCglyZXR1cm4gU0NBUkRfU19TVUNDRVNTOw0KfQ0KDQpMT05HIFdJ TkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tBdWRpdCgNCglfSW5fIFNDQVJEQ09OVEVYVCBoQ29u dGV4dCwNCglfSW5fIERXT1JEIGR3RXZlbnQpDQp7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5G TywgTCItPiBbSG9va10gU0NhcmRBdWRpdCAlcCIsIGhDb250ZXh0KTsNCglyZXR1cm4gU0NB UkRfU19TVUNDRVNTOw0KfQ0KDQpMT05HIFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tMaXN0 Q2FyZHNXKA0KCV9Jbl8gU0NBUkRDT05URVhUIGhDb250ZXh0LA0KCV9Jbl9vcHRfIExQQ0JZ VEUgcGJBdHIsDQoJX0luX3JlYWRzX29wdF8oY2d1aWQpIExQQ0dVSUQgcmdxdWlkLA0KCV9J bl8gRFdPUkQgY2d1aWQsDQoJX091dF93cml0ZXNfb3B0XygqcGNjaENhcmRzKSBMUFdTVFIg bXN6Q2FyZHMsDQoJX0lub3V0XyBMUERXT1JEIHBjY2hDYXJkcykNCnsNCglUcmFjZShUUkFD RV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBTQ2FyZExpc3RDYXJkc1cgJXAiLCBoQ29udGV4 dCk7DQoNCgkvLyBMZSBub20gb2ZmaWNpZWwgcG91ciBsZSBwcm9maWwgZ8OpbsOpcmlxdWUg TWljcm9zb2Z0IEdJRFMNCgljb25zdCB3Y2hhcl90KiBzekNhcmROYW1lID0gTCJJZGVudGl0 eSBEZXZpY2UgKE1pY3Jvc29mdCBHZW5lcmljIFByb2ZpbGUpXDAiOw0KDQoJLy8gVGFpbGxl IDogbG9uZ3VldXIgZHUgdGV4dGUgKyAxIG51bCBkZSBmaW4gZGUgY2hhw65uZSArIDEgbnVs IGRlIGZpbiBkZSBtdWx0aS1zdHJpbmcNCglEV09SRCBsZW4gPSAoRFdPUkQpd2NzbGVuKHN6 Q2FyZE5hbWUpICsgMjsNCg0KCWlmIChwY2NoQ2FyZHMgPT0gTlVMTCkNCgl7DQoJCXJldHVy biBTQ0FSRF9FX0lOVkFMSURfUEFSQU1FVEVSOw0KCX0NCg0KCS8vIENhcyAxIDogUmVxdcOq dGUgZGUgdGFpbGxlDQoJaWYgKG1zekNhcmRzID09IE5VTEwpDQoJew0KCQkqcGNjaENhcmRz ID0gbGVuOw0KCQlUcmFjZShUUkFDRV9MRVZFTF9WRVJCT1NFLCBMIiAgIFtJbmZvXSBTQ2Fy ZExpc3RDYXJkc1cgOiBSZXRvdXJuZSB0YWlsbGUgcmVxdWlzZSAlZCIsIGxlbik7DQoJCXJl dHVybiBTQ0FSRF9TX1NVQ0NFU1M7DQoJfQ0KDQoJLy8gQ2FzIDIgOiBBdXRvLWFsbG9jYXRp b24gKFNDQVJEX0FVVE9BTExPQ0FURSA9IC0xKQ0KCWlmICgqcGNjaENhcmRzID09IFNDQVJE X0FVVE9BTExPQ0FURSkNCgl7DQoJCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sIEwiICAgW0lu Zm9dIFNDYXJkTGlzdENhcmRzVyA6IEFsbG9jYXRpb24gYXV0b21hdGlxdWUiKTsNCg0KCQlM UFdTVFIqIHBwbXN6Q2FyZHMgPSByZWludGVycHJldF9jYXN0PExQV1NUUio+KG1zekNhcmRz KTsNCgkJKnBwbXN6Q2FyZHMgPSAoTFBXU1RSKUhlYXBBbGxvYyhHZXRQcm9jZXNzSGVhcCgp LCBIRUFQX1pFUk9fTUVNT1JZLCBsZW4gKiBzaXplb2Yod2NoYXJfdCkpOw0KCQlUcmFjZShU UkFDRV9MRVZFTF9WRVJCT1NFLCBMIiAgIFtEZWJ1Z10gU0NhcmRMaXN0Q2FyZHNXIDogQWxs b2Mgb2YgJXAgJWQgYnl0ZXMiLCAqcHBtc3pDYXJkcywgbGVuICogc2l6ZW9mKHdjaGFyX3Qp KTsNCgkJaWYgKCpwcG1zekNhcmRzID09IE5VTEwpDQoJCXsNCgkJCXJldHVybiBTQ0FSRF9F X05PX01FTU9SWTsNCgkJfQ0KDQoJCW1lbWNweSgqcHBtc3pDYXJkcywgc3pDYXJkTmFtZSwg bGVuICogc2l6ZW9mKHdjaGFyX3QpKTsNCgkJKnBjY2hDYXJkcyA9IGxlbjsNCgkJcmV0dXJu IFNDQVJEX1NfU1VDQ0VTUzsNCgl9DQoNCgkvLyBDYXMgMyA6IEJ1ZmZlciBmb3VybmkgdHJv cCBwZXRpdA0KCWlmICgqcGNjaENhcmRzIDwgbGVuKQ0KCXsNCgkJVHJhY2UoVFJBQ0VfTEVW RUxfV0FSTklORywgTCIgICBbIV0gU0NhcmRMaXN0Q2FyZHNXIDogQnVmZmVyIGluc3VmZmlz YW50ICglZCA8ICVkKSIsICpwY2NoQ2FyZHMsIGxlbik7DQoJCSpwY2NoQ2FyZHMgPSBsZW47 DQoJCXJldHVybiBTQ0FSRF9FX0lOU1VGRklDSUVOVF9CVUZGRVI7DQoJfQ0KDQoJLy8gQ2Fz IDQgOiBDb3BpZSBkYW5zIGxlIGJ1ZmZlciBmb3VybmkNCgltZW1jcHkobXN6Q2FyZHMsIHN6 Q2FyZE5hbWUsIGxlbiAqIHNpemVvZih3Y2hhcl90KSk7DQoJKnBjY2hDYXJkcyA9IGxlbjsN Cg0KCXJldHVybiBTQ0FSRF9TX1NVQ0NFU1M7DQp9DQoNCkxPTkcgV0lOQVBJIE1zQ3J5cHRv QmFzZTo6SG9va0dldENhcmRUeXBlUHJvdmlkZXJOYW1lVygNCglfSW5fIFNDQVJEQ09OVEVY VCBoQ29udGV4dCwNCglfSW5fIExQQ1dTVFIgc3pDYXJkTmFtZSwNCglfSW5fIERXT1JEIGR3 UHJvdmlkZXJJZCwNCglfT3V0X3dyaXRlc19vcHRfKCpwY2NoUHJvdmlkZXIpIExQV1NUUiBz elByb3ZpZGVyLA0KCV9Jbm91dF8gTFBEV09SRCBwY2NoUHJvdmlkZXIpDQp7DQoJVHJhY2Uo VFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gU0NhcmRHZXRDYXJkVHlwZVByb3ZpZGVy TmFtZVcgJXAgKFByb3ZpZGVySWQ6ICVkKSIsIGhDb250ZXh0LCBkd1Byb3ZpZGVySWQpOw0K DQoJLy8gTm91cyBuZSByw6lwb25kb25zIHF1ZSBwb3VyIGxlIG1vZHVsZSBkZSBjYXJ0ZSAo TWluaWRyaXZlcikNCglpZiAoZHdQcm92aWRlcklkICE9IFNDQVJEX1BST1ZJREVSX0NBUkRf TU9EVUxFKQ0KCXsNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfV0FSTklORywgTCIgICBbIV0gUHJv dmlkZXJJZCAlZCBub24gZ8OpcsOpIChvbiBhdHRlbmQgU0NBUkRfUFJPVklERVJfQ0FSRF9N T0RVTEU9MHgwMSkiLCBkd1Byb3ZpZGVySWQpOw0KCQlyZXR1cm4gU0NBUkRfRV9JTlZBTElE X1ZBTFVFOw0KCX0NCg0KCS8vIExlIG5vbSBkZSBsYSBETEwgZHUgTWluaWRyaXZlciBHSURT IHN0YW5kYXJkDQoJY29uc3Qgd2NoYXJfdCogc3pEbGxOYW1lID0gTCJtc2NsbWQuZGxsIjsN CgkvL2NvbnN0IHdjaGFyX3QqIHN6RGxsTmFtZSA9IEwiR2lkc1Rlc3RzLmRsbCI7ICAvLyB0 YSBETEwgZGUgdGVzdA0KCURXT1JEIGxlbiA9IChEV09SRCl3Y3NsZW4oc3pEbGxOYW1lKSAr IDE7IC8vICsxIHBvdXIgbGUgbnVsIGZpbmFsDQoNCglpZiAocGNjaFByb3ZpZGVyID09IE5V TEwpDQoJew0KCQlyZXR1cm4gU0NBUkRfRV9JTlZBTElEX1BBUkFNRVRFUjsNCgl9DQoNCgkv LyBDYXMgMSA6IERlbWFuZGUgZGUgdGFpbGxlDQoJaWYgKHN6UHJvdmlkZXIgPT0gTlVMTCkN Cgl7DQoJCSpwY2NoUHJvdmlkZXIgPSBsZW47DQoJCXJldHVybiBTQ0FSRF9TX1NVQ0NFU1M7 DQoJfQ0KDQoJLy8gQ2FzIDIgOiBBdXRvLWFsbG9jYXRpb24NCglpZiAoKnBjY2hQcm92aWRl ciA9PSBTQ0FSRF9BVVRPQUxMT0NBVEUpDQoJew0KCQlUcmFjZShUUkFDRV9MRVZFTF9JTkZP LCBMIiAgIFtJbmZvXSBTQ2FyZEdldENhcmRUeXBlUHJvdmlkZXJOYW1lVyA6IEFsbG9jYXRp b24gYXV0b21hdGlxdWUiKTsNCg0KCQlMUFdTVFIqIHBwc3pQcm92aWRlciA9IHJlaW50ZXJw cmV0X2Nhc3Q8TFBXU1RSKj4oc3pQcm92aWRlcik7DQoJCSpwcHN6UHJvdmlkZXIgPSAoTFBX U1RSKUhlYXBBbGxvYyhHZXRQcm9jZXNzSGVhcCgpLCBIRUFQX1pFUk9fTUVNT1JZLCBsZW4g KiBzaXplb2Yod2NoYXJfdCkpOw0KCQlUcmFjZShUUkFDRV9MRVZFTF9WRVJCT1NFLCBMIiAg IFtEZWJ1Z10gU0NhcmRHZXRDYXJkVHlwZVByb3ZpZGVyTmFtZVcgOiBBbGxvYyBvZiAlcCAl ZCBieXRlcyIsICpwcHN6UHJvdmlkZXIsIGxlbiAqIHNpemVvZih3Y2hhcl90KSk7DQoJCWlm ICgqcHBzelByb3ZpZGVyID09IE5VTEwpDQoJCXsNCgkJCXJldHVybiBTQ0FSRF9FX05PX01F TU9SWTsNCgkJfQ0KDQoJCW1lbWNweSgqcHBzelByb3ZpZGVyLCBzekRsbE5hbWUsIGxlbiAq IHNpemVvZih3Y2hhcl90KSk7DQoJCSpwY2NoUHJvdmlkZXIgPSBsZW47DQoJCXJldHVybiBT Q0FSRF9TX1NVQ0NFU1M7DQoJfQ0KDQoJLy8gQ2FzIDMgOiBCdWZmZXIgdHJvcCBwZXRpdA0K CWlmICgqcGNjaFByb3ZpZGVyIDwgbGVuKQ0KCXsNCgkJVHJhY2UoVFJBQ0VfTEVWRUxfV0FS TklORywgTCIgICBbIV0gU0NhcmRHZXRDYXJkVHlwZVByb3ZpZGVyTmFtZVcgOiBCdWZmZXIg dHJvcCBwZXRpdCAoJWQgPCAlZCkiLCAqcGNjaFByb3ZpZGVyLCBsZW4pOw0KCQkqcGNjaFBy b3ZpZGVyID0gbGVuOw0KCQlyZXR1cm4gU0NBUkRfRV9JTlNVRkZJQ0lFTlRfQlVGRkVSOw0K CX0NCg0KCS8vIENhcyA0IDogQ29waWUgc3RhbmRhcmQNCgltZW1jcHkoc3pQcm92aWRlciwg c3pEbGxOYW1lLCBsZW4gKiBzaXplb2Yod2NoYXJfdCkpOw0KCSpwY2NoUHJvdmlkZXIgPSBs ZW47DQoNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIiAgIFtPS10gU0NhcmRHZXRDYXJk VHlwZVByb3ZpZGVyTmFtZVcgOiBSZW52b2llICdtc2NsbWQuZGxsJyIpOw0KCXJldHVybiBT Q0FSRF9TX1NVQ0NFU1M7DQp9DQoNCkxPTkcgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va0dl dFJlYWRlckljb25XKA0KCV9Jbl8gU0NBUkRDT05URVhUIGhDb250ZXh0LA0KCV9Jbl8gTFBD V1NUUiBzelJlYWRlck5hbWUsDQoJX091dF93cml0ZXNfYnl0ZXNfb3B0XygqcGNiSWNvbikg TFBCWVRFIHBiSWNvbiwNCglfSW5vdXRfb3B0XyBMUERXT1JEIHBjYkljb24pDQp7DQoJVHJh Y2UoVFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gU0NhcmRHZXRSZWFkZXJJY29uVyAl cCIsIGhDb250ZXh0KTsNCglyZXR1cm4gU0NBUkRfRV9OT19TRVJWSUNFOw0KfQ0KDQpMT05H IFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tHZXREZXZpY2VUeXBlSWRXKA0KCV9Jbl8gU0NB UkRDT05URVhUIGhDb250ZXh0LA0KCV9Jbl8gTFBDV1NUUiBzelJlYWRlck5hbWUsDQoJX091 dF8gTFBEV09SRCBwZHdEZXZpY2VUeXBlSWQpDQp7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5G TywgTCItPiBbSG9va10gU0NhcmRHZXREZXZpY2VUeXBlSWRXICVwIiwgaENvbnRleHQpOw0K CWlmIChwZHdEZXZpY2VUeXBlSWQpDQoJCSpwZHdEZXZpY2VUeXBlSWQgPSBTQ0FSRF9SRUFE RVJfVFlQRV9VU0I7DQoJcmV0dXJuIFNDQVJEX1NfU1VDQ0VTUzsNCn0NCg0KLy8gVHlwZWRl ZiBkZSBsYSBmb25jdGlvbiByw6llbGxlDQp0eXBlZGVmIERXT1JEKFdJTkFQSSogUEZOX0Ny ZWRVSVByb21wdEZvcldpbmRvd3NDcmVkZW50aWFscykoDQoJX0luX29wdF8gIFBDUkVEVUlf SU5GT1cgcFVpSW5mbywNCglfSW5fICAgICAgRFdPUkQgICAgICAgICBkd0F1dGhFcnJvciwN CglfSW5vdXRfICAgVUxPTkcqIHB1bEF1dGhQYWNrYWdlLA0KCV9Jbl9vcHRfICBMUENWT0lE ICAgICAgIHB2SW5BdXRoQnVmZmVyLA0KCV9Jbl8gICAgICBVTE9ORyAgICAgICAgIHVsSW5B dXRoQnVmZmVyU2l6ZSwNCglfT3V0XyAgICAgTFBWT0lEKiBwcHZPdXRBdXRoQnVmZmVyLA0K CV9PdXRfICAgICBVTE9ORyogcHVsT3V0QXV0aEJ1ZmZlclNpemUsDQoJX0lub3V0XyAgIEJP T0wqIHBmU2F2ZSwNCglfSW5fICAgICAgRFdPUkQgICAgICAgICBkd0ZsYWdzDQoJKTsNCg0K dHlwZWRlZiBzdHJ1Y3QgX0tFUkJfU01BUlRDQVJEX0NTUF9JTkZPIHsNCglEV09SRCBkd0Nz cEluZm9MZW47DQoJRFdPUkQgTWVzc2FnZVR5cGU7DQoJdW5pb24gew0KCQlQVk9JRCAgIENv bnRleHRJbmZvcm1hdGlvbjsNCgkJVUxPTkc2NCBTcGFjZUhvbGRlckZvcldvdzY0Ow0KCX07 DQoJRFdPUkQgZmxhZ3M7DQoJRFdPUkQgS2V5U3BlYzsNCglVTE9ORyBuQ2FyZE5hbWVPZmZz ZXQ7DQoJVUxPTkcgblJlYWRlck5hbWVPZmZzZXQ7DQoJVUxPTkcgbkNvbnRhaW5lck5hbWVP ZmZzZXQ7DQoJVUxPTkcgbkNTUE5hbWVPZmZzZXQ7DQoJVENIQVIgYkJ1ZmZlcjsNCn0gS0VS Ql9TTUFSVENBUkRfQ1NQX0lORk8sICogUEtFUkJfU01BUlRDQVJEX0NTUF9JTkZPOw0KDQpE V09SRCBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rQ3JlZFVJUHJvbXB0Rm9yV2luZG93c0Ny ZWRlbnRpYWxzKA0KCV9Jbl9vcHRfICBQQ1JFRFVJX0lORk9XIHBVaUluZm8sDQoJX0luXyAg ICAgIERXT1JEICAgICAgICAgZHdBdXRoRXJyb3IsDQoJX0lub3V0XyAgIFVMT05HKiBwdWxB dXRoUGFja2FnZSwNCglfSW5fb3B0XyAgTFBDVk9JRCAgICAgICBwdkluQXV0aEJ1ZmZlciwN CglfSW5fICAgICAgVUxPTkcgICAgICAgICB1bEluQXV0aEJ1ZmZlclNpemUsDQoJX091dF8g ICAgIExQVk9JRCogcHB2T3V0QXV0aEJ1ZmZlciwNCglfT3V0XyAgICAgVUxPTkcqIHB1bE91 dEF1dGhCdWZmZXJTaXplLA0KCV9Jbm91dF8gICBCT09MKiBwZlNhdmUsDQoJX0luXyAgICAg IERXT1JEICAgICAgICAgZHdGbGFncykNCnsNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBM Ii0+IFtIb29rXSBDcmVkVUlQcm9tcHRGb3JXaW5kb3dzQ3JlZGVudGlhbHMiKTsNCg0KCWF1 dG8gcGZuUmVhbCA9IEdldE9yaWdpbmFsPFBGTl9DcmVkVUlQcm9tcHRGb3JXaW5kb3dzQ3Jl ZGVudGlhbHM+KA0KCQkiQ3JlZFVJUHJvbXB0Rm9yV2luZG93c0NyZWRlbnRpYWxzVyIpOw0K DQoJUENBUkRfREFUQSBkYXRhID0gKFBDQVJEX0RBVEEpcHVsQXV0aFBhY2thZ2U7DQoJLy8g VsOpcmlmaWVyIHF1ZSBjJ2VzdCBiaWVuIHVuIGFwcGVsIEJhc2VDU1AgcG91ciBQSU4gc21h cnQgY2FyZA0KCS8vIChmbGFncyA9IENSRURVSVdJTl9JTl9DUkVEX09OTFkgPSAweDIwKQ0K CWlmICghKGR3RmxhZ3MgJiBDUkVEVUlXSU5fSU5fQ1JFRF9PTkxZKSB8fCAhcHZJbkF1dGhC dWZmZXIgfHwgIXBwdk91dEF1dGhCdWZmZXIpDQoJew0KCQkvLyBQYXMgdW4gYXBwZWwgUElO IHNtYXJ0IGNhcmQgLSBsYWlzc2VyIHBhc3Nlcg0KCQlEV09SRCBzdGF0dXMgPSBwZm5SZWFs KA0KCQkJcFVpSW5mbywgZHdBdXRoRXJyb3IsIHB1bEF1dGhQYWNrYWdlLA0KCQkJcHZJbkF1 dGhCdWZmZXIsIHVsSW5BdXRoQnVmZmVyU2l6ZSwNCgkJCXBwdk91dEF1dGhCdWZmZXIsIHB1 bE91dEF1dGhCdWZmZXJTaXplLA0KCQkJcGZTYXZlLCBkd0ZsYWdzKTsNCg0KCQlLRVJCX0NF UlRJRklDQVRFX0xPR09OKiBkZWJ1Z2xvZ29uID0gKEtFUkJfQ0VSVElGSUNBVEVfTE9HT04q KSpwcHZPdXRBdXRoQnVmZmVyOw0KCQlyZXR1cm4gc3RhdHVzOw0KCX0NCg0KCVRyYWNlKFRS QUNFX0xFVkVMX0lORk8sIEwiICAgW0hvb2tdIEluamVjdGlvbiBQSU4gYXV0b21hdGlxdWUg KGJ5cGFzcyBkaWFsb2cpIik7DQoJS0VSQl9DRVJUSUZJQ0FURV9MT0dPTiogZGVidWdsb2dv biA9IChLRVJCX0NFUlRJRklDQVRFX0xPR09OKilwdkluQXV0aEJ1ZmZlcjsNCgkvLyBMZSBi dWZmZXIgZCdlbnRyw6llIGVzdCB1biBLRVJCX0NFUlRJRklDQVRFX0xPR09ODQoJLy8gT24g ZG9pdCBjb25zdHJ1aXJlIGxlIGJ1ZmZlciBkZSBzb3J0aWUgYXZlYyBsZSBQSU4gaW5qZWN0 w6kNCgkvLyBMZSBidWZmZXIgZGUgc29ydGllIGVzdCBpZGVudGlxdWUgw6AgbCdlbnRyw6ll IG1haXMgYXZlYyBsZSBQSU4gcmVuc2VpZ27DqQ0KDQoJLy8gUsOpY3Vww6lyZXIgbGUgUElO IGRlIHRlc3QgY29uZmlndXLDqQ0KCS8vTFBDV1NUUiB3c3pQaW4gPSBMIjEyMzQ1Njc4Ijsg ICAvLyB0b24gUElOIGRlIHRlc3QsIGV4OiBMIjEyMzQ1Njc4Ig0KCUxQQ1NUUiAgc3pQaW4g PSAiMTIzNDU2NzgiOyAgLy8gdmVyc2lvbiBBTlNJIDogIjEyMzQ1Njc4Ig0KCURXT1JEICAg Y2JQaW4gPSAoRFdPUkQpc3RybGVuKHN6UGluKTsNCg0KCS8vIEFsbG91ZXIgbGUgYnVmZmVy IGRlIHNvcnRpZSA9IGNvcGllIGR1IGJ1ZmZlciBkJ2VudHLDqWUNCgkvLyBCYXNlQ1NQIGxp YsOocmUgY2UgYnVmZmVyIGF2ZWMgQ29UYXNrTWVtRnJlZQ0KCVVMT05HIGNiT3V0ID0gdWxJ bkF1dGhCdWZmZXJTaXplICsgY2JQaW4gKyBzaXplb2YoQ0hBUik7DQoJTFBCWVRFIHBiT3V0 ID0gKExQQllURSlDb1Rhc2tNZW1BbGxvYyhjYk91dCk7DQoJaWYgKCFwYk91dCkNCgkJcmV0 dXJuIEVSUk9SX09VVE9GTUVNT1JZOw0KDQoJbWVtY3B5KHBiT3V0LCBwdkluQXV0aEJ1ZmZl ciwgdWxJbkF1dGhCdWZmZXJTaXplKTsNCg0KCS8vIExlIEtFUkJfQ0VSVElGSUNBVEVfTE9H T04gZW4gc29ydGllIGRvaXQgYXZvaXIgbGUgUElOIHJlbnNlaWduw6kNCgkvLyBkYW5zIGxl IGNoYW1wIFBpbiAoVU5JQ09ERV9TVFJJTkcpIG91IGRpcmVjdGVtZW50IGVuIEFOU0kgc2Vs b24gbGEgdmVyc2lvbg0KCS8vIFN0cnVjdHVyZSA6IEtFUkJfQ0VSVElGSUNBVEVfTE9HT04g Y29tbWVuY2UgcGFyIE1lc3NhZ2VUeXBlIChEV09SRCkNCgkvLyBzdWl2aSBkZXMgY2hhbXBz IC0gbGUgUElOIGVzdCDDoCBsYSBmaW4gZHUgYnVmZmVyIGVuIEFOU0kNCgkvLw0KCS8vIEZv cm1hdCBhdHRlbmR1IHBhciBCYXNlQ1NQIGVuIHNvcnRpZSA6DQoJLy8gICBbIEtFUkJfQ0VS VElGSUNBVEVfTE9HT04gXVsgZG9ubsOpZXMgdmFyaWFibGVzIF1bIFBJTiBBTlNJIFwwIF0N CgkvLw0KCS8vIE9uIHBhdGNoIGxlIGNoYW1wIFBpbi5CdWZmZXIgcG91ciBwb2ludGVyIHZl cnMgbGEgZmluIGR1IGJ1ZmZlcg0KCS8vIGV0IFBpbi5MZW5ndGggLyBQaW4uTWF4aW11bUxl bmd0aCBhdmVjIGxhIHRhaWxsZSBkdSBQSU4gQU5TSQ0KDQoJS0VSQl9DRVJUSUZJQ0FURV9M T0dPTiogcExvZ29uID0gKEtFUkJfQ0VSVElGSUNBVEVfTE9HT04qKXBiT3V0Ow0KCUtFUkJf U01BUlRDQVJEX0NTUF9JTkZPKiBwSW5mbyA9IChLRVJCX1NNQVJUQ0FSRF9DU1BfSU5GTyop KChQQllURSlwdkluQXV0aEJ1ZmZlciArIChVTE9OR19QVFIpKChLRVJCX0NFUlRJRklDQVRF X0xPR09OKilwdkluQXV0aEJ1ZmZlciktPkNzcERhdGEpOw0KCS8vIExlIFBJTiBBTlNJIGVz dCDDqWNyaXQgw6AgbGEgZmluIGR1IGJ1ZmZlciBkZSBzb3J0aWUNCglMUEJZVEUgcGJQaW5E c3QgPSBwYk91dCArIHVsSW5BdXRoQnVmZmVyU2l6ZTsNCgltZW1jcHkocGJQaW5Ec3QsIHN6 UGluLCBjYlBpbiArIDEpOw0KDQoJLy8gUmVuc2VpZ25lciBsZSBVTklDT0RFX1NUUklORyBQ aW4gYXZlYyBsb25ndWV1cnMgQU5TSQ0KCS8vIChCYXNlQ1NQIGludGVycHLDqHRlIFBpbiBj b21tZSBBTlNJIG1hbGdyw6kgbGUgdHlwZSBVTklDT0RFX1NUUklORykNCglwTG9nb24tPlBp bi5MZW5ndGggPSAoVVNIT1JUKWNiUGluOw0KCXBMb2dvbi0+UGluLk1heGltdW1MZW5ndGgg PSAoVVNIT1JUKShjYlBpbiArIDEpOw0KCXBMb2dvbi0+UGluLkJ1ZmZlciA9IChQV1NUUiko KFVMT05HX1BUUilwYlBpbkRzdCAtIChVTE9OR19QVFIpcExvZ29uKTsgLy8gb2Zmc2V0IHLD qWVsIGVuIG3DqW1vaXJlDQoNCgkqcHB2T3V0QXV0aEJ1ZmZlciA9IHBiT3V0Ow0KCSpwdWxP dXRBdXRoQnVmZmVyU2l6ZSA9IGNiT3V0Ow0KDQoJaWYgKHBmU2F2ZSkgKnBmU2F2ZSA9IEZB TFNFOw0KDQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCIgICBbSG9va10gUElOIGluamVj dMOpICglZCBieXRlcykiLCBjYlBpbik7DQoJcmV0dXJuIEVSUk9SX1NVQ0NFU1M7DQp9DQoN CkhBTkRMRSBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rQWNjZXNzU3RhcnRlZEV2ZW50KCkN CnsNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBTQ2FyZEFjY2Vzc1N0 YXJ0ZWRFdmVudCIpOw0KDQoJc3RhdGljIEhBTkRMRSBoRXZlbnQgPSBDcmVhdGVFdmVudChO VUxMLCBUUlVFLCBUUlVFLCBMIkdsb2JhbFxcU0NhcmQkUmVzTWdyRGVmYXVsdFN0YXJ0ZWQk Iik7DQoNCglyZXR1cm4gaEV2ZW50Ow0KfQ0KDQpWT0lEIFdJTkFQSSBNc0NyeXB0b0Jhc2U6 Okhvb2tSZWxlYXNlU3RhcnRlZEV2ZW50KCkNCnsNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZP LCBMIi0+IFtIb29rXSBTQ2FyZFJlbGVhc2VTdGFydGVkRXZlbnQiKTsNCn0NCg0KTE9ORyBX SU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rR2V0UmVhZGVyRGV2aWNlSW5zdGFuY2VJZFcoDQoJ X0luXyBTQ0FSRENPTlRFWFQgaENvbnRleHQsDQoJX0luXyBMUENXU1RSIHN6UmVhZGVyTmFt ZSwNCglfT3V0X3dyaXRlc19vcHRfKCpwY2NoRGV2aWNlSW5zdGFuY2VJZCkgTFBXU1RSIHN6 RGV2aWNlSW5zdGFuY2VJZCwNCglfSW5vdXRfIExQRFdPUkQgcGNjaERldmljZUluc3RhbmNl SWQpDQp7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gU0NhcmRHZXRS ZWFkZXJEZXZpY2VJbnN0YW5jZUlkVyAoJXMpIiwgc3pSZWFkZXJOYW1lKTsNCg0KCS8vIElk ZW50aWZpYW50IGQnaW5zdGFuY2UgZmFjdGljZSBwb3VyIHNpbXVsYXRldXINCgljb25zdCB3 Y2hhcl90KiBtb2NrSWQgPSBMIlVTQlxcVklEXzAwMDAmUElEXzAwMDBcXEZBS0VfUkVBREVS XzAxIjsNCglEV09SRCBkd0xlbiA9IChEV09SRCl3Y3NsZW4obW9ja0lkKSArIDE7DQoNCglp ZiAoc3pEZXZpY2VJbnN0YW5jZUlkID09IE5VTEwpIHsNCgkJKnBjY2hEZXZpY2VJbnN0YW5j ZUlkID0gZHdMZW47DQoJCXJldHVybiBTQ0FSRF9TX1NVQ0NFU1M7DQoJfQ0KDQoJaWYgKCpw Y2NoRGV2aWNlSW5zdGFuY2VJZCA8IGR3TGVuKSB7DQoJCSpwY2NoRGV2aWNlSW5zdGFuY2VJ ZCA9IGR3TGVuOw0KCQlyZXR1cm4gU0NBUkRfRV9JTlNVRkZJQ0lFTlRfQlVGRkVSOw0KCX0N Cg0KCXdjc2NweV9zKHN6RGV2aWNlSW5zdGFuY2VJZCwgKnBjY2hEZXZpY2VJbnN0YW5jZUlk LCBtb2NrSWQpOw0KCSpwY2NoRGV2aWNlSW5zdGFuY2VJZCA9IGR3TGVuOw0KDQoJcmV0dXJu IFNDQVJEX1NfU1VDQ0VTUzsNCn0NCg0KUEZOX0NBUkRfQUNRVUlSRV9DT05URVhUIE1zQ3J5 cHRvQmFzZTo6c19wZm5PcmlnaW5hbENhcmRBY3F1aXJlQ29udGV4dCA9IG51bGxwdHI7DQpQ Rk5fQ0FSRF9SRUFEX0ZJTEUgTXNDcnlwdG9CYXNlOjpzX3Bmbk9yaWdpbmFsQ2FyZFJlYWRG aWxlID0gbnVsbHB0cjsNClBGTl9DU1BfQ0FDSEVfTE9PS1VQX0ZJTEUgTXNDcnlwdG9CYXNl OjpzX3Bmbk9yaWdpbmFsQ3NwQ2FjaGVMb29rdXBGaWxlID0gbnVsbHB0cjsNClBGTl9DU1Bf Q0FDSEVfREVMRVRFX0ZJTEUgTXNDcnlwdG9CYXNlOjpzX3Bmbk9yaWdpbmFsQ3NwQ2FjaGVE ZWxldGVGaWxlID0gbnVsbHB0cjsNClBGTl9DU1BfQ0FDSEVfQUREX0ZJTEUgICAgTXNDcnlw dG9CYXNlOjpzX3Bmbk9yaWdpbmFsQ3NwQ2FjaGVBZGRGaWxlID0gbnVsbHB0cjsNClBGTl9D U1BfQUxMT0MgICAgICAgICAgICAgTXNDcnlwdG9CYXNlOjpzX3Bmbk9yaWdpbmFsQ3NwQWxs b2MgPSBudWxscHRyOw0KUEZOX0NTUF9SRUFMTE9DICAgICAgICAgICBNc0NyeXB0b0Jhc2U6 OnNfcGZuT3JpZ2luYWxDc3BSZWFsbG9jID0gbnVsbHB0cjsNClBGTl9DU1BfRlJFRSAgICAg ICAgICAgICAgTXNDcnlwdG9CYXNlOjpzX3Bmbk9yaWdpbmFsQ3NwRnJlZSA9IG51bGxwdHI7 DQoNCg0KVk9JRCBNc0NyeXB0b0Jhc2U6OlNldHVwTWluaWRyaXZlckhvb2tzKF9Jbl8gUENB UkRfREFUQSAgcENhcmREYXRhKQ0Kew0KCWlmIChwQ2FyZERhdGEtPnBmbkNzcENhY2hlTG9v a3VwRmlsZSAhPSBNc0NyeXB0b0Jhc2U6Okhvb2tDc3BDYWNoZUxvb2t1cEZpbGUpDQoJew0K CQlzX3Bmbk9yaWdpbmFsQ3NwQ2FjaGVMb29rdXBGaWxlID0gcENhcmREYXRhLT5wZm5Dc3BD YWNoZUxvb2t1cEZpbGU7DQoJCXBDYXJkRGF0YS0+cGZuQ3NwQ2FjaGVMb29rdXBGaWxlID0g TXNDcnlwdG9CYXNlOjpIb29rQ3NwQ2FjaGVMb29rdXBGaWxlOw0KDQoJCXNfcGZuT3JpZ2lu YWxDc3BDYWNoZURlbGV0ZUZpbGUgPSBwQ2FyZERhdGEtPnBmbkNzcENhY2hlRGVsZXRlRmls ZTsNCgkJcENhcmREYXRhLT5wZm5Dc3BDYWNoZURlbGV0ZUZpbGUgPSBNc0NyeXB0b0Jhc2U6 Okhvb2tDc3BDYWNoZURlbGV0ZUZpbGU7DQoNCgkJc19wZm5PcmlnaW5hbENzcENhY2hlQWRk RmlsZSA9IHBDYXJkRGF0YS0+cGZuQ3NwQ2FjaGVBZGRGaWxlOw0KCQlwQ2FyZERhdGEtPnBm bkNzcENhY2hlQWRkRmlsZSA9IE1zQ3J5cHRvQmFzZTo6SG9va0NzcENhY2hlQWRkRmlsZTsN Cg0KCQlzX3Bmbk9yaWdpbmFsQ3NwQWxsb2MgPSBwQ2FyZERhdGEtPnBmbkNzcEFsbG9jOw0K CQlwQ2FyZERhdGEtPnBmbkNzcEFsbG9jID0gTXNDcnlwdG9CYXNlOjpIb29rQ3NwQWxsb2M7 DQoNCgkJc19wZm5PcmlnaW5hbENzcFJlYWxsb2MgPSBwQ2FyZERhdGEtPnBmbkNzcFJlQWxs b2M7DQoJCXBDYXJkRGF0YS0+cGZuQ3NwUmVBbGxvYyA9IE1zQ3J5cHRvQmFzZTo6SG9va0Nz cFJlYWxsb2M7DQoNCgkJc19wZm5PcmlnaW5hbENzcEZyZWUgPSBwQ2FyZERhdGEtPnBmbkNz cEZyZWU7DQoJCXBDYXJkRGF0YS0+cGZuQ3NwRnJlZSA9IE1zQ3J5cHRvQmFzZTo6SG9va0Nz cEZyZWU7DQoNCgl9DQp9DQoNCg0KRFdPUkQgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va0Nh cmRSZWFkRmlsZSgNCglfSW5fIFBDQVJEX0RBVEEgIHBDYXJkRGF0YSwNCglfSW5fIExQU1RS ICAgICAgIHBzekRpcmVjdG9yeU5hbWUsDQoJX0luXyBMUFNUUiAgICAgICBwc3pGaWxlTmFt ZSwNCglfSW5fIERXT1JEICAgICAgIGR3RmxhZ3MsDQoJX091dF93cml0ZXNfYnl0ZXNfb3B0 XygqcGNiRGF0YSkgUEJZVEUqIHBwYkRhdGEsDQoJX091dF8gUERXT1JEICAgICBwY2JEYXRh KQ0Kew0KCVNldHVwTWluaWRyaXZlckhvb2tzKHBDYXJkRGF0YSk7DQoNCglEV09SRCBkd1Jl dCA9IHNfcGZuT3JpZ2luYWxDYXJkUmVhZEZpbGUoDQoJCXBDYXJkRGF0YSwgcHN6RGlyZWN0 b3J5TmFtZSwgcHN6RmlsZU5hbWUsIGR3RmxhZ3MsIHBwYkRhdGEsIHBjYkRhdGEpOw0KDQoJ aWYgKGR3UmV0ID09IEVSUk9SX1NVQ0NFU1MgJiYgcHBiRGF0YSAmJiAqcHBiRGF0YSAmJiBw Y2JEYXRhKSB7DQoJCVRyYWNlKFRSQUNFX0xFVkVMX0lORk8sDQoJCQlMIltIb29rQ2FyZFJl YWRGaWxlXSBkaXI9JVMgZmlsZT0lUyByZXQ9JXUgc2l6ZT0ldSIsDQoJCQlwc3pEaXJlY3Rv cnlOYW1lID8gcHN6RGlyZWN0b3J5TmFtZSA6ICIobnVsbCkiLA0KCQkJcHN6RmlsZU5hbWUg PyBwc3pGaWxlTmFtZSA6ICIobnVsbCkiLA0KCQkJZHdSZXQsICpwY2JEYXRhKTsNCgkJLy8g RHVtcCBkZXMgcHJlbWllcnMgb2N0ZXRzDQoJCURXT1JEIGNiRHVtcCA9IG1pbigqcGNiRGF0 YSwgMzJ1KTsNCgkJd2NoYXJfdCBzekhleFsxMjhdID0ge307DQoJCWZvciAoRFdPUkQgaSA9 IDA7IGkgPCBjYkR1bXA7IGkrKykNCgkJCXN3cHJpbnRmX3Moc3pIZXggKyBpICogMywgNCwg TCIlMDJYICIsICgqcHBiRGF0YSlbaV0pOw0KCQlUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBM IltIb29rQ2FyZFJlYWRGaWxlXSBkYXRhOiAlcyIsIHN6SGV4KTsNCgl9DQoJZWxzZSB7DQoJ CVRyYWNlKFRSQUNFX0xFVkVMX1dBUk5JTkcsDQoJCQlMIltIb29rQ2FyZFJlYWRGaWxlXSBk aXI9JVMgZmlsZT0lUyBGQUlMRUQgcmV0PTB4JTA4WCIsDQoJCQlwc3pEaXJlY3RvcnlOYW1l ID8gcHN6RGlyZWN0b3J5TmFtZSA6ICIobnVsbCkiLA0KCQkJcHN6RmlsZU5hbWUgPyBwc3pG aWxlTmFtZSA6ICIobnVsbCkiLA0KCQkJZHdSZXQpOw0KCX0NCglyZXR1cm4gZHdSZXQ7DQp9 DQoNCg0KDQoNCi8vIE9uIHNpbXVsZSBxdWUgbGUgZmljaGllciBuJ2V4aXN0ZSBqYW1haXMg ZGFucyBsZSBjYWNoZQ0KRFdPUkQgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va0NzcENhY2hl TG9va3VwRmlsZSgNCglfSW5fICAgICAgICBQVk9JRCAgIHB2Q2FjaGVDb250ZXh0LA0KCV9J bl8gICAgICAgIExQV1NUUiAgd3N6VGFnLA0KCV9Jbl8gICAgICAgIERXT1JEICAgZHdGbGFn cywNCglfT3V0XyAgICAgICBQQllURSogcHBiRGF0YSwNCglfT3V0XyAgICAgICBQRFdPUkQg IHBjYkRhdGEpDQp7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfSU5GTywgTCItPiBbSG9va10gQ3Nw Q2FjaGVMb29rdXBGaWxlIChCWVBBU1MpIHRhZz0lcyIsIHdzelRhZyk7DQoNCgkvLyBTw6lj dXJpdMOpIDogb24gaW5pdGlhbGlzZSBsZXMgc29ydGllcyDDoCB6w6lybw0KCWlmIChwcGJE YXRhKSAqcHBiRGF0YSA9IG51bGxwdHI7DQoJaWYgKHBjYkRhdGEpICpwY2JEYXRhID0gMDsN Cg0KCS8vIE9uIHJldG91cm5lICJGaWNoaWVyIG5vbiB0cm91dsOpIiBwb3VyIGZvcmNlciBs ZSBDU1Agw6AgcmVjYWxjdWxlci9yZWxpcmUgbGVzIGRvbm7DqWVzDQoJcmV0dXJuIENSWVBU X0VfTk9UX0ZPVU5EOw0KfQ0KDQovLyBPbiBzaW11bGUgdW5lIHN1cHByZXNzaW9uIHLDqXVz c2llIHNhbnMgcmllbiBmYWlyZQ0KRFdPUkQgV0lOQVBJIE1zQ3J5cHRvQmFzZTo6SG9va0Nz cENhY2hlRGVsZXRlRmlsZSgNCglfSW5fIFBWT0lEICBwdkNhY2hlQ29udGV4dCwNCglfSW5f IExQV1NUUiB3c3pUYWcsDQoJX0luXyBEV09SRCAgZHdGbGFncykNCnsNCglUcmFjZShUUkFD RV9MRVZFTF9JTkZPLCBMIi0+IFtIb29rXSBDc3BDYWNoZURlbGV0ZUZpbGUgKEJZUEFTUykg dGFnPSVzIiwgd3N6VGFnKTsNCg0KCS8vIE9uIHJldG91cm5lIEVSUk9SX1NVQ0NFU1MgcG91 ciBxdWUgbCdhcHBlbGFudCBwZW5zZSBxdWUgYydlc3QgZmFpdA0KCXJldHVybiBFUlJPUl9T VUNDRVNTOw0KfQ0KDQovLyBPbiBpZ25vcmUgbCdham91dCBhdSBjYWNoZQ0KRFdPUkQgV0lO QVBJIE1zQ3J5cHRvQmFzZTo6SG9va0NzcENhY2hlQWRkRmlsZSgNCglfSW5fIFBWT0lEICBw dkNhY2hlQ29udGV4dCwNCglfSW5fIExQV1NUUiB3c3pUYWcsDQoJX0luXyBEV09SRCAgZHdG bGFncywNCglfSW5fcmVhZHNfYnl0ZXNfKGNiRGF0YSkgUEJZVEUgcGJEYXRhLA0KCV9Jbl8g RFdPUkQgIGNiRGF0YSkNCnsNCglUcmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIi0+IFtIb29r XSBDc3BDYWNoZUFkZEZpbGUgKEJZUEFTUykgdGFnPSVzIHNpemU9JXUiLCB3c3pUYWcsIGNi RGF0YSk7DQoNCgkvLyBPbiBuZSBmYWl0IHJpZW4gYXZlYyBsZXMgZG9ubsOpZXMgcGJEYXRh Lg0KCS8vIE9uIHJldG91cm5lIEVSUk9SX1NVQ0NFU1MgcG91ciBuZSBwYXMgYmxvcXVlciBs ZSBmbHV4IGRlIGwnYXBwbGljYXRpb24uDQoJcmV0dXJuIEVSUk9SX1NVQ0NFU1M7DQp9DQoN CkxQVk9JRCBXSU5BUEkgTXNDcnlwdG9CYXNlOjpIb29rQ3NwQWxsb2MoX0luXyBTSVpFX1Qg Y2JTaXplKQ0Kew0KCUxQVk9JRCBwUHRyID0gc19wZm5PcmlnaW5hbENzcEFsbG9jKGNiU2l6 ZSk7DQoJVHJhY2UoVFJBQ0VfTEVWRUxfVkVSQk9TRSwgTCJbSG9va10gQ3NwQWxsb2Mgc2l6 ZT0lenUgLT4gcHRyPSVwIiwgY2JTaXplLCBwUHRyKTsNCglyZXR1cm4gcFB0cjsNCn0NCg0K TFBWT0lEIFdJTkFQSSBNc0NyeXB0b0Jhc2U6Okhvb2tDc3BSZWFsbG9jKF9Jbl8gTFBWT0lE IHB2LCBfSW5fIFNJWkVfVCBjYlNpemUpDQp7DQoJTFBWT0lEIHBOZXdQdHIgPSBzX3Bmbk9y aWdpbmFsQ3NwUmVhbGxvYyhwdiwgY2JTaXplKTsNCglUcmFjZShUUkFDRV9MRVZFTF9WRVJC T1NFLCBMIltIb29rXSBDc3BSZWFsbG9jIG9sZD0lcCBzaXplPSV6dSAtPiBuZXc9JXAiLCBw diwgY2JTaXplLCBwTmV3UHRyKTsNCglyZXR1cm4gcE5ld1B0cjsNCn0NCg0KVk9JRCBXSU5B UEkgTXNDcnlwdG9CYXNlOjpIb29rQ3NwRnJlZShfSW5fIExQVk9JRCBwdikNCnsNCglUcmFj ZShUUkFDRV9MRVZFTF9WRVJCT1NFLCBMIltIb29rXSBDc3BGcmVlIHB0cj0lcCIsIHB2KTsN CglzX3Bmbk9yaWdpbmFsQ3NwRnJlZShwdik7DQp9DQoNCg0KLy8gUG9pbnRldXIgdmVycyBs J29yaWdpbmFsIHBvdXIgdm9zIGJlc29pbnMNClBGTl9DQVJEX1JFQURfRklMRSBzX3Bmbk9y aWdpbmFsQ2FyZFJlYWRGaWxlID0gbnVsbHB0cjsNCg0KTE9ORyBDQUxMQkFDSyBIYXJkd2Fy ZUJyZWFrcG9pbnRIYW5kbGVyKFBFWENFUFRJT05fUE9JTlRFUlMgRXhjZXB0aW9uSW5mbykg ew0KCWlmIChFeGNlcHRpb25JbmZvLT5FeGNlcHRpb25SZWNvcmQtPkV4Y2VwdGlvbkNvZGUg PT0gRVhDRVBUSU9OX1NJTkdMRV9TVEVQKSB7DQoJCS8vIE9uIHbDqXJpZmllIHNpIGMnZXN0 IGJpZW4gbm90cmUgRFIwIHF1aSBhIGTDqWNsZW5jaMOpIGwnZXhjZXB0aW9uDQoJCWlmIChF eGNlcHRpb25JbmZvLT5Db250ZXh0UmVjb3JkLT5EcjYgJiAweDEpIHsNCg0KCQkJLy8gTCdh ZHJlc3NlIHN1cnZlaWxsw6llIGVzdCBkYW5zIERyMA0KCQkJdm9pZCoqIHBwZm5UYXJnZXQg PSAodm9pZCoqKUV4Y2VwdGlvbkluZm8tPkNvbnRleHRSZWNvcmQtPkRyMDsNCg0KCQkJLy8g LS0tIFLDiUNVUMOJUkFUSU9OIERFIExBIFZBTEVVUiAtLS0NCgkJCWlmICgqcHBmblRhcmdl dCAhPSBNc0NyeXB0b0Jhc2U6Okhvb2tDYXJkUmVhZEZpbGUpDQoJCQkJc19wZm5PcmlnaW5h bENhcmRSZWFkRmlsZSA9IChQRk5fQ0FSRF9SRUFEX0ZJTEUpKnBwZm5UYXJnZXQ7DQoNCgkJ CS8vIC0tLSBMT0cgLyBUUkFDRSAtLS0NCgkJCS8vIE5vdGU6IEF0dGVudGlvbiBhdXggZm9u Y3Rpb25zIGJsb3F1YW50ZXMgZGFucyB1biBoYW5kbGVyIGQnZXhjZXB0aW9uDQoJCQkvLyBU cmFjZShUUkFDRV9MRVZFTF9JTkZPLCBMIkludGVyY2VwdMOpICEgTm91dmVsbGUgdmFsZXVy IDogJXAiLCBzX3Bmbk9yaWdpbmFsQ2FyZFJlYWRGaWxlKTsNCg0KCQkJLy8gLS0tIElOSkVD VElPTiBERSBWT1RSRSBIT09LIC0tLQ0KCQkJLy8gU2kgdm91cyB2b3VsZXogZm9yY2VyIHZv dHJlIHByb3ByZSBmb25jdGlvbiBpbW3DqWRpYXRlbWVudCA6DQoJCQkvLyAqcHBmblRhcmdl dCA9ICh2b2lkKilNb25Ib29rQ2FyZFJlYWRGaWxlOw0KDQoJCQkvLyBPbiBuZXR0b2llIERy NiBwb3VyIGFjcXVpdHRlciBsJ8OpdsOobmVtZW50DQoJCQlFeGNlcHRpb25JbmZvLT5Db250 ZXh0UmVjb3JkLT5EcjYgJj0gfjB4MTsNCg0KCQkJcmV0dXJuIEVYQ0VQVElPTl9DT05USU5V RV9FWEVDVVRJT047DQoJCX0NCgl9DQoJcmV0dXJuIEVYQ0VQVElPTl9DT05USU5VRV9TRUFS Q0g7DQp9DQoNCnZvaWQgU2V0V3JpdGVCcmVha3BvaW50KHZvaWQqIGFkZHJlc3MpIHsNCgkv LyAxLiBFbnJlZ2lzdHJlciBsZSBoYW5kbGVyIChwcmlvcml0w6kgaGF1dGUpDQoJQWRkVmVj dG9yZWRFeGNlcHRpb25IYW5kbGVyKDEsIEhhcmR3YXJlQnJlYWtwb2ludEhhbmRsZXIpOw0K DQoJQ09OVEVYVCBjdHggPSB7IDAgfTsNCgljdHguQ29udGV4dEZsYWdzID0gQ09OVEVYVF9E RUJVR19SRUdJU1RFUlM7DQoJSEFORExFIGhUaHJlYWQgPSBHZXRDdXJyZW50VGhyZWFkKCk7 DQoNCglpZiAoR2V0VGhyZWFkQ29udGV4dChoVGhyZWFkLCAmY3R4KSkgew0KCQljdHguRHIw ID0gKERXT1JEX1BUUilhZGRyZXNzOw0KDQoJCS8vIENvbmZpZ3VyYXRpb24gZGUgRFI3IDoN CgkJLy8gYml0IDAgIChMMCkgOiBBY3RpdmUgRFIwIGxvY2FsZW1lbnQgPSAxDQoJCS8vIGJp dCAxNi0xNyAoUlcwKSA6IDAxID0gQnJlYWsgb24gV3JpdGUgKG91IDExIHBvdXIgUmVhZC9X cml0ZSkNCgkJLy8gYml0IDE4LTE5IChMRU4wKSA6IDExID0gOCBieXRlcyAoeDY0KSBvdSAx MCA9IDIgYnl0ZXMsIDAwID0gMSBieXRlDQoJCWN0eC5EcjcgfD0gMHgxOyAgICAgICAgICAv LyBMMCA9IDENCgkJY3R4LkRyNyB8PSAoMHgxIDw8IDE2KTsgIC8vIFJXMCA9IDAxIChXcml0 ZSkNCgkJY3R4LkRyNyB8PSAoMHgzIDw8IDE4KTsgIC8vIExFTjAgPSAxMSAoOCBieXRlcykN Cg0KCQlTZXRUaHJlYWRDb250ZXh0KGhUaHJlYWQsICZjdHgpOw0KCX0NCn0NCg0KLy8gcmVw bGFjZSB0aGUgZGxsIG5hbWUgaW4gU0NhcmQgSG9va0dldENhcmRUeXBlUHJvdmlkZXJOYW1l VyB0byBnZXQgaW50byB0aGlzIGRsbA0KDQpEV09SRCBXSU5BUEkgTXNDcnlwdG9CYXNlOjpI b29rQ2FyZEFjcXVpcmVDb250ZXh0KA0KCV9Jbm91dF8gUENBUkRfREFUQSBwQ2FyZERhdGEs DQoJX0luXyAgICBEV09SRCAgICAgIGR3RmxhZ3MpDQp7DQoJLy8gQ2hhcmdlciBsZSB2cmFp IG1pbmlkcml2ZXINCglzdGF0aWMgSE1PRFVMRSBoTXNjbG1kID0gTG9hZExpYnJhcnlXKEwi bXNjbG1kLmRsbCIpOw0KCXN0YXRpYyBQRk5fQ0FSRF9BQ1FVSVJFX0NPTlRFWFQgcGZuUmVh bCA9DQoJCShQRk5fQ0FSRF9BQ1FVSVJFX0NPTlRFWFQpR2V0UHJvY0FkZHJlc3MoaE1zY2xt ZCwgIkNhcmRBY3F1aXJlQ29udGV4dCIpOw0KDQoJaWYgKHBDYXJkRGF0YSkgew0KCQkvLyBz ZXR1cCBpbnRlcnJ1cHQgb25jZSB0aGUgcG9pbnRlciBpcyBzZXQNCgkJLy8gT24gcG9zZSBs ZSBwacOoZ2Ugc3VyIGwnYWRyZXNzZSBkdSBwb2ludGV1ciBkZSBmb25jdGlvbg0KCQlTZXRX cml0ZUJyZWFrcG9pbnQoJihwQ2FyZERhdGEtPnBmbkNhcmRSZWFkRmlsZSkpOw0KCX0NCg0K CURXT1JEIGR3UmV0ID0gcGZuUmVhbChwQ2FyZERhdGEsIGR3RmxhZ3MpOw0KDQoJaWYgKGR3 UmV0ID09IEVSUk9SX1NVQ0NFU1MgJiYgcENhcmREYXRhKSB7DQoJCXNfcGZuT3JpZ2luYWxD YXJkUmVhZEZpbGUgPSBwQ2FyZERhdGEtPnBmbkNhcmRSZWFkRmlsZTsNCgkJcENhcmREYXRh LT5wZm5DYXJkUmVhZEZpbGUgPSBIb29rQ2FyZFJlYWRGaWxlOw0KCX0NCglyZXR1cm4gZHdS ZXQ7DQp9DQoNCg== --------------CMpnZc4mniyJIb4rYpAh0MRF Content-Type: text/x-c-code; charset=windows-1252; name="MsCryptoBase.h" Content-Disposition: attachment; filename="MsCryptoBase.h" Content-Transfer-Encoding: 8bit #pragma once #include "pch.h" #include "GidsTestBase.h" #include <winscard.h> #include <vector> #include <string> #include <wincred.h> #include "../EIDVirtualSmartCardMinidriverTest/cardmod.h" // Forward declaration pour le bootstrapping class MsCryptoBase; extern MsCryptoBase* g_pStaticInstance; // Structure pour la table des hooks struct HookEntry { LPCSTR pszDllName; LPCSTR pszFuncName; PROC pfnHook; PROC pfnOriginal; bool isData; // True pour les variables comme g_rgSCardT1Pci }; class MsCryptoBase : public GidsBaseTestClass { public: virtual void setUpHook(); virtual void tearDownHook(); protected: HMODULE m_hMinidriver = NULL; HMODULE m_hBaseCsp = NULL; HMODULE m_hKsp = NULL; static MsCryptoBase* FromHandle(SCARDHANDLE h); template<typename T> static T GetOriginal(LPCSTR pszFuncName) { for (auto& entry : MsCryptoBase::s_Hooks) if (_stricmp(entry.pszFuncName, pszFuncName) == 0) return (T)entry.pfnOriginal; return nullptr; } public: // --- Prototypes des Hooks --- // Gestion du contexte static LONG WINAPI HookEstablishContext( _In_ DWORD dwScope, _Reserved_ LPCVOID pvReserved1, _Reserved_ LPCVOID pvReserved2, _Out_ LPSCARDCONTEXT phContext); static LONG WINAPI HookReleaseContext( _In_ SCARDCONTEXT hContext); static LONG WINAPI HookFreeMemory( _In_ SCARDCONTEXT hContext, _In_ LPCVOID pvMem); static LONG WINAPI HookCancel( _In_ SCARDCONTEXT hContext); // Gestion des lecteurs et cartes static LONG WINAPI HookConnectW( _In_ SCARDCONTEXT hContext, _In_ LPCWSTR szReader, _In_ DWORD dwShareMode, _In_ DWORD dwPreferredProtocols, _Out_ LPSCARDHANDLE phCard, _Out_ LPDWORD pdwActiveProtocol); static LONG WINAPI HookReconnect( _In_ SCARDHANDLE hCard, _In_ DWORD dwShareMode, _In_ DWORD dwPreferredProtocols, _In_ DWORD dwInitialization, _Out_opt_ LPDWORD pdwActiveProtocol); static LONG WINAPI HookDisconnect( _In_ SCARDHANDLE hCard, _In_ DWORD dwDisposition); static LONG WINAPI HookStatusW( _In_ SCARDHANDLE hCard, _Out_writes_opt_(*pcchReaderLen) LPWSTR mszReaderNames, _Inout_opt_ LPDWORD pcchReaderLen, _Out_opt_ LPDWORD pdwState, _Out_opt_ LPDWORD pdwProtocol, _Out_writes_opt_(*pcbAtrLen) LPBYTE pbAtr, _Inout_opt_ LPDWORD pcbAtrLen); static LONG WINAPI HookGetStatusChangeW( _In_ SCARDCONTEXT hContext, _In_ DWORD dwTimeout, _Inout_updates_(cReaders) LPSCARD_READERSTATEW rgReaderStates, _In_ DWORD cReaders); // Communications (IO) static LONG WINAPI HookTransmit( _In_ SCARDHANDLE hCard, _In_ LPCSCARD_IO_REQUEST pioSendPci, _In_reads_(cbSendLength) LPCBYTE pbSendBuffer, _In_ DWORD cbSendLength, _Inout_opt_ LPCSCARD_IO_REQUEST pioRecvPci, _Out_writes_bytes_to_opt_(*pcbRecvLength, *pcbRecvLength) LPBYTE pbRecvBuffer, _Inout_ LPDWORD pcbRecvLength); static LONG WINAPI HookControl( _In_ SCARDHANDLE hCard, _In_ DWORD dwControlCode, _In_reads_bytes_opt_(cbInBufferSize) LPCVOID lpInBuffer, _In_ DWORD cbInBufferSize, _Out_writes_bytes_to_opt_(cbOutBufferSize, *lpBytesReturned) LPVOID lpOutBuffer, _In_ DWORD cbOutBufferSize, _Out_ LPDWORD lpBytesReturned); // Transactions static LONG WINAPI HookBeginTrans( _In_ SCARDHANDLE hCard); static LONG WINAPI HookEndTrans( _In_ SCARDHANDLE hCard, _In_ DWORD dwDisposition); // Énumération et Recherche static LONG WINAPI HookListReadersW( _In_ SCARDCONTEXT hContext, _In_opt_ LPCWSTR mszGroups, _Out_writes_opt_(*pcchReaders) LPWSTR mszReaders, _Inout_ LPDWORD pcchReaders); static LONG WINAPI HookListCardsW( _In_ SCARDCONTEXT hContext, _In_opt_ LPCBYTE pbAtr, _In_reads_opt_(cguid) LPCGUID rgquid, _In_ DWORD cguid, _Out_writes_opt_(*pcchCards) LPWSTR mszCards, _Inout_ LPDWORD pcchCards); // Cache et Métadonnées static LONG WINAPI HookReadCacheW( _In_ SCARDCONTEXT hContext, _In_ UUID* pCardIdentifier, _In_ DWORD FreshnessCounter, _In_ LPWSTR szLookupName, _Out_writes_bytes_(*pcbDataLen) PBYTE pbData, _Inout_ LPDWORD pcbDataLen); static LONG WINAPI HookWriteCacheW( _In_ SCARDCONTEXT hContext, _In_ UUID* pCardIdentifier, _In_ DWORD FreshnessCounter, _In_ LPWSTR szLookupName, _In_reads_bytes_(cbDataLen) PBYTE pbData, _In_ DWORD cbDataLen); static LONG WINAPI HookGetCardTypeProviderNameW( _In_ SCARDCONTEXT hContext, _In_ LPCWSTR szCardName, _In_ DWORD dwProviderId, _Out_writes_opt_(*pcchProvider) LPWSTR szProvider, _Inout_ LPDWORD pcchProvider); // Divers et Extension static LONG WINAPI HookGetReaderIconW( _In_ SCARDCONTEXT hContext, _In_ LPCWSTR szReaderName, _Out_writes_bytes_opt_(*pcbIcon) LPBYTE pbIcon, _Inout_opt_ LPDWORD pcbIcon); static LONG WINAPI HookGetDeviceTypeIdW( _In_ SCARDCONTEXT hContext, _In_ LPCWSTR szReaderName, _Out_ LPDWORD pdwDeviceTypeId); static LONG WINAPI HookAudit( _In_ SCARDCONTEXT hContext, _In_ DWORD dwEvent); static HANDLE WINAPI HookAccessStartedEvent(); static VOID WINAPI HookReleaseStartedEvent(); static LONG WINAPI HookGetReaderDeviceInstanceIdW( _In_ SCARDCONTEXT hContext, _In_ LPCWSTR szReaderName, _Out_writes_opt_(*pcchDeviceInstanceId) LPWSTR szDeviceInstanceId, _Inout_ LPDWORD pcchDeviceInstanceId); static DWORD WINAPI HookCredUIPromptForWindowsCredentials( _In_opt_ PCREDUI_INFOW pUiInfo, _In_ DWORD dwAuthError, _Inout_ ULONG* pulAuthPackage, _In_opt_ LPCVOID pvInAuthBuffer, _In_ ULONG ulInAuthBufferSize, _Out_ LPVOID* ppvOutAuthBuffer, _Out_ ULONG* pulOutAuthBufferSize, _Inout_ BOOL* pfSave, _In_ DWORD dwFlags); static PFN_CARD_READ_FILE s_pfnOriginalCardReadFile; static PFN_CSP_CACHE_DELETE_FILE s_pfnOriginalCspCacheDeleteFile; static PFN_CSP_CACHE_ADD_FILE s_pfnOriginalCspCacheAddFile; static PFN_CSP_ALLOC s_pfnOriginalCspAlloc; static PFN_CSP_REALLOC s_pfnOriginalCspRealloc; static PFN_CSP_FREE s_pfnOriginalCspFree; static PFN_CARD_ACQUIRE_CONTEXT s_pfnOriginalCardAcquireContext; static PFN_CSP_CACHE_LOOKUP_FILE s_pfnOriginalCspCacheLookupFile; static DWORD WINAPI HookCardAcquireContext( _Inout_ PCARD_DATA pCardData, _In_ DWORD dwFlags); static DWORD WINAPI HookCspCacheLookupFile( _In_ PVOID pvCacheContext, _In_ LPWSTR wszTag, _In_ DWORD dwFlags, /*_Deref_out_bcount_(*pcbData)*/ PBYTE* ppbData, _Out_ PDWORD pcbData); static DWORD WINAPI HookCardReadFile( _In_ PCARD_DATA pCardData, _In_ LPSTR pszDirectoryName, _In_ LPSTR pszFileName, _In_ DWORD dwFlags, _Out_writes_bytes_opt_(*pcbData) PBYTE* ppbData, _Out_ PDWORD pcbData); static DWORD WINAPI HookCspCacheDeleteFile( _In_ PVOID pvCacheContext, _In_ LPWSTR wszTag, _In_ DWORD dwFlags); static DWORD WINAPI HookCspCacheAddFile( _In_ PVOID pvCacheContext, _In_ LPWSTR wszTag, _In_ DWORD dwFlags, _In_reads_bytes_(cbData) PBYTE pbData, _In_ DWORD cbData); static LPVOID WINAPI HookCspAlloc(_In_ SIZE_T cbSize); static LPVOID WINAPI HookCspRealloc(_In_ LPVOID pv, _In_ SIZE_T cbSize); static VOID WINAPI HookCspFree(_In_ LPVOID pv); static VOID SetupMinidriverHooks(_In_ PCARD_DATA pCardData); private: static HookEntry s_Hooks[]; // --- Logique de Patching IAT --- void PatchIAT(HMODULE hTargetDll, bool restore); void ApplyPatchStandard(HMODULE hTargetDll, PIMAGE_IMPORT_DESCRIPTOR pImportDesc, bool restore, const wchar_t* dllName); void ApplyPatchDelayLoad(HMODULE hTargetDll, PIMAGE_DELAYLOAD_DESCRIPTOR pDelayDesc, bool restore, const wchar_t* dllName); }; --------------CMpnZc4mniyJIb4rYpAh0MRF Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline --------------CMpnZc4mniyJIb4rYpAh0MRF Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Disposition: inline _______________________________________________ Opensc-devel mailing list [email protected] https://lists.sourceforge.net/lists/listinfo/opensc-devel --------------CMpnZc4mniyJIb4rYpAh0MRF--