Home > front end >  Incorrect struct members value when passing the struct reference from C# to C
Incorrect struct members value when passing the struct reference from C# to C

Time:06-17

I am working on to use a C library in C#. Library is the globalplatform by kaoh, here is the github link.

What I have done is compiled the C library into a shared library (DLL) by following these steps written in the readme.

cd \path\to\globalplatform
cmake -G "NMake Makefiles" -DZLIB_ROOT="C:\Users\OFFICE\globalplatform\zlib-1.2.8\win32-build" -DCMOCKA_ROOT="C:\Users\OFFICE\Desktop\globalplatform\cmocka-cmocka-1.1.5\build-w32"
nmake

I used dllimport in my C# code and successfully called some of the library functions.

But then I hit a problem when trying to call a certain function that have a specific struct as its parameter. The problem is, its members value in the C side is differs from what it have been assigned in C# side.

I decided to do some testing by replicating the same struct then printing its members value. Here I created two structs which is identical to the struct that causing the problem:

typedef struct {
    BYTE securityLevel;
    BYTE secureChannelProtocol; 
    BYTE secureChannelProtocolImpl; 
    BYTE keySetVersion; 
    BYTE keyIndex; 
    DWORD invokingAidLength;
    LONG sessionEncryptionCounter; 
    DWORD keyLength; 
    BYTE C_MACSessionKey[32]; 
    BYTE R_MACSessionKey[32]; 
    BYTE encryptionSessionKey[32]; 
    BYTE dataEncryptionSessionKey[32]; 
    BYTE lastC_MAC[16];
    BYTE invokingAid[16];   
    BYTE lastR_MAC[8]; 
}ZEN;

typedef struct{
    BYTE securityLevel;
    BYTE secureChannelProtocol;
    BYTE secureChannelProtocolImpl;
    BYTE keySetVersion; 
    BYTE keyIndex; 
    DWORD invokingAidLength;
    LONG sessionEncryptionCounter;
    DWORD keyLength; 
    BYTE C_MACSessionKey[32]; 
    BYTE R_MACSessionKey[32]; 
    BYTE encryptionSessionKey[32]; 
    BYTE dataEncryptionSessionKey[32];
    BYTE lastC_MAC[16];
    BYTE invokingAid[16];
    BYTE lastR_MAC[8];
}MYARRAYSTRUCT;

For printing its member value, I add these functions in the C library:

void test_zen(ZEN * pZen);
void test_myarraystruct(MYARRAYSTRUCT * pStruct);
#define _T(arg) arg
void test_myarraystruct(MYARRAYSTRUCT * pStruct){
    _tprintf(_T("MY->securityLevel: %x\n"), pStruct->securityLevel);
    _tprintf(_T("MY->secureChannelProtocol: %x\n"), pStruct->secureChannelProtocol);
    _tprintf(_T("MY->secureChannelProtocolImpl: %x\n"), pStruct->secureChannelProtocolImpl);
    unsigned int i;
    _tprintf(_T("MY->C_MACSessionKey\n"));
    for(i = 0; i < 32; i  ){
        _tprintf(_T("%x "),pStruct->C_MACSessionKey[i]);
    }
    _tprintf(_T("\n"));
    _tprintf(_T("MY->R_MACSessionKey\n"));
    for(i = 0; i < 32; i  ){
        _tprintf(_T("%x "),pStruct->R_MACSessionKey[i]);
    }
    _tprintf(_T("\n"));
    _tprintf(_T("MY->encryptionSessionKey\n"));
    for(i = 0; i < 32; i  ){
        _tprintf(_T("%x "),pStruct->encryptionSessionKey[i]);
    }
    _tprintf(_T("\n"));
    _tprintf(_T("MY->dataEncryptionSessionKey\n"));
    for(i = 0; i < 32; i  ){
        _tprintf(_T("%x "),pStruct->dataEncryptionSessionKey[i]);
    }
    _tprintf(_T("\n"));
    _tprintf(_T("MY->lastC_MAC\n"));
    for(i = 0; i < 16; i  ){
        _tprintf(_T("%x "),pStruct->lastC_MAC[i]);
    }
    _tprintf(_T("\n"));
    _tprintf(_T("MY->lastR_MAC\n"));
    for(i = 0; i < 8; i  ){
        _tprintf(_T("%x "),pStruct->lastR_MAC[i]);
    }
    _tprintf(_T("\n"));
    _tprintf(_T("MY->keySetVersion: %x\n"), pStruct->keySetVersion);
    _tprintf(_T("MY->keyIndex: %x\n"), pStruct->keyIndex);
    _tprintf(_T("MY->invokingAid\n"));
    for(i = 0; i < 16; i  ){
        _tprintf(_T("%x "),pStruct->invokingAid[i]);
    }
    _tprintf(_T("\n"));
    _tprintf(_T("MY->invokingAidLength: %d\n"), pStruct->invokingAidLength);
    _tprintf(_T("MY->sessionEncryptionCounter: %ld\n"), pStruct->sessionEncryptionCounter);
    _tprintf(_T("MY->keyLength: %d\n"), pStruct->keyLength);
}

I used pass by pointer to pass the struct, replicating the function that causing the problem.

Now, for the C#. First, I created a C# library project and did the prototyping/signature/marshalling as follow :

[StructLayout(LayoutKind.Sequential)]
public struct MYARRAYSTRUCT
{
    public byte securityLevel;
    public byte secureChannelProtocol;
    public byte secureChannelProtocolImpl;
    public byte keySetVersion;
    public byte keyIndex;        
    public int invokingAidLength;
    public int sessionEncryptionCounter;
    public int keyLength;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] C_MACSessionKey;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] R_MACSessionKey;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] encryptionSessionKey;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] dataEncryptionSessionKey;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] lastC_MAC;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] invokingAid;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]  public byte[] lastR_MAC;
}

