Skip to content

Commit 0c75245

Browse files
committed
refactor
1 parent d284324 commit 0c75245

File tree

6 files changed

+46
-57
lines changed

6 files changed

+46
-57
lines changed

app/index-zh.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ <h2>图像加密工具
2323
隐写:将秘密文本信息隐藏在图像中。
2424
</li>
2525
</ul>
26-
本应用完全在浏览器中运行,因此你的数据不会发送给任何服务器,始终保持安全:)
26+
本应用完全在浏览器中运行,你的数据不会发送给任何服务器,绝对安全 :)
2727
</p>
2828

2929
模式:
@@ -48,11 +48,11 @@ <h2>图像加密工具
4848

4949
<div id="msgInput-container" style="display: none; flex-direction: column; margin-bottom: 0.5rem;">
5050
<label for="msgInput">信息:</label>
51-
<textarea id="msgInput" placeholder="Enter your message here" autocomplete="off" rows="3"></textarea>
51+
<textarea id="msgInput" placeholder="请输入注入图像的秘密信息" autocomplete="off" rows="3"></textarea>
5252
</div>
5353
<div id="secretInput-container" style="display: flex; flex-direction: column; margin-bottom: 0.5rem;">
5454
<label for="secretInput">密钥:</label>
55-
<input id="secretInput" placeholder="Your secret key for encryption / decryption." autocomplete="off">
55+
<input id="secretInput" placeholder="请输入密钥" autocomplete="off" style="font-family: monospace;">
5656
</div>
5757

5858
<span style="margin-top: 0.25rem">

app/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ <h2>Image cipher
4848
</div>
4949
<div id="secretInput-container" style="display: flex; flex-direction: column; margin-bottom: 0.5rem;">
5050
<label for="secretInput">Secret:</label>
51-
<input id="secretInput" placeholder="Your secret key for encryption / decryption." autocomplete="off">
51+
<input id="secretInput" placeholder="Your secret key for encryption / decryption." autocomplete="off" style="font-family: monospace;">
5252
</div>
5353

5454
<span style="margin-top: 0.25rem">

app/script.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const footer = document.getElementById('footer');
2727
const worker = new Worker('./worker.js', { type: 'module' });
2828

