Related
I am having problems with the last characters of the encrypted message that I am generating with the OpenSSL library and the AES 128 algorithm in CBC mode, these are the data I am using:
Message in Hex = "7b22494443223a2232363930393439376434222c22444553223a2256656e74616d656e7564656f222c22414d4f223a3530302c22444154223a313530383233303035383730362c22524546223a302c22434f4d223a312c22545950223a31392c2276223a7b224e414d223a2252616661656c56616c656e7a75656c614172656e6173222c22414343223a2235383732313233343536373836303132222c2242414e223a34303132372c22545943223a332c22444556223a22353532373139323132382f30227d7da"
Key128 in Hex = "dadf11e74d014a62d73ccadd9591442a"
Initialization Vector in Hex = "cab9da8940cd7dc9510c7249fe47c6e6"
This is the Code I'm using:
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <math.h>
#include <assert.h>
#include <stdint.h>
#include <stdbool.h>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <locale>
#include <openssl/aes.h>
#include <openssl/des.h>
#include <openssl/sha.h>
#include <openssl/hmac.h>
using namespace std;
/* AES key for Encryption and Decryption */
const static unsigned char aes_key[16]={0xda, 0xdf, 0x11, 0xe7, 0x4d, 0x01, 0x4a, 0x62, 0xd7, 0x3c, 0xca, 0xdd, 0x95, 0x91, 0x44, 0x2a};
/* Print Encrypted and Decrypted data packets */
void print_data(const char *tittle, const void* data, int len)
{
printf("%s : ",tittle);
const unsigned char * p = (const unsigned char*)data;
int i = 0;
for (; i<len; ++i)
{
printf("%02X ", *p++);
}
printf("\n");
}
int main( )
{
/* Input data to encrypt */
unsigned char enc_out[235]={0x7b, 0x22, 0x49, 0x44, 0x43, 0x22, 0x3a, 0x22, 0x32, 0x36, 0x39, 0x30, 0x39, 0x34, 0x39, 0x37, 0x64, 0x34, 0x22, 0x2c, 0x22, 0x44, 0x45, 0x53, 0x22, 0x3a, 0x22, 0x56, 0x65, 0x6e, 0x74, 0x61, 0x6d, 0x65, 0x6e, 0x75, 0x64, 0x65, 0x6f, 0x22, 0x2c, 0x22, 0x41, 0x4d, 0x4f, 0x22, 0x3a, 0x35, 0x30, 0x30, 0x2c, 0x22, 0x44, 0x41, 0x54, 0x22, 0x3a, 0x31, 0x35, 0x30, 0x38, 0x32, 0x33, 0x30, 0x30, 0x35, 0x38, 0x37, 0x30, 0x36, 0x2c, 0x22, 0x52, 0x45, 0x46, 0x22, 0x3a, 0x30, 0x2c, 0x22, 0x43, 0x4f, 0x4d, 0x22, 0x3a, 0x31, 0x2c, 0x22, 0x54, 0x59, 0x50, 0x22, 0x3a, 0x31, 0x39, 0x2c, 0x22, 0x76, 0x22, 0x3a, 0x7b, 0x22, 0x4e, 0x41, 0x4d, 0x22, 0x3a, 0x22, 0x52, 0x61, 0x66, 0x61, 0x65, 0x6c, 0x56, 0x61, 0x6c, 0x65, 0x6e, 0x7a, 0x75, 0x65, 0x6c, 0x61, 0x41, 0x72, 0x65, 0x6e, 0x61, 0x73, 0x22, 0x2c, 0x22, 0x41, 0x43, 0x43, 0x22, 0x3a, 0x22, 0x35, 0x38, 0x37, 0x32, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x36, 0x30, 0x31, 0x32, 0x22, 0x2c, 0x22, 0x42, 0x41, 0x4e, 0x22, 0x3a, 0x34, 0x30, 0x31, 0x32, 0x37, 0x2c, 0x22, 0x54, 0x59, 0x43, 0x22, 0x3a, 0x33, 0x2c, 0x22, 0x44, 0x45, 0x56, 0x22, 0x3a, 0x22, 0x35, 0x35, 0x32, 0x37, 0x31, 0x39, 0x32, 0x31, 0x32, 0x38, 0x2f, 0x30, 0x22, 0x7d, 0x7d, 0xa};
/* Init vector */
unsigned char iv[16]={0xca, 0xb9, 0xda, 0x89, 0x40, 0xcd, 0x7d, 0xc9, 0x51, 0x0c, 0x72, 0x49, 0xfe, 0x47, 0xc6, 0xe6};
//memset(iv, 0x00, AES_BLOCK_SIZE);
/* Buffers for Encryption and Decryption */
unsigned char dec_out[400];
unsigned char aux_out[400];
memset(dec_out, 0, sizeof(dec_out));
memset(aux_out, 0, sizeof(aux_out));
/* AES-128 bit CBC Encryption */
AES_KEY enc_key, dec_key;
AES_set_encrypt_key(aes_key, sizeof(aes_key)*8, &enc_key);
AES_cbc_encrypt(enc_out, dec_out, sizeof(enc_out), &enc_key, iv, AES_ENCRYPT);
/* AES-128 bit CBC Decryption */
memset(iv, 0x00, AES_BLOCK_SIZE); // don't forget to set iv vector again, else you can't decrypt data properly
AES_set_decrypt_key(aes_key, sizeof(aes_key)*8, &dec_key); // Size of key is in bits
AES_cbc_encrypt(dec_out, aux_out, sizeof(enc_out), &dec_key, iv, AES_DECRYPT);
/* Printing and Verifying */
print_data("\n Original ",enc_out, sizeof(enc_out)); // you can not print data as a string, because after Encryption its not ASCII
print_data("\n Encrypted",dec_out, sizeof(enc_out));
print_data("\n Decrypted",aux_out, sizeof(dec_out));
return 0;
}
Apparently only the end of the chain is the one that is incorrect (the last 32 characters) the rest is fine, I have researched a bit and everything points to what is the type of padding of the text but according to what I read it is already paddated with PKCS5 (which is the padding I need in fact) so I can not see what the error is.
The Correct Encryption = EFC063DD33406D424D359809695D0B1E2D65027E803962C6A115DF7CCABEEB0C8C358830E556ED23943FA4F02E6461D235EF913CFCE5519F7CE2279DD07D3C4054D045827D5D7D9FE94DA3C5B718A24E79539B3FFC1E68E4C3FF441EEA176F61EE3D7B33B622E3069D95815F6407FBC79342BB972A2DDE4E50FDE9302BDE4409B7D2BD388AB6A043B9EF236D982937D8537F954564FF4134BD8A6EAB994FE4C29E9DC4E54D53A561A4688C45C90961EDB1763B6EF6C86B593C7E16FDF35C49CE16B1E6948BB1EAE6A8692326A019960B
The Output of the Program = EFC063DD33406D424D359809695D0B1E2D65027E803962C6A115DF7CCABEEB0C8C358830E556ED23943FA4F02E6461D235EF913CFCE5519F7CE2279DD07D3C4054D045827D5D7D9FE94DA3C5B718A24E79539B3FFC1E68E4C3FF441EEA176F61EE3D7B33B622E3069D95815F6407FBC79342BB972A2DDE4E50FDE9302BDE4409B7D2BD388AB6A043B9EF236D982937D8537F954564FF4134BD8A6EAB994FE4C29E9DC4E54D53A561A4688C45C90961EDB1763B6EF6C86B593C7E16FDF35C49CE608E3F73FC8E3DDF1D3BCF40B3DFACD00B732A9FCC10F6E0FB18E126A1C21A082D7A4F053F131A9329474D
I understand that the code has many improvements and I am not contemplating the desencryption, but at the moment I do not need it.
AES_cbc_encrypt is encrypting using the low level primitives. It does not perform any kind of padding. Here is a link to the underlying CBC source code that the AES CBC code directly calls: https://github.com/openssl/openssl/blob/master/crypto/modes/cbc128.c - no padding indicated at all.
The real question is why you would use ill-described, low level implementation functions rather than the EVP API that OpenSSL itself advocates. You may think that the lower level primitives are more performant, but this may not be the case if e.g. AES-NI is deployed; the calling overhead will be minimal compared to the cipher itself anyway.
The enc_out-array is dimensioned for 235 bytes, but is only initialized explicitly for 200 bytes, i.e. the remaining bytes are implicitly initialized with 0x00-values. This corresponds to a ciphertext with a length of 240 bytes (= 15 x 16 bytes). Therefore, the current code returns a ciphertext with a length of 240 bytes whose last three blocks are:
608E3F73FC8E3DDF1D3BCF40B3DFACD0 0B732A9FCC10F6E0FB18E126A1C21A08 2D7A4F053F131A9329474DE44DA772BC
This corresponds to an implicit padding with 0x00-values. The last 5 bytes are missing in the posted ciphertext, which is because the length of the plaintext (instead of the ciphertext) is used for the output.
Most likely only the first 200 bytes should be encrypted. This would correspond to a ciphertext with a length of 208 bytes (= 13 x 16 bytes), which is just the length of the expected ciphertext. For this, the size of the enc_out-array must be reduced from 235 to 200, or even better, the size should not be explicitly specified at all.
Then a two blocks shorter, but otherwise identical ciphertext is generated, which differs from the expected ciphertext only in the last block, which is now caused only by the missing PKCS7-padding.
I am trying to use an arduino to encrypt data as AES128 and then decrypt it. I am also experimenting with AES192 and AES256 but first I'd like to get it to work with AES128. I am using the Cyptography Library and I am struggling to figure out what is happening here. In the code below one may observe that a testVector has a name to identify it, a unique key, plain text, and cipher text. When I tried modifying the ".plaintext" in the array it causes the encryption and thus decryption to fail. My goal is to encrypt "My Message" and then decrypt it afterwords.
Please see my code below:
#include <Crypto.h>
#include <AES.h>
#include <string.h>
struct TestVector
{
const char *name;
byte key[32];
byte plaintext[16];
byte ciphertext[16];
};
// Define the ECB test vectors from the FIPS specification.
static TestVector const testVectorAES128 = {
.name = "AES-128-ECB",
.key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F},
.plaintext = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
.ciphertext = {0x69, 0xC4, 0xE0, 0xD8, 0x6A, 0x7B, 0x04, 0x30,
0xD8, 0xCD, 0xB7, 0x80, 0x70, 0xB4, 0xC5, 0x5A}
};
static TestVector const testVectorAES192 = {
.name = "AES-192-ECB",
.key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17},
.plaintext = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
.ciphertext = {0xDD, 0xA9, 0x7C, 0xA4, 0x86, 0x4C, 0xDF, 0xE0,
0x6E, 0xAF, 0x70, 0xA0, 0xEC, 0x0D, 0x71, 0x91}
};
static TestVector const testVectorAES256 = {
.name = "AES-256-ECB",
.key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F},
.plaintext = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF},
.ciphertext = {0x8E, 0xA2, 0xB7, 0xCA, 0x51, 0x67, 0x45, 0xBF,
0xEA, 0xFC, 0x49, 0x90, 0x4B, 0x49, 0x60, 0x89}
};
AES128 aes128;
AES192 aes192;
AES256 aes256;
byte buffer[16];
void testCipher(BlockCipher *cipher, const struct TestVector *test)
{
crypto_feed_watchdog();
Serial.print(test->name);
Serial.print(" Encryption ... ");
cipher->setKey(test->key, cipher->keySize());
cipher->encryptBlock(buffer, test->plaintext);
if (memcmp(buffer, test->ciphertext, 16) == 0){
Serial.println("Passed");
String myString = String((char*)buffer);
Serial.println(myString);
}
else
Serial.println("Failed");
Serial.print(test->name);
Serial.print(" Decryption ... ");
cipher->decryptBlock(buffer, test->ciphertext);
if (memcmp(buffer, test->plaintext, 16) == 0){
Serial.println("Passed");
String myString2 = String((char*)buffer);
Serial.println(myString2);
}
else
Serial.println("Failed");
}
void perfCipher(BlockCipher *cipher, const struct TestVector *test)
{
unsigned long start;
unsigned long elapsed;
int count;
crypto_feed_watchdog();
Serial.print(test->name);
Serial.print(" Set Key ... ");
start = micros();
for (count = 0; count < 10000; ++count) {
cipher->setKey(test->key, cipher->keySize());
}
elapsed = micros() - start;
Serial.print(elapsed / 10000.0);
Serial.print("us per operation, ");
Serial.print((10000.0 * 1000000.0) / elapsed);
Serial.println(" per second");
Serial.print(test->name);
Serial.print(" Encrypt ... ");
start = micros();
for (count = 0; count < 5000; ++count) {
cipher->encryptBlock(buffer, buffer);
}
elapsed = micros() - start;
Serial.print(elapsed / (5000.0 * 16.0));
Serial.print("us per byte, ");
Serial.print((16.0 * 5000.0 * 1000000.0) / elapsed);
Serial.println(" bytes per second");
Serial.print(test->name);
Serial.print(" Decrypt ... ");
start = micros();
for (count = 0; count < 5000; ++count) {
cipher->decryptBlock(buffer, buffer);
}
elapsed = micros() - start;
Serial.print(elapsed / (5000.0 * 16.0));
Serial.print("us per byte, ");
Serial.print((16.0 * 5000.0 * 1000000.0) / elapsed);
Serial.println(" bytes per second");
Serial.println();
}
void setup()
{
Serial.begin(9600);
Serial.println();
Serial.println("State Sizes:");
Serial.print("AES128 ... ");
Serial.println(sizeof(AES128));
Serial.print("AES192 ... ");
Serial.println(sizeof(AES192));
Serial.print("AES256 ... ");
Serial.println(sizeof(AES256));
Serial.println();
Serial.println("Test Vectors:");
testCipher(&aes128, &testVectorAES128);
testCipher(&aes192, &testVectorAES192);
testCipher(&aes256, &testVectorAES256);
Serial.println();
Serial.println("Performance Tests:");
perfCipher(&aes128, &testVectorAES128);
perfCipher(&aes192, &testVectorAES192);
perfCipher(&aes256, &testVectorAES256);
}
void loop()
{
}
Here is some information on the library:
https://rweather.github.io/arduinolibs/classBlockCipher.html#ac3ba2450222aa1ea804ae4881ab6440c
I am looking for examples of calling the libtomcrypt ecc sign and verify code using inputs created using the OpenSSL command line. I have created private and public keys, using the prime256v1 curve, created a signature for a text file and verified it using the command line. I have pulled those keys and the signature in to my C++ test driver and used the libtomcrypt methods to import those. I created the hash of the text file using the openssl command line and put that into my test driver as well, but when I call the libtomcrypt ecc_verify_hash method, it fails to verify. At this point I have verified that the keys and the signature are importing correctly, so I'm not sure what I'm doing incorrectly.
Here is the code for my test driver. Any help with this would be greatly appreciated.
int main ()
{
ltc_mp = ltm_desc;
int idx = 0;
prng_state myPrng;
//unsigned char buf[4096];
//unsigned long b;
int stat, err;
ecc_key pubKey;
ecc_key privKey;
void* sig;
static unsigned char MSG_STR[] = "Test Message";
const int MSG_STR_LEN = 12;
static unsigned char DGST[] =
"C87D25A09584C040F3BFC53B5701199591DEB10BA";
const int DGST_LEN = 41;
// the DER forms of the keys
static unsigned char PRIV_KEY[] =
{
0x30, 0x77, 0x02, 0x01, 0x01, 0x04, 0x20, 0x4f,
0xab, 0xef, 0xfb, 0x6a, 0xe4, 0xcd, 0xd5, 0x83,
0xb7, 0x39, 0x57, 0xf5, 0x63, 0xd4, 0x60, 0xa6,
0x83, 0x26, 0x56, 0xb8, 0x1e, 0x78, 0xee, 0x3d,
0xf0, 0xa9, 0xe8, 0x3b, 0x2c, 0x34, 0xc1, 0xa0,
0x0a, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d,
0x03, 0x01, 0x07, 0xa1, 0x44, 0x03, 0x42, 0x00,
0x04, 0x24, 0x72, 0xcb, 0x56, 0xb5, 0xae, 0x0a,
0x8e, 0x14, 0xc9, 0x89, 0x3d, 0xc8, 0x61, 0xb6,
0xed, 0x74, 0xa2, 0x2f, 0x15, 0xb7, 0x31, 0x14,
0xc6, 0xd5, 0x38, 0x71, 0xaa, 0x3f, 0x52, 0x5c,
0x8e, 0x3e, 0x59, 0xaa, 0x68, 0xcd, 0xcd, 0x8c,
0x2c, 0x63, 0x9b, 0xbc, 0x46, 0x79, 0x87, 0xd4,
0xdd, 0x4f, 0xbd, 0x97, 0x2b, 0xc2, 0x6a, 0x5d,
0x5c, 0xb5, 0xc7, 0x91, 0xaa, 0x15, 0xf8, 0x7c,
0x27
};
const int PRIV_KEY_LEN = 121;
static unsigned char PUB_KEY[] =
{
0x30, 0x59, 0x30, 0x13, 0x06, 0x07, 0x2a, 0x86,
0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a,
0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, 0x03,
0x42, 0x00, 0x04, 0x24, 0x72, 0xcb, 0x56, 0xb5,
0xae, 0x0a, 0x8e, 0x14, 0xc9, 0x89, 0x3d, 0xc8,
0x61, 0xb6, 0xed, 0x74, 0xa2, 0x2f, 0x15, 0xb7,
0x31, 0x14, 0xc6, 0xd5, 0x38, 0x71, 0xaa, 0x3f,
0x52, 0x5c, 0x8e, 0x3e, 0x59, 0xaa, 0x68, 0xcd,
0xcd, 0x8c, 0x2c, 0x63, 0x9b, 0xbc, 0x46, 0x79,
0x87, 0xd4, 0xdd, 0x4f, 0xbd, 0x97, 0x2b, 0xc2,
0x6a, 0x5d, 0x5c, 0xb5, 0xc7, 0x91, 0xaa, 0x15,
0xf8, 0x7c, 0x27
};
const int PUB_KEY_LEN = 91;
static unsigned char OPENSSL_SIG[] =
{
0x30, 0x46, 0x02, 0x21, 0x00, 0xf3, 0x69, 0xd9,
0xaf, 0xad, 0xc7, 0x16, 0xac, 0x03, 0x9a, 0x5c,
0xae, 0xf7, 0x05, 0x86, 0x56, 0x24, 0x18, 0x09,
0x44, 0x3b, 0x07, 0xc9, 0xec, 0x6b, 0x72, 0x69,
0x18, 0x13, 0x10, 0xd7, 0x32, 0x02, 0x21, 0x00,
0xd5, 0xa1, 0x21, 0xc0, 0xf8, 0x5d, 0xb6, 0x6f,
0x46, 0x04, 0x9b, 0x2e, 0x1f, 0x96, 0xdc, 0x55,
0xd6, 0x5e, 0xc5, 0x14, 0x3c, 0x68, 0x70, 0x2a,
0x0c, 0x17, 0xad, 0xa1, 0xb8, 0x90, 0x0b, 0x07
};
const int OPENSSL_SIG_LEN = 72;
static unsigned char RAND_DATA[] =
"8e2582c586c37099c3126170e48352c5416c41ef44972ec0708ef2caab08"
"583f10b277276c2507dee509a0c492bb806108590db675f6d908025999a6"
"7cc049290fe4ea0beb6b62004cbc82c580e5c00fa360d2f55b565535f6ec"
"41a2adfe83ffc3a2007ee1e1ba03b94fb777350e1c3a2caffcf6434534bd"
"8d29c2b62a9abf279c9a419a53ae54c3f22d0d0886ced81d7422bfb2493b"
"2231d8aad890d80f1dc42280e6803fa768d2a5ebaf954e91713d9e0d93d9"
"9749b7b29d8deac683fa930d39758212020e6ebc850993804ce6a6a969a3"
"261ad870040634bb2c506c911e06b20ab723d03df239c39ecf210fa67ab2"
"54573c5531a475c3e7158eb89074a77238182b9b72d0f4d1407cffb6d59c"
"53ec202a5b36bd649b2a6f31ad182072d549402df2e1ed8289ad08973444"
"1269bbccca6bc8899e8a750329c35a1c7a19281bf90c1575ebc5c197e19f"
"5cc4e3f3dd5f507df6457a6e39b4609a774c11e5b15210d2a49ee3dab9e3"
"fb89966b8fa83cc9c3045f67001f8447d9d035c512f3c1c6332c701b403c"
"52d424d116b78426e013e34b1c3b7dbf87669970799a719971e1583135db"
"727ea7297bf76d093fc4648fca0cf79dbd6144963061838178d8b32efe9d"
"ee5ff58e9c6e6fc922b9e94631c7d76e06a0d28fc1c40d634e65e332ed4d"
"ff85293e5a9f103410c5e974775be91a773508293b5fcd8672a1a30640a7"
"9e42755d2b5229292848b6e56b540ba2d1bd2e7e5d61895293c5e9ee83ce"
"bba4194a3191fc9f0e6924835761c512d0515bb63b334c5e98dc0d43e7ce"
"3ee11e46872e4d8d767104189429f5cbcf4fd8db7c3022dd5e5ae99f5a48"
"e595fdff8d9db23e3beebef219341c53aa7e6da2fad750d2a648b58a0151"
"d0b029cf5d1900b65c047b95dd3004a048be4611d49f6a96f3f054b3c476"
"4b2e1a521a57e46b3df537d8a442726ce0e318fc356538e888d8825edfa2"
"4c3c42716a628c331db6261455c9c5d4b98d466f4831d28a6f9eb2abe758"
"38f81ca42e06f362d3d263a9c0e7bffaff635fc640434b02dd2f740a4737"
"6180bbe217436b86d481f83825e28b92ab3444b7c3326e18d796ac9c2633"
"4d9cc9310f29f42dfe4d9b53bd40b7f1433708c87ad412bbf8b646ec1468"
"76a8083e08029de1b84f7ebb606bf84a59dc7f1b46df2cc5c0ba5008761d"
"9da2344434f524e7ad6e648964a761907c1a0ccb1fdee645ad7aaf4ea0c8"
"68fd39ae75b2bc41b8c86f5adaa2105f84e9b187c5a887c9e0d726383564"
"34fe79c7ae0460cc7eddffe3f3109f83cf7a80ba8432ff9dd9f0b664cb6d"
"fc215800e4253d0bfa1a74df5f7b52f0d0ac9e3ce1af856058d7c2b117ae"
"7a5a7cfac7ea592e88c8417d16fe10f4545af981e937fb194c26a2e44f3f"
"40ea26dd794ad8f2d16f36eb27c6eaa6780925e8d7e96dfdde483800a9b3"
"95c7a358";
const int RAND_DATA_LEN = 2049;
ltm_desc.init(&privKey.k);
ltm_desc.init(&pubKey.pubkey.x);
ltm_desc.init(&pubKey.pubkey.y);
ltm_desc.init(&pubKey.pubkey.z);
ltm_desc.init(&sig);
// set up the random number generator
if ((err = register_prng(&yarrow_desc)) != CRYPT_OK) {
cout << "Failed to register prng. Err = " << err << endl;
return err;
}
if ((err = yarrow_start(&myPrng)) != CRYPT_OK) {
cout << "Yarrow failed to start! Err = " << err << endl;
return err;
}
if ((err = yarrow_add_entropy(RAND_DATA, RAND_DATA_LEN, &myPrng))
!= CRYPT_OK) {
cout << "Failed adding entropy Err = " << err << endl;
return err;
}
if ((err = yarrow_ready(&myPrng)) != CRYPT_OK) {
cout << "Failed to set state to ready. Err = " << err << endl;
return err;
}
// read in the private key information
if ((err = ecc_import_openssl(PRIV_KEY, PRIV_KEY_LEN, &privKey))
!= CRYPT_OK)
{
cout << "Failed to import private key, err =" << err << endl;
return err;
}
// read in the public key information
if ((err = ecc_import_openssl(PUB_KEY, PUB_KEY_LEN, &pubKey))
!= CRYPT_OK)
{
cout << "Failed to import public key, err =" << err << endl;
return err;
}
// try to verify the signature created using OpenSSL
// with the libtom verify method
if (((err = ecc_verify_hash(OPENSSL_SIG,
OPENSSL_SIG_LEN,
DGST,
DGST_LEN,
&stat,
&pubKey)) != CRYPT_OK)) {
cout << "Failed to verify OpenSSL signature using libtom. Error ="
<< err << endl;
return err;
} else if (stat == 0) {
cout << "Failed to verify OpenSSL signature using libtom. Error ="
<< err << endl;
return err;
}
else {
cout << "Successfully verifed OpenSSL signature using libtom."
<< endl;
}
ecc_free (&pubKey);
ecc_free (&privKey);
}
I need to do AES-128/CTR encryption/decryption using Intel TinyCrypt (written in C). I took a look at the test case provided by the library, but still have a few questions on how to use it. Here's the code of the test case (see the complete source code here):
/*
* NIST SP 800-38a CTR Test for encryption and decryption.
*/
uint32_t test_1_and_2(void)
{
const uint8_t key[16] = {
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, 0x88,
0x09, 0xcf, 0x4f, 0x3c
};
uint8_t ctr[16] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
0xfc, 0xfd, 0xfe, 0xff
};
const uint8_t plaintext[64] = {
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
0x73, 0x93, 0x17, 0x2a, 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, 0x30, 0xc8, 0x1c, 0x46,
0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b,
0xe6, 0x6c, 0x37, 0x10
};
const uint8_t ciphertext[80] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb,
0xfc, 0xfd, 0xfe, 0xff, 0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce, 0x98, 0x06, 0xf6, 0x6b,
0x79, 0x70, 0xfd, 0xff, 0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f, 0x09, 0x02,
0x0d, 0xb0, 0x3e, 0xab, 0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
};
struct tc_aes_key_sched_struct sched;
uint8_t out[80];
uint8_t decrypted[64];
uint32_t result = TC_PASS;
TC_PRINT("CTR test #1 (encryption SP 800-38a tests):\n");
(void)tc_aes128_set_encrypt_key(&sched, key);
(void)memcpy(out, ctr, sizeof(ctr));
if (tc_ctr_mode(&out[TC_AES_BLOCK_SIZE], sizeof(plaintext),
plaintext, sizeof(plaintext), ctr, &sched) == 0) {
TC_ERROR("CTR test #1 (encryption SP 800-38a tests) failed in %s.\n", __func__);
result = TC_FAIL;
goto exitTest1;
}
result = check_result(1, ciphertext, sizeof(out), out, sizeof(out));
TC_END_RESULT(result);
TC_PRINT("CTR test #2 (decryption SP 800-38a tests):\n");
(void) memcpy(ctr, out, sizeof(ctr));
if (tc_ctr_mode(decrypted, sizeof(decrypted), &out[TC_AES_BLOCK_SIZE],
sizeof(decrypted), ctr, &sched) == 0) {
TC_ERROR("CTR test #2 (decryption SP 800-38a tests) failed in %s.\n", __func__);
result = TC_FAIL;
goto exitTest1;
}
result = check_result(2, plaintext, sizeof(plaintext),
decrypted, sizeof(plaintext));
exitTest1:
TC_END_RESULT(result);
return result;
}
My input data is a double pointer (2D array) and I'd like to keep it that way:
uint8_t ** mydata = gen_rand_data(const size_t height, const size_t length);
Here's my understanding (correct me if I'm wrong):
AES-128 is a block cipher, with 128-bit key.
CTR mode makes the AES-128 a stream cipher and lets encrypt/decrypt data with arbitrary size.
Having AES-128/CTR, the output (ciphertext) has the same length as plaintext.
To improve the security, it's better to use 128-bit IV (nonce) for encryption.
Now here's my questions:
Can I say ctr (initial counter, right?) is our IV?
Why the ciphertext (expected value) and out (calculated value) are 128-bit bigger than plaintext?
Why the code copies ctr value in the beginning of out?
To encrypt mydata (2D array), I just need to loop over it and simple feed bytes from mydata to tc_ctr_mode, regardless of the dimension of mydata. Is this correct?
Thanks in advance.
The counter is generally supplied as the iv parameter if there is no specific counter parameter.
The output is 16-bytes longer because the counter is prepended, the counter is block sized and AES has a 16-byte block size..
The counter value is not secret and is needed for decryption. Since the same key/counter combination can never be reused if the key is going to be reused (a common situation) the counter must be unique and thus not pre-shared. The general answer is to pre-pend the counter value to the encrypted data.
Yes providing the decryption end knows a priori the array dimensions. But is is probably better to use some form of marshaling to preserve the array shape.
I have a telemetry stream that is passed through a hardware CRC generator appending the CRC to the end of each telemetry frame. Now I am trying to make something to verify the hardware generated CRC. I have old legacy code (see below) that computes the correct CRC (verified multiple times). However it is slow since each telemetry frame is 300+ bytes and there can be upwards of 10,000,000+ frames to process.
After some research I found some literature pointing me to a table driven approach. The legacy method uses a Poly of 0x8005, reverses the bit order of each byte before processing, and initializes the CRC to zero. However, after building tables for that poly with reversed and non-reversed input and just trying to work through even the first byte of data (0x10) I can not get anything to match what the legacy method is generating.
Inside the calcCRC16 function (below) the existing method checks the LSB where others seem to check the MSB ... and the bit shifts are to the right where other examples I have seen are to the left. I am getting lost in why this is especially since the bit order is swapped before passing it to this function. I have tried on-line calculators & manually doing the look-ups in tables switching the poly from 8005 to A001(reversed), normal and reversed input bytes, and every combination I can think of but can't get any table approach to match the legacy code which I know to be correct for our hardware implementation.
Can anyone help me out if I am missing something obvious? How would you go about creating a table based approach to create the same output? I am a novice at C++ and not really familiar with CRC generation and could be overlooking something fundamental. Sample code and output which was verified against the hardware CRC follows:
Sample Code: I just hard coded a few bytes out of a know telemetry stream as an example
/** ********************************************************************************
* TEST CRC METHOD
*******************************************************************************/
#include <iostream>
using namespace std;
unsigned char swapBits(unsigned char d);
void calcCRC16(unsigned int *CRCVal, unsigned char value);
/** **************************************************************************
* #function main
* TEST CRC METHOd
*******************************************************************************/
int main(int argc, char* argv[])
{
short dataLength = 5;
unsigned int givenCrc;
unsigned int calcCrc;
unsigned char byte[] = {0x10,0xbb,0x42,0x4d,0xfd};
/* Init CRC. */
calcCrc = 0;
cout << "Inital CRC = " << hex << calcCrc << "\n";
/* Read frame data. */
for (int i = 0; i < dataLength; i++)
{
cout << "byte = " << hex << static_cast<int16_t>(byte[i]) << " ";
calcCRC16(&calcCrc, swapBits(byte[i]));
cout << "calcCRC = " << hex << calcCrc << "\n";
}
}
/** ********************************************************************
* #function swapBits
* Swaps the bits so they match the order sent to CRC Gen Hardware
************************************************************************/
unsigned char swapBits(unsigned char d)
{
unsigned char t = 0;
int i = 0;
int n = 0x80;
for(i=0x01; i<0x100; i=i<<1)
{
if (d & i)
{
t|=n;
}
n=n>>1;
}
return t;
}
/** ********************************************************************
* #function calcCRC16
* WORKING METHOD VERIFIED AGAINST CRC HARDWARE
************************************************************************/
void calcCRC16(unsigned int *CRCVal, unsigned char value)
{
unsigned char lsb;
unsigned char bcnt;
for (bcnt=0 ; bcnt<8 ; bcnt++)
{
lsb = (value ^ *CRCVal) & 0x01;
*CRCVal >>= 1;
value >>= 1;
if (lsb != 0)
{
*CRCVal ^= 0x8005;
}
}
}
Output:
Inital CRC = 0
byte = 10 calcCRC = b804
byte = bb calcCRC = 1fb8
byte = 42 calcCRC = 461d
byte = 4d calcCRC = 3d47
byte = fd calcCRC = 683e
rev16(crc16(0, buf, len)) will give you the CRCs listed.
#include <stdio.h>
static unsigned short crc16_table[] = {
0x0000, 0xa001, 0xe003, 0x4002, 0x6007, 0xc006, 0x8004, 0x2005,
0xc00e, 0x600f, 0x200d, 0x800c, 0xa009, 0x0008, 0x400a, 0xe00b,
0x201d, 0x801c, 0xc01e, 0x601f, 0x401a, 0xe01b, 0xa019, 0x0018,
0xe013, 0x4012, 0x0010, 0xa011, 0x8014, 0x2015, 0x6017, 0xc016,
0x403a, 0xe03b, 0xa039, 0x0038, 0x203d, 0x803c, 0xc03e, 0x603f,
0x8034, 0x2035, 0x6037, 0xc036, 0xe033, 0x4032, 0x0030, 0xa031,
0x6027, 0xc026, 0x8024, 0x2025, 0x0020, 0xa021, 0xe023, 0x4022,
0xa029, 0x0028, 0x402a, 0xe02b, 0xc02e, 0x602f, 0x202d, 0x802c,
0x8074, 0x2075, 0x6077, 0xc076, 0xe073, 0x4072, 0x0070, 0xa071,
0x407a, 0xe07b, 0xa079, 0x0078, 0x207d, 0x807c, 0xc07e, 0x607f,
0xa069, 0x0068, 0x406a, 0xe06b, 0xc06e, 0x606f, 0x206d, 0x806c,
0x6067, 0xc066, 0x8064, 0x2065, 0x0060, 0xa061, 0xe063, 0x4062,
0xc04e, 0x604f, 0x204d, 0x804c, 0xa049, 0x0048, 0x404a, 0xe04b,
0x0040, 0xa041, 0xe043, 0x4042, 0x6047, 0xc046, 0x8044, 0x2045,
0xe053, 0x4052, 0x0050, 0xa051, 0x8054, 0x2055, 0x6057, 0xc056,
0x205d, 0x805c, 0xc05e, 0x605f, 0x405a, 0xe05b, 0xa059, 0x0058,
0xa0e9, 0x00e8, 0x40ea, 0xe0eb, 0xc0ee, 0x60ef, 0x20ed, 0x80ec,
0x60e7, 0xc0e6, 0x80e4, 0x20e5, 0x00e0, 0xa0e1, 0xe0e3, 0x40e2,
0x80f4, 0x20f5, 0x60f7, 0xc0f6, 0xe0f3, 0x40f2, 0x00f0, 0xa0f1,
0x40fa, 0xe0fb, 0xa0f9, 0x00f8, 0x20fd, 0x80fc, 0xc0fe, 0x60ff,
0xe0d3, 0x40d2, 0x00d0, 0xa0d1, 0x80d4, 0x20d5, 0x60d7, 0xc0d6,
0x20dd, 0x80dc, 0xc0de, 0x60df, 0x40da, 0xe0db, 0xa0d9, 0x00d8,
0xc0ce, 0x60cf, 0x20cd, 0x80cc, 0xa0c9, 0x00c8, 0x40ca, 0xe0cb,
0x00c0, 0xa0c1, 0xe0c3, 0x40c2, 0x60c7, 0xc0c6, 0x80c4, 0x20c5,
0x209d, 0x809c, 0xc09e, 0x609f, 0x409a, 0xe09b, 0xa099, 0x0098,
0xe093, 0x4092, 0x0090, 0xa091, 0x8094, 0x2095, 0x6097, 0xc096,
0x0080, 0xa081, 0xe083, 0x4082, 0x6087, 0xc086, 0x8084, 0x2085,
0xc08e, 0x608f, 0x208d, 0x808c, 0xa089, 0x0088, 0x408a, 0xe08b,
0x60a7, 0xc0a6, 0x80a4, 0x20a5, 0x00a0, 0xa0a1, 0xe0a3, 0x40a2,
0xa0a9, 0x00a8, 0x40aa, 0xe0ab, 0xc0ae, 0x60af, 0x20ad, 0x80ac,
0x40ba, 0xe0bb, 0xa0b9, 0x00b8, 0x20bd, 0x80bc, 0xc0be, 0x60bf,
0x80b4, 0x20b5, 0x60b7, 0xc0b6, 0xe0b3, 0x40b2, 0x00b0, 0xa0b1};
static unsigned char rev_table[] = {
0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, 0x10, 0x90, 0x50, 0xd0,
0x30, 0xb0, 0x70, 0xf0, 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, 0x04, 0x84, 0x44, 0xc4,
0x24, 0xa4, 0x64, 0xe4, 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, 0x1c, 0x9c, 0x5c, 0xdc,
0x3c, 0xbc, 0x7c, 0xfc, 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, 0x0a, 0x8a, 0x4a, 0xca,
0x2a, 0xaa, 0x6a, 0xea, 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, 0x16, 0x96, 0x56, 0xd6,
0x36, 0xb6, 0x76, 0xf6, 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, 0x01, 0x81, 0x41, 0xc1,
0x21, 0xa1, 0x61, 0xe1, 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, 0x19, 0x99, 0x59, 0xd9,
0x39, 0xb9, 0x79, 0xf9, 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, 0x0d, 0x8d, 0x4d, 0xcd,
0x2d, 0xad, 0x6d, 0xed, 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, 0x13, 0x93, 0x53, 0xd3,
0x33, 0xb3, 0x73, 0xf3, 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, 0x07, 0x87, 0x47, 0xc7,
0x27, 0xa7, 0x67, 0xe7, 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, 0x1f, 0x9f, 0x5f, 0xdf,
0x3f, 0xbf, 0x7f, 0xff};
unsigned crc16(unsigned crc, unsigned char *buf, int len)
{
while (len--) {
crc ^= *buf++ << 8;
crc = (crc << 8) ^ crc16_table[(crc >> 8) & 0xff];
}
return crc & 0xffff;
}
inline unsigned rev16(unsigned val)
{
return (rev_table[val & 0xff] << 8) | rev_table[(val >> 8) & 0xff];
}