diff --git a/acme.js b/acme.js index fac81ec..c762bb6 100644 --- a/acme.js +++ b/acme.js @@ -8,11 +8,13 @@ require('@root/encoding/bytes'); var Enc = require('@root/encoding/base64'); var ACME = module.exports; -//var Keypairs = exports.Keypairs || {}; -//var CSR = exports.CSR; +var Keypairs = require('@root/keypairs'); +var CSR = require('@root/csr'); var sha2 = require('@root/keypairs/lib/node/sha2.js'); var http = require('./lib/node/http.js'); +var native = require('./native.js'); + ACME.formatPemChain = function formatPemChain(str) { return ( str @@ -95,6 +97,7 @@ ACME.challengeTests = { ACME._directory = function(me) { // GET-as-GET ok + // TODO cache the directory URL return me.request({ method: 'GET', url: me.directoryUrl, json: true }); }; ACME._getNonce = function(me) { @@ -345,12 +348,6 @@ ACME._testChallengeOptions = function() { 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', @@ -1317,8 +1314,8 @@ ACME.create = function create(me) { } // me.debug = true; me.challengePrefixes = ACME.challengePrefixes; - me.Keypairs = me.Keypairs || require('@root/keypairs'); - me.CSR = me.CSR || require('@root/csr'); + me.Keypairs = me.Keypairs || Keypairs; + me.CSR = me.CSR || CSR; me._nonces = []; me._canUse = {}; if (!me._baseUrl) { @@ -1328,7 +1325,7 @@ ACME.create = function create(me) { //me.request = me.request || require('@root/request'); if (!me.dns01) { me.dns01 = function(ch) { - return ACME._dns01(me, ch); + return native._dns01(me, ch); }; } // backwards compat @@ -1337,7 +1334,7 @@ ACME.create = function create(me) { } if (!me.http01) { me.http01 = function(ch) { - return ACME._http01(me, ch); + return native._http01(me, ch); }; } @@ -1362,19 +1359,10 @@ ACME.create = function create(me) { 'you must supply either the ACME directory url as a string or an object of the ACME urls' ); } + var p = Promise.resolve(); if (!me.skipChallengeTest) { - p = me - .request({ url: me._baseUrl + '/api/_acme_api_/' }) - .then(function(resp) { - if (resp.body.success) { - me._canCheck['http-01'] = true; - me._canCheck['dns-01'] = true; - } - }) - .catch(function() { - // ignore - }); + p = native._canCheck(me); } return p.then(function() { return ACME._directory(me).then(function(resp) { @@ -1530,36 +1518,6 @@ ACME._prnd = function(n) { ACME._toHex = function(pair) { return parseInt(pair, 10).toString(16); }; -ACME._dns01 = function(me, ch) { - return new me.request({ - url: me._baseUrl + '/api/dns/' + ch.dnsHost + '?type=TXT' - }).then(function(resp) { - var err; - if (!resp.body || !Array.isArray(resp.body.answer)) { - err = new Error('failed to get DNS response'); - console.error(err); - throw err; - } - if (!resp.body.answer.length) { - err = new Error('failed to get DNS answer record in response'); - console.error(err); - throw err; - } - return { - answer: resp.body.answer.map(function(ans) { - return { data: ans.data, ttl: ans.ttl }; - }) - }; - }); -}; -ACME._http01 = function(me, ch) { - var url = encodeURIComponent(ch.challengeUrl); - return new me.request({ - url: me._baseUrl + '/api/http?url=' + url - }).then(function(resp) { - return resp.body; - }); -}; ACME._removeChallenge = function(me, options, auth) { var challengers = options.challenges || {}; var ch = auth.challenge; diff --git a/browser.js b/browser.js new file mode 100644 index 0000000..d489415 --- /dev/null +++ b/browser.js @@ -0,0 +1,49 @@ +'use strict'; + +var native = module.exports; + +native._canCheck = function(me) { + return me + .request({ url: me._baseUrl + '/api/_acme_api_/' }) + .then(function(resp) { + if (resp.body.success) { + me._canCheck['http-01'] = true; + me._canCheck['dns-01'] = true; + } + }) + .catch(function() { + // ignore + }); +}; + +native._dns01 = function(me, ch) { + return new me.request({ + url: me._baseUrl + '/api/dns/' + ch.dnsHost + '?type=TXT' + }).then(function(resp) { + var err; + if (!resp.body || !Array.isArray(resp.body.answer)) { + err = new Error('failed to get DNS response'); + console.error(err); + throw err; + } + if (!resp.body.answer.length) { + err = new Error('failed to get DNS answer record in response'); + console.error(err); + throw err; + } + return { + answer: resp.body.answer.map(function(ans) { + return { data: ans.data, ttl: ans.ttl }; + }) + }; + }); +}; + +native._http01 = function(me, ch) { + var url = encodeURIComponent(ch.challengeUrl); + return new me.request({ + url: me._baseUrl + '/api/http?url=' + url + }).then(function(resp) { + return resp.body; + }); +}; diff --git a/native.js b/native.js new file mode 100644 index 0000000..ead4fe4 --- /dev/null +++ b/native.js @@ -0,0 +1,32 @@ +'use strict'; + +var native = module.exports; +var promisify = require('util').promisify; +var resolveTxt = promisify(require('dns').resolveTxt); + +native._canCheck = function(me) { + me._canCheck['http-01'] = true; + me._canCheck['dns-01'] = true; + return Promise.resolve(); +}; + +native._dns01 = function(me, ch) { + // TODO use digd.js + return resolveTxt(ch.dnsHost).then(function(records) { + return { + answer: records.map(function(rr) { + return { + data: rr + }; + }) + }; + }); +}; + +native._http01 = function(me, ch) { + return new me.request({ + url: ch.challengeUrl + }).then(function(resp) { + return resp.body; + }); +}; diff --git a/package-lock.json b/package-lock.json index d48b8c8..201b099 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@root/acme", - "version": "3.0.0-wip.3", + "version": "3.0.0-wip.4", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -16,6 +16,7 @@ "version": "0.8.1", "resolved": "https://registry.npmjs.org/@root/csr/-/csr-0.8.1.tgz", "integrity": "sha512-hKl0VuE549TK6SnS2Yn9nRvKbFZXn/oAg+dZJU/tlKl/f/0yRXeuUzf8akg3JjtJq+9E592zDqeXZ7yyrg8fSQ==", + "dev": true, "requires": { "@root/asn1": "^1.0.0", "@root/pem": "^1.0.4", @@ -117,13 +118,20 @@ } }, "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==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/dns-suite/-/dns-suite-1.2.13.tgz", + "integrity": "sha512-veYKPHUc2RfRCe7c4G/iKxhRv0S4InJ3JsW8tEhW6Yb7dn3ac34iozC6cNX0uzHYZUw0BG5V9Fu65L1bx1GeBg==", "dev": true, "requires": { - "bluebird": "^3.5.0", - "hexdump.js": "git+https://git.coolaj86.com/coolaj86/hexdump.js#v1.0.4" + "@root/hexdump": "^1.1.1" + }, + "dependencies": { + "@root/hexdump": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@root/hexdump/-/hexdump-1.1.1.tgz", + "integrity": "sha512-AmrmLOutlzctR599ittO06lINOco1TIqb0c1wu83fP2Eoi5iSvx7kVWC4mDufze8rxPewC+aQOx4e6Pw7izV4A==", + "dev": true + } } }, "dotenv": { diff --git a/package.json b/package.json index 81e34eb..42354e3 100644 --- a/package.json +++ b/package.json @@ -1,10 +1,11 @@ { "name": "@root/acme", - "version": "3.0.0-wip.3", - "description": "Free SSL certificates through Let's Encrypt, right in your browser", + "version": "3.0.0-wip.4", + "description": "Free SSL certificates for Node.js and Browsers. Issued via Let's Encrypt", "homepage": "https://rootprojects.org/acme/", "main": "acme.js", "browser": { + "./native.js": "./browser.js", "./lib/node/sha2.js": "./lib/browser/sha2.js", "./lib/node/http.js": "./lib/browser/http.js" }, @@ -14,7 +15,7 @@ "dist" ], "scripts": { - "build": "nodex bin/bundle.js", + "build": "node_xxx bin/bundle.js", "lint": "jshint lib bin", "test": "node server.js", "start": "node server.js" @@ -26,10 +27,10 @@ "keywords": [ "ACME", "Let's Encrypt", - "browser", "EC", "RSA", "CSR", + "browser", "greenlock", "VanillaJS", "ZeroSSL" @@ -37,7 +38,6 @@ "author": "AJ ONeal (https://coolaj86.com/)", "license": "MPL-2.0", "dependencies": { - "@root/csr": "^0.8.1", "@root/encoding": "^1.0.1", "@root/keypairs": "^0.9.0", "@root/pem": "^1.0.4", @@ -45,8 +45,9 @@ "@root/x509": "^0.7.2" }, "devDependencies": { + "@root/csr": "^0.8.1", "dig.js": "^1.3.9", - "dns-suite": "^1.2.12", + "dns-suite": "^1.2.13", "dotenv": "^8.1.0", "punycode": "^1.4.1" }, diff --git a/tests/index.js b/tests/index.js index 49b47b6..9df6e21 100644 --- a/tests/index.js +++ b/tests/index.js @@ -95,6 +95,7 @@ async function happyPath(accKty, srvKty, rnd) { accountKeypair: { privateKeyJwk: accountKeypair.private }, subscriberEmail: config.email }); + // TODO top-level agree function agree(tos) { if (config.debug) {