First commit
This commit is contained in:
commit
f9728d490d
7 changed files with 891 additions and 0 deletions
476
Cargo.lock
generated
Normal file
476
Cargo.lock
generated
Normal file
|
@ -0,0 +1,476 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "aead"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cipher",
|
||||
"cpufeatures",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aes-gcm"
|
||||
version = "0.10.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
|
||||
dependencies = [
|
||||
"aead",
|
||||
"aes",
|
||||
"cipher",
|
||||
"ctr",
|
||||
"ghash",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "argon2"
|
||||
version = "0.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"blake2",
|
||||
"cpufeatures",
|
||||
"password-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "base64ct"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitvec"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
|
||||
dependencies = [
|
||||
"funty",
|
||||
"radium",
|
||||
"tap",
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blake2"
|
||||
version = "0.10.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
|
||||
dependencies = [
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "cipher"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51e852e6dc9a5bed1fae92dd2375037bf2b768725bf3be87811edee3249d09ad"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"rand_core",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ctr"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
|
||||
dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f211bbe8e69bbd0cfdea405084f128ae8b4aaa6b0b522fc8f2b009084797920"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "funty"
|
||||
version = "2.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getopts"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14dbbfd5c71d70241ecf9e6f13737f7b5ce823821063188d7e46c41d371eebd5"
|
||||
dependencies = [
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ghash"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0d8a4362ccb29cb0b265253fb0a2728f592895ee6854fd9bc13f2ffda266ff1"
|
||||
dependencies = [
|
||||
"opaque-debug",
|
||||
"polyval",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.155"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "opaque-debug"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
|
||||
|
||||
[[package]]
|
||||
name = "password-hash"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
|
||||
dependencies = [
|
||||
"base64ct",
|
||||
"rand_core",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png_data2"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aes-gcm",
|
||||
"argon2",
|
||||
"bitvec",
|
||||
"getopts",
|
||||
"png",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "polyval"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"opaque-debug",
|
||||
"universal-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.36"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "radium"
|
||||
version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.74"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fceb41e3d546d0bd83421d3409b1460cc7444cd389341a4c880fe7a042cb3d7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tap"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d"
|
||||
|
||||
[[package]]
|
||||
name = "universal-hash"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
|
||||
dependencies = [
|
||||
"crypto-common",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wyz"
|
||||
version = "0.5.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
|
||||
dependencies = [
|
||||
"tap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.7.35"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
13
Cargo.toml
Normal file
13
Cargo.toml
Normal file
|
@ -0,0 +1,13 @@
|
|||
[package]
|
||||
name = "png_data2"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
aes-gcm = "0.10.3"
|
||||
argon2 = "0.5.3"
|
||||
bitvec = "1.0.1"
|
||||
getopts = "0.2.21"
|
||||
png = "0.17.13"
|
||||
rand = "0.8.5"
|
||||
rand_chacha = "0.3.1"
|
58
src/block.rs
Normal file
58
src/block.rs
Normal file
|
@ -0,0 +1,58 @@
|
|||
use std::{fmt::Formatter, intrinsics::ctlz, str::FromStr};
|
||||
|
||||
/// Block mode for embedded data
|
||||
#[derive(Debug)]
|
||||
pub struct BlockMode {
|
||||
pub len: usize,
|
||||
pub crc_len: usize,
|
||||
}
|
||||
|
||||
impl BlockMode {
|
||||
/// Gets the best [`BlockMode`] and the remainder
|
||||
pub fn from_length(len: usize, crc: bool) -> Self {
|
||||
let mut best_remainder = len;
|
||||
let mut best_p = 0;
|
||||
for p in 4..16 {
|
||||
let remainder = len % (1 << p);
|
||||
if remainder <= best_remainder {
|
||||
best_remainder = remainder;
|
||||
best_p = p;
|
||||
}
|
||||
}
|
||||
|
||||
Self {
|
||||
len: 1 << best_p,
|
||||
crc_len: (best_p/4) * crc as usize,
|
||||
}
|
||||
}
|
||||
pub fn to_data(&self) -> u8 {
|
||||
((self.crc_len != 0) as u8) | (ctlz(self.len) << 1) as u8
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for BlockMode {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "(len: {}, crc_len: {})", self.len, self.crc_len)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for BlockMode {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let size = s
|
||||
.parse::<usize>()
|
||||
.map_err(|err| format!("Failed to parse `{}` as block size: {err}", s))?;
|
||||
|
||||
if size < 6 || size > 16 {
|
||||
Err(format!(
|
||||
"Invalid block size specified: `{size}` expected value within [6; 16]"
|
||||
))?;
|
||||
}
|
||||
|
||||
Ok(BlockMode {
|
||||
len: 1 << size,
|
||||
crc_len: size,
|
||||
})
|
||||
}
|
||||
}
|
93
src/embed.rs
Normal file
93
src/embed.rs
Normal file
|
@ -0,0 +1,93 @@
|
|||
use std::fmt::Formatter;
|
||||
use std::str::FromStr;
|
||||
|
||||
use bitvec::prelude::*;
|
||||
use bitvec::vec::BitVec;
|
||||
|
||||
use crate::block::BlockMode;
|
||||
use crate::image::ImageInfo;
|
||||
|
||||
pub enum EmbedAlgorithm {
|
||||
Lo(u8),
|
||||
}
|
||||
|
||||
impl EmbedAlgorithm {
|
||||
pub fn max_size(&self, blockmode: &BlockMode, info: &Box<dyn ImageInfo>) -> usize {
|
||||
let blocks_num = info.size() / blockmode.len;
|
||||
|
||||
match self {
|
||||
EmbedAlgorithm::Lo(bits) => {
|
||||
(((blockmode.len - blockmode.crc_len)*blocks_num) as f64 * (*bits as f64) / 8f64).floor() as usize
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_block(&self, original_data: &[u8], embed_data: &BitVec<u8>, mut embed_offset: usize, blockmode: &BlockMode) -> (Vec<u8>, usize) {
|
||||
let mut result = Vec::<u8>::with_capacity(blockmode.len);
|
||||
|
||||
match self {
|
||||
EmbedAlgorithm::Lo(bits) => {
|
||||
let mask = (1<<bits) -1;
|
||||
|
||||
fn bits_to_byte(slice: &BitSlice<u8>, bits: u8) -> u8
|
||||
{
|
||||
let mut result : u8 = 0;
|
||||
for i in 0..bits
|
||||
{
|
||||
result |= (slice[i as usize] as u8) << i;
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
for i in 0..(blockmode.len-blockmode.crc_len)
|
||||
{
|
||||
let embed = bits_to_byte(embed_data.get(embed_offset..embed_offset+*bits as usize).unwrap(), *bits);
|
||||
let b = original_data[i];
|
||||
|
||||
result.push((b & !mask) | embed);
|
||||
|
||||
embed_offset += *bits as usize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
(result, embed_offset)
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Display for EmbedAlgorithm {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
EmbedAlgorithm::Lo(bits) => write!(f, "Lo({bits})"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for EmbedAlgorithm {
|
||||
type Err = String;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let (dig_pos, _) = s
|
||||
.char_indices()
|
||||
.find(|(_, c)| c.is_ascii_digit())
|
||||
.ok_or(format!("Unknown algorithm: {s}"))?;
|
||||
|
||||
let (first, second) = s.split_at(dig_pos);
|
||||
match first {
|
||||
"lo" => {
|
||||
let value = second.parse::<u8>().map_err(|err| {
|
||||
format!("Failed to convert `{second}` to a number of bits: {err}")
|
||||
})?;
|
||||
if value > 8 || value == 0 {
|
||||
Err(format!(
|
||||
"Cannot specify {value} bits for `lo` method, must be within [1, 8]"
|
||||
))
|
||||
} else {
|
||||
Ok(EmbedAlgorithm::Lo(value))
|
||||
}
|
||||
}
|
||||
_ => Err(format!("Unknown algorithm: {s}")),
|
||||
}
|
||||
}
|
||||
}
|
33
src/header.rs
Normal file
33
src/header.rs
Normal file
|
@ -0,0 +1,33 @@
|
|||
use crate::block::BlockMode;
|
||||
|
||||
pub struct Header {
|
||||
pub blockmode: BlockMode,
|
||||
pub comment: Option<String>,
|
||||
}
|
||||
|
||||
impl Header {
|
||||
pub fn to_data(&self, version: u16, embed_len: u32) -> Vec<u8> {
|
||||
let mut header = vec![];
|
||||
|
||||
// Version
|
||||
header.extend_from_slice(version.to_le_bytes().as_slice());
|
||||
|
||||
// TODO: IV+Cipherinfo
|
||||
// Blockmode
|
||||
header.push(self.blockmode.to_data().to_le());
|
||||
|
||||
// Data len
|
||||
header.extend_from_slice(embed_len.to_le_bytes().as_slice());
|
||||
|
||||
// Comment len
|
||||
let comment_len = self.comment.as_ref().map(|c| c.len()).unwrap_or(0);
|
||||
header.extend_from_slice(comment_len.to_le_bytes().as_slice());
|
||||
|
||||
// Comment
|
||||
if let Some(comment) = &self.comment {
|
||||
header.extend_from_slice(comment.as_bytes());
|
||||
}
|
||||
|
||||
header
|
||||
}
|
||||
}
|
5
src/image.rs
Normal file
5
src/image.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
pub trait ImageInfo {
|
||||
fn width(&self) -> u32;
|
||||
fn height(&self) -> u32;
|
||||
fn size(&self) -> usize;
|
||||
}
|
213
src/main.rs
Normal file
213
src/main.rs
Normal file
|
@ -0,0 +1,213 @@
|
|||
#![feature(core_intrinsics)]
|
||||
pub mod block;
|
||||
pub mod embed;
|
||||
pub mod header;
|
||||
pub mod image;
|
||||
|
||||
use std::env;
|
||||
use std::fs::File;
|
||||
use std::process::ExitCode;
|
||||
use std::str::FromStr;
|
||||
|
||||
use bitvec::slice::BitSlice;
|
||||
use bitvec::vec::BitVec;
|
||||
use block::BlockMode;
|
||||
use embed::EmbedAlgorithm;
|
||||
use getopts::Matches;
|
||||
use getopts::Options;
|
||||
use header::Header;
|
||||
use image::ImageInfo;
|
||||
use rand::SeedableRng;
|
||||
use rand_chacha::ChaCha8Rng;
|
||||
use rand::Rng;
|
||||
use rand::prelude::SliceRandom;
|
||||
|
||||
fn print_usage(program: &str, opts: Options) {
|
||||
let brief = format!(
|
||||
"Usage: {0} -(e|d|i) FILE [opts]
|
||||
Encode: {0} -e file.tar -l rgba8 -c \"(.tar) my archive\" > out.png
|
||||
Decode: {0} -d out.png > file.tar
|
||||
Info: {0} -i out.png # (.tar) my archive",
|
||||
program
|
||||
);
|
||||
print!("{}", opts.usage(&brief));
|
||||
}
|
||||
|
||||
fn print_version() {
|
||||
print!(
|
||||
"png_data -- Embed data into PNG\n
|
||||
Public domain\n"
|
||||
);
|
||||
}
|
||||
|
||||
impl ImageInfo for png::OutputInfo {
|
||||
fn width(&self) -> u32 { self.width }
|
||||
|
||||
fn height(&self) -> u32 { self.height }
|
||||
|
||||
fn size(&self) -> usize { self.buffer_size() }
|
||||
}
|
||||
|
||||
fn get_algorithm(s: Option<String>) -> Result<EmbedAlgorithm, String> {
|
||||
if let Some(s) = &s {
|
||||
EmbedAlgorithm::from_str(s.as_str())
|
||||
} else {
|
||||
Err("Missing required algorithm parameter".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn get_blockmode(s: Option<String>) -> Result<BlockMode, String> {
|
||||
if let Some(s) = &s {
|
||||
BlockMode::from_str(s)
|
||||
} else {
|
||||
Err("Missing requires blockmode parameter".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn decode_image(image: String) -> Result<(Vec<u8>, Box<dyn ImageInfo>), String> {
|
||||
match image.split_at(image.find('.').unwrap_or(0)).1 {
|
||||
".png" => {
|
||||
let decoder = png::Decoder::new(
|
||||
File::open(&image).map_err(|err| format!("Failed to read `{image}`: {err}"))?,
|
||||
);
|
||||
let mut reader = decoder
|
||||
.read_info()
|
||||
.map_err(|err| format!("Failed to read png info for `{image}`: {err}"))?;
|
||||
let mut result = Vec::with_capacity(reader.output_buffer_size());
|
||||
result.resize(reader.output_buffer_size(), 0);
|
||||
let info = reader
|
||||
.next_frame(result.as_mut_slice())
|
||||
.map_err(|err| format!("Failed to read png info for `{image}`: {err}"))?;
|
||||
result.resize(info.buffer_size(), 0);
|
||||
|
||||
Ok((result, Box::new(info)))
|
||||
}
|
||||
_ => Err(format!("Unable get image type for {image}")),
|
||||
}
|
||||
}
|
||||
|
||||
fn derive_seed(seed: &str) -> Result<[u8; 32], String> {
|
||||
let mut result = [0u8; 32];
|
||||
argon2::Argon2::default().hash_password_into(seed.as_bytes(), b"SEED SALT", &mut result)
|
||||
.map_err(|err| format!("Failed to derive seed `{seed}`: {err}"))?;
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn encode(image: String, matches: Matches) -> Result<Vec<u8>, String> {
|
||||
let algorithm = get_algorithm(matches.opt_str("l"))?;
|
||||
let crc = false;
|
||||
let embed_file = matches
|
||||
.opt_str("i")
|
||||
.ok_or(format!("Embed file is required"))?;
|
||||
|
||||
let (data, info) = decode_image(image)?;
|
||||
let blockmode = BlockMode::from_length(info.size(), crc);
|
||||
let seed = derive_seed(
|
||||
matches
|
||||
.opt_str("s")
|
||||
.unwrap_or(format!("{}x{}", info.width(), info.height()))
|
||||
.as_str(),
|
||||
)?;
|
||||
let max_size = algorithm.max_size(&blockmode, &info);
|
||||
|
||||
let embed_data = std::fs::read(&embed_file)
|
||||
.map_err(|err| format!("Failed to read embed file `{embed_file}`: {err}"))?;
|
||||
|
||||
// Get header
|
||||
let header = Header {
|
||||
blockmode,
|
||||
comment: matches.opt_str("c"),
|
||||
};
|
||||
let header_data = header.to_data(1, embed_data.len() as u32);
|
||||
|
||||
// Check length
|
||||
if embed_data.len() + header_data.len() > max_size {
|
||||
Err(format!(
|
||||
"Cannot embed {}bytes into {}bytes using the {algorithm} algorithm with blockmode {}. Max embeddable size: {max_size}bytes",
|
||||
embed_data.len()+header_data.len(),
|
||||
data.len(),
|
||||
header.blockmode,
|
||||
))?;
|
||||
}
|
||||
|
||||
// Shuffle the blocks
|
||||
let mut rand = ChaCha8Rng::from_seed(seed);
|
||||
let blocks_num = info.size() / (header.blockmode.len-header.blockmode.crc_len);
|
||||
|
||||
let mut blocks_pos = (0..blocks_num).collect::<Vec<_>>();
|
||||
blocks_pos.shuffle(&mut rand);
|
||||
|
||||
|
||||
let mut bv = BitVec::<u8>::from_vec(header_data);
|
||||
bv.extend_from_raw_slice(embed_data.as_slice());
|
||||
|
||||
let mut embed_offset = 0;
|
||||
for i in 0 .. blocks_num
|
||||
{
|
||||
println!("{:#?}", bv.len());
|
||||
let (block, mut new_offset) = algorithm.next_block(
|
||||
&data.as_slice()[i*header.blockmode.len..],
|
||||
&bv,
|
||||
embed_offset,
|
||||
&header.blockmode);
|
||||
new_offset += header.blockmode.crc_len*8;
|
||||
|
||||
embed_offset = new_offset;
|
||||
}
|
||||
algorithm.next_block(data.as_slice(), &bv, 48, &header.blockmode);
|
||||
// TODO: WRITE CRC
|
||||
|
||||
println!("Ok");
|
||||
|
||||
Ok(vec![])
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let program = args[0].clone();
|
||||
|
||||
let mut opts = Options::new();
|
||||
opts.optopt("i", "input", "Input file", "PATH");
|
||||
opts.optflag("e", "encode", "Encode file");
|
||||
opts.optflag("d", "decode", "Decode mode");
|
||||
opts.optopt("c", "comment", "Header comment", "TXT");
|
||||
opts.optopt("s", "seed", "Force seed", "TXT");
|
||||
opts.optflag("", "no-crc", "Disables CRC");
|
||||
opts.optflag("z", "info", "Read information");
|
||||
opts.optopt("l", "algorithm", "Embed algorithm", "lo3");
|
||||
opts.optflag("h", "help", "Print this help menu");
|
||||
opts.optflag("v", "version", "Print program version and licenses");
|
||||
|
||||
let matches = match opts.parse(&args[1..]) {
|
||||
Ok(m) => m,
|
||||
Err(f) => {
|
||||
panic!("{}", f.to_string())
|
||||
}
|
||||
};
|
||||
if matches.opt_present("v") {
|
||||
print_version();
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
if matches.opt_present("h") {
|
||||
print_usage(&program, opts);
|
||||
return ExitCode::SUCCESS;
|
||||
}
|
||||
if matches.free.is_empty() {
|
||||
print_usage(&program, opts);
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
|
||||
let input = matches.free[0].clone();
|
||||
|
||||
if matches.opt_present("e") {
|
||||
match encode(input, matches) {
|
||||
Ok(_) => todo!(""),
|
||||
Err(e) => {
|
||||
eprintln!("{e}");
|
||||
return ExitCode::FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ExitCode::SUCCESS
|
||||
}
|
Loading…
Reference in a new issue