diff --git a/src/ec.js b/src/ec.js index 24d488c..22705e9 100644 --- a/src/ec.js +++ b/src/ec.js @@ -2,6 +2,7 @@ // Ported loosely from BouncyCastle's Java EC code // Only Fp curves implemented for now +var assert = require('assert') var BigInteger = require('bigi') function ECFieldElementFp(q,x) { @@ -326,34 +327,43 @@ ECPointFp.prototype.getEncoded = function(compressed) { return buffer } -ECPointFp.decodeFrom = function (curve, enc) { - var type = enc[0]; - var dataLen = enc.length-1; +var SEVEN = BigInteger.valueOf(7) - // Extract x and y as byte arrays - if (type == 4) { - var xBa = enc.slice(1, 1 + dataLen/2), - yBa = enc.slice(1 + dataLen/2, 1 + dataLen), - x = BigInteger.fromBuffer(xBa), - y = BigInteger.fromBuffer(yBa); - } - else { - var xBa = enc.slice(1), - x = BigInteger.fromBuffer(xBa), - p = curve.getQ(), - xCubedPlus7 = x.multiply(x).multiply(x).add(new BigInteger('7')).mod(p), - pPlus1Over4 = p.add(new BigInteger('1')) - .divide(new BigInteger('4')), - y = xCubedPlus7.modPow(pPlus1Over4,p); - if (y.mod(new BigInteger('2')).toString() != ''+(type % 2)) { - y = p.subtract(y) - } +ECPointFp.decodeFrom = function (curve, buffer) { + var type = buffer.readUInt8(0) + var compressed = type !== 0x04 + var x = BigInteger.fromBuffer(buffer.slice(1, 33)) + var y + + if (compressed) { + assert.equal(buffer.length, 33, 'Invalid sequence length') + assert(type === 0x02 || type === 0x03, 'Invalid sequence tag') + + var isYEven = type === 0x03 + var p = curve.getQ() + + // We precalculate (p + 1) / 4 where p is the field order + var P_OVER_FOUR = p.add(BigInteger.ONE).shiftRight(2) + + // Convert x to point + var alpha = x.square().multiply(x).add(SEVEN).mod(p) + var beta = alpha.modPow(P_OVER_FOUR, p) + + y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta) + + } else { + assert.equal(buffer.length, 65, 'Invalid sequence length') + + y = BigInteger.fromBuffer(buffer.slice(33)) } - return new ECPointFp(curve, - curve.fromBigInteger(x), - curve.fromBigInteger(y)); -}; + var Q = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y)) + + return { + Q: Q, + compressed: compressed + } +} ECPointFp.prototype.add2D = function (b) { if(this.isInfinity()) return b; diff --git a/src/ecpubkey.js b/src/ecpubkey.js index 0c7dafb..bd2e7e8 100644 --- a/src/ecpubkey.js +++ b/src/ecpubkey.js @@ -21,14 +21,8 @@ function ECPubKey(Q, compressed) { // Static constructors ECPubKey.fromBuffer = function(buffer) { - var type = buffer.readUInt8(0) - assert(type >= 0x02 || type <= 0x04, 'Invalid public key') - - var compressed = (type !== 0x04) - assert.strictEqual(buffer.length, compressed ? 33 : 65, 'Invalid public key') - - var Q = ECPointFp.decodeFrom(ecparams.getCurve(), buffer) - return new ECPubKey(Q, compressed) + var decode = ECPointFp.decodeFrom(ecparams.getCurve(), buffer) + return new ECPubKey(decode.Q, decode.compressed) } ECPubKey.fromHex = function(hex) { diff --git a/test/ec.js b/test/ec.js index a547ea1..9cd74c8 100644 --- a/test/ec.js +++ b/test/ec.js @@ -48,25 +48,22 @@ describe('ec', function() { var buffer = new Buffer(f.hex, 'hex') var decoded = ECPointFp.decodeFrom(curve, buffer) - assert.equal(decoded.getX().toBigInteger().toString(), f.x) - assert.equal(decoded.getY().toBigInteger().toString(), f.y) - - // TODO -// assert.equal(decoded.compressed, f.compressed) + assert.equal(decoded.Q.getX().toBigInteger().toString(), f.x) + assert.equal(decoded.Q.getY().toBigInteger().toString(), f.y) + assert.equal(decoded.compressed, f.compressed) }) }) - // FIXME - // fixtures.invalid.ECPointFp.forEach(function(f) { - // it('throws on ' + f.description, function() { - // var curve = ecparams.getCurve() - // var buffer = new Buffer(f.hex, 'hex') - // - // assert.throws(function() { - // ECPointFp.decodeFrom(curve, buffer) - // }) - // }) - // }) + fixtures.invalid.ECPointFp.forEach(function(f) { + it('throws on ' + f.description, function() { + var curve = ecparams.getCurve() + var buffer = new Buffer(f.hex, 'hex') + + assert.throws(function() { + ECPointFp.decodeFrom(curve, buffer) + }) + }) + }) }) }) })