cancel
Showing results for 
Search instead for 
Did you mean: 

How to chose random seed for RSA encrypt in X-CUBE-CRYPTOLIB?

LWChris
Associate III

The call to "cmox_rsa_pkcs1v15_encrypt" requires a "Random buffer" and a "Random Length (in Bytes)" as input parameters. It will return error code CMOX_RSA_ERR_WRONG_RANDOM if "Random material too short or not valid for the functionality".

I keep getting this error code, but I cannot find any details on how I am SUPPOSED to choose or set the buffer size or contents etc.

The example uses a 36 byte message and a 20 byte random seed. I am trying to encrypt a 16 byte message with a 20 byte seed, but get the result "material too short or not valid". So, what shall I do? Any documentation on that parameter except for "it is a random seed that can be too short or not valid under certain untold circumstances"?

13 REPLIES 13
Jocelyn RICARD
ST Employee

Hello @LWChris​ ;

I made a quick test using STM32CubeExpansion_Crypto_V4.0.1\Projects\NUCLEO-L476RG\Applications\RSA\PKCS1v2.2_EncryptDecrypt\ as base example.

I just changed the call to cmox_rsa_pkcs1v22_encrypt by cmox_rsa_pkcs1v15_encrypt and removed the hash method and label parameter.

The encryption is working fine.

Also, I could provide a seed made of a 1 and then only zeros, it worked fine.

Could you please check with this example ?

Best regards

Jocelyn

LWChris
Associate III

Hi @Jocelyn RICARD​ ;

here are my findings:

It must be something with the memory address of the variables I pass.

I have now defined all test values as "const uint8_t Xyz[] = { ... };" outside of the function. I have also duplicated the "Seed" variable, once above the modulus and once below.

This works:

UNUSED(Seed);
UNUSED(Seed2);
 
result = cmox_rsa_pkcs1v15_encrypt(&rsaContext, &rsaKey, Message, 16, Seed, 20, resBuffer, &resSize);
 
// Address of Seed: 0x801db4c
// result = 0x50000 = 327680 = CMOX_RSA_SUCCESS

This doesn't work:

UNUSED(Seed);
UNUSED(Seed2);
 
result = cmox_rsa_pkcs1v15_encrypt(&rsaContext, &rsaKey, Message, 16, Seed2, 20, resBuffer, &resSize);
 
// Address of Seed2: 0x801dc50
// result = 0x5000b = 327691 = CMOX_RSA_ERR_WRONG_RANDOM

Depending on where I copy the "const int Seed2[] = { ... };" line, it works or not. I am using FreeRTOS, stack size is 4096 in the task I'm in. Maybe a stack issue?

LWChris
Associate III

Okay, I tested around even more and I am completely lost on what's going on here. It remains true that depending on the order of declaration of the constant variables, it is either working or not.

If I also define a constant dummy string above the Seed variable, I can "manipulate" the address the Seed variable will have by changing the length of the string.

Here comes the weird part:

I can order the variables so the Seed constant end up at an address that doesn't work.

I can re-order the variables such that the Seed constant ends up further top, and it works.

I can then insert the dummy string and chose a length such that the Seed constant will be at the same address that didn't work before, but now it works.

I have no idea what is going on inside the library. I have verified that the whole data is present at all addresses. And even if I understand what is going on, obviously a constant "random" seed is not random, so using a const uint8_t Seed[] for testing is fine, but when I pass a pointer to a local variable for any of exp/mod/seed, it seemingly won't work.

LWChris
Associate III

I now have a very reduced example to illustrate the weirdness:

rsa.h:

#ifndef INC_CUSTOM_RSA_H_
#define INC_CUSTOM_RSA_H_
 
#include <stdbool.h>
#include <stdint.h>
 
#define RSA_EXPONENT_SIZE_BYTES  3
#define RSA_KEY_SIZE_BYTES       (RSA_EXPONENT_SIZE_BYTES + RSA_MODULUS_SIZE_BYTES)
#define RSA_MODULUS_SIZE_BYTES   256 // 2048 Bit
 
bool Test_RSA_Encrypt(uint8_t *res);
void MX_RSA_Init(void);
 
#endif /* INC_CUSTOM_RSA_H_ */

rsa.c:

#include "rsa.h"
#include "cmox_crypto.h"
#include <string.h>
 
#define RSA_WORKING_BUFFER_SIZE_BYTES 7000
 
static uint8_t rsaWorkingBuffer[RSA_WORKING_BUFFER_SIZE_BYTES] = { 0 };
 
const uint8_t Message[] =
{
  0xbc, 0xdd, 0x19, 0x0d,
  0xa3, 0xb7, 0xd3, 0x00,
  0xdf, 0x9a, 0x06, 0xe2,
  0x2c, 0xaa, 0xe2, 0xa7
};
 
const uint8_t Seed1[] =
{
  0x5c, 0xac, 0xa6, 0xa0,
  0xf7, 0x64, 0x16, 0x1a,
  0x96, 0x84, 0xf8, 0x5d,
  0x92, 0xb6, 0xe0, 0xef,
  0x37, 0xca, 0x8b, 0x65
};
 
const uint8_t Public_Exponent[] =
{
  0x01, 0x00, 0x01
};
 
const uint8_t Modulus[] =
{
  0xae, 0x45, 0xed, 0x56, 0x01, 0xce, 0xc6, 0xb8, 0xcc, 0x05, 0xf8, 0x03, 0x93, 0x5c, 0x67, 0x4d,
  0xdb, 0xe0, 0xd7, 0x5c, 0x4c, 0x09, 0xfd, 0x79, 0x51, 0xfc, 0x6b, 0x0c, 0xae, 0xc3, 0x13, 0xa8,
  0xdf, 0x39, 0x97, 0x0c, 0x51, 0x8b, 0xff, 0xba, 0x5e, 0xd6, 0x8f, 0x3f, 0x0d, 0x7f, 0x22, 0xa4,
  0x02, 0x9d, 0x41, 0x3f, 0x1a, 0xe0, 0x7e, 0x4e, 0xbe, 0x9e, 0x41, 0x77, 0xce, 0x23, 0xe7, 0xf5,
  0x40, 0x4b, 0x56, 0x9e, 0x4e, 0xe1, 0xbd, 0xcf, 0x3c, 0x1f, 0xb0, 0x3e, 0xf1, 0x13, 0x80, 0x2d,
  0x4f, 0x85, 0x5e, 0xb9, 0xb5, 0x13, 0x4b, 0x5a, 0x7c, 0x80, 0x85, 0xad, 0xca, 0xe6, 0xfa, 0x2f,
  0xa1, 0x41, 0x7e, 0xc3, 0x76, 0x3b, 0xe1, 0x71, 0xb0, 0xc6, 0x2b, 0x76, 0x0e, 0xde, 0x23, 0xc1,
  0x2a, 0xd9, 0x2b, 0x98, 0x08, 0x84, 0xc6, 0x41, 0xf5, 0xa8, 0xfa, 0xc2, 0x6b, 0xda, 0xd4, 0xa0,
  0x33, 0x81, 0xa2, 0x2f, 0xe1, 0xb7, 0x54, 0x88, 0x50, 0x94, 0xc8, 0x25, 0x06, 0xd4, 0x01, 0x9a,
  0x53, 0x5a, 0x28, 0x6a, 0xfe, 0xb2, 0x71, 0xbb, 0x9b, 0xa5, 0x92, 0xde, 0x18, 0xdc, 0xf6, 0x00,
  0xc2, 0xae, 0xea, 0xe5, 0x6e, 0x02, 0xf7, 0xcf, 0x79, 0xfc, 0x14, 0xcf, 0x3b, 0xdc, 0x7c, 0xd8,
  0x4f, 0xeb, 0xbb, 0xf9, 0x50, 0xca, 0x90, 0x30, 0x4b, 0x22, 0x19, 0xa7, 0xaa, 0x06, 0x3a, 0xef,
  0xa2, 0xc3, 0xc1, 0x98, 0x0e, 0x56, 0x0c, 0xd6, 0x4a, 0xfe, 0x77, 0x95, 0x85, 0xb6, 0x10, 0x76,
  0x57, 0xb9, 0x57, 0x85, 0x7e, 0xfd, 0xe6, 0x01, 0x09, 0x88, 0xab, 0x7d, 0xe4, 0x17, 0xfc, 0x88,
  0xd8, 0xf3, 0x84, 0xc4, 0xe6, 0xe7, 0x2c, 0x3f, 0x94, 0x3e, 0x0c, 0x31, 0xc0, 0xc4, 0xa5, 0xcc,
  0x36, 0xf8, 0x79, 0xd8, 0xa3, 0xac, 0x9d, 0x7d, 0x59, 0x86, 0x0e, 0xaa, 0xda, 0x6b, 0x83, 0xbb
};
 
const uint8_t Seed2[] =
{
  0x5c, 0xac, 0xa6, 0xa0,
  0xf7, 0x64, 0x16, 0x1a,
  0x96, 0x84, 0xf8, 0x5d,
  0x92, 0xb6, 0xe0, 0xef,
  0x37, 0xca, 0x8b, 0x65
};
 
