Commit 1c3207db authored by Cédric Moreau's avatar Cédric Moreau

Generate & accept pulse keyblock (keyblock#1) - needs link persistance

parent 0217fc2a
......@@ -30,6 +30,46 @@ module.exports = {
packets = readKeys[0].toPacketlist();
}
return new KeyHelper(packets);
},
toPacketlist: function (encodedPackets){
var packets = new PacketList();
packets.read(base64.decode(encodedPackets));
return packets;
},
hasOnlySubkeyMaterial: function (encodedPackets){
var packets = new PacketList();
packets.read(base64.decode(encodedPackets));
var subkeyMaterial = packets.filterByTag(openpgp.enums.packet.publicSubkey);
var signatures = packets.filterByTag(openpgp.enums.packet.signature);
signatures.forEach(function(packet){
if (packet.signatureType == openpgp.enums.signature.subkey_binding) {
subkeyMaterial.push(packet);
}
});
return subkeyMaterial.length == packets.length;
},
hasOnlyCertificationMaterial: function (encodedPackets){
var packets = new PacketList();
packets.read(base64.decode(encodedPackets));
var certifMaterial = new PacketList();
var signatures = packets.filterByTag(openpgp.enums.packet.signature);
signatures.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
];
if (~signaturesToKeep.indexOf(packet.signatureType)) {
certifMaterial.push(packet);
}
});
return certifMaterial.length == packets.length;
}
};
......@@ -45,10 +85,14 @@ function KeyHelper (packetList) {
return key && key.primaryKey && key.primaryKey.getFingerprint().toUpperCase();
};
this.hasPrimaryKey = function (){
this.getPrimaryKey = function (){
return key && key.primaryKey;
};
this.hasPrimaryKey = function (){
return this.getPrimaryKey() != null;
};
this.getArmored = function (){
return key.armor();
};
......
......@@ -190,7 +190,7 @@ module.exports = new function() {
raw += "CertificationPackets:\n" + json.certpackets;
if (!raw.match(/\n$/))
raw += '\n';
if (json.membership.membership) {
if (json.membership && json.membership.membership) {
raw += "Membership:\n";
raw += json.membership.membership + "\n";
raw += json.membership.signature;
......
......@@ -10,6 +10,7 @@ var KeySchema = new Schema({
member: { type: Boolean, default: false },
kick: { type: Boolean, default: false },
eligible: { type: Boolean, default: false },
distanced: [String], // Array of distanced keys fingerprints
certifs: [String], // Array of md5 hashes of packets to integrate
subkeys: [String], // Array of md5 hashes of packets to integrate
created: { type: Date, default: Date.now },
......@@ -131,9 +132,9 @@ KeySchema.statics.removeMember = function(fingerprint, done){
});
};
KeySchema.statics.setKicked = function(fingerprint, done){
KeySchema.statics.setKicked = function(fingerprint, distancedKeys, done){
var Key = this.model('Key');
Key.update({ fingerprint: fingerprint }, { kick: true }, function (err) {
Key.update({ fingerprint: fingerprint }, { kick: distancedKeys.length > 0, distanced: distancedKeys }, function (err) {
done(err);
});
};
......
......@@ -22,6 +22,7 @@ var KeyBlockSchema = new Schema({
keysChanges: Array,
signature: String,
hash: String,
issuer: String,
created: { type: Date, default: Date.now },
updated: { type: Date, default: Date.now }
});
......@@ -151,24 +152,26 @@ KeyBlockSchema.methods = {
var notFoundMembership = 0;
var mss = {};
this.keysChanges.forEach(function(kc){
var shortSIG = kc.membership.signature;
var shortMS = kc.membership.membership;
// Membership content
var sp = shortMS.split(':');
// Signature
var signature = '-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v1\n\n';
signature += shortSIG;
signature += '-----END PGP SIGNATURE-----\n';
var ms = {
version: sp[0],
keyID: sp[1].substring(24),
fingerprint: sp[1],
membership: sp[2],
date: new Date(parseInt(sp[3])*1000),
userid: sp[4],
signature: signature
};
mss[ms.keyID] = ms;
if (kc.membership) {
var shortSIG = kc.membership.signature;
var shortMS = kc.membership.membership;
// Membership content
var sp = shortMS.split(':');
// Signature
var signature = '-----BEGIN PGP SIGNATURE-----\nVersion: GnuPG v1\n\n';
signature += shortSIG;
signature += '-----END PGP SIGNATURE-----\n';
var ms = {
version: sp[0],
keyID: sp[1].substring(24),
fingerprint: sp[1],
membership: sp[2],
date: new Date(parseInt(sp[3])*1000),
userid: sp[4],
signature: signature
};
mss[ms.keyID] = ms;
}
});
return {
'notFoundMembership': notFoundMembership,
......
......@@ -33,12 +33,126 @@ LinkSchema.statics.obsoletes = function (minTimestamp, done) {
/**
* Mark as obsolete the links with an age equal to or below a given date
**/
LinkSchema.statics.isOver3StepsOfAMember = function (theMember, members, done) {
var fpr = theMember.fingerprint;
LinkSchema.statics.isStillOver3Steps = function (keyToKick, newLinks, done) {
var Link = this.model('Link');
var fpr = keyToKick.fingerprint;
var newCertifiers = newLinks[fpr];
var remainingKeys = [];
keyToKick.distanced.forEach(function(m){
remainingKeys.push(m);
});
// Without self
remainingKeys = _(remainingKeys).difference([fpr]);
var dist1Links = [];
async.waterfall([
function (next){
// Remove direct links (dist 1)
remainingKeys = _(remainingKeys).difference(newCertifiers);
next();
},
function (next) {
if (remainingKeys.length > 0) {
// Look for 1 distance links
Link.find({ target: fpr, obsolete: false }, function (err, links) {
dist1Links = [];
links.forEach(function(lnk){
dist1Links.push(lnk.fingerprint);
});
// Add new certifiers as distance 1 links
dist1Links = _(dist1Links.concat(newCertifiers)).uniq();
next(err);
});
}
else next();
},
function (next){
// Remove distance 2 links (those for whom new links make 1 distance)
var found = [];
if (remainingKeys.length > 0) {
async.forEachSeries(remainingKeys, function(member, callback){
// Exists distance 1 link?
async.detect(dist1Links, function (dist1member, callbackDist1) {
// dist1member signed 'fpr', so here we look for (member => dist1member => fpr sigchain)
Link.find({ source: member, target: dist1member, obsolete: false }, function (err, links) {
if (links && links.length > 0) {
found.push(member);
callbackDist1(true);
}
else callbackDist1(false);
});
}, function (detected) {
if (detected)
found.push(member);
callback();
});
}, function(err){
remainingKeys = _(remainingKeys).difference(found);
next(err);
});
}
else next();
},
function (next){
// Remove distance 3 links (those for whom new links make 2 distance)
var found = [];
if (remainingKeys.length > 0) {
async.forEachSeries(remainingKeys, function(member, callback){
var dist2Links = [];
async.waterfall([
function (next){
// Step 1. Detect distance 1 members from current member (potential dist 2 from 'fpr')
Link.find({ source: member, obsolete: false }, function (err, links) {
dist2Links = [];
links.forEach(function(lnk){
dist2Links.push(lnk.fingerprint);
});
next(err);
});
},
function (next){
// Step 2. Detect links between distance 2 & distance 1 members
async.detect(dist2Links, function (dist2member, callbackDist2) {
// Exists distance 1 link?
async.detect(dist1Links, function (dist1member, callbackDist1) {
// dist1member signed 'fpr', so here we look for (member => dist1member => fpr sigchain)
Link.find({ source: dist2member, target: dist1member, obsolete: false }, function (err, links) {
if (links && links.length > 0) {
callbackDist1(true);
}
else callbackDist1(false);
});
}, callbackDist2);
}, function (detected) {
if (detected)
found.push(member);
callback();
});
},
], callback);
}, function(err){
remainingKeys = _(remainingKeys).difference(found);
next(err);
});
}
else next();
},
], function (err) {
done(err, remainingKeys);
});
}
/**
* Mark as obsolete the links with an age equal to or below a given date
**/
LinkSchema.statics.isOver3StepsOfAMember = function (key, members, done) {
var fpr = key.fingerprint;
var remainingKeys = [];
members.forEach(function(m){
remainingKeys.push(m.fingerprint);
});
// Without self
remainingKeys = _(remainingKeys).difference([fpr]);
var Link = this.model('Link');
var dist1Links = [];
async.waterfall([
......@@ -55,7 +169,7 @@ LinkSchema.statics.isOver3StepsOfAMember = function (theMember, members, done) {
callback();
});
}, function(err){
remainingKeys = _(remainingKeys).without(found);
remainingKeys = _(remainingKeys).difference(found);
next(err);
});
}
......@@ -65,7 +179,10 @@ LinkSchema.statics.isOver3StepsOfAMember = function (theMember, members, done) {
if (remainingKeys.length > 0) {
// Look for 1 distance links
Link.find({ target: fpr, obsolete: false }, function (err, links) {
dist1Links = links;
dist1Links = [];
links.forEach(function(lnk){
dist1Links.push(lnk.fingerprint);
});
next(err);
});
}
......@@ -92,7 +209,7 @@ LinkSchema.statics.isOver3StepsOfAMember = function (theMember, members, done) {
callback();
});
}, function(err){
remainingKeys = _(remainingKeys).without(found);
remainingKeys = _(remainingKeys).difference(found);
next(err);
});
}
......@@ -109,7 +226,10 @@ LinkSchema.statics.isOver3StepsOfAMember = function (theMember, members, done) {
function (next){
// Step 1. Detect distance 1 members from current member (potential dist 2 from 'fpr')
Link.find({ source: member, obsolete: false }, function (err, links) {
dist2Links = links;
dist2Links = [];
links.forEach(function(lnk){
dist2Links.push(lnk.fingerprint);
});
next(err);
});
},
......@@ -134,15 +254,14 @@ LinkSchema.statics.isOver3StepsOfAMember = function (theMember, members, done) {
},
], callback);
}, function(err){
remainingKeys = _(remainingKeys).without(found);
remainingKeys = _(remainingKeys).difference(found);
next(err);
});
}
else next();
},
], function (err) {
var nbOutdistanced = remainingKeys.length;
done(err, nbOutdistanced);
done(err, remainingKeys);
});
}
......
var jpgp = require('../lib/jpgp');
var mongoose = require('mongoose');
var async = require('async');
var sha1 = require('sha1');
var _ = require('underscore');
var Schema = mongoose.Schema;
var openpgp = require('openpgp');
var KHelper = require('../lib/keyhelper');
var base64 = require('../lib/base64');
var md5 = require('../lib/md5');
var unix2dos = require('../lib/unix2dos');
var parsers = require('../lib/streams/parsers/doc');
var logger = require('../lib/logger')('pubkey');
var jpgp = require('../lib/jpgp');
var mongoose = require('mongoose');
var async = require('async');
var sha1 = require('sha1');
var _ = require('underscore');
var Schema = mongoose.Schema;
var openpgp = require('openpgp');
var KHelper = require('../lib/keyhelper');
var base64 = require('../lib/base64');
var md5 = require('../lib/md5');
var unix2dos = require('../lib/unix2dos');
var parsers = require('../lib/streams/parsers/doc');
var keyhelper = require('../lib/keyhelper');
var logger = require('../lib/logger')('pubkey');
var PublicKeySchema = new Schema({
raw: String,
fingerprint: { type: String, unique: true },
subkeys: [String], // Array of keyId
hashes: [String], // Array of ASCII armor representation fingerprints
registered: [String], // Array of md5 hashes of the known packets (base64 encoded)
eligible: [String], // Array of md5 hashes of the unknown packets (base64 encoded)
name: String,
email: String,
comment: String,
......@@ -86,6 +85,14 @@ PublicKeySchema.methods = {
return this.raw;
},
getKey: function () {
return keyhelper.fromArmored(this.raw);
},
getUserID: function () {
return [this.name, "(" + this.comment + ")", "<" + this.email + ">"].join(' ');
},
getWritablePacketsWithoutOtherCertifications: function (){
var wrappedKey = KHelper.fromArmored(this.raw);
var packets = new openpgp.packet.List();
......@@ -108,6 +115,21 @@ PublicKeySchema.methods = {
});
return packets;
},
getCertificationsFromMD5List: function (md5array){
var packets = new openpgp.packet.List();
var key = keyhelper.fromArmored(this.raw);
var hashedCertifs = key.getHashedCertifPackets();
md5array.forEach(function(md5hash){
if (hashedCertifs[md5hash]) {
var decoded = base64.decode(hashedCertifs[md5hash]);
var otherList = new openpgp.packet.List();
otherList.read(decoded);
packets.concat(otherList);
}
});
return packets;
},
};
PublicKeySchema.statics.getTheOne = function (keyID, done) {
......
This diff is collapsed.
......@@ -323,6 +323,41 @@ program
});
}));
program
.command('gen-pulse [host] [port] [difficulty]')
.description('Tries to generate the 2nd keyblock of the keychain, aka "pulse" keyblock')
.action(service(DO_NOT_LISTEN_HTTP, ucoin.createWOTServer, function (host, port, difficulty, server, conf) {
var KeychainService = server.KeychainService;
async.waterfall([
function (next){
KeychainService.generatePulse(next);
},
function (block, 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){
proveAndSend(server, block, sigFunc, difficulty, host, port, next);
},
], next);
},
], function (err) {
if (err) {
logger.error(err);
}
server.disconnect();
process.exit();
});
}));
function proveAndSend (server, block, sigFunc, difficulty, host, port, done) {
var KeychainService = server.KeychainService;
async.waterfall([
......@@ -334,7 +369,7 @@ function proveAndSend (server, block, sigFunc, difficulty, host, port, done) {
var peer = new Peer({
endpoints: [['BASIC_MERKLED_API', host, port].join(' ')]
});
// console.log(block.getRaw());
console.log(block.getRaw());
// console.log(block.signature);
multicaster().sendKeyblock(peer, block, next);
},
......
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