| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | (function () { | 
					
						
							|  |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   /*global URLSearchParams,Headers*/ | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |   var PromiseA = window.Promise; | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |   var VERSION = '2'; | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |   // ACME recommends ECDSA P-256, but RSA 2048 is still required by some old servers (like what replicated.io uses )
 | 
					
						
							|  |  |  |   // ECDSA P-384, P-521, and RSA 3072, 4096 are NOT recommend standards (and not properly supported)
 | 
					
						
							|  |  |  |   var BROWSER_SUPPORTS_RSA = false; | 
					
						
							|  |  |  |   var ECDSA_OPTS = { kty: 'EC', namedCurve: 'P-256' }; | 
					
						
							|  |  |  |   var RSA_OPTS = { kty: 'RSA', modulusLength: 2048 }; | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |   var Promise = window.Promise; | 
					
						
							|  |  |  |   var Keypairs = window.Keypairs; | 
					
						
							|  |  |  |   var ACME = window.ACME; | 
					
						
							|  |  |  |   var CSR = window.CSR; | 
					
						
							|  |  |  |   var $qs = function (s) { return window.document.querySelector(s); }; | 
					
						
							|  |  |  |   var $qsa = function (s) { return window.document.querySelectorAll(s); }; | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |   var acme; | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |   var info = {}; | 
					
						
							|  |  |  |   var steps = {}; | 
					
						
							|  |  |  |   var i = 1; | 
					
						
							|  |  |  |   var apiUrl = 'https://acme-{{env}}.api.letsencrypt.org/directory'; | 
					
						
							| 
									
										
										
										
											2019-05-22 02:45:19 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   // fix previous browsers
 | 
					
						
							|  |  |  |   var isCurrent = (localStorage.getItem('version') === VERSION); | 
					
						
							|  |  |  |   if (!isCurrent) { | 
					
						
							|  |  |  |     localStorage.clear(); | 
					
						
							|  |  |  |     localStorage.setItem('version', VERSION); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   localStorage.setItem('version', VERSION); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |   function updateApiType() { | 
					
						
							|  |  |  |     /*jshint validthis: true */ | 
					
						
							|  |  |  |     var input = this || Array.prototype.filter.call( | 
					
						
							|  |  |  |       $qsa('.js-acme-api-type'), function ($el) { return $el.checked; } | 
					
						
							|  |  |  |     )[0]; | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     //#console.log('ACME api type radio:', input.value);
 | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     $qs('.js-acme-directory-url').value = apiUrl.replace(/{{env}}/g, input.value); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function hideForms() { | 
					
						
							|  |  |  |     $qsa('.js-acme-form').forEach(function (el) { | 
					
						
							|  |  |  |       el.hidden = true; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function updateProgress(currentStep) { | 
					
						
							|  |  |  |     var progressSteps = $qs("#js-progress-bar").children; | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     var j; | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     for (j = 0; j < progressSteps.length; j += 1) { | 
					
						
							|  |  |  |       if (j < currentStep) { | 
					
						
							|  |  |  |         progressSteps[j].classList.add("js-progress-step-complete"); | 
					
						
							|  |  |  |         progressSteps[j].classList.remove("js-progress-step-started"); | 
					
						
							|  |  |  |       } else if (j === currentStep) { | 
					
						
							|  |  |  |         progressSteps[j].classList.remove("js-progress-step-complete"); | 
					
						
							|  |  |  |         progressSteps[j].classList.add("js-progress-step-started"); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         progressSteps[j].classList.remove("js-progress-step-complete"); | 
					
						
							|  |  |  |         progressSteps[j].classList.remove("js-progress-step-started"); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function submitForm(ev) { | 
					
						
							|  |  |  |     var j = i; | 
					
						
							|  |  |  |     i += 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return PromiseA.resolve(steps[j].submit(ev)).catch(function (err) { | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |       var ourfault = true; | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |       console.error(err); | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |       console.error(Object.keys(err)); | 
					
						
							|  |  |  |       if ('E_CHALLENGE_INVALID' === err.code) { | 
					
						
							|  |  |  |         if ('dns-01' === err.type) { | 
					
						
							|  |  |  |           ourfault = false; | 
					
						
							|  |  |  |           window.alert("It looks like the DNS record you set for " | 
					
						
							|  |  |  |             + err.altname + " was incorrect or did not propagate. " | 
					
						
							|  |  |  |             + "The error message was '" + err.message + "'"); | 
					
						
							|  |  |  |         } else if ('http-01' === err.type) { | 
					
						
							|  |  |  |           ourfault = false; | 
					
						
							|  |  |  |           window.alert("It looks like the file you uploaded for " | 
					
						
							|  |  |  |             + err.altname + " was incorrect or could not be downloaded. " | 
					
						
							|  |  |  |             + "The error message was '" + err.message + "'"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       if (ourfault) { | 
					
						
							|  |  |  |         err.auth = undefined; | 
					
						
							|  |  |  |         window.alert("Something went wrong. It's probably our fault, not yours." | 
					
						
							|  |  |  |           + " Please email aj@rootprojects.org to let him know. The error message is: \n" | 
					
						
							|  |  |  |           + JSON.stringify(err, null, 2)); | 
					
						
							|  |  |  |       } | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function testKeypairSupport() { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     return Keypairs.generate(RSA_OPTS).then(function () { | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |       console.info("[crypto] RSA is supported"); | 
					
						
							|  |  |  |       BROWSER_SUPPORTS_RSA = true; | 
					
						
							|  |  |  |     }).catch(function () { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |       console.warn("[crypto] RSA is NOT supported"); | 
					
						
							|  |  |  |       return Keypairs.generate(ECDSA_OPTS).then(function () { | 
					
						
							|  |  |  |         console.info('[crypto] ECDSA is supported'); | 
					
						
							|  |  |  |       }).catch(function (e) { | 
					
						
							|  |  |  |         console.warn("[crypto] EC is NOT supported"); | 
					
						
							|  |  |  |         throw e; | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function getServerKeypair() { | 
					
						
							|  |  |  |     var sortedAltnames = info.identifiers.map(function (ident) { return ident.value; }).sort().join(','); | 
					
						
							|  |  |  |     var serverJwk = JSON.parse(localStorage.getItem('server:' + sortedAltnames) || 'null'); | 
					
						
							|  |  |  |     if (serverJwk) { | 
					
						
							|  |  |  |       return PromiseA.resolve(serverJwk); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     var keypairOpts; | 
					
						
							|  |  |  |     // TODO allow for user preference
 | 
					
						
							|  |  |  |     if (BROWSER_SUPPORTS_RSA) { | 
					
						
							|  |  |  |       keypairOpts = RSA_OPTS; | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       keypairOpts = ECDSA_OPTS; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return Keypairs.generate(RSA_OPTS).catch(function (err) { | 
					
						
							|  |  |  |       console.error("[Error] Keypairs.generate(" + JSON.stringify(RSA_OPTS) + "):"); | 
					
						
							|  |  |  |       throw err; | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     }).then(function (pair) { | 
					
						
							|  |  |  |       localStorage.setItem('server:'+sortedAltnames, JSON.stringify(pair.private)); | 
					
						
							|  |  |  |       return pair.private; | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |   function getAccountKeypair(email) { | 
					
						
							|  |  |  |     var json = localStorage.getItem('account:'+email); | 
					
						
							|  |  |  |     if (json) { | 
					
						
							|  |  |  |       return Promise.resolve(JSON.parse(json)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return Keypairs.generate(ECDSA_OPTS).catch(function (err) { | 
					
						
							|  |  |  |       console.warn("[Error] Keypairs.generate(" + JSON.stringify(ECDSA_OPTS) + "):\n", err); | 
					
						
							|  |  |  |       return Keypairs.generate(RSA_OPTS).catch(function (err) { | 
					
						
							|  |  |  |         console.error("[Error] Keypairs.generate(" + JSON.stringify(RSA_OPTS) + "):"); | 
					
						
							|  |  |  |         throw err; | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }).then(function (pair) { | 
					
						
							|  |  |  |       localStorage.setItem('account:'+email, JSON.stringify(pair.private)); | 
					
						
							|  |  |  |       return pair.private; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |   function updateChallengeType() { | 
					
						
							|  |  |  |     /*jshint validthis: true*/ | 
					
						
							|  |  |  |     var input = this || Array.prototype.filter.call( | 
					
						
							|  |  |  |       $qsa('.js-acme-challenge-type'), function ($el) { return $el.checked; } | 
					
						
							|  |  |  |     )[0]; | 
					
						
							|  |  |  |     $qs('.js-acme-verification-wildcard').hidden = true; | 
					
						
							|  |  |  |     $qs('.js-acme-verification-http-01').hidden = true; | 
					
						
							|  |  |  |     $qs('.js-acme-verification-dns-01').hidden = true; | 
					
						
							|  |  |  |     if (info.challenges.wildcard) { | 
					
						
							|  |  |  |       $qs('.js-acme-verification-wildcard').hidden = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (info.challenges[input.value]) { | 
					
						
							|  |  |  |       $qs('.js-acme-verification-' + input.value).hidden = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   function saveContact(email, domains) { | 
					
						
							|  |  |  |     // to be used for good, not evil
 | 
					
						
							|  |  |  |     return window.fetch('https://api.rootprojects.org/api/rootprojects.org/public/community', { | 
					
						
							|  |  |  |       method: 'POST' | 
					
						
							|  |  |  |     , cors: true | 
					
						
							|  |  |  |     , headers: new Headers({ 'Content-Type': 'application/json' }) | 
					
						
							|  |  |  |     , body: JSON.stringify({ | 
					
						
							|  |  |  |         address: email | 
					
						
							|  |  |  |       , project: 'greenlock-domains@rootprojects.org' | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |       , timezone: new Intl.DateTimeFormat().resolvedOptions().timeZone | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |       , domain: domains.join(',') | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     }).catch(function (err) { | 
					
						
							|  |  |  |       console.error(err); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   steps[1] = function () { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:50:31 -06:00
										 |  |  |     console.info("\n1. Show domains form"); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     updateProgress(0); | 
					
						
							|  |  |  |     hideForms(); | 
					
						
							|  |  |  |     $qs('.js-acme-form-domains').hidden = false; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  |   steps[1].submit = function () { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     console.info("[submit] 1. Process domains, create ACME client", info.domains); | 
					
						
							|  |  |  |     info.domains = $qs('.js-acme-domains').value | 
					
						
							|  |  |  |       .replace(/https?:\/\//g, ' ').replace(/[,+]/g, ' ').trim().split(/\s+/g); | 
					
						
							|  |  |  |     console.info("[domains]", info.domains.join(' ')); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |     info.identifiers = info.domains.map(function (hostname) { | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |       return { type: 'dns', value: hostname.toLowerCase().trim() }; | 
					
						
							| 
									
										
										
										
											2019-05-22 03:33:51 -06:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     info.identifiers.sort(function (a, b) { | 
					
						
							|  |  |  |       if (a === b) { return 0; } | 
					
						
							|  |  |  |       if (a < b) { return 1; } | 
					
						
							|  |  |  |       if (a > b) { return -1; } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     var acmeDirectoryUrl = $qs('.js-acme-directory-url').value; | 
					
						
							|  |  |  |     acme = ACME.create({ Keypairs: Keypairs, CSR: CSR }); | 
					
						
							|  |  |  |     return acme.init(acmeDirectoryUrl).then(function (directory) { | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |       $qs('.js-acme-tos-url').href = directory.meta.termsOfService; | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |       steps[i](); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   steps[2] = function () { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:50:31 -06:00
										 |  |  |     console.info("\n2. Show account (email, ToS) form"); | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     updateProgress(0); | 
					
						
							|  |  |  |     hideForms(); | 
					
						
							|  |  |  |     $qs('.js-acme-form-account').hidden = false; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  |   steps[2].submit = function () { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     console.info("[submit] 2. Create ACME account (get Key ID)"); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     var email = $qs('.js-acme-account-email').value.toLowerCase().trim(); | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |     info.email = email; | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     info.contact = [ 'mailto:' + email ]; | 
					
						
							|  |  |  |     info.agree = $qs('.js-acme-account-tos').checked; | 
					
						
							|  |  |  |     //info.greenlockAgree = $qs('.js-gl-tos').checked;
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // TODO ping with version and account creation
 | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |     setTimeout(saveContact, 100, email, info.domains); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 03:33:51 -06:00
										 |  |  |     $qs('.js-account-next').disabled = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     return info.cryptoCheck.then(function () { | 
					
						
							|  |  |  |       return getAccountKeypair(email).then(function (jwk) { | 
					
						
							|  |  |  |         // TODO save account id rather than always retrieving it?
 | 
					
						
							|  |  |  |         console.info("[accounts] upsert for", email); | 
					
						
							|  |  |  |         return acme.accounts.create({ | 
					
						
							|  |  |  |           email: email | 
					
						
							|  |  |  |         , agreeToTerms: info.agree && true | 
					
						
							|  |  |  |         , accountKeypair: { privateKeyJwk: jwk } | 
					
						
							|  |  |  |         }).then(function (account) { | 
					
						
							|  |  |  |           console.info("[accounts] result:", account); | 
					
						
							|  |  |  |           info.account = account; | 
					
						
							|  |  |  |           info.privateJwk = jwk; | 
					
						
							|  |  |  |           info.email = email; | 
					
						
							|  |  |  |         }).catch(function (err) { | 
					
						
							|  |  |  |           console.error("[accounts] failed to upsert account:"); | 
					
						
							|  |  |  |           console.error(err); | 
					
						
							|  |  |  |           window.alert(err.message || JSON.stringify(err, null, 2)); | 
					
						
							|  |  |  |           return new Promise(function () { | 
					
						
							|  |  |  |             if (window.confirm("Start over?")) { | 
					
						
							|  |  |  |               document.location.reload(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }).then(function () { | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |       var jwk = info.privateJwk; | 
					
						
							|  |  |  |       var account = info.account; | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |       console.info("[orders] requesting"); | 
					
						
							|  |  |  |       return acme.orders.request({ | 
					
						
							|  |  |  |         account: account | 
					
						
							|  |  |  |       , accountKeypair: { privateKeyJwk: jwk } | 
					
						
							|  |  |  |       , domains: info.domains | 
					
						
							|  |  |  |       }).then(function (order) { | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |         info.order = order; | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |         console.info("[orders] created ", order); | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |         var claims = order.claims; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var obj = { 'dns-01': [], 'http-01': [], 'wildcard': [] }; | 
					
						
							|  |  |  |         info.challenges = obj; | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-22 03:33:51 -06:00
										 |  |  |         var $httpList = $qs('.js-acme-http'); | 
					
						
							|  |  |  |         var $dnsList = $qs('.js-acme-dns'); | 
					
						
							|  |  |  |         var $wildList = $qs('.js-acme-wildcard'); | 
					
						
							|  |  |  |         var httpTpl = $httpList.innerHTML; | 
					
						
							|  |  |  |         var dnsTpl = $dnsList.innerHTML; | 
					
						
							|  |  |  |         var wildTpl = $wildList.innerHTML; | 
					
						
							|  |  |  |         $httpList.innerHTML = ''; | 
					
						
							|  |  |  |         $dnsList.innerHTML = ''; | 
					
						
							|  |  |  |         $wildList.innerHTML = ''; | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |         claims.forEach(function (claim) { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |           //#console.log("claims[i]", claim);
 | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |           var hostname = claim.identifier.value; | 
					
						
							|  |  |  |           claim.challenges.forEach(function (c) { | 
					
						
							|  |  |  |             var auth = c; | 
					
						
							|  |  |  |             var data = { | 
					
						
							|  |  |  |               type: c.type | 
					
						
							|  |  |  |             , hostname: hostname | 
					
						
							|  |  |  |             , url: c.url | 
					
						
							|  |  |  |             , token: c.token | 
					
						
							|  |  |  |             , httpPath: auth.challengeUrl | 
					
						
							|  |  |  |             , httpAuth: auth.keyAuthorization | 
					
						
							|  |  |  |             , dnsType: 'TXT' | 
					
						
							|  |  |  |             , dnsHost: auth.dnsHost | 
					
						
							|  |  |  |             , dnsAnswer: auth.keyAuthorizationDigest | 
					
						
							|  |  |  |             }; | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |             //#console.log("claims[i].challenge", data);
 | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |             var $tpl = document.createElement("div"); | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |             if (claim.wildcard) { | 
					
						
							|  |  |  |               obj.wildcard.push(data); | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |               $tpl.innerHTML = wildTpl; | 
					
						
							|  |  |  |               $tpl.querySelector(".js-acme-ver-txt-host").innerHTML = data.dnsHost; | 
					
						
							|  |  |  |               $tpl.querySelector(".js-acme-ver-txt-value").innerHTML = data.dnsAnswer; | 
					
						
							|  |  |  |               $wildList.appendChild($tpl); | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |             } else if(obj[data.type]) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               obj[data.type].push(data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               if ('dns-01' === data.type) { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |                 $tpl.innerHTML = dnsTpl; | 
					
						
							|  |  |  |                 $tpl.querySelector(".js-acme-ver-txt-host").innerHTML = data.dnsHost; | 
					
						
							|  |  |  |                 $tpl.querySelector(".js-acme-ver-txt-value").innerHTML = data.dnsAnswer; | 
					
						
							|  |  |  |                 $dnsList.appendChild($tpl); | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |               } else if ('http-01' === data.type) { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |                 $tpl.innerHTML = httpTpl; | 
					
						
							|  |  |  |                 $tpl.querySelector(".js-acme-ver-file-location").innerHTML = data.httpPath.split("/").slice(-1); | 
					
						
							|  |  |  |                 $tpl.querySelector(".js-acme-ver-content").innerHTML = data.httpAuth; | 
					
						
							|  |  |  |                 $tpl.querySelector(".js-acme-ver-uri").innerHTML = data.httpPath; | 
					
						
							|  |  |  |                 $tpl.querySelector(".js-download-verify-link").href = | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |                   "data:text/octet-stream;base64," + window.btoa(data.httpAuth); | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |                 $tpl.querySelector(".js-download-verify-link").download = data.httpPath.split("/").slice(-1); | 
					
						
							|  |  |  |                 $httpList.appendChild($tpl); | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |               } | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |           }); | 
					
						
							|  |  |  |         }); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |         // hide wildcard if no wildcard
 | 
					
						
							|  |  |  |         // hide http-01 and dns-01 if only wildcard
 | 
					
						
							|  |  |  |         if (!obj.wildcard.length) { | 
					
						
							|  |  |  |           $qs('.js-acme-wildcard-challenges').hidden = true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         if (!obj['http-01'].length) { | 
					
						
							|  |  |  |           $qs('.js-acme-challenges').hidden = true; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |         console.info("[housekeeping] challenges", info.challenges); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |         updateChallengeType(); | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |         steps[i](); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |       }); | 
					
						
							|  |  |  |     }).catch(function (err) { | 
					
						
							|  |  |  |       console.error('Step \'\' Error:'); | 
					
						
							|  |  |  |       console.error(err, err.stack); | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |       window.alert("An error happened (but it's not your fault)." | 
					
						
							|  |  |  |         + " Email aj@rootprojects.org to let him know that 'order and get challenges' failed."); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   steps[3] = function () { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:50:31 -06:00
										 |  |  |     console.info("\n3. Present challenge options"); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     updateProgress(1); | 
					
						
							|  |  |  |     hideForms(); | 
					
						
							|  |  |  |     $qs('.js-acme-form-challenges').hidden = false; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  |   steps[3].submit = function () { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     console.info("[submit] 3. Fulfill challenges, fetch certificate"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |     var challengePriority = [ 'dns-01' ]; | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     if ('http-01' === $qs('.js-acme-challenge-type:checked').value) { | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |       challengePriority.unshift('http-01'); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     console.info("[challenge] selected ", challengePriority[0]); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     // for now just show the next page immediately (its a spinner)
 | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |     steps[i](); | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |     return getAccountKeypair(info.email).then(function (jwk) { | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |       // TODO put a test challenge in the list
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |       // info.order.claims.push(...)
 | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |       // TODO warn about wait-time if DNS
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |       return getServerKeypair().then(function (serverJwk) { | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |         return acme.orders.complete({ | 
					
						
							|  |  |  |           account: info.account | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |         , accountKeypair: { privateKeyJwk: jwk } | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |         , order: info.order | 
					
						
							|  |  |  |         , domains: info.domains | 
					
						
							|  |  |  |         , domainKeypair: { privateKeyJwk: serverJwk } | 
					
						
							|  |  |  |         , challengePriority: challengePriority | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |         , challenges: false | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |         }).then(function (certs) { | 
					
						
							|  |  |  |           return Keypairs.export({ jwk: serverJwk }).then(function (keyPem) { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |             console.info('WINNING!'); | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  |             console.log(certs); | 
					
						
							|  |  |  |             $qs('#js-fullchain').innerHTML = [ | 
					
						
							|  |  |  |               certs.cert.trim() + "\n" | 
					
						
							|  |  |  |             , certs.chain + "\n" | 
					
						
							|  |  |  |             ].join("\n"); | 
					
						
							|  |  |  |             $qs("#js-download-fullchain-link").href = | 
					
						
							|  |  |  |               "data:text/octet-stream;base64," + window.btoa(certs); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             $qs('#js-privkey').innerHTML = keyPem; | 
					
						
							|  |  |  |             $qs("#js-download-privkey-link").href = | 
					
						
							|  |  |  |               "data:text/octet-stream;base64," + window.btoa(keyPem); | 
					
						
							|  |  |  |             submitForm(); | 
					
						
							|  |  |  |           }); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // spinner
 | 
					
						
							|  |  |  |   steps[4] = function () { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:50:31 -06:00
										 |  |  |     console.info('\n4. Show loading spinner'); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     updateProgress(1); | 
					
						
							|  |  |  |     hideForms(); | 
					
						
							|  |  |  |     $qs('.js-acme-form-poll').hidden = false; | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  |   steps[4].submit = function () { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     console.info('[submit] 4. Order complete'); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-21 00:59:20 -06:00
										 |  |  |     steps[i](); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   steps[5] = function () { | 
					
						
							| 
									
										
										
										
											2019-05-23 13:50:31 -06:00
										 |  |  |     console.info('\n5. Present certificates (yay!)'); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     updateProgress(2); | 
					
						
							|  |  |  |     hideForms(); | 
					
						
							|  |  |  |     $qs('.js-acme-form-download').hidden = false; | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2019-05-21 00:37:07 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |   function init() { | 
					
						
							|  |  |  |     $qsa('.js-acme-api-type').forEach(function ($el) { | 
					
						
							|  |  |  |       $el.addEventListener('change', updateApiType); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     updateApiType(); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     $qsa('.js-acme-form').forEach(function ($el) { | 
					
						
							|  |  |  |       $el.addEventListener('submit', function (ev) { | 
					
						
							|  |  |  |         ev.preventDefault(); | 
					
						
							|  |  |  |         submitForm(ev); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     $qsa('.js-acme-challenge-type').forEach(function ($el) { | 
					
						
							|  |  |  |       $el.addEventListener('change', updateChallengeType); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:50:31 -06:00
										 |  |  | 
 | 
					
						
							|  |  |  |     var params = new URLSearchParams(window.location.search); | 
					
						
							|  |  |  |     var apiType = params.get('acme-api-type') || "staging-v02"; | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     if (params.has('acme-domains')) { | 
					
						
							|  |  |  |       $qs('.js-acme-domains').value = params.get('acme-domains'); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |       $qsa('.js-acme-api-type').forEach(function(ele) { | 
					
						
							|  |  |  |         if(ele.value === apiType) { | 
					
						
							|  |  |  |           ele.checked = true; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |       updateApiType(); | 
					
						
							|  |  |  |       steps[2](); | 
					
						
							|  |  |  |       submitForm(); | 
					
						
							| 
									
										
										
										
											2019-05-23 13:50:31 -06:00
										 |  |  |     } else { | 
					
						
							|  |  |  |       steps[1](); | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |   init(); | 
					
						
							|  |  |  |   $qs('body').hidden = false; | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-05-23 13:36:19 -06:00
										 |  |  |   // in the background
 | 
					
						
							|  |  |  |   info.cryptoCheck = testKeypairSupport().then(function () { | 
					
						
							|  |  |  |     console.info("[crypto] self-check: passed"); | 
					
						
							|  |  |  |   }).catch(function (err) { | 
					
						
							|  |  |  |     console.error('[crypto] could not use either RSA nor EC.'); | 
					
						
							|  |  |  |     console.error(err); | 
					
						
							|  |  |  |     window.alert("Generating secure certificates requires a browser with cryptography support." | 
					
						
							|  |  |  |       + "Please consider a recent version of Chrome, Firefox, or Safari."); | 
					
						
							|  |  |  |     throw err; | 
					
						
							| 
									
										
										
										
											2019-05-19 01:10:49 -06:00
										 |  |  |   }); | 
					
						
							|  |  |  | }()); |