#include <stdlib.h>
#include <string.h> // memcpy
#include <CommonCrypto/CommonDigest.h>
#include <CommonCrypto/CommonDigestSPI.h>
#include <corecrypto/ccn.h>
#include "p12pbegen.h"
static uint8_t *concatenate_to_blocksize(const uint8_t *data, size_t data_length,
size_t blocksize, size_t *blocklength)
{
size_t block_length = blocksize * ((data_length + blocksize - 1) / blocksize);
uint8_t *block_ptr, *block;
block_ptr = block = malloc(block_length);
if (!block_ptr)
return NULL;
while (block_ptr < block + block_length) {
size_t bytes_to_move = block + block_length - block_ptr;
memcpy(block_ptr, data, bytes_to_move > data_length ? data_length : bytes_to_move);
block_ptr += data_length;
}
*blocklength = block_length;
return block;
}
int p12_pbe_gen(CFStringRef passphrase, uint8_t *salt_ptr, size_t salt_length,
unsigned iter_count, P12_PBE_ID pbe_id, uint8_t *data, size_t length)
{
unsigned int hash_blocksize = CC_SHA1_BLOCK_BYTES;
unsigned int hash_outputsize = CC_SHA1_DIGEST_LENGTH;
if (!passphrase)
return -1;
unsigned char diversifier[hash_blocksize];
memset(diversifier, pbe_id, sizeof(diversifier));
CFDataRef passphrase_be_unicode = CFStringCreateExternalRepresentation(kCFAllocatorDefault, passphrase, kCFStringEncodingUTF16BE, '\0');
if (!passphrase_be_unicode)
return -1;
uint8_t null_termination[2] = { 0, 0 };
CFMutableDataRef passphrase_be_unicode_null_term = CFDataCreateMutableCopy(NULL, 0, passphrase_be_unicode);
CFRelease(passphrase_be_unicode);
if (!passphrase_be_unicode_null_term)
return -1;
CFDataAppendBytes(passphrase_be_unicode_null_term, null_termination, sizeof(null_termination));
uint8_t *passphrase_data = NULL;
size_t passphrase_data_len = 0;
size_t passphrase_length = CFDataGetLength(passphrase_be_unicode_null_term);
const unsigned char *passphrase_ptr = CFDataGetBytePtr(passphrase_be_unicode_null_term);
passphrase_data = concatenate_to_blocksize(passphrase_ptr, passphrase_length, hash_blocksize, &passphrase_data_len);
CFRelease(passphrase_be_unicode_null_term);
if (!passphrase_data)
return -1;
uint8_t *salt_data = NULL;
size_t salt_data_len = 0;
if (salt_length)
salt_data = concatenate_to_blocksize(salt_ptr, salt_length, hash_blocksize, &salt_data_len);
if (!salt_data){
free(passphrase_data);
return -1;
}
size_t I_length = salt_data_len + passphrase_data_len;
uint8_t *I_data = malloc(I_length);
if (!I_data){
free(salt_data);
free(passphrase_data);
return -1;
}
memcpy(I_data + 0, salt_data, salt_data_len);
memcpy(I_data + salt_data_len, passphrase_data, passphrase_data_len);
free(salt_data);
free(passphrase_data);
size_t hash_output_blocks = (length + hash_outputsize - 1) / hash_outputsize;
size_t temp_buf_size = hash_output_blocks * hash_outputsize;
uint8_t *temp_buf = malloc(temp_buf_size);
uint8_t *cursor = temp_buf;
if (!temp_buf){
free(I_data);
return -1;
}
while (cursor < temp_buf + temp_buf_size) {
CC_SHA1_CTX ctx;
CC_SHA1_Init(&ctx);
CC_SHA1_Update(&ctx, diversifier, (CC_LONG)sizeof(diversifier));
assert(I_length<=UINT32_MAX);
CC_SHA1_Update(&ctx, I_data, (CC_LONG)I_length);
CC_SHA1_Final(cursor, &ctx);
unsigned int i;
for (i = 1; i < iter_count; i++)
CCDigest(kCCDigestSHA1, cursor, hash_outputsize, cursor);
size_t A_i_len = 0;
uint8_t *A_i = concatenate_to_blocksize(cursor,
hash_outputsize, hash_blocksize, &A_i_len);
if (!A_i){
free(I_data);
free(temp_buf);
return -1;
}
const cc_size tmp_n = ccn_nof_size(A_i_len + 1) > ccn_nof_size(hash_blocksize) ? ccn_nof_size(A_i_len + 1) : ccn_nof_size(hash_blocksize);
cc_unit tmp1[tmp_n];
ccn_read_uint(tmp_n, tmp1, A_i_len, A_i);
ccn_add1(tmp_n, tmp1, tmp1, 1);
free(A_i);
cc_unit tmp2[tmp_n];
unsigned int j;
for (j = 0; j < I_length; j+=hash_blocksize) {
ccn_read_uint(tmp_n, tmp2, hash_blocksize, I_data + j);
ccn_add(tmp_n, tmp2, tmp2, tmp1);
size_t bitSize;
const size_t hash_blocksize_bits = hash_blocksize * 8;
while ((bitSize = ccn_bitlen(tmp_n, tmp2)) > hash_blocksize_bits)
{
ccn_set_bit(tmp2, bitSize - 1, 0);
}
ccn_write_uint_padded(tmp_n, tmp2, hash_blocksize, I_data + j);
}
cursor += hash_outputsize;
}
memmove(data, temp_buf, length);
free(temp_buf);
free(I_data);
return 0;
}
#if 0
bool test()
{
CFStringRef password = CFSTR("smeg");
unsigned char salt_bytes[] = { 0x0A, 0x58, 0xCF, 0x64, 0x53, 0x0D, 0x82, 0x3F };
CFDataRef salt = CFDataCreate(NULL, salt_bytes, sizeof(salt_bytes));
unsigned char correct_result[] = { 0x8A, 0xAA, 0xE6, 0x29, 0x7B, 0x6C, 0xB0, 0x46, 0x42, 0xAB, 0x5B, 0x07, 0x78, 0x51, 0x28, 0x4E, 0xB7, 0x12, 0x8F, 0x1A, 0x2A, 0x7F, 0xBC, 0xA3 };
unsigned char result[24];
p12PbeGen(password, salt, 1, PBE_ID_Key, result, sizeof(result));
if (memcmp(correct_result, result, sizeof(correct_result))) {
printf("test failure\n");
return false;
}
return true;
}
bool test2()
{
CFStringRef password = CFSTR("queeg");
unsigned char salt_bytes[] = { 0x05,0xDE,0xC9,0x59,0xAC,0xFF,0x72,0xF7 };
CFDataRef salt = CFDataCreate(NULL, salt_bytes, sizeof(salt_bytes));
unsigned char correct_result[] = { 0xED,0x20,0x34,0xE3,0x63,0x28,0x83,0x0F,0xF0,0x9D,0xF1,0xE1,0xA0,0x7D,0xD3,0x57,0x18,0x5D,0xAC,0x0D,0x4F,0x9E,0xB3,0xD4 };
unsigned char result[24];
p12PbeGen(password, salt, 1000, PBE_ID_Key, result, sizeof(result));
if (memcmp(correct_result, result, sizeof(correct_result))) {
printf("test failure\n");
return false;
}
return true;
}
int main(int argc, char *argv[])
{
test();
test2();
}
#endif