bc-csharp icon indicating copy to clipboard operation
bc-csharp copied to clipboard

EC Public Key Calculate Y Coordinate from X

Open jspmu opened this issue 2 years ago • 5 comments

I am currently in need of verifying a block of data signed with a private EC key (P-256) and only have access to the X coordinate of the Public Key.

I have found that the Y coordinate of the Public Key can be calculated by using the following formula:

y^2 = b + x ( a + x^2)

Which is mostly achievable, however there is no BigInteger Square Root function that I can find.

Could you please point me towards a Square Root function or possibly where I may be going wrong / something I have missed?

jspmu avatar Sep 06 '22 15:09 jspmu

An EC private key doesn't have an X coordinate, rather it is just a single scalar (it's the public key that is a point with X,Y coordinates). You could construct a private key object as follows:

Org.BouncyCastle.Math.BigInteger d = ...; // this would be the value you've been given
Org.BouncyCastle.Crypto.Parameters.ECDomainParameters dp = Org.BouncyCastle.Asn1.X9.ECNamedCurveTable.GetByName("P-256");
var privateKey = new Org.BouncyCastle.Crypto.Parameters.ECPrivateKeyParameters(d, dp);

and that could then be e.g. used with an ISigner of some kind to verify a signature. You'd need to provide more details about the signature to know exactly how.

peterdettman avatar Sep 06 '22 18:09 peterdettman

Apologies, i wasn't clear in my original message.

I have the X coordinate of the Public Key, but no Y coordinate. I want to calculate the Y coordinate of the Public Key so that I can verify the block signed by the private key.

Below is my code:

            ECDomainParameters domainParameters = new ECDomainParameters(ecParams.Curve, ecParams.G, ecParams.N, ecParams.H, ecParams.GetSeed());
            var G = ecParams.G;

            ECCurve curve = ecParams.Curve;

            var x = new BigInteger(1, publicKeyXCoord);
            ////calculate the y coordinate from the X
            var a = curve.A.ToBigInteger();
            var b = curve.B.ToBigInteger();
            var y2 = b.Add(x.Multiply(a.Add(x.Square())));

            var y = y2; // Need to get the square root of y2

            ECPoint q = curve.CreatePoint(x, y);

            var pubKeyParam = new ECPublicKeyParameters(q, domainParameters);

            var verifier = SignerUtilities.GetSigner("SHA-256withECDSA");
            verifier.Init(false, pubKeyParam);
            verifier.BlockUpdate(dataToVerify, 0, dataToVerify.Length);
            var result = verifier.VerifySignature(signature);

jspmu avatar Sep 07 '22 07:09 jspmu

Are you sure you have "the X coordinate" and not a compressed public key? The X coordinate by itself would be a 32 byte value (and by itself it would not tell you which of two possible public keys it corresponds to - i.e. which square root), while a compressed public key would be 33 bytes beginning with a 0x02 or 0x03 byte.

If it's the 33 byte compressed key, then you just use curve.DecodePoint and it will handle the details.

peterdettman avatar Sep 08 '22 04:09 peterdettman

I am certain I have the X coordinate, it is of 32 bytes exactly and has been extracted from the public key.

In this case I have the full public key for reference, but for other cases I will not have the public key available (except the X coordinate)

jspmu avatar Sep 08 '22 08:09 jspmu

Well the X coordinate alone is ambiguous, since there are two possible public keys with any given X coordinate. Either there is some unstated assumption about which key is meant, or something else is missing in your description.

ECCurve.DecodePoint is the way to do what you are trying to do, but to do that you need to either be given the 33-byte compressed key, or I guess there could be a rule that says you always prepend 0x02 (or 0x03) to the X coordinate to create the compressed key.

Note that calculating the square root yourself doesn't escape the issue since there are two roots to choose from.

peterdettman avatar Sep 13 '22 09:09 peterdettman