aboutsummaryrefslogtreecommitdiff
path: root/base/src/assetfed.rs
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2024-01-24 18:11:23 +0100
committermetamuffin <metamuffin@disroot.org>2024-01-24 18:47:29 +0100
commit7323709537c6ff14136cd79fb07606cd79391758 (patch)
tree3d817d449d4c0a821b9b5073c8acf826c6ccfda1 /base/src/assetfed.rs
parentcbb2e163abfefd8ed61c41a096d5d6c27b4721b4 (diff)
downloadjellything-7323709537c6ff14136cd79fb07606cd79391758.tar
jellything-7323709537c6ff14136cd79fb07606cd79391758.tar.bz2
jellything-7323709537c6ff14136cd79fb07606cd79391758.tar.zst
refactor asset system pt. 1
Diffstat (limited to 'base/src/assetfed.rs')
-rw-r--r--base/src/assetfed.rs71
1 files changed, 71 insertions, 0 deletions
diff --git a/base/src/assetfed.rs b/base/src/assetfed.rs
new file mode 100644
index 0000000..800e458
--- /dev/null
+++ b/base/src/assetfed.rs
@@ -0,0 +1,71 @@
+use aes_gcm_siv::{
+ aead::{generic_array::GenericArray, Aead},
+ Aes256GcmSiv, KeyInit,
+};
+use anyhow::{anyhow, bail, Context};
+use base64::Engine;
+use bincode::{Decode, Encode};
+use jellycommon::Asset;
+use log::warn;
+use std::{path::PathBuf, sync::LazyLock};
+
+use crate::{cache::CachePath, SECRETS};
+
+const VERSION: u32 = 2;
+
+static ASSET_KEY: LazyLock<Aes256GcmSiv> = LazyLock::new(|| {
+ if let Some(sk) = &SECRETS.session_key {
+ let r = base64::engine::general_purpose::STANDARD
+ .decode(sk)
+ .expect("key invalid; should be valid base64");
+ aes_gcm_siv::Aes256GcmSiv::new_from_slice(&r)
+ .expect("key has the wrong length; should be 32 bytes")
+ } else {
+ warn!("session_key not configured; generating a random one.");
+ aes_gcm_siv::Aes256GcmSiv::new_from_slice(&[(); 32].map(|_| rand::random())).unwrap()
+ }
+});
+
+#[derive(Debug, Encode, Decode)]
+pub enum AssetInner {
+ Federated { host: String, asset: Vec<u8> },
+ Cache(CachePath),
+ Assets(PathBuf),
+}
+
+impl AssetInner {
+ pub fn ser(&self) -> Asset {
+ let mut plaintext = Vec::new();
+ plaintext.extend(u32::to_le_bytes(VERSION));
+ plaintext.extend(bincode::encode_to_vec(&self, bincode::config::standard()).unwrap());
+
+ while plaintext.len() % 16 == 0 {
+ plaintext.push(0);
+ }
+
+ let nonce = [(); 12].map(|_| rand::random());
+ let mut ciphertext = ASSET_KEY
+ .encrypt(&GenericArray::from(nonce), plaintext.as_slice())
+ .unwrap();
+ ciphertext.extend(nonce);
+
+ Asset(base64::engine::general_purpose::URL_SAFE.encode(&ciphertext))
+ }
+ pub fn deser(s: &str) -> anyhow::Result<Self> {
+ let ciphertext = base64::engine::general_purpose::URL_SAFE.decode(&s)?;
+ let (ciphertext, nonce) = ciphertext.split_at(ciphertext.len() - 12);
+ let plaintext = ASSET_KEY
+ .decrypt(nonce.into(), ciphertext)
+ .map_err(|_| anyhow!("asset token decrypt failed"))?;
+
+ let version = u32::from_le_bytes(plaintext[0..4].try_into().unwrap());
+ if version != VERSION {
+ bail!("asset token version mismatch");
+ }
+
+ let (data, _): (AssetInner, _) =
+ bincode::decode_from_slice(&plaintext[4..], bincode::config::standard())
+ .context("asset token has invalid format")?;
+ Ok(data)
+ }
+}