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("=
;Memory allocation failed\n"));<br> return;<br> }<br><br> for (DWORD =
i =3D 0; i < length; i++) {<br> _stprintf_s(&hexStr[i * 3], 4, TEXT=
("%02X "), buffer[i]);<br> }<br> hexStr[3 * length] =3D '\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("pbSendBuffer: "));<br> PrintHexToDebug(pbSendBuf=
fer, cbSendLength);<br> OutputDebugString(TEXT("\n"));<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("Return code=
: %08X\n"), result);<br> OutputDebugString(returnCodeStr);<br><br> // =
If the return code is successful, dump the output buffer<br> if (result =3D=
=3D SCARD_S_SUCCESS && pcbRecvLength && pbRecvBuffer) {<br>=
(TEXT("pbRecvBuffer: "));<br> PrintHexToDebug(pbRecvBuffer, *p=
cbRecvLength);<br> OutputDebugString(TEXT("\n"));<br> }<br><br> =
return result;<br>}</div><div><br></div><div>VOID EnableHook(HMODULE hModul=
e)<br>{<br> HMODULE hScard =3D LoadLibrary(TEXT("Winscard.dll"));=
<br> PROC pfnScardTransmit =3D GetProcAddress(hScard, "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->e_lfanew);<br> PIMAGE_IMPORT_DESCRIPTOR pImportDesc =3D (PIMAGE_=
IMPORT_DESCRIPTOR)((BYTE*)hModule + pNtHeaders->OptionalHeader.DataDirec=
tory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);<br><br> while (pImportD=
esc->Name) {<br> LPCSTR pszModName =3D (LPCSTR)((BYTE*)hModule + pImpor=
tDesc->Name);<br> if (_stricmp(pszModName, "Winscard.dll") =
=3D=3D 0) {<br> PIMAGE_THUNK_DATA pThunk =3D (PIMAGE_THUNK_DATA)((BYTE*)h=
Module + pImportDesc->FirstThunk);<br> while (pThunk->u1.Function) =
{<br> PROC* ppfn =3D (PROC*)&pThunk->u1.Function;<br> if (*ppf=
n =3D=3D (PROC)pfnScardTransmit) {<br> DWORD oldProtect;<br> Virtua=
lProtect(ppfn, sizeof(PROC), PAGE_EXECUTE_READWRITE, &oldProtect);<br> =
*ppfn =3D (PROC)MySCardTransmit;<br> VirtualProtect(ppfn, sizeof(PR=
OC), oldProtect, &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> &hSCar=
dContext);<br> if (SCARD_S_SUCCESS !=3D dwReturn)<br> {<br> __leave;<br=
> }<br><br> // Initialize the structure.<br> memset(&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"Select Card=
";<br> dlgStruct.dwShareMode =3D 0;<br> // Display the select card d=
ialog box.<br> dwReturn =3D SCardUIDlgSelectCard(&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)&szCardModule,<br> &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("Name of the dll.dll"));<=
br>#else<br> _tcscpy_s(szCardModule, dwCardModuleSize, TEXT("Name of=
the dll.dll"));<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, &hSCardHandle, &dwProtocol);<br> if (=
SCARD_S_SUCCESS !=3D dwReturn)<br> {<br> __leave;<br> }<br> atr.cbAtr =
=3D 32;<br> dwReturn =3D SCardStatus(hSCardHandle, szReader, &dwReader=
Size, &dwState, &dwProtocol, atr.rgbAtr, &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, "CardAcquireContext")))<br> {<br> dwReturn =3D Get=
LastError();<br> __leave;<br> }<br> // initialize context<br> ////////=
//////////////<br> pCardData =3D &CardData;<br> pCardData->dwVersi=
on =3D CARD_DATA_CURRENT_VERSION;<br> pCardData->pfnCspAlloc =3D _Alloc=
;<br> pCardData->pfnCspFree =3D _Free;<br> pCardData->pfnCspReAlloc=
=3D _ReAlloc;<br> pCardData->pfnCspCacheAddFile =3D _CacheAddFileStub;=
<br> pCardData->pfnCspCacheLookupFile =3D _CacheLookupFileStub;<br> pC=
ardData->pfnCspCacheDeleteFile =3D _CacheDeleteFileStub;<br> pCardData-=
>hScard =3D hSCardHandle;<br> pCardData->hSCardCtx =3D hSCardContext=
;<br> pCardData->cbAtr =3D atr.cbAtr;<br> pCardData->pbAtr =3D atr.=
rgbAtr;<br> pCardData->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->hScard)<br> {<br> SCardEndT=
ransaction(pCardData->hScard, SCARD_LEAVE_CARD);<br> SCardDisconnect(p=
CardData->hScard, 0);<br> }<br> if (pCardData->hSCardCtx)<br> SCa=
rdReleaseContext(pCardData->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->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 <<a href=3D"mailto:[email protected]"=
>[email protected]</a>> 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'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'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'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.</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'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'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:"Mono""><span=
style=3D"font-size:15px;line-height:24px;letter-spacing:-0.08px"><kbd styl=
e=3D"display:inline;font-family:"Mono";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:"Mono""><span=
style=3D"font-size:15px;line-height:24px;letter-spacing:-0.08px"><kbd styl=
e=3D"display:inline;font-family:"Mono";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:"Mono""><span=
style=3D"font-size:15px;line-height:24px;letter-spacing:-0.08px"><kbd styl=
e=3D"display:inline;font-family:"Mono";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 "NationalID". 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't work, not even
PIN-based PACE. I'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'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'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 "Card is invalid or cannot be handled" regardless=
of
what I try.</li>
</ul>
<div>I'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:"1. "">=
<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:"2. "">=
<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:"3. "">=
Get the
openSC suite of tools to work with the card.</li>
<li style=3D"font-size:10.5pt;list-style-type:"4. "">=
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==--