diff --git a/lib/bcoin/ec.js b/lib/bcoin/ec.js index d9ebbbca..8c2b1ccb 100644 --- a/lib/bcoin/ec.js +++ b/lib/bcoin/ec.js @@ -86,6 +86,135 @@ ec.publicKeyCreate = function publicKeyCreate(priv, compressed) { return new Buffer(priv); }; +/** + * Decode a point. + * @param {Buffer} key + * @returns {elliptic.Point} + */ + +ec.decodePoint = function decodePoint(key) { + var hybrid, point; + + if (key[0] === 0x06 || key[0] === 0x07) { + hybrid = key[0]; + key[0] = 0x04; + } + + point = ec.elliptic.curve.decodePoint(key); + + if (hybrid != null) + key[0] = hybrid; + + return point; +}; + +/** + * Compress or decompress public key. + * @param {Buffer} pub + * @returns {Buffer} + */ + +ec.publicKeyConvert = function(key, compressed) { + var point; + + if (secp256k1) + return secp256k1.publicKeyConvert(key, compressed); + + switch (key[0]) { + case 0x02: + case 0x03: + if (compressed) + return key; + point = ec.decodePoint(key); + return new Buffer(point.encode('array', false)); + case 0x04: + case 0x06: + case 0x07: + if (compressed) { + point = ec.decodePoint(key); + return new Buffer(point.encode('array', true)); + } + return key; + default: + throw new Error('Bad point format.'); + } +}; + +/** + * Compress a public key to coins compression format. + * @param {Buffer} key + * @returns {Buffer} + */ + +ec.compress = function compress(key) { + var out; + + // We can't compress it if it's not valid. + if (!ec.publicKeyVerify(key)) + return; + + switch (key[0]) { + case 0x02: + case 0x03: + // Key is already compressed. + out = key; + break; + case 0x04: + case 0x06: + case 0x07: + // Compress the key normally. + out = ec.publicKeyConvert(key, true); + // Store the original format (which + // may be a hybrid byte) in the hi + // 3 bits so we can restore it later. + // The hi bits being set also lets us + // know that this key was originally + // decompressed. + out[0] |= key[0] << 2; + break; + default: + throw new Error('Bad point format.'); + } + + assert(out.length === 33); + + return out; +}; + +/** + * Decompress a public key from the coins compression format. + * @param {Buffer} key + * @returns {Buffer} + */ + +ec.decompress = function decompress(key) { + var format = key[0] >>> 2; + var out; + + assert(key.length === 33); + + // Hi bits are not set. This key + // is not meant to be decompressed. + if (format === 0) + return key; + + // Decompress the key, and off the + // low bits so publicKeyConvert + // actually understands it. + key[0] &= 0x03; + out = ec.publicKeyConvert(key, false); + + // Reset the hi bits so as not to + // mutate the original buffer. + key[0] |= format << 2; + + // Set the original format, which + // may have been a hybrid prefix byte. + out[0] = format; + + return out; +}; + /** * Create an ecdh. * @param {Buffer} pub @@ -211,9 +340,26 @@ ec.verify = function verify(msg, sig, key, historical, high) { */ ec.publicKeyVerify = function publicKeyVerify(key) { + var result, hybrid; + if (secp256k1) return secp256k1.publicKeyVerify(key); - return ec.elliptic.keyPair({ pub: key }).validate(); + + if (key[0] === 0x06 || key[0] === 0x07) { + hybrid = key[0]; + key[0] = 0x04; + } + + try { + result = ec.elliptic.keyPair({ pub: key }).validate(); + } catch (e) { + result = false; + } + + if (hybrid != null) + key[0] = hybrid; + + return result; }; /**