weidai11 / cryptopp

free C++ class library of cryptographic schemes
https://cryptopp.com
Other
4.92k stars 1.51k forks source link

ECDSA, Secp256k1 - LowS value & perform correct signature generation and validation. #1122

Closed Loopite closed 2 years ago

Loopite commented 2 years ago

Crypto++ Issue Report

Hello, please consider this configuration :

Hi, I've recently an error about signature validation.

I generate sig thought my function which is :

    /**
     * Get a DER signature format.
     * @param privateKey a constant QByteArray reference argument.
     * @param message a constant QByteArray reference argument.
     */
    QByteArray getDERSignature(const QByteArray& privateKey, const QByteArray& message)
    {
        AutoSeededRandomPool prng;

        /** Init the private key. */
        ECDSA<ECP, SHA256>::PrivateKey privateKeyMaker;

        QByteArray const privateKeyByte { privateKey.toHex() };

        HexDecoder decoder;
        decoder.Put((byte*)&privateKeyByte.constData()[0], privateKeyByte.size());
        decoder.MessageEnd();

        Integer x;
        x.Decode(decoder, decoder.MaxRetrievable());

        privateKeyMaker.Initialize(ASN1::secp256k1(), x);

        /** Init the signer. */
        ECDSA<ECP, SHA256>::Signer signer(privateKeyMaker);

        QByteArray signature;
        size_t siglenth { signer.SignatureLength() };

        signature.resize(siglenth);
        siglenth = signer.SignMessage(prng,
                     (const byte*) (message.constData()), message.length(),
                     (byte*) (&signature.constData()[0]));
        signature.resize(siglenth);

        QByteArray derSign;

        derSign.resize(3+3+3+2+signature.size());

        size_t converted_size = DSAConvertSignatureFormat(
            (byte*) (&derSign.constData()[0]), derSign.size(), DSA_DER,
            (const byte*) (signature.data()), signature.size(), DSA_P1363);

        derSign.resize(converted_size);

        return derSign;
    }

And my program crashs when I call the verify function :


    /**
     * Verify a DER signature format.
     * @param publicKey a constant QByteArray reference argument.
     * @param message a constant QByteArray reference argument.
     * @param derSig a constant QByteArray reference argument.
     */
    bool verifySignature(const QByteArray& publicKey, const QByteArray& message, const QByteArray& derSig)
    {
        /** Init the publicKey. */
        ECDSA<ECP, SHA256>::PublicKey publicKeyMaker;
        publicKeyMaker.AccessGroupParameters().Initialize(ASN1::secp256k1());

        StringSource ss(publicKey.toHex(), true, new CryptoPP::HexDecoder);
        ECP::Point point;

        publicKeyMaker.GetGroupParameters().GetCurve().DecodePoint(point, ss, ss.MaxRetrievable());

        /** Init verifier. */
        ECDSA<ECP, SHA256>::Verifier verifier(publicKeyMaker);

        /** Verify */
        SecByteBlock signature(verifier.SignatureLength());
        DSAConvertSignatureFormat(signature, signature.size(), DSA_P1363, (const byte*) derSig.data(), derSig.size(), DSA_DER);

        bool const okSafeMode { verifier.VerifyMessage((const byte*)&message.data()[0], message.size(), (const byte*)&signature[0], signature.size()) };

        return okSafeMode;
    }

I'm doing currently my tests in my main :

    QByteArray privTest { QByteArray::fromHex("E4A6CFB431471CFCAE491FD566D19C87082CF9FA7722D7FA24B2B3F5669DBEFB") };
    QByteArray publicKeyTest { CryptoSecp256k1::getCompressedPublicKey(privTest) };
    QByteArray testmsg { QCryptographicHash::hash("Do or do not. There is no try.", QCryptographicHash::Sha256) };

    logPrint("DER SIG RESULT = 0x" + CryptoSecp256k1::getDERSignature(privTest, testmsg).toHex());

    QByteArray derSigTest { QByteArray::fromHex("30450221009cfc808decd2fe0b568991724422561b97933cad7fed9e32a596cbd3a79c8be2022002d1eff5ea6b3035834f1fb1674e0554f13c3e4e6717485f52b19fc6fdaf0f55") };

    // CRASH
    logPrint("isValid = " + QString::number(CryptoSecp256k1::verifySignature(publicKeyTest, testmsg, derSigTest)));

I have this output :

2022-05-22 11:40:29 DER SIG RESULT = 0x30440220298fae13dd5d0eb42f70a850aad07536fb7daa8e9b65418a89ba970d9e1c1cb802201d1bd40f10cd0141ae0e353d2bbc787d0e91319d069f31f15260b6ce558e2d7b
11:40:30: test.exe exited with code -529697949 // CRASH

I watched the documentation here : https://www.cryptopp.com/wiki/Elliptic_Curve_Digital_Signature_Algorithm#Test_Program Moreover, I would like a low-s signature generation and verification. I don't know if the CRYPTO++ Secp256k1 implementation does it by default.

image

I hope you'll be able to help me. Best regards, Loopite

Loopite commented 2 years ago

Hi, Finally, I found something interesting :

QByteArray const pubKey { QByteArray::fromHex("044aeaf55040fa16de37303d13ca1dde85f4ca9baa36e2963a27a1c0c1165fe2b11511a626b232de4ed05b204bd9eccaf1b79f5752e14dd1e847aa2f4db6a52768") };

Integer r { "0xF01D6B9018AB421DD410404CB869072065522BF85734008F105CF385A023A80F" };
Integer s { "0xA3243A18521B20DC80A8798A1A36463FFE8279574127DA214D39E6B34134305B" };

/** Init the publicKey. */
ECDSA<ECP, SHA256>::PublicKey publicKeyMaker;
publicKeyMaker.AccessGroupParameters().Initialize(ASN1::secp256k1());

StringSource ss(pubKey.toHex(), true, new CryptoPP::HexDecoder);
ECP::Point point;

publicKeyMaker.GetGroupParameters().GetCurve().DecodePoint(point, ss, ss.MaxRetrievable());

/** Init verifier. */
ECDSA<ECP, SHA256>::Verifier verifier(publicKeyMaker);

        /** Verify */
        if (r >= publicKeyMaker.GetGroupParameters().GetSubgroupOrder())
               std::cout << "R is too large" << std::endl;
           else
               std::cout << "R is OK" << std::endl;

           if (s >= publicKeyMaker.GetGroupParameters().GetSubgroupOrder())
               std::cout << "S is too large" << std::endl;
           else
               std::cout << "S is OK" << std::endl;

           std::vector<uint8_t> ct = {0x31, 0x32, 0x33, 0x34, 0x30, 0x30};
           size_t siglen { verifier.SignatureLength() };

           std::vector<uint8_t> sig(siglen);

           r.Encode(sig.data() + 0, siglen / 2);
           s.Encode(sig.data() + (siglen / 2), siglen / 2);

           //std::cout << "Verify: " << verifier.VerifyMessage(ct.constdata(), ct.size(), sig.data(), siglen) << std::endl;

           logPrint("isValid = " + QString::number(verifier.VerifyMessage((const byte*) ct.data(), ct.size(), (byte*) sig.data(), sig.size())));

The program is still crashing, are you able to tell me why ? Thanks.

noloader commented 2 years ago

Please ask questions on the mailing list.

Loopite commented 2 years ago

It's not a question @noloader ... It's a bug. Signatures don't work as well as expected. So the verification produces the same bug... I can't verify a signature from R and S or to generate a R and a S with high/low option. Do you understand ? It's very problematic.