use std::{io::{Read, Write}, mem::size_of};
use sodiumoxide::crypto::box_::{self, PublicKey, Nonce, SecretKey};
use crate::io::{disk_lookup};
use super::common::*;
pub fn decrypt(keydir: &str, input: &mut dyn Read, output: &mut dyn Write) -> anyhow::Result<()> {
_decrypt(keydir, input, Box::new(disk_lookup), output)
}
pub fn _decrypt(keydir: &str, input: &mut dyn Read,
lookup: Box<dyn Fn(&str, &PublicKey) -> anyhow::Result<SecretKey>>,
output: &mut dyn Write) -> anyhow::Result<()> {
let (source_pkey, target_pkey, initial_nonce) = read_header(input)?;
let target_skey = lookup(keydir, &target_pkey)?;
let symkey = box_::precompute(&source_pkey, &target_skey);
let mut len_buf = [0u8; size_of::<u16>()];
for chunk_num in 0u64.. {
let chunk_nonce = calculate_chunk_nonce(&initial_nonce, chunk_num);
let num_read = input.read(&mut len_buf)?;
if num_read < size_of::<u16>() {
return Ok(()); }
let len = u16::from_be_bytes(len_buf);
if len == 0u16 {
return Ok(());
}
if len > MAX_CIPHERTEXT_CHUNK as u16 {
return Err(anyhow::anyhow!("chunk size > MAX_CIPHERTEXT_CHUNK"));
}
let mut buf = vec![0u8; len as usize];
input.read_exact(&mut buf)?;
let result = box_::open_precomputed(&buf, &chunk_nonce,&symkey);
if let Ok(plaintext) = result {
output.write_all(&plaintext)?;
} else {
return Err(anyhow::anyhow!("bad ciphertext"));
}
}
unreachable!("loop never exits");
}
pub fn read_header(input: &mut dyn Read) -> anyhow::Result<(PublicKey, PublicKey, Nonce)> {
let mut buf = [0u8; 16];
input.read_exact(&mut buf)?;
if &buf[..4] != FADEDBEE {
return Err(anyhow::anyhow!("invalid magic"));
}
if &buf[4..13] != TURNSTILE {
return Err(anyhow::anyhow!("invalid protocol"));
}
if buf[13..15] != version_bytes()[0..2] { return Err(anyhow::anyhow!("invalid version"));
}
let mut source_pkey_buf = [0u8; 32];
input.read_exact(&mut source_pkey_buf)?;
let source_pkey = PublicKey(source_pkey_buf);
let mut target_pkey_buf = [0u8; 32];
input.read_exact(&mut target_pkey_buf)?;
let target_pkey = PublicKey(target_pkey_buf);
let mut initial_nonce_buf = [0u8; 24];
input.read_exact(&mut initial_nonce_buf)?;
let initial_nonce = Nonce(initial_nonce_buf);
Ok((source_pkey, target_pkey, initial_nonce))
}