Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,17 @@ jobs:
- uses: actions/checkout@v7.0.1
- uses: dtolnay/rust-toolchain@v1
with:
toolchain: v1
toolchain: stable
components: clippy, rustfmt
- uses: Swatinem/rust-cache@v2.9.2
- name: Build
run: cargo build --release
- name: Unit + P2P tests
run: cargo test --all -- --test-threads=1
- name: Clippy
run: cargo clippy --all -- -D warnings || true
run: cargo clippy --all --all-targets -- -D warnings
- name: Format check
run: cargo fmt --all -- --check || true
run: cargo fmt --all -- --check

benchmarks:
name: Criterion Benchmarks
Expand All @@ -39,6 +40,8 @@ jobs:
steps:
- uses: actions/checkout@v7.0.1
- uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
- uses: Swatinem/rust-cache@v2.9.2
- name: Run benchmarks
run: cargo bench -- --output-format bencher 2>/dev/null || echo "Benchmarks completed"
Expand All @@ -50,6 +53,8 @@ jobs:
steps:
- uses: actions/checkout@v7.0.1
- uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
- uses: Swatinem/rust-cache@v2.9.2
- name: Build release
run: cargo build --release
Expand Down Expand Up @@ -89,6 +94,8 @@ jobs:
steps:
- uses: actions/checkout@v7.0.1
- uses: dtolnay/rust-toolchain@v1
with:
toolchain: stable
- name: Install panic-attack
run: cargo install --git https://github.com/hyperpolymath/panic-attacker.git 2>/dev/null || echo "panic-attack unavailable"
- name: Run assail scan
Expand Down
5 changes: 2 additions & 3 deletions benches/januskey_benchmarks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,8 @@ fn bench_transactions(c: &mut Criterion) {

group.bench_function("begin_commit", |b| {
b.iter(|| {
let mut active = false;
// Begin
active = true;
let mut active = true;
black_box(active);
// Commit
active = false;
Expand Down Expand Up @@ -156,7 +155,7 @@ fn bench_key_derivation(c: &mut Criterion) {

for _ in 0..1000 {
let mut hasher = Sha256::new();
hasher.update(&hash);
hasher.update(hash);
hash.copy_from_slice(&hasher.finalize());
}
black_box(hash);
Expand Down
9 changes: 2 additions & 7 deletions crates/januskey-cli/src/attestation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -200,11 +200,7 @@ impl AuditLog {
/// Errors if no attestation key is set: an unkeyed attestation is
/// forgeable and must never be silently produced (the previous
/// `unwrap_or([0u8; 32])` all-zero-key fallback did exactly that).
fn compute_attestation(
&self,
data: &str,
previous_hash: &str,
) -> std::io::Result<String> {
fn compute_attestation(&self, data: &str, previous_hash: &str) -> std::io::Result<String> {
let key = self.attestation_key.ok_or_else(|| {
std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
Expand All @@ -213,8 +209,7 @@ impl AuditLog {
)
})?;

let mut mac = <Hmac<Sha256>>::new_from_slice(&key)
.expect("HMAC accepts keys of any length");
let mut mac = <Hmac<Sha256>>::new_from_slice(&key).map_err(std::io::Error::other)?;
mac.update(Self::ATTESTATION_SCHEME.as_bytes());
mac.update(b"\x00");
mac.update(data.as_bytes());
Expand Down
12 changes: 5 additions & 7 deletions crates/januskey-cli/src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -184,15 +184,14 @@ impl SecretKey {

pub fn generate() -> Result<Self> {
let mut bytes = [0u8; KEY_LENGTH];
rand::thread_rng().fill_bytes(&mut bytes);
rand::rng().fill_bytes(&mut bytes);
Ok(Self { bytes })
}
}

/// Key manager for JanusKey
pub struct KeyManager {
store_path: PathBuf,
root_path: PathBuf,
kek: Option<SecretKey>,
audit_log: AuditLog,
}
Expand All @@ -204,7 +203,6 @@ impl KeyManager {
let audit_log = AuditLog::new(root);
Self {
store_path,
root_path: root.to_path_buf(),
kek: None,
audit_log,
}
Expand All @@ -230,7 +228,7 @@ impl KeyManager {

// Generate salt
let mut salt = [0u8; SALT_LENGTH];
rand::thread_rng().fill_bytes(&mut salt);
rand::rng().fill_bytes(&mut salt);

// Derive KEK from passphrase
let kek = derive_kek(passphrase, &salt)?;
Expand All @@ -246,7 +244,7 @@ impl KeyManager {

// Generate initial nonce
let mut nonce = [0u8; NONCE_LENGTH];
rand::thread_rng().fill_bytes(&mut nonce);
rand::rng().fill_bytes(&mut nonce);

// Create empty key store
let store = KeyStoreData {
Expand Down Expand Up @@ -548,7 +546,7 @@ impl KeyManager {
let path = self.store_path.join("keystore.jks");
let content = ({
use std::io::Read;
std::fs::File::open(&path).and_then(|mut f| {
std::fs::File::open(&path).and_then(|f| {
let mut buf = String::new();
f.take(10 * 1024 * 1024).read_to_string(&mut buf)?;
Ok(buf)
Expand Down Expand Up @@ -607,7 +605,7 @@ fn derive_kek(passphrase: &str, salt: &[u8; SALT_LENGTH]) -> Result<SecretKey> {
/// Wrap (encrypt) key material
fn wrap_key(kek: &SecretKey, key: &[u8], metadata: &KeyMetadata) -> Result<WrappedKey> {
let mut nonce_bytes = [0u8; NONCE_LENGTH];
rand::thread_rng().fill_bytes(&mut nonce_bytes);
rand::rng().fill_bytes(&mut nonce_bytes);

let cipher = Aes256Gcm::new(kek.as_bytes().into());
let nonce = Nonce::from_slice(&nonce_bytes);
Expand Down
19 changes: 7 additions & 12 deletions crates/januskey-cli/src/keys_cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
use clap::{Parser, Subcommand};
use colored::Colorize;
use dialoguer::{Confirm, Password};
use std::path::PathBuf;
use std::path::{Path, PathBuf};
use uuid::Uuid;

mod attestation;
mod keys;
use attestation::AuditEventType;
use januskey::attestation;
use januskey::keys;
use keys::{KeyAlgorithm, KeyManager, KeyPurpose, KeyState};

#[derive(Parser)]
Expand Down Expand Up @@ -293,9 +293,7 @@ fn cmd_generate(
)
.into())
}
_ => {
return Err(format!("Unknown key type: {}. Use: aes256", key_type).into())
}
_ => return Err(format!("Unknown key type: {}. Use: aes256", key_type).into()),
};

let key_purpose = match purpose.to_lowercase().as_str() {
Expand Down Expand Up @@ -447,7 +445,7 @@ fn cmd_revoke(
Ok(())
}

fn cmd_backup(km: &mut KeyManager, output: &PathBuf) -> Result<(), Box<dyn std::error::Error>> {
fn cmd_backup(km: &mut KeyManager, output: &Path) -> Result<(), Box<dyn std::error::Error>> {
unlock_store(km)?;

if output.exists() {
Expand Down Expand Up @@ -567,7 +565,7 @@ fn cmd_audit_show(km: &mut KeyManager, limit: usize) -> Result<(), Box<dyn std::
};

let details = if let Some(ref kd) = entry.key_details {
format!("key:{}", &kd.fingerprint)
format!("key:{}", kd.fingerprint)
} else if let Some(ref reason) = entry.reason {
if reason.len() > 30 {
format!("{}...", &reason[..27])
Expand Down Expand Up @@ -675,10 +673,7 @@ fn cmd_audit_verify(km: &mut KeyManager) -> Result<(), Box<dyn std::error::Error
Ok(())
}

fn cmd_audit_export(
km: &mut KeyManager,
output: &PathBuf,
) -> Result<(), Box<dyn std::error::Error>> {
fn cmd_audit_export(km: &mut KeyManager, output: &Path) -> Result<(), Box<dyn std::error::Error>> {
unlock_store(km)?;

if output.exists() {
Expand Down
6 changes: 3 additions & 3 deletions crates/januskey-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,14 @@ impl Config {
pub fn load(dir: &std::path::Path) -> Self {
let config_path = dir.join(".januskey").join("config.json");
if config_path.exists() {
if let Ok(content) = ({
if let Ok(content) = {
use std::io::Read;
std::fs::File::open(&config_path).and_then(|mut f| {
std::fs::File::open(&config_path).and_then(|f| {
let mut buf = String::new();
f.take(10 * 1024 * 1024).read_to_string(&mut buf)?;
Ok(buf)
})
}) {
} {
if let Ok(config) = serde_json::from_str(&content) {
return config;
}
Expand Down
39 changes: 19 additions & 20 deletions crates/januskey-cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ use januskey::{
transaction::TransactionPreview,
JanusKey,
};
use std::fs;
use std::path::PathBuf;
use std::path::{Path, PathBuf};

#[derive(Parser)]
#[command(
Expand Down Expand Up @@ -217,7 +216,7 @@ fn main() -> Result<()> {
}
}

fn cmd_init(dir: &PathBuf) -> Result<()> {
fn cmd_init(dir: &Path) -> Result<()> {
if JanusKey::is_initialized(dir) {
println!(
"{} JanusKey already initialized in {}",
Expand All @@ -239,7 +238,7 @@ fn cmd_init(dir: &PathBuf) -> Result<()> {
}

fn cmd_delete(
dir: &PathBuf,
dir: &Path,
paths: &[String],
recursive: bool,
dry_run: bool,
Expand Down Expand Up @@ -360,7 +359,7 @@ fn cmd_delete(
}

fn cmd_modify(
dir: &PathBuf,
dir: &Path,
pattern: &str,
paths: &[String],
dry_run: bool,
Expand Down Expand Up @@ -394,7 +393,7 @@ fn cmd_modify(
for file in &files {
let content = ({
use std::io::Read;
std::fs::File::open(file).and_then(|mut f| {
std::fs::File::open(file).and_then(|f| {
let mut buf = String::new();
f.take(10 * 1024 * 1024).read_to_string(&mut buf)?;
Ok(buf)
Expand Down Expand Up @@ -480,12 +479,12 @@ fn parse_sed_pattern(pattern: &str) -> Result<(String, String, bool)> {

let search = parts[0].to_string();
let replace = parts[1].to_string();
let global = parts.get(2).map_or(false, |f| f.contains('g'));
let global = parts.get(2).is_some_and(|f| f.contains('g'));

Ok((search, replace, global))
}

fn cmd_move(dir: &PathBuf, source: &str, destination: &PathBuf, dry_run: bool) -> Result<()> {
fn cmd_move(dir: &Path, source: &str, destination: &PathBuf, dry_run: bool) -> Result<()> {
let mut jk = JanusKey::open(dir).context("Failed to open JanusKey directory")?;

let source_path = if PathBuf::from(source).is_absolute() {
Expand Down Expand Up @@ -536,7 +535,7 @@ fn cmd_move(dir: &PathBuf, source: &str, destination: &PathBuf, dry_run: bool) -
Ok(())
}

fn cmd_copy(dir: &PathBuf, source: &PathBuf, destination: &PathBuf, dry_run: bool) -> Result<()> {
fn cmd_copy(dir: &Path, source: &PathBuf, destination: &PathBuf, dry_run: bool) -> Result<()> {
let mut jk = JanusKey::open(dir).context("Failed to open JanusKey directory")?;

let source_path = if source.is_absolute() {
Expand Down Expand Up @@ -592,7 +591,7 @@ fn cmd_copy(dir: &PathBuf, source: &PathBuf, destination: &PathBuf, dry_run: boo
/// purge its operation-log entries (recording each shred in
/// `.januskey/obliterations.json`); otherwise only the working files are
/// shredded.
fn cmd_obliterate(dir: &PathBuf, paths: &[PathBuf], dry_run: bool, auto_yes: bool) -> Result<()> {
fn cmd_obliterate(dir: &Path, paths: &[PathBuf], dry_run: bool, auto_yes: bool) -> Result<()> {
use januskey::obliteration::{
obliterate_file, obliterate_path, ObliterationManager, OBLITERATION_LOG_FILE,
};
Expand Down Expand Up @@ -713,7 +712,7 @@ fn cmd_obliterate(dir: &PathBuf, paths: &[PathBuf], dry_run: bool, auto_yes: boo
Ok(())
}

fn cmd_undo(dir: &PathBuf, count: usize, id: Option<String>) -> Result<()> {
fn cmd_undo(dir: &Path, count: usize, id: Option<String>) -> Result<()> {
let mut jk = JanusKey::open(dir).context("Failed to open JanusKey directory")?;

if let Some(op_id) = id {
Expand Down Expand Up @@ -767,7 +766,7 @@ fn cmd_undo(dir: &PathBuf, count: usize, id: Option<String>) -> Result<()> {
Ok(())
}

fn cmd_begin(dir: &PathBuf, name: Option<String>) -> Result<()> {
fn cmd_begin(dir: &Path, name: Option<String>) -> Result<()> {
let mut jk = JanusKey::open(dir).context("Failed to open JanusKey directory")?;

let tx = jk.transaction_manager.begin(name.clone())?;
Expand All @@ -786,7 +785,7 @@ fn cmd_begin(dir: &PathBuf, name: Option<String>) -> Result<()> {
Ok(())
}

fn cmd_commit(dir: &PathBuf) -> Result<()> {
fn cmd_commit(dir: &Path) -> Result<()> {
let mut jk = JanusKey::open(dir).context("Failed to open JanusKey directory")?;

let tx = jk.transaction_manager.commit()?;
Expand All @@ -801,7 +800,7 @@ fn cmd_commit(dir: &PathBuf) -> Result<()> {
Ok(())
}

fn cmd_rollback(dir: &PathBuf) -> Result<()> {
fn cmd_rollback(dir: &Path) -> Result<()> {
let mut jk = JanusKey::open(dir).context("Failed to open JanusKey directory")?;

// Get the active transaction's operation IDs before modifying state
Expand Down Expand Up @@ -830,7 +829,7 @@ fn cmd_rollback(dir: &PathBuf) -> Result<()> {
Ok(())
}

fn cmd_preview(dir: &PathBuf) -> Result<()> {
fn cmd_preview(dir: &Path) -> Result<()> {
let jk = JanusKey::open(dir).context("Failed to open JanusKey directory")?;

let tx = jk
Expand All @@ -843,7 +842,7 @@ fn cmd_preview(dir: &PathBuf) -> Result<()> {
let name = preview
.transaction_name
.unwrap_or_else(|| tx.id[..8].to_string());
println!("{} Transaction: {}", "📋".to_string(), name.cyan());
println!("📋 Transaction: {}", name.cyan());
println!("Operations pending: {}", preview.operations.len());
println!();

Expand Down Expand Up @@ -887,7 +886,7 @@ fn cmd_preview(dir: &PathBuf) -> Result<()> {
Ok(())
}

fn cmd_history(dir: &PathBuf, limit: usize, filter: Option<String>) -> Result<()> {
fn cmd_history(dir: &Path, limit: usize, filter: Option<String>) -> Result<()> {
let jk = JanusKey::open(dir).context("Failed to open JanusKey directory")?;

let ops: Vec<_> = if let Some(ref filter_str) = filter {
Expand Down Expand Up @@ -949,7 +948,7 @@ fn cmd_history(dir: &PathBuf, limit: usize, filter: Option<String>) -> Result<()
Ok(())
}

fn cmd_status(dir: &PathBuf) -> Result<()> {
fn cmd_status(dir: &Path) -> Result<()> {
let jk = JanusKey::open(dir).context("Failed to open JanusKey directory")?;

println!("{}", "JanusKey Status".bold());
Expand All @@ -965,7 +964,7 @@ fn cmd_status(dir: &PathBuf) -> Result<()> {
if let Some(tx) = jk.transaction_manager.active() {
let name = tx.name.clone().unwrap_or_else(|| tx.id[..8].to_string());
println!();
println!("{} Active transaction: {}", "📝".to_string(), name.cyan());
println!("📝 Active transaction: {}", name.cyan());
println!(" Started: {}", tx.started_at.format("%Y-%m-%d %H:%M:%S"));
println!(" Operations: {}", tx.operation_ids.len());
} else {
Expand All @@ -976,7 +975,7 @@ fn cmd_status(dir: &PathBuf) -> Result<()> {
Ok(())
}

fn cmd_gc(dir: &PathBuf, keep: Option<usize>, _older_than: Option<u32>) -> Result<()> {
fn cmd_gc(dir: &Path, keep: Option<usize>, _older_than: Option<u32>) -> Result<()> {
let mut jk = JanusKey::open(dir).context("Failed to open JanusKey directory")?;

let keep_count = keep.unwrap_or(jk.config.max_history);
Expand Down
Loading
Loading