bool Test_RSA_Encrypt(uint8_t *res) {
  cmox_rsa_handle_t rsaContext = { 0 };
  cmox_rsa_key_t rsaKey = { 0 };
  size_t resSize = 0;
 
  cmox_rsa_construct(&rsaContext, CMOX_RSA_MATH_FUNCS, CMOX_MODEXP_PUBLIC, rsaWorkingBuffer, RSA_WORKING_BUFFER_SIZE_BYTES);
 
  cmox_rsa_retval_t result1a = cmox_rsa_setKey(&rsaKey, Modulus, RSA_MODULUS_SIZE_BYTES, Public_Exponent, RSA_EXPONENT_SIZE_BYTES);
  cmox_rsa_retval_t result1b = result1a;
  cmox_rsa_retval_t result2a = result1a;
  cmox_rsa_retval_t result2b = result1a;
 
  if (result1a != CMOX_RSA_SUCCESS) {
    return false;
  }
 
  result1a = cmox_rsa_pkcs1v15_encrypt(&rsaContext, &rsaKey, Message, sizeof(Message), Seed1, sizeof(Seed1), res, &resSize);
  result2a = cmox_rsa_pkcs1v15_encrypt(&rsaContext, &rsaKey, Message, sizeof(Message), Seed2, sizeof(Seed2), res, &resSize);
  result1b = cmox_rsa_pkcs1v15_encrypt(&rsaContext, &rsaKey, Message, sizeof(Message), Seed1, sizeof(Seed1), res, &resSize);
  result2b = cmox_rsa_pkcs1v15_encrypt(&rsaContext, &rsaKey, Message, sizeof(Message), Seed2, sizeof(Seed2), res, &resSize);
 
  if (result1a != CMOX_RSA_SUCCESS ||
      result2a != CMOX_RSA_SUCCESS ||
      result1b != CMOX_RSA_SUCCESS ||
      result2b != CMOX_RSA_SUCCESS ||
      resSize != RSA_MODULUS_SIZE_BYTES) {
    // Reset result if encryption was unsuccessful
    memset(res, 0, RSA_MODULUS_SIZE_BYTES);
    return false;
  }
 
  cmox_rsa_cleanup(&rsaContext);
 
  return true;
}
 
void MX_RSA_Init(void) {
  cmox_initialize(NULL);
}

(Note 1: CMOX_RSA_MATH_FUNCS currently maps to CMOX_MATH_FUNCS_FAST, but CMOX_MATH_FUNCS_SMALL doesn't work either)

(Note 2: I have used the original "Message" bytes, but shortened the length to 16 bytes to be closer to my later usecase)

I have set a breakpoint in rsa.c, line 87. result1a and 1b (both calls using Seed1, memory address 0x801db88) are "CMOX_RSA_SUCCESS", result2a and 2b (using Seed2, address 0x801dca0) are "CMOX_RSA_ERR_WRONG_RANDOM". So it is apparently dependent on the address and not the content. It seems to me that it doesn't work if the seed's memory address is higher than the modulus' address (0x801dba0 in my experiment).

LWChris
Associate III

Further testing seems to confirm my assumption that it always succeeds if the memory address of the seed is lower than the memory address of the modulo, and that it never succeeds if the memory address of the seed is higher than the address of the modulo.

So, just a very wild shot in the dark, BUT:

I know that for OAEP (which should be irrelevant for pkcs1v15, but still...) there is a requirement that the numerical value of the random seed is less than the numerical value of the modulus to ensure unique decryption.

Could it be that there is a bug in X-CUBE-CRYPTOLIB and that it a) accidentially checks/compares the addresses of those two instead of the numerical values, and b) performs the check for pkcs1v15, too (not sure if that would be unnecessary or still correct)?

LWChris
Associate III

I restored the originally intended functionality of using local variables inside the function. My findings further appear to confirm my suspicions:

If I declare separate local uint8_t* variables for seed, exp and mod, and the compiler happens to optimize the code such that the address of mod ends up being lower than the address of seed, the call to cmox_rsa_pkcs1v15_encrypt will result in CMOX_RSA_ERR_WRONG_RANDOM.

If I change the code to force the order of addresses to be "seed < exp < mod" by putting all three in one large uint8_t[] and then define seed, exp, mod as pointers into that array, then I can see that the memory address order "seed < exp < mod" is preserved, and indeed cmox_rsa_pkcs1v15_encrypt will return CMOX_RSA_SUCCESS.

Jocelyn RICARD
ST Employee

Hello @LWChris​ ,

thank you for this deep investigation. This is really weird behaviour indeed:

I'll try to reproduce the issue and come back to you

Best regards

Jocelyn

Jocelyn RICARD
ST Employee

Hello @LWChris​ ,

I could reproduce the issue.

I found that for some reason I cannot explain, the seed needs to be at beginning of a buffer of 220 bytes that does not contain series of 0.

So, waiting for issue to be fixed, a workaround using a 220 bytes buffer with non 0 bytes and generating your random at the beginning of this buffer should work.

I'll raise the issue to developer.

Best regards

Jocelyn

LWChris
Associate III

220 bytes seems to be an unlikely arbitrary number.

Since the message is 36 bytes and 220 + 36 = 256, would it change if the message was of different size?

Could you also check if repeated calls to cmox_rsa_pkcs1v15_encrypt yield the same result for you? Right now, the first-ever call to the function returns garbage that cannot be decrypted.

Calling it repeatedly afterwards yields always the same result.

Like so:

loop (3) {
  cmox_rsa_construct
  cmox_rsa_setKey
  seed = <rnd()>
  loop (3) {
    cmox_rsa_pkcs1v15_encrypt
  }
  cmox_rsa_cleanup
}

Result is:

Garbage
Result 1
Result 1
Result 2
Result 2
Result 2
Result 3
Result 3
Result 3

Thank you for issueing tickets.

Chris