Re: Support for the Montenegrin eID

Vincent Le Toux <[email protected]> Sat, 26 Apr 2025 08:49:56 +0000
Newsgroups gmane.comp.encryption.opensc.devel
Message-ID <011001967149fc23-83637ffe-7ba8-419b-8f2b-07b50fa9a98a-000000@eu-north-1.amazonses.com>
--===============3276615978543762410==
Content-Type: multipart/alternative; boundary="000000000000d216330633aa8888"

--000000000000d216330633aa8888
Content-Type: text/plain; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

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 =3D (TCHAR*)malloc((3 * length + 1) * sizeof(TCHAR));
if (hexStr =3D=3D NULL) {
OutputDebugString(TEXT("Memory allocation failed\n"));
return;
}

for (DWORD i =3D 0; i < length; i++) {
_stprintf_s(&hexStr[i * 3], 4, TEXT("%02X "), buffer[i]);
}
hexStr[3 * length] =3D '\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 =3D 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 =3D=3D SCARD_S_SUCCESS && pcbRecvLength && pbRecvBuffer) {
(TEXT("pbRecvBuffer: "));
PrintHexToDebug(pbRecvBuffer, *pcbRecvLength);
OutputDebugString(TEXT("\n"));
}

return result;
}

VOID EnableHook(HMODULE hModule)
{
HMODULE hScard =3D LoadLibrary(TEXT("Winscard.dll"));
PROC pfnScardTransmit =3D GetProcAddress(hScard, "SCardTransmit");
PIMAGE_DOS_HEADER pDosHeader =3D (PIMAGE_DOS_HEADER)hModule;
PIMAGE_NT_HEADERS pNtHeaders =3D (PIMAGE_NT_HEADERS)((BYTE*)hModule +
pDosHeader->e_lfanew);
PIMAGE_IMPORT_DESCRIPTOR pImportDesc =3D
(PIMAGE_IMPORT_DESCRIPTOR)((BYTE*)hModule +
pNtHeaders->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].Virt=
ualAddress);

while (pImportDesc->Name) {
LPCSTR pszModName =3D (LPCSTR)((BYTE*)hModule + pImportDesc->Name);
if (_stricmp(pszModName, "Winscard.dll") =3D=3D 0) {
PIMAGE_THUNK_DATA pThunk =3D (PIMAGE_THUNK_DATA)((BYTE*)hModule +
pImportDesc->FirstThunk);
while (pThunk->u1.Function) {
PROC* ppfn =3D (PROC*)&pThunk->u1.Function;
if (*ppfn =3D=3D (PROC)pfnScardTransmit) {
DWORD oldProtect;
VirtualProtect(ppfn, sizeof(PROC), PAGE_EXECUTE_READWRITE, &oldProtect);
*ppfn =3D (PROC)MySCardTransmit;
VirtualProtect(ppfn, sizeof(PROC), oldProtect, &oldProtect);
}
pThunk++;
}
break;
}
pImportDesc++;
}
}


And to initialize the minidriver:


