Commit b267ae7c authored by Cédric Moreau's avatar Cédric Moreau

Generating manually proof-of-work

parent 982238c4
......@@ -145,6 +145,9 @@ module.exports = new function() {
return unix2dos(signed(that.getMembershipWithoutSignature(json), json));
};
var KEYBLOCK_PUBK_PREFIX = "#####-----"
var KEYBLOCK_PUBK_SUFFIX = "-----#####"
this.getKeyblockWithoutSignature = function (json) {
var raw = "";
raw += "Version: " + json.version + "\n";
......@@ -165,8 +168,11 @@ module.exports = new function() {
}
raw += "PublicKeys:\n";
for(var i = 0; i < json.publicKeys.length; i++){
raw += '#' + json.publicKeys[i].fingerprint + '\n';
raw += json.publicKeys[i].packets;
var packets = json.publicKeys[i].packets;
raw += KEYBLOCK_PUBK_PREFIX + json.publicKeys[i].fingerprint + KEYBLOCK_PUBK_SUFFIX + '\n';
raw += packets;
if (!packets.match(/\n$/))
raw += '\n';
}
raw += "Memberships:\n";
for(var i = 0; i < json.memberships.length; i++){
......@@ -174,8 +180,11 @@ module.exports = new function() {
}
raw += "MembershipsSignatures:\n";
for(var i = 0; i < json.membershipsSigs.length; i++){
raw += '#' + json.membershipsSigs[i].fingerprint + '\n';
raw += json.membershipsSigs[i].packets;
var packets = json.membershipsSigs[i].packets;
raw += KEYBLOCK_PUBK_PREFIX + json.membershipsSigs[i].fingerprint + KEYBLOCK_PUBK_SUFFIX + '\n';
raw += packets;
if (!packets.match(/\n$/))
raw += '\n';
}
return unix2dos(raw);
};
......
var async = require('async');
var openpgp = require('openpgp');
var jpgp = require('./jpgp');
var logger = require('./logger')('peerserver');
module.exports = function (armoredPrivateKey, password, withOpenPGPJS, done) {
var privateKey = openpgp.key.readArmored(armoredPrivateKey).keys[0];
var fingerprint = privateKey.getKeyPacket().getFingerprint().toUpperCase();
async.waterfall([
function (next) {
if (withOpenPGPJS) {
var pgp = jpgp();
privateKey.decrypt(password);
var signingFunc = async.apply(pgp.sign.bind(pgp.sign), privateKey);
next(null, function (message, done) {
jpgp().sign(message, privateKey, done);
});
} else {
var asciiPrivateKey = armoredPrivateKey;
var keyring = '~/.gnupg/ucoin_' + fingerprint;
logger.debug("Keyring = %s", keyring);
var gnupg = new (require('./gnupg'))(asciiPrivateKey, password, fingerprint, keyring);
gnupg.init(function (err) {
next(err, function (message, done) {
gnupg.sign(message, done);
});
});
}
},
function (signFunc, next){
try{
signFunc("some test\nwith line return", function (err) {
next(err, signFunc);
});
} catch(ex){
next("Wrong private key password.");
}
},
], function (err, signFunc) {
done(err, signFunc);
});
};
......@@ -130,6 +130,7 @@ function Multicaster () {
});
this.sendPubkey = sendPubkey;
this.sendKeyblock = sendKeyblock;
}
util.inherits(Multicaster, stream.Transform);
......@@ -143,6 +144,15 @@ function sendPubkey(peer, pubkey, done) {
}, done);
}
function sendKeyblock(peer, keyblock, done) {
var keyID = peer.keyID();
logger.info('POST keyblock to %s', keyID.match(/\?/) ? peer.getURL() : keyID);
post(peer, '/keychain/keyblock', {
"keyblock": keyblock.getRaw(),
"signature": keyblock.signature
}, done);
}
function sendTransaction(peer, transaction, done) {
logger.info('POST transaction to %s', peer.keyID());
post(peer, '/hdc/transactions/process', {
......
......@@ -114,11 +114,11 @@ function extractFingerprintSeparatedPackets(raw) {
var lines = raw.split(/\n/);
var nbKeys = 0;
lines.forEach(function(line){
if (line.match(/^#[A-Z0-9]{40}$/)) {
if (line.match(/^#####-----[A-Z0-9]{40}-----#####$/)) {
// New key block
packetsByFPR.push({
number: nbKeys++,
fingerprint: line.substring(1),
fingerprint: line.substring(10, 50),
packets: ""
});
} else if (line.match(/^[A-Za-z0-9\/+=]{1,64}$/) && nbKeys > 0) {
......
......@@ -258,6 +258,10 @@ KeyBlockSchema.methods = {
},
getRaw: function() {
return require('../lib/rawer').getKeyblockWithoutSignature(this);
},
getRawSigned: function() {
return require('../lib/rawer').getKeyblock(this);
},
......
......@@ -15,7 +15,7 @@ var STATUS = {
DOWN: "DOWN",
NOTHING: "NOTHING"
};
var BMA_REGEXP = /^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]+))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/;
var BMA_REGEXP = /^BASIC_MERKLED_API( ([a-z_][a-z0-9-_.]*))?( ([0-9.]+))?( ([0-9a-f:]+))?( ([0-9]+))$/;
var PeerSchema = new Schema({
version: String,
......
......@@ -2,10 +2,13 @@ var jpgp = require('../lib/jpgp');
var async = require('async');
var _ = require('underscore');
var openpgp = require('openpgp');
var merkle = require('merkle');
var base64 = require('../lib/base64');
var unix2dos = require('../lib/unix2dos');
var dos2unix = require('../lib/dos2unix');
var parsers = require('../lib/streams/parsers/doc');
var mlogger = require('../lib/logger')('membership');
var logger = require('../lib/logger')('membership');
var moment = require('moment');
module.exports.get = function (conn, conf, PublicKeyService) {
return new KeyService(conn, conf, PublicKeyService);
......@@ -34,7 +37,7 @@ function KeyService (conn, conf, PublicKeyService) {
var entry = new Membership(ms);
async.waterfall([
function (next){
mlogger.debug('⬇ %s %s', entry.issuer, entry.membership);
logger.debug('⬇ %s %s', entry.issuer, entry.membership);
// Get already existing Membership with same parameters
Membership.getForHashAndIssuer(entry.hash, entry.issuer, next);
},
......@@ -51,7 +54,7 @@ function KeyService (conn, conf, PublicKeyService) {
});
},
function (next){
mlogger.debug('✔ %s %s', entry.issuer, entry.membership);
logger.debug('✔ %s %s', entry.issuer, entry.membership);
next(null, entry);
}
], done);
......@@ -581,6 +584,37 @@ function KeyService (conn, conf, PublicKeyService) {
return certifs;
};
this.getPurePubkey = function () {
return new openpgp.key.Key(this.getPurePackets());
};
// Get signatories' certification packet of the userid (not checked yet)
this.getPurePackets = function () {
var purePackets = [];
var packets = this.packets.filterByTag(
openpgp.enums.packet.publicKey,
openpgp.enums.packet.publicSubkey,
openpgp.enums.packet.userid,
openpgp.enums.packet.signature);
packets.forEach(function(packet){
var signaturesToKeep = [
openpgp.enums.signature.cert_generic,
openpgp.enums.signature.cert_persona,
openpgp.enums.signature.cert_casual,
openpgp.enums.signature.cert_positive,
openpgp.enums.signature.subkey_binding
];
if (~signaturesToKeep.indexOf(packet.signatureType)) {
var issuerKeyId = packet.issuerKeyId.toHex().toUpperCase();
var isSelfSig = fingerprint.match(new RegExp(issuerKeyId + '$'));
if (isSelfSig) {
purePackets.push(packet);
}
}
});
return purePackets;
};
this.getPubKey = function () {
return new openpgp.key.Key(this.packets);
};
......@@ -595,4 +629,127 @@ function KeyService (conn, conf, PublicKeyService) {
return armor;
}
}
this.current = function (done) {
KeyBlock.current(function (err, kb) {
done(err, kb || null);
})
}
this.generateRoot = function (uids, done) {
var joinData = {};
var fingerprints = [];
async.forEach(uids, function(uid, callback){
var join = { pubkey: null, ms: null };
async.waterfall([
function (next){
Membership.find({ userid: uid }, next);
},
function (mss, next){
if (mss.length == 0) {
next('Membership not found?!')
return;
}
else if (mss.length > 1) {
next('Multiple membership found! Stopping.')
return;
}
else {
join.ms = mss[0];
fingerprints.push(join.ms.issuer);
PublicKey.getTheOne(join.ms.issuer, next);
}
},
function (pubk, next){
join.pubkey = pubk;
joinData[join.pubkey.fingerprint] = join;
next();
},
], callback);
}, function(err){
var block = new KeyBlock();
block.version = 1;
block.currency = joinData[fingerprints[0]].ms.currency;
block.number = 0;
// Members merkle
fingerprints.sort();
var tree = merkle(fingerprints, 'sha1').process();
block.membersCount = fingerprints.length;
block.membersRoot = tree.root();
block.membersChanges = [];
fingerprints.forEach(function(fpr){
block.membersChanges.push('+' + fpr);
});
// Public keys
block.publicKeys = [];
_(joinData).values().forEach(function(join){
var key = openpgp.key.readArmored(join.pubkey.raw).keys[0];
var pkData = {
fingerprint: join.pubkey.fingerprint,
packets: base64.encode(key.toPacketlist().write())
};
block.publicKeys.push(pkData);
});
// Memberships
block.memberships = [];
_(joinData).values().forEach(function(join){
var ms = join.ms;
var shortMS = [1, join.pubkey.fingerprint, 'IN', ms.date.timestamp(), ms.userid].join(':');
block.memberships.push(shortMS);
});
// Memberships signatures
block.membershipsSigs = [];
_(joinData).values().forEach(function(join){
var ms = join.ms;
var splits = dos2unix(ms.signature).split('\n');
var signature = "";
var keep = false;
splits.forEach(function(line){
if (keep && !line.match('-----END PGP') && line != '') signature += line + '\n';
if (line == "") keep = true;
});
block.membershipsSigs.push({
fingerprint: join.pubkey.fingerprint,
packets: signature
});
});
done(null, block);
});
};
this.prove = function (block, sigFunc, nbZeros, done) {
var powRegexp = new RegExp('^0{' + nbZeros + '}');
var pow = "", sig = "", raw = "";
var start = new Date().timestamp();
var testsCount = 0;
logger.debug('Generating proof-of-work...');
async.whilst(
function(){ return !pow.match(powRegexp); },
function (next) {
var newTS = new Date().timestamp();
if (newTS == block.timestamp) {
block.nonce++;
} else {
block.nonce = 0;
block.timestamp = newTS;
}
raw = block.getRaw();
sigFunc(raw, function (err, sigResult) {
sig = unix2dos(sigResult);
var full = raw + sig;
pow = full.hash();
testsCount++;
if (testsCount % 100 == 0) process.stdout.write('.');
next();
});
}, function (err) {
console.log(raw);
block.signature = sig;
var end = new Date().timestamp();
var duration = moment.duration((end - start)) + 's';
var testsPerSecond = (testsCount / (end - start)).toFixed(2);
logger.debug('Done: ' + pow + ' in ' + duration + ' (~' + testsPerSecond + ' tests/s)');
done(err, block);
});
};
}
......@@ -13,6 +13,7 @@ var wizard = require('../app/lib/wizard');
var router = require('../app/lib/streams/router');
var multicaster = require('../app/lib/streams/multicaster');
var logger = require('../app/lib/logger')('ucoind');
var signature = require('../app/lib/signature');
var ucoin = require('./..');
function keys (val) {
......@@ -220,6 +221,86 @@ function handleKey (server, key, isManaged, message) {
});
}
program
.command('gen-root [host] [port] [difficulty]')
.description('Tries to generate the root keyblock of the keychain using already received keys & memberships')
.action(service(DO_NOT_LISTEN_HTTP, ucoin.createWOTServer, function (host, port, difficulty, server, conf) {
var Membership = server.conn.model('Membership');
var KeychainService = server.KeychainService;
async.waterfall([
function (next){
if (!host || !port) {
next('usage: gen-root [host] [port]');
return;
}
KeychainService.current(function (err, current) {
if (current) {
next('Local keychain is already started.');
return;
}
else next();
})
},
function (next){
Membership.find({}, next);
},
function (mss, next){
var uids = [];
mss.forEach(function(ms){
uids.push(ms.userid);
});
inquirer.prompt([{
type: "checkbox",
name: "uids",
message: "Initial members of the Web of Trust",
choices: uids,
default: uids[0]
}], function (answers) {
next(null, answers.uids);
});
},
function (uids, next){
if (uids.length == 0) {
next('You must select at least 1 user');
return;
}
KeychainService.generateRoot(uids, next);
},
function (root, next){
var wiz = wizard(server);
async.waterfall([
function (next){
wiz.configOpenpgp(conf, next);
},
function (next){
wiz.configKey(conf, next);
},
function (next){
signature(conf.pgpkey, conf.pgppasswd, conf.openpgpjs, next);
},
function (sigFunc, next){
KeychainService.prove(root, sigFunc, difficulty, next);
},
function (block, next){
var Peer = server.conn.model('Peer');
var peer = new Peer({
endpoints: [['BASIC_MERKLED_API', host, port].join(' ')]
});
// console.log(block.getRaw());
// console.log(block.signature);
multicaster().sendKeyblock(peer, block, next);
},
], next);
},
], function (err) {
if (err) {
logger.error(err);
}
server.disconnect();
process.exit();
});
}));
program
.command('check-config')
.description('Checks the node\'s configuration')
......
......@@ -8,7 +8,8 @@ var plogger = require('./app/lib/logger')('peer');
var flogger = require('./app/lib/logger')('forward');
var slogger = require('./app/lib/logger')('status');
var wlogger = require('./app/lib/logger')('wallet');
var WOT = require('./wotserver');
var WOT = require('./wotserver');
var signature = require('./app/lib/signature');
var parsers = require('./app/lib/streams/parsers/doc');
function PeerServer (dbConf, overrideConf, interceptors, onInit) {
......@@ -178,37 +179,8 @@ function PeerServer (dbConf, overrideConf, interceptors, onInit) {
};
this.createSignFunction = function (conf, done) {
async.waterfall([
function (next) {
if (conf.openpgpjs) {
var pgp = jpgp();
var privateKey = openpgp.key.readArmored(conf.pgpkey).keys[0];
privateKey.decrypt(conf.pgppasswd);
var signingFunc = async.apply(pgp.sign.bind(pgp.sign), privateKey);
next(null, function (message, done) {
jpgp().sign(message, privateKey, done);
});
} else {
var asciiPrivateKey = conf.pgpkey;
var keyring = '~/.gnupg/ucoin_' + that.PeeringService.cert.fingerprint;
logger.debug("Keyring = %s", keyring);
var gnupg = new (require('./app/lib/gnupg'))(asciiPrivateKey, conf.pgppasswd, that.PeeringService.cert.fingerprint, keyring);
gnupg.init(function (err) {
next(err, function (message, done) {
gnupg.sign(message, done);
});
});
}
},
function (signFunc, next){
that.sign = signFunc;
try{
that.sign("some test\nwith line return", next);
} catch(ex){
next("Wrong private key password.");
}
},
], function (err) {
signature(conf.pgpkey, conf.pgppasswd, conf.openpgpjs, function (err, sigFunc) {
that.sign = sigFunc;
done(err);
});
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment