diff --git a/app.js b/app.js
index 3531ee2..03c7bf2 100644
--- a/app.js
+++ b/app.js
@@ -1,4 +1,3 @@
-/*global Promise*/
(function () {
'use strict';
@@ -6,7 +5,6 @@
var Rasha = window.Rasha;
var Eckles = window.Eckles;
var x509 = window.x509;
- var ACME = window.ACME;
function $(sel) {
return document.querySelector(sel);
@@ -104,69 +102,6 @@
});
});
- $('form.js-acme-account').addEventListener('submit', function (ev) {
- ev.preventDefault();
- ev.stopPropagation();
- $('.js-loading').hidden = false;
- var acme = ACME.create({
- Keypairs: Keypairs
- });
- acme.init('https://acme-staging-v02.api.letsencrypt.org/directory').then(function (result) {
- console.log('acme result', result);
- var privJwk = JSON.parse($('.js-jwk').innerText).private;
- var email = $('.js-email').innerText;
- function checkTos(tos) {
- console.log("TODO checkbox for agree to terms");
- return tos;
- }
- return acme.accounts.create({
- email: email
- , agreeToTerms: checkTos
- , accountKeypair: { privateKeyJwk: privJwk }
- }).then(function (account) {
- console.log("account created result:", account);
- return Keypairs.generate({
- kty: 'RSA'
- , modulusLength: 2048
- }).then(function (pair) {
- console.log('domain keypair:', pair);
- var domains = ($('.js-domains').innerText||'example.com').split(/[, ]+/g);
- return acme.certificates.create({
- accountKeypair: { privateKeyJwk: privJwk }
- , account: account
- , domainKeypair: { privateKeyJwk: pair.private }
- , email: email
- , domains: domains
- , agreeToTerms: checkTos
- , challenges: {
- 'dns-01': {
- set: function (opts) {
- console.log('dns-01 set challenge:');
- console.log(JSON.stringify(opts, null, 2));
- return new Promise(function (resolve) {
- while (!window.confirm("Did you set the challenge?")) {}
- resolve();
- });
- }
- , remove: function (opts) {
- console.log('dns-01 remove challenge:');
- console.log(JSON.stringify(opts, null, 2));
- return new Promise(function (resolve) {
- while (!window.confirm("Did you delete the challenge?")) {}
- resolve();
- });
- }
- }
- }
- });
- });
- }).catch(function (err) {
- console.error("A bad thing happened:");
- console.error(err);
- window.alert(err.message || JSON.stringify(err, null, 2));
- });
- });
- });
$('.js-generate').hidden = false;
$('.js-create-account').hidden = false;
diff --git a/index.html b/index.html
index b4d91c8..4cfdb8a 100644
--- a/index.html
+++ b/index.html
@@ -59,17 +59,6 @@
-
ACME Account
-
-
Loading
@@ -104,14 +93,7 @@
PEM Public (base64-encoded SPKI/PKIX DER)
-
- ACME Account Request
-
-
-
- ACME Account Response
-
-
+
diff --git a/lib/acme.js b/lib/acme.js
deleted file mode 100644
index d5b3438..0000000
--- a/lib/acme.js
+++ /dev/null
@@ -1,1017 +0,0 @@
-// Copyright 2018-present AJ ONeal. All rights reserved
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-(function (exports) {
-'use strict';
-/* globals Promise */
-
-var ACME = exports.ACME = {};
-//var Keypairs = exports.Keypairs || {};
-var Enc = exports.Enc || {};
-var Crypto = exports.Crypto || {};
-
-ACME.formatPemChain = function formatPemChain(str) {
- return str.trim().replace(/[\r\n]+/g, '\n').replace(/\-\n\-/g, '-\n\n-') + '\n';
-};
-ACME.splitPemChain = function splitPemChain(str) {
- return str.trim().split(/[\r\n]{2,}/g).map(function (str) {
- return str + '\n';
- });
-};
-
-
-// http-01: GET https://example.org/.well-known/acme-challenge/{{token}} => {{keyAuth}}
-// dns-01: TXT _acme-challenge.example.org. => "{{urlSafeBase64(sha256(keyAuth))}}"
-ACME.challengePrefixes = {
- 'http-01': '/.well-known/acme-challenge'
-, 'dns-01': '_acme-challenge'
-};
-ACME.challengeTests = {
- 'http-01': function (me, auth) {
- var url = 'http://' + auth.hostname + ACME.challengePrefixes['http-01'] + '/' + auth.token;
- return me.request({ method: 'GET', url: url }).then(function (resp) {
- var err;
-
- // TODO limit the number of bytes that are allowed to be downloaded
- if (auth.keyAuthorization === resp.body.toString('utf8').trim()) {
- return true;
- }
-
- err = new Error(
- "Error: Failed HTTP-01 Pre-Flight / Dry Run.\n"
- + "curl '" + url + "'\n"
- + "Expected: '" + auth.keyAuthorization + "'\n"
- + "Got: '" + resp.body + "'\n"
- + "See https://git.coolaj86.com/coolaj86/acme-v2.js/issues/4"
- );
- err.code = 'E_FAIL_DRY_CHALLENGE';
- return Promise.reject(err);
- });
- }
-, 'dns-01': function (me, auth) {
- // remove leading *. on wildcard domains
- return me.dig({
- type: 'TXT'
- , name: auth.dnsHost
- }).then(function (ans) {
- var err;
-
- if (ans.answer.some(function (txt) {
- return auth.dnsAuthorization === txt.data[0];
- })) {
- return true;
- }
-
- err = new Error(
- "Error: Failed DNS-01 Pre-Flight Dry Run.\n"
- + "dig TXT '" + auth.dnsHost + "' does not return '" + auth.dnsAuthorization + "'\n"
- + "See https://git.coolaj86.com/coolaj86/acme-v2.js/issues/4"
- );
- err.code = 'E_FAIL_DRY_CHALLENGE';
- return Promise.reject(err);
- });
- }
-};
-
-ACME._directory = function (me) {
- // GET-as-GET ok
- return me.request({ method: 'GET', url: me.directoryUrl, json: true });
-};
-ACME._getNonce = function (me) {
- // GET-as-GET, HEAD-as-HEAD ok
- var nonce;
- while (true) {
- nonce = me._nonces.shift();
- if (!nonce) { break; }
- if (Date.now() - nonce.createdAt > (15 * 60 * 1000)) {
- nonce = null;
- } else {
- break;
- }
- }
- if (nonce) { return Promise.resolve(nonce.nonce); }
- return me.request({ method: 'HEAD', url: me._directoryUrls.newNonce }).then(function (resp) {
- return resp.headers['replay-nonce'];
- });
-};
-ACME._setNonce = function (me, nonce) {
- me._nonces.unshift({ nonce: nonce, createdAt: Date.now() });
-};
-// ACME RFC Section 7.3 Account Creation
-/*
- {
- "protected": base64url({
- "alg": "ES256",
- "jwk": {...},
- "nonce": "6S8IqOGY7eL2lsGoTZYifg",
- "url": "https://example.com/acme/new-account"
- }),
- "payload": base64url({
- "termsOfServiceAgreed": true,
- "onlyReturnExisting": false,
- "contact": [
- "mailto:cert-admin@example.com",
- "mailto:admin@example.com"
- ]
- }),
- "signature": "RZPOnYoPs1PhjszF...-nh6X1qtOFPB519I"
- }
-*/
-ACME._registerAccount = function (me, options) {
- if (me.debug) { console.debug('[acme-v2] accounts.create'); }
-
- return new Promise(function (resolve, reject) {
-
- function agree(tosUrl) {
- var err;
- if (me._tos !== tosUrl) {
- err = new Error("You must agree to the ToS at '" + me._tos + "'");
- err.code = "E_AGREE_TOS";
- reject(err);
- return;
- }
-
- return ACME._importKeypair(me, options.accountKeypair).then(function (pair) {
- var contact;
- if (options.contact) {
- contact = options.contact.slice(0);
- } else if (options.email) {
- contact = [ 'mailto:' + options.email ];
- }
- var body = {
- termsOfServiceAgreed: tosUrl === me._tos
- , onlyReturnExisting: false
- , contact: contact
- };
- var pExt;
- if (options.externalAccount) {
- pExt = me.Keypairs.signJws({
- // TODO is HMAC the standard, or is this arbitrary?
- secret: options.externalAccount.secret
- , protected: {
- alg: options.externalAccount.alg || "HS256"
- , kid: options.externalAccount.id
- , url: me._directoryUrls.newAccount
- }
- , payload: Enc.strToBuf(JSON.stringify(pair.public))
- }).then(function (jws) {
- body.externalAccountBinding = jws;
- return body;
- });
- } else {
- pExt = Promise.resolve(body);
- }
- return pExt.then(function (body) {
- var payload = JSON.stringify(body);
- return ACME._jwsRequest(me, {
- options: options
- , url: me._directoryUrls.newAccount
- , protected: { kid: false, jwk: pair.public }
- , payload: Enc.binToBuf(payload)
- }).then(function (resp) {
- var account = resp.body;
-
- if (2 !== Math.floor(resp.statusCode / 100)) {
- throw new Error('account error: ' + JSON.stringify(resp.body));
- }
-
- var location = resp.headers.location;
- // the account id url
- options._kid = location;
- if (me.debug) { console.debug('[DEBUG] new account location:'); }
- if (me.debug) { console.debug(location); }
- if (me.debug) { console.debug(resp); }
-
- /*
- {
- contact: ["mailto:jon@example.com"],
- orders: "https://some-url",
- status: 'valid'
- }
- */
- if (!account) { account = { _emptyResponse: true }; }
- // https://git.coolaj86.com/coolaj86/acme-v2.js/issues/8
- if (!account.key) { account.key = {}; }
- account.key.kid = options._kid;
- return account;
- }).then(resolve, reject);
- });
- });
- }
-
- if (me.debug) { console.debug('[acme-v2] agreeToTerms'); }
- if (1 === options.agreeToTerms.length) {
- // newer promise API
- return Promise.resolve(options.agreeToTerms(me._tos)).then(agree, reject);
- }
- else if (2 === options.agreeToTerms.length) {
- // backwards compat cb API
- return options.agreeToTerms(me._tos, function (err, tosUrl) {
- if (!err) { agree(tosUrl); return; }
- reject(err);
- });
- }
- else {
- reject(new Error('agreeToTerms has incorrect function signature.'
- + ' Should be fn(tos) { return Promise; }'));
- }
- });
-};
-/*
- POST /acme/new-order HTTP/1.1
- Host: example.com
- Content-Type: application/jose+json
-
- {
- "protected": base64url({
- "alg": "ES256",
- "kid": "https://example.com/acme/acct/1",
- "nonce": "5XJ1L3lEkMG7tR6pA00clA",
- "url": "https://example.com/acme/new-order"
- }),
- "payload": base64url({
- "identifiers": [{"type:"dns","value":"example.com"}],
- "notBefore": "2016-01-01T00:00:00Z",
- "notAfter": "2016-01-08T00:00:00Z"
- }),
- "signature": "H6ZXtGjTZyUnPeKn...wEA4TklBdh3e454g"
- }
-*/
-ACME._getChallenges = function (me, options, authUrl) {
- if (me.debug) { console.debug('\n[DEBUG] getChallenges\n'); }
- // TODO POST-as-GET
-
- return ACME._jwsRequest(me, {
- options: options
- , protected: {}
- , payload: ''
- , url: authUrl
- }).then(function (resp) {
- return resp.body;
- });
-};
-ACME._wait = function wait(ms) {
- return new Promise(function (resolve) {
- setTimeout(resolve, (ms || 1100));
- });
-};
-
-ACME._testChallengeOptions = function () {
- var chToken = ACME._prnd(16);
- return [
- {
- "type": "http-01",
- "status": "pending",
- "url": "https://acme-staging-v02.example.com/0",
- "token": "test-" + chToken + "-0"
- }
- , {
- "type": "dns-01",
- "status": "pending",
- "url": "https://acme-staging-v02.example.com/1",
- "token": "test-" + chToken + "-1",
- "_wildcard": true
- }
- , {
- "type": "tls-sni-01",
- "status": "pending",
- "url": "https://acme-staging-v02.example.com/2",
- "token": "test-" + chToken + "-2"
- }
- , {
- "type": "tls-alpn-01",
- "status": "pending",
- "url": "https://acme-staging-v02.example.com/3",
- "token": "test-" + chToken + "-3"
- }
- ];
-};
-ACME._testChallenges = function (me, options) {
- if (me.skipChallengeTest) {
- return Promise.resolve();
- }
-
- var CHECK_DELAY = 0;
- return Promise.all(options.domains.map(function (identifierValue) {
- // TODO we really only need one to pass, not all to pass
- var challenges = ACME._testChallengeOptions();
- if (identifierValue.includes("*")) {
- challenges = challenges.filter(function (ch) { return ch._wildcard; });
- }
-
- var challenge = ACME._chooseChallenge(options, { challenges: challenges });
- if (!challenge) {
- // For example, wildcards require dns-01 and, if we don't have that, we have to bail
- var enabled = options.challengeTypes.join(', ') || 'none';
- var suitable = challenges.map(function (r) { return r.type; }).join(', ') || 'none';
- return Promise.reject(new Error(
- "None of the challenge types that you've enabled ( " + enabled + " )"
- + " are suitable for validating the domain you've selected (" + identifierValue + ")."
- + " You must enable one of ( " + suitable + " )."
- ));
- }
- if ('dns-01' === challenge.type) {
- // Give the nameservers a moment to propagate
- CHECK_DELAY = 1.5 * 1000;
- }
-
- return Promise.resolve().then(function () {
- var results = {
- identifier: {
- type: "dns"
- , value: identifierValue.replace(/^\*\./, '')
- }
- , challenges: [ challenge ]
- , expires: new Date(Date.now() + (60 * 1000)).toISOString()
- , wildcard: identifierValue.includes('*.') || undefined
- };
- var dryrun = true;
- return ACME._challengeToAuth(me, options, results, challenge, dryrun).then(function (auth) {
- return ACME._setChallenge(me, options, auth).then(function () {
- return auth;
- });
- });
- });
- })).then(function (auths) {
- return ACME._wait(CHECK_DELAY).then(function () {
- return Promise.all(auths.map(function (auth) {
- return ACME.challengeTests[auth.type](me, auth);
- }));
- });
- });
-};
-ACME._chooseChallenge = function(options, results) {
- // For each of the challenge types that we support
- var challenge;
- options.challengeTypes.some(function (chType) {
- // And for each of the challenge types that are allowed
- return results.challenges.some(function (ch) {
- // Check to see if there are any matches
- if (ch.type === chType) {
- challenge = ch;
- return true;
- }
- });
- });
-
- return challenge;
-};
-ACME._challengeToAuth = function (me, options, request, challenge, dryrun) {
- // we don't poison the dns cache with our dummy request
- var dnsPrefix = ACME.challengePrefixes['dns-01'];
- if (dryrun) {
- dnsPrefix = dnsPrefix.replace('acme-challenge', 'greenlock-dryrun-' + ACME._prnd(4));
- }
-
- var auth = {};
-
- // straight copy from the new order response
- // { identifier, status, expires, challenges, wildcard }
- Object.keys(request).forEach(function (key) {
- auth[key] = request[key];
- });
-
- // copy from the challenge we've chosen
- // { type, status, url, token }
- // (note the duplicate status overwrites the one above, but they should be the same)
- Object.keys(challenge).forEach(function (key) {
- // don't confused devs with the id url
- auth[key] = challenge[key];
- });
-
- // batteries-included helpers
- auth.hostname = auth.identifier.value;
- // because I'm not 100% clear if the wildcard identifier does or doesn't have the leading *. in all cases
- auth.altname = ACME._untame(auth.identifier.value, auth.wildcard);
- return ACME._importKeypair(me, options.accountKeypair).then(function (pair) {
- return me.Keypairs.thumbprint({ jwk: pair.public }).then(function (thumb) {
- auth.thumbprint = thumb;
- // keyAuthorization = token || '.' || base64url(JWK_Thumbprint(accountKey))
- auth.keyAuthorization = challenge.token + '.' + auth.thumbprint;
- // conflicts with ACME challenge id url is already in use, so we call this challengeUrl instead
- auth.challengeUrl = 'http://' + auth.identifier.value + ACME.challengePrefixes['http-01'] + '/' + auth.token;
- auth.dnsHost = dnsPrefix + '.' + auth.hostname.replace('*.', '');
-
- return Crypto._sha('sha256', auth.keyAuthorization).then(function (hash) {
- auth.dnsAuthorization = hash;
- return auth;
- });
- });
- });
-};
-
-ACME._untame = function (name, wild) {
- if (wild) { name = '*.' + name.replace('*.', ''); }
- return name;
-};
-
-// https://tools.ietf.org/html/draft-ietf-acme-acme-10#section-7.5.1
-ACME._postChallenge = function (me, options, auth) {
- var RETRY_INTERVAL = me.retryInterval || 1000;
- var DEAUTH_INTERVAL = me.deauthWait || 10 * 1000;
- var MAX_POLL = me.retryPoll || 8;
- var MAX_PEND = me.retryPending || 4;
- var count = 0;
-
- var altname = ACME._untame(auth.identifier.value, auth.wildcard);
-
- /*
- POST /acme/authz/1234 HTTP/1.1
- Host: example.com
- Content-Type: application/jose+json
-
- {
- "protected": base64url({
- "alg": "ES256",
- "kid": "https://example.com/acme/acct/1",
- "nonce": "xWCM9lGbIyCgue8di6ueWQ",
- "url": "https://example.com/acme/authz/1234"
- }),
- "payload": base64url({
- "status": "deactivated"
- }),
- "signature": "srX9Ji7Le9bjszhu...WTFdtujObzMtZcx4"
- }
- */
- function deactivate() {
- if (me.debug) { console.debug('[acme-v2.js] deactivate:'); }
- return ACME._jwsRequest({
- options: options
- , url: auth.url
- , protected: { kid: options._kid }
- , payload: Enc.strToBuf(JSON.stringify({ "status": "deactivated" }))
- }).then(function (resp) {
- if (me.debug) { console.debug('deactivate challenge: resp.body:'); }
- if (me.debug) { console.debug(resp.body); }
- return ACME._wait(DEAUTH_INTERVAL);
- });
- }
-
- function pollStatus() {
- if (count >= MAX_POLL) {
- return Promise.reject(new Error(
- "[acme-v2] stuck in bad pending/processing state for '" + altname + "'"
- ));
- }
-
- count += 1;
-
- if (me.debug) { console.debug('\n[DEBUG] statusChallenge\n'); }
- // TODO POST-as-GET
- return me.request({ method: 'GET', url: auth.url, json: true }).then(function (resp) {
- if ('processing' === resp.body.status) {
- if (me.debug) { console.debug('poll: again'); }
- return ACME._wait(RETRY_INTERVAL).then(pollStatus);
- }
-
- // This state should never occur
- if ('pending' === resp.body.status) {
- if (count >= MAX_PEND) {
- return ACME._wait(RETRY_INTERVAL).then(deactivate).then(respondToChallenge);
- }
- if (me.debug) { console.debug('poll: again'); }
- return ACME._wait(RETRY_INTERVAL).then(respondToChallenge);
- }
-
- if ('valid' === resp.body.status) {
- if (me.debug) { console.debug('poll: valid'); }
-
- try {
- if (1 === options.removeChallenge.length) {
- options.removeChallenge(auth).then(function () {}, function () {});
- } else if (2 === options.removeChallenge.length) {
- options.removeChallenge(auth, function (err) { return err; });
- } else {
- if (!ACME._removeChallengeWarn) {
- console.warn("Please update to acme-v2 removeChallenge(options) or removeChallenge(options, cb).");
- console.warn("The API has been changed for compatibility with all ACME / Let's Encrypt challenge types.");
- ACME._removeChallengeWarn = true;
- }
- options.removeChallenge(auth.request.identifier, auth.token, function () {});
- }
- } catch(e) {}
- return resp.body;
- }
-
- var errmsg;
- if (!resp.body.status) {
- errmsg = "[acme-v2] (E_STATE_EMPTY) empty challenge state for '" + altname + "':";
- }
- else if ('invalid' === resp.body.status) {
- errmsg = "[acme-v2] (E_STATE_INVALID) challenge state for '" + altname + "': '" + resp.body.status + "'";
- }
- else {
- errmsg = "[acme-v2] (E_STATE_UKN) challenge state for '" + altname + "': '" + resp.body.status + "'";
- }
-
- return Promise.reject(new Error(errmsg));
- });
- }
-
- function respondToChallenge() {
- if (me.debug) { console.debug('[acme-v2.js] responding to accept challenge:'); }
- return ACME._jwsRequest({
- options: options
- , url: auth.url
- , protected: { kid: options._kid }
- , payload: Enc.strToBuf(JSON.stringify({}))
- }).then(function (resp) {
- if (me.debug) { console.debug('respond to challenge: resp.body:'); }
- if (me.debug) { console.debug(resp.body); }
- return ACME._wait(RETRY_INTERVAL).then(pollStatus);
- });
- }
-
- return respondToChallenge();
-};
-ACME._setChallenge = function (me, options, auth) {
- console.log('challenge auth:', auth);
- console.log('challenges:', options.challenges);
- return new Promise(function (resolve, reject) {
- var challengers = options.challenges || {};
- var challenger = (challengers[auth.type] && challengers[auth.type].set) || options.setChallenge;
- try {
- if (1 === challenger.length) {
- challenger(auth).then(resolve).catch(reject);
- } else if (2 === challenger.length) {
- challenger(auth, function (err) {
- if(err) { reject(err); } else { resolve(); }
- });
- } else {
- // TODO remove this old backwards-compat
- var challengeCb = function(err) {
- if(err) { reject(err); } else { resolve(); }
- };
- // for backwards compat adding extra keys without changing params length
- Object.keys(auth).forEach(function (key) {
- challengeCb[key] = auth[key];
- });
- if (!ACME._setChallengeWarn) {
- console.warn("Please update to acme-v2 setChallenge(options) or setChallenge(options, cb).");
- console.warn("The API has been changed for compatibility with all ACME / Let's Encrypt challenge types.");
- ACME._setChallengeWarn = true;
- }
- challenger(auth.identifier.value, auth.token, auth.keyAuthorization, challengeCb);
- }
- } catch(e) {
- reject(e);
- }
- }).then(function () {
- // TODO: Do we still need this delay? Or shall we leave it to plugins to account for themselves?
- var DELAY = me.setChallengeWait || 500;
- if (me.debug) { console.debug('\n[DEBUG] waitChallengeDelay %s\n', DELAY); }
- return ACME._wait(DELAY);
- });
-};
-ACME._finalizeOrder = function (me, options, validatedDomains) {
- if (me.debug) { console.debug('finalizeOrder:'); }
- return ACME._generateCsrWeb64(me, options, validatedDomains).then(function (csr) {
- var body = { csr: csr };
- var payload = JSON.stringify(body);
-
- function pollCert() {
- if (me.debug) { console.debug('[acme-v2.js] pollCert:'); }
- return ACME._jwsRequest({
- options: options
- , url: options._finalize
- , protected: { kid: options._kid }
- , payload: Enc.strToBuf(payload)
- }).then(function (resp) {
- if (me.debug) { console.debug('order finalized: resp.body:'); }
- if (me.debug) { console.debug(resp.body); }
-
- // https://tools.ietf.org/html/draft-ietf-acme-acme-12#section-7.1.3
- // Possible values are: "pending" => ("invalid" || "ready") => "processing" => "valid"
- if ('valid' === resp.body.status) {
- options._expires = resp.body.expires;
- options._certificate = resp.body.certificate;
-
- return resp.body; // return order
- }
-
- if ('processing' === resp.body.status) {
- return ACME._wait().then(pollCert);
- }
-
- if (me.debug) { console.debug("Error: bad status:\n" + JSON.stringify(resp.body, null, 2)); }
-
- if ('pending' === resp.body.status) {
- return Promise.reject(new Error(
- "Did not finalize order: status 'pending'."
- + " Best guess: You have not accepted at least one challenge for each domain:\n"
- + "Requested: '" + options.domains.join(', ') + "'\n"
- + "Validated: '" + validatedDomains.join(', ') + "'\n"
- + JSON.stringify(resp.body, null, 2)
- ));
- }
-
- if ('invalid' === resp.body.status) {
- return Promise.reject(new Error(
- "Did not finalize order: status 'invalid'."
- + " Best guess: One or more of the domain challenges could not be verified"
- + " (or the order was canceled).\n"
- + "Requested: '" + options.domains.join(', ') + "'\n"
- + "Validated: '" + validatedDomains.join(', ') + "'\n"
- + JSON.stringify(resp.body, null, 2)
- ));
- }
-
- if ('ready' === resp.body.status) {
- return Promise.reject(new Error(
- "Did not finalize order: status 'ready'."
- + " Hmmm... this state shouldn't be possible here. That was the last state."
- + " This one should at least be 'processing'.\n"
- + "Requested: '" + options.domains.join(', ') + "'\n"
- + "Validated: '" + validatedDomains.join(', ') + "'\n"
- + JSON.stringify(resp.body, null, 2) + "\n\n"
- + "Please open an issue at https://git.coolaj86.com/coolaj86/acme-v2.js"
- ));
- }
-
- return Promise.reject(new Error(
- "Didn't finalize order: Unhandled status '" + resp.body.status + "'."
- + " This is not one of the known statuses...\n"
- + "Requested: '" + options.domains.join(', ') + "'\n"
- + "Validated: '" + validatedDomains.join(', ') + "'\n"
- + JSON.stringify(resp.body, null, 2) + "\n\n"
- + "Please open an issue at https://git.coolaj86.com/coolaj86/acme-v2.js"
- ));
- });
- }
-
- return pollCert();
- });
-};
-// _kid
-// registerAccount
-// postChallenge
-// finalizeOrder
-// getCertificate
-ACME._getCertificate = function (me, options) {
- if (me.debug) { console.debug('[acme-v2] DEBUG get cert 1'); }
-
- // Lot's of error checking to inform the user of mistakes
- if (!(options.challengeTypes||[]).length) {
- options.challengeTypes = Object.keys(options.challenges||{});
- }
- if (!options.challengeTypes.length) {
- options.challengeTypes = [ options.challengeType ].filter(Boolean);
- }
- if (options.challengeType) {
- options.challengeTypes.sort(function (a, b) {
- if (a === options.challengeType) { return -1; }
- if (b === options.challengeType) { return 1; }
- return 0;
- });
- if (options.challengeType !== options.challengeTypes[0]) {
- return Promise.reject(new Error("options.challengeType is '" + options.challengeType + "',"
- + " which does not exist in the supplied types '" + options.challengeTypes.join(',') + "'"));
- }
- }
- // TODO check that all challengeTypes are represented in challenges
- if (!options.challengeTypes.length) {
- return Promise.reject(new Error("options.challengeTypes (string array) must be specified"
- + " (and in order of preferential priority)."));
- }
- if (!(options.domains && options.domains.length)) {
- return Promise.reject(new Error("options.domains must be a list of string domain names,"
- + " with the first being the subject of the certificate (or options.subject must specified)."));
- }
-
- // It's just fine if there's no account, we'll go get the key id we need via the existing key
- options._kid = options._kid || options.accountKid
- || (options.account && (options.account.kid
- || (options.account.key && options.account.key.kid)));
- if (!options._kid) {
- //return Promise.reject(new Error("must include KeyID"));
- // This is an idempotent request. It'll return the same account for the same public key.
- return ACME._registerAccount(me, options).then(function (account) {
- options._kid = account.key.kid;
- // start back from the top
- return ACME._getCertificate(me, options);
- });
- }
-
- // Do a little dry-run / self-test
- return ACME._testChallenges(me, options).then(function () {
- if (me.debug) { console.debug('[acme-v2] certificates.create'); }
- var body = {
- // raw wildcard syntax MUST be used here
- identifiers: options.domains.sort(function (a, b) {
- // the first in the list will be the subject of the certificate, I believe (and hope)
- if (!options.subject) { return 0; }
- if (options.subject === a) { return -1; }
- if (options.subject === b) { return 1; }
- return 0;
- }).map(function (hostname) {
- return { type: "dns", value: hostname };
- })
- //, "notBefore": "2016-01-01T00:00:00Z"
- //, "notAfter": "2016-01-08T00:00:00Z"
- };
-
- var payload = JSON.stringify(body);
- if (me.debug) { console.debug('\n[DEBUG] newOrder\n'); }
- return ACME._jwsRequest({
- options: options
- , url: me._directoryUrls.newOrder
- , protected: { kid: options._kid }
- , payload: Enc.strToBuf(payload)
- }).then(function (resp) {
- var location = resp.headers.location;
- var setAuths;
- var auths = [];
- if (me.debug) { console.debug('[ordered]', location); } // the account id url
- if (me.debug) { console.debug(resp); }
- options._authorizations = resp.body.authorizations;
- options._order = location;
- options._finalize = resp.body.finalize;
- //if (me.debug) console.debug('[DEBUG] finalize:', options._finalize); return;
-
- if (!options._authorizations) {
- return Promise.reject(new Error(
- "[acme-v2.js] authorizations were not fetched for '" + options.domains.join() + "':\n"
- + JSON.stringify(resp.body)
- ));
- }
- if (me.debug) { console.debug("[acme-v2] POST newOrder has authorizations"); }
- setAuths = options._authorizations.slice(0);
-
- function setNext() {
- var authUrl = setAuths.shift();
- if (!authUrl) { return; }
-
- return ACME._getChallenges(me, options, authUrl).then(function (results) {
- // var domain = options.domains[i]; // results.identifier.value
-
- // If it's already valid, we're golden it regardless
- if (results.challenges.some(function (ch) { return 'valid' === ch.status; })) {
- return setNext();
- }
-
- var challenge = ACME._chooseChallenge(options, results);
- if (!challenge) {
- // For example, wildcards require dns-01 and, if we don't have that, we have to bail
- return Promise.reject(new Error(
- "Server didn't offer any challenge we can handle for '" + options.domains.join() + "'."
- ));
- }
-
- return ACME._challengeToAuth(me, options, results, challenge, false).then(function (auth) {
- auths.push(auth);
- return ACME._setChallenge(me, options, auth).then(setNext);
- });
- });
- }
-
- function challengeNext() {
- var auth = auths.shift();
- if (!auth) { return; }
- return ACME._postChallenge(me, options, auth).then(challengeNext);
- }
-
- // First we set every challenge
- // Then we ask for each challenge to be checked
- // Doing otherwise would potentially cause us to poison our own DNS cache with misses
- return setNext().then(challengeNext).then(function () {
- if (me.debug) { console.debug("[getCertificate] next.then"); }
- var validatedDomains = body.identifiers.map(function (ident) {
- return ident.value;
- });
-
- return ACME._finalizeOrder(me, options, validatedDomains);
- }).then(function (order) {
- if (me.debug) { console.debug('acme-v2: order was finalized'); }
- // TODO POST-as-GET
- return me.request({ method: 'GET', url: options._certificate, json: true }).then(function (resp) {
- if (me.debug) { console.debug('acme-v2: csr submitted and cert received:'); }
- // https://github.com/certbot/certbot/issues/5721
- var certsarr = ACME.splitPemChain(ACME.formatPemChain((resp.body||'')));
- // cert, chain, fullchain, privkey, /*TODO, subject, altnames, issuedAt, expiresAt */
- var certs = {
- expires: order.expires
- , identifiers: order.identifiers
- //, authorizations: order.authorizations
- , cert: certsarr.shift()
- //, privkey: privkeyPem
- , chain: certsarr.join('\n')
- };
- if (me.debug) { console.debug(certs); }
- return certs;
- });
- });
- });
- });
-};
-ACME._generateCsrWeb64 = function (me, options, validatedDomains) {
- return ACME._importKeypair(me, options.domainKeypair).then(function (/*pair*/) {
- return me.Keypairs.generateCsr(options.domainKeypair, validatedDomains).then(function (der) {
- return Enc.bufToUrlBase64(der);
- });
- });
-};
-
-ACME.create = function create(me) {
- if (!me) { me = {}; }
- // me.debug = true;
- me.challengePrefixes = ACME.challengePrefixes;
- me.Keypairs = me.Keypairs || me.RSA || require('rsa-compat').RSA;
- me._nonces = [];
- //me.Keypairs = me.Keypairs || require('keypairs');
- //me.request = me.request || require('@root/request');
- if (!me.dig) {
- me.dig = function (query) {
- // TODO use digd.js
- return new me.request({ url: "/api/dns/" + query.name + "?type=" + query.type }).then(function (resp) {
- if (!resp.body || !Array.isArray(resp.body.answer)) {
- throw new Error("failed to get DNS response");
- }
- return {
- answer: resp.body.answer.map(function (ans) {
- return { data: ans.data, ttl: ans.ttl };
- })
- };
- });
- };
- }
-
- if ('function' !== typeof me.request) {
- me.request = ACME._defaultRequest;
- }
-
- me.init = function (opts) {
- function fin(dir) {
- me._directoryUrls = dir;
- me._tos = dir.meta.termsOfService;
- return dir;
- }
- if (opts && opts.meta && opts.termsOfService) {
- return Promise.resolve(fin(opts));
- }
- if (!me.directoryUrl) { me.directoryUrl = opts; }
- if ('string' !== typeof me.directoryUrl) {
- throw new Error("you must supply either the ACME directory url as a string or an object of the ACME urls");
- }
- return ACME._directory(me).then(function (resp) {
- return fin(resp.body);
- });
- };
- me.accounts = {
- create: function (options) {
- return ACME._registerAccount(me, options);
- }
- };
- me.certificates = {
- create: function (options) {
- return ACME._getCertificate(me, options);
- }
- };
- return me;
-};
-
-// Handle nonce, signing, and request altogether
-ACME._jwsRequest = function (me, bigopts) {
- return ACME._getNonce(me).then(function (nonce) {
- bigopts.protected.nonce = nonce;
- bigopts.protected.url = bigopts.url;
- // protected.alg: added by Keypairs.signJws
- return me.Keypairs.signJws(
- { jwk: bigopts.options.accountKeypair.privateKeyJwk
- , protected: bigopts.protected
- , payload: bigopts.payload
- }
- ).then(function (jws) {
- if (me.debug) { console.debug('[acme-v2] ' + bigopts.url + ':'); }
- if (me.debug) { console.debug(jws); }
- return ACME._request(me, { url: bigopts.url, json: jws });
- });
- });
-};
-// Handle some ACME-specific defaults
-ACME._request = function (me, opts) {
- if (!opts.headers) { opts.headers = {}; }
- if (opts.json && true !== opts.json) {
- opts.headers['Content-Type'] = 'application/jose+json';
- opts.body = JSON.stringify(opts.json);
- if (!opts.method) { opts.method = 'POST'; }
- }
- return me.request(opts).then(function (resp) {
- resp = resp.toJSON();
- if (resp.headers['replay-nonce']) {
- ACME._setNonce(me, resp.headers['replay-nonce']);
- }
- return resp;
- });
-};
-// A very generic, swappable request lib
-ACME._defaultRequest = function (opts) {
- // Note: normally we'd have to supply a User-Agent string, but not here in a browser
- if (!opts.headers) { opts.headers = {}; }
- if (opts.json) {
- opts.headers.Accept = 'application/json';
- if (true !== opts.json) { opts.body = JSON.stringify(opts.json); }
- }
- if (!opts.method) {
- opts.method = 'GET';
- if (opts.body) { opts.method = 'POST'; }
- }
- opts.cors = true;
- return window.fetch(opts.url, opts).then(function (resp) {
- var headers = {};
- var result = { statusCode: resp.status, headers: headers, toJSON: function () { return this; } };
- Array.from(resp.headers.entries()).forEach(function (h) { headers[h[0]] = h[1]; });
- if (!headers['content-type']) {
- return result;
- }
- if (/json/.test(headers['content-type'])) {
- return resp.json().then(function (json) {
- result.body = json;
- return result;
- });
- }
- return resp.text().then(function (txt) {
- result.body = txt;
- return result;
- });
- });
-};
-
-ACME._importKeypair = function (me, kp) {
- var jwk = kp.privateKeyJwk;
- var p;
- if (jwk) {
- // nix the browser jwk extras
- jwk.key_ops = undefined;
- jwk.ext = undefined;
- p = Promise.resolve({ private: jwk, public: me.Keypairs.neuter({ jwk: jwk }) });
- } else {
- p = me.Keypairs.import({ pem: kp.privateKeyPem });
- }
- return p.then(function (pair) {
- kp.privateKeyJwk = pair.private;
- kp.publicKeyJwk = pair.public;
- if (pair.public.kid) {
- pair = JSON.parse(JSON.stringify(pair));
- delete pair.public.kid;
- delete pair.private.kid;
- }
- return pair;
- });
-};
-
-/*
-TODO
-Per-Order State Params
- _kty
- _alg
- _finalize
- _expires
- _certificate
- _order
- _authorizations
-*/
-
-ACME._toWebsafeBase64 = function (b64) {
- return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g,"");
-};
-
-// In v8 this is crypto random, but we're just using it for pseudorandom
-ACME._prnd = function (n) {
- var rnd = '';
- while (rnd.length / 2 < n) {
- var num = Math.random().toString().substr(2);
- if (num.length % 2) {
- num = '0' + num;
- }
- var pairs = num.match(/(..?)/g);
- rnd += pairs.map(ACME._toHex).join('');
- }
- return rnd.substr(0, n*2);
-};
-ACME._toHex = function (pair) {
- return parseInt(pair, 10).toString(16);
-};
-
-Enc.bufToUrlBase64 = function (u8) {
- return Enc.bufToBase64(u8)
- .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
-};
-Enc.bufToBase64 = function (u8) {
- var bin = '';
- u8.forEach(function (i) {
- bin += String.fromCharCode(i);
- });
- return btoa(bin);
-};
-
-Crypto._sha = function (sha, str) {
- var encoder = new TextEncoder();
- var data = encoder.encode(str);
- sha = 'SHA-' + sha.replace(/^sha-?/i, '');
- return window.crypto.subtle.digest(sha, data).then(function (hash) {
- return Enc.bufToUrlBase64(new Uint8Array(hash));
- });
-};
-
-}('undefined' === typeof window ? module.exports : window));
diff --git a/package-lock.json b/package-lock.json
deleted file mode 100644
index 2060678..0000000
--- a/package-lock.json
+++ /dev/null
@@ -1,547 +0,0 @@
-{
- "name": "bluecrypt-keypairs",
- "version": "0.1.0",
- "lockfileVersion": 1,
- "requires": true,
- "dependencies": {
- "accepts": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.6.tgz",
- "integrity": "sha512-QsaoUD2dpVpjENy8JFpQnXP9vyzoZPmAoKrE3S6HtSB7qzSebkJNnmdY4p004FQUSSiHXPueENpoeuUW/7a8Ig==",
- "dev": true,
- "requires": {
- "mime-types": "~2.1.24",
- "negotiator": "0.6.1"
- }
- },
- "array-flatten": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
- "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=",
- "dev": true
- },
- "balanced-match": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
- "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
- "dev": true
- },
- "bluebird": {
- "version": "3.5.4",
- "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz",
- "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==",
- "dev": true
- },
- "body-parser": {
- "version": "1.18.3",
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.18.3.tgz",
- "integrity": "sha1-WykhmP/dVTs6DyDe0FkrlWlVyLQ=",
- "dev": true,
- "requires": {
- "bytes": "3.0.0",
- "content-type": "~1.0.4",
- "debug": "2.6.9",
- "depd": "~1.1.2",
- "http-errors": "~1.6.3",
- "iconv-lite": "0.4.23",
- "on-finished": "~2.3.0",
- "qs": "6.5.2",
- "raw-body": "2.3.3",
- "type-is": "~1.6.16"
- }
- },
- "brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
- "dev": true,
- "requires": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "bytes": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.0.0.tgz",
- "integrity": "sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=",
- "dev": true
- },
- "cli": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
- "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
- "dev": true,
- "requires": {
- "exit": "0.1.2",
- "glob": "^7.1.1"
- }
- },
- "concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
- "dev": true
- },
- "content-disposition": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.2.tgz",
- "integrity": "sha1-DPaLud318r55YcOoUXjLhdunjLQ=",
- "dev": true
- },
- "content-type": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz",
- "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==",
- "dev": true
- },
- "cookie": {
- "version": "0.3.1",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.3.1.tgz",
- "integrity": "sha1-5+Ch+e9DtMi6klxcWpboBtFoc7s=",
- "dev": true
- },
- "cookie-signature": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
- "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=",
- "dev": true
- },
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- },
- "depd": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
- "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=",
- "dev": true
- },
- "destroy": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
- "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
- "dev": true
- },
- "dig.js": {
- "version": "1.3.9",
- "resolved": "https://registry.npmjs.org/dig.js/-/dig.js-1.3.9.tgz",
- "integrity": "sha512-O/tSWZuW7AwpjsgePPmTanwvSDL9xF+FzLTJD9byN3C6lk79iMejC/Ahz9CERAXTW4e2TXL1vtqh3T0Ug79ocA==",
- "dev": true,
- "requires": {
- "cli": "^1.0.1",
- "dns-suite": "git+https://git.coolaj86.com/coolaj86/dns-suite.js#v1.2",
- "hexdump.js": "git+https://git.coolaj86.com/coolaj86/hexdump.js#v1.0.4"
- },
- "dependencies": {
- "dns-suite": {
- "version": "git+https://git.coolaj86.com/coolaj86/dns-suite.js#092008f766540909d27c934211495c9e03705bf3",
- "from": "git+https://git.coolaj86.com/coolaj86/dns-suite.js#v1.2",
- "dev": true,
- "requires": {
- "bluebird": "^3.5.0",
- "hexdump.js": "git+https://git.coolaj86.com/coolaj86/hexdump.js#v1.0.4"
- }
- }
- }
- },
- "dns-suite": {
- "version": "1.2.12",
- "resolved": "https://registry.npmjs.org/dns-suite/-/dns-suite-1.2.12.tgz",
- "integrity": "sha512-K4LWqmJT/T2QLaGCJ+qRvrT9DicKs5XxXYXw6uIZ1apdwyfToQk7K9AZbpFd0FLRdZG809v2vAcsquPbQh+Ipg==",
- "dev": true,
- "requires": {
- "bluebird": "^3.5.0",
- "hexdump.js": "git+https://git.coolaj86.com/coolaj86/hexdump.js#v1.0.4"
- }
- },
- "ee-first": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
- "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=",
- "dev": true
- },
- "encodeurl": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
- "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=",
- "dev": true
- },
- "escape-html": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
- "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=",
- "dev": true
- },
- "etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
- "dev": true
- },
- "exit": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
- "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
- "dev": true
- },
- "express": {
- "version": "4.16.4",
- "resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
- "integrity": "sha512-j12Uuyb4FMrd/qQAm6uCHAkPtO8FDTRJZBDd5D2KOL2eLaz1yUNdUB/NOIyq0iU4q4cFarsUCrnFDPBcnksuOg==",
- "dev": true,
- "requires": {
- "accepts": "~1.3.5",
- "array-flatten": "1.1.1",
- "body-parser": "1.18.3",
- "content-disposition": "0.5.2",
- "content-type": "~1.0.4",
- "cookie": "0.3.1",
- "cookie-signature": "1.0.6",
- "debug": "2.6.9",
- "depd": "~1.1.2",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "finalhandler": "1.1.1",
- "fresh": "0.5.2",
- "merge-descriptors": "1.0.1",
- "methods": "~1.1.2",
- "on-finished": "~2.3.0",
- "parseurl": "~1.3.2",
- "path-to-regexp": "0.1.7",
- "proxy-addr": "~2.0.4",
- "qs": "6.5.2",
- "range-parser": "~1.2.0",
- "safe-buffer": "5.1.2",
- "send": "0.16.2",
- "serve-static": "1.13.2",
- "setprototypeof": "1.1.0",
- "statuses": "~1.4.0",
- "type-is": "~1.6.16",
- "utils-merge": "1.0.1",
- "vary": "~1.1.2"
- }
- },
- "finalhandler": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.1.tgz",
- "integrity": "sha512-Y1GUDo39ez4aHAw7MysnUD5JzYX+WaIj8I57kO3aEPT1fFRL4sr7mjei97FgnwhAyyzRYmQZaTHb2+9uZ1dPtg==",
- "dev": true,
- "requires": {
- "debug": "2.6.9",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "on-finished": "~2.3.0",
- "parseurl": "~1.3.2",
- "statuses": "~1.4.0",
- "unpipe": "~1.0.0"
- }
- },
- "forwarded": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
- "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=",
- "dev": true
- },
- "fresh": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
- "dev": true
- },
- "fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
- "dev": true
- },
- "glob": {
- "version": "7.1.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
- "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
- "dev": true,
- "requires": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.0.4",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- }
- },
- "hexdump.js": {
- "version": "git+https://git.coolaj86.com/coolaj86/hexdump.js#222fa7de5036a16397de2fe703c35ac54a3d8d0c",
- "from": "git+https://git.coolaj86.com/coolaj86/hexdump.js#v1.0.4",
- "dev": true
- },
- "http-errors": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.6.3.tgz",
- "integrity": "sha1-i1VoC7S+KDoLW/TqLjhYC+HZMg0=",
- "dev": true,
- "requires": {
- "depd": "~1.1.2",
- "inherits": "2.0.3",
- "setprototypeof": "1.1.0",
- "statuses": ">= 1.4.0 < 2"
- }
- },
- "iconv-lite": {
- "version": "0.4.23",
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
- "integrity": "sha512-neyTUVFtahjf0mB3dZT77u+8O0QB89jFdnBkd5P1JgYPbPaia3gXXOVL2fq8VyU2gMMD7SaN7QukTB/pmXYvDA==",
- "dev": true,
- "requires": {
- "safer-buffer": ">= 2.1.2 < 3"
- }
- },
- "inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
- "dev": true,
- "requires": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "inherits": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
- "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
- "dev": true
- },
- "ipaddr.js": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz",
- "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==",
- "dev": true
- },
- "media-typer": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
- "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=",
- "dev": true
- },
- "merge-descriptors": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
- "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=",
- "dev": true
- },
- "methods": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
- "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=",
- "dev": true
- },
- "mime": {
- "version": "1.4.1",
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.4.1.tgz",
- "integrity": "sha512-KI1+qOZu5DcW6wayYHSzR/tXKCDC5Om4s1z2QJjDULzLcmf3DvzS7oluY4HCTrc+9FiKmWUgeNLg7W3uIQvxtQ==",
- "dev": true
- },
- "mime-db": {
- "version": "1.40.0",
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.40.0.tgz",
- "integrity": "sha512-jYdeOMPy9vnxEqFRRo6ZvTZ8d9oPb+k18PKoYNYUe2stVEBPPwsln/qWzdbmaIvnhZ9v2P+CuecK+fpUfsV2mA==",
- "dev": true
- },
- "mime-types": {
- "version": "2.1.24",
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.24.tgz",
- "integrity": "sha512-WaFHS3MCl5fapm3oLxU4eYDw77IQM2ACcxQ9RIxfaC3ooc6PFuBMGZZsYpvoXS5D5QTWPieo1jjLdAm3TBP3cQ==",
- "dev": true,
- "requires": {
- "mime-db": "1.40.0"
- }
- },
- "minimatch": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
- "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
- "dev": true,
- "requires": {
- "brace-expansion": "^1.1.7"
- }
- },
- "ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
- "dev": true
- },
- "negotiator": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.1.tgz",
- "integrity": "sha1-KzJxhOiZIQEXeyhWP7XnECrNDKk=",
- "dev": true
- },
- "on-finished": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
- "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=",
- "dev": true,
- "requires": {
- "ee-first": "1.1.1"
- }
- },
- "once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
- "dev": true,
- "requires": {
- "wrappy": "1"
- }
- },
- "parseurl": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
- "dev": true
- },
- "path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
- "dev": true
- },
- "path-to-regexp": {
- "version": "0.1.7",
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
- "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=",
- "dev": true
- },
- "proxy-addr": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.5.tgz",
- "integrity": "sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==",
- "dev": true,
- "requires": {
- "forwarded": "~0.1.2",
- "ipaddr.js": "1.9.0"
- }
- },
- "qs": {
- "version": "6.5.2",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
- "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
- "dev": true
- },
- "range-parser": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.0.tgz",
- "integrity": "sha1-9JvmtIeJTdxA3MlKMi9hEJLgDV4=",
- "dev": true
- },
- "raw-body": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.3.3.tgz",
- "integrity": "sha512-9esiElv1BrZoI3rCDuOuKCBRbuApGGaDPQfjSflGxdy4oyzqghxu6klEkkVIvBje+FF0BX9coEv8KqW6X/7njw==",
- "dev": true,
- "requires": {
- "bytes": "3.0.0",
- "http-errors": "1.6.3",
- "iconv-lite": "0.4.23",
- "unpipe": "1.0.0"
- }
- },
- "safe-buffer": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
- "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
- "dev": true
- },
- "safer-buffer": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
- "dev": true
- },
- "send": {
- "version": "0.16.2",
- "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
- "integrity": "sha512-E64YFPUssFHEFBvpbbjr44NCLtI1AohxQ8ZSiJjQLskAdKuriYEP6VyGEsRDH8ScozGpkaX1BGvhanqCwkcEZw==",
- "dev": true,
- "requires": {
- "debug": "2.6.9",
- "depd": "~1.1.2",
- "destroy": "~1.0.4",
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "etag": "~1.8.1",
- "fresh": "0.5.2",
- "http-errors": "~1.6.2",
- "mime": "1.4.1",
- "ms": "2.0.0",
- "on-finished": "~2.3.0",
- "range-parser": "~1.2.0",
- "statuses": "~1.4.0"
- }
- },
- "serve-static": {
- "version": "1.13.2",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.2.tgz",
- "integrity": "sha512-p/tdJrO4U387R9oMjb1oj7qSMaMfmOyd4j9hOFoxZe2baQszgHcSWjuya/CiT5kgZZKRudHNOA0pYXOl8rQ5nw==",
- "dev": true,
- "requires": {
- "encodeurl": "~1.0.2",
- "escape-html": "~1.0.3",
- "parseurl": "~1.3.2",
- "send": "0.16.2"
- }
- },
- "setprototypeof": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz",
- "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
- "dev": true
- },
- "statuses": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
- "integrity": "sha512-zhSCtt8v2NDrRlPQpCNtw/heZLtfUDqxBM1udqikb/Hbk52LK4nQSwr10u77iopCW5LsyHpuXS0GnEc48mLeew==",
- "dev": true
- },
- "type-is": {
- "version": "1.6.18",
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
- "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
- "dev": true,
- "requires": {
- "media-typer": "0.3.0",
- "mime-types": "~2.1.24"
- }
- },
- "unpipe": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
- "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=",
- "dev": true
- },
- "utils-merge": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
- "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=",
- "dev": true
- },
- "vary": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
- "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=",
- "dev": true
- },
- "wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
- "dev": true
- }
- }
-}
diff --git a/package.json b/package.json
index a29af32..3dd19e2 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,7 @@
{
"name": "bluecrypt-keypairs",
- "version": "0.1.0",
+ "version": "0.1.1",
"description": "Zero-Dependency Native Browser support for ECDSA P-256 and P-384, and RSA 2048/3072/4096 written in VanillaJS",
- "main": "server.js",
"directories": {
"lib": "lib"
},
@@ -29,8 +28,5 @@
"author": "AJ ONeal (https://coolaj86.com/)",
"license": "MPL-2.0",
"devDependencies": {
- "dig.js": "^1.3.9",
- "dns-suite": "^1.2.12",
- "express": "^4.16.4"
}
}
diff --git a/server.js b/server.js
deleted file mode 100644
index 34938da..0000000
--- a/server.js
+++ /dev/null
@@ -1,125 +0,0 @@
-'use strict';
-
-var crypto = require('crypto');
-//var dnsjs = require('dns-suite');
-var dig = require('dig.js/dns-request');
-var express = require('express');
-var app = express();
-
-var nameservers = require('dns').getServers();
-var index = crypto.randomBytes(2).readUInt16BE(0) % nameservers.length;
-var nameserver = nameservers[index];
-
-app.use('/', express.static('./'));
-app.use('/api', express.json());
-app.get('/api/dns/:domain', function (req, res, next) {
- console.log(req.params);
- var domain = req.params.domain;
- var casedDomain = domain.toLowerCase().split('').map(function (ch) {
- // dns0x20 takes advantage of the fact that the binary operation for toUpperCase is
- // ch = ch | 0x20;
- return Math.round(Math.random()) % 2 ? ch : ch.toUpperCase();
- }).join('');
- var typ = req.query.type;
- var query = {
- header: {
- id: crypto.randomBytes(2).readUInt16BE(0)
- , qr: 0
- , opcode: 0
- , aa: 0 // Authoritative-Only
- , tc: 0 // NA
- , rd: 1 // Recurse
- , ra: 0 // NA
- , rcode: 0 // NA
- }
- , question: [
- { name: casedDomain
- //, type: typ || 'A'
- , typeName: typ || 'A'
- , className: 'IN'
- }
- ]
- };
- var opts = {
- onError: function (err) {
- next(err);
- }
- , onMessage: function (packet) {
- var fail0x20;
-
- if (packet.id !== query.id) {
- console.error('[SECURITY] ignoring packet for \'' + packet.question[0].name + '\' due to mismatched id');
- console.error(packet);
- return;
- }
-
- packet.question.forEach(function (q) {
- // if (-1 === q.name.lastIndexOf(cli.casedQuery))
- if (q.name !== casedDomain) {
- fail0x20 = q.name;
- }
- });
-
- [ 'question', 'answer', 'authority', 'additional' ].forEach(function (group) {
- (packet[group]||[]).forEach(function (a) {
- var an = a.name;
- var i = domain.toLowerCase().lastIndexOf(a.name.toLowerCase()); // answer is something like ExAMPle.cOM and query was wWw.ExAMPle.cOM
- var j = a.name.toLowerCase().lastIndexOf(domain.toLowerCase()); // answer is something like www.ExAMPle.cOM and query was ExAMPle.cOM
-
- // it's important to note that these should only relpace changes in casing that we expected
- // any abnormalities should be left intact to go "huh?" about
- // TODO detect abnormalities?
- if (-1 !== i) {
- // "EXamPLE.cOm".replace("wWw.EXamPLE.cOm".substr(4), "www.example.com".substr(4))
- a.name = a.name.replace(casedDomain.substr(i), domain.substr(i));
- } else if (-1 !== j) {
- // "www.example.com".replace("EXamPLE.cOm", "example.com")
- a.name = a.name.substr(0, j) + a.name.substr(j).replace(casedDomain, domain);
- }
-
- // NOTE: right now this assumes that anything matching the query matches all the way to the end
- // it does not handle the case of a record for example.com.uk being returned in response to a query for www.example.com correctly
- // (but I don't think it should need to)
- if (a.name.length !== an.length) {
- console.error("[ERROR] question / answer mismatch: '" + an + "' != '" + a.length + "'");
- console.error(a);
- }
- });
- });
-
- if (fail0x20) {
- console.warn(";; Warning: DNS 0x20 security not implemented (or packet spoofed). Queried '"
- + casedDomain + "' but got response for '" + fail0x20 + "'.");
- return;
- }
-
- res.send({
- header: packet.header
- , question: packet.question
- , answer: packet.answer
- , authority: packet.authority
- , additional: packet.additional
- , edns_options: packet.edns_options
- });
- }
- , onListening: function () {}
- , onSent: function (/*res*/) { }
- , onTimeout: function (res) {
- console.error('dns timeout:', res);
- next(new Error("DNS timeout - no response"));
- }
- , onClose: function () { }
- //, mdns: cli.mdns
- , nameserver: nameserver
- , port: 53
- , timeout: 2000
- };
-
- dig.resolveJson(query, opts);
-});
-
-// curl -L http://localhost:3000/api/dns/example.com?type=A
-console.log("Listening on localhost:3000");
-app.listen(3000);
-console.log("Try this:");
-console.log("\tcurl -L 'http://localhost:3000/api/dns/example.com?type=A'");