From f0efd7e2ddddeef958f539397a9781439ab61bf0 Mon Sep 17 00:00:00 2001 From: Armando Faz Date: Fri, 17 Apr 2026 20:27:11 -0700 Subject: [PATCH 1/2] Add testing modules. --- tests/arc_tests/mod.rs | 16 ++ tests/arc_tests/protocol.rs | 155 +++++++++++++++++ tests/arc_tests/vectors.rs | 320 ++++++++++++++++++++++++++++++++++++ tests/test_protocol.rs | 25 +++ tests/test_vectors.rs | 37 +++++ tests/vectors/arc_v01.json | 69 ++++++++ 6 files changed, 622 insertions(+) create mode 100644 tests/arc_tests/mod.rs create mode 100644 tests/arc_tests/protocol.rs create mode 100644 tests/arc_tests/vectors.rs create mode 100644 tests/test_protocol.rs create mode 100644 tests/test_vectors.rs create mode 100644 tests/vectors/arc_v01.json diff --git a/tests/arc_tests/mod.rs b/tests/arc_tests/mod.rs new file mode 100644 index 0000000..8ec5c14 --- /dev/null +++ b/tests/arc_tests/mod.rs @@ -0,0 +1,16 @@ +// Copyright (c) 2025 Cloudflare, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub mod protocol; +pub mod vectors; diff --git a/tests/arc_tests/protocol.rs b/tests/arc_tests/protocol.rs new file mode 100644 index 0000000..239dd86 --- /dev/null +++ b/tests/arc_tests/protocol.rs @@ -0,0 +1,155 @@ +// Copyright (c) 2025 Cloudflare, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::{any::type_name, num::NonZero}; + +use arc::{ + ClientSecret, Credential, CredentialRequest, CredentialResponse, Presentation, SecretKey, + State, Suite, +}; +use rand::rngs::ThreadRng; + +#[macro_export] +macro_rules! generate_tests { + ($suite:ty, tests: [ $($test_fn:ident),* $(,)? ]) => { + $( + #[test] + fn $test_fn() { + $crate::arc_tests::protocol::$test_fn::<$suite>(); + } + )* + }; +} + +#[macro_export] +macro_rules! all_arc_tests { + ($suite:ty) => { + $crate::generate_tests!($suite, tests: [happy_path, multiple_presentations]); + }; +} + +macro_rules! test_serialization { + ($T:ty, $val:expr $(, $context:expr)?) => { + let a_bytes = $val.to_bytes(); + let b = <$T>::from_bytes(&a_bytes $(, $context)? ) + .expect(&format!("deserialization of {} failed", type_name::<$T>())); + let b_bytes = b.to_bytes(); + assert_eq!(a_bytes, b_bytes); + }; +} + +#[allow(dead_code)] +pub fn happy_path() { + let req_context = b"RequestContext"; + let csrng = &mut ThreadRng::default(); + let key = SecretKey::::new(csrng); + test_serialization!(SecretKey::, &key); + + let params = key.issuer_params().unwrap(); + test_serialization!(SecretKey::, &key); + + let (request, secrets) = CredentialRequest::new(csrng, req_context).unwrap(); + test_serialization!(CredentialRequest::, &request); + test_serialization!(ClientSecret::, &secrets); + + let response = CredentialResponse::new(csrng, &key, request.clone()).unwrap(); + test_serialization!(CredentialResponse::, &response); + + let credential = Credential::new(¶ms, request, secrets, response).unwrap(); + test_serialization!(Credential::, &credential); + + let pres_limit = NonZero::new(3).expect("non-zero limit"); + let pres_context = b"PresentationContext"; + + let mut state = credential + .presentation_state(pres_limit, pres_context) + .unwrap(); + test_serialization!(State::, &state); + + assert_eq!(state.used_presentations(), 0); + assert_eq!(state.remaining_presentations(), pres_limit.get()); + + for _ in 0..pres_limit.get() { + let presentation = state.present(csrng).unwrap(); + test_serialization!(Presentation::, &presentation, pres_limit); + + let result_verify = presentation.verify(&key, req_context, pres_limit, pres_context); + assert!(result_verify.is_ok()); + } + + assert_eq!(state.used_presentations(), pres_limit.get()); + assert_eq!(state.remaining_presentations(), 0); + + let result = state.present(csrng); + assert!(result.is_err()); +} + +#[allow(dead_code)] +pub fn multiple_presentations() { + let request_ctx = b"RequestContext"; + let csrng = &mut ThreadRng::default(); + let key = SecretKey::::new(csrng); + let params = key.issuer_params().unwrap(); + + let (request, secrets) = CredentialRequest::new(csrng, request_ctx).unwrap(); + let response = CredentialResponse::new(csrng, &key, request.clone()).unwrap(); + let credential = Credential::new(¶ms, request, secrets, response).unwrap(); + + let photos_limit = 11.try_into().unwrap(); + let photos_ctx = "photos.example.com".as_bytes(); + let mut state_photos = credential + .presentation_state(photos_limit, photos_ctx) + .unwrap(); + let photos1 = state_photos.present(csrng).unwrap(); + + let movies_limit = 10.try_into().unwrap(); + let movies_ctx = "movies.example.com".as_bytes(); + let mut state_movies = credential + .presentation_state(movies_limit, movies_ctx) + .unwrap(); + + let mut result = photos1.verify(&key, request_ctx, photos_limit, photos_ctx); + assert!(result.is_ok()); + + // Check mismatching of verification parameters. + let key2 = SecretKey::::new(csrng); + result = photos1.verify(&key2, request_ctx, photos_limit, photos_ctx); + assert!(result.is_err(), "wrong key"); + + result = photos1.verify(&key, b"bad request context", photos_limit, photos_ctx); + assert!(result.is_err(), "wrong request context"); + + result = photos1.verify(&key, request_ctx, movies_limit, photos_ctx); + assert!(result.is_err(), "wrong presentation limit"); + + result = photos1.verify(&key, request_ctx, photos_limit, b"bad presentation context"); + assert!(result.is_err(), "wrong presentation context"); + + // Check mismatching of presentation parameters. + let movies1 = state_movies.present(csrng).unwrap(); + result = movies1.verify(&key, request_ctx, movies_limit, movies_ctx); + assert!(result.is_ok()); + + let movies2 = state_photos.present(csrng).unwrap(); + result = movies2.verify(&key, request_ctx, movies_limit, movies_ctx); + assert!(result.is_err(), "must fail as generated with a wrong state"); + + // Single presentation. + let single_ctx = "single.example.com".as_bytes(); + const ONE: NonZero = NonZero::::MIN; + let mut state_single = credential.presentation_state(ONE, single_ctx).unwrap(); + let single_presentation = state_single.present(csrng).unwrap(); + result = single_presentation.verify(&key, request_ctx, ONE, single_ctx); + assert!(result.is_ok()); +} diff --git a/tests/arc_tests/vectors.rs b/tests/arc_tests/vectors.rs new file mode 100644 index 0000000..3c1a47b --- /dev/null +++ b/tests/arc_tests/vectors.rs @@ -0,0 +1,320 @@ +// Copyright (c) 2025 Cloudflare, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::num::NonZero; + +use arc::{Credential, CredentialRequest, CredentialResponse, SecretKey, Suite}; +use rand_core::{ + CryptoRng, Error, RngCore, + impls::{next_u32_via_fill, next_u64_via_fill}, +}; +use serde::{Deserialize, Serialize}; +use serde_json::json; +use serde_with::serde_as; +use spongefish::{DuplexSpongeInterface as _, instantiations::Shake128}; + +macro_rules! assert_eq_bytes { + ($a:expr, $b:expr) => { + assert_eq!( + json!(Hex($a.to_vec())).as_str().unwrap(), + json!(Hex($b.to_vec())).as_str().unwrap() + ) + }; +} + +#[serde_as] +#[derive(Debug, Serialize, Deserialize)] +#[serde(transparent)] +struct Hex(#[serde_as(as = "serde_with::hex::Hex")] Vec); + +#[derive(Debug, Deserialize)] +struct ServerKeysVector { + #[serde(flatten)] + params: IssuerParamsVector, + #[serde(flatten)] + private: SecretKeyVector, +} + +#[derive(Debug, Deserialize)] +struct IssuerParamsVector { + #[serde(rename = "X0")] + x0: Hex, + #[serde(rename = "X1")] + x1: Hex, + #[serde(rename = "X2")] + x2: Hex, +} + +impl IssuerParamsVector { + fn bytes(self) -> Vec { + [self.x0.0, self.x1.0, self.x2.0].concat() + } +} + +#[derive(Debug, Deserialize)] +struct SecretKeyVector { + x0: Hex, + x1: Hex, + x2: Hex, + xb: Hex, +} + +impl SecretKeyVector { + fn bytes(self) -> Vec { + [self.x0.0, self.x1.0, self.x2.0, self.xb.0].concat() + } +} + +#[derive(Debug, Deserialize)] +struct CredentialVector { + m1: Hex, + #[serde(rename = "U")] + u: Hex, + #[serde(rename = "U_prime")] + u_prime: Hex, + #[serde(rename = "X1")] + x1: Hex, +} + +impl CredentialVector { + fn bytes(self) -> Vec { + [self.u.0, self.u_prime.0, self.m1.0, self.x1.0].concat() + } +} + +#[derive(Debug, Deserialize)] +struct CredentialRequestVector { + #[serde(flatten)] + client_secrets: ClientSecretVector, + m1_enc: Hex, + m2_enc: Hex, + proof: Hex, + request_context: Hex, +} + +impl CredentialRequestVector { + fn bytes(self) -> (Vec, Vec) { + ( + [self.m1_enc.0, self.m2_enc.0, self.proof.0].concat(), + self.client_secrets.bytes(), + ) + } +} + +#[derive(Debug, Deserialize)] +struct ClientSecretVector { + m1: Hex, + m2: Hex, + r1: Hex, + r2: Hex, +} + +impl ClientSecretVector { + fn bytes(self) -> Vec { + [self.m1.0, self.r1.0, self.m2.0, self.r2.0].concat() + } +} + +#[derive(Debug, Deserialize)] +struct CredentialResponseVector { + #[serde(rename = "enc_U_prime")] + enc_u_prime: Hex, + #[serde(rename = "H_aux")] + h_aux: Hex, + proof: Hex, + #[serde(rename = "U")] + u: Hex, + #[serde(rename = "X0_aux")] + x0_aux: Hex, + #[serde(rename = "X1_aux")] + x1_aux: Hex, + #[serde(rename = "X2_aux")] + x2_aux: Hex, +} + +impl CredentialResponseVector { + fn bytes(self) -> Vec { + [ + self.u.0, + self.enc_u_prime.0, + self.x0_aux.0, + self.x1_aux.0, + self.x2_aux.0, + self.h_aux.0, + self.proof.0, + ] + .concat() + } +} + +#[derive(Debug, Deserialize)] +struct PresentationVector { + m1_commit: Hex, + nonce_commit: Hex, + presentation_context: Hex, + proof: Hex, + tag: Hex, + #[serde(rename = "U")] + u: Hex, + #[serde(rename = "U_prime_commit")] + u_prime_commit: Hex, +} + +impl PresentationVector { + fn bytes(self) -> Vec { + [ + self.u.0, + self.u_prime_commit.0, + self.m1_commit.0, + self.tag.0, + self.nonce_commit.0, + self.proof.0, + ] + .concat() + } +} + +#[derive(Debug, Deserialize)] +pub struct SuiteVector { + #[serde(rename = "Credential")] + credential: CredentialVector, + #[serde(rename = "CredentialRequest")] + credential_request: CredentialRequestVector, + #[serde(rename = "CredentialResponse")] + credential_response: CredentialResponseVector, + #[serde(rename = "Presentation1")] + presentation1: PresentationVector, + #[serde(rename = "Presentation2")] + presentation2: PresentationVector, + #[serde(rename = "ServerKey")] + server_key: ServerKeysVector, +} + +pub struct TestDrng(Shake128); + +impl TestDrng { + pub fn from_seed(seed_label: &[u8]) -> Self { + let mut sponge = Shake128::default(); + let mut initial_block = [0u8; 168]; + let domain = b"sigma-proofs/TestDRNG/SHAKE128"; + initial_block[..domain.len()].copy_from_slice(domain); + sponge.absorb(&initial_block); + sponge.absorb(&fixed_seed(seed_label)); + Self(sponge) + } +} + +fn fixed_seed(label: &[u8]) -> [u8; 32] { + if label.len() > 32 { + panic!("seed label length must be less or equal to 32 bytes") + } + + let mut seed = [0u8; 32]; + seed[..label.len()].copy_from_slice(label); + seed +} + +impl CryptoRng for TestDrng {} + +impl RngCore for TestDrng { + fn next_u32(&mut self) -> u32 { + next_u32_via_fill(self) + } + + fn next_u64(&mut self) -> u64 { + next_u64_via_fill(self) + } + + fn fill_bytes(&mut self, dst: &mut [u8]) { + self.0.squeeze(dst); + } + + fn try_fill_bytes(&mut self, dst: &mut [u8]) -> Result<(), Error> { + self.fill_bytes(dst); + Ok(()) + } +} +#[allow(dead_code)] +pub fn test_vectors_arc(v: SuiteVector) { + const SEED: &[u8] = b"test vector seed"; + let rng = &mut TestDrng::from_seed(SEED); + let private_key = SecretKey::::new(rng); + let sk_got = private_key.to_bytes(); + let sk_want = v.server_key.private.bytes(); + assert_eq_bytes!(sk_got, sk_want); + + let issuer_params = private_key.issuer_params().unwrap(); + let ip_got = issuer_params.to_bytes(); + let ip_want = v.server_key.params.bytes(); + assert_eq_bytes!(ip_got, ip_want); + + let request_context = &v.credential_request.request_context.0.clone(); + let (credential_request, client_secrets) = CredentialRequest::::new(rng, request_context) + .expect("creating CredentialRequest failed"); + let creq_got = credential_request.to_bytes(); + let cs_got = client_secrets.to_bytes(); + let (creq_want, cs_want) = v.credential_request.bytes(); + assert_eq_bytes!(creq_got, creq_want); + assert_eq_bytes!(cs_got, cs_want); + + let credential_response = + CredentialResponse::new(rng, &private_key, credential_request.clone()) + .expect("creating CredentialResponse failed"); + let cres_got = credential_response.to_bytes(); + let cres_want = v.credential_response.bytes(); + assert_eq_bytes!(cres_got, cres_want); + + let credential = Credential::new( + &issuer_params, + credential_request, + client_secrets, + credential_response, + ) + .expect("creating Credential failed"); + let cred_got = credential.to_bytes(); + let cred_want = v.credential.bytes(); + assert_eq_bytes!(cred_got, cred_want); + + const PRESENTATION_LIMIT: NonZero = NonZero::new(2).expect("non-zero limit"); + let presentation_context = &v.presentation1.presentation_context.0.clone(); + + let mut state = credential + .presentation_state(PRESENTATION_LIMIT, presentation_context) + .unwrap(); + let pres1 = state.present(rng).expect("first presentation failed"); + let pres1_result = pres1.verify( + &private_key, + request_context, + PRESENTATION_LIMIT, + presentation_context, + ); + assert!(pres1_result.is_ok()); + + let p1_got = pres1.to_bytes(); + let p1_want = v.presentation1.bytes(); + assert_eq_bytes!(p1_got, p1_want); + + let pres2 = state.present(rng).expect("second presentation failed"); + let pres2_result = pres2.verify( + &private_key, + request_context, + PRESENTATION_LIMIT, + presentation_context, + ); + assert!(pres2_result.is_ok()); + + let p2_got = pres2.to_bytes(); + let p2_want = v.presentation2.bytes(); + assert_eq_bytes!(p2_got, p2_want); +} diff --git a/tests/test_protocol.rs b/tests/test_protocol.rs new file mode 100644 index 0000000..2bba2a2 --- /dev/null +++ b/tests/test_protocol.rs @@ -0,0 +1,25 @@ +// Copyright (c) 2025 Cloudflare, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod arc_tests; + +#[cfg(feature = "suite_p256")] +mod p256 { + crate::all_arc_tests!(arc::suites::P256); +} + +#[cfg(feature = "suite_ristretto255")] +mod ristretto255 { + crate::all_arc_tests!(arc::suites::Ristretto255); +} diff --git a/tests/test_vectors.rs b/tests/test_vectors.rs new file mode 100644 index 0000000..24711a9 --- /dev/null +++ b/tests/test_vectors.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2025 Cloudflare, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +mod arc_tests; + +#[cfg(feature = "suite_p256")] +mod p256 { + + use crate::arc_tests::vectors::{SuiteVector, test_vectors_arc}; + use serde::Deserialize; + + #[derive(Deserialize)] + struct Vector { + #[serde(rename = "ARCV1-P256")] + suite: SuiteVector, + } + + #[test] + fn test_vectors() { + let vectors_json = include_str!("../tests/vectors/arc_v01.json"); + let v = serde_json::from_str::(vectors_json) + .map_err(|e| format!("JSON parsing error: {e}")) + .unwrap(); + test_vectors_arc::(v.suite) + } +} diff --git a/tests/vectors/arc_v01.json b/tests/vectors/arc_v01.json new file mode 100644 index 0000000..b40ad67 --- /dev/null +++ b/tests/vectors/arc_v01.json @@ -0,0 +1,69 @@ +{ + "ARCV1-P256": { + "Credential": { + "U": "0318d861aacf3f42ad356c3c0ad90184a8717762274bcdf4faa1f9d237e0b056b4", + "U_prime": "02bed79f62edabf8833b105630f27a83c708304638ccf2b350654adb6cc7de8652", + "X1": "03913c27f2a2f50bfb0895d9f56aa583eb00be102f7a7ac662896fc0683a0223c3", + "m1": "141c4ca5e614af8e5e323eb47a7e76737de803fb5c0fc5386685ff1d369e2a06" + }, + "CredentialRequest": { + "m1": "141c4ca5e614af8e5e323eb47a7e76737de803fb5c0fc5386685ff1d369e2a06", + "m1_enc": "0299cbf0f4c53787aebac181db3e2e93e3a3b82a48c25edd0e08db490578c4ba13", + "m2": "911fb315257d9ae29d47ecb48c6fa27074dee6860a0489f8db6ac9a486be6a3e", + "m2_enc": "03a586d0d40f4548015e9b12d054ff953a021737ea7c631ea1757590ef6876fc10", + "proof": "eb7dfe77ee80364ed4452cdf2993cb74ece96ea6710ab80dabcae115f852da40b5a17902677f864fc74a2efea001126e2b78d83de226029be276801404c06030b744f4c9d81281400f430f01d9e2404b6c54f8096fc5e8052de94da3717b2ca5432958bf729822a5dcd43644baf960b822ac6bec5db28ffc192f85c9088037f8d401b3d1ae84b8fa8885e4838e5ef4d9d3a11a939342f455c43fdd66f2faa3a9", + "r1": "5c183d2dea942eb2780afb90cfd9498387426c25ef582e0744c7ba0d65145379", + "r2": "044d4a5b5daf00dd1fb4444ca2f8c3f9da1f750850131b376056f1c1e86a76a0", + "request_context": "74657374207265717565737420636f6e74657874" + }, + "CredentialResponse": { + "H_aux": "03e1e59f959c25774f3dfa5a23feac65243caf1faa716eab007ceeeb035f07414d", + "U": "0318d861aacf3f42ad356c3c0ad90184a8717762274bcdf4faa1f9d237e0b056b4", + "X0_aux": "03f1b707708c30fba247be2ca8aafa0873292f545d21389981533ef00d65300be8", + "X1_aux": "02567d76f0bd5015f6b8aa3307e03c3f71e70a323f3eb3badf386a2e8f9022342f", + "X2_aux": "034c380139f46b2ad96830f092f655a3d8a33f3760d1d9efb0459e397eb8b82854", + "b": "9ac9d836ef405f4c6c1de4de18d210c8496beca2fd39c4819c5d626a9aea1043", + "enc_U_prime": "037f41cad28206c524627829d9e9e8a64969c33ae6c6016314d986ff1e1634d763", + "proof": "2d8a68a4b7b74f3dc73f9a7340cc52d46b8c8131febb428675184eb6a2e0df3b6cf7804399c3592ed175e5fad01f4aec82b5b90c4a78e5db0cd6f2fe09e238312e8d407c1cce94f17b03a7354b7b6e8d46850282477793afef80eb18720852550678b87883070092a5b2858a2b413595e4ac3b947fc7d4b3fe546efe4d4d01973cfac7cd43e6e79ed8fe97a41b643c2451600acf3213ee65071b2f9549bc30e28390aea744983a22229618ff4abac9453e9245554584a6fea851d6809b1935ba4c0d00a751d9689793db574ee7204a5a3df4017e55681da53e62d289f63aafe6606febd903b267865a10b6ae454c0f42619a3c26b7928265b5a27f3cee4b050f" + }, + "Presentation1": { + "D_0": "022b4db8497de8afd15c0263986ce3c993a001bdb12a2b7a2a2c0df54c55aa17dd", + "U": "0387a786795f7d8891d0f61e62f34e00937a1b40fcfe3dc9cfafc3d371d6d6c9bf", + "U_prime_commit": "02e0ea4889c2837d3ed2442e1cfb9e766f156ee1bdf7d21468d09ff262e7424303", + "a": "a3c469d2d55062b463b17f45acd2fb16ee659265fc900356e553487ea846e9e7", + "m1_commit": "039dc93a94798626512cddeb21b79991d2760672f1ef285bca87853bbf4b432a1a", + "nonce": "00", + "nonce_blinding": "ed8b643e3c8ba74cc417b5bbfc4f42be8b4e6197ad59bed42269dc781f679d66", + "nonce_commit": "022b4db8497de8afd15c0263986ce3c993a001bdb12a2b7a2a2c0df54c55aa17dd", + "presentation_context": "746573742070726573656e746174696f6e20636f6e74657874", + "proof": "022b4db8497de8afd15c0263986ce3c993a001bdb12a2b7a2a2c0df54c55aa17ddd146335145e067552bbc98042918c893fe1c9dc0a003c6aab72bc02cf4c87c11c814099ed225a8f456aad03f72108f14f16fdf19d0ad3452e1e9cbd77a78df696b76a67c5b1fa7ed45499a55bacb1aee3d0d7f323f6323211ef4702b6db894e5e7ee14c0d9bcbcbc8b4d4b9b5a40aaaea0597c6a3b1f4e572bb1a153277e55566bbdb55b649021bf2f777b9130c2e375f560eee4691d04bd38e9571d94512578273265a40e847145a13f430bfe3eae2a2beffd05f427e88587709ce713a16fc9acc4f2ba0d724f2bfd182d5df4d038e74b5c35cc7c4aa7622c2682e040877eeb9a7251a638a8246070fbda88656b25c244bf273ba82fe17f2d74f66601aaf7ad399c8dd24728e580d5d9691a90214fa3154365c47657e54cc7bb97eb008d2828", + "r": "54925f70e9ec2128114c6ae8bfc6e1a1bf7ee4e261cbf119c0d5447a2eb0e934", + "tag": "036c46e86eac84c68806dad845a5be5d007263285022e3c98e298f97205e831ead", + "z": "9ed3c3ddb1b5ff64e55b1d0a4f58eee5c7472f211e0e8ccf4ab17a2f784d287c" + }, + "Presentation2": { + "D_0": "0383476a5a3d85aaa47e07023ce7ab6aa2ae775c3f6bed5e34924dfe9cc3deae14", + "U": "03e867857097728b99446da74095f6873142386996e8ce283479d1521ce94786e7", + "U_prime_commit": "028cd873d34082ca4952bb51c9a973a87348d44781a314506740d02baf144062ab", + "a": "ae14ddaf96907f2fee72069664e1883f00ef5c86e447ceac3a192fd3df7a44c1", + "m1_commit": "039c9d565204fc5d43ffd4c6eaa81172fffe5d34dadc2cf9cb9826e507dc96179f", + "nonce": "01", + "nonce_blinding": "90b8387fe4145c2d47a0f042c2611992bc4de48f81168a0d94b56c69ea0efff2", + "nonce_commit": "0383476a5a3d85aaa47e07023ce7ab6aa2ae775c3f6bed5e34924dfe9cc3deae14", + "presentation_context": "746573742070726573656e746174696f6e20636f6e74657874", + "proof": "0383476a5a3d85aaa47e07023ce7ab6aa2ae775c3f6bed5e34924dfe9cc3deae14bf818dad5dd87e9633e2384cd28774f8df1d839461291b880172b3c91f1630e711d4e62706768fec64f33d8d707cb11b9706c41c25de93be8a8233ab921733a387a45d3a857a5a556657745b1f86f15786d19917aeefcca8a0a9a3f8180abd29c0b5c0d8d65141618a71cc2bbf94c15dc3d7026aa011162aebf8f299d984dde281f6d4ceae8597deb73ea6379cd66934f54ff3c8973773bf9f6a9cf983d6b73c1edbc20ff037b3535191eca66ff05f913bebf530f560d41d530960eaea8f6a6c23316ae50ae38ef06d96b9865b0a6c268be1b27e23e6890be68f81fe7a4779ff74e5835492bb618a16463b97ec7beae9da0c03e79dad8b5addb41739077db27d320ec9e1019260efe2cbf6e9cba6871ffee3b5566d5e865c729c5e1d48529559", + "r": "f994bf66d0c7943ce97331da186e2311f8d2399bbbfe87f0eaaa7d50f7453630", + "tag": "03d790d0a88e92eb010554de4a327e0228773c8e0d865a86762f06a4900bc7d93b", + "z": "fa27efc5066bca91121642d629477eb10b5437eb07cd2783e9537c0ee7736dac" + }, + "ServerKey": { + "X0": "02984e3a37e21054d2e54614acb7f95a624afc7eb22de66131bf458fa478e2f62b", + "X1": "03913c27f2a2f50bfb0895d9f56aa583eb00be102f7a7ac662896fc0683a0223c3", + "X2": "0340bf069b5aa61019d6c6d826e7f5dc6f6767a288c5bc1738a7a9fa5bc3cf3d5f", + "x0": "1008f2c706ae2157c75e41b2d75695c7571644a476f2fb1ecc13f7e2c30b80a5", + "x1": "526e009578f6f25fdec992343f09f5e61aeedef029d8f9d0194746a1da89e7b5", + "x2": "549075ccd3d1c36b3546725c43e71942978901a9171ef7e771c86ca7b1140906", + "xb": "7276533ce3c89f04a007c2e8aa7d2e3abe4cb92695630c7d4575d16c47071974" + } + } +} \ No newline at end of file From 1cdfa9566aa25f50b9dfec958bacaef2b140879d Mon Sep 17 00:00:00 2001 From: Armando Faz Date: Wed, 22 Apr 2026 13:04:19 -0700 Subject: [PATCH 2/2] tests: refactor to simplify modules. --- tests/arc_tests/mod.rs | 16 ----------- .../protocol.rs => protocol/mod.rs} | 27 +++++++------------ tests/test_protocol.rs | 2 +- tests/test_vectors.rs | 8 +++--- .../{arc_tests/vectors.rs => vectors/mod.rs} | 10 +++---- 5 files changed, 19 insertions(+), 44 deletions(-) delete mode 100644 tests/arc_tests/mod.rs rename tests/{arc_tests/protocol.rs => protocol/mod.rs} (91%) rename tests/{arc_tests/vectors.rs => vectors/mod.rs} (97%) diff --git a/tests/arc_tests/mod.rs b/tests/arc_tests/mod.rs deleted file mode 100644 index 8ec5c14..0000000 --- a/tests/arc_tests/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright (c) 2025 Cloudflare, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -pub mod protocol; -pub mod vectors; diff --git a/tests/arc_tests/protocol.rs b/tests/protocol/mod.rs similarity index 91% rename from tests/arc_tests/protocol.rs rename to tests/protocol/mod.rs index 239dd86..d679e3c 100644 --- a/tests/arc_tests/protocol.rs +++ b/tests/protocol/mod.rs @@ -20,22 +20,17 @@ use arc::{ }; use rand::rngs::ThreadRng; -#[macro_export] -macro_rules! generate_tests { - ($suite:ty, tests: [ $($test_fn:ident),* $(,)? ]) => { - $( - #[test] - fn $test_fn() { - $crate::arc_tests::protocol::$test_fn::<$suite>(); - } - )* - }; -} - #[macro_export] macro_rules! all_arc_tests { ($suite:ty) => { - $crate::generate_tests!($suite, tests: [happy_path, multiple_presentations]); + #[test] + fn happy_path() { + $crate::protocol::happy_path::<$suite>(); + } + #[test] + fn multiple_presentations() { + $crate::protocol::multiple_presentations::<$suite>(); + } }; } @@ -49,8 +44,7 @@ macro_rules! test_serialization { }; } -#[allow(dead_code)] -pub fn happy_path() { +pub(crate) fn happy_path() { let req_context = b"RequestContext"; let csrng = &mut ThreadRng::default(); let key = SecretKey::::new(csrng); @@ -95,8 +89,7 @@ pub fn happy_path() { assert!(result.is_err()); } -#[allow(dead_code)] -pub fn multiple_presentations() { +pub(crate) fn multiple_presentations() { let request_ctx = b"RequestContext"; let csrng = &mut ThreadRng::default(); let key = SecretKey::::new(csrng); diff --git a/tests/test_protocol.rs b/tests/test_protocol.rs index 2bba2a2..5e68a6c 100644 --- a/tests/test_protocol.rs +++ b/tests/test_protocol.rs @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod arc_tests; +mod protocol; #[cfg(feature = "suite_p256")] mod p256 { diff --git a/tests/test_vectors.rs b/tests/test_vectors.rs index 24711a9..ee58364 100644 --- a/tests/test_vectors.rs +++ b/tests/test_vectors.rs @@ -12,15 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. -mod arc_tests; +mod vectors; #[cfg(feature = "suite_p256")] mod p256 { + use crate::vectors::{SuiteVector, test_vectors_arc}; - use crate::arc_tests::vectors::{SuiteVector, test_vectors_arc}; - use serde::Deserialize; - - #[derive(Deserialize)] + #[derive(serde::Deserialize)] struct Vector { #[serde(rename = "ARCV1-P256")] suite: SuiteVector, diff --git a/tests/arc_tests/vectors.rs b/tests/vectors/mod.rs similarity index 97% rename from tests/arc_tests/vectors.rs rename to tests/vectors/mod.rs index 3c1a47b..821eede 100644 --- a/tests/arc_tests/vectors.rs +++ b/tests/vectors/mod.rs @@ -186,7 +186,7 @@ impl PresentationVector { } #[derive(Debug, Deserialize)] -pub struct SuiteVector { +pub(crate) struct SuiteVector { #[serde(rename = "Credential")] credential: CredentialVector, #[serde(rename = "CredentialRequest")] @@ -201,10 +201,10 @@ pub struct SuiteVector { server_key: ServerKeysVector, } -pub struct TestDrng(Shake128); +struct TestDrng(Shake128); impl TestDrng { - pub fn from_seed(seed_label: &[u8]) -> Self { + fn from_seed(seed_label: &[u8]) -> Self { let mut sponge = Shake128::default(); let mut initial_block = [0u8; 168]; let domain = b"sigma-proofs/TestDRNG/SHAKE128"; @@ -245,8 +245,8 @@ impl RngCore for TestDrng { Ok(()) } } -#[allow(dead_code)] -pub fn test_vectors_arc(v: SuiteVector) { + +pub(crate) fn test_vectors_arc(v: SuiteVector) { const SEED: &[u8] = b"test vector seed"; let rng = &mut TestDrng::from_seed(SEED); let private_key = SecretKey::::new(rng);