DWORD Connect(BOOL fSystemDll =3D TRUE)
{
DWORD dwReturn =3D 0;
SCARDCONTEXT     hSCardContext =3D NULL;
SCARDHANDLE hSCardHandle =3D NULL;
TCHAR szCardModule[256];
TCHAR szReader[256];
DWORD dwCardModuleSize =3D ARRAYSIZE(szCardModule);
DWORD dwReaderSize =3D ARRAYSIZE(szReader);
OPENCARDNAME_EX  dlgStruct;
PFN_CARD_ACQUIRE_CONTEXT pfnCardAcquireContext;

__try
{
// find a smart card
/////////////////////

dwReturn =3D SCardEstablishContext(SCARD_SCOPE_USER,
NULL,
NULL,
&hSCardContext);
if (SCARD_S_SUCCESS !=3D dwReturn)
{
__leave;
}

// Initialize the structure.
memset(&dlgStruct, 0, sizeof(dlgStruct));
dlgStruct.dwStructSize =3D sizeof(dlgStruct);
dlgStruct.hSCardContext =3D hSCardContext;
dlgStruct.dwFlags =3D SC_DLG_MINIMAL_UI;
dlgStruct.lpstrRdr =3D szReader;
dlgStruct.nMaxRdr =3D dwReaderSize;
dlgStruct.lpstrCard =3D szCard;
dlgStruct.nMaxCard =3D ARRAYSIZE(szCard);
dlgStruct.lpstrTitle =3D L"Select Card";
dlgStruct.dwShareMode =3D 0;
// Display the select card dialog box.
dwReturn =3D SCardUIDlgSelectCard(&dlgStruct);
if (SCARD_S_SUCCESS !=3D dwReturn)
{
__leave;
}

// find the dll path / name
////////////////////////////
if (fSystemDll)
{


dwReturn =3D SCardGetCardTypeProviderName(
hSCardContext,
szCard,
SCARD_PROVIDER_CARD_MODULE,
(PTSTR)&szCardModule,
&dwCardModuleSize);
if (0 =3D=3D dwCardModuleSize)
{
dwReturn =3D (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 =3D SCardConnect(hSCardContext, szReader, SCARD_SHARE_SHARED,
SCARD_PROTOCOL_T1 | SCARD_PROTOCOL_T0, &hSCardHandle, &dwProtocol);
if (SCARD_S_SUCCESS !=3D dwReturn)
{
__leave;
}
atr.cbAtr =3D 32;
dwReturn =3D SCardStatus(hSCardHandle, szReader, &dwReaderSize, &dwState,
&dwProtocol, atr.rgbAtr, &atr.cbAtr);
if (SCARD_S_SUCCESS !=3D dwReturn)
{
__leave;
}
// load
////////
if (NULL =3D=3D (hModule =3D LoadLibrary(szCardModule)))
{
dwReturn =3D GetLastError();
__leave;
}
if (fSystemDll)
{
EnableHook(hModule);
}
if (NULL =3D=3D (pfnCardAcquireContext =3D
(PFN_CARD_ACQUIRE_CONTEXT)GetProcAddress(
hModule, "CardAcquireContext")))
{
dwReturn =3D GetLastError();
__leave;
}
// initialize context
//////////////////////
pCardData =3D &CardData;
pCardData->dwVersion =3D CARD_DATA_CURRENT_VERSION;
pCardData->pfnCspAlloc =3D _Alloc;
pCardData->pfnCspFree =3D _Free;
pCardData->pfnCspReAlloc =3D _ReAlloc;
pCardData->pfnCspCacheAddFile =3D _CacheAddFileStub;
pCardData->pfnCspCacheLookupFile =3D _CacheLookupFileStub;
pCardData->pfnCspCacheDeleteFile =3D _CacheDeleteFileStub;
pCardData->hScard =3D hSCardHandle;
pCardData->hSCardCtx =3D hSCardContext;
pCardData->cbAtr =3D atr.cbAtr;
pCardData->pbAtr =3D atr.rgbAtr;
pCardData->pwszCardName =3D szCard;
//dwReturn =3D SCardBeginTransaction(hSCardHandle);
if (SCARD_S_SUCCESS !=3D dwReturn)
{
__leave;
}
dwReturn =3D pfnCardAcquireContext(pCardData, 0);
}
__finally
{
if (dwReturn !=3D 0)
{
if (hSCardHandle)
{
SCardEndTransaction(hSCardHandle, SCARD_LEAVE_CARD);
SCardDisconnect(hSCardHandle, 0);
}
if (hSCardContext)
SCardReleaseContext(hSCardContext);
}
}
return dwReturn;
}

DWORD Disconnect()
{
DWORD dwReturn =3D 0;
if (pCardData)
{
if (pCardData->hScard)
{
SCardEndTransaction(pCardData->hScard, SCARD_LEAVE_CARD);
SCardDisconnect(pCardData->hScard, 0);
}
if (pCardData->hSCardCtx)
SCardReleaseContext(pCardData->hSCardCtx);
pCardData =3D NULL;
}
else
{
dwReturn =3D 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 =3D SCARD_E_COMM_DATA_LOST;
__leave;
}
switch(dwIndex)
{
case 0: //Signature,
dwKeySpec =3D AT_SIGNATURE;
PinId =3D ROLE_USER;
break;
case 2: //Authentication,
dwKeySpec =3D AT_SIGNATURE;
PinId =3D 3;
break;
case 1: // Confidentiality,
dwKeySpec =3D AT_KEYEXCHANGE;
PinId =3D 4;
break;
default:
dwReturn =3D SCARD_E_UNEXPECTED;
__leave;
}
dwReturn =3D pCardData->pfnCardCreateContainerEx(pCardData, (BYTE) dwIndex,
CARD_CREATE_CONTAINER_KEY_GEN,
dwKeySpec, 1024, NULL, PinId);
}
__finally
{
}
return dwReturn;
}

br
Vincent


Le ven. 25 avr. 2025 =C3=A0 22:53, Frank Morgner <[email protected]> a
=C3=A9crit :

> 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
> <https://www.gov.me/mup/elk>, 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 specificatio=
n,
>    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 025031A004040250324F0EE828BD080F
>    D2504543432D654944610F4F07A00000 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 ev=
en
>    PIN-based PACE. I'm just using it as an APDU debugger with PACE suppor=
t.
>    - The official middleware supplied by the government is Athena
>    IDProtect.
>    - The activation software is available here
>    <https://wapi.gov.me/download/e63b50c5-9ccc-4034-961f-5bb401a9b375?ver=
sion=3D1.0>.
>    It's a java program developed by M=C3=BChlbauer <https://www.muehlbaue=
r.de/>.
>    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 car=
d
>    (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 kno=
w
> 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 [email protected]://lists.=
sourceforge.net/lists/listinfo/opensc-devel
>
> _______________________________________________
> Opensc-devel mailing list
> [email protected]
> https://lists.sourceforge.net/lists/listinfo/opensc-devel
>

--000000000000d216330633aa8888
Content-Type: text/html; charset="UTF-8"
Content-Transfer-Encoding: quoted-printable

<div dir=3D"ltr"><div>Inside the middleware, there is a minidriver named ci=
amd.dll</div><div><br></div><div>What I would suggest is to write a program=
 like the one I wrote here (<a href=3D"https://github.com/vletoux/openpgpmd=
rv/tree/master/OpenPGPminidriverTest">https://github.com/vletoux/openpgpmdr=
v/tree/master/OpenPGPminidriverTest</a>) that connects to the minidriver an=
d realize basic functions (enumerating public keys, certificates, encrypts,=
 change pin, etc).</div><div>You can add a hook to dump the instructions se=
nt to the card.</div><div><br></div><div>You can use the following code to =
hook the SCardTransmit function:</div><div><br></div><div><br></div><div>vo=
id PrintHexToDebug(const BYTE* buffer, DWORD length) {<br>	// Allocate memo=
ry dynamically<br>	TCHAR* hexStr =3D (TCHAR*)malloc((3 * length + 1) * size=
of(TCHAR));<br>	if (hexStr =3D=3D NULL) {<br>		OutputDebugString(TEXT(&quot=
;Memory allocation failed\n&quot;));<br>		return;<br>	}<br><br>	for (DWORD =
i =3D 0; i &lt; length; i++) {<br>		_stprintf_s(&amp;hexStr[i * 3], 4, TEXT=
(&quot;%02X &quot;), buffer[i]);<br>	}<br>	hexStr[3 * length] =3D &#39;\0&#=
39;;<br>	OutputDebugString(hexStr);<br><br>	// Free the allocated memory<br=
>	free(hexStr);<br>}<br><br>LONG WINAPI MySCardTransmit(<br>	SCARDHANDLE hC=
ard,<br>	LPCSCARD_IO_REQUEST pioSendPci,<br>	LPCBYTE pbSendBuffer,<br>	DWOR=
D cbSendLength,<br>	LPSCARD_IO_REQUEST pioRecvPci,<br>	LPBYTE pbRecvBuffer,=
<br>	LPDWORD pcbRecvLength<br>) {<br>	// Trace the input buffer<br>	OutputD=
ebugString(TEXT(&quot;pbSendBuffer: &quot;));<br>	PrintHexToDebug(pbSendBuf=
fer, cbSendLength);<br>	OutputDebugString(TEXT(&quot;\n&quot;));<br>	// Cal=
l the original SCardTransmit<br>	LONG result =3D SCardTransmit(hCard, pioSe=
ndPci, pbSendBuffer, cbSendLength, pioRecvPci, pbRecvBuffer, pcbRecvLength)=
;<br><br>	// Write the return code as hex<br>	TCHAR returnCodeStr[30];<br>	=
_stprintf_s(returnCodeStr, ARRAYSIZE(returnCodeStr), TEXT(&quot;Return code=
: %08X\n&quot;), result);<br>	OutputDebugString(returnCodeStr);<br><br>	// =
If the return code is successful, dump the output buffer<br>	if (result =3D=
=3D SCARD_S_SUCCESS &amp;&amp; pcbRecvLength &amp;&amp; pbRecvBuffer) {<br>=
		(TEXT(&quot;pbRecvBuffer: &quot;));<br>		PrintHexToDebug(pbRecvBuffer, *p=
cbRecvLength);<br>		OutputDebugString(TEXT(&quot;\n&quot;));<br>	}<br><br>	=
return result;<br>}</div><div><br></div><div>VOID EnableHook(HMODULE hModul=
e)<br>{<br>	HMODULE hScard =3D LoadLibrary(TEXT(&quot;Winscard.dll&quot;));=
<br>	PROC pfnScardTransmit =3D GetProcAddress(hScard, &quot;SCardTransmit&q=
uot;);<br>	PIMAGE_DOS_HEADER pDosHeader =3D (PIMAGE_DOS_HEADER)hModule;<br>=
	PIMAGE_NT_HEADERS pNtHeaders =3D (PIMAGE_NT_HEADERS)((BYTE*)hModule + pDos=
Header-&gt;e_lfanew);<br>	PIMAGE_IMPORT_DESCRIPTOR pImportDesc =3D (PIMAGE_=
IMPORT_DESCRIPTOR)((BYTE*)hModule + pNtHeaders-&gt;OptionalHeader.DataDirec=
tory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);<br><br>	while (pImportD=
esc-&gt;Name) {<br>		LPCSTR pszModName =3D (LPCSTR)((BYTE*)hModule + pImpor=
tDesc-&gt;Name);<br>		if (_stricmp(pszModName, &quot;Winscard.dll&quot;) =
=3D=3D 0) {<br>			PIMAGE_THUNK_DATA pThunk =3D (PIMAGE_THUNK_DATA)((BYTE*)h=
Module + pImportDesc-&gt;FirstThunk);<br>			while (pThunk-&gt;u1.Function) =
{<br>				PROC* ppfn =3D (PROC*)&amp;pThunk-&gt;u1.Function;<br>				if (*ppf=
n =3D=3D (PROC)pfnScardTransmit) {<br>					DWORD oldProtect;<br>					Virtua=
lProtect(ppfn, sizeof(PROC), PAGE_EXECUTE_READWRITE, &amp;oldProtect);<br>	=
				*ppfn =3D (PROC)MySCardTransmit;<br>					VirtualProtect(ppfn, sizeof(PR=
OC), oldProtect, &amp;oldProtect);<br>				}<br>				pThunk++;<br>			}<br>			=
break;<br>		}<br>		pImportDesc++;<br>	}<br>}<br></div><div><br></div><div><=
br></div><div>And to initialize the minidriver:</div><div><br></div><div><b=
r></div><div>DWORD Connect(BOOL fSystemDll =3D TRUE)<br>{<br>	DWORD dwRetur=
n =3D 0;<br>	SCARDCONTEXT =C2=A0 =C2=A0 hSCardContext =3D NULL;<br>	SCARDHA=
NDLE hSCardHandle =3D NULL;<br>	TCHAR szCardModule[256];<br>	TCHAR szReader=
[256];<br>	DWORD dwCardModuleSize =3D ARRAYSIZE(szCardModule);<br>	DWORD dw=
ReaderSize =3D ARRAYSIZE(szReader);<br>	OPENCARDNAME_EX =C2=A0dlgStruct;<br=
>	PFN_CARD_ACQUIRE_CONTEXT pfnCardAcquireContext;<br><br>	__try<br>	{<br>		=
// find a smart card<br>		/////////////////////<br><br>		dwReturn =3D SCard=
EstablishContext(SCARD_SCOPE_USER,<br>			NULL,<br>			NULL,<br>			&amp;hSCar=
dContext);<br>		if (SCARD_S_SUCCESS !=3D dwReturn)<br>		{<br>			__leave;<br=
>		}<br><br>		// Initialize the structure.<br>		memset(&amp;dlgStruct, 0, s=
izeof(dlgStruct));<br>		dlgStruct.dwStructSize =3D sizeof(dlgStruct);<br>		=
dlgStruct.hSCardContext =3D hSCardContext;<br>		dlgStruct.dwFlags =3D SC_DL=
G_MINIMAL_UI;<br>		dlgStruct.lpstrRdr =3D szReader;<br>		dlgStruct.nMaxRdr =
=3D dwReaderSize;<br>		dlgStruct.lpstrCard =3D szCard;<br>		dlgStruct.nMaxC=
ard =3D ARRAYSIZE(szCard);<br>		dlgStruct.lpstrTitle =3D L&quot;Select Card=
&quot;;<br>		dlgStruct.dwShareMode =3D 0;<br>		// Display the select card d=
ialog box.<br>		dwReturn =3D SCardUIDlgSelectCard(&amp;dlgStruct);<br>		if =
(SCARD_S_SUCCESS !=3D dwReturn)<br>		{<br>			__leave;<br>		}<br><br>		// fi=
nd the dll path / name<br>		////////////////////////////<br>		if (fSystemDl=
l)<br>		{<br><br><br>			dwReturn =3D SCardGetCardTypeProviderName(<br>				h=
SCardContext,<br>				szCard,<br>				SCARD_PROVIDER_CARD_MODULE,<br>				(PTS=
TR)&amp;szCardModule,<br>				&amp;dwCardModuleSize);<br>			if (0 =3D=3D dwC=
ardModuleSize)<br>			{<br>				dwReturn =3D (DWORD)SCARD_E_UNKNOWN_CARD;<br>=
				__leave;<br>			}<br>		}<br>		else<br>		{<br>#ifdef _M_X64<br>			_tcscpy=
_s(szCardModule, dwCardModuleSize, TEXT(&quot;Name of the dll.dll&quot;));<=
br>#else<br>			_tcscpy_s(szCardModule, dwCardModuleSize, TEXT(&quot;Name of=
 the dll.dll&quot;));<br>#endif<br>		}<br>		// connect to the smart card<br=
>		////////////////////////////<br>		DWORD dwProtocol, dwState;<br>		dwRetu=
rn =3D SCardConnect(hSCardContext, szReader, SCARD_SHARE_SHARED, SCARD_PROT=
OCOL_T1 | SCARD_PROTOCOL_T0, &amp;hSCardHandle, &amp;dwProtocol);<br>		if (=
SCARD_S_SUCCESS !=3D dwReturn)<br>		{<br>			__leave;<br>		}<br>		atr.cbAtr =
=3D 32;<br>		dwReturn =3D SCardStatus(hSCardHandle, szReader, &amp;dwReader=
Size, &amp;dwState, &amp;dwProtocol, atr.rgbAtr, &amp;atr.cbAtr);<br>		if (=
SCARD_S_SUCCESS !=3D dwReturn)<br>		{<br>			__leave;<br>		}<br>		// load<br=
>		////////<br>		if (NULL =3D=3D (hModule =3D LoadLibrary(szCardModule)))<b=
r>		{<br>			dwReturn =3D GetLastError();<br>			__leave;<br>		}<br>		if (fSy=
stemDll)<br>		{<br>			EnableHook(hModule);<br>		}<br>		if (NULL =3D=3D (pfn=
CardAcquireContext =3D<br>			(PFN_CARD_ACQUIRE_CONTEXT)GetProcAddress(<br>	=
			hModule, &quot;CardAcquireContext&quot;)))<br>		{<br>			dwReturn =3D Get=
LastError();<br>			__leave;<br>		}<br>		// initialize context<br>		////////=
//////////////<br>		pCardData =3D &amp;CardData;<br>		pCardData-&gt;dwVersi=
on =3D CARD_DATA_CURRENT_VERSION;<br>		pCardData-&gt;pfnCspAlloc =3D _Alloc=
;<br>		pCardData-&gt;pfnCspFree =3D _Free;<br>		pCardData-&gt;pfnCspReAlloc=
 =3D _ReAlloc;<br>		pCardData-&gt;pfnCspCacheAddFile =3D _CacheAddFileStub;=
<br>		pCardData-&gt;pfnCspCacheLookupFile =3D _CacheLookupFileStub;<br>		pC=
ardData-&gt;pfnCspCacheDeleteFile =3D _CacheDeleteFileStub;<br>		pCardData-=
&gt;hScard =3D hSCardHandle;<br>		pCardData-&gt;hSCardCtx =3D hSCardContext=
;<br>		pCardData-&gt;cbAtr =3D atr.cbAtr;<br>		pCardData-&gt;pbAtr =3D atr.=
rgbAtr;<br>		pCardData-&gt;pwszCardName =3D szCard;<br>		//dwReturn =3D SCa=
rdBeginTransaction(hSCardHandle);<br>		if (SCARD_S_SUCCESS !=3D dwReturn)<b=
r>		{<br>			__leave;<br>		}<br>		dwReturn =3D pfnCardAcquireContext(pCardDa=
ta, 0);<br>	}<br>	__finally<br>	{<br>		if (dwReturn !=3D 0)<br>		{<br>			if=
 (hSCardHandle)<br>			{<br>				SCardEndTransaction(hSCardHandle, SCARD_LEAV=
E_CARD);<br>				SCardDisconnect(hSCardHandle, 0);<br>			}<br>			if (hSCardC=
ontext)<br>				SCardReleaseContext(hSCardContext);<br>		}<br>	}<br>	return =
dwReturn;<br>}<br><br>DWORD Disconnect()<br>{<br>	DWORD dwReturn =3D 0;<br>=
	if (pCardData)<br>	{<br>		if (pCardData-&gt;hScard)<br>		{<br>			SCardEndT=
ransaction(pCardData-&gt;hScard, SCARD_LEAVE_CARD);<br>			SCardDisconnect(p=
CardData-&gt;hScard, 0);<br>		}<br>		if (pCardData-&gt;hSCardCtx)<br>			SCa=
rdReleaseContext(pCardData-&gt;hSCardCtx);<br>		pCardData =3D NULL;<br>	}<b=
r>	else<br>	{<br>		dwReturn =3D SCARD_E_COMM_DATA_LOST;<br>	}<br>	return dw=
Return;<br>}</div><div><br></div><div>You can then call directly :</div><di=
v><br></div><div>DWORD GenerateNewKey(DWORD dwIndex)<br>{<br>	DWORD dwRetur=
n, dwKeySpec;<br>	PIN_ID =C2=A0PinId;<br>	__try<br>	{<br>		 if (!pCardData)=
<br>		{<br>			dwReturn =3D SCARD_E_COMM_DATA_LOST;<br>			__leave;<br>		}<br=
>		switch(dwIndex)<br>		{<br>		case 0:	//Signature,<br>			dwKeySpec =3D AT_=
SIGNATURE;<br>			PinId =3D ROLE_USER;<br>			break;<br>		case 2: //Authentic=
ation,<br>			dwKeySpec =3D AT_SIGNATURE;<br>			PinId =3D 3;<br>			break;<br=
>		case 1: // Confidentiality,<br>			dwKeySpec =3D AT_KEYEXCHANGE;<br>			Pi=
nId =3D 4;<br>			break;<br>		default:<br>			dwReturn =3D SCARD_E_UNEXPECTED=
;<br>			__leave;<br>		}<br>		dwReturn =3D pCardData-&gt;pfnCardCreateContai=
nerEx(pCardData, (BYTE) dwIndex, <br>											CARD_CREATE_CONTAINER_KEY_G=
EN, <br>											dwKeySpec, 1024, NULL, PinId);<br>	}<br>	__finally<br>	{=
<br>	}<br>	return dwReturn;<br>}</div><div><br></div><div>br</div><div>Vinc=
ent</div><div><br></div></div><br><div class=3D"gmail_quote gmail_quote_con=
tainer"><div dir=3D"ltr" class=3D"gmail_attr">Le=C2=A0ven. 25 avr. 2025 =C3=
=A0=C2=A022:53, Frank Morgner &lt;<a href=3D"mailto:[email protected]"=
>[email protected]</a>&gt; a =C3=A9crit=C2=A0:<br></div><blockquote cl=
ass=3D"gmail_quote" style=3D"margin:0px 0px 0px 0.8ex;border-left:1px solid=
 rgb(204,204,204);padding-left:1ex"><u></u>

 =20
   =20
 =20
  <div>
    <p>The middleware is available on the bottom of this page<br>
      <a href=3D"https://www.gov.me/clanak/preuzmite-software-i-uputstva" t=
