Skip to content

Commit 4cf20c3

Browse files
committed
fix: correct e2e tests
1 parent afca667 commit 4cf20c3

19 files changed

Lines changed: 586 additions & 367 deletions

File tree

.auths/allowed_signers

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
11
# auths:managed — do not edit manually
22
# auths:attestation
3+
<<<<<<< Updated upstream
34
zDnaeTDAGwQd8YFykWwyeQEQC8hrHHWbeb9AsoJanKqheTQ9g@auths.local namespaces="git" ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBClLNRlBdgjEEPozFdM4rZh556aLyLCJLj77b+Ru5ACTaqMmXLuRlUWkonba8LKP2NKBWNme+4+tRLYngOaDDxo=
5+
=======
6+
zDnaeQaiejhv26gSRpcw2GuXyFwnBsg5d4LnEYmwswkL6xqNq@auths.local namespaces="git" ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBAI983sl/v/wrXA3Eh6z1pbEUrSISl90Ydt6pagriWA6af/KRqhnahp5ZfUFLDxBNRRLHj8y/aWvWO9NqCQWRXI=
7+
>>>>>>> Stashed changes
48
# auths:manual

.pre-commit-config.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,14 @@ repos:
5151

5252
- id: cargo-clippy
5353
name: cargo clippy
54-
entry: cargo clippy --all-targets --all-features -- -D warnings
54+
entry: cargo clippy --all-targets --all-features --keep-going -- -D warnings
5555
language: system
5656
types: [rust]
5757
pass_filenames: false
5858

5959
- id: cargo-clippy-packages
6060
name: cargo clippy (packages/)
61-
entry: bash -c 'for d in packages/auths-node packages/auths-python packages/auths-verifier-swift; do [ -f "$d/Cargo.toml" ] && CARGO_TARGET_DIR=../../target cargo clippy --manifest-path "$d/Cargo.toml" --all-targets -- -D warnings || exit 1; done'
61+
entry: bash -c 'failed=0; for d in packages/auths-node packages/auths-python packages/auths-verifier-swift; do [ -f "$d/Cargo.toml" ] || continue; CARGO_TARGET_DIR=../../target cargo clippy --manifest-path "$d/Cargo.toml" --all-targets --keep-going -- -D warnings || failed=1; done; exit $failed'
6262
language: system
6363
types: [rust]
6464
pass_filenames: false

