├── .gitignore ├── .awsbox.json ├── .travis.yml ├── LICENSE ├── server ├── etc │ ├── aws.js │ ├── dev.js │ └── default.js ├── views │ ├── index.jade │ ├── prov_success.jade │ ├── prov_failure.jade │ ├── auth_failure.jade │ ├── lint.jade │ └── layout.jade ├── lib │ ├── substitution-middleware.js │ ├── config.js │ ├── logging.js │ ├── well-known-parser.js │ ├── check-primary-support.js │ └── primary.js ├── static │ ├── authentication_api.js │ ├── provisioning_api.js │ └── bidbundle.js ├── test │ └── resources-available-test.js └── start.js ├── .jshintrc ├── wercker.yml ├── README.md └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | cert 3 | 4 | -------------------------------------------------------------------------------- /.awsbox.json: -------------------------------------------------------------------------------- 1 | { 2 | "processes": [ "./server/start.js" ], 3 | "env": { 4 | "NODE_ENV": "aws" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - 0.8 5 | 6 | notifications: 7 | email: 8 | - shane@shanetomlinson.com 9 | - stomlinson@mozilla.com 10 | 11 | 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This Source Code Form is subject to the terms of the Mozilla Public 2 | License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 | You can obtain one at http://mozilla.org/MPL/2.0/. 4 | -------------------------------------------------------------------------------- /server/etc/aws.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const _ = require('underscore'); 6 | 7 | module.exports = Object.create(require('./default')); 8 | _.extend(module.exports, { 9 | }); 10 | -------------------------------------------------------------------------------- /server/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block title 4 | title checkmyidp.org - Mozilla Persona Identity Provider (IdP) Linter 5 | 6 | block content 7 | form(action="/lint", method="get") 8 | label(for="idp_url") Identity Provider URL: 9 | input(type="text", placeholder="eyedee.me", name="idp_url", autofocus="autofocus", required="required")#idp_url 10 | button Check IdP 11 | 12 | -------------------------------------------------------------------------------- /server/etc/dev.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const _ = require('underscore'); 6 | 7 | module.exports = Object.create(require('./default')); 8 | _.extend(module.exports, { 9 | public_url: "http://" + module.exports.host + ":" + module.exports.port 10 | }); 11 | 12 | -------------------------------------------------------------------------------- /server/views/prov_success.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block title 4 | title checkmyidp.org - #{domain} authentication and provisioning page checks - Mozilla Persona Identity Provider (IdP) Linter 5 | 6 | block heading 7 | h3 #{domain} results 8 | 9 | block content 10 | 11 | h3 Success! 12 | p #{domain} looks like it is a valid Persona identity provider! 13 | 14 | nav 15 | ul 16 | li 17 | a(href="#{urls.retry}") Check again 18 | li 19 | a(href="/") Try another domain 20 | 21 | -------------------------------------------------------------------------------- /server/etc/default.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * The default configuration. Values are overridden by environment 7 | * specific configuration. 8 | */ 9 | 10 | module.exports = { 11 | // port to listen on 12 | host: process.env['IP_ADDRESS'] || '127.0.0.1', 13 | port: process.env['PORT'] || 3000, 14 | public_url: "https://checkmyidp.org" 15 | }; 16 | 17 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "passfail": false, 3 | "maxerr": 100, 4 | "node": true, 5 | "forin": false, 6 | "boss": true, 7 | "noarg": true, 8 | "undef": true, 9 | "unused": true, 10 | "browser": true, 11 | "laxbreak": true, 12 | "laxcomma": true, 13 | "eqeqeq": true, 14 | "eqnull": true, 15 | "expr": true, 16 | "indent": 2, 17 | "white": false, 18 | "predef": [ 19 | "exports", 20 | "require", 21 | "process" 22 | ], 23 | "es5": true, 24 | "esnext": true, 25 | "shadow": false, 26 | "supernew": false, 27 | "strict": false 28 | } 29 | -------------------------------------------------------------------------------- /wercker.yml: -------------------------------------------------------------------------------- 1 | box: wercker/nodejs 2 | # Build definition 3 | build: 4 | # The steps that will be executed on build 5 | steps: 6 | # A step that executes `npm install` command 7 | - npm-install 8 | # A step that executes `npm test` command 9 | - npm-test 10 | 11 | # A custom script step, name value is used in the UI 12 | # and the code value contains the command that get executed 13 | - script: 14 | name: echo nodejs information 15 | code: | 16 | echo "node version $(node -v) running" 17 | echo "npm version $(npm -v) running" 18 | -------------------------------------------------------------------------------- /server/lib/substitution-middleware.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const postprocess = require('postprocess'); 6 | 7 | exports.setup = function(config) { 8 | var from = new RegExp(config.from, 'g'); 9 | var to = config.to; 10 | 11 | return postprocess(function(req, buffer) { 12 | var text = String(buffer); 13 | var updated = text.replace(from, to); 14 | return updated; 15 | }); 16 | }; 17 | 18 | -------------------------------------------------------------------------------- /server/views/prov_failure.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block title 4 | title checkmyidp.org - #{domain} provisioning page failure - Mozilla Persona Identity Provider (IdP) Linter 5 | 6 | block heading 7 | h3 #{domain} Provisioning Failure 8 | 9 | block content 10 | 11 | ul 12 | li 13 | label Provisioning page: 14 | a(href="#{urls.retry}") #{urls.retry} 15 | li 16 | label Reason: 17 | span #{reason} 18 | 19 | nav 20 | ul 21 | li 22 | a(href="#{urls.retry}") Check again 23 | li 24 | a(href="/") Try another domain 25 | 26 | -------------------------------------------------------------------------------- /server/views/auth_failure.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block title 4 | title checkmyidp.org - #{domain} authentication page failure - Mozilla Persona Identity authider (IdP) Linter 5 | 6 | block heading 7 | h3 #{domain} Authentication Failure 8 | 9 | block content 10 | 11 | ul 12 | li 13 | label Authentication page: 14 | a(href="#{urls.retry}") #{urls.retry} 15 | li 16 | label Reason: 17 | span #{reason} 18 | 19 | nav 20 | ul 21 | li 22 | a(href="#{urls.retry}") Check again 23 | li 24 | a(href="/") Try another domain 25 | 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # checkmyidp.org 2 | 3 | A super simple linter to check whether a domain is set up to be a Mozilla 4 | Persona/BrowserID Identity Provider. 5 | 6 | Check it out at http://checkmyidp.org 7 | 8 | ## Build Status 9 | [![Build Status](https://secure.travis-ci.org/shane-tomlinson/checkmyidp.org.png?branch=master)](https://github.com/shane-tomlinson/checkmyidp.org) 10 | 11 | ## Author: 12 | * Shane Tomlinson 13 | * shane@shanetomlinson.com 14 | * stomlinson@mozilla.com 15 | * set117@yahoo.com 16 | * https://shanetomlinson.com 17 | * http://github.com/stomlinson 18 | * http://github.com/shane-tomlinson 19 | * @shane_tomlinson 20 | 21 | ## Getting involved: 22 | I am happy to review submissions! 23 | 24 | ## License: 25 | This software is available under version 2.0 of the MPL: 26 | 27 | https://www.mozilla.org/MPL/ 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /server/lib/config.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const path = require('path'), 6 | fs = require('fs'); 7 | 8 | const config_dir = path.join(__dirname, "..", "etc"); 9 | 10 | var config = getConfig(); 11 | 12 | 13 | exports.get = function(key) { 14 | if (!(key in config)) { 15 | throw new Error("Invalid configuration option: " + key); 16 | } 17 | 18 | return config[key]; 19 | }; 20 | 21 | function getConfig() { 22 | var env = process.env['NODE_ENV'] || 'dev'; 23 | console.log('Using configuration environment:', env); 24 | 25 | var config_path = path.join(config_dir, env); 26 | var config = require(config_path); 27 | 28 | return config; 29 | } 30 | -------------------------------------------------------------------------------- /server/lib/logging.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /* 6 | * const logger = require('../libs/logging.js').logger; 7 | * logger.debug("you can probably ignore this. just for debugging."); 8 | * logger.info("something happened, here's info about it!"); 9 | * logger.warn("this isn't good. it's not a fatal error, but needs attention"); 10 | * logger.error("this isn't good at all. I will probably crash soon."); 11 | */ 12 | 13 | module.exports = { 14 | debug: function(msg) { 15 | console.log(msg); 16 | }, 17 | info: function(msg) { 18 | console.log(msg); 19 | }, 20 | warn: function(msg) { 21 | console.log(msg); 22 | }, 23 | error: function(msg) { 24 | console.log(msg); 25 | } 26 | }; 27 | 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Shane Tomlinson (https://shanetomlinson.com)", 3 | "name": "checkmyidp.org", 4 | "description": "A BrowserID/Mozilla Persona Identity Provider linter", 5 | "keywords": ["mozilla", "persona", "browserid"], 6 | "homepage": "https://github.com/shane-tomlinson/checkmyidp.org", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/shane-tomlinson/checkmyidp.org.git" 10 | }, 11 | "bugs": { 12 | "url": "https://github.com/shane-tomlinson/checkmyidp.org/issues" 13 | }, 14 | "version": "0.0.1-dev1", 15 | "engines": { 16 | "node": ">= 0.4.7" 17 | }, 18 | "main": "index", 19 | "dependencies": { 20 | "underscore": "1.3.1", 21 | "express": "3.1.0", 22 | "jade": "0.28.1", 23 | "postprocess": "0.2.4", 24 | "urlparse": "0.0.1", 25 | "jwcrypto": "0.4.2", 26 | "connect-fonts": "0.0.9", 27 | "connect-fonts-opensans": "0.0.3-beta1", 28 | "connect-fonts-sourcesanspro": "0.0.1", 29 | "underscore": "1.3.1", 30 | "postprocess": "0.2.4" 31 | }, 32 | "devDependencies": { 33 | "nodeunit": ">=0.6.4", 34 | "awsbox": "0.3.6" 35 | }, 36 | "scripts": { 37 | "test": "nodeunit server/test", 38 | "start": "node server/start.js" 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /server/static/authentication_api.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | (function() { 5 | "use strict"; 6 | 7 | if (!navigator.id) navigator.id = {}; 8 | 9 | navigator.id.beginAuthentication = function(cb) { 10 | // the authentication page needs to know the email address. 11 | var email = getQueryParameter("email"); 12 | cb(email); 13 | }; 14 | 15 | navigator.id.completeAuthentication = function(cb) { 16 | // authentication is good, go to the provisioning page. 17 | var redirectTo = getQueryParameter("prov") + toQueryString({ 18 | email: getQueryParameter("email") 19 | }); 20 | 21 | location.href=redirectTo; 22 | }; 23 | 24 | navigator.id.raiseAuthenticationFailure = function(reason) { 25 | // authentication failed, show why. 26 | var redirectTo = "https://checkmyidp.org/auth_failure" + toQueryString({ 27 | reason: reason, 28 | retry: location.href 29 | }); 30 | 31 | location.href=redirectTo; 32 | }; 33 | 34 | function getQueryParameter(name) { 35 | name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); 36 | var regexS = "[\\?&]" + name + "=([^&#]*)"; 37 | var regex = new RegExp(regexS); 38 | var results = regex.exec(window.location.href); 39 | if(results == null) 40 | return ""; 41 | else 42 | return decodeURIComponent(results[1].replace(/\+/g, " ")); 43 | } 44 | 45 | function toQueryString(params) { 46 | var queryString = "?"; 47 | var queryParams = []; 48 | for(var key in params) { 49 | queryParams.push(key + "=" + encodeURIComponent(params[key])); 50 | } 51 | return queryString + queryParams.join("&"); 52 | } 53 | 54 | 55 | }()); 56 | -------------------------------------------------------------------------------- /server/test/resources-available-test.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | /** 6 | * Check to make sure all of the available resources are available 7 | */ 8 | 9 | 10 | const spawn = require('child_process').spawn, 11 | path = require('path'), 12 | http = require('http'), 13 | config = require('../lib/config'); 14 | 15 | const host = config.get('host'); 16 | const port = config.get('port'); 17 | 18 | // XXX read this from package.json 19 | const server_path = path.join(__dirname, '..', 'start.js'); 20 | 21 | const urls_to_check = { 22 | '/': 200, 23 | '/lint?idp_url=eyedee.me': 200, 24 | // check_pages redirects to the authentication page. 25 | '/check_pages': 302, 26 | '/auth_failure?retry=https://eyedee.me/auth': 200, 27 | '/prov_failure?retry=https://eyedee.me/prov': 200, 28 | '/prov_success?retry=https://eyedee.me/prov': 200, 29 | '/authentication_api.js': 200, 30 | '/provisioning_api.js': 200, 31 | '/bidbundle.js': 200 32 | }; 33 | 34 | exports.resources_available = function(test) { 35 | var urls = [].concat(Object.keys(urls_to_check)); 36 | 37 | startServer(check_next_url); 38 | 39 | function check_next_url() { 40 | var next_url = urls.shift(); 41 | 42 | if (next_url) { 43 | respondsWith(host, port, next_url, 44 | urls_to_check[next_url], test, check_next_url); 45 | } 46 | else { 47 | stopServer(function() { 48 | test.done(); 49 | }); 50 | } 51 | } 52 | }; 53 | 54 | var proc; 55 | process.on('exit', function() { 56 | if (proc) proc.kill(); 57 | }); 58 | 59 | function startServer(done) { 60 | proc = spawn('node', [ server_path ]); 61 | proc.stdout.on('data', function(buf) { 62 | var text = String(buf); 63 | console.log(text); 64 | 65 | if (text.indexOf('Server running at:') > -1) { 66 | done(); 67 | } 68 | }); 69 | proc.stderr.on('data', function(buf) { 70 | console.log(String(buf)); 71 | }); 72 | } 73 | 74 | function stopServer(done) { 75 | proc.kill('SIGINT'); 76 | proc.on('exit', done); 77 | } 78 | 79 | function respondsWith(hostname, port, path, code, test, done) { 80 | console.log("checking:", hostname + ":" + port + path); 81 | http.get({ 82 | hostname: hostname, 83 | port: port, 84 | path: path 85 | }, function(res) { 86 | test.equal(res.statusCode, code, path); 87 | done(); 88 | }); 89 | } 90 | 91 | -------------------------------------------------------------------------------- /server/views/lint.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block title 4 | title checkmyidp.org - #{domain} results - Mozilla Persona Identity Provider (IdP) Linter 5 | 6 | block heading 7 | h3 #{domain} results 8 | 9 | block content 10 | 11 | - if (error) 12 | div #{error} 13 | - else 14 | ul 15 | li 16 | label Authoritative Domain: 17 | span #{authoritativeDomain} 18 | li 19 | label SSL Certificate: 20 | span 21 | a(href="https://www.ssllabs.com/ssltest/analyze.html?hideResults=on&d="+authoritativeDomain) test the certificate 22 | li 23 | label Authentication: 24 | ul 25 | li 26 | label page: 27 | a(href=urls.auth) #{urls.auth} 28 | li 29 | label X-Frame-Option header: 30 | span #{auth_x_frame_option || "none"} 31 | - if (auth_x_frame_option && auth_x_frame_option !== "ALLOW") 32 | span - Must be set to "ALLOW" 33 | li 34 | label included shim: 35 | span #{auth_include || "none"} 36 | li 37 | label Provisioning: 38 | ul 39 | li 40 | label page: 41 | a(href=urls.prov) #{urls.prov} 42 | li 43 | label X-Frame-Options header: 44 | span #{prov_x_frame_option || "none"} 45 | - if (prov_x_frame_option && prov_x_frame_option !== "ALLOW") 46 | span - Must be set to "ALLOW" 47 | li 48 | label included shim: 49 | span #{prov_include || "none"} 50 | 51 | 52 | li 53 | label Public Key: 54 | ul 55 | li 56 | label algorithm: 57 | span #{publicKey.algorithm} 58 | li 59 | label keysize: 60 | span #{publicKey.keysize} 61 | 62 | each val, key in publicKey.serialized 63 | li 64 | label #{key}: 65 | span#key #{val} 66 | 67 | nav 68 | ul 69 | li 70 | a(href="#{url}") Check again 71 | li 72 | a(href="/") Try another domain 73 | 74 | 75 | - if (!error && canCheckPages) 76 | h3 Check authentication and provisioning pages 77 | ul 78 | li 79 | form(action="/check_pages", method="get") 80 | label Username: 81 | input#username(name="username", type="text") 82 | input(name="auth", type="hidden", value="#{urls.auth}") 83 | input(name="prov", type="hidden", value="#{urls.prov}") 84 | input(name="domain", type="hidden", value="#{domain}") 85 | button Check Pages 86 | 87 | 88 | -------------------------------------------------------------------------------- /server/lib/well-known-parser.js: -------------------------------------------------------------------------------- 1 | var jwcrypto = require("jwcrypto"); 2 | 3 | // parse a well-known document. throw an exception if it is invalid, return 4 | // a parsed version if it is valid. return is an object having the following fields: 5 | // * 'type' - one of "disabled", "delegation", or "supported" 6 | // * if type is "delegation", also: 7 | // * authority - the domain authority is delegated to 8 | // * if type is "supported": 9 | // * publicKey - a parsed representation of the public key 10 | // * paths.authentication - the path to the 'authentication' html 11 | // * paths.provisioning - the path to the 'provisioning' html 12 | module.exports = function(doc) { 13 | try { 14 | doc = JSON.parse(doc); 15 | } catch(e) { 16 | throw "declaration of support is malformed (invalid json)"; 17 | } 18 | 19 | if (typeof doc !== 'object') { 20 | throw "support document must contain a json object"; 21 | } 22 | 23 | // there are three main types of support documents 24 | // 1. "supported" - declares the domain is a browserid authority, 25 | // contains public-key, authentication, and provisioning 26 | // 2. "delegation" - declares the domain allows a different domain 27 | // to be authoritative for it. 28 | // 3. "disable" - domain declares explicitly that it wants a secondary 29 | // to be authoritative. 30 | 31 | // is this a "disable" document? Any value will cause the domain to 32 | // be disabled for the purposes of parsing. For the purposes of the spec 33 | // we should insist on 'true'. Rationale is that if disabled is present 34 | // in the file, the most likely intent is to DISABLE. 35 | if (doc.disabled) { 36 | return { type: "disabled" }; 37 | } 38 | 39 | // is this a delegation document? 40 | if (doc.authority) { 41 | if (typeof doc.authority !== 'string') throw "malformed authority"; 42 | 43 | return { 44 | type: "delegation", 45 | authority: doc.authority 46 | }; 47 | } 48 | 49 | // is this a support document? 50 | 51 | // the response that we'll populate as we go 52 | var parsed = { 53 | type: "supported", 54 | paths: {}, 55 | publicKey: null 56 | }; 57 | 58 | [ 'authentication', 'provisioning' ].forEach(function(requiredKey) { 59 | if (typeof doc[requiredKey] !== 'string') { 60 | throw "support document missing required '" + requiredKey + "'"; 61 | } else { 62 | parsed.paths[requiredKey] = doc[requiredKey]; 63 | } 64 | }); 65 | 66 | if (!doc['public-key']) { 67 | throw "support document missing required 'public-key'"; 68 | } 69 | 70 | // can we parse that key? 71 | try { 72 | parsed.publicKey = jwcrypto.loadPublicKeyFromObject(doc['public-key']); 73 | } catch(e) { 74 | throw "mal-formed public key in support doc: " + e.toString(); 75 | } 76 | 77 | // success! 78 | return parsed; 79 | }; 80 | -------------------------------------------------------------------------------- /server/start.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const express = require('express'), 6 | path = require('path'), 7 | url = require('url'), 8 | check_support = require('./lib/check-primary-support'), 9 | config = require('./lib/config'), 10 | substitution_middleware 11 | = require('./lib/substitution-middleware'), 12 | connect_fonts = require('connect-fonts'), 13 | open_sans = require('connect-fonts-opensans'), 14 | source_sans_pro = require('connect-fonts-sourcesanspro'); 15 | 16 | var app = express(); 17 | 18 | app.engine('jade', require('jade').__express); 19 | app.set('views', path.join(__dirname, "views")); 20 | app.use(express.bodyParser()); 21 | 22 | 23 | app.use(express.compress()); 24 | 25 | 26 | var public_url = config.get('public_url'); 27 | if (public_url !== "https://checkmyidp.org") { 28 | app.use(substitution_middleware.setup({ 29 | from: "https://checkmyidp.org", 30 | to: public_url 31 | })); 32 | } 33 | 34 | app.use(connect_fonts.setup({ 35 | fonts: [ open_sans, source_sans_pro ], 36 | allow_origin: "https://checkmyidp.org", 37 | maxage: 180 * 24 * 60 * 60 * 1000, // 180 days 38 | compress: true 39 | })); 40 | 41 | app.use(express.static(path.join(__dirname, 'static'))); 42 | 43 | app.get('/', function(req, res) { 44 | res.render('index.jade'); 45 | }); 46 | 47 | app.get('/lint', function(req, res) { 48 | var idp_url = req.query.idp_url || ""; 49 | // url.parse requires that the protocol be prepended to the domain name 50 | var with_http = 'http://' + idp_url.replace(/https?:\/\//, ''); 51 | var domain = url.parse(with_http).hostname; 52 | 53 | if (!(domain === req.query.idp_url)) { 54 | res.redirect('lint?idp_url=' + domain); 55 | return; 56 | } 57 | 58 | check_support.checkSupport(domain, function(err, result) { 59 | // The error will contain the error message to print to the user. 60 | if (err) result = { error: String(err) }; 61 | 62 | result.url = req.url; 63 | result.domain = domain; 64 | result.error = result.error || ""; 65 | res.render('lint.jade', result); 66 | }); 67 | }); 68 | 69 | app.get('/check_pages', function(req, res) { 70 | var auth = req.query.auth; 71 | var prov = req.query.prov; 72 | var username = req.query.username; 73 | var domain = req.query.domain; 74 | var redirectTo = auth + toQueryString({ 75 | email: username + "@" + domain, 76 | prov: prov, 77 | }); 78 | 79 | res.redirect(redirectTo); 80 | }); 81 | 82 | app.get('/auth_failure', function(req, res) { 83 | res.render('auth_failure.jade', { 84 | domain: url.parse(req.query.retry).hostname, 85 | reason: req.query.reason, 86 | urls: { 87 | retry: req.query.retry 88 | } 89 | }); 90 | }); 91 | 92 | app.get('/prov_failure', function(req, res) { 93 | res.render('prov_failure.jade', { 94 | domain: url.parse(req.query.retry).hostname, 95 | reason: req.query.reason, 96 | urls: { 97 | retry: req.query.retry 98 | } 99 | }); 100 | }); 101 | 102 | app.get('/prov_success', function(req, res) { 103 | res.render('prov_success.jade', { 104 | domain: url.parse(req.query.retry).hostname, 105 | urls: { 106 | retry: req.query.retry 107 | } 108 | }); 109 | }); 110 | 111 | var port = config.get('port'); 112 | var host = config.get('host'); 113 | app.listen(port, host, function() { 114 | console.log("Server running at: ", (host + ":" + port)); 115 | }); 116 | 117 | function toQueryString(params) { 118 | var queryString = "?"; 119 | var queryParams = []; 120 | for(var key in params) { 121 | queryParams.push(key + "=" + encodeURIComponent(params[key])); 122 | } 123 | return queryString + queryParams.join("&"); 124 | } 125 | 126 | -------------------------------------------------------------------------------- /server/static/provisioning_api.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | (function() { 5 | "use strict"; 6 | 7 | if (!navigator.id) navigator.id = {}; 8 | 9 | /** 10 | * WARNING - 11 | * DO NOT TRUST THE CERTIFICATES GENERATED BY THIS FILE FOR ANYTHING 12 | * OTHER THAN TESTING. JWCRYPTO IS NOT PROPERLY SEEDED. 13 | */ 14 | 15 | addScript("https://checkmyidp.org/bidbundle.js"); 16 | 17 | var jwcrypto; 18 | 19 | navigator.id.beginProvisioning = function(cb) { 20 | // require must exist before trying to include jwcrypto. 21 | waitUntilExists("require", function() { 22 | jwcrypto = require('./lib/jwcrypto'); 23 | 24 | // SEEDING WITH FIXED ENTROPY IS A BAD BAD THING TO DO. 25 | jwcrypto.addEntropy("sJ3/VNLAWaKhpqT54YMPiT9hppkOHMNYxk7W5cweloM="); 26 | 27 | var email = getQueryParameter("email"); 28 | // certs must be good for at least 2 minutes. Try 3. 29 | cb(email, 180); 30 | }); 31 | }; 32 | 33 | navigator.id.genKeyPair = function(cb) { 34 | jwcrypto.generateKeypair({ algorithm: "DS", keysize: 128 }, function(err, kp) { 35 | cb(kp.publicKey.serialize()); 36 | }); 37 | }; 38 | 39 | navigator.id.registerCertificate = function(certificate) { 40 | // a certificate has been generated, check it. 41 | var reason = checkCertificate(certificate); 42 | if (reason) { 43 | provisioningFailure(reason); 44 | } 45 | else { 46 | location.href = "https://checkmyidp.org/prov_success" + toQueryString({ 47 | retry: location.href 48 | }); 49 | } 50 | 51 | }; 52 | 53 | navigator.id.raiseProvisioningFailure = function(reason) { 54 | provisioningFailure(reason); 55 | }; 56 | 57 | function provisioningFailure(reason) { 58 | // provisioning has failed. Show why 59 | var redirectTo = "https://checkmyidp.org/prov_failure" + toQueryString({ 60 | reason: reason, 61 | retry: location.href 62 | }); 63 | 64 | location.href = redirectTo; 65 | } 66 | 67 | function getQueryParameter(name) { 68 | name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]"); 69 | var regexS = "[\\?&]" + name + "=([^&#]*)"; 70 | var regex = new RegExp(regexS); 71 | var results = regex.exec(window.location.href); 72 | if(results == null) 73 | return ""; 74 | else 75 | return decodeURIComponent(results[1].replace(/\+/g, " ")); 76 | } 77 | 78 | function toQueryString(params) { 79 | var queryString = "?"; 80 | var queryParams = []; 81 | for(var key in params) { 82 | queryParams.push(key + "=" + encodeURIComponent(params[key])); 83 | } 84 | return queryString + queryParams.join("&"); 85 | } 86 | 87 | 88 | function addScript(src) { 89 | var script = document.createElement("script"); 90 | script.setAttribute("src", src); 91 | document.head.appendChild(script); 92 | } 93 | 94 | function waitUntilExists(checkFor, done) { 95 | if(checkFor in window) return done(); 96 | 97 | setTimeout(function() { 98 | waitUntilExists(checkFor, done); 99 | }, 100); 100 | } 101 | 102 | function checkCertificate(certificate) { 103 | try { 104 | var cert = jwcrypto.extractComponents(certificate); 105 | 106 | // if it expires in less than 2 minutes, it's too old to use. 107 | var diff = cert.payload.exp.valueOf() - new Date().getTime(); 108 | if (diff < (60 * 2 * 1000)) { 109 | throw new Error("Certificate is expired: clock skew between " + 110 | "your local clock and the IdP's clock can " + 111 | " cause this failure: " + diff); 112 | } 113 | 114 | // or if it was issued before the last time the domain key 115 | // was updated, it's invalid 116 | if (!cert.payload.iat) { 117 | throw new Error('Malformed certificate: missing iat'); 118 | } 119 | 120 | } catch(e) { 121 | var error = String(e); 122 | console.error("Invalid certificate - ", error); 123 | return error; 124 | } 125 | 126 | return false; 127 | } 128 | 129 | }()); 130 | 131 | -------------------------------------------------------------------------------- /server/lib/check-primary-support.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | const 6 | https = require('https'), 7 | und = require('underscore'), 8 | urlp = require('url'), 9 | util = require('util'), 10 | primary = require('./primary'); 11 | 12 | /* 13 | * The location of the Persona servers. Used to look for the correct includes. 14 | */ 15 | const PERSONA_HOSTS = [ 16 | 'https://login.persona.org', 17 | 'https://login.anosrep.org', 18 | 'https://login.dev.anosrep.org' 19 | ]; 20 | 21 | /* 22 | * The location of the checkmyidp.org servers. Used to look for the correct 23 | * includes. 24 | */ 25 | const CHECKMYIDP_HOSTS = [ 26 | 'https://checkmyidp.org' 27 | ]; 28 | 29 | /* 30 | * The includes to look for 31 | */ 32 | const INCLUDES = { 33 | 'auth': '/authentication_api.js', 34 | 'prov': '/provisioning_api.js' 35 | }; 36 | 37 | 38 | exports.checkSupport = function(domain, done) { 39 | primary.checkSupport(domain, function(err, r) { 40 | if (err || r.publicKey === null) { 41 | if (err) { 42 | process.stderr.write('error: ' + err + '\n'); 43 | return done && done(err); 44 | } 45 | } 46 | 47 | var temp = r.publicKey.serialized = {}; 48 | r.publicKey.serializeToObject(temp); 49 | 50 | var opts = { 51 | xframe: true, 52 | mode: 'auth' 53 | }; 54 | 55 | getResource(r.urls.auth, opts, function (err, auth_res) { 56 | r.auth_include = err ? String(err) : auth_res.include; 57 | r.auth_x_frame_option = auth_res.x_frame_option; 58 | 59 | opts.mode = 'prov'; 60 | getResource(r.urls.prov, opts, function(err, prov_res) { 61 | r.prov_include = err ? String(err) : prov_res.include; 62 | r.prov_x_frame_option = prov_res.x_frame_option; 63 | 64 | // The user can check the full flow if they are using the 65 | // checkmyidp.org includes. 66 | r.canCheckPages = auth_res.checkmyidp_include && 67 | prov_res.checkmyidp_include; 68 | 69 | done && done(null, r); 70 | }); 71 | }); 72 | }); 73 | }; 74 | 75 | /** 76 | * Retrieve one of their urls and examine aspects of it for issues 77 | */ 78 | function getResource(url, opts, cb) { 79 | var domain = urlp.parse(url).host; 80 | var path = urlp.parse(url).path; 81 | var body = ''; 82 | 83 | https.request({ 84 | host: domain, 85 | path: path, 86 | method: 'GET' 87 | }, function(res) { 88 | res.on('data', function (chunk) { 89 | body += chunk; 90 | }); 91 | 92 | res.on('end', function() { 93 | checkResource(res, opts, body, function(err, results) { 94 | cb && cb(null, results); 95 | }); 96 | }); 97 | }).on('error', function (e) { 98 | console.log('ERROR: ', e.message); 99 | cb && cb(e); 100 | }).end(); 101 | } 102 | 103 | function getInclude(body, lookFor) { 104 | var allHosts = PERSONA_HOSTS.concat(CHECKMYIDP_HOSTS); 105 | 106 | for (var i = 0, host; host = allHosts[i]; ++i) { 107 | var include = host + lookFor; 108 | var hostRegExp = new RegExp(include, 'g'); 109 | 110 | if (hostRegExp.test(body)) return include; 111 | } 112 | 113 | return null; 114 | } 115 | 116 | function isCheckMyIdPInclude(include) { 117 | for (var i = 0, host; host = CHECKMYIDP_HOSTS[i]; ++i) { 118 | if(include.indexOf(host) === 0) return true; 119 | } 120 | 121 | return false; 122 | } 123 | 124 | /** 125 | * check a resource to see if its status code is legit and x-frame-options 126 | * header is not set. 127 | */ 128 | function checkResource(res, opts, body, done) { 129 | var include = getInclude(body, INCLUDES[opts.mode]); 130 | var results = { 131 | statusCode: res.statusCode, 132 | include: include || false, 133 | checkmyidp_include: include && isCheckMyIdPInclude(include), 134 | body: body 135 | }; 136 | 137 | 138 | if (opts.xframe === true) { 139 | var xframeOption = und.filter(res.headers, function (value, header) { 140 | if (header.toLowerCase() === 'x-frame-options') return true; 141 | })[0]; 142 | 143 | results.x_frame_option = xframeOption; 144 | } 145 | 146 | done(null, results); 147 | } 148 | -------------------------------------------------------------------------------- /server/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype 5 2 | html 3 | head 4 | meta(charset='utf-8') 5 | meta(name="viewport", content="initial-scale=1.0, width=device-width") 6 | block title 7 | 8 | link(href="/en/opensans-regular,sourcesanspro-regular/fonts.css", type="text/css", rel="stylesheet") 9 | 10 | style 11 | /* http://meyerweb.com/eric/tools/css/reset/ 12 | * v2.0 | 20110126 13 | * License: none (public domain) 14 | * 15 | */ 16 | 17 | html, body, div, span, applet, object, iframe, 18 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 19 | a, abbr, acronym, address, big, cite, code, 20 | del, dfn, em, img, ins, kbd, q, s, samp, 21 | small, strike, strong, sub, sup, tt, var, 22 | b, u, i, center, 23 | dl, dt, dd, ol, ul, li, 24 | fieldset, form, label, legend, 25 | table, caption, tbody, tfoot, thead, tr, th, td, 26 | article, aside, canvas, details, embed, 27 | figure, figcaption, footer, header, hgroup, 28 | menu, nav, output, ruby, section, summary, 29 | time, mark, audio, video { 30 | margin: 0; 31 | padding: 0; 32 | border: 0; 33 | font-size: 100%; 34 | font: inherit; 35 | vertical-align: baseline; 36 | } 37 | /* HTML5 display-role reset for older browsers */ 38 | article, aside, details, figcaption, figure, 39 | footer, header, hgroup, menu, nav, section { 40 | display: block; 41 | } 42 | body { 43 | line-height: 1; 44 | font-family: 'Source Sans Pro', 'Helvetica', 'sans-serif', 'serif'; 45 | } 46 | ol, ul { 47 | list-style: none; 48 | } 49 | blockquote, q { 50 | quotes: none; 51 | } 52 | blockquote:before, blockquote:after, 53 | q:before, q:after { 54 | content: ''; 55 | content: none; 56 | } 57 | table { 58 | border-collapse: collapse; 59 | border-spacing: 0; 60 | } 61 | 62 | /* site specific CSS */ 63 | html { 64 | padding: 1.1em; 65 | } 66 | 67 | body { 68 | line-height: 1.3; 69 | } 70 | 71 | header { 72 | margin-bottom: 1em; 73 | } 74 | 75 | hgroup { 76 | color: #888; 77 | } 78 | 79 | h1, h2, h3 { 80 | font-weight: bold; 81 | line-height: 1.3em; 82 | } 83 | 84 | h1 { 85 | font-size: 1.3em; 86 | } 87 | 88 | label { 89 | display: block; 90 | } 91 | 92 | input { 93 | padding: 5px; 94 | width: 100%; 95 | border-radius: 5px; 96 | border: 2px solid #bbb; 97 | margin-bottom: 1em; 98 | } 99 | 100 | button { 101 | font-size: 1.2em; 102 | color: #fff; 103 | display: block; 104 | margin: 0 auto; 105 | padding: 10px; 106 | background-color: #00ff00; 107 | border: 0; 108 | color: #fff; 109 | text-shadow: 0 1px rgba(0,0,0,0.5); 110 | border-radius: 7px; 111 | box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.3), 0 1px 0 rgba(0, 0, 0, 0.2); 112 | 113 | background-color: #4eb5e5; 114 | background-image: -webkit-gradient(linear, left top, left bottom, from(#4eb5e5), to(#3196cf)); 115 | background-image: -webkit-linear-gradient(top, #4eb5e5, #3196cf); 116 | background-image: -moz-linear-gradient(top, #4eb5e5, #3196cf); 117 | background-image: -ms-linear-gradient(top, #4eb5e5, #3196cf); 118 | background-image: -o-linear-gradient(top, #4eb5e5, #3196cf); 119 | background-image: linear-gradient(top, #4eb5e5, #3196cf); 120 | } 121 | 122 | button:hover, 123 | button:focus, 124 | .button:hover, 125 | .button:focus { 126 | box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.3), 0 1px 0 rgba(0, 0, 0, 0.2), 0 2px 0 rgba(0, 0, 0, 0.1); 127 | 128 | background-color: #4aafe5; 129 | background-image: -webkit-gradient(linear, left top, left bottom, from(#4aafe5), to(#2c89c8)); 130 | background-image: -webkit-linear-gradient(top, #4aafe5, #2c89c8); 131 | background-image: -moz-linear-gradient(top, #4aafe5, #2c89c8); 132 | background-image: -ms-linear-gradient(top, #4aafe5, #2c89c8); 133 | background-image: -o-linear-gradient(top, #4aafe5, #2c89c8); 134 | background-image: linear-gradient(top, #4aafe5, #2c89c8); 135 | } 136 | 137 | button:focus, 138 | .button:focus { 139 | box-shadow: 0 0 1px #fff, 0 0 1px 3px #49ADE3; 140 | box-shadow: 0 0 1px rgba(255, 255, 255, 0.5), 0 0 1px 3px rgba(73, 173, 227, 0.6); 141 | } 142 | 143 | button:active, 144 | .button:active { 145 | background-color: #184a73; 146 | background-image: -webkit-gradient(linear, left top, left bottom, from(#184a73), to(#276084)); 147 | background-image: -webkit-linear-gradient(top, #184a73, #276084); 148 | background-image: -moz-linear-gradient(top, #184a73, #276084); 149 | background-image: -ms-linear-gradient(top, #184a73, #276084); 150 | background-image: -o-linear-gradient(top, #184a73, #276084); 151 | background-image: linear-gradient(top, #184a73, #276084); 152 | color: #97b6ca; 153 | text-shadow: 0 1px rgba(0,0,0,0.4); 154 | box-shadow: inset 0 2px 1px rgba(0,0,0,0.3); 155 | } 156 | 157 | button::-moz-focus-inner, .button::-moz-focus-inner { 158 | padding: 0; 159 | border: 0 160 | } 161 | 162 | a { 163 | color: #348fd0; 164 | text-decoration: none; 165 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 166 | font-weight: 300; 167 | } 168 | 169 | a:hover { 170 | color: #000; 171 | } 172 | 173 | h1 a, h1 a:hover { 174 | color: #888; 175 | } 176 | 177 | li label { 178 | display: inline-block; 179 | width: 175px; 180 | } 181 | 182 | li li label { 183 | margin-left: 20px; 184 | width: 155px; 185 | } 186 | 187 | nav { 188 | margin-top: 2em; 189 | } 190 | 191 | footer { 192 | margin-top: 2em; 193 | padding-top: 1em; 194 | border-top: 1px solid #ccc; 195 | } 196 | 197 | nav ul { 198 | text-align: center; 199 | } 200 | 201 | nav li, footer li { 202 | display: inline-block; 203 | margin-right: 1em; 204 | } 205 | 206 | #container { 207 | max-width: 60%; 208 | margin: 0 auto; 209 | } 210 | 211 | #key { 212 | word-wrap: break-word; 213 | } 214 | 215 | body 216 | div#container 217 | header 218 | hgroup 219 | h1 checkmyidp.org 220 | h2 A Mozilla Persona Identity Provider (IdP) Linter 221 | block heading 222 | 223 | block content 224 | 225 | block footer 226 | footer 227 | ul 228 | li A Friday Hack by Shane Tomlinson 229 | li Source code on GitHub 230 | -------------------------------------------------------------------------------- /server/lib/primary.js: -------------------------------------------------------------------------------- 1 | /* This Source Code Form is subject to the terms of the Mozilla Public 2 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 | 5 | // this file is an abstraction around "primary identity authority" support, 6 | // specifically checks and a cache to see if a primary supports browserid 7 | // natively. 8 | 9 | const 10 | https = require('https'), 11 | http = require('http'), 12 | logger = require('./logging'), 13 | urlparse = require('urlparse'), 14 | jwcrypto = require("jwcrypto"), 15 | events = require("events"), 16 | wellKnownParser = require('./well-known-parser.js'), 17 | primaryTimeout = 30000; 18 | 19 | // alg 20 | require("jwcrypto/lib/algs/rs"); 21 | require("jwcrypto/lib/algs/ds"); 22 | 23 | const WELL_KNOWN_URL = "/.well-known/browserid"; 24 | 25 | // Protect from stack overflows and network DDOS attacks 26 | const MAX_AUTHORITY_DELEGATIONS = 6; 27 | 28 | // the event emitter will raise "idp_seen" events when we sucessfully 29 | // check an IdP's well-known hosts file and see that they are online 30 | exports.events = new events.EventEmitter(); 31 | 32 | // hit the network and fetch a .well-known document in its unparsed form 33 | var fetchWellKnown = function (currentDomain, principalDomain, clientCB) { 34 | // in many cases the http layer can send both an 'error' and an 'end'. In 35 | // other cases, only 'error' will be emitted. We want to 36 | // ensure the client callback is invoked only once. this function does it. 37 | var cb = function() { 38 | if (clientCB) { 39 | clientCB.apply(null, arguments); 40 | clientCB = null; 41 | } 42 | }; 43 | 44 | // if a network attempt to retrieve a support document from the principal 45 | // domain fails, let's see if we have a "proxy" IDP available for this domain, 46 | // if so, we'll create a delegation of authority document. 47 | function handleProxyIDP(err) { 48 | // log the error with the inital fetch if defined 49 | if (err) logger.debug(err); 50 | 51 | cb(err); 52 | } 53 | 54 | function handleResponse(res) { 55 | if (res.statusCode !== 200) { 56 | return handleProxyIDP(currentDomain + 57 | ' is not a browserid primary - non-200 response code to ' + 58 | WELL_KNOWN_URL); 59 | } 60 | if (res.headers['content-type'].indexOf('application/json') !== 0) { 61 | return handleProxyIDP(currentDomain + 62 | ' is not a browserid primary - non "application/json" response to ' + 63 | WELL_KNOWN_URL); 64 | } 65 | 66 | var body = ""; 67 | res.on('data', function(chunk) { body += chunk; }); 68 | res.on('end', function() { 69 | cb(null, body, currentDomain); 70 | }); 71 | } 72 | 73 | var req; 74 | req = https.get({ 75 | host: currentDomain, 76 | path: WELL_KNOWN_URL + "?domain=" + principalDomain, 77 | agent: false 78 | }, handleResponse); 79 | 80 | // front-end shows xhr delay message after 10 sec; timeout sooner to avoid this 81 | var reqTimeout = setTimeout(function() { 82 | req.abort(); 83 | handleProxyIDP('timeout trying to load well-known for ' + currentDomain); 84 | }, primaryTimeout); 85 | req.on('response', function() { clearTimeout(reqTimeout); }); 86 | 87 | req.on('error', function(e) { 88 | if (reqTimeout) { clearTimeout(reqTimeout); } 89 | handleProxyIDP(currentDomain + ' is not a browserid primary: ' + String(e)); 90 | }); 91 | }; 92 | 93 | // Fetch a .well-known file from the network, following delegation 94 | function deepFetchWellKnown(principalDomain, cb, currentDomain, delegationChain) { 95 | // this function is recursive, the last two parameters are only specified 96 | // when invoking ourselves. 97 | if (!currentDomain) currentDomain = principalDomain; 98 | if (!delegationChain) delegationChain = [ principalDomain ]; 99 | 100 | fetchWellKnown(currentDomain, principalDomain, function(err, unparsedDoc) { 101 | if (err) return cb(err); 102 | 103 | var supportDoc; 104 | try { 105 | supportDoc = wellKnownParser(unparsedDoc); 106 | } catch (e) { 107 | return cb("bad support document for '" + currentDomain + "': " + String(e)); 108 | } 109 | 110 | if (supportDoc.type === 'disabled') 111 | { 112 | return cb(null, { 113 | disabled: true, 114 | delegationChain: delegationChain, 115 | authoritativeDomain: delegationChain[delegationChain.length - 1], 116 | }); 117 | } 118 | else if (supportDoc.type === 'delegation') 119 | { 120 | currentDomain = supportDoc.authority; 121 | 122 | // check for cycles in delegation 123 | if (delegationChain.indexOf(currentDomain) !== -1) { 124 | return cb("Circular reference in delegating authority: " + delegationChain.join(" > ")); 125 | } 126 | 127 | delegationChain.push(currentDomain); 128 | 129 | logger.debug(delegationChain[delegationChain.length - 2] + " delegates to " + 130 | delegationChain[delegationChain.length - 1]); 131 | 132 | // check for max delegation length 133 | if (delegationChain.length > MAX_AUTHORITY_DELEGATIONS) { 134 | return cb("Too many hops while delegating authority: " + delegationChain.join(" > ")); 135 | } 136 | 137 | // recurse 138 | return deepFetchWellKnown(principalDomain, cb, currentDomain, delegationChain); 139 | } 140 | else if (supportDoc.type === 'supported') 141 | { 142 | // DEBUGGING INSTRUMENTATION: Allow SHIMMED_PRIMARIES to change example.com into 127.0.0.1:10005 143 | var url_prefix = 'https://' + currentDomain; 144 | var details = { 145 | publicKey: supportDoc.publicKey, 146 | urls: { 147 | auth: url_prefix + supportDoc.paths.authentication, 148 | prov: url_prefix + supportDoc.paths.provisioning 149 | }, 150 | delegationChain: delegationChain, 151 | authoritativeDomain: delegationChain[delegationChain.length - 1], 152 | proxied: false 153 | }; 154 | 155 | // validate the urls 156 | try { 157 | urlparse(details.urls.auth).validate(); 158 | urlparse(details.urls.prov).validate(); 159 | } catch(e) { 160 | return cb("invalid URLs in support document: " + e.toString()); 161 | } 162 | 163 | // success! 164 | cb(null, details); 165 | } 166 | else 167 | { 168 | var msg = "unhandled error while parsing support document for " + currentDomain; 169 | logger.error(msg); 170 | return cb(msg); 171 | } 172 | }); 173 | } 174 | 175 | exports.checkSupport = function(principalDomain, cb) { 176 | if (!cb) throw "missing required callback function"; 177 | 178 | if (typeof principalDomain !== 'string' || !principalDomain.length) { 179 | return process.nextTick(function() { cb("invalid domain"); }); 180 | } 181 | 182 | deepFetchWellKnown(principalDomain, function (err, r) { 183 | if (err) { 184 | logger.debug(err); 185 | cb(err); 186 | } else if (r) { 187 | if (r.disabled) { 188 | // Don't emit events for disabled idps. This could be very noisy. Rather 189 | // we perform a lazy cleanup of stale database records inside address_info. 190 | logger.info(principalDomain + ' has explicitly disabled browserid support'); 191 | } else { 192 | exports.events.emit("idp_seen", principalDomain); 193 | logger.info(principalDomain + ' is a valid browserid primary'); 194 | } 195 | return cb(null, r); 196 | } 197 | }); 198 | }; 199 | 200 | exports.emailRegex = /\@(.*)$/; 201 | 202 | exports.getPublicKey = function(domain, cb) { 203 | exports.checkSupport(domain, function(err, r) { 204 | if (err || !r || !r.publicKey) { 205 | cb("can't get public key for " + domain + (err ? ": " + err : "")); 206 | } else { 207 | cb(err, r.publicKey); 208 | } 209 | }); 210 | }; 211 | 212 | // Is issuingDomain allowed to issue certifications for emails from 213 | // emailDomain. 214 | exports.delegatesAuthority = function (emailDomain, issuingDomain, cb) { 215 | exports.checkSupport(emailDomain, function(err, r) { 216 | cb(!err && r && (r.authoritativeDomain === issuingDomain)); 217 | }); 218 | }; 219 | 220 | -------------------------------------------------------------------------------- /server/static/bidbundle.js: -------------------------------------------------------------------------------- 1 | OVERRIDE = {window:window, navigator:navigator}; 2 | var require = function (file, cwd) { 3 | var resolved = require.resolve(file, cwd || '/'); 4 | var mod = require.modules[resolved]; 5 | if (!mod) throw new Error( 6 | 'Failed to resolve module ' + file + ', tried ' + resolved 7 | ); 8 | var cached = require.cache[resolved]; 9 | var res = cached? cached.exports : mod(); 10 | return res; 11 | } 12 | 13 | require.paths = []; 14 | require.modules = {}; 15 | require.cache = {}; 16 | require.extensions = [".js",".coffee"]; 17 | 18 | require._core = { 19 | 'assert': true, 20 | 'events': true, 21 | 'fs': true, 22 | 'path': true, 23 | 'vm': true 24 | }; 25 | 26 | require.resolve = (function () { 27 | return function (x, cwd) { 28 | if (!cwd) cwd = '/'; 29 | 30 | if (require._core[x]) return x; 31 | var path = require.modules.path(); 32 | cwd = path.resolve('/', cwd); 33 | var y = cwd || '/'; 34 | 35 | if (x.match(/^(?:\.\.?\/|\/)/)) { 36 | var m = loadAsFileSync(path.resolve(y, x)) 37 | || loadAsDirectorySync(path.resolve(y, x)); 38 | if (m) return m; 39 | } 40 | 41 | var n = loadNodeModulesSync(x, y); 42 | if (n) return n; 43 | 44 | throw new Error("Cannot find module '" + x + "'"); 45 | 46 | function loadAsFileSync (x) { 47 | x = path.normalize(x); 48 | if (require.modules[x]) { 49 | return x; 50 | } 51 | 52 | for (var i = 0; i < require.extensions.length; i++) { 53 | var ext = require.extensions[i]; 54 | if (require.modules[x + ext]) return x + ext; 55 | } 56 | } 57 | 58 | function loadAsDirectorySync (x) { 59 | x = x.replace(/\/+$/, ''); 60 | var pkgfile = path.normalize(x + '/package.json'); 61 | if (require.modules[pkgfile]) { 62 | var pkg = require.modules[pkgfile](); 63 | var b = pkg.browserify; 64 | if (typeof b === 'object' && b.main) { 65 | var m = loadAsFileSync(path.resolve(x, b.main)); 66 | if (m) return m; 67 | } 68 | else if (typeof b === 'string') { 69 | var m = loadAsFileSync(path.resolve(x, b)); 70 | if (m) return m; 71 | } 72 | else if (pkg.main) { 73 | var m = loadAsFileSync(path.resolve(x, pkg.main)); 74 | if (m) return m; 75 | } 76 | } 77 | 78 | return loadAsFileSync(x + '/index'); 79 | } 80 | 81 | function loadNodeModulesSync (x, start) { 82 | var dirs = nodeModulesPathsSync(start); 83 | for (var i = 0; i < dirs.length; i++) { 84 | var dir = dirs[i]; 85 | var m = loadAsFileSync(dir + '/' + x); 86 | if (m) return m; 87 | var n = loadAsDirectorySync(dir + '/' + x); 88 | if (n) return n; 89 | } 90 | 91 | var m = loadAsFileSync(x); 92 | if (m) return m; 93 | } 94 | 95 | function nodeModulesPathsSync (start) { 96 | var parts; 97 | if (start === '/') parts = [ '' ]; 98 | else parts = path.normalize(start).split('/'); 99 | 100 | var dirs = []; 101 | for (var i = parts.length - 1; i >= 0; i--) { 102 | if (parts[i] === 'node_modules') continue; 103 | var dir = parts.slice(0, i + 1).join('/') + '/node_modules'; 104 | dirs.push(dir); 105 | } 106 | 107 | return dirs; 108 | } 109 | }; 110 | })(); 111 | 112 | require.alias = function (from, to) { 113 | var path = require.modules.path(); 114 | var res = null; 115 | try { 116 | res = require.resolve(from + '/package.json', '/'); 117 | } 118 | catch (err) { 119 | res = require.resolve(from, '/'); 120 | } 121 | var basedir = path.dirname(res); 122 | 123 | var keys = (Object.keys || function (obj) { 124 | var res = []; 125 | for (var key in obj) res.push(key); 126 | return res; 127 | })(require.modules); 128 | 129 | for (var i = 0; i < keys.length; i++) { 130 | var key = keys[i]; 131 | if (key.slice(0, basedir.length + 1) === basedir + '/') { 132 | var f = key.slice(basedir.length); 133 | require.modules[to + f] = require.modules[basedir + f]; 134 | } 135 | else if (key === basedir) { 136 | require.modules[to] = require.modules[basedir]; 137 | } 138 | } 139 | }; 140 | 141 | (function () { 142 | var process = {}; 143 | 144 | require.define = function (filename, fn) { 145 | if (require.modules.__browserify_process) { 146 | process = require.modules.__browserify_process(); 147 | } 148 | 149 | var dirname = require._core[filename] 150 | ? '' 151 | : require.modules.path().dirname(filename) 152 | ; 153 | 154 | var require_ = function (file) { 155 | return require(file, dirname); 156 | }; 157 | require_.resolve = function (name) { 158 | return require.resolve(name, dirname); 159 | }; 160 | require_.modules = require.modules; 161 | require_.define = require.define; 162 | require_.cache = require.cache; 163 | var module_ = { exports : {} }; 164 | 165 | require.modules[filename] = function () { 166 | require.cache[filename] = module_; 167 | fn.call( 168 | module_.exports, 169 | require_, 170 | module_, 171 | module_.exports, 172 | dirname, 173 | filename, 174 | process 175 | ); 176 | return module_.exports; 177 | }; 178 | }; 179 | })(); 180 | 181 | 182 | require.define("path",function(require,module,exports,__dirname,__filename,process){function filter (xs, fn) { 183 | var res = []; 184 | for (var i = 0; i < xs.length; i++) { 185 | if (fn(xs[i], i, xs)) res.push(xs[i]); 186 | } 187 | return res; 188 | } 189 | 190 | // resolves . and .. elements in a path array with directory names there 191 | // must be no slashes, empty elements, or device names (c:\) in the array 192 | // (so also no leading and trailing slashes - it does not distinguish 193 | // relative and absolute paths) 194 | function normalizeArray(parts, allowAboveRoot) { 195 | // if the path tries to go above the root, `up` ends up > 0 196 | var up = 0; 197 | for (var i = parts.length; i >= 0; i--) { 198 | var last = parts[i]; 199 | if (last == '.') { 200 | parts.splice(i, 1); 201 | } else if (last === '..') { 202 | parts.splice(i, 1); 203 | up++; 204 | } else if (up) { 205 | parts.splice(i, 1); 206 | up--; 207 | } 208 | } 209 | 210 | // if the path is allowed to go above the root, restore leading ..s 211 | if (allowAboveRoot) { 212 | for (; up--; up) { 213 | parts.unshift('..'); 214 | } 215 | } 216 | 217 | return parts; 218 | } 219 | 220 | // Regex to split a filename into [*, dir, basename, ext] 221 | // posix version 222 | var splitPathRe = /^(.+\/(?!$)|\/)?((?:.+?)?(\.[^.]*)?)$/; 223 | 224 | // path.resolve([from ...], to) 225 | // posix version 226 | exports.resolve = function() { 227 | var resolvedPath = '', 228 | resolvedAbsolute = false; 229 | 230 | for (var i = arguments.length; i >= -1 && !resolvedAbsolute; i--) { 231 | var path = (i >= 0) 232 | ? arguments[i] 233 | : process.cwd(); 234 | 235 | // Skip empty and invalid entries 236 | if (typeof path !== 'string' || !path) { 237 | continue; 238 | } 239 | 240 | resolvedPath = path + '/' + resolvedPath; 241 | resolvedAbsolute = path.charAt(0) === '/'; 242 | } 243 | 244 | // At this point the path should be resolved to a full absolute path, but 245 | // handle relative paths to be safe (might happen when process.cwd() fails) 246 | 247 | // Normalize the path 248 | resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) { 249 | return !!p; 250 | }), !resolvedAbsolute).join('/'); 251 | 252 | return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.'; 253 | }; 254 | 255 | // path.normalize(path) 256 | // posix version 257 | exports.normalize = function(path) { 258 | var isAbsolute = path.charAt(0) === '/', 259 | trailingSlash = path.slice(-1) === '/'; 260 | 261 | // Normalize the path 262 | path = normalizeArray(filter(path.split('/'), function(p) { 263 | return !!p; 264 | }), !isAbsolute).join('/'); 265 | 266 | if (!path && !isAbsolute) { 267 | path = '.'; 268 | } 269 | if (path && trailingSlash) { 270 | path += '/'; 271 | } 272 | 273 | return (isAbsolute ? '/' : '') + path; 274 | }; 275 | 276 | 277 | // posix version 278 | exports.join = function() { 279 | var paths = Array.prototype.slice.call(arguments, 0); 280 | return exports.normalize(filter(paths, function(p, index) { 281 | return p && typeof p === 'string'; 282 | }).join('/')); 283 | }; 284 | 285 | 286 | exports.dirname = function(path) { 287 | var dir = splitPathRe.exec(path)[1] || ''; 288 | var isWindows = false; 289 | if (!dir) { 290 | // No dirname 291 | return '.'; 292 | } else if (dir.length === 1 || 293 | (isWindows && dir.length <= 3 && dir.charAt(1) === ':')) { 294 | // It is just a slash or a drive letter with a slash 295 | return dir; 296 | } else { 297 | // It is a full dirname, strip trailing slash 298 | return dir.substring(0, dir.length - 1); 299 | } 300 | }; 301 | 302 | 303 | exports.basename = function(path, ext) { 304 | var f = splitPathRe.exec(path)[2] || ''; 305 | // TODO: make this comparison case-insensitive on windows? 306 | if (ext && f.substr(-1 * ext.length) === ext) { 307 | f = f.substr(0, f.length - ext.length); 308 | } 309 | return f; 310 | }; 311 | 312 | 313 | exports.extname = function(path) { 314 | return splitPathRe.exec(path)[3] || ''; 315 | }; 316 | }); 317 | 318 | require.define("__browserify_process",function(require,module,exports,__dirname,__filename,process){var process = module.exports = {}; 319 | 320 | process.nextTick = (function () { 321 | var queue = []; 322 | var canPost = typeof window !== 'undefined' 323 | && window.postMessage && window.addEventListener 324 | ; 325 | 326 | if (canPost) { 327 | window.addEventListener('message', function (ev) { 328 | if (ev.source === window && ev.data === 'browserify-tick') { 329 | ev.stopPropagation(); 330 | if (queue.length > 0) { 331 | var fn = queue.shift(); 332 | fn(); 333 | } 334 | } 335 | }, true); 336 | } 337 | 338 | return function (fn) { 339 | if (canPost) { 340 | queue.push(fn); 341 | window.postMessage('browserify-tick', '*'); 342 | } 343 | else setTimeout(fn, 0); 344 | }; 345 | })(); 346 | 347 | process.title = 'browser'; 348 | process.browser = true; 349 | process.env = {}; 350 | process.argv = []; 351 | 352 | process.binding = function (name) { 353 | if (name === 'evals') return (require)('vm') 354 | else throw new Error('No such module. (Possibly not yet loaded)') 355 | }; 356 | 357 | (function () { 358 | var cwd = '/'; 359 | var path; 360 | process.cwd = function () { return cwd }; 361 | process.chdir = function (dir) { 362 | if (!path) path = require('path'); 363 | cwd = path.resolve(dir, cwd); 364 | }; 365 | })(); 366 | }); 367 | 368 | require.define("/lib/jwcrypto.js",function(require,module,exports,__dirname,__filename,process){/* This Source Code Form is subject to the terms of the Mozilla Public 369 | * License, v. 2.0. If a copy of the MPL was not distributed with this 370 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 371 | 372 | /* 373 | * the new jwcrypto API 374 | */ 375 | 376 | var algs = require("./algs/index"), 377 | utils = require("./utils"), 378 | delay = utils.delay, 379 | rng = require("./rng"), 380 | libs = require("../libs/minimal"), 381 | version = require("./version"); 382 | 383 | var RNG = new rng.RNG(); 384 | 385 | var IS_SEEDED = false; 386 | var POST_SEED_CALLBACKS = []; 387 | 388 | // start autoseeding 389 | // queue up the things waiting for seeds 390 | RNG.autoseed(function() { 391 | // mark this true so that, in case some of the callbacks in 392 | // POST_SEED_CALLBACKS do asynchronous things, the POST_SEED_CALLBACKS 393 | // array will no longer be modified. 394 | IS_SEEDED = true; 395 | 396 | // go through callbacks 397 | for (var i = 0; i < POST_SEED_CALLBACKS.length; i++) { 398 | POST_SEED_CALLBACKS[i](); 399 | } 400 | 401 | // clean up as null so that weird egregious errors will 402 | // show up (e.g. double seeding.) 403 | POST_SEED_CALLBACKS = null; 404 | }); 405 | 406 | function waitForSeed(doStuff) { 407 | if (IS_SEEDED) { 408 | return doStuff(); 409 | } else { 410 | POST_SEED_CALLBACKS.push(doStuff); 411 | } 412 | } 413 | 414 | function NoSuchAlgorithmException(message) { 415 | this.message = message; 416 | this.toString = function() { return "No such algorithm: "+this.message; }; 417 | } 418 | 419 | function MalformedException(message) { 420 | this.message = message; 421 | this.toString = function() { return "malformed input: "+this.message; }; 422 | } 423 | 424 | exports.generateKeypair = function(opts, cb) { 425 | cb = delay(cb); 426 | var algObject = algs.ALGS[opts.algorithm]; 427 | if (!algObject) 428 | throw new algs.NotImplementedException("algorithm " + opts.algorithm + " not implemented"); 429 | 430 | waitForSeed(function() { 431 | // generate on the specific algorithm 432 | // no progress callback 433 | algObject.generate(opts.keysize, RNG, cb); 434 | }); 435 | }; 436 | 437 | exports.loadPublicKey = function(str) { 438 | return algs.PublicKey.deserialize(str); 439 | }; 440 | 441 | exports.loadPublicKeyFromObject = function(obj) { 442 | return algs.PublicKey.fromSimpleObject(obj); 443 | }; 444 | 445 | exports.loadSecretKey = function(str) { 446 | return algs.SecretKey.deserialize(str); 447 | }; 448 | 449 | exports.loadSecretKeyFromObject = function(obj) { 450 | return algs.SecretKey.fromSimpleObject(obj); 451 | }; 452 | 453 | 454 | exports.sign = function(payload, secretKey, cb) { 455 | var header = {"alg": secretKey.getAlgorithm()}; 456 | var algBytes = utils.base64urlencode(JSON.stringify(header)); 457 | var jsonBytes = utils.base64urlencode(JSON.stringify(payload)); 458 | 459 | waitForSeed(function() { 460 | secretKey.sign(algBytes + "." + jsonBytes, RNG, function() {}, function(rawSignature) { 461 | var signatureValue = utils.hex2b64urlencode(rawSignature); 462 | 463 | delay(cb)(null, algBytes + "." + jsonBytes + "." + signatureValue); 464 | }); 465 | }); 466 | }; 467 | 468 | // extract components 469 | var extractComponents = function(signedObject) { 470 | if (typeof(signedObject) != 'string') 471 | throw new MalformedException("malformed signature"); 472 | 473 | var parts = signedObject.split("."); 474 | if (parts.length != 3) { 475 | throw new MalformedException("signed object must have three parts, this one has " + parts.length); 476 | } 477 | 478 | var headerSegment = parts[0]; 479 | var payloadSegment = parts[1]; 480 | var cryptoSegment = parts[2]; 481 | 482 | // we verify based on the actual string 483 | // FIXME: we should validate that the header contains only proper fields 484 | var header = JSON.parse(utils.base64urldecode(headerSegment)); 485 | var payload = JSON.parse(utils.base64urldecode(payloadSegment)); 486 | var signature = utils.b64urltohex(cryptoSegment); 487 | 488 | return {header: header, 489 | payload: payload, 490 | signature: signature, 491 | headerSegment: headerSegment, 492 | payloadSegment: payloadSegment, 493 | cryptoSegment: cryptoSegment}; 494 | }; 495 | 496 | exports.extractComponents = extractComponents; 497 | 498 | exports.verify = function(signedObject, publicKey, cb) { 499 | cb = delay(cb); 500 | try { 501 | var components = extractComponents(signedObject); 502 | 503 | // check that algorithm matches 504 | if (publicKey.getAlgorithm() != components.header.alg) { 505 | cb("invalid signature"); 506 | return; 507 | } 508 | } catch (x) { 509 | cb("malformed signature"); 510 | return; 511 | } 512 | 513 | // decode the signature, and verify it 514 | publicKey.verify(components.headerSegment + "." + components.payloadSegment, components.signature, function(err, result) { 515 | if (err) 516 | return cb("malformed signature"); 517 | 518 | if (!result) 519 | return cb("invalid signature"); 520 | 521 | return cb(null, components.payload); 522 | }); 523 | }; 524 | 525 | // 526 | // probably for future stuff 527 | // 528 | 529 | // for symmetric keys, it's plural because encryption and MACing. 530 | exports.generateKeys = function(opts, cb) { 531 | throw new Error("Not Implemented"); 532 | }; 533 | 534 | exports.encrypt = function(payload, encryptionAndMACKeys, cb) { 535 | throw new Error("Not Implemented"); 536 | }; 537 | 538 | exports.decrypt = function(encryptedPayload, encryptionAndMACKeys, cb) { 539 | throw new Error("Not Implemented"); 540 | }; 541 | 542 | // entropy here is a string that is expected to be relatively high entropy 543 | exports.addEntropy = function(entropy) { 544 | RNG.addEntropy(entropy); 545 | }; 546 | 547 | exports.assertion = require("./assertion"); 548 | exports.cert = require("./cert"); 549 | 550 | // versioning 551 | exports.getDataFormatVersion = version.getDataFormatVersion; 552 | exports.setDataFormatVersion = version.setDataFormatVersion; 553 | }); 554 | 555 | require.define("/lib/algs/index.js",function(require,module,exports,__dirname,__filename,process){/* This Source Code Form is subject to the terms of the Mozilla Public 556 | * License, v. 2.0. If a copy of the MPL was not distributed with this 557 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 558 | 559 | /* 560 | * baseline objects for all algorithms 561 | */ 562 | 563 | function NotImplementedException(message) { 564 | this.message = message; 565 | this.toString = function() { return "Not implemented: "+this.message; }; 566 | } 567 | 568 | var ALGS = { 569 | }; 570 | 571 | function NotImplementedException(message) { 572 | this.message = message; 573 | this.toString = function() { return "Not implemented: "+this.message; }; 574 | } 575 | 576 | function KeyPair() { 577 | this.publicKey = null; 578 | this.secretKey = null; 579 | this.algorithm = null; 580 | this.keysize = null; 581 | }; 582 | 583 | var _getAlgorithm = function _getAlgorithm() { 584 | return this.algorithm + this.keysize.toString(); 585 | }; 586 | 587 | KeyPair.prototype = { 588 | getAlgorithm: _getAlgorithm 589 | }; 590 | 591 | exports.register = function(alg, cls) { 592 | ALGS[alg] = cls; 593 | }; 594 | 595 | 596 | function PublicKey() { 597 | } 598 | 599 | PublicKey.prototype = { 600 | // produce a ready-to-be-JSON'ed object 601 | toSimpleObject: function() { 602 | var obj = {algorithm: this.algorithm}; 603 | this.serializeToObject(obj); 604 | return obj; 605 | }, 606 | 607 | // ok, JSON'ify it 608 | serialize: function() { 609 | return JSON.stringify(this.toSimpleObject()); 610 | }, 611 | 612 | getAlgorithm : _getAlgorithm 613 | }; 614 | 615 | PublicKey.fromSimpleObject = function(obj) { 616 | if (!ALGS[obj.algorithm]) 617 | throw new NotImplementedException("no such algorithm: " + obj.algorithm); 618 | 619 | var pk = new ALGS[obj.algorithm].PublicKey(); 620 | pk.algorithm = obj.algorithm; 621 | pk.deserializeFromObject(obj); 622 | return pk; 623 | }; 624 | 625 | PublicKey.deserialize = function(str) { 626 | var obj = JSON.parse(str); 627 | return PublicKey.fromSimpleObject(obj); 628 | }; 629 | 630 | 631 | function SecretKey() { 632 | } 633 | 634 | SecretKey.prototype = { 635 | toSimpleObject: function() { 636 | var obj = {algorithm: this.algorithm}; 637 | this.serializeToObject(obj); 638 | return obj; 639 | }, 640 | 641 | serialize: function() { 642 | return JSON.stringify(this.toSimpleObject()); 643 | }, 644 | 645 | getAlgorithm: _getAlgorithm 646 | 647 | }; 648 | 649 | SecretKey.fromSimpleObject = function(obj) { 650 | if (!ALGS[obj.algorithm]) 651 | throw new NotImplementedException("no such algorithm: " + obj.algorithm); 652 | 653 | var sk = new ALGS[obj.algorithm].SecretKey(); 654 | sk.algorithm = obj.algorithm; 655 | sk.deserializeFromObject(obj); 656 | return sk; 657 | }; 658 | 659 | SecretKey.deserialize = function(str) { 660 | var obj = JSON.parse(str); 661 | return SecretKey.fromSimpleObject(obj); 662 | }; 663 | 664 | 665 | exports.ALGS = ALGS; 666 | exports.PublicKey = PublicKey; 667 | exports.SecretKey = SecretKey; 668 | exports.KeyPair = KeyPair; 669 | exports.NotImplementedException = NotImplementedException; 670 | }); 671 | 672 | require.define("/lib/utils.js",function(require,module,exports,__dirname,__filename,process){/* This Source Code Form is subject to the terms of the Mozilla Public 673 | * License, v. 2.0. If a copy of the MPL was not distributed with this 674 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 675 | 676 | var libs = require("../libs/minimal"); 677 | 678 | function InputException(message) { 679 | this.message = message; 680 | this.toString = function() { return "Malformed input: "+this.message; }; 681 | } 682 | 683 | // patch the window object; 684 | if (typeof(window) === "undefined") 685 | var window = libs.window; 686 | 687 | var int2char = libs.int2char; 688 | 689 | // convert a base64url string to hex 690 | var b64urlmap="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"; 691 | function b64urltohex(s) { 692 | var ret = ""; 693 | var i; 694 | var k = 0; // b64 state, 0-3 695 | var slop; 696 | for(i = 0; i < s.length; ++i) { 697 | var v = b64urlmap.indexOf(s.charAt(i)); 698 | if(v < 0) continue; 699 | if(k == 0) { 700 | ret += int2char(v >> 2); 701 | slop = v & 3; 702 | k = 1; 703 | } 704 | else if(k == 1) { 705 | ret += int2char((slop << 2) | (v >> 4)); 706 | slop = v & 0xf; 707 | k = 2; 708 | } 709 | else if(k == 2) { 710 | ret += int2char(slop); 711 | ret += int2char(v >> 2); 712 | slop = v & 3; 713 | k = 3; 714 | } 715 | else { 716 | ret += int2char((slop << 2) | (v >> 4)); 717 | ret += int2char(v & 0xf); 718 | k = 0; 719 | } 720 | } 721 | if(k == 1) 722 | ret += int2char(slop << 2); 723 | 724 | // initial 0? only one for now 725 | if (ret[0] == '0') 726 | return ret.substring(1); 727 | else 728 | return ret; 729 | } 730 | 731 | function hex2b64urlencode(arg) { 732 | // consider the case where the hex is not a 733 | // proper number of octets. 734 | if ((arg.length % 2) != 0) 735 | arg = "0" + arg; 736 | 737 | return libs.hex2b64(arg).split('=')[0] 738 | .replace(/\+/g, '-') // 62nd char of encoding 739 | .replace(/\//g, '_'); // 63rd char of encoding 740 | } 741 | 742 | function base64urlencode(arg) { 743 | var s = window.btoa(arg); 744 | s = s.split('=')[0]; // Remove any trailing '='s 745 | s = s.replace(/\+/g, '-'); // 62nd char of encoding 746 | s = s.replace(/\//g, '_'); // 63rd char of encoding 747 | // TODO optimize this; we can do much better 748 | return s; 749 | } 750 | 751 | function base64urldecode(arg) { 752 | var s = arg; 753 | s = s.replace(/-/g, '+'); // 62nd char of encoding 754 | s = s.replace(/_/g, '/'); // 63rd char of encoding 755 | switch (s.length % 4) // Pad with trailing '='s 756 | { 757 | case 0: break; // No pad chars in this case 758 | case 2: s += "=="; break; // Two pad chars 759 | case 3: s += "="; break; // One pad char 760 | default: throw new InputException("Illegal base64url string!"); 761 | } 762 | return window.atob(s); // Standard base64 decoder 763 | } 764 | 765 | function copyInto(oldObj, newObj) { 766 | for (var k in oldObj) { 767 | if (oldObj.hasOwnProperty(k)) newObj[k] = oldObj[k]; 768 | } 769 | } 770 | 771 | function getDate(d) { 772 | if (!d) 773 | return null; 774 | 775 | var r = new Date(); 776 | r.setTime(d); 777 | return r; 778 | } 779 | 780 | // delay a function 781 | function delay(cb) { 782 | var delayedFunction = function() { 783 | var funcArguments = arguments; 784 | process.nextTick(function() { 785 | cb.apply(cb, funcArguments); 786 | }); 787 | }; 788 | 789 | return delayedFunction; 790 | } 791 | 792 | exports.b64urltohex = b64urltohex; 793 | exports.hex2b64urlencode = hex2b64urlencode; 794 | exports.base64urldecode = base64urldecode; 795 | exports.base64urlencode = base64urlencode; 796 | exports.copyInto = copyInto; 797 | exports.getDate = getDate; 798 | exports.delay = delay;}); 799 | 800 | require.define("/libs/minimal.js",function(require,module,exports,__dirname,__filename,process){// faking some objects so all goes well 801 | if (typeof(OVERRIDE) == "undefined") { 802 | var navigator = { 803 | appName: "Netscape" 804 | }; 805 | 806 | var window = { 807 | atob: function(str) { 808 | return new Buffer(str, 'base64').toString('utf-8'); 809 | }, 810 | btoa: function(str) { 811 | return new Buffer(str).toString('base64'); 812 | } 813 | }; 814 | 815 | var alert = function(msg) { 816 | console.log(msg); 817 | }; 818 | } else { 819 | var navigator = OVERRIDE.navigator; 820 | var window = OVERRIDE.window; 821 | } 822 | 823 | 824 | var sha1 = { 825 | hex: function(){ 826 | throw new Error("Not Implemented"); 827 | } 828 | }; 829 | 830 | var sha256 = { 831 | hex: function(i) {return sjcl.codec.hex.fromBits(sjcl.hash.sha256.hash(i));} 832 | }; 833 | 834 | (function () { 835 | var 836 | characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', 837 | fromCharCode = String.fromCharCode, 838 | INVALID_CHARACTER_ERR = (function () { 839 | // fabricate a suitable error object 840 | try { document.createElement('$'); } 841 | catch (error) { return error; }}()); 842 | 843 | // encoder 844 | window.btoa || ( 845 | window.btoa = function (string) { 846 | var 847 | a, b, b1, b2, b3, b4, c, i = 0, 848 | len = string.length, max = Math.max, result = ''; 849 | 850 | while (i < len) { 851 | a = string.charCodeAt(i++) || 0; 852 | b = string.charCodeAt(i++) || 0; 853 | c = string.charCodeAt(i++) || 0; 854 | 855 | if (max(a, b, c) > 0xFF) { 856 | throw INVALID_CHARACTER_ERR; 857 | } 858 | 859 | b1 = (a >> 2) & 0x3F; 860 | b2 = ((a & 0x3) << 4) | ((b >> 4) & 0xF); 861 | b3 = ((b & 0xF) << 2) | ((c >> 6) & 0x3); 862 | b4 = c & 0x3F; 863 | 864 | if (!b) { 865 | b3 = b4 = 64; 866 | } else if (!c) { 867 | b4 = 64; 868 | } 869 | result += characters.charAt(b1) + characters.charAt(b2) + characters.charAt(b3) + characters.charAt(b4); 870 | } 871 | return result; 872 | }); 873 | 874 | // decoder 875 | window.atob || ( 876 | window.atob = function (string) { 877 | string = string.replace(/=+$/, ''); 878 | var 879 | a, b, b1, b2, b3, b4, c, i = 0, 880 | len = string.length, chars = []; 881 | 882 | if (len % 4 === 1) throw INVALID_CHARACTER_ERR; 883 | 884 | while (i < len) { 885 | b1 = characters.indexOf(string.charAt(i++)); 886 | b2 = characters.indexOf(string.charAt(i++)); 887 | b3 = characters.indexOf(string.charAt(i++)); 888 | b4 = characters.indexOf(string.charAt(i++)); 889 | 890 | a = ((b1 & 0x3F) << 2) | ((b2 >> 4) & 0x3); 891 | b = ((b2 & 0xF) << 4) | ((b3 >> 2) & 0xF); 892 | c = ((b3 & 0x3) << 6) | (b4 & 0x3F); 893 | 894 | chars.push(fromCharCode(a)); 895 | b && chars.push(fromCharCode(b)); 896 | c && chars.push(fromCharCode(c)); 897 | } 898 | return chars.join(''); 899 | }); 900 | })(); 901 | /* 902 | * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined 903 | * in FIPS 180-1 904 | * Version 2.2 Copyright Paul Johnston 2000 - 2009. 905 | * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet 906 | * Distributed under the BSD License 907 | * See http://pajhome.org.uk/crypt/md5 for details. 908 | */ 909 | 910 | /* 911 | * Configurable variables. You may need to tweak these to be compatible with 912 | * the server-side, but the defaults work in most cases. 913 | */ 914 | var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ 915 | var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ 916 | 917 | /* 918 | * These are the functions you'll usually want to call 919 | * They take string arguments and return either hex or base-64 encoded strings 920 | */ 921 | function hex_sha1(s) { return rstr2hex(rstr_sha1(str2rstr_utf8(s))); } 922 | function b64_sha1(s) { return rstr2b64(rstr_sha1(str2rstr_utf8(s))); } 923 | function any_sha1(s, e) { return rstr2any(rstr_sha1(str2rstr_utf8(s)), e); } 924 | function hex_hmac_sha1(k, d) 925 | { return rstr2hex(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); } 926 | function b64_hmac_sha1(k, d) 927 | { return rstr2b64(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d))); } 928 | function any_hmac_sha1(k, d, e) 929 | { return rstr2any(rstr_hmac_sha1(str2rstr_utf8(k), str2rstr_utf8(d)), e); } 930 | 931 | /* 932 | * Perform a simple self-test to see if the VM is working 933 | */ 934 | function sha1_vm_test() 935 | { 936 | return hex_sha1("abc").toLowerCase() == "a9993e364706816aba3e25717850c26c9cd0d89d"; 937 | } 938 | 939 | /* 940 | * Calculate the SHA1 of a raw string 941 | */ 942 | function rstr_sha1(s) 943 | { 944 | return binb2rstr(binb_sha1(rstr2binb(s), s.length * 8)); 945 | } 946 | 947 | /* 948 | * Calculate the HMAC-SHA1 of a key and some data (raw strings) 949 | */ 950 | function rstr_hmac_sha1(key, data) 951 | { 952 | var bkey = rstr2binb(key); 953 | if(bkey.length > 16) bkey = binb_sha1(bkey, key.length * 8); 954 | 955 | var ipad = Array(16), opad = Array(16); 956 | for(var i = 0; i < 16; i++) 957 | { 958 | ipad[i] = bkey[i] ^ 0x36363636; 959 | opad[i] = bkey[i] ^ 0x5C5C5C5C; 960 | } 961 | 962 | var hash = binb_sha1(ipad.concat(rstr2binb(data)), 512 + data.length * 8); 963 | return binb2rstr(binb_sha1(opad.concat(hash), 512 + 160)); 964 | } 965 | 966 | /* 967 | * Convert a raw string to a hex string 968 | */ 969 | function rstr2hex(input) 970 | { 971 | try { hexcase } catch(e) { hexcase=0; } 972 | var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; 973 | var output = ""; 974 | var x; 975 | for(var i = 0; i < input.length; i++) 976 | { 977 | x = input.charCodeAt(i); 978 | output += hex_tab.charAt((x >>> 4) & 0x0F) 979 | + hex_tab.charAt( x & 0x0F); 980 | } 981 | return output; 982 | } 983 | 984 | /* 985 | * Convert a raw string to a base-64 string 986 | */ 987 | function rstr2b64(input) 988 | { 989 | try { b64pad } catch(e) { b64pad=''; } 990 | var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 991 | var output = ""; 992 | var len = input.length; 993 | for(var i = 0; i < len; i += 3) 994 | { 995 | var triplet = (input.charCodeAt(i) << 16) 996 | | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0) 997 | | (i + 2 < len ? input.charCodeAt(i+2) : 0); 998 | for(var j = 0; j < 4; j++) 999 | { 1000 | if(i * 8 + j * 6 > input.length * 8) output += b64pad; 1001 | else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F); 1002 | } 1003 | } 1004 | return output; 1005 | } 1006 | 1007 | /* 1008 | * Convert a raw string to an arbitrary string encoding 1009 | */ 1010 | function rstr2any(input, encoding) 1011 | { 1012 | var divisor = encoding.length; 1013 | var remainders = Array(); 1014 | var i, q, x, quotient; 1015 | 1016 | /* Convert to an array of 16-bit big-endian values, forming the dividend */ 1017 | var dividend = Array(Math.ceil(input.length / 2)); 1018 | for(i = 0; i < dividend.length; i++) 1019 | { 1020 | dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1); 1021 | } 1022 | 1023 | /* 1024 | * Repeatedly perform a long division. The binary array forms the dividend, 1025 | * the length of the encoding is the divisor. Once computed, the quotient 1026 | * forms the dividend for the next step. We stop when the dividend is zero. 1027 | * All remainders are stored for later use. 1028 | */ 1029 | while(dividend.length > 0) 1030 | { 1031 | quotient = Array(); 1032 | x = 0; 1033 | for(i = 0; i < dividend.length; i++) 1034 | { 1035 | x = (x << 16) + dividend[i]; 1036 | q = Math.floor(x / divisor); 1037 | x -= q * divisor; 1038 | if(quotient.length > 0 || q > 0) 1039 | quotient[quotient.length] = q; 1040 | } 1041 | remainders[remainders.length] = x; 1042 | dividend = quotient; 1043 | } 1044 | 1045 | /* Convert the remainders to the output string */ 1046 | var output = ""; 1047 | for(i = remainders.length - 1; i >= 0; i--) 1048 | output += encoding.charAt(remainders[i]); 1049 | 1050 | /* Append leading zero equivalents */ 1051 | var full_length = Math.ceil(input.length * 8 / 1052 | (Math.log(encoding.length) / Math.log(2))) 1053 | for(i = output.length; i < full_length; i++) 1054 | output = encoding[0] + output; 1055 | 1056 | return output; 1057 | } 1058 | 1059 | /* 1060 | * Encode a string as utf-8. 1061 | * For efficiency, this assumes the input is valid utf-16. 1062 | */ 1063 | function str2rstr_utf8(input) 1064 | { 1065 | var output = ""; 1066 | var i = -1; 1067 | var x, y; 1068 | 1069 | while(++i < input.length) 1070 | { 1071 | /* Decode utf-16 surrogate pairs */ 1072 | x = input.charCodeAt(i); 1073 | y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0; 1074 | if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF) 1075 | { 1076 | x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF); 1077 | i++; 1078 | } 1079 | 1080 | /* Encode output as utf-8 */ 1081 | if(x <= 0x7F) 1082 | output += String.fromCharCode(x); 1083 | else if(x <= 0x7FF) 1084 | output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F), 1085 | 0x80 | ( x & 0x3F)); 1086 | else if(x <= 0xFFFF) 1087 | output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F), 1088 | 0x80 | ((x >>> 6 ) & 0x3F), 1089 | 0x80 | ( x & 0x3F)); 1090 | else if(x <= 0x1FFFFF) 1091 | output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07), 1092 | 0x80 | ((x >>> 12) & 0x3F), 1093 | 0x80 | ((x >>> 6 ) & 0x3F), 1094 | 0x80 | ( x & 0x3F)); 1095 | } 1096 | return output; 1097 | } 1098 | 1099 | /* 1100 | * Encode a string as utf-16 1101 | */ 1102 | function str2rstr_utf16le(input) 1103 | { 1104 | var output = ""; 1105 | for(var i = 0; i < input.length; i++) 1106 | output += String.fromCharCode( input.charCodeAt(i) & 0xFF, 1107 | (input.charCodeAt(i) >>> 8) & 0xFF); 1108 | return output; 1109 | } 1110 | 1111 | function str2rstr_utf16be(input) 1112 | { 1113 | var output = ""; 1114 | for(var i = 0; i < input.length; i++) 1115 | output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF, 1116 | input.charCodeAt(i) & 0xFF); 1117 | return output; 1118 | } 1119 | 1120 | /* 1121 | * Convert a raw string to an array of big-endian words 1122 | * Characters >255 have their high-byte silently ignored. 1123 | */ 1124 | function rstr2binb(input) 1125 | { 1126 | var output = Array(input.length >> 2); 1127 | for(var i = 0; i < output.length; i++) 1128 | output[i] = 0; 1129 | for(var i = 0; i < input.length * 8; i += 8) 1130 | output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32); 1131 | return output; 1132 | } 1133 | 1134 | /* 1135 | * Convert an array of big-endian words to a string 1136 | */ 1137 | function binb2rstr(input) 1138 | { 1139 | var output = ""; 1140 | for(var i = 0; i < input.length * 32; i += 8) 1141 | output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF); 1142 | return output; 1143 | } 1144 | 1145 | /* 1146 | * Calculate the SHA-1 of an array of big-endian words, and a bit length 1147 | */ 1148 | function binb_sha1(x, len) 1149 | { 1150 | /* append padding */ 1151 | x[len >> 5] |= 0x80 << (24 - len % 32); 1152 | x[((len + 64 >> 9) << 4) + 15] = len; 1153 | 1154 | var w = Array(80); 1155 | var a = 1732584193; 1156 | var b = -271733879; 1157 | var c = -1732584194; 1158 | var d = 271733878; 1159 | var e = -1009589776; 1160 | 1161 | for(var i = 0; i < x.length; i += 16) 1162 | { 1163 | var olda = a; 1164 | var oldb = b; 1165 | var oldc = c; 1166 | var oldd = d; 1167 | var olde = e; 1168 | 1169 | for(var j = 0; j < 80; j++) 1170 | { 1171 | if(j < 16) w[j] = x[i + j]; 1172 | else w[j] = bit_rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); 1173 | var t = safe_add(safe_add(bit_rol(a, 5), sha1_ft(j, b, c, d)), 1174 | safe_add(safe_add(e, w[j]), sha1_kt(j))); 1175 | e = d; 1176 | d = c; 1177 | c = bit_rol(b, 30); 1178 | b = a; 1179 | a = t; 1180 | } 1181 | 1182 | a = safe_add(a, olda); 1183 | b = safe_add(b, oldb); 1184 | c = safe_add(c, oldc); 1185 | d = safe_add(d, oldd); 1186 | e = safe_add(e, olde); 1187 | } 1188 | return Array(a, b, c, d, e); 1189 | 1190 | } 1191 | 1192 | /* 1193 | * Perform the appropriate triplet combination function for the current 1194 | * iteration 1195 | */ 1196 | function sha1_ft(t, b, c, d) 1197 | { 1198 | if(t < 20) return (b & c) | ((~b) & d); 1199 | if(t < 40) return b ^ c ^ d; 1200 | if(t < 60) return (b & c) | (b & d) | (c & d); 1201 | return b ^ c ^ d; 1202 | } 1203 | 1204 | /* 1205 | * Determine the appropriate additive constant for the current iteration 1206 | */ 1207 | function sha1_kt(t) 1208 | { 1209 | return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : 1210 | (t < 60) ? -1894007588 : -899497514; 1211 | } 1212 | 1213 | /* 1214 | * Add integers, wrapping at 2^32. This uses 16-bit operations internally 1215 | * to work around bugs in some JS interpreters. 1216 | */ 1217 | function safe_add(x, y) 1218 | { 1219 | var lsw = (x & 0xFFFF) + (y & 0xFFFF); 1220 | var msw = (x >> 16) + (y >> 16) + (lsw >> 16); 1221 | return (msw << 16) | (lsw & 0xFFFF); 1222 | } 1223 | 1224 | /* 1225 | * Bitwise rotate a 32-bit number to the left. 1226 | */ 1227 | function bit_rol(num, cnt) 1228 | { 1229 | return (num << cnt) | (num >>> (32 - cnt)); 1230 | }"use strict";var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}}; 1231 | sjcl.cipher.aes=function(a){this.h[0][0][0]||this.w();var b,c,d,e,f=this.h[0][4],g=this.h[1];b=a.length;var h=1;if(b!==4&&b!==6&&b!==8)throw new sjcl.exception.invalid("invalid aes key size");this.a=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(a%b===0||b===8&&a%b===4){c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255];if(a%b===0){c=c<<8^c>>>24^h<<24;h=h<<1^(h>>7)*283}}d[a]=d[a-b]^c}for(b=0;a;b++,a--){c=d[b&3?a:a-4];e[b]=a<=4||b<4?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^ 1232 | g[3][f[c&255]]}}; 1233 | sjcl.cipher.aes.prototype={encrypt:function(a){return this.H(a,0)},decrypt:function(a){return this.H(a,1)},h:[[[],[],[],[],[]],[[],[],[],[],[]]],w:function(){var a=this.h[0],b=this.h[1],c=a[4],d=b[4],e,f,g,h=[],i=[],k,j,l,m;for(e=0;e<0x100;e++)i[(h[e]=e<<1^(e>>7)*283)^e]=e;for(f=g=0;!c[f];f^=k||1,g=i[g]||1){l=g^g<<1^g<<2^g<<3^g<<4;l=l>>8^l&255^99;c[f]=l;d[l]=f;j=h[e=h[k=h[f]]];m=j*0x1010101^e*0x10001^k*0x101^f*0x1010100;j=h[l]*0x101^l*0x1010100;for(e=0;e<4;e++){a[e][f]=j=j<<24^j>>>8;b[e][l]=m=m<<24^m>>>8}}for(e= 1234 | 0;e<5;e++){a[e]=a[e].slice(0);b[e]=b[e].slice(0)}},H:function(a,b){if(a.length!==4)throw new sjcl.exception.invalid("invalid aes block size");var c=this.a[b],d=a[0]^c[0],e=a[b?3:1]^c[1],f=a[2]^c[2];a=a[b?1:3]^c[3];var g,h,i,k=c.length/4-2,j,l=4,m=[0,0,0,0];g=this.h[b];var n=g[0],o=g[1],p=g[2],q=g[3],r=g[4];for(j=0;j>>24]^o[e>>16&255]^p[f>>8&255]^q[a&255]^c[l];h=n[e>>>24]^o[f>>16&255]^p[a>>8&255]^q[d&255]^c[l+1];i=n[f>>>24]^o[a>>16&255]^p[d>>8&255]^q[e&255]^c[l+2];a=n[a>>>24]^o[d>>16& 1235 | 255]^p[e>>8&255]^q[f&255]^c[l+3];l+=4;d=g;e=h;f=i}for(j=0;j<4;j++){m[b?3&-j:j]=r[d>>>24]<<24^r[e>>16&255]<<16^r[f>>8&255]<<8^r[a&255]^c[l++];g=d;d=e;e=f;f=a;a=g}return m}}; 1236 | sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===undefined?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<0&&b)a[c-1]=sjcl.bitArray.partial(b,a[c-1]&2147483648>>b-1,1);return a},partial:function(a,b,c){if(a===32)return b;return(c?b|0:b<<32-a)+a*0x10000000000},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return false;var c=0,d;for(d=0;d=32;b-=32){d.push(c);c=0}if(b===0)return d.concat(a);for(e=0;e>>b);c=a[e]<<32-b}e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,b+a>32?c:d.pop(),1));return d},k:function(a,b){return[a[0]^b[0],a[1]^b[1],a[2]^b[2],a[3]^b[3]]}}; 1239 | sjcl.codec.utf8String={fromBits:function(a){var b="",c=sjcl.bitArray.bitLength(a),d,e;for(d=0;d>>24);e<<=8}return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c>>e)>>>26);if(e<6){g=a[c]<<6-e;e+=26;c++}else{g<<=6;e-=6}}for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d=0,e=sjcl.codec.base64.D,f=0,g;if(b)e=e.substr(0,62)+"-_";for(b=0;b26){d-=26;c.push(f^g>>>d);f=g<<32-d}else{d+=6;f^=g<<32-d}}d&56&&c.push(sjcl.bitArray.partial(d&56,f,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.a[0]||this.w();if(a){this.n=a.n.slice(0);this.i=a.i.slice(0);this.e=a.e}else this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()}; 1243 | sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.n=this.N.slice(0);this.i=[];this.e=0;return this},update:function(a){if(typeof a==="string")a=sjcl.codec.utf8String.toBits(a);var b,c=this.i=sjcl.bitArray.concat(this.i,a);b=this.e;a=this.e=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)this.C(c.splice(0,16));return this},finalize:function(){var a,b=this.i,c=this.n;b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.e/ 1244 | 4294967296));for(b.push(this.e|0);b.length;)this.C(b.splice(0,16));this.reset();return c},N:[],a:[],w:function(){function a(e){return(e-Math.floor(e))*0x100000000|0}var b=0,c=2,d;a:for(;b<64;c++){for(d=2;d*d<=c;d++)if(c%d===0)continue a;if(b<8)this.N[b]=a(Math.pow(c,0.5));this.a[b]=a(Math.pow(c,1/3));b++}},C:function(a){var b,c,d=a.slice(0),e=this.n,f=this.a,g=e[0],h=e[1],i=e[2],k=e[3],j=e[4],l=e[5],m=e[6],n=e[7];for(a=0;a<64;a++){if(a<16)b=d[a];else{b=d[a+1&15];c=d[a+14&15];b=d[a&15]=(b>>>7^b>>>18^ 1245 | b>>>3^b<<25^b<<14)+(c>>>17^c>>>19^c>>>10^c<<15^c<<13)+d[a&15]+d[a+9&15]|0}b=b+n+(j>>>6^j>>>11^j>>>25^j<<26^j<<21^j<<7)+(m^j&(l^m))+f[a];n=m;m=l;l=j;j=k+b|0;k=i;i=h;h=g;g=b+(h&i^k&(h^i))+(h>>>2^h>>>13^h>>>22^h<<30^h<<19^h<<10)|0}e[0]=e[0]+g|0;e[1]=e[1]+h|0;e[2]=e[2]+i|0;e[3]=e[3]+k|0;e[4]=e[4]+j|0;e[5]=e[5]+l|0;e[6]=e[6]+m|0;e[7]=e[7]+n|0}}; 1246 | sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,i=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];if(i<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(f=2;f<4&&k>>>8*f;f++);if(f<15-i)f=15-i;c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.G(a,b,c,d,e,f);g=sjcl.mode.ccm.I(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),i=f.clamp(b,h-e),k=f.bitSlice(b, 1247 | h-e);h=(h-e)/8;if(g<7)throw new sjcl.exception.invalid("ccm: iv must be at least 7 bytes");for(b=2;b<4&&h>>>8*b;b++);if(b<15-g)b=15-g;c=f.clamp(c,8*(15-b));i=sjcl.mode.ccm.I(a,i,c,k,e,b);a=sjcl.mode.ccm.G(a,i.data,c,d,e,b);if(!f.equal(i.tag,a))throw new sjcl.exception.corrupt("ccm: tag doesn't match");return i.data},G:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,i=h.k;e/=8;if(e%2||e<4||e>16)throw new sjcl.exception.invalid("ccm: invalid tag length");if(d.length>0xffffffff||b.length>0xffffffff)throw new sjcl.exception.bug("ccm: can't deal with 4GiB or more data"); 1248 | f=[h.partial(8,(d.length?64:0)|e-2<<2|f-1)];f=h.concat(f,c);f[3]|=h.bitLength(b)/8;f=a.encrypt(f);if(d.length){c=h.bitLength(d)/8;if(c<=65279)g=[h.partial(16,c)];else if(c<=0xffffffff)g=h.concat([h.partial(16,65534)],[c]);g=h.concat(g,d);for(d=0;d>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^(a[0]>>>31)*135]}};sjcl.misc.hmac=function(a,b){this.M=b=b||sjcl.hash.sha256;var c=[[],[]],d=b.prototype.blockSize/32;this.l=[new b,new b];if(a.length>d)a=b.hash(a);for(b=0;b0;){b++;e>>>=1}this.b[g].update([d,this.J++,2,b,f,a.length].concat(a));break;case "string":if(b===undefined)b=a.length;this.b[g].update([d,this.J++,3,b,f,a.length]);this.b[g].update(a);break;default:throw new sjcl.exception.bug("random: addEntropy only supports number, array or string");}this.j[g]+=b;this.f+=b;if(h===0){this.isReady()!==0&&this.K("seeded",Math.max(this.g, 1258 | this.f));this.K("progress",this.getProgress())}},isReady:function(a){a=this.B[a!==undefined?a:this.t];return this.g&&this.g>=a?this.j[0]>80&&(new Date).valueOf()>this.O?3:1:this.f>=a?2:0},getProgress:function(a){a=this.B[a?a:this.t];return this.g>=a?1["0"]:this.f>a?1["0"]:this.f/a},startCollectors:function(){if(!this.m){if(window.addEventListener){window.addEventListener("load",this.o,false);window.addEventListener("mousemove",this.p,false)}else if(document.attachEvent){document.attachEvent("onload", 1259 | this.o);document.attachEvent("onmousemove",this.p)}else throw new sjcl.exception.bug("can't attach event");this.m=true}},stopCollectors:function(){if(this.m){if(window.removeEventListener){window.removeEventListener("load",this.o,false);window.removeEventListener("mousemove",this.p,false)}else if(window.detachEvent){window.detachEvent("onload",this.o);window.detachEvent("onmousemove",this.p)}this.m=false}},addEventListener:function(a,b){this.r[a][this.Q++]=b},removeEventListener:function(a,b){var c; 1260 | a=this.r[a];var d=[];for(c in a)a.hasOwnProperty(c)&&a[c]===b&&d.push(c);for(b=0;b=1<this.g)this.g=c;this.z++; 1262 | this.T(b)},p:function(a){sjcl.random.addEntropy([a.x||a.clientX||a.offsetX,a.y||a.clientY||a.offsetY],2,"mouse")},o:function(){sjcl.random.addEntropy(new Date,2,"loadtime")},K:function(a,b){var c;a=sjcl.random.r[a];var d=[];for(c in a)a.hasOwnProperty(c)&&d.push(a[c]);for(c=0;c 1264 | 4)throw new sjcl.exception.invalid("json encrypt: invalid parameters");if(typeof a==="string"){c=sjcl.misc.cachedPbkdf2(a,f);a=c.key.slice(0,f.ks/32);f.salt=c.salt}if(typeof b==="string")b=sjcl.codec.utf8String.toBits(b);c=new sjcl.cipher[f.cipher](a);e.c(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(c,b,f.iv,f.adata,f.ts);return e.encode(e.V(f,e.defaults))},decrypt:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.c(e.c(e.c({},e.defaults),e.decode(b)),c,true);if(typeof b.salt==="string")b.salt= 1265 | sjcl.codec.base64.toBits(b.salt);if(typeof b.iv==="string")b.iv=sjcl.codec.base64.toBits(b.iv);if(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||typeof a==="string"&&b.iter<=100||b.ts!==64&&b.ts!==96&&b.ts!==128||b.ks!==128&&b.ks!==192&&b.ks!==0x100||!b.iv||b.iv.length<2||b.iv.length>4)throw new sjcl.exception.invalid("json decrypt: invalid parameters");if(typeof a==="string"){c=sjcl.misc.cachedPbkdf2(a,b);a=c.key.slice(0,b.ks/32);b.salt=c.salt}c=new sjcl.cipher[b.cipher](a);c=sjcl.mode[b.mode].decrypt(c, 1266 | b.ct,b.iv,b.adata,b.ts);e.c(d,b);d.key=a;return sjcl.codec.utf8String.fromBits(c)},encode:function(a){var b,c="{",d="";for(b in a)if(a.hasOwnProperty(b)){if(!b.match(/^[a-z0-9]+$/i))throw new sjcl.exception.invalid("json encode: invalid property name");c+=d+b+":";d=",";switch(typeof a[b]){case "number":case "boolean":c+=a[b];break;case "string":c+='"'+escape(a[b])+'"';break;case "object":c+='"'+sjcl.codec.base64.fromBits(a[b],1)+'"';break;default:throw new sjcl.exception.bug("json encode: unsupported type"); 1267 | }}return c+"}"},decode:function(a){a=a.replace(/\s/g,"");if(!a.match(/^\{.*\}$/))throw new sjcl.exception.invalid("json decode: this isn't json!");a=a.replace(/^\{|\}$/g,"").split(/,/);var b={},c,d;for(c=0;c> 6) + b64map.charAt(c & 63); 1285 | } 1286 | if(i+1 == h.length) { 1287 | c = parseInt(h.substring(i,i+1),16); 1288 | ret += b64map.charAt(c << 2); 1289 | } 1290 | else if(i+2 == h.length) { 1291 | c = parseInt(h.substring(i,i+2),16); 1292 | ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4); 1293 | } 1294 | while((ret.length & 3) > 0) ret += b64pad; 1295 | return ret; 1296 | } 1297 | 1298 | // convert a base64 string to hex 1299 | function b64tohex(s) { 1300 | var ret = "" 1301 | var i; 1302 | var k = 0; // b64 state, 0-3 1303 | var slop; 1304 | for(i = 0; i < s.length; ++i) { 1305 | if(s.charAt(i) == b64pad) break; 1306 | v = b64map.indexOf(s.charAt(i)); 1307 | if(v < 0) continue; 1308 | if(k == 0) { 1309 | ret += int2char(v >> 2); 1310 | slop = v & 3; 1311 | k = 1; 1312 | } 1313 | else if(k == 1) { 1314 | ret += int2char((slop << 2) | (v >> 4)); 1315 | slop = v & 0xf; 1316 | k = 2; 1317 | } 1318 | else if(k == 2) { 1319 | ret += int2char(slop); 1320 | ret += int2char(v >> 2); 1321 | slop = v & 3; 1322 | k = 3; 1323 | } 1324 | else { 1325 | ret += int2char((slop << 2) | (v >> 4)); 1326 | ret += int2char(v & 0xf); 1327 | k = 0; 1328 | } 1329 | } 1330 | if(k == 1) 1331 | ret += int2char(slop << 2); 1332 | return ret; 1333 | } 1334 | 1335 | // convert a base64 string to a byte/number array 1336 | function b64toBA(s) { 1337 | //piggyback on b64tohex for now, optimize later 1338 | var h = b64tohex(s); 1339 | var i; 1340 | var a = new Array(); 1341 | for(i = 0; 2*i < h.length; ++i) { 1342 | a[i] = parseInt(h.substring(2*i,2*i+2),16); 1343 | } 1344 | return a; 1345 | } 1346 | // Copyright (c) 2005 Tom Wu 1347 | // All Rights Reserved. 1348 | // See "LICENSE" for details. 1349 | 1350 | // Basic JavaScript BN library - subset useful for RSA encryption. 1351 | 1352 | // Bits per digit 1353 | var dbits; 1354 | 1355 | // JavaScript engine analysis 1356 | var canary = 0xdeadbeefcafe; 1357 | var j_lm = ((canary&0xffffff)==0xefcafe); 1358 | 1359 | // (public) Constructor 1360 | function BigInteger(a,b,c) { 1361 | this.arr = new Array(); 1362 | if(a != null) 1363 | if("number" == typeof a) this.fromNumber(a,b,c); 1364 | else if(b == null && "string" != typeof a) this.fromString(a,256); 1365 | else this.fromString(a,b); 1366 | } 1367 | 1368 | // return new, unset BigInteger 1369 | function nbi() { return new BigInteger(null); } 1370 | 1371 | // am: Compute w_j += (x*this_i), propagate carries, 1372 | // c is initial carry, returns final carry. 1373 | // c < 3*dvalue, x < 2*dvalue, this_i < dvalue 1374 | // We need to select the fastest one that works in this environment. 1375 | 1376 | // am1: use a single mult and divide to get the high bits, 1377 | // max digit bits should be 26 because 1378 | // max internal value = 2*dvalue^2-2*dvalue (< 2^53) 1379 | function am1(i,x,w,j,c,n) { 1380 | while(--n >= 0) { 1381 | var v = x*this.arr[i++]+w.arr[j]+c; 1382 | c = Math.floor(v/0x4000000); 1383 | w.arr[j++] = v&0x3ffffff; 1384 | } 1385 | return c; 1386 | } 1387 | // am2 avoids a big mult-and-extract completely. 1388 | // Max digit bits should be <= 30 because we do bitwise ops 1389 | // on values up to 2*hdvalue^2-hdvalue-1 (< 2^31) 1390 | function am2(i,x,w,j,c,n) { 1391 | var xl = x&0x7fff, xh = x>>15; 1392 | while(--n >= 0) { 1393 | var l = this.arr[i]&0x7fff; 1394 | var h = this.arr[i++]>>15; 1395 | var m = xh*l+h*xl; 1396 | l = xl*l+((m&0x7fff)<<15)+w.arr[j]+(c&0x3fffffff); 1397 | c = (l>>>30)+(m>>>15)+xh*h+(c>>>30); 1398 | w.arr[j++] = l&0x3fffffff; 1399 | } 1400 | return c; 1401 | } 1402 | // Alternately, set max digit bits to 28 since some 1403 | // browsers slow down when dealing with 32-bit numbers. 1404 | function am3(i,x,w,j,c,n) { 1405 | var xl = x&0x3fff, xh = x>>14; 1406 | while(--n >= 0) { 1407 | var l = this.arr[i]&0x3fff; 1408 | var h = this.arr[i++]>>14; 1409 | var m = xh*l+h*xl; 1410 | l = xl*l+((m&0x3fff)<<14)+w.arr[j]+c; 1411 | c = (l>>28)+(m>>14)+xh*h; 1412 | w.arr[j++] = l&0xfffffff; 1413 | } 1414 | return c; 1415 | } 1416 | if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) { 1417 | BigInteger.prototype.am = am2; 1418 | dbits = 30; 1419 | } 1420 | else if(j_lm && (navigator.appName != "Netscape")) { 1421 | BigInteger.prototype.am = am1; 1422 | dbits = 26; 1423 | } 1424 | else { // Mozilla/Netscape seems to prefer am3 1425 | BigInteger.prototype.am = am3; 1426 | dbits = 28; 1427 | } 1428 | 1429 | BigInteger.prototype.DB = dbits; 1430 | BigInteger.prototype.DM = ((1<= 0; --i) r.arr[i] = this.arr[i]; 1458 | r.t = this.t; 1459 | r.s = this.s; 1460 | } 1461 | 1462 | // (protected) set from integer value x, -DV <= x < DV 1463 | function bnpFromInt(x) { 1464 | this.t = 1; 1465 | this.s = (x<0)?-1:0; 1466 | if(x > 0) this.arr[0] = x; 1467 | else if(x < -1) this.arr[0] = x+DV; 1468 | else this.t = 0; 1469 | } 1470 | 1471 | // return bigint initialized to value 1472 | function nbv(i) { var r = nbi(); r.fromInt(i); return r; } 1473 | 1474 | // (protected) set from string and radix 1475 | function bnpFromString(s,b) { 1476 | var k; 1477 | if(b == 16) k = 4; 1478 | else if(b == 8) k = 3; 1479 | else if(b == 256) k = 8; // byte array 1480 | else if(b == 2) k = 1; 1481 | else if(b == 32) k = 5; 1482 | else if(b == 4) k = 2; 1483 | else { this.fromRadix(s,b); return; } 1484 | this.t = 0; 1485 | this.s = 0; 1486 | var i = s.length, mi = false, sh = 0; 1487 | while(--i >= 0) { 1488 | var x = (k==8)?s[i]&0xff:intAt(s,i); 1489 | if(x < 0) { 1490 | if(s.charAt(i) == "-") mi = true; 1491 | continue; 1492 | } 1493 | mi = false; 1494 | if(sh == 0) 1495 | this.arr[this.t++] = x; 1496 | else if(sh+k > this.DB) { 1497 | this.arr[this.t-1] |= (x&((1<<(this.DB-sh))-1))<>(this.DB-sh)); 1499 | } 1500 | else 1501 | this.arr[this.t-1] |= x<= this.DB) sh -= this.DB; 1504 | } 1505 | if(k == 8 && (s[0]&0x80) != 0) { 1506 | this.s = -1; 1507 | if(sh > 0) this.arr[this.t-1] |= ((1<<(this.DB-sh))-1)< 0 && this.arr[this.t-1] == c) --this.t; 1517 | } 1518 | 1519 | // (public) return string representation in given radix 1520 | function bnToString(b) { 1521 | if(this.s < 0) return "-"+this.negate().toString(b); 1522 | var k; 1523 | if(b == 16) k = 4; 1524 | else if(b == 8) k = 3; 1525 | else if(b == 2) k = 1; 1526 | else if(b == 32) k = 5; 1527 | else if(b == 4) k = 2; 1528 | else return this.toRadix(b); 1529 | var km = (1< 0) { 1532 | if(p < this.DB && (d = this.arr[i]>>p) > 0) { m = true; r = int2char(d); } 1533 | while(i >= 0) { 1534 | if(p < k) { 1535 | d = (this.arr[i]&((1<>(p+=this.DB-k); 1537 | } 1538 | else { 1539 | d = (this.arr[i]>>(p-=k))&km; 1540 | if(p <= 0) { p += this.DB; --i; } 1541 | } 1542 | if(d > 0) m = true; 1543 | if(m) r += int2char(d); 1544 | } 1545 | } 1546 | return m?r:"0"; 1547 | } 1548 | 1549 | // (public) -this 1550 | function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; } 1551 | 1552 | // (public) |this| 1553 | function bnAbs() { return (this.s<0)?this.negate():this; } 1554 | 1555 | // (public) return + if this > a, - if this < a, 0 if equal 1556 | function bnCompareTo(a) { 1557 | var r = this.s-a.s; 1558 | if(r != 0) return r; 1559 | var i = this.t; 1560 | r = i-a.t; 1561 | if(r != 0) return r; 1562 | while(--i >= 0) if((r=this.arr[i]-a.arr[i]) != 0) return r; 1563 | return 0; 1564 | } 1565 | 1566 | // returns bit length of the integer x 1567 | function nbits(x) { 1568 | var r = 1, t; 1569 | if((t=x>>>16) != 0) { x = t; r += 16; } 1570 | if((t=x>>8) != 0) { x = t; r += 8; } 1571 | if((t=x>>4) != 0) { x = t; r += 4; } 1572 | if((t=x>>2) != 0) { x = t; r += 2; } 1573 | if((t=x>>1) != 0) { x = t; r += 1; } 1574 | return r; 1575 | } 1576 | 1577 | // (public) return the number of bits in "this" 1578 | function bnBitLength() { 1579 | if(this.t <= 0) return 0; 1580 | return this.DB*(this.t-1)+nbits(this.arr[this.t-1]^(this.s&this.DM)); 1581 | } 1582 | 1583 | // (protected) r = this << n*DB 1584 | function bnpDLShiftTo(n,r) { 1585 | var i; 1586 | for(i = this.t-1; i >= 0; --i) r.arr[i+n] = this.arr[i]; 1587 | for(i = n-1; i >= 0; --i) r.arr[i] = 0; 1588 | r.t = this.t+n; 1589 | r.s = this.s; 1590 | } 1591 | 1592 | // (protected) r = this >> n*DB 1593 | function bnpDRShiftTo(n,r) { 1594 | for(var i = n; i < this.t; ++i) r.arr[i-n] = this.arr[i]; 1595 | r.t = Math.max(this.t-n,0); 1596 | r.s = this.s; 1597 | } 1598 | 1599 | // (protected) r = this << n 1600 | function bnpLShiftTo(n,r) { 1601 | var bs = n%this.DB; 1602 | var cbs = this.DB-bs; 1603 | var bm = (1<= 0; --i) { 1606 | r.arr[i+ds+1] = (this.arr[i]>>cbs)|c; 1607 | c = (this.arr[i]&bm)<= 0; --i) r.arr[i] = 0; 1610 | r.arr[ds] = c; 1611 | r.t = this.t+ds+1; 1612 | r.s = this.s; 1613 | r.clamp(); 1614 | } 1615 | 1616 | // (protected) r = this >> n 1617 | function bnpRShiftTo(n,r) { 1618 | r.s = this.s; 1619 | var ds = Math.floor(n/this.DB); 1620 | if(ds >= this.t) { r.t = 0; return; } 1621 | var bs = n%this.DB; 1622 | var cbs = this.DB-bs; 1623 | var bm = (1<>bs; 1625 | for(var i = ds+1; i < this.t; ++i) { 1626 | r.arr[i-ds-1] |= (this.arr[i]&bm)<>bs; 1628 | } 1629 | if(bs > 0) r.arr[this.t-ds-1] |= (this.s&bm)<>= this.DB; 1641 | } 1642 | if(a.t < this.t) { 1643 | c -= a.s; 1644 | while(i < this.t) { 1645 | c += this.arr[i]; 1646 | r.arr[i++] = c&this.DM; 1647 | c >>= this.DB; 1648 | } 1649 | c += this.s; 1650 | } 1651 | else { 1652 | c += this.s; 1653 | while(i < a.t) { 1654 | c -= a.arr[i]; 1655 | r.arr[i++] = c&this.DM; 1656 | c >>= this.DB; 1657 | } 1658 | c -= a.s; 1659 | } 1660 | r.s = (c<0)?-1:0; 1661 | if(c < -1) r.arr[i++] = this.DV+c; 1662 | else if(c > 0) r.arr[i++] = c; 1663 | r.t = i; 1664 | r.clamp(); 1665 | } 1666 | 1667 | // (protected) r = this * a, r != this,a (HAC 14.12) 1668 | // "this" should be the larger one if appropriate. 1669 | function bnpMultiplyTo(a,r) { 1670 | var x = this.abs(), y = a.abs(); 1671 | var i = x.t; 1672 | r.t = i+y.t; 1673 | while(--i >= 0) r.arr[i] = 0; 1674 | for(i = 0; i < y.t; ++i) r.arr[i+x.t] = x.am(0,y.arr[i],r,i,0,x.t); 1675 | r.s = 0; 1676 | r.clamp(); 1677 | if(this.s != a.s) BigInteger.ZERO.subTo(r,r); 1678 | } 1679 | 1680 | // (protected) r = this^2, r != this (HAC 14.16) 1681 | function bnpSquareTo(r) { 1682 | var x = this.abs(); 1683 | var i = r.t = 2*x.t; 1684 | while(--i >= 0) r.arr[i] = 0; 1685 | for(i = 0; i < x.t-1; ++i) { 1686 | var c = x.am(i,x.arr[i],r,2*i,0,1); 1687 | if((r.arr[i+x.t]+=x.am(i+1,2*x.arr[i],r,2*i+1,c,x.t-i-1)) >= x.DV) { 1688 | r.arr[i+x.t] -= x.DV; 1689 | r.arr[i+x.t+1] = 1; 1690 | } 1691 | } 1692 | if(r.t > 0) r.arr[r.t-1] += x.am(i,x.arr[i],r,2*i,0,1); 1693 | r.s = 0; 1694 | r.clamp(); 1695 | } 1696 | 1697 | // (protected) divide this by m, quotient and remainder to q, r (HAC 14.20) 1698 | // r != q, this != m. q or r may be null. 1699 | function bnpDivRemTo(m,q,r) { 1700 | var pm = m.abs(); 1701 | if(pm.t <= 0) return; 1702 | var pt = this.abs(); 1703 | if(pt.t < pm.t) { 1704 | if(q != null) q.fromInt(0); 1705 | if(r != null) this.copyTo(r); 1706 | return; 1707 | } 1708 | if(r == null) r = nbi(); 1709 | var y = nbi(), ts = this.s, ms = m.s; 1710 | var nsh = this.DB-nbits(pm.arr[pm.t-1]); // normalize modulus 1711 | if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); } 1712 | else { pm.copyTo(y); pt.copyTo(r); } 1713 | var ys = y.t; 1714 | var y0 = y.arr[ys-1]; 1715 | if(y0 == 0) return; 1716 | var yt = y0*(1<1)?y.arr[ys-2]>>this.F2:0); 1717 | var d1 = this.FV/yt, d2 = (1<= 0) { 1721 | r.arr[r.t++] = 1; 1722 | r.subTo(t,r); 1723 | } 1724 | BigInteger.ONE.dlShiftTo(ys,t); 1725 | t.subTo(y,y); // "negative" y so we can replace sub with am later 1726 | while(y.t < ys) y.arr[y.t++] = 0; 1727 | while(--j >= 0) { 1728 | // Estimate quotient digit 1729 | var qd = (r.arr[--i]==y0)?this.DM:Math.floor(r.arr[i]*d1+(r.arr[i-1]+e)*d2); 1730 | if((r.arr[i]+=y.am(0,qd,r,j,0,ys)) < qd) { // Try it out 1731 | y.dlShiftTo(j,t); 1732 | r.subTo(t,r); 1733 | while(r.arr[i] < --qd) r.subTo(t,r); 1734 | } 1735 | } 1736 | if(q != null) { 1737 | r.drShiftTo(ys,q); 1738 | if(ts != ms) BigInteger.ZERO.subTo(q,q); 1739 | } 1740 | r.t = ys; 1741 | r.clamp(); 1742 | if(nsh > 0) r.rShiftTo(nsh,r); // Denormalize remainder 1743 | if(ts < 0) BigInteger.ZERO.subTo(r,r); 1744 | } 1745 | 1746 | // (public) this mod a 1747 | function bnMod(a) { 1748 | var r = nbi(); 1749 | this.abs().divRemTo(a,null,r); 1750 | if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r); 1751 | return r; 1752 | } 1753 | 1754 | // Modular reduction using "classic" algorithm 1755 | function Classic(m) { this.m = m; } 1756 | function cConvert(x) { 1757 | if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m); 1758 | else return x; 1759 | } 1760 | function cRevert(x) { return x; } 1761 | function cReduce(x) { x.divRemTo(this.m,null,x); } 1762 | function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } 1763 | function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); } 1764 | 1765 | Classic.prototype.convert = cConvert; 1766 | Classic.prototype.revert = cRevert; 1767 | Classic.prototype.reduce = cReduce; 1768 | Classic.prototype.mulTo = cMulTo; 1769 | Classic.prototype.sqrTo = cSqrTo; 1770 | 1771 | // (protected) return "-1/this % 2^DB"; useful for Mont. reduction 1772 | // justification: 1773 | // xy == 1 (mod m) 1774 | // xy = 1+km 1775 | // xy(2-xy) = (1+km)(1-km) 1776 | // x.arr[y(2-xy)] = 1-k^2m^2 1777 | // x.arr[y(2-xy)] == 1 (mod m^2) 1778 | // if y is 1/x mod m, then y(2-xy) is 1/x mod m^2 1779 | // should reduce x and y(2-xy) by m^2 at each step to keep size bounded. 1780 | // JS multiply "overflows" differently from C/C++, so care is needed here. 1781 | function bnpInvDigit() { 1782 | if(this.t < 1) return 0; 1783 | var x = this.arr[0]; 1784 | if((x&1) == 0) return 0; 1785 | var y = x&3; // y == 1/x mod 2^2 1786 | y = (y*(2-(x&0xf)*y))&0xf; // y == 1/x mod 2^4 1787 | y = (y*(2-(x&0xff)*y))&0xff; // y == 1/x mod 2^8 1788 | y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff; // y == 1/x mod 2^16 1789 | // last step - calculate inverse mod DV directly; 1790 | // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints 1791 | y = (y*(2-x*y%this.DV))%this.DV; // y == 1/x mod 2^dbits 1792 | // we really want the negative inverse, and -DV < y < DV 1793 | return (y>0)?this.DV-y:-y; 1794 | } 1795 | 1796 | // Montgomery reduction 1797 | function Montgomery(m) { 1798 | this.m = m; 1799 | this.mp = m.invDigit(); 1800 | this.mpl = this.mp&0x7fff; 1801 | this.mph = this.mp>>15; 1802 | this.um = (1<<(m.DB-15))-1; 1803 | this.mt2 = 2*m.t; 1804 | } 1805 | 1806 | // xR mod m 1807 | function montConvert(x) { 1808 | var r = nbi(); 1809 | x.abs().dlShiftTo(this.m.t,r); 1810 | r.divRemTo(this.m,null,r); 1811 | if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r); 1812 | return r; 1813 | } 1814 | 1815 | // x/R mod m 1816 | function montRevert(x) { 1817 | var r = nbi(); 1818 | x.copyTo(r); 1819 | this.reduce(r); 1820 | return r; 1821 | } 1822 | 1823 | // x = x/R mod m (HAC 14.32) 1824 | function montReduce(x) { 1825 | while(x.t <= this.mt2) // pad x so am has enough room later 1826 | x.arr[x.t++] = 0; 1827 | for(var i = 0; i < this.m.t; ++i) { 1828 | // faster way of calculating u0 = x.arr[i]*mp mod DV 1829 | var j = x.arr[i]&0x7fff; 1830 | var u0 = (j*this.mpl+(((j*this.mph+(x.arr[i]>>15)*this.mpl)&this.um)<<15))&x.DM; 1831 | // use am to combine the multiply-shift-add into one call 1832 | j = i+this.m.t; 1833 | x.arr[j] += this.m.am(0,u0,x,i,0,this.m.t); 1834 | // propagate carry 1835 | while(x.arr[j] >= x.DV) { x.arr[j] -= x.DV; x.arr[++j]++; } 1836 | } 1837 | x.clamp(); 1838 | x.drShiftTo(this.m.t,x); 1839 | if(x.compareTo(this.m) >= 0) x.subTo(this.m,x); 1840 | } 1841 | 1842 | // r = "x^2/R mod m"; x != r 1843 | function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); } 1844 | 1845 | // r = "xy/R mod m"; x,y != r 1846 | function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } 1847 | 1848 | Montgomery.prototype.convert = montConvert; 1849 | Montgomery.prototype.revert = montRevert; 1850 | Montgomery.prototype.reduce = montReduce; 1851 | Montgomery.prototype.mulTo = montMulTo; 1852 | Montgomery.prototype.sqrTo = montSqrTo; 1853 | 1854 | // (protected) true iff this is even 1855 | function bnpIsEven() { return ((this.t>0)?(this.arr[0]&1):this.s) == 0; } 1856 | 1857 | // (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79) 1858 | function bnpExp(e,z) { 1859 | if(e > 0xffffffff || e < 1) return BigInteger.ONE; 1860 | var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1; 1861 | g.copyTo(r); 1862 | while(--i >= 0) { 1863 | z.sqrTo(r,r2); 1864 | if((e&(1< 0) z.mulTo(r2,g,r); 1865 | else { var t = r; r = r2; r2 = t; } 1866 | } 1867 | return z.revert(r); 1868 | } 1869 | 1870 | // (public) this^e % m, 0 <= e < 2^32 1871 | function bnModPowInt(e,m) { 1872 | var z; 1873 | if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m); 1874 | return this.exp(e,z); 1875 | } 1876 | 1877 | // protected 1878 | BigInteger.prototype.copyTo = bnpCopyTo; 1879 | BigInteger.prototype.fromInt = bnpFromInt; 1880 | BigInteger.prototype.fromString = bnpFromString; 1881 | BigInteger.prototype.clamp = bnpClamp; 1882 | BigInteger.prototype.dlShiftTo = bnpDLShiftTo; 1883 | BigInteger.prototype.drShiftTo = bnpDRShiftTo; 1884 | BigInteger.prototype.lShiftTo = bnpLShiftTo; 1885 | BigInteger.prototype.rShiftTo = bnpRShiftTo; 1886 | BigInteger.prototype.subTo = bnpSubTo; 1887 | BigInteger.prototype.multiplyTo = bnpMultiplyTo; 1888 | BigInteger.prototype.squareTo = bnpSquareTo; 1889 | BigInteger.prototype.divRemTo = bnpDivRemTo; 1890 | BigInteger.prototype.invDigit = bnpInvDigit; 1891 | BigInteger.prototype.isEven = bnpIsEven; 1892 | BigInteger.prototype.exp = bnpExp; 1893 | 1894 | // public 1895 | BigInteger.prototype.toString = bnToString; 1896 | BigInteger.prototype.negate = bnNegate; 1897 | BigInteger.prototype.abs = bnAbs; 1898 | BigInteger.prototype.compareTo = bnCompareTo; 1899 | BigInteger.prototype.bitLength = bnBitLength; 1900 | BigInteger.prototype.mod = bnMod; 1901 | BigInteger.prototype.modPowInt = bnModPowInt; 1902 | 1903 | // "constants" 1904 | BigInteger.ZERO = nbv(0); 1905 | BigInteger.ONE = nbv(1); 1906 | // Copyright (c) 2005-2009 Tom Wu 1907 | // All Rights Reserved. 1908 | // See "LICENSE" for details. 1909 | 1910 | // Extended JavaScript BN functions, required for RSA private ops. 1911 | 1912 | // Version 1.1: new BigInteger("0", 10) returns "proper" zero 1913 | 1914 | // (public) 1915 | function bnClone() { var r = nbi(); this.copyTo(r); return r; } 1916 | 1917 | // (public) return value as integer 1918 | function bnIntValue() { 1919 | if(this.s < 0) { 1920 | if(this.t == 1) return this.arr[0]-this.DV; 1921 | else if(this.t == 0) return -1; 1922 | } 1923 | else if(this.t == 1) return this.arr[0]; 1924 | else if(this.t == 0) return 0; 1925 | // assumes 16 < DB < 32 1926 | return ((this.arr[1]&((1<<(32-this.DB))-1))<>24; } 1931 | 1932 | // (public) return value as short (assumes DB>=16) 1933 | function bnShortValue() { return (this.t==0)?this.s:(this.arr[0]<<16)>>16; } 1934 | 1935 | // (protected) return x s.t. r^x < DV 1936 | function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); } 1937 | 1938 | // (public) 0 if this == 0, 1 if this > 0 1939 | function bnSigNum() { 1940 | if(this.s < 0) return -1; 1941 | else if(this.t <= 0 || (this.t == 1 && this.arr[0] <= 0)) return 0; 1942 | else return 1; 1943 | } 1944 | 1945 | // (protected) convert to radix string 1946 | function bnpToRadix(b) { 1947 | if(b == null) b = 10; 1948 | if(this.signum() == 0 || b < 2 || b > 36) return "0"; 1949 | var cs = this.chunkSize(b); 1950 | var a = Math.pow(b,cs); 1951 | var d = nbv(a), y = nbi(), z = nbi(), r = ""; 1952 | this.divRemTo(d,y,z); 1953 | while(y.signum() > 0) { 1954 | r = (a+z.intValue()).toString(b).substr(1) + r; 1955 | y.divRemTo(d,y,z); 1956 | } 1957 | return z.intValue().toString(b) + r; 1958 | } 1959 | 1960 | // (protected) convert from radix string 1961 | function bnpFromRadix(s,b) { 1962 | this.fromInt(0); 1963 | if(b == null) b = 10; 1964 | var cs = this.chunkSize(b); 1965 | var d = Math.pow(b,cs), mi = false, j = 0, w = 0; 1966 | for(var i = 0; i < s.length; ++i) { 1967 | var x = intAt(s,i); 1968 | if(x < 0) { 1969 | if(s.charAt(i) == "-" && this.signum() == 0) mi = true; 1970 | continue; 1971 | } 1972 | w = b*w+x; 1973 | if(++j >= cs) { 1974 | this.dMultiply(d); 1975 | this.dAddOffset(w,0); 1976 | j = 0; 1977 | w = 0; 1978 | } 1979 | } 1980 | if(j > 0) { 1981 | this.dMultiply(Math.pow(b,j)); 1982 | this.dAddOffset(w,0); 1983 | } 1984 | if(mi) BigInteger.ZERO.subTo(this,this); 1985 | } 1986 | 1987 | // (protected) alternate constructor 1988 | function bnpFromNumber(a,b,c) { 1989 | if("number" == typeof b) { 1990 | // new BigInteger(int,int,RNG) 1991 | if(a < 2) this.fromInt(1); 1992 | else { 1993 | this.fromNumber(a,c); 1994 | if(!this.testBit(a-1)) // force MSB set 1995 | this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this); 1996 | if(this.isEven()) this.dAddOffset(1,0); // force odd 1997 | while(!this.isProbablePrime(b)) { 1998 | this.dAddOffset(2,0); 1999 | if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this); 2000 | } 2001 | } 2002 | } 2003 | else { 2004 | // new BigInteger(int,RNG) 2005 | var x = new Array(), t = a&7; 2006 | x.length = (a>>3)+1; 2007 | b.nextBytes(x); 2008 | if(t > 0) x[0] &= ((1< 0) { 2019 | if(p < this.DB && (d = this.arr[i]>>p) != (this.s&this.DM)>>p) 2020 | r.arr[k++] = d|(this.s<<(this.DB-p)); 2021 | while(i >= 0) { 2022 | if(p < 8) { 2023 | d = (this.arr[i]&((1<>(p+=this.DB-8); 2025 | } 2026 | else { 2027 | d = (this.arr[i]>>(p-=8))&0xff; 2028 | if(p <= 0) { p += this.DB; --i; } 2029 | } 2030 | if((d&0x80) != 0) d |= -256; 2031 | if(k == 0 && (this.s&0x80) != (d&0x80)) ++k; 2032 | if(k > 0 || d != this.s) r.arr[k++] = d; 2033 | } 2034 | } 2035 | return r; 2036 | } 2037 | 2038 | function bnEquals(a) { return(this.compareTo(a)==0); } 2039 | function bnMin(a) { return(this.compareTo(a)<0)?this:a; } 2040 | function bnMax(a) { return(this.compareTo(a)>0)?this:a; } 2041 | 2042 | // (protected) r = this op a (bitwise) 2043 | function bnpBitwiseTo(a,op,r) { 2044 | var i, f, m = Math.min(a.t,this.t); 2045 | for(i = 0; i < m; ++i) r.arr[i] = op(this.arr[i],a.arr[i]); 2046 | if(a.t < this.t) { 2047 | f = a.s&this.DM; 2048 | for(i = m; i < this.t; ++i) r.arr[i] = op(this.arr[i],f); 2049 | r.t = this.t; 2050 | } 2051 | else { 2052 | f = this.s&this.DM; 2053 | for(i = m; i < a.t; ++i) r.arr[i] = op(f,a.arr[i]); 2054 | r.t = a.t; 2055 | } 2056 | r.s = op(this.s,a.s); 2057 | r.clamp(); 2058 | } 2059 | 2060 | // (public) this & a 2061 | function op_and(x,y) { return x&y; } 2062 | function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; } 2063 | 2064 | // (public) this | a 2065 | function op_or(x,y) { return x|y; } 2066 | function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; } 2067 | 2068 | // (public) this ^ a 2069 | function op_xor(x,y) { return x^y; } 2070 | function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; } 2071 | 2072 | // (public) this & ~a 2073 | function op_andnot(x,y) { return x&~y; } 2074 | function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; } 2075 | 2076 | // (public) ~this 2077 | function bnNot() { 2078 | var r = nbi(); 2079 | for(var i = 0; i < this.t; ++i) r.arr[i] = this.DM&~this.arr[i]; 2080 | r.t = this.t; 2081 | r.s = ~this.s; 2082 | return r; 2083 | } 2084 | 2085 | // (public) this << n 2086 | function bnShiftLeft(n) { 2087 | var r = nbi(); 2088 | if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r); 2089 | return r; 2090 | } 2091 | 2092 | // (public) this >> n 2093 | function bnShiftRight(n) { 2094 | var r = nbi(); 2095 | if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r); 2096 | return r; 2097 | } 2098 | 2099 | // return index of lowest 1-bit in x, x < 2^31 2100 | function lbit(x) { 2101 | if(x == 0) return -1; 2102 | var r = 0; 2103 | if((x&0xffff) == 0) { x >>= 16; r += 16; } 2104 | if((x&0xff) == 0) { x >>= 8; r += 8; } 2105 | if((x&0xf) == 0) { x >>= 4; r += 4; } 2106 | if((x&3) == 0) { x >>= 2; r += 2; } 2107 | if((x&1) == 0) ++r; 2108 | return r; 2109 | } 2110 | 2111 | // (public) returns index of lowest 1-bit (or -1 if none) 2112 | function bnGetLowestSetBit() { 2113 | for(var i = 0; i < this.t; ++i) 2114 | if(this.arr[i] != 0) return i*this.DB+lbit(this.arr[i]); 2115 | if(this.s < 0) return this.t*this.DB; 2116 | return -1; 2117 | } 2118 | 2119 | // return number of 1 bits in x 2120 | function cbit(x) { 2121 | var r = 0; 2122 | while(x != 0) { x &= x-1; ++r; } 2123 | return r; 2124 | } 2125 | 2126 | // (public) return number of set bits 2127 | function bnBitCount() { 2128 | var r = 0, x = this.s&this.DM; 2129 | for(var i = 0; i < this.t; ++i) r += cbit(this.arr[i]^x); 2130 | return r; 2131 | } 2132 | 2133 | // (public) true iff nth bit is set 2134 | function bnTestBit(n) { 2135 | var j = Math.floor(n/this.DB); 2136 | if(j >= this.t) return(this.s!=0); 2137 | return((this.arr[j]&(1<<(n%this.DB)))!=0); 2138 | } 2139 | 2140 | // (protected) this op (1<>= this.DB; 2163 | } 2164 | if(a.t < this.t) { 2165 | c += a.s; 2166 | while(i < this.t) { 2167 | c += this.arr[i]; 2168 | r.arr[i++] = c&this.DM; 2169 | c >>= this.DB; 2170 | } 2171 | c += this.s; 2172 | } 2173 | else { 2174 | c += this.s; 2175 | while(i < a.t) { 2176 | c += a.arr[i]; 2177 | r.arr[i++] = c&this.DM; 2178 | c >>= this.DB; 2179 | } 2180 | c += a.s; 2181 | } 2182 | r.s = (c<0)?-1:0; 2183 | if(c > 0) r.arr[i++] = c; 2184 | else if(c < -1) r.arr[i++] = this.DV+c; 2185 | r.t = i; 2186 | r.clamp(); 2187 | } 2188 | 2189 | // (public) this + a 2190 | function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; } 2191 | 2192 | // (public) this - a 2193 | function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; } 2194 | 2195 | // (public) this * a 2196 | function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; } 2197 | 2198 | // (public) this / a 2199 | function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; } 2200 | 2201 | // (public) this % a 2202 | function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; } 2203 | 2204 | // (public) [this/a,this%a] 2205 | function bnDivideAndRemainder(a) { 2206 | var q = nbi(), r = nbi(); 2207 | this.divRemTo(a,q,r); 2208 | return new Array(q,r); 2209 | } 2210 | 2211 | // (protected) this *= n, this >= 0, 1 < n < DV 2212 | function bnpDMultiply(n) { 2213 | this.arr[this.t] = this.am(0,n-1,this,0,0,this.t); 2214 | ++this.t; 2215 | this.clamp(); 2216 | } 2217 | 2218 | // (protected) this += n << w words, this >= 0 2219 | function bnpDAddOffset(n,w) { 2220 | if(n == 0) return; 2221 | while(this.t <= w) this.arr[this.t++] = 0; 2222 | this.arr[w] += n; 2223 | while(this.arr[w] >= this.DV) { 2224 | this.arr[w] -= this.DV; 2225 | if(++w >= this.t) this.arr[this.t++] = 0; 2226 | ++this.arr[w]; 2227 | } 2228 | } 2229 | 2230 | // A "null" reducer 2231 | function NullExp() {} 2232 | function nNop(x) { return x; } 2233 | function nMulTo(x,y,r) { x.multiplyTo(y,r); } 2234 | function nSqrTo(x,r) { x.squareTo(r); } 2235 | 2236 | NullExp.prototype.convert = nNop; 2237 | NullExp.prototype.revert = nNop; 2238 | NullExp.prototype.mulTo = nMulTo; 2239 | NullExp.prototype.sqrTo = nSqrTo; 2240 | 2241 | // (public) this^e 2242 | function bnPow(e) { return this.exp(e,new NullExp()); } 2243 | 2244 | // (protected) r = lower n words of "this * a", a.t <= n 2245 | // "this" should be the larger one if appropriate. 2246 | function bnpMultiplyLowerTo(a,n,r) { 2247 | var i = Math.min(this.t+a.t,n); 2248 | r.s = 0; // assumes a,this >= 0 2249 | r.t = i; 2250 | while(i > 0) r.arr[--i] = 0; 2251 | var j; 2252 | for(j = r.t-this.t; i < j; ++i) r.arr[i+this.t] = this.am(0,a.arr[i],r,i,0,this.t); 2253 | for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a.arr[i],r,i,0,n-i); 2254 | r.clamp(); 2255 | } 2256 | 2257 | // (protected) r = "this * a" without lower n words, n > 0 2258 | // "this" should be the larger one if appropriate. 2259 | function bnpMultiplyUpperTo(a,n,r) { 2260 | --n; 2261 | var i = r.t = this.t+a.t-n; 2262 | r.s = 0; // assumes a,this >= 0 2263 | while(--i >= 0) r.arr[i] = 0; 2264 | for(i = Math.max(n-this.t,0); i < a.t; ++i) 2265 | r.arr[this.t+i-n] = this.am(n-i,a.arr[i],r,0,0,this.t+i-n); 2266 | r.clamp(); 2267 | r.drShiftTo(1,r); 2268 | } 2269 | 2270 | // Barrett modular reduction 2271 | function Barrett(m) { 2272 | // setup Barrett 2273 | this.r2 = nbi(); 2274 | this.q3 = nbi(); 2275 | BigInteger.ONE.dlShiftTo(2*m.t,this.r2); 2276 | this.mu = this.r2.divide(m); 2277 | this.m = m; 2278 | } 2279 | 2280 | function barrettConvert(x) { 2281 | if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m); 2282 | else if(x.compareTo(this.m) < 0) return x; 2283 | else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; } 2284 | } 2285 | 2286 | function barrettRevert(x) { return x; } 2287 | 2288 | // x = x mod m (HAC 14.42) 2289 | function barrettReduce(x) { 2290 | x.drShiftTo(this.m.t-1,this.r2); 2291 | if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); } 2292 | this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3); 2293 | this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2); 2294 | while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1); 2295 | x.subTo(this.r2,x); 2296 | while(x.compareTo(this.m) >= 0) x.subTo(this.m,x); 2297 | } 2298 | 2299 | // r = x^2 mod m; x != r 2300 | function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); } 2301 | 2302 | // r = x*y mod m; x,y != r 2303 | function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); } 2304 | 2305 | Barrett.prototype.convert = barrettConvert; 2306 | Barrett.prototype.revert = barrettRevert; 2307 | Barrett.prototype.reduce = barrettReduce; 2308 | Barrett.prototype.mulTo = barrettMulTo; 2309 | Barrett.prototype.sqrTo = barrettSqrTo; 2310 | 2311 | // (public) this^e % m (HAC 14.85) 2312 | function bnModPow(e,m) { 2313 | var i = e.bitLength(), k, r = nbv(1), z; 2314 | if(i <= 0) return r; 2315 | else if(i < 18) k = 1; 2316 | else if(i < 48) k = 3; 2317 | else if(i < 144) k = 4; 2318 | else if(i < 768) k = 5; 2319 | else k = 6; 2320 | if(i < 8) 2321 | z = new Classic(m); 2322 | else if(m.isEven()) 2323 | z = new Barrett(m); 2324 | else 2325 | z = new Montgomery(m); 2326 | 2327 | // precomputation 2328 | var g = new Array(), n = 3, k1 = k-1, km = (1< 1) { 2331 | var g2 = nbi(); 2332 | z.sqrTo(g[1],g2); 2333 | while(n <= km) { 2334 | g[n] = nbi(); 2335 | z.mulTo(g2,g[n-2],g[n]); 2336 | n += 2; 2337 | } 2338 | } 2339 | 2340 | var j = e.t-1, w, is1 = true, r2 = nbi(), t; 2341 | i = nbits(e.arr[j])-1; 2342 | while(j >= 0) { 2343 | if(i >= k1) w = (e.arr[j]>>(i-k1))&km; 2344 | else { 2345 | w = (e.arr[j]&((1<<(i+1))-1))<<(k1-i); 2346 | if(j > 0) w |= e.arr[j-1]>>(this.DB+i-k1); 2347 | } 2348 | 2349 | n = k; 2350 | while((w&1) == 0) { w >>= 1; --n; } 2351 | if((i -= n) < 0) { i += this.DB; --j; } 2352 | if(is1) { // ret == 1, don't bother squaring or multiplying it 2353 | g[w].copyTo(r); 2354 | is1 = false; 2355 | } 2356 | else { 2357 | while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; } 2358 | if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; } 2359 | z.mulTo(r2,g[w],r); 2360 | } 2361 | 2362 | while(j >= 0 && (e.arr[j]&(1< 0) { 2379 | x.rShiftTo(g,x); 2380 | y.rShiftTo(g,y); 2381 | } 2382 | while(x.signum() > 0) { 2383 | if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x); 2384 | if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y); 2385 | if(x.compareTo(y) >= 0) { 2386 | x.subTo(y,x); 2387 | x.rShiftTo(1,x); 2388 | } 2389 | else { 2390 | y.subTo(x,y); 2391 | y.rShiftTo(1,y); 2392 | } 2393 | } 2394 | if(g > 0) y.lShiftTo(g,y); 2395 | return y; 2396 | } 2397 | 2398 | // (protected) this % n, n < 2^26 2399 | function bnpModInt(n) { 2400 | if(n <= 0) return 0; 2401 | var d = this.DV%n, r = (this.s<0)?n-1:0; 2402 | if(this.t > 0) 2403 | if(d == 0) r = this.arr[0]%n; 2404 | else for(var i = this.t-1; i >= 0; --i) r = (d*r+this.arr[i])%n; 2405 | return r; 2406 | } 2407 | 2408 | // (public) 1/this % m (HAC 14.61) 2409 | function bnModInverse(m) { 2410 | var ac = m.isEven(); 2411 | if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO; 2412 | var u = m.clone(), v = this.clone(); 2413 | var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1); 2414 | while(u.signum() != 0) { 2415 | while(u.isEven()) { 2416 | u.rShiftTo(1,u); 2417 | if(ac) { 2418 | if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); } 2419 | a.rShiftTo(1,a); 2420 | } 2421 | else if(!b.isEven()) b.subTo(m,b); 2422 | b.rShiftTo(1,b); 2423 | } 2424 | while(v.isEven()) { 2425 | v.rShiftTo(1,v); 2426 | if(ac) { 2427 | if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); } 2428 | c.rShiftTo(1,c); 2429 | } 2430 | else if(!d.isEven()) d.subTo(m,d); 2431 | d.rShiftTo(1,d); 2432 | } 2433 | if(u.compareTo(v) >= 0) { 2434 | u.subTo(v,u); 2435 | if(ac) a.subTo(c,a); 2436 | b.subTo(d,b); 2437 | } 2438 | else { 2439 | v.subTo(u,v); 2440 | if(ac) c.subTo(a,c); 2441 | d.subTo(b,d); 2442 | } 2443 | } 2444 | if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO; 2445 | if(d.compareTo(m) >= 0) return d.subtract(m); 2446 | if(d.signum() < 0) d.addTo(m,d); else return d; 2447 | if(d.signum() < 0) return d.add(m); else return d; 2448 | } 2449 | 2450 | var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509]; 2451 | var lplim = (1<<26)/lowprimes[lowprimes.length-1]; 2452 | 2453 | // (public) test primality with certainty >= 1-.5^t 2454 | function bnIsProbablePrime(t) { 2455 | var i, x = this.abs(); 2456 | if(x.t == 1 && x.arr[0] <= lowprimes[lowprimes.length-1]) { 2457 | for(i = 0; i < lowprimes.length; ++i) 2458 | if(x.arr[0] == lowprimes[i]) return true; 2459 | return false; 2460 | } 2461 | if(x.isEven()) return false; 2462 | i = 1; 2463 | while(i < lowprimes.length) { 2464 | var m = lowprimes[i], j = i+1; 2465 | while(j < lowprimes.length && m < lplim) m *= lowprimes[j++]; 2466 | m = x.modInt(m); 2467 | while(i < j) if(m%lowprimes[i++] == 0) return false; 2468 | } 2469 | return x.millerRabin(t); 2470 | } 2471 | 2472 | // (protected) true if probably prime (HAC 4.24, Miller-Rabin) 2473 | function bnpMillerRabin(t) { 2474 | var n1 = this.subtract(BigInteger.ONE); 2475 | var k = n1.getLowestSetBit(); 2476 | if(k <= 0) return false; 2477 | var r = n1.shiftRight(k); 2478 | t = (t+1)>>1; 2479 | if(t > lowprimes.length) t = lowprimes.length; 2480 | var a = nbi(); 2481 | for(var i = 0; i < t; ++i) { 2482 | a.fromInt(lowprimes[i]); 2483 | var y = a.modPow(r,this); 2484 | if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) { 2485 | var j = 1; 2486 | while(j++ < k && y.compareTo(n1) != 0) { 2487 | y = y.modPowInt(2,this); 2488 | if(y.compareTo(BigInteger.ONE) == 0) return false; 2489 | } 2490 | if(y.compareTo(n1) != 0) return false; 2491 | } 2492 | } 2493 | return true; 2494 | } 2495 | 2496 | // protected 2497 | BigInteger.prototype.chunkSize = bnpChunkSize; 2498 | BigInteger.prototype.toRadix = bnpToRadix; 2499 | BigInteger.prototype.fromRadix = bnpFromRadix; 2500 | BigInteger.prototype.fromNumber = bnpFromNumber; 2501 | BigInteger.prototype.bitwiseTo = bnpBitwiseTo; 2502 | BigInteger.prototype.changeBit = bnpChangeBit; 2503 | BigInteger.prototype.addTo = bnpAddTo; 2504 | BigInteger.prototype.dMultiply = bnpDMultiply; 2505 | BigInteger.prototype.dAddOffset = bnpDAddOffset; 2506 | BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo; 2507 | BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo; 2508 | BigInteger.prototype.modInt = bnpModInt; 2509 | BigInteger.prototype.millerRabin = bnpMillerRabin; 2510 | 2511 | // public 2512 | BigInteger.prototype.clone = bnClone; 2513 | BigInteger.prototype.intValue = bnIntValue; 2514 | BigInteger.prototype.byteValue = bnByteValue; 2515 | BigInteger.prototype.shortValue = bnShortValue; 2516 | BigInteger.prototype.signum = bnSigNum; 2517 | BigInteger.prototype.toByteArray = bnToByteArray; 2518 | BigInteger.prototype.equals = bnEquals; 2519 | BigInteger.prototype.min = bnMin; 2520 | BigInteger.prototype.max = bnMax; 2521 | BigInteger.prototype.and = bnAnd; 2522 | BigInteger.prototype.or = bnOr; 2523 | BigInteger.prototype.xor = bnXor; 2524 | BigInteger.prototype.andNot = bnAndNot; 2525 | BigInteger.prototype.not = bnNot; 2526 | BigInteger.prototype.shiftLeft = bnShiftLeft; 2527 | BigInteger.prototype.shiftRight = bnShiftRight; 2528 | BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit; 2529 | BigInteger.prototype.bitCount = bnBitCount; 2530 | BigInteger.prototype.testBit = bnTestBit; 2531 | BigInteger.prototype.setBit = bnSetBit; 2532 | BigInteger.prototype.clearBit = bnClearBit; 2533 | BigInteger.prototype.flipBit = bnFlipBit; 2534 | BigInteger.prototype.add = bnAdd; 2535 | BigInteger.prototype.subtract = bnSubtract; 2536 | BigInteger.prototype.multiply = bnMultiply; 2537 | BigInteger.prototype.divide = bnDivide; 2538 | BigInteger.prototype.remainder = bnRemainder; 2539 | BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder; 2540 | BigInteger.prototype.modPow = bnModPow; 2541 | BigInteger.prototype.modInverse = bnModInverse; 2542 | BigInteger.prototype.pow = bnPow; 2543 | BigInteger.prototype.gcd = bnGCD; 2544 | BigInteger.prototype.isProbablePrime = bnIsProbablePrime; 2545 | 2546 | // BigInteger interfaces not implemented in jsbn: 2547 | 2548 | // BigInteger(int signum, byte[] magnitude) 2549 | // double doubleValue() 2550 | // float floatValue() 2551 | // int hashCode() 2552 | // long longValue() 2553 | // static BigInteger valueOf(long val) 2554 | 2555 | // add base64 methods 2556 | BigInteger.prototype.toBase64 = function() { 2557 | return hex2b64(this.toString(16)); 2558 | }; 2559 | 2560 | BigInteger.fromBase64 = function(str) { 2561 | return new BigInteger(b64tohex(str), 16); 2562 | }; 2563 | 2564 | var jsBigInteger = BigInteger; 2565 | var nativeBigInteger = null; 2566 | 2567 | // patches to bigint to use native node code if possible 2568 | try { 2569 | // if we can get node-bigint, we continue. If not, blarg. 2570 | var bigint = require("bigint"); 2571 | var crypto = require("crypto"); 2572 | 2573 | // trying to mimick Tom Wu's constructor 2574 | // except we ignore the rng for now 2575 | BigInteger = function(a, b, ignore_c) { 2576 | if (a) { 2577 | if (typeof a == 'number') { 2578 | if (typeof b == 'number') { 2579 | // random *prime* of bit size a, with certainty b 2580 | var starting_point; 2581 | while(true) { 2582 | // starting_point = bigint.rand(bigint("1").shiftLeft(a)); 2583 | // strong starting point, assume even number of bytes 2584 | starting_point = bigint(crypto.randomBytes(a/8).toString('hex'), 16); 2585 | if (starting_point.bitLength() == a) 2586 | break; 2587 | } 2588 | this._bigint = starting_point.nextPrime(); 2589 | } else { 2590 | // random number of bit size a 2591 | this._bigint = bigint.rand(bigint("1").shiftLeft(a)); 2592 | } 2593 | } else { 2594 | this._bigint = bigint(a, b); 2595 | } 2596 | } 2597 | }; 2598 | 2599 | // wrap 2600 | BigInteger._from_bigint = function(bi) { 2601 | var new_bigint = new BigInteger(); 2602 | new_bigint._bigint = bi; 2603 | return new_bigint; 2604 | }; 2605 | 2606 | BigInteger._is_native = true; 2607 | 2608 | BigInteger.ONE = new BigInteger("1"); 2609 | BigInteger.ZERO = new BigInteger("0"); 2610 | 2611 | // shim methods 2612 | BigInteger.prototype = { 2613 | modPow : function(e, m) { 2614 | return BigInteger._from_bigint(this._bigint.powm(e._bigint, m._bigint)); 2615 | }, 2616 | modPowInt: function(e, m) { 2617 | return this.modPow(BigInteger._from_bigint(bigint(e)), m); 2618 | }, 2619 | modInverse: function(m) { 2620 | return BigInteger._from_bigint(this._bigint.invertm(m._bigint)); 2621 | }, 2622 | equals: function(other) { 2623 | return this._bigint.eq(other._bigint); 2624 | }, 2625 | multiply: function(other) { 2626 | return BigInteger._from_bigint(this._bigint.mul(other._bigint)); 2627 | }, 2628 | mod: function(m) { 2629 | return BigInteger._from_bigint(this._bigint.mod(m._bigint)); 2630 | }, 2631 | add: function(other) { 2632 | return BigInteger._from_bigint(this._bigint.add(other._bigint)); 2633 | }, 2634 | subtract: function(other) { 2635 | return BigInteger._from_bigint(this._bigint.sub(other._bigint)); 2636 | }, 2637 | bitLength: function() { 2638 | return this._bigint.bitLength(); 2639 | }, 2640 | compareTo: function(other) { 2641 | return this._bigint.cmp(other._bigint); 2642 | }, 2643 | toString: function(base) { 2644 | return this._bigint.toString(base || 10); 2645 | }, 2646 | gcd: function(other) { 2647 | return BigInteger._from_bigint(this._bigint.gcd(other._bigint)); 2648 | }, 2649 | isProbablePrime: function(reps) { 2650 | return this._bigint.probPrime(reps); 2651 | }, 2652 | toBase64: function() { 2653 | return this._bigint.toBuffer().toString('base64'); 2654 | } 2655 | }; 2656 | 2657 | BigInteger.fromBase64 = function(b64_string) { 2658 | var bi = bigint.fromBuffer(new Buffer(b64_string, 'base64')); 2659 | return BigInteger._from_bigint(bi); 2660 | }; 2661 | 2662 | nativeBigInteger = BigInteger; 2663 | } catch (x) { 2664 | // oh well, we use normal JS 2665 | } 2666 | 2667 | // 2668 | // adding export statements to the all.js file 2669 | // 2670 | // FIXME: not clear this is how we want to do it 2671 | // 2672 | 2673 | exports.BigInteger = BigInteger; 2674 | exports.sjcl = sjcl; 2675 | exports.hex2b64 = hex2b64; 2676 | exports.b64tohex = b64tohex; 2677 | exports.int2char = int2char; 2678 | 2679 | // sha1 exports, no hmac for now 2680 | exports.hex_sha1 = hex_sha1; 2681 | exports.b64_sha1 = b64_sha1; 2682 | 2683 | // this is so we can export the faked window and navigator 2684 | // objects and not write the code for base64 twice 2685 | exports.window = window; 2686 | exports.navigator = navigator; 2687 | }); 2688 | 2689 | require.define("/lib/rng.js",function(require,module,exports,__dirname,__filename,process){/* This Source Code Form is subject to the terms of the Mozilla Public 2690 | * License, v. 2.0. If a copy of the MPL was not distributed with this 2691 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 2692 | 2693 | /* 2694 | * abstract out RNG depending on client or server. 2695 | * 2696 | * auto-seeding has to be requested. 2697 | * (the seed is automatic, not the decision to auto-seed.) 2698 | * 2699 | * nextBytes takes a byteArray as input and populates it, 2700 | * because that's how the cool kids do it and so we will not bikeshed. 2701 | */ 2702 | 2703 | var utils = require("./utils"), 2704 | delay = utils.delay, 2705 | libs = require("../libs/minimal"), 2706 | sjcl = libs.sjcl; 2707 | 2708 | // detect if we have native crypto support 2709 | var crypto = null; 2710 | try { 2711 | crypto = require("crypto"); 2712 | } catch(e) {} 2713 | 2714 | // proper boolean for whether we have native support 2715 | var IS_NATIVE = !!crypto; 2716 | 2717 | function NativeRNG() { 2718 | } 2719 | 2720 | NativeRNG.prototype = { 2721 | addEntropy: function(seed_in) { 2722 | // do nothing, natively we don't care 2723 | }, 2724 | autoseed: function(cb) { 2725 | // yay, don't need to do anything 2726 | if (cb) 2727 | delay(cb)(); 2728 | }, 2729 | nextBytes: function(byteArray) { 2730 | var randomBytes = crypto.randomBytes(byteArray.length); 2731 | for (var i=0; i now.valueOf()) 2920 | return cb("assertion issued later than verification date"); 2921 | } 2922 | 2923 | // check exp expiration 2924 | if (assertionParams.expiresAt) { 2925 | if (assertionParams.expiresAt.valueOf() < now.valueOf()) 2926 | return cb("assertion has expired"); 2927 | } 2928 | 2929 | cb(null, payload, assertionParams); 2930 | }); 2931 | }; 2932 | }); 2933 | 2934 | require.define("/lib/cert.js",function(require,module,exports,__dirname,__filename,process){/* This Source Code Form is subject to the terms of the Mozilla Public 2935 | * License, v. 2.0. If a copy of the MPL was not distributed with this 2936 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 2937 | 2938 | var jwcrypto = require("./jwcrypto"), 2939 | assertion = require("./assertion"), 2940 | utils = require("./utils"), 2941 | delay = utils.delay, 2942 | version = require("./version"), 2943 | und = require("./underscore.js"); 2944 | 2945 | var SERIALIZER = {}; 2946 | 2947 | SERIALIZER._LEGACY_serializeCertParamsInto = function(certParams, params) { 2948 | params['public-key'] = certParams.publicKey.toSimpleObject(); 2949 | params.principal = certParams.principal; 2950 | }; 2951 | 2952 | SERIALIZER._20120815_serializeCertParamsInto = function(certParams, params) { 2953 | params['publicKey'] = certParams.publicKey.toSimpleObject(); 2954 | params.principal = certParams.principal; 2955 | 2956 | params.version = "2012.08.15"; 2957 | } 2958 | 2959 | var serializeCertParamsInto = function(certParams, params) { 2960 | version.dispatchOnDataFormatVersion(SERIALIZER, 'serializeCertParamsInto', version.getDataFormatVersion(), certParams, params); 2961 | }; 2962 | 2963 | SERIALIZER._LEGACY_extractCertParamsFrom = function(params) { 2964 | var certParams = {}; 2965 | 2966 | certParams.publicKey = jwcrypto.loadPublicKey(JSON.stringify(params['public-key'])); 2967 | delete params['public-key']; 2968 | certParams.principal = params.principal; 2969 | delete params.principal; 2970 | 2971 | return certParams; 2972 | }; 2973 | 2974 | SERIALIZER._20120815_extractCertParamsFrom = function(params) { 2975 | delete params.version; 2976 | 2977 | var certParams = {}; 2978 | 2979 | certParams.publicKey = jwcrypto.loadPublicKey(JSON.stringify(params.publicKey)); 2980 | delete params.publicKey; 2981 | certParams.principal = params.principal; 2982 | delete params.principal; 2983 | 2984 | return certParams; 2985 | }; 2986 | 2987 | 2988 | function extractCertParamsFrom(params, originalComponents) { 2989 | return version.dispatchOnDataFormatVersion(SERIALIZER, 'extractCertParamsFrom', originalComponents.payload.version, params); 2990 | }; 2991 | 2992 | 2993 | exports.sign = function(certParams, assertionParams, additionalPayload, 2994 | secretKey, cb) { 2995 | var payload = {}; 2996 | utils.copyInto(additionalPayload || {}, payload); 2997 | 2998 | serializeCertParamsInto(certParams, payload); 2999 | 3000 | assertion.sign(payload, assertionParams, secretKey, cb); 3001 | }; 3002 | 3003 | var verify = function(signedObject, publicKey, now, cb) { 3004 | assertion.verify(signedObject, publicKey, now, function(err, payload, assertionParams) { 3005 | if (err) 3006 | return cb(err); 3007 | 3008 | // compatible with old format 3009 | var originalComponents = jwcrypto.extractComponents(signedObject); 3010 | var certParams = extractCertParamsFrom(payload, originalComponents); 3011 | 3012 | // make the key appear under both public-key and publicKey 3013 | cb(err, payload, assertionParams, certParams); 3014 | }); 3015 | }; 3016 | 3017 | exports.verify = verify; 3018 | 3019 | exports.bundle = function(certs, signedAssertion) { 3020 | if (!certs || certs.length == 0) 3021 | throw "certificates must be a non-empty array"; 3022 | return [].concat(certs, signedAssertion).join('~'); 3023 | }; 3024 | 3025 | exports.unbundle = function(b) { 3026 | var arr = b.split('~'); 3027 | var obj = {}; 3028 | obj.signedAssertion = arr.pop(); 3029 | obj.certs = arr; 3030 | return obj; 3031 | }; 3032 | 3033 | // verify just a chain of certs 3034 | var verifyChain = function(certs, now, getRoot, cb) { 3035 | if (!certs.length) 3036 | return delay(cb)("certs must be an array of at least one cert"); 3037 | 3038 | var rootIssuer; 3039 | try { 3040 | // the root 3041 | rootIssuer = jwcrypto.extractComponents(certs[0]).payload.iss; 3042 | } catch (x) { 3043 | // can't extract components 3044 | return delay(cb)("malformed signature"); 3045 | } 3046 | 3047 | // iterate through the certs 3048 | function verifyCert(i, pk, certParamsArray, cb) { 3049 | // do a normal verify on that cert 3050 | verify(certs[i], pk, now, function(err, payload, assertionParams, certParams) { 3051 | if (err) return cb(err); 3052 | 3053 | i += 1; 3054 | certParamsArray.push({payload: payload, 3055 | assertionParams: assertionParams, 3056 | certParams: certParams}); 3057 | 3058 | if (i >= certs.length) 3059 | cb(null, certParamsArray, certParams.publicKey); 3060 | else 3061 | delay(verifyCert)(i, certParams.publicKey, certParamsArray, cb); 3062 | }); 3063 | } 3064 | 3065 | // get the root public key 3066 | getRoot(rootIssuer, function(err, rootPK) { 3067 | if (err) return delay(cb)(err); 3068 | 3069 | verifyCert(0, rootPK, [], function(err, certParamsArray, lastPK) { 3070 | if (err) return cb(err); 3071 | 3072 | // we're done 3073 | cb(null, certParamsArray); 3074 | }); 3075 | 3076 | }); 3077 | 3078 | }; 3079 | 3080 | exports.verifyChain = verifyChain; 3081 | 3082 | exports.verifyBundle = function(bundle, now, getRoot, cb) { 3083 | // unbundle 3084 | if (typeof(bundle) != 'string') 3085 | return delay(cb)("malformed backed assertion"); 3086 | 3087 | var parsedBundle = exports.unbundle(bundle); 3088 | var signedAssertion = parsedBundle.signedAssertion; 3089 | var certs = parsedBundle.certs; 3090 | 3091 | // no certs? not okay 3092 | if (certs.length == 0) 3093 | return delay(cb)("no certificates provided"); 3094 | 3095 | // verify the chain 3096 | verifyChain(certs, now, getRoot, function(err, certParamsArray) { 3097 | // simplify error message 3098 | if (err) { 3099 | // allow through the malformed signature 3100 | if (err == 'malformed signature' || 3101 | err == "assertion issued later than verification date" || 3102 | err == "assertion has expired") 3103 | return cb(err); 3104 | else 3105 | return cb("bad signature in chain"); 3106 | } 3107 | 3108 | // what was the last PK in the successful chain? 3109 | var lastPK = certParamsArray[certParamsArray.length - 1].certParams.publicKey; 3110 | 3111 | // now verify the assertion 3112 | assertion.verify(signedAssertion, lastPK, now, function(err, payload, assertionParams) { 3113 | if (err) return cb(err); 3114 | 3115 | // we're good! 3116 | cb(null, certParamsArray, payload, assertionParams); 3117 | }); 3118 | }); 3119 | }; 3120 | }); 3121 | 3122 | require.define("/lib/underscore.js",function(require,module,exports,__dirname,__filename,process){// Underscore.js 1.1.7 3123 | // (c) 2011 Jeremy Ashkenas, DocumentCloud Inc. 3124 | // Underscore is freely distributable under the MIT license. 3125 | // Portions of Underscore are inspired or borrowed from Prototype, 3126 | // Oliver Steele's Functional, and John Resig's Micro-Templating. 3127 | // For all details and documentation: 3128 | // http://documentcloud.github.com/underscore 3129 | (function(){var p=this,C=p._,m={},i=Array.prototype,n=Object.prototype,f=i.slice,D=i.unshift,E=n.toString,l=n.hasOwnProperty,s=i.forEach,t=i.map,u=i.reduce,v=i.reduceRight,w=i.filter,x=i.every,y=i.some,o=i.indexOf,z=i.lastIndexOf;n=Array.isArray;var F=Object.keys,q=Function.prototype.bind,b=function(a){return new j(a)};typeof module!=="undefined"&&module.exports?(module.exports=b,b._=b):p._=b;b.VERSION="1.1.7";var h=b.each=b.forEach=function(a,c,b){if(a!=null)if(s&&a.forEach===s)a.forEach(c,b);else if(a.length=== 3130 | +a.length)for(var e=0,k=a.length;e=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a, 3134 | c,d){if(!c&&b.isArray(a))return Math.min.apply(Math,a);var e={computed:Infinity};h(a,function(a,b,f){b=c?c.call(d,a,b,f):a;bd?1:0}),"value")};b.groupBy=function(a,b){var d={};h(a,function(a,f){var g=b(a,f);(d[g]||(d[g]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d|| 3135 | (d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.difference=function(a,c){return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a=f.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after= 3141 | function(a,b){return function(){if(--a<1)return b.apply(this,arguments)}};b.keys=F||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var b=[],d;for(d in a)l.call(a,d)&&(b[b.length]=d);return b};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&&c.push(d);return c.sort()};b.extend=function(a){h(f.call(arguments,1),function(b){for(var d in b)b[d]!==void 0&&(a[d]=b[d])});return a};b.defaults=function(a){h(f.call(arguments, 3142 | 1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,c){if(a===c)return!0;var d=typeof a;if(d!=typeof c)return!1;if(a==c)return!0;if(!a&&c||a&&!c)return!1;if(a._chain)a=a._wrapped;if(c._chain)c=c._wrapped;if(a.isEqual)return a.isEqual(c);if(c.isEqual)return c.isEqual(a);if(b.isDate(a)&&b.isDate(c))return a.getTime()===c.getTime();if(b.isNaN(a)&&b.isNaN(c))return!1; 3143 | if(b.isRegExp(a)&&b.isRegExp(c))return a.source===c.source&&a.global===c.global&&a.ignoreCase===c.ignoreCase&&a.multiline===c.multiline;if(d!=="object")return!1;if(a.length&&a.length!==c.length)return!1;d=b.keys(a);var e=b.keys(c);if(d.length!=e.length)return!1;for(var f in a)if(!(f in c)||!b.isEqual(a[f],c[f]))return!1;return!0};b.isEmpty=function(a){if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(l.call(a,c))return!1;return!0};b.isElement=function(a){return!!(a&&a.nodeType== 3144 | 1)};b.isArray=n||function(a){return E.call(a)==="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return!(!a||!l.call(a,"callee"))};b.isFunction=function(a){return!(!a||!a.constructor||!a.call||!a.apply)};b.isString=function(a){return!!(a===""||a&&a.charCodeAt&&a.substr)};b.isNumber=function(a){return!!(a===0||a&&a.toExponential&&a.toFixed)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===!0||a===!1};b.isDate=function(a){return!(!a||!a.getTimezoneOffset|| 3145 | !a.setUTCFullYear)};b.isRegExp=function(a){return!(!a||!a.test||!a.exec||!(a.ignoreCase||a.ignoreCase===!1))};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.noConflict=function(){p._=C;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,interpolate:/<%=([\s\S]+?)%>/g}; 3146 | b.template=function(a,c){var d=b.templateSettings;d="var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push('"+a.replace(/\\/g,"\\\\").replace(/'/g,"\\'").replace(d.interpolate,function(a,b){return"',"+b.replace(/\\'/g,"'")+",'"}).replace(d.evaluate||null,function(a,b){return"');"+b.replace(/\\'/g,"'").replace(/[\r\n\t]/g," ")+"__p.push('"}).replace(/\r/g,"\\r").replace(/\n/g,"\\n").replace(/\t/g,"\\t")+"');}return __p.join('');";d=new Function("obj",d);return c?d(c):d}; 3147 | var j=function(a){this._wrapped=a};b.prototype=j.prototype;var r=function(a,c){return c?b(a).chain():a},H=function(a,c){j.prototype[a]=function(){var a=f.call(arguments);D.call(a,this._wrapped);return r(c.apply(b,a),this._chain)}};b.mixin(b);h(["pop","push","reverse","shift","sort","splice","unshift"],function(a){var b=i[a];j.prototype[a]=function(){b.apply(this._wrapped,arguments);return r(this._wrapped,this._chain)}});h(["concat","join","slice"],function(a){var b=i[a];j.prototype[a]=function(){return r(b.apply(this._wrapped, 3148 | arguments),this._chain)}});j.prototype.chain=function(){this._chain=!0;return this};j.prototype.value=function(){return this._wrapped}})(); 3149 | }); 3150 | 3151 | require.define("/lib/algs/ds.js",function(require,module,exports,__dirname,__filename,process){/* This Source Code Form is subject to the terms of the Mozilla Public 3152 | * License, v. 2.0. If a copy of the MPL was not distributed with this 3153 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 3154 | 3155 | var algs = require("./index"); 3156 | var libs = require("../../libs/minimal"); 3157 | var version = require("../version"); 3158 | var BigInteger = libs.BigInteger; 3159 | 3160 | var HASH_ALGS = { 3161 | "sha1": libs.hex_sha1, 3162 | "sha256": function(message) {return libs.sjcl.codec.hex.fromBits(libs.sjcl.hash.sha256.hash(message));} 3163 | }; 3164 | 3165 | function doHash(hashAlg, message, modulus) { 3166 | // updated for FIPS186-3, section 4.6, and integer/string conversion as per appendix c.2 3167 | var raw_hash = HASH_ALGS[hashAlg](message); 3168 | 3169 | // not really taking the minlength of bitlength and hash output, because assuming 3170 | // that the parameters we use match hash-output bitlength. 3171 | 3172 | // we don't actually need to do modulus here, because of the previous assumption 3173 | return new BigInteger(raw_hash, "16"); 3174 | } 3175 | 3176 | // pad with leading 0s a hex string 3177 | function hex_lpad(str, length) { 3178 | while (str.length < length) { 3179 | str = "0" + str; 3180 | } 3181 | return str; 3182 | } 3183 | 3184 | // supported keysizes 3185 | // the Diffie-Hellman group is specified for each keysize 3186 | // this means we don't need to specify a parameter generation step. 3187 | var KEYSIZES = { 3188 | // 160 is the keysize for standard DSA 3189 | // the following are based on the first FIPS186-3 test vectors for 1024/160 SHA-256 3190 | // under the category A.2.3 Verifiable Canonical Generation of the Generator g 3191 | // HOWEVER***** for backwards compatibility we are labeling this 128 for now 3192 | // XXXX this should be changed to 160 3193 | 128: { 3194 | p: "ff600483db6abfc5b45eab78594b3533d550d9f1bf2a992a7a8daa6dc34f8045ad4e6e0c429d334eeeaaefd7e23d4810be00e4cc1492cba325ba81ff2d5a5b305a8d17eb3bf4a06a349d392e00d329744a5179380344e82a18c47933438f891e22aeef812d69c8f75e326cb70ea000c3f776dfdbd604638c2ef717fc26d02e17", 3195 | q: "e21e04f911d1ed7991008ecaab3bf775984309c3", 3196 | g: "c52a4a0ff3b7e61fdf1867ce84138369a6154f4afa92966e3c827e25cfa6cf508b90e5de419e1337e07a2e9e2a3cd5dea704d175f8ebf6af397d69e110b96afb17c7a03259329e4829b0d03bbc7896b15b4ade53e130858cc34d96269aa89041f409136c7242a38895c9d5bccad4f389af1d7a4bd1398bd072dffa896233397a", 3197 | hashAlg: "sha1" 3198 | }, 3199 | // the following are based on the first FIPS186-3 test vectors for 2048/256 SHA-256 3200 | // under the category A.2.3 Verifiable Canonical Generation of the Generator g 3201 | 256: { 3202 | p: "d6c4e5045697756c7a312d02c2289c25d40f9954261f7b5876214b6df109c738b76226b199bb7e33f8fc7ac1dcc316e1e7c78973951bfc6ff2e00cc987cd76fcfb0b8c0096b0b460fffac960ca4136c28f4bfb580de47cf7e7934c3985e3b3d943b77f06ef2af3ac3494fc3c6fc49810a63853862a02bb1c824a01b7fc688e4028527a58ad58c9d512922660db5d505bc263af293bc93bcd6d885a157579d7f52952236dd9d06a4fc3bc2247d21f1a70f5848eb0176513537c983f5a36737f01f82b44546e8e7f0fabc457e3de1d9c5dba96965b10a2a0580b0ad0f88179e10066107fb74314a07e6745863bc797b7002ebec0b000a98eb697414709ac17b401", 3203 | q: "b1e370f6472c8754ccd75e99666ec8ef1fd748b748bbbc08503d82ce8055ab3b", 3204 | g: "9a8269ab2e3b733a5242179d8f8ddb17ff93297d9eab00376db211a22b19c854dfa80166df2132cbc51fb224b0904abb22da2c7b7850f782124cb575b116f41ea7c4fc75b1d77525204cd7c23a15999004c23cdeb72359ee74e886a1dde7855ae05fe847447d0a68059002c3819a75dc7dcbb30e39efac36e07e2c404b7ca98b263b25fa314ba93c0625718bd489cea6d04ba4b0b7f156eeb4c56c44b50e4fb5bce9d7ae0d55b379225feb0214a04bed72f33e0664d290e7c840df3e2abb5e48189fa4e90646f1867db289c6560476799f7be8420a6dc01d078de437f280fff2d7ddf1248d56e1a54b933a41629d6c252983c58795105802d30d7bcd819cf6ef", 3205 | hashAlg: "sha256" 3206 | } 3207 | }; 3208 | 3209 | function getParams(keysize) { 3210 | return KEYSIZES[parseInt(keysize)]; 3211 | } 3212 | 3213 | // turn the keysize params to bigints 3214 | for (keysize in KEYSIZES) { 3215 | var the_params = getParams(keysize); 3216 | the_params.p = new BigInteger(the_params.p, "16"); 3217 | the_params.q = new BigInteger(the_params.q, "16"); 3218 | the_params.g = new BigInteger(the_params.g, "16"); 3219 | 3220 | // sizes 3221 | the_params.q_bitlength = the_params.q.bitLength(); 3222 | } 3223 | 3224 | 3225 | function _getKeySizeFromBitlength(size) { 3226 | for (keysize in KEYSIZES) { 3227 | var keysize_nbits = KEYSIZES[keysize].p.bitLength(); 3228 | var diff = keysize_nbits - size; 3229 | 3230 | // extremely unlikely to be more than 30 bits smaller than p 3231 | // 2^-30. FIXME: should we be more tolerant here. 3232 | if (diff >= 0 && diff < 30) { 3233 | return keysize; 3234 | } 3235 | } 3236 | 3237 | return null; 3238 | } 3239 | 3240 | function randomNumberMod(q, rng) { 3241 | // do a few more bits than q so we can wrap around with not too much bias 3242 | // wow, turns out this was actually not far off from FIPS186-3, who knew? 3243 | // FIPS186-3 says to generate 64 more bits than needed into "c", then to do: 3244 | // result = (c mod (q-1)) + 1 3245 | return new BigInteger(q.bitLength() + 64, rng).mod(q.subtract(BigInteger.ONE)).add(BigInteger.ONE); 3246 | } 3247 | 3248 | function generate(keysize, rng, doneCB) { 3249 | var params = getParams(keysize); 3250 | if (!params) 3251 | throw "keysize not supported: " + keysize.toString(); 3252 | 3253 | var keypair = new algs.KeyPair(); 3254 | keypair.keysize= keysize; 3255 | 3256 | // DSA key gen: random x modulo q 3257 | var x = randomNumberMod(params.q, rng); 3258 | 3259 | // the secret key will compute y 3260 | keypair.secretKey = new SecretKey(x, keypair.keysize, params); 3261 | keypair.publicKey = new PublicKey(keypair.secretKey.y, keypair.keysize, params); 3262 | 3263 | keypair.publicKey.algorithm = keypair.secretKey.algorithm = keypair.algorithm = 'DS'; 3264 | 3265 | // XXX - timeout or nexttick? 3266 | doneCB(null, keypair); 3267 | }; 3268 | 3269 | var PublicKey = function(y, keysize, params) { 3270 | this.y = y; 3271 | 3272 | if (keysize && params) { 3273 | this.keysize = keysize; 3274 | 3275 | // copy params 3276 | this.q = params.q; this.g = params.g; this.p = params.p; 3277 | } 3278 | }; 3279 | 3280 | PublicKey.prototype = new algs.PublicKey(); 3281 | 3282 | PublicKey.prototype._20120815_serializeToObject = function(obj) { 3283 | obj.version = '2012.08.15'; 3284 | obj.y = this.y.toBase64(); 3285 | obj.p = this.p.toBase64(); 3286 | obj.q = this.q.toBase64(); 3287 | obj.g = this.g.toBase64(); 3288 | }; 3289 | 3290 | PublicKey.prototype._LEGACY_serializeToObject = function(obj) { 3291 | obj.y = this.y.toString(16); 3292 | obj.p = this.p.toString(16); 3293 | obj.q = this.q.toString(16); 3294 | obj.g = this.g.toString(16); 3295 | }; 3296 | 3297 | PublicKey.prototype.serializeToObject = version.versionDispatcher('serializeToObject'); 3298 | 3299 | PublicKey.prototype.equals = function(other) { 3300 | if (other == null) 3301 | return false; 3302 | 3303 | return (other.algorithm == this.algorithm && 3304 | this.p.equals(other.p) && 3305 | this.y.equals(other.y) && 3306 | this.g.equals(other.g) && 3307 | this.q.equals(other.q)); 3308 | }; 3309 | 3310 | PublicKey.prototype._LEGACY_deserializeFromObject = function(obj) { 3311 | this.p = new BigInteger(obj.p, 16); 3312 | this.q = new BigInteger(obj.q, 16); 3313 | this.g = new BigInteger(obj.g, 16); 3314 | this.y = new BigInteger(obj.y, 16); 3315 | }; 3316 | 3317 | PublicKey.prototype._20120815_deserializeFromObject = function(obj) { 3318 | this.p = BigInteger.fromBase64(obj.p); 3319 | this.q = BigInteger.fromBase64(obj.q); 3320 | this.g = BigInteger.fromBase64(obj.g); 3321 | this.y = BigInteger.fromBase64(obj.y); 3322 | }; 3323 | 3324 | PublicKey.prototype.deserializeFromObject = function(obj) { 3325 | version.dispatchOnDataFormatVersion(this, 'deserializeFromObject', obj.version, obj); 3326 | 3327 | this.keysize = _getKeySizeFromBitlength(this.y.bitLength()); 3328 | return this; 3329 | }; 3330 | 3331 | // note: this deserialization code does not check that the public key is 3332 | // well-formed (P and Q are primes, G is actually a generator, etc), and it 3333 | // allows the use of any group (instead of being restricted to e.g. the 3334 | // ones published by NIST). For sign/verify that is ok: when someone else 3335 | // gives a public key, they're instructing us how to distinguish between good 3336 | // signatures and forgeries, and giving us a corrupt pubkey is their 3337 | // perogative (plus we have no secrets to lose). 3338 | // 3339 | // Do not use this approach for DH key agreement. In that world, we *do* have 3340 | // a private key that could be lost, and a maliciously crafted pubkey could 3341 | // be used to learn it. Additional checks would be necessary to do that 3342 | // safely. 3343 | 3344 | function SecretKey(x, keysize, params) { 3345 | this.x = x; 3346 | 3347 | // compute y if need be 3348 | if (params && keysize) { 3349 | this.y = params.g.modPow(this.x, params.p); 3350 | this.keysize = keysize; 3351 | 3352 | // copy params 3353 | this.q = params.q; this.g = params.g; this.p = params.p; 3354 | } 3355 | }; 3356 | 3357 | SecretKey.prototype = new algs.SecretKey(); 3358 | 3359 | SecretKey.prototype._LEGACY_serializeToObject = function(obj) { 3360 | obj.x = this.x.toString(16); 3361 | obj.p = this.p.toString(16); 3362 | obj.q = this.q.toString(16); 3363 | obj.g = this.g.toString(16); 3364 | }; 3365 | 3366 | SecretKey.prototype._20120815_serializeToObject = function(obj) { 3367 | obj.version = '2012.08.15'; 3368 | obj.x = this.x.toBase64(); 3369 | obj.p = this.p.toBase64(); 3370 | obj.q = this.q.toBase64(); 3371 | obj.g = this.g.toBase64(); 3372 | }; 3373 | 3374 | SecretKey.prototype.serializeToObject = version.versionDispatcher('serializeToObject'); 3375 | 3376 | SecretKey.prototype._LEGACY_deserializeFromObject = function(obj) { 3377 | this.x = new BigInteger(obj.x, 16); 3378 | 3379 | this.p = new BigInteger(obj.p, 16); 3380 | this.q = new BigInteger(obj.q, 16); 3381 | this.g = new BigInteger(obj.g, 16); 3382 | }; 3383 | 3384 | SecretKey.prototype._20120815_deserializeFromObject = function(obj) { 3385 | this.x = BigInteger.fromBase64(obj.x); 3386 | 3387 | this.p = BigInteger.fromBase64(obj.p); 3388 | this.q = BigInteger.fromBase64(obj.q); 3389 | this.g = BigInteger.fromBase64(obj.g); 3390 | }; 3391 | 3392 | SecretKey.prototype.deserializeFromObject = function(obj) { 3393 | version.dispatchOnDataFormatVersion(this, 'deserializeFromObject', obj.version, 3394 | obj); 3395 | 3396 | this.keysize = _getKeySizeFromBitlength(this.p.bitLength()); 3397 | 3398 | return this; 3399 | }; 3400 | 3401 | 3402 | SecretKey.prototype.sign = function(message, rng, progressCB, doneCB) { 3403 | var params = getParams(this.keysize); 3404 | 3405 | // see https://secure.wikimedia.org/wikipedia/en/wiki/Digital_Signature_Algorithm 3406 | 3407 | // only using single-letter vars here because that's how this is defined in the algorithm 3408 | var k, r, s; 3409 | 3410 | // do this until r != 0 (very unlikely, but hey) 3411 | while(true) { 3412 | k = randomNumberMod(this.q, rng); 3413 | r = this.g.modPow(k, this.p).mod(this.q); 3414 | 3415 | if (r.equals(BigInteger.ZERO)) { 3416 | console.log("oops r is zero"); 3417 | continue; 3418 | } 3419 | 3420 | // the hash 3421 | var bigint_hash = doHash(params.hashAlg, message, this.q); 3422 | 3423 | // compute H(m) + (x*r) 3424 | var message_dep = bigint_hash.add(this.x.multiply(r).mod(this.q)).mod(this.q); 3425 | 3426 | // compute s 3427 | s = k.modInverse(this.q).multiply(message_dep).mod(this.q); 3428 | 3429 | if (s.equals(BigInteger.ZERO)) { 3430 | console.log("oops s is zero"); 3431 | continue; 3432 | } 3433 | 3434 | // r and s are non-zero, we can continue 3435 | break; 3436 | } 3437 | 3438 | // format the signature, it's r and s 3439 | var hexlength = params.q_bitlength / 4; 3440 | var signature = hex_lpad(r.toString(16), hexlength) + hex_lpad(s.toString(16), hexlength); 3441 | 3442 | if (!progressCB) 3443 | return signature; 3444 | else 3445 | doneCB(signature); 3446 | }; 3447 | 3448 | PublicKey.prototype.verify = function(message, signature, cb) { 3449 | var params = getParams(this.keysize); 3450 | 3451 | // extract r and s 3452 | var hexlength = params.q_bitlength / 4; 3453 | 3454 | // we pre-pad with 0s because encoding may have gotten rid of some 3455 | signature = hex_lpad(signature, hexlength * 2); 3456 | 3457 | // now this should only happen if the signature was longer 3458 | if (signature.length != (hexlength * 2)) { 3459 | //return cb("problem with r/s combo: " + signature.length + "/" + hexlength + " - " + signature); 3460 | return cb("malformed signature"); 3461 | } 3462 | 3463 | var r = new BigInteger(signature.substring(0, hexlength), 16), 3464 | s = new BigInteger(signature.substring(hexlength, hexlength*2), 16); 3465 | 3466 | // check rangeconstraints 3467 | if ((r.compareTo(BigInteger.ZERO) < 0) || (r.compareTo(this.q) > 0)) { 3468 | //return cb("problem with r: " + r.toString(16)); 3469 | return cb("invalid signature"); 3470 | } 3471 | if ((s.compareTo(BigInteger.ZERO) < 0) || (s.compareTo(this.q) > 0)) { 3472 | //return cb("problem with s"); 3473 | return cb("invalid signature"); 3474 | } 3475 | 3476 | var w = s.modInverse(this.q); 3477 | var u1 = doHash(params.hashAlg, message, this.q).multiply(w).mod(this.q); 3478 | var u2 = r.multiply(w).mod(this.q); 3479 | var v = this.g 3480 | .modPow(u1,this.p) 3481 | .multiply(this.y.modPow(u2,this.p)).mod(this.p) 3482 | .mod(this.q); 3483 | 3484 | cb(null, v.equals(r)); 3485 | }; 3486 | 3487 | // register this stuff 3488 | algs.register("DS", { 3489 | generate: generate, 3490 | PublicKey: PublicKey, 3491 | SecretKey: SecretKey}); 3492 | 3493 | }); 3494 | 3495 | require.define("/bundle.js",function(require,module,exports,__dirname,__filename,process){/* ***** BEGIN LICENSE BLOCK ***** 3496 | * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3497 | * 3498 | * The contents of this file are subject to the Mozilla Public License Version 3499 | * 1.1 (the "License"); you may not use this file except in compliance with 3500 | * the License. You may obtain a copy of the License at 3501 | * http://www.mozilla.org/MPL/ 3502 | * 3503 | * Software distributed under the License is distributed on an "AS IS" basis, 3504 | * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 3505 | * for the specific language governing rights and limitations under the 3506 | * License. 3507 | * 3508 | * Contributor(s): 3509 | * Ben Adida 3510 | * Michael Hanson 3511 | * 3512 | * Alternatively, the contents of this file may be used under the terms of 3513 | * either the GNU General Public License Version 2 or later (the "GPL"), or 3514 | * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 3515 | * in which case the provisions of the GPL or the LGPL are applicable instead 3516 | * of those above. If you wish to allow use of your version of this file only 3517 | * under the terms of either the GPL or the LGPL, and not to allow others to 3518 | * use your version of this file under the terms of the MPL, indicate your 3519 | * decision by deleting the provisions above and replace them with the notice 3520 | * and other provisions required by the GPL or the LGPL. If you do not delete 3521 | * the provisions above, a recipient may use your version of this file under 3522 | * the terms of any one of the MPL, the GPL or the LGPL. 3523 | * 3524 | * ***** END LICENSE BLOCK ***** */ 3525 | 3526 | var jwcrypto = require("./lib/jwcrypto"); 3527 | require("./lib/algs/ds"); 3528 | 3529 | }); 3530 | require("/bundle.js"); 3531 | --------------------------------------------------------------------------------