Ryan X. Charles
11 years ago
9 changed files with 28460 additions and 222 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,131 @@ |
|||
'use strict'; |
|||
var imports = require('soop').imports(); |
|||
var coinUtil = imports.coinUtil || require('../util'); |
|||
var Point = imports.Point || require('./Point'); |
|||
var SecureRandom = imports.SecureRandom || require('./SecureRandom'); |
|||
var Key = imports.Key || require('./Key'); |
|||
var crypto = require('crypto'); |
|||
|
|||
// http://en.wikipedia.org/wiki/Integrated_Encryption_Scheme
|
|||
var ECIES = function() { |
|||
}; |
|||
|
|||
ECIES.encryptObj = function(pubkey, message, r, iv) { |
|||
var ecies = new ECIES(); |
|||
ecies.KB = pubkey; |
|||
ecies.message = message; |
|||
r = ecies.r = ecies.rand(r); |
|||
var R = ecies.R; |
|||
var S = ecies.S = ecies.getSfromPubkey(); |
|||
var buf = ECIES.kdf(S); |
|||
var kE = ecies.kE = buf.slice(0, 32); |
|||
var kM = ecies.kM = buf.slice(32, 64); |
|||
iv = iv || SecureRandom.getRandomBuffer(16); |
|||
var c = ecies.c = ECIES.symmetricEncrypt(kE, iv, message); |
|||
var d = ecies.d = ECIES.mac(kM, c); |
|||
return ecies; |
|||
}; |
|||
|
|||
ECIES.encrypt = function(pubkey, message, r, iv) { |
|||
var ecies = ECIES.encryptObj(pubkey, message, r, iv); |
|||
var key = Key(); |
|||
key.compressed = false; |
|||
key.public = ecies.R.toUncompressedPubKey(); |
|||
key.compressed = true; |
|||
var Rbuf = key.public; |
|||
var buf = Buffer.concat([Rbuf, ecies.c, ecies.d]); |
|||
return buf; |
|||
}; |
|||
|
|||
ECIES.decryptObj = function(ecies) { |
|||
var kB = ecies.kB; |
|||
var R = ecies.R; |
|||
var c = ecies.c; |
|||
var d = ecies.d; |
|||
var P = Point.multiply(R, kB); |
|||
var S = P.x.toBuffer({size: 32}); |
|||
var buf = ECIES.kdf(S); |
|||
var kE = ecies.kE = buf.slice(0, 32); |
|||
var kM = ecies.kM = buf.slice(32, 64); |
|||
var d2 = ECIES.mac(kM, c); |
|||
if (d.toString('hex') !== d2.toString('hex')) |
|||
throw new Error('MAC check incorrect. Data is invalid.'); |
|||
var decrypted = ECIES.symmetricDecrypt(kE, c); |
|||
return decrypted; |
|||
}; |
|||
|
|||
ECIES.decrypt = function(privkey, buf) { |
|||
if (buf.length < 33 + 0 + 64) |
|||
throw new Error('invalid length of encrypted data'); |
|||
var ecies = new ECIES(); |
|||
ecies.kB = privkey; |
|||
var Rbuf = buf.slice(0, 33); |
|||
var key = new Key(); |
|||
key.public = Rbuf; |
|||
key.compressed = false; |
|||
ecies.R = Point.fromUncompressedPubKey(key.public); |
|||
ecies.c = buf.slice(33, buf.length - 64); |
|||
ecies.d = buf.slice(buf.length - 64, buf.length); |
|||
return ECIES.decryptObj(ecies); |
|||
}; |
|||
|
|||
ECIES.symmetricEncrypt = function(key, iv, message) { |
|||
var cipheriv = crypto.createCipheriv('AES256', key, iv); |
|||
var a = cipheriv.update(message); |
|||
var b = cipheriv.final(); |
|||
var r = Buffer.concat([iv, a, b]); |
|||
return r; |
|||
}; |
|||
|
|||
ECIES.symmetricDecrypt = function(key, encrypted) { |
|||
var iv = encrypted.slice(0, 16); |
|||
var decipheriv = crypto.createDecipheriv('AES256', key, iv); |
|||
var todecrypt = encrypted.slice(16, encrypted.length); |
|||
var a = decipheriv.update(todecrypt); |
|||
var b = decipheriv.final(); |
|||
var r = Buffer.concat([a, b]); |
|||
return r; |
|||
}; |
|||
|
|||
ECIES.kdf = function(S) { |
|||
var buf = coinUtil.sha512(S); |
|||
return buf; |
|||
}; |
|||
|
|||
ECIES.mac = function(data, key) { |
|||
var buf = coinUtil.sha512hmac(data, key); |
|||
return buf; |
|||
}; |
|||
|
|||
ECIES.prototype.rand = function(r) { |
|||
if (r) { |
|||
this.key.private = r; |
|||
this.key.regenerateSync(); |
|||
} else { |
|||
this.key = Key.generateSync(); |
|||
}; |
|||
this.r = this.key.private; |
|||
this.key.compressed = false; |
|||
this.R = Point.fromUncompressedPubKey(this.key.public); |
|||
return this.r; |
|||
}; |
|||
|
|||
ECIES.prototype.getSfromPubkey = function() { |
|||
var key2 = new Key(); |
|||
key2.public = this.KB; |
|||
key2.compressed = false; |
|||
var KBP = Point.fromUncompressedPubKey(key2.public); |
|||
this.P = Point.multiply(KBP, this.r); |
|||
this.S = this.P.x.toBuffer({size: 32}); |
|||
return this.S; |
|||
}; |
|||
|
|||
ECIES.prototype.getSfromPrivkey = function() { |
|||
var R = this.R; |
|||
var kB = this.kB; |
|||
var SP = Point.multiply(R, kB); |
|||
var S = SP.x.toBuffer({size: 32}); |
|||
return S; |
|||
}; |
|||
|
|||
module.exports = require('soop')(ECIES); |
@ -0,0 +1,172 @@ |
|||
'use strict'; |
|||
|
|||
var chai = chai || require('chai'); |
|||
var bitcore = bitcore || require('../bitcore'); |
|||
var should = chai.should(); |
|||
var assert = chai.assert; |
|||
var ECIES = bitcore.ECIES; |
|||
var Point = bitcore.Point; |
|||
|
|||
describe('ECIES', function() { |
|||
|
|||
describe('#rand', function() { |
|||
|
|||
it('should set r and R', function() { |
|||
var ecies = new ECIES(); |
|||
ecies.rand(); |
|||
ecies.r.length.should.equal(32); |
|||
ecies.R.toUncompressedPubKey().length.should.equal(65); |
|||
}); |
|||
|
|||
it('should not set the same r twice in a row', function() { |
|||
var ecies = new ECIES(); |
|||
ecies.rand(); |
|||
var ecies2 = new ECIES(); |
|||
ecies2.rand(); |
|||
ecies.r.toString('hex').should.not.equal(ecies2.r.toString('hex')); |
|||
}); |
|||
|
|||
}); |
|||
|
|||
describe('#encryptObj', function() { |
|||
|
|||
it('should not fail', function() { |
|||
var key = new bitcore.Key(); |
|||
key.private = bitcore.util.sha256('test'); |
|||
key.regenerateSync(); |
|||
|
|||
var message = new Buffer('this is my message'); |
|||
var ecies = ECIES.encryptObj(key.public, message); |
|||
|
|||
should.exist(ecies.R); |
|||
should.exist(ecies.c); |
|||
should.exist(ecies.d); |
|||
}); |
|||
|
|||
}); |
|||
|
|||
describe('#encrypt', function() { |
|||
|
|||
it('should not fail', function() { |
|||
var key = new bitcore.Key(); |
|||
key.private = bitcore.util.sha256('test'); |
|||
key.regenerateSync(); |
|||
|
|||
var message = new Buffer('this is my message'); |
|||
var encrypted = ECIES.encrypt(key.public, message); |
|||
|
|||
should.exist(encrypted); |
|||
}); |
|||
|
|||
}); |
|||
|
|||
describe('#decrypt', function() { |
|||
|
|||
it('should not fail', function() { |
|||
var key = new bitcore.Key(); |
|||
key.private = bitcore.util.sha256('test'); |
|||
key.regenerateSync(); |
|||
|
|||
var message = new Buffer('this is my message'); |
|||
var encrypted = ECIES.encrypt(key.public, message); |
|||
|
|||
var decrypted = ECIES.decrypt(key.private, encrypted); |
|||
|
|||
decrypted.toString().should.equal('this is my message'); |
|||
}); |
|||
|
|||
it('should not fail for long messages', function() { |
|||
var key = new bitcore.Key(); |
|||
key.private = bitcore.util.sha256('test'); |
|||
key.regenerateSync(); |
|||
|
|||
var message = new Buffer('this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message this is my message '); |
|||
|
|||
var encrypted = ECIES.encrypt(key.public, message); |
|||
|
|||
var decrypted = ECIES.decrypt(key.private, encrypted); |
|||
|
|||
decrypted.toString().should.equal(message.toString()); |
|||
}); |
|||
|
|||
}); |
|||
|
|||
describe('#symmetricEncrypt', function() { |
|||
|
|||
it('should not fail', function() { |
|||
var data = new Buffer([1, 2, 3, 4, 5]); |
|||
var key = bitcore.util.sha256('test'); |
|||
var iv = bitcore.util.sha256('test').slice(0, 16); |
|||
var encrypted = ECIES.symmetricEncrypt(key, iv, data); |
|||
encrypted.length.should.equal(16 + 16); |
|||
}); |
|||
|
|||
}); |
|||
|
|||
describe('#symmetricDecrypt', function() { |
|||
|
|||
it('should decrypt that which was encrypted', function() { |
|||
var data = new Buffer([1, 2, 3, 4, 5]); |
|||
var key = bitcore.util.sha256('test'); |
|||
var iv = bitcore.util.sha256('test').slice(0, 16); |
|||
var encrypted = ECIES.symmetricEncrypt(key, iv, data); |
|||
var decrypted = ECIES.symmetricDecrypt(key, encrypted); |
|||
decrypted[0].should.equal(1); |
|||
decrypted[4].should.equal(5); |
|||
}); |
|||
|
|||
}); |
|||
|
|||
describe('#getSfromPubkey', function() { |
|||
|
|||
it('should find S correctly', function() { |
|||
var key = new bitcore.Key(); |
|||
key.private = bitcore.util.sha256('test'); |
|||
key.regenerateSync(); |
|||
var ecies = new ECIES(); |
|||
ecies.r = bitcore.util.sha256('test test'); |
|||
ecies.KB = key.public; |
|||
var S = ecies.getSfromPubkey(); |
|||
S.toString('hex').should.equal('9de4c42c4190fa987d84ce735a0370f7bb42f8646cec6274c5420f5af8fbfdbc'); |
|||
}); |
|||
|
|||
}); |
|||
|
|||
describe('#getSfromPrivkey', function() { |
|||
|
|||
it('should find S the same as getSfromPubkey', function() { |
|||
var key = new bitcore.Key(); |
|||
key.private = bitcore.util.sha256('test'); |
|||
key.regenerateSync(); |
|||
|
|||
var r = bitcore.util.sha256('test test'); |
|||
var key2 = new bitcore.Key(); |
|||
key2.private = r; |
|||
key2.regenerateSync(); |
|||
key2.compressed = false; |
|||
var R = Point.fromUncompressedPubKey(key2.public); |
|||
|
|||
var ecies = new ECIES(); |
|||
ecies.r = r; |
|||
ecies.KB = key.public; |
|||
var S = ecies.getSfromPubkey(); |
|||
|
|||
var ecies2 = new ECIES(); |
|||
ecies2.R = R; |
|||
ecies2.kB = key.private; |
|||
var S2 = ecies2.getSfromPrivkey(); |
|||
|
|||
S.toString('hex').should.equal(S2.toString('hex')); |
|||
}); |
|||
|
|||
}); |
|||
|
|||
describe('#kdf', function() { |
|||
|
|||
it('should be sha512', function() { |
|||
var data = new Buffer([0, 1, 2, 3, 4, 5, 6]); |
|||
ECIES.kdf(data).toString('hex').should.equal(bitcore.util.sha512(data).toString('hex')); |
|||
}); |
|||
}); |
|||
|
|||
}); |
Loading…
Reference in new issue