crates/auths-cli/src/commands/artifact/verify.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -310,20 +310,26 @@ pub async fn handle_verify(
310310
fn resolve_identity_key(
311311
identity_bundle: &Option<PathBuf>,
312312
attestation: &Attestation,
313-
) -> Result<(Vec<u8>, CanonicalDid)> {
313+
) -> Result<(auths_verifier::DevicePublicKey, CanonicalDid)> {
314314
if let Some(bundle_path) = identity_bundle {
315315
let bundle_content = fs::read_to_string(bundle_path)
316316
.with_context(|| format!("Failed to read identity bundle: {:?}", bundle_path))?;
317317
let bundle: IdentityBundle = serde_json::from_str(&bundle_content)
318318
.with_context(|| format!("Failed to parse identity bundle: {:?}", bundle_path))?;
319-
let pk = hex::decode(bundle.public_key_hex.as_str())
319+
let pk_bytes = hex::decode(bundle.public_key_hex.as_str())
320320
.context("Invalid public key hex in bundle")?;
321+
let curve = auths_crypto::CurveType::from_public_key_len(pk_bytes.len())
322+
.ok_or_else(|| anyhow!("Invalid bundle public key length: {}", pk_bytes.len()))?;
323+
let pk = auths_verifier::DevicePublicKey::try_new(curve, &pk_bytes)
324+
.map_err(|e| anyhow!("Invalid bundle public key: {e}"))?;
321325
Ok((pk, bundle.identity_did.into()))
322326
} else {
323327
// Resolve public key from the issuer DID
324328
let issuer = &attestation.issuer;
325-
let (pk, _curve) = resolve_pk_from_did(issuer)
329+
let (pk_bytes, curve) = resolve_pk_from_did(issuer)
326330
.with_context(|| format!("Failed to resolve public key from issuer DID '{}'. Use --identity-bundle for stateless verification.", issuer))?;
331+
let pk = auths_verifier::DevicePublicKey::try_new(curve, &pk_bytes)
332+
.map_err(|e| anyhow!("Invalid issuer public key resolved from DID: {e}"))?;
327333
Ok((pk, issuer.clone()))
328334
}
329335
}
@@ -357,7 +363,7 @@ fn resolve_pk_from_did(did: &str) -> Result<(Vec<u8>, auths_crypto::CurveType)>
357363
/// Verify witness receipts if provided.
358364
async fn verify_witnesses(
359365
chain: &[Attestation],
360-
root_pk: &[u8],
366+
root_pk: &auths_verifier::DevicePublicKey,
361367
receipts_path: &Option<PathBuf>,
362368
witness_keys_raw: &[String],
363369
threshold: usize,

crates/auths-cli/src/commands/device/verify_attestation.rs

Lines changed: 30 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -165,36 +165,38 @@ fn effective_trust_policy(cmd: &VerifyCommand) -> TrustPolicy {
165165
}
166166
}
167167

168+
/// Wrap raw pubkey bytes from a trust store (pin or roots.json) into a curve-tagged
169+
/// `DevicePublicKey`. Infers the curve from length via `CurveType::from_public_key_len`.
170+
fn bytes_to_device_public_key(
171+
bytes: &[u8],
172+
source: &str,
173+
) -> Result<auths_verifier::DevicePublicKey> {
174+
let curve = auths_crypto::CurveType::from_public_key_len(bytes.len())
175+
.ok_or_else(|| anyhow!("Invalid {} public key length: {}", source, bytes.len()))?;
176+
auths_verifier::DevicePublicKey::try_new(curve, bytes)
177+
.map_err(|e| anyhow!("Invalid {} public key: {e}", source))
178+
}
179+
168180
/// Resolve the issuer public key from various sources.
169181
///
170182
/// Resolution precedence:
171-
/// 1. --issuer-pk (direct key, bypasses trust)
183+
/// 1. `--issuer-pk` (direct key, bypasses trust)
172184
/// 2. Pinned identity store
173-
/// 3. roots.json file
185+
/// 3. `roots.json` file
174186
/// 4. Trust policy (TOFU prompt or explicit rejection)
175187
fn resolve_issuer_key(
176188
now: chrono::DateTime<Utc>,
177189
cmd: &VerifyCommand,
178190
att: &Attestation,
179-
) -> Result<Vec<u8>> {
191+
) -> Result<auths_verifier::DevicePublicKey> {
180192
// 1. Direct key takes precedence
181193
if let Some(ref pk_hex) = cmd.issuer_pk {
182194
let pk_bytes =
183195
hex::decode(pk_hex).context("Invalid hex string provided for issuer public key")?;
184-
let curve = match pk_bytes.len() {
185-
32 => auths_crypto::CurveType::Ed25519,
186-
33 | 65 => auths_crypto::CurveType::P256,
187-
_ => {
188-
return Err(anyhow!(
189-
"Invalid issuer public key length: {}",
190-
pk_bytes.len()
191-
));
192-
}
193-
};
194-
// Validate via DevicePublicKey type system
195-
auths_verifier::DevicePublicKey::try_new(curve, &pk_bytes)
196-
.map_err(|e| anyhow!("Invalid issuer public key: {e}"))?;
197-
return Ok(pk_bytes);
196+
let curve = auths_crypto::CurveType::from_public_key_len(pk_bytes.len())
197+
.ok_or_else(|| anyhow!("Invalid issuer public key length: {}", pk_bytes.len()))?;
198+
return auths_verifier::DevicePublicKey::try_new(curve, &pk_bytes)
199+
.map_err(|e| anyhow!("Invalid issuer public key: {e}"));
198200
}
199201

200202
// Determine the DID to look up
@@ -209,7 +211,7 @@ fn resolve_issuer_key(
209211
if !is_json_mode() {
210212
println!("Using pinned identity: {}", did);
211213
}
212-
return Ok(pin.public_key_bytes()?);
214+
return bytes_to_device_public_key(&pin.public_key_bytes()?, "pinned identity");
213215
}
214216

215217
// 3. Check roots.json file
@@ -240,7 +242,7 @@ fn resolve_issuer_key(
240242
trust_level: TrustLevel::OrgPolicy,
241243
};
242244
store.pin(pin)?;
243-
return Ok(root.public_key_bytes()?);
245+
return bytes_to_device_public_key(&root.public_key_bytes()?, "roots.json");
244246
}
245247
}
246248

