gcenter.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. "use strict";
  2. /* Apple Game Center Auth
  3. https://developer.apple.com/documentation/gamekit/gklocalplayer/1515407-generateidentityverificationsign#discussion
  4. const authData = {
  5. publicKeyUrl: 'https://valid.apple.com/public/timeout.cer',
  6. timestamp: 1460981421303,
  7. signature: 'PoDwf39DCN464B49jJCU0d9Y0J',
  8. salt: 'saltST==',
  9. bundleId: 'com.valid.app'
  10. id: 'playerId',
  11. };
  12. */
  13. const {
  14. Parse
  15. } = require('parse/node');
  16. const crypto = require('crypto');
  17. const https = require('https');
  18. const {
  19. pki
  20. } = require('node-forge');
  21. const ca = {
  22. cert: null,
  23. url: null
  24. };
  25. const cache = {}; // (publicKey -> cert) cache
  26. function verifyPublicKeyUrl(publicKeyUrl) {
  27. try {
  28. const regex = /^https:\/\/(?:[-_A-Za-z0-9]+\.){0,}apple\.com\/.*\.cer$/;
  29. return regex.test(publicKeyUrl);
  30. } catch (error) {
  31. return false;
  32. }
  33. }
  34. function convertX509CertToPEM(X509Cert) {
  35. const pemPreFix = '-----BEGIN CERTIFICATE-----\n';
  36. const pemPostFix = '-----END CERTIFICATE-----';
  37. const base64 = X509Cert;
  38. const certBody = base64.match(new RegExp('.{0,64}', 'g')).join('\n');
  39. return pemPreFix + certBody + pemPostFix;
  40. }
  41. async function getAppleCertificate(publicKeyUrl) {
  42. if (!verifyPublicKeyUrl(publicKeyUrl)) {
  43. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `Apple Game Center - invalid publicKeyUrl: ${publicKeyUrl}`);
  44. }
  45. if (cache[publicKeyUrl]) {
  46. return cache[publicKeyUrl];
  47. }
  48. const url = new URL(publicKeyUrl);
  49. const headOptions = {
  50. hostname: url.hostname,
  51. path: url.pathname,
  52. method: 'HEAD'
  53. };
  54. const cert_headers = await new Promise((resolve, reject) => https.get(headOptions, res => resolve(res.headers)).on('error', reject));
  55. const validContentTypes = ['application/x-x509-ca-cert', 'application/pkix-cert'];
  56. if (!validContentTypes.includes(cert_headers['content-type']) || cert_headers['content-length'] == null || cert_headers['content-length'] > 10000) {
  57. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `Apple Game Center - invalid publicKeyUrl: ${publicKeyUrl}`);
  58. }
  59. const {
  60. certificate,
  61. headers
  62. } = await getCertificate(publicKeyUrl);
  63. if (headers['cache-control']) {
  64. const expire = headers['cache-control'].match(/max-age=([0-9]+)/);
  65. if (expire) {
  66. cache[publicKeyUrl] = certificate;
  67. // we'll expire the cache entry later, as per max-age
  68. setTimeout(() => {
  69. delete cache[publicKeyUrl];
  70. }, parseInt(expire[1], 10) * 1000);
  71. }
  72. }
  73. return verifyPublicKeyIssuer(certificate, publicKeyUrl);
  74. }
  75. function getCertificate(url, buffer) {
  76. return new Promise((resolve, reject) => {
  77. https.get(url, res => {
  78. const data = [];
  79. res.on('data', chunk => {
  80. data.push(chunk);
  81. });
  82. res.on('end', () => {
  83. if (buffer) {
  84. resolve({
  85. certificate: Buffer.concat(data),
  86. headers: res.headers
  87. });
  88. return;
  89. }
  90. let cert = '';
  91. for (const chunk of data) {
  92. cert += chunk.toString('base64');
  93. }
  94. const certificate = convertX509CertToPEM(cert);
  95. resolve({
  96. certificate,
  97. headers: res.headers
  98. });
  99. });
  100. }).on('error', reject);
  101. });
  102. }
  103. function convertTimestampToBigEndian(timestamp) {
  104. const buffer = Buffer.alloc(8);
  105. const high = ~~(timestamp / 0xffffffff);
  106. const low = timestamp % (0xffffffff + 0x1);
  107. buffer.writeUInt32BE(parseInt(high, 10), 0);
  108. buffer.writeUInt32BE(parseInt(low, 10), 4);
  109. return buffer;
  110. }
  111. function verifySignature(publicKey, authData) {
  112. const verifier = crypto.createVerify('sha256');
  113. verifier.update(authData.playerId, 'utf8');
  114. verifier.update(authData.bundleId, 'utf8');
  115. verifier.update(convertTimestampToBigEndian(authData.timestamp));
  116. verifier.update(authData.salt, 'base64');
  117. if (!verifier.verify(publicKey, authData.signature, 'base64')) {
  118. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Apple Game Center - invalid signature');
  119. }
  120. }
  121. function verifyPublicKeyIssuer(cert, publicKeyUrl) {
  122. const publicKeyCert = pki.certificateFromPem(cert);
  123. if (!ca.cert) {
  124. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Apple Game Center auth adapter parameter `rootCertificateURL` is invalid.');
  125. }
  126. try {
  127. if (!ca.cert.verify(publicKeyCert)) {
  128. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `Apple Game Center - invalid publicKeyUrl: ${publicKeyUrl}`);
  129. }
  130. } catch (e) {
  131. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `Apple Game Center - invalid publicKeyUrl: ${publicKeyUrl}`);
  132. }
  133. return cert;
  134. }
  135. // Returns a promise that fulfills if this user id is valid.
  136. async function validateAuthData(authData) {
  137. if (!authData.id) {
  138. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Apple Game Center - authData id missing');
  139. }
  140. authData.playerId = authData.id;
  141. const publicKey = await getAppleCertificate(authData.publicKeyUrl);
  142. return verifySignature(publicKey, authData);
  143. }
  144. // Returns a promise that fulfills if this app id is valid.
  145. async function validateAppId(appIds, authData, options = {}) {
  146. if (!options.rootCertificateUrl) {
  147. options.rootCertificateUrl = 'https://cacerts.digicert.com/DigiCertTrustedG4CodeSigningRSA4096SHA3842021CA1.crt.pem';
  148. }
  149. if (ca.url === options.rootCertificateUrl) {
  150. return;
  151. }
  152. const {
  153. certificate,
  154. headers
  155. } = await getCertificate(options.rootCertificateUrl, true);
  156. if (headers['content-type'] !== 'application/x-pem-file' || headers['content-length'] == null || headers['content-length'] > 10000) {
  157. throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Apple Game Center auth adapter parameter `rootCertificateURL` is invalid.');
  158. }
  159. ca.cert = pki.certificateFromPem(certificate);
  160. ca.url = options.rootCertificateUrl;
  161. }
  162. module.exports = {
  163. validateAppId,
  164. validateAuthData,
  165. cache
  166. };
  167. //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJQYXJzZSIsInJlcXVpcmUiLCJjcnlwdG8iLCJodHRwcyIsInBraSIsImNhIiwiY2VydCIsInVybCIsImNhY2hlIiwidmVyaWZ5UHVibGljS2V5VXJsIiwicHVibGljS2V5VXJsIiwicmVnZXgiLCJ0ZXN0IiwiZXJyb3IiLCJjb252ZXJ0WDUwOUNlcnRUb1BFTSIsIlg1MDlDZXJ0IiwicGVtUHJlRml4IiwicGVtUG9zdEZpeCIsImJhc2U2NCIsImNlcnRCb2R5IiwibWF0Y2giLCJSZWdFeHAiLCJqb2luIiwiZ2V0QXBwbGVDZXJ0aWZpY2F0ZSIsIkVycm9yIiwiT0JKRUNUX05PVF9GT1VORCIsIlVSTCIsImhlYWRPcHRpb25zIiwiaG9zdG5hbWUiLCJwYXRoIiwicGF0aG5hbWUiLCJtZXRob2QiLCJjZXJ0X2hlYWRlcnMiLCJQcm9taXNlIiwicmVzb2x2ZSIsInJlamVjdCIsImdldCIsInJlcyIsImhlYWRlcnMiLCJvbiIsInZhbGlkQ29udGVudFR5cGVzIiwiaW5jbHVkZXMiLCJjZXJ0aWZpY2F0ZSIsImdldENlcnRpZmljYXRlIiwiZXhwaXJlIiwic2V0VGltZW91dCIsInBhcnNlSW50IiwidmVyaWZ5UHVibGljS2V5SXNzdWVyIiwiYnVmZmVyIiwiZGF0YSIsImNodW5rIiwicHVzaCIsIkJ1ZmZlciIsImNvbmNhdCIsInRvU3RyaW5nIiwiY29udmVydFRpbWVzdGFtcFRvQmlnRW5kaWFuIiwidGltZXN0YW1wIiwiYWxsb2MiLCJoaWdoIiwibG93Iiwid3JpdGVVSW50MzJCRSIsInZlcmlmeVNpZ25hdHVyZSIsInB1YmxpY0tleSIsImF1dGhEYXRhIiwidmVyaWZpZXIiLCJjcmVhdGVWZXJpZnkiLCJ1cGRhdGUiLCJwbGF5ZXJJZCIsImJ1bmRsZUlkIiwic2FsdCIsInZlcmlmeSIsInNpZ25hdHVyZSIsInB1YmxpY0tleUNlcnQiLCJjZXJ0aWZpY2F0ZUZyb21QZW0iLCJlIiwidmFsaWRhdGVBdXRoRGF0YSIsImlkIiwidmFsaWRhdGVBcHBJZCIsImFwcElkcyIsIm9wdGlvbnMiLCJyb290Q2VydGlmaWNhdGVVcmwiLCJtb2R1bGUiLCJleHBvcnRzIl0sInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL0FkYXB0ZXJzL0F1dGgvZ2NlbnRlci5qcyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiBBcHBsZSBHYW1lIENlbnRlciBBdXRoXG5odHRwczovL2RldmVsb3Blci5hcHBsZS5jb20vZG9jdW1lbnRhdGlvbi9nYW1la2l0L2drbG9jYWxwbGF5ZXIvMTUxNTQwNy1nZW5lcmF0ZWlkZW50aXR5dmVyaWZpY2F0aW9uc2lnbiNkaXNjdXNzaW9uXG5cbmNvbnN0IGF1dGhEYXRhID0ge1xuICBwdWJsaWNLZXlVcmw6ICdodHRwczovL3ZhbGlkLmFwcGxlLmNvbS9wdWJsaWMvdGltZW91dC5jZXInLFxuICB0aW1lc3RhbXA6IDE0NjA5ODE0MjEzMDMsXG4gIHNpZ25hdHVyZTogJ1BvRHdmMzlEQ040NjRCNDlqSkNVMGQ5WTBKJyxcbiAgc2FsdDogJ3NhbHRTVD09JyxcbiAgYnVuZGxlSWQ6ICdjb20udmFsaWQuYXBwJ1xuICBpZDogJ3BsYXllcklkJyxcbn07XG4qL1xuXG5jb25zdCB7IFBhcnNlIH0gPSByZXF1aXJlKCdwYXJzZS9ub2RlJyk7XG5jb25zdCBjcnlwdG8gPSByZXF1aXJlKCdjcnlwdG8nKTtcbmNvbnN0IGh0dHBzID0gcmVxdWlyZSgnaHR0cHMnKTtcbmNvbnN0IHsgcGtpIH0gPSByZXF1aXJlKCdub2RlLWZvcmdlJyk7XG5jb25zdCBjYSA9IHsgY2VydDogbnVsbCwgdXJsOiBudWxsIH07XG5jb25zdCBjYWNoZSA9IHt9OyAvLyAocHVibGljS2V5IC0+IGNlcnQpIGNhY2hlXG5cbmZ1bmN0aW9uIHZlcmlmeVB1YmxpY0tleVVybChwdWJsaWNLZXlVcmwpIHtcbiAgdHJ5IHtcbiAgICBjb25zdCByZWdleCA9IC9eaHR0cHM6XFwvXFwvKD86Wy1fQS1aYS16MC05XStcXC4pezAsfWFwcGxlXFwuY29tXFwvLipcXC5jZXIkLztcbiAgICByZXR1cm4gcmVnZXgudGVzdChwdWJsaWNLZXlVcmwpO1xuICB9IGNhdGNoIChlcnJvcikge1xuICAgIHJldHVybiBmYWxzZTtcbiAgfVxufVxuXG5mdW5jdGlvbiBjb252ZXJ0WDUwOUNlcnRUb1BFTShYNTA5Q2VydCkge1xuICBjb25zdCBwZW1QcmVGaXggPSAnLS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tXFxuJztcbiAgY29uc3QgcGVtUG9zdEZpeCA9ICctLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tJztcblxuICBjb25zdCBiYXNlNjQgPSBYNTA5Q2VydDtcbiAgY29uc3QgY2VydEJvZHkgPSBiYXNlNjQubWF0Y2gobmV3IFJlZ0V4cCgnLnswLDY0fScsICdnJykpLmpvaW4oJ1xcbicpO1xuXG4gIHJldHVybiBwZW1QcmVGaXggKyBjZXJ0Qm9keSArIHBlbVBvc3RGaXg7XG59XG5cbmFzeW5jIGZ1bmN0aW9uIGdldEFwcGxlQ2VydGlmaWNhdGUocHVibGljS2V5VXJsKSB7XG4gIGlmICghdmVyaWZ5UHVibGljS2V5VXJsKHB1YmxpY0tleVVybCkpIHtcbiAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoXG4gICAgICBQYXJzZS5FcnJvci5PQkpFQ1RfTk9UX0ZPVU5ELFxuICAgICAgYEFwcGxlIEdhbWUgQ2VudGVyIC0gaW52YWxpZCBwdWJsaWNLZXlVcmw6ICR7cHVibGljS2V5VXJsfWBcbiAgICApO1xuICB9XG4gIGlmIChjYWNoZVtwdWJsaWNLZXlVcmxdKSB7XG4gICAgcmV0dXJuIGNhY2hlW3B1YmxpY0tleVVybF07XG4gIH1cbiAgY29uc3QgdXJsID0gbmV3IFVSTChwdWJsaWNLZXlVcmwpO1xuICBjb25zdCBoZWFkT3B0aW9ucyA9IHtcbiAgICBob3N0bmFtZTogdXJsLmhvc3RuYW1lLFxuICAgIHBhdGg6IHVybC5wYXRobmFtZSxcbiAgICBtZXRob2Q6ICdIRUFEJyxcbiAgfTtcbiAgY29uc3QgY2VydF9oZWFkZXJzID0gYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT5cbiAgICBodHRwcy5nZXQoaGVhZE9wdGlvbnMsIHJlcyA9PiByZXNvbHZlKHJlcy5oZWFkZXJzKSkub24oJ2Vycm9yJywgcmVqZWN0KVxuICApO1xuICBjb25zdCB2YWxpZENvbnRlbnRUeXBlcyA9IFsnYXBwbGljYXRpb24veC14NTA5LWNhLWNlcnQnLCAnYXBwbGljYXRpb24vcGtpeC1jZXJ0J107XG4gIGlmIChcbiAgICAhdmFsaWRDb250ZW50VHlwZXMuaW5jbHVkZXMoY2VydF9oZWFkZXJzWydjb250ZW50LXR5cGUnXSkgfHxcbiAgICBjZXJ0X2hlYWRlcnNbJ2NvbnRlbnQtbGVuZ3RoJ10gPT0gbnVsbCB8fFxuICAgIGNlcnRfaGVhZGVyc1snY29udGVudC1sZW5ndGgnXSA+IDEwMDAwXG4gICkge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihcbiAgICAgIFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsXG4gICAgICBgQXBwbGUgR2FtZSBDZW50ZXIgLSBpbnZhbGlkIHB1YmxpY0tleVVybDogJHtwdWJsaWNLZXlVcmx9YFxuICAgICk7XG4gIH1cbiAgY29uc3QgeyBjZXJ0aWZpY2F0ZSwgaGVhZGVycyB9ID0gYXdhaXQgZ2V0Q2VydGlmaWNhdGUocHVibGljS2V5VXJsKTtcbiAgaWYgKGhlYWRlcnNbJ2NhY2hlLWNvbnRyb2wnXSkge1xuICAgIGNvbnN0IGV4cGlyZSA9IGhlYWRlcnNbJ2NhY2hlLWNvbnRyb2wnXS5tYXRjaCgvbWF4LWFnZT0oWzAtOV0rKS8pO1xuICAgIGlmIChleHBpcmUpIHtcbiAgICAgIGNhY2hlW3B1YmxpY0tleVVybF0gPSBjZXJ0aWZpY2F0ZTtcbiAgICAgIC8vIHdlJ2xsIGV4cGlyZSB0aGUgY2FjaGUgZW50cnkgbGF0ZXIsIGFzIHBlciBtYXgtYWdlXG4gICAgICBzZXRUaW1lb3V0KCgpID0+IHtcbiAgICAgICAgZGVsZXRlIGNhY2hlW3B1YmxpY0tleVVybF07XG4gICAgICB9LCBwYXJzZUludChleHBpcmVbMV0sIDEwKSAqIDEwMDApO1xuICAgIH1cbiAgfVxuICByZXR1cm4gdmVyaWZ5UHVibGljS2V5SXNzdWVyKGNlcnRpZmljYXRlLCBwdWJsaWNLZXlVcmwpO1xufVxuXG5mdW5jdGlvbiBnZXRDZXJ0aWZpY2F0ZSh1cmwsIGJ1ZmZlcikge1xuICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgIGh0dHBzXG4gICAgICAuZ2V0KHVybCwgcmVzID0+IHtcbiAgICAgICAgY29uc3QgZGF0YSA9IFtdO1xuICAgICAgICByZXMub24oJ2RhdGEnLCBjaHVuayA9PiB7XG4gICAgICAgICAgZGF0YS5wdXNoKGNodW5rKTtcbiAgICAgICAgfSk7XG4gICAgICAgIHJlcy5vbignZW5kJywgKCkgPT4ge1xuICAgICAgICAgIGlmIChidWZmZXIpIHtcbiAgICAgICAgICAgIHJlc29sdmUoeyBjZXJ0aWZpY2F0ZTogQnVmZmVyLmNvbmNhdChkYXRhKSwgaGVhZGVyczogcmVzLmhlYWRlcnMgfSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfVxuICAgICAgICAgIGxldCBjZXJ0ID0gJyc7XG4gICAgICAgICAgZm9yIChjb25zdCBjaHVuayBvZiBkYXRhKSB7XG4gICAgICAgICAgICBjZXJ0ICs9IGNodW5rLnRvU3RyaW5nKCdiYXNlNjQnKTtcbiAgICAgICAgICB9XG4gICAgICAgICAgY29uc3QgY2VydGlmaWNhdGUgPSBjb252ZXJ0WDUwOUNlcnRUb1BFTShjZXJ0KTtcbiAgICAgICAgICByZXNvbHZlKHsgY2VydGlmaWNhdGUsIGhlYWRlcnM6IHJlcy5oZWFkZXJzIH0pO1xuICAgICAgICB9KTtcbiAgICAgIH0pXG4gICAgICAub24oJ2Vycm9yJywgcmVqZWN0KTtcbiAgfSk7XG59XG5cbmZ1bmN0aW9uIGNvbnZlcnRUaW1lc3RhbXBUb0JpZ0VuZGlhbih0aW1lc3RhbXApIHtcbiAgY29uc3QgYnVmZmVyID0gQnVmZmVyLmFsbG9jKDgpO1xuXG4gIGNvbnN0IGhpZ2ggPSB+fih0aW1lc3RhbXAgLyAweGZmZmZmZmZmKTtcbiAgY29uc3QgbG93ID0gdGltZXN0YW1wICUgKDB4ZmZmZmZmZmYgKyAweDEpO1xuXG4gIGJ1ZmZlci53cml0ZVVJbnQzMkJFKHBhcnNlSW50KGhpZ2gsIDEwKSwgMCk7XG4gIGJ1ZmZlci53cml0ZVVJbnQzMkJFKHBhcnNlSW50KGxvdywgMTApLCA0KTtcblxuICByZXR1cm4gYnVmZmVyO1xufVxuXG5mdW5jdGlvbiB2ZXJpZnlTaWduYXR1cmUocHVibGljS2V5LCBhdXRoRGF0YSkge1xuICBjb25zdCB2ZXJpZmllciA9IGNyeXB0by5jcmVhdGVWZXJpZnkoJ3NoYTI1NicpO1xuICB2ZXJpZmllci51cGRhdGUoYXV0aERhdGEucGxheWVySWQsICd1dGY4Jyk7XG4gIHZlcmlmaWVyLnVwZGF0ZShhdXRoRGF0YS5idW5kbGVJZCwgJ3V0ZjgnKTtcbiAgdmVyaWZpZXIudXBkYXRlKGNvbnZlcnRUaW1lc3RhbXBUb0JpZ0VuZGlhbihhdXRoRGF0YS50aW1lc3RhbXApKTtcbiAgdmVyaWZpZXIudXBkYXRlKGF1dGhEYXRhLnNhbHQsICdiYXNlNjQnKTtcblxuICBpZiAoIXZlcmlmaWVyLnZlcmlmeShwdWJsaWNLZXksIGF1dGhEYXRhLnNpZ25hdHVyZSwgJ2Jhc2U2NCcpKSB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsICdBcHBsZSBHYW1lIENlbnRlciAtIGludmFsaWQgc2lnbmF0dXJlJyk7XG4gIH1cbn1cblxuZnVuY3Rpb24gdmVyaWZ5UHVibGljS2V5SXNzdWVyKGNlcnQsIHB1YmxpY0tleVVybCkge1xuICBjb25zdCBwdWJsaWNLZXlDZXJ0ID0gcGtpLmNlcnRpZmljYXRlRnJvbVBlbShjZXJ0KTtcbiAgaWYgKCFjYS5jZXJ0KSB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFxuICAgICAgUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCxcbiAgICAgICdBcHBsZSBHYW1lIENlbnRlciBhdXRoIGFkYXB0ZXIgcGFyYW1ldGVyIGByb290Q2VydGlmaWNhdGVVUkxgIGlzIGludmFsaWQuJ1xuICAgICk7XG4gIH1cbiAgdHJ5IHtcbiAgICBpZiAoIWNhLmNlcnQudmVyaWZ5KHB1YmxpY0tleUNlcnQpKSB7XG4gICAgICB0aHJvdyBuZXcgUGFyc2UuRXJyb3IoXG4gICAgICAgIFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsXG4gICAgICAgIGBBcHBsZSBHYW1lIENlbnRlciAtIGludmFsaWQgcHVibGljS2V5VXJsOiAke3B1YmxpY0tleVVybH1gXG4gICAgICApO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgIHRocm93IG5ldyBQYXJzZS5FcnJvcihcbiAgICAgIFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsXG4gICAgICBgQXBwbGUgR2FtZSBDZW50ZXIgLSBpbnZhbGlkIHB1YmxpY0tleVVybDogJHtwdWJsaWNLZXlVcmx9YFxuICAgICk7XG4gIH1cbiAgcmV0dXJuIGNlcnQ7XG59XG5cbi8vIFJldHVybnMgYSBwcm9taXNlIHRoYXQgZnVsZmlsbHMgaWYgdGhpcyB1c2VyIGlkIGlzIHZhbGlkLlxuYXN5bmMgZnVuY3Rpb24gdmFsaWRhdGVBdXRoRGF0YShhdXRoRGF0YSkge1xuICBpZiAoIWF1dGhEYXRhLmlkKSB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFBhcnNlLkVycm9yLk9CSkVDVF9OT1RfRk9VTkQsICdBcHBsZSBHYW1lIENlbnRlciAtIGF1dGhEYXRhIGlkIG1pc3NpbmcnKTtcbiAgfVxuICBhdXRoRGF0YS5wbGF5ZXJJZCA9IGF1dGhEYXRhLmlkO1xuICBjb25zdCBwdWJsaWNLZXkgPSBhd2FpdCBnZXRBcHBsZUNlcnRpZmljYXRlKGF1dGhEYXRhLnB1YmxpY0tleVVybCk7XG4gIHJldHVybiB2ZXJpZnlTaWduYXR1cmUocHVibGljS2V5LCBhdXRoRGF0YSk7XG59XG5cbi8vIFJldHVybnMgYSBwcm9taXNlIHRoYXQgZnVsZmlsbHMgaWYgdGhpcyBhcHAgaWQgaXMgdmFsaWQuXG5hc3luYyBmdW5jdGlvbiB2YWxpZGF0ZUFwcElkKGFwcElkcywgYXV0aERhdGEsIG9wdGlvbnMgPSB7fSkge1xuICBpZiAoIW9wdGlvbnMucm9vdENlcnRpZmljYXRlVXJsKSB7XG4gICAgb3B0aW9ucy5yb290Q2VydGlmaWNhdGVVcmwgPVxuICAgICAgJ2h0dHBzOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNydC5wZW0nO1xuICB9XG4gIGlmIChjYS51cmwgPT09IG9wdGlvbnMucm9vdENlcnRpZmljYXRlVXJsKSB7XG4gICAgcmV0dXJuO1xuICB9XG4gIGNvbnN0IHsgY2VydGlmaWNhdGUsIGhlYWRlcnMgfSA9IGF3YWl0IGdldENlcnRpZmljYXRlKG9wdGlvbnMucm9vdENlcnRpZmljYXRlVXJsLCB0cnVlKTtcbiAgaWYgKFxuICAgIGhlYWRlcnNbJ2NvbnRlbnQtdHlwZSddICE9PSAnYXBwbGljYXRpb24veC1wZW0tZmlsZScgfHxcbiAgICBoZWFkZXJzWydjb250ZW50LWxlbmd0aCddID09IG51bGwgfHxcbiAgICBoZWFkZXJzWydjb250ZW50LWxlbmd0aCddID4gMTAwMDBcbiAgKSB7XG4gICAgdGhyb3cgbmV3IFBhcnNlLkVycm9yKFxuICAgICAgUGFyc2UuRXJyb3IuT0JKRUNUX05PVF9GT1VORCxcbiAgICAgICdBcHBsZSBHYW1lIENlbnRlciBhdXRoIGFkYXB0ZXIgcGFyYW1ldGVyIGByb290Q2VydGlmaWNhdGVVUkxgIGlzIGludmFsaWQuJ1xuICAgICk7XG4gIH1cbiAgY2EuY2VydCA9IHBraS5jZXJ0aWZpY2F0ZUZyb21QZW0oY2VydGlmaWNhdGUpO1xuICBjYS51cmwgPSBvcHRpb25zLnJvb3RDZXJ0aWZpY2F0ZVVybDtcbn1cblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIHZhbGlkYXRlQXBwSWQsXG4gIHZhbGlkYXRlQXV0aERhdGEsXG4gIGNhY2hlLFxufTtcbiJdLCJtYXBwaW5ncyI6Ijs7QUFBQTtBQUNBO0FB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Q0FBQ1osUUFBUSxFQUFFO0VBQ3hDLElBQUksQ0FBQ0EsUUFBUSxDQUFDYSxFQUFFLEVBQUU7SUFDaEIsTUFBTSxJQUFJNUUsS0FBSyxDQUFDd0IsS0FBSyxDQUFDeEIsS0FBSyxDQUFDd0IsS0FBSyxDQUFDQyxnQkFBZ0IsRUFBRSx5Q0FBeUMsQ0FBQztFQUNoRztFQUNBc0MsUUFBUSxDQUFDSSxRQUFRLEdBQUdKLFFBQVEsQ0FBQ2EsRUFBRTtFQUMvQixNQUFNZCxTQUFTLEdBQUcsTUFBTXZDLG1CQUFtQixDQUFDd0MsUUFBUSxDQUFDckQsWUFBWSxDQUFDO0VBQ2xFLE9BQU9tRCxlQUFlLENBQUNDLFNBQVMsRUFBRUMsUUFBUSxDQUFDO0FBQzdDOztBQUVBO0FBQ0EsZUFBZWMsYUFBYUEsQ0FBQ0MsTUFBTSxFQUFFZixRQUFRLEVBQUVnQixPQUFPLEdBQUcsQ0FBQyxDQUFDLEVBQUU7RUFDM0QsSUFBSSxDQUFDQSxPQUFPLENBQUNDLGtCQUFrQixFQUFFO0lBQy9CRCxPQUFPLENBQUNDLGtCQUFrQixHQUN4Qix1RkFBdUY7RUFDM0Y7RUFDQSxJQUFJM0UsRUFBRSxDQUFDRSxHQUFHLEtBQUt3RSxPQUFPLENBQUNDLGtCQUFrQixFQUFFO0lBQ3pDO0VBQ0Y7RUFDQSxNQUFNO0lBQUV0QyxXQUFXO0lBQUVKO0VBQVEsQ0FBQyxHQUFHLE1BQU1LLGNBQWMsQ0FBQ29DLE9BQU8sQ0FBQ0Msa0JBQWtCLEVBQUUsSUFBSSxDQUFDO0VBQ3ZGLElBQ0UxQyxPQUFPLENBQUMsY0FBYyxDQUFDLEtBQUssd0JBQXdCLElBQ3BEQSxPQUFPLENBQUMsZ0JBQWdCLENBQUMsSUFBSSxJQUFJLElBQ2pDQSxPQUFPLENBQUMsZ0JBQWdCLENBQUMsR0FBRyxLQUFLLEVBQ2pDO0lBQ0EsTUFBTSxJQUFJdEMsS0FBSyxDQUFDd0IsS0FBSyxDQUNuQnhCLEtBQUssQ0FBQ3dCLEtBQUssQ0FBQ0MsZ0JBQWdCLEVBQzVCLDJFQUNGLENBQUM7RUFDSDtFQUNBcEIsRUFBRSxDQUFDQyxJQUFJLEdBQUdGLEdBQUcsQ0FBQ3FFLGtCQUFrQixDQUFDL0IsV0FBVyxDQUFDO0VBQzdDckMsRUFBRSxDQUFDRSxHQUFHLEdBQUd3RSxPQUFPLENBQUNDLGtCQUFrQjtBQUNyQztBQUVBQyxNQUFNLENBQUNDLE9BQU8sR0FBRztFQUNmTCxhQUFhO0VBQ2JGLGdCQUFnQjtFQUNoQm5FO0FBQ0YsQ0FBQyIsImlnbm9yZUxpc3QiOltdfQ==