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!