13

I am trying to use PKCS5_PBKDF2_HMAC_SHA1() and below is my sample program. I wanted to make sure if my result of PKCS5_PBKDF2_HMAC_SHA1() is correct so I verified the same with the website http://anandam.name/pbkdf2/ and I see a different result. Am I using the API correctly?

I am having doubts if I am passing salt value correctly.

I have pasted my result and website result after the program.

Please help me understand this.

#include <stdio.h>     
#include <types.h> 
#include <string.h> 
#include <stdio.h> 
#include <stdlib.h> 

#include <malloc.h> 

#include <openssl/hmac.h> 
#include <openssl/evp.h> 
#include <openssl/engine.h> 
#include <openssl/aes.h>
#include <openssl/rand.h> 

#include <proto.h> 
#define KEY_LEN    32// 32 bytes - 256 bits 
#define KEK_KEY_LEN   5 
#define ITERATION   1000 

unsigned char salt_value[KEY_LEN]; 
unsigned char AESkey[KEY_LEN]; 
unsigned char XTSkey[KEY_LEN]; 
u8 fuse_key[KEY_LEN]; 

void main() 
{ 
    s32 i=0; 
    s32 len =0; 
    u8 *out; 
    u8 *rspHMAC; 
    const s8 pwd[] = "test"; 
    s8 rspPKCS5[KEK_KEY_LEN * 2]; 
    s32 ret; 

    rspHMAC = (unsigned char *) malloc(sizeof(char) * KEY_LEN); 
    out = (unsigned char *) malloc(sizeof(char) * KEK_KEY_LEN); 

    RAND_bytes(salt_value, KEY_LEN); 

    printf("\n salt_value[0] = %x; salt_value[31]= %x", salt_value[0], salt_value[31]); 
    printf("\n strlen(salt_value) = %d; sizeof(salt_value) = %d\n", strlen(salt_value), sizeof(salt_value));  

    for(i = 0; i < KEY_LEN; i++) { 
        printf("%02x", salt_value[i]); 
    } 

    ret = PKCS5_PBKDF2_HMAC_SHA1(pwd, strlen(pwd), salt_value, strlen(salt_value), ITERATION, KEK_KEY_LEN, out); 
    printf("\n PKCS#5 :"); 

    for(len = 0; len < KEK_KEY_LEN; len++){ 
        printf("%02x", out[len]);

        sprintf(&rspPKCS5[len * 2], "%02x", out[len]); 
    } 

    printf("\n"); 
} 

Sample Output:

salt_value[0] = e2; salt_value[31]= 12 
strlen(salt_value) = 32; sizeof(salt_value) = 32 
e258017933f3e629a4166cece78f3162a3b0b7edb2e94c93d76fe6c38198ea12 
PKCS#5 :7d7ec9f411 

Website result:

The derived 40-bit key is: a5caf6a0d3 
jww
  • 97,681
  • 90
  • 411
  • 885
pkumarn
  • 1,383
  • 4
  • 22
  • 29
  • Related, there's now a man page for `PKCS5_PBKDF2_HMAC` and `PKCS5_PBKDF2_HMAC_SHA1`. See [`PKCS5_PBKDF2_HMAC (3)`](https://www.openssl.org/docs/crypto/PKCS5_PBKDF2_HMAC.html). – jww Apr 12 '15 at 21:49

1 Answers1

19

First, let's look at an official test vector for PBKDF2 HMAC-SHA1:

Input:
   P = "password" (8 octets)
   S = "salt" (4 octets)
   c = 1
   dkLen = 20

 Output:
   DK = 0c 60 c8 0f 96 1f 0e 71
        f3 a9 b5 24 af 60 12 06
        2f e0 37 a6             (20 octets)

So now we know what we're shooting for both on the web and in your program. So using that info, we find out that the web site wants your salt as an ASCII string, which it will then convert to bytes. This is important because you'll never be able to match the web page's output if you use RAND_bytes to generate a salt.