arget=3D"_blank">https://www.gov.me/clanak/preuzmite-software-i-uputstva</a=
></p>
    <p>But I think you already know that. You analyzed that in 2024
      already, didn&#39;t you?</p>
    <p>Regards.<br>
    </p>
    <div>Am 22.04.25 um 14:44 schrieb dzeri96
      via Opensc-devel:<br>
    </div>
    <blockquote type=3D"cite">
     =20
      <div style=3D"font-family:Arial,sans-serif;font-size:14px">Hello
        everyone,</div>
      <div style=3D"font-family:Arial,sans-serif;font-size:14px"><br>
      </div>
      <div style=3D"font-family:Arial,sans-serif;font-size:14px">I&#39;m
        trying to kickstart support for the new <a title=3D"Montenegrin eID=
" href=3D"https://www.gov.me/mup/elk" rel=3D"noreferrer nofollow noopener" =
target=3D"_blank">Montenegrin eID</a>, or at least figure
        out how it works. I&#39;ve sent multiple requests for technical
        specs to the government, but unless I take them to court, I
        doubt I&#39;ll get any useful information. Therefore I&#39;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&#39;t implement anything custom, as it&#39;s not financially
        viable.</div>
      <div style=3D"font-family:Arial,sans-serif;font-size:14px"><br>
      </div>
      <div style=3D"font-family:Arial,sans-serif;font-size:14px"><span styl=
