| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Explained here: https://groups.google.com/d/msg/nodejs/AjkHSYmiGYs/1LfNHbMhd48J
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | var fs = require('fs') | 
					
						
							|  |  |  |   , path = require('path') | 
					
						
							|  |  |  |   , request = require('request') | 
					
						
							|  |  |  |   , CERTDB_URL = 'https://mxr.mozilla.org/nss/source/lib/ckfw/builtins/certdata.txt?raw=1' | 
					
						
							| 
									
										
										
										
											2014-06-17 20:07:25 -06:00
										 |  |  |   , outputFile | 
					
						
							|  |  |  |   , outputPemsDir | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  |   ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function Certificate() { | 
					
						
							|  |  |  |   this.name = null; | 
					
						
							|  |  |  |   this.body = ''; | 
					
						
							|  |  |  |   this.trusted = true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Certificate.prototype.quasiPEM = function quasiPEM() { | 
					
						
							|  |  |  |   var bytes = this.body.split('\\') | 
					
						
							|  |  |  |     , offset = 0 | 
					
						
							|  |  |  |     , converted | 
					
						
							|  |  |  |     ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   bytes.shift(); | 
					
						
							|  |  |  |   converted = new Buffer(bytes.length); | 
					
						
							|  |  |  |   while(bytes.length > 0) { | 
					
						
							|  |  |  |     converted.writeUInt8(parseInt(bytes.shift(), 8), offset++); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-17 20:03:47 -06:00
										 |  |  |   return { | 
					
						
							|  |  |  |     name: this.name | 
					
						
							|  |  |  |   , value: '  // ' + this.name + '\n' | 
					
						
							|  |  |  |       + '  "-----BEGIN CERTIFICATE-----\\n" +\n' | 
					
						
							|  |  |  |       + converted.toString('base64').replace(/(.{1,76})/g, '  "$1\\n" +\n') | 
					
						
							|  |  |  |       + '  "-----END CERTIFICATE-----\\n"' | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function parseBody(current, lines) { | 
					
						
							|  |  |  |   var line | 
					
						
							|  |  |  |     ; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (lines.length > 0) { | 
					
						
							|  |  |  |     line = lines.shift(); | 
					
						
							|  |  |  |     if (line.match(/^END/)) { break; } | 
					
						
							|  |  |  |     current.body += line; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (lines.length > 0) { | 
					
						
							|  |  |  |     line = lines.shift(); | 
					
						
							|  |  |  |     if (line.match(/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/)) { break; } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   while (lines.length > 0) { | 
					
						
							|  |  |  |     line = lines.shift(); | 
					
						
							|  |  |  |     if (line.match(/^#|^\s*$/)) { break; } | 
					
						
							|  |  |  |     if (line.match(/^CKA_TRUST_SERVER_AUTH\s+CK_TRUST\s+CKT_NSS_NOT_TRUSTED$/) || | 
					
						
							|  |  |  |         line.match(/^CKA_TRUST_SERVER_AUTH\s+CK_TRUST\s+CKT_NSS_TRUST_UNKNOWN$/)) { | 
					
						
							|  |  |  |       current.trusted = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   if (current.trusted) return current; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | function parseCertData(lines) { | 
					
						
							|  |  |  |   var certs = [] | 
					
						
							|  |  |  |     , current | 
					
						
							|  |  |  |     , skipped = 0 | 
					
						
							|  |  |  |     , match | 
					
						
							|  |  |  |     , finished | 
					
						
							|  |  |  |     ; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-17 20:11:50 -06:00
										 |  |  |   function parseLine(line) { | 
					
						
							| 
									
										
										
										
											2014-06-17 20:36:53 -06:00
										 |  |  |     //
 | 
					
						
							|  |  |  |     // Find & nuke whitespace and comments
 | 
					
						
							|  |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2014-06-17 20:11:50 -06:00
										 |  |  |     if (line.match(/^#|^\s*$/)) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-17 20:36:53 -06:00
										 |  |  |     //
 | 
					
						
							|  |  |  |     // Find CERT
 | 
					
						
							|  |  |  |     //
 | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  |     if (line.match(/^CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE/)) { | 
					
						
							|  |  |  |       current = new Certificate(); | 
					
						
							| 
									
										
										
										
											2014-06-17 20:36:53 -06:00
										 |  |  |       return; | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-17 20:36:53 -06:00
										 |  |  |     if (!current) { | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-17 20:36:53 -06:00
										 |  |  |     //
 | 
					
						
							|  |  |  |     // Find Name
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     match = line.match(/^CKA_LABEL UTF8 \"(.*)\"/); | 
					
						
							|  |  |  |     if (match) { | 
					
						
							|  |  |  |       current.name = decodeURIComponent(match[1]); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     // Find Body
 | 
					
						
							|  |  |  |     //
 | 
					
						
							|  |  |  |     if (line.match(/^CKA_VALUE MULTILINE_OCTAL/)) { | 
					
						
							|  |  |  |       finished = parseBody(current, lines); | 
					
						
							|  |  |  |       if (finished) { | 
					
						
							|  |  |  |         certs.push(finished); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         skipped += 1; | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  |       } | 
					
						
							| 
									
										
										
										
											2014-06-17 20:36:53 -06:00
										 |  |  |       current = null; | 
					
						
							|  |  |  |       return; | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-17 20:11:50 -06:00
										 |  |  |   while (lines.length > 0) { | 
					
						
							|  |  |  |     parseLine(lines.shift()); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  |   console.info("Skipped %s untrusted certificates.", skipped); | 
					
						
							|  |  |  |   console.info("Processed %s certificates.", certs.length); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return certs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-16 14:26:45 -04:00
										 |  |  | function dumpCerts(certs, filename, pemsDir) { | 
					
						
							| 
									
										
										
										
											2014-06-17 20:05:45 -06:00
										 |  |  |   certs.forEach(function (cert) { | 
					
						
							| 
									
										
										
										
											2014-06-17 20:03:47 -06:00
										 |  |  |     var pem = cert.quasiPEM() | 
					
						
							| 
									
										
										
										
											2014-07-30 10:22:41 -07:00
										 |  |  |       , pemName = pem.name.toLowerCase().replace(/[\\\s\/\(\)\.]+/g, '-').replace(/-+/g, '-') | 
					
						
							| 
									
										
										
										
											2014-06-17 20:03:47 -06:00
										 |  |  |       , pemsFile = path.join(pemsDir, pemName + '.pem') | 
					
						
							|  |  |  |       ; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-30 10:22:41 -07:00
										 |  |  |     /* | 
					
						
							|  |  |  |     if (/[^\w\-]/.test(pemName)) { | 
					
						
							|  |  |  |       //pemName = pemName.replace(/\\/g, '-');
 | 
					
						
							|  |  |  |       //pemName = pemName.replace(/[^\w-]/g, '-');
 | 
					
						
							|  |  |  |       console.log(pemName); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     */ | 
					
						
							| 
									
										
										
										
											2014-06-17 20:03:47 -06:00
										 |  |  |     fs.writeFileSync(pemsFile, pem.value); | 
					
						
							| 
									
										
										
										
											2014-03-07 17:48:44 -07:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2014-07-16 11:57:51 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-07 17:48:44 -07:00
										 |  |  |   console.info("Wrote " + certs.length + " certificates in '" | 
					
						
							|  |  |  |     + path.join(__dirname, 'pems/').replace(/'/g, "\\'") + "'."); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  |   fs.writeFileSync( | 
					
						
							| 
									
										
										
										
											2014-06-16 14:11:15 -04:00
										 |  |  |     filename | 
					
						
							| 
									
										
										
										
											2016-10-20 11:57:26 -06:00
										 |  |  |   , fs.readFileSync(path.join(__dirname, 'ssl-root-cas.tpl.js'), 'utf8') | 
					
						
							|  |  |  |       .replace(/\/\*TPL\*\//, certs.map(function (cert) { return cert.quasiPEM().value; }).join(',\n\n')) | 
					
						
							|  |  |  |   , 'utf8' | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  |   ); | 
					
						
							| 
									
										
										
										
											2014-06-16 14:11:15 -04:00
										 |  |  |   console.info("Wrote '" + filename.replace(/'/g, "\\'") + "'."); | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-16 11:57:51 -06:00
										 |  |  | function run(filename) { | 
					
						
							| 
									
										
										
										
											2016-06-21 17:18:27 +08:00
										 |  |  |   var PromiseA = require('bluebird').Promise; | 
					
						
							| 
									
										
										
										
											2016-06-21 17:16:55 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return new PromiseA(function (resolve, reject) { | 
					
						
							| 
									
										
										
										
											2014-07-16 11:57:51 -06:00
										 |  |  |     if (!filename) { | 
					
						
							|  |  |  |       console.error("Error: No file specified"); | 
					
						
							|  |  |  |       console.info("Usage: %s <outputfile>", process.argv[1]); | 
					
						
							|  |  |  |       console.info("   where <outputfile> is the name of the file to write to, relative to %s", process.argv[1]); | 
					
						
							|  |  |  |       console.info("Note that a 'pems/' directory will also be created at the same location as the <outputfile>, containing individual .pem files."); | 
					
						
							|  |  |  |       reject(3); | 
					
						
							|  |  |  |       return; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-06-16 14:11:15 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-16 11:57:51 -06:00
										 |  |  |     // main (combined) output file location, relative to this script's location
 | 
					
						
							|  |  |  |     outputFile = path.resolve(__dirname, filename); | 
					
						
							| 
									
										
										
										
											2014-06-16 14:21:06 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-16 11:57:51 -06:00
										 |  |  |     // pems/ output directory, in the same directory as the outputFile
 | 
					
						
							|  |  |  |     outputPemsDir = path.resolve(outputFile, '../pems'); | 
					
						
							| 
									
										
										
										
											2014-06-16 14:26:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-17 17:53:47 -06:00
										 |  |  |     if (!fs.existsSync(outputPemsDir)) { | 
					
						
							|  |  |  |       fs.mkdirSync(outputPemsDir); | 
					
						
							| 
									
										
										
										
											2016-06-21 17:16:55 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2014-06-16 14:26:45 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-16 11:57:51 -06:00
										 |  |  |     console.info("Loading latest certificates from " + CERTDB_URL); | 
					
						
							|  |  |  |     request.get(CERTDB_URL, function (error, response, body) { | 
					
						
							|  |  |  |       if (error) { | 
					
						
							|  |  |  |         console.error(error); | 
					
						
							|  |  |  |         console.error(error.stacktrace); | 
					
						
							|  |  |  |         reject({ code: 1, error: error }); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-16 11:57:51 -06:00
										 |  |  |       if (response.statusCode !== 200) { | 
					
						
							|  |  |  |         console.error("Fetching failed with status code %s", response.statusCode); | 
					
						
							|  |  |  |         reject({ code: 2, error: "Fetching failed with status code " + response.statusCode }); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2014-03-07 17:14:13 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-06-28 14:34:48 -07:00
										 |  |  |       if (response.headers['content-type'].indexOf('text/plain') !== 0) { | 
					
						
							| 
									
										
										
										
											2016-06-18 16:41:26 -07:00
										 |  |  |         console.error("Fetching failed with incorrect content type %s", response.headers['content-type']); | 
					
						
							|  |  |  |         reject({ code: 2, error: "Fetching failed with incorrect content type " + response.headers['content-type'] }); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-16 11:57:51 -06:00
										 |  |  |       var lines = body.split("\n") | 
					
						
							|  |  |  |         , certs = parseCertData(lines) | 
					
						
							|  |  |  |         , pemsFile = path.join(outputPemsDir, 'mozilla-certdata.txt') | 
					
						
							|  |  |  |         ; | 
					
						
							| 
									
										
										
										
											2014-06-17 20:07:25 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-07-16 11:57:51 -06:00
										 |  |  |       fs.writeFileSync(pemsFile, body); | 
					
						
							|  |  |  |       dumpCerts(certs, outputFile, outputPemsDir); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       resolve(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | module.exports.generate = run; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if (require.main === module) { | 
					
						
							|  |  |  |   run(process.argv[2]) | 
					
						
							|  |  |  |     .then | 
					
						
							|  |  |  |       (function () { | 
					
						
							|  |  |  |         // something
 | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     , function (errcode) { | 
					
						
							|  |  |  |         process.exit(errcode); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     ); | 
					
						
							|  |  |  | } |