22use crate :: xdr:: ScVal ;
33use crate :: {
44 env:: internal:: { self , BytesObject , U256Val , U64Val } ,
5- impl_bytesn_repr ,
5+ impl_bytesn_repr_without_from_bytes ,
66 unwrap:: { UnwrapInfallible , UnwrapOptimized } ,
77 Bytes , BytesN , ConversionError , Env , IntoVal , TryFromVal , Val , Vec , U256 ,
88} ;
@@ -162,10 +162,55 @@ pub struct Fp2(BytesN<FP2_SERIALIZED_SIZE>);
162162#[ repr( transparent) ]
163163pub struct Fr ( U256 ) ;
164164
165- impl_bytesn_repr ! ( G1Affine , G1_SERIALIZED_SIZE ) ;
166- impl_bytesn_repr ! ( G2Affine , G2_SERIALIZED_SIZE ) ;
167- impl_bytesn_repr ! ( Fp , FP_SERIALIZED_SIZE ) ;
168- impl_bytesn_repr ! ( Fp2 , FP2_SERIALIZED_SIZE ) ;
165+ impl_bytesn_repr_without_from_bytes ! ( G1Affine , G1_SERIALIZED_SIZE ) ;
166+ impl_bytesn_repr_without_from_bytes ! ( G2Affine , G2_SERIALIZED_SIZE ) ;
167+ impl_bytesn_repr_without_from_bytes ! ( Fp , FP_SERIALIZED_SIZE ) ;
168+ impl_bytesn_repr_without_from_bytes ! ( Fp2 , FP2_SERIALIZED_SIZE ) ;
169+
170+ // BLS12-381 base field modulus p in big-endian bytes.
171+ // p = 0x1a0111ea397fe69a4b1ba7b6434bacd764774b84f38512bf6730d2a0f6b0f6241eabfffeb153ffffb9feffffffffaaab
172+ const BLS12_381_FP_MODULUS_BE : [ u8 ; FP_SERIALIZED_SIZE ] = [
173+ 0x1a , 0x01 , 0x11 , 0xea , 0x39 , 0x7f , 0xe6 , 0x9a , 0x4b , 0x1b , 0xa7 , 0xb6 , 0x43 , 0x4b , 0xac , 0xd7 ,
174+ 0x64 , 0x77 , 0x4b , 0x84 , 0xf3 , 0x85 , 0x12 , 0xbf , 0x67 , 0x30 , 0xd2 , 0xa0 , 0xf6 , 0xb0 , 0xf6 , 0x24 ,
175+ 0x1e , 0xab , 0xff , 0xfe , 0xb1 , 0x53 , 0xff , 0xff , 0xb9 , 0xfe , 0xff , 0xff , 0xff , 0xff , 0xaa , 0xab ,
176+ ] ;
177+
178+ fn validate_fp ( bytes : & [ u8 ; FP_SERIALIZED_SIZE ] ) {
179+ if bytes >= & BLS12_381_FP_MODULUS_BE {
180+ sdk_panic ! ( "Bls12-381: Invalid Fp" ) ;
181+ }
182+ }
183+
184+ fn validate_fp2 ( bytes : & [ u8 ; FP2_SERIALIZED_SIZE ] ) {
185+ validate_fp ( bytes[ 0 ..FP_SERIALIZED_SIZE ] . try_into ( ) . unwrap ( ) ) ;
186+ validate_fp ( bytes[ FP_SERIALIZED_SIZE ..] . try_into ( ) . unwrap ( ) ) ;
187+ }
188+
189+ impl G1Affine {
190+ pub fn from_bytes ( bytes : BytesN < G1_SERIALIZED_SIZE > ) -> Self {
191+ Self ( bytes)
192+ }
193+ }
194+
195+ impl G2Affine {
196+ pub fn from_bytes ( bytes : BytesN < G2_SERIALIZED_SIZE > ) -> Self {
197+ Self ( bytes)
198+ }
199+ }
200+
201+ impl Fp {
202+ pub fn from_bytes ( bytes : BytesN < FP_SERIALIZED_SIZE > ) -> Self {
203+ validate_fp ( & bytes. to_array ( ) ) ;
204+ Self ( bytes)
205+ }
206+ }
207+
208+ impl Fp2 {
209+ pub fn from_bytes ( bytes : BytesN < FP2_SERIALIZED_SIZE > ) -> Self {
210+ validate_fp2 ( & bytes. to_array ( ) ) ;
211+ Self ( bytes)
212+ }
213+ }
169214
170215impl Fp {
171216 pub fn env ( & self ) -> & Env {
@@ -476,9 +521,28 @@ impl Fr {
476521 }
477522}
478523
524+ // BLS12-381 scalar field modulus r in big-endian bytes.
525+ // r = 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000001
526+ const BLS12_381_FR_MODULUS_BE : [ u8 ; 32 ] = [
527+ 0x73 , 0xed , 0xa7 , 0x53 , 0x29 , 0x9d , 0x7d , 0x48 , 0x33 , 0x39 , 0xd8 , 0x08 , 0x09 , 0xa1 , 0xd8 , 0x05 ,
528+ 0x53 , 0xbd , 0xa4 , 0x02 , 0xff , 0xfe , 0x5b , 0xfe , 0xff , 0xff , 0xff , 0xff , 0x00 , 0x00 , 0x00 , 0x01 ,
529+ ] ;
530+
531+ fn fr_modulus ( env : & Env ) -> U256 {
532+ U256 :: from_be_bytes ( env, & Bytes :: from_array ( env, & BLS12_381_FR_MODULUS_BE ) )
533+ }
534+
479535impl From < U256 > for Fr {
480536 fn from ( value : U256 ) -> Self {
481- Self ( value)
537+ // Keep all Fr construction paths canonical by reducing modulo r here.
538+ // Skip the expensive rem_euclid when value is already canonical (< r),
539+ // which is always the case for host-returned arithmetic results.
540+ let modulus = fr_modulus ( value. env ( ) ) ;
541+ if value >= modulus {
542+ Self ( value. rem_euclid ( & modulus) )
543+ } else {
544+ Self ( value)
545+ }
482546 }
483547}
484548
@@ -493,7 +557,7 @@ impl TryFromVal<Env, Val> for Fr {
493557
494558 fn try_from_val ( env : & Env , val : & Val ) -> Result < Self , Self :: Error > {
495559 let u = U256 :: try_from_val ( env, val) ?;
496- Ok ( Fr ( u ) )
560+ Ok ( u . into ( ) )
497561 }
498562}
499563
@@ -761,3 +825,178 @@ impl Bls12_381 {
761825 U256 :: try_from_val ( env, & v) . unwrap_infallible ( ) . into ( )
762826 }
763827}
828+
829+ #[ cfg( test) ]
830+ mod test {
831+ use super :: * ;
832+ use crate :: bytesn;
833+
834+ #[ test]
835+ fn test_fr_eq_both_unreduced ( ) {
836+ let env = Env :: default ( ) ;
837+ let r = fr_modulus ( & env) ;
838+ let one = U256 :: from_u32 ( & env, 1 ) ;
839+
840+ let a = Fr :: from_u256 ( r. add ( & one) ) ;
841+ let b = Fr :: from_u256 ( one. clone ( ) ) ;
842+ assert_eq ! ( a, b) ;
843+
844+ let two_r_plus_one = r. add ( & r) . add ( & one) ;
845+ let c = Fr :: from_u256 ( two_r_plus_one) ;
846+ assert_eq ! ( a, c) ;
847+ assert_eq ! ( b, c) ;
848+ }
849+
850+ #[ test]
851+ fn test_fr_eq_unreduced_vs_zero ( ) {
852+ let env = Env :: default ( ) ;
853+ let r = fr_modulus ( & env) ;
854+ let zero = U256 :: from_u32 ( & env, 0 ) ;
855+
856+ let a = Fr :: from_u256 ( r) ;
857+ let b = Fr :: from_u256 ( zero) ;
858+ assert_eq ! ( a, b) ;
859+ }
860+
861+ #[ test]
862+ fn test_fr_reduced_value_unchanged ( ) {
863+ let env = Env :: default ( ) ;
864+ let r = fr_modulus ( & env) ;
865+ let val = r. sub ( & U256 :: from_u32 ( & env, 1 ) ) ;
866+
867+ let fr = Fr :: from_u256 ( val. clone ( ) ) ;
868+ assert_eq ! ( fr. to_u256( ) , val) ;
869+
870+ let fr42 = Fr :: from_u256 ( U256 :: from_u32 ( & env, 42 ) ) ;
871+ assert_eq ! ( fr42. to_u256( ) , U256 :: from_u32( & env, 42 ) ) ;
872+ }
873+
874+ #[ test]
875+ fn test_fr_from_bytes_reduces ( ) {
876+ let env = Env :: default ( ) ;
877+ let one_fr = Fr :: from_u256 ( U256 :: from_u32 ( & env, 1 ) ) ;
878+
879+ // BLS12-381 r+1 as big-endian bytes
880+ let fr_from_bytes = Fr :: from_bytes ( bytesn ! (
881+ & env,
882+ 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002
883+ ) ) ;
884+ assert_eq ! ( fr_from_bytes, one_fr) ;
885+ }
886+
887+ #[ test]
888+ fn test_fr_try_from_val_reduces ( ) {
889+ let env = Env :: default ( ) ;
890+ let r = fr_modulus ( & env) ;
891+ let one = U256 :: from_u32 ( & env, 1 ) ;
892+
893+ let unreduced_u256 = r. add ( & one) ;
894+ let val: Val = unreduced_u256. into_val ( & env) ;
895+ let fr_from_val: Fr = val. into_val ( & env) ;
896+ let fr_one = Fr :: from_u256 ( one) ;
897+ assert_eq ! ( fr_from_val, fr_one) ;
898+ }
899+
900+ #[ test]
901+ fn test_fr_u256_into_reduces ( ) {
902+ let env = Env :: default ( ) ;
903+ let r = fr_modulus ( & env) ;
904+ let one = U256 :: from_u32 ( & env, 1 ) ;
905+
906+ let fr: Fr = r. add ( & one) . into ( ) ;
907+ let fr_one: Fr = one. into ( ) ;
908+ assert_eq ! ( fr, fr_one) ;
909+ }
910+
911+ #[ test]
912+ fn test_fr_eq_unreduced_vs_host_computed ( ) {
913+ let env = Env :: default ( ) ;
914+ let bls = Bls12_381 :: new ( & env) ;
915+ let r = fr_modulus ( & env) ;
916+ let five = U256 :: from_u32 ( & env, 5 ) ;
917+
918+ let user_fr = Fr :: from_u256 ( r. add ( & five) ) ;
919+ let host_fr = bls. fr_add (
920+ & Fr :: from_u256 ( U256 :: from_u32 ( & env, 2 ) ) ,
921+ & Fr :: from_u256 ( U256 :: from_u32 ( & env, 3 ) ) ,
922+ ) ;
923+ assert_eq ! ( user_fr, host_fr) ;
924+ }
925+
926+ // Fp validation tests
927+
928+ #[ test]
929+ fn test_fp_max_valid_accepted ( ) {
930+ let env = Env :: default ( ) ;
931+ let mut p_minus_1 = BLS12_381_FP_MODULUS_BE ;
932+ p_minus_1[ FP_SERIALIZED_SIZE - 1 ] -= 1 ;
933+ let _ = Fp :: from_array ( & env, & p_minus_1) ;
934+ }
935+
936+ #[ test]
937+ #[ should_panic( expected = "Bls12-381: Invalid Fp" ) ]
938+ fn test_fp_at_modulus_panics ( ) {
939+ let env = Env :: default ( ) ;
940+ let _ = Fp :: from_array ( & env, & BLS12_381_FP_MODULUS_BE ) ;
941+ }
942+
943+ #[ test]
944+ #[ should_panic( expected = "Bls12-381: Invalid Fp" ) ]
945+ fn test_fp_above_modulus_panics ( ) {
946+ let env = Env :: default ( ) ;
947+ let mut above = BLS12_381_FP_MODULUS_BE ;
948+ above[ FP_SERIALIZED_SIZE - 1 ] += 1 ;
949+ let _ = Fp :: from_array ( & env, & above) ;
950+ }
951+
952+ #[ test]
953+ fn test_fp_from_bytes_validates ( ) {
954+ let env = Env :: default ( ) ;
955+ let _ = Fp :: from_bytes ( BytesN :: from_array ( & env, & [ 0u8 ; FP_SERIALIZED_SIZE ] ) ) ;
956+ }
957+
958+ #[ test]
959+ #[ should_panic( expected = "Bls12-381: Invalid Fp" ) ]
960+ fn test_fp_from_bytes_rejects_modulus ( ) {
961+ let env = Env :: default ( ) ;
962+ let _ = Fp :: from_bytes ( BytesN :: from_array ( & env, & BLS12_381_FP_MODULUS_BE ) ) ;
963+ }
964+
965+ #[ test]
966+ #[ should_panic( expected = "Bls12-381: Invalid Fp" ) ]
967+ fn test_fp_try_from_val_rejects_modulus ( ) {
968+ let env = Env :: default ( ) ;
969+ let bytes = BytesN :: from_array ( & env, & BLS12_381_FP_MODULUS_BE ) ;
970+ let val: Val = bytes. into_val ( & env) ;
971+ let _: Fp = val. into_val ( & env) ;
972+ }
973+
974+ #[ test]
975+ #[ should_panic( expected = "Bls12-381: Invalid Fp" ) ]
976+ fn test_fp2_component_above_modulus_panics ( ) {
977+ let env = Env :: default ( ) ;
978+ let mut fp2_bytes = [ 0u8 ; FP2_SERIALIZED_SIZE ] ;
979+ fp2_bytes[ 0 ..FP_SERIALIZED_SIZE ] . copy_from_slice ( & BLS12_381_FP_MODULUS_BE ) ;
980+ let _ = Fp2 :: from_array ( & env, & fp2_bytes) ;
981+ }
982+
983+ #[ test]
984+ #[ should_panic( expected = "Bls12-381: Invalid Fp" ) ]
985+ fn test_fp2_second_component_above_modulus_panics ( ) {
986+ let env = Env :: default ( ) ;
987+ let mut fp2_bytes = [ 0u8 ; FP2_SERIALIZED_SIZE ] ;
988+ fp2_bytes[ FP_SERIALIZED_SIZE ..] . copy_from_slice ( & BLS12_381_FP_MODULUS_BE ) ;
989+ let _ = Fp2 :: from_array ( & env, & fp2_bytes) ;
990+ }
991+
992+ #[ test]
993+ fn test_fp2_max_valid_accepted ( ) {
994+ let env = Env :: default ( ) ;
995+ let mut p_minus_1 = BLS12_381_FP_MODULUS_BE ;
996+ p_minus_1[ FP_SERIALIZED_SIZE - 1 ] -= 1 ;
997+ let mut fp2_bytes = [ 0u8 ; FP2_SERIALIZED_SIZE ] ;
998+ fp2_bytes[ 0 ..FP_SERIALIZED_SIZE ] . copy_from_slice ( & p_minus_1) ;
999+ fp2_bytes[ FP_SERIALIZED_SIZE ..] . copy_from_slice ( & p_minus_1) ;
1000+ let _ = Fp2 :: from_array ( & env, & fp2_bytes) ;
1001+ }
1002+ }
0 commit comments