From 99a1b7274cd884c301b0bb3c96ba6ee226b36727 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Sat, 17 May 2014 00:22:56 +1000
Subject: [PATCH 01/10] Transaction: use the type embedded in the signature

---
 src/transaction.js | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/transaction.js b/src/transaction.js
index 2b50940..bfbbe0e 100644
--- a/src/transaction.js
+++ b/src/transaction.js
@@ -380,10 +380,11 @@ Transaction.prototype.setScriptSig = function(index, script) {
   this.ins[index].script = script
 }
 
-Transaction.prototype.validateSig = function(index, script, pub, sig, type) {
-  type = type || SIGHASH_ALL
+Transaction.prototype.validateSig = function(index, script, pub, sig) {
+  var type = sig[sig.length - 1]
   var hash = this.hashForSignature(script, index, type)
 
+  sig = sig.slice(0, -1)
   return pub.verify(hash, sig)
 }
 

From b208a6ab782b1da7a8f383b37c54fca024a58a1e Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Fri, 16 May 2014 12:36:09 +1000
Subject: [PATCH 02/10] crypto/ecdsa: moves HmacSHA256 to crypto

---
 src/crypto.js           |  5 +++++
 src/ecdsa.js            | 16 +++++--------
 test/crypto.js          | 50 ++++++++++++++++++++++++++++-------------
 test/fixtures/crypto.js | 22 ++++++++++--------
 4 files changed, 58 insertions(+), 35 deletions(-)

diff --git a/src/crypto.js b/src/crypto.js
index 6937f05..3e35da0 100644
--- a/src/crypto.js
+++ b/src/crypto.js
@@ -34,6 +34,10 @@ function sha256(buffer) {
 }
 
 // FIXME: Name not consistent with others
+function HmacSHA256(buffer, secret) {
+  return crypto.createHmac('sha256', secret).update(buffer).digest()
+}
+
 function HmacSHA512(data, secret) {
   assert(Buffer.isBuffer(data), 'Expected Buffer for data, got ' + data)
   assert(Buffer.isBuffer(secret), 'Expected Buffer for secret, got ' + secret)
@@ -51,5 +55,6 @@ module.exports = {
   sha256: sha256,
   hash160: hash160,
   hash256: hash256,
+  HmacSHA256: HmacSHA256,
   HmacSHA512: HmacSHA512
 }
diff --git a/src/ecdsa.js b/src/ecdsa.js
index 451ba4b..0023715 100644
--- a/src/ecdsa.js
+++ b/src/ecdsa.js
@@ -1,5 +1,5 @@
 var assert = require('assert')
-var crypto = require('crypto')
+var crypto = require('./crypto')
 var sec = require('./sec')
 var ecparams = sec("secp256k1")
 
@@ -36,10 +36,6 @@ function implShamirsTrick(P, k, Q, l) {
 
 var ecdsa = {
   deterministicGenerateK: function(hash, D) {
-    function HmacSHA256(buffer, secret) {
-      return crypto.createHmac('sha256', secret).update(buffer).digest()
-    }
-
     assert(Buffer.isBuffer(hash), 'Hash must be a Buffer')
     assert.equal(hash.length, 32, 'Hash must be 256 bit')
     assert(D instanceof BigInteger, 'Private key must be a BigInteger')
@@ -50,12 +46,12 @@ var ecdsa = {
     k.fill(0)
     v.fill(1)
 
-    k = HmacSHA256(Buffer.concat([v, new Buffer([0]), x, hash]), k)
-    v = HmacSHA256(v, k)
+    k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([0]), x, hash]), k)
+    v = crypto.HmacSHA256(v, k)
 
-    k = HmacSHA256(Buffer.concat([v, new Buffer([1]), x, hash]), k)
-    v = HmacSHA256(v, k)
-    v = HmacSHA256(v, k)
+    k = crypto.HmacSHA256(Buffer.concat([v, new Buffer([1]), x, hash]), k)
+    v = crypto.HmacSHA256(v, k)
+    v = crypto.HmacSHA256(v, k)
 
     var n = ecparams.getN()
     var kB = BigInteger.fromBuffer(v).mod(n)
diff --git a/test/crypto.js b/test/crypto.js
index 36ab9cc..e5ca84e 100644
--- a/test/crypto.js
+++ b/test/crypto.js
@@ -5,57 +5,75 @@ var fixtures = require('./fixtures/crypto')
 
 describe('Crypto', function() {
   describe('HASH160', function() {
-    it('matches the test vector', function() {
+    it('matches the test vectors', function() {
       fixtures.before.hex.forEach(function(hex, i) {
-        var actual = crypto.hash160(new Buffer(hex, 'hex')).toString('hex')
+        var data = new Buffer(hex, 'hex')
+        var actual = crypto.hash160(data)
         var expected = fixtures.after.hash160[i]
 
-        assert.equal(actual, expected)
+        assert.equal(actual.toString('hex'), expected)
       })
     })
   })
 
   describe('HASH256', function() {
-    it('matches the test vector', function() {
+    it('matches the test vectors', function() {
       fixtures.before.hex.forEach(function(hex, i) {
-        var actual = crypto.hash256(new Buffer(hex, 'hex')).toString('hex')
+        var data = new Buffer(hex, 'hex')
+        var actual = crypto.hash256(data)
         var expected = fixtures.after.hash256[i]
 
-        assert.equal(actual, expected)
+        assert.equal(actual.toString('hex'), expected)
       })
     })
   })
 
   describe('SHA1', function() {
-    it('matches the test vector', function() {
+    it('matches the test vectors', function() {
       fixtures.before.hex.forEach(function(hex, i) {
-        var actual = crypto.sha1(new Buffer(hex, 'hex')).toString('hex')
+        var data = new Buffer(hex, 'hex')
+        var actual = crypto.sha1(data)
         var expected = fixtures.after.sha1[i]
 
-        assert.equal(actual, expected)
+        assert.equal(actual.toString('hex'), expected)
       })
     })
   })
 
   describe('SHA256', function() {
-    it('matches the test vector', function() {
+    it('matches the test vectors', function() {
       fixtures.before.hex.forEach(function(hex, i) {
-        var actual = crypto.sha256(new Buffer(hex, 'hex')).toString('hex')
+        var data = new Buffer(hex, 'hex')
+        var actual = crypto.sha256(data)
         var expected = fixtures.after.sha256[i]
 
-        assert.equal(actual, expected)
+        assert.equal(actual.toString('hex'), expected)
       })
     })
   })
 
-  describe('HMAC SHA512', function() {
-    it('matches the test vector', function() {
+  describe('HmacSHA256', function() {
+    it('matches the test vectors', function() {
       fixtures.before.hex.forEach(function(hex, i) {
         var data = new Buffer(hex, 'hex')
-        var secret = new Buffer(fixtures.after.hmacsha512.secret)
+        var secret = new Buffer(fixtures.before.secret)
+
+        var actual = crypto.HmacSHA256(data, secret)
+        var expected = fixtures.after.hmacsha256[i]
+
+        assert.equal(actual.toString('hex'), expected)
+      })
+    })
+  })
+
+  describe('HmacSHA512', function() {
+    it('matches the test vectors', function() {
+      fixtures.before.hex.forEach(function(hex, i) {
+        var data = new Buffer(hex, 'hex')
+        var secret = new Buffer(fixtures.before.secret)
 
         var actual = crypto.HmacSHA512(data, secret)
-        var expected = fixtures.after.hmacsha512.hash[i]
+        var expected = fixtures.after.hmacsha512[i]
 
         assert.equal(actual.toString('hex'), expected)
       })
diff --git a/test/fixtures/crypto.js b/test/fixtures/crypto.js
index 1600a06..a89ac4a 100644
--- a/test/fixtures/crypto.js
+++ b/test/fixtures/crypto.js
@@ -1,5 +1,6 @@
 module.exports = {
   before: {
+    secret: 'vires is numeris',
     hex: [
       '0000000000000001',
       '0101010101010101',
@@ -33,14 +34,17 @@ module.exports = {
       '12a3ae445661ce5dee78d0650d33362dec29c4f82af05e7e57fb595bbbacf0ca',
       'a7fb8276035057ed6479c5f2305a96da100ac43f0ac10f277e5ab8c5457429da'
     ],
-    hmacsha512: {
-      secret: 'vires is numeris',
-      hash: [
-        '4c0595aed1f5d066ea9f797727c060eb86cb55ff29d4d4fd2cd0ad3a012386763aea604c030619c79aa7fd8d03cda1b73a9ebd17906a3d2a350108d1a98b24ac',
-        'f80b90d63b804b3d2ab03b9bfb3ac94ee271352eb8bddfb6b4f5cf2a4fc9176acea35f517728e64943d1eb8af1e4674a114082c81bc8874d88b408b3b406d6a4',
-        '134cf60c30a5cd412c7a5cd6c3f878279e139b47c19550b7456fa137fbf90e580ae0a923a22052f42ec801ac658db32821e271161b563eac4926285ba6b8f410',
-        '7dee95aa3c462d3eb7ecb61536cb215e471d1fa73d8643a967905946e26c536588c5058abd5a049a22b987db95a7fb420f3bff12359dc53d03d7ce7df714e029'
-      ]
-    }
+    hmacsha256: [
+      '73442dc8dd7f71a106a20fddd49d31856b1db12956c75070c8186b0b3eb71251',
+      '7204c72af7c73f5e84447a752dc8a2708f91b896f29de5fcf4b7f42f13a30c6e',
+      'a03c2ac6e9ca86678b5608a3d8682de46d17026f5fac4fd7147d2e5022061833',
+      'a780cd6e5c29cf11f756536ea5779992687c1b3b5e37f31b027a392d94e91fb8'
+    ],
+    hmacsha512: [
+      '4c0595aed1f5d066ea9f797727c060eb86cb55ff29d4d4fd2cd0ad3a012386763aea604c030619c79aa7fd8d03cda1b73a9ebd17906a3d2a350108d1a98b24ac',
+      'f80b90d63b804b3d2ab03b9bfb3ac94ee271352eb8bddfb6b4f5cf2a4fc9176acea35f517728e64943d1eb8af1e4674a114082c81bc8874d88b408b3b406d6a4',
+      '134cf60c30a5cd412c7a5cd6c3f878279e139b47c19550b7456fa137fbf90e580ae0a923a22052f42ec801ac658db32821e271161b563eac4926285ba6b8f410',
+      '7dee95aa3c462d3eb7ecb61536cb215e471d1fa73d8643a967905946e26c536588c5058abd5a049a22b987db95a7fb420f3bff12359dc53d03d7ce7df714e029'
+    ]
   }
 }

From 4c7108d5618a6b32cc40d55bc17d4d1da5190bd4 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Fri, 16 May 2014 23:11:04 +1000
Subject: [PATCH 03/10] ecdsa: add serializeSigCompact and tests

This also adds tests for all other ECDSA serialize/parsing functions.
The k, r, s and D values were sourced from test vectors on
https://bitcointalk.org/index.php?topic=285142.40 .

The compact signatures (aka, i values) were generated from bitcoinjslib, but they
are straight forward anyway.
---
 src/ecdsa.js           |  15 ++++
 test/ecdsa.js          |  90 ++++++++++++++++++--
 test/fixtures/ecdsa.js | 186 ++++++++++++++++++++++++++++++-----------
 3 files changed, 233 insertions(+), 58 deletions(-)

diff --git a/src/ecdsa.js b/src/ecdsa.js
index 0023715..74cf7b3 100644
--- a/src/ecdsa.js
+++ b/src/ecdsa.js
@@ -204,6 +204,21 @@ var ecdsa = {
     return {r: r, s: s}
   },
 
+  serializeSigCompact: function(r, s, i, compressed) {
+    if (compressed) {
+      i += 4
+    }
+
+    i += 27
+
+    var buffer = new Buffer(65)
+    buffer.writeUInt8(i, 0)
+    r.toBuffer(32).copy(buffer, 1)
+    s.toBuffer(32).copy(buffer, 33)
+
+    return buffer
+  },
+
   parseSigCompact: function (sig) {
     if (sig.length !== 65) {
       throw new Error("Signature has the wrong length")
diff --git a/test/ecdsa.js b/test/ecdsa.js
index f42c76e..4637df5 100644
--- a/test/ecdsa.js
+++ b/test/ecdsa.js
@@ -16,12 +16,12 @@ var fixtures = require('./fixtures/ecdsa.js')
 describe('ecdsa', function() {
   describe('deterministicGenerateK', function() {
     it('matches the test vectors', function() {
-      fixtures.forEach(function(f) {
+      fixtures.valid.forEach(function(f) {
         var priv = BigInteger.fromHex(f.D)
         var h1 = crypto.sha256(f.message)
 
         var k = ecdsa.deterministicGenerateK(h1, priv)
-        assert.deepEqual(k.toHex(), f.k)
+        assert.equal(k.toHex(), f.k)
       })
     })
   })
@@ -41,14 +41,14 @@ describe('ecdsa', function() {
 
   describe('sign', function() {
     it('matches the test vectors', function() {
-      fixtures.forEach(function(f) {
+      fixtures.valid.forEach(function(f) {
         var D = BigInteger.fromHex(f.D)
         var priv = new ECKey(D)
         var hash = crypto.sha256(f.message)
         var sig = ecdsa.parseSig(priv.sign(hash))
 
-        assert.equal(sig.r.toHex(), f.signature.slice(0, 64))
-        assert.equal(sig.s.toHex(), f.signature.slice(64))
+        assert.equal(sig.r.toString(), f.signature.r)
+        assert.equal(sig.s.toString(), f.signature.s)
       })
     })
 
@@ -65,16 +65,90 @@ describe('ecdsa', function() {
 
   describe('verifyRaw', function() {
     it('matches the test vectors', function() {
-      fixtures.forEach(function(f) {
+      fixtures.valid.forEach(function(f) {
         var D = BigInteger.fromHex(f.D)
         var priv = new ECKey(D)
 
-        var r = BigInteger.fromHex(f.signature.slice(0, 64))
-        var s = BigInteger.fromHex(f.signature.slice(64))
+        var r = new BigInteger(f.signature.r)
+        var s = new BigInteger(f.signature.s)
         var e = BigInteger.fromBuffer(crypto.sha256(f.message))
 
         assert(ecdsa.verifyRaw(e, r, s, priv.pub.Q))
       })
     })
   })
+
+  describe('serializeSig', function() {
+    it('encodes a DER signature', function() {
+      fixtures.valid.forEach(function(f) {
+        var r = new BigInteger(f.signature.r)
+        var s = new BigInteger(f.signature.s)
+
+        var signature = new Buffer(ecdsa.serializeSig(r, s))
+        assert.equal(signature.toString('hex'), f.DER)
+      })
+    })
+  })
+
+  describe('parseSig', function() {
+    it('decodes the correct signature', function() {
+      fixtures.valid.forEach(function(f) {
+        var buffer = new Buffer(f.DER, 'hex')
+        var signature = ecdsa.parseSig(buffer)
+
+        assert.equal(signature.r.toString(), f.signature.r)
+        assert.equal(signature.s.toString(), f.signature.s)
+      })
+    })
+
+    fixtures.invalid.DER.forEach(function(f) {
+      it('throws on ' + f.description, function() {
+        var buffer = new Buffer(f.hex)
+
+        assert.throws(function() {
+          ecdsa.parseSig(buffer)
+        })
+      })
+    })
+  })
+
+  describe('serializeSigCompact', function() {
+    it('encodes a compact signature', function() {
+      fixtures.valid.forEach(function(f) {
+        var r = new BigInteger(f.signature.r)
+        var s = new BigInteger(f.signature.s)
+        var i = f.signature.i
+        var compressed = f.signature.compressed
+
+        var signature = ecdsa.serializeSigCompact(r, s, i, compressed)
+        assert.equal(signature.toString('hex'), f.compact)
+      })
+    })
+  })
+
+  describe('parseSigCompact', function() {
+    it('decodes the correct signature', function() {
+      fixtures.valid.forEach(function(f) {
+        var buffer = new Buffer(f.compact, 'hex')
+        var signature = ecdsa.parseSigCompact(buffer)
+
+        assert.equal(signature.r.toString(), f.signature.r)
+        assert.equal(signature.s.toString(), f.signature.s)
+
+        //TODO
+//        assert.equal(signature.i, f.signature.i)
+//        assert.equal(signature.compressed, f.publicKey.compressed)
+      })
+    })
+
+    fixtures.invalid.compact.forEach(function(f) {
+      it('throws on ' + f.description, function() {
+        var buffer = new Buffer(f.hex)
+
+        assert.throws(function() {
+          ecdsa.parseSigCompact(buffer)
+        })
+      })
+    })
+  })
 })
diff --git a/test/fixtures/ecdsa.js b/test/fixtures/ecdsa.js
index 631475e..6936dd1 100644
--- a/test/fixtures/ecdsa.js
+++ b/test/fixtures/ecdsa.js
@@ -1,51 +1,137 @@
-// Test Vectors for RFC 6979 ECDSA, secp256k1, SHA-256
-module.exports = [
-  {
-    "D": "0000000000000000000000000000000000000000000000000000000000000001",
-    "k": "8f8a276c19f4149656b280621e358cce24f5f52542772691ee69063b74f15d15",
-    "message": "Satoshi Nakamoto",
-    "signature": "934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d82442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5"
-  },
-  {
-    "D": "0000000000000000000000000000000000000000000000000000000000000001",
-    "k": "38aa22d72376b4dbc472e06c3ba403ee0a394da63fc58d88686c611aba98d6b3",
-    "message": "All those moments will be lost in time, like tears in rain. Time to die...",
-    "signature": "8600dbd41e348fe5c9465ab92d23e3db8b98b873beecd930736488696438cb6b547fe64427496db33bf66019dacbf0039c04199abb0122918601db38a72cfc21"
-  },
-  {
-    "D": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
-    "k": "33a19b60e25fb6f4435af53a3d42d493644827367e6453928554f43e49aa6f90",
-    "message": "Satoshi Nakamoto",
-    "signature": "fd567d121db66e382991534ada77a6bd3106f0a1098c231e47993447cd6af2d06b39cd0eb1bc8603e159ef5c20a5c8ad685a45b06ce9bebed3f153d10d93bed5"
-  },
-  {
-    "D": "f8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181",
-    "k": "525a82b70e67874398067543fd84c83d30c175fdc45fdeee082fe13b1d7cfdf1",
-    "message": "Alan Turing",
-    "signature": "7063ae83e7f62bbb171798131b4a0564b956930092b33b07b395615d9ec7e15c58dfcc1e00a35e1572f366ffe34ba0fc47db1e7189759b9fb233c5b05ab388ea"
-  },
-  {
-    "D": "e91671c46231f833a6406ccbea0e3e392c76c167bac1cb013f6f1013980455c2",
-    "k": "1f4b84c23a86a221d233f2521be018d9318639d5b8bbd6374a8a59232d16ad3d",
-    "message": "There is a computer disease that anybody who works with computers knows about. It's a very serious disease and it interferes completely with the work. The trouble with computers is that you 'play' with them!",
-    "signature": "b552edd27580141f3b2a5463048cb7cd3e047b97c9f98076c32dbdf85a68718b279fa72dd19bfae05577e06c7c0c1900c371fcd5893f7e1d56a37d30174671f6"
-  },
-  {
-    "D": "0000000000000000000000000000000000000000000000000000000000000001",
-    "k": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5",
-    "message": "Everything should be made as simple as possible, but not simpler.",
-    "signature": "33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262"
-  },
-  {
-    "D": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
-    "k": "9dc74cbfd383980fb4ae5d2680acddac9dac956dca65a28c80ac9c847c2374e4",
-    "message": "Equations are more important to me, because politics is for the present, but an equation is something for eternity.",
-    "signature": "54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5"
-  },
-  {
-    "D": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
-    "k": "fd27071f01648ebbdd3e1cfbae48facc9fa97edc43bbbc9a7fdc28eae13296f5",
-    "message": "Not only is the Universe stranger than we think, it is stranger than we can think.",
-    "signature": "ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283"
+module.exports = {
+  "valid": [
+    {
+      "D": "0000000000000000000000000000000000000000000000000000000000000001",
+      "k": "ec633bd56a5774a0940cb97e27a9e4e51dc94af737596a0c5cbb3d30332d92a5",
+      "message": "Everything should be made as simple as possible, but not simpler.",
+      "compact": "1f33a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c96f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262",
+      "DER": "3044022033a69cd2065432a30f3d1ce4eb0d59b8ab58c74f27c41a7fdb5696ad4e6108c902206f807982866f785d3f6418d24163ddae117b7db4d5fdf0071de069fa54342262",
+      "signature": {
+        "r": "23362334225185207751494092901091441011938859014081160902781146257181456271561",
+        "s": "50433721247292933944369538617440297985091596895097604618403996029256432099938",
+        "i": 0,
+        "compressed": true
+      }
+    },
+    {
+      "D": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
+      "k": "9dc74cbfd383980fb4ae5d2680acddac9dac956dca65a28c80ac9c847c2374e4",
+      "message": "Equations are more important to me, because politics is for the present, but an equation is something for eternity.",
+      "compact": "1b54c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed07082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5",
+      "DER": "3044022054c4a33c6423d689378f160a7ff8b61330444abb58fb470f96ea16d99d4a2fed022007082304410efa6b2943111b6a4e0aaa7b7db55a07e9861d1fb3cb1f421044a5",
+      "signature": {
+        "r": "38341707918488238920692284707283974715538935465589664377561695343399725051885",
+        "s": "3180566392414476763164587487324397066658063772201694230600609996154610926757",
+        "i": 0,
+        "compressed": false
+      }
+    },
+    {
+      "D": "fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364140",
+      "k": "fd27071f01648ebbdd3e1cfbae48facc9fa97edc43bbbc9a7fdc28eae13296f5",
+      "message": "Not only is the Universe stranger than we think, it is stranger than we can think.",
+      "compact": "1fff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd06fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283",
+      "DER": "3045022100ff466a9f1b7b273e2f4c3ffe032eb2e814121ed18ef84665d0f515360dab3dd002206fc95f5132e5ecfdc8e5e6e616cc77151455d46ed48f5589b7db7771a332b283",
+      "signature": {
+        "r": "115464191557905790016094131873849783294273568009648050793030031933291767741904",
+        "s": "50562520307781850052192542766631199590053690478900449960232079510155113443971",
+        "i": 0,
+        "compressed": true
+      }
+    },
+    {
+      "D": "0000000000000000000000000000000000000000000000000000000000000001",
+      "k": "f0cd2ba5fc7c183de589f6416220a36775a146740798756d8d949f7166dcc87f",
+      "message": "How wonderful that we have met with a paradox. Now we have some hope of making progress.",
+      "compact": "1cc0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d375afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3",
+      "DER": "3045022100c0dafec8251f1d5010289d210232220b03202cba34ec11fec58b3e93a85b91d3022075afdc06b7d6322a590955bf264e7aaa155847f614d80078a90292fe205064d3",
+      "signature": {
+        "r": "87230998027579607140680851455601772643840468630989315269459846730712163783123",
+        "s": "53231320085894623106179381504478252331065330583563809963303318469380290929875",
+        "i": 1,
+        "compressed": false
+      }
+    },
+    {
+      "D": "69ec59eaa1f4f2e36b639716b7c30ca86d9a5375c7b38d8918bd9c0ebc80ba64",
+      "k": "6bb4a594ad57c1aa22dbe991a9d8501daf4688bf50a4892ef21bd7c711afda97",
+      "message": "Computer science is no more about computers than astronomy is about telescopes.",
+      "compact": "1f7186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d0de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6",
+      "DER": "304402207186363571d65e084e7f02b0b77c3ec44fb1b257dee26274c38c928986fea45d02200de0b38e06807e46bda1f1e293f4f6323e854c86d58abdd00c46c16441085df6",
+      "signature": {
+        "r": "51348483531757779992459563033975330355971795607481991320287437101831125115997",
+        "s": "6277080015686056199074771961940657638578000617958603212944619747099038735862",
+        "i": 0,
+        "compressed": true
+      }
+    },
+    {
+      "D": "00000000000000000000000000007246174ab1e92e9149c6e446fe194d072637",
+      "k": "097b5c8ee22c3ea78a4d3635e0ff6fe85a1eb92ce317ded90b9e71aab2b861cb",
+      "message": "...if you aren't, at any given time, scandalized by code you wrote five or even three years ago, you're not learning anywhere near enough",
+      "compact": "1cfbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda4870e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37",
+      "DER": "3045022100fbfe5076a15860ba8ed00e75e9bd22e05d230f02a936b653eb55b61c99dda48702200e68880ebb0050fe4312b1b1eb0899e1b82da89baa5b895f612619edf34cbd37",
+      "signature": {
+        "r": "113979859486826658566290715281614250298918272782414232881639314569529560769671",
+        "s": "6517071009538626957379450615706485096874328019806177698938278220732027419959",
+        "i": 1,
+        "compressed": false
+      }
+    },
+    {
+      "D": "000000000000000000000000000000000000000000056916d0f9b31dc9b637f3",
+      "k": "19355c36c8cbcdfb2382e23b194b79f8c97bf650040fc7728dfbf6b39a97c25b",
+      "message": "The question of whether computers can think is like the question of whether submarines can swim.",
+      "compact": "20cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf906ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef",
+      "DER": "3045022100cde1302d83f8dd835d89aef803c74a119f561fbaef3eb9129e45f30de86abbf9022006ce643f5049ee1f27890467b77a6a8e11ec4661cc38cd8badf90115fbd03cef",
+      "signature": {
+        "r": "93122007060065279508564838030979550535085999589142852106617159184757394422777",
+        "s": "3078539468410661027472930027406594684630312677495124015420811882501887769839",
+        "i": 1,
+        "compressed": true
+      }
+    }
+  ],
+  "invalid": {
+    "compact": [
+      {
+        "description": "Invalid signature parameters",
+        "hex": "23987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62"
+      },
+      {
+        "description": "Signature too long",
+        "hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e93791d7629795b62000000"
+      },
+      {
+        "description": "Signature too short",
+        "hex": "1c987ceade6a304fc5823ab38f99fc3c5f772a2d3e89ea05931e2726105fc53b9e601fc3231f35962c714fcbce5c95b427496edc7ae8b3d12e9379"
+      }
+    ],
+    "DER": [
+      {
+        "description": "Invalid sequence length",
+        "hex": "30ff0204ffffffff0204ffffffff"
+      },
+      {
+        "description": "Invalid integer(s) length",
+        "hex": "30080201ffffffff0201ffffffff"
+      },
+      {
+        "description": "Invalid sequence tag",
+        "hex": "ff080204ffffffff0204ffffffff"
+      },
+      {
+        "description": "Invalid integer tag",
+        "hex": "30080304ffffffff0304ffffffff"
+      },
+      {
+        "description": "Sequence too short",
+        "hex": "30080304ffffffff0304ff"
+      },
+      {
+        "description": "Sequence too long",
+        "hex": "30080304ffffffff0304ffffffffffffff"
+      }
+    ]
   }
-]
+}

From 1c76bdf9c3405145e8ffb6a38daf2ab288484947 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Fri, 16 May 2014 23:17:46 +1000
Subject: [PATCH 04/10] ec: add better test vectors for ECPointFp encode/decode

These test vectors were generated internally.
---
 test/ec.js          | 54 +++++++++++++++++++++++++++++++---------
 test/fixtures/ec.js | 60 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 102 insertions(+), 12 deletions(-)
 create mode 100644 test/fixtures/ec.js

diff --git a/test/ec.js b/test/ec.js
index af3e65d..963bf0e 100644
--- a/test/ec.js
+++ b/test/ec.js
@@ -6,6 +6,8 @@ var ecparams = sec('secp256k1')
 var BigInteger = require('bigi')
 var ECPointFp = require('../src/ec').ECPointFp
 
+var fixtures = require('./fixtures/ec.js')
+
 describe('ec', function() {
   describe('ECPointFp', function() {
     it('behaves correctly', function() {
@@ -22,21 +24,49 @@ describe('ec', function() {
 
       assert.ok(P.validate(), "kG validates as a public key")
     })
-  })
 
-  describe('decodeFrom', function() {
-    it('decodes compressed ECPoints', function() {
-      var s = new Buffer('02789ece95adf35fb3de994b8b16c90166736d70913a18378fff79503e8c5db7fb', 'hex')
-      var Q = ECPointFp.decodeFrom(ecparams.getCurve(), s)
-      assert.ok(Q)
-      assert.ok(Q.validate())
+    describe('getEncoded', function() {
+      it('encodes a point correctly', function() {
+        fixtures.valid.ECPointFp.forEach(function(f) {
+          var curve = ecparams.getCurve()
+          var Q = new ECPointFp(
+            curve,
+            curve.fromBigInteger(new BigInteger(f.x)),
+            curve.fromBigInteger(new BigInteger(f.y))
+          )
+
+          var encoded = new Buffer(Q.getEncoded(f.compressed))
+          assert.equal(encoded.toString('hex'), f.hex)
+        })
+      })
     })
 
-    it('decodes uncompressed ECPoints', function() {
-      var s = new Buffer('0486f356006a38b847bedec1bf47013776925d939d5a35a97a4d1263e550c7f1ab5aba44ab74d22892097a0e851addf07ba97e33416df5affaceeb35d5607cd23c', 'hex')
-      var Q = ECPointFp.decodeFrom(ecparams.getCurve(), s)
-      assert.ok(Q)
-      assert.ok(Q.validate())
+    describe('decodeFrom', function() {
+      it('decodes the correct point', function() {
+        fixtures.valid.ECPointFp.forEach(function(f) {
+          var curve = ecparams.getCurve()
+          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)
+        })
+      })
+
+      // 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)
+  //        })
+  //      })
+  //    })
     })
   })
 })
diff --git a/test/fixtures/ec.js b/test/fixtures/ec.js
new file mode 100644
index 0000000..b7b973f
--- /dev/null
+++ b/test/fixtures/ec.js
@@ -0,0 +1,60 @@
+module.exports = {
+  "valid": {
+    "ECPointFp": [
+      {
+        "x": "55066263022277343669578718895168534326250603453777594175500187360389116729240",
+        "y": "32670510020758816978083085130507043184471273380659243275938904335757337482424",
+        "compressed": false,
+        "hex": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8"
+      },
+      {
+        "x": "55066263022277343669578718895168534326250603453777594175500187360389116729240",
+        "y": "32670510020758816978083085130507043184471273380659243275938904335757337482424",
+        "compressed": true,
+        "hex": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+      },
+      {
+        "x": "83225686012142088543596389522774768397204444195709443235253141114409346958144",
+        "y": "23739058578904784236915560265041168694780215705543362357495033621678991351768",
+        "compressed": true,
+        "hex": "02b80011a883a0fd621ad46dfc405df1e74bf075cbaf700fd4aebef6e96f848340"
+      },
+      {
+        "x": "30095590000961171681152428142595206241714764354580127609094760797518133922356",
+        "y": "93521207164355458151597931319591130635754976513751247168472016818884561919702",
+        "compressed": true,
+        "hex": "024289801366bcee6172b771cf5a7f13aaecd237a0b9a1ff9d769cabc2e6b70a34"
+      },
+      {
+        "x": "55066263022277343669578718895168534326250603453777594175500187360389116729240",
+        "y": "83121579216557378445487899878180864668798711284981320763518679672151497189239",
+        "compressed": true,
+        "hex": "0379be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+      }
+    ]
+  },
+  "invalid": {
+    "ECPointFp": [
+      {
+        "description": "Invalid sequence tag",
+        "hex": "0179be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
+      },
+      {
+        "description": "Sequence too short",
+        "hex": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10"
+      },
+      {
+        "description": "Sequence too short (compressed)",
+        "hex": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f8"
+      },
+      {
+        "description": "Sequence too long",
+        "hex": "0479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b80000"
+      },
+      {
+        "description": "Sequence too long (compressed)",
+        "hex": "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f817980000"
+      }
+    ]
+  }
+}

From db3ffe58d1a3f6800e21f34a1139ddb1929e6a80 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Sat, 17 May 2014 00:27:54 +1000
Subject: [PATCH 05/10] message: use serializeSigCompact

---
 src/message.js | 10 +---------
 1 file changed, 1 insertion(+), 9 deletions(-)

diff --git a/src/message.js b/src/message.js
index 58715b4..15c355a 100644
--- a/src/message.js
+++ b/src/message.js
@@ -27,15 +27,7 @@ function sign(key, message, network) {
   var sig = ecdsa.parseSig(key.sign(hash))
   var i = ecdsa.calcPubKeyRecoveryParam(key.pub.Q, sig.r, sig.s, hash)
 
-  i += 27
-  if (key.pub.compressed) {
-    i += 4
-  }
-
-  var rB = sig.r.toBuffer(32)
-  var sB = sig.s.toBuffer(32)
-
-  return Buffer.concat([new Buffer([i]), rB, sB], 65)
+  return ecdsa.serializeSigCompact(sig.r, sig.s, i, key.pub.compressed)
 }
 
 // TODO: network could be implied from address

From ccca6989b566540122d5b9616e38f91653f19a84 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Sat, 17 May 2014 00:33:33 +1000
Subject: [PATCH 06/10] ec: getEncoded now uses Buffer API

---
 src/ec.js       | 40 ++++++++++++++++++----------------------
 src/ecpubkey.js |  2 +-
 test/ec.js      |  2 +-
 3 files changed, 20 insertions(+), 24 deletions(-)

diff --git a/src/ec.js b/src/ec.js
index 9f450f8..24d488c 100644
--- a/src/ec.js
+++ b/src/ec.js
@@ -303,32 +303,28 @@ ECFieldElementFp.prototype.getByteLength = function () {
   return Math.floor((this.toBigInteger().bitLength() + 7) / 8);
 };
 
-ECPointFp.prototype.getEncoded = function (compressed) {
-  var x = this.getX().toBigInteger();
-  var y = this.getY().toBigInteger();
-
-  // Get value as a 32-byte Buffer
-  // Fixed length based on a patch by bitaddress.org and Casascius
-  var enc = integerToBytes(x, 32);
+ECPointFp.prototype.getEncoded = function(compressed) {
+  var x = this.getX().toBigInteger()
+  var y = this.getY().toBigInteger()
+  var buffer
 
+  // 0x02/0x03 | X
   if (compressed) {
-    if (y.isEven()) {
-      // Compressed even pubkey
-      // M = 02 || X
-      enc.unshift(0x02);
-    } else {
-      // Compressed uneven pubkey
-      // M = 03 || X
-      enc.unshift(0x03);
-    }
+    buffer = new Buffer(33)
+    buffer.writeUInt8(y.isEven() ? 0x02 : 0x03, 0)
+
+  // 0x04 | X | Y
   } else {
-    // Uncompressed pubkey
-    // M = 04 || X || Y
-    enc.unshift(0x04);
-    enc = enc.concat(integerToBytes(y, 32));
+    buffer = new Buffer(65)
+    buffer.writeUInt8(0x04, 0)
+
+    y.toBuffer(32).copy(buffer, 33)
   }
-  return enc;
-};
+
+  x.toBuffer(32).copy(buffer, 1)
+
+  return buffer
+}
 
 ECPointFp.decodeFrom = function (curve, enc) {
   var type = enc[0];
diff --git a/src/ecpubkey.js b/src/ecpubkey.js
index 8bcf984..0c7dafb 100644
--- a/src/ecpubkey.js
+++ b/src/ecpubkey.js
@@ -48,7 +48,7 @@ ECPubKey.prototype.verify = function(hash, sig) {
 
 // Export functions
 ECPubKey.prototype.toBuffer = function() {
-  return new Buffer(this.Q.getEncoded(this.compressed))
+  return this.Q.getEncoded(this.compressed)
 }
 
 ECPubKey.prototype.toHex = function() {
diff --git a/test/ec.js b/test/ec.js
index 963bf0e..a547ea1 100644
--- a/test/ec.js
+++ b/test/ec.js
@@ -35,7 +35,7 @@ describe('ec', function() {
             curve.fromBigInteger(new BigInteger(f.y))
           )
 
-          var encoded = new Buffer(Q.getEncoded(f.compressed))
+          var encoded = Q.getEncoded(f.compressed)
           assert.equal(encoded.toString('hex'), f.hex)
         })
       })

From f7c7265d64dc935174ee3e896dc99dcbd0f1ce09 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Sat, 17 May 2014 00:23:40 +1000
Subject: [PATCH 07/10] ecdsa: parseSig now uses buffer API

---
 src/ecdsa.js | 46 ++++++++++++++++++----------------------------
 1 file changed, 18 insertions(+), 28 deletions(-)

diff --git a/src/ecdsa.js b/src/ecdsa.js
index 74cf7b3..68c38c0 100644
--- a/src/ecdsa.js
+++ b/src/ecdsa.js
@@ -164,7 +164,7 @@ var ecdsa = {
   },
 
   /**
-   * Parses a byte array containing a DER-encoded signature.
+   * Parses a buffer containing a DER-encoded signature.
    *
    * This function will return an object of the form:
    *
@@ -173,35 +173,25 @@ var ecdsa = {
    *   s: BigInteger
    * }
    */
-  parseSig: function (sig) {
-    if(Array.isArray(sig)) sig = new Buffer(sig);
+  parseSig: function (buffer) {
+    if (Array.isArray(buffer)) buffer = new Buffer(buffer) // FIXME: transitionary
 
-    var cursor
-    if (sig[0] != 0x30) {
-      throw new Error("Signature not a valid DERSequence")
+    assert.equal(buffer.readUInt8(0), 0x30, 'Not a DER sequence')
+    assert.equal(buffer.readUInt8(1), buffer.length - 2, 'Invalid sequence length')
+
+    assert.equal(buffer.readUInt8(2), 0x02, 'Expected DER integer')
+    var rLen = buffer.readUInt8(3)
+    var rB = buffer.slice(4, 4 + rLen)
+
+    var offset = 4 + rLen
+    assert.equal(buffer.readUInt8(offset), 0x02, 'Expected a 2nd DER integer')
+    var sLen = buffer.readUInt8(1 + offset)
+    var sB = buffer.slice(2 + offset)
+
+    return {
+      r: BigInteger.fromByteArraySigned(rB),
+      s: BigInteger.fromByteArraySigned(sB)
     }
-
-    cursor = 2
-    if (sig[cursor] != 0x02) {
-      throw new Error("First element in signature must be a DERInteger")
-    }
-    var rBa = sig.slice(cursor+2, cursor+2+sig[cursor+1])
-
-    cursor += 2+sig[cursor+1]
-    if (sig[cursor] != 0x02) {
-      throw new Error("Second element in signature must be a DERInteger")
-    }
-    var sBa = sig.slice(cursor+2, cursor+2+sig[cursor+1])
-
-    cursor += 2+sig[cursor+1]
-
-    //if (cursor != sig.length)
-    //  throw new Error("Extra bytes in signature")
-
-    var r = BigInteger.fromBuffer(rBa)
-    var s = BigInteger.fromBuffer(sBa)
-
-    return {r: r, s: s}
   },
 
   serializeSigCompact: function(r, s, i, compressed) {

From a3f691bf7ca17588ea7b83a05239347ffa3dd3c1 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Sat, 17 May 2014 00:28:39 +1000
Subject: [PATCH 08/10] ecdsa: parseSigCompact use Buffer API

parseSigCompact also now returns the correct recovert parameter without
the need to subtract the compression bit.
This makes it easier to use.
---
 src/ecdsa.js   | 35 +++++++++++++++++++----------------
 src/message.js |  3 +--
 test/ecdsa.js  |  6 ++----
 3 files changed, 22 insertions(+), 22 deletions(-)

diff --git a/src/ecdsa.js b/src/ecdsa.js
index 68c38c0..6a7fd72 100644
--- a/src/ecdsa.js
+++ b/src/ecdsa.js
@@ -209,23 +209,26 @@ var ecdsa = {
     return buffer
   },
 
-  parseSigCompact: function (sig) {
-    if (sig.length !== 65) {
-      throw new Error("Signature has the wrong length")
+  parseSigCompact: function (buffer) {
+    assert.equal(buffer.length, 65, 'Invalid signature length')
+    var i = buffer.readUInt8(0) - 27
+
+    // At most 3 bits
+    assert.equal(i, i & 7, 'Invalid signature type')
+    var compressed = !!(i & 4)
+
+    // Recovery param only
+    i = i & 3
+
+    var r = BigInteger.fromBuffer(buffer.slice(1, 33))
+    var s = BigInteger.fromBuffer(buffer.slice(33))
+
+    return {
+      r: r,
+      s: s,
+      i: i,
+      compressed: compressed
     }
-
-    // Signature is prefixed with a type byte storing three bits of
-    // information.
-    var i = sig[0] - 27
-    if (i < 0 || i > 7) {
-      throw new Error("Invalid signature type")
-    }
-
-    var n = ecparams.getN()
-    var r = BigInteger.fromBuffer(sig.slice(1, 33)).mod(n)
-    var s = BigInteger.fromBuffer(sig.slice(33, 65)).mod(n)
-
-    return {r: r, s: s, i: i}
   },
 
   /**
diff --git a/src/message.js b/src/message.js
index 15c355a..b7d545b 100644
--- a/src/message.js
+++ b/src/message.js
@@ -41,9 +41,8 @@ function verify(address, compactSig, message, network) {
   var hash = magicHash(message, network)
   var sig = ecdsa.parseSigCompact(compactSig)
   var Q = ecdsa.recoverPubKey(sig.r, sig.s, hash, sig.i)
-  var compressed = !!(sig.i & 4)
 
-  var pubKey = new ECPubKey(Q, compressed)
+  var pubKey = new ECPubKey(Q, sig.compressed)
   return pubKey.getAddress(address.version).toString() === address.toString()
 }
 
diff --git a/test/ecdsa.js b/test/ecdsa.js
index 4637df5..aef4f2d 100644
--- a/test/ecdsa.js
+++ b/test/ecdsa.js
@@ -134,10 +134,8 @@ describe('ecdsa', function() {
 
         assert.equal(signature.r.toString(), f.signature.r)
         assert.equal(signature.s.toString(), f.signature.s)
-
-        //TODO
-//        assert.equal(signature.i, f.signature.i)
-//        assert.equal(signature.compressed, f.publicKey.compressed)
+        assert.equal(signature.i, f.signature.i)
+        assert.equal(signature.compressed, f.signature.compressed)
       })
     })
 

From 24371425f92079dcbb8e725e2ace62455c6f317b Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Sat, 17 May 2014 00:39:30 +1000
Subject: [PATCH 09/10] ec: decodeFrom now uses Buffers and handles errors

---
 src/ec.js       | 60 ++++++++++++++++++++++++++++---------------------
 src/ecpubkey.js | 10 ++-------
 test/ec.js      | 29 +++++++++++-------------
 3 files changed, 50 insertions(+), 49 deletions(-)

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)
+          })
+        })
+      })
     })
   })
 })

From d14b08efd1bf95749cbc32b29fb0f69f67a35b42 Mon Sep 17 00:00:00 2001
From: Daniel Cousens <github@dcousens.com>
Date: Thu, 22 May 2014 12:28:14 +1000
Subject: [PATCH 10/10] ec/dsa: recovery param now used consistently

Also added an assertion rather than massaging the input.
---
 src/ec.js    |  6 ++++--
 src/ecdsa.js | 14 ++++++--------
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/src/ec.js b/src/ec.js
index 22705e9..854532a 100644
--- a/src/ec.js
+++ b/src/ec.js
@@ -339,7 +339,7 @@ ECPointFp.decodeFrom = function (curve, buffer) {
     assert.equal(buffer.length, 33, 'Invalid sequence length')
     assert(type === 0x02 || type === 0x03, 'Invalid sequence tag')
 
-    var isYEven = type === 0x03
+    var isYEven = (type === 0x02)
     var p = curve.getQ()
 
     // We precalculate (p + 1) / 4 where p is the field order
@@ -349,7 +349,9 @@ ECPointFp.decodeFrom = function (curve, buffer) {
     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)
+    // If beta is even, but y isn't, or vice versa, then convert it,
+    // otherwise we're done and y == beta.
+    y = (beta.isEven() ^ isYEven) ? p.subtract(beta) : beta
 
   } else {
     assert.equal(buffer.length, 65, 'Invalid sequence length')
diff --git a/src/ecdsa.js b/src/ecdsa.js
index 6a7fd72..9993af0 100644
--- a/src/ecdsa.js
+++ b/src/ecdsa.js
@@ -240,12 +240,11 @@ var ecdsa = {
    * http://www.secg.org/download/aid-780/sec1-v2.pdf
    */
   recoverPubKey: function (r, s, hash, i) {
-    // The recovery parameter i has two bits.
-    i = i & 3
+    assert.strictEqual(i & 3, i, 'The recovery param is more than two bits')
 
-    // The less significant bit specifies whether the y coordinate
-    // of the compressed point is even or not.
-    var isYEven = i & 1
+    // A set LSB signifies that the y-coordinate is odd
+    // By reduction, the y-coordinate is even if it is clear
+    var isYEven = !(i & 1)
 
     // The more significant bit specifies whether we should use the
     // first or second candidate key.
@@ -270,10 +269,9 @@ var ecdsa = {
     var alpha = x.multiply(x).multiply(x).add(a.multiply(x)).add(b).mod(p)
     var beta = alpha.modPow(P_OVER_FOUR, p)
 
-    //    var xorOdd = beta.isEven() ? (i % 2) : ((i+1) % 2)
-    // If beta is even, but y isn't or vice versa, then convert it,
+    // If beta is even, but y isn't, or vice versa, then convert it,
     // otherwise we're done and y == beta.
-    var y = (beta.isEven() ? !isYEven : isYEven) ? beta : p.subtract(beta)
+    var y = (beta.isEven() ^ isYEven) ? p.subtract(beta) : beta
 
     // 1.4 Check that nR is at infinity
     var R = new ECPointFp(curve, curve.fromBigInteger(x), curve.fromBigInteger(y))