| 
									
										
										
										
											2018-04-30 18:03:12 -06:00
										 |  |  | (function () { | 
					
						
							|  |  |  | 'use strict'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-01 08:15:41 +00:00
										 |  |  |   var $qs = function (s) { return window.document.querySelector(s); }; | 
					
						
							|  |  |  |   var $qsa = function (s) { return window.document.querySelectorAll(s); }; | 
					
						
							|  |  |  |   var info = {}; | 
					
						
							|  |  |  |   var steps = {}; | 
					
						
							| 
									
										
										
										
											2018-05-02 08:02:22 +00:00
										 |  |  |   var nonce; | 
					
						
							|  |  |  |   var kid; | 
					
						
							| 
									
										
										
										
											2018-05-01 08:15:41 +00:00
										 |  |  |   var i = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 20:26:06 +00:00
										 |  |  |   var apiUrl = 'https://acme-{{env}}.api.letsencrypt.org/directory'; | 
					
						
							|  |  |  |   function updateApiType() { | 
					
						
							|  |  |  |     var input = this || Array.prototype.filter.call( | 
					
						
							|  |  |  |       $qsa('.js-acme-api-type'), function ($el) { return $el.checked; } | 
					
						
							|  |  |  |     )[0]; | 
					
						
							|  |  |  |     console.log('ACME api type radio:', input.value); | 
					
						
							|  |  |  |     $qs('.js-acme-directory-url').value = apiUrl.replace(/{{env}}/g, input.value); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   $qsa('.js-acme-api-type').forEach(function ($el) { | 
					
						
							|  |  |  |     $el.addEventListener('change', updateApiType); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  |   updateApiType(); | 
					
						
							| 
									
										
										
										
											2018-05-01 08:15:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   function hideForms() { | 
					
						
							|  |  |  |     $qsa('.js-acme-form').forEach(function (el) { | 
					
						
							|  |  |  |       el.hidden = true; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |   function submitForm(ev) { | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |     var j = i; | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |     i += 1; | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |     steps[j].submit(ev); | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-05-02 08:02:22 +00:00
										 |  |  |   $qsa('.js-acme-form').forEach(function ($el) { | 
					
						
							|  |  |  |     $el.addEventListener('submit', function (ev) { | 
					
						
							|  |  |  |       ev.preventDefault(); | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |       submitForm(ev); | 
					
						
							| 
									
										
										
										
											2018-05-02 08:02:22 +00:00
										 |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-05-01 08:15:41 +00:00
										 |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |   function updateChallengeType() { | 
					
						
							| 
									
										
										
										
											2018-05-04 11:10:43 +00:00
										 |  |  |     var input = this || Array.prototype.filter.call( | 
					
						
							|  |  |  |       $qsa('.js-acme-challenge-type'), function ($el) { return $el.checked; } | 
					
						
							|  |  |  |     )[0]; | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |     console.log('ch type radio:', input.value); | 
					
						
							|  |  |  |     $qs('.js-acme-table-wildcard').hidden = true; | 
					
						
							|  |  |  |     $qs('.js-acme-table-http-01').hidden = true; | 
					
						
							|  |  |  |     $qs('.js-acme-table-dns-01').hidden = true; | 
					
						
							|  |  |  |     if (info.challenges.wildcard) { | 
					
						
							|  |  |  |       $qs('.js-acme-table-wildcard').hidden = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     if (info.challenges[input.value]) { | 
					
						
							|  |  |  |       $qs('.js-acme-table-' + input.value).hidden = false; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  |   $qsa('.js-acme-challenge-type').forEach(function ($el) { | 
					
						
							|  |  |  |     $el.addEventListener('change', updateChallengeType); | 
					
						
							|  |  |  |   }); | 
					
						
							| 
									
										
										
										
											2018-05-01 08:15:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 23:59:05 +00:00
										 |  |  |   function saveContact(email, domains) { | 
					
						
							|  |  |  |     // to be used for good, not evil
 | 
					
						
							|  |  |  |     return window.fetch('https://api.ppl.family/api/ppl.family/public/list', { | 
					
						
							|  |  |  |       method: 'POST' | 
					
						
							|  |  |  |     , cors: true | 
					
						
							|  |  |  |     , headers: new Headers({ 'Content-Type': 'application/json' }) | 
					
						
							|  |  |  |     , body: JSON.stringify({ address: email, comment: 'greenlock sub for ' + domains.join(',') }) | 
					
						
							|  |  |  |     }).then(function (resp) { | 
					
						
							|  |  |  |       return resp.json().then(function (data) { | 
					
						
							|  |  |  |         /* | 
					
						
							|  |  |  |         if (data.error) { | 
					
						
							|  |  |  |           window.alert("Couldn't save your contact. Email coolaj86@gmail.com instead."); | 
					
						
							|  |  |  |           return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         */ | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }, function () { | 
					
						
							|  |  |  |       /* | 
					
						
							|  |  |  |       window.alert("Didn't get your contact. Bad network connection? Email coolaj86@gmail.com instead."); | 
					
						
							|  |  |  |       */ | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-01 08:15:41 +00:00
										 |  |  |   steps[1] = function () { | 
					
						
							|  |  |  |     hideForms(); | 
					
						
							|  |  |  |     $qs('.js-acme-form-domains').hidden = false; | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-05-01 08:29:19 +00:00
										 |  |  |   steps[1].submit = function () { | 
					
						
							|  |  |  |     info.identifiers = $qs('.js-acme-domains').value.split(/\s*,\s*/g).map(function (hostname) { | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |       return { type: 'dns', value: hostname.toLowerCase().trim() }; | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     info.identifiers.sort(function (a, b) { | 
					
						
							|  |  |  |       if (a === b) { return 0; } | 
					
						
							|  |  |  |       if (a < b) { return 1; } | 
					
						
							|  |  |  |       if (a > b) { return -1; } | 
					
						
							| 
									
										
										
										
											2018-05-01 08:29:19 +00:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 20:35:34 +00:00
										 |  |  |     return BACME.directory({ directoryUrl: $qs('.js-acme-directory-url').value }).then(function (directory) { | 
					
						
							| 
									
										
										
										
											2018-05-01 08:29:19 +00:00
										 |  |  |       $qs('.js-acme-tos-url').href = directory.meta.termsOfService; | 
					
						
							| 
									
										
										
										
											2018-05-02 08:02:22 +00:00
										 |  |  |       return BACME.nonce().then(function (_nonce) { | 
					
						
							|  |  |  |         nonce = _nonce; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |         console.log("MAGIC STEP NUMBER in 1 is:", i); | 
					
						
							| 
									
										
										
										
											2018-05-02 08:02:22 +00:00
										 |  |  |         steps[i](); | 
					
						
							|  |  |  |       }); | 
					
						
							| 
									
										
										
										
											2018-05-01 08:29:19 +00:00
										 |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-05-01 08:15:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   steps[2] = function () { | 
					
						
							|  |  |  |     hideForms(); | 
					
						
							|  |  |  |     $qs('.js-acme-form-account').hidden = false; | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-05-01 08:29:19 +00:00
										 |  |  |   steps[2].submit = function () { | 
					
						
							| 
									
										
										
										
											2018-05-02 08:02:22 +00:00
										 |  |  |     var email = $qs('.js-acme-account-email').value.toLowerCase().trim(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 23:59:05 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-02 08:02:22 +00:00
										 |  |  |     info.contact = [ 'mailto:' + email ]; | 
					
						
							| 
									
										
										
										
											2018-05-01 08:29:19 +00:00
										 |  |  |     info.agree = $qs('.js-acme-account-tos').checked; | 
					
						
							|  |  |  |     info.greenlockAgree = $qs('.js-gl-tos').checked; | 
					
						
							|  |  |  |     // TODO
 | 
					
						
							| 
									
										
										
										
											2018-05-02 08:02:22 +00:00
										 |  |  |     // options for
 | 
					
						
							|  |  |  |     // * regenerate key
 | 
					
						
							|  |  |  |     // * ECDSA / RSA / bitlength
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // TODO ping with version and account creation
 | 
					
						
							| 
									
										
										
										
											2018-05-04 23:59:05 +00:00
										 |  |  |     setTimeout(saveContact, 100, email, info.identifiers.map(function (ident) { return ident.value; })); | 
					
						
							| 
									
										
										
										
											2018-05-02 08:02:22 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     var jwk = JSON.parse(localStorage.getItem('account:' + email) || 'null'); | 
					
						
							|  |  |  |     var p; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function createKeypair() { | 
					
						
							|  |  |  |       return BACME.accounts.generateKeypair({ | 
					
						
							|  |  |  |         type: 'ECDSA' | 
					
						
							|  |  |  |       , bitlength: '256' | 
					
						
							|  |  |  |       }).then(function (jwk) { | 
					
						
							|  |  |  |         localStorage.setItem('account:' + email, JSON.stringify(jwk)); | 
					
						
							|  |  |  |         return jwk; | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (jwk) { | 
					
						
							|  |  |  |       p = Promise.resolve(jwk); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       p = createKeypair(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function createAccount(jwk) { | 
					
						
							|  |  |  |       console.log('account jwk:'); | 
					
						
							|  |  |  |       console.log(jwk); | 
					
						
							|  |  |  |       delete jwk.key_ops; | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |       info.jwk = jwk; | 
					
						
							| 
									
										
										
										
											2018-05-02 08:02:22 +00:00
										 |  |  |       return BACME.accounts.sign({ | 
					
						
							|  |  |  |         jwk: jwk | 
					
						
							|  |  |  |       , contacts: [ 'mailto:' + email ] | 
					
						
							|  |  |  |       , agree: info.agree | 
					
						
							|  |  |  |       , nonce: nonce | 
					
						
							|  |  |  |       , kid: kid | 
					
						
							|  |  |  |       }).then(function (signedAccount) { | 
					
						
							|  |  |  |         return BACME.accounts.set({ | 
					
						
							|  |  |  |           signedAccount: signedAccount | 
					
						
							|  |  |  |         }).then(function (account) { | 
					
						
							|  |  |  |           console.log('account:'); | 
					
						
							|  |  |  |           console.log(account); | 
					
						
							|  |  |  |           kid = account.kid; | 
					
						
							|  |  |  |           return kid; | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return p.then(function (_jwk) { | 
					
						
							|  |  |  |       jwk = _jwk; | 
					
						
							|  |  |  |       kid = JSON.parse(localStorage.getItem('account-kid:' + email) || 'null'); | 
					
						
							|  |  |  |       var p2 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       // TODO save account id rather than always retrieving it
 | 
					
						
							|  |  |  |       if (kid) { | 
					
						
							|  |  |  |         p2 = Promise.resolve(kid); | 
					
						
							|  |  |  |       } else { | 
					
						
							|  |  |  |         p2 = createAccount(jwk); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return p2.then(function (_kid) { | 
					
						
							|  |  |  |         kid = _kid; | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |         info.kid = kid; | 
					
						
							| 
									
										
										
										
											2018-05-02 08:02:22 +00:00
										 |  |  |         return BACME.orders.sign({ | 
					
						
							|  |  |  |           jwk: jwk | 
					
						
							|  |  |  |         , identifiers: info.identifiers | 
					
						
							|  |  |  |         , kid: kid | 
					
						
							|  |  |  |         }).then(function (signedOrder) { | 
					
						
							|  |  |  |           return BACME.orders.create({ | 
					
						
							|  |  |  |             signedOrder: signedOrder | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |           }).then(function (order) { | 
					
						
							|  |  |  |             info.finalizeUrl = order.finalize; | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |             info.orderUrl = order.url; // from header Location ???
 | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |             return BACME.thumbprint({ jwk: jwk }).then(function (thumbprint) { | 
					
						
							|  |  |  |               return BACME.challenges.all().then(function (claims) { | 
					
						
							|  |  |  |                 console.log('claims:'); | 
					
						
							|  |  |  |                 console.log(claims); | 
					
						
							|  |  |  |                 var obj = { 'dns-01': [], 'http-01': [], 'wildcard': [] }; | 
					
						
							|  |  |  |                 var map = { | 
					
						
							|  |  |  |                   'http-01': '.js-acme-table-http-01' | 
					
						
							|  |  |  |                 , 'dns-01': '.js-acme-table-dns-01' | 
					
						
							|  |  |  |                 , 'wildcard': '.js-acme-table-wildcard' | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |                 var tpls = {}; | 
					
						
							|  |  |  |                 info.challenges = obj; | 
					
						
							|  |  |  |                 Object.keys(map).forEach(function (k) { | 
					
						
							|  |  |  |                   var sel = map[k] + ' tbody'; | 
					
						
							|  |  |  |                   console.log(sel); | 
					
						
							|  |  |  |                   tpls[k] = $qs(sel).innerHTML; | 
					
						
							|  |  |  |                   $qs(map[k] + ' tbody').innerHTML = ''; | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // TODO make Promise-friendly
 | 
					
						
							|  |  |  |                 return Promise.all(claims.map(function (claim) { | 
					
						
							|  |  |  |                   var hostname = claim.identifier.value; | 
					
						
							|  |  |  |                   return Promise.all(claim.challenges.map(function (c) { | 
					
						
							|  |  |  |                     var keyAuth = BACME.challenges['http-01']({ | 
					
						
							|  |  |  |                       token: c.token | 
					
						
							|  |  |  |                     , thumbprint: thumbprint | 
					
						
							|  |  |  |                     , challengeDomain: hostname | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  |                     return BACME.challenges['dns-01']({ | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |                       keyAuth: keyAuth.value | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |                     , challengeDomain: hostname | 
					
						
							|  |  |  |                     }).then(function (dnsAuth) { | 
					
						
							|  |  |  |                       var data = { | 
					
						
							|  |  |  |                         type: c.type | 
					
						
							|  |  |  |                       , hostname: hostname | 
					
						
							|  |  |  |                       , url: c.url | 
					
						
							|  |  |  |                       , token: c.token | 
					
						
							|  |  |  |                       , keyAuthorization: keyAuth | 
					
						
							|  |  |  |                       , httpPath: keyAuth.path | 
					
						
							|  |  |  |                       , httpAuth: keyAuth.value | 
					
						
							|  |  |  |                       , dnsType: dnsAuth.type | 
					
						
							|  |  |  |                       , dnsHost: dnsAuth.host | 
					
						
							|  |  |  |                       , dnsAnswer: dnsAuth.answer | 
					
						
							|  |  |  |                       }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                       console.log(''); | 
					
						
							|  |  |  |                       console.log('CHALLENGE'); | 
					
						
							|  |  |  |                       console.log(claim); | 
					
						
							|  |  |  |                       console.log(c); | 
					
						
							|  |  |  |                       console.log(data); | 
					
						
							|  |  |  |                       console.log(''); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                       if (claim.wildcard) { | 
					
						
							|  |  |  |                         obj.wildcard.push(data); | 
					
						
							|  |  |  |                         $qs(map.wildcard).innerHTML += '<tr><td>' + data.hostname + '</td><td>' + data.dnsHost + '</td><td>' + data.dnsAnswer + '</td></tr>'; | 
					
						
							|  |  |  |                       } else { | 
					
						
							|  |  |  |                         obj[data.type].push(data); | 
					
						
							|  |  |  |                         if ('dns-01' === data.type) { | 
					
						
							|  |  |  |                           $qs(map[data.type]).innerHTML += '<tr><td>' + data.hostname + '</td><td>' + data.dnsHost + '</td><td>' + data.dnsAnswer + '</td></tr>'; | 
					
						
							|  |  |  |                         } else if ('http-01' === data.type) { | 
					
						
							|  |  |  |                           $qs(map[data.type]).innerHTML += '<tr><td>' + data.hostname + '</td><td>' + data.httpPath + '</td><td>' + data.httpAuth + '</td></tr>'; | 
					
						
							|  |  |  |                         } else { | 
					
						
							|  |  |  |                           throw new Error('Unexpected type: ' + data.type); | 
					
						
							|  |  |  |                         } | 
					
						
							|  |  |  |                       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                   })); | 
					
						
							|  |  |  |                 })).then(function () { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                   // hide wildcard if no wildcard
 | 
					
						
							|  |  |  |                   // hide http-01 and dns-01 if only wildcard
 | 
					
						
							|  |  |  |                   if (!obj.wildcard.length) { | 
					
						
							|  |  |  |                     $qs('.js-acme-wildcard').hidden = true; | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  |                   if (!obj['http-01'].length) { | 
					
						
							|  |  |  |                     $qs('.js-acme-challenges').hidden = true; | 
					
						
							|  |  |  |                   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                   updateChallengeType(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |                   console.log("MAGIC STEP NUMBER in 2 is:", i); | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |                   steps[i](); | 
					
						
							|  |  |  |                 }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |               }); | 
					
						
							| 
									
										
										
										
											2018-05-02 08:02:22 +00:00
										 |  |  |             }); | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }).catch(function (err) { | 
					
						
							|  |  |  |       console.error('Step \'' + i + '\' Error:'); | 
					
						
							|  |  |  |       console.error(err); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-05-01 08:29:19 +00:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-05-01 08:15:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   steps[3] = function () { | 
					
						
							|  |  |  |     hideForms(); | 
					
						
							|  |  |  |     $qs('.js-acme-form-challenges').hidden = false; | 
					
						
							|  |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |   steps[3].submit = function () { | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |     var chType; | 
					
						
							|  |  |  |     Array.prototype.some.call($qsa('.js-acme-challenge-type'), function ($el) { | 
					
						
							|  |  |  |       if ($el.checked) { | 
					
						
							|  |  |  |         chType = $el.value; | 
					
						
							|  |  |  |         return true; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |     console.log('chType is:', chType); | 
					
						
							|  |  |  |     var chs = []; | 
					
						
							| 
									
										
										
										
											2018-05-01 08:15:41 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |     // do each wildcard, if any
 | 
					
						
							|  |  |  |     // do each challenge, by selected type only
 | 
					
						
							|  |  |  |     [ 'wildcard', chType].forEach(function (typ) { | 
					
						
							|  |  |  |       info.challenges[typ].forEach(function (ch) { | 
					
						
							|  |  |  |         // { jwk, challengeUrl, accountId (kid) }
 | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |         chs.push({ | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |           jwk: info.jwk | 
					
						
							|  |  |  |         , challengeUrl: ch.url | 
					
						
							|  |  |  |         , accountId: info.kid | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |         }); | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-05-04 11:10:43 +00:00
										 |  |  |     console.log("INFO.challenges !!!!!", info.challenges); | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |     var results = []; | 
					
						
							|  |  |  |     function nextChallenge() { | 
					
						
							|  |  |  |       var ch = chs.pop(); | 
					
						
							|  |  |  |       if (!ch) { return results; } | 
					
						
							|  |  |  |       return BACME.challenges.accept(ch).then(function (result) { | 
					
						
							|  |  |  |         results.push(result); | 
					
						
							|  |  |  |         return nextChallenge(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 11:10:43 +00:00
										 |  |  |     // for now just show the next page immediately (its a spinner)
 | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |     steps[i](); | 
					
						
							|  |  |  |     return nextChallenge().then(function (results) { | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |       console.log('challenge status:', results); | 
					
						
							|  |  |  |       var polls = results.slice(0); | 
					
						
							|  |  |  |       var allsWell = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       function checkPolls() { | 
					
						
							|  |  |  |         return new Promise(function (resolve) { | 
					
						
							|  |  |  |           setTimeout(resolve, 1000); | 
					
						
							|  |  |  |         }).then(function () { | 
					
						
							|  |  |  |           return Promise.all(polls.map(function (poll) { | 
					
						
							|  |  |  |             return BACME.challenges.check({ challengePollUrl: poll.url }); | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |           })).then(function (polls) { | 
					
						
							|  |  |  |             console.log(polls); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |             polls = polls.filter(function (poll) { | 
					
						
							|  |  |  |               //return 'valid' !== poll.status && 'invalid' !== poll.status;
 | 
					
						
							|  |  |  |               if ('pending' === poll.status) { | 
					
						
							|  |  |  |                 return true; | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |               if ('valid' !== poll.status) { | 
					
						
							|  |  |  |                 allsWell = false; | 
					
						
							|  |  |  |                 console.warn('BAD POLL STATUS', poll); | 
					
						
							|  |  |  |               } | 
					
						
							|  |  |  |               // TODO show status in HTML
 | 
					
						
							|  |  |  |             }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if (polls.length) { | 
					
						
							|  |  |  |               return checkPolls(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return true; | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       return checkPolls().then(function () { | 
					
						
							|  |  |  |         if (allsWell) { | 
					
						
							|  |  |  |           return submitForm(); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   // spinner
 | 
					
						
							| 
									
										
										
										
											2018-05-01 08:15:41 +00:00
										 |  |  |   steps[4] = function () { | 
					
						
							|  |  |  |     hideForms(); | 
					
						
							|  |  |  |     $qs('.js-acme-form-poll').hidden = false; | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |   steps[4].submit = function () { | 
					
						
							|  |  |  |     console.log('Congrats! Auto advancing...'); | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |     var key = info.identifiers.map(function (ident) { return ident.value; }).join(','); | 
					
						
							|  |  |  |     var serverJwk = JSON.parse(localStorage.getItem('server:' + key) || 'null'); | 
					
						
							|  |  |  |     var p; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     function createKeypair() { | 
					
						
							|  |  |  |       return BACME.accounts.generateKeypair({ | 
					
						
							|  |  |  |         type: 'ECDSA' | 
					
						
							|  |  |  |       , bitlength: '256' | 
					
						
							|  |  |  |       }).then(function (serverJwk) { | 
					
						
							|  |  |  |         localStorage.setItem('server:' + key, JSON.stringify(serverJwk)); | 
					
						
							|  |  |  |         return serverJwk; | 
					
						
							|  |  |  |       }) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (serverJwk) { | 
					
						
							|  |  |  |       p = Promise.resolve(serverJwk); | 
					
						
							|  |  |  |     } else { | 
					
						
							|  |  |  |       p = createKeypair(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return p.then(function (_serverJwk) { | 
					
						
							|  |  |  |       serverJwk = _serverJwk; | 
					
						
							| 
									
										
										
										
											2018-05-04 11:10:43 +00:00
										 |  |  |       info.serverJwk = serverJwk; | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |       // { serverJwk, domains }
 | 
					
						
							|  |  |  |       return BACME.orders.generateCsr({ | 
					
						
							|  |  |  |         serverJwk: serverJwk | 
					
						
							|  |  |  |       , domains: info.identifiers.map(function (ident) { | 
					
						
							|  |  |  |           return ident.value; | 
					
						
							|  |  |  |         }) | 
					
						
							|  |  |  |       }).then(function (csrweb64) { | 
					
						
							| 
									
										
										
										
											2018-05-04 11:10:43 +00:00
										 |  |  |         return BACME.orders.finalize({ | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |           csr: csrweb64 | 
					
						
							|  |  |  |         , jwk: info.jwk | 
					
						
							|  |  |  |         , finalizeUrl: info.finalizeUrl | 
					
						
							|  |  |  |         , accountId: info.kid | 
					
						
							|  |  |  |         }); | 
					
						
							|  |  |  |       }).then(function () { | 
					
						
							|  |  |  |         function checkCert() { | 
					
						
							|  |  |  |           return new Promise(function (resolve) { | 
					
						
							|  |  |  |             setTimeout(resolve, 1000); | 
					
						
							|  |  |  |           }).then(function () { | 
					
						
							| 
									
										
										
										
											2018-05-04 11:10:43 +00:00
										 |  |  |             return BACME.orders.check({ orderUrl: info.orderUrl }); | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |           }).then(function (reply) { | 
					
						
							|  |  |  |             if ('processing' === reply) { | 
					
						
							|  |  |  |               return checkCert(); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             return reply; | 
					
						
							|  |  |  |           }); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return checkCert(); | 
					
						
							|  |  |  |       }).then(function (reply) { | 
					
						
							| 
									
										
										
										
											2018-05-04 11:10:43 +00:00
										 |  |  |         return BACME.orders.receive({ certificateUrl: reply.certificate }); | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |       }).then(function (certs) { | 
					
						
							|  |  |  |         console.log('WINNING!'); | 
					
						
							|  |  |  |         console.log(certs); | 
					
						
							| 
									
										
										
										
											2018-05-04 11:10:43 +00:00
										 |  |  |         $qs('.js-fullchain').value = certs; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // https://stackoverflow.com/questions/40314257/export-webcrypto-key-to-pem-format
 | 
					
						
							|  |  |  | 				function spkiToPEM(keydata){ | 
					
						
							|  |  |  | 						var keydataS = arrayBufferToString(keydata); | 
					
						
							|  |  |  | 						var keydataB64 = window.btoa(keydataS); | 
					
						
							|  |  |  | 						var keydataB64Pem = formatAsPem(keydataB64); | 
					
						
							|  |  |  | 						return keydataB64Pem; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				function arrayBufferToString( buffer ) { | 
					
						
							|  |  |  | 						var binary = ''; | 
					
						
							|  |  |  | 						var bytes = new Uint8Array( buffer ); | 
					
						
							|  |  |  | 						var len = bytes.byteLength; | 
					
						
							|  |  |  | 						for (var i = 0; i < len; i++) { | 
					
						
							|  |  |  | 								binary += String.fromCharCode( bytes[ i ] ); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 						return binary; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				function formatAsPem(str) { | 
					
						
							|  |  |  | 						var finalString = '-----BEGIN ' + pemName + ' PRIVATE KEY-----\n'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						while(str.length > 0) { | 
					
						
							|  |  |  | 								finalString += str.substring(0, 64) + '\n'; | 
					
						
							|  |  |  | 								str = str.substring(64); | 
					
						
							|  |  |  | 						} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						finalString = finalString + '-----END ' + pemName + ' PRIVATE KEY-----'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 						return finalString; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         var wcOpts; | 
					
						
							|  |  |  |         var pemName; | 
					
						
							|  |  |  |         if (/^R/.test(info.serverJwk.kty)) { | 
					
						
							|  |  |  |           pemName = 'RSA'; | 
					
						
							|  |  |  |           wcOpts = { | 
					
						
							|  |  |  |             name: "RSASSA-PKCS1-v1_5" | 
					
						
							|  |  |  |           , hash: { name: "SHA-256" } | 
					
						
							|  |  |  |           }; | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |           pemName = 'EC'; | 
					
						
							|  |  |  |           wcOpts = { | 
					
						
							|  |  |  |             name: "ECDSA" | 
					
						
							|  |  |  |           , namedCurve: "P-256" | 
					
						
							|  |  |  |           } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 				return crypto.subtle.importKey( | 
					
						
							|  |  |  |           "jwk" | 
					
						
							|  |  |  |         , info.serverJwk | 
					
						
							|  |  |  |         , wcOpts | 
					
						
							|  |  |  |         , true | 
					
						
							|  |  |  |         , ["sign"] | 
					
						
							|  |  |  | 				).then(function (privateKey) { | 
					
						
							|  |  |  | 				  return window.crypto.subtle.exportKey("pkcs8", privateKey); | 
					
						
							|  |  |  | 				}).then (function (keydata) { | 
					
						
							|  |  |  | 					var pem = spkiToPEM(keydata); | 
					
						
							|  |  |  | 					$qs('.js-privkey').value = pem; | 
					
						
							|  |  |  |           steps[i](); | 
					
						
							|  |  |  | 				}).catch(function(err){ | 
					
						
							|  |  |  | 					console.error(err); | 
					
						
							|  |  |  | 				}); | 
					
						
							| 
									
										
										
										
											2018-05-04 10:16:31 +00:00
										 |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2018-05-04 08:40:04 +00:00
										 |  |  |   }; | 
					
						
							| 
									
										
										
										
											2018-05-01 08:15:41 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   steps[5] = function () { | 
					
						
							|  |  |  |     hideForms(); | 
					
						
							|  |  |  |     $qs('.js-acme-form-download').hidden = false; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   steps[1](); | 
					
						
							| 
									
										
										
										
											2018-05-14 18:53:30 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |   $qs('body').hidden = false; | 
					
						
							| 
									
										
										
										
											2018-04-30 18:03:12 -06:00
										 |  |  | }()); |