Search code examples
c++cryptographycrypto++der

Convert signature from P1363 to ASN.1/DER format using Crypto++?


I have a signature created this way:

size_t siglenth = _signer.MaxSignatureLength();
QByteArray signature(siglenth, 0x00);
signature.reserve(siglenth);
siglenth = _signer.SignMessage(_prng,
        (const CryptoPP::byte*) (message.constData()),
        message.length(), (CryptoPP::byte*) signature.data());

My signature have a size of 64 and contains:

ECCD530E5F232B7C566CA5322F990B3D55ED91156DF3845C4B9105BFE57606DDD68F332A0A5BF7CAB673E4970D10109B72F114571E7474F93ED7C89CD1B89AD4

From what I have read in dsa.h file this signature is currently in DSA_P1363 format. I need to convert it to DSA_DER format.

To perform this action I try :

QByteArray derSign(70, 0xFF);
size_t converted_size = CryptoPP::DSAConvertSignatureFormat(
        (CryptoPP::byte*) (derSign.data()), sizeof(derSign.data()), CryptoPP::DSA_DER,
        (CryptoPP::byte*) (signature.data()), sizeof(signature.data()), CryptoPP::DSA_P1363);

The output of this conversion is shown below. It seems to be only the first part of the signature. It has a size of 8 and contains:

300D020500ECCD53

What is wrong?

Thanks.


Solution

  • The output of this convertion is curious, it seems to be only the first part of the signature. It have a size of 8 and contains :

    300D020500ECCD53
    

    What is wrong ?

    Instead of:

    size_t converted_size = DSAConvertSignatureFormat(
        (byte*) (derSign.data()), sizeof(derSign.data()), DSA_DER,
        (byte*) (signature.data()), sizeof(signature.data()), DSA_P1363);
    

    You should use something like:

    size_t converted_size = DSAConvertSignatureFormat(
        (byte*) (derSign.data()), derSign.size(), DSA_DER,
        (const byte*) (signature.data()), signature.size(), DSA_P1363);
    

    sizeof(derSign.data()) yields the sizeof a size_t, which is different then the size of the string data.

    Also, since derSign is being written to, you need a non-const pointer. The way to get that with nearly all versions of C++ is using the address of the first element:

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

    Nearly finally, this is what you have in P1363, where r and s are a concatenation and each is based on the size of a field element and subgroup order:

    [ r ] [ s ]
    

    This is what you need in ASN.1/DER. There are 3 ASN.1 objects - one sequence and two integers. Each object needs one octet for the ASN.1 type, and at most two octets for the length. r and s are the size of the field element. Each ASN.1 integer may need a leading 0, so add two additional bytes for both r and s.

    SEQUENCE = {
        INTEGER r;
        INTEGER s;
    }
    

    So, for the ASN.1/DER buffer, you need 3+3+3+COUNTOF(r)+1+COUNTOF(s)+1.

    Finally, the snippet may look like:

    using namespace CryptoPP;
    // ... Other gyrations
    
    std::string derSign, signature;
    // ...Calculate signature
    
    // Make room for the ASN.1/DER encoding
    derSign.resize(3+3+3+2+signature.size())
    
    size_t converted_size = DSAConvertSignatureFormat(
        (byte*) (&derSign[0]), derSign.size(), DSA_DER,
        (const byte*) (signature.data()), signature.size(), DSA_P1363);
    
    ASSERT(converted_size <= derSign.size());
    derSign.resize(converted_size);
    

    Crypto++ now has a page on the wiki at DSAConvertSignatureFormat. There is an example of using DSAConvertSignatureFormat at ECDSA | OpenSSL and Java, but the conversion is going the other way.

    (Your question and lack of documentation effectively triggered a bug, and we closed the gap).


    I just noticed this...

    size_t siglenth = _signer.SignatureLength();
    QByteArray signature(siglenth, 0x00);
    signature.reserve(siglenth);
    siglenth = _signer.SignMessage(_prng, (const CryptoPP::byte*) (message.constData()),
                                   message.length(), (CryptoPP::byte*) signature.data());
    

    Instead use this:

    QByteArray signature;
    size_t siglenth = _signer.SignatureLength();
    
    signature.resize(siglenth);
    siglenth = _signer.SignMessage(_prng,
                 (const byte*) (message.constData()), message.length(),
                 (byte*) (&signature[0]));
    signature.resize(siglenth);