e=3D"font-size:13.5pt;line-height:normal">Here&#39;s what I
          have so far:</span></div>
      <div style=3D"font-family:Arial,sans-serif;font-size:14px">
        <ul style=3D"margin-top:0px;margin-bottom:0px">
          <li style=3D"list-style-type:disc"><b>ATR</b>: <span>3b:dc:96:ff:=
81:91:fe:1f:c3:80:73:c8:21:13:66:05:03:63:51:00:02:de</span>.
            It doesn&#39;t seem to comply with the ATR scheme in the IAS EC=
C
            specification, even though the government says the card
            complies with all EU ID regulations (unclear which ones).</li>
          <li style=3D"list-style-type:disc"><b>EF.ATR raw data</b>:
            80004301B946040400ECC24703940180
            4F0BF0496173456363526F6F74E01002
            020104020200E6020200E6020200E678
            0806062B8122F8780282029000</li>
          <li style=3D"list-style-type:disc"><b>EF.DIR raw data</b>: <span>=
61374F0EE828BD080FD25047656E6572
              6963500743686970446F63731C300404
              025031A004040250324F0EE828BD080F</span>
            <div><span>D2504543432D654944610F4F07A00000
                0247100150044943414F61184F0A4D4F
                4E54454E4547524F500A4E6174696F6E </span><span>616C4944</spa=
