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
@@ -773,6 +837,7 @@ impl Bls12_381 {
773837#[ cfg( test) ]
774838mod test {
775839 use super :: * ;
840+ use crate :: bytesn;
776841
777842 #[ test]
778843 fn test_g1affine_to_val ( ) {
@@ -839,4 +904,173 @@ mod test {
839904
840905 assert_eq ! ( fr, rt) ;
841906 }
907+
908+ #[ test]
909+ fn test_fr_eq_both_unreduced ( ) {
910+ let env = Env :: default ( ) ;
911+ let r = fr_modulus ( & env) ;
912+ let one = U256 :: from_u32 ( & env, 1 ) ;
913+
914+ let a = Fr :: from_u256 ( r. add ( & one) ) ;
915+ let b = Fr :: from_u256 ( one. clone ( ) ) ;
916+ assert_eq ! ( a, b) ;
917+
918+ let two_r_plus_one = r. add ( & r) . add ( & one) ;
919+ let c = Fr :: from_u256 ( two_r_plus_one) ;
920+ assert_eq ! ( a, c) ;
921+ assert_eq ! ( b, c) ;
922+ }
923+
924+ #[ test]
925+ fn test_fr_eq_unreduced_vs_zero ( ) {
926+ let env = Env :: default ( ) ;
927+ let r = fr_modulus ( & env) ;
928+ let zero = U256 :: from_u32 ( & env, 0 ) ;
929+
930+ let a = Fr :: from_u256 ( r) ;
931+ let b = Fr :: from_u256 ( zero) ;
932+ assert_eq ! ( a, b) ;
933+ }
934+
935+ #[ test]
936+ fn test_fr_reduced_value_unchanged ( ) {
937+ let env = Env :: default ( ) ;
938+ let r = fr_modulus ( & env) ;
939+ let val = r. sub ( & U256 :: from_u32 ( & env, 1 ) ) ;
940+
941+ let fr = Fr :: from_u256 ( val. clone ( ) ) ;
942+ assert_eq ! ( fr. to_u256( ) , val) ;
943+
944+ let fr42 = Fr :: from_u256 ( U256 :: from_u32 ( & env, 42 ) ) ;
945+ assert_eq ! ( fr42. to_u256( ) , U256 :: from_u32( & env, 42 ) ) ;
946+ }
947+
948+ #[ test]
949+ fn test_fr_from_bytes_reduces ( ) {
950+ let env = Env :: default ( ) ;
951+ let one_fr = Fr :: from_u256 ( U256 :: from_u32 ( & env, 1 ) ) ;
952+
953+ // BLS12-381 r+1 as big-endian bytes
954+ let fr_from_bytes = Fr :: from_bytes ( bytesn ! (
955+ & env,
956+ 0x73eda753299d7d483339d80809a1d80553bda402fffe5bfeffffffff00000002
957+ ) ) ;
958+ assert_eq ! ( fr_from_bytes, one_fr) ;
959+ }
960+
961+ #[ test]
962+ fn test_fr_try_from_val_reduces ( ) {
963+ let env = Env :: default ( ) ;
964+ let r = fr_modulus ( & env) ;
965+ let one = U256 :: from_u32 ( & env, 1 ) ;
966+
967+ let unreduced_u256 = r. add ( & one) ;
968+ let val: Val = unreduced_u256. into_val ( & env) ;
969+ let fr_from_val: Fr = val. into_val ( & env) ;
970+ let fr_one = Fr :: from_u256 ( one) ;
971+ assert_eq ! ( fr_from_val, fr_one) ;
972+ }
973+
974+ #[ test]
975+ fn test_fr_u256_into_reduces ( ) {
976+ let env = Env :: default ( ) ;
977+ let r = fr_modulus ( & env) ;
978+ let one = U256 :: from_u32 ( & env, 1 ) ;
979+
980+ let fr: Fr = r. add ( & one) . into ( ) ;
981+ let fr_one: Fr = one. into ( ) ;
982+ assert_eq ! ( fr, fr_one) ;
983+ }
984+
985+ #[ test]
986+ fn test_fr_eq_unreduced_vs_host_computed ( ) {
987+ let env = Env :: default ( ) ;
988+ let bls = Bls12_381 :: new ( & env) ;
989+ let r = fr_modulus ( & env) ;
990+ let five = U256 :: from_u32 ( & env, 5 ) ;
991+
992+ let user_fr = Fr :: from_u256 ( r. add ( & five) ) ;
993+ let host_fr = bls. fr_add (
994+ & Fr :: from_u256 ( U256 :: from_u32 ( & env, 2 ) ) ,
995+ & Fr :: from_u256 ( U256 :: from_u32 ( & env, 3 ) ) ,
996+ ) ;
997+ assert_eq ! ( user_fr, host_fr) ;
998+ }
999+
1000+ // Fp validation tests
1001+
1002+ #[ test]
1003+ fn test_fp_max_valid_accepted ( ) {
1004+ let env = Env :: default ( ) ;
1005+ let mut p_minus_1 = BLS12_381_FP_MODULUS_BE ;
1006+ p_minus_1[ FP_SERIALIZED_SIZE - 1 ] -= 1 ;
1007+ let _ = Fp :: from_array ( & env, & p_minus_1) ;
1008+ }
1009+
1010+ #[ test]
1011+ #[ should_panic( expected = "Bls12-381: Invalid Fp" ) ]
1012+ fn test_fp_at_modulus_panics ( ) {
1013+ let env = Env :: default ( ) ;
1014+ let _ = Fp :: from_array ( & env, & BLS12_381_FP_MODULUS_BE ) ;
1015+ }
1016+
1017+ #[ test]
1018+ #[ should_panic( expected = "Bls12-381: Invalid Fp" ) ]
1019+ fn test_fp_above_modulus_panics ( ) {
1020+ let env = Env :: default ( ) ;
1021+ let mut above = BLS12_381_FP_MODULUS_BE ;
1022+ above[ FP_SERIALIZED_SIZE - 1 ] += 1 ;
1023+ let _ = Fp :: from_array ( & env, & above) ;
1024+ }
1025+
1026+ #[ test]
1027+ fn test_fp_from_bytes_validates ( ) {
1028+ let env = Env :: default ( ) ;
1029+ let _ = Fp :: from_bytes ( BytesN :: from_array ( & env, & [ 0u8 ; FP_SERIALIZED_SIZE ] ) ) ;
1030+ }
1031+
1032+ #[ test]
1033+ #[ should_panic( expected = "Bls12-381: Invalid Fp" ) ]
1034+ fn test_fp_from_bytes_rejects_modulus ( ) {
1035+ let env = Env :: default ( ) ;
1036+ let _ = Fp :: from_bytes ( BytesN :: from_array ( & env, & BLS12_381_FP_MODULUS_BE ) ) ;
1037+ }
1038+
1039+ #[ test]
1040+ #[ should_panic( expected = "Bls12-381: Invalid Fp" ) ]
1041+ fn test_fp_try_from_val_rejects_modulus ( ) {
1042+ let env = Env :: default ( ) ;
1043+ let bytes = BytesN :: from_array ( & env, & BLS12_381_FP_MODULUS_BE ) ;
1044+ let val: Val = bytes. into_val ( & env) ;
1045+ let _: Fp = val. into_val ( & env) ;
1046+ }
1047+
1048+ #[ test]
1049+ #[ should_panic( expected = "Bls12-381: Invalid Fp" ) ]
1050+ fn test_fp2_component_above_modulus_panics ( ) {
1051+ let env = Env :: default ( ) ;
1052+ let mut fp2_bytes = [ 0u8 ; FP2_SERIALIZED_SIZE ] ;
1053+ fp2_bytes[ 0 ..FP_SERIALIZED_SIZE ] . copy_from_slice ( & BLS12_381_FP_MODULUS_BE ) ;
1054+ let _ = Fp2 :: from_array ( & env, & fp2_bytes) ;
1055+ }
1056+
1057+ #[ test]
1058+ #[ should_panic( expected = "Bls12-381: Invalid Fp" ) ]
1059+ fn test_fp2_second_component_above_modulus_panics ( ) {
1060+ let env = Env :: default ( ) ;
1061+ let mut fp2_bytes = [ 0u8 ; FP2_SERIALIZED_SIZE ] ;
1062+ fp2_bytes[ FP_SERIALIZED_SIZE ..] . copy_from_slice ( & BLS12_381_FP_MODULUS_BE ) ;
1063+ let _ = Fp2 :: from_array ( & env, & fp2_bytes) ;
1064+ }
1065+
1066+ #[ test]
1067+ fn test_fp2_max_valid_accepted ( ) {
1068+ let env = Env :: default ( ) ;
1069+ let mut p_minus_1 = BLS12_381_FP_MODULUS_BE ;
1070+ p_minus_1[ FP_SERIALIZED_SIZE - 1 ] -= 1 ;
1071+ let mut fp2_bytes = [ 0u8 ; FP2_SERIALIZED_SIZE ] ;
1072+ fp2_bytes[ 0 ..FP_SERIALIZED_SIZE ] . copy_from_slice ( & p_minus_1) ;
1073+ fp2_bytes[ FP_SERIALIZED_SIZE ..] . copy_from_slice ( & p_minus_1) ;
1074+ let _ = Fp2 :: from_array ( & env, & fp2_bytes) ;
1075+ }
8421076}
0 commit comments