Add basic filename decoding support
This commit is contained in:
parent
3b0fbb2e38
commit
55899706c0
|
@ -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",
|
||||
|
|
|
@ -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"
|
17
src/args.rs
17
src/args.rs
|
@ -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>
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
}
|
45
src/main.rs
45
src/main.rs
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue