I'm trying to implement a simple command-line tool to calculate the (Key Check Value) KCV of some encryption keys. The algorithm for the KCV is simply to encrypt a block of 8 zeros with DES and get the first three bytes of it (this is called VISA KCV, and there are a bunch of other KCV algorithms).
So, I wrote this code:
/*----------------------------------------------------------------------------*/
static void
str2DES_cblock_(const char *str, DES_cblock* out)
{
// Make a char pointer and point it at the start of the array
unsigned char *o;
o = out[0];
// Read the string
int i;
for (i = 0; i < 8; i++)
sscanf(&(str[i*2]),"%2hhx", o++);
}
static int
getVisaKcv_( const char* key, const int keyStrLen, unsigned char* kcv )
{
DES_cblock key1, key2;
DES_key_schedule keySchedule1, keySchedule2;
DES_cblock encryptedBlock;
int rc = EXIT_SUCCESS;
static const_DES_cblock blockToEncrypt_ = { 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 };
memset( encryptedBlock, 0x00, sizeof(encryptedBlock) );
if( 16 == keyStrLen )
{
str2DES_cblock_( key, &key1 );
DES_set_key_unchecked( &key1, &keySchedule1 );
DES_ecb_encrypt(
&blockToEncrypt_,
&encryptedBlock,
&keySchedule1,
DES_ENCRYPT
);
kcv[0] = encryptedBlock[0];
kcv[1] = encryptedBlock[1];
kcv[2] = encryptedBlock[2];
}
else if( 32 == keyStrLen )
{
str2DES_cblock_( key, &key1 );
str2DES_cblock_( key+16, &key2 );
DES_set_key_unchecked( &key1, &keySchedule1 );
DES_set_key_unchecked( &key2, &keySchedule2 );
DES_ecb2_encrypt( &blockToEncrypt_,
&encryptedBlock,
&keySchedule1,
&keySchedule2,
DES_ENCRYPT );
kcv[0] = encryptedBlock[0];
kcv[1] = encryptedBlock[1];
kcv[2] = encryptedBlock[2];
}
else
{
rc = ERROR_INVALID_PARAM;
}
return rc;
}
It uses OpenSSL's functions to perform encryption. The things is that it is giving me wrong results. For exemple, encrypting "19c7221b33c1b2b4c68bc48deb86234b" results in encryptedBlock = [ 33 4D 78 7E FB C0 ED E8 ]. Some high quality validation tools I have (it is called BP-Tools and works for all things payment-industry related) gives encryptedBlock = [ 90 D4 49 08 A7 9F 45 44 ] as result. I'm not sure where the problem is (it is a very simple encryption). Did I forget something in the sequence of OpenSSL API calls? It gives wrong results for 8-byte keys also.
Here are some test cases (they match both EFTLab's and BP-Tools results):
Enc( plaintext=[0000000000000000], key=[9be8ec942f9f7791] ) = [F4E94D2A2901D6C6]
Enc( plaintext=[0000000000000000], key=[6afbadf94fd06945d365856382898c25] ) = [F65EC8B0B78F2C0A]
Enc( plaintext=[0000000000000000], key=[8f40053aae44e017] ) = [4A83D7F98644363E]
Enc( plaintext=[0000000000000000], key=[19c7221b33c1b2b4c68bc48deb86234b] ) = [90D44908A79F4544]
Enc( plaintext=[0000000000000000], key=[b0e9c35e20171642] ) = [7FBA49A7CD6A8AEB]
Enc( plaintext=[0000000000000000], key=[a12815befca06412f357db3ab41d7cf9] ) = [67F735CD1696E114]
Enc( plaintext=[0000000000000000], key=[e5c82dbe8c0b3c17] ) = [4CACBD023C95FD7C]
Enc( plaintext=[0000000000000000], key=[c74b48ebfa79f8ca46d06f8436597230] ) = [7FD3FA9F5B338E13]
str2DES_cblock_
but it must be wrong, because if I add a correct one your code exactly as posted works. It does give compile warnings (which you should always check) because you omitted the&
on the first two args todes_ecb2_encrypt
and thus pass pointers of the wrong type; the C standard allows these to be different and cause Undefined Behavior, but in practice on all systems 'normal' programmers use today all data pointers are the same. $\endgroup$