CryptTool.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. 'use strict';
  2. const common = require('../common');
  3. describe('CryptTool', function () {
  4. describe('cipher & decipher', function () {
  5. afterEach(async function () {
  6. // pause to let async functions conclude
  7. await new Promise(resolve => setTimeout(resolve, 1900));
  8. });
  9. this.timeout(30000);
  10. it('can en- and decrypt any message', function () {
  11. jsc.assert(jsc.forall(
  12. 'string',
  13. 'string',
  14. 'string',
  15. async function (key, password, message) {
  16. const clean = jsdom();
  17. // ensure zlib is getting loaded
  18. $.PrivateBin.Controller.initZ();
  19. Object.defineProperty(window, 'crypto', {
  20. value: new WebCrypto(),
  21. writeable: false,
  22. });
  23. global.atob = common.atob;
  24. global.btoa = common.btoa;
  25. message = message.trim();
  26. const cipherMessage = await $.PrivateBin.CryptTool.cipher(
  27. key, password, message, []
  28. ),
  29. plaintext = await $.PrivateBin.CryptTool.decipher(
  30. key, password, cipherMessage
  31. );
  32. clean();
  33. const result = (message === plaintext);
  34. if (!result) console.log(plaintext, cipherMessage);
  35. return result;
  36. }
  37. ),
  38. {tests: 3});
  39. });
  40. // The below static unit tests are included to ensure deciphering of "classic"
  41. // SJCL based pastes still works
  42. it(
  43. 'supports PrivateBin v1 ciphertext 1 (SJCL & browser atob)',
  44. async function () {
  45. delete global.Base64;
  46. const clean = jsdom();
  47. Object.defineProperty(window, 'crypto', {
  48. value: new WebCrypto(),
  49. writeable: false,
  50. });
  51. global.atob = common.atob;
  52. // Of course you can easily decipher the following texts, if you like.
  53. // Bonus points for finding their sources and hidden meanings.
  54. const paste = await $.PrivateBin.CryptTool.decipher(
  55. '6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
  56. // -- "That's amazing. I've got the same combination on my luggage."
  57. Array.apply(0, Array(6)).map((_,b) => b + 1).join(''),
  58. '{"iv":"4HNFIl7eYbCh6HuShctTIA==","v":1,"iter":10000,"ks"' +
  59. ':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
  60. 'lt":"u0lQvePq6L0=","ct":"fGPUVrDyaVr1ZDGb+kqQ3CPEW8x4YKG' +
  61. 'fzHDmA0Vjkh250aWNe7Cnigkps9aaFVMX9AaerrTp3yZbojJtNqVGMfL' +
  62. 'dUTu+53xmZHqRKxCCqSfDNSNoW4Oxk5OVgAtRyuG4bXHDsWTXDNz2xce' +
  63. 'qzVFqhkwTwlUchrV7uuFK/XUKTNjPFM744moivIcBbfM2FOeKlIFs8RY' +
  64. 'PYuvqQhp2rMLlNGwwKh//4kykQsHMQDeSDuJl8stMQzgWR/btUBZuwNZ' +
  65. 'EydkMH6IPpTdf5WTSrZ+wC2OK0GutCm4UaEe6txzaTMfu+WRVu4PN6q+' +
  66. 'N+2zljWJ1XdpVcN/i0Sv4QVMym0Xa6y0eccEhj/69o47PmExmMMeEwEx' +
  67. 'ImPalMNT9JUSiZdOZJ/GdzwrwoIuq1mdQR6vSH+XJ/8jXJQ7bjjJVJYX' +
  68. 'TcT0Di5jixArI2Kpp1GGlGVFbLgPugwU1wczg+byqeDOAECXRRnQcoge' +
  69. 'aJtVcRwXwfy4j3ORFcblYMilxyHqKBewcYPRVBGtBs50cVjSIkAfR84r' +
  70. 'nc1nfvnxK/Gmm+4VBNHI6ODWNpRolVMCzXjbKYnV3Are5AgSpsTqaGl4' +
  71. '1VJGpcco6cAwi4K0Bys1seKR+bLSdUgqRrkEqSRSdu3/VTu9HhEk8an0' +
  72. 'rjTE4CBB5/LMn16p0TGLoOb32odKFIEtpanVvLjeyiVMvSxcgYLNnTi/' +
  73. '5FiaAC4pJxRD+AZHedU1FICUeEXxIcac/4E5qjkHjX9SpQtLl80QLIVn' +
  74. 'jNliZm7QLB/nKu7W8Jb0+/CiTdV3Q9LhxlH4ciprnX+W0B00BKYFHnL9' +
  75. 'jRVzKdXhf1EHydbXMAfpCjHAXIVCkFakJinQBDIIw/SC6Yig0u0ddEID' +
  76. '2B7LYAP1iE4RZwzTrxCB+ke2jQr8c20Jj6u6ShFOPC9DCw9XupZ4HAal' +
  77. 'VG00kSgjus+b8zrVji3/LKEhb4EBzp1ctBJCFTeXwej8ZETLoXTylev5' +
  78. 'dlwZSYAbuBPPcbFR/xAIPx3uDabd1E1gTqUc68ICIGhd197Mb2eRWiSv' +
  79. 'Hr5SPsASerMxId6XA6+iQlRiI+NDR+TGVNmCnfxSlyPFMOHGTmslXOGI' +
  80. 'qGfBR8l4ft8YVZ70lCwmwTuViGc75ULSf9mM57/LmRzQFMYQtvI8IFK9' +
  81. 'JaQEMY5xz0HLtR4iyQUUdwR9e0ytBNdWF2a2WPDEnJuY/QJo4GzTlgv4' +
  82. 'QUxMXI5htsn2rf0HxCFu7Po8DNYLxTS+67hYjDIYWYaEIc8LXWMLyDm9' +
  83. 'C5fARPJ4F2BIWgzgzkNj+dVjusft2XnziamWdbS5u3kuRlVuz5LQj+R5' +
  84. 'imnqQAincdZTkTT1nYx+DatlOLllCYIHffpI="}'
  85. );
  86. clean();
  87. const result = typeof paste === 'string' && paste.includes('securely packed in iron');
  88. if (!result) console.log(paste);
  89. assert.ok(result);
  90. }
  91. );
  92. it(
  93. 'supports PrivateBin v1 ciphertext 2 (SJCL & browser atob)',
  94. async function () {
  95. delete global.Base64;
  96. const clean = jsdom();
  97. // ensure zlib is getting loaded
  98. $.PrivateBin.Controller.initZ();
  99. Object.defineProperty(window, 'crypto', {
  100. value: new WebCrypto(),
  101. writeable: false,
  102. });
  103. global.atob = common.atob;
  104. // Of course you can easily decipher the following texts, if you like.
  105. // Bonus points for finding their sources and hidden meanings.
  106. const paste = await $.PrivateBin.CryptTool.decipher(
  107. 's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
  108. '', // no password
  109. '{"iv":"WA42mdxIVXUwBqZu7JYNiw==","v":1,"iter":10000,"ks"' +
  110. ':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
  111. 'lt":"jN6CjbQMJCM=","ct":"kYYMo5DFG1+w0UHiYXT5pdV0IUuXxzO' +
  112. 'lslkW/c3DRCbGFROCVkAskHce7HoRczee1N9c5MhHjVMJUIZE02qIS8U' +
  113. 'yHdJ/GqcPVidTUcj9rnDNWsTXkjVv8jCwHS/cwmAjDTWpwp5ThECN+ov' +
  114. '/wNp/NdtTj8Qj7f/T3rfZIOCWfwLH9s4Des35UNcUidfPTNQ1l0Gm0X+' +
  115. 'r98CCUSYZjQxkZc6hRZBLPQ8EaNVooUwd5eP4GiYlmSDNA0wOSA+5isP' +
  116. 'YxomVCt+kFf58VBlNhpfNi7BLYAUTPpXT4SfH5drR9+C7NTeZ+tTCYjb' +
  117. 'U94PzYItOpu8vgnB1/a6BAM5h3m9w+giUb0df4hgTWeZnZxLjo5BN8WV' +
  118. '+kdTXMj3/Vv0gw0DQrDcCuX/cBAjpy3lQGwlAN1vXoOIyZJUjMpQRrOL' +
  119. 'dKvLB+zcmVNtGDbgnfP2IYBzk9NtodpUa27ne0T0ZpwOPlVwevsIVZO2' +
  120. '24WLa+iQmmHOWDFFpVDlS0t0fLfOk7Hcb2xFsTxiCIiyKMho/IME1Du3' +
  121. 'X4e6BVa3hobSSZv0rRtNgY1KcyYPrUPW2fxZ+oik3y9SgGvb7XpjVIta' +
  122. '8DWlDWRfZ9kzoweWEYqz9IA8Xd373RefpyuWI25zlHoX3nwljzsZU6dC' +
  123. '//h/Dt2DNr+IAvKO3+u23cWoB9kgcZJ2FJuqjLvVfCF+OWcig7zs2pTY' +
  124. 'JW6Rg6lqbBCxiUUlae6xJrjfv0pzD2VYCLY7v1bVTagppwKzNI3WaluC' +
  125. 'OrdDYUCxUSe56yd1oAoLPRVbYvomRboUO6cjQhEknERyvt45og2kORJO' +
  126. 'EJayHW+jZgR0Y0jM3Nk17ubpij2gHxNx9kiLDOiCGSV5mn9mV7qd3HHc' +
  127. 'OMSykiBgbyzjobi96LT2dIGLeDXTIdPOog8wyobO4jWq0GGs0vBB8oSY' +
  128. 'XhHvixZLcSjX2KQuHmEoWzmJcr3DavdoXZmAurGWLKjzEdJc5dSD/eNr' +
  129. '99gjHX7wphJ6umKMM+fn6PcbYJkhDh2GlJL5COXjXfm/5aj/vuyaRRWZ' +
  130. 'MZtmnYpGAtAPg7AUG"}'
  131. );
  132. clean();
  133. const result = typeof paste === 'string' && paste.includes('Sol is right');
  134. if (!result) console.log(paste);
  135. assert.ok(result);
  136. }
  137. );
  138. it(
  139. 'supports ZeroBin ciphertext 1 (SJCL & Base64 1.7)',
  140. async function () {
  141. global.Base64 = require('../base64-1.7').Base64;
  142. const clean = jsdom();
  143. Object.defineProperty(window, 'crypto', {
  144. value: new WebCrypto(),
  145. writeable: false,
  146. });
  147. global.atob = common.atob;
  148. // Of course you can easily decipher the following texts, if you like.
  149. // Bonus points for finding their sources and hidden meanings.
  150. const paste = await $.PrivateBin.CryptTool.decipher(
  151. '6t2qsmLyfXIokNCL+3/yl15rfTUBQvm5SOnFPvNE7Q8=',
  152. // -- "That's amazing. I've got the same combination on my luggage."
  153. Array.apply(0, Array(6)).map((_,b) => b + 1).join(''),
  154. '{"iv":"aTnR2qBL1CAmLX8FdWe3VA==","v":1,"iter":10000,"ks"' +
  155. ':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
  156. 'lt":"u0lQvePq6L0=","ct":"A3nBTvICZtYy6xqbIJE0c8Veored5lM' +
  157. 'JUGgGUm4581wjrPFlU0Q0tUZSf+RUUoZj2jqDa4kiyyZ5YNMe30hNMV0' +
  158. 'oVSalNhRgD9svVMnPuF162IbyhVCwr7ULjT981CHxVlGNqGqmIU6L/Xi' +
  159. 'xgdArxAA8x1GCrfAkBWWGeq8Qw5vJPG/RCHpwR4Wy3azrluqeyERBzma' +
  160. 'OQjO/kM35TiI6IrLYFyYyL7upYlxAaxS0XBMZvN8QU8Lnerwvh5JVC6O' +
  161. 'kkKrhogajTJIKozCF79yI78c50LUh7tTuI3Yoh7+fXxhoODvQdYFmoiU' +
  162. 'lrutN7Y5ZMRdITvVu8fTYtX9c7Fiufmcq5icEimiHp2g1bvfpOaGOsFT' +
  163. '+XNFgC9215jcp5mpBdN852xs7bUtw+nDrf+LsDEX6iRpRZ+PYgLDN5xQ' +
  164. 'T1ByEtYbeP+tO38pnx72oZdIB3cj8UkOxnxdNiZM5YB5egn4jUj1fHot' +
  165. '1I69WoTiUJipZ5PIATv7ScymRB+AYzjxjurQ9lVfX9QtAbEH2dhdmoUo' +
  166. '3IDRSXpWNCe9RC1aUIyWfZO7oI7FEohNscHNTLEcT+wFnFUPByLlXmjN' +
  167. 'Z7FKeNpvUm3jTY4t4sbZH8o2dUl624PAw1INcJ6FKqWGWwoFT2j1MYC+' +
  168. 'YV/LkLTdjuWfayvwLMh27G/FfKCRbW36vqinegqpPDylsx9+3oFkEw3y' +
  169. '5Z8+44oN91rE/4Md7JhPJeRVlFC9TNCj4dA+EVhbbQqscvSnIH2uHkMw' +
  170. '7mNNo7xba/YT9KoPDaniqnYqb+q2pX1WNWE7dLS2wfroMAS3kh8P22DA' +
  171. 'V37AeiNoD2PcI6ZcHbRdPa+XRrRcJhSPPW7UQ0z4OvBfjdu/w390QxAx' +
  172. 'SxvZewoh49fKKB6hTsRnZb4tpHkjlww=="}'
  173. );
  174. clean();
  175. delete global.Base64;
  176. const result = typeof paste === 'string' && paste.includes('securely packed in iron');
  177. if (!result) console.log(paste);
  178. assert.ok(result);
  179. }
  180. );
  181. it(
  182. 'supports ZeroBin ciphertext 2 (SJCL & Base64 1.7)',
  183. async function () {
  184. global.Base64 = require('../base64-1.7').Base64;
  185. const clean = jsdom();
  186. Object.defineProperty(window, 'crypto', {
  187. value: new WebCrypto(),
  188. writeable: false,
  189. });
  190. global.atob = common.atob;
  191. const paste = await $.PrivateBin.CryptTool.decipher(
  192. 's9pmKZKOBN7EVvHpTA8jjLFH3Xlz/0l8lB4+ONPACrM=',
  193. '', // no password
  194. '{"iv":"Z7lAZQbkrqGMvruxoSm6Pw==","v":1,"iter":10000,"ks"' +
  195. ':256,"ts":128,"mode":"gcm","adata":"","cipher":"aes","sa' +
  196. 'lt":"jN6CjbQMJCM=","ct":"PuOPWB3i2FPcreSrLYeQf84LdE8RHjs' +
  197. 'c+MGtiOr4b7doNyWKYtkNorbRadxaPnEee2/Utrp1MIIfY5juJSy8RGw' +
  198. 'EPX5ciWcYe6EzsXWznsnvhmpKNj9B7eIIrfSbxfy8E2e/g7xav1nive+' +
  199. 'ljToka3WT1DZ8ILQd/NbnJeHWaoSEOfvz8+d8QJPb1tNZvs7zEY95Dum' +
  200. 'QwbyOsIMKAvcZHJ9OJNpujXzdMyt6DpcFcqlldWBZ/8q5rAUTw0HNx/r' +
  201. 'CgbhAxRYfNoTLIcMM4L0cXbPSgCjwf5FuO3EdE13mgEDhcClW79m0Qvc' +
  202. 'nIh8xgzYoxLbp0+AwvC/MbZM8savN/0ieWr2EKkZ04ggiOIEyvfCUuNp' +
  203. 'rQBYO+y8kKduNEN6by0Yf4LRCPfmwN+GezDLuzTnZIMhPbGqUAdgV6Ex' +
  204. 'qK2ULEEIrQEMoOuQIxfoMhqLlzG79vXGt2O+BY+4IiYfvmuRLks4UXfy' +
  205. 'HqxPXTJg48IYbGs0j4TtJPUgp3523EyYLwEGyVTAuWhYAmVIwd/hoV7d' +
  206. '7tmfcF73w9dufDFI3LNca2KxzBnWNPYvIZKBwWbq8ncxkb191dP6mjEi' +
  207. '7NnhqVk5A6vIBbu4AC5PZf76l6yep4xsoy/QtdDxCMocCXeAML9MQ9uP' +
  208. 'QbuspOKrBvMfN5igA1kBqasnxI472KBNXsdZnaDddSVUuvhTcETM="}'
  209. );
  210. clean();
  211. delete global.Base64;
  212. const result = typeof paste === 'string' && paste.includes('Sol is right');
  213. if (!result) console.log(paste);
  214. assert.ok(result);
  215. }
  216. );
  217. it('does not truncate messages', async function () {
  218. const message = fs.readFileSync('test/compression-sample.txt', 'ascii').trim(),
  219. clean = jsdom();
  220. Object.defineProperty(window, 'crypto', {
  221. value: new WebCrypto(),
  222. writeable: false,
  223. });
  224. // ensure zlib is getting loaded
  225. $.PrivateBin.Controller.initZ();
  226. global.atob = common.atob;
  227. global.btoa = common.btoa;
  228. const cipherMessage = await $.PrivateBin.CryptTool.cipher(
  229. 'foo', 'bar', message, []
  230. ),
  231. plaintext = await $.PrivateBin.CryptTool.decipher(
  232. 'foo', 'bar', cipherMessage
  233. );
  234. clean();
  235. const result = (message === plaintext);
  236. if (!result) console.log(plaintext, cipherMessage);
  237. assert.ok(result);
  238. });
  239. it('can en- and decrypt a particular message (#260)', function () {
  240. jsc.assert(jsc.forall(
  241. 'string',
  242. 'string',
  243. async function (key, password) {
  244. const message = `
  245. 1 subgoal
  246. inv : Assert
  247. expr : Expr
  248. sBody : Instr
  249. deduction : (|- [|inv /\ assertOfExpr expr|] sBody [|inv|])%assert
  250. IHdeduction : (|= [|inv /\ assertOfExpr expr |] sBody [|inv|])%assert
  251. mem : Mem
  252. preInMem : inv mem
  253. m : Mem
  254. n : nat
  255. interpRel : interp (nth_iterate sBody n) (MemElem mem) = CpoElem Mem m
  256. lastIter : interp (nth_iterate sBody n) (MemElem mem) |=e expr_neg expr
  257. notLastIter : forall p : nat,
  258. p < n -> interp (nth_iterate sBody p) (MemElem mem) |=e expr
  259. isWhile : interp (while expr sBody) (MemElem mem) =
  260. interp (nth_iterate sBody n) (MemElem mem)
  261. ======================== ( 1 / 1 )
  262. conseq_or_bottom inv (interp (nth_iterate sBody n) (MemElem mem))
  263. `;
  264. const clean = jsdom();
  265. // ensure zlib is getting loaded
  266. $.PrivateBin.Controller.initZ();
  267. Object.defineProperty(window, 'crypto', {
  268. value: new WebCrypto(),
  269. writeable: false,
  270. });
  271. const cipherMessage = await $.PrivateBin.CryptTool.cipher(
  272. key, password, message, []
  273. ),
  274. plaintext = await $.PrivateBin.CryptTool.decipher(
  275. key, password, cipherMessage
  276. );
  277. clean();
  278. const result = (message === plaintext);
  279. if (!result) console.log(plaintext, cipherMessage);
  280. return result;
  281. }
  282. ),
  283. {tests: 3});
  284. });
  285. });
  286. describe('getSymmetricKey', function () {
  287. this.timeout(10000);
  288. let keys = [];
  289. // the parameter is used to ensure the test is run more then one time
  290. it('returns random, non-empty keys', function () {
  291. jsc.assert(jsc.forall(
  292. 'integer',
  293. function(counter) {
  294. const clean = jsdom();
  295. Object.defineProperty(window, 'crypto', {
  296. value: new WebCrypto(),
  297. writeable: false,
  298. });
  299. const key = $.PrivateBin.CryptTool.getSymmetricKey(),
  300. result = (key !== '' && keys.indexOf(key) === -1);
  301. keys.push(key);
  302. clean();
  303. return result;
  304. }
  305. ),
  306. {tests: 10});
  307. });
  308. });
  309. });