Write out precomputed hash with ECDSA or DSA
I am playing with Crypto ++ subscribers and using the following code straight from 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 IEEE P1363 format to display r and s:
cout << signature.substr(0, slen) << "\n"
<< signature.substr(slen, slen) << endl;
Now, I would like to know how I can override SHA256 there to directly specify the digest value that I want to pass to the signature algorithm.
I dug up the wiki and doxigenic documentation but with no success. At first I thought that maybe NullHash could help there, but it really is only a zero hash per source. I also had hope with PK_MessageAccumulator , but it doesn't 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 about it by building something like this to indicate that the digest should be signed directly?
source to share
H(M)=M
can work. Would it be possible to submit such a customHashTransformation
oneECDSA<ECP,H>::Signer
?
Yes. The program is shown below. It provides a class IdentityHash
that copies the input for output. A template parameter is required to specify the size of the hash.
But be careful. The message is formatted after it has been hashed. In fact, we have 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 don't know if this is the correct way out:
skylake:cryptopp$ g++ test.cxx ./libcryptopp.a -o test.exe skylake:cryptopp$ ./test.exe Signature: cafb8fca487c7d5023fbc76ccf96f107f72a07fecca77254e8845a2c8f2ed0ee8b50b 8ee0702beb7572eaa30c8d250a7b082c79f2f02e58ccfb97d7091755e91
You can test IdentityHash
as follows. The class has IdentityHash
not changed from the previous example. Function completed main
.
$ 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
source to share