2

I am using C_Decrypt with the CKM_AES_CBC_PAD mechanism. I know that my ciphertext which is 272 bytes long should actually decrypt to 256 bytes, which means a full block of padding was added.

I know that according to the standard when invoking C_Decrypt with a NULL output buffer the function may return an output length which is somewhat longer than the actual required length, in particular when padding is used this is understandable, as the function can't know how many padding bytes are in the final block without carrying out the actual decryption.

So the question is whether if I know that I should get exactly 256 bytes back, such as in the scenario I explained above, does it make sense that I am still getting a CKR_BUFFER_TOO_SMALL error as a result, despite passing a 256 bytes buffer? (To make it clear: I am indicating that this is the length of the output buffer in the appropriate output buffer length parameter, see the parameters of C_Decrypt to observe what I mean)

I am encountering this behavior with a Safenet Luna device and am not sure what to make of it. Is it my code's fault for not querying for the length first by passing NULL in the output buffer, or is this a bug on the HSM/PKCS11 library side?

One more thing I should perhaps mention is that when I provide a 272 (256+16) bytes output buffer, the call succeeds and I am noticing that I am getting back my expected plaintext, but also the padding block which means 16 final bytes with the value 0x10. However, the output length is updated correctly to 256, not 272 - this also proves that I am not using CKM_AES_CBC instead of CKM_AES_CBC_PAD accidentally, which I suspected for a moment as well :)

Amit
  • 173
  • 1
  • 1
  • 9
  • One thing that crossed my mind is that perhaps this is actually a defense against a type of a padding oracle attack in which an attacker somehow controls the output buffer size, and in that way reveals the length of the plaintext. Is that possible? – Amit Jul 01 '19 at 11:50
  • AFAIK the extra buffer space is required for internal purpose of library to be sure it can produce correct output (for example memory movements). I think this is done for guardant purpose and in your case it is equal to the just one extra padding block size just for this reason. Try to encrypt/decrypt 255 bytes and check the buffer size on first step. – Alexander Jul 02 '19 at 12:42
  • The question is whether mandating this guardant space is correct behavior. I'm quite sure that it is used for the purpose you explained. – Amit Jul 02 '19 at 18:59
  • 2
    It is comply with the specifications of PKCS#11, so the answer is yes. The same behavior can be seen in Microsoft's Crypto API. – Alexander Jul 02 '19 at 19:08
  • I agree that it doesn't seem to contradict the standard. – Amit Jul 02 '19 at 19:19
  • ..But I want to point out again, that the standard also does not explicitly say that you have to invoke the function with a NULL output buffer to determine the output length. It appears to be just an optional thing to do if you don't know how much output length to allocate. So I want to leave open the possibility that it is a bug that is specific to Luna. I also think that it would be good to ask the authors of pkcs11 about this, as it seems to be a kind of a gray area in the standard that should be more well defined. I will try to do that soon :) – Amit Jul 05 '19 at 06:48
  • Please review section 5.2 of standard - it is one of 2 ways to call function that return the output of unknown size. In my case I always allocate "extra large" buffer 10k-16k size for all such calls in advance and have no same problems. – Alexander Jul 05 '19 at 07:12
  • Yes I have linked to that exact section in the original post. My point is that the standard does not indicate that you have to use the 1st method before invoking the 2nd. And also in practice in my use case the output size IS known, so I can also argue that the section is irrelevant for my use case. – Amit Jul 05 '19 at 08:06
  • If you can suggest the size of data + extras - you can make only the 2nd call. But if the size is unknown - you must call twice. This behavior is mostly for developers of PKCS#11 libraries to expect this call sequence and correctly process NULL_PTR at pBuf argument. – Alexander Jul 05 '19 at 09:04

1 Answers1

1

I have used CKM.AES_CBC_PAD padding mechanism with C_Decrypt in past. You have to make 2 calls to C_Decrypt (1st ==> To get the size of the plain text, 2nd ==> Actual decryption). see the documentation here which talks about determining the length of the buffer needed to hold the plain-text.

Below is the step-by-step code to show the behavior of decryption:

//Defining the decryption mechanism
CK_MECHANISM mechanism = new CK_MECHANISM(CKM.AES_CBC_PAD);

//Initialize to zero -> variable to hold size of plain text
LongRef lRefDec = new LongRef();

// Get ready to decrypt 
CryptokiEx.C_DecryptInit(session_1, mechanism, key_handleId_in_hsm);

// Get the size of the plain text -> 1st call to decrypt
CryptokiEx.C_Decrypt(session_1, your_cipher, your_cipher.length, null, lRefDec);

// Allocate space to the buffer to store plain text.  
byte[] clearText = new byte[(int)lRefDec.value];

// Actual decryption -> 2nd call to decrypt
CryptokiEx.C_Decrypt(session_1, eFileCipher, eFileCipher.length, eFileInClear,lRefDec);

Sometimes, decryption fails because your input encryption data was misleading (however, encryption is successful but corresponding decryption will fail) the decryption algorithm. So it is important not to send raw bytes directly to the encryption algorithm; rather encoding the input data with UTF-8/16 schema's preserves the data from getting misunderstood as network control bytes.

  • This is (in part) good information but I can't mark that as the accepted answer because it's not completely accurate. Nothing in the standard forces you to make both calls in case you know how much output to expect. Also, the part regarding the input data format is very unclear and doesn't seem relevant to me. Perhaps this is a platform specific issue you were thinking about, which may be relevant to Java in particular but not in general. – Amit Jul 11 '19 at 22:44