diff --git a/Cargo.lock b/Cargo.lock index 1767498a..59704d5f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2311,6 +2311,16 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "coset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1eb98d5e9155e2cf7cd942c8b3033097d4563b6fb0a00b9caecb74669555c058" +dependencies = [ + "ciborium", + "ciborium-io", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -2693,7 +2703,7 @@ dependencies = [ "libc", "option-ext", "redox_users 0.5.2", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -4345,7 +4355,7 @@ version = "0.50.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -6290,7 +6300,7 @@ dependencies = [ "security-framework", "security-framework-sys", "webpki-root-certs 0.26.11", - "windows-sys 0.59.0", + "windows-sys 0.52.0", ] [[package]] @@ -7458,9 +7468,9 @@ dependencies = [ [[package]] name = "taceo-oprf" -version = "0.17.2" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c10bf82ba41bbc8a6693362d67738145c77bdcec36345d85d4a86d2fb7dc44dc" +checksum = "50935dca161ff8ec1a0405b023639f2e6c181e2271ef41443abca1fe4be55444" dependencies = [ "taceo-oprf-client", "taceo-oprf-core", @@ -7469,9 +7479,9 @@ dependencies = [ [[package]] name = "taceo-oprf-client" -version = "0.10.3" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cc6179c5bad58f1210bd8872963960145227e8488dfedb4893538acecc5f8ae" +checksum = "ef8c7fbd38a4c78cbd172f71e3de826cafccae51c1a93c2777d87da73c055ded" dependencies = [ "ark-ec", "ciborium", @@ -7479,6 +7489,7 @@ dependencies = [ "getrandom 0.2.17", "gloo-net", "http", + "reqwest 0.13.4", "serde", "taceo-ark-babyjubjub", "taceo-oprf-core", @@ -7488,14 +7499,15 @@ dependencies = [ "tokio", "tokio-tungstenite", "tracing", + "url", "uuid", ] [[package]] name = "taceo-oprf-core" -version = "0.6.1" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be7f117e2bff79209ce40cdf4af6d566501cb1e63d2e1f9be6134a3bd3a3a2a3" +checksum = "e5627044f0bba8551bda54b0496010a3f1267297333ca70eb39491e767ff2ad9" dependencies = [ "ark-ec", "ark-ff 0.5.0", @@ -7515,9 +7527,9 @@ dependencies = [ [[package]] name = "taceo-oprf-types" -version = "0.15.0" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4b3645c7a7592e8aacdb4ca7270e75da3cca6061ae012521f3cb0dd01ff0b16" +checksum = "08a58191080b4456f5659722dd0abb14fa1edc739d52108858bda861c35dcb95" dependencies = [ "ark-ff 0.5.0", "ark-serialize 0.5.0", @@ -8832,15 +8844,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.61.2" @@ -9061,8 +9064,7 @@ checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" [[package]] name = "world-id-authenticator" version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc05e2d364f3613e562aef520913a0044b71e504c1d19e8c6b9d80e61bc5af8b" +source = "git+https://github.com/worldcoin/world-id-protocol?rev=79f3983e5f1c29326de2938fe56eff76c156c698#79f3983e5f1c29326de2938fe56eff76c156c698" dependencies = [ "alloy", "anyhow", @@ -9085,7 +9087,6 @@ dependencies = [ "taceo-eddsa-babyjubjub", "taceo-groth16-material", "taceo-oprf", - "taceo-poseidon2", "thiserror 2.0.18", "tokio", "webpki-roots 1.0.8", @@ -9097,8 +9098,7 @@ dependencies = [ [[package]] name = "world-id-core" version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e741ad4aa64d9a681ad4b325b0f49235a84af78af1f359dcbbbb66312286c43" +source = "git+https://github.com/worldcoin/world-id-protocol?rev=79f3983e5f1c29326de2938fe56eff76c156c698#79f3983e5f1c29326de2938fe56eff76c156c698" dependencies = [ "taceo-eddsa-babyjubjub", "world-id-authenticator", @@ -9110,8 +9110,7 @@ dependencies = [ [[package]] name = "world-id-primitives" version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7430d4229c914b7136a028ad478dacc7983a8bfb926af9f5a4e5e31550d4f1cf" +source = "git+https://github.com/worldcoin/world-id-protocol?rev=79f3983e5f1c29326de2938fe56eff76c156c698#79f3983e5f1c29326de2938fe56eff76c156c698" dependencies = [ "alloy", "alloy-primitives", @@ -9145,16 +9144,17 @@ dependencies = [ [[package]] name = "world-id-proof" version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0530a43963153b34ba8a9f91a781c50771c8baef3a69bddfa230b75d2c84b3ff" +source = "git+https://github.com/worldcoin/world-id-protocol?rev=79f3983e5f1c29326de2938fe56eff76c156c698#79f3983e5f1c29326de2938fe56eff76c156c698" dependencies = [ "ark-bn254", "ark-ec", "ark-ff 0.5.0", "ark-groth16", "ark-serialize 0.5.0", + "coset", "eyre", "once_cell", + "p256", "provekit-common", "provekit-prover", "provekit-r1cs-compiler", @@ -9170,7 +9170,6 @@ dependencies = [ "taceo-groth16-material", "taceo-groth16-sol", "taceo-oprf", - "taceo-poseidon2", "tar", "thiserror 2.0.18", "tracing", @@ -9182,8 +9181,7 @@ dependencies = [ [[package]] name = "world-id-registries" version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f47e1d0889ef5c5ee541621bb12cb4dfa64e3f0e3730c8fae08c075dfec6d67e" +source = "git+https://github.com/worldcoin/world-id-protocol?rev=79f3983e5f1c29326de2938fe56eff76c156c698#79f3983e5f1c29326de2938fe56eff76c156c698" dependencies = [ "alloy", "anyhow", diff --git a/Cargo.toml b/Cargo.toml index 5cd5f510..8d1b9755 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -59,7 +59,7 @@ sha2 = "0.10" sqlite-wasm-rs = "0.5" strum = "0.27" subtle = "2" -taceo-oprf = { version = "0.17", default-features = false } +taceo-oprf = { version = "0.18.1", default-features = false } tempfile = "3" test-case = "3.3" thiserror = "2" @@ -78,8 +78,8 @@ zeroize = "1" zip = { version = "2", default-features = false } # world-id-protocol crates -world-id-core = { version = "0.13", default-features = false } -world-id-proof = { version = "0.13", default-features = false } +world-id-core = { git = "https://github.com/worldcoin/world-id-protocol", rev = "79f3983e5f1c29326de2938fe56eff76c156c698", default-features = false } +world-id-proof = { git = "https://github.com/worldcoin/world-id-protocol", rev = "79f3983e5f1c29326de2938fe56eff76c156c698", default-features = false } # internal walletkit-core = { version = "0.21.2", path = "crates/walletkit-core", default-features = false } diff --git a/crates/walletkit-cli/src/commands/proof.rs b/crates/walletkit-cli/src/commands/proof.rs index da8e9aff..f7052cc8 100644 --- a/crates/walletkit-cli/src/commands/proof.rs +++ b/crates/walletkit-cli/src/commands/proof.rs @@ -104,6 +104,9 @@ pub enum ProofCommand { /// Nonce used when generating the proof, as a 32-byte hex field element (with optional `0x` prefix). #[arg(long)] nonce: String, + /// Context that binds the proof to the issuer operation, as a 32-byte hex field element. + #[arg(long)] + context: String, /// Credential `sub` (commitment) the proof claims ownership of, as a 32-byte hex field element. #[arg(long)] sub: String, @@ -131,9 +134,8 @@ fn read_file_or_stdin(path: &str) -> eyre::Result { fn parse_proof_type_arg(value: &str) -> Result { match value.trim().to_ascii_lowercase().as_str() { "uniqueness" => Ok(ProofType::Uniqueness), - "create-session" | "create_session" => Ok(ProofType::CreateSession), "session" => Ok(ProofType::Session), - _ => Err("expected one of: uniqueness, create-session, session".to_string()), + _ => Err("expected one of: uniqueness, session".to_string()), } } @@ -252,12 +254,9 @@ fn run_generate_test_request( session_id: Option, ) -> eyre::Result<()> { let session_id = match (proof_type, session_id) { - (ProofType::Uniqueness | ProofType::CreateSession, Some(_)) => { + (ProofType::Uniqueness, Some(_)) => { eyre::bail!("--session-id is only valid with --proof-type session"); } - (ProofType::Session, None) => { - eyre::bail!("--session-id is required with --proof-type session"); - } (ProofType::Session, Some(session_id)) => Some(session_id), (_, None) => None, }; @@ -360,6 +359,7 @@ fn run_verify_ownership( cli: &Cli, proof_path: &str, nonce: &str, + context: &str, sub: &str, ) -> eyre::Result<()> { let b64 = read_file_or_stdin(proof_path)?; @@ -370,12 +370,19 @@ fn run_verify_ownership( .wrap_err("failed to decode ownership proof CBOR")?; let nonce_fe = parse_field_element(nonce, "--nonce")?; + let context_fe = parse_field_element(context, "--context")?; let sub_fe = parse_field_element(sub, "--sub")?; let root = resolve_root(cli)?; let artifacts = create_artifact_source(&root); - let result = verify_ownership_proof(&proof, nonce_fe, sub_fe, artifacts.as_ref()); + let result = verify_ownership_proof( + &proof, + nonce_fe, + sub_fe, + context_fe, + artifacts.as_ref(), + ); let merkle_root = proof.merkle_root.to_string(); if cli.json { @@ -432,8 +439,11 @@ pub async fn run(cli: &Cli, action: &ProofCommand) -> eyre::Result<()> { signal, verifier_address, } => run_test(cli, signal, verifier_address.as_deref()).await, - ProofCommand::VerifyOwnership { proof, nonce, sub } => { - run_verify_ownership(cli, proof, nonce, sub) - } + ProofCommand::VerifyOwnership { + proof, + nonce, + context, + sub, + } => run_verify_ownership(cli, proof, nonce, context, sub), } } diff --git a/crates/walletkit-cli/tests/cli_tests.rs b/crates/walletkit-cli/tests/cli_tests.rs index a11cd03d..05f94964 100644 --- a/crates/walletkit-cli/tests/cli_tests.rs +++ b/crates/walletkit-cli/tests/cli_tests.rs @@ -318,7 +318,7 @@ fn proof_generate_test_request_defaults_to_uniqueness() { } #[test] -fn proof_generate_test_request_supports_create_session() { +fn proof_generate_test_request_supports_canonical_session_creation() { let output = Command::new(walletkit_bin()) .args([ "--json", @@ -327,7 +327,7 @@ fn proof_generate_test_request_supports_create_session() { "--issuer-schema-id", "47", "--proof-type", - "create-session", + "session", ]) .output() .expect("failed to run"); @@ -341,9 +341,9 @@ fn proof_generate_test_request_supports_create_session() { let parsed: serde_json::Value = serde_json::from_str(&stdout).expect("invalid json"); let data = &parsed["data"]; - assert_eq!(data["proof_type"], "create_session"); + assert_eq!(data["proof_type"], "session"); assert!(data["action"].is_null()); - assert!(data["session_id"].is_null()); + assert_eq!(data["session_id"], "create"); } #[test] @@ -383,7 +383,12 @@ fn proof_generate_test_request_supports_session() { } #[test] -fn proof_generate_test_request_requires_session_id_for_session() { +fn proof_generate_test_request_rejects_session_id_for_uniqueness() { + let session_id = serde_json::to_value(SessionId::default()) + .expect("session id should serialize") + .as_str() + .expect("session id should serialize as string") + .to_string(); let output = Command::new(walletkit_bin()) .args([ "--json", @@ -391,8 +396,8 @@ fn proof_generate_test_request_requires_session_id_for_session() { "generate-test-request", "--issuer-schema-id", "47", - "--proof-type", - "session", + "--session-id", + &session_id, ]) .output() .expect("failed to run"); @@ -405,8 +410,8 @@ fn proof_generate_test_request_requires_session_id_for_session() { parsed["error"]["message"] .as_str() .unwrap() - .contains("--session-id is required"), - "expected missing session id error, got: {stderr}" + .contains("--session-id is only valid"), + "expected uniqueness session id error, got: {stderr}" ); } diff --git a/crates/walletkit-core/src/authenticator/mod.rs b/crates/walletkit-core/src/authenticator/mod.rs index 3773f233..5e068ef1 100644 --- a/crates/walletkit-core/src/authenticator/mod.rs +++ b/crates/walletkit-core/src/authenticator/mod.rs @@ -646,6 +646,7 @@ impl Authenticator { proof_request .0 .session_id + .existing() .and_then(|session_id| { match self.store.get_session_seed(session_id.oprf_seed, now) { Ok(seed) => seed, @@ -666,8 +667,8 @@ impl Authenticator { )) .await?; - // Cache session seed if returned. Create-session requests do not carry a - // session_id, so use the session_id generated in the proof response. + // Cache a newly created session seed using the session ID returned in the + // proof response. if let Some(seed) = result.session_id_r_seed { if let Some(session_id) = result.proof_response.session_id { if let Err(err) = @@ -698,6 +699,8 @@ impl Authenticator { /// /// # Arguments /// * `nonce` - A field element provided by the Issuer to prevent replay. + /// * `context` - A field element provided by the Issuer that binds the proof + /// to the specific operation being performed. /// * `blinding_factor` - The credential blinding factor previously used to /// derive the credential `sub`. /// * `sub` - The credential `sub` (commitment) to prove ownership of. @@ -712,12 +715,13 @@ impl Authenticator { pub async fn prove_credential_sub( &self, nonce: &FieldElement, + context: &FieldElement, blinding_factor: &FieldElement, sub: &FieldElement, ) -> Result { #[cfg(target_arch = "wasm32")] { - let _ = (nonce, blinding_factor, sub); + let _ = (nonce, context, blinding_factor, sub); return Err(WalletKitError::Generic { error: "credential ownership proofs are not supported on wasm32" .to_string(), @@ -738,6 +742,7 @@ impl Authenticator { .inner .prove_credential_sub( nonce.0, + context.0, blinding_factor.0, sub.0, Some(inclusion_proof), diff --git a/crates/walletkit-core/src/error.rs b/crates/walletkit-core/src/error.rs index bb2b8cb8..f6298725 100644 --- a/crates/walletkit-core/src/error.rs +++ b/crates/walletkit-core/src/error.rs @@ -226,6 +226,7 @@ impl From for WalletKitError { attribute: "index".to_string(), reason: "Provided index is out of bounds".to_string(), }, + PrimitiveError::SessionIdCommitmentMismatch => Self::SessionIdMismatch, } } } @@ -237,8 +238,8 @@ impl From for WalletKitError { WorldIdRequestAuthError::DuplicateNonce => Self::DuplicateNonce, WorldIdRequestAuthError::UnknownRp => Self::UnknownRp, WorldIdRequestAuthError::InactiveRp => Self::InactiveRp, - WorldIdRequestAuthError::TimestampTooOld => Self::TimestampTooOld, - WorldIdRequestAuthError::TimestampTooFarInFuture => { + WorldIdRequestAuthError::CreatedAtTooOld => Self::TimestampTooOld, + WorldIdRequestAuthError::CreatedAtTooFarInFuture => { Self::TimestampTooFarInFuture } WorldIdRequestAuthError::InvalidTimestamp => Self::InvalidTimestamp, @@ -311,8 +312,6 @@ impl From for WalletKitError { AuthenticatorError::ResponseValidationError(err) => { Self::ResponseValidation(err.to_string()) } - AuthenticatorError::SessionIdMismatch => Self::SessionIdMismatch, - AuthenticatorError::OhttpEncapsulationError(_) | AuthenticatorError::BhttpError(_) | AuthenticatorError::OhttpRelayError { .. } @@ -365,11 +364,11 @@ mod tests { (WorldIdRequestAuthError::UnknownRp, "unknown_rp"), (WorldIdRequestAuthError::InactiveRp, "inactive_rp"), ( - WorldIdRequestAuthError::TimestampTooOld, + WorldIdRequestAuthError::CreatedAtTooOld, "timestamp_too_old", ), ( - WorldIdRequestAuthError::TimestampTooFarInFuture, + WorldIdRequestAuthError::CreatedAtTooFarInFuture, "timestamp_too_far_in_future", ), ( @@ -407,4 +406,11 @@ mod tests { other => panic!("expected proof generation error, got {other:?}"), } } + + #[test] + fn maps_session_commitment_mismatch_to_public_session_error() { + let error = WalletKitError::from(PrimitiveError::SessionIdCommitmentMismatch); + + assert!(matches!(error, WalletKitError::SessionIdMismatch)); + } } diff --git a/crates/walletkit-core/src/proof_request_credential_constraints_check.rs b/crates/walletkit-core/src/proof_request_credential_constraints_check.rs index 895d1758..06dde358 100644 --- a/crates/walletkit-core/src/proof_request_credential_constraints_check.rs +++ b/crates/walletkit-core/src/proof_request_credential_constraints_check.rs @@ -188,7 +188,7 @@ mod tests { use alloy_core::primitives::{Signature, U160}; use taceo_oprf::types::OprfKeyId; use world_id_core::{ - primitives::rp::RpId, + primitives::{rp::RpId, SessionRef}, requests::{ ConstraintExpr, ConstraintNode, ProofRequest as CoreProofRequest, ProofType, RequestItem, RequestVersion, @@ -216,7 +216,7 @@ mod tests { expires_at: u64::MAX, rp_id: RpId::new(1), oprf_key_id: OprfKeyId::new(U160::from(1u64)), - session_id: None, + session_id: SessionRef::None, action: None, signature: Signature::test_signature(), nonce: CoreFieldElement::ZERO, diff --git a/crates/walletkit-core/src/requests.rs b/crates/walletkit-core/src/requests.rs index bfe90a87..9f6c24b4 100644 --- a/crates/walletkit-core/src/requests.rs +++ b/crates/walletkit-core/src/requests.rs @@ -113,7 +113,7 @@ mod tests { use serde_json::Value; use taceo_oprf::types::OprfKeyId; use world_id_core::{ - primitives::{rp::RpId, FieldElement}, + primitives::{rp::RpId, FieldElement, SessionRef}, requests::{ProofType, RequestItem, RequestVersion}, }; @@ -136,7 +136,7 @@ mod tests { expires_at: 1_700_000_300, rp_id: RpId::new(1), oprf_key_id: OprfKeyId::new(U160::from(1)), - session_id: None, + session_id: SessionRef::None, action: Some(FieldElement::from(1u64)), signature: test_signature(), nonce: FieldElement::from(2u64), @@ -192,4 +192,75 @@ mod tests { other => panic!("expected invalid input error, got {other:?}"), } } + + #[test] + fn from_json_accepts_canonical_session_creation() { + let mut request = base_core_request(ProofType::Session); + request.action = None; + request.session_id = SessionRef::Create; + + let json = serde_json::to_string(&request).expect("request should serialize"); + let request = ProofRequest::from_json(&json).expect("request should parse"); + + assert_eq!(request.0.proof_type, ProofType::Session); + assert_eq!(request.0.session_id, SessionRef::Create); + } + + #[test] + fn from_json_rejects_legacy_create_session_proof_type() { + let mut value = serde_json::to_value(base_core_request(ProofType::Session)) + .expect("request should serialize"); + let object = value.as_object_mut().expect("request should be an object"); + object.insert( + "proof_type".to_string(), + Value::String("create_session".to_string()), + ); + object.remove("session_id"); + object.remove("action"); + + let json = + serde_json::to_string(&value).expect("request json should serialize"); + let error = ProofRequest::from_json(&json) + .expect_err("legacy create_session proof type should be rejected"); + + assert!(matches!(error, WalletKitError::InvalidInput { .. })); + } + + #[test] + fn from_json_ignores_unknown_forward_compatible_fields() { + let mut value = serde_json::to_value(base_core_request(ProofType::Uniqueness)) + .expect("request should serialize"); + let object = value.as_object_mut().expect("request should be an object"); + object.insert("future_top_level_field".to_string(), Value::Bool(true)); + object + .get_mut("proof_requests") + .and_then(Value::as_array_mut) + .and_then(|items| items.first_mut()) + .and_then(Value::as_object_mut) + .expect("request item should be an object") + .insert("future_request_field".to_string(), Value::Bool(true)); + + let json = + serde_json::to_string(&value).expect("request json should serialize"); + let request = ProofRequest::from_json(&json).expect("request should parse"); + + assert_eq!(request.0.proof_type, ProofType::Uniqueness); + } + + #[test] + fn from_json_rejects_unsupported_request_version() { + let mut value = serde_json::to_value(base_core_request(ProofType::Uniqueness)) + .expect("request should serialize"); + value + .as_object_mut() + .expect("request should be an object") + .insert("version".to_string(), Value::from(2)); + + let json = + serde_json::to_string(&value).expect("request json should serialize"); + let error = ProofRequest::from_json(&json) + .expect_err("unsupported request version should be rejected"); + + assert!(matches!(error, WalletKitError::InvalidInput { .. })); + } } diff --git a/crates/walletkit-core/tests/proof_generation_integration.rs b/crates/walletkit-core/tests/proof_generation_integration.rs index 7d08fbef..1706fa20 100644 --- a/crates/walletkit-core/tests/proof_generation_integration.rs +++ b/crates/walletkit-core/tests/proof_generation_integration.rs @@ -122,7 +122,7 @@ async fn e2e_session_proof() -> Result<()> { schema_id, SIGNAL, REQUEST_TTL_SECS, - ProofType::CreateSession, + ProofType::Session, None, ) .wrap_err("failed to build create-session request")?; @@ -197,8 +197,9 @@ async fn e2e_session_proof() -> Result<()> { .ok_or_else(|| eyre::eyre!("issued credential missing from store"))?; let sub = credential.sub(); let nonce = FieldElement::random(&mut OsRng).into(); + let context = FieldElement::random(&mut OsRng).into(); let ownership_proof = authenticator - .prove_credential_sub(&nonce, &blinding_factor, &sub) + .prove_credential_sub(&nonce, &context, &blinding_factor, &sub) .await .wrap_err("ownership proof generation failed")?; assert_eq!( diff --git a/crates/walletkit-testkit/src/lib.rs b/crates/walletkit-testkit/src/lib.rs index 0a714f76..62d6cc30 100644 --- a/crates/walletkit-testkit/src/lib.rs +++ b/crates/walletkit-testkit/src/lib.rs @@ -107,8 +107,8 @@ pub struct TestProofOutcome { pub verification: VerifyItemResult, /// Session ID from the proof response (`None` for uniqueness proofs). /// - /// For `ProofType::CreateSession` this is the newly created session, which - /// can be passed to a follow-up `ProofType::Session` request. + /// For a `ProofType::Session` request without an existing session ID, this + /// is the newly created session that can be passed to a follow-up request. pub session_id: Option, } @@ -156,9 +156,8 @@ pub async fn issue_credential( /// Registers an account, issues a credential of this type, generates a proof /// of `proof_type` for `signal`, and verifies it on-chain. /// -/// For `ProofType::Session`, pass the `session_id` of an existing session -/// (e.g. from a prior `ProofType::CreateSession` outcome); otherwise pass -/// `None`. +/// For `ProofType::Session`, pass an existing `session_id` to reuse a session, +/// or `None` to create one. /// /// # Errors /// diff --git a/crates/walletkit-testkit/src/proof.rs b/crates/walletkit-testkit/src/proof.rs index 7cde7f52..7bf6243f 100644 --- a/crates/walletkit-testkit/src/proof.rs +++ b/crates/walletkit-testkit/src/proof.rs @@ -7,7 +7,9 @@ use alloy_core::primitives::U160; use eyre::WrapErr as _; use rand::rngs::OsRng; use uuid::Uuid; -use world_id_core::primitives::{rp::RpId, FieldElement, OprfKeyId, SessionId}; +use world_id_core::primitives::{ + rp::RpId, FieldElement, OprfKeyId, SessionId, SessionRef, +}; use world_id_core::requests::{ ProofRequest, ProofResponse, ProofType, RequestItem, RequestVersion, }; @@ -48,13 +50,14 @@ sol!( /// Builds a proof [`ProofRequest`] signed by the RP key configured in `env`. /// /// The request expires at `created_at + expires_in`. For uniqueness proofs an `action` of `1` is set and -/// included in the RP signature. Pass an existing `session_id` for `ProofType::Session` proofs. +/// included in the RP signature. For session proofs, omit `session_id` to create +/// a session or pass an existing ID to reuse one. /// /// # Errors /// /// Returns an error if the RP signer cannot be constructed from the configured /// key, if signing the RP message fails, or if `proof_type` and `session_id` -/// are inconsistent (e.g. `ProofType::Session` without a `session_id`). +/// are inconsistent (e.g. `ProofType::Uniqueness` with a `session_id`). pub fn build_test_request( env: &TestEnv, issuer_schema_id: u64, @@ -72,6 +75,14 @@ pub fn build_test_request( let action = (proof_type == ProofType::Uniqueness).then(|| FieldElement::from(1u64)); + let session_id = match (proof_type, session_id) { + (ProofType::Uniqueness, None) => SessionRef::None, + (ProofType::Uniqueness, Some(_)) => { + eyre::bail!("session_id is only valid for session proofs"); + } + (ProofType::Session, None) => SessionRef::Create, + (ProofType::Session, Some(session_id)) => SessionRef::Existing(session_id), + }; let msg = world_id_core::primitives::rp::compute_rp_signature_msg( *nonce, created_at, @@ -193,7 +204,7 @@ pub async fn verify_proof_onchain( .await .map(|_| ()) } - ProofType::CreateSession | ProofType::Session => { + ProofType::Session => { let session_nullifier = response_item.session_nullifier.ok_or_else(|| { eyre::eyre!("response item missing session_nullifier") diff --git a/crates/walletkit-testkit/tests/e2e.rs b/crates/walletkit-testkit/tests/e2e.rs index 46c78b61..ac898025 100644 --- a/crates/walletkit-testkit/tests/e2e.rs +++ b/crates/walletkit-testkit/tests/e2e.rs @@ -114,7 +114,7 @@ async fn e2e_session_proof() { &SESSION_TEST_SEED, root.path(), SIGNAL, - ProofType::CreateSession, + ProofType::Session, None, ) .await