From 736d22cdfe06355139ab7fe42e7e926ba5f51f22 Mon Sep 17 00:00:00 2001 From: Dmitriy Titarenko Date: Tue, 27 Feb 2024 20:03:39 +0500 Subject: [PATCH] Add compression --- README.md | 8 +++++++- aes.css | 20 +++++++++++--------- aes.js | 45 ++++++++++++++++++++++++++++++++++++--------- favicon.png | Bin 0 -> 268 bytes hex.css | 8 ++++++++ hex.js | 7 ++++++- index.html | 44 ++++++++++++++++++++++++++++++++++---------- 7 files changed, 102 insertions(+), 30 deletions(-) create mode 100644 favicon.png diff --git a/README.md b/README.md index 7141b13..e1e8b02 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,11 @@ # AES Crypto Playground  [![License](https://img.shields.io/github/license/dscheg/aes.svg)](https://raw.githubusercontent.com/dscheg/aes/main/LICENSE) -Play around with AES encryption and decryption to learn about the different complexities of symmetric encryption implementations. This page uses only client-side cryptography and does not send data anywhere. But still, do not play with sensitive data. The playground provided "AS IS" without any warranties +Play around with AES encryption and decryption to learn about the different complexities of symmetric encryption implementations. This page uses only client-side cryptography and does not send data anywhere. But still, do not play with sensitive data. The playground provided "AS IS" without any warranties. + +Try attacks: +* Bit Flipping +* Padding Oracle +* Compression Oracle +* ... https://dscheg.github.io/aes diff --git a/aes.css b/aes.css index 5d98e3a..a65cd38 100644 --- a/aes.css +++ b/aes.css @@ -45,8 +45,8 @@ input { #dir { position: absolute; - bottom: 0; - right: 0; + bottom: 4px; + right: 4px; font-size: 50%; } @@ -87,10 +87,7 @@ input { background-image: url("modes/CFB_encryption.min.svg"); } -.xed.cipher button { - display: none; -} -.xed.decrypt button { +.xed.decrypt .xed-load { display: none; } @@ -100,16 +97,21 @@ input { .xor:has(textarea:focus) { opacity: 1; } -.grayed { +.grayed, .grayed textarea { color: #777; } #result { - white-space: pre; font: min(1.8vw, 1.8vh)/1em 'Julia'; + width: min(80vw, 78vh); + resize: none; + border: 1px dotted #bbb; +} +#result:focus { + outline: transparent !important; } .night #result { - filter: invert(1) brightness(0.5); + filter: hue-rotate(180deg); } #result.error { color: red; diff --git a/aes.js b/aes.js index eaf860b..749043b 100644 --- a/aes.js +++ b/aes.js @@ -9,8 +9,10 @@ const $modeimg = $("#mode-img"); const $padding = $("#padding"); const $keysize = $("#keysize"); const $blocksize = $("#blocksize"); +const $compress = $("#compress"); const $plain = $(".xed.plain"); +const $deflate = $(".xed.deflate"); const $cipher = $(".xed.cipher"); const $decrypt = $(".xed.decrypt"); const $xor = $(".xed.xor"); @@ -23,7 +25,7 @@ const update = () => $plain.dispatchEvent(new Event("change")); const setResult = (isOk, text) => { $result.classList[isOk ? 'add' : 'remove']('success'); $result.classList[isOk ? 'remove' : 'add']('error'); - $result.textContent = text; + $result.value = text; }; $dir.value = localStorage.getItem("dir") || $dir[0].value; @@ -71,6 +73,15 @@ $key.oninput = e => { }; $key.oninput(); +$compress.value = localStorage.getItem("compression") || ""; +$compress.oninput = e => { + localStorage.setItem("compression", $compress.value); + const hide = $compress.value.length === 0; + $deflate.previousElementSibling.hidden = hide; + $deflate.hidden = hide; + update(); +}; + //const importKey = () => crypto.subtle.importKey("raw", fromHex($key.value), {name: "AES-CBC"}, true, ["encrypt", "decrypt"]); const getCryptoParams = () => Object({ @@ -79,14 +90,26 @@ const getCryptoParams = () => Object({ padding: CryptoJS.pad[$padding.value] }); +const compress = (data, mode) => stream = mode != 0 && $compress.value != "" + ? new Response(new Blob([data]).stream().pipeThrough(mode > 0 ? new CompressionStream($compress.value) : new DecompressionStream($compress.value))) + .arrayBuffer().then(buf => new Uint8Array(buf)) + : new Promise((resolve, _) => resolve(data)); + $plain.addEventListener("change", e => { - const plain = $plain.getDataHex(); + const plain = $plain.getData(); + compress(plain, 1).then(data => { + $deflate.setData(data); + localStorage.setItem("hex", toHex(plain)); + }).catch(e => setResult(false, e)); +}); + +$deflate.addEventListener("change", e => { + const compressed = $deflate.getDataHex(); const enc = CryptoJS.AES.encrypt( - CryptoJS.enc.Hex.parse(plain), + CryptoJS.enc.Hex.parse(compressed), CryptoJS.enc.Hex.parse($key.value), getCryptoParams()); $cipher.setDataHex(enc.ciphertext.toString(CryptoJS.enc.Hex)); - localStorage.setItem("hex", plain); /*importKey().then(key => crypto.subtle.encrypt({name: "AES-" + $mode.value, iv: fromHex($iv.value)}, key, $plain.getData()).then(cipher => { $cipher.setData(new Uint8Array(cipher)); @@ -95,20 +118,24 @@ $plain.addEventListener("change", e => { $cipher.addEventListener("change", e => { try { + const cipher = $cipher.getDataHex(); const decrypt = CryptoJS.AES.decrypt( - CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Hex.parse($cipher.getDataHex())}), + CryptoJS.lib.CipherParams.create({ciphertext: CryptoJS.enc.Hex.parse(cipher)}), CryptoJS.enc.Hex.parse($key.value), getCryptoParams()); const hex = decrypt.toString(CryptoJS.enc.Hex); if($plain.getDataHex().length > 0 && hex.length === 0) { - $decrypt.setDataHex(''); + $decrypt.classList.add('grayed'); throw new Error("Decryption failed"); } - $decrypt.setDataHex(hex); - const text = new TextDecoder().decode(fromHex(hex)); - setResult(true, JSON.stringify(JSON.parse(text.replace(/[\x00-\x1f]/ig, '\uFFFD')), null, 4)); + compress(fromHex(hex), -1).then(data => { + $decrypt.setData(data); + $decrypt.classList.remove('grayed'); + const text = new TextDecoder().decode(data); + setResult(true, JSON.stringify(JSON.parse(text.replace(/[\x00-\x1f]/ig, '\uFFFD')), null, 4)); + }).catch(e => setResult(false, e)); } catch(e) { setResult(false, e); } diff --git a/favicon.png b/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..72d620db0db9e0473c9e51e0e580908fbab27cbf GIT binary patch literal 268 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbL!cYsfbE0E^p z^UKGN8>8~|Y^?tO|L@@7V3@aJTYA;!mv6p)`t<(8hmX%*+}V5h-Tg=RZr?t8_U!4? zr|%s;d3*c*_3PKKv6^)TXbNXZkY6x^xv8P-zdsQQhqx~62MVTpx;TbNTn_E^6gr^5 z!D4F2-4Xrm|9+Ds!Ns;mmv%FzesEsR)YM?nwIN~c*+&V=Oc6HfukxA`1s6Q(ZkQjy zBEH0|d&SI*mu5nDO}3by$nUdyShVC2v&j0078aqvdgija> { $a.click(); }; + const $size = $(".xed-size", $xed); const $left = $(".xed-left", $xed); const $right = $(".xed-right", $xed); + $right.value = ''; $right.oninput = function() { const pos = $right.value @@ -81,11 +83,14 @@ $$(".xed").forEach($xed => { .map(i => (1E7 + (16 * i).toString(16)).slice(-8)) .join('\n'); - $right.value = range(Math.floor(($hex.value.length + 1) / 3)) + const len = Math.floor(($hex.value.length + 1) / 3); + $right.value = range(len) .map(i => parseInt($hex.value.substr(i * 3, 2), 16)) .map(c => String.fromCodePoint(c + (0x20 <= c && c < 0x7F ? 0 : 0xF000))) .join('').replace(/(.{16})(?=.)/g, '$1\n'); + $size.textContent = 'size=' + len; + const newpos = pos > 0 && [' ', '\n'].includes($hex.value[pos - 1]) ? pos - 1 : pos; $hex.setSelectionRange(newpos, newpos); diff --git a/index.html b/index.html index a69ab27..7babe7f 100644 --- a/index.html +++ b/index.html @@ -9,6 +9,7 @@ + @@ -26,7 +27,7 @@

AES Crypto Playground

- @@ -42,6 +43,12 @@

AES Crypto Playground

Key Key Size Block Size + Compression

Plain

@@ -52,7 +59,7 @@

Plain

- + @@ -61,6 +68,23 @@

Plain

+ + + + + + + + + + + +

Cipher

@@ -69,12 +93,12 @@

Cipher

- + +
-
@@ -86,12 +110,12 @@

Decrypted

- + - - + + @@ -103,17 +127,17 @@

XOR Lines

- + - +

Result

-
n/a
+