initial commit

This commit is contained in:
Maple Nebel 2026-07-03 22:15:58 +02:00
commit 366cc3b759
6 changed files with 422 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

287
Cargo.lock generated Normal file
View file

@ -0,0 +1,287 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "aes"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138"
dependencies = [
"cipher",
"cpubits",
"cpufeatures",
]
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]]
name = "block-buffer"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
dependencies = [
"hybrid-array",
]
[[package]]
name = "bytemuck"
version = "1.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
dependencies = [
"bytemuck_derive",
]
[[package]]
name = "bytemuck_derive"
version = "1.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "cfg-if"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "chacha20"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
dependencies = [
"cfg-if",
"cpufeatures",
"rand_core",
]
[[package]]
name = "cipher"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
dependencies = [
"crypto-common",
"inout",
]
[[package]]
name = "cmov"
version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
[[package]]
name = "const-oid"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "cpubits"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae"
[[package]]
name = "cpufeatures"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
"hybrid-array",
]
[[package]]
name = "ctutils"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
dependencies = [
"cmov",
]
[[package]]
name = "digest"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer",
"const-oid",
"crypto-common",
"ctutils",
]
[[package]]
name = "getrandom"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
"r-efi",
"rand_core",
]
[[package]]
name = "hmac"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f"
dependencies = [
"digest",
]
[[package]]
name = "hybrid-array"
version = "0.4.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c"
dependencies = [
"bytemuck",
"typenum",
]
[[package]]
name = "inout"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
dependencies = [
"hybrid-array",
]
[[package]]
name = "libc"
version = "0.2.186"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
[[package]]
name = "md-5"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98"
dependencies = [
"cfg-if",
"digest",
]
[[package]]
name = "proc-macro2"
version = "1.0.106"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.46"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
dependencies = [
"proc-macro2",
]
[[package]]
name = "r-efi"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80"
dependencies = [
"chacha20",
"getrandom",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "spbr-common"
version = "0.1.0"
dependencies = [
"aes",
"base64",
"bytemuck",
"hmac",
"hybrid-array",
"md-5",
"rand",
"thiserror",
]
[[package]]
name = "syn"
version = "2.0.118"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "2.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"

19
Cargo.toml Normal file
View file

@ -0,0 +1,19 @@
[package]
name = "spbr-common"
version = "0.1.0"
edition = "2024"
[dependencies]
aes = "0.9.1"
base64 = { version = "0.22.1",
optional = true
}
bytemuck = { version = "1.25.0", features = ["derive"] }
hmac = {version = "0.13.0", optional = true}
hybrid-array = { version = "0.4.13", features = ["bytemuck"], optional = true }
md-5 = {version = "0.11.0", optional = true}
thiserror = "2.0.18"
rand = "0.10.2"
[features]
crypto = ["dep:base64", "dep:hmac", "dep:md-5", "dep:hybrid-array"]

112
src/crypto/encsign.rs Normal file
View file

@ -0,0 +1,112 @@
use aes::{
Aes128,
cipher::{Block, BlockCipherDecrypt, BlockCipherEncrypt, BlockSizeUser, block},
};
use bytemuck::{Pod, PodCastError, Zeroable};
use hmac::{Hmac, KeyInit, Mac};
use hybrid_array::Array;
use md5::Md5;
use rand::random;
use thiserror::Error;
use crate::crypto::encsign::Error::{BlockCastError, InvalidSignature, MessageTooSmall};
// first is aes second is hmac
pub struct EncSignKeypair(pub [u8; 0x10], pub [u8; 0x40]);
type HmacMd5 = Hmac<Md5>;
impl EncSignKeypair {
fn new_random() -> Self {
Self(random(), random())
}
}
pub fn blob_calc_signature(data: &[u8], key: &[u8; 0x40]) -> [u8; 0x10] {
let mut hmac = HmacMd5::new(Array::cast_from_core(key));
hmac.update(data);
hmac.finalize().into_bytes().0
}
pub fn encrypt_and_gen_signature_blocks(
data: &mut [Block<Aes128>],
pair: &EncSignKeypair,
) -> [u8; 0x10] {
let signature = blob_calc_signature(bytemuck::cast_slice(&data), &pair.1);
Aes128::new(Array::cast_from_core(&pair.0)).encrypt_blocks(data);
signature
}
pub fn decrypt_and_gen_signature_blocks<'a, 'b>(
data: &'a mut [Block<Aes128>],
pair: &'b EncSignKeypair,
) -> [u8; 0x10] {
Aes128::new(Array::cast_from_core(&pair.0)).decrypt_blocks(data);
blob_calc_signature(bytemuck::cast_slice(&data), &pair.1)
}
#[derive(Error, Debug, PartialEq)]
pub enum Error {
#[error("an error occurred whilest casting data to blocks: {0}")]
BlockCastError(PodCastError),
#[error("invalid signature")]
InvalidSignature,
#[error("message too small, it must be at least 0x10 in size")]
MessageTooSmall,
}
pub type Result<T, E = Error> = std::result::Result<T, E>;
pub fn encrypt_and_sign(mut data: Vec<u8>, keypair: &EncSignKeypair) -> Result<Vec<u8>> {
let block_data = bytemuck::try_cast_slice_mut(&mut data).map_err(BlockCastError)?;
let signature = encrypt_and_gen_signature_blocks(block_data, keypair);
data.extend_from_slice(&signature[..]);
Ok(data)
}
pub fn decrypt_and_check<'a, 'b>(
data: &'a mut [u8],
keypair: &'b EncSignKeypair,
) -> Result<&'a [u8]> {
let signature_delim_point = data.len().checked_sub(0x10).ok_or(MessageTooSmall)?;
let (crypt_data, signature_data) = data
.split_at_mut_checked(signature_delim_point)
.ok_or(MessageTooSmall)?;
let block_data = bytemuck::try_cast_slice_mut(crypt_data).map_err(BlockCastError)?;
let signature = decrypt_and_gen_signature_blocks(block_data, keypair);
if signature_data != &signature[..] {
return Err(InvalidSignature);
}
Ok(crypt_data)
}
#[cfg(test)]
mod test {
use super::{EncSignKeypair, Error, decrypt_and_check, encrypt_and_sign};
const FUZZ_ROUNDS: usize = 10000;
#[test]
fn fuzz_test_succeed() {
for _ in 0..FUZZ_ROUNDS {
let keypair = EncSignKeypair::new_random();
let random_data: [u8; 0x20] = rand::random();
let random_data_vec: Vec<u8> = random_data.into();
let mut message = encrypt_and_sign(random_data_vec, &keypair).unwrap();
let result = decrypt_and_check(&mut message, &keypair).unwrap();
assert_eq!(&random_data[..], result);
}
}
#[test]
fn fuzz_test_fail() {
for _ in 0..FUZZ_ROUNDS {
let keypair = EncSignKeypair::new_random();
let random_data: [u8; 0x20] = rand::random();
let random_data_vec: Vec<u8> = random_data.into();
let mut message = encrypt_and_sign(random_data_vec, &keypair).unwrap();
message.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
assert_eq!(
decrypt_and_check(&mut message, &keypair),
Err(Error::InvalidSignature)
);
}
}
}

1
src/crypto/mod.rs Normal file
View file

@ -0,0 +1 @@
pub mod encsign;

2
src/lib.rs Normal file
View file

@ -0,0 +1,2 @@
#[cfg(feature = "crypto")]
pub mod crypto;