[StructLayout(LayoutKind.Sequential)]
public class ZEN
{
    public byte securityLevel;
    public byte secureChannelProtocol;
    public byte secureChannelProtocolImpl;
    public byte keySetVersion;
    public byte keyIndex;
    public int invokingAidLength;
    public int sessionEncryptionCounter;
    public int keyLength;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] C_MACSessionKey;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] R_MACSessionKey;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] encryptionSessionKey;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)] public byte[] dataEncryptionSessionKey;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] lastC_MAC;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 16)] public byte[] invokingAid;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]  public byte[] lastR_MAC;
}

[DllImport("globalplatform.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void test_zen(ref ZEN pStruct);

[DllImport("globalplatform.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void test_myarraystruct(ref MYARRAYSTRUCT pStruct);

Then, call the C# library above via console project, as follow:

string aidmanager = "A000000003000000";
byte[] AID = HexStringToArrayOfByte(aidmanager);

GlobalPlatform.MYARRAYSTRUCT miown = new GlobalPlatform.MYARRAYSTRUCT();
miown.securityLevel = 0;
miown.secureChannelProtocol = 0;
miown.secureChannelProtocolImpl = 0;
miown.C_MACSessionKey = new byte[32];
miown.R_MACSessionKey = new byte[32];
miown.encryptionSessionKey = new byte[32];
miown.dataEncryptionSessionKey = new byte[32];
miown.lastC_MAC = new byte[16];
miown.lastR_MAC = new byte[8];
miown.keySetVersion = 0;
miown.keyIndex = 0;
miown.invokingAid = new byte[16];
Array.Copy(AID, 0, miown.invokingAid, 0, 8);
miown.invokingAidLength = 8;
miown.sessionEncryptionCounter = 0;
miown.keyLength = 16;
GlobalPlatform.test_myarraystruct(ref miown);

GlobalPlatform.ZEN myStruct = new GlobalPlatform.ZEN();
myStruct.securityLevel = 0;
myStruct.secureChannelProtocol = 1;
myStruct.secureChannelProtocolImpl = 3;
myStruct.C_MACSessionKey = new byte[32];
myStruct.R_MACSessionKey = new byte[32];
myStruct.encryptionSessionKey = new byte[32];
myStruct.dataEncryptionSessionKey = new byte[32];
myStruct.lastC_MAC = new byte[16];
myStruct.lastR_MAC = new byte[8];
myStruct.keySetVersion = 0;
myStruct.keyIndex = 1;
myStruct.invokingAid = new byte[16];
Array.Copy(AID, 0, myStruct.invokingAid, 0, 8);
myStruct.invokingAidLength = 8;
myStruct.sessionEncryptionCounter = 100;
myStruct.keyLength = 24;
GlobalPlatform.test_zen(ref myStruct);

Both C# library and C# console are set to use x86 as its platform target. (Project>Properties>Build>Platform targer: x86)

Finally, this is what I got:

MY->securityLevel: 0
MY->secureChannelProtocol: 0
MY->secureChannelProtocolImpl: 0
MY->C_MACSessionKey
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
MY->R_MACSessionKey
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
MY->encryptionSessionKey
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
MY->dataEncryptionSessionKey
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
MY->lastC_MAC
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
MY->lastR_MAC
0 0 0 0 0 0 0 0
MY->keySetVersion: 0
MY->keyIndex: 0
MY->invokingAid
a0 0 0 0 3 0 0 0 0 0 0 0 0 0 0 0
MY->invokingAidLength: 8
MY->sessionEncryptionCounter: 0
MY->keyLength: 16

ZEN->securityLevel: 28
ZEN->secureChannelProtocol: f6
ZEN->secureChannelProtocolImpl: 94
ZEN->C_MACSessionKey
3 0 0 0 f4 ee 6f 0 2c f0 6f 0 70 ef 6f 0 28 ef 6f 0 88 ef 6f 0 de 7 c9 4 60 51 75 2
ZEN->R_MACSessionKey
7c 4b 75 2 28 24 75 2 30 58 75 2 18 57 75 2 1c 58 75 2 18 57 75 2 0 58 75 2 18 57 75 2
ZEN->encryptionSessionKey
d4 57 75 2 18 57 75 2 a8 57 75 2 18 57 75 2 7c 57 75 2 18 57 75 2 50 57 75 2 18 57 75 2
ZEN->dataEncryptionSessionKey
18 57 75 2 e4 55 75 2 f8 ee 6f 0 d0 55 75 2 f8 ee 6f 0 b4 55 75 2 f8 ee 6f 0 88 55 75 2
ZEN->lastC_MAC
f8 ee 6f 0 5c 55 75 2 f8 ee 6f 0 30 55 75 2
ZEN->lastR_MAC
50 4f 75 2 4 4e 75 2
ZEN->keySetVersion: 0
ZEN->keyIndex: 1
ZEN->invokingAid
f8 ee 6f 0 4 55 75 2 f8 ee 6f 0 58 51 75 2
ZEN->invokingAidLength: 1726417387
ZEN->sessionEncryptionCounter: 7335428
ZEN->keyLength: 0

The ZEN members value seems to be assigned with random value and it changes every time its compiled.

Why is the ZEN members have incorrect value but the MYARRAYSTRUCT members is correct?

Any suggestion to fix this?

CodePudding user response:

The problem is that you declare ZEN in your C# as class. This makes it a reference type. And so when you pass ZEN as ref then you pass a pointer to a pointer to the structure. That's one level of indirdction too many.

By contrast you declared MYARRAYSTRUCT as struct, a value type. When you pass this as ref, you pass a pointer to the structure.

Change ZEN to be declared as struct.

  • Related