1

I'm building a 32 bits embedded device that an external actor can communicate with through a PKCS#11 interface.

Basically there are 3 components :

  • Our embedded device (only supports 32 bits)
  • The host (may be 32 or 64 bits, not up to me)
  • The host library (can be compiled in 32 or 64 bits)

I'm developing both the device and the host library and my main issue right now is to ensure compatibility with both 32 and 64 bits host applications.

The pkcs11t.h defines CK_ULONG as such :

/* an unsigned value, at least 32 bits long */
typedef unsigned long int CK_ULONG;

The thing is that long int size won't be the same on the host (8 bytes) and on our device (4 bytes).

I was tempted to use uint32_t instead of unsigned long int. But the standard states the following :

It follows that many of the data and pointer types will vary somewhat from one environment to another (e.g., a CK_ULONG will sometimes be 32 bits, and sometimes perhaps 64 bits). However, these details should not affect an application, assuming it is compiled with Cryptoki header files consistent with the Cryptoki library to which the application is linked.

The host will send requests to the embedded device, let's say the host sends a request ID of 0. How the embedded device will know if it's a 32 or a 64 bits value ?

So my question is the following : what's the best way to handle 64 bits data types in a 32 bits embedded device ? I was thinking about something like that on the host's library :

CK_ULONG data = 0x42; // user input, could be anything

#ifdef HOST_64
assert(data <= 4294967295);
#endif

send_data_to_device((uint32_t)data);

But it feels wrong...

ShellCode
  • 1,072
  • 8
  • 17
  • 1
    Your environment seems to define appropriate data types. Why do you want to change something? – Yunnosch Sep 14 '21 at 10:07
  • I do not get the shown codes purpose. In which situation do you expect a value of 0x42 to fail the assertion of being smaller or equal to a much higher value? Did you intend to use a `sizeof()` there somehow? – Yunnosch Sep 14 '21 at 10:08
  • This is not a duplicate, but the posts there might help you see other angles on your problems. https://stackoverflow.com/questions/46442411/how-big-can-a-64-bit-unsigned-integer-be/46442848#46442848 – Yunnosch Sep 14 '21 at 10:10
  • `what's the best way to handle 64 bits data types in a 32 bits embedded device?` Don't? What do you want to handle exactly? What is there to "handle"? `to ensure compatibility with both 32 and 64 bits host applications` And the standard PKCS#11 is for that. And `ULONG`is "at least 32-bits long", not exactly 32-bits long, and the standard knows that. Why do you care? – KamilCuk Sep 14 '21 at 10:14
  • I edited my post for (hopefully) more clarity. I care about that because when the host sends some data to my embedded device (say a ULONG) then I don't know if I should read 4 or 8 bytes because the embedded device doesn't know if the host is 64 or 32 bits – ShellCode Sep 14 '21 at 10:21
  • Sidenote: Usage of terms is a bit confusing to me. Typically clients send requests and hosts respond. So I would expect the embedded device to be host and the external actor to be the client. – user694733 Sep 14 '21 at 10:52
  • @user694733 thing is our device aims to be embedded in something larger, that's why we call this larger thing the "host" – ShellCode Sep 14 '21 at 11:48
  • This answer might be helpful [https://stackoverflow.com/a/48830425/1401213](https://stackoverflow.com/a/48830425/1401213) – kkrambo Sep 14 '21 at 18:56
  • Not sure really what your problem is, but on your device obviously can only handle 32bits, why would your host actually send down 64bits. Your code to send data should only send 32bits (4byte) down to the device, no matter if the host uses 32bit or 64bit. Any value bigger than 64bits should be blocked. Everything between `UINT32_MIN` and `UINT32_MAX` should be accepted and transmitted to the device according to your protocol (incl. endianess). – kesselhaus Sep 15 '21 at 06:04

2 Answers2

2

Presumably your embedded device performs a service. This service needs an interface.

You need to specify that interface. Each available command, value, action, etc. should be documented along with acceptable ranges for values and what happens in error conditions. Then you simply implement the specification on both sides.

Bit-depth or data types of either side is irrelevant. If data doesn't fit the interface requirements, then it must be converted before using the interface.

user694733
  • 15,208
  • 2
  • 42
  • 68
2

There isn't really an issue here. Your embedded system supports only 32bit values. You'll communicate between the systems using some specified link, which I'm sure have already defined e.g. TCP/IP, RS232 etc.

Whatever the transport layer is you'll need to define a protocol to package the data that you transport between the two systems.

This protocol should handle the data sizes and also allow for any disparity between the packing of data for larger data types and endianness by the respective CPUs.

For example you may think that a 32 bit value is packaged as [byte1][byte2][byte3][byte4] in memory, but in actual fact it may be handled as [byte3][byte4][byte1][byte2] by one of the processors which means that you must actively pack your data into an agreed format as specified in your protocol rather than just copying data from memory.

If your 32bit system must support 64 bit values then you will need to write your own handler for it - even if that is unpacking 64 bit values to two 32 bit values e.g. 64bit_value -> 32bit_value_high_bytes + 32bit_value_low_bytes.

Your 64 bit system can carry on supporting 64 bits, although any values that you wish to parse before transporting to you 32 bit system should be converted according to you protocol.

If you're providing library code to perform your protocol handling then I would suggest that all library function calls return an error message value.

Thus you can have on your 64 bit system:

if(send_data_to_device(data) != RESULT_SUCCESS)
{
   // some error handling
}

Your library code could then be:

int send_data_to_device(ULONG data)
{
    #ifdef HOST_64
       if(data > 4294967295) return RESULT_VALUE_OUT_OF_BOUNDS;
    #endif
    // preform data transport

   return RESULT_SUCCESS;
}

Points to be wary of:

  • Be cautious about casting 64 bit values to 32 bit values as a positive signed 64 bit value can give a negative signed 32 bit value.

  • Don't rely on using structures and unions to switch between live and packed data.

ChrisBD
  • 9,104
  • 3
  • 22
  • 35