n></div>
          </li>
          <li style=3D"list-style-type:disc">By deciphering the EF.DIR
            data, we can discover 4 applications:</li>
          <ul style=3D"margin-top:0px;margin-bottom:0px;list-style-type:cir=
cle">
            <li style=3D"font-size:15px;font-family:&quot;Mono&quot;"><span=
 style=3D"font-size:15px;line-height:24px;letter-spacing:-0.08px"><kbd styl=
e=3D"display:inline;font-family:&quot;Mono&quot;;line-height:1.71;backgroun=
d:rgba(247,245,240,0.5);padding:0px 4px;border-radius:2px">E828BD080FD25047=
656E65726963</kbd></span>
              - ECC Generic PKI / ChipDocs Applet</li>
            <li style=3D"font-size:15px;font-family:&quot;Mono&quot;"><span=
 style=3D"font-size:15px;line-height:24px;letter-spacing:-0.08px"><kbd styl=
e=3D"display:inline;font-family:&quot;Mono&quot;;line-height:1.71;backgroun=
d:rgba(247,245,240,0.5);padding:0px 4px;border-radius:2px">E828BD080FD25045=
43432D654944</kbd></span>
              - ECC eID</li>
            <li style=3D"font-size:15px;font-family:&quot;Mono&quot;"><span=
 style=3D"font-size:15px;line-height:24px;letter-spacing:-0.08px"><kbd styl=
