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
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 5 additions & 8 deletions crates/auths-cli/src/bin/sign.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
#![allow(
clippy::print_stdout,
clippy::print_stderr,
clippy::disallowed_methods,
clippy::exit
)]
#![allow(clippy::print_stdout, clippy::print_stderr, clippy::exit)]
//! auths-sign: Git SSH signing program compatible with `gpg.ssh.program`
//!
//! Git calls this binary with ssh-keygen compatible arguments:
Expand Down Expand Up @@ -266,8 +261,10 @@ fn run_sign(args: &Args) -> Result<()> {
params = params.with_repo_path(path);
}

let signature_pem = CommitSigningWorkflow::execute(&ctx, params, chrono::Utc::now())
.map_err(anyhow::Error::new)?;
#[allow(clippy::disallowed_methods)]
let now = chrono::Utc::now();
let signature_pem =
CommitSigningWorkflow::execute(&ctx, params, now).map_err(anyhow::Error::new)?;

let sig_path = format!("{}.sig", buffer_file.display());
fs::write(&sig_path, &signature_pem)
Expand Down
7 changes: 1 addition & 6 deletions crates/auths-cli/src/bin/verify.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,4 @@
#![allow(
clippy::print_stdout,
clippy::print_stderr,
clippy::disallowed_methods,
clippy::exit
)]
#![allow(clippy::print_stdout, clippy::print_stderr, clippy::exit)]
//! auths-verify: SSH signature verification for Auths identities
//!
//! Supports two modes:
Expand Down
3 changes: 2 additions & 1 deletion crates/auths-cli/src/commands/artifact/publish.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ use auths_infra_http::HttpRegistryClient;
use auths_sdk::workflows::artifact::{
ArtifactPublishConfig, ArtifactPublishError, publish_artifact,
};
use auths_verifier::core::ResourceId;
use serde::Serialize;

use crate::ux::format::{JsonResponse, Output, is_json_mode};

#[derive(Serialize)]
struct PublishJsonResponse {
attestation_rid: String,
attestation_rid: ResourceId,
registry: String,
package_name: Option<String>,
signer_did: String,
Expand Down
9 changes: 5 additions & 4 deletions crates/auths-cli/src/commands/artifact/verify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
use auths_verifier::core::Attestation;
use auths_verifier::witness::{WitnessQuorum, WitnessReceipt, WitnessVerifyConfig};
use auths_verifier::{
Capability, IdentityBundle, IdentityDID, VerificationReport, verify_chain,
CanonicalDid, Capability, IdentityBundle, VerificationReport, verify_chain,
verify_chain_with_capability, verify_chain_with_witnesses,
};

Expand Down Expand Up @@ -209,14 +209,15 @@ pub async fn handle_verify(
fn resolve_identity_key(
identity_bundle: &Option<PathBuf>,
attestation: &Attestation,
) -> Result<(Vec<u8>, IdentityDID)> {
) -> Result<(Vec<u8>, CanonicalDid)> {
if let Some(bundle_path) = identity_bundle {
let bundle_content = fs::read_to_string(bundle_path)
.with_context(|| format!("Failed to read identity bundle: {:?}", bundle_path))?;
let bundle: IdentityBundle = serde_json::from_str(&bundle_content)
.with_context(|| format!("Failed to parse identity bundle: {:?}", bundle_path))?;
let pk = hex::decode(&bundle.public_key_hex).context("Invalid public key hex in bundle")?;
Ok((pk, bundle.identity_did))
let pk = hex::decode(bundle.public_key_hex.as_str())
.context("Invalid public key hex in bundle")?;
Ok((pk, bundle.identity_did.into()))
} else {
// Resolve public key from the issuer DID
let issuer = &attestation.issuer;
Expand Down
4 changes: 3 additions & 1 deletion crates/auths-cli/src/commands/audit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,9 @@ pub struct AuditReport {
}

/// Handle the audit command.
#[allow(clippy::disallowed_methods)]
pub fn handle_audit(cmd: AuditCommand) -> Result<()> {
let now = chrono::Utc::now();
let out = Output::new();

let since = cmd.since.as_ref().map(|s| parse_date_arg(s)).transpose()?;
Expand All @@ -126,7 +128,7 @@ pub fn handle_audit(cmd: AuditCommand) -> Result<()> {

let summary = summarize_commits(&commits);
let unsigned_commits = summary.unsigned_commits;
let generated_at = chrono::Utc::now().to_rfc3339();
let generated_at = now.to_rfc3339();
let repository = cmd.repo.display().to_string();

let output = match cmd.format {
Expand Down
20 changes: 10 additions & 10 deletions crates/auths-cli/src/commands/device/authorization.rs
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ pub fn handle_device(
passphrase_provider: Arc<dyn PassphraseProvider + Send + Sync>,
env_config: &EnvironmentConfig,
) -> Result<()> {
#[allow(clippy::disallowed_methods)]
let now = Utc::now();
let repo_path = layout::resolve_repo_path(repo_opt)?;

let mut config = StorageLayoutConfig::default();
Expand All @@ -220,7 +222,7 @@ pub fn handle_device(

match cmd.command {
DeviceSubcommand::List { include_revoked } => {
list_devices(&repo_path, &config, include_revoked)
list_devices(now, &repo_path, &config, include_revoked)
}
DeviceSubcommand::Resolve { device_did } => resolve_device(&repo_path, &device_did),
DeviceSubcommand::Pair(pair_cmd) => super::pair::handle_pair(pair_cmd, env_config),
Expand Down Expand Up @@ -433,7 +435,8 @@ fn handle_extend(
) -> Result<()> {
let config = auths_sdk::types::DeviceExtensionConfig {
repo_path: repo_path.to_path_buf(),
device_did: device_did.to_string(),
#[allow(clippy::disallowed_methods)] // INVARIANT: device_did from CLI arg validated upstream
device_did: auths_verifier::types::DeviceDID::new_unchecked(device_did),
days: days as u32,
identity_key_alias: KeyAlias::new_unchecked(identity_key_alias),
device_key_alias: Some(KeyAlias::new_unchecked(device_key_alias)),
Expand Down Expand Up @@ -471,6 +474,7 @@ fn resolve_device(repo_path: &Path, device_did_str: &str) -> Result<()> {
}

fn list_devices(
now: chrono::DateTime<Utc>,
repo_path: &Path,
_config: &StorageLayoutConfig,
include_revoked: bool,
Expand Down Expand Up @@ -499,19 +503,15 @@ fn list_devices(
.last()
.expect("Grouped attestations should not be empty");

let verification_result = auths_id::attestation::verify::verify_with_resolver(
chrono::Utc::now(),
&resolver,
latest,
None,
);
let verification_result =
auths_id::attestation::verify::verify_with_resolver(now, &resolver, latest, None);

let status_string = match verification_result {
Ok(()) => {
if latest.is_revoked() {
"revoked".to_string()
} else if let Some(expiry) = latest.expires_at {
if Utc::now() > expiry {
if now > expiry {
"expired".to_string()
} else {
format!("active (expires {})", expiry.date_naive())
Expand Down Expand Up @@ -544,7 +544,7 @@ fn list_devices(
}
};

let is_inactive = latest.is_revoked() || latest.expires_at.is_some_and(|e| Utc::now() > e);
let is_inactive = latest.is_revoked() || latest.expires_at.is_some_and(|e| now > e);
if !include_revoked && is_inactive {
continue;
}
Expand Down
17 changes: 12 additions & 5 deletions crates/auths-cli/src/commands/device/pair/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ pub(crate) fn display_sas_mismatch_warning() {

/// Handle a successful pairing response — verify signature, complete ECDH, create attestation.
pub(crate) fn handle_pairing_response(
now: chrono::DateTime<chrono::Utc>,
session: &mut PairingSession,
response: SubmitResponseRequest,
auths_dir: &Path,
Expand Down Expand Up @@ -229,7 +230,7 @@ pub(crate) fn handle_pairing_response(
style("No local identity found at ~/.auths").yellow()
);
println!(" Run 'auths init' first to create an identity.");
save_device_info(auths_dir, &response)?;
save_device_info(now, auths_dir, &response)?;
return Ok(());
}

Expand Down Expand Up @@ -277,7 +278,8 @@ pub(crate) fn handle_pairing_response(
let decrypted = DecryptedPairingResponse {
auths_dir: auths_dir.to_path_buf(),
device_pubkey: device_signing_bytes,
device_did: response.device_did.to_string(),
#[allow(clippy::disallowed_methods)] // INVARIANT: device_did from pairing protocol response
device_did: auths_verifier::types::DeviceDID::new_unchecked(response.device_did.to_string()),
device_name: response.device_name.clone(),
capabilities: capabilities.to_vec(),
identity_key_alias,
Expand Down Expand Up @@ -324,15 +326,19 @@ pub(crate) fn handle_pairing_response(
" {}",
style(format!("auths device link --device-did {} ...", device_did)).dim()
);
save_device_info(auths_dir, &response)?;
save_device_info(now, auths_dir, &response)?;
}
}

Ok(())
}

/// Save device info as a JSON file (fallback when attestation creation fails).
pub(crate) fn save_device_info(auths_dir: &Path, response: &SubmitResponseRequest) -> Result<()> {
pub(crate) fn save_device_info(
now: chrono::DateTime<chrono::Utc>,
auths_dir: &Path,
response: &SubmitResponseRequest,
) -> Result<()> {
let devices_dir = auths_dir.join("devices");
create_restricted_dir(&devices_dir)?;

Expand All @@ -345,7 +351,7 @@ pub(crate) fn save_device_info(auths_dir: &Path, response: &SubmitResponseReques
"signing_pubkey": response.device_signing_pubkey.as_str(),
"x25519_pubkey": response.device_x25519_pubkey.as_str(),
"name": response.device_name,
"paired_at": chrono::Utc::now().to_rfc3339(),
"paired_at": now.to_rfc3339(),
});

write_sensitive_file(&device_file, serde_json::to_string_pretty(&device_info)?)?;
Expand All @@ -359,6 +365,7 @@ pub(crate) fn save_device_info(auths_dir: &Path, response: &SubmitResponseReques
}

/// Get the hostname of this machine for device naming.
#[allow(clippy::disallowed_methods)] // CLI boundary: hostname from env
pub(crate) fn hostname() -> String {
std::env::var("HOSTNAME")
.or_else(|_| std::env::var("HOST"))
Expand Down
9 changes: 4 additions & 5 deletions crates/auths-cli/src/commands/device/pair/join.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use auths_core::ports::pairing::PairingRelayClient;
use auths_infra_http::HttpPairingRelayClient;
use auths_pairing_protocol::sas;
use auths_sdk::pairing::{load_device_signing_material, validate_short_code};
use chrono::Utc;
use console::style;

use crate::core::provider::CliPassphraseProvider;
Expand All @@ -18,6 +17,7 @@ use super::common::*;

/// Join an existing pairing session using a short code.
pub(crate) async fn handle_join(
now: chrono::DateTime<chrono::Utc>,
code: &str,
registry: &str,
env_config: &EnvironmentConfig,
Expand Down Expand Up @@ -83,20 +83,19 @@ pub(crate) async fn handle_join(
endpoint: registry.to_string(),
short_code: normalized.clone(),
ephemeral_pubkey: token_data.ephemeral_pubkey.to_string(),
expires_at: chrono::DateTime::from_timestamp(token_data.expires_at, 0)
.unwrap_or_else(Utc::now),
expires_at: chrono::DateTime::from_timestamp(token_data.expires_at, 0).unwrap_or(now),
capabilities: token_data.capabilities.clone(),
};

if token.is_expired(Utc::now()) {
if token.is_expired(now) {
anyhow::bail!("Session expired");
}

let create_spinner = create_wait_spinner(&format!("{GEAR}Creating pairing response..."));

// Create the response + ECDH
let (pairing_response, shared_secret) = PairingResponse::create(
Utc::now(),
now,
&token,
&material.seed,
&material.public_key,
Expand Down
12 changes: 9 additions & 3 deletions crates/auths-cli/src/commands/device/pair/lan.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use super::lan_server::{LanPairingServer, detect_lan_ip};
/// 6. Wait for response
/// 7. Verify + create attestation
pub async fn handle_initiate_lan(
now: chrono::DateTime<chrono::Utc>,
no_qr: bool,
no_mdns: bool,
expiry_secs: u64,
Expand All @@ -46,7 +47,7 @@ pub async fn handle_initiate_lan(
// Generate a session token with a placeholder endpoint — we'll update it after
// the server starts and we know the actual port.
let mut session = PairingToken::generate_with_expiry(
chrono::Utc::now(),
now,
controller_did.clone(),
"http://placeholder".to_string(), // replaced below
capabilities.to_vec(),
Expand Down Expand Up @@ -186,6 +187,7 @@ pub async fn handle_initiate_lan(
}

handle_pairing_response(
now,
&mut session,
response_data,
&auths_dir,
Expand All @@ -212,7 +214,11 @@ pub async fn handle_initiate_lan(
}

/// Join a LAN pairing session by discovering it via mDNS.
pub async fn handle_join_lan(code: &str, env_config: &EnvironmentConfig) -> Result<()> {
pub async fn handle_join_lan(
now: chrono::DateTime<chrono::Utc>,
code: &str,
env_config: &EnvironmentConfig,
) -> Result<()> {
use auths_core::pairing::normalize_short_code;

let normalized = normalize_short_code(code);
Expand Down Expand Up @@ -254,5 +260,5 @@ pub async fn handle_join_lan(code: &str, env_config: &EnvironmentConfig) -> Resu
let registry = format!("http://{}", addr);

// Delegate to the standard join flow
super::join::handle_join(&normalized, &registry, env_config).await
super::join::handle_join(now, &normalized, &registry, env_config).await
}
Loading
Loading