2929
function onModeChange(mode) {
30-
if (mode === 'cipher') {
30+
if (mode === 'crypto') {
3131
msgInputContainer.style.display = 'none';
3232
imTypeSelect.disabled = false;
3333
imTypeContainer.style.display = 'block';
@@ -38,6 +38,12 @@ function onModeChange(mode) {
3838
imTypeSelect.disabled = true;
3939
imTypeContainer.style.display = 'none';
4040
}
41+
const urlParams = new URLSearchParams(window.location.search);
42+
// remove existing mode and secret params
43+
urlParams.delete('mode');
44+
urlParams.delete('m');
45+
urlParams.set('m', mode);
46+
window.history.replaceState({}, '', `${window.location.pathname}?${urlParams.toString()}`);
4147
}
4248

4349
// handle initial mode setup
@@ -57,18 +63,19 @@ function onModeChange(mode) {
5763
onModeChange('stegano');
5864
break;
5965
case 'cipher':
66+
case 'crypto':
6067
case 'cryptography':
6168
default:
6269
cipherModeInput.checked = true;
6370
steganoModeInput.checked = false;
64-
onModeChange('cipher');
71+
onModeChange('crypto');
6572
break;
6673
}
6774
}
6875

6976
function getMode() {
7077
if (cipherModeInput.checked) {
71-
return 'cipher';
78+
return 'crypto';
7279
} else if (steganoModeInput.checked) {
7380
return 'stegano';
7481
}
@@ -87,8 +94,10 @@ function checkInputRaise() {
8794
throw new Error("No image file selected");
8895
}
8996
if (secretInput.value.trim() === '') {
90-
showError("Secret cannot be empty");
91-
throw new Error("Secret cannot be empty");
97+
if (getMode() === 'crypto') {
98+
showError("Secret cannot be empty");
99+
throw new Error("Secret cannot be empty");
100+
}
92101
}
93102
}
94103

@@ -149,7 +158,7 @@ async function encode_image() {
149158

150159
ensureOutput();
151160
switch (getMode()) {
152-
case 'cipher':
161+
case 'crypto':
153162
worker.postMessage({
154163
type: 'encode',
155164
buffer: inputBlob,
@@ -181,7 +190,7 @@ async function decode_image() {
181190

182191
ensureOutput();
183192
switch (getMode()) {
184-
case 'cipher':
193+
case 'crypto':
185194
worker.postMessage({
186195
type: 'decode',
187196
buffer: inputBlob,
@@ -241,7 +250,7 @@ worker.onmessage = async (event) => {
241250
})
242251
cipherModeInput.addEventListener('change', () => {
243252
if (cipherModeInput.checked) {
244-
onModeChange('cipher');
253+
onModeChange('crypto');
245254
}
246255
})
247256

app/worker.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ self.onmessage = async function(event) {
3030
if (type === 'stega_encode') {
3131
const encoded = stega_encode(buffer, message, secret, maxSide);
3232
self.postMessage({
33-
type: 'encoded_stega',
33+
type: 'stega_encode',
3434
buffer: encoded,
3535
format: 'png',
3636
});
@@ -39,7 +39,7 @@ self.onmessage = async function(event) {
3939
if (type === 'stega_decode') {
4040
const decoded = stega_decode(buffer, secret, maxSide);
4141
self.postMessage({
42-
type: 'decoded_stega',
42+
type: 'stega_decode',
4343
message: decoded,
4444
});
4545
}

src/lib.rs

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ pub fn encode(im: &[u8], secret: &str, max_side: i32, as_type: &str) -> Result<B
120120
vec2imblob(&pixels, im_opt, None, match as_type {
121121
"png" => ImageType::Png,
122122
"jpeg" => ImageType::Jpeg,
123-
_ => panic!("Unsupported image type: {}", as_type),
123+
_ => return Err(format!("Unsupported image type: {}", as_type)),
124124
})
125125
}
126126

@@ -137,7 +137,7 @@ pub fn decode(im: &[u8], secret: &str, max_side: i32, as_type: &str) -> Result<B
137137
vec2imblob(&pixels, im_opt, max_side, match as_type {
138138
"png" => ImageType::Png,
139139
"jpeg" => ImageType::Jpeg,
140-
_ => panic!("Unsupported image type: {}", as_type),
140+
_ => return Err(format!("Unsupported image type: {}", as_type)),
141141
})
142142
}
143143

@@ -152,13 +152,7 @@ pub fn stega_encode(
152152

153153
console_log!("Encoding stega image");
154154
let max_side = if max_side < 1 { None } else { Some(max_side as u32) };
155-
let seed: Option<f64> = match secret {
156-
"" => None,
157-
s => {
158-
console_log!("Seed: {}", s);
159-
Some(str2f(s))
160-
},
161-
};
155+
let seed = Some(str2f(secret));
162156

163157
let (mut im_v, im_opt) = img2vec(im, max_side)?;
164158
stega::inject_lsb(&mut im_v[..], message, seed)?;
@@ -174,13 +168,7 @@ pub fn stega_decode(
174168
) -> Result<String, String> {
175169
console_log!("Decoding stega image");
176170
let max_side = if max_side < 1 { None } else { Some(max_side as u32) };
177-
let seed: Option<f64> = match secret {
178-
"" => None,
179-
s => {
180-
console_log!("Seed: {}", s);
181-
Some(str2f(s))
182-
},
183-
};
171+
let seed = Some(str2f(secret));
184172

185173
let (im_v, _) = img2vec(im, max_side)?;
186174
stega::extract_lsb(&im_v[..], seed)

src/stega.rs

Lines changed: 19 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::vec;
2+
13
use crate::logistic_map;
24
use wasm_bindgen::prelude::*;
35

@@ -21,18 +23,19 @@ pub fn inject_lsb(
2123
let msg_len_encode = (msg_vec_raw.len() as u32).to_le_bytes();
2224

2325
let mut msg_vec = vec![0; (im.len() + 7) / 8];
24-
console_log!("Message channel length (encode): {}", msg_vec.len());
25-
if msg_vec.len() < msg_len_encode.len() + msg_vec_raw.len() {
26+
let msg_len = msg_len_encode.len() + msg_vec_raw.len();
27+
console_log!("[lsb encode] Capacity: {}, message length: {}", msg_vec.len(), msg_len);
28+
29+
if msg_vec.len() < msg_len {
2630
return Err("Channel capacity not enough for message".to_string());
2731
}
2832

29-
// Copy the message length and raw message into the padded vector
30-
for (i, &byte) in msg_len_encode.iter().enumerate() {
31-
msg_vec[i] = byte;
32-
}
33-
for (i, &byte) in msg_vec_raw.iter().enumerate() {
34-
msg_vec[i + msg_len_encode.len()] = byte;
35-
}
33+
msg_len_encode.iter().enumerate().for_each(
34+
|(i, &byte)| { msg_vec[i] = byte; }
35+
);
36+
msg_vec_raw.iter().enumerate().for_each(
37+
|(i, &byte)| { msg_vec[i + msg_len_encode.len()] = byte; }
38+
);
3639

3740
if let Some(seed) = encryption_seed {
3841
msg_vec = logistic_map::encode::<1>(&msg_vec, seed);
@@ -42,36 +45,26 @@ pub fn inject_lsb(
4245
let msg_idx = i / 8;
4346
let bit_idx = i % 8;
4447

45-
if msg_idx > msg_vec.len() - 1 {
46-
break; // No more bits to inject
47-
}
48+
// No more bits to inject
49+
if msg_idx > msg_vec.len() - 1 { break; }
4850

4951
let bit = (msg_vec[msg_idx] >> (7 - bit_idx)) & 1;
5052
im[i] = im[i] & 0xFE | bit;
5153
}
52-
5354
Ok(())
5455
}
5556

5657
pub fn extract_lsb(
5758
im: &[u8],
5859
encryption_seed: Option<f64>,
5960
) -> Result<String, String> {
60-
if im.len() < 8 {
61-
return Err("Image too small to contain message".to_string());
62-
}
61+
if im.len() < 32 { return Err("Image too small to contain message".to_string()); }
6362

64-
let mut msg_bits = Vec::new();
65-
for i in 0..im.len() {
66-
let bit = im[i] & 1; // Extract the least significant bit
67-
msg_bits.push(bit);
68-
}
69-
let npad = msg_bits.len() % 8;
70-
if npad != 0 {
71-
msg_bits.extend(vec![0; 8 - npad]);
72-
}
63+
let npad = (im.len() + 7) / 8 * 8 - im.len();
64+
let mut msg_bits: Vec<u8> = im.iter().map(|&byte| { 0x01 & byte }).collect();
65+
msg_bits.extend(vec![0; npad]);
7366

74-
let mut msg_vec = Vec::new();
67+
let mut msg_vec = Vec::with_capacity(msg_bits.len()/8);
7568
for i in (0..msg_bits.len()).step_by(8) {
7669
let byte = msg_bits[i..i + 8].iter().fold(0, |acc, &bit| (acc << 1) | bit);
7770
msg_vec.push(byte);
@@ -87,7 +80,6 @@ pub fn extract_lsb(
8780
console_err!("Message length: {}, message channel length: {}", msg_len, msg_vec.len());
8881
return Err("Failed to decode message".to_string());
8982
}
90-
9183
let msg_raw = &msg_vec[4..4 + msg_len as usize];
9284

9385
String::from_utf8(msg_raw.to_vec()).map_err(|e| e.to_string())

0 commit comments

Comments
 (0)