encryptedstorage_test.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. // Copyright 2011 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.storage.EncryptedStorageTest');
  15. goog.setTestOnly('goog.storage.EncryptedStorageTest');
  16. goog.require('goog.json');
  17. goog.require('goog.storage.EncryptedStorage');
  18. goog.require('goog.storage.ErrorCode');
  19. goog.require('goog.storage.RichStorage');
  20. goog.require('goog.storage.collectableStorageTester');
  21. goog.require('goog.storage.storageTester');
  22. goog.require('goog.testing.MockClock');
  23. goog.require('goog.testing.PseudoRandom');
  24. goog.require('goog.testing.jsunit');
  25. goog.require('goog.testing.storage.FakeMechanism');
  26. function getEncryptedWrapper(storage, key) {
  27. return goog.json.parse(
  28. storage.mechanism.get(storage.hashKeyWithSecret_(key)));
  29. }
  30. function getEncryptedData(storage, key) {
  31. return getEncryptedWrapper(storage, key)[goog.storage.RichStorage.DATA_KEY];
  32. }
  33. function decryptWrapper(storage, key, wrapper) {
  34. return goog.json.parse(
  35. storage.decryptValue_(
  36. wrapper[goog.storage.EncryptedStorage.SALT_KEY], key,
  37. wrapper[goog.storage.RichStorage.DATA_KEY]));
  38. }
  39. function hammingDistance(a, b) {
  40. if (a.length != b.length) {
  41. throw Error('Lengths must be the same for Hamming distance');
  42. }
  43. var distance = 0;
  44. for (var i = 0; i < a.length; ++i) {
  45. if (a.charAt(i) != b.charAt(i)) {
  46. ++distance;
  47. }
  48. }
  49. return distance;
  50. }
  51. function testBasicOperations() {
  52. var mechanism = new goog.testing.storage.FakeMechanism();
  53. var storage = new goog.storage.EncryptedStorage(mechanism, 'secret');
  54. goog.storage.storageTester.runBasicTests(storage);
  55. }
  56. function testExpiredKeyCollection() {
  57. var mechanism = new goog.testing.storage.FakeMechanism();
  58. var clock = new goog.testing.MockClock(true);
  59. var storage = new goog.storage.EncryptedStorage(mechanism, 'secret');
  60. goog.storage.collectableStorageTester.runBasicTests(
  61. mechanism, clock, storage);
  62. }
  63. function testEncryption() {
  64. var mechanism = new goog.testing.storage.FakeMechanism();
  65. var clock = new goog.testing.MockClock(true);
  66. var storage = new goog.storage.EncryptedStorage(mechanism, 'secret');
  67. var mallory = new goog.storage.EncryptedStorage(mechanism, 'guess');
  68. // Simple Objects.
  69. storage.set('first', 'Hello world!');
  70. storage.set('second', ['one', 'two', 'three'], 1000);
  71. storage.set('third', {'a': 97, 'b': 98});
  72. // Wrong secret can't find keys.
  73. assertNull(mechanism.get('first'));
  74. assertNull(mechanism.get('second'));
  75. assertNull(mechanism.get('third'));
  76. assertUndefined(mallory.get('first'));
  77. assertUndefined(mallory.get('second'));
  78. assertUndefined(mallory.get('third'));
  79. // Wrong secret can't overwrite keys.
  80. mallory.set('first', 'Ho ho ho!');
  81. assertObjectEquals('Ho ho ho!', mallory.get('first'));
  82. assertObjectEquals('Hello world!', storage.get('first'));
  83. mallory.remove('first');
  84. // Correct key decrypts properly.
  85. assertObjectEquals('Hello world!', storage.get('first'));
  86. assertObjectEquals(['one', 'two', 'three'], storage.get('second'));
  87. assertObjectEquals({'a': 97, 'b': 98}, storage.get('third'));
  88. // Wrong secret can't decode values even if the key is revealed.
  89. var encryptedWrapper = getEncryptedWrapper(storage, 'first');
  90. assertObjectEquals(
  91. 'Hello world!', decryptWrapper(storage, 'first', encryptedWrapper));
  92. assertThrows(function() {
  93. decryptWrapper(mallory, 'first', encryptedWrapper);
  94. });
  95. // If the value is overwritten, it can't be decrypted.
  96. encryptedWrapper[goog.storage.RichStorage.DATA_KEY] = 'kaboom';
  97. mechanism.set(
  98. storage.hashKeyWithSecret_('first'),
  99. goog.json.serialize(encryptedWrapper));
  100. assertEquals(
  101. goog.storage.ErrorCode.DECRYPTION_ERROR, assertThrows(function() {
  102. storage.get('first');
  103. }));
  104. // Test garbage collection.
  105. storage.collect();
  106. assertNotNull(getEncryptedWrapper(storage, 'first'));
  107. assertObjectEquals(['one', 'two', 'three'], storage.get('second'));
  108. assertObjectEquals({'a': 97, 'b': 98}, storage.get('third'));
  109. clock.tick(2000);
  110. storage.collect();
  111. assertNotNull(getEncryptedWrapper(storage, 'first'));
  112. assertUndefined(storage.get('second'));
  113. assertObjectEquals({'a': 97, 'b': 98}, storage.get('third'));
  114. mechanism.set(storage.hashKeyWithSecret_('first'), '"kaboom"');
  115. storage.collect();
  116. assertNotNull(getEncryptedWrapper(storage, 'first'));
  117. assertObjectEquals({'a': 97, 'b': 98}, storage.get('third'));
  118. storage.collect(true);
  119. assertUndefined(storage.get('first'));
  120. assertObjectEquals({'a': 97, 'b': 98}, storage.get('third'));
  121. // Clean up.
  122. storage.remove('third');
  123. assertUndefined(storage.get('third'));
  124. clock.uninstall();
  125. }
  126. function testSalting() {
  127. var mechanism = new goog.testing.storage.FakeMechanism();
  128. var randomMock = new goog.testing.PseudoRandom(0, true);
  129. var storage = new goog.storage.EncryptedStorage(mechanism, 'secret');
  130. // Same value under two different keys should appear very different,
  131. // even with the same salt.
  132. storage.set('one', 'Hello world!');
  133. randomMock.seed(0); // Reset the generator so we get the same salt.
  134. storage.set('two', 'Hello world!');
  135. var golden = getEncryptedData(storage, 'one');
  136. assertRoughlyEquals(
  137. 'Ciphertext did not change with keys', golden.length,
  138. hammingDistance(golden, getEncryptedData(storage, 'two')), 2);
  139. // Same key-value pair written second time should appear very different.
  140. storage.set('one', 'Hello world!');
  141. assertRoughlyEquals(
  142. 'Salting seems to have failed', golden.length,
  143. hammingDistance(golden, getEncryptedData(storage, 'one')), 2);
  144. // Clean up.
  145. storage.remove('1');
  146. storage.remove('2');
  147. randomMock.uninstall();
  148. }