-
Notifications
You must be signed in to change notification settings - Fork 0
/
demo.html
266 lines (221 loc) · 9.93 KB
/
demo.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
<!doctype html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="width=device-width, initial-scale=1">
<title>Generic Speck</title>
<link rel="stylesheet" href="https://unpkg.com/spectre.css/dist/spectre.min.css">
<body>
<div class="container grid-sm my-2"><div class="columns"><div class="column">
<h1>Generic Speck Demos</h1>
<p style="display:none" id="load-error">Your browser does not support ES modules. Please upgrade it.</p>
<script nomodule>document.getElementById('load-error').style.display = 'block'</script>
<h2>Basic demo</h2>
<p>Enter a number between 0 and 1073741823 (2<sup>30</sup> - 1) and it will return an obfuscated result in the same range. Keys are a list of numbers between 0 and 32767 (2<sup>15</sup> - 1), the default in this demo being randomly generated. It will be formatted using a base 32 alphabet (23​45​67​89​AB​CD​EF​GH​IJ​KM​NP​QR​ST​UV​WX​YZ).</p>
<form class="form-horizontal">
<div class="form-group">
<div class="col-3 col-sm-12">
<label class="form-label" for="input__key">Key</label>
</div>
<div class="col-9 col-sm-12">
<input class="form-input" type="text" id="input__key">
</div>
</div>
<div class="form-group">
<div class="col-3 col-sm-12">
<label class="form-label" for="input__input">Input</label>
</div>
<div class="col-9 col-sm-12">
<input class="form-input" type="number" id="input__input" value="0" min="0" max="1073741823">
</div>
</div>
<div class="form-group">
<div class="col-3 col-sm-12">
<label class="form-label" for="input__output">Output</label>
</div>
<div class="col-9 col-sm-12">
<input class="form-input" type="number" id="input__output">
</div>
</div>
<div class="form-group">
<div class="col-3 col-sm-12">
<label class="form-label" for="input__output">Formatted output</label>
</div>
<div class="col-9 col-sm-12">
<input class="form-input" type="text" id="input__formatted_output">
</div>
</div>
</form>
<p>Because the number of the bits in the output (30) is multiple of the number of bits of each character from the alphabet (5) it will generate a 6 length string (30/5) with an uniform distribution for every character.</p>
<h2>Visual demo</h2>
<p>In the left there is a 256x256 image where each pixel color is determined by the formula <code>r=x g=y b=0</code>, in the right there is a image where the colors are taken using the obfuscation function like this <code>[r, g] = encrypt(x, y) b=0</code>. Every color from the first image appears in the second image.</p>
<canvas id="originalImage" width="256" height="256"></canvas>
<canvas id="obfuscatedImage" width="256" height="256"></canvas>
<p>Keys are a list of numbers between 0 and 255 (2<sup>8</sup> - 1). Rounds can lowered down to one so you can see somewhat how the obfuscation works. Setting rotations to any number higher than the half block size (8) breaks the function.</p>
<form class="form-horizontal" id="canvas-demo">
<div class="form-group">
<div class="col-3 col-sm-12">
<label class="form-label" for="input__key2">Key</label>
</div>
<div class="col-9 col-sm-12">
<input class="form-input" type="text" id="input__key2" value="0, 0, 0, 0">
</div>
</div>
<div class="form-group">
<div class="col-3 col-sm-12">
<label class="form-label" for="input__input">Rounds</label>
</div>
<div class="col-9 col-sm-12">
<input class="form-input" type="number" id="input__rounds" value="22" min="1" step="1">
</div>
</div>
<div class="form-group">
<div class="col-3 col-sm-12">
<label class="form-label" for="input__leftrot">Left rotations</label>
</div>
<div class="col-9 col-sm-12">
<input class="form-input" type="number" id="input__leftrot" value="7" min="1" max="10" step="1">
</div>
</div>
<div class="form-group">
<div class="col-3 col-sm-12">
<label class="form-label" for="input__rightrot">Right rotations</label>
</div>
<div class="col-9 col-sm-12">
<input class="form-input" type="number" id="input__rightrot" value="2" min="1" max="10" step="1">
</div>
</div>
</form>
<h2>Obfuscated image demo</h2>
<p>Just for fun, on the left is a scrambled image and on the right will appear a unscrambled version if you can guess the key.</p>
<canvas id="funOriginalImage" width="256" height="256"></canvas>
<canvas id="funDeobfuscatedImage" width="256" height="256"></canvas>
<p>The key you enter will be processed using SHA-1 then used as a key for a <code>createSpeck</code> function and processed like the above demo, but using the left image as source instead of <code>r=x g=y b=0</code>.</p>
<form class="form-horizontal" id="fun-demo">
<div class="form-group">
<div class="col-3 col-sm-12">
<label class="form-label" for="input__key3">Key</label>
</div>
<div class="col-9 col-sm-12">
<input class="form-input" type="text" id="input__key3" value="">
</div>
</div>
</form>
<p><em>Tip:</em> the key is the name of the colorful object in the image.</p>
<script type="module">
import createSpeck from './speck.mjs'
import { encode, decode } from './format.mjs'
const $ = id => document.getElementById(id)
const formatAlphabet = '23456789ABCDEFGHIJKMNPQRSTUVWXYZ'
const maxValue = 1073741823
// Basic demo code
function runEncryptDemo () {
const key = ($('input__key').value.match(/\d+/g) || []).map(e => Number(e) & 0xFFFF)
const speck = createSpeck({ bits: 15 })
const value = speck.encrypt($('input__input').value, key)
$('input__output').value = value
$('input__formatted_output').value = encode(value, formatAlphabet, maxValue)
}
function runDecryptDemo (isFormattedEdit) {
const key = ($('input__key').value.match(/\d+/g) || []).map(e => Number(e) & 0xFFFF)
const speck = createSpeck({ bits: 15 })
const outputValue = $('input__output').value
const value = speck.decrypt(outputValue, key)
$('input__input').value = value
if (!isFormattedEdit) {
$('input__formatted_output').value = encode(outputValue, formatAlphabet, maxValue)
}
}
const initialKey = []
while (initialKey.length < 4) {
const value = crypto.getRandomValues(new Uint16Array(1))[0]
if (value <= 32767) initialKey.push(value)
}
$('input__key').value = initialKey.join(', ')
$('input__input').addEventListener('input', runEncryptDemo)
runEncryptDemo()
$('input__output').addEventListener('input', () => {
runDecryptDemo()
})
$('input__formatted_output').addEventListener('input', () => {
$('input__output').value = decode($('input__formatted_output').value, formatAlphabet, maxValue)
runDecryptDemo(true)
})
// Canvas demo code
const originalImage = $('originalImage')
const obfuscatedImage = $('obfuscatedImage')
const originalCtx = originalImage.getContext('2d')
const originalData = originalCtx.createImageData(256, 256)
for (let x = 0; x < 256; x++) {
for (let y = 0; y < 256; y++) {
const index = (x * 256 + y) * 4
originalData.data[index] = x
originalData.data[index + 1] = y
originalData.data[index + 3] = 255
}
}
originalCtx.putImageData(originalData, 0, 0)
const obfuscatedCtx = obfuscatedImage.getContext('2d')
$('input__key2').value = Array.from(crypto.getRandomValues(new Uint8Array(8))).join(', ')
const generateObfuscatedData = () => {
const key = ($('input__key2').value.match(/\d+/g) || []).map(e => Number(e) & 0xFF)
const speck = createSpeck({
bits: 8,
rounds: Number($('input__rounds').value),
rightRotations: Number($('input__rightrot').value),
leftRotations: Number($('input__leftrot').value)
})
const obfuscatedData = obfuscatedCtx.createImageData(256, 256)
for (let x = 0; x < 256; x++) {
for (let y = 0; y < 256; y++) {
const index = (x * 256 + y) * 4
const cipher = speck.encryptRaw([x, y], key)
obfuscatedData.data[index] = cipher[0]
obfuscatedData.data[index + 1] = cipher[1]
obfuscatedData.data[index + 3] = 255
}
}
obfuscatedCtx.putImageData(obfuscatedData, 0, 0)
}
$('canvas-demo').addEventListener('input', generateObfuscatedData)
generateObfuscatedData()
// Fun image obfuscation demo
const funOriginalImage = $('funOriginalImage')
const funDeobfuscatedImage = $('funDeobfuscatedImage')
const funOriginalCtx = funOriginalImage.getContext('2d')
const funDeobfuscatedCtx = funDeobfuscatedImage.getContext('2d')
const deobfuscateImage = async () => {
// Handle string -> SHA-1
const keyString = $('input__key3').value.trim().toLowerCase()
const keyArray = new TextEncoder().encode(keyString)
const keyBuffer = await window.crypto.subtle.digest({ name: 'SHA-1' }, keyArray)
const key = new Uint8Array(keyBuffer)
// This challenge should be easy, but just to make it easier for someone
// trying to brute force, it uses just 8 rounds (so just the first 8 bytes
// of the 20 byte SHA-1 derived key), also making it faster to process.
const speck = createSpeck({ bits: 8, rounds: 8 })
const originalData = funOriginalCtx.getImageData(0, 0, 256, 256)
const deobfuscatedData = funDeobfuscatedCtx.createImageData(256, 256)
for (let x = 0; x < 256; x++) {
for (let y = 0; y < 256; y++) {
const cipher = speck.decryptRaw([x, y], key)
const sourceIndex = (x * 256 + y) * 4
const targetIndex = (cipher[0] * 256 + cipher[1]) * 4
deobfuscatedData.data[targetIndex] = originalData.data[sourceIndex]
deobfuscatedData.data[targetIndex + 1] = originalData.data[sourceIndex + 1]
deobfuscatedData.data[targetIndex + 2] = originalData.data[sourceIndex + 2]
deobfuscatedData.data[targetIndex + 3] = 255
}
}
funDeobfuscatedCtx.putImageData(deobfuscatedData, 0, 0)
}
$('fun-demo').addEventListener('input', deobfuscateImage)
const funImage = new Image()
funImage.src = 'fun-demo-image.png'
funImage.addEventListener('load', () => {
funOriginalCtx.drawImage(funImage, 0, 0)
deobfuscateImage()
})
</script>
<hr>
<p><a href="https://github.com/qgustavor/generic-speck">Return to the project page</a></p>
</div></div></div>