| Crates.io | polarstego |
| lib.rs | polarstego |
| version | 0.3.0 |
| created_at | 2026-01-05 10:43:47.159192+00 |
| updated_at | 2026-01-25 21:22:58.080093+00 |
| description | Rust implementation of Steganographic Polar Codes |
| homepage | |
| repository | https://gitlab.com/harrose/polarstego |
| max_upload_size | |
| id | 2023605 |
| size | 27,035 |
Rust implementation of Steganographic Polar Codes (SPC). It's used to embed a secret message in a cover signal with near-optimal distortion.
Based on Designing Near-Optimal Steganographic Codes in Practice Based on Polar Codes.
Part of the Rooks project.
Algorithms 2 and 3 are removed because they are special cases of Algorithms 4 and 5.
The input is supposed to be scrambled by a secret key. There are two reasons for this:
1e10), their modification is highly discouraged, but theoretically can happen2^n, we pad it with zeros to the closest power of two. The same thing is done during extraction. The padding bits must remain zeros and their modification probability is set to 0.0. If SC Decoder flips some of them anyway, it means that embedding failed. We won't be able to extract our message. In this case, embed returns Err(PolarStegoError::WetPaddingModified(amount)), where amount is the number of flipped bits. To fix this, you should shorten the message.If you don't specify scrambling_key, a fixed zero key [0; 32] will be used.
Providing a custom key is optional. In my opinion, it's more useful to implement scrambling by a secret key on a higher level. But scrambling itself is necessary for SPC to work properly.
use polarstego::{embed, extract};
// `x` is usually an array of the least significant bits of image pixels
let x = [1, 0, 1, 0, 1, 0, 1, 0, 1, 0];
// Cost map `rho`: how "expensive" it is to modify each bit
// High costs (like 10.0) mark "wet" bits that shouldn't be touched
// They are usually called "wet pixels" because image steganography is the most common
let rho = [0.2, 10.0, 10.0, 0.5, 10.0, 10.0, 0.2, 0.2, 0.2, 5.0];
// Our secret message
let m = [1, 1, 0];
let y = embed(&x, &rho, &m, None).unwrap();
println!("{:?}", y);
// y = [1, 0, 1, 0, 1, 0, 1, 1, 0, 0]
// ^ ^ these bits were changed
// Total distortion: 0.4
// The message can be extracted back if you know it's length
let m_extracted = extract(&y, m.len(), None);
assert_eq!(m.to_vec(), m_extracted);