diff options
Diffstat (limited to 'base/src/assetfed.rs')
-rw-r--r-- | base/src/assetfed.rs | 71 |
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) + } +} |