Search code examples
signingelliptic-curvecrypto++ecdsacompression

How can I recover compressed y value from sender?


I am working on following scenario:

  1. Generate private and public key
  2. Generate compressed public key with:

    PublicKey.AccessGroupParameters().SetPointCompression(true)

  3. Sign some data and send to other end with compressed public key.

  4. [At other end] verify signature using public key

For step 4, I need recover y value. Is there some API I can use among Crypto++?


Solution

  • For step 4, I need recover y value. Is there some API I can use among Crypto++?

    During verification, you will load the persisted or serialized key after setting point compression to true.


    Below is a little program to experiment with point compression. You can find it on the Crypto++ wiki under Point Compression.

    It generates a random key, then creates two public key - one with and and without compression. They two public keys are serialized. Then, it loads two new public keys with the serialized values.

    Key 1 (no compress) and Key 2 (compress) and saved, then Key 3 (no compress) and Key 4 (no compress) are loaded from saved values. The keys are the same, and the output is:

    $ ./cryptopp-test.exe
    Key 1 size (no compression): 214
      3081D33081A406072A8648CE3D0201308198020101302006072A8648CE3D0101021500FFFFFFFFFF
    FFFFFFFFFFFFFFFFFFFFFF7FFFFFFF302C0414FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC0414
    1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA450429044A96B5688EF573284664698968C38BB913CB
    FC8223A628553168947D59DCC912042351377AC5FB3202150100000000000000000001F4C8F927AED3
    CA752257020101032A0004CBFD13CEB20D677D9D3781AFA2E66B7BD5BC0E3C4EB8702144AA62BE5235
    DFC691567AA2A7101AB1
    
    Key 2 size (compression): 174
      3081AB30819006072A8648CE3D0201308184020101302006072A8648CE3D0101021500FFFFFFFFFF
    FFFFFFFFFFFFFFFFFFFFFF7FFFFFFF302C0414FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC0414
    1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA450415024A96B5688EF573284664698968C38BB913CB
    FC8202150100000000000000000001F4C8F927AED3CA75225702010103160003CBFD13CEB20D677D9D
    3781AFA2E66B7BD5BC0E3C
    
    Key 3 (after deserialization of Key 1):
      y3.x: cbfd13ceb20d677d9d3781afa2e66b7bd5bc0e3ch
      y3.y: 4eb8702144aa62be5235dfc691567aa2a7101ab1h
    Key 4 (after deserialization of Key 2):
      y4.x: cbfd13ceb20d677d9d3781afa2e66b7bd5bc0e3ch
      y4.y: 4eb8702144aa62be5235dfc691567aa2a7101ab1h
    

    Here's the program to create, copy, save, load, compress, uncompress and serialize the keys and points.

    #include <iostream>
    using std::cout;
    using std::endl;
    
    #include <string>
    using std::string;
    
    #include <cryptopp/osrng.h>
    using CryptoPP::AutoSeededRandomPool;
    
    #include <cryptopp/secblock.h>
    using CryptoPP::SecByteBlock;
    
    #include <cryptopp/filters.h>
    using CryptoPP::StringSource;
    using CryptoPP::StringSink;
    
    #include <cryptopp/hex.h>
    using CryptoPP::HexEncoder;
    
    #include <cryptopp/sha.h>
    using CryptoPP::SHA1;
    
    #include <cryptopp/integer.h>
    using CryptoPP::Integer;
    
    #include <cryptopp/eccrypto.h>
    using CryptoPP::ECP;
    using CryptoPP::ECDSA;
    
    #include <cryptopp/oids.h>
    using CryptoPP::ASN1::secp160r1;
    
    int main(int argc, char* argv[])
    {
        AutoSeededRandomPool prng;
    
        // Generate a private key, and two public keys.
        //   One with and one without compression
        ECDSA<ECP, SHA1>::PrivateKey privateKey;
        privateKey.Initialize(prng, secp160r1());
    
        ECDSA<ECP, SHA1>::PublicKey publicKey1;
        privateKey.MakePublicKey(publicKey1);
    
        ECDSA<ECP, SHA1>::PublicKey publicKey2;
        privateKey.MakePublicKey(publicKey2);
        publicKey2.AccessGroupParameters().SetPointCompression(true);
    
        // Save the public keys
        string p1, p2;
        publicKey1.Save(StringSink(p1).Ref());
        publicKey2.Save(StringSink(p2).Ref());
    
        // Print some stuff about them
        string s3, s4;
        StringSource ss3(p1, true, new HexEncoder(new StringSink(s3)));
        StringSource ss4(p2, true, new HexEncoder(new StringSink(s4)));
    
        cout << "Key 1 (not compressed): " << p1.size() << " bytes" << endl;
        cout << "  " << s3 << endl;
        cout << "Key 2 (compressed): " << p2.size() << " bytes" << endl;
        cout << "  " << s4 << endl;
        cout << endl;
    
        // Two new keys to load up the persisted keys
        ECDSA<ECP, SHA1>::PublicKey publicKey3, publicKey4;
        publicKey4.AccessGroupParameters().SetPointCompression(true);
    
        publicKey3.Load(StringSource(p1, true).Ref());
        publicKey4.Load(StringSource(p2, true).Ref());
    
        // And validate them
        publicKey3.Validate(prng, 3);
        publicKey4.Validate(prng, 3);
    
        // Get the public elements of the loaded keys
        const ECP::Point& y3 = publicKey3.GetPublicElement();
        const Integer& y3_x = y3.x;
        const Integer& y3_y = y3.y;
    
        const ECP::Point& y4 = publicKey4.GetPublicElement();
        const Integer& y4_x = y4.x;
        const Integer& y4_y = y4.y;
    
        // Print some stuff about them
        cout << "Key 3 (after deserialization of Key 1):" << endl;
        cout << "  y3.x: " << std::hex << y3_x << endl;
        cout << "  y3.y: " << std::hex << y3_y << endl;
        cout << "Key 4 (after deserialization of Key 2):" << endl;
        cout << "  y4.x: " << std::hex << y4_x << endl;
        cout << "  y4.y: " << std::hex << y4_y << endl;
        cout << endl;
    
        return 0;
    }
    

    You can even cross wires when loading the serialized keys and it just works. Below, the compressed key was loaded from a non-compressed serialization (and vice-versa):

    //////////////////////////////////////////////////////////////////////
    // Two new keys to load up the persisted keys, but crossing wires
    //   so so there's a compress/uncompressed mismatch
    ECDSA<ECP, SHA1>::PublicKey publicKey5, publicKey6;
    publicKey6.AccessGroupParameters().SetPointCompression(true);
    
    // This should be `p1`
    publicKey5.Load(StringSource(p2, true).Ref());
    // This should be `p2`
    publicKey6.Load(StringSource(p1, true).Ref());
    
    // Get the public elemnts of the loaded keys
    const ECP::Point& y5 = publicKey5.GetPublicElement();
    const Integer& y5_x = y5.x;
    const Integer& y5_y = y5.y;
    
    const ECP::Point& y6 = publicKey6.GetPublicElement();
    const Integer& y6_x = y6.x;
    const Integer& y6_y = y6.y;
    
    // Print some stuff about them
    cout << "Key 5 (after deserialization of Key 1):" << endl;
    cout << "  y5.x: " << std::hex << y5_x << endl;
    cout << "  y5.y: " << std::hex << y5_y << endl;
    cout << "Key 6 (after deserialization of Key 2):" << endl;
    cout << "  y6.x: " << std::hex << y6_x << endl;
    cout << "  y6.y: " << std::hex << y6_y << endl;
    cout << endl;
    

    If you want to get at the underlying domain parameters (like the base point), use:

    const DL_GroupParameters_EC< ECP >& params = publicKey.GetGroupParameters()