forked from root/acme.js
		
	make Prettier
This commit is contained in:
		
							parent
							
								
									87a12c36b0
								
							
						
					
					
						commit
						6c11446e2f
					
				
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@ -1,3 +1,4 @@
 | 
			
		||||
dist/
 | 
			
		||||
*.gz
 | 
			
		||||
.*.sw*
 | 
			
		||||
.ignore
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										8
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								.prettierrc
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
  "bracketSpacing": true,
 | 
			
		||||
  "printWidth": 80,
 | 
			
		||||
  "singleQuote": true,
 | 
			
		||||
  "tabWidth": 4,
 | 
			
		||||
  "trailingComma": "none",
 | 
			
		||||
  "useTabs": true
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										60
									
								
								bin/bundle.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								bin/bundle.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
			
		||||
#!/usr/bin/env node
 | 
			
		||||
(async function() {
 | 
			
		||||
	'use strict';
 | 
			
		||||
 | 
			
		||||
	var UglifyJS = require('uglify-js');
 | 
			
		||||
	var path = require('path');
 | 
			
		||||
	var fs = require('fs');
 | 
			
		||||
	var promisify = require('util').promisify;
 | 
			
		||||
	var readFile = promisify(fs.readFile);
 | 
			
		||||
	var writeFile = promisify(fs.writeFile);
 | 
			
		||||
	var gzip = promisify(require('zlib').gzip);
 | 
			
		||||
 | 
			
		||||
	// The order is specific, and it matters
 | 
			
		||||
	var files = await Promise.all(
 | 
			
		||||
		[
 | 
			
		||||
			'../lib/encoding.js',
 | 
			
		||||
			'../lib/asn1-packer.js',
 | 
			
		||||
			'../lib/x509.js',
 | 
			
		||||
			'../lib/ecdsa.js',
 | 
			
		||||
			'../lib/rsa.js',
 | 
			
		||||
			'../lib/keypairs.js',
 | 
			
		||||
			'../lib/asn1-parser.js',
 | 
			
		||||
			'../lib/csr.js',
 | 
			
		||||
			'../lib/acme.js'
 | 
			
		||||
		].map(async function(file) {
 | 
			
		||||
			return (await readFile(path.join(__dirname, file), 'utf8')).trim();
 | 
			
		||||
		})
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	var header =
 | 
			
		||||
		[
 | 
			
		||||
			'// Copyright 2015-2019 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/. */'
 | 
			
		||||
		].join('\n') + '\n';
 | 
			
		||||
 | 
			
		||||
	var file = header + files.join('\n') + '\n';
 | 
			
		||||
	await writeFile(path.join(__dirname, '../dist', 'acme.js'), file);
 | 
			
		||||
	await writeFile(
 | 
			
		||||
		path.join(__dirname, '../dist', 'acme.js.gz'),
 | 
			
		||||
		await gzip(file)
 | 
			
		||||
	);
 | 
			
		||||
 | 
			
		||||
	// TODO source maps?
 | 
			
		||||
	var result = UglifyJS.minify(file, {
 | 
			
		||||
		compress: true,
 | 
			
		||||
		// mangling doesn't save significant
 | 
			
		||||
		mangle: false
 | 
			
		||||
	});
 | 
			
		||||
	if (result.error) {
 | 
			
		||||
		throw result.error;
 | 
			
		||||
	}
 | 
			
		||||
	file = header + result.code;
 | 
			
		||||
	await writeFile(path.join(__dirname, '../dist', 'acme.min.js'), file);
 | 
			
		||||
	await writeFile(
 | 
			
		||||
		path.join(__dirname, '../dist', 'acme.min.js.gz'),
 | 
			
		||||
		await gzip(file)
 | 
			
		||||
	);
 | 
			
		||||
})();
 | 
			
		||||
							
								
								
									
										2324
									
								
								lib/acme.js
									
									
									
									
									
								
							
							
						
						
									
										2324
									
								
								lib/acme.js
									
									
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,127 +1,147 @@
 | 
			
		||||
;(function (exports) {
 | 
			
		||||
'use strict';
 | 
			
		||||
(function(exports) {
 | 
			
		||||
	'use strict';
 | 
			
		||||
 | 
			
		||||
if (!exports.ASN1) { exports.ASN1 = {}; }
 | 
			
		||||
if (!exports.Enc) { exports.Enc = {}; }
 | 
			
		||||
if (!exports.PEM) { exports.PEM = {}; }
 | 
			
		||||
	if (!exports.ASN1) {
 | 
			
		||||
		exports.ASN1 = {};
 | 
			
		||||
	}
 | 
			
		||||
	if (!exports.Enc) {
 | 
			
		||||
		exports.Enc = {};
 | 
			
		||||
	}
 | 
			
		||||
	if (!exports.PEM) {
 | 
			
		||||
		exports.PEM = {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
var ASN1 = exports.ASN1;
 | 
			
		||||
var Enc = exports.Enc;
 | 
			
		||||
var PEM = exports.PEM;
 | 
			
		||||
	var ASN1 = exports.ASN1;
 | 
			
		||||
	var Enc = exports.Enc;
 | 
			
		||||
	var PEM = exports.PEM;
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Packer
 | 
			
		||||
//
 | 
			
		||||
	//
 | 
			
		||||
	// Packer
 | 
			
		||||
	//
 | 
			
		||||
 | 
			
		||||
// Almost every ASN.1 type that's important for CSR
 | 
			
		||||
// can be represented generically with only a few rules.
 | 
			
		||||
exports.ASN1 = function ASN1(/*type, hexstrings...*/) {
 | 
			
		||||
  var args = Array.prototype.slice.call(arguments);
 | 
			
		||||
  var typ = args.shift();
 | 
			
		||||
  var str = args.join('').replace(/\s+/g, '').toLowerCase();
 | 
			
		||||
  var len = (str.length/2);
 | 
			
		||||
  var lenlen = 0;
 | 
			
		||||
  var hex = typ;
 | 
			
		||||
	// Almost every ASN.1 type that's important for CSR
 | 
			
		||||
	// can be represented generically with only a few rules.
 | 
			
		||||
	exports.ASN1 = function ASN1(/*type, hexstrings...*/) {
 | 
			
		||||
		var args = Array.prototype.slice.call(arguments);
 | 
			
		||||
		var typ = args.shift();
 | 
			
		||||
		var str = args
 | 
			
		||||
			.join('')
 | 
			
		||||
			.replace(/\s+/g, '')
 | 
			
		||||
			.toLowerCase();
 | 
			
		||||
		var len = str.length / 2;
 | 
			
		||||
		var lenlen = 0;
 | 
			
		||||
		var hex = typ;
 | 
			
		||||
 | 
			
		||||
  // We can't have an odd number of hex chars
 | 
			
		||||
  if (len !== Math.round(len)) {
 | 
			
		||||
    throw new Error("invalid hex");
 | 
			
		||||
  }
 | 
			
		||||
		// We can't have an odd number of hex chars
 | 
			
		||||
		if (len !== Math.round(len)) {
 | 
			
		||||
			throw new Error('invalid hex');
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
  // The first byte of any ASN.1 sequence is the type (Sequence, Integer, etc)
 | 
			
		||||
  // The second byte is either the size of the value, or the size of its size
 | 
			
		||||
		// The first byte of any ASN.1 sequence is the type (Sequence, Integer, etc)
 | 
			
		||||
		// The second byte is either the size of the value, or the size of its size
 | 
			
		||||
 | 
			
		||||
  // 1. If the second byte is < 0x80 (128) it is considered the size
 | 
			
		||||
  // 2. If it is > 0x80 then it describes the number of bytes of the size
 | 
			
		||||
  //    ex: 0x82 means the next 2 bytes describe the size of the value
 | 
			
		||||
  // 3. The special case of exactly 0x80 is "indefinite" length (to end-of-file)
 | 
			
		||||
		// 1. If the second byte is < 0x80 (128) it is considered the size
 | 
			
		||||
		// 2. If it is > 0x80 then it describes the number of bytes of the size
 | 
			
		||||
		//    ex: 0x82 means the next 2 bytes describe the size of the value
 | 
			
		||||
		// 3. The special case of exactly 0x80 is "indefinite" length (to end-of-file)
 | 
			
		||||
 | 
			
		||||
  if (len > 127) {
 | 
			
		||||
    lenlen += 1;
 | 
			
		||||
    while (len > 255) {
 | 
			
		||||
      lenlen += 1;
 | 
			
		||||
      len = len >> 8;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
		if (len > 127) {
 | 
			
		||||
			lenlen += 1;
 | 
			
		||||
			while (len > 255) {
 | 
			
		||||
				lenlen += 1;
 | 
			
		||||
				len = len >> 8;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
  if (lenlen) { hex += Enc.numToHex(0x80 + lenlen); }
 | 
			
		||||
  return hex + Enc.numToHex(str.length/2) + str;
 | 
			
		||||
};
 | 
			
		||||
		if (lenlen) {
 | 
			
		||||
			hex += Enc.numToHex(0x80 + lenlen);
 | 
			
		||||
		}
 | 
			
		||||
		return hex + Enc.numToHex(str.length / 2) + str;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
// The Integer type has some special rules
 | 
			
		||||
ASN1.UInt = function UINT() {
 | 
			
		||||
  var str = Array.prototype.slice.call(arguments).join('');
 | 
			
		||||
  var first = parseInt(str.slice(0, 2), 16);
 | 
			
		||||
	// The Integer type has some special rules
 | 
			
		||||
	ASN1.UInt = function UINT() {
 | 
			
		||||
		var str = Array.prototype.slice.call(arguments).join('');
 | 
			
		||||
		var first = parseInt(str.slice(0, 2), 16);
 | 
			
		||||
 | 
			
		||||
  // If the first byte is 0x80 or greater, the number is considered negative
 | 
			
		||||
  // Therefore we add a '00' prefix if the 0x80 bit is set
 | 
			
		||||
  if (0x80 & first) { str = '00' + str; }
 | 
			
		||||
		// If the first byte is 0x80 or greater, the number is considered negative
 | 
			
		||||
		// Therefore we add a '00' prefix if the 0x80 bit is set
 | 
			
		||||
		if (0x80 & first) {
 | 
			
		||||
			str = '00' + str;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
  return ASN1('02', str);
 | 
			
		||||
};
 | 
			
		||||
		return ASN1('02', str);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
// The Bit String type also has a special rule
 | 
			
		||||
ASN1.BitStr = function BITSTR() {
 | 
			
		||||
  var str = Array.prototype.slice.call(arguments).join('');
 | 
			
		||||
  // '00' is a mask of how many bits of the next byte to ignore
 | 
			
		||||
  return ASN1('03', '00' + str);
 | 
			
		||||
};
 | 
			
		||||
	// The Bit String type also has a special rule
 | 
			
		||||
	ASN1.BitStr = function BITSTR() {
 | 
			
		||||
		var str = Array.prototype.slice.call(arguments).join('');
 | 
			
		||||
		// '00' is a mask of how many bits of the next byte to ignore
 | 
			
		||||
		return ASN1('03', '00' + str);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
ASN1.pack = function (arr) {
 | 
			
		||||
  var typ = Enc.numToHex(arr[0]);
 | 
			
		||||
  var str = '';
 | 
			
		||||
  if (Array.isArray(arr[1])) {
 | 
			
		||||
    arr[1].forEach(function (a) {
 | 
			
		||||
      str += ASN1.pack(a);
 | 
			
		||||
    });
 | 
			
		||||
  } else if ('string' === typeof arr[1]) {
 | 
			
		||||
    str = arr[1];
 | 
			
		||||
  } else {
 | 
			
		||||
    throw new Error("unexpected array");
 | 
			
		||||
  }
 | 
			
		||||
  if ('03' === typ) {
 | 
			
		||||
    return ASN1.BitStr(str);
 | 
			
		||||
  } else if ('02' === typ) {
 | 
			
		||||
    return ASN1.UInt(str);
 | 
			
		||||
  } else {
 | 
			
		||||
    return ASN1(typ, str);
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
Object.keys(ASN1).forEach(function (k) {
 | 
			
		||||
  exports.ASN1[k] = ASN1[k];
 | 
			
		||||
});
 | 
			
		||||
ASN1 = exports.ASN1;
 | 
			
		||||
	ASN1.pack = function(arr) {
 | 
			
		||||
		var typ = Enc.numToHex(arr[0]);
 | 
			
		||||
		var str = '';
 | 
			
		||||
		if (Array.isArray(arr[1])) {
 | 
			
		||||
			arr[1].forEach(function(a) {
 | 
			
		||||
				str += ASN1.pack(a);
 | 
			
		||||
			});
 | 
			
		||||
		} else if ('string' === typeof arr[1]) {
 | 
			
		||||
			str = arr[1];
 | 
			
		||||
		} else {
 | 
			
		||||
			throw new Error('unexpected array');
 | 
			
		||||
		}
 | 
			
		||||
		if ('03' === typ) {
 | 
			
		||||
			return ASN1.BitStr(str);
 | 
			
		||||
		} else if ('02' === typ) {
 | 
			
		||||
			return ASN1.UInt(str);
 | 
			
		||||
		} else {
 | 
			
		||||
			return ASN1(typ, str);
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
	Object.keys(ASN1).forEach(function(k) {
 | 
			
		||||
		exports.ASN1[k] = ASN1[k];
 | 
			
		||||
	});
 | 
			
		||||
	ASN1 = exports.ASN1;
 | 
			
		||||
 | 
			
		||||
PEM.packBlock = function (opts) {
 | 
			
		||||
  // TODO allow for headers?
 | 
			
		||||
  return '-----BEGIN ' + opts.type + '-----\n'
 | 
			
		||||
    + Enc.bufToBase64(opts.bytes).match(/.{1,64}/g).join('\n') + '\n'
 | 
			
		||||
    + '-----END ' + opts.type + '-----'
 | 
			
		||||
  ;
 | 
			
		||||
};
 | 
			
		||||
	PEM.packBlock = function(opts) {
 | 
			
		||||
		// TODO allow for headers?
 | 
			
		||||
		return (
 | 
			
		||||
			'-----BEGIN ' +
 | 
			
		||||
			opts.type +
 | 
			
		||||
			'-----\n' +
 | 
			
		||||
			Enc.bufToBase64(opts.bytes)
 | 
			
		||||
				.match(/.{1,64}/g)
 | 
			
		||||
				.join('\n') +
 | 
			
		||||
			'\n' +
 | 
			
		||||
			'-----END ' +
 | 
			
		||||
			opts.type +
 | 
			
		||||
			'-----'
 | 
			
		||||
		);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.bufToBase64 = function (u8) {
 | 
			
		||||
  var bin = '';
 | 
			
		||||
  u8.forEach(function (i) {
 | 
			
		||||
    bin += String.fromCharCode(i);
 | 
			
		||||
  });
 | 
			
		||||
  return btoa(bin);
 | 
			
		||||
};
 | 
			
		||||
	Enc.bufToBase64 = function(u8) {
 | 
			
		||||
		var bin = '';
 | 
			
		||||
		u8.forEach(function(i) {
 | 
			
		||||
			bin += String.fromCharCode(i);
 | 
			
		||||
		});
 | 
			
		||||
		return btoa(bin);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.hexToBuf = function (hex) {
 | 
			
		||||
  var arr = [];
 | 
			
		||||
  hex.match(/.{2}/g).forEach(function (h) {
 | 
			
		||||
    arr.push(parseInt(h, 16));
 | 
			
		||||
  });
 | 
			
		||||
  return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
 | 
			
		||||
};
 | 
			
		||||
	Enc.hexToBuf = function(hex) {
 | 
			
		||||
		var arr = [];
 | 
			
		||||
		hex.match(/.{2}/g).forEach(function(h) {
 | 
			
		||||
			arr.push(parseInt(h, 16));
 | 
			
		||||
		});
 | 
			
		||||
		return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.numToHex = function (d) {
 | 
			
		||||
  d = d.toString(16);
 | 
			
		||||
  if (d.length % 2) {
 | 
			
		||||
    return '0' + d;
 | 
			
		||||
  }
 | 
			
		||||
  return d;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}('undefined' !== typeof window ? window : module.exports));
 | 
			
		||||
	Enc.numToHex = function(d) {
 | 
			
		||||
		d = d.toString(16);
 | 
			
		||||
		if (d.length % 2) {
 | 
			
		||||
			return '0' + d;
 | 
			
		||||
		}
 | 
			
		||||
		return d;
 | 
			
		||||
	};
 | 
			
		||||
})('undefined' !== typeof window ? window : module.exports);
 | 
			
		||||
 | 
			
		||||
@ -2,160 +2,221 @@
 | 
			
		||||
/* 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';
 | 
			
		||||
(function(exports) {
 | 
			
		||||
	'use strict';
 | 
			
		||||
 | 
			
		||||
if (!exports.ASN1) { exports.ASN1 = {}; }
 | 
			
		||||
if (!exports.Enc) { exports.Enc = {}; }
 | 
			
		||||
if (!exports.PEM) { exports.PEM = {}; }
 | 
			
		||||
	if (!exports.ASN1) {
 | 
			
		||||
		exports.ASN1 = {};
 | 
			
		||||
	}
 | 
			
		||||
	if (!exports.Enc) {
 | 
			
		||||
		exports.Enc = {};
 | 
			
		||||
	}
 | 
			
		||||
	if (!exports.PEM) {
 | 
			
		||||
		exports.PEM = {};
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
var ASN1 = exports.ASN1;
 | 
			
		||||
var Enc = exports.Enc;
 | 
			
		||||
var PEM = exports.PEM;
 | 
			
		||||
	var ASN1 = exports.ASN1;
 | 
			
		||||
	var Enc = exports.Enc;
 | 
			
		||||
	var PEM = exports.PEM;
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// Parser
 | 
			
		||||
//
 | 
			
		||||
	//
 | 
			
		||||
	// Parser
 | 
			
		||||
	//
 | 
			
		||||
 | 
			
		||||
// Although I've only seen 9 max in https certificates themselves,
 | 
			
		||||
// but each domain list could have up to 100
 | 
			
		||||
ASN1.ELOOPN = 102;
 | 
			
		||||
ASN1.ELOOP = "uASN1.js Error: iterated over " + ASN1.ELOOPN + "+ elements (probably a malformed file)";
 | 
			
		||||
// I've seen https certificates go 29 deep
 | 
			
		||||
ASN1.EDEEPN = 60;
 | 
			
		||||
ASN1.EDEEP = "uASN1.js Error: element nested " + ASN1.EDEEPN + "+ layers deep (probably a malformed file)";
 | 
			
		||||
// Container Types are Sequence 0x30, Container Array? (0xA0, 0xA1)
 | 
			
		||||
// Value Types are Boolean 0x01, Integer 0x02, Null 0x05, Object ID 0x06, String 0x0C, 0x16, 0x13, 0x1e Value Array? (0x82)
 | 
			
		||||
// Bit String (0x03) and Octet String (0x04) may be values or containers
 | 
			
		||||
// Sometimes Bit String is used as a container (RSA Pub Spki)
 | 
			
		||||
ASN1.CTYPES = [ 0x30, 0x31, 0xa0, 0xa1 ];
 | 
			
		||||
ASN1.VTYPES = [ 0x01, 0x02, 0x05, 0x06, 0x0c, 0x82 ];
 | 
			
		||||
ASN1.parse = function parseAsn1Helper(buf) {
 | 
			
		||||
  //var ws = '  ';
 | 
			
		||||
  function parseAsn1(buf, depth, eager) {
 | 
			
		||||
    if (depth.length >= ASN1.EDEEPN) { throw new Error(ASN1.EDEEP); }
 | 
			
		||||
	// Although I've only seen 9 max in https certificates themselves,
 | 
			
		||||
	// but each domain list could have up to 100
 | 
			
		||||
	ASN1.ELOOPN = 102;
 | 
			
		||||
	ASN1.ELOOP =
 | 
			
		||||
		'uASN1.js Error: iterated over ' +
 | 
			
		||||
		ASN1.ELOOPN +
 | 
			
		||||
		'+ elements (probably a malformed file)';
 | 
			
		||||
	// I've seen https certificates go 29 deep
 | 
			
		||||
	ASN1.EDEEPN = 60;
 | 
			
		||||
	ASN1.EDEEP =
 | 
			
		||||
		'uASN1.js Error: element nested ' +
 | 
			
		||||
		ASN1.EDEEPN +
 | 
			
		||||
		'+ layers deep (probably a malformed file)';
 | 
			
		||||
	// Container Types are Sequence 0x30, Container Array? (0xA0, 0xA1)
 | 
			
		||||
	// Value Types are Boolean 0x01, Integer 0x02, Null 0x05, Object ID 0x06, String 0x0C, 0x16, 0x13, 0x1e Value Array? (0x82)
 | 
			
		||||
	// Bit String (0x03) and Octet String (0x04) may be values or containers
 | 
			
		||||
	// Sometimes Bit String is used as a container (RSA Pub Spki)
 | 
			
		||||
	ASN1.CTYPES = [0x30, 0x31, 0xa0, 0xa1];
 | 
			
		||||
	ASN1.VTYPES = [0x01, 0x02, 0x05, 0x06, 0x0c, 0x82];
 | 
			
		||||
	ASN1.parse = function parseAsn1Helper(buf) {
 | 
			
		||||
		//var ws = '  ';
 | 
			
		||||
		function parseAsn1(buf, depth, eager) {
 | 
			
		||||
			if (depth.length >= ASN1.EDEEPN) {
 | 
			
		||||
				throw new Error(ASN1.EDEEP);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
    var index = 2; // we know, at minimum, data starts after type (0) and lengthSize (1)
 | 
			
		||||
    var asn1 = { type: buf[0], lengthSize: 0, length: buf[1] };
 | 
			
		||||
    var child;
 | 
			
		||||
    var iters = 0;
 | 
			
		||||
    var adjust = 0;
 | 
			
		||||
    var adjustedLen;
 | 
			
		||||
			var index = 2; // we know, at minimum, data starts after type (0) and lengthSize (1)
 | 
			
		||||
			var asn1 = { type: buf[0], lengthSize: 0, length: buf[1] };
 | 
			
		||||
			var child;
 | 
			
		||||
			var iters = 0;
 | 
			
		||||
			var adjust = 0;
 | 
			
		||||
			var adjustedLen;
 | 
			
		||||
 | 
			
		||||
    // Determine how many bytes the length uses, and what it is
 | 
			
		||||
    if (0x80 & asn1.length) {
 | 
			
		||||
      asn1.lengthSize = 0x7f & asn1.length;
 | 
			
		||||
      // I think that buf->hex->int solves the problem of Endianness... not sure
 | 
			
		||||
      asn1.length = parseInt(Enc.bufToHex(buf.slice(index, index + asn1.lengthSize)), 16);
 | 
			
		||||
      index += asn1.lengthSize;
 | 
			
		||||
    }
 | 
			
		||||
			// Determine how many bytes the length uses, and what it is
 | 
			
		||||
			if (0x80 & asn1.length) {
 | 
			
		||||
				asn1.lengthSize = 0x7f & asn1.length;
 | 
			
		||||
				// I think that buf->hex->int solves the problem of Endianness... not sure
 | 
			
		||||
				asn1.length = parseInt(
 | 
			
		||||
					Enc.bufToHex(buf.slice(index, index + asn1.lengthSize)),
 | 
			
		||||
					16
 | 
			
		||||
				);
 | 
			
		||||
				index += asn1.lengthSize;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
    // High-order bit Integers have a leading 0x00 to signify that they are positive.
 | 
			
		||||
    // Bit Streams use the first byte to signify padding, which x.509 doesn't use.
 | 
			
		||||
    if (0x00 === buf[index] && (0x02 === asn1.type || 0x03 === asn1.type)) {
 | 
			
		||||
      // However, 0x00 on its own is a valid number
 | 
			
		||||
      if (asn1.length > 1) {
 | 
			
		||||
        index += 1;
 | 
			
		||||
        adjust = -1;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
    adjustedLen = asn1.length + adjust;
 | 
			
		||||
			// High-order bit Integers have a leading 0x00 to signify that they are positive.
 | 
			
		||||
			// Bit Streams use the first byte to signify padding, which x.509 doesn't use.
 | 
			
		||||
			if (
 | 
			
		||||
				0x00 === buf[index] &&
 | 
			
		||||
				(0x02 === asn1.type || 0x03 === asn1.type)
 | 
			
		||||
			) {
 | 
			
		||||
				// However, 0x00 on its own is a valid number
 | 
			
		||||
				if (asn1.length > 1) {
 | 
			
		||||
					index += 1;
 | 
			
		||||
					adjust = -1;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			adjustedLen = asn1.length + adjust;
 | 
			
		||||
 | 
			
		||||
    //console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
 | 
			
		||||
			//console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
 | 
			
		||||
 | 
			
		||||
    function parseChildren(eager) {
 | 
			
		||||
      asn1.children = [];
 | 
			
		||||
      //console.warn('1 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', 0);
 | 
			
		||||
      while (iters < ASN1.ELOOPN && index < (2 + asn1.length + asn1.lengthSize)) {
 | 
			
		||||
        iters += 1;
 | 
			
		||||
        depth.length += 1;
 | 
			
		||||
        child = parseAsn1(buf.slice(index, index + adjustedLen), depth, eager);
 | 
			
		||||
        depth.length -= 1;
 | 
			
		||||
        // The numbers don't match up exactly and I don't remember why...
 | 
			
		||||
        // probably something with adjustedLen or some such, but the tests pass
 | 
			
		||||
        index += (2 + child.lengthSize + child.length);
 | 
			
		||||
        //console.warn('2 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', (2 + child.lengthSize + child.length));
 | 
			
		||||
        if (index > (2 + asn1.lengthSize + asn1.length)) {
 | 
			
		||||
          if (!eager) { console.error(JSON.stringify(asn1, ASN1._replacer, 2)); }
 | 
			
		||||
          throw new Error("Parse error: child value length (" + child.length
 | 
			
		||||
            + ") is greater than remaining parent length (" + (asn1.length - index)
 | 
			
		||||
            + " = " + asn1.length + " - " + index + ")");
 | 
			
		||||
        }
 | 
			
		||||
        asn1.children.push(child);
 | 
			
		||||
        //console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
 | 
			
		||||
      }
 | 
			
		||||
      if (index !== (2 + asn1.lengthSize + asn1.length)) {
 | 
			
		||||
        //console.warn('index:', index, 'length:', (2 + asn1.lengthSize + asn1.length));
 | 
			
		||||
        throw new Error("premature end-of-file");
 | 
			
		||||
      }
 | 
			
		||||
      if (iters >= ASN1.ELOOPN) { throw new Error(ASN1.ELOOP); }
 | 
			
		||||
			function parseChildren(eager) {
 | 
			
		||||
				asn1.children = [];
 | 
			
		||||
				//console.warn('1 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', 0);
 | 
			
		||||
				while (
 | 
			
		||||
					iters < ASN1.ELOOPN &&
 | 
			
		||||
					index < 2 + asn1.length + asn1.lengthSize
 | 
			
		||||
				) {
 | 
			
		||||
					iters += 1;
 | 
			
		||||
					depth.length += 1;
 | 
			
		||||
					child = parseAsn1(
 | 
			
		||||
						buf.slice(index, index + adjustedLen),
 | 
			
		||||
						depth,
 | 
			
		||||
						eager
 | 
			
		||||
					);
 | 
			
		||||
					depth.length -= 1;
 | 
			
		||||
					// The numbers don't match up exactly and I don't remember why...
 | 
			
		||||
					// probably something with adjustedLen or some such, but the tests pass
 | 
			
		||||
					index += 2 + child.lengthSize + child.length;
 | 
			
		||||
					//console.warn('2 len:', (2 + asn1.lengthSize + asn1.length), 'idx:', index, 'clen:', (2 + child.lengthSize + child.length));
 | 
			
		||||
					if (index > 2 + asn1.lengthSize + asn1.length) {
 | 
			
		||||
						if (!eager) {
 | 
			
		||||
							console.error(
 | 
			
		||||
								JSON.stringify(asn1, ASN1._replacer, 2)
 | 
			
		||||
							);
 | 
			
		||||
						}
 | 
			
		||||
						throw new Error(
 | 
			
		||||
							'Parse error: child value length (' +
 | 
			
		||||
								child.length +
 | 
			
		||||
								') is greater than remaining parent length (' +
 | 
			
		||||
								(asn1.length - index) +
 | 
			
		||||
								' = ' +
 | 
			
		||||
								asn1.length +
 | 
			
		||||
								' - ' +
 | 
			
		||||
								index +
 | 
			
		||||
								')'
 | 
			
		||||
						);
 | 
			
		||||
					}
 | 
			
		||||
					asn1.children.push(child);
 | 
			
		||||
					//console.warn(depth.join(ws) + '0x' + Enc.numToHex(asn1.type), index, 'len:', asn1.length, asn1);
 | 
			
		||||
				}
 | 
			
		||||
				if (index !== 2 + asn1.lengthSize + asn1.length) {
 | 
			
		||||
					//console.warn('index:', index, 'length:', (2 + asn1.lengthSize + asn1.length));
 | 
			
		||||
					throw new Error('premature end-of-file');
 | 
			
		||||
				}
 | 
			
		||||
				if (iters >= ASN1.ELOOPN) {
 | 
			
		||||
					throw new Error(ASN1.ELOOP);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
      delete asn1.value;
 | 
			
		||||
      return asn1;
 | 
			
		||||
    }
 | 
			
		||||
				delete asn1.value;
 | 
			
		||||
				return asn1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
    // Recurse into types that are _always_ containers
 | 
			
		||||
    if (-1 !== ASN1.CTYPES.indexOf(asn1.type)) { return parseChildren(eager); }
 | 
			
		||||
			// Recurse into types that are _always_ containers
 | 
			
		||||
			if (-1 !== ASN1.CTYPES.indexOf(asn1.type)) {
 | 
			
		||||
				return parseChildren(eager);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
    // Return types that are _always_ values
 | 
			
		||||
    asn1.value = buf.slice(index, index + adjustedLen);
 | 
			
		||||
    if (-1 !== ASN1.VTYPES.indexOf(asn1.type)) { return asn1; }
 | 
			
		||||
			// Return types that are _always_ values
 | 
			
		||||
			asn1.value = buf.slice(index, index + adjustedLen);
 | 
			
		||||
			if (-1 !== ASN1.VTYPES.indexOf(asn1.type)) {
 | 
			
		||||
				return asn1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
    // For ambigious / unknown types, recurse and return on failure
 | 
			
		||||
    // (and return child array size to zero)
 | 
			
		||||
    try { return parseChildren(true); }
 | 
			
		||||
    catch(e) { asn1.children.length = 0; return asn1; }
 | 
			
		||||
  }
 | 
			
		||||
			// For ambigious / unknown types, recurse and return on failure
 | 
			
		||||
			// (and return child array size to zero)
 | 
			
		||||
			try {
 | 
			
		||||
				return parseChildren(true);
 | 
			
		||||
			} catch (e) {
 | 
			
		||||
				asn1.children.length = 0;
 | 
			
		||||
				return asn1;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
  var asn1 = parseAsn1(buf, []);
 | 
			
		||||
  var len = buf.byteLength || buf.length;
 | 
			
		||||
  if (len !== 2 + asn1.lengthSize + asn1.length) {
 | 
			
		||||
    throw new Error("Length of buffer does not match length of ASN.1 sequence.");
 | 
			
		||||
  }
 | 
			
		||||
  return asn1;
 | 
			
		||||
};
 | 
			
		||||
ASN1._replacer = function (k, v) {
 | 
			
		||||
  if ('type' === k) { return '0x' + Enc.numToHex(v); }
 | 
			
		||||
  if (v && 'value' === k) { return '0x' + Enc.bufToHex(v.data || v); }
 | 
			
		||||
  return v;
 | 
			
		||||
};
 | 
			
		||||
		var asn1 = parseAsn1(buf, []);
 | 
			
		||||
		var len = buf.byteLength || buf.length;
 | 
			
		||||
		if (len !== 2 + asn1.lengthSize + asn1.length) {
 | 
			
		||||
			throw new Error(
 | 
			
		||||
				'Length of buffer does not match length of ASN.1 sequence.'
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
		return asn1;
 | 
			
		||||
	};
 | 
			
		||||
	ASN1._replacer = function(k, v) {
 | 
			
		||||
		if ('type' === k) {
 | 
			
		||||
			return '0x' + Enc.numToHex(v);
 | 
			
		||||
		}
 | 
			
		||||
		if (v && 'value' === k) {
 | 
			
		||||
			return '0x' + Enc.bufToHex(v.data || v);
 | 
			
		||||
		}
 | 
			
		||||
		return v;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
// don't replace the full parseBlock, if it exists
 | 
			
		||||
PEM.parseBlock = PEM.parseBlock || function (str) {
 | 
			
		||||
  var der = str.split(/\n/).filter(function (line) {
 | 
			
		||||
    return !/-----/.test(line);
 | 
			
		||||
  }).join('');
 | 
			
		||||
  return { bytes: Enc.base64ToBuf(der) };
 | 
			
		||||
};
 | 
			
		||||
	// don't replace the full parseBlock, if it exists
 | 
			
		||||
	PEM.parseBlock =
 | 
			
		||||
		PEM.parseBlock ||
 | 
			
		||||
		function(str) {
 | 
			
		||||
			var der = str
 | 
			
		||||
				.split(/\n/)
 | 
			
		||||
				.filter(function(line) {
 | 
			
		||||
					return !/-----/.test(line);
 | 
			
		||||
				})
 | 
			
		||||
				.join('');
 | 
			
		||||
			return { bytes: Enc.base64ToBuf(der) };
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
Enc.base64ToBuf = function (b64) {
 | 
			
		||||
  return Enc.binToBuf(atob(b64));
 | 
			
		||||
};
 | 
			
		||||
Enc.binToBuf = function (bin) {
 | 
			
		||||
  var arr = bin.split('').map(function (ch) {
 | 
			
		||||
    return ch.charCodeAt(0);
 | 
			
		||||
  });
 | 
			
		||||
  return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
 | 
			
		||||
};
 | 
			
		||||
Enc.bufToHex = function (u8) {
 | 
			
		||||
  var hex = [];
 | 
			
		||||
  var i, h;
 | 
			
		||||
  var len = (u8.byteLength || u8.length);
 | 
			
		||||
	Enc.base64ToBuf = function(b64) {
 | 
			
		||||
		return Enc.binToBuf(atob(b64));
 | 
			
		||||
	};
 | 
			
		||||
	Enc.binToBuf = function(bin) {
 | 
			
		||||
		var arr = bin.split('').map(function(ch) {
 | 
			
		||||
			return ch.charCodeAt(0);
 | 
			
		||||
		});
 | 
			
		||||
		return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
 | 
			
		||||
	};
 | 
			
		||||
	Enc.bufToHex = function(u8) {
 | 
			
		||||
		var hex = [];
 | 
			
		||||
		var i, h;
 | 
			
		||||
		var len = u8.byteLength || u8.length;
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < len; i += 1) {
 | 
			
		||||
    h = u8[i].toString(16);
 | 
			
		||||
    if (h.length % 2) { h = '0' + h; }
 | 
			
		||||
    hex.push(h);
 | 
			
		||||
  }
 | 
			
		||||
		for (i = 0; i < len; i += 1) {
 | 
			
		||||
			h = u8[i].toString(16);
 | 
			
		||||
			if (h.length % 2) {
 | 
			
		||||
				h = '0' + h;
 | 
			
		||||
			}
 | 
			
		||||
			hex.push(h);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
  return hex.join('').toLowerCase();
 | 
			
		||||
};
 | 
			
		||||
Enc.numToHex = function (d) {
 | 
			
		||||
  d = d.toString(16);
 | 
			
		||||
  if (d.length % 2) {
 | 
			
		||||
    return '0' + d;
 | 
			
		||||
  }
 | 
			
		||||
  return d;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}('undefined' !== typeof window ? window : module.exports));
 | 
			
		||||
		return hex.join('').toLowerCase();
 | 
			
		||||
	};
 | 
			
		||||
	Enc.numToHex = function(d) {
 | 
			
		||||
		d = d.toString(16);
 | 
			
		||||
		if (d.length % 2) {
 | 
			
		||||
			return '0' + d;
 | 
			
		||||
		}
 | 
			
		||||
		return d;
 | 
			
		||||
	};
 | 
			
		||||
})('undefined' !== typeof window ? window : module.exports);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										644
									
								
								lib/csr.js
									
									
									
									
									
								
							
							
						
						
									
										644
									
								
								lib/csr.js
									
									
									
									
									
								
							@ -2,297 +2,401 @@
 | 
			
		||||
/* 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';
 | 
			
		||||
/*global Promise*/
 | 
			
		||||
(function(exports) {
 | 
			
		||||
	'use strict';
 | 
			
		||||
	/*global Promise*/
 | 
			
		||||
 | 
			
		||||
var ASN1 = exports.ASN1;
 | 
			
		||||
var Enc = exports.Enc;
 | 
			
		||||
var PEM = exports.PEM;
 | 
			
		||||
var X509 = exports.x509;
 | 
			
		||||
var Keypairs = exports.Keypairs;
 | 
			
		||||
	var ASN1 = exports.ASN1;
 | 
			
		||||
	var Enc = exports.Enc;
 | 
			
		||||
	var PEM = exports.PEM;
 | 
			
		||||
	var X509 = exports.x509;
 | 
			
		||||
	var Keypairs = exports.Keypairs;
 | 
			
		||||
 | 
			
		||||
// TODO find a way that the prior node-ish way of `module.exports = function () {}` isn't broken
 | 
			
		||||
var CSR = exports.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);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
	// TODO find a way that the prior node-ish way of `module.exports = function () {}` isn't broken
 | 
			
		||||
	var CSR = (exports.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 () {
 | 
			
		||||
    var Keypairs;
 | 
			
		||||
    opts = JSON.parse(JSON.stringify(opts));
 | 
			
		||||
	CSR._prepare = function(opts) {
 | 
			
		||||
		return Promise.resolve().then(function() {
 | 
			
		||||
			var Keypairs;
 | 
			
		||||
			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");
 | 
			
		||||
    }
 | 
			
		||||
			// 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");
 | 
			
		||||
    }
 | 
			
		||||
			// 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");
 | 
			
		||||
    }
 | 
			
		||||
			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');
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
    Keypairs = exports.Keypairs;
 | 
			
		||||
    if (!exports.Keypairs) {
 | 
			
		||||
      throw new Error("Keypairs.js is an optional dependency for PEM-to-JWK.\n"
 | 
			
		||||
        + "Install it if you'd like to use it:\n"
 | 
			
		||||
        + "\tnpm install --save rasha\n"
 | 
			
		||||
        + "Otherwise supply a jwk as the private key."
 | 
			
		||||
      );
 | 
			
		||||
    }
 | 
			
		||||
			Keypairs = exports.Keypairs;
 | 
			
		||||
			if (!exports.Keypairs) {
 | 
			
		||||
				throw new Error(
 | 
			
		||||
					'Keypairs.js is an optional dependency for PEM-to-JWK.\n' +
 | 
			
		||||
						"Install it if you'd like to use it:\n" +
 | 
			
		||||
						'\tnpm install --save rasha\n' +
 | 
			
		||||
						'Otherwise supply a jwk as the private key.'
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
    return Keypairs.import({ pem: opts.pem || opts.key }).then(function (pair) {
 | 
			
		||||
      opts.jwk = pair.private;
 | 
			
		||||
      return opts;
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
			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._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);
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
	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);
 | 
			
		||||
};
 | 
			
		||||
	//
 | 
			
		||||
	// 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._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
 | 
			
		||||
  , ASN1.BitStr(Enc.bufToHex(opts.signature))
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
	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
 | 
			
		||||
			ASN1.BitStr(Enc.bufToHex(opts.signature))
 | 
			
		||||
		);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
X509.packCsr = function (asn1pubkey, domains) {
 | 
			
		||||
  return ASN1('30'
 | 
			
		||||
    // Version (0)
 | 
			
		||||
  , ASN1.UInt('00')
 | 
			
		||||
	X509.packCsr = function(asn1pubkey, domains) {
 | 
			
		||||
		return ASN1(
 | 
			
		||||
			'30',
 | 
			
		||||
			// Version (0)
 | 
			
		||||
			ASN1.UInt('00'),
 | 
			
		||||
 | 
			
		||||
    // 2.5.4.3 commonName (X.520 DN component)
 | 
			
		||||
  , ASN1('30', ASN1('31', ASN1('30', ASN1('06', '550403'), ASN1('0c', Enc.utf8ToHex(domains[0])))))
 | 
			
		||||
			// 2.5.4.3 commonName (X.520 DN component)
 | 
			
		||||
			ASN1(
 | 
			
		||||
				'30',
 | 
			
		||||
				ASN1(
 | 
			
		||||
					'31',
 | 
			
		||||
					ASN1(
 | 
			
		||||
						'30',
 | 
			
		||||
						ASN1('06', '550403'),
 | 
			
		||||
						ASN1('0c', Enc.utf8ToHex(domains[0]))
 | 
			
		||||
					)
 | 
			
		||||
				)
 | 
			
		||||
			),
 | 
			
		||||
 | 
			
		||||
    // Public Key (RSA or EC)
 | 
			
		||||
  , asn1pubkey
 | 
			
		||||
			// 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) {
 | 
			
		||||
                  return ASN1('82', Enc.utf8ToHex(d));
 | 
			
		||||
                }).join(''))))))))
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
			// 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) {
 | 
			
		||||
												return ASN1(
 | 
			
		||||
													'82',
 | 
			
		||||
													Enc.utf8ToHex(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 = ASN1.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', ASN1.UInt(Enc.bufToHex(sig.children[0].value)), ASN1.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];
 | 
			
		||||
  // TODO utf8
 | 
			
		||||
  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.bufToBin(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
 | 
			
		||||
        return Enc.bufToBin(name.value);
 | 
			
		||||
      });
 | 
			
		||||
    })[0];
 | 
			
		||||
  })[0];
 | 
			
		||||
	// 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 = ASN1.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',
 | 
			
		||||
				ASN1.UInt(Enc.bufToHex(sig.children[0].value)),
 | 
			
		||||
				ASN1.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];
 | 
			
		||||
		// TODO utf8
 | 
			
		||||
		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.bufToBin(
 | 
			
		||||
			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
 | 
			
		||||
								return Enc.bufToBin(name.value);
 | 
			
		||||
							}
 | 
			
		||||
						);
 | 
			
		||||
					})[0];
 | 
			
		||||
			})[0];
 | 
			
		||||
 | 
			
		||||
  return {
 | 
			
		||||
    subject: sub
 | 
			
		||||
  , altnames: domains
 | 
			
		||||
  , jwk: pub
 | 
			
		||||
  , signature: sig
 | 
			
		||||
  };
 | 
			
		||||
};
 | 
			
		||||
		return {
 | 
			
		||||
			subject: sub,
 | 
			
		||||
			altnames: domains,
 | 
			
		||||
			jwk: pub,
 | 
			
		||||
			signature: sig
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
X509.packCsrRsaPublicKey = function (jwk) {
 | 
			
		||||
  // Sequence the key
 | 
			
		||||
  var n = ASN1.UInt(Enc.base64ToHex(jwk.n));
 | 
			
		||||
  var e = ASN1.UInt(Enc.base64ToHex(jwk.e));
 | 
			
		||||
  var asn1pub = ASN1('30', n, e);
 | 
			
		||||
	X509.packCsrRsaPublicKey = function(jwk) {
 | 
			
		||||
		// Sequence the key
 | 
			
		||||
		var n = ASN1.UInt(Enc.base64ToHex(jwk.n));
 | 
			
		||||
		var e = ASN1.UInt(Enc.base64ToHex(jwk.e));
 | 
			
		||||
		var asn1pub = ASN1('30', n, e);
 | 
			
		||||
 | 
			
		||||
  // Add the CSR pub key header
 | 
			
		||||
  return ASN1('30', ASN1('30', ASN1('06', '2a864886f70d010101'), ASN1('05')), ASN1.BitStr(asn1pub));
 | 
			
		||||
};
 | 
			
		||||
		// Add the CSR pub key header
 | 
			
		||||
		return ASN1(
 | 
			
		||||
			'30',
 | 
			
		||||
			ASN1('30', ASN1('06', '2a864886f70d010101'), ASN1('05')),
 | 
			
		||||
			ASN1.BitStr(asn1pub)
 | 
			
		||||
		);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
X509.packCsrEcPublicKey = function (jwk) {
 | 
			
		||||
  var ecOid = X509._oids[jwk.crv];
 | 
			
		||||
  if (!ecOid) {
 | 
			
		||||
    throw new Error("Unsupported namedCurve '" + jwk.crv + "'. Supported types are " + Object.keys(X509._oids));
 | 
			
		||||
  }
 | 
			
		||||
  var cmp = '04'; // 04 == x+y, 02 == x-only
 | 
			
		||||
  var hxy = '';
 | 
			
		||||
  // Placeholder. I'm not even sure if compression should be supported.
 | 
			
		||||
  if (!jwk.y) { cmp = '02'; }
 | 
			
		||||
  hxy += Enc.base64ToHex(jwk.x);
 | 
			
		||||
  if (jwk.y) { hxy += Enc.base64ToHex(jwk.y); }
 | 
			
		||||
	X509.packCsrEcPublicKey = function(jwk) {
 | 
			
		||||
		var ecOid = X509._oids[jwk.crv];
 | 
			
		||||
		if (!ecOid) {
 | 
			
		||||
			throw new Error(
 | 
			
		||||
				"Unsupported namedCurve '" +
 | 
			
		||||
					jwk.crv +
 | 
			
		||||
					"'. Supported types are " +
 | 
			
		||||
					Object.keys(X509._oids)
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
		var cmp = '04'; // 04 == x+y, 02 == x-only
 | 
			
		||||
		var hxy = '';
 | 
			
		||||
		// Placeholder. I'm not even sure if compression should be supported.
 | 
			
		||||
		if (!jwk.y) {
 | 
			
		||||
			cmp = '02';
 | 
			
		||||
		}
 | 
			
		||||
		hxy += Enc.base64ToHex(jwk.x);
 | 
			
		||||
		if (jwk.y) {
 | 
			
		||||
			hxy += Enc.base64ToHex(jwk.y);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
  // 1.2.840.10045.2.1 ecPublicKey
 | 
			
		||||
  return ASN1('30', ASN1('30', ASN1('06', '2a8648ce3d0201'), ASN1('06', ecOid)), ASN1.BitStr(cmp + hxy));
 | 
			
		||||
};
 | 
			
		||||
X509._oids = {
 | 
			
		||||
  // 1.2.840.10045.3.1.7 prime256v1
 | 
			
		||||
  // (ANSI X9.62 named elliptic curve) (06 08 - 2A 86 48 CE 3D 03 01 07)
 | 
			
		||||
  'P-256': '2a8648ce3d030107'
 | 
			
		||||
  // 1.3.132.0.34 P-384 (06 05 - 2B 81 04 00 22)
 | 
			
		||||
  // (SEC 2 recommended EC domain secp256r1)
 | 
			
		||||
, 'P-384': '2b81040022'
 | 
			
		||||
  // requires more logic and isn't a recommended standard
 | 
			
		||||
  // 1.3.132.0.35 P-521 (06 05 - 2B 81 04 00 23)
 | 
			
		||||
  // (SEC 2 alternate P-521)
 | 
			
		||||
//, 'P-521': '2B 81 04 00 23'
 | 
			
		||||
};
 | 
			
		||||
		// 1.2.840.10045.2.1 ecPublicKey
 | 
			
		||||
		return ASN1(
 | 
			
		||||
			'30',
 | 
			
		||||
			ASN1('30', ASN1('06', '2a8648ce3d0201'), ASN1('06', ecOid)),
 | 
			
		||||
			ASN1.BitStr(cmp + hxy)
 | 
			
		||||
		);
 | 
			
		||||
	};
 | 
			
		||||
	X509._oids = {
 | 
			
		||||
		// 1.2.840.10045.3.1.7 prime256v1
 | 
			
		||||
		// (ANSI X9.62 named elliptic curve) (06 08 - 2A 86 48 CE 3D 03 01 07)
 | 
			
		||||
		'P-256': '2a8648ce3d030107',
 | 
			
		||||
		// 1.3.132.0.34 P-384 (06 05 - 2B 81 04 00 22)
 | 
			
		||||
		// (SEC 2 recommended EC domain secp256r1)
 | 
			
		||||
		'P-384': '2b81040022'
 | 
			
		||||
		// requires more logic and isn't a recommended standard
 | 
			
		||||
		// 1.3.132.0.35 P-521 (06 05 - 2B 81 04 00 23)
 | 
			
		||||
		// (SEC 2 alternate P-521)
 | 
			
		||||
		//, 'P-521': '2B 81 04 00 23'
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
// don't replace the full parseBlock, if it exists
 | 
			
		||||
PEM.parseBlock = PEM.parseBlock || function (str) {
 | 
			
		||||
  var der = str.split(/\n/).filter(function (line) {
 | 
			
		||||
    return !/-----/.test(line);
 | 
			
		||||
  }).join('');
 | 
			
		||||
  return { bytes: Enc.base64ToBuf(der) };
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}('undefined' === typeof window ? module.exports : window));
 | 
			
		||||
	// don't replace the full parseBlock, if it exists
 | 
			
		||||
	PEM.parseBlock =
 | 
			
		||||
		PEM.parseBlock ||
 | 
			
		||||
		function(str) {
 | 
			
		||||
			var der = str
 | 
			
		||||
				.split(/\n/)
 | 
			
		||||
				.filter(function(line) {
 | 
			
		||||
					return !/-----/.test(line);
 | 
			
		||||
				})
 | 
			
		||||
				.join('');
 | 
			
		||||
			return { bytes: Enc.base64ToBuf(der) };
 | 
			
		||||
		};
 | 
			
		||||
})('undefined' === typeof window ? module.exports : window);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										375
									
								
								lib/ecdsa.js
									
									
									
									
									
								
							
							
						
						
									
										375
									
								
								lib/ecdsa.js
									
									
									
									
									
								
							@ -1,172 +1,227 @@
 | 
			
		||||
/*global Promise*/
 | 
			
		||||
(function (exports) {
 | 
			
		||||
'use strict';
 | 
			
		||||
(function(exports) {
 | 
			
		||||
	'use strict';
 | 
			
		||||
 | 
			
		||||
var EC = exports.Eckles = {};
 | 
			
		||||
var x509 = exports.x509;
 | 
			
		||||
if ('undefined' !== typeof module) { module.exports = EC; }
 | 
			
		||||
var PEM = exports.PEM;
 | 
			
		||||
var SSH = exports.SSH;
 | 
			
		||||
var Enc = {};
 | 
			
		||||
var textEncoder = new TextEncoder();
 | 
			
		||||
	var EC = (exports.Eckles = {});
 | 
			
		||||
	var x509 = exports.x509;
 | 
			
		||||
	if ('undefined' !== typeof module) {
 | 
			
		||||
		module.exports = EC;
 | 
			
		||||
	}
 | 
			
		||||
	var PEM = exports.PEM;
 | 
			
		||||
	var SSH = exports.SSH;
 | 
			
		||||
	var Enc = {};
 | 
			
		||||
	var textEncoder = new TextEncoder();
 | 
			
		||||
 | 
			
		||||
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.";
 | 
			
		||||
EC._universal = "Bluecrypt only supports crypto with standard cross-browser and cross-platform support.";
 | 
			
		||||
EC.generate = function (opts) {
 | 
			
		||||
  var wcOpts = {};
 | 
			
		||||
  if (!opts) { opts = {}; }
 | 
			
		||||
  if (!opts.kty) { opts.kty = 'EC'; }
 | 
			
		||||
	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.";
 | 
			
		||||
	EC._universal =
 | 
			
		||||
		'Bluecrypt only supports crypto with standard cross-browser and cross-platform support.';
 | 
			
		||||
	EC.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'. "
 | 
			
		||||
      + EC._stance));
 | 
			
		||||
  }
 | 
			
		||||
		// 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'. " +
 | 
			
		||||
						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
 | 
			
		||||
      , public: EC.neuter({ jwk: privJwk })
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
		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,
 | 
			
		||||
							public: EC.neuter({ jwk: privJwk })
 | 
			
		||||
						};
 | 
			
		||||
					});
 | 
			
		||||
			});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
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");
 | 
			
		||||
    }
 | 
			
		||||
	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);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
EC.pack = function (opts) {
 | 
			
		||||
  return Promise.resolve().then(function () {
 | 
			
		||||
    return EC.exportSync(opts);
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
			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
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
	EC.pack = function(opts) {
 | 
			
		||||
		return Promise.resolve().then(function() {
 | 
			
		||||
			return EC.exportSync(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;
 | 
			
		||||
};
 | 
			
		||||
	// 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;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
// 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';
 | 
			
		||||
  }
 | 
			
		||||
  return window.crypto.subtle.digest(
 | 
			
		||||
    { name: alg }
 | 
			
		||||
  , textEncoder.encode('{"crv":"' + jwk.crv + '","kty":"EC","x":"' + jwk.x + '","y":"' + jwk.y + '"}')
 | 
			
		||||
  ).then(function (hash) {
 | 
			
		||||
    return Enc.bufToUrlBase64(new Uint8Array(hash));
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
	// 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';
 | 
			
		||||
		}
 | 
			
		||||
		return window.crypto.subtle
 | 
			
		||||
			.digest(
 | 
			
		||||
				{ name: alg },
 | 
			
		||||
				textEncoder.encode(
 | 
			
		||||
					'{"crv":"' +
 | 
			
		||||
						jwk.crv +
 | 
			
		||||
						'","kty":"EC","x":"' +
 | 
			
		||||
						jwk.x +
 | 
			
		||||
						'","y":"' +
 | 
			
		||||
						jwk.y +
 | 
			
		||||
						'"}'
 | 
			
		||||
				)
 | 
			
		||||
			)
 | 
			
		||||
			.then(function(hash) {
 | 
			
		||||
				return Enc.bufToUrlBase64(new Uint8Array(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 EC.import(opts).then(function (jwk) {
 | 
			
		||||
        return EC.__thumbprint(jwk);
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
    return EC.__thumbprint(jwk);
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
	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 EC.import(opts).then(function(jwk) {
 | 
			
		||||
					return EC.__thumbprint(jwk);
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
			return EC.__thumbprint(jwk);
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.bufToUrlBase64 = function (u8) {
 | 
			
		||||
  return Enc.bufToBase64(u8)
 | 
			
		||||
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
 | 
			
		||||
};
 | 
			
		||||
	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);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}('undefined' !== typeof module ? module.exports : window));
 | 
			
		||||
	Enc.bufToBase64 = function(u8) {
 | 
			
		||||
		var bin = '';
 | 
			
		||||
		u8.forEach(function(i) {
 | 
			
		||||
			bin += String.fromCharCode(i);
 | 
			
		||||
		});
 | 
			
		||||
		return btoa(bin);
 | 
			
		||||
	};
 | 
			
		||||
})('undefined' !== typeof module ? module.exports : window);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										255
									
								
								lib/encoding.js
									
									
									
									
									
								
							
							
						
						
									
										255
									
								
								lib/encoding.js
									
									
									
									
									
								
							@ -1,140 +1,151 @@
 | 
			
		||||
(function (exports) {
 | 
			
		||||
(function(exports) {
 | 
			
		||||
	var Enc = (exports.Enc = {});
 | 
			
		||||
 | 
			
		||||
var Enc = exports.Enc = {};
 | 
			
		||||
	Enc.bufToBin = function(buf) {
 | 
			
		||||
		var bin = '';
 | 
			
		||||
		// cannot use .map() because Uint8Array would return only 0s
 | 
			
		||||
		buf.forEach(function(ch) {
 | 
			
		||||
			bin += String.fromCharCode(ch);
 | 
			
		||||
		});
 | 
			
		||||
		return bin;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.bufToBin = function (buf) {
 | 
			
		||||
  var bin = '';
 | 
			
		||||
  // cannot use .map() because Uint8Array would return only 0s
 | 
			
		||||
  buf.forEach(function (ch) {
 | 
			
		||||
    bin += String.fromCharCode(ch);
 | 
			
		||||
  });
 | 
			
		||||
  return bin;
 | 
			
		||||
};
 | 
			
		||||
	Enc.bufToHex = function toHex(u8) {
 | 
			
		||||
		var hex = [];
 | 
			
		||||
		var i, h;
 | 
			
		||||
		var len = u8.byteLength || u8.length;
 | 
			
		||||
 | 
			
		||||
Enc.bufToHex = function toHex(u8) {
 | 
			
		||||
  var hex = [];
 | 
			
		||||
  var i, h;
 | 
			
		||||
  var len = (u8.byteLength || u8.length);
 | 
			
		||||
		for (i = 0; i < len; i += 1) {
 | 
			
		||||
			h = u8[i].toString(16);
 | 
			
		||||
			if (h.length % 2) {
 | 
			
		||||
				h = '0' + h;
 | 
			
		||||
			}
 | 
			
		||||
			hex.push(h);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < len; i += 1) {
 | 
			
		||||
    h = u8[i].toString(16);
 | 
			
		||||
    if (h.length % 2) { h = '0' + h; }
 | 
			
		||||
    hex.push(h);
 | 
			
		||||
  }
 | 
			
		||||
		return hex.join('').toLowerCase();
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
  return hex.join('').toLowerCase();
 | 
			
		||||
};
 | 
			
		||||
	Enc.urlBase64ToBase64 = function urlsafeBase64ToBase64(str) {
 | 
			
		||||
		var r = str % 4;
 | 
			
		||||
		if (2 === r) {
 | 
			
		||||
			str += '==';
 | 
			
		||||
		} else if (3 === r) {
 | 
			
		||||
			str += '=';
 | 
			
		||||
		}
 | 
			
		||||
		return str.replace(/-/g, '+').replace(/_/g, '/');
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.urlBase64ToBase64 = function urlsafeBase64ToBase64(str) {
 | 
			
		||||
  var r = str % 4;
 | 
			
		||||
  if (2 === r) {
 | 
			
		||||
    str += '==';
 | 
			
		||||
  } else if (3 === r) {
 | 
			
		||||
    str += '=';
 | 
			
		||||
  }
 | 
			
		||||
  return str.replace(/-/g, '+').replace(/_/g, '/');
 | 
			
		||||
};
 | 
			
		||||
	Enc.base64ToBuf = function(b64) {
 | 
			
		||||
		return Enc.binToBuf(atob(b64));
 | 
			
		||||
	};
 | 
			
		||||
	Enc.binToBuf = function(bin) {
 | 
			
		||||
		var arr = bin.split('').map(function(ch) {
 | 
			
		||||
			return ch.charCodeAt(0);
 | 
			
		||||
		});
 | 
			
		||||
		return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
 | 
			
		||||
	};
 | 
			
		||||
	Enc.bufToHex = function(u8) {
 | 
			
		||||
		var hex = [];
 | 
			
		||||
		var i, h;
 | 
			
		||||
		var len = u8.byteLength || u8.length;
 | 
			
		||||
 | 
			
		||||
Enc.base64ToBuf = function (b64) {
 | 
			
		||||
  return Enc.binToBuf(atob(b64));
 | 
			
		||||
};
 | 
			
		||||
Enc.binToBuf = function (bin) {
 | 
			
		||||
  var arr = bin.split('').map(function (ch) {
 | 
			
		||||
    return ch.charCodeAt(0);
 | 
			
		||||
  });
 | 
			
		||||
  return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
 | 
			
		||||
};
 | 
			
		||||
Enc.bufToHex = function (u8) {
 | 
			
		||||
  var hex = [];
 | 
			
		||||
  var i, h;
 | 
			
		||||
  var len = (u8.byteLength || u8.length);
 | 
			
		||||
		for (i = 0; i < len; i += 1) {
 | 
			
		||||
			h = u8[i].toString(16);
 | 
			
		||||
			if (h.length % 2) {
 | 
			
		||||
				h = '0' + h;
 | 
			
		||||
			}
 | 
			
		||||
			hex.push(h);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
  for (i = 0; i < len; i += 1) {
 | 
			
		||||
    h = u8[i].toString(16);
 | 
			
		||||
    if (h.length % 2) { h = '0' + h; }
 | 
			
		||||
    hex.push(h);
 | 
			
		||||
  }
 | 
			
		||||
		return hex.join('').toLowerCase();
 | 
			
		||||
	};
 | 
			
		||||
	Enc.numToHex = function(d) {
 | 
			
		||||
		d = d.toString(16);
 | 
			
		||||
		if (d.length % 2) {
 | 
			
		||||
			return '0' + d;
 | 
			
		||||
		}
 | 
			
		||||
		return d;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
  return hex.join('').toLowerCase();
 | 
			
		||||
};
 | 
			
		||||
Enc.numToHex = function (d) {
 | 
			
		||||
  d = d.toString(16);
 | 
			
		||||
  if (d.length % 2) {
 | 
			
		||||
    return '0' + d;
 | 
			
		||||
  }
 | 
			
		||||
  return d;
 | 
			
		||||
};
 | 
			
		||||
	Enc.bufToUrlBase64 = function(u8) {
 | 
			
		||||
		return Enc.base64ToUrlBase64(Enc.bufToBase64(u8));
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.bufToUrlBase64 = function (u8) {
 | 
			
		||||
  return Enc.base64ToUrlBase64(Enc.bufToBase64(u8));
 | 
			
		||||
};
 | 
			
		||||
	Enc.base64ToUrlBase64 = function(str) {
 | 
			
		||||
		return str
 | 
			
		||||
			.replace(/\+/g, '-')
 | 
			
		||||
			.replace(/\//g, '_')
 | 
			
		||||
			.replace(/=/g, '');
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.base64ToUrlBase64 = function (str) {
 | 
			
		||||
  return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
 | 
			
		||||
};
 | 
			
		||||
	Enc.bufToBase64 = function(u8) {
 | 
			
		||||
		var bin = '';
 | 
			
		||||
		u8.forEach(function(i) {
 | 
			
		||||
			bin += String.fromCharCode(i);
 | 
			
		||||
		});
 | 
			
		||||
		return btoa(bin);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.bufToBase64 = function (u8) {
 | 
			
		||||
  var bin = '';
 | 
			
		||||
  u8.forEach(function (i) {
 | 
			
		||||
    bin += String.fromCharCode(i);
 | 
			
		||||
  });
 | 
			
		||||
  return btoa(bin);
 | 
			
		||||
};
 | 
			
		||||
	Enc.hexToBuf = function(hex) {
 | 
			
		||||
		var arr = [];
 | 
			
		||||
		hex.match(/.{2}/g).forEach(function(h) {
 | 
			
		||||
			arr.push(parseInt(h, 16));
 | 
			
		||||
		});
 | 
			
		||||
		return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.hexToBuf = function (hex) {
 | 
			
		||||
  var arr = [];
 | 
			
		||||
  hex.match(/.{2}/g).forEach(function (h) {
 | 
			
		||||
    arr.push(parseInt(h, 16));
 | 
			
		||||
  });
 | 
			
		||||
  return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
 | 
			
		||||
};
 | 
			
		||||
	Enc.numToHex = function(d) {
 | 
			
		||||
		d = d.toString(16);
 | 
			
		||||
		if (d.length % 2) {
 | 
			
		||||
			return '0' + d;
 | 
			
		||||
		}
 | 
			
		||||
		return d;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.numToHex = function (d) {
 | 
			
		||||
  d = d.toString(16);
 | 
			
		||||
  if (d.length % 2) {
 | 
			
		||||
    return '0' + d;
 | 
			
		||||
  }
 | 
			
		||||
  return d;
 | 
			
		||||
};
 | 
			
		||||
	//
 | 
			
		||||
	// JWK to SSH (tested working)
 | 
			
		||||
	//
 | 
			
		||||
	Enc.base64ToHex = function(b64) {
 | 
			
		||||
		var bin = atob(Enc.urlBase64ToBase64(b64));
 | 
			
		||||
		return Enc.binToHex(bin);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	Enc.binToHex = function(bin) {
 | 
			
		||||
		return bin
 | 
			
		||||
			.split('')
 | 
			
		||||
			.map(function(ch) {
 | 
			
		||||
				var h = ch.charCodeAt(0).toString(16);
 | 
			
		||||
				if (h.length % 2) {
 | 
			
		||||
					h = '0' + h;
 | 
			
		||||
				}
 | 
			
		||||
				return h;
 | 
			
		||||
			})
 | 
			
		||||
			.join('');
 | 
			
		||||
	};
 | 
			
		||||
	// TODO are there any nuance differences here?
 | 
			
		||||
	Enc.utf8ToHex = Enc.binToHex;
 | 
			
		||||
 | 
			
		||||
//
 | 
			
		||||
// JWK to SSH (tested working)
 | 
			
		||||
//
 | 
			
		||||
Enc.base64ToHex = function (b64) {
 | 
			
		||||
  var bin = atob(Enc.urlBase64ToBase64(b64));
 | 
			
		||||
  return Enc.binToHex(bin);
 | 
			
		||||
};
 | 
			
		||||
	Enc.hexToBase64 = function(hex) {
 | 
			
		||||
		return btoa(Enc.hexToBin(hex));
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.binToHex = function (bin) {
 | 
			
		||||
  return bin.split('').map(function (ch) {
 | 
			
		||||
    var h = ch.charCodeAt(0).toString(16);
 | 
			
		||||
    if (h.length % 2) { h = '0' + h; }
 | 
			
		||||
    return h;
 | 
			
		||||
  }).join('');
 | 
			
		||||
};
 | 
			
		||||
// TODO are there any nuance differences here?
 | 
			
		||||
Enc.utf8ToHex = Enc.binToHex;
 | 
			
		||||
	Enc.hexToBin = function(hex) {
 | 
			
		||||
		return hex
 | 
			
		||||
			.match(/.{2}/g)
 | 
			
		||||
			.map(function(h) {
 | 
			
		||||
				return String.fromCharCode(parseInt(h, 16));
 | 
			
		||||
			})
 | 
			
		||||
			.join('');
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.hexToBase64 = function (hex) {
 | 
			
		||||
  return btoa(Enc.hexToBin(hex));
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Enc.hexToBin = function (hex) {
 | 
			
		||||
  return hex.match(/.{2}/g).map(function (h) {
 | 
			
		||||
    return String.fromCharCode(parseInt(h, 16));
 | 
			
		||||
  }).join('');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
Enc.urlBase64ToBase64 = function urlsafeBase64ToBase64(str) {
 | 
			
		||||
  var r = str % 4;
 | 
			
		||||
  if (2 === r) {
 | 
			
		||||
    str += '==';
 | 
			
		||||
  } else if (3 === r) {
 | 
			
		||||
    str += '=';
 | 
			
		||||
  }
 | 
			
		||||
  return str.replace(/-/g, '+').replace(/_/g, '/');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}('undefined' !== typeof exports ? module.exports : window ));
 | 
			
		||||
	Enc.urlBase64ToBase64 = function urlsafeBase64ToBase64(str) {
 | 
			
		||||
		var r = str % 4;
 | 
			
		||||
		if (2 === r) {
 | 
			
		||||
			str += '==';
 | 
			
		||||
		} else if (3 === r) {
 | 
			
		||||
			str += '=';
 | 
			
		||||
		}
 | 
			
		||||
		return str.replace(/-/g, '+').replace(/_/g, '/');
 | 
			
		||||
	};
 | 
			
		||||
})('undefined' !== typeof exports ? module.exports : window);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										712
									
								
								lib/keypairs.js
									
									
									
									
									
								
							
							
						
						
									
										712
									
								
								lib/keypairs.js
									
									
									
									
									
								
							@ -1,348 +1,432 @@
 | 
			
		||||
/*global Promise*/
 | 
			
		||||
(function (exports) {
 | 
			
		||||
'use strict';
 | 
			
		||||
(function(exports) {
 | 
			
		||||
	'use strict';
 | 
			
		||||
 | 
			
		||||
var Keypairs = exports.Keypairs = {};
 | 
			
		||||
var Rasha = exports.Rasha;
 | 
			
		||||
var Eckles = exports.Eckles;
 | 
			
		||||
var Enc = exports.Enc || {};
 | 
			
		||||
	var Keypairs = (exports.Keypairs = {});
 | 
			
		||||
	var Rasha = exports.Rasha;
 | 
			
		||||
	var Eckles = exports.Eckles;
 | 
			
		||||
	var Enc = exports.Enc || {};
 | 
			
		||||
 | 
			
		||||
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._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);
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
	Keypairs.export = function(opts) {
 | 
			
		||||
		return Eckles.export(opts).catch(function(err) {
 | 
			
		||||
			return Rasha.export(opts).catch(function() {
 | 
			
		||||
				return Promise.reject(err);
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	 * 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;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * 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.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));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
  /** 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.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;
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
  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';
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
				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 (!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.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'
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
    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('.');
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
Keypairs.signJws = function (opts) {
 | 
			
		||||
  return Keypairs.thumbprint(opts).then(function (thumb) {
 | 
			
		||||
			function sign() {
 | 
			
		||||
				var protect = opts.protected;
 | 
			
		||||
				var payload = opts.payload;
 | 
			
		||||
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
				// 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);
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
    function sign() {
 | 
			
		||||
      var protect = opts.protected;
 | 
			
		||||
      var payload = opts.payload;
 | 
			
		||||
				// 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.binToBuf(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);
 | 
			
		||||
      }
 | 
			
		||||
				// node specifies RSA-SHAxxx even when it's actually ecdsa (it's all encoded x509 shasums anyway)
 | 
			
		||||
				var protected64 = Enc.strToUrlBase64(protectedHeader);
 | 
			
		||||
				var payload64 = Enc.bufToUrlBase64(payload);
 | 
			
		||||
				var msg = protected64 + '.' + payload64;
 | 
			
		||||
 | 
			
		||||
      // 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.binToBuf(payload);
 | 
			
		||||
      }
 | 
			
		||||
				return Keypairs._sign(opts, msg).then(function(buf) {
 | 
			
		||||
					var signedMsg = {
 | 
			
		||||
						protected: protected64,
 | 
			
		||||
						payload: payload64,
 | 
			
		||||
						signature: Enc.bufToUrlBase64(buf)
 | 
			
		||||
					};
 | 
			
		||||
 | 
			
		||||
      // node specifies RSA-SHAxxx even when it's actually ecdsa (it's all encoded x509 shasums anyway)
 | 
			
		||||
      var protected64 = Enc.strToUrlBase64(protectedHeader);
 | 
			
		||||
      var payload64 = Enc.bufToUrlBase64(payload);
 | 
			
		||||
      var msg = protected64 + '.' + payload64;
 | 
			
		||||
					return signedMsg;
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
      return Keypairs._sign(opts, msg).then(function (buf) {
 | 
			
		||||
        var signedMsg = {
 | 
			
		||||
          protected: protected64
 | 
			
		||||
        , payload: payload64
 | 
			
		||||
        , signature: Enc.bufToUrlBase64(buf)
 | 
			
		||||
        };
 | 
			
		||||
			if (opts.jwk) {
 | 
			
		||||
				return sign();
 | 
			
		||||
			} else {
 | 
			
		||||
				return Keypairs.import({ pem: opts.pem }).then(function(pair) {
 | 
			
		||||
					opts.jwk = pair.private;
 | 
			
		||||
					return sign();
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
        return signedMsg;
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
	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._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);
 | 
			
		||||
 | 
			
		||||
    if (opts.jwk) {
 | 
			
		||||
      return sign();
 | 
			
		||||
    } else {
 | 
			
		||||
      return Keypairs.import({ pem: opts.pem }).then(function (pair) {
 | 
			
		||||
        opts.jwk = pair.private;
 | 
			
		||||
        return sign();
 | 
			
		||||
      });
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
		// 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';
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
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._getBits = function (opts) {
 | 
			
		||||
  if (opts.alg) { return opts.alg.replace(/[a-z\-]/ig, ''); }
 | 
			
		||||
  // base64 len to byte len
 | 
			
		||||
  var len = Math.floor((opts.jwk.n||'').length * 0.75);
 | 
			
		||||
		return '256';
 | 
			
		||||
	};
 | 
			
		||||
	Keypairs._getName = function(opts) {
 | 
			
		||||
		if (/EC/i.test(opts.jwk.kty)) {
 | 
			
		||||
			return 'ECDSA';
 | 
			
		||||
		} else {
 | 
			
		||||
			return 'RSASSA-PKCS1-v1_5';
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
  // 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 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);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
  return '256';
 | 
			
		||||
};
 | 
			
		||||
Keypairs._getName = function (opts) {
 | 
			
		||||
  if (/EC/i.test(opts.jwk.kty)) {
 | 
			
		||||
    return 'ECDSA';
 | 
			
		||||
  } else {
 | 
			
		||||
    return 'RSASSA-PKCS1-v1_5';
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
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;
 | 
			
		||||
		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 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); }
 | 
			
		||||
		return Uint8Array.from(
 | 
			
		||||
			head.concat([0x02, r.length], r, [0x02, s.length], s)
 | 
			
		||||
		);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
	function setTime(time) {
 | 
			
		||||
		if ('number' === typeof time) {
 | 
			
		||||
			return time;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
  return Uint8Array.from(head.concat([0x02, r.length], r, [0x02, s.length], s));
 | 
			
		||||
};
 | 
			
		||||
		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"
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
function setTime(time) {
 | 
			
		||||
  if ('number' === typeof time) { return time; }
 | 
			
		||||
		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;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
  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");
 | 
			
		||||
  }
 | 
			
		||||
		return now + mult * num;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
  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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
Enc.hexToBuf = function (hex) {
 | 
			
		||||
  var arr = [];
 | 
			
		||||
  hex.match(/.{2}/g).forEach(function (h) {
 | 
			
		||||
    arr.push(parseInt(h, 16));
 | 
			
		||||
  });
 | 
			
		||||
  return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
 | 
			
		||||
};
 | 
			
		||||
Enc.strToUrlBase64 = function (str) {
 | 
			
		||||
  return Enc.bufToUrlBase64(Enc.binToBuf(str));
 | 
			
		||||
};
 | 
			
		||||
Enc.binToBuf = function (bin) {
 | 
			
		||||
  var arr = bin.split('').map(function (ch) {
 | 
			
		||||
    return ch.charCodeAt(0);
 | 
			
		||||
  });
 | 
			
		||||
  return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}('undefined' !== typeof module ? module.exports : window));
 | 
			
		||||
	Enc.hexToBuf = function(hex) {
 | 
			
		||||
		var arr = [];
 | 
			
		||||
		hex.match(/.{2}/g).forEach(function(h) {
 | 
			
		||||
			arr.push(parseInt(h, 16));
 | 
			
		||||
		});
 | 
			
		||||
		return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
 | 
			
		||||
	};
 | 
			
		||||
	Enc.strToUrlBase64 = function(str) {
 | 
			
		||||
		return Enc.bufToUrlBase64(Enc.binToBuf(str));
 | 
			
		||||
	};
 | 
			
		||||
	Enc.binToBuf = function(bin) {
 | 
			
		||||
		var arr = bin.split('').map(function(ch) {
 | 
			
		||||
			return ch.charCodeAt(0);
 | 
			
		||||
		});
 | 
			
		||||
		return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
 | 
			
		||||
	};
 | 
			
		||||
})('undefined' !== typeof module ? module.exports : window);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										398
									
								
								lib/rsa.js
									
									
									
									
									
								
							
							
						
						
									
										398
									
								
								lib/rsa.js
									
									
									
									
									
								
							@ -1,187 +1,235 @@
 | 
			
		||||
/*global Promise*/
 | 
			
		||||
(function (exports) {
 | 
			
		||||
'use strict';
 | 
			
		||||
(function(exports) {
 | 
			
		||||
	'use strict';
 | 
			
		||||
 | 
			
		||||
var RSA = exports.Rasha = {};
 | 
			
		||||
var x509 = exports.x509;
 | 
			
		||||
if ('undefined' !== typeof module) { module.exports = RSA; }
 | 
			
		||||
var PEM = exports.PEM;
 | 
			
		||||
var SSH = exports.SSH;
 | 
			
		||||
var Enc = {};
 | 
			
		||||
var textEncoder = new TextEncoder();
 | 
			
		||||
	var RSA = (exports.Rasha = {});
 | 
			
		||||
	var x509 = exports.x509;
 | 
			
		||||
	if ('undefined' !== typeof module) {
 | 
			
		||||
		module.exports = RSA;
 | 
			
		||||
	}
 | 
			
		||||
	var PEM = exports.PEM;
 | 
			
		||||
	var SSH = exports.SSH;
 | 
			
		||||
	var Enc = {};
 | 
			
		||||
	var textEncoder = new TextEncoder();
 | 
			
		||||
 | 
			
		||||
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.";
 | 
			
		||||
RSA._universal = "Bluecrypt only supports crypto with standard cross-browser and cross-platform support.";
 | 
			
		||||
RSA.generate = function (opts) {
 | 
			
		||||
  var wcOpts = {};
 | 
			
		||||
  if (!opts) { opts = {}; }
 | 
			
		||||
  if (!opts.kty) { opts.kty = 'RSA'; }
 | 
			
		||||
	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.";
 | 
			
		||||
	RSA._universal =
 | 
			
		||||
		'Bluecrypt only supports crypto with standard cross-browser and cross-platform support.';
 | 
			
		||||
	RSA.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]);
 | 
			
		||||
		// 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 })
 | 
			
		||||
      };
 | 
			
		||||
    });
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
		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 })
 | 
			
		||||
						};
 | 
			
		||||
					});
 | 
			
		||||
			});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
// 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;
 | 
			
		||||
};
 | 
			
		||||
	// 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;
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
// 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 window.crypto.subtle.digest(
 | 
			
		||||
    { name: alg }
 | 
			
		||||
  , textEncoder.encode('{"e":"' + jwk.e + '","kty":"RSA","n":"' + jwk.n + '"}')
 | 
			
		||||
  ).then(function (hash) {
 | 
			
		||||
    return Enc.bufToUrlBase64(new Uint8Array(hash));
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
	// 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 window.crypto.subtle
 | 
			
		||||
			.digest(
 | 
			
		||||
				{ name: alg },
 | 
			
		||||
				textEncoder.encode(
 | 
			
		||||
					'{"e":"' + jwk.e + '","kty":"RSA","n":"' + jwk.n + '"}'
 | 
			
		||||
				)
 | 
			
		||||
			)
 | 
			
		||||
			.then(function(hash) {
 | 
			
		||||
				return Enc.bufToUrlBase64(new Uint8Array(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.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");
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
	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);
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
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);
 | 
			
		||||
  });
 | 
			
		||||
};
 | 
			
		||||
			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
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
	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);
 | 
			
		||||
		});
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
Enc.bufToUrlBase64 = function (u8) {
 | 
			
		||||
  return Enc.bufToBase64(u8)
 | 
			
		||||
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
 | 
			
		||||
};
 | 
			
		||||
	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);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
}('undefined' !== typeof module ? module.exports : window));
 | 
			
		||||
	Enc.bufToBase64 = function(u8) {
 | 
			
		||||
		var bin = '';
 | 
			
		||||
		u8.forEach(function(i) {
 | 
			
		||||
			bin += String.fromCharCode(i);
 | 
			
		||||
		});
 | 
			
		||||
		return btoa(bin);
 | 
			
		||||
	};
 | 
			
		||||
})('undefined' !== typeof module ? module.exports : window);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										538
									
								
								lib/x509.js
									
									
									
									
									
								
							
							
						
						
									
										538
									
								
								lib/x509.js
									
									
									
									
									
								
							@ -1,279 +1,301 @@
 | 
			
		||||
(function (exports) {
 | 
			
		||||
  'use strict';
 | 
			
		||||
(function(exports) {
 | 
			
		||||
	'use strict';
 | 
			
		||||
 | 
			
		||||
  var x509 = exports.x509 = {};
 | 
			
		||||
  var ASN1 = exports.ASN1;
 | 
			
		||||
  var Enc = exports.Enc;
 | 
			
		||||
	var x509 = (exports.x509 = {});
 | 
			
		||||
	var ASN1 = exports.ASN1;
 | 
			
		||||
	var Enc = exports.Enc;
 | 
			
		||||
 | 
			
		||||
  // 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();
 | 
			
		||||
  // 1.2.840.10045.2.1
 | 
			
		||||
  // ecPublicKey (ANSI X9.62 public key type)
 | 
			
		||||
  var OBJ_ID_EC_PUB = '06 07 2A8648CE3D0201'.replace(/\s+/g, '').toLowerCase();
 | 
			
		||||
	// 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();
 | 
			
		||||
	// 1.2.840.10045.2.1
 | 
			
		||||
	// ecPublicKey (ANSI X9.62 public key type)
 | 
			
		||||
	var OBJ_ID_EC_PUB = '06 07 2A8648CE3D0201'
 | 
			
		||||
		.replace(/\s+/g, '')
 | 
			
		||||
		.toLowerCase();
 | 
			
		||||
 | 
			
		||||
  x509.parseSec1 = function parseEcOnlyPrivkey(u8, jwk) {
 | 
			
		||||
    var index = 7;
 | 
			
		||||
    var len = 32;
 | 
			
		||||
    var olen = OBJ_ID_EC.length / 2;
 | 
			
		||||
	x509.parseSec1 = function parseEcOnlyPrivkey(u8, jwk) {
 | 
			
		||||
		var index = 7;
 | 
			
		||||
		var len = 32;
 | 
			
		||||
		var olen = OBJ_ID_EC.length / 2;
 | 
			
		||||
 | 
			
		||||
    if ("P-384" === jwk.crv) {
 | 
			
		||||
      olen = OBJ_ID_EC_384.length / 2;
 | 
			
		||||
      index = 8;
 | 
			
		||||
      len = 48;
 | 
			
		||||
    }
 | 
			
		||||
    if (len !== u8[index - 1]) {
 | 
			
		||||
      throw new Error("Unexpected bitlength " + len);
 | 
			
		||||
    }
 | 
			
		||||
		if ('P-384' === jwk.crv) {
 | 
			
		||||
			olen = OBJ_ID_EC_384.length / 2;
 | 
			
		||||
			index = 8;
 | 
			
		||||
			len = 48;
 | 
			
		||||
		}
 | 
			
		||||
		if (len !== u8[index - 1]) {
 | 
			
		||||
			throw new Error('Unexpected bitlength ' + len);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
    // private part is d
 | 
			
		||||
    var d = u8.slice(index, index + len);
 | 
			
		||||
    // compression bit index
 | 
			
		||||
    var ci = index + len + 2 + olen + 2 + 3;
 | 
			
		||||
    var c = u8[ci];
 | 
			
		||||
    var x, y;
 | 
			
		||||
		// private part is d
 | 
			
		||||
		var d = u8.slice(index, index + len);
 | 
			
		||||
		// compression bit index
 | 
			
		||||
		var ci = index + len + 2 + olen + 2 + 3;
 | 
			
		||||
		var c = u8[ci];
 | 
			
		||||
		var x, y;
 | 
			
		||||
 | 
			
		||||
    if (0x04 === c) {
 | 
			
		||||
      y = u8.slice(ci + 1 + len, ci + 1 + len + len);
 | 
			
		||||
    } else if (0x02 !== c) {
 | 
			
		||||
      throw new Error("not a supported EC private key");
 | 
			
		||||
    }
 | 
			
		||||
    x = u8.slice(ci + 1, ci + 1 + len);
 | 
			
		||||
		if (0x04 === c) {
 | 
			
		||||
			y = u8.slice(ci + 1 + len, ci + 1 + len + len);
 | 
			
		||||
		} else if (0x02 !== c) {
 | 
			
		||||
			throw new Error('not a supported EC private key');
 | 
			
		||||
		}
 | 
			
		||||
		x = u8.slice(ci + 1, ci + 1 + len);
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      kty: jwk.kty
 | 
			
		||||
      , crv: jwk.crv
 | 
			
		||||
      , d: Enc.bufToUrlBase64(d)
 | 
			
		||||
      //, dh: Enc.bufToHex(d)
 | 
			
		||||
      , x: Enc.bufToUrlBase64(x)
 | 
			
		||||
      //, xh: Enc.bufToHex(x)
 | 
			
		||||
      , y: Enc.bufToUrlBase64(y)
 | 
			
		||||
      //, yh: Enc.bufToHex(y)
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
		return {
 | 
			
		||||
			kty: jwk.kty,
 | 
			
		||||
			crv: jwk.crv,
 | 
			
		||||
			d: Enc.bufToUrlBase64(d),
 | 
			
		||||
			//, dh: Enc.bufToHex(d)
 | 
			
		||||
			x: Enc.bufToUrlBase64(x),
 | 
			
		||||
			//, xh: Enc.bufToHex(x)
 | 
			
		||||
			y: Enc.bufToUrlBase64(y)
 | 
			
		||||
			//, yh: Enc.bufToHex(y)
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
  x509.packPkcs1 = function (jwk) {
 | 
			
		||||
    var n = ASN1.UInt(Enc.base64ToHex(jwk.n));
 | 
			
		||||
    var e = ASN1.UInt(Enc.base64ToHex(jwk.e));
 | 
			
		||||
	x509.packPkcs1 = function(jwk) {
 | 
			
		||||
		var n = ASN1.UInt(Enc.base64ToHex(jwk.n));
 | 
			
		||||
		var e = ASN1.UInt(Enc.base64ToHex(jwk.e));
 | 
			
		||||
 | 
			
		||||
    if (!jwk.d) {
 | 
			
		||||
      return Enc.hexToBuf(ASN1('30', n, e));
 | 
			
		||||
    }
 | 
			
		||||
		if (!jwk.d) {
 | 
			
		||||
			return Enc.hexToBuf(ASN1('30', n, e));
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
    return Enc.hexToBuf(ASN1('30'
 | 
			
		||||
    , ASN1.UInt('00')
 | 
			
		||||
    , n
 | 
			
		||||
    , e
 | 
			
		||||
    , ASN1.UInt(Enc.base64ToHex(jwk.d))
 | 
			
		||||
    , ASN1.UInt(Enc.base64ToHex(jwk.p))
 | 
			
		||||
    , ASN1.UInt(Enc.base64ToHex(jwk.q))
 | 
			
		||||
    , ASN1.UInt(Enc.base64ToHex(jwk.dp))
 | 
			
		||||
    , ASN1.UInt(Enc.base64ToHex(jwk.dq))
 | 
			
		||||
    , ASN1.UInt(Enc.base64ToHex(jwk.qi))
 | 
			
		||||
    ));
 | 
			
		||||
  };
 | 
			
		||||
		return Enc.hexToBuf(
 | 
			
		||||
			ASN1(
 | 
			
		||||
				'30',
 | 
			
		||||
				ASN1.UInt('00'),
 | 
			
		||||
				n,
 | 
			
		||||
				e,
 | 
			
		||||
				ASN1.UInt(Enc.base64ToHex(jwk.d)),
 | 
			
		||||
				ASN1.UInt(Enc.base64ToHex(jwk.p)),
 | 
			
		||||
				ASN1.UInt(Enc.base64ToHex(jwk.q)),
 | 
			
		||||
				ASN1.UInt(Enc.base64ToHex(jwk.dp)),
 | 
			
		||||
				ASN1.UInt(Enc.base64ToHex(jwk.dq)),
 | 
			
		||||
				ASN1.UInt(Enc.base64ToHex(jwk.qi))
 | 
			
		||||
			)
 | 
			
		||||
		);
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
  x509.parsePkcs8 = function parseEcPkcs8(u8, jwk) {
 | 
			
		||||
    var index = 24 + (OBJ_ID_EC.length / 2);
 | 
			
		||||
    var len = 32;
 | 
			
		||||
    if ("P-384" === jwk.crv) {
 | 
			
		||||
      index = 24 + (OBJ_ID_EC_384.length / 2) + 2;
 | 
			
		||||
      len = 48;
 | 
			
		||||
    }
 | 
			
		||||
	x509.parsePkcs8 = function parseEcPkcs8(u8, jwk) {
 | 
			
		||||
		var index = 24 + OBJ_ID_EC.length / 2;
 | 
			
		||||
		var len = 32;
 | 
			
		||||
		if ('P-384' === jwk.crv) {
 | 
			
		||||
			index = 24 + OBJ_ID_EC_384.length / 2 + 2;
 | 
			
		||||
			len = 48;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
    //console.log(index, u8.slice(index));
 | 
			
		||||
    if (0x04 !== u8[index]) {
 | 
			
		||||
      //console.log(jwk);
 | 
			
		||||
      throw new Error("privkey not found");
 | 
			
		||||
    }
 | 
			
		||||
    var d = u8.slice(index + 2, index + 2 + len);
 | 
			
		||||
    var ci = index + 2 + len + 5;
 | 
			
		||||
    var xi = ci + 1;
 | 
			
		||||
    var x = u8.slice(xi, xi + len);
 | 
			
		||||
    var yi = xi + len;
 | 
			
		||||
    var y;
 | 
			
		||||
    if (0x04 === u8[ci]) {
 | 
			
		||||
      y = u8.slice(yi, yi + len);
 | 
			
		||||
    } else if (0x02 !== u8[ci]) {
 | 
			
		||||
      throw new Error("invalid compression bit (expected 0x04 or 0x02)");
 | 
			
		||||
    }
 | 
			
		||||
		//console.log(index, u8.slice(index));
 | 
			
		||||
		if (0x04 !== u8[index]) {
 | 
			
		||||
			//console.log(jwk);
 | 
			
		||||
			throw new Error('privkey not found');
 | 
			
		||||
		}
 | 
			
		||||
		var d = u8.slice(index + 2, index + 2 + len);
 | 
			
		||||
		var ci = index + 2 + len + 5;
 | 
			
		||||
		var xi = ci + 1;
 | 
			
		||||
		var x = u8.slice(xi, xi + len);
 | 
			
		||||
		var yi = xi + len;
 | 
			
		||||
		var y;
 | 
			
		||||
		if (0x04 === u8[ci]) {
 | 
			
		||||
			y = u8.slice(yi, yi + len);
 | 
			
		||||
		} else if (0x02 !== u8[ci]) {
 | 
			
		||||
			throw new Error('invalid compression bit (expected 0x04 or 0x02)');
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      kty: jwk.kty
 | 
			
		||||
      , crv: jwk.crv
 | 
			
		||||
      , d: Enc.bufToUrlBase64(d)
 | 
			
		||||
      //, dh: Enc.bufToHex(d)
 | 
			
		||||
      , x: Enc.bufToUrlBase64(x)
 | 
			
		||||
      //, xh: Enc.bufToHex(x)
 | 
			
		||||
      , y: Enc.bufToUrlBase64(y)
 | 
			
		||||
      //, yh: Enc.bufToHex(y)
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
		return {
 | 
			
		||||
			kty: jwk.kty,
 | 
			
		||||
			crv: jwk.crv,
 | 
			
		||||
			d: Enc.bufToUrlBase64(d),
 | 
			
		||||
			//, dh: Enc.bufToHex(d)
 | 
			
		||||
			x: Enc.bufToUrlBase64(x),
 | 
			
		||||
			//, xh: Enc.bufToHex(x)
 | 
			
		||||
			y: Enc.bufToUrlBase64(y)
 | 
			
		||||
			//, yh: Enc.bufToHex(y)
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
  x509.parseSpki = function parsePem(u8, jwk) {
 | 
			
		||||
    var ci = 16 + OBJ_ID_EC.length / 2;
 | 
			
		||||
    var len = 32;
 | 
			
		||||
	x509.parseSpki = function parsePem(u8, jwk) {
 | 
			
		||||
		var ci = 16 + OBJ_ID_EC.length / 2;
 | 
			
		||||
		var len = 32;
 | 
			
		||||
 | 
			
		||||
    if ("P-384" === jwk.crv) {
 | 
			
		||||
      ci = 16 + OBJ_ID_EC_384.length / 2;
 | 
			
		||||
      len = 48;
 | 
			
		||||
    }
 | 
			
		||||
		if ('P-384' === jwk.crv) {
 | 
			
		||||
			ci = 16 + OBJ_ID_EC_384.length / 2;
 | 
			
		||||
			len = 48;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
    var c = u8[ci];
 | 
			
		||||
    var xi = ci + 1;
 | 
			
		||||
    var x = u8.slice(xi, xi + len);
 | 
			
		||||
    var yi = xi + len;
 | 
			
		||||
    var y;
 | 
			
		||||
    if (0x04 === c) {
 | 
			
		||||
      y = u8.slice(yi, yi + len);
 | 
			
		||||
    } else if (0x02 !== c) {
 | 
			
		||||
      throw new Error("not a supported EC private key");
 | 
			
		||||
    }
 | 
			
		||||
		var c = u8[ci];
 | 
			
		||||
		var xi = ci + 1;
 | 
			
		||||
		var x = u8.slice(xi, xi + len);
 | 
			
		||||
		var yi = xi + len;
 | 
			
		||||
		var y;
 | 
			
		||||
		if (0x04 === c) {
 | 
			
		||||
			y = u8.slice(yi, yi + len);
 | 
			
		||||
		} else if (0x02 !== c) {
 | 
			
		||||
			throw new Error('not a supported EC private key');
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
    return {
 | 
			
		||||
      kty: jwk.kty
 | 
			
		||||
      , crv: jwk.crv
 | 
			
		||||
      , x: Enc.bufToUrlBase64(x)
 | 
			
		||||
      //, xh: Enc.bufToHex(x)
 | 
			
		||||
      , y: Enc.bufToUrlBase64(y)
 | 
			
		||||
      //, yh: Enc.bufToHex(y)
 | 
			
		||||
    };
 | 
			
		||||
  };
 | 
			
		||||
  x509.parsePkix = x509.parseSpki;
 | 
			
		||||
		return {
 | 
			
		||||
			kty: jwk.kty,
 | 
			
		||||
			crv: jwk.crv,
 | 
			
		||||
			x: Enc.bufToUrlBase64(x),
 | 
			
		||||
			//, xh: Enc.bufToHex(x)
 | 
			
		||||
			y: Enc.bufToUrlBase64(y)
 | 
			
		||||
			//, yh: Enc.bufToHex(y)
 | 
			
		||||
		};
 | 
			
		||||
	};
 | 
			
		||||
	x509.parsePkix = x509.parseSpki;
 | 
			
		||||
 | 
			
		||||
  x509.packSec1 = function (jwk) {
 | 
			
		||||
    var d = Enc.base64ToHex(jwk.d);
 | 
			
		||||
    var x = Enc.base64ToHex(jwk.x);
 | 
			
		||||
    var y = Enc.base64ToHex(jwk.y);
 | 
			
		||||
    var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;
 | 
			
		||||
    return Enc.hexToBuf(
 | 
			
		||||
      ASN1('30'
 | 
			
		||||
        , ASN1.UInt('01')
 | 
			
		||||
        , ASN1('04', d)
 | 
			
		||||
        , ASN1('A0', objId)
 | 
			
		||||
        , ASN1('A1', ASN1.BitStr('04' + x + y)))
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
  /**
 | 
			
		||||
   * take a private jwk and creates a der from it
 | 
			
		||||
   * @param {*} jwk
 | 
			
		||||
   */
 | 
			
		||||
  x509.packPkcs8 = function (jwk) {
 | 
			
		||||
    if ('RSA' === jwk.kty) {
 | 
			
		||||
      if (!jwk.d) {
 | 
			
		||||
        // Public RSA
 | 
			
		||||
        return Enc.hexToBuf(ASN1('30'
 | 
			
		||||
          , ASN1('30'
 | 
			
		||||
            , ASN1('06', '2a864886f70d010101')
 | 
			
		||||
            , ASN1('05')
 | 
			
		||||
          )
 | 
			
		||||
          , ASN1.BitStr(ASN1('30'
 | 
			
		||||
            , ASN1.UInt(Enc.base64ToHex(jwk.n))
 | 
			
		||||
            , ASN1.UInt(Enc.base64ToHex(jwk.e))
 | 
			
		||||
          ))
 | 
			
		||||
        ));
 | 
			
		||||
      }
 | 
			
		||||
	x509.packSec1 = function(jwk) {
 | 
			
		||||
		var d = Enc.base64ToHex(jwk.d);
 | 
			
		||||
		var x = Enc.base64ToHex(jwk.x);
 | 
			
		||||
		var y = Enc.base64ToHex(jwk.y);
 | 
			
		||||
		var objId = 'P-256' === jwk.crv ? OBJ_ID_EC : OBJ_ID_EC_384;
 | 
			
		||||
		return Enc.hexToBuf(
 | 
			
		||||
			ASN1(
 | 
			
		||||
				'30',
 | 
			
		||||
				ASN1.UInt('01'),
 | 
			
		||||
				ASN1('04', d),
 | 
			
		||||
				ASN1('A0', objId),
 | 
			
		||||
				ASN1('A1', ASN1.BitStr('04' + x + y))
 | 
			
		||||
			)
 | 
			
		||||
		);
 | 
			
		||||
	};
 | 
			
		||||
	/**
 | 
			
		||||
	 * take a private jwk and creates a der from it
 | 
			
		||||
	 * @param {*} jwk
 | 
			
		||||
	 */
 | 
			
		||||
	x509.packPkcs8 = function(jwk) {
 | 
			
		||||
		if ('RSA' === jwk.kty) {
 | 
			
		||||
			if (!jwk.d) {
 | 
			
		||||
				// Public RSA
 | 
			
		||||
				return Enc.hexToBuf(
 | 
			
		||||
					ASN1(
 | 
			
		||||
						'30',
 | 
			
		||||
						ASN1(
 | 
			
		||||
							'30',
 | 
			
		||||
							ASN1('06', '2a864886f70d010101'),
 | 
			
		||||
							ASN1('05')
 | 
			
		||||
						),
 | 
			
		||||
						ASN1.BitStr(
 | 
			
		||||
							ASN1(
 | 
			
		||||
								'30',
 | 
			
		||||
								ASN1.UInt(Enc.base64ToHex(jwk.n)),
 | 
			
		||||
								ASN1.UInt(Enc.base64ToHex(jwk.e))
 | 
			
		||||
							)
 | 
			
		||||
						)
 | 
			
		||||
					)
 | 
			
		||||
				);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
      // Private RSA
 | 
			
		||||
      return Enc.hexToBuf(ASN1('30'
 | 
			
		||||
        , ASN1.UInt('00')
 | 
			
		||||
        , ASN1('30'
 | 
			
		||||
          , ASN1('06', '2a864886f70d010101')
 | 
			
		||||
          , ASN1('05')
 | 
			
		||||
        )
 | 
			
		||||
        , ASN1('04'
 | 
			
		||||
          , ASN1('30'
 | 
			
		||||
            , ASN1.UInt('00')
 | 
			
		||||
            , ASN1.UInt(Enc.base64ToHex(jwk.n))
 | 
			
		||||
            , ASN1.UInt(Enc.base64ToHex(jwk.e))
 | 
			
		||||
            , ASN1.UInt(Enc.base64ToHex(jwk.d))
 | 
			
		||||
            , ASN1.UInt(Enc.base64ToHex(jwk.p))
 | 
			
		||||
            , ASN1.UInt(Enc.base64ToHex(jwk.q))
 | 
			
		||||
            , ASN1.UInt(Enc.base64ToHex(jwk.dp))
 | 
			
		||||
            , ASN1.UInt(Enc.base64ToHex(jwk.dq))
 | 
			
		||||
            , ASN1.UInt(Enc.base64ToHex(jwk.qi))
 | 
			
		||||
          )
 | 
			
		||||
        )
 | 
			
		||||
      ));
 | 
			
		||||
    }
 | 
			
		||||
			// Private RSA
 | 
			
		||||
			return Enc.hexToBuf(
 | 
			
		||||
				ASN1(
 | 
			
		||||
					'30',
 | 
			
		||||
					ASN1.UInt('00'),
 | 
			
		||||
					ASN1('30', ASN1('06', '2a864886f70d010101'), ASN1('05')),
 | 
			
		||||
					ASN1(
 | 
			
		||||
						'04',
 | 
			
		||||
						ASN1(
 | 
			
		||||
							'30',
 | 
			
		||||
							ASN1.UInt('00'),
 | 
			
		||||
							ASN1.UInt(Enc.base64ToHex(jwk.n)),
 | 
			
		||||
							ASN1.UInt(Enc.base64ToHex(jwk.e)),
 | 
			
		||||
							ASN1.UInt(Enc.base64ToHex(jwk.d)),
 | 
			
		||||
							ASN1.UInt(Enc.base64ToHex(jwk.p)),
 | 
			
		||||
							ASN1.UInt(Enc.base64ToHex(jwk.q)),
 | 
			
		||||
							ASN1.UInt(Enc.base64ToHex(jwk.dp)),
 | 
			
		||||
							ASN1.UInt(Enc.base64ToHex(jwk.dq)),
 | 
			
		||||
							ASN1.UInt(Enc.base64ToHex(jwk.qi))
 | 
			
		||||
						)
 | 
			
		||||
					)
 | 
			
		||||
				)
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
    var d = Enc.base64ToHex(jwk.d);
 | 
			
		||||
    var x = Enc.base64ToHex(jwk.x);
 | 
			
		||||
    var y = Enc.base64ToHex(jwk.y);
 | 
			
		||||
    var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;
 | 
			
		||||
    return Enc.hexToBuf(
 | 
			
		||||
      ASN1('30'
 | 
			
		||||
        , ASN1.UInt('00')
 | 
			
		||||
        , ASN1('30'
 | 
			
		||||
          , OBJ_ID_EC_PUB
 | 
			
		||||
          , objId
 | 
			
		||||
        )
 | 
			
		||||
        , ASN1('04'
 | 
			
		||||
          , ASN1('30'
 | 
			
		||||
            , ASN1.UInt('01')
 | 
			
		||||
            , ASN1('04', d)
 | 
			
		||||
            , ASN1('A1', ASN1.BitStr('04' + x + y)))))
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
  x509.packSpki = function (jwk) {
 | 
			
		||||
    if (/EC/i.test(jwk.kty)) {
 | 
			
		||||
      return x509.packSpkiEc(jwk);
 | 
			
		||||
    }
 | 
			
		||||
    return x509.packSpkiRsa(jwk);
 | 
			
		||||
  };
 | 
			
		||||
  x509.packSpkiRsa = function (jwk) {
 | 
			
		||||
  if (!jwk.d) {
 | 
			
		||||
    // Public RSA
 | 
			
		||||
    return Enc.hexToBuf(ASN1('30'
 | 
			
		||||
      , ASN1('30'
 | 
			
		||||
        , ASN1('06', '2a864886f70d010101')
 | 
			
		||||
        , ASN1('05')
 | 
			
		||||
      )
 | 
			
		||||
      , ASN1.BitStr(ASN1('30'
 | 
			
		||||
        , ASN1.UInt(Enc.base64ToHex(jwk.n))
 | 
			
		||||
        , ASN1.UInt(Enc.base64ToHex(jwk.e))
 | 
			
		||||
      ))
 | 
			
		||||
    ));
 | 
			
		||||
  }
 | 
			
		||||
		var d = Enc.base64ToHex(jwk.d);
 | 
			
		||||
		var x = Enc.base64ToHex(jwk.x);
 | 
			
		||||
		var y = Enc.base64ToHex(jwk.y);
 | 
			
		||||
		var objId = 'P-256' === jwk.crv ? OBJ_ID_EC : OBJ_ID_EC_384;
 | 
			
		||||
		return Enc.hexToBuf(
 | 
			
		||||
			ASN1(
 | 
			
		||||
				'30',
 | 
			
		||||
				ASN1.UInt('00'),
 | 
			
		||||
				ASN1('30', OBJ_ID_EC_PUB, objId),
 | 
			
		||||
				ASN1(
 | 
			
		||||
					'04',
 | 
			
		||||
					ASN1(
 | 
			
		||||
						'30',
 | 
			
		||||
						ASN1.UInt('01'),
 | 
			
		||||
						ASN1('04', d),
 | 
			
		||||
						ASN1('A1', ASN1.BitStr('04' + x + y))
 | 
			
		||||
					)
 | 
			
		||||
				)
 | 
			
		||||
			)
 | 
			
		||||
		);
 | 
			
		||||
	};
 | 
			
		||||
	x509.packSpki = function(jwk) {
 | 
			
		||||
		if (/EC/i.test(jwk.kty)) {
 | 
			
		||||
			return x509.packSpkiEc(jwk);
 | 
			
		||||
		}
 | 
			
		||||
		return x509.packSpkiRsa(jwk);
 | 
			
		||||
	};
 | 
			
		||||
	x509.packSpkiRsa = function(jwk) {
 | 
			
		||||
		if (!jwk.d) {
 | 
			
		||||
			// Public RSA
 | 
			
		||||
			return Enc.hexToBuf(
 | 
			
		||||
				ASN1(
 | 
			
		||||
					'30',
 | 
			
		||||
					ASN1('30', ASN1('06', '2a864886f70d010101'), ASN1('05')),
 | 
			
		||||
					ASN1.BitStr(
 | 
			
		||||
						ASN1(
 | 
			
		||||
							'30',
 | 
			
		||||
							ASN1.UInt(Enc.base64ToHex(jwk.n)),
 | 
			
		||||
							ASN1.UInt(Enc.base64ToHex(jwk.e))
 | 
			
		||||
						)
 | 
			
		||||
					)
 | 
			
		||||
				)
 | 
			
		||||
			);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
  // Private RSA
 | 
			
		||||
  return Enc.hexToBuf(ASN1('30'
 | 
			
		||||
    , ASN1.UInt('00')
 | 
			
		||||
    , ASN1('30'
 | 
			
		||||
      , ASN1('06', '2a864886f70d010101')
 | 
			
		||||
      , ASN1('05')
 | 
			
		||||
    )
 | 
			
		||||
    , ASN1('04'
 | 
			
		||||
      , ASN1('30'
 | 
			
		||||
        , ASN1.UInt('00')
 | 
			
		||||
        , ASN1.UInt(Enc.base64ToHex(jwk.n))
 | 
			
		||||
        , ASN1.UInt(Enc.base64ToHex(jwk.e))
 | 
			
		||||
        , ASN1.UInt(Enc.base64ToHex(jwk.d))
 | 
			
		||||
        , ASN1.UInt(Enc.base64ToHex(jwk.p))
 | 
			
		||||
        , ASN1.UInt(Enc.base64ToHex(jwk.q))
 | 
			
		||||
        , ASN1.UInt(Enc.base64ToHex(jwk.dp))
 | 
			
		||||
        , ASN1.UInt(Enc.base64ToHex(jwk.dq))
 | 
			
		||||
        , ASN1.UInt(Enc.base64ToHex(jwk.qi))
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
  ));
 | 
			
		||||
};
 | 
			
		||||
  x509.packSpkiEc = function (jwk) {
 | 
			
		||||
    var x = Enc.base64ToHex(jwk.x);
 | 
			
		||||
    var y = Enc.base64ToHex(jwk.y);
 | 
			
		||||
    var objId = ('P-256' === jwk.crv) ? OBJ_ID_EC : OBJ_ID_EC_384;
 | 
			
		||||
    return Enc.hexToBuf(
 | 
			
		||||
      ASN1('30'
 | 
			
		||||
        , ASN1('30'
 | 
			
		||||
          , OBJ_ID_EC_PUB
 | 
			
		||||
          , objId
 | 
			
		||||
        )
 | 
			
		||||
        , ASN1.BitStr('04' + x + y))
 | 
			
		||||
    );
 | 
			
		||||
  };
 | 
			
		||||
  x509.packPkix = x509.packSpki;
 | 
			
		||||
 | 
			
		||||
}('undefined' !== typeof module ? module.exports : window));
 | 
			
		||||
		// Private RSA
 | 
			
		||||
		return Enc.hexToBuf(
 | 
			
		||||
			ASN1(
 | 
			
		||||
				'30',
 | 
			
		||||
				ASN1.UInt('00'),
 | 
			
		||||
				ASN1('30', ASN1('06', '2a864886f70d010101'), ASN1('05')),
 | 
			
		||||
				ASN1(
 | 
			
		||||
					'04',
 | 
			
		||||
					ASN1(
 | 
			
		||||
						'30',
 | 
			
		||||
						ASN1.UInt('00'),
 | 
			
		||||
						ASN1.UInt(Enc.base64ToHex(jwk.n)),
 | 
			
		||||
						ASN1.UInt(Enc.base64ToHex(jwk.e)),
 | 
			
		||||
						ASN1.UInt(Enc.base64ToHex(jwk.d)),
 | 
			
		||||
						ASN1.UInt(Enc.base64ToHex(jwk.p)),
 | 
			
		||||
						ASN1.UInt(Enc.base64ToHex(jwk.q)),
 | 
			
		||||
						ASN1.UInt(Enc.base64ToHex(jwk.dp)),
 | 
			
		||||
						ASN1.UInt(Enc.base64ToHex(jwk.dq)),
 | 
			
		||||
						ASN1.UInt(Enc.base64ToHex(jwk.qi))
 | 
			
		||||
					)
 | 
			
		||||
				)
 | 
			
		||||
			)
 | 
			
		||||
		);
 | 
			
		||||
	};
 | 
			
		||||
	x509.packSpkiEc = function(jwk) {
 | 
			
		||||
		var x = Enc.base64ToHex(jwk.x);
 | 
			
		||||
		var y = Enc.base64ToHex(jwk.y);
 | 
			
		||||
		var objId = 'P-256' === jwk.crv ? OBJ_ID_EC : OBJ_ID_EC_384;
 | 
			
		||||
		return Enc.hexToBuf(
 | 
			
		||||
			ASN1(
 | 
			
		||||
				'30',
 | 
			
		||||
				ASN1('30', OBJ_ID_EC_PUB, objId),
 | 
			
		||||
				ASN1.BitStr('04' + x + y)
 | 
			
		||||
			)
 | 
			
		||||
		);
 | 
			
		||||
	};
 | 
			
		||||
	x509.packPkix = x509.packSpki;
 | 
			
		||||
})('undefined' !== typeof module ? module.exports : window);
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										26
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										26
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							@ -1,6 +1,6 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "@bluecrypt/acme",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "name": "acme",
 | 
			
		||||
  "version": "2.0.0-wip.0",
 | 
			
		||||
  "lockfileVersion": 1,
 | 
			
		||||
  "requires": true,
 | 
			
		||||
  "dependencies": {
 | 
			
		||||
@ -82,6 +82,12 @@
 | 
			
		||||
        "glob": "^7.1.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "commander": {
 | 
			
		||||
      "version": "2.20.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.1.tgz",
 | 
			
		||||
      "integrity": "sha512-cCuLsMhJeWQ/ZpsFTbE765kvVfoeSddc4nU3up4fV+fDBcfUXnbITJ+JzhkdjzOqhURjZgujxaioam4RM9yGUg==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "concat-map": {
 | 
			
		||||
      "version": "0.0.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
 | 
			
		||||
@ -509,6 +515,12 @@
 | 
			
		||||
      "integrity": "sha512-BvE/TwpZX4FXExxOxZyRGQQv651MSwmWKZGqvmPcRIjDqWub67kTKuIMx43cZZrS/cBBzwBcNDWoFxt2XEFIpQ==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "source-map": {
 | 
			
		||||
      "version": "0.6.1",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
 | 
			
		||||
      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
 | 
			
		||||
      "dev": true
 | 
			
		||||
    },
 | 
			
		||||
    "statuses": {
 | 
			
		||||
      "version": "1.4.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.4.0.tgz",
 | 
			
		||||
@ -525,6 +537,16 @@
 | 
			
		||||
        "mime-types": "~2.1.24"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "uglify-js": {
 | 
			
		||||
      "version": "3.6.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.6.0.tgz",
 | 
			
		||||
      "integrity": "sha512-W+jrUHJr3DXKhrsS7NUVxn3zqMOFn0hL/Ei6v0anCIMoKC93TjcflTagwIHLW7SfMFfiQuktQyFVCFHGUE0+yg==",
 | 
			
		||||
      "dev": true,
 | 
			
		||||
      "requires": {
 | 
			
		||||
        "commander": "~2.20.0",
 | 
			
		||||
        "source-map": "~0.6.1"
 | 
			
		||||
      }
 | 
			
		||||
    },
 | 
			
		||||
    "unpipe": {
 | 
			
		||||
      "version": "1.0.0",
 | 
			
		||||
      "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										82
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								package.json
									
									
									
									
									
								
							@ -1,42 +1,44 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "@bluecrypt/acme",
 | 
			
		||||
  "version": "1.0.1",
 | 
			
		||||
  "description": "Free SSL certificates through Let's Encrypt, right in your browser",
 | 
			
		||||
  "main": "bluecrypt-acme.js",
 | 
			
		||||
  "homepage": "https://rootprojects.org/acme/",
 | 
			
		||||
  "directories": {
 | 
			
		||||
    "lib": "lib"
 | 
			
		||||
  },
 | 
			
		||||
  "files": [
 | 
			
		||||
    "lib",
 | 
			
		||||
    "bluecrypt-acme.js",
 | 
			
		||||
    "bluecrypt-acme.min.js"
 | 
			
		||||
  ],
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "test": "node server.js",
 | 
			
		||||
    "start": "node server.js"
 | 
			
		||||
  },
 | 
			
		||||
  "repository": {
 | 
			
		||||
    "type": "git",
 | 
			
		||||
    "url": "https://git.coolaj86.com/coolaj86/bluecrypt-acme.js.git"
 | 
			
		||||
  },
 | 
			
		||||
  "keywords": [
 | 
			
		||||
    "ACME",
 | 
			
		||||
    "Let's Encrypt",
 | 
			
		||||
    "browser",
 | 
			
		||||
    "EC",
 | 
			
		||||
    "RSA",
 | 
			
		||||
    "CSR",
 | 
			
		||||
    "greenlock",
 | 
			
		||||
    "VanillaJS",
 | 
			
		||||
    "ZeroSSL"
 | 
			
		||||
  ],
 | 
			
		||||
  "author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
 | 
			
		||||
  "license": "MPL-2.0",
 | 
			
		||||
  "devDependencies": {
 | 
			
		||||
    "@root/request": "^1.3.10",
 | 
			
		||||
    "dig.js": "^1.3.9",
 | 
			
		||||
    "dns-suite": "^1.2.12",
 | 
			
		||||
    "express": "^4.16.4"
 | 
			
		||||
  }
 | 
			
		||||
	"name": "acme",
 | 
			
		||||
	"version": "2.0.0-wip.0",
 | 
			
		||||
	"description": "Free SSL certificates through Let's Encrypt, right in your browser",
 | 
			
		||||
	"main": "bluecrypt-acme.js",
 | 
			
		||||
	"homepage": "https://rootprojects.org/acme/",
 | 
			
		||||
	"directories": {
 | 
			
		||||
		"lib": "lib"
 | 
			
		||||
	},
 | 
			
		||||
	"files": [
 | 
			
		||||
		"lib",
 | 
			
		||||
		"dist"
 | 
			
		||||
	],
 | 
			
		||||
	"scripts": {
 | 
			
		||||
		"build": "node bin/bundle.js",
 | 
			
		||||
		"lint": "jshint lib bin",
 | 
			
		||||
		"test": "node server.js",
 | 
			
		||||
		"start": "node server.js"
 | 
			
		||||
	},
 | 
			
		||||
	"repository": {
 | 
			
		||||
		"type": "git",
 | 
			
		||||
		"url": "https://git.coolaj86.com/coolaj86/bluecrypt-acme.js.git"
 | 
			
		||||
	},
 | 
			
		||||
	"keywords": [
 | 
			
		||||
		"ACME",
 | 
			
		||||
		"Let's Encrypt",
 | 
			
		||||
		"browser",
 | 
			
		||||
		"EC",
 | 
			
		||||
		"RSA",
 | 
			
		||||
		"CSR",
 | 
			
		||||
		"greenlock",
 | 
			
		||||
		"VanillaJS",
 | 
			
		||||
		"ZeroSSL"
 | 
			
		||||
	],
 | 
			
		||||
	"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
 | 
			
		||||
	"license": "MPL-2.0",
 | 
			
		||||
	"devDependencies": {
 | 
			
		||||
		"@root/request": "^1.3.10",
 | 
			
		||||
		"dig.js": "^1.3.9",
 | 
			
		||||
		"dns-suite": "^1.2.12",
 | 
			
		||||
		"express": "^4.16.4",
 | 
			
		||||
		"uglify-js": "^3.6.0"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user