Add basic filename decoding support

This commit is contained in:
oupson 2022-05-17 22:22:28 +02:00
parent 3b0fbb2e38
commit 55899706c0
6 changed files with 143 additions and 8 deletions

34
Cargo.lock generated
View File

@ -23,6 +23,17 @@ dependencies = [
"opaque-debug",
]
[[package]]
name = "aes"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bfe0133578c0986e1fe3dfcd4af1cc5b2dd6c3dbf534d69916ce16a2701d40ba"
dependencies = [
"cfg-if",
"cipher 0.4.3",
"cpufeatures",
]
[[package]]
name = "aes-gcm"
version = "0.9.4"
@ -30,7 +41,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "df5f85a83a7d8b0442b6aa7b504b8212c1733da07b98aae43d4bc21b2cb3cdf6"
dependencies = [
"aead",
"aes",
"aes 0.7.5",
"cipher 0.3.0",
"ctr",
"ghash",
@ -87,6 +98,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a90ec2df9600c28a01c56c4784c9207a96d2451833aeceb8cc97e4c9548bb78"
dependencies = [
"generic-array",
]
[[package]]
name = "bumpalo"
version = "3.9.1"
@ -202,6 +222,15 @@ dependencies = [
"subtle",
]
[[package]]
name = "eme-mode"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063654bca41486d52535de195491968cf9557d30e78b21280816072632df43ef"
dependencies = [
"cipher 0.4.3",
]
[[package]]
name = "generic-array"
version = "0.14.5"
@ -288,6 +317,7 @@ version = "0.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5"
dependencies = [
"block-padding",
"generic-array",
]
@ -447,11 +477,13 @@ dependencies = [
name = "rustcryptfs"
version = "0.0.1"
dependencies = [
"aes 0.8.1",
"aes-gcm",
"anyhow",
"base64",
"cipher 0.4.3",
"clap",
"eme-mode",
"hkdf",
"log",
"rustls",

View File

@ -12,9 +12,11 @@ rustls = "0.20.2"
scrypt = "0.8"
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.78"
aes= "0.8.1"
aes-gcm = "0.9.4"
hkdf = "0.12.2"
sha2 = "0.10.2"
cipher = "0.4.3"
clap = { version = "3.1.18", features = ["derive"] }
log = "0.4.17"
eme-mode = "0.3.1"

View File

@ -11,6 +11,9 @@ pub(crate) struct Args {
pub(crate) enum Commands {
/// Decrypt a file
Decrypt(DecryptCommand),
// List file contained in a directory
Ls(LsCommand),
}
#[derive(Debug, Parser)]
@ -22,6 +25,20 @@ pub(crate) struct DecryptCommand {
#[clap(short('c'), long)]
pub(crate) gocryptfs_conf_path : Option<String>,
/// The password
#[clap(short, long)]
pub(crate) password : Option<String>
}
#[derive(Debug, Parser)]
pub(crate) struct LsCommand {
/// The directory
pub(crate) folder_path : String,
/// Path to the gocryptfs.conf
#[clap(short('c'), long)]
pub(crate) gocryptfs_conf_path : Option<String>,
/// The password
#[clap(short, long)]
pub(crate) password : Option<String>

View File

@ -1,9 +1,9 @@
use aes_gcm::{
aead::generic_array::GenericArray, aes::Aes256, AeadInPlace, NewAead, AesGcm,
};
use cipher::consts::U32;
use aes_gcm::{aead::generic_array::GenericArray, aes::Aes256, AeadInPlace, AesGcm, NewAead};
use cipher::consts::{U16, U32};
use hkdf::Hkdf;
type Aes256Gcm = AesGcm<Aes256, U16>;
pub struct ContentEnc {
key: GenericArray<u8, U32>,
iv_len: usize,
@ -56,7 +56,7 @@ impl ContentEnc {
aad.extend(file_id);
}
let aes = AesGcm::<Aes256, cipher::consts::U16>::new(&self.key);
let aes = Aes256Gcm::new(&self.key);
aes.decrypt_in_place_detached(
GenericArray::from_slice(nonce),
@ -66,6 +66,6 @@ impl ContentEnc {
)
.unwrap();
return Ok(buf);
return Ok(buf.to_vec());
}
}

41
src/filename.rs Normal file
View File

@ -0,0 +1,41 @@
use aes::Aes256;
use cipher::{block_padding::Pkcs7, KeyIvInit};
use eme_mode::DynamicEme;
use hkdf::Hkdf;
pub struct FilenameDecoder {
filename_key: [u8; 32],
}
impl FilenameDecoder {
pub fn new(master_key: &[u8]) -> anyhow::Result<Self> {
let mut key = [0u8; 32];
let hdkf = Hkdf::<sha2::Sha256>::new(None, &master_key);
hdkf.expand(b"EME filename encryption", &mut key).unwrap();
Ok(Self { filename_key: key })
}
pub fn get_decoder_for_dir<'a, 'b>(&'a self, iv: &'b [u8]) -> DirFilenameDecoder<'a, 'b> {
DirFilenameDecoder {
filename_key: &self.filename_key,
iv: iv,
}
}
}
pub struct DirFilenameDecoder<'a, 'b> {
filename_key: &'a [u8],
iv: &'b [u8],
}
impl<'a, 'b> DirFilenameDecoder<'a, 'b> {
pub fn decode_filename(&self, name: &str) -> anyhow::Result<String> {
let cipher = DynamicEme::<Aes256>::new_from_slices(self.filename_key, self.iv)?;
let mut filename = base64::decode_config(name, base64::URL_SAFE)?;
let filename_decoded = cipher.decrypt_padded_mut::<Pkcs7>(&mut filename).unwrap();
Ok(String::from_utf8_lossy(filename_decoded).to_string())
}
}

View File

@ -5,14 +5,17 @@ use std::{
};
use anyhow::Context;
use args::DecryptCommand;
use clap::Parser;
use filename::FilenameDecoder;
use crate::content_enc::ContentEnc;
use args::{DecryptCommand, LsCommand};
mod args;
mod config;
mod content_enc;
mod filename;
fn main() -> anyhow::Result<()> {
let args = args::Args::parse();
@ -20,9 +23,49 @@ fn main() -> anyhow::Result<()> {
match &args.command {
args::Commands::Decrypt(c) => decrypt_file(c),
args::Commands::Ls(c) => ls(c),
}
}
fn ls(c: &LsCommand) -> anyhow::Result<()> {
let folder_path = Path::new(&c.folder_path);
let config_path = c
.gocryptfs_conf_path
.as_ref()
.map(|p| PathBuf::from(p))
.unwrap_or_else(|| folder_path.join("gocryptfs.conf"));
let content = fs::read_to_string(config_path)?;
let conf: config::CryptConf =
serde_json::from_str(&content).context("Failed to decode configuration")?;
let master_key = conf.get_master_key(c.password.as_ref().unwrap().as_bytes())?;
let filename_decoder = FilenameDecoder::new(&master_key)?;
let iv = std::fs::read(folder_path.join("gocryptfs.diriv"))?;
let dir_decoder = filename_decoder.get_decoder_for_dir(&iv);
for dir in std::fs::read_dir(folder_path)?.flat_map(|e| e.ok()) {
let filename = dir.file_name();
let filename = filename.to_str().unwrap();
if filename != "."
&& filename != ".."
&& filename != "gocryptfs.conf"
&& filename != "gocryptfs.diriv"
{
if let Ok(res) = dir_decoder.decode_filename(filename) {
println!("{}", res);
}
}
}
return Ok(());
}
fn decrypt_file(c: &DecryptCommand) -> anyhow::Result<()> {
let file_path = Path::new(&c.file_path);
let config_path = c