Crypto++: Generating a password based key with PBKDF2 and using it with ECDSA (converted from Python code)

Viewed 249

I'm trying to convert a Python code for password signing to C++ using Crypto++. I considered it a rather easy task because of the example code I have but I got stuck at loading the private key generated with PBKDF2 as private key for ECDSA. But as I'm unfamiliar with crypto algorithms it could be that I miss something.

Python example code (taken from the ErisX-REST-API-v3.3 (on page 9) you can find here: https://www.erisx.com/api-documentation/):

import​ hashlib
import​ ecdsa
from​ ecdsa.util ​ import​ sigencode_der_canonize
import​ base58

def​ ​ privateKeyFromPassword​ (authId, password):
    return​ hashlib.pbkdf2_hmac(
        hash_name=​ 'sha256'​ ,
        password=password.encode(),
        salt=authId.encode(),
        iterations=​ 100000​ ,
        dklen=​ 32​ )

def​ ​ signMessage​ (message, authId, password):
    privateKey = privateKeyFromPassword(authId, password)
    sk = ecdsa.SigningKey.from_string(privateKey, curve=ecdsa.SECP256k1)
    signature = sk.sign_deterministic(
        message.encode(),
        sigencode=sigencode_der_canonize,
        hashfunc=hashlib.sha256)
    return​ base58.b58encode(signature).decode(​ 'ascii'​ )

signature = signMessage(message, authId, password)

The resulting signature is: 381yXZ1tstiJroZCoFypQ19xPFus8nHqnWbJGg7oQfEA2VzocCG9ZP5erdXxM4YAkV4vcuyZBTH4g8NfYxiSHWvufPi7RGGX

And here is my C++ implementation:

#include <iostream>

#include <cryptlib.h>
#include <pwdbased.h>
#include <sha.h>
#include <hex.h>
#include <eccrypto.h>
#include <osrng.h>
#include <asn.h>
#include <oids.h>
#include <sstream>
#include <base64.h>

int main(int n, char **args){
    std::string message = "test message";
    std::string authId = "12345";
    int iterations= 100000;
    
    // https://cryptopp.com/wiki/PKCS5_PBKDF2_HMAC
    CryptoPP::byte password[] ="abcde";
    size_t plen = strlen((const char*)password);
    CryptoPP::byte salt[] = "12345"; // authID from Python code is the salt;
    size_t slen = strlen((const char*)salt);

    CryptoPP::byte key[32]; // dklen from the Python example

    PKCS5_PBKDF2_HMAC<SHA256> pbkdf;
    byte purpose = 0; // purpose is unused in PKCS5_PBKDF2_HMAC => 0.

    pbkdf.DeriveKey(key, sizeof(key), purpose, password, plen, salt, slen, iterations, 0.0f);

    std::string result;
    std::stringstream ss_result;
    ss_result << "" << key;
    std::cout << "(Derived) key with stringstream: " << key << std::endl;
        
    /// Define a custom encoder for Base58
    // Encoder
    CryptoPP::Base64Encoder encoder(new StringSink(result));
    const CryptoPP::byte ALPHABET[] = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
    AlgorithmParameters params = MakeParameters(Name::EncodingLookupArray(),(const byte *)ALPHABET);
    encoder.IsolatedInitialize(params);

    // Decoder
    //int lookup[256];
    //const CryptoPP::byte ALPHABET[] = "123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ";
    //Base64Decoder::InitializeDecodingLookupArray(lookup, ALPHABET, 58, false);

    //Base64Decoder decoder;
    //AlgorithmParameters params_dec = MakeParameters(Name::DecodingLookupArray(),(const int *)lookup);
    //decoder.IsolatedInitialize(params_dec);

    encoder.Put(key, sizeof(key));
    encoder.MessageEnd();

    std::cout << "(Derived) key with Base58-Encoding: " << result << std::endl;

    CryptoPP::AutoSeededRandomPool prng; //Create a random number
    CryptoPP::ECDSA<ECP, CryptoPP::SHA256>::PrivateKey privateKey;
    ECDSA<ECP,CryptoPP::SHA256>::Signer signer ( privateKey );
    signer.AccessKey().Initialize(prng, CryptoPP::ASN1::secp256k1());

    CryptoPP::StringSource ss2(result, true);
    signer.AccessKey().Load(ss2);
    
    std::string signature;
    StringSource ss1( message, true /*pump all*/,
            new SignerFilter( prng,    signer,
            new HexEncoder(new StringSink(signature))
        ) // SignerFilter
    );
    std::cout << "Signature: " << (signature) << std::endl;

}

The problematic line is signer.AccessKey().Load(ss2); as it throws the following exception when I run the compiled program: "terminate called after throwing an instance of 'CryptoPP::BERDecodeErr' what(): BER decode error". So my question is: What causes this problem? Is the private key from PBKDF2 incompatible for the usage with ECDSA? Also the Python implementation of ECDSA doesn't use a RNG for it's key generation. I tried CryptoPP::NullRNG() as parameter but that didn't work. Is there a way to do this or do I have to customize the function from the Crypto++-lib? The last part of the code uses a StringSource to create the signature. I'm aware that it still uses a HexEncoder instead of the Base58Encoder. Tried different versions of passing it as parameter. I would appreciate help here also.

Thank you very much for your help!

0 Answers
Related