2010-12-09 18:13:58 -08:00
|
|
|
Cu.import("resource://services-sync/log4moz.js");
|
|
|
|
Cu.import("resource://services-sync/jpakeclient.js");
|
|
|
|
Cu.import("resource://services-sync/constants.js");
|
|
|
|
Cu.import("resource://services-sync/auth.js");
|
|
|
|
Cu.import("resource://services-sync/util.js");
|
|
|
|
|
|
|
|
const JPAKE_LENGTH_SECRET = 8;
|
|
|
|
const JPAKE_LENGTH_CLIENTID = 256;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Simple server.
|
|
|
|
*/
|
|
|
|
|
|
|
|
function check_headers(request) {
|
|
|
|
// There shouldn't be any Basic auth
|
|
|
|
do_check_false(request.hasHeader("Authorization"));
|
|
|
|
|
|
|
|
// Ensure key exchange ID is set and the right length
|
|
|
|
do_check_true(request.hasHeader("X-KeyExchange-Id"));
|
|
|
|
do_check_eq(request.getHeader("X-KeyExchange-Id").length,
|
|
|
|
JPAKE_LENGTH_CLIENTID);
|
|
|
|
}
|
|
|
|
|
|
|
|
function new_channel() {
|
|
|
|
// Create a new channel and register it with the server.
|
|
|
|
let cid = Math.floor(Math.random() * 10000);
|
|
|
|
while (channels[cid])
|
|
|
|
cid = Math.floor(Math.random() * 10000);
|
|
|
|
let channel = channels[cid] = new ServerChannel();
|
|
|
|
server.registerPathHandler("/" + cid, channel.handler());
|
|
|
|
return cid;
|
|
|
|
}
|
|
|
|
|
|
|
|
let server;
|
|
|
|
let channels = {}; // Map channel -> ServerChannel object
|
|
|
|
function server_new_channel(request, response) {
|
|
|
|
check_headers(request);
|
|
|
|
let cid = new_channel();
|
|
|
|
let body = JSON.stringify("" + cid);
|
|
|
|
response.setStatusLine(request.httpVersion, 200, "OK");
|
|
|
|
response.bodyOutputStream.write(body, body.length);
|
|
|
|
}
|
|
|
|
|
|
|
|
let error_report;
|
|
|
|
function server_report(request, response) {
|
|
|
|
check_headers(request);
|
|
|
|
|
|
|
|
if (request.hasHeader("X-KeyExchange-Log"))
|
|
|
|
error_report = request.getHeader("X-KeyExchange-Log");
|
|
|
|
|
|
|
|
if (request.hasHeader("X-KeyExchange-Cid")) {
|
|
|
|
let cid = request.getHeader("X-KeyExchange-Cid");
|
|
|
|
let channel = channels[cid];
|
|
|
|
if (channel)
|
|
|
|
channel.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
response.setStatusLine(request.httpVersion, 200, "OK");
|
|
|
|
}
|
|
|
|
|
|
|
|
function ServerChannel() {
|
|
|
|
this.data = "{}";
|
|
|
|
this.getCount = 0;
|
|
|
|
}
|
|
|
|
ServerChannel.prototype = {
|
|
|
|
|
|
|
|
GET: function GET(request, response) {
|
|
|
|
if (!this.data) {
|
|
|
|
response.setStatusLine(request.httpVersion, 404, "Not Found");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (request.hasHeader("If-None-Match")) {
|
|
|
|
let etag = request.getHeader("If-None-Match");
|
|
|
|
if (etag == this._etag) {
|
|
|
|
response.setStatusLine(request.httpVersion, 304, "Not Modified");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
response.setStatusLine(request.httpVersion, 200, "OK");
|
|
|
|
response.bodyOutputStream.write(this.data, this.data.length);
|
|
|
|
|
|
|
|
// Automatically clear the channel after 6 successful GETs.
|
|
|
|
this.getCount += 1;
|
|
|
|
if (this.getCount == 6)
|
|
|
|
this.clear();
|
|
|
|
},
|
|
|
|
|
|
|
|
PUT: function PUT(request, response) {
|
|
|
|
this.data = readBytesFromInputStream(request.bodyInputStream);
|
|
|
|
this._etag = '"' + Utils.sha1(this.data) + '"';
|
|
|
|
response.setHeader("ETag", this._etag);
|
|
|
|
response.setStatusLine(request.httpVersion, 200, "OK");
|
|
|
|
},
|
|
|
|
|
|
|
|
clear: function clear() {
|
|
|
|
delete this.data;
|
|
|
|
},
|
|
|
|
|
|
|
|
handler: function handler() {
|
|
|
|
let self = this;
|
|
|
|
return function(request, response) {
|
|
|
|
check_headers(request);
|
|
|
|
let method = self[request.method];
|
|
|
|
return method.apply(self, arguments);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const DATA = {"msg": "eggstreamly sekrit"};
|
|
|
|
const POLLINTERVAL = 50;
|
|
|
|
|
|
|
|
function run_test() {
|
2010-12-09 22:53:08 -08:00
|
|
|
if (DISABLE_TESTS_BUG_618233)
|
|
|
|
return;
|
|
|
|
|
2010-12-09 18:13:58 -08:00
|
|
|
Svc.Prefs.set("jpake.serverURL", "http://localhost:8080/");
|
|
|
|
Svc.Prefs.set("jpake.pollInterval", POLLINTERVAL);
|
|
|
|
Svc.Prefs.set("jpake.maxTries", 5);
|
|
|
|
Svc.Prefs.set("jpake.firstMsgMaxTries", 5);
|
|
|
|
// Ensure clean up
|
|
|
|
Svc.Obs.add("profile-before-change", function() {
|
|
|
|
Svc.Prefs.resetBranch("");
|
|
|
|
});
|
|
|
|
|
|
|
|
// Ensure PSM is initialized.
|
|
|
|
Cc["@mozilla.org/psm;1"].getService(Ci.nsISupports);
|
|
|
|
|
|
|
|
// Simulate Sync setup with a default authenticator in place. We
|
|
|
|
// want to make sure the J-PAKE requests don't include those data.
|
|
|
|
Auth.defaultAuthenticator = new BasicAuthenticator(
|
|
|
|
new Identity("Some Realm", "johndoe"));
|
|
|
|
|
|
|
|
server = httpd_setup({"/new_channel": server_new_channel,
|
|
|
|
"/report": server_report});
|
|
|
|
function tearDown() {
|
|
|
|
server.stop(do_test_finished);
|
|
|
|
}
|
|
|
|
|
|
|
|
initTestLogging("Trace");
|
|
|
|
|
|
|
|
do_test_pending();
|
|
|
|
Utils.asyncChain(test_success_receiveNoPIN,
|
|
|
|
test_firstMsgMaxTries,
|
|
|
|
test_wrongPIN,
|
|
|
|
test_abort_receiver,
|
|
|
|
test_abort_sender,
|
|
|
|
test_wrongmessage,
|
|
|
|
test_error_channel,
|
|
|
|
test_error_network,
|
|
|
|
tearDown
|
|
|
|
)();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function test_success_receiveNoPIN(next) {
|
|
|
|
_("Test a successful exchange started by receiveNoPIN().");
|
|
|
|
|
|
|
|
let snd = new JPAKEClient({
|
|
|
|
displayPIN: function displayPIN() {
|
|
|
|
do_throw("displayPIN shouldn't have been called!");
|
|
|
|
},
|
|
|
|
onAbort: function onAbort(error) {
|
|
|
|
do_throw("Shouldn't have aborted!" + error);
|
|
|
|
},
|
|
|
|
onComplete: function onComplete() {}
|
|
|
|
});
|
|
|
|
|
|
|
|
let rec = new JPAKEClient({
|
|
|
|
displayPIN: function displayPIN(pin) {
|
|
|
|
_("Received PIN " + pin + ". Entering it in the other computer...");
|
|
|
|
this.cid = pin.slice(JPAKE_LENGTH_SECRET);
|
|
|
|
Utils.delay(function() { snd.sendWithPIN(pin, DATA); }, 0,
|
|
|
|
this, "_timer");
|
|
|
|
},
|
|
|
|
onAbort: function onAbort(error) {
|
|
|
|
do_throw("Shouldn't have aborted! " + error);
|
|
|
|
},
|
|
|
|
onComplete: function onComplete(a) {
|
|
|
|
// Ensure channel was cleared, no error report.
|
|
|
|
do_check_eq(channels[this.cid].data, undefined);
|
|
|
|
do_check_eq(error_report, undefined);
|
|
|
|
next();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
rec.receiveNoPIN();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function test_firstMsgMaxTries(next) {
|
|
|
|
_("Test abort when sender doesn't upload anything.");
|
|
|
|
|
|
|
|
let rec = new JPAKEClient({
|
|
|
|
displayPIN: function displayPIN(pin) {
|
|
|
|
_("Received PIN " + pin + ". Doing nothing...");
|
|
|
|
this.cid = pin.slice(JPAKE_LENGTH_SECRET);
|
|
|
|
},
|
|
|
|
onAbort: function onAbort(error) {
|
|
|
|
do_check_eq(error, JPAKE_ERROR_TIMEOUT);
|
|
|
|
// Ensure channel was cleared, error report was sent.
|
|
|
|
do_check_eq(channels[this.cid].data, undefined);
|
|
|
|
do_check_eq(error_report, JPAKE_ERROR_TIMEOUT);
|
|
|
|
error_report = undefined;
|
|
|
|
next();
|
|
|
|
},
|
|
|
|
onComplete: function onComplete() {
|
|
|
|
do_throw("Shouldn't have completed! ");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
rec.receiveNoPIN();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function test_wrongPIN(next) {
|
|
|
|
_("Test abort when PINs don't match.");
|
|
|
|
|
|
|
|
let snd = new JPAKEClient({
|
|
|
|
displayPIN: function displayPIN() {
|
|
|
|
do_throw("displayPIN shouldn't have been called!");
|
|
|
|
},
|
|
|
|
onAbort: function onAbort(error) {
|
|
|
|
do_check_eq(error, JPAKE_ERROR_KEYMISMATCH);
|
|
|
|
do_check_eq(error_report, JPAKE_ERROR_KEYMISMATCH);
|
|
|
|
error_report = undefined;
|
|
|
|
},
|
|
|
|
onComplete: function onComplete() {
|
|
|
|
do_throw("Shouldn't have completed!");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let rec = new JPAKEClient({
|
|
|
|
displayPIN: function displayPIN(pin) {
|
|
|
|
this.cid = pin.slice(JPAKE_LENGTH_SECRET);
|
|
|
|
let secret = pin.slice(0, JPAKE_LENGTH_SECRET);
|
|
|
|
secret = [char for each (char in secret)].reverse().join("");
|
|
|
|
let new_pin = secret + this.cid;
|
|
|
|
_("Received PIN " + pin + ", but I'm entering " + new_pin);
|
|
|
|
|
|
|
|
Utils.delay(function() { snd.sendWithPIN(new_pin, DATA); }, 0,
|
|
|
|
this, "_timer");
|
|
|
|
},
|
|
|
|
onAbort: function onAbort(error) {
|
|
|
|
do_check_eq(error, JPAKE_ERROR_NODATA);
|
|
|
|
// Ensure channel was cleared.
|
|
|
|
do_check_eq(channels[this.cid].data, undefined);
|
|
|
|
next();
|
|
|
|
},
|
|
|
|
onComplete: function onComplete() {
|
|
|
|
do_throw("Shouldn't have completed! ");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
rec.receiveNoPIN();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function test_abort_receiver(next) {
|
|
|
|
_("Test user abort on receiving side.");
|
|
|
|
|
|
|
|
let rec = new JPAKEClient({
|
|
|
|
onComplete: function onComplete(data) {
|
|
|
|
do_throw("onComplete shouldn't be called.");
|
|
|
|
},
|
|
|
|
onAbort: function onAbort(error) {
|
|
|
|
// Manual abort = no error
|
|
|
|
do_check_eq(error, undefined);
|
|
|
|
// Ensure channel was cleared, no error report.
|
|
|
|
do_check_eq(channels[this.cid].data, undefined);
|
|
|
|
do_check_eq(error_report, undefined);
|
|
|
|
next();
|
|
|
|
},
|
|
|
|
displayPIN: function displayPIN(pin) {
|
|
|
|
this.cid = pin.slice(JPAKE_LENGTH_SECRET);
|
|
|
|
Utils.delay(function() { rec.abort(); }, 0, this, "_timer");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
rec.receiveNoPIN();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function test_abort_sender(next) {
|
|
|
|
_("Test user abort on sending side.");
|
|
|
|
|
|
|
|
let snd = new JPAKEClient({
|
|
|
|
displayPIN: function displayPIN() {
|
|
|
|
do_throw("displayPIN shouldn't have been called!");
|
|
|
|
},
|
|
|
|
onAbort: function onAbort(error) {
|
|
|
|
// Manual abort == no error.
|
|
|
|
do_check_eq(error, undefined);
|
|
|
|
},
|
|
|
|
onComplete: function onComplete() {
|
|
|
|
do_throw("Shouldn't have completed!");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
let rec = new JPAKEClient({
|
|
|
|
onComplete: function onComplete(data) {
|
|
|
|
do_throw("onComplete shouldn't be called.");
|
|
|
|
},
|
|
|
|
onAbort: function onAbort(error) {
|
|
|
|
do_check_eq(error, JPAKE_ERROR_NODATA);
|
|
|
|
// Ensure channel was cleared, no error report.
|
|
|
|
do_check_eq(channels[this.cid].data, undefined);
|
|
|
|
do_check_eq(error_report, undefined);
|
|
|
|
next();
|
|
|
|
},
|
|
|
|
displayPIN: function displayPIN(pin) {
|
|
|
|
_("Received PIN " + pin + ". Entering it in the other computer...");
|
|
|
|
this.cid = pin.slice(JPAKE_LENGTH_SECRET);
|
|
|
|
Utils.delay(function() { snd.sendWithPIN(pin, DATA); }, 0,
|
|
|
|
this, "_timer");
|
|
|
|
Utils.delay(function() { snd.abort(); }, POLLINTERVAL,
|
|
|
|
this, "_abortTimer");
|
|
|
|
}
|
|
|
|
});
|
|
|
|
rec.receiveNoPIN();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function test_wrongmessage(next) {
|
|
|
|
let cid = new_channel();
|
|
|
|
channels[cid].data = JSON.stringify({type: "receiver2", payload: {}});
|
|
|
|
let snd = new JPAKEClient({
|
|
|
|
onComplete: function onComplete(data) {
|
|
|
|
do_throw("onComplete shouldn't be called.");
|
|
|
|
},
|
|
|
|
onAbort: function onAbort(error) {
|
|
|
|
do_check_eq(error, JPAKE_ERROR_WRONGMESSAGE);
|
|
|
|
next();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
snd.sendWithPIN("01234567" + cid, DATA);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function test_error_channel(next) {
|
|
|
|
Svc.Prefs.set("jpake.serverURL", "http://localhost:12345/");
|
|
|
|
|
|
|
|
let rec = new JPAKEClient({
|
|
|
|
onComplete: function onComplete(data) {
|
|
|
|
do_throw("onComplete shouldn't be called.");
|
|
|
|
},
|
|
|
|
onAbort: function onAbort(error) {
|
|
|
|
do_check_eq(error, JPAKE_ERROR_CHANNEL);
|
|
|
|
Svc.Prefs.reset("jpake.serverURL");
|
|
|
|
next();
|
|
|
|
},
|
|
|
|
displayPIN: function displayPIN(pin) {}
|
|
|
|
});
|
|
|
|
rec.receiveNoPIN();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
function test_error_network(next) {
|
|
|
|
Svc.Prefs.set("jpake.serverURL", "http://localhost:12345/");
|
|
|
|
|
|
|
|
let snd = new JPAKEClient({
|
|
|
|
onComplete: function onComplete(data) {
|
|
|
|
do_throw("onComplete shouldn't be called.");
|
|
|
|
},
|
|
|
|
onAbort: function onAbort(error) {
|
|
|
|
do_check_eq(error, JPAKE_ERROR_NETWORK);
|
|
|
|
Svc.Prefs.reset("jpake.serverURL");
|
|
|
|
next();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
snd.sendWithPIN("0123456789ab", DATA);
|
|
|
|
}
|