|
|
|
'use strict';
|
|
|
|
var coinUtil = require('../../util');
|
|
|
|
var Point = require('../Point');
|
|
|
|
var SecureRandom = require('../SecureRandom');
|
|
|
|
var Key = require('../Key');
|
|
|
|
|
|
|
|
// 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.getRandomSeed(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 = new 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.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.getRandomSeed = function(r) {
|
|
|
|
if (r) {
|
|
|
|
this.key = new Key();
|
|
|
|
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 = ECIES;
|