CryptTool.js 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. 'use strict';
  2. const common = require('../common');
  3. const fc = require('fast-check');
  4. const fs = require('fs');
  5. describe('CryptTool', function () {
  6. describe('cipher & decipher', function () {
  7. afterEach(async function () {
  8. // pause to let async functions conclude
  9. await new Promise(resolve => setTimeout(resolve, 1900));
  10. });
  11. this.timeout(30000);
  12. it('can en- and decrypt any message', async function () {
  13. await fc.assert(fc.asyncProperty(
  14. fc.string(),
  15. fc.string(),
  16. fc.string(),
  17. async function (key, password, message) {
  18. const clean = globalThis.cleanup();
  19. // ensure zlib is getting loaded
  20. PrivateBin.Controller.initZlib();
  21. Object.defineProperty(window, 'crypto', {
  22. value: new WebCrypto(),
  23. configurable: true,
  24. enumerable: true,
  25. writable: false
  26. });
  27. global.atob = common.atob;
  28. global.btoa = common.btoa;
  29. message = message.trim();
  30. const cipherMessage = await PrivateBin.CryptTool.cipher(
  31. key, password, message, []
  32. ),
  33. plaintext = await PrivateBin.CryptTool.decipher(
  34. key, password, cipherMessage
  35. );
  36. clean();
  37. const result = (message === plaintext);
  38. if (!result) console.log(plaintext, cipherMessage);
  39. return result;
  40. }
  41. ),
  42. {numRuns: 3});
  43. });
  44. it('does not truncate messages', async function () {
  45. const message = fs.readFileSync('test/compression-sample.txt', 'ascii').trim(),
  46. clean = globalThis.cleanup();
  47. Object.defineProperty(window, 'crypto', {
  48. value: new WebCrypto(),
  49. configurable: true,
  50. enumerable: true,
  51. writable: false
  52. });
  53. // ensure zlib is getting loaded
  54. PrivateBin.Controller.initZlib();
  55. global.atob = common.atob;
  56. global.btoa = common.btoa;
  57. const cipherMessage = await PrivateBin.CryptTool.cipher(
  58. 'foo', 'bar', message, []
  59. ),
  60. plaintext = await PrivateBin.CryptTool.decipher(
  61. 'foo', 'bar', cipherMessage
  62. );
  63. clean();
  64. if (message !== plaintext) {
  65. console.log(plaintext, cipherMessage);
  66. }
  67. assert.strictEqual(message, plaintext);
  68. });
  69. it('can en- and decrypt a particular message (#260)', async function () {
  70. await fc.assert(fc.asyncProperty(
  71. fc.string(),
  72. fc.string(),
  73. async function (key, password) {
  74. const message = `
  75. 1 subgoal
  76. inv : Assert
  77. expr : Expr
  78. sBody : Instr
  79. deduction : (|- [|inv /\ assertOfExpr expr|] sBody [|inv|])%assert
  80. IHdeduction : (|= [|inv /\ assertOfExpr expr |] sBody [|inv|])%assert
  81. mem : Mem
  82. preInMem : inv mem
  83. m : Mem
  84. n : nat
  85. interpRel : interp (nth_iterate sBody n) (MemElem mem) = CpoElem Mem m
  86. lastIter : interp (nth_iterate sBody n) (MemElem mem) |=e expr_neg expr
  87. notLastIter : forall p : nat,
  88. p < n -> interp (nth_iterate sBody p) (MemElem mem) |=e expr
  89. isWhile : interp (while expr sBody) (MemElem mem) =
  90. interp (nth_iterate sBody n) (MemElem mem)
  91. ======================== ( 1 / 1 )
  92. conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
  93. `;
  94. const clean = globalThis.cleanup();
  95. // ensure zlib is getting loaded
  96. PrivateBin.Controller.initZlib();
  97. Object.defineProperty(window, 'crypto', {
  98. value: new WebCrypto(),
  99. configurable: true,
  100. enumerable: true,
  101. writable: false
  102. });
  103. global.atob = common.atob;
  104. global.btoa = common.btoa;
  105. const cipherMessage = await PrivateBin.CryptTool.cipher(
  106. key, password, message, []
  107. ),
  108. plaintext = await PrivateBin.CryptTool.decipher(
  109. key, password, cipherMessage
  110. );
  111. clean();
  112. const result = (message === plaintext);
  113. if (!result) console.log(plaintext, cipherMessage);
  114. return result;
  115. }
  116. ),
  117. {numRuns: 3});
  118. });
  119. it('can en- and decrypt any unicode message and password', async function () {
  120. await fc.assert(fc.asyncProperty(
  121. fc.string({unit: 'binary', minLength: 1}),
  122. fc.string({unit: 'binary'}),
  123. async function (message, password) {
  124. const clean = globalThis.cleanup();
  125. // ensure zlib is getting loaded
  126. PrivateBin.Controller.initZlib();
  127. Object.defineProperty(window, 'crypto', {
  128. value: new WebCrypto(),
  129. configurable: true,
  130. enumerable: true,
  131. writable: false
  132. });
  133. global.atob = common.atob;
  134. global.btoa = common.btoa;
  135. const key = PrivateBin.CryptTool.getSymmetricKey(),
  136. cipherMessage = await PrivateBin.CryptTool.cipher(
  137. key, password, message, []
  138. ),
  139. plaintext = await PrivateBin.CryptTool.decipher(
  140. key, password, cipherMessage
  141. );
  142. clean();
  143. const result = (message === plaintext);
  144. if (!result) console.log(plaintext, cipherMessage);
  145. return result;
  146. }
  147. ),
  148. {numRuns: 3});
  149. });
  150. it('does not accept passwords that only share the low byte of each character', async function () {
  151. const passwordPairs = [
  152. ['\u013c', '<'], // U+013C vs <
  153. ['\u4e2d\u6587', '-\u0087'], // U+4E2D U+6587 vs 0x2D 0x87
  154. ['\u20ac', '\u00ac'] // U+20AC vs 0xAC
  155. ];
  156. for (const [password, lowBytePassword] of passwordPairs) {
  157. const clean = globalThis.cleanup();
  158. // ensure zlib is getting loaded
  159. PrivateBin.Controller.initZlib();
  160. Object.defineProperty(window, 'crypto', {
  161. value: new WebCrypto(),
  162. configurable: true,
  163. enumerable: true,
  164. writable: false
  165. });
  166. global.atob = common.atob;
  167. global.btoa = common.btoa;
  168. const key = PrivateBin.CryptTool.getSymmetricKey(),
  169. cipherMessage = await PrivateBin.CryptTool.cipher(
  170. key, password, 'secret', []
  171. ),
  172. plaintext = await PrivateBin.CryptTool.decipher(
  173. key, lowBytePassword, cipherMessage
  174. );
  175. clean();
  176. assert.strictEqual(
  177. plaintext, '',
  178. 'password ' + JSON.stringify(lowBytePassword) +
  179. ' must not decrypt a document encrypted with ' +
  180. JSON.stringify(password)
  181. );
  182. }
  183. });
  184. });
  185. describe('getSymmetricKey', function () {
  186. this.timeout(10000);
  187. let keys = [];
  188. // the parameter is used to ensure the test is run more then one time
  189. it('returns random, non-empty keys', () => {
  190. fc.assert(fc.property(
  191. fc.integer(),
  192. function() {
  193. const clean = globalThis.cleanup();
  194. Object.defineProperty(window, 'crypto', {
  195. value: new WebCrypto(),
  196. configurable: true,
  197. enumerable: true,
  198. writable: false
  199. });
  200. const key = PrivateBin.CryptTool.getSymmetricKey(),
  201. result = (key !== '' && keys.indexOf(key) === -1);
  202. keys.push(key);
  203. clean();
  204. return result;
  205. }
  206. ),
  207. {numRuns: 10});
  208. });
  209. });
  210. });