e=3D"display:inline;font-family:&quot;Mono&quot;;line-height:1.71;backgroun=
d:rgba(247,245,240,0.5);padding:0px 4px;border-radius:2px">A0000002471001</=
kbd></span>
              - ICAO</li>
            <li>4D4F4E54454E4547524F - Spells out MONTENEGRO in
              ASCII, label is &quot;NationalID&quot;. No idea what this cou=
ld
              be... maybe something related to healthcare?</li>
          </ul>
          <li style=3D"list-style-type:disc">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&#39;t work, not even
            PIN-based PACE. I&#39;m just using it as an APDU debugger with
            PACE support.</li>
          <li style=3D"list-style-type:disc">The official middleware
            supplied by the government is Athena IDProtect.</li>
          <li style=3D"list-style-type:disc">The activation software is
            available <a href=3D"https://wapi.gov.me/download/e63b50c5-9ccc=
-4034-961f-5bb401a9b375?version=3D1.0" title=3D"here" target=3D"_blank">her=
e</a>. It&#39;s a java
            program developed by <a href=3D"https://www.muehlbauer.de/" tit=
le=3D"M=C3=BChlbauer" target=3D"_blank">M=C3=BChlbauer</a>. I
            decompiled it and saw that it&#39;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).</li>
          <li style=3D"list-style-type:disc">iasecc-tool and pkcs15-tool
            say &quot;Card is invalid or cannot be handled&quot; regardless=
 of
            what I try.</li>
        </ul>
        <div>I&#39;ve skimmed over hundreds of pages of standards, includin=
