From c8dbdbf464a0188701149a562cf78ce0dea32f09 Mon Sep 17 00:00:00 2001
From: Lloyd Hilaiel <lloyd@hilaiel.com>
Date: Tue, 1 Nov 2011 22:20:53 -0600
Subject: [PATCH] initial sketch of keysigner - issue #460

---
 bin/keysigner       | 89 +++++++++++++++++++++++++++++++++++++++++++++
 lib/keysigner/ca.js | 82 +++++++++++++++++++++++++++++++++++++++++
 2 files changed, 171 insertions(+)
 create mode 100755 bin/keysigner
 create mode 100644 lib/keysigner/ca.js

diff --git a/bin/keysigner b/bin/keysigner
new file mode 100755
index 000000000..8cead72a2
--- /dev/null
+++ b/bin/keysigner
@@ -0,0 +1,89 @@
+#!/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 ***** */
+
+// I sign keys.  That's what I do.
+
+const
+path = require('path'),
+express = require('express');
+
+// add lib/ to the require path for our own libs
+require.paths.unshift(path.join(__dirname, '..', 'lib'));
+
+const
+config = require('configuration.js'),
+validate = require('validate.js'),
+metrics = require("metrics.js"),
+logger = require("logging.js").logger,
+ca = require('keysigner/ca.js');
+
+// create an express server
+var app = express.createServer();
+
+// our server will log
+app.use(express.logger({
+  format: 'dev',
+  stream: {
+    write: function(x) {
+      logger.info(typeof x === 'string' ? x.trim() : x);
+    }
+  }
+}));
+
+// parse POST bodies
+app.use(express.bodyParser());
+
+// and our single function
+app.post('/wsapi/cert_key', validate(["email", "pubkey"]), function(req, resp) {
+  // parse the pubkey
+  var pk = ca.parsePublicKey(req.body.pubkey);
+
+  // same account, we certify the key
+  // we certify it for a day for now
+  var expiration = new Date();
+  expiration.setTime(new Date().valueOf() + config.get('certificate_validity_ms'));
+  var cert = ca.certify(req.body.email, pk, expiration);
+
+  resp.writeHead(200, {'Content-Type': 'text/plain'});
+  resp.write(cert);
+  resp.end();
+});
+
+var bindTo = config.get('bind_to');
+app.listen(bindTo.port, bindTo.host, function() {
+  logger.info("running on http://" + app.address().address + ":" + app.address().port);
+});
diff --git a/lib/keysigner/ca.js b/lib/keysigner/ca.js
new file mode 100644
index 000000000..ab07a592b
--- /dev/null
+++ b/lib/keysigner/ca.js
@@ -0,0 +1,82 @@
+/* ***** 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('jwcrypto/jwcert'),
+    jwk = require('jwcrypto/jwk'),
+    jws = require('jwcrypto/jws'),
+    configuration = require('configuration'),
+    path = require("path"),
+    fs = require("fs");
+
+var HOSTNAME = configuration.get('hostname');
+
+function parsePublicKey(serializedPK) {
+  return jwk.PublicKey.deserialize(serializedPK);
+}
+
+function parseCert(serializedCert) {
+  var cert = new jwcert.JWCert();
+  cert.parse(serializedCert);
+  return cert;
+}
+
+function certify(email, publicKey, expiration) {
+  if (expiration == null)
+    throw "expiration cannot be null";
+  return new jwcert.JWCert(HOSTNAME, expiration, publicKey, {email: email}).sign(configuration.get('secret_key'));
+}
+
+function verifyChain(certChain, cb) {
+  // raw certs
+  return jwcert.JWCert.verifyChain(
+    certChain, new Date(),
+    function(issuer, next) {
+      // for now we only do browserid.org issued keys
+      if (issuer != HOSTNAME)
+        return next(null);
+
+      next(exports.PUBLIC_KEY);
+    }, cb);
+}
+
+// exports, not the key stuff
+exports.certify = certify;
+exports.verifyChain = verifyChain;
+exports.parsePublicKey = parsePublicKey;
+exports.parseCert = parseCert;
+exports.PUBLIC_KEY = configuration.get('public_key');
-- 
GitLab