1

I have C# code as below:


        private static string password = "Password";
        private static string salt = "SALT";
        private static string hashAlgorithm = "SHA1";
        private static int iterations = 2;

        var saltValueBytes = Encoding.UTF8.GetBytes(salt);
        var passwordKey = new PasswordDeriveBytes(password, saltValueBytes, hashAlgorithm, iterations)
...

I need to implement the same in Mac, I came to know that Opnessl implements related methods(i.e. libcrypto).

What is the equivalent method in Opnessl to above code?

Raviprakash
  • 2,410
  • 6
  • 34
  • 56
  • 1
    An iteration count of 2? Really? RFC2898 recommends at least 1000... – caf Oct 05 '10 at 12:57
  • Although it has nothing to do with your question, I'm currently asking myself why you're using an iteration count of 2. One of the goals of the iteration count is to increase the work needed for a brute force attack. With an iteration count of 2 you're actually doing a `SHA1(SHA1(message || salt))` which isn't very complex from a computational point of view. Also, [SHA-1's broken](http://www.schneier.com/blog/archives/2005/02/cryptanalysis_o.html). I don't know the circumstances, but at least use a much larger number of iterations. – Giuseppe Accaputo Oct 05 '10 at 13:54

3 Answers3

3

This shows how to implement PBKDF1 with OpenSSL, which according to the documentation is the algorithm used by PasswordDeriveBytes.

#include <string.h>
#include <stdlib.h>
#include <openssl/sha.h>

void pbkdf1(const char *password, const char *salt, long iter, unsigned char dk[SHA_DIGEST_LENGTH])
{
    size_t pwlen = strlen(password);
    size_t dlen = pwlen + 8;
    unsigned char *buf;

    if (dlen > SHA_DIGEST_LENGTH)
        buf = malloc(dlen);
    else
        buf = malloc(SHA_DIGEST_LENGTH);

    memcpy(buf, password, pwlen);
    strncpy((char *)buf + pwlen, salt, 8);

    while (iter-- > 0)
    {
        SHA1(buf, dlen, buf);
        dlen = SHA_DIGEST_LENGTH;
    }

    memcpy(dk, buf, SHA_DIGEST_LENGTH);
    free(buf);
}
caf
  • 233,326
  • 40
  • 323
  • 462
  • Thanks caf. But It is not matching with PasswordDeriveBytes. The Base64 encoding string generated by PasswordDeriveBytes is vYJcVqBV40q+9wT/X0/MAa2nr7Epvz1u4p6LdGYNwC4= . The method given by you generates string 809DD7DDGZ+3wzWqnqP9kAIVa5j/fwAAAAAEAAEAAAA= – Raviprakash Oct 06 '10 at 05:07
  • @Devara Gudda: PBKDF1 with SHA1 as the hash can't generate more than 20 bytes of output, but those base64 strings you've given are for 32 bytes of data, so *something* is up. You'll have to find out precisely how `PasswordDeriveBytes` differs from genuine PBKDF1. – caf Oct 06 '10 at 09:09
1

This is a c++ quick and dirty translation of the mono source code to perform GetBytes(X) where X can be greater than the size of the hash. As you can see I'v implemented only the SHA1 version...

#include <iostream>
#include <string.h>
#include <openssl/sha.h>

#define SHA1_BYTES_LEN 20

using namespace std;

namespace DeriveKeys
{
class PasswordDeriveBytes
{

private:
    unsigned char* password;
    int pass_len;
    unsigned char* salt;
    int salt_len;
    int IterationCount;
    int state;
    unsigned char* initial;
    unsigned char* output;
    unsigned int output_len;
    unsigned int position;
    int hashnumber;
public:


    PasswordDeriveBytes(unsigned char* password, unsigned char* salt, int iterations)
    {
    Prepare(password, salt, iterations);
    }


private:
    string convertInt(int number)
    {
    if (number == 0)
        return "0";
    string temp="";
    string returnvalue="";
    while (number>0)
    {
        temp+=number%10+48;
        number/=10;
    }
    for (unsigned int i=0; i<temp.length(); i++)
        returnvalue+=temp[temp.length()-i-1];
    return returnvalue;
    }

    void Prepare(unsigned char* password, unsigned char* salt, int iterations)
    {
    if (password == NULL)
        return;

    Prepare(password, strlen((const char*)password), salt, strlen((const char*)salt), iterations);
    }

    void Prepare(unsigned char* password, int pass_len, unsigned char* salt, int salt_len, int iterations)
    {
    if (password == NULL)
        return;

    this->password = new unsigned char[pass_len];
    memcpy(this->password,password,pass_len);
    //memcpy((char *)this->password, (const char*)password, pass_len);
    this->pass_len = pass_len;
    //(unsigned char*)password.Clone();

    this->salt = new unsigned char[salt_len];
    //strncpy((char *)this->salt, (const char*)salt, salt_len);
    memcpy(this->salt,salt,salt_len);
    this->salt_len = salt_len;

    this->IterationCount = iterations;
    state = 0;
    }

public:
    unsigned char* GetBytes(int cb)
    {
    if (cb < 1)
        return NULL;

    if (state == 0)
    {
        // it's now impossible to change the HashName, Salt
        // and IterationCount
        Reset();
        state = 1;
    }

    unsigned char* result = new unsigned char[cb];
    int cpos = 0;
    // the initial hash (in reset) + at least one iteration
    int iter = IterationCount-1;
    if (iter < 1)
    {
        iter = 1;
    }

    // start with the PKCS5 key
    if (this->output == NULL)
    {
        // calculate the PKCS5 key
        this->output = initial;
        this->output_len = SHA1_BYTES_LEN;

        // generate new key material
        for (int i = 0; i < iter - 1; i++)
        {
            SHA1((const unsigned char*)this->output,this->output_len,this->output);
            this->output_len = SHA1_BYTES_LEN;
        }
    }

    while (cpos < cb)
    {
        unsigned char* output2 = new unsigned char[SHA1_BYTES_LEN];
        unsigned int output2_len = SHA1_BYTES_LEN;
        if (hashnumber == 0)
        {
            SHA1((const unsigned char*)this->output,this->output_len,output2);
            output2_len = SHA1_BYTES_LEN;
        }
        else if (hashnumber < 1000)
        {
            string n = convertInt(hashnumber);
            output2 = new unsigned char[this->output_len + n.length()];
            output2_len = this->output_len + n.length();
            for (unsigned int j = 0; j < n.length(); j++)
                output2[j] = (unsigned char)(n[j]);

            memcpy(output2 + n.length(),this->output,this->output_len);
            SHA1((const unsigned char*)output2,output2_len,output2);
            output2_len = SHA1_BYTES_LEN;
        }
        else
        {
            return NULL;
        }

        int rem = this->output_len - this->position;
        int l = cb - cpos;
        if (l > rem)
        {
            l = rem;
        }
        memcpy(result + cpos, output2 + this->position, l);
        cpos += l;
        this->position += l;
        while (this->position >= output2_len)
        {
            this->position -= output2_len;
            this->hashnumber++;
        }
    }
    return result;
    }

    void Reset()
    {
    this->state = 0;
    this->position = 0;
    this->hashnumber = 0;
    this->initial = new unsigned char[SHA1_BYTES_LEN];
    this->output = NULL;
    this->output_len = 0;
    if (this->salt != NULL)
    {
        unsigned char* rv = new unsigned char[this->pass_len + this->salt_len];
        memcpy(rv,this->password, this->pass_len);
        memcpy(rv + this->pass_len, this->salt, this->salt_len);
        SHA1((const unsigned char*)rv,this->pass_len + this->salt_len, initial);

    }
    else
    {
        SHA1((const unsigned char*)this->password,this->pass_len,initial);
    }
    }
};
}
DaniRG
  • 11
  • 2
1

OpenSSL implements PBKDF2, which .NET exposes as Rfc2898DeriveBytes. PasswordDeriveBytes uses (according to the .NET 4 docs) "an extension of the PBKDF1 algorithm". PBKDF1 is not exposed by OpenSSL (and who knows what the 'extension' in question may be).

Using PBKDF2 (aka Rfc2898DeriveBytes) if possible will save you a lot of problems here.

Jack Lloyd
  • 8,215
  • 2
  • 37
  • 47