Reputation: 11
I am trying to decrypt some information that has been encrypted with the SJCL (Stanford Javascript Crypto Library). An example page is at http://bitwiseshiftleft.github.io/sjcl/demo/.
If I encrypt some data, I have been unable to decrypt it using OpenSSL (version 1.0.1f). I have seen another question on stackoverflow asking about this - but that question and its answers didn't really help.
For instance, encrypting with a password of 'password', and a random salt of '6515636B 82C5AC56' with 10000 iterations of a 256 bit key size gives a Key of 'D8CCAA75 3E2983F0 3657AB3C 8A68A85A 9E9F1CAC 43DAB645 489CDE58 0A9EBDAE', which is exactly what I get with OpenSSL. So far, so good.
When I use SJCL with this key and an IV of '9F62544C 9D3FCAB2 DD0833DF 21CA80CF' to encrypt, say, the message 'mymessage', then I get the ciphertext:
{"iv":"n2JUTJ0/yrLdCDPfIcqAzw==",
"v":1,
"iter":10000,
"ks":256,
"ts":64,
"mode":"ccm",
"adata":"",
"cipher":"aes",
"salt":"ZRVja4LFrFY=",
"ct":"FCuQWGYz3onE/lRt/7vCl5A="}
However, no matter how I modify or rewrite my OpenSSL
C++ code, I cannot decrypt this data.
I've googled and found a few code samples, but nothing that has actually worked.
I'm aware that I need to use the CCM
cipher mode in OpenSSL - but this mode is poorly documented.
Can anyone post some OpenSSL code to successfully decrypt this data?
Upvotes: 1
Views: 1537
Reputation: 61
You can copy-paste the example at http://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption with a few changes.
First, you need to Base64 decode SJCL's data. But you knew that.
Second, you need to split the message into ct and tag. In this case, ct is the first 9 bytes, and tag is the 8 bytes starting at ct[9].
Third, you need to set the tag length to ts/8 = 8, and you need to set the IV length correctly. If you set the IV too long in SJCL, it will truncate it to 15 - LOL (length of length), where LOL is between 2 and 4 (because SJCL enforces <2^32 bytes length), and is the number of bytes required to describe the length of the message. This is 2 unless the message is at least 65536 bytes long, in which case it is 3, unless the message is at least 2^24 bytes long, in which case it is 4. Keep in mind that if you're decrypting, the ciphertext you are passed includes the tag but LOL must be computed from the message length, which does not include the tag.
With those changes, it should work:
#include <openssl/evp.h>
void handleErrors() {
abort();
}
int decryptccm(unsigned char *ciphertext, int ciphertext_len, unsigned char *aad,
int aad_len, unsigned char *tag, unsigned char *key, unsigned char *iv,
unsigned char *plaintext)
{
EVP_CIPHER_CTX *ctx;
int len;
int plaintext_len;
int ret;
/* Create and initialise the context */
if(!(ctx = EVP_CIPHER_CTX_new())) handleErrors();
/* Initialise the decryption operation. */
if(1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_ccm(), NULL, NULL, NULL))
handleErrors();
int lol = 2;
if (ciphertext_len >= 1<<16) lol++;
if (ciphertext_len >= 1<<24) lol++;
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_IVLEN, 15-lol, NULL))
handleErrors();
/* Set expected tag value. */
if(1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_CCM_SET_TAG, 8, tag))
handleErrors();
/* Initialise key and IV */
if(1 != EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) handleErrors();
/* Provide the total ciphertext length
*/
if(1 != EVP_DecryptUpdate(ctx, NULL, &len, NULL, ciphertext_len))
handleErrors();
/* Provide any AAD data. This can be called zero or more times as
* required
*/
if(1 != EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len))
handleErrors();
/* Provide the message to be decrypted, and obtain the plaintext output.
* EVP_DecryptUpdate can be called multiple times if necessary
*/
ret = EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len);
plaintext_len = len;
/* Clean up */
EVP_CIPHER_CTX_free(ctx);
if(ret > 0)
{
/* Success */
return plaintext_len;
}
else
{
/* Verify failed */
return -1;
}
}
int main(int argc, char **argv) {
// base64-decoded from your example
unsigned char iv[] = { 0x9F,0x62,0x54,0x4C,0x9D,0x3F,0xCA,0xB2,0xDD,0x08,0x33,0xDF,0x21,0xCA,0x80,0xCF };
unsigned char ct[] = { 0x14,0x2B,0x90,0x58,0x66,0x33,0xDE,0x89,0xC4,0xFE,0x54,0x6D,0xFF,0xBB,0xC2,0x97,0x90 };
unsigned char ky[] = { 0xD8,0xCC,0xAA,0x75 ,0x3E,0x29,0x83,0xF0 ,0x36,0x57,0xAB,0x3C ,0x8A,0x68,0xA8,0x5A ,0x9E,0x9F,0x1C,0xAC ,0x43,0xDA,0xB6,0x45 ,0x48,0x9C,0xDE,0x58 ,0x0A,0x9E,0xBD,0xAE };
const unsigned char *message = (const unsigned char *)"mymessage";
unsigned char plaintext[1000];
int ret = decryptccm(ct, 9, "", 0, &ct[9], ky, iv, plaintext);
plaintext[9] = 0;
printf("%d,%s\n",ret,plaintext);
return 0;
}
This program returns "9,mymessage" on my machine.
Upvotes: 3