@@ -297,7 +299,7 @@ async fn run_verify(now: chrono::DateTime<Utc>, cmd: &VerifyCommand) -> Result<V
297299
}
298300

299301
// 3. Resolve issuer public key
300-
let issuer_pk_bytes = resolve_issuer_key(now, cmd, &att)?;
302+
let issuer_pk = resolve_issuer_key(now, cmd, &att)?;
301303

302304
let required_capability: Option<Capability> = cmd.require_capability.as_ref().map(|cap| {
303305
cap.parse::<Capability>().unwrap_or_else(|e| {
@@ -308,9 +310,9 @@ async fn run_verify(now: chrono::DateTime<Utc>, cmd: &VerifyCommand) -> Result<V
308310

309311
// 5. Verify the attestation (with or without capability check)
310312
let verify_result = if let Some(ref cap) = required_capability {
311-
verify_with_capability(&att, cap, &issuer_pk_bytes).await
313+
verify_with_capability(&att, cap, &issuer_pk).await
312314
} else {
313-
verify_with_keys(&att, &issuer_pk_bytes).await
315+
verify_with_keys(&att, &issuer_pk).await
314316
};
315317

316318
match verify_result {
@@ -330,13 +332,10 @@ async fn run_verify(now: chrono::DateTime<Utc>, cmd: &VerifyCommand) -> Result<V
330332
threshold: cmd.witness_threshold,
331333
};
332334

333-
let report = verify_chain_with_witnesses(
334-
std::slice::from_ref(&att),
335-
&issuer_pk_bytes,
336-
&config,
337-
)
338-
.await
339-
.context("Witness chain verification failed")?;
335+
let report =
336+
verify_chain_with_witnesses(std::slice::from_ref(&att), &issuer_pk, &config)
337+
.await
338+
.context("Witness chain verification failed")?;
340339

341340
if !report.is_valid() {
342341
if !is_json_mode()
@@ -447,21 +446,9 @@ pub async fn handle_verify_attestation(
447446

448447
let issuer_pk_bytes = hex::decode(issuer_pubkey_hex)
449448
.context("Invalid hex string provided for issuer public key")?;
449+
let issuer_pk = bytes_to_device_public_key(&issuer_pk_bytes, "issuer")?;
450450

451-
let curve = match issuer_pk_bytes.len() {
452-
32 => auths_crypto::CurveType::Ed25519,
453-
33 | 65 => auths_crypto::CurveType::P256,
454-
_ => {
455-
return Err(anyhow!(
456-
"Invalid issuer public key length: {}",
457-
issuer_pk_bytes.len()
458-
));
459-
}
460-
};
461-
auths_verifier::DevicePublicKey::try_new(curve, &issuer_pk_bytes)
462-
.map_err(|e| anyhow!("Invalid issuer public key: {e}"))?;
463-
464-
match verify_with_keys(&att, &issuer_pk_bytes).await {
451+
match verify_with_keys(&att, &issuer_pk).await {
465452
Ok(_) => {
466453
println!("Attestation verified successfully.");
467454
Ok(())

crates/auths-cli/src/commands/verify_commit.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ async fn verify_bundle_chain(
455455
);
456456
}
457457

458-
let root_pk = match hex::decode(bundle.public_key_hex.as_str()) {
458+
let root_pk_bytes = match hex::decode(bundle.public_key_hex.as_str()) {
459459
Ok(pk) => pk,
460460
Err(e) => {
461461
return (
@@ -465,6 +465,15 @@ async fn verify_bundle_chain(
465465
);
466466
}
467467
};
468+
let root_pk = match auths_crypto::CurveType::from_public_key_len(root_pk_bytes.len())
469+
.ok_or_else(|| format!("Invalid bundle public key length: {}", root_pk_bytes.len()))
470+
.and_then(|curve| {
471+
auths_verifier::DevicePublicKey::try_new(curve, &root_pk_bytes)
472+
.map_err(|e| format!("Invalid bundle public key: {e}"))
473+
}) {
474+
Ok(pk) => pk,
475+
Err(msg) => return (Some(false), None, vec![msg]),
476+
};
468477

469478
match verify_chain(&bundle.attestation_chain, &root_pk).await {
470479
Ok(report) => {
@@ -525,8 +534,12 @@ async fn verify_witnesses(
525534
if let Some(bundle) = bundle
526535
&& !bundle.attestation_chain.is_empty()
527536
{
528-
let root_pk = hex::decode(bundle.public_key_hex.as_str())
537+
let root_pk_bytes = hex::decode(bundle.public_key_hex.as_str())
529538
.context("Invalid public key hex in bundle")?;
539+
let curve = auths_crypto::CurveType::from_public_key_len(root_pk_bytes.len())
540+
.ok_or_else(|| anyhow!("Invalid bundle public key length: {}", root_pk_bytes.len()))?;
541+
let root_pk = auths_verifier::DevicePublicKey::try_new(curve, &root_pk_bytes)
542+
.map_err(|e| anyhow!("Invalid bundle public key: {e}"))?;
530543

531544
let report = verify_chain_with_witnesses(&bundle.attestation_chain, &root_pk, &config)
532545
.await

crates/auths-crypto/src/provider.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,22 @@ impl CurveType {
267267
Self::P256 => P256_SIGNATURE_LEN,
268268
}
269269
}
270+
271+
/// Infer the curve from a raw public key's byte length.
272+
///
273+
/// Used only at external ingestion boundaries (FFI, WASM, CLI flags) where
274+
/// the caller supplies raw bytes and no typed curve signal is available.
275+
/// Returns `None` for lengths that do not match any supported curve.
276+
///
277+
/// Accepts:
278+
/// - 32 bytes → Ed25519
279+
/// - 33 bytes → P-256 (compressed SEC1)
280+
/// - 65 bytes → P-256 (uncompressed SEC1)
281+
pub fn from_public_key_len(len: usize) -> Option<Self> {
282+
match len {
283+
ED25519_PUBLIC_KEY_LEN => Some(Self::Ed25519),
284+
P256_PUBLIC_KEY_LEN | 65 => Some(Self::P256),
285+
_ => None,
286+
}
287+
}
270288
}

crates/auths-id/tests/cases/lifecycle.rs

Lines changed: 25 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ use auths_id::storage::git_refs::AttestationMetadata;
99
use auths_id::storage::layout::StorageLayoutConfig;
1010
use auths_id::testing::fakes::FakeIdentityStorage;
1111
use auths_verifier::verify::{verify_at_time, verify_with_keys};
12-
use auths_verifier::{DeviceDID, VerificationStatus, verify_chain, verify_device_authorization};
12+
use auths_verifier::{
13+
DeviceDID, DevicePublicKey, VerificationStatus, verify_chain, verify_device_authorization,
14+
};
15+
16+
/// Wrap a raw Ed25519 public key (32 bytes) into a `DevicePublicKey` for tests.
17+
fn ed(pk: &[u8]) -> DevicePublicKey {
18+
DevicePublicKey::try_new(auths_crypto::CurveType::Ed25519, pk).unwrap()
19+
}
1320

1421
use chrono::Utc;
1522
use git2::Repository;
@@ -211,7 +218,7 @@ async fn test_full_identity_lifecycle() {
211218
);
212219

213220
// 4. Verify the attestation with the identity's public key
214-
verify_with_keys(&attestation, &identity_pk)
221+
verify_with_keys(&attestation, &ed(&identity_pk))
215222
.await
216223
.expect("Attestation should verify");
217224

@@ -221,7 +228,7 @@ async fn test_full_identity_lifecycle() {
221228
// 6. Verify OLD attestation still passes with historical key (sequence 0)
222229
let old_pk = resolve_identity_public_key_at_sequence(&repo_path, &identity_did, 0);
223230
assert_eq!(old_pk, identity_pk, "Historical key should match original");
224-
verify_at_time(&attestation, &old_pk, attestation.timestamp.unwrap())
231+
verify_at_time(&attestation, &ed(&old_pk), attestation.timestamp.unwrap())
225232
.await
226233
.expect("Old attestation should verify with historical key");
227234

@@ -246,7 +253,7 @@ async fn test_full_identity_lifecycle() {
246253
);
247254

248255
// 8. Verify new attestation with new public key
249-
verify_with_keys(&new_attestation, &new_identity_pk)
256+
verify_with_keys(&new_attestation, &ed(&new_identity_pk))
250257
.await
251258
.expect("New attestation should verify with rotated key");
252259
}
@@ -294,7 +301,7 @@ async fn test_attestation_chain_after_rotation() {
294301
);
295302

296303
// Verify 2-link chain
297-
let report = verify_chain(&[att1.clone(), att2], &identity_pk)
304+
let report = verify_chain(&[att1.clone(), att2], &ed(&identity_pk))
298305
.await
299306
.expect("Chain verify failed");
300307
assert!(report.is_valid(), "Chain should be valid");
@@ -305,7 +312,7 @@ async fn test_attestation_chain_after_rotation() {
305312

306313
// Verify the first link still works with historical key
307314
let old_pk = resolve_identity_public_key_at_sequence(&repo_path, &identity_did, 0);
308-
verify_at_time(&att1, &old_pk, att1.timestamp.unwrap())
315+
verify_at_time(&att1, &ed(&old_pk), att1.timestamp.unwrap())
309316
.await
310317
.expect("First chain link should still verify with historical key");
311318
}
@@ -341,7 +348,7 @@ async fn test_verify_device_authorization_lifecycle() {
341348
&identity_did,
342349
&device_did,
343350
std::slice::from_ref(&attestation),
344-
&identity_pk,
351+
&ed(&identity_pk),
345352
)
346353
.await
347354
.expect("verify_device_authorization failed");
@@ -351,10 +358,14 @@ async fn test_verify_device_authorization_lifecycle() {
351358
let mut revoked_att = attestation;
352359
revoked_att.revoked_at = Some(Utc::now());
353360

354-
let report =
355-
verify_device_authorization(&identity_did, &device_did, &[revoked_att], &identity_pk)
356-
.await
357-
.expect("verify_device_authorization failed");
361+
let report = verify_device_authorization(
362+
&identity_did,
363+
&device_did,
364+
&[revoked_att],
365+
&ed(&identity_pk),
366+
)
367+
.await
368+
.expect("verify_device_authorization failed");
358369
assert!(
359370
!report.is_valid(),
360371
"Revoked device should not be authorized"
@@ -393,7 +404,7 @@ async fn test_multiple_rotations_maintain_verification() {
393404
);
394405

395406
// Verify initial attestation
396-
verify_with_keys(&original_attestation, &original_pk)
407+
verify_with_keys(&original_attestation, &ed(&original_pk))
397408
.await
398409
.expect("Original attestation should verify");
399410

@@ -407,7 +418,7 @@ async fn test_multiple_rotations_maintain_verification() {
407418
assert_eq!(historical_pk, original_pk);
408419
verify_at_time(
409420
&original_attestation,
410-
&historical_pk,
421+
&ed(&historical_pk),
411422
original_attestation.timestamp.unwrap(),
412423
)
413424
.await
@@ -434,7 +445,7 @@ async fn test_multiple_rotations_maintain_verification() {
434445
);
435446

436447
// Verify new attestation with current key
437-
verify_with_keys(&new_attestation, &current_pk)
448+
verify_with_keys(&new_attestation, &ed(&current_pk))
438449
.await
439450
.expect("New attestation should verify with current key");
440451
}

crates/auths-sdk/tests/cases/ephemeral_signing.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,10 +175,15 @@ fn tamper_commit_sha_breaks_signature() {
175175

176176
// Extract the pubkey from the issuer DID for verification
177177
let issuer_did = att.issuer.as_str();
178-
let pk = match auths_crypto::did_key_decode(issuer_did).expect("should resolve did:key") {
179-
auths_crypto::DecodedDidKey::Ed25519(k) => k.to_vec(),
180-
auths_crypto::DecodedDidKey::P256(k) => k,
181-
};
178+
let (pk_bytes, curve): (Vec<u8>, auths_crypto::CurveType) =
179+
match auths_crypto::did_key_decode(issuer_did).expect("should resolve did:key") {
180+
auths_crypto::DecodedDidKey::Ed25519(k) => {
181+
(k.to_vec(), auths_crypto::CurveType::Ed25519)
182+
}
183+
auths_crypto::DecodedDidKey::P256(k) => (k, auths_crypto::CurveType::P256),
184+
};
185+
let pk =
186+
auths_verifier::DevicePublicKey::try_new(curve, &pk_bytes).expect("valid device pubkey");
182187

183188
// Verify the tampered attestation — should fail because signature covers commit_sha
184189
let rt = tokio::runtime::Runtime::new().unwrap();

0 commit comments

Comments
 (0)