g
          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.</div>
        <div><br>
        </div>
        <div><span style=3D"font-size:13.5pt;line-height:normal">My
            goals are:</span></div>
        <div>
          <ol style=3D"margin-top:0px;margin-bottom:0px">
            <li style=3D"font-size:10.5pt;list-style-type:&quot;1. &quot;">=
<span style=3D"font-size:10.5pt;line-height:normal">Get
                general knowledge about the card and build some PoC APDU
                chains to read/set data.</span></li>
            <li style=3D"font-size:10.5pt;list-style-type:&quot;2. &quot;">=
<span style=3D"font-size:10.5pt;line-height:normal">Get the
                birthdate of the person via PIN-based auth and verify
                the authenticity of the data.</span></li>
            <li style=3D"font-size:10.5pt;list-style-type:&quot;3. &quot;">=
Get the
              openSC suite of tools to work with the card.</li>
            <li style=3D"font-size:10.5pt;list-style-type:&quot;4. &quot;">=
Replace the
              closed-source middleware provided by the government.</li>
          </ol>
          <div><br>
          </div>
          <div>I would really appreciate any help here. Thanks!</div>
        </div>
      </div>
      <br>
      <fieldset></fieldset>
      <br>
      <fieldset></fieldset>
      <pre>_______________________________________________
Opensc-devel mailing list
<a href=3D"mailto:[email protected]" target=3D"_blank">Ope=
[email protected]</a>
<a href=3D"https://lists.sourceforge.net/lists/listinfo/opensc-devel" targe=
t=3D"_blank">https://lists.sourceforge.net/lists/listinfo/opensc-devel</a>
</pre>
    </blockquote>
  </div>

_______________________________________________<br>
Opensc-devel mailing list<br>
<a href=3D"mailto:[email protected]" target=3D"_blank">Ope=
[email protected]</a><br>
<a href=3D"https://lists.sourceforge.net/lists/listinfo/opensc-devel" rel=
=3D"noreferrer" target=3D"_blank">https://lists.sourceforge.net/lists/listi=
nfo/opensc-devel</a><br>
</blockquote></div>

--000000000000d216330633aa8888--


--===============3276615978543762410==
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline


--===============3276615978543762410==
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

--===============3276615978543762410==--