I tried to implement the JavaScript code to recover y coordinate from a compressed P-256 X point at https://stackoverflow.com/a/30431547/1332416.
To my eyes it looks like the following code is equivalent in .NET/C#, but this does not seem to work as the result is not correct. So it stands to conclude there is something wrong with the code, but I seem to be unable to spot the problem. Can someone else perhaps see it?
/*
Curves and their primes
NIST P-256 (secp256r1) 2^256 - 2^224 2^192 2^96 - 1
NIST P-384 (secp384r1) 2^384 - 2^128 - 2^96 2^32 - 1
NIST P-521 (secp521r1) 2^521 - 1
const two = new bigInt(2),
// 115792089210356248762697446949407573530086143415290314195533631308867097853951
prime = two.pow(256).sub( two.pow(224) ).add( two.pow(192) ).add( two.pow(96) ).sub(1),
b = new bigInt( '41058363725152142129326129780047268409114441015993725554835256314039467401291' ),
// Pre-computed value, or literal
pIdent = prime.add(1).divide(4); // 28948022302589062190674361737351893382521535853822578548883407827216774463488
*/
var key = ECDsa.Create(ECCurve.NamedCurves.nistP256);
var keyParams = key.ExportParameters(includePrivateParameters: false);
var prime = BigInteger.Parse("115792089210356248762697446949407573530086143415290314195533631308867097853951");
var b = BigInteger.Parse("41058363725152142129326129780047268409114441015993725554835256314039467401291");
var pIdent = BigInteger.Parse("28948022302589062190674361737351893382521535853822578548883407827216774463488");
// Other combinations of isUnsighed and isBigEndian do not seem to work.
var xBig = new BigInteger(keyParams.Q.X, isUnsigned: false, isBigEndian: false);
var y = BigInteger.ModPow(BigInteger.Pow(xBig, 3) - (xBig * 3) b, pIdent, prime);
// Either yarr0 or yarr1 should match with yParams. This is not the case here now. Calculation going wrong?
var yParams = keyParams.Q.Y;
var yarr0 = y.ToByteArray();
var yarr1 = (prime - y).ToByteArray();
(Code to add and check 0x02
, 0x03
or 0x04
or is omitted on purpose as .NET by default does not do that and it's not need for this minimal case. This also lacks other checks to see if points are on the curve etc.)
CodePudding user response:
keyParams.Q.X
and keyParams.Q.Y
are to be interpreted as unsigned byte arrays in big endian byte order. Accordingly, the parameters are to be set when converting the byte array to a BigInteger
:
var xBig = new BigInteger(keyParams.Q.X, isUnsigned: true, isBigEndian: true);
Analogous for the reverse direction:
var yarr0 = y.ToByteArray(true, true);
var yarr1 = (prime - y).ToByteArray(true, true);
With these changes, the code returns the expected result, i.e. either yarr0
or yarr1
is equal to yParams
.