Question

How can I generate compressed ECDSA keys in Crypto++?

AutoSeededRandomPool prng;
ECDSA<ECP, SHA1>::PrivateKey privateKey;
ECDSA<ECP, SHA1>::PublicKey publicKey;
privateKey.Initialize( prng, CryptoPP::ASN1::secp256r1());

const Integer& x1 = privateKey.GetPrivateExponent();
cout << "priv:  " << std::hex << x1 << endl;
privateKey.MakePublicKey( publicKey );
const ECP::Point& q = publicKey.GetPublicElement();
const Integer& qx = q.x;
const Integer& qy = q.y;
cout << "pub x: " << std::hex << qx << endl;
cout << "pub y: " << std::hex << qy << endl;

This code generates a keypair and prints the X and Y components of the public key.

I need to know if there is a way to print the Y component of the compressed key, or if I need to generate it from the uncompressed y component. If I need to generate it, can someone link me to a good explanation of how to work with the Integer class?

Was it helpful?

Solution

How can I generate compressed ECDSA keys in CryptoPP?

You don't create a compressed key. You generate a public key, and then you compress it if that's what is required. From How to construct a public ECDSA key with point compression turned on? on the Crpyto++ user group:

ECDSA<ECP, SHA1>::Verifier verifier(...);
verifier.AccessKey().AccessGroupParameters().SetPointCompression(true); 

In your case, it will be:

publicKey.AccessGroupParameters().SetPointCompression(true);

since verifier.AccessKey() returns the ECDSA public key.


I need to know if there is a way to print the Y component of the compressed key

Compression is a presentation format option or optimization. You can't print the Y component on a serialized key with compression because its not there (see the ASN.1 dumps below).

With compression on, the Y "shorthand" is going to be 1 or -1, depending on the sign of the component (the sign indicates what quadrant the point is in). The idea is someone sends you a {1,X} pair or {-1,X} pair and you can solve for Y because you know which quadrant it should be in. If Y is only allowed to be positive, you only need to serialize {X} (and not {-1,X} or {1,X}). (I did not dive into the books or standards, so there may be some errata here).


... or if I need to generate it from the uncompressed y component. If I need to generate it, can someone link me to a good explanation of how to work with the Integer class?

I have no idea what you're talking about at here. Point compression affects presentation. If you turn on point compression and fetch Y, you will still get Y out. You can't compute on a compressed point. You need the X and Y coordinates.


Add the following to your program:

publicKey.AccessGroupParameters().SetPointCompression(false);
ByteQueue q1;
publicKey.Save(q1);
cout << "Uncompressed size: " << dec << q1.MaxRetrievable() << endl;

publicKey.AccessGroupParameters().SetPointCompression(true);
ByteQueue q2;
publicKey.Save(q2);
cout << "Compressed size: " << dec << q2.MaxRetrievable() << endl;

Here's what I got for the output:

$ ./cryptopp-test.exe
priv:  4ce30d22d9593d9c7f4406eda1ce0740c7486106374d0abe7e352e1d5b1d5622h
pub x: 41a9bc936b6d1dd3a1ded997d7da08f1df990e9b50f9b58e9e4fd9319758ea34h
pub y: 4ad39ffb79c402063a99ecbc0cac8fde606db6764ace90933feee5f8d65937a2h
Uncompressed size: 311
Compressed size: 246

If you fetch Y after turning on point compression, you will still get 4ad39ffb79c402063a99ecbc0cac8fde606db6764ace90933feee5f8d65937a2h because compression affects presentation.


Now, add the following to dump the uncompressed and compressed key:

FileSink fs1("key-1.der", true);
q1.TransferTo(fs1);

FileSink fs2("key-2.der", true);
q2.TransferTo(fs2);

