11use super :: {
22 super :: signers:: { DynSigner , Eip712PayLoadSigner , P256Key , P256Signer , WebAuthnSigner } ,
3- U40 ,
3+ U40 , WebAuthnP256 ,
44 rpc:: { AuthorizeKey , CallKey , Permission , RevokeKey } ,
55} ;
66use IDelegation :: getKeysReturn;
@@ -364,6 +364,15 @@ impl KeyWith712Signer {
364364 }
365365 }
366366
367+ /// Wraps signer to produce high-S signatures for testing P256 normalization.
368+ pub fn with_high_s_signature ( self ) -> Self {
369+ Self {
370+ key : self . key ,
371+ signer : Arc :: new ( HighSSignerWrapper ( self . signer ) ) ,
372+ permissions : self . permissions ,
373+ }
374+ }
375+
367376 /// Returns [`KeyWith712Signer`] with additional permissions.
368377 pub fn with_permissions ( mut self , permissions : Vec < Permission > ) -> Self {
369378 self . permissions = permissions;
@@ -404,6 +413,10 @@ impl KeyWith712Signer {
404413
405414#[ async_trait:: async_trait]
406415impl Eip712PayLoadSigner for KeyWith712Signer {
416+ fn key_type ( & self ) -> KeyType {
417+ self . signer . key_type ( )
418+ }
419+
407420 async fn sign_payload_hash ( & self , payload_hash : B256 ) -> eyre:: Result < Bytes > {
408421 Ok ( self . signer . sign_payload_hash ( payload_hash) . await ?)
409422 }
@@ -426,6 +439,68 @@ pub const ITHACA_ACCOUNT_STORAGE_SLOT: u128 = 1264628507133665080054;
426439/// contract.
427440pub const ITHACA_KEY_STORAGE_SLOT_OFFSET : u128 = 3 ;
428441
442+ const P256_N : U256 =
443+ alloy:: uint!( 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551_ U256 ) ;
444+ const P256_HALF_N : U256 =
445+ alloy:: uint!( 0x7fffffff800000007fffffffffffffffde737d56d38bcf4279dce5617e3192a8_ U256 ) ;
446+
447+ /// Wrapper that converts signatures to high-S for testing normalization.
448+ #[ derive( Debug ) ]
449+ struct HighSSignerWrapper ( Arc < dyn Eip712PayLoadSigner > ) ;
450+
451+ #[ async_trait:: async_trait]
452+ impl Eip712PayLoadSigner for HighSSignerWrapper {
453+ fn key_type ( & self ) -> KeyType {
454+ self . 0 . key_type ( )
455+ }
456+
457+ async fn sign_payload_hash ( & self , payload_hash : B256 ) -> eyre:: Result < Bytes > {
458+ let sig = self . 0 . sign_payload_hash ( payload_hash) . await ?;
459+ match self . 0 . key_type ( ) {
460+ KeyType :: P256 => {
461+ let s = U256 :: from_be_slice ( & sig[ 32 ..] ) ;
462+ if s < P256_HALF_N {
463+ let mut out = sig[ ..32 ] . to_vec ( ) ;
464+ out. extend_from_slice ( B256 :: from ( P256_N - s) . as_slice ( ) ) ;
465+ return Ok ( out. into ( ) ) ;
466+ }
467+ }
468+ KeyType :: WebAuthnP256 => {
469+ if let Ok ( mut w) = WebAuthnP256 :: abi_decode ( & sig) {
470+ let s: U256 = w. s . into ( ) ;
471+ if s < P256_HALF_N {
472+ w. s = ( P256_N - s) . into ( ) ;
473+ return Ok ( w. abi_encode ( ) . into ( ) ) ;
474+ }
475+ }
476+ }
477+ _ => { }
478+ }
479+ Ok ( sig)
480+ }
481+ }
482+
483+ /// Normalizes P256 signature S value to lower half of curve.
484+ ///
485+ /// Handles both raw P256 (64 bytes) and ABI-encoded WebAuthnP256 formats.
486+ pub fn normalize_p256_s ( signature : Bytes ) -> Bytes {
487+ if signature. len ( ) == 64 {
488+ let s = U256 :: from_be_slice ( & signature[ 32 ..] ) ;
489+ if s > P256_HALF_N {
490+ let mut out = signature[ ..32 ] . to_vec ( ) ;
491+ out. extend_from_slice ( B256 :: from ( P256_N - s) . as_slice ( ) ) ;
492+ return out. into ( ) ;
493+ }
494+ } else if let Ok ( mut w) = WebAuthnP256 :: abi_decode ( & signature) {
495+ let s: U256 = w. s . into ( ) ;
496+ if s > P256_HALF_N {
497+ w. s = ( P256_N - s) . into ( ) ;
498+ return WebAuthnP256 :: abi_encode ( & w) . into ( ) ;
499+ }
500+ }
501+ signature
502+ }
503+
429504#[ cfg( test) ]
430505mod tests {
431506 use super :: { Key , KeyType } ;
0 commit comments