Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial work on ECIES for Bitcore 0.8.
- Loading branch information
1 parent
66c427f
commit 4409492
Showing
9 changed files
with
410 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
service_name: travis-ci |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
language: node_js | ||
node_js: | ||
- '0.10' | ||
after_script: istanbul cover ./node_modules/mocha/bin/_mocha --report lcovonly -- -R spec && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js && rm -rf ./coverage |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = require('./lib/ECIES'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
var aes = require('aes'); | ||
|
||
var AES = function AES() { | ||
}; | ||
|
||
AES.encrypt = function(messagebuf, keybuf) { | ||
var key = AES.buf2words(keybuf); | ||
var message = AES.buf2words(messagebuf); | ||
var a = new aes(key); | ||
var enc = a.encrypt(message); | ||
var encbuf = AES.words2buf(enc); | ||
return encbuf; | ||
}; | ||
|
||
AES.decrypt = function(encbuf, keybuf) { | ||
var enc = AES.buf2words(encbuf); | ||
var key = AES.buf2words(keybuf); | ||
var a = new aes(key); | ||
var message = a.decrypt(enc); | ||
var messagebuf = AES.words2buf(message); | ||
return messagebuf; | ||
}; | ||
|
||
AES.buf2words = function(buf) { | ||
if (buf.length % 4) | ||
throw new Error('buf length must be a multiple of 4'); | ||
|
||
var words = []; | ||
|
||
for (var i = 0; i < buf.length / 4; i++) { | ||
words.push(buf.readUInt32BE(i * 4)); | ||
}; | ||
|
||
return words; | ||
}; | ||
|
||
AES.words2buf = function(words) { | ||
var buf = new Buffer(words.length * 4); | ||
|
||
for (var i = 0; i < words.length; i++) { | ||
buf.writeUInt32BE(words[i], i * 4); | ||
}; | ||
|
||
return buf; | ||
}; | ||
|
||
module.exports = AES; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
var bitcore = require('bitcore'); | ||
|
||
var AES = require('./AES'); | ||
var CBC = require('./CBC'); | ||
var Random = bitcore.crypto.Random; | ||
var Hash = bitcore.crypto.Hash; | ||
|
||
// Symmetric encryption with AES and CBC convenience class | ||
var AESCBC = function AESCBC() {}; | ||
|
||
AESCBC.encrypt = function(messagebuf, passwordstr) { | ||
var cipherkeybuf = Hash.sha256(new Buffer(passwordstr)); | ||
return AESCBC.encryptCipherkey(messagebuf, cipherkeybuf); | ||
}; | ||
|
||
AESCBC.decrypt = function(encbuf, passwordstr) { | ||
var cipherkeybuf = Hash.sha256(new Buffer(passwordstr)); | ||
return AESCBC.decryptCipherkey(encbuf, cipherkeybuf); | ||
}; | ||
|
||
AESCBC.encryptCipherkey = function(messagebuf, cipherkeybuf, ivbuf) { | ||
ivbuf = ivbuf || Random.getRandomBuffer(128 / 8); | ||
var ctbuf = CBC.encrypt(messagebuf, ivbuf, AES, cipherkeybuf); | ||
var encbuf = Buffer.concat([ivbuf, ctbuf]); | ||
return encbuf; | ||
}; | ||
|
||
AESCBC.decryptCipherkey = function(encbuf, cipherkeybuf) { | ||
var ivbuf = encbuf.slice(0, 128 / 8); | ||
var ctbuf = encbuf.slice(128 / 8); | ||
var messagebuf = CBC.decrypt(ctbuf, ivbuf, AES, cipherkeybuf); | ||
return messagebuf; | ||
}; | ||
|
||
module.exports = AESCBC; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
var Random = require('bitcore').crypto.Random; | ||
|
||
// Cipher Block Chaining | ||
// http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29 | ||
var CBC = function CBC(blockcipher, cipherkeybuf, ivbuf) { | ||
if (!(this instanceof CBC)) | ||
return new CBC(blockcipher, cipherkeybuf, ivbuf); | ||
|
||
this.blockcipher = blockcipher; | ||
this.cipherkeybuf = cipherkeybuf; | ||
this.ivbuf = ivbuf; | ||
}; | ||
|
||
CBC.buf2blockbufs = function(buf, blocksize) { | ||
var bytesize = blocksize / 8; | ||
var blockbufs = []; | ||
|
||
for (var i = 0; i <= buf.length / bytesize; i++) { | ||
var blockbuf = buf.slice(i * bytesize, i * bytesize + bytesize); | ||
|
||
if (blockbuf.length < blocksize) | ||
blockbuf = CBC.pkcs7pad(blockbuf, blocksize); | ||
|
||
blockbufs.push(blockbuf); | ||
} | ||
|
||
return blockbufs; | ||
}; | ||
|
||
CBC.blockbufs2buf = function(blockbufs, blocksize) { | ||
var bytesize = blocksize / 8; | ||
|
||
var last = blockbufs[blockbufs.length - 1]; | ||
last = CBC.pkcs7unpad(last); | ||
blockbufs[blockbufs.length - 1] = last; | ||
|
||
var buf = Buffer.concat(blockbufs); | ||
|
||
return buf; | ||
}; | ||
|
||
CBC.encrypt = function(messagebuf, ivbuf, blockcipher, cipherkeybuf) { | ||
var blocksize = ivbuf.length * 8; | ||
var blockbufs = CBC.buf2blockbufs(messagebuf, blocksize); | ||
var encbufs = CBC.encryptblocks(blockbufs, ivbuf, blockcipher, cipherkeybuf); | ||
var encbuf = Buffer.concat(encbufs); | ||
return encbuf; | ||
}; | ||
|
||
CBC.decrypt = function(encbuf, ivbuf, blockcipher, cipherkeybuf) { | ||
var blocksize = ivbuf.length * 8; | ||
var bytesize = ivbuf.length; | ||
var encbufs = []; | ||
for (var i = 0; i < encbuf.length / bytesize; i++) { | ||
encbufs.push(encbuf.slice(i * bytesize, i * bytesize + bytesize)); | ||
} | ||
var blockbufs = CBC.decryptblocks(encbufs, ivbuf, blockcipher, cipherkeybuf); | ||
var buf = CBC.blockbufs2buf(blockbufs, blocksize); | ||
return buf; | ||
}; | ||
|
||
CBC.encryptblock = function(blockbuf, ivbuf, blockcipher, cipherkeybuf) { | ||
var xorbuf = CBC.xorbufs(blockbuf, ivbuf); | ||
var encbuf = blockcipher.encrypt(xorbuf, cipherkeybuf); | ||
return encbuf; | ||
}; | ||
|
||
CBC.decryptblock = function(encbuf, ivbuf, blockcipher, cipherkeybuf) { | ||
var xorbuf = blockcipher.decrypt(encbuf, cipherkeybuf); | ||
var blockbuf = CBC.xorbufs(xorbuf, ivbuf); | ||
return blockbuf; | ||
}; | ||
|
||
CBC.encryptblocks = function(blockbufs, ivbuf, blockcipher, cipherkeybuf) { | ||
var encbufs = []; | ||
|
||
for (var i = 0; i < blockbufs.length; i++) { | ||
var blockbuf = blockbufs[i]; | ||
var encbuf = CBC.encryptblock(blockbuf, ivbuf, blockcipher, cipherkeybuf); | ||
|
||
encbufs.push(encbuf); | ||
|
||
ivbuf = encbuf; | ||
} | ||
|
||
return encbufs; | ||
}; | ||
|
||
CBC.decryptblocks = function(encbufs, ivbuf, blockcipher, cipherkeybuf) { | ||
var blockbufs = []; | ||
|
||
for (var i = 0; i < encbufs.length; i++) { | ||
var encbuf = encbufs[i]; | ||
var blockbuf = CBC.decryptblock(encbuf, ivbuf, blockcipher, cipherkeybuf); | ||
|
||
blockbufs.push(blockbuf); | ||
|
||
ivbuf = encbuf; | ||
} | ||
|
||
return blockbufs; | ||
}; | ||
|
||
CBC.pkcs7pad = function(buf, blocksize) { | ||
var bytesize = blocksize / 8; | ||
var padbytesize = bytesize - buf.length; | ||
var pad = new Buffer(padbytesize); | ||
pad.fill(padbytesize); | ||
var paddedbuf = Buffer.concat([buf, pad]); | ||
return paddedbuf; | ||
}; | ||
|
||
CBC.pkcs7unpad = function(paddedbuf, blocksize) { | ||
var bytesize = blocksize / 8; | ||
var padbytesize = bytesize - paddedbuf.length; | ||
var padlength = paddedbuf[paddedbuf.length - 1]; | ||
var padbuf = paddedbuf.slice(paddedbuf.length - padlength, paddedbuf.length); | ||
var padbuf2 = new Buffer(padlength); | ||
padbuf2.fill(padlength); | ||
if (padbuf.toString('hex') !== padbuf2.toString('hex')) | ||
throw new Error('invalid padding'); | ||
return paddedbuf.slice(0, paddedbuf.length - padlength); | ||
}; | ||
|
||
CBC.xorbufs = function(buf1, buf2) { | ||
if (buf1.length !== buf2.length) | ||
throw new Error('bufs must have the same length'); | ||
|
||
var buf = new Buffer(buf1.length); | ||
|
||
for (var i = 0; i < buf1.length; i++) { | ||
buf[i] = buf1[i] ^ buf2[i]; | ||
} | ||
|
||
return buf; | ||
}; | ||
|
||
module.exports = CBC; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
var bitcore = require('bitcore'); | ||
|
||
var Point = bitcore.crypto.Point; | ||
var Hash = bitcore.crypto.Hash; | ||
var Random = bitcore.crypto.Random; | ||
|
||
var PublicKey = bitcore.PublicKey; | ||
var PrivateKey = bitcore.PrivateKey; | ||
|
||
var AESCBC = require('./AESCBC'); | ||
|
||
// http://en.wikipedia.org/wiki/Integrated_Encryption_Scheme | ||
var ECIES = function ECIES() { | ||
if (!(this instanceof ECIES)) return new ECIES(); | ||
}; | ||
|
||
ECIES.prototype.privateKey = function( privateKey ) { | ||
if (!privateKey) throw new Error('no private key provided'); | ||
|
||
this._privateKey = privateKey || null; | ||
|
||
return this; | ||
}; | ||
|
||
ECIES.prototype.publicKey = function( publicKey ) { | ||
if (!publicKey) throw new Error('no public key provided'); | ||
|
||
this._publicKey = publicKey || null; | ||
|
||
return this; | ||
}; | ||
|
||
ECIES.prototype.encrypt = function( message ) { | ||
if (!Buffer.isBuffer( message )) message = new Buffer( message ); | ||
var ivbuf = Random.getRandomBuffer( 128 / 8 ); | ||
|
||
var r = this._privateKey.bn; | ||
var R = this._privateKey.publicKey.point; | ||
var Rpubkey = this._privateKey.publicKey; | ||
var Rbuf = Rpubkey.toDER( true ); | ||
var KB = this._publicKey.point; | ||
var P = KB.mul( r ); | ||
var S = P.getX(); | ||
var Sbuf = S.toBuffer({ size: 32 }); | ||
var kEkM = Hash.sha512(Sbuf); | ||
var kE = kEkM.slice(0, 32); | ||
var kM = kEkM.slice(32, 64); | ||
var c = AESCBC.encryptCipherkey(message, kE, ivbuf); | ||
var d = Hash.sha256hmac(c, kM); | ||
var encbuf = Buffer.concat([Rbuf, c, d]); | ||
|
||
return encbuf; | ||
}; | ||
|
||
ECIES.prototype.decrypt = function( encbuf ) { | ||
var kB = this._privateKey.bn; | ||
var frompubkey = this._publicKey; | ||
var R = frompubkey.point; | ||
var P = R.mul(kB); | ||
var S = P.getX(); | ||
|
||
var Sbuf = S.toBuffer({ size: 32 }); | ||
var kEkM = Hash.sha512(Sbuf); | ||
|
||
var kE = kEkM.slice(0, 32); | ||
var kM = kEkM.slice(32, 64); | ||
|
||
var c = encbuf.slice(33, encbuf.length - 32); | ||
var d = encbuf.slice(encbuf.length - 32, encbuf.length); | ||
|
||
var d2 = Hash.sha256hmac(c, kM); | ||
if (d.toString('hex') !== d2.toString('hex')) throw new Error('Invalid checksum'); | ||
var messagebuf = AESCBC.decryptCipherkey(c, kE); | ||
|
||
return messagebuf; | ||
}; | ||
|
||
module.exports = ECIES; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
{ | ||
"name": "bitcore-ecies", | ||
"version": "0.0.1", | ||
"description": "ECIES implemented for Bitcore.", | ||
"main": "index.js", | ||
"directories": { | ||
"test": "test" | ||
}, | ||
"scripts": { | ||
"test": "mocha", | ||
"coverage": "istanbul cover _mocha" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/bitpay/bitcore-ecies.git" | ||
}, | ||
"keywords": [ | ||
"bitcoin", | ||
"bitcore", | ||
"ecies", | ||
"crypto", | ||
"cryptography", | ||
"encryption" | ||
], | ||
"author": "BitPay", | ||
"license": "MIT", | ||
"bugs": { | ||
"url": "https://github.com/bitpay/bitcore-ecies/issues" | ||
}, | ||
"homepage": "https://github.com/bitpay/bitcore-ecies", | ||
"devDependencies": { | ||
"assert": "^1.1.2", | ||
"coveralls": "^2.11.2", | ||
"istanbul": "^0.3.5", | ||
"mocha": "^2.0.1" | ||
}, | ||
"dependencies": { | ||
"aes": "^0.1.0", | ||
"bitcore": "git://github.com/bitpay/bitcore#v0.8" | ||
} | ||
} |
Oops, something went wrong.