index.html 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="utf-8">
  5. <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  6. <meta name="description" content="steganography, cryptography, png, webp, hack, encrypt, decrypt">
  7. <meta name="author" content="bzt">
  8. <!-- o
  9. o hi, hacker!
  10. ooo -->
  11. <title>Steganography</title>
  12. <link href="logo.png" rel="shortcut icon">
  13. <style>
  14. input, textarea { font-family: monospace,fixed; }
  15. #url, #input { width:48%; }
  16. input[type="password"] { width:100%; }
  17. textarea { width:100%; height:100%; min-height:256px; }
  18. button { width:33%; }
  19. table { width:100%; }
  20. small { opacity:50%; }
  21. a[download] { display:none; }
  22. #jserr { color:white; background:red; font-weight:bold; font-size: 150%; text-align:center; padding: 32px; }
  23. </style>
  24. </head>
  25. <body>
  26. <h1>Steganography</h1>
  27. <div id="jserr">Please enable JavaScript</div>
  28. <script>document.getElementById("jserr").style.display = "none";</script>
  29. <p>No ads. No logs. No telemetry. No server components. Maximum privacy. It's <a href="https://gitlab.com/bztsrc/stegano">Open Source</a>, so you can even run it on your own site or locally if you don't trust gitlab (the wasm file works with a "file:///" URL in Firefox, but precompiled portable executables for Windows and Linux are also available in the <a href="https://gitlab.com/bztsrc/stegano/-/tree/main/bin">repo</a>).</p>
  30. <table>
  31. <tr><td>Image<sup>1</sup></td><td width="100%"><input id="url" onchange="geturl()" placeholder="https://...">&nbsp;&nbsp;&nbsp;<input type="file" id="input" onchange="getfile()"></td></tr>
  32. <tr><td>Password<sup>2</sup></td><td><input type="password" id="pass" maxlength="255"></td></tr>
  33. <tr><td></td><td align="center"><button onclick="load()">Load</button>&nbsp;&nbsp;&nbsp;<button onclick="save()">Save<sup>3</sup></button></td></tr>
  34. <tr><td>Message</td><td><textarea id="message"></textarea><br>Capacity <span id="capacity">0</span> bytes.</td></tr>
  35. </table>
  36. <small>
  37. <sup>1</sup> - Input image can be either an URL or a local file in WEBP, PNG, GIF or JPEG format<br>
  38. <sup>2</sup> - password is optional. If given, message is encrypted with military grade AES<br>
  39. <sup>3</sup> - the resulting image is saved in WEBP format<br>
  40. </small>
  41. <br><img src="data:type/png;base64,iVBORw0KGgoAAAANSUhEUgAAAH8AAAAzAgMAAADDST+XAAAADFBMVEUAAAC8AAC9AAC+AADyLdeuAAAAAXRSTlMAQObYZgAAAn5JREFUOMt9lbtuU0EQhidrcQQrZCGq4OooitDRiooOV34EiqShSovFQwymsUzjIAWJisUyaL2JIosiRZQiT0PhhwDmsru248tK5+Ld7+zM/DM7Btg33C/Yvx5vkB4m7hg/vwKc7QO+9QCOCbBrs5Plq6fFww3gPL/cRnaj2QB+5xfntwODYooDgJquzpwjYt56Gz8lwAaJlKcdo5YpQ0DawYuLAPIxXJy+rfDR6YlBlwGrFgwKQB9YWgsMoKwHF0kfgIqpuAZAiiXQC1lmILQScLUELM0w8EoA0hq70Ke5ewL6aoF31CAUcLAggLd1BKDoEcBiBr4vxJEMVCQk5+KFRBIknbQ2z0Bw4kgPFgL4KgHeJcD3BUgysHz0ETvStBRQsa6S0qsA3Sh3l7aUA1/tub3893cVCN1kQQGSaEqhSPIV8Ko2z4wVCNAqihCAxQKgAuSB51h9O3KOJOwZVUdKpgLsmOhvQpuBqSEjClACpmuABDFnI5JMKQcF5pJB71LVoZTssl5ophPjvcG4FeDY+dYzkNZzthcrO9ANLZSDZUq2ve0V4HE+qwBPoNRLRwwzJXqnXDUKZEdEfYu1yS6qCyZmRxQYe5NdVBeMnuQyxmhyKt4AbGkfHg9ysqcKiIYzE/1ITzWC1mOyAIZbyiJM4g+to2uUqhTHZVQfz+rGTl8iwrBunnV47z9YwuO4nmM9irYZNE/v6omNuQfqoaHnaDJ8bb+cN5+PAY8GrgAug6MBeju2ZnRohkdYgANfuultnKkWN/F6FjPgkgXoPmyfPQ0uLPvxg4HrG2wAs7FscFU6+kaHPuHZPu4GLjB1zV2jiu/fhb1/I+34YeXXfzyvR6xFYz9TAAAAAElFTkSuQmCC" alt="GPLv3+">
  42. <script src="stegano.js"></script>
  43. <script>
  44. var imgdata = new Uint8Array();
  45. try {
  46. var url = document.location.href.split('?')[1];
  47. if(url && url != "" && url != undefined) {
  48. document.getElementById("url").value = url;
  49. geturl();
  50. }
  51. }catch(e){}
  52. function calccap() {
  53. var getcapacity = Module.cwrap("getcapacity", "number", [ "number", "number" ]);
  54. if(imgdata.length < 1) { alert("No input image."); return; }
  55. const buf = Module._malloc(imgdata.length);
  56. Module.HEAPU8.set(imgdata, buf);
  57. var cap = getcapacity(buf, imgdata.length);
  58. Module._free(buf);
  59. document.getElementById("capacity").innerText = cap.toLocaleString('en-US');
  60. document.getElementById("message").maxLength = cap;
  61. document.getElementById("message").value = "";
  62. }
  63. function load() {
  64. var decode = Module.cwrap("decode", "number", [ "number", "number", "number", "number", "number", "number" ]);
  65. var cap = document.getElementById("message").maxLength;
  66. var pin = new TextEncoder().encode(document.getElementById("pass").value);
  67. document.getElementById("message").value = "";
  68. if(imgdata.length < 1 || cap < 16) { alert("No input image."); return; }
  69. const buf = Module._malloc(imgdata.length);
  70. const pas = Module._malloc(pin.length+1);
  71. const msg = Module._malloc(cap);
  72. Module.HEAPU8.set(imgdata, buf);
  73. Module.HEAPU8.set(pin, pas);
  74. var len = decode(buf, imgdata.length, pas, pin.length, msg, cap);
  75. if(len > 0) {
  76. const view = new Uint8Array(Module.HEAPU8.buffer,msg,len);
  77. document.getElementById("message").value = new TextDecoder().decode(view);
  78. } else
  79. alert("Probably no message in image or bad password.");
  80. Module._free(buf);
  81. Module._free(pas);
  82. Module._free(msg);
  83. document.getElementById("pass").value = "";
  84. }
  85. function save() {
  86. var encode = Module.cwrap("encode", "number", [ "number", "number", "number", "number", "number", "number" ]);
  87. var txt = new TextEncoder().encode(document.getElementById("message").value);
  88. var pin = new TextEncoder().encode(document.getElementById("pass").value);
  89. if(imgdata.length < 1) { alert("No input image."); return; }
  90. if(txt.length < 1) { alert("No message."); return; }
  91. const buf = Module._malloc(128*1024*1024); /* must match MAXIMGSIZE in stegano.h */
  92. const pas = Module._malloc(pin.length+1);
  93. const msg = Module._malloc(txt.length+32);
  94. Module.HEAPU8.set(imgdata, buf);
  95. Module.HEAPU8.set(pin, pas);
  96. Module.HEAPU8.set(txt, msg);
  97. var len = encode(buf, imgdata.length, pas, pin.length, msg, txt.length);
  98. if(len > 0) {
  99. const view = new Uint8Array(Module.HEAPU8.buffer,buf,len);
  100. var blob = new Blob([view], { type: "image/webp" });
  101. var url = window.URL.createObjectURL(blob);
  102. var a = document.createElement("A");
  103. a.href = url;
  104. a.download = "stegano.webp";
  105. document.body.appendChild(a);
  106. a.click();
  107. a.remove();
  108. window.URL.revokeObjectURL(url);
  109. } else
  110. alert("Unable to generate output image.");
  111. Module._free(buf);
  112. Module._free(pas);
  113. Module._free(msg);
  114. document.getElementById("pass").value = "";
  115. }
  116. function geturl() {
  117. fetch(document.getElementById("url").value).then((response) => {
  118. if(response.ok) response.arrayBuffer().then((buf) => { imgdata = new Uint8Array(buf); calccap(); });
  119. })
  120. .catch((error) => alert("Unable to get url\n" + error));
  121. }
  122. function getfile() {
  123. var input=document.getElementById("input");
  124. if(input.files.length<1) {
  125. alert("No file given.");
  126. } else {
  127. var reader = new FileReader();
  128. reader.onloadend = function() { imgdata = new Uint8Array(reader.result); calccap(); };
  129. reader.readAsArrayBuffer(input.files[0]);
  130. }
  131. }
  132. </script>
  133. </body>
  134. </html>