@@ -58,22 +58,22 @@ where
58
58
presignature : Uint8Array ,
59
59
signature_shares : Vec < Uint8Array > ,
60
60
) -> JsResult < EcdsaSignature > {
61
- let ( big_r, s) = Self :: combine_inner ( presignature, signature_shares) ?;
62
- Self :: signature_into_js ( big_r. to_affine ( ) , s)
61
+ let ( big_r, s, was_flipped ) = Self :: combine_inner ( presignature, signature_shares) ?;
62
+ Self :: signature_into_js ( big_r. to_affine ( ) , s, was_flipped )
63
63
}
64
64
65
65
pub ( crate ) fn combine_inner (
66
66
presignature : Uint8Array ,
67
67
signature_shares : Vec < Uint8Array > ,
68
- ) -> JsResult < ( C :: ProjectivePoint , C :: Scalar ) > {
68
+ ) -> JsResult < ( C :: ProjectivePoint , C :: Scalar , bool ) > {
69
69
let signature_shares = signature_shares
70
70
. into_iter ( )
71
71
. map ( Self :: scalar_from_js)
72
72
. collect :: < JsResult < Vec < _ > > > ( ) ?;
73
73
74
74
let big_r: C :: AffinePoint = Self :: point_from_js ( presignature) ?;
75
- let s = Self :: sum_scalars ( signature_shares) ?;
76
- Ok ( ( C :: ProjectivePoint :: from ( big_r) , s) )
75
+ let ( s , was_flipped ) = Self :: sum_scalars ( signature_shares) ?;
76
+ Ok ( ( C :: ProjectivePoint :: from ( big_r) , s, was_flipped ) )
77
77
}
78
78
79
79
pub fn verify (
@@ -108,13 +108,14 @@ where
108
108
Ok ( ( ) )
109
109
}
110
110
111
- fn sum_scalars ( values : Vec < C :: Scalar > ) -> JsResult < C :: Scalar > {
111
+ fn sum_scalars ( values : Vec < C :: Scalar > ) -> JsResult < ( C :: Scalar , bool ) > {
112
112
if values. is_empty ( ) {
113
113
return Err ( JsError :: new ( "no shares provided" ) ) ;
114
114
}
115
115
let mut acc: C :: Scalar = values. into_iter ( ) . sum ( ) ;
116
+ let acc_flipped = acc. is_high ( ) . into ( ) ;
116
117
acc. conditional_assign ( & ( -acc) , acc. is_high ( ) ) ;
117
- Ok ( acc)
118
+ Ok ( ( acc, acc_flipped ) )
118
119
}
119
120
120
121
pub fn derive_key ( id : Uint8Array , public_keys : Vec < Uint8Array > ) -> JsResult < Uint8Array > {
@@ -168,10 +169,15 @@ where
168
169
Ok ( ( r, s, v) )
169
170
}
170
171
171
- fn signature_into_js ( big_r : C :: AffinePoint , s : C :: Scalar ) -> JsResult < EcdsaSignature > {
172
+ fn signature_into_js ( big_r : C :: AffinePoint , s : C :: Scalar , was_flipped : bool ) -> JsResult < EcdsaSignature > {
172
173
let r = Self :: x_coordinate ( & big_r) . to_repr ( ) ;
173
174
let s = s. to_repr ( ) ;
174
- let v = u8:: conditional_select ( & 0 , & 1 , big_r. y_is_odd ( ) ) ;
175
+ let mut v = u8:: conditional_select ( & 0 , & 1 , big_r. y_is_odd ( ) ) ;
176
+
177
+ // Flip v if s was normalized (flipped, low-s rule)
178
+ if was_flipped {
179
+ v = 1 - v;
180
+ }
175
181
176
182
Ok ( EcdsaSignature {
177
183
obj : into_js ( & ( Bytes :: new ( & r) , Bytes :: new ( & s) , v) ) ?,
@@ -222,7 +228,7 @@ where
222
228
public_key : C :: ProjectivePoint ,
223
229
) -> JsResult < EcdsaSignature > {
224
230
let z = Self :: scalar_from_hash ( message_hash) ?;
225
- let ( big_r, s) = Self :: combine_inner ( pre_signature, signature_shares) ?;
231
+ let ( big_r, s, was_flipped ) = Self :: combine_inner ( pre_signature, signature_shares) ?;
226
232
let r = Self :: x_coordinate ( & big_r. to_affine ( ) ) ;
227
233
228
234
if z. is_zero ( ) . into ( ) {
@@ -241,7 +247,7 @@ where
241
247
. is_identity ( )
242
248
. into ( )
243
249
{
244
- Self :: signature_into_js ( big_r. to_affine ( ) , s)
250
+ Self :: signature_into_js ( big_r. to_affine ( ) , s, was_flipped )
245
251
} else {
246
252
Err ( JsError :: new ( "invalid signature" ) )
247
253
}
0 commit comments