native.js 2.05 KB
'use strict';

var native = module.exports;
var promisify = require('util').promisify;
var resolveTxt = promisify(require('dns').resolveTxt);
var crypto = require('crypto');

native._canCheck = function(me) {
	me._canCheck = {};
	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;
	});
};

// the hashcash here is for browser parity only
// basically we ask the client to find a needle in a haystack
// (very similar to CloudFlare's api protection)
native._hashcash = function(ch) {
	if (!ch || !ch.nonce) {
		ch = { nonce: 'xxx' };
	}
	return Promise.resolve()
		.then(function() {
			// only get easy answers
			var len = ch.needle.length;
			var start = ch.start || 0;
			var end = ch.end || Math.ceil(len / 2);
			var window = parseInt(end - start, 10) || 0;

			var maxLen = 6;
			var maxTries = Math.pow(2, maxLen * 8);
			if (
				len > maxLen ||
				window < Math.ceil(len / 2) ||
				ch.needle.toLowerCase() !== ch.needle ||
				ch.alg !== 'SHA-256'
			) {
				// bail unless the server is issuing very easy challenges
				throw new Error('possible and easy answers only, please');
			}

			var haystack;
			var i;
			var answer;
			var needle = Buffer.from(ch.needle, 'hex');
			for (i = 0; i < maxTries; i += 1) {
				answer = i.toString(16);
				if (answer.length % 2) {
					answer = '0' + answer;
				}
				haystack = crypto
					.createHash('sha256')
					.update(Buffer.from(ch.nonce + answer, 'hex'))
					.digest()
					.slice(ch.start, ch.end);
				if (-1 !== haystack.indexOf(needle)) {
					return ch.nonce + ':' + answer;
				}
			}
			return ch.nonce + ':xxx';
		})
		.catch(function() {
			//console.log('[debug]', err);
			// ignore any error
			return ch.nonce + ':xxx';
		});
};