decodeGoogleEarthEnterpriseData.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. import Check from "./Check.js";
  2. import RuntimeError from "./RuntimeError.js";
  3. const compressedMagic = 0x7468dead;
  4. const compressedMagicSwap = 0xadde6874;
  5. /**
  6. * Decodes data that is received from the Google Earth Enterprise server.
  7. *
  8. * @param {ArrayBuffer} key The key used during decoding.
  9. * @param {ArrayBuffer} data The data to be decoded.
  10. *
  11. * @private
  12. */
  13. function decodeGoogleEarthEnterpriseData(key, data) {
  14. if (decodeGoogleEarthEnterpriseData.passThroughDataForTesting) {
  15. return data;
  16. }
  17. //>>includeStart('debug', pragmas.debug);
  18. Check.typeOf.object("key", key);
  19. Check.typeOf.object("data", data);
  20. //>>includeEnd('debug');
  21. const keyLength = key.byteLength;
  22. if (keyLength === 0 || keyLength % 4 !== 0) {
  23. throw new RuntimeError(
  24. "The length of key must be greater than 0 and a multiple of 4."
  25. );
  26. }
  27. const dataView = new DataView(data);
  28. const magic = dataView.getUint32(0, true);
  29. if (magic === compressedMagic || magic === compressedMagicSwap) {
  30. // Occasionally packets don't come back encoded, so just return
  31. return data;
  32. }
  33. const keyView = new DataView(key);
  34. let dp = 0;
  35. const dpend = data.byteLength;
  36. const dpend64 = dpend - (dpend % 8);
  37. const kpend = keyLength;
  38. let kp;
  39. let off = 8;
  40. // This algorithm is intentionally asymmetric to make it more difficult to
  41. // guess. Security through obscurity. :-(
  42. // while we have a full uint64 (8 bytes) left to do
  43. // assumes buffer is 64bit aligned (or processor doesn't care)
  44. while (dp < dpend64) {
  45. // rotate the key each time through by using the offets 16,0,8,16,0,8,...
  46. off = (off + 8) % 24;
  47. kp = off;
  48. // run through one key length xor'ing one uint64 at a time
  49. // then drop out to rotate the key for the next bit
  50. while (dp < dpend64 && kp < kpend) {
  51. dataView.setUint32(
  52. dp,
  53. dataView.getUint32(dp, true) ^ keyView.getUint32(kp, true),
  54. true
  55. );
  56. dataView.setUint32(
  57. dp + 4,
  58. dataView.getUint32(dp + 4, true) ^ keyView.getUint32(kp + 4, true),
  59. true
  60. );
  61. dp += 8;
  62. kp += 24;
  63. }
  64. }
  65. // now the remaining 1 to 7 bytes
  66. if (dp < dpend) {
  67. if (kp >= kpend) {
  68. // rotate the key one last time (if necessary)
  69. off = (off + 8) % 24;
  70. kp = off;
  71. }
  72. while (dp < dpend) {
  73. dataView.setUint8(dp, dataView.getUint8(dp) ^ keyView.getUint8(kp));
  74. dp++;
  75. kp++;
  76. }
  77. }
  78. }
  79. decodeGoogleEarthEnterpriseData.passThroughDataForTesting = false;
  80. export default decodeGoogleEarthEnterpriseData;