I'm playing around with Crypto++ signers and use the following code, straight out of the wiki:
ECDSA<ECP, SHA256>::PrivateKey privateKey;
const Integer D(string("8964e19c5ae38669db3047f6b460863f5dc6c4510d3427e33545caf9527aafcf").c_str());
privateKey.Initialize(CryptoPP::ASN1::secp256r1(), D);
if (!privateKey.Validate(rng, 3)) {
cerr << "ECDSA privateKey key validation failed after setting private parameter." << endl;
return 1;
}
ECDSA<ECP,SHA256>::Signer signer(privateKey);
StringSource ss1(message, true,
new SignerFilter(rng, signer,
new HexEncoder(new StringSink(signature), false)
) // SignerFilter
); // StringSource
int slen = signature.length() / 2;
// since it's IEEE P1363 format to display r and s:
cout << signature.substr(0, slen) << "\n"
<< signature.substr(slen, slen) << endl;
Now, I'd like to know how I could override the SHA256 there to specify directly the digest value I want to pass to the signature algorithm.
I've digged into the wiki and the doxygen documentation, but had no success doing so. At first I thought maybe the NullHash could help there, but it is really only the zero hash according to the source. I also had some hope with the PK_MessageAccumulator but it does not appear to work as I expected.
So, is there some sort of "identity" function inheriting from the HashTransformation class that I completely missed?
If not, how would you go around building something like that allowing to specify the digest to be signed directly?
H(M)=M
might work. Would it be possible to feed such a customHashTransformation
to theECDSA<ECP,H>::Signer
?
Yes. The program is below. It provides an IdentityHash
class that copies input to output. It needs a template parameter to specify the hash size.
But be careful. The message is formatted after it is hashed. Really what we have is to_sign = MF(H(M))
.
$ cat test.cxx
#include "cryptlib.h"
#include "secblock.h"
#include "eccrypto.h"
#include "osrng.h"
#include "oids.h"
#include "hex.h"
#include <iostream>
#include <string>
using namespace CryptoPP;
template <unsigned int HASH_SIZE = 32>
class IdentityHash : public HashTransformation
{
public:
CRYPTOPP_CONSTANT(DIGESTSIZE = HASH_SIZE)
static const char * StaticAlgorithmName()
{
return "IdentityHash";
}
IdentityHash() : m_digest(HASH_SIZE), m_idx(0) {}
virtual unsigned int DigestSize() const
{
return DIGESTSIZE;
}
virtual void Update(const byte *input, size_t length)
{
size_t s = STDMIN(STDMIN<size_t>(DIGESTSIZE, length),
DIGESTSIZE - m_idx);
if (s)
::memcpy(&m_digest[m_idx], input, s);
m_idx += s;
}
virtual void TruncatedFinal(byte *digest, size_t digestSize)
{
if (m_idx != DIGESTSIZE)
throw Exception(Exception::OTHER_ERROR, "Input size must be " + IntToString(DIGESTSIZE));
ThrowIfInvalidTruncatedSize(digestSize);
if (digest)
::memcpy(digest, m_digest, digestSize);
m_idx = 0;
}
private:
SecByteBlock m_digest;
size_t m_idx;
};
int main(int argc, char* argv[])
{
AutoSeededRandomPool prng;
ECDSA<ECP, IdentityHash<32> >::PrivateKey privateKey;
privateKey.Initialize(prng, ASN1::secp256r1());
std::string message;
message.resize(IdentityHash<32>::DIGESTSIZE);
::memset(&message[0], 0xAA, message.size());
ECDSA<ECP, IdentityHash<32> >::Signer signer(privateKey);
std::string signature;
StringSource ss(message, true,
new SignerFilter(prng, signer,
new HexEncoder(new StringSink(signature))
) // SignerFilter
); // StringSource
std::cout << "Signature: " << signature << std::endl;
return 0;
}
I know it compiles and produces output. I have no idea if it is the correct output:
skylake:cryptopp$ g++ test.cxx ./libcryptopp.a -o test.exe
skylake:cryptopp$ ./test.exe
Signature: cafb8fca487c7d5023fbc76ccf96f107f72a07fecca77254e8845a2c8f2ed0ee8b50b
8ee0702beb7572eaa30c8d250a7b082c79f2f02e58ccfb97d7091755e91
You can test IdentityHash
with the following. The class IdentityHash
did not change from the previous example. The main
function did.
$ cat test.cxx
#include "cryptlib.h"
#include "secblock.h"
#include <iostream>
#include <string>
using namespace CryptoPP;
template <unsigned int HASH_SIZE = 32>
class IdentityHash : public HashTransformation
{
public:
CRYPTOPP_CONSTANT(DIGESTSIZE = HASH_SIZE)
static const char * StaticAlgorithmName()
{
return "IdentityHash";
}
IdentityHash() : m_digest(HASH_SIZE), m_idx(0) {}
virtual unsigned int DigestSize() const
{
return DIGESTSIZE;
}
virtual void Update(const byte *input, size_t length)
{
size_t s = STDMIN(STDMIN<size_t>(DIGESTSIZE, length),
DIGESTSIZE - m_idx);
if (s)
::memcpy(&m_digest[m_idx], input, s);
m_idx += s;
}
virtual void TruncatedFinal(byte *digest, size_t digestSize)
{
if (m_idx != DIGESTSIZE)
throw Exception(Exception::OTHER_ERROR, "Input size must be " + IntToString(DIGESTSIZE));
ThrowIfInvalidTruncatedSize(digestSize);
if (digest)
::memcpy(digest, m_digest, digestSize);
m_idx = 0;
}
private:
SecByteBlock m_digest;
size_t m_idx;
};
int main(int argc, char* argv[])
{
std::string message;
message.resize(IdentityHash<32>::DIGESTSIZE);
::memset(&message[0], 'A', message.size());
IdentityHash<32> hash;
hash.Update((const byte*)message.data(), message.size());
std::string digest(32, 0);
hash.TruncatedFinal((byte*)digest.data(), digest.size());
std::cout << "Message: " << message << std::endl;
std::cout << " Digest: " << digest << std::endl;
return 0;
}
It produces:
skylake:cryptopp$ g++ test.cxx ./libcryptopp.a -o test.exe
skylake:cryptopp$ ./test.exe
Message: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Digest: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA