使用ECDSA或DSA对预先计算的哈希值进行签名

3
我正在尝试使用Crypto++签名器,并使用来自维基的以下代码:
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;

现在,我想知道如何覆盖SHA256以直接指定要传递给签名算法的摘要值。
我已经查看了wikidoxygen documentation,但没有成功。起初,我以为NullHash可以帮助解决问题,但根据源代码,它只是零哈希。我也对PK_MessageAccumulator抱有一些希望,但它似乎并没有按照我的期望工作。
那么,是否有一种继承自HashTransformation类的“身份”函数完全错过了?如果没有,您将如何构建允许直接指定要签名的摘要的东西?

说实话,这是库中非常棘手的领域。我帮忙加入了RFC 6979确定性签名。由于组件之间(如消息累加器、格式化程序和签名函数)的交互非常多,所以我很难保持事情的整洁和有序。对我来说相当痛苦(而我在Crypto++方面有很高的门槛)。 - jww
@jww,实际上我想做的是“简单地”签名/验证预计算哈希。我不知道你是否了解EdDSA及其变体PureEdDSA。你可以将EdDSA描述为将PureEdDSA应用于散列消息H(M)的简单应用程序。同样地,我正在尝试使用“纯”ECDSA对我收到的预计算哈希进行签名。因此,我在想,也许一个什么都不做并输出消息的HashTransformation:H(M)=M可能会起作用。是否可能将这样的自定义HashTransformation提供给ECDSA<ECP,H>::Signer? - Lery
1个回答

3

H(M)=M 可能有效。是否可以将这样的自定义HashTransformation提供给ECDSA<ECP,H>::Signer

是的。以下是程序。它提供了一个IdentityHash类,该类将输入复制到输出。它需要一个模板参数来指定哈希大小。

但要小心。消息在哈希后被格式化。实际上我们有的是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;
}

我知道它可以编译并产生输出,但我不确定它是否是正确的输出:

skylake:cryptopp$ g++ test.cxx ./libcryptopp.a -o test.exe
skylake:cryptopp$ ./test.exe
Signature: cafb8fca487c7d5023fbc76ccf96f107f72a07fecca77254e8845a2c8f2ed0ee8b50b
8ee0702beb7572eaa30c8d250a7b082c79f2f02e58ccfb97d7091755e91

您可以使用以下方法测试IdentityHash。类IdentityHash与先前示例中未发生变化,只有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;
}

它会产生:
skylake:cryptopp$ g++ test.cxx ./libcryptopp.a -o test.exe
skylake:cryptopp$ ./test.exe
Message: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
 Digest: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

谢谢,我会在明天第一时间尝试并告诉你它的效果。我对你提到的MF函数有点困惑,你是指我们按照Sec1v2的4.1.3节将哈希转换为整数的方式吗? - Lery
太棒了,它完美地运作,并且可以与OpenSSL、mbedTLS和Go进行互操作!你是以另一个HashTransformation为例吗?我试图从SHA3开始这样做,但无法理解所有基于模板的编程。与我查看的Crypto++代码中的其他哈希相比,你的版本非常简洁。 - Lery
1
@Lery - 目前页面已经完成:添加哈希。我需要继续进行Crypto++ 6.0版本的其他任务。我可能会在未来重新访问维基页面。IdentityHash被清理了一下。你可能想要获取最新版本。例如,它可以正确处理重启。 - jww

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接