password
salt
1
20
0c60c80f961f0e71f3a9b524af6012062fe037a6

And you are using the salt incorrectly. In your commented line, you're generating a string with ASCII characters. If you want to use that salt, you'd have to declare it as an array of bytes. Plus, you're missing a digit.

unsigned char salt_value[]= { 0x5d, 0x85, 0x94, 0x7b, … /* and so on */ };

And in the uncommented code, you're generating an array of bytes but treating it as a string. You don't call strlen on an array of bytes because arrays of bytes can contain 0, which strlen will interpret as a null terminator. So you either track the size manually (e.g., your KEK_KEY_LEN define for the array you malloc) or you use sizeof when appropriate.

PKCS5_PBKDF2_HMAC_SHA1(pwd, strlen(pwd), salt_value, sizeof(salt_value), ITERATION, KEK_KEY_LEN, out);

So now that we know all these things, we can put together a full program that matches the output of both the web site and the official test vector.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <openssl/evp.h>

#define KEY_LEN      32
#define KEK_KEY_LEN  20
#define ITERATION     1 

int main()
{
    size_t i;
    unsigned char *out;
    const char pwd[] = "password";
    unsigned char salt_value[] = {'s','a','l','t'};

    out = (unsigned char *) malloc(sizeof(unsigned char) * KEK_KEY_LEN);

    printf("pass: %s\n", pwd);
    printf("ITERATION: %u\n", ITERATION);
    printf("salt: "); for(i=0;i<sizeof(salt_value);i++) { printf("%02x", salt_value[i]); } printf("\n");

    if( PKCS5_PBKDF2_HMAC_SHA1(pwd, strlen(pwd), salt_value, sizeof(salt_value), ITERATION, KEK_KEY_LEN, out) != 0 )
    {
        printf("out: "); for(i=0;i<KEK_KEY_LEN;i++) { printf("%02x", out[i]); } printf("\n");
    }
    else
    {
        fprintf(stderr, "PKCS5_PBKDF2_HMAC_SHA1 failed\n");
    }
 
    free(out);

    return 0;
}

(and note that main needs to return an int and you should free your allocated memory)

gcc pkcs5.c -o pkcs5 -g -lcrypto -Wall
./pkcs5
pass: password
ITERATION: 1
salt: 73616c74
out: 0c60c80f961f0e71f3a9b524af6012062fe037a6
Community
  • 1
  • 1
indiv
  • 17,306
  • 6
  • 61
  • 82
  • Thanks indiv for the reply. When i generate salt using RAND_bytes(), convert it to ASCII i am able to match the result (as my program does it) but i am looking at using Binary values for PKCS5_PBKDF2_HMAC_SHA1(). Is their any way to verify if the result generated using binary matches with the program output? – pkumarn Mar 20 '12 at 08:38
  • 1
    The document I linked to has a test vector that will catch mistakes in a binary implementation by using the password "pass\0word" and the salt "sa\0lt". The `\0` represents the byte 0x00. If your implementation can pass all the test vector test cases, then it's considered functionally correct. – indiv Mar 20 '12 at 14:02
  • Thanks for the answer, @indiv. Your solution worked for me, but I had to use strlen(salt_value) instead of sizeof(salt_value) in the call to PKCS5_PBKDF2_HMAC_SHA1(). – SlimPDX Jun 07 '17 at 05:04
  • 2
    @MaxMarchuk: I don't know why that's the case (but my guess is that you're using sizeof on a pointer instead of array), but be careful with `strlen`. `strlen` stops scanning at the first 0x00 byte. If you are using a binary (non-text) salt, any bytes with the value of 0 will break it. E.g., for this salt, `unsigned char salt_value[] = {0x00, 0x01, 0x02};`, you'll get a strlen of 0, which is wrong. If you are using only text (ASCII) strings for the salt, you should be fine. – indiv Jun 07 '17 at 15:37