From: dzeri96 <dz...@pr...> - 2025-04-26 15:18:08
|
I just got the card this year so you might be mixing me up with somebody. I want to delay the reverse-engineering of Athena OpenID for three reasons: 1. Signing stuff is actually the last thing on my wish-list. I'm more interested in the identification data and the activation procedure. The tool seems to only do signing so reverse-engineering it probably won't help me much. 2. The 2 AIDs related to IAS ECC (starting with E8 28 BD 08 0F) are already mentioned in https://github.com/OpenSC/OpenSC/blob/master/etc/opensc.conf.example.in. Someone put them there so someone must know what they do. This leads me to believe that there must be a specification floating out there somewhere. 3. Obviously, it's very time-consuming. I've been reading the IAS ECC spec more thoroughly and chapter 10.4 describes Cryptographic Information Applications whose IDs start with the above-mentioned prefix and continue with the AID of the application they refer to. My card seems to contradict this since, for example 50 45 43 43 2D 65 49 44, is not a selectable AID, it just spells out PECC-eID. I don't know... I think getting my hands on the ChipDocs User Manual from NXP would potentially clear up some things, but they only give it to trusted partners apparently. Cheers, Dzeri96 On Saturday, 26 April 2025 at 10:49, Vincent Le Toux <vin...@my...> wrote: > Inside the middleware, there is a minidriver named ciamd.dll > > What I would suggest is to write a program like the one I wrote here (https://github.com/vletoux/openpgpmdrv/tree/master/OpenPGPminidriverTest) that connects to the minidriver and realize basic functions (enumerating public keys, certificates, encrypts, change pin, etc). > You can add a hook to dump the instructions sent to the card. > > You can use the following code to hook the SCardTransmit function: > > > void PrintHexToDebug(const BYTE* buffer, DWORD length) { > // Allocate memory dynamically > TCHAR* hexStr = (TCHAR*)malloc((3 * length + 1) * sizeof(TCHAR)); > if (hexStr == NULL) { > OutputDebugString(TEXT("Memory allocation failed\n")); > return; > } > > for (DWORD i = 0; i < length; i++) { > _stprintf_s(&hexStr[i * 3], 4, TEXT("%02X "), buffer[i]); > } > hexStr[3 * length] = '\0'; > OutputDebugString(hexStr); > > // Free the allocated memory > free(hexStr); > } > > LONG WINAPI MySCardTransmit( > SCARDHANDLE hCard, > LPCSCARD_IO_REQUEST pioSendPci, > LPCBYTE pbSendBuffer, > DWORD cbSendLength, > LPSCARD_IO_REQUEST pioRecvPci, > LPBYTE pbRecvBuffer, > LPDWORD pcbRecvLength > ) { > // Trace the input buffer > OutputDebugString(TEXT("pbSendBuffer: ")); > PrintHexToDebug(pbSendBuffer, cbSendLength); > OutputDebugString(TEXT("\n")); > // Call the original SCardTransmit > LONG result = SCardTransmit(hCard, pioSendPci, pbSendBuffer, cbSendLength, pioRecvPci, pbRecvBuffer, pcbRecvLength); > > // Write the return code as hex > TCHAR returnCodeStr[30]; > _stprintf_s(returnCodeStr, ARRAYSIZE(returnCodeStr), TEXT("Return code: %08X\n"), result); > OutputDebugString(returnCodeStr); > > // If the return code is successful, dump the output buffer > if (result == SCARD_S_SUCCESS && pcbRecvLength && pbRecvBuffer) { > (TEXT("pbRecvBuffer: ")); > PrintHexToDebug(pbRecvBuffer, *pcbRecvLength); > OutputDebugString(TEXT("\n")); > } > > return result; > } > > VOID EnableHook(HMODULE hModule) > { > HMODULE hScard = LoadLibrary(TEXT("Winscard.dll")); > PROC pfnScardTransmit = GetProcAddress(hScard, "SCardTransmit"); > PIMAGE_DOS_HEADER pDosHeader = (PIMAGE_DOS_HEADER)hModule; > PIMAGE_NT_HEADERS pNtHeaders = (PIMAGE_NT_HEADERS)((BYTE*)hModule + pDosHeader->e_lfanew); > PIMAGE_IMPORT_DESCRIPTOR pImportDesc = (PIMAGE_IMPORT_DESCRIPTOR)((BYTE*)hModule + pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress); > > while (pImportDesc->Name) { > LPCSTR pszModName = (LPCSTR)((BYTE*)hModule + pImportDesc->Name); > if (_stricmp(pszModName, "Winscard.dll") == 0) { > PIMAGE_THUNK_DATA pThunk = (PIMAGE_THUNK_DATA)((BYTE*)hModule + pImportDesc->FirstThunk); > while (pThunk->u1.Function) { > PROC* ppfn = (PROC*)&pThunk->u1.Function; > if (*ppfn == (PROC)pfnScardTransmit) { > DWORD oldProtect; > VirtualProtect(ppfn, sizeof(PROC), PAGE_EXECUTE_READWRITE, &oldProtect); > *ppfn = (PROC)MySCardTransmit; > VirtualProtect(ppfn, sizeof(PROC), oldProtect, &oldProtect); > } > pThunk++; > } > break; > } > pImportDesc++; > } > } > > > And to initialize the minidriver: > > > DWORD Connect(BOOL fSystemDll = TRUE) > { > DWORD dwReturn = 0; > SCARDCONTEXT hSCardContext = NULL; > SCARDHANDLE hSCardHandle = NULL; > TCHAR szCardModule[256]; > TCHAR szReader[256]; > DWORD dwCardModuleSize = ARRAYSIZE(szCardModule); > DWORD dwReaderSize = ARRAYSIZE(szReader); > OPENCARDNAME_EX dlgStruct; > PFN_CARD_ACQUIRE_CONTEXT pfnCardAcquireContext; > > __try > { > // find a smart card > ///////////////////// > > dwReturn = SCardEstablishContext(SCARD_SCOPE_USER, > NULL, > NULL, > &hSCardContext); > if (SCARD_S_SUCCESS != dwReturn) > { > __leave; > } > > // Initialize the structure. > memset(&dlgStruct, 0, sizeof(dlgStruct)); > dlgStruct.dwStructSize = sizeof(dlgStruct); > dlgStruct.hSCardContext = hSCardContext; > dlgStruct.dwFlags = SC_DLG_MINIMAL_UI; > dlgStruct.lpstrRdr = szReader; > dlgStruct.nMaxRdr = dwReaderSize; > dlgStruct.lpstrCard = szCard; > dlgStruct.nMaxCard = ARRAYSIZE(szCard); > dlgStruct.lpstrTitle = L"Select Card"; > dlgStruct.dwShareMode = 0; > // Display the select card dialog box. > dwReturn = SCardUIDlgSelectCard(&dlgStruct); > if (SCARD_S_SUCCESS != dwReturn) > { > __leave; > } > > // find the dll path / name > //////////////////////////// > if (fSystemDll) > { > > > dwReturn = SCardGetCardTypeProviderName( > hSCardContext, > szCard, > SCARD_PROVIDER_CARD_MODULE, > (PTSTR)&szCardModule, > &dwCardModuleSize); > if (0 == dwCardModuleSize) > { > dwReturn = (DWORD)SCARD_E_UNKNOWN_CARD; > __leave; > } > } > else > { > #ifdef _M_X64 > _tcscpy_s(szCardModule, dwCardModuleSize, TEXT("Name of the dll.dll")); > #else > _tcscpy_s(szCardModule, dwCardModuleSize, TEXT("Name of the dll.dll")); > #endif > } > // connect to the smart card > //////////////////////////// > DWORD dwProtocol, dwState; > dwReturn = SCardConnect(hSCardContext, szReader, SCARD_SHARE_SHARED, SCARD_PROTOCOL_T1 | SCARD_PROTOCOL_T0, &hSCardHandle, &dwProtocol); > if (SCARD_S_SUCCESS != dwReturn) > { > __leave; > } > atr.cbAtr = 32; > dwReturn = SCardStatus(hSCardHandle, szReader, &dwReaderSize, &dwState, &dwProtocol, atr.rgbAtr, &atr.cbAtr); > if (SCARD_S_SUCCESS != dwReturn) > { > __leave; > } > // load > //////// > if (NULL == (hModule = LoadLibrary(szCardModule))) > { > dwReturn = GetLastError(); > __leave; > } > if (fSystemDll) > { > EnableHook(hModule); > } > if (NULL == (pfnCardAcquireContext = > (PFN_CARD_ACQUIRE_CONTEXT)GetProcAddress( > hModule, "CardAcquireContext"))) > { > dwReturn = GetLastError(); > __leave; > } > // initialize context > ////////////////////// > pCardData = &CardData; > pCardData->dwVersion = CARD_DATA_CURRENT_VERSION; > pCardData->pfnCspAlloc = _Alloc; > pCardData->pfnCspFree = _Free; > pCardData->pfnCspReAlloc = _ReAlloc; > pCardData->pfnCspCacheAddFile = _CacheAddFileStub; > pCardData->pfnCspCacheLookupFile = _CacheLookupFileStub; > pCardData->pfnCspCacheDeleteFile = _CacheDeleteFileStub; > pCardData->hScard = hSCardHandle; > pCardData->hSCardCtx = hSCardContext; > pCardData->cbAtr = atr.cbAtr; > pCardData->pbAtr = atr.rgbAtr; > pCardData->pwszCardName = szCard; > //dwReturn = SCardBeginTransaction(hSCardHandle); > if (SCARD_S_SUCCESS != dwReturn) > { > __leave; > } > dwReturn = pfnCardAcquireContext(pCardData, 0); > } > __finally > { > if (dwReturn != 0) > { > if (hSCardHandle) > { > SCardEndTransaction(hSCardHandle, SCARD_LEAVE_CARD); > SCardDisconnect(hSCardHandle, 0); > } > if (hSCardContext) > SCardReleaseContext(hSCardContext); > } > } > return dwReturn; > } > > DWORD Disconnect() > { > DWORD dwReturn = 0; > if (pCardData) > { > if (pCardData->hScard) > { > SCardEndTransaction(pCardData->hScard, SCARD_LEAVE_CARD); > SCardDisconnect(pCardData->hScard, 0); > } > if (pCardData->hSCardCtx) > SCardReleaseContext(pCardData->hSCardCtx); > pCardData = NULL; > } > else > { > dwReturn = SCARD_E_COMM_DATA_LOST; > } > return dwReturn; > } > > You can then call directly : > > DWORD GenerateNewKey(DWORD dwIndex) > { > DWORD dwReturn, dwKeySpec; > PIN_ID PinId; > __try > { > if (!pCardData) > { > dwReturn = SCARD_E_COMM_DATA_LOST; > __leave; > } > switch(dwIndex) > { > case 0: //Signature, > dwKeySpec = AT_SIGNATURE; > PinId = ROLE_USER; > break; > case 2: //Authentication, > dwKeySpec = AT_SIGNATURE; > PinId = 3; > break; > case 1: // Confidentiality, > dwKeySpec = AT_KEYEXCHANGE; > PinId = 4; > break; > default: > dwReturn = SCARD_E_UNEXPECTED; > __leave; > } > dwReturn = pCardData->pfnCardCreateContainerEx(pCardData, (BYTE) dwIndex, > CARD_CREATE_CONTAINER_KEY_GEN, > dwKeySpec, 1024, NULL, PinId); > } > __finally > { > } > return dwReturn; > } > > br > Vincent > > > Le ven. 25 avr. 2025 à 22:53, Frank Morgner <fra...@gm...> a écrit : > > > The middleware is available on the bottom of this page > > https://www.gov.me/clanak/preuzmite-software-i-uputstva > > > > But I think you already know that. You analyzed that in 2024 already, didn't you? > > > > Regards. > > > > Am 22.04.25 um 14:44 schrieb dzeri96 via Opensc-devel: > > > > > Hello everyone, > > > > > > I'm trying to kickstart support for the new Montenegrin eID, or at least figure out how it works. I've sent multiple requests for technical specs to the government, but unless I take them to court, I doubt I'll get any useful information. Therefore I'll just write down what I manage to figure out on my own, and hopefully you can provide further insight. One thing about a country as small as Montenegro, is that there is a very high probability we didn't implement anything custom, as it's not financially viable. > > > > > > Here's what I have so far: > > > > > > - ATR: 3b:dc:96:ff:81:91:fe:1f:c3:80:73:c8:21:13:66:05:03:63:51:00:02:de. It doesn't seem to comply with the ATR scheme in the IAS ECC specification, even though the government says the card complies with all EU ID regulations (unclear which ones). > > > - EF.ATR raw data: 80004301B946040400ECC24703940180 4F0BF0496173456363526F6F74E01002 020104020200E6020200E6020200E678 0806062B8122F8780282029000 > > > - EF.DIR raw data: 61374F0EE828BD080FD25047656E6572 6963500743686970446F63731C300404 025031A004040250324F0EE828BD080FD2504543432D654944610F4F07A00000 0247100150044943414F61184F0A4D4F 4E54454E4547524F500A4E6174696F6E 616C4944 > > > > > > - By deciphering the EF.DIR data, we can discover 4 applications: > > > > > > - E828BD080FD25047656E65726963 - ECC Generic PKI / ChipDocs Applet > > > - E828BD080FD2504543432D654944 - ECC eID > > > - A0000002471001 - ICAO > > > - 4D4F4E54454E4547524F - Spells out MONTENEGRO in ASCII, label is "NationalID". No idea what this could be... maybe something related to healthcare? > > > > > > - I managed to use npa-tool and read the MRZ stored on the card using CAN-based PACE, but all other functions of the tool don't work, not even PIN-based PACE. I'm just using it as an APDU debugger with PACE support. > > > - The official middleware supplied by the government is Athena IDProtect. > > > - The activation software is available here. It's a java program developed by Mühlbauer. I decompiled it and saw that it's accessing the ECC eID application. I managed to extract some APDUs and get the activation status of the card (PIN change is required on first use). > > > - iasecc-tool and pkcs15-tool say "Card is invalid or cannot be handled" regardless of what I try. > > > > > > I've skimmed over hundreds of pages of standards, including the ISO-7816 parts, the NXP ChipDoc v4 spec, the BSI TR-03110, the IAS ECC spec, but I can barely find any concrete info on these applications. Someone must know how to access them because there are vendor-provided tools to do so. > > > > > > My goals are: > > > > > > 1. Get general knowledge about the card and build some PoC APDU chains to read/set data. > > > 2. Get the birthdate of the person via PIN-based auth and verify the authenticity of the data. > > > 3. Get the openSC suite of tools to work with the card. > > > 4. Replace the closed-source middleware provided by the government. > > > > > > > > > I would really appreciate any help here. Thanks! > > > > > > > > > > > > > > > _______________________________________________ > > > Opensc-devel mailing list > > > Ope...@li... > > > https://lists.sourceforge.net/lists/listinfo/opensc-devel > > > > _______________________________________________ > > Opensc-devel mailing list > > Ope...@li... > > https://lists.sourceforge.net/lists/listinfo/opensc-devel |