forked from root/acme.js
		
	moved common code to own modules
This commit is contained in:
		
							parent
							
								
									5623ed1914
								
							
						
					
					
						commit
						21f7e87606
					
				
							
								
								
									
										4
									
								
								acme.js
									
									
									
									
									
								
							
							
						
						
									
										4
									
								
								acme.js
									
									
									
									
									
								
							@ -10,7 +10,7 @@ var Enc = require('@root/encoding/base64');
 | 
			
		||||
var ACME = module.exports;
 | 
			
		||||
//var Keypairs = exports.Keypairs || {};
 | 
			
		||||
//var CSR = exports.CSR;
 | 
			
		||||
var sha2 = require('./lib/node/sha2.js');
 | 
			
		||||
var sha2 = require('@root/keypairs/lib/node/sha2.js');
 | 
			
		||||
var http = require('./lib/node/http.js');
 | 
			
		||||
 | 
			
		||||
ACME.formatPemChain = function formatPemChain(str) {
 | 
			
		||||
@ -1318,7 +1318,7 @@ ACME.create = function create(me) {
 | 
			
		||||
	// me.debug = true;
 | 
			
		||||
	me.challengePrefixes = ACME.challengePrefixes;
 | 
			
		||||
	me.Keypairs = me.Keypairs || require('@root/keypairs');
 | 
			
		||||
	me.CSR = me.CSR || require('./csr.js');
 | 
			
		||||
	me.CSR = me.CSR || require('@root/csr');
 | 
			
		||||
	me._nonces = [];
 | 
			
		||||
	me._canUse = {};
 | 
			
		||||
	if (!me._baseUrl) {
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										322
									
								
								csr.js
									
									
									
									
									
								
							
							
						
						
									
										322
									
								
								csr.js
									
									
									
									
									
								
							@ -1,322 +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/. */
 | 
			
		||||
'use strict';
 | 
			
		||||
/*global Promise*/
 | 
			
		||||
 | 
			
		||||
var Enc = require('@root/encoding');
 | 
			
		||||
 | 
			
		||||
var ASN1 = require('./asn1/packer.js'); // DER, actually
 | 
			
		||||
var Asn1 = ASN1.Any;
 | 
			
		||||
var BitStr = ASN1.BitStr;
 | 
			
		||||
var UInt = ASN1.UInt;
 | 
			
		||||
var Asn1Parser = require('./asn1/parser.js');
 | 
			
		||||
var PEM = require('./pem.js');
 | 
			
		||||
var X509 = require('./x509.js');
 | 
			
		||||
var Keypairs = require('@root/keypairs');
 | 
			
		||||
 | 
			
		||||
// TODO find a way that the prior node-ish way of `module.exports = function () {}` isn't broken
 | 
			
		||||
var CSR = module.exports;
 | 
			
		||||
CSR.csr = function(opts) {
 | 
			
		||||
	// We're using a Promise here to be compatible with the browser version
 | 
			
		||||
	// which will probably use the webcrypto API for some of the conversions
 | 
			
		||||
	return CSR._prepare(opts).then(function(opts) {
 | 
			
		||||
		return CSR.create(opts).then(function(bytes) {
 | 
			
		||||
			return CSR._encode(opts, bytes);
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CSR._prepare = function(opts) {
 | 
			
		||||
	return Promise.resolve().then(function() {
 | 
			
		||||
		opts = JSON.parse(JSON.stringify(opts));
 | 
			
		||||
 | 
			
		||||
		// We do a bit of extra error checking for user convenience
 | 
			
		||||
		if (!opts) {
 | 
			
		||||
			throw new Error(
 | 
			
		||||
				'You must pass options with key and domains to rsacsr'
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
		if (!Array.isArray(opts.domains) || 0 === opts.domains.length) {
 | 
			
		||||
			new Error('You must pass options.domains as a non-empty array');
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// I need to check that 例.中国 is a valid domain name
 | 
			
		||||
		if (
 | 
			
		||||
			!opts.domains.every(function(d) {
 | 
			
		||||
				// allow punycode? xn--
 | 
			
		||||
				if (
 | 
			
		||||
					'string' === typeof d /*&& /\./.test(d) && !/--/.test(d)*/
 | 
			
		||||
				) {
 | 
			
		||||
					return true;
 | 
			
		||||
				}
 | 
			
		||||
			})
 | 
			
		||||
		) {
 | 
			
		||||
			throw new Error('You must pass options.domains as strings');
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (opts.jwk) {
 | 
			
		||||
			return opts;
 | 
			
		||||
		}
 | 
			
		||||
		if (opts.key && opts.key.kty) {
 | 
			
		||||
			opts.jwk = opts.key;
 | 
			
		||||
			return opts;
 | 
			
		||||
		}
 | 
			
		||||
		if (!opts.pem && !opts.key) {
 | 
			
		||||
			throw new Error('You must pass options.key as a JSON web key');
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return Keypairs.import({ pem: opts.pem || opts.key }).then(function(
 | 
			
		||||
			pair
 | 
			
		||||
		) {
 | 
			
		||||
			opts.jwk = pair.private;
 | 
			
		||||
			return opts;
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CSR._encode = function(opts, bytes) {
 | 
			
		||||
	if ('der' === (opts.encoding || '').toLowerCase()) {
 | 
			
		||||
		return bytes;
 | 
			
		||||
	}
 | 
			
		||||
	return PEM.packBlock({
 | 
			
		||||
		type: 'CERTIFICATE REQUEST',
 | 
			
		||||
		bytes: bytes /* { jwk: jwk, domains: opts.domains } */
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CSR.create = function createCsr(opts) {
 | 
			
		||||
	var hex = CSR.request(opts.jwk, opts.domains);
 | 
			
		||||
	return CSR._sign(opts.jwk, hex).then(function(csr) {
 | 
			
		||||
		return Enc.hexToBuf(csr);
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// EC / RSA
 | 
			
		||||
//
 | 
			
		||||
CSR.request = function createCsrBodyEc(jwk, domains) {
 | 
			
		||||
	var asn1pub;
 | 
			
		||||
	if (/^EC/i.test(jwk.kty)) {
 | 
			
		||||
		asn1pub = X509.packCsrEcPublicKey(jwk);
 | 
			
		||||
	} else {
 | 
			
		||||
		asn1pub = X509.packCsrRsaPublicKey(jwk);
 | 
			
		||||
	}
 | 
			
		||||
	return X509.packCsr(asn1pub, domains);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CSR._sign = function csrEcSig(jwk, request) {
 | 
			
		||||
	// Took some tips from https://gist.github.com/codermapuche/da4f96cdb6d5ff53b7ebc156ec46a10a
 | 
			
		||||
	// TODO will have to convert web ECDSA signatures to PEM ECDSA signatures (but RSA should be the same)
 | 
			
		||||
	// TODO have a consistent non-private way to sign
 | 
			
		||||
	return Keypairs.sign(
 | 
			
		||||
		{ jwk: jwk, format: 'x509' },
 | 
			
		||||
		Enc.hexToBuf(request)
 | 
			
		||||
	).then(function(sig) {
 | 
			
		||||
		return CSR._toDer({
 | 
			
		||||
			request: request,
 | 
			
		||||
			signature: sig,
 | 
			
		||||
			kty: jwk.kty
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
CSR._toDer = function encode(opts) {
 | 
			
		||||
	var sty;
 | 
			
		||||
	if (/^EC/i.test(opts.kty)) {
 | 
			
		||||
		// 1.2.840.10045.4.3.2 ecdsaWithSHA256 (ANSI X9.62 ECDSA algorithm with SHA256)
 | 
			
		||||
		sty = Asn1('30', Asn1('06', '2a8648ce3d040302'));
 | 
			
		||||
	} else {
 | 
			
		||||
		// 1.2.840.113549.1.1.11 sha256WithRSAEncryption (PKCS #1)
 | 
			
		||||
		sty = Asn1('30', Asn1('06', '2a864886f70d01010b'), Asn1('05'));
 | 
			
		||||
	}
 | 
			
		||||
	return Asn1(
 | 
			
		||||
		'30',
 | 
			
		||||
		// The Full CSR Request Body
 | 
			
		||||
		opts.request,
 | 
			
		||||
		// The Signature Type
 | 
			
		||||
		sty,
 | 
			
		||||
		// The Signature
 | 
			
		||||
		BitStr(Enc.bufToHex(opts.signature))
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
X509.packCsr = function(asn1pubkey, domains) {
 | 
			
		||||
	return Asn1(
 | 
			
		||||
		'30',
 | 
			
		||||
		// Version (0)
 | 
			
		||||
		UInt('00'),
 | 
			
		||||
 | 
			
		||||
		// 2.5.4.3 commonName (X.520 DN component)
 | 
			
		||||
		Asn1(
 | 
			
		||||
			'30',
 | 
			
		||||
			Asn1(
 | 
			
		||||
				'31',
 | 
			
		||||
				Asn1(
 | 
			
		||||
					'30',
 | 
			
		||||
					Asn1('06', '550403'),
 | 
			
		||||
					// TODO utf8 => punycode
 | 
			
		||||
					Asn1('0c', Enc.strToHex(domains[0]))
 | 
			
		||||
				)
 | 
			
		||||
			)
 | 
			
		||||
		),
 | 
			
		||||
 | 
			
		||||
		// Public Key (RSA or EC)
 | 
			
		||||
		asn1pubkey,
 | 
			
		||||
 | 
			
		||||
		// Request Body
 | 
			
		||||
		Asn1(
 | 
			
		||||
			'a0',
 | 
			
		||||
			Asn1(
 | 
			
		||||
				'30',
 | 
			
		||||
				// 1.2.840.113549.1.9.14 extensionRequest (PKCS #9 via CRMF)
 | 
			
		||||
				Asn1('06', '2a864886f70d01090e'),
 | 
			
		||||
				Asn1(
 | 
			
		||||
					'31',
 | 
			
		||||
					Asn1(
 | 
			
		||||
						'30',
 | 
			
		||||
						Asn1(
 | 
			
		||||
							'30',
 | 
			
		||||
							// 2.5.29.17 subjectAltName (X.509 extension)
 | 
			
		||||
							Asn1('06', '551d11'),
 | 
			
		||||
							Asn1(
 | 
			
		||||
								'04',
 | 
			
		||||
								Asn1(
 | 
			
		||||
									'30',
 | 
			
		||||
									domains
 | 
			
		||||
										.map(function(d) {
 | 
			
		||||
											// TODO utf8 => punycode
 | 
			
		||||
											return Asn1('82', Enc.strToHex(d));
 | 
			
		||||
										})
 | 
			
		||||
										.join('')
 | 
			
		||||
								)
 | 
			
		||||
							)
 | 
			
		||||
						)
 | 
			
		||||
					)
 | 
			
		||||
				)
 | 
			
		||||
			)
 | 
			
		||||
		)
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// TODO finish this later
 | 
			
		||||
// we want to parse the domains, the public key, and verify the signature
 | 
			
		||||
CSR._info = function(der) {
 | 
			
		||||
	// standard base64 PEM
 | 
			
		||||
	if ('string' === typeof der && '-' === der[0]) {
 | 
			
		||||
		der = PEM.parseBlock(der).bytes;
 | 
			
		||||
	}
 | 
			
		||||
	// jose urlBase64 not-PEM
 | 
			
		||||
	if ('string' === typeof der) {
 | 
			
		||||
		der = Enc.base64ToBuf(der);
 | 
			
		||||
	}
 | 
			
		||||
	// not supporting binary-encoded bas64
 | 
			
		||||
	var c = Asn1Parser.parse(der);
 | 
			
		||||
	var kty;
 | 
			
		||||
	// A cert has 3 parts: cert, signature meta, signature
 | 
			
		||||
	if (c.children.length !== 3) {
 | 
			
		||||
		throw new Error(
 | 
			
		||||
			"doesn't look like a certificate request: expected 3 parts of header"
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
	var sig = c.children[2];
 | 
			
		||||
	if (sig.children.length) {
 | 
			
		||||
		// ASN1/X509 EC
 | 
			
		||||
		sig = sig.children[0];
 | 
			
		||||
		sig = Asn1(
 | 
			
		||||
			'30',
 | 
			
		||||
			UInt(Enc.bufToHex(sig.children[0].value)),
 | 
			
		||||
			UInt(Enc.bufToHex(sig.children[1].value))
 | 
			
		||||
		);
 | 
			
		||||
		sig = Enc.hexToBuf(sig);
 | 
			
		||||
		kty = 'EC';
 | 
			
		||||
	} else {
 | 
			
		||||
		// Raw RSA Sig
 | 
			
		||||
		sig = sig.value;
 | 
			
		||||
		kty = 'RSA';
 | 
			
		||||
	}
 | 
			
		||||
	//c.children[1]; // signature type
 | 
			
		||||
	var req = c.children[0];
 | 
			
		||||
	if (4 !== req.children.length) {
 | 
			
		||||
		throw new Error(
 | 
			
		||||
			"doesn't look like a certificate request: expected 4 parts to request"
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
	// 0 null
 | 
			
		||||
	// 1 commonName / subject
 | 
			
		||||
	var sub = Enc.bufToStr(
 | 
			
		||||
		req.children[1].children[0].children[0].children[1].value
 | 
			
		||||
	);
 | 
			
		||||
	// 3 public key (type, key)
 | 
			
		||||
	//console.log('oid', Enc.bufToHex(req.children[2].children[0].children[0].value));
 | 
			
		||||
	var pub;
 | 
			
		||||
	// TODO reuse ASN1 parser for these?
 | 
			
		||||
	if ('EC' === kty) {
 | 
			
		||||
		// throw away compression byte
 | 
			
		||||
		pub = req.children[2].children[1].value.slice(1);
 | 
			
		||||
		pub = { kty: kty, x: pub.slice(0, 32), y: pub.slice(32) };
 | 
			
		||||
		while (0 === pub.x[0]) {
 | 
			
		||||
			pub.x = pub.x.slice(1);
 | 
			
		||||
		}
 | 
			
		||||
		while (0 === pub.y[0]) {
 | 
			
		||||
			pub.y = pub.y.slice(1);
 | 
			
		||||
		}
 | 
			
		||||
		if ((pub.x.length || pub.x.byteLength) > 48) {
 | 
			
		||||
			pub.crv = 'P-521';
 | 
			
		||||
		} else if ((pub.x.length || pub.x.byteLength) > 32) {
 | 
			
		||||
			pub.crv = 'P-384';
 | 
			
		||||
		} else {
 | 
			
		||||
			pub.crv = 'P-256';
 | 
			
		||||
		}
 | 
			
		||||
		pub.x = Enc.bufToUrlBase64(pub.x);
 | 
			
		||||
		pub.y = Enc.bufToUrlBase64(pub.y);
 | 
			
		||||
	} else {
 | 
			
		||||
		pub = req.children[2].children[1].children[0];
 | 
			
		||||
		pub = {
 | 
			
		||||
			kty: kty,
 | 
			
		||||
			n: pub.children[0].value,
 | 
			
		||||
			e: pub.children[1].value
 | 
			
		||||
		};
 | 
			
		||||
		while (0 === pub.n[0]) {
 | 
			
		||||
			pub.n = pub.n.slice(1);
 | 
			
		||||
		}
 | 
			
		||||
		while (0 === pub.e[0]) {
 | 
			
		||||
			pub.e = pub.e.slice(1);
 | 
			
		||||
		}
 | 
			
		||||
		pub.n = Enc.bufToUrlBase64(pub.n);
 | 
			
		||||
		pub.e = Enc.bufToUrlBase64(pub.e);
 | 
			
		||||
	}
 | 
			
		||||
	// 4 extensions
 | 
			
		||||
	var domains = req.children[3].children
 | 
			
		||||
		.filter(function(seq) {
 | 
			
		||||
			//  1.2.840.113549.1.9.14 extensionRequest (PKCS #9 via CRMF)
 | 
			
		||||
			if ('2a864886f70d01090e' === Enc.bufToHex(seq.children[0].value)) {
 | 
			
		||||
				return true;
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
		.map(function(seq) {
 | 
			
		||||
			return seq.children[1].children[0].children
 | 
			
		||||
				.filter(function(seq2) {
 | 
			
		||||
					// subjectAltName (X.509 extension)
 | 
			
		||||
					if ('551d11' === Enc.bufToHex(seq2.children[0].value)) {
 | 
			
		||||
						return true;
 | 
			
		||||
					}
 | 
			
		||||
				})
 | 
			
		||||
				.map(function(seq2) {
 | 
			
		||||
					return seq2.children[1].children[0].children.map(function(
 | 
			
		||||
						name
 | 
			
		||||
					) {
 | 
			
		||||
						// TODO utf8 => punycode
 | 
			
		||||
						return Enc.bufToStr(name.value);
 | 
			
		||||
					});
 | 
			
		||||
				})[0];
 | 
			
		||||
		})[0];
 | 
			
		||||
 | 
			
		||||
	return {
 | 
			
		||||
		subject: sub,
 | 
			
		||||
		altnames: domains,
 | 
			
		||||
		jwk: pub,
 | 
			
		||||
		signature: sig
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										239
									
								
								ecdsa.js
									
									
									
									
									
								
							
							
						
						
									
										239
									
								
								ecdsa.js
									
									
									
									
									
								
							@ -1,239 +0,0 @@
 | 
			
		||||
/*global Promise*/
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var Enc = require('@root/encoding');
 | 
			
		||||
 | 
			
		||||
var EC = module.exports;
 | 
			
		||||
var native = require('./lib/node/ecdsa.js');
 | 
			
		||||
 | 
			
		||||
// TODO SSH
 | 
			
		||||
var SSH;
 | 
			
		||||
 | 
			
		||||
var x509 = require('./x509.js');
 | 
			
		||||
var PEM = require('./pem.js');
 | 
			
		||||
//var SSH = require('./ssh-keys.js');
 | 
			
		||||
var sha2 = require('./lib/node/sha2.js');
 | 
			
		||||
 | 
			
		||||
// 1.2.840.10045.3.1.7
 | 
			
		||||
// prime256v1 (ANSI X9.62 named elliptic curve)
 | 
			
		||||
var OBJ_ID_EC = '06 08 2A8648CE3D030107'.replace(/\s+/g, '').toLowerCase();
 | 
			
		||||
// 1.3.132.0.34
 | 
			
		||||
// secp384r1 (SECG (Certicom) named elliptic curve)
 | 
			
		||||
var OBJ_ID_EC_384 = '06 05 2B81040022'.replace(/\s+/g, '').toLowerCase();
 | 
			
		||||
 | 
			
		||||
EC._stance =
 | 
			
		||||
	"We take the stance that if you're knowledgeable enough to" +
 | 
			
		||||
	" properly and securely use non-standard crypto then you shouldn't need Bluecrypt anyway.";
 | 
			
		||||
native._stance = EC._stance;
 | 
			
		||||
EC._universal =
 | 
			
		||||
	'Bluecrypt only supports crypto with standard cross-browser and cross-platform support.';
 | 
			
		||||
EC.generate = native.generate;
 | 
			
		||||
 | 
			
		||||
EC.export = function(opts) {
 | 
			
		||||
	return Promise.resolve().then(function() {
 | 
			
		||||
		if (!opts || !opts.jwk || 'object' !== typeof opts.jwk) {
 | 
			
		||||
			throw new Error('must pass { jwk: jwk } as a JSON object');
 | 
			
		||||
		}
 | 
			
		||||
		var jwk = JSON.parse(JSON.stringify(opts.jwk));
 | 
			
		||||
		var format = opts.format;
 | 
			
		||||
		if (
 | 
			
		||||
			opts.public ||
 | 
			
		||||
			-1 !== ['spki', 'pkix', 'ssh', 'rfc4716'].indexOf(format)
 | 
			
		||||
		) {
 | 
			
		||||
			jwk.d = null;
 | 
			
		||||
		}
 | 
			
		||||
		if ('EC' !== jwk.kty) {
 | 
			
		||||
			throw new Error("options.jwk.kty must be 'EC' for EC keys");
 | 
			
		||||
		}
 | 
			
		||||
		if (!jwk.d) {
 | 
			
		||||
			if (!format || -1 !== ['spki', 'pkix'].indexOf(format)) {
 | 
			
		||||
				format = 'spki';
 | 
			
		||||
			} else if (-1 !== ['ssh', 'rfc4716'].indexOf(format)) {
 | 
			
		||||
				format = 'ssh';
 | 
			
		||||
			} else {
 | 
			
		||||
				throw new Error(
 | 
			
		||||
					"options.format must be 'spki' or 'ssh' for public EC keys, not (" +
 | 
			
		||||
						typeof format +
 | 
			
		||||
						') ' +
 | 
			
		||||
						format
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			if (!format || 'sec1' === format) {
 | 
			
		||||
				format = 'sec1';
 | 
			
		||||
			} else if ('pkcs8' !== format) {
 | 
			
		||||
				throw new Error(
 | 
			
		||||
					"options.format must be 'sec1' or 'pkcs8' for private EC keys, not '" +
 | 
			
		||||
						format +
 | 
			
		||||
						"'"
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if (-1 === ['P-256', 'P-384'].indexOf(jwk.crv)) {
 | 
			
		||||
			throw new Error(
 | 
			
		||||
				"options.jwk.crv must be either P-256 or P-384 for EC keys, not '" +
 | 
			
		||||
					jwk.crv +
 | 
			
		||||
					"'"
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
		if (!jwk.y) {
 | 
			
		||||
			throw new Error(
 | 
			
		||||
				'options.jwk.y must be a urlsafe base64-encoded either P-256 or P-384'
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ('sec1' === format) {
 | 
			
		||||
			return PEM.packBlock({
 | 
			
		||||
				type: 'EC PRIVATE KEY',
 | 
			
		||||
				bytes: x509.packSec1(jwk)
 | 
			
		||||
			});
 | 
			
		||||
		} else if ('pkcs8' === format) {
 | 
			
		||||
			return PEM.packBlock({
 | 
			
		||||
				type: 'PRIVATE KEY',
 | 
			
		||||
				bytes: x509.packPkcs8(jwk)
 | 
			
		||||
			});
 | 
			
		||||
		} else if (-1 !== ['spki', 'pkix'].indexOf(format)) {
 | 
			
		||||
			return PEM.packBlock({
 | 
			
		||||
				type: 'PUBLIC KEY',
 | 
			
		||||
				bytes: x509.packSpki(jwk)
 | 
			
		||||
			});
 | 
			
		||||
		} else if (-1 !== ['ssh', 'rfc4716'].indexOf(format)) {
 | 
			
		||||
			return SSH.packSsh(jwk);
 | 
			
		||||
		} else {
 | 
			
		||||
			throw new Error(
 | 
			
		||||
				'Sanity Error: reached unreachable code block with format: ' +
 | 
			
		||||
					format
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
native.export = EC.export;
 | 
			
		||||
 | 
			
		||||
EC.import = function(opts) {
 | 
			
		||||
	return Promise.resolve().then(function() {
 | 
			
		||||
		if (!opts || !opts.pem || 'string' !== typeof opts.pem) {
 | 
			
		||||
			throw new Error('must pass { pem: pem } as a string');
 | 
			
		||||
		}
 | 
			
		||||
		if (0 === opts.pem.indexOf('ecdsa-sha2-')) {
 | 
			
		||||
			return SSH.parseSsh(opts.pem);
 | 
			
		||||
		}
 | 
			
		||||
		var pem = opts.pem;
 | 
			
		||||
		var u8 = PEM.parseBlock(pem).bytes;
 | 
			
		||||
		var hex = Enc.bufToHex(u8);
 | 
			
		||||
		var jwk = { kty: 'EC', crv: null, x: null, y: null };
 | 
			
		||||
 | 
			
		||||
		//console.log();
 | 
			
		||||
		if (
 | 
			
		||||
			-1 !== hex.indexOf(OBJ_ID_EC) ||
 | 
			
		||||
			-1 !== hex.indexOf(OBJ_ID_EC_384)
 | 
			
		||||
		) {
 | 
			
		||||
			if (-1 !== hex.indexOf(OBJ_ID_EC_384)) {
 | 
			
		||||
				jwk.crv = 'P-384';
 | 
			
		||||
			} else {
 | 
			
		||||
				jwk.crv = 'P-256';
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// PKCS8
 | 
			
		||||
			if (0x02 === u8[3] && 0x30 === u8[6] && 0x06 === u8[8]) {
 | 
			
		||||
				//console.log("PKCS8", u8[3].toString(16), u8[6].toString(16), u8[8].toString(16));
 | 
			
		||||
				jwk = x509.parsePkcs8(u8, jwk);
 | 
			
		||||
				// EC-only
 | 
			
		||||
			} else if (0x02 === u8[2] && 0x04 === u8[5] && 0xa0 === u8[39]) {
 | 
			
		||||
				//console.log("EC---", u8[2].toString(16), u8[5].toString(16), u8[39].toString(16));
 | 
			
		||||
				jwk = x509.parseSec1(u8, jwk);
 | 
			
		||||
				// EC-only
 | 
			
		||||
			} else if (0x02 === u8[3] && 0x04 === u8[6] && 0xa0 === u8[56]) {
 | 
			
		||||
				//console.log("EC---", u8[3].toString(16), u8[6].toString(16), u8[56].toString(16));
 | 
			
		||||
				jwk = x509.parseSec1(u8, jwk);
 | 
			
		||||
				// SPKI/PKIK (Public)
 | 
			
		||||
			} else if (0x30 === u8[2] && 0x06 === u8[4] && 0x06 === u8[13]) {
 | 
			
		||||
				//console.log("SPKI-", u8[2].toString(16), u8[4].toString(16), u8[13].toString(16));
 | 
			
		||||
				jwk = x509.parseSpki(u8, jwk);
 | 
			
		||||
				// Error
 | 
			
		||||
			} else {
 | 
			
		||||
				//console.log("PKCS8", u8[3].toString(16), u8[6].toString(16), u8[8].toString(16));
 | 
			
		||||
				//console.log("EC---", u8[2].toString(16), u8[5].toString(16), u8[39].toString(16));
 | 
			
		||||
				//console.log("EC---", u8[3].toString(16), u8[6].toString(16), u8[56].toString(16));
 | 
			
		||||
				//console.log("SPKI-", u8[2].toString(16), u8[4].toString(16), u8[13].toString(16));
 | 
			
		||||
				throw new Error('unrecognized key format');
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			throw new Error('Supported key types are P-256 and P-384');
 | 
			
		||||
		}
 | 
			
		||||
		if (opts.public) {
 | 
			
		||||
			if (true !== opts.public) {
 | 
			
		||||
				throw new Error(
 | 
			
		||||
					'options.public must be either `true` or `false` not (' +
 | 
			
		||||
						typeof opts.public +
 | 
			
		||||
						") '" +
 | 
			
		||||
						opts.public +
 | 
			
		||||
						"'"
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
			delete jwk.d;
 | 
			
		||||
		}
 | 
			
		||||
		return jwk;
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
native.import = EC.import;
 | 
			
		||||
 | 
			
		||||
EC.pack = function(opts) {
 | 
			
		||||
	return Promise.resolve().then(function() {
 | 
			
		||||
		return EC.export(opts);
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Chopping off the private parts is now part of the public API.
 | 
			
		||||
// I thought it sounded a little too crude at first, but it really is the best name in every possible way.
 | 
			
		||||
EC.neuter = function(opts) {
 | 
			
		||||
	// trying to find the best balance of an immutable copy with custom attributes
 | 
			
		||||
	var jwk = {};
 | 
			
		||||
	Object.keys(opts.jwk).forEach(function(k) {
 | 
			
		||||
		if ('undefined' === typeof opts.jwk[k]) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		// ignore EC private parts
 | 
			
		||||
		if ('d' === k) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		jwk[k] = JSON.parse(JSON.stringify(opts.jwk[k]));
 | 
			
		||||
	});
 | 
			
		||||
	return jwk;
 | 
			
		||||
};
 | 
			
		||||
native.neuter = EC.neuter;
 | 
			
		||||
 | 
			
		||||
// https://stackoverflow.com/questions/42588786/how-to-fingerprint-a-jwk
 | 
			
		||||
EC.__thumbprint = function(jwk) {
 | 
			
		||||
	// Use the same entropy for SHA as for key
 | 
			
		||||
	var alg = 'SHA-256';
 | 
			
		||||
	if (/384/.test(jwk.crv)) {
 | 
			
		||||
		alg = 'SHA-384';
 | 
			
		||||
	}
 | 
			
		||||
	var payload =
 | 
			
		||||
		'{"crv":"' +
 | 
			
		||||
		jwk.crv +
 | 
			
		||||
		'","kty":"EC","x":"' +
 | 
			
		||||
		jwk.x +
 | 
			
		||||
		'","y":"' +
 | 
			
		||||
		jwk.y +
 | 
			
		||||
		'"}';
 | 
			
		||||
	return sha2.sum(alg, payload).then(function(hash) {
 | 
			
		||||
		return Enc.bufToUrlBase64(Uint8Array.from(hash));
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
EC.thumbprint = function(opts) {
 | 
			
		||||
	return Promise.resolve().then(function() {
 | 
			
		||||
		var jwk;
 | 
			
		||||
		if ('EC' === opts.kty) {
 | 
			
		||||
			jwk = opts;
 | 
			
		||||
		} else if (opts.jwk) {
 | 
			
		||||
			jwk = opts.jwk;
 | 
			
		||||
		} else {
 | 
			
		||||
			return native.import(opts).then(function(jwk) {
 | 
			
		||||
				return EC.__thumbprint(jwk);
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
		return EC.__thumbprint(jwk);
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										315
									
								
								keypairs.js
									
									
									
									
									
								
							
							
						
						
									
										315
									
								
								keypairs.js
									
									
									
									
									
								
							@ -1,315 +0,0 @@
 | 
			
		||||
/*global Promise*/
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
require('@root/encoding/bytes');
 | 
			
		||||
var Enc = require('@root/encoding/base64');
 | 
			
		||||
 | 
			
		||||
var Keypairs = module.exports;
 | 
			
		||||
var Rasha = require('./rsa.js');
 | 
			
		||||
var Eckles = require('./ecdsa.js');
 | 
			
		||||
var native = require('./lib/node/keypairs.js');
 | 
			
		||||
 | 
			
		||||
Keypairs._stance =
 | 
			
		||||
	"We take the stance that if you're knowledgeable enough to" +
 | 
			
		||||
	" properly and securely use non-standard crypto then you shouldn't need Bluecrypt anyway.";
 | 
			
		||||
Keypairs._universal =
 | 
			
		||||
	'Bluecrypt only supports crypto with standard cross-browser and cross-platform support.';
 | 
			
		||||
Keypairs.generate = function(opts) {
 | 
			
		||||
	opts = opts || {};
 | 
			
		||||
	var p;
 | 
			
		||||
	if (!opts.kty) {
 | 
			
		||||
		opts.kty = opts.type;
 | 
			
		||||
	}
 | 
			
		||||
	if (!opts.kty) {
 | 
			
		||||
		opts.kty = 'EC';
 | 
			
		||||
	}
 | 
			
		||||
	if (/^EC/i.test(opts.kty)) {
 | 
			
		||||
		p = Eckles.generate(opts);
 | 
			
		||||
	} else if (/^RSA$/i.test(opts.kty)) {
 | 
			
		||||
		p = Rasha.generate(opts);
 | 
			
		||||
	} else {
 | 
			
		||||
		return Promise.Reject(
 | 
			
		||||
			new Error(
 | 
			
		||||
				"'" +
 | 
			
		||||
					opts.kty +
 | 
			
		||||
					"' is not a well-supported key type." +
 | 
			
		||||
					Keypairs._universal +
 | 
			
		||||
					" Please choose 'EC', or 'RSA' if you have good reason to."
 | 
			
		||||
			)
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
	return p.then(function(pair) {
 | 
			
		||||
		return Keypairs.thumbprint({ jwk: pair.public }).then(function(thumb) {
 | 
			
		||||
			pair.private.kid = thumb; // maybe not the same id on the private key?
 | 
			
		||||
			pair.public.kid = thumb;
 | 
			
		||||
			return pair;
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Keypairs.export = function(opts) {
 | 
			
		||||
	return Eckles.export(opts).catch(function(err) {
 | 
			
		||||
		return Rasha.export(opts).catch(function() {
 | 
			
		||||
			return Promise.reject(err);
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
// XXX
 | 
			
		||||
native.export = Keypairs.export;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Chopping off the private parts is now part of the public API.
 | 
			
		||||
 * I thought it sounded a little too crude at first, but it really is the best name in every possible way.
 | 
			
		||||
 */
 | 
			
		||||
Keypairs.neuter = function(opts) {
 | 
			
		||||
	/** trying to find the best balance of an immutable copy with custom attributes */
 | 
			
		||||
	var jwk = {};
 | 
			
		||||
	Object.keys(opts.jwk).forEach(function(k) {
 | 
			
		||||
		if ('undefined' === typeof opts.jwk[k]) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		// ignore RSA and EC private parts
 | 
			
		||||
		if (-1 !== ['d', 'p', 'q', 'dp', 'dq', 'qi'].indexOf(k)) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		jwk[k] = JSON.parse(JSON.stringify(opts.jwk[k]));
 | 
			
		||||
	});
 | 
			
		||||
	return jwk;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Keypairs.thumbprint = function(opts) {
 | 
			
		||||
	return Promise.resolve().then(function() {
 | 
			
		||||
		if (/EC/i.test(opts.jwk.kty)) {
 | 
			
		||||
			return Eckles.thumbprint(opts);
 | 
			
		||||
		} else {
 | 
			
		||||
			return Rasha.thumbprint(opts);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Keypairs.publish = function(opts) {
 | 
			
		||||
	if ('object' !== typeof opts.jwk || !opts.jwk.kty) {
 | 
			
		||||
		throw new Error('invalid jwk: ' + JSON.stringify(opts.jwk));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/** returns a copy */
 | 
			
		||||
	var jwk = Keypairs.neuter(opts);
 | 
			
		||||
 | 
			
		||||
	if (jwk.exp) {
 | 
			
		||||
		jwk.exp = setTime(jwk.exp);
 | 
			
		||||
	} else {
 | 
			
		||||
		if (opts.exp) {
 | 
			
		||||
			jwk.exp = setTime(opts.exp);
 | 
			
		||||
		} else if (opts.expiresIn) {
 | 
			
		||||
			jwk.exp = Math.round(Date.now() / 1000) + opts.expiresIn;
 | 
			
		||||
		} else if (opts.expiresAt) {
 | 
			
		||||
			jwk.exp = opts.expiresAt;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if (!jwk.use && false !== jwk.use) {
 | 
			
		||||
		jwk.use = 'sig';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (jwk.kid) {
 | 
			
		||||
		return Promise.resolve(jwk);
 | 
			
		||||
	}
 | 
			
		||||
	return Keypairs.thumbprint({ jwk: jwk }).then(function(thumb) {
 | 
			
		||||
		jwk.kid = thumb;
 | 
			
		||||
		return jwk;
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// JWT a.k.a. JWS with Claims using Compact Serialization
 | 
			
		||||
Keypairs.signJwt = function(opts) {
 | 
			
		||||
	return Keypairs.thumbprint({ jwk: opts.jwk }).then(function(thumb) {
 | 
			
		||||
		var header = opts.header || {};
 | 
			
		||||
		var claims = JSON.parse(JSON.stringify(opts.claims || {}));
 | 
			
		||||
		header.typ = 'JWT';
 | 
			
		||||
 | 
			
		||||
		if (!header.kid && false !== header.kid) {
 | 
			
		||||
			header.kid = thumb;
 | 
			
		||||
		}
 | 
			
		||||
		if (!header.alg && opts.alg) {
 | 
			
		||||
			header.alg = opts.alg;
 | 
			
		||||
		}
 | 
			
		||||
		if (!claims.iat && (false === claims.iat || false === opts.iat)) {
 | 
			
		||||
			claims.iat = undefined;
 | 
			
		||||
		} else if (!claims.iat) {
 | 
			
		||||
			claims.iat = Math.round(Date.now() / 1000);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (opts.exp) {
 | 
			
		||||
			claims.exp = setTime(opts.exp);
 | 
			
		||||
		} else if (
 | 
			
		||||
			!claims.exp &&
 | 
			
		||||
			(false === claims.exp || false === opts.exp)
 | 
			
		||||
		) {
 | 
			
		||||
			claims.exp = undefined;
 | 
			
		||||
		} else if (!claims.exp) {
 | 
			
		||||
			throw new Error(
 | 
			
		||||
				"opts.claims.exp should be the expiration date as seconds, human form (i.e. '1h' or '15m') or false"
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (opts.iss) {
 | 
			
		||||
			claims.iss = opts.iss;
 | 
			
		||||
		}
 | 
			
		||||
		if (!claims.iss && (false === claims.iss || false === opts.iss)) {
 | 
			
		||||
			claims.iss = undefined;
 | 
			
		||||
		} else if (!claims.iss) {
 | 
			
		||||
			throw new Error(
 | 
			
		||||
				'opts.claims.iss should be in the form of https://example.com/, a secure OIDC base url'
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return Keypairs.signJws({
 | 
			
		||||
			jwk: opts.jwk,
 | 
			
		||||
			pem: opts.pem,
 | 
			
		||||
			protected: header,
 | 
			
		||||
			header: undefined,
 | 
			
		||||
			payload: claims
 | 
			
		||||
		}).then(function(jws) {
 | 
			
		||||
			return [jws.protected, jws.payload, jws.signature].join('.');
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Keypairs.signJws = function(opts) {
 | 
			
		||||
	return Keypairs.thumbprint(opts).then(function(thumb) {
 | 
			
		||||
		function alg() {
 | 
			
		||||
			if (!opts.jwk) {
 | 
			
		||||
				throw new Error("opts.jwk must exist and must declare 'typ'");
 | 
			
		||||
			}
 | 
			
		||||
			if (opts.jwk.alg) {
 | 
			
		||||
				return opts.jwk.alg;
 | 
			
		||||
			}
 | 
			
		||||
			var typ = 'RSA' === opts.jwk.kty ? 'RS' : 'ES';
 | 
			
		||||
			return typ + Keypairs._getBits(opts);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		function sign() {
 | 
			
		||||
			var protect = opts.protected;
 | 
			
		||||
			var payload = opts.payload;
 | 
			
		||||
 | 
			
		||||
			// Compute JWS signature
 | 
			
		||||
			var protectedHeader = '';
 | 
			
		||||
			// Because unprotected headers are allowed, regrettably...
 | 
			
		||||
			// https://stackoverflow.com/a/46288694
 | 
			
		||||
			if (false !== protect) {
 | 
			
		||||
				if (!protect) {
 | 
			
		||||
					protect = {};
 | 
			
		||||
				}
 | 
			
		||||
				if (!protect.alg) {
 | 
			
		||||
					protect.alg = alg();
 | 
			
		||||
				}
 | 
			
		||||
				// There's a particular request where ACME / Let's Encrypt explicitly doesn't use a kid
 | 
			
		||||
				if (false === protect.kid) {
 | 
			
		||||
					protect.kid = undefined;
 | 
			
		||||
				} else if (!protect.kid) {
 | 
			
		||||
					protect.kid = thumb;
 | 
			
		||||
				}
 | 
			
		||||
				protectedHeader = JSON.stringify(protect);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			// Not sure how to handle the empty case since ACME POST-as-GET must be empty
 | 
			
		||||
			//if (!payload) {
 | 
			
		||||
			//  throw new Error("opts.payload should be JSON, string, or ArrayBuffer (it may be empty, but that must be explicit)");
 | 
			
		||||
			//}
 | 
			
		||||
			// Trying to detect if it's a plain object (not Buffer, ArrayBuffer, Array, Uint8Array, etc)
 | 
			
		||||
			if (
 | 
			
		||||
				payload &&
 | 
			
		||||
				'string' !== typeof payload &&
 | 
			
		||||
				'undefined' === typeof payload.byteLength &&
 | 
			
		||||
				'undefined' === typeof payload.buffer
 | 
			
		||||
			) {
 | 
			
		||||
				payload = JSON.stringify(payload);
 | 
			
		||||
			}
 | 
			
		||||
			// Converting to a buffer, even if it was just converted to a string
 | 
			
		||||
			if ('string' === typeof payload) {
 | 
			
		||||
				payload = Enc.strToBuf(payload);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var protected64 = Enc.strToUrlBase64(protectedHeader);
 | 
			
		||||
			var payload64 = Enc.bufToUrlBase64(payload);
 | 
			
		||||
			var msg = protected64 + '.' + payload64;
 | 
			
		||||
 | 
			
		||||
			return native._sign(opts, msg).then(function(buf) {
 | 
			
		||||
				var signedMsg = {
 | 
			
		||||
					protected: protected64,
 | 
			
		||||
					payload: payload64,
 | 
			
		||||
					signature: Enc.bufToUrlBase64(buf)
 | 
			
		||||
				};
 | 
			
		||||
 | 
			
		||||
				return signedMsg;
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (opts.jwk) {
 | 
			
		||||
			return sign();
 | 
			
		||||
		} else {
 | 
			
		||||
			return Keypairs.import({ pem: opts.pem }).then(function(pair) {
 | 
			
		||||
				opts.jwk = pair.private;
 | 
			
		||||
				return sign();
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// TODO expose consistently
 | 
			
		||||
Keypairs.sign = native._sign;
 | 
			
		||||
 | 
			
		||||
Keypairs._getBits = function(opts) {
 | 
			
		||||
	if (opts.alg) {
 | 
			
		||||
		return opts.alg.replace(/[a-z\-]/gi, '');
 | 
			
		||||
	}
 | 
			
		||||
	// base64 len to byte len
 | 
			
		||||
	var len = Math.floor((opts.jwk.n || '').length * 0.75);
 | 
			
		||||
 | 
			
		||||
	// TODO this may be a bug
 | 
			
		||||
	// need to confirm that the padding is no more or less than 1 byte
 | 
			
		||||
	if (/521/.test(opts.jwk.crv) || len >= 511) {
 | 
			
		||||
		return '512';
 | 
			
		||||
	} else if (/384/.test(opts.jwk.crv) || len >= 383) {
 | 
			
		||||
		return '384';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return '256';
 | 
			
		||||
};
 | 
			
		||||
// XXX
 | 
			
		||||
native._getBits = Keypairs._getBits;
 | 
			
		||||
 | 
			
		||||
function setTime(time) {
 | 
			
		||||
	if ('number' === typeof time) {
 | 
			
		||||
		return time;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var t = time.match(/^(\-?\d+)([dhms])$/i);
 | 
			
		||||
	if (!t || !t[0]) {
 | 
			
		||||
		throw new Error(
 | 
			
		||||
			"'" +
 | 
			
		||||
				time +
 | 
			
		||||
				"' should be datetime in seconds or human-readable format (i.e. 3d, 1h, 15m, 30s"
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var now = Math.round(Date.now() / 1000);
 | 
			
		||||
	var num = parseInt(t[1], 10);
 | 
			
		||||
	var unit = t[2];
 | 
			
		||||
	var mult = 1;
 | 
			
		||||
	switch (unit) {
 | 
			
		||||
		// fancy fallthrough, what fun!
 | 
			
		||||
		case 'd':
 | 
			
		||||
			mult *= 24;
 | 
			
		||||
		/*falls through*/
 | 
			
		||||
		case 'h':
 | 
			
		||||
			mult *= 60;
 | 
			
		||||
		/*falls through*/
 | 
			
		||||
		case 'm':
 | 
			
		||||
			mult *= 60;
 | 
			
		||||
		/*falls through*/
 | 
			
		||||
		case 's':
 | 
			
		||||
			mult *= 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return now + mult * num;
 | 
			
		||||
}
 | 
			
		||||
@ -1,57 +0,0 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var native = module.exports;
 | 
			
		||||
// XXX received from caller
 | 
			
		||||
var EC = native;
 | 
			
		||||
 | 
			
		||||
native.generate = function(opts) {
 | 
			
		||||
	var wcOpts = {};
 | 
			
		||||
	if (!opts) {
 | 
			
		||||
		opts = {};
 | 
			
		||||
	}
 | 
			
		||||
	if (!opts.kty) {
 | 
			
		||||
		opts.kty = 'EC';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// ECDSA has only the P curves and an associated bitlength
 | 
			
		||||
	wcOpts.name = 'ECDSA';
 | 
			
		||||
	if (!opts.namedCurve) {
 | 
			
		||||
		opts.namedCurve = 'P-256';
 | 
			
		||||
	}
 | 
			
		||||
	wcOpts.namedCurve = opts.namedCurve; // true for supported curves
 | 
			
		||||
	if (/256/.test(wcOpts.namedCurve)) {
 | 
			
		||||
		wcOpts.namedCurve = 'P-256';
 | 
			
		||||
		wcOpts.hash = { name: 'SHA-256' };
 | 
			
		||||
	} else if (/384/.test(wcOpts.namedCurve)) {
 | 
			
		||||
		wcOpts.namedCurve = 'P-384';
 | 
			
		||||
		wcOpts.hash = { name: 'SHA-384' };
 | 
			
		||||
	} else {
 | 
			
		||||
		return Promise.Reject(
 | 
			
		||||
			new Error(
 | 
			
		||||
				"'" +
 | 
			
		||||
					wcOpts.namedCurve +
 | 
			
		||||
					"' is not an NIST approved ECDSA namedCurve. " +
 | 
			
		||||
					" Please choose either 'P-256' or 'P-384'. " +
 | 
			
		||||
					// XXX received from caller
 | 
			
		||||
					EC._stance
 | 
			
		||||
			)
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var extractable = true;
 | 
			
		||||
	return window.crypto.subtle
 | 
			
		||||
		.generateKey(wcOpts, extractable, ['sign', 'verify'])
 | 
			
		||||
		.then(function(result) {
 | 
			
		||||
			return window.crypto.subtle
 | 
			
		||||
				.exportKey('jwk', result.privateKey)
 | 
			
		||||
				.then(function(privJwk) {
 | 
			
		||||
					privJwk.key_ops = undefined;
 | 
			
		||||
					privJwk.ext = undefined;
 | 
			
		||||
					return {
 | 
			
		||||
						private: privJwk,
 | 
			
		||||
						// XXX received from caller
 | 
			
		||||
						public: EC.neuter({ jwk: privJwk })
 | 
			
		||||
					};
 | 
			
		||||
				});
 | 
			
		||||
		});
 | 
			
		||||
};
 | 
			
		||||
@ -1,108 +0,0 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var Keypairs = module.exports;
 | 
			
		||||
 | 
			
		||||
Keypairs._sign = function(opts, payload) {
 | 
			
		||||
	return Keypairs._import(opts).then(function(privkey) {
 | 
			
		||||
		if ('string' === typeof payload) {
 | 
			
		||||
			payload = new TextEncoder().encode(payload);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return window.crypto.subtle
 | 
			
		||||
			.sign(
 | 
			
		||||
				{
 | 
			
		||||
					name: Keypairs._getName(opts),
 | 
			
		||||
					hash: { name: 'SHA-' + Keypairs._getBits(opts) }
 | 
			
		||||
				},
 | 
			
		||||
				privkey,
 | 
			
		||||
				payload
 | 
			
		||||
			)
 | 
			
		||||
			.then(function(signature) {
 | 
			
		||||
				signature = new Uint8Array(signature); // ArrayBuffer -> u8
 | 
			
		||||
				// This will come back into play for CSRs, but not for JOSE
 | 
			
		||||
				if ('EC' === opts.jwk.kty && /x509|asn1/i.test(opts.format)) {
 | 
			
		||||
					return Keypairs._ecdsaJoseSigToAsn1Sig(signature);
 | 
			
		||||
				} else {
 | 
			
		||||
					// jose/jws/jwt
 | 
			
		||||
					return signature;
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Keypairs._import = function(opts) {
 | 
			
		||||
	return Promise.resolve().then(function() {
 | 
			
		||||
		var ops;
 | 
			
		||||
		// all private keys just happen to have a 'd'
 | 
			
		||||
		if (opts.jwk.d) {
 | 
			
		||||
			ops = ['sign'];
 | 
			
		||||
		} else {
 | 
			
		||||
			ops = ['verify'];
 | 
			
		||||
		}
 | 
			
		||||
		// gotta mark it as extractable, as if it matters
 | 
			
		||||
		opts.jwk.ext = true;
 | 
			
		||||
		opts.jwk.key_ops = ops;
 | 
			
		||||
 | 
			
		||||
		return window.crypto.subtle
 | 
			
		||||
			.importKey(
 | 
			
		||||
				'jwk',
 | 
			
		||||
				opts.jwk,
 | 
			
		||||
				{
 | 
			
		||||
					name: Keypairs._getName(opts),
 | 
			
		||||
					namedCurve: opts.jwk.crv,
 | 
			
		||||
					hash: { name: 'SHA-' + Keypairs._getBits(opts) }
 | 
			
		||||
				},
 | 
			
		||||
				true,
 | 
			
		||||
				ops
 | 
			
		||||
			)
 | 
			
		||||
			.then(function(privkey) {
 | 
			
		||||
				delete opts.jwk.ext;
 | 
			
		||||
				return privkey;
 | 
			
		||||
			});
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// ECDSA JOSE / JWS / JWT signatures differ from "normal" ASN1/X509 ECDSA signatures
 | 
			
		||||
// https://tools.ietf.org/html/rfc7518#section-3.4
 | 
			
		||||
Keypairs._ecdsaJoseSigToAsn1Sig = function(bufsig) {
 | 
			
		||||
	// it's easier to do the manipulation in the browser with an array
 | 
			
		||||
	bufsig = Array.from(bufsig);
 | 
			
		||||
	var hlen = bufsig.length / 2; // should be even
 | 
			
		||||
	var r = bufsig.slice(0, hlen);
 | 
			
		||||
	var s = bufsig.slice(hlen);
 | 
			
		||||
	// unpad positive ints less than 32 bytes wide
 | 
			
		||||
	while (!r[0]) {
 | 
			
		||||
		r = r.slice(1);
 | 
			
		||||
	}
 | 
			
		||||
	while (!s[0]) {
 | 
			
		||||
		s = s.slice(1);
 | 
			
		||||
	}
 | 
			
		||||
	// pad (or re-pad) ambiguously non-negative BigInts, up to 33 bytes wide
 | 
			
		||||
	if (0x80 & r[0]) {
 | 
			
		||||
		r.unshift(0);
 | 
			
		||||
	}
 | 
			
		||||
	if (0x80 & s[0]) {
 | 
			
		||||
		s.unshift(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var len = 2 + r.length + 2 + s.length;
 | 
			
		||||
	var head = [0x30];
 | 
			
		||||
	// hard code 0x80 + 1 because it won't be longer than
 | 
			
		||||
	// two SHA512 plus two pad bytes (130 bytes <= 256)
 | 
			
		||||
	if (len >= 0x80) {
 | 
			
		||||
		head.push(0x81);
 | 
			
		||||
	}
 | 
			
		||||
	head.push(len);
 | 
			
		||||
 | 
			
		||||
	return Uint8Array.from(
 | 
			
		||||
		head.concat([0x02, r.length], r, [0x02, s.length], s)
 | 
			
		||||
	);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Keypairs._getName = function(opts) {
 | 
			
		||||
	if (/EC/i.test(opts.jwk.kty)) {
 | 
			
		||||
		return 'ECDSA';
 | 
			
		||||
	} else {
 | 
			
		||||
		return 'RSASSA-PKCS1-v1_5';
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
@ -1,59 +0,0 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var native = module.exports;
 | 
			
		||||
// XXX added by caller: _stance, neuter
 | 
			
		||||
var RSA = native;
 | 
			
		||||
 | 
			
		||||
native.generate = function(opts) {
 | 
			
		||||
	var wcOpts = {};
 | 
			
		||||
	if (!opts) {
 | 
			
		||||
		opts = {};
 | 
			
		||||
	}
 | 
			
		||||
	if (!opts.kty) {
 | 
			
		||||
		opts.kty = 'RSA';
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Support PSS? I don't think it's used for Let's Encrypt
 | 
			
		||||
	wcOpts.name = 'RSASSA-PKCS1-v1_5';
 | 
			
		||||
	if (!opts.modulusLength) {
 | 
			
		||||
		opts.modulusLength = 2048;
 | 
			
		||||
	}
 | 
			
		||||
	wcOpts.modulusLength = opts.modulusLength;
 | 
			
		||||
	if (wcOpts.modulusLength >= 2048 && wcOpts.modulusLength < 3072) {
 | 
			
		||||
		// erring on the small side... for no good reason
 | 
			
		||||
		wcOpts.hash = { name: 'SHA-256' };
 | 
			
		||||
	} else if (wcOpts.modulusLength >= 3072 && wcOpts.modulusLength < 4096) {
 | 
			
		||||
		wcOpts.hash = { name: 'SHA-384' };
 | 
			
		||||
	} else if (wcOpts.modulusLength < 4097) {
 | 
			
		||||
		wcOpts.hash = { name: 'SHA-512' };
 | 
			
		||||
	} else {
 | 
			
		||||
		// Public key thumbprints should be paired with a hash of similar length,
 | 
			
		||||
		// so anything above SHA-512's keyspace would be left under-represented anyway.
 | 
			
		||||
		return Promise.Reject(
 | 
			
		||||
			new Error(
 | 
			
		||||
				"'" +
 | 
			
		||||
					wcOpts.modulusLength +
 | 
			
		||||
					"' is not within the safe and universally" +
 | 
			
		||||
					' acceptable range of 2048-4096. Typically you should pick 2048, 3072, or 4096, though other values' +
 | 
			
		||||
					' divisible by 8 are allowed. ' +
 | 
			
		||||
					RSA._stance
 | 
			
		||||
			)
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
	// TODO maybe allow this to be set to any of the standard values?
 | 
			
		||||
	wcOpts.publicExponent = new Uint8Array([0x01, 0x00, 0x01]);
 | 
			
		||||
 | 
			
		||||
	var extractable = true;
 | 
			
		||||
	return window.crypto.subtle
 | 
			
		||||
		.generateKey(wcOpts, extractable, ['sign', 'verify'])
 | 
			
		||||
		.then(function(result) {
 | 
			
		||||
			return window.crypto.subtle
 | 
			
		||||
				.exportKey('jwk', result.privateKey)
 | 
			
		||||
				.then(function(privJwk) {
 | 
			
		||||
					return {
 | 
			
		||||
						private: privJwk,
 | 
			
		||||
						public: RSA.neuter({ jwk: privJwk })
 | 
			
		||||
					};
 | 
			
		||||
				});
 | 
			
		||||
		});
 | 
			
		||||
};
 | 
			
		||||
@ -1,113 +0,0 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var native = module.exports;
 | 
			
		||||
// XXX provided by caller: import, export
 | 
			
		||||
var EC = native;
 | 
			
		||||
// TODO SSH
 | 
			
		||||
 | 
			
		||||
native.generate = function(opts) {
 | 
			
		||||
	return Promise.resolve().then(function() {
 | 
			
		||||
		var typ = 'ec';
 | 
			
		||||
		var format = opts.format;
 | 
			
		||||
		var encoding = opts.encoding;
 | 
			
		||||
		var priv;
 | 
			
		||||
		var pub = 'spki';
 | 
			
		||||
 | 
			
		||||
		if (!format) {
 | 
			
		||||
			format = 'jwk';
 | 
			
		||||
		}
 | 
			
		||||
		if (-1 !== ['spki', 'pkcs8', 'ssh'].indexOf(format)) {
 | 
			
		||||
			format = 'pkcs8';
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ('pem' === format) {
 | 
			
		||||
			format = 'sec1';
 | 
			
		||||
			encoding = 'pem';
 | 
			
		||||
		} else if ('der' === format) {
 | 
			
		||||
			format = 'sec1';
 | 
			
		||||
			encoding = 'der';
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ('jwk' === format || 'json' === format) {
 | 
			
		||||
			format = 'jwk';
 | 
			
		||||
			encoding = 'json';
 | 
			
		||||
		} else {
 | 
			
		||||
			priv = format;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (!encoding) {
 | 
			
		||||
			encoding = 'pem';
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (priv) {
 | 
			
		||||
			priv = { type: priv, format: encoding };
 | 
			
		||||
			pub = { type: pub, format: encoding };
 | 
			
		||||
		} else {
 | 
			
		||||
			// jwk
 | 
			
		||||
			priv = { type: 'sec1', format: 'pem' };
 | 
			
		||||
			pub = { type: 'spki', format: 'pem' };
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return new Promise(function(resolve, reject) {
 | 
			
		||||
			return require('crypto').generateKeyPair(
 | 
			
		||||
				typ,
 | 
			
		||||
				{
 | 
			
		||||
					namedCurve: opts.crv || opts.namedCurve || 'P-256',
 | 
			
		||||
					privateKeyEncoding: priv,
 | 
			
		||||
					publicKeyEncoding: pub
 | 
			
		||||
				},
 | 
			
		||||
				function(err, pubkey, privkey) {
 | 
			
		||||
					if (err) {
 | 
			
		||||
						reject(err);
 | 
			
		||||
					}
 | 
			
		||||
					resolve({
 | 
			
		||||
						private: privkey,
 | 
			
		||||
						public: pubkey
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
			);
 | 
			
		||||
		}).then(function(keypair) {
 | 
			
		||||
			if ('jwk' === format) {
 | 
			
		||||
				return Promise.all([
 | 
			
		||||
					native.import({
 | 
			
		||||
						pem: keypair.private,
 | 
			
		||||
						format: priv.type
 | 
			
		||||
					}),
 | 
			
		||||
					native.import({
 | 
			
		||||
						pem: keypair.public,
 | 
			
		||||
						format: pub.type,
 | 
			
		||||
						public: true
 | 
			
		||||
					})
 | 
			
		||||
				]).then(function(pair) {
 | 
			
		||||
					return {
 | 
			
		||||
						private: pair[0],
 | 
			
		||||
						public: pair[1]
 | 
			
		||||
					};
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if ('ssh' !== opts.format) {
 | 
			
		||||
				return keypair;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return native
 | 
			
		||||
				.import({
 | 
			
		||||
					pem: keypair.public,
 | 
			
		||||
					format: format,
 | 
			
		||||
					public: true
 | 
			
		||||
				})
 | 
			
		||||
				.then(function(jwk) {
 | 
			
		||||
					return EC.export({
 | 
			
		||||
						jwk: jwk,
 | 
			
		||||
						format: opts.format,
 | 
			
		||||
						public: true
 | 
			
		||||
					}).then(function(pub) {
 | 
			
		||||
						return {
 | 
			
		||||
							private: keypair.private,
 | 
			
		||||
							public: pub
 | 
			
		||||
						};
 | 
			
		||||
					});
 | 
			
		||||
				});
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
@ -1,55 +0,0 @@
 | 
			
		||||
// Copyright 2016-2018 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/. */
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
module.exports = function(bitlen, exp) {
 | 
			
		||||
	var k = require('node-forge').pki.rsa.generateKeyPair({
 | 
			
		||||
		bits: bitlen || 2048,
 | 
			
		||||
		e: exp || 0x10001
 | 
			
		||||
	}).privateKey;
 | 
			
		||||
	var jwk = {
 | 
			
		||||
		kty: 'RSA',
 | 
			
		||||
		n: _toUrlBase64(k.n),
 | 
			
		||||
		e: _toUrlBase64(k.e),
 | 
			
		||||
		d: _toUrlBase64(k.d),
 | 
			
		||||
		p: _toUrlBase64(k.p),
 | 
			
		||||
		q: _toUrlBase64(k.q),
 | 
			
		||||
		dp: _toUrlBase64(k.dP),
 | 
			
		||||
		dq: _toUrlBase64(k.dQ),
 | 
			
		||||
		qi: _toUrlBase64(k.qInv)
 | 
			
		||||
	};
 | 
			
		||||
	return {
 | 
			
		||||
		private: jwk,
 | 
			
		||||
		public: {
 | 
			
		||||
			kty: jwk.kty,
 | 
			
		||||
			n: jwk.n,
 | 
			
		||||
			e: jwk.e
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function _toUrlBase64(fbn) {
 | 
			
		||||
	var hex = fbn.toRadix(16);
 | 
			
		||||
	if (hex.length % 2) {
 | 
			
		||||
		// Invalid hex string
 | 
			
		||||
		hex = '0' + hex;
 | 
			
		||||
	}
 | 
			
		||||
	while ('00' === hex.slice(0, 2)) {
 | 
			
		||||
		hex = hex.slice(2);
 | 
			
		||||
	}
 | 
			
		||||
	return Buffer.from(hex, 'hex')
 | 
			
		||||
		.toString('base64')
 | 
			
		||||
		.replace(/\+/g, '-')
 | 
			
		||||
		.replace(/\//g, '_')
 | 
			
		||||
		.replace(/=/g, '');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if (require.main === module) {
 | 
			
		||||
	var keypair = module.exports(2048, 0x10001);
 | 
			
		||||
	console.info(keypair.private);
 | 
			
		||||
	console.warn(keypair.public);
 | 
			
		||||
	//console.info(keypair.privateKeyJwk);
 | 
			
		||||
	//console.warn(keypair.publicKeyJwk);
 | 
			
		||||
}
 | 
			
		||||
@ -1,21 +0,0 @@
 | 
			
		||||
// Copyright 2016-2018 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/. */
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
module.exports = function(bitlen, exp) {
 | 
			
		||||
	var keypair = require('crypto').generateKeyPairSync('rsa', {
 | 
			
		||||
		modulusLength: bitlen,
 | 
			
		||||
		publicExponent: exp,
 | 
			
		||||
		privateKeyEncoding: { type: 'pkcs1', format: 'pem' },
 | 
			
		||||
		publicKeyEncoding: { type: 'pkcs1', format: 'pem' }
 | 
			
		||||
	});
 | 
			
		||||
	var result = { privateKeyPem: keypair.privateKey.trim() };
 | 
			
		||||
	return result;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if (require.main === module) {
 | 
			
		||||
	var keypair = module.exports(2048, 0x10001);
 | 
			
		||||
	console.info(keypair.privateKeyPem);
 | 
			
		||||
}
 | 
			
		||||
@ -1,27 +0,0 @@
 | 
			
		||||
// Copyright 2016-2018 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/. */
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
module.exports = function(bitlen, exp) {
 | 
			
		||||
	var ursa;
 | 
			
		||||
	try {
 | 
			
		||||
		ursa = require('ursa');
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
		ursa = require('ursa-optional');
 | 
			
		||||
	}
 | 
			
		||||
	var keypair = ursa.generatePrivateKey(bitlen, exp);
 | 
			
		||||
	var result = {
 | 
			
		||||
		privateKeyPem: keypair
 | 
			
		||||
			.toPrivatePem()
 | 
			
		||||
			.toString('ascii')
 | 
			
		||||
			.trim()
 | 
			
		||||
	};
 | 
			
		||||
	return result;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if (require.main === module) {
 | 
			
		||||
	var keypair = module.exports(2048, 0x10001);
 | 
			
		||||
	console.info(keypair.privateKeyPem);
 | 
			
		||||
}
 | 
			
		||||
@ -1,90 +0,0 @@
 | 
			
		||||
// Copyright 2016-2018 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/. */
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var oldver = false;
 | 
			
		||||
 | 
			
		||||
module.exports = function(bitlen, exp) {
 | 
			
		||||
	bitlen = parseInt(bitlen, 10) || 2048;
 | 
			
		||||
	exp = parseInt(exp, 10) || 65537;
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		return require('./generate-privkey-node.js')(bitlen, exp);
 | 
			
		||||
	} catch (e) {
 | 
			
		||||
		if (!/generateKeyPairSync is not a function/.test(e.message)) {
 | 
			
		||||
			throw e;
 | 
			
		||||
		}
 | 
			
		||||
		try {
 | 
			
		||||
			return require('./generate-privkey-ursa.js')(bitlen, exp);
 | 
			
		||||
		} catch (e) {
 | 
			
		||||
			if (e.code !== 'MODULE_NOT_FOUND') {
 | 
			
		||||
				console.error(
 | 
			
		||||
					"[rsa-compat] Unexpected error when using 'ursa':"
 | 
			
		||||
				);
 | 
			
		||||
				console.error(e);
 | 
			
		||||
			}
 | 
			
		||||
			if (!oldver) {
 | 
			
		||||
				oldver = true;
 | 
			
		||||
				console.warn(
 | 
			
		||||
					'[WARN] rsa-compat: Your version of node does not have crypto.generateKeyPair()'
 | 
			
		||||
				);
 | 
			
		||||
				console.warn(
 | 
			
		||||
					"[WARN] rsa-compat: Please update to node >= v10.12 or 'npm install --save ursa node-forge'"
 | 
			
		||||
				);
 | 
			
		||||
				console.warn(
 | 
			
		||||
					'[WARN] rsa-compat: Using node-forge as a fallback may be unacceptably slow.'
 | 
			
		||||
				);
 | 
			
		||||
				if (/arm|mips/i.test(require('os').arch)) {
 | 
			
		||||
					console.warn(
 | 
			
		||||
						'================================================================'
 | 
			
		||||
					);
 | 
			
		||||
					console.warn('                         WARNING');
 | 
			
		||||
					console.warn(
 | 
			
		||||
						'================================================================'
 | 
			
		||||
					);
 | 
			
		||||
					console.warn('');
 | 
			
		||||
					console.warn(
 | 
			
		||||
						'WARNING: You are generating an RSA key using pure JavaScript on'
 | 
			
		||||
					);
 | 
			
		||||
					console.warn(
 | 
			
		||||
						'         a VERY SLOW cpu. This could take DOZENS of minutes!'
 | 
			
		||||
					);
 | 
			
		||||
					console.warn('');
 | 
			
		||||
					console.warn(
 | 
			
		||||
						"         We recommend installing node >= v10.12, or 'gcc' and 'ursa'"
 | 
			
		||||
					);
 | 
			
		||||
					console.warn('');
 | 
			
		||||
					console.warn('EXAMPLE:');
 | 
			
		||||
					console.warn('');
 | 
			
		||||
					console.warn(
 | 
			
		||||
						'        sudo apt-get install build-essential && npm install ursa'
 | 
			
		||||
					);
 | 
			
		||||
					console.warn('');
 | 
			
		||||
					console.warn(
 | 
			
		||||
						'================================================================'
 | 
			
		||||
					);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			try {
 | 
			
		||||
				return require('./generate-privkey-forge.js')(bitlen, exp);
 | 
			
		||||
			} catch (e) {
 | 
			
		||||
				if (e.code !== 'MODULE_NOT_FOUND') {
 | 
			
		||||
					throw e;
 | 
			
		||||
				}
 | 
			
		||||
				console.error(
 | 
			
		||||
					'[ERROR] rsa-compat: could not generate a private key.'
 | 
			
		||||
				);
 | 
			
		||||
				console.error(
 | 
			
		||||
					'None of crypto.generateKeyPair, ursa, nor node-forge are present'
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
if (require.main === module) {
 | 
			
		||||
	var keypair = module.exports(2048, 0x10001);
 | 
			
		||||
	console.info(keypair.privateKeyPem);
 | 
			
		||||
}
 | 
			
		||||
@ -1,84 +0,0 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var Keypairs = module.exports;
 | 
			
		||||
var crypto = require('crypto');
 | 
			
		||||
 | 
			
		||||
Keypairs._sign = function(opts, payload) {
 | 
			
		||||
	return Keypairs._import(opts).then(function(pem) {
 | 
			
		||||
		payload = Buffer.from(payload);
 | 
			
		||||
 | 
			
		||||
		// node specifies RSA-SHAxxx even when it's actually ecdsa (it's all encoded x509 shasums anyway)
 | 
			
		||||
		// TODO opts.alg = (protect||header).alg
 | 
			
		||||
		var nodeAlg = 'SHA' + Keypairs._getBits(opts);
 | 
			
		||||
		var binsig = crypto
 | 
			
		||||
			.createSign(nodeAlg)
 | 
			
		||||
			.update(payload)
 | 
			
		||||
			.sign(pem);
 | 
			
		||||
 | 
			
		||||
		if ('EC' === opts.jwk.kty && !/x509|asn1/i.test(opts.format)) {
 | 
			
		||||
			// ECDSA JWT signatures differ from "normal" ECDSA signatures
 | 
			
		||||
			// https://tools.ietf.org/html/rfc7518#section-3.4
 | 
			
		||||
			binsig = Keypairs._ecdsaAsn1SigToJoseSig(binsig);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return binsig;
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Keypairs._import = function(opts) {
 | 
			
		||||
	if (opts.pem && opts.jwk) {
 | 
			
		||||
		return Promise.resolve(opts.pem);
 | 
			
		||||
	} else {
 | 
			
		||||
		// XXX added by caller
 | 
			
		||||
		return Keypairs.export({ jwk: opts.jwk });
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Keypairs._ecdsaAsn1SigToJoseSig = function(binsig) {
 | 
			
		||||
	// should have asn1 sequence header of 0x30
 | 
			
		||||
	if (0x30 !== binsig[0]) {
 | 
			
		||||
		throw new Error('Impossible EC SHA head marker');
 | 
			
		||||
	}
 | 
			
		||||
	var index = 2; // first ecdsa "R" header byte
 | 
			
		||||
	var len = binsig[1];
 | 
			
		||||
	var lenlen = 0;
 | 
			
		||||
	// Seek length of length if length is greater than 127 (i.e. two 512-bit / 64-byte R and S values)
 | 
			
		||||
	if (0x80 & len) {
 | 
			
		||||
		lenlen = len - 0x80; // should be exactly 1
 | 
			
		||||
		len = binsig[2]; // should be <= 130 (two 64-bit SHA-512s, plus padding)
 | 
			
		||||
		index += lenlen;
 | 
			
		||||
	}
 | 
			
		||||
	// should be of BigInt type
 | 
			
		||||
	if (0x02 !== binsig[index]) {
 | 
			
		||||
		throw new Error('Impossible EC SHA R marker');
 | 
			
		||||
	}
 | 
			
		||||
	index += 1;
 | 
			
		||||
 | 
			
		||||
	var rlen = binsig[index];
 | 
			
		||||
	var bits = 32;
 | 
			
		||||
	if (rlen > 49) {
 | 
			
		||||
		bits = 64;
 | 
			
		||||
	} else if (rlen > 33) {
 | 
			
		||||
		bits = 48;
 | 
			
		||||
	}
 | 
			
		||||
	var r = binsig.slice(index + 1, index + 1 + rlen).toString('hex');
 | 
			
		||||
	var slen = binsig[index + 1 + rlen + 1]; // skip header and read length
 | 
			
		||||
	var s = binsig.slice(index + 1 + rlen + 1 + 1).toString('hex');
 | 
			
		||||
	if (2 * slen !== s.length) {
 | 
			
		||||
		throw new Error('Impossible EC SHA S length');
 | 
			
		||||
	}
 | 
			
		||||
	// There may be one byte of padding on either
 | 
			
		||||
	while (r.length < 2 * bits) {
 | 
			
		||||
		r = '00' + r;
 | 
			
		||||
	}
 | 
			
		||||
	while (s.length < 2 * bits) {
 | 
			
		||||
		s = '00' + s;
 | 
			
		||||
	}
 | 
			
		||||
	if (2 * (bits + 1) === r.length) {
 | 
			
		||||
		r = r.slice(2);
 | 
			
		||||
	}
 | 
			
		||||
	if (2 * (bits + 1) === s.length) {
 | 
			
		||||
		s = s.slice(2);
 | 
			
		||||
	}
 | 
			
		||||
	return Buffer.concat([Buffer.from(r, 'hex'), Buffer.from(s, 'hex')]);
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										154
									
								
								lib/node/rsa.js
									
									
									
									
									
								
							
							
						
						
									
										154
									
								
								lib/node/rsa.js
									
									
									
									
									
								
							@ -1,154 +0,0 @@
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var native = module.exports;
 | 
			
		||||
 | 
			
		||||
// XXX provided by caller: export
 | 
			
		||||
var RSA = native;
 | 
			
		||||
var PEM = require('../../pem.js');
 | 
			
		||||
var x509 = require('../../x509.js');
 | 
			
		||||
var ASN1 = require('../../asn1/parser.js');
 | 
			
		||||
 | 
			
		||||
native.generate = function(opts) {
 | 
			
		||||
	opts.kty = 'RSA';
 | 
			
		||||
	return native._generate(opts).then(function(pair) {
 | 
			
		||||
		var format = opts.format;
 | 
			
		||||
		var encoding = opts.encoding;
 | 
			
		||||
 | 
			
		||||
		// The easy way
 | 
			
		||||
		if ('json' === format && !encoding) {
 | 
			
		||||
			format = 'jwk';
 | 
			
		||||
			encoding = 'json';
 | 
			
		||||
		}
 | 
			
		||||
		if (
 | 
			
		||||
			('jwk' === format || !format) &&
 | 
			
		||||
			('json' === encoding || !encoding)
 | 
			
		||||
		) {
 | 
			
		||||
			return pair;
 | 
			
		||||
		}
 | 
			
		||||
		if ('jwk' === format || 'json' === encoding) {
 | 
			
		||||
			throw new Error(
 | 
			
		||||
				"format '" +
 | 
			
		||||
					format +
 | 
			
		||||
					"' is incompatible with encoding '" +
 | 
			
		||||
					encoding +
 | 
			
		||||
					"'"
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// The... less easy way
 | 
			
		||||
		/*
 | 
			
		||||
    var priv;
 | 
			
		||||
    var pub;
 | 
			
		||||
 | 
			
		||||
    if ('spki' === format || 'pkcs8' === format) {
 | 
			
		||||
      format = 'pkcs8';
 | 
			
		||||
      pub = 'spki';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if ('pem' === format) {
 | 
			
		||||
      format = 'pkcs1';
 | 
			
		||||
      encoding = 'pem';
 | 
			
		||||
    } else if ('der' === format) {
 | 
			
		||||
      format = 'pkcs1';
 | 
			
		||||
      encoding = 'der';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    priv = format;
 | 
			
		||||
    pub = pub || format;
 | 
			
		||||
 | 
			
		||||
    if (!encoding) {
 | 
			
		||||
      encoding = 'pem';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (priv) {
 | 
			
		||||
      priv = { type: priv, format: encoding };
 | 
			
		||||
      pub = { type: pub, format: encoding };
 | 
			
		||||
    } else {
 | 
			
		||||
      // jwk
 | 
			
		||||
      priv = { type: 'pkcs1', format: 'pem' };
 | 
			
		||||
      pub = { type: 'pkcs1', format: 'pem' };
 | 
			
		||||
    }
 | 
			
		||||
    */
 | 
			
		||||
		if (('pem' === format || 'der' === format) && !encoding) {
 | 
			
		||||
			encoding = format;
 | 
			
		||||
			format = 'pkcs1';
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		var exOpts = { jwk: pair.private, format: format, encoding: encoding };
 | 
			
		||||
		return RSA.export(exOpts).then(function(priv) {
 | 
			
		||||
			exOpts.public = true;
 | 
			
		||||
			if ('pkcs8' === exOpts.format) {
 | 
			
		||||
				exOpts.format = 'spki';
 | 
			
		||||
			}
 | 
			
		||||
			return RSA.export(exOpts).then(function(pub) {
 | 
			
		||||
				return { private: priv, public: pub };
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
native._generate = function(opts) {
 | 
			
		||||
	if (!opts) {
 | 
			
		||||
		opts = {};
 | 
			
		||||
	}
 | 
			
		||||
	return new Promise(function(resolve, reject) {
 | 
			
		||||
		try {
 | 
			
		||||
			var modlen = opts.modulusLength || 2048;
 | 
			
		||||
			var exp = opts.publicExponent || 0x10001;
 | 
			
		||||
			var pair = require('./generate-privkey.js')(modlen, exp);
 | 
			
		||||
			if (pair.private) {
 | 
			
		||||
				resolve(pair);
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
			pair = toJwks(pair);
 | 
			
		||||
			resolve({ private: pair.private, public: pair.public });
 | 
			
		||||
		} catch (e) {
 | 
			
		||||
			reject(e);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// PKCS1 to JWK only
 | 
			
		||||
function toJwks(oldpair) {
 | 
			
		||||
	var block = PEM.parseBlock(oldpair.privateKeyPem);
 | 
			
		||||
	var asn1 = ASN1.parse(block.bytes);
 | 
			
		||||
	var jwk = { kty: 'RSA', n: null, e: null };
 | 
			
		||||
	jwk = x509.parsePkcs1(block.bytes, asn1, jwk);
 | 
			
		||||
	return { private: jwk, public: RSA.neuter({ jwk: jwk }) };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TODO
 | 
			
		||||
var Enc = require('@root/encoding/base64');
 | 
			
		||||
x509.parsePkcs1 = function parseRsaPkcs1(buf, asn1, jwk) {
 | 
			
		||||
	if (
 | 
			
		||||
		!asn1.children.every(function(el) {
 | 
			
		||||
			return 0x02 === el.type;
 | 
			
		||||
		})
 | 
			
		||||
	) {
 | 
			
		||||
		throw new Error(
 | 
			
		||||
			'not an RSA PKCS#1 public or private key (not all ints)'
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (2 === asn1.children.length) {
 | 
			
		||||
		jwk.n = Enc.bufToUrlBase64(asn1.children[0].value);
 | 
			
		||||
		jwk.e = Enc.bufToUrlBase64(asn1.children[1].value);
 | 
			
		||||
		return jwk;
 | 
			
		||||
	} else if (asn1.children.length >= 9) {
 | 
			
		||||
		// the standard allows for "otherPrimeInfos", hence at least 9
 | 
			
		||||
 | 
			
		||||
		jwk.n = Enc.bufToUrlBase64(asn1.children[1].value);
 | 
			
		||||
		jwk.e = Enc.bufToUrlBase64(asn1.children[2].value);
 | 
			
		||||
		jwk.d = Enc.bufToUrlBase64(asn1.children[3].value);
 | 
			
		||||
		jwk.p = Enc.bufToUrlBase64(asn1.children[4].value);
 | 
			
		||||
		jwk.q = Enc.bufToUrlBase64(asn1.children[5].value);
 | 
			
		||||
		jwk.dp = Enc.bufToUrlBase64(asn1.children[6].value);
 | 
			
		||||
		jwk.dq = Enc.bufToUrlBase64(asn1.children[7].value);
 | 
			
		||||
		jwk.qi = Enc.bufToUrlBase64(asn1.children[8].value);
 | 
			
		||||
		return jwk;
 | 
			
		||||
	} else {
 | 
			
		||||
		throw new Error(
 | 
			
		||||
			'not an RSA PKCS#1 public or private key (wrong number of ints)'
 | 
			
		||||
		);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										4844
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										4844
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
							
								
								
									
										11
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								package.json
									
									
									
									
									
								
							@ -40,15 +40,20 @@
 | 
			
		||||
	"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
 | 
			
		||||
	"license": "MPL-2.0",
 | 
			
		||||
	"dependencies": {
 | 
			
		||||
		"@root/csr": "^1.0.0-wip.0",
 | 
			
		||||
		"@root/csr": "^0.8.0",
 | 
			
		||||
		"@root/encoding": "^1.0.1",
 | 
			
		||||
		"@root/keypairs": "^1.0.0-wip.0"
 | 
			
		||||
		"@root/keypairs": "^0.9.0",
 | 
			
		||||
		"@root/pem": "^1.0.4",
 | 
			
		||||
		"@root/x509": "^0.7.2"
 | 
			
		||||
	},
 | 
			
		||||
	"devDependencies": {
 | 
			
		||||
		"@root/request": "^1.3.10",
 | 
			
		||||
		"dig.js": "^1.3.9",
 | 
			
		||||
		"dns-suite": "^1.2.12",
 | 
			
		||||
		"dotenv": "^8.1.0",
 | 
			
		||||
		"punycode": "^1.4.1"
 | 
			
		||||
		"eslint": "^6.5.1",
 | 
			
		||||
		"punycode": "^1.4.1",
 | 
			
		||||
		"webpack": "^4.41.0",
 | 
			
		||||
		"webpack-cli": "^3.3.9"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										158
									
								
								rsa.js
									
									
									
									
									
								
							
							
						
						
									
										158
									
								
								rsa.js
									
									
									
									
									
								
							@ -1,158 +0,0 @@
 | 
			
		||||
/*global Promise*/
 | 
			
		||||
'use strict';
 | 
			
		||||
 | 
			
		||||
var RSA = module.exports;
 | 
			
		||||
var native = require('./lib/node/rsa.js');
 | 
			
		||||
var x509 = require('./x509.js');
 | 
			
		||||
var PEM = require('./pem.js');
 | 
			
		||||
//var SSH = require('./ssh-keys.js');
 | 
			
		||||
var sha2 = require('./lib/node/sha2.js');
 | 
			
		||||
var Enc = require('@root/encoding/base64');
 | 
			
		||||
 | 
			
		||||
RSA._universal =
 | 
			
		||||
	'Bluecrypt only supports crypto with standard cross-browser and cross-platform support.';
 | 
			
		||||
RSA._stance =
 | 
			
		||||
	"We take the stance that if you're knowledgeable enough to" +
 | 
			
		||||
	" properly and securely use non-standard crypto then you shouldn't need Bluecrypt anyway.";
 | 
			
		||||
native._stance = RSA._stance;
 | 
			
		||||
 | 
			
		||||
RSA.generate = native.generate;
 | 
			
		||||
 | 
			
		||||
// Chopping off the private parts is now part of the public API.
 | 
			
		||||
// I thought it sounded a little too crude at first, but it really is the best name in every possible way.
 | 
			
		||||
RSA.neuter = function(opts) {
 | 
			
		||||
	// trying to find the best balance of an immutable copy with custom attributes
 | 
			
		||||
	var jwk = {};
 | 
			
		||||
	Object.keys(opts.jwk).forEach(function(k) {
 | 
			
		||||
		if ('undefined' === typeof opts.jwk[k]) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		// ignore RSA private parts
 | 
			
		||||
		if (-1 !== ['d', 'p', 'q', 'dp', 'dq', 'qi'].indexOf(k)) {
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		jwk[k] = JSON.parse(JSON.stringify(opts.jwk[k]));
 | 
			
		||||
	});
 | 
			
		||||
	return jwk;
 | 
			
		||||
};
 | 
			
		||||
native.neuter = RSA.neuter;
 | 
			
		||||
 | 
			
		||||
// https://stackoverflow.com/questions/42588786/how-to-fingerprint-a-jwk
 | 
			
		||||
RSA.__thumbprint = function(jwk) {
 | 
			
		||||
	// Use the same entropy for SHA as for key
 | 
			
		||||
	var len = Math.floor(jwk.n.length * 0.75);
 | 
			
		||||
	var alg = 'SHA-256';
 | 
			
		||||
	// TODO this may be a bug
 | 
			
		||||
	// need to confirm that the padding is no more or less than 1 byte
 | 
			
		||||
	if (len >= 511) {
 | 
			
		||||
		alg = 'SHA-512';
 | 
			
		||||
	} else if (len >= 383) {
 | 
			
		||||
		alg = 'SHA-384';
 | 
			
		||||
	}
 | 
			
		||||
	return sha2
 | 
			
		||||
		.sum(alg, '{"e":"' + jwk.e + '","kty":"RSA","n":"' + jwk.n + '"}')
 | 
			
		||||
		.then(function(hash) {
 | 
			
		||||
			return Enc.bufToUrlBase64(Uint8Array.from(hash));
 | 
			
		||||
		});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
RSA.thumbprint = function(opts) {
 | 
			
		||||
	return Promise.resolve().then(function() {
 | 
			
		||||
		var jwk;
 | 
			
		||||
		if ('EC' === opts.kty) {
 | 
			
		||||
			jwk = opts;
 | 
			
		||||
		} else if (opts.jwk) {
 | 
			
		||||
			jwk = opts.jwk;
 | 
			
		||||
		} else {
 | 
			
		||||
			return RSA.import(opts).then(function(jwk) {
 | 
			
		||||
				return RSA.__thumbprint(jwk);
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
		return RSA.__thumbprint(jwk);
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
RSA.export = function(opts) {
 | 
			
		||||
	return Promise.resolve().then(function() {
 | 
			
		||||
		if (!opts || !opts.jwk || 'object' !== typeof opts.jwk) {
 | 
			
		||||
			throw new Error('must pass { jwk: jwk }');
 | 
			
		||||
		}
 | 
			
		||||
		var jwk = JSON.parse(JSON.stringify(opts.jwk));
 | 
			
		||||
		var format = opts.format;
 | 
			
		||||
		var pub = opts.public;
 | 
			
		||||
		if (pub || -1 !== ['spki', 'pkix', 'ssh', 'rfc4716'].indexOf(format)) {
 | 
			
		||||
			jwk = RSA.neuter({ jwk: jwk });
 | 
			
		||||
		}
 | 
			
		||||
		if ('RSA' !== jwk.kty) {
 | 
			
		||||
			throw new Error("options.jwk.kty must be 'RSA' for RSA keys");
 | 
			
		||||
		}
 | 
			
		||||
		if (!jwk.p) {
 | 
			
		||||
			// TODO test for n and e
 | 
			
		||||
			pub = true;
 | 
			
		||||
			if (!format || 'pkcs1' === format) {
 | 
			
		||||
				format = 'pkcs1';
 | 
			
		||||
			} else if (-1 !== ['spki', 'pkix'].indexOf(format)) {
 | 
			
		||||
				format = 'spki';
 | 
			
		||||
			} else if (-1 !== ['ssh', 'rfc4716'].indexOf(format)) {
 | 
			
		||||
				format = 'ssh';
 | 
			
		||||
			} else {
 | 
			
		||||
				throw new Error(
 | 
			
		||||
					"options.format must be 'spki', 'pkcs1', or 'ssh' for public RSA keys, not (" +
 | 
			
		||||
						typeof format +
 | 
			
		||||
						') ' +
 | 
			
		||||
						format
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// TODO test for all necessary keys (d, p, q ...)
 | 
			
		||||
			if (!format || 'pkcs1' === format) {
 | 
			
		||||
				format = 'pkcs1';
 | 
			
		||||
			} else if ('pkcs8' !== format) {
 | 
			
		||||
				throw new Error(
 | 
			
		||||
					"options.format must be 'pkcs1' or 'pkcs8' for private RSA keys"
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if ('pkcs1' === format) {
 | 
			
		||||
			if (jwk.d) {
 | 
			
		||||
				return PEM.packBlock({
 | 
			
		||||
					type: 'RSA PRIVATE KEY',
 | 
			
		||||
					bytes: x509.packPkcs1(jwk)
 | 
			
		||||
				});
 | 
			
		||||
			} else {
 | 
			
		||||
				return PEM.packBlock({
 | 
			
		||||
					type: 'RSA PUBLIC KEY',
 | 
			
		||||
					bytes: x509.packPkcs1(jwk)
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		} else if ('pkcs8' === format) {
 | 
			
		||||
			return PEM.packBlock({
 | 
			
		||||
				type: 'PRIVATE KEY',
 | 
			
		||||
				bytes: x509.packPkcs8(jwk)
 | 
			
		||||
			});
 | 
			
		||||
		} else if (-1 !== ['spki', 'pkix'].indexOf(format)) {
 | 
			
		||||
			return PEM.packBlock({
 | 
			
		||||
				type: 'PUBLIC KEY',
 | 
			
		||||
				bytes: x509.packSpki(jwk)
 | 
			
		||||
			});
 | 
			
		||||
		} else if (-1 !== ['ssh', 'rfc4716'].indexOf(format)) {
 | 
			
		||||
			return SSH.pack({ jwk: jwk, comment: opts.comment });
 | 
			
		||||
		} else {
 | 
			
		||||
			throw new Error(
 | 
			
		||||
				'Sanity Error: reached unreachable code block with format: ' +
 | 
			
		||||
					format
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
native.export = RSA.export;
 | 
			
		||||
 | 
			
		||||
RSA.pack = function(opts) {
 | 
			
		||||
	// wrapped in a promise for API compatibility
 | 
			
		||||
	// with the forthcoming browser version
 | 
			
		||||
	// (and potential future native node capability)
 | 
			
		||||
	return Promise.resolve().then(function() {
 | 
			
		||||
		return RSA.export(opts);
 | 
			
		||||
	});
 | 
			
		||||
};
 | 
			
		||||
@ -4,10 +4,10 @@ require('dotenv').config();
 | 
			
		||||
 | 
			
		||||
var CSR = require('@root/csr');
 | 
			
		||||
var Enc = require('@root/encoding/base64');
 | 
			
		||||
var PEM = require('../pem.js');
 | 
			
		||||
var PEM = require('@root/pem');
 | 
			
		||||
var punycode = require('punycode');
 | 
			
		||||
var ACME = require('../acme.js');
 | 
			
		||||
var Keypairs = require('../keypairs.js');
 | 
			
		||||
var Keypairs = require('@root/keypairs');
 | 
			
		||||
var acme = ACME.create({
 | 
			
		||||
	// debug: true
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user