c++delphidllsmartcardglobalplatform

Translating GlobalPlatform from C to Delphi - Access violation errors


I want to use the GlobalPlatform.dll from kaoh Karsten Ohme in Delphi. So i tried to translate the headers so i can use the GlobalPlatform.dll in Delphi.

The first i translated was Connection.h, i uploaded it on pastebin here.
The second i translated was Security.h i uploaded it on pastebin here.

First i establish a context with the OPGP_establish_context function, that seems to go alright because the result is a OPGP_ERROR_STATUS_SUCCESS and the message also states "success".

But then i try to list the readers with the OPGP_list_readers function, which also returns a success - but when i try to read the returned names i get various access violations (mostly at adress 00000000 and trying to read 00000000, but there are variations between my tries).

My code is assigned to a button click:

procedure TfrmFormatCard.Button1Click(Sender: TObject);
const
  BUFLEN = 1024;
var
  Status,
  Status2 : OPGP_ERROR_STATUS;
  Context : OPGP_CARD_CONTEXT;
  Names   : array [0..BUFLEN +1] of Char;
  Len     : DWord;
begin
  Context.libraryName    := 'gppcscconnectionplugin';
  Context.libraryVersion := '211';
  Status := OPGP_establish_context(Context);
  if Status.errorStatus = OPGP_ERROR_STATUS_SUCCESS then
  begin
    Len := 1024;
    Status2 := OPGP_list_readers(Context, Names, Len);
    if Status2.errorStatus = OPGP_ERROR_STATUS_SUCCESS then
    begin
      // Messagebox(application.Handle, names, '', 0);
    end;
    OPGP_release_context(Context);
  end;
end;

When i use the above code i get no errors, but when i uncomment the messagebox - i get the access violations. I have been trying all day, and i modified everything .. but no luck. I can't see what im doing wrong. Maybe someone can help me out and point me in the right direction. I understand what a access violation on adress 00000000 means, but i dont know if i translated the headers the right way what might cause the error.

If someone could help me by checking, or testing it themselves - that would be highly appreciated.

I am using Delphi 10.4, and i have a internal smartcard reader (in the laptop), a Omnikey smartcard reader, and another unknown brand.

ps. Yes i am aware of the GPShell commandline utility, but i would like to avoid having to use that. I want to use smartcards for security, and the need for the commandline tool would make this a weak point - hence why i want to use the Library directly.


Solution

  • In the 1st record you translated, OPGP_ERROR_STATUS, the errorMessage field is declared in the C code as:

    TCHAR errorMessage[ERROR_MESSAGE_LENGTH+1];
    

    where ERROR_MESSAGE_LENGTH is defined as 256, thus this array has 257 chars max.

    But your translation:

    errorMessage : array [0..ERROR_MESSAGE_LENGTH + 1] of Char;
    

    has 258 chars max. This is because an array declaration in Delphi defines the indexes of the array, inclusive, so in your case you are declaring the array as having indexes 0..257, but it should be 0..256 instead, so drop the +1:

    errorMessage : array [0..ERROR_MESSAGE_LENGTH] of Char;
    

    You are making that same mistake in your translation of the OPGP_CARD_CONTEXT record, too:

    OPGP_CARD_CONTEXT = record
      librarySpecific     : Pointer;
      libraryName         : array [0..64] of Char; // <--
      libraryVersion      : array [0..32] of Char; // <--
      libraryHandle       : Pointer;
      connectionFunctions : OPGP_CONNECTION_FUNCTIONS;
    end;
    

    You are declaring libraryName as having 65 chars, and libraryVersion as having 33 chars. They need to be 64 and 32 instead, respectively:

    OPGP_CARD_CONTEXT = record
      librarySpecific     : Pointer;
      libraryName         : array [0..63] of Char;
      libraryVersion      : array [0..31] of Char;
      libraryHandle       : Pointer;
      connectionFunctions : OPGP_CONNECTION_FUNCTIONS;
    end;
    

    Per the original C declaration:

    typedef struct {
        PVOID librarySpecific; //!< Library specific data.
        TCHAR libraryName[64]; //!< The name of the connection library to use.
        TCHAR libraryVersion[32]; //!< The version of the connection library to use.
        PVOID libraryHandle; //!< The handle to the library.
        OPGP_CONNECTION_FUNCTIONS connectionFunctions; //!< Connection functions of the connection library. Is automatically filled in if the connection library can be loaded correctly.
    } OPGP_CARD_CONTEXT;
    

    So, it makes sense why an AV could occur, since OPGP_list_readers() internally accesses function pointers that are stored in the Context.connectionFunctions field following the arrays, thus the pointers would be accessed at the wrong memory offsets.

    Something else to watch out for is TCHAR, which will map to either char or wchar_t depending on how the DLL is actually compiled. So that may or may not translate to Char in Delphi, depending on what version you are using (which you didn't say). In general, char -> AnsiChar, wchar_t -> WideChar. The project's unicode.h file suggests non-Windows builds are compiled to use char. But the project makefile suggests the Windows build is compiled to use wchar_t instead. It is not a good idea to use (P)Char in interop code because of this. Use (P)AnsiChar or (P)WideChar as needed instead.

    UPDATE

    Also, try zeroing out the memory of the Context before passing it to OPGP_establish_context(). The first thing OPGP_establish_context() does internally is call OPGP_release_context() on the Context, which means the Context can't contain any garbage in it (particularly in the libraryHandle and connectionFunctions.releaseContext fields) or else it will be mishandled.