The keys are dumped in ASN.1's DER encoding and conform to Certicom's SEC 1: Elliptic Curve Cryptography (and to a lesser extent, ANSI 9.62 and RFC 5480, ECC SubjectPublicKeyInfo Format - see below on the Field Element's OCTET STRING vs BIT STRING).

You can run Peter Gutmann's dumpasn1 on them:

$ dumpasn1.exe key-1.der 
  0 307: SEQUENCE {
  4 236:   SEQUENCE {
  7   7:     OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
 16 224:     SEQUENCE {
 19   1:       INTEGER 1
 22  44:       SEQUENCE {
 24   7:         OBJECT IDENTIFIER prime-field (1 2 840 10045 1 1)
 33  33:         INTEGER
       :           00 FF FF FF FF 00 00 00 01 00 00 00 00 00 00 00
       :           00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF
       :           FF
       :         }
 68  68:       SEQUENCE {
 70  32:         OCTET STRING
       :           FF FF FF FF 00 00 00 01 00 00 00 00 00 00 00 00
       :           00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FC
104  32:         OCTET STRING
       :           5A C6 35 D8 AA 3A 93 E7 B3 EB BD 55 76 98 86 BC
       :           65 1D 06 B0 CC 53 B0 F6 3B CE 3C 3E 27 D2 60 4B
       :         }
138  65:       OCTET STRING
       :         04 6B 17 D1 F2 E1 2C 42 47 F8 BC E6 E5 63 A4 40
       :         F2 77 03 7D 81 2D EB 33 A0 F4 A1 39 45 D8 98 C2
       :         96 4F E3 42 E2 FE 1A 7F 9B 8E E7 EB 4A 7C 0F 9E
       :         16 2B CE 33 57 6B 31 5E CE CB B6 40 68 37 BF 51
       :         F5
205  33:       INTEGER
       :         00 FF FF FF FF 00 00 00 00 FF FF FF FF FF FF FF
       :         FF BC E6 FA AD A7 17 9E 84 F3 B9 CA C2 FC 63 25
       :         51
240   1:       INTEGER 1
       :       }
       :     }
243  66:   BIT STRING
       :     04 41 A9 BC 93 6B 6D 1D D3 A1 DE D9 97 D7 DA 08
       :     F1 DF 99 0E 9B 50 F9 B5 8E 9E 4F D9 31 97 58 EA
       :     34 4A D3 9F FB 79 C4 02 06 3A 99 EC BC 0C AC 8F
       :     DE 60 6D B6 76 4A CE 90 93 3F EE E5 F8 D6 59 37
       :     A2
       :   }

$ dumpasn1.exe key-2.der 
  0 243: SEQUENCE {
  3 204:   SEQUENCE {
  6   7:     OBJECT IDENTIFIER ecPublicKey (1 2 840 10045 2 1)
 15 192:     SEQUENCE {
 18   1:       INTEGER 1
 21  44:       SEQUENCE {
 23   7:         OBJECT IDENTIFIER prime-field (1 2 840 10045 1 1)
 32  33:         INTEGER
       :           00 FF FF FF FF 00 00 00 01 00 00 00 00 00 00 00
       :           00 00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF
       :           FF
       :         }
 67  68:       SEQUENCE {
 69  32:         OCTET STRING
       :           FF FF FF FF 00 00 00 01 00 00 00 00 00 00 00 00
       :           00 00 00 00 FF FF FF FF FF FF FF FF FF FF FF FC
103  32:         OCTET STRING
       :           5A C6 35 D8 AA 3A 93 E7 B3 EB BD 55 76 98 86 BC
       :           65 1D 06 B0 CC 53 B0 F6 3B CE 3C 3E 27 D2 60 4B
       :         }
137  33:       OCTET STRING
       :         03 6B 17 D1 F2 E1 2C 42 47 F8 BC E6 E5 63 A4 40
       :         F2 77 03 7D 81 2D EB 33 A0 F4 A1 39 45 D8 98 C2
       :         96
172  33:       INTEGER
       :         00 FF FF FF FF 00 00 00 00 FF FF FF FF FF FF FF
       :         FF BC E6 FA AD A7 17 9E 84 F3 B9 CA C2 FC 63 25
       :         51
207   1:       INTEGER 1
       :       }
       :     }
210  34:   BIT STRING
       :     02 41 A9 BC 93 6B 6D 1D D3 A1 DE D9 97 D7 DA 08
       :     F1 DF 99 0E 9B 50 F9 B5 8E 9E 4F D9 31 97 58 EA
       :     34
       :   }

Notice the differences:

# key-1.der
243  66:   BIT STRING
       :     04 41 A9 BC 93 6B 6D 1D D3 A1 DE D9 97 D7 DA 08
       :     F1 DF 99 0E 9B 50 F9 B5 8E 9E 4F D9 31 97 58 EA
       :     34 4A D3 9F FB 79 C4 02 06 3A 99 EC BC 0C AC 8F
       :     DE 60 6D B6 76 4A CE 90 93 3F EE E5 F8 D6 59 37
       :     A2

versus:

# key-2.der
210  34:   BIT STRING
       :     02 41 A9 BC 93 6B 6D 1D D3 A1 DE D9 97 D7 DA 08
       :     F1 DF 99 0E 9B 50 F9 B5 8E 9E 4F D9 31 97 58 EA
       :     34
       :   }

The first has two Integers encoded, while the second has one Integer encoded. The common value is 41 A9 BC 93 ... 97 58 EA 34, and that's your pub x above. The missing string from key-2 is 4A D3 9F FB ... D6 59 37 A2, and that's the pub y above. For completeness, the values are encoded as either {X,Y} or {X}, and not ASN.1's encoding of integers.

The final difference is the first octet of the BIT STRING: 02 versus 04 (or 03). 04 indicates its an uncompressed point. From RFC 5480, Section 2.2:

    The first octet of the OCTET STRING indicates whether the key is
    compressed or uncompressed.  The uncompressed form is indicated
    by 0x04 and the compressed form is indicated by either 0x02 or
    0x03 (see 2.3.3 in [SEC1]).  The public key MUST be rejected if
    any other value is included in the first octet.

And after looking at the standard and section 2.2, Crypto++ may have a bug: its writing a BIT STRING, but the standard is clearly discussing a OCTET STRING.

EDIT 1: It appears this is a Crypto++ bug under ANSI 9.62, too. Section 6.2, Syntax for Finite Field Elements and Elliptic Curve Points (p. 20)

    A finite field element shall be represented by a value of type FieldElement:
      FieldElement ::= OCTET STRING
    The value of FieldElement shall be the octet string representation of a field
    elementfollowing the conversion routine in Section 4.3.1.
    An elliptic curve point shall be represented by a value of type ECPoint:
      ECPoint ::= OCTET STRING

EDIT 2: Crypto++ is using the format specified by Certicom's SEC 1: Elliptic Curve Cryptography. Appendix C (page 77) states:

    Finally, a specific field element is represented by the following type
      FieldElement ::= OCTET STRING
    whose value is the octet string obtained from the conversion routines
    given in Section 2.3.5.
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top