@@ -9,3 +9,227 @@ test_group!(pairing_output; ark_ec::pairing::PairingOutput<Bn254>; msm);
99test_pairing ! ( pairing; crate :: Bn254 ) ;
1010test_group ! ( g1_glv; G1Projective ; glv) ;
1111test_group ! ( g2_glv; G2Projective ; glv) ;
12+
13+ // Test compressed pairing.
14+ #[ cfg( test) ]
15+ mod test {
16+ use ark_ec:: pairing:: { CompressedPairing , Pairing } ;
17+ use ark_ff:: { AdditiveGroup , CyclotomicMultSubgroup , Field , UniformRand } ;
18+ use ark_std:: { test_rng, vec:: Vec } ;
19+
20+ use crate :: {
21+ compressible_fq12_to_fq12, fq12_to_compressible_fq12, torus_compress_fq6,
22+ torus_compress_psi_6_pow_to_two_fq2, torus_decompress_fq6, Bn254 , CompressibleFq12 , Fq12 ,
23+ Fq2 , Fq6 , G1Projective , G2Projective ,
24+ } ;
25+
26+ const PSI_6 : [ u64 ; 32 ] = [
27+ 0x72ab9d4cf9110b20 ,
28+ 0x973cd2a37a1b4236 ,
29+ 0x1ba98b1bc336a6d5 ,
30+ 0x2dfcbbaca5d60846 ,
31+ 0xd5439cc568e9448a ,
32+ 0xe1db0edd8549f5a5 ,
33+ 0x6f93e1753d4af731 ,
34+ 0x49c8a9c36c6fee88 ,
35+ 0x15f3f80815d4b9fa ,
36+ 0x1ba650a506fe3ed0 ,
37+ 0xaefe7d8fff4f3dff ,
38+ 0x6ac0657132447def ,
39+ 0xf1f3ac284dd0152c ,
40+ 0x37018c9976be9e50 ,
41+ 0x22c60855715fb2d0 ,
42+ 0x208847a342f135b0 ,
43+ 0x3ee1e2cb94f42a1d ,
44+ 0x3c4a7ccbcfffdab3 ,
45+ 0xad8c7c82ecaf3f33 ,
46+ 0x6ca3fffc37db0759 ,
47+ 0x37cd075497b1b668 ,
48+ 0xea10af7b063fb6d2 ,
49+ 0x4c05f030809f6505 ,
50+ 0x2a2ce31f8f0d0104 ,
51+ 0x8cabad13097c3c76 ,
52+ 0x4d2a25ff065eb903 ,
53+ 0xa3df56ffaff05a83 ,
54+ 0x2d5b0867d19408a1 ,
55+ 0x223eaadf9b381c71 ,
56+ 0xe8809c2c51000097 ,
57+ 0x0e97a02f2739dcf3 ,
58+ 0x1b59e685800b ,
59+ ] ;
60+
61+ #[ test]
62+ fn test_compression ( ) {
63+ let q2: [ u64 ; 8 ] = [
64+ 0x3b5458a2275d69b1 ,
65+ 0xa602072d09eac101 ,
66+ 0x4a50189c6d96cadc ,
67+ 0x04689e957a1242c8 ,
68+ 0x26edfa5c34c6b38d ,
69+ 0xb00b855116375606 ,
70+ 0x599a6f7c0348d21c ,
71+ 0x925c4b8763cbf9c ,
72+ ] ;
73+
74+ let num_trials = 5 ;
75+ let mut rng = test_rng ( ) ;
76+
77+ // Step by step testing of the compression algorithm described in
78+ // https://eprint.iacr.org/2007/429.pdf Proposition 1
79+ for _ in 0 ..num_trials {
80+ let fq12_ele = CompressibleFq12 :: rand ( & mut rng) ;
81+ let c1 = fq12_ele. torus_compress_base_order_minus_one_pow ( ) ;
82+ let c1_pow = -c1. pow ( q2) ;
83+ let compressed_prod = CompressibleFq12 :: mul_torus_compressed_elements ( c1_pow, c1) ;
84+
85+ assert_eq ! (
86+ CompressibleFq12 :: torus_decompress( compressed_prod) ,
87+ fq12_ele. pow( PSI_6 )
88+ ) ;
89+ let compressed_fq6 = torus_compress_fq6 ( compressed_prod) ;
90+ let decompressed_fq6 = torus_decompress_fq6 ( compressed_fq6) ;
91+ assert_eq ! ( compressed_prod, decompressed_fq6) ;
92+ }
93+
94+ // Test that the compression does not work with Fq12 as expected because the generator of
95+ // the quadratic extension inside Fq12 is not of degree 2 over Fq2.
96+ for _ in 0 ..num_trials {
97+ let fq12_ele = Fq12 :: rand ( & mut rng) ;
98+ let c1 = fq12_ele. torus_compress_base_order_minus_one_pow ( ) ;
99+ let c1_pow = -c1. pow ( q2) ;
100+ let compressed_prod = Fq12 :: mul_torus_compressed_elements ( c1_pow, c1) ;
101+
102+ assert_ne ! ( Fq12 :: torus_decompress( compressed_prod) , fq12_ele. pow( PSI_6 ) ) ;
103+ }
104+
105+ // Test compression of an CompressibleFq12 element e2e.
106+ for _ in 0 ..num_trials {
107+ let compressible_fq12 = CompressibleFq12 :: rand ( & mut rng) ;
108+ let compressed_fq12 = torus_compress_psi_6_pow_to_two_fq2 ( compressible_fq12) ;
109+ let decompressed_fq12 = compressed_fq12. decompress ( ) ;
110+ assert_eq ! ( compressible_fq12. pow( PSI_6 ) , decompressed_fq12) ;
111+ }
112+ }
113+
114+ #[ test]
115+ fn test_compressible_fq12_to_fq12_conversion ( ) {
116+ let num_trials = 100 ;
117+ let mut rng = test_rng ( ) ;
118+
119+ let x = Fq6 {
120+ c0 : Fq2 :: ZERO ,
121+ c1 : Fq2 :: ONE ,
122+ c2 : Fq2 :: ZERO ,
123+ } ;
124+
125+ for _ in 0 ..num_trials {
126+ // a = c0 + c1 * residue^(1/2)
127+ let a = CompressibleFq12 :: rand ( & mut rng) ;
128+ // a_prime = c0 + c1 * x * residue^(1/6), where x = residue^(1/3)
129+ let a_prime = compressible_fq12_to_fq12 ( a) ;
130+ assert_eq ! ( a_prime. c1, x * a. c1) ;
131+ }
132+
133+ for _ in 0 ..num_trials {
134+ // a = c0 + c1 * residue^(1/6)
135+ let a = Fq12 :: rand ( & mut rng) ;
136+ // a_prime = c0 + c1 / x * residue^(1/2), where x = residue^(1/3)
137+ let a_prime = fq12_to_compressible_fq12 ( a) ;
138+ assert_eq ! ( a_prime. c1, x. inverse( ) . unwrap( ) * a. c1) ;
139+ }
140+
141+ // Check inverses
142+ for _ in 0 ..num_trials {
143+ let fq12_ele = Fq12 :: rand ( & mut rng) ;
144+ let compressible_fq12 = fq12_to_compressible_fq12 ( fq12_ele) ;
145+ let fq12_back = compressible_fq12_to_fq12 ( compressible_fq12) ;
146+ assert_eq ! ( fq12_ele, fq12_back) ;
147+
148+ let compressible_fq12 = CompressibleFq12 :: rand ( & mut rng) ;
149+ let fq12_ele = compressible_fq12_to_fq12 ( compressible_fq12) ;
150+ let compressible_fq12_back = fq12_to_compressible_fq12 ( fq12_ele) ;
151+ assert_eq ! ( compressible_fq12, compressible_fq12_back) ;
152+ }
153+
154+ // Homomorphic property
155+ for _ in 0 ..num_trials {
156+ let a = CompressibleFq12 :: rand ( & mut rng) ;
157+ let b = CompressibleFq12 :: rand ( & mut rng) ;
158+ let c = a * b;
159+ let a_prime = compressible_fq12_to_fq12 ( a) ;
160+ let b_prime = compressible_fq12_to_fq12 ( b) ;
161+ let c_prime = compressible_fq12_to_fq12 ( c) ;
162+ assert_eq ! ( a_prime * b_prime, c_prime) ;
163+
164+ let a = Fq12 :: rand ( & mut rng) ;
165+ let b = Fq12 :: rand ( & mut rng) ;
166+ let c = a * b;
167+ let a_prime = fq12_to_compressible_fq12 ( a) ;
168+ let b_prime = fq12_to_compressible_fq12 ( b) ;
169+ let c_prime = fq12_to_compressible_fq12 ( c) ;
170+ assert_eq ! ( a_prime * b_prime, c_prime) ;
171+ }
172+
173+ // Check that converting an Fq12 element to a CompressibleFq12 element before compressing
174+ // its psi_6 power works, where decompressing is simply converting back to an Fq12 element.
175+ for _ in 0 ..num_trials {
176+ let a = Fq12 :: rand ( & mut rng) ;
177+ let a_prime = fq12_to_compressible_fq12 ( a) ;
178+ let compressed = torus_compress_psi_6_pow_to_two_fq2 ( a_prime) ;
179+
180+ let decompressed = compressed. decompress ( ) ;
181+ let decompressed_fq12 = compressible_fq12_to_fq12 ( decompressed) ;
182+ assert_eq ! ( a. pow( PSI_6 ) , decompressed_fq12) ;
183+ }
184+ }
185+
186+ #[ test]
187+ fn test_compressed_pairing ( ) {
188+ let num_trials = 10 ;
189+ let mut rng = test_rng ( ) ;
190+
191+ // Test pairing e2e.
192+ for _ in 0 ..num_trials {
193+ let g1 = G1Projective :: rand ( & mut rng) ;
194+ let g2 = G2Projective :: rand ( & mut rng) ;
195+ let pairing_value = Bn254 :: pairing ( g1, g2) . 0 ;
196+ let compressed_pairing_value = Bn254 :: compressed_pairing ( g1, g2) ;
197+ assert_eq ! ( pairing_value, compressed_pairing_value. decompress_to_fq12( ) ) ;
198+ }
199+
200+ // Test multi-pairing e2e.
201+ let num_pairs = 10 ;
202+ for _ in 0 ..num_trials {
203+ let g1 = ( 0 ..num_pairs)
204+ . map ( |_| G1Projective :: rand ( & mut rng) )
205+ . collect :: < Vec < _ > > ( ) ;
206+ let g2 = ( 0 ..num_pairs)
207+ . map ( |_| G2Projective :: rand ( & mut rng) )
208+ . collect :: < Vec < _ > > ( ) ;
209+ let pairing_value = Bn254 :: multi_pairing ( g1. iter ( ) . cloned ( ) , g2. iter ( ) . cloned ( ) ) . 0 ;
210+ let compressed_pairing_value =
211+ Bn254 :: compressed_multi_pairing ( g1. iter ( ) . cloned ( ) , g2. iter ( ) . cloned ( ) ) ;
212+ assert_eq ! ( pairing_value, compressed_pairing_value. decompress_to_fq12( ) ) ;
213+ }
214+
215+ // This is more for documentation purposes. For the compressed pairing calculation, we swap
216+ // the order of computing exponentiation by \Psi_6(q^2) and \Phi_6(q^2) (see documentation
217+ // for their definitions). The Miller loop output is not in the cyclotomic subgroup of the
218+ // right order where the optimization (defined in the CyclotomicMultSubgroup trait) can be
219+ // applied.
220+ for _ in 0 ..num_trials {
221+ let g1 = G1Projective :: rand ( & mut rng) ;
222+ let g2 = G2Projective :: rand ( & mut rng) ;
223+
224+ let miller_loop_output = Bn254 :: multi_miller_loop ( [ g1] , [ g2] ) . 0 ;
225+ assert_ne ! (
226+ miller_loop_output. cyclotomic_inverse( ) ,
227+ miller_loop_output. inverse( )
228+ ) ;
229+ assert_ne ! (
230+ miller_loop_output. cyclotomic_exp( PSI_6 ) ,
231+ miller_loop_output. pow( PSI_6 )
232+ ) ;
233+ }
234+ }
235+ }
0 commit comments