Signing and Verification¶
Signing¶
SecureSigner Trait¶
The SecureSigner trait (defined in auths-core) abstracts key-based signing operations. It handles loading encrypted keys, obtaining passphrases, decrypting, signing, and secure cleanup.
// auths_core::signing::SecureSigner
pub trait SecureSigner: Send + Sync {
fn sign_with_alias(
&self,
alias: &KeyAlias,
passphrase_provider: &dyn PassphraseProvider,
message: &[u8],
) -> Result<Vec<u8>, AgentError>;
fn sign_for_identity(
&self,
identity_did: &IdentityDID,
passphrase_provider: &dyn PassphraseProvider,
message: &[u8],
) -> Result<Vec<u8>, AgentError>;
}
sign_with_alias loads the encrypted key by alias, calls the passphrase provider (up to 3 attempts on incorrect passphrase), decrypts, and signs. Returns raw Ed25519 signature bytes.
sign_for_identity resolves the identity DID to an alias via list_aliases_for_identity(), then delegates to sign_with_alias.
StorageSigner¶
StorageSigner<S: KeyStorage> is the standard concrete implementation that backs SecureSigner with any KeyStorage backend:
use auths_core::signing::StorageSigner;
let signer = StorageSigner::new(Arc::clone(&keychain));
let signature = signer.sign_with_alias(&alias, &passphrase_provider, message)?;
PassphraseProvider Variants¶
| Type | Usage |
|---|---|
PrefilledPassphraseProvider |
CI, tests, headless -- returns a fixed passphrase |
CallbackPassphraseProvider |
GUI, FFI -- delegates to a callback function |
CachedPassphraseProvider |
Agent sessions -- caches with configurable TTL |
UnifiedPassphraseProvider |
Multi-key operations -- prompts once for all keys |
use auths_core::signing::PrefilledPassphraseProvider;
// Headless / CI
let provider = PrefilledPassphraseProvider::new("my-secret");
// Callback-based (GUI/FFI)
use auths_core::signing::CallbackPassphraseProvider;
let provider = CallbackPassphraseProvider::new(|prompt| {
Ok(zeroize::Zeroizing::new("user-entered-passphrase".to_string()))
});
// Cached with TTL
use auths_core::signing::CachedPassphraseProvider;
let cached = CachedPassphraseProvider::new(
Arc::new(inner_provider),
std::time::Duration::from_secs(300),
);
// Unified (prompts once for all keys in a multi-key operation)
use auths_core::signing::UnifiedPassphraseProvider;
let unified = UnifiedPassphraseProvider::new(Arc::new(inner_provider));
SSHSIG Commit Signing Pipeline¶
The auths_sdk::signing module provides the commit signing pipeline:
use auths_sdk::signing::{validate_freeze_state, construct_signature_payload, sign_with_seed};
// 1. Check that the identity is not frozen
validate_freeze_state(&repo_path, clock.now())?;
// 2. Construct the SSHSIG signed-data payload
let payload = construct_signature_payload(data, "git")?;
// 3. Create a complete SSHSIG PEM signature
let pem = sign_with_seed(&seed, data, "git")?;
SigningError¶
pub enum SigningError {
IdentityFrozen(String),
KeyResolution(String),
SigningFailed(String),
InvalidPassphrase,
PemEncoding(String),
}
Artifact Attestation Signing¶
For signing release artifacts with dual-signed attestations:
use auths_sdk::signing::{
sign_artifact_attestation, ArtifactSigningParams, SigningKeyMaterial,
};
let params = ArtifactSigningParams {
artifact: Arc::new(my_artifact_source),
identity_key: Some(SigningKeyMaterial::Alias(KeyAlias::new("my-key")?)),
device_key: SigningKeyMaterial::Direct(my_seed),
expires_in: Some(31_536_000),
note: Some("v1.0.0 release".into()),
commit_sha: None,
};
let result = sign_artifact_attestation(params, &ctx)?;
println!("Attestation JSON: {}", result.attestation_json);
println!("RID: {}", result.rid); // e.g. "sha256:abc123..."
println!("Digest: {}", result.digest); // hex-encoded SHA-256
SigningKeyMaterial selects how a key is supplied:
| Variant | Behavior |
|---|---|
Alias(KeyAlias) |
Resolves the key from the platform keychain at call time |
Direct(SecureSeed) |
Injects a raw Ed25519 seed, bypassing the keychain (for headless CI) |
Verification¶
auths-verifier Crate¶
auths-verifier is the standalone verification library with minimal dependencies, designed for embedding in servers, WASM, and C-FFI contexts.
Free Functions (async, native feature)¶
These are the primary verification entry points, available with the native feature flag (enabled by default):
use auths_verifier::{
verify_chain, verify_with_keys, verify_with_capability,
verify_chain_with_capability, verify_at_time,
verify_chain_with_witnesses, verify_device_authorization,
};
verify_chain¶
Verifies an ordered attestation chain starting from a known root public key.
pub async fn verify_chain(
attestations: &[Attestation],
root_pk: &[u8],
) -> Result<VerificationReport, AttestationError>
verify_with_keys¶
Verifies a single attestation's signatures against the issuer's public key.
pub async fn verify_with_keys(
att: &Attestation,
issuer_pk_bytes: &[u8],
) -> Result<VerifiedAttestation, AttestationError>
verify_with_capability¶
Verifies a single attestation and checks that it grants a required capability.
pub async fn verify_with_capability(
att: &Attestation,
required: &Capability,
issuer_pk_bytes: &[u8],
) -> Result<VerifiedAttestation, AttestationError>
verify_chain_with_capability¶
Verifies an entire chain and asserts that all attestations share a required capability.
pub async fn verify_chain_with_capability(
attestations: &[Attestation],
required: &Capability,
root_pk: &[u8],
) -> Result<VerificationReport, AttestationError>
verify_at_time¶
Verifies a single attestation against a specific point in time (for historical checks).
pub async fn verify_at_time(
att: &Attestation,
issuer_pk_bytes: &[u8],
at: DateTime<Utc>,
) -> Result<VerifiedAttestation, AttestationError>
verify_chain_with_witnesses¶
Verifies a chain and validates witness receipts against a quorum threshold.
pub async fn verify_chain_with_witnesses(
attestations: &[Attestation],
root_pk: &[u8],
witness_config: &WitnessVerifyConfig<'_>,
) -> Result<VerificationReport, AttestationError>
verify_device_authorization¶
Verifies that a specific device is authorized under a given identity.
pub async fn verify_device_authorization(
identity_did: &str,
device_did: &DeviceDID,
attestations: &[Attestation],
identity_pk: &[u8],
) -> Result<VerificationReport, AttestationError>
Sync Utility Functions (always available)¶
These functions are available on all targets including WASM:
did_to_ed25519¶
Resolves a did:key:z... DID to raw Ed25519 public key bytes.
Note: KERI DIDs (did:keri:) contain opaque SAIDs and require external key state resolution.
is_device_listed¶
Checks if a device appears in a list of already-verified attestations.
pub fn is_device_listed(
identity_did: &str,
device_did: &DeviceDID,
attestations: &[VerifiedAttestation],
now: DateTime<Utc>,
) -> bool
Verifier Struct¶
For more control, use Verifier directly with a custom CryptoProvider and ClockProvider:
use auths_verifier::Verifier;
use auths_crypto::RingCryptoProvider;
use auths_verifier::clock::SystemClock;
let verifier = Verifier::new(
Arc::new(RingCryptoProvider),
Arc::new(SystemClock),
);
let report = verifier.verify_chain(&attestations, &root_pk).await?;
VerificationReport¶
The result of chain verification:
pub struct VerificationReport {
pub status: VerificationStatus,
pub chain: Vec<ChainLink>,
pub warnings: Vec<String>,
pub witness_quorum: Option<WitnessQuorum>,
}
impl VerificationReport {
pub fn is_valid(&self) -> bool { ... }
}
VerificationStatus¶
pub enum VerificationStatus {
Valid,
Expired { at: DateTime<Utc> },
Revoked { at: Option<DateTime<Utc>> },
InvalidSignature { step: usize },
BrokenChain { missing_link: String },
InsufficientWitnesses { required: usize, verified: usize },
}
ChainLink¶
pub struct ChainLink {
pub issuer: String,
pub subject: String,
pub valid: bool,
pub error: Option<String>,
}
Verification Checks¶
Each attestation undergoes these checks in order:
- Revocation -- rejected if
revoked_at <= reference_time - Expiration -- rejected if
reference_time > expires_at - Timestamp skew -- rejected if
timestamp > reference_time + 5 minutes - Issuer public key length -- must be 32 bytes for Ed25519
- Issuer signature -- verified against canonical attestation data
- Device signature -- verified against canonical attestation data
For chain verification, the issuer of each link (after the root) must match the subject of the preceding link. The root attestation is verified against the provided root public key; subsequent links are verified against the device public key from the previous attestation.