Search code examples
c++hexcrypto++ecdsa

Creation of ECDSA public key given curve and public point?


I am struggling with the creation of a ECDSA public key from a string representation of a public key i.e

string     devicePublicKey("86FB5EB3CA0507226BE7197058B9EC041D3A3758D9D9C91902ACA3391F4E58AEF13AFF63CC4EF68942B9B94904DC1B890EDBEABD16B992110624968E894E560E");

previously I found that I had to prefix this key with '04' so not sure if this is require this time?

I am trying to generate it to use in verifying a signature

string ecs04b2ExpSignature("0199E984CEC75DDCA7F1DDF6E53E2E67352A2BE38A4B66F8ED596606FAB983FF300CAA76DE88CED9D563A5C03E8F3A7C000780F3F2061C611E9AA0B18B460D77");

where the data to be signed is

string      ecs04b2SigningData("020000000000000001FFFFFFFFFFFFFFFE123456789ABCDEF000B3DA2000000100000300000003030003000300");

My rough code for now looks like this

SecByteBlock message(convertHexStrToSecByteBlock(messageIn));
SecByteBlock signature(convertHexStrToSecByteBlock(signatureIn));

ECDSA<ECP, SHA256>::PublicKey publicKey;
string inPublicKey("04");
inPublicKey.append(pubKeyIn);
SecByteBlock pubKey = encryptBase::convertHexStrToSecByteBlock(inPublicKey);



ECP::Point p;
publicKey.AccessGroupParameters().Initialize(CryptoPP::ASN1::secp256r1());
publicKey.GetGroupParameters().GetCurve().DecodePoint(p, pubKey, publicKey.GetGroupParameters().GetCurve().EncodedPointSize(true));
publicKey.SetPublicElement(p);

//ByteQueue qt;
//qt.Put((byte*)exp.c_str(),(size_t)exp.size());
AutoSeededRandomPool prng;
bool result = publicKey.Validate(prng, 3);
if (result) 
{  
    // Load public key (in ByteQueue, X509 format)
    ECDSA<ECP, SHA256>::Verifier verifier(publicKey);

    bool result = verifier.VerifyMessage(message.data(), messageIn.size(), signature.data(), signature.size());
    if (result)
        cout << "Verified signature on message" << endl;
    else
        cerr << "Failed to verify signature on message" << endl;
}
else
{
    cout << "Failed to validate key" << endl;
}

this is chopped together so wont build. Any help would be great

PS I asked a similar question relating to private keys here Creation of ECDSA private key given curve and private exponent?


Solution

  • The answer is detailed on the ECDSA wiki page, but its not readily apparent. You need to perform the following to initialize the publicKey given the curve and public point:

    string pt = "2DB45A3F21889438B42C8F464C75292BACF5FDDB5DA0B492501B299CBFE92D8F"
                "DB90FC8FF4026129838B1BCAD1402CAE47FE7D8084E409A41AFCE16D63579C5F";
    
    HexDecoder decoder;
    decoder.Put((byte*)pt.data(), pt.size());
    decoder.MessageEnd();
    
    ECP::Point q;
    size_t len = decoder.MaxRetrievable();
    // len should be GetField().MaxElementByteLength()
    
    q.identity = false;
    q.x.Decode(decoder, len/2);
    q.y.Decode(decoder, len/2);
    
    ECDSA<ECP, SHA256>::PublicKey publicKey;
    publicKey.Initialize(ASN1::secp256r1(), q);
    
    bool result = publicKey.Validate( prng, 3 );
    if( result )
    {
        cout << "Validated public key" << endl;
    }
    else
    {
        cerr << "Failed to validate public key" << endl;
        exit(1);
    }
    
    const ECP::Point& qq = publicKey.GetPublicElement();
    cout << "Q.x: " << std::hex << qq.x << endl;
    cout << "Q.y: " << std::hex << qq.y << endl;
    

    The program above produces the following results.

    $ ./cryptopp-test.exe
    Validated public key
    Q.x: 2db45a3f21889438b42c8f464c75292bacf5fddb5da0b492501b299cbfe92d8fh
    Q.y: db90fc8ff4026129838b1bcad1402cae47fe7d8084e409a41afce16d63579c5fh
    

    You can't use GetField().MaxElementByteLength() because the only thing available are the x and y coordinates. Things like field size won't be available until you initialize the underlying DL_GroupParameters_EC< EC > in the public key.

    As an example, the following causes a segmentation fault:

    ECDSA<ECP, SHA256>::PublicKey publicKey;
    unsigned int u = publicKey.GetGroupParameters().GetCurve().GetField().MaxElementByteLength();
    cout << "Field element length: " << u << endl;
    

    You can tamper with the public key to ensure a validation failure with:

    q.y.Decode(decoder, len/2);
    q.y++;