diff --git a/browserid/lib/ca.js b/browserid/lib/ca.js new file mode 100644 index 0000000000000000000000000000000000000000..78be143f98ec69443dc7dc66a3b0ad67c46ef372 --- /dev/null +++ b/browserid/lib/ca.js @@ -0,0 +1,94 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla BrowserID. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Ben Adida <benadida@mozilla.com> + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +// certificate authority + +var jwcert = require('../../lib/jwcrypto/jwcert'), + jws = require('../../lib/jwcrypto/jws'), + configuration = require('../../libs/configuration'), + path = require("path"), + fs = require("fs"); + +function loadSecretKey(name, dir) { + var p = path.join(dir, name + ".secretkey"); + var fileExists = false; + var secret = undefined; + + try{ secret = fs.readFileSync(p).toString(); } catch(e) {}; + + if (secret === undefined) { + return null; + } + + // parse it + // it should be a JSON structure with alg and serialized key + // {alg: <ALG>, value: <SERIALIZED_KEY>} + var key = JSON.parse(secret); + return jws.getByAlg(key.alg).SecretKey.deserialize(key.value); +} + +function loadPublicKey(name, dir) { + var p = path.join(dir, name + ".publickey"); + var fileExists = false; + var secret = undefined; + + try{ secret = fs.readFileSync(p).toString(); } catch(e) {}; + + if (secret === undefined) { + return null; + } + + // parse it + // it should be a JSON structure with alg and serialized key + // {alg: <ALG>, value: <SERIALIZED_KEY>} + var key = JSON.parse(secret); + return jws.getByAlg(key.alg).PublicKey.deserialize(key.value); +} + +var SECRET_KEY = loadSecretKey('root', configuration.get('var_path')); +var PUBLIC_KEY = loadPublicKey('root', configuration.get('var_path')); + +function certify(email, serializedPublicKey) { + return "cert"; +} + +function verifyChain(certChain, serializedPublicKey) { + return true; +} + +// exports, not the key stuff +exports.certify = certify; +exports.verifyChain = verifyChain; diff --git a/browserid/lib/wsapi.js b/browserid/lib/wsapi.js index 43cb858fd26c3489eb066aec19e0c1527b576d09..36665a045036020f51a0db0e6edc02902a93cf03 100644 --- a/browserid/lib/wsapi.js +++ b/browserid/lib/wsapi.js @@ -298,8 +298,10 @@ function setup(app) { // not same account? big fat error if (!sameAccount) return httputils.badRequest(resp, "that email does not belong to you"); + // FIXME: make sure it's truly a public key + // same account, we certify the key - var cert = ca.certify(email, pubkey); + var cert = ca.certify(email, req.body.pubkey); resp.json(cert); }); }); diff --git a/browserid/tests/cert-emails-test.js b/browserid/tests/cert-emails-test.js new file mode 100755 index 0000000000000000000000000000000000000000..fb010398ccdb8d25120668c7aba811ac90cfe1f6 --- /dev/null +++ b/browserid/tests/cert-emails-test.js @@ -0,0 +1,137 @@ +#!/usr/bin/env node + +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla BrowserID. + * + * The Initial Developer of the Original Code is Mozilla. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +require('./lib/test_env.js'); + +const assert = require('assert'), +vows = require('vows'), +start_stop = require('./lib/start-stop.js'), +wsapi = require('./lib/wsapi.js'), +email = require('../lib/email.js'), +ca = require('../lib/ca.js'), +jwcert = require('../../lib/jwcrypto/jwcert'); + +var suite = vows.describe('cert-emails'); + +// disable vows (often flakey?) async error behavior +suite.options.error = false; + +start_stop.addStartupBatches(suite); + +// ever time a new token is sent out, let's update the global +// var 'token' +var token = undefined; +email.setInterceptor(function(email, site, secret) { token = secret; }); + +// INFO: some of these tests are repeat of sync-emails... to set +// things up properly for key certification + +// create a new account via the api with (first address) +suite.addBatch({ + "stage an account": { + topic: wsapi.post('/wsapi/stage_user', { + email: 'syncer@somehost.com', + pass: 'fakepass', + pubkey: 'fakekey', + site:'fakesite.com' + }), + "yields a sane token": function(r, err) { + assert.strictEqual(typeof token, 'string'); + } + } +}); + +suite.addBatch({ + "verifying account ownership": { + topic: function() { + wsapi.get('/wsapi/prove_email_ownership', { token: token }).call(this); + }, + "works": function(r, err) { + assert.equal(r.code, 200); + assert.strictEqual(true, JSON.parse(r.body)); + } + } +}); + +suite.addBatch({ + "calling registration_status after a registration is complete": { + topic: wsapi.get("/wsapi/registration_status"), + "yields a HTTP 200": function (r, err) { + assert.strictEqual(r.code, 200); + }, + "returns a json encoded string - `complete`": function (r, err) { + assert.strictEqual(JSON.parse(r.body), "complete"); + } + } +}); + +var cert_key_url = "/wsapi/cert_key"; + +suite.addBatch({ + "cert key with no parameters": { + topic: wsapi.post(cert_key_url, {}), + "fails with HTTP 400" : function(r, err) { + assert.strictEqual(r.code, 400); + } + }, + "cert key invoked with just an email": { + topic: wsapi.post(cert_key_url, { email: 'syncer@somehost.com' }), + "returns a 400" : function(r, err) { + assert.strictEqual(r.code, 400); + } + }, + "cert key invoked with proper argument": { + topic: wsapi.post(cert_key_url, { email: 'syncer@somehost.com', pubkey: 'fakepubkey' }), + "returns a response with a proper content-type" : function(r, err) { + assert.strictEqual(r.code, 200); + assert.isTrue(r.headers['content-type'].indexOf('application/json; charset=utf-8') > -1); + }, + "returns a proper cert": function(r, err) { + var cert = new jwcert.JWCert(); + cert.parse(r.body); + + assert.isTrue(ca.verifyChain([cert], 'fakepubkey')); + } + } + // NOTE: db-test has more thorough tests of the algorithm behind the sync_emails API +}); + +start_stop.addShutdownBatches(suite); + +// run or export the suite. +if (process.argv[1] === __filename) suite.run(); +else suite.export(module); diff --git a/browserid/tests/set-key-wsapi-test.js b/browserid/tests/set-key-wsapi-test.js index b04579c6860ad7f9d129121b711c07def98245cc..1ab26b7a53b913dbeafc11345aa1e5b458d21689 100755 --- a/browserid/tests/set-key-wsapi-test.js +++ b/browserid/tests/set-key-wsapi-test.js @@ -100,7 +100,7 @@ suite.addBatch({ email: 'setkeyabuser@somehost.com', pubkey: 'fakekey' }), - "fails with a false return0" : function(r, err) { + "fails with a false return" : function(r, err) { assert.strictEqual(r.code, 200); assert.strictEqual(r.body, 'false'); } diff --git a/libs/secrets.js b/libs/secrets.js index b57bf0ea3a0e57fcba6487ec13e118348bec6336..d6cc4db5ee3f652b2afeb8170b816dc1708ec867 100644 --- a/libs/secrets.js +++ b/libs/secrets.js @@ -35,7 +35,8 @@ const path = require('path'), -fs = require('fs'); +fs = require('fs'), +jws = require('../lib/jwcrypto/jws'); exports.generate = function(chars) { var str = ""; @@ -59,3 +60,4 @@ exports.hydrateSecret = function(name, dir) { } return secret; }; +