I wrote a converter in javascript language, may will be useful for mobile apps or browser extensions.
It is using
javascript big integer library latest version 1.6.36, and work with hex string directly:
const bigInt = require("big-integer");
function pad_with_zeroes(number, length) {
var retval = '' + number;
while (retval.length < length) {
retval = '0' + retval;
}
return retval;
}
// Consts for secp256k1 curve. Adjust accordingly
// https://en.bitcoin.it/wiki/Secp256k1
const prime = new bigInt('fffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f', 16),
pIdent = prime.add(1).divide(4);
/**
* Point decompress secp256k1 curve
* @param {string} Compressed representation in hex string
* @return {string} Uncompressed representation in hex string
*/
function ECPointDecompress( comp ) {
var signY = new Number(comp[1]) - 2;
var x = new bigInt(comp.substring(2), 16);
// y mod p = +-(x^3 + 7)^((p+1)/4) mod p
var y = x.modPow(3, prime).add(7).mod(prime).modPow( pIdent, prime );
// If the parity doesn't match it's the *other* root
if( y.mod(2).toJSNumber() !== signY ) {
// y = prime - y
y = prime.subtract( y );
}
return '04' + pad_with_zeroes(x.toString(16), 64) + pad_with_zeroes(y.toString(16), 64);
}
Test with private key from Tim's example 55255657523dd1c65a77d3cb53fcd050bf7fc2c11bb0bb6edabdbd41ea51f641:
ECPointDecompress('0314fc03b8df87cd7b872996810db8458d61da8448e531569c8517b469a119d267')
returns:
'0414fc03b8df87cd7b872996810db8458d61da8448e531569c8517b469a119d267be5645686309c6e6736dbd93940707cc9143d3cf29f1b877ff340e2cb2d259cf'
refer to
https://stackoverflow.com/a/53480175/5630352From
https://en.wikipedia.org/wiki/Quadratic_residue and
http://mersennewiki.org/index.php/Modular_Square_Root, if
r^2 = a mod m where m = 3 mod 4 (as
secp256k1's p does)
then
r = +-a^((m+1)/4) mod m
So:
y^2 mod p = (x^3 + 7) mod p
y mod p = +-(x^3 + 7)^((p+1)/4) mod p
So calculate (x^3 + 7)^((p+1)/4) mod p, and if the parity of the first answer you get is wrong, then take the negative of that answer (since we're working modulo an odd number, taking the negative will flip the even/odd parity).
Just for fun, here's some Python code that'll do the calculation in the blink of an eye, preset to work on the public key of (the random) private key 55255657523dd1c65a77d3cb53fcd050bf7fc2c11bb0bb6edabdbd41ea51f641
def pow_mod(x, y, z):
"Calculate (x ** y) % z efficiently."
number = 1
while y:
if y & 1:
number = number * x % z
y >>= 1
x = x * x % z
return number
p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f
compressed_key = '0314fc03b8df87cd7b872996810db8458d61da8448e531569c8517b469a119d267'
y_parity = int(compressed_key[:2]) - 2
x = int(compressed_key[2:], 16)
a = (pow_mod(x, 3, p) + 7) % p
y = pow_mod(a, (p+1)//4, p)
if y % 2 != y_parity:
y = -y % p
uncompressed_key = '04{:x}{:x}'.format(x, y)
print(uncompressed_key)