diff --git a/lib/bip38.js b/lib/bip38.js index 944b335..573f136 100644 --- a/lib/bip38.js +++ b/lib/bip38.js @@ -223,4 +223,34 @@ Bip38.prototype.decryptECMult = function(encData, passphrase) { } } +Bip38.prototype.verify = function(encryptedBase58) { + var decoded + try { + decoded = cs.decode(encryptedBase58) + } catch (e) { + return false + } + + if (decoded.length !== 39) return false + if (decoded.readUInt8(0) !== 0x01) return false + + var type = decoded.readUInt8(1) + var flag = decoded.readUInt8(2) + + // encrypted WIF + if (type === 0x42) { + if (flag !== 0xc0 && flag !== 0xe0) return false + + // EC mult + } else if (type === 0x43) { + if ((flag & ~0x24)) return false + + } else { + return false + } + + + return true +} + module.exports = Bip38 diff --git a/test/bip38.test.js b/test/bip38.test.js index 2896a73..b7e9fb2 100644 --- a/test/bip38.test.js +++ b/test/bip38.test.js @@ -18,13 +18,21 @@ describe('bip38', function() { }) }) - fixtures.invalid.forEach(function(f) { + fixtures.invalid.decrypt.forEach(function(f) { it('should throw ' + f.description, function() { assert.throws(function() { bip38.decrypt(f.bip38, f.passphrase) }, new RegExp(f.description, 'i')) }) }) + + fixtures.invalid.verify.forEach(function(f) { + it('should throw because ' + f.description, function() { + assert.throws(function() { + bip38.decrypt(f.base58, 'foobar') + }, new RegExp(f.exception)) + }) + }) }) describe('encrypt', function() { @@ -36,4 +44,18 @@ describe('bip38', function() { }) }) }) + + describe('verify', function() { + fixtures.valid.forEach(function(f) { + it('should return true for ' + f.bip38, function() { + assert(bip38.verify(f.bip38)) + }) + }) + + fixtures.invalid.verify.forEach(function(f) { + it('should return false for ' + f.description, function() { + assert(!bip38.verify(f.base58)) + }) + }) + }) }) diff --git a/test/fixtures.json b/test/fixtures.json index b1845cf..4a0e4a4 100644 --- a/test/fixtures.json +++ b/test/fixtures.json @@ -71,6 +71,51 @@ "sequence": 1 } ], - "invalid": [ - ] + "invalid": { + "decrypt": [], + "encrypt": [], + "verify": [ + { + "description": "Invalid base58", + "exception": "Invalid checksum", + "base58": "6PgGWtx25kUg8QWvwuJAgorN6k9FbE25rv5dMRwu5SKMnfpfVe5marXXXX" + }, + { + "description": "Length > 39", + "exception": "Invalid BIP38 data length", + "hex": "0142c000000000000000000000000000000000000000000000000000000000000000000000000000", + "base58": "QmxDezFMDL7ExfYmsETsQXAtBbw5YE1CDyA8pm1AGpMpVVUpsVy1yXv4VTL" + }, + { + "description": "Length < 39", + "exception": "Invalid BIP38 data length", + "hex": "0142c00000000000000000000000000000000000000000000000000000000000000000000000", + "base58": "2DnNxWcx4Prn8wmjbkvtYGDALsq8BMWxQ33KnXkeH8vrxE41psDLXRmK3" + }, + { + "description": "prefix !== 0x01", + "exception": "Invalid BIP38 prefix", + "hex": "0242c0000000000000000000000000000000000000000000000000000000000000000000000000", + "base58": "AfE1YY4Wr2FLAENaH9PVaLRdyk714V4rhwiJMSGyQCGFB3rhGDCs2R7c4s" + }, + { + "description": "flag !== 0xc0 && flag !== 0xe0", + "exception": "Invalid BIP38 type", + "hex": "0101ff000000000000000000000000000000000000000000000000000000000000000000000000", + "base58": "5JjnYkbFBmUnhGeDMVhR7aSitLToe1odEfXDBeg4RMK6JmAm9g7rkm7qY3" + }, + { + "description": "EC Mult: ~(flag & 0x24)", + "exception": "Invalid BIP38 type", + "hex": "0101db000000000000000000000000000000000000000000000000000000000000000000000000", + "base58": "5JbtdQFKSemRTqMuWrJgSfzE8AX2jdz1KiZuMmuUcv9iXha1s6UarQTciW" + }, + { + "description": "EC Mult: ~(flag & 0x24)", + "exception": "Invalid BIP38 type", + "hex": "010135000000000000000000000000000000000000000000000000000000000000000000000000", + "base58": "5HyV7HSYdHUgLf7w36mxMHDPH9muTgUYHEj6cEogKMuV7ae8VRM3VEg56w" + } + ] + } }