In comparing these test vectors to my own results, for tests #2 and #5, I end up with negative s values. My results show that for test #2, test_s = my_s * -1
, as an integer, yet the hexadecimal representation matches up correctly with the test, while for test #5, test_s = my_s + curve_order
. What is the proper way to handle a negative s value, or possibly, should I not be getting negative s values for these tests?
2 Answers
Let us denote curve_order
by q
. When computing a signature (r,s)
it is important to remember that r
and s
are meant to represent elements of Fq
(the field of integers modulo q
). The formula s = k^(-1)(e + rx)
is an algebraic expression in Fq
. So r
an s
are not really integers, they are integers modulo q
(so for example two integers u
and u + q
represent the same elements of Fq
). When implementing a function returning (r,s)
there is nothing wrong with internally representing these quantities as integers. But when it comes to returning values, you have to decide how you wish to encode the values r
and s
. If s
represents your implementation specific integer, do you simply want to return s
? or s + q
? or s - 3q
? All these answers are the same (simply different encodings) but there is little chance you will match a test-vector
unless you have a common encoding with it. When returning an integer s
modulo q
it is customary to encode s
as the unique integer s'
with 0 <= s' < q
equal to s mod q
.
So for example, when you say that test_s = my_s + curve_order
, you effectively agree with the test, but have not enforced the usual encoding. When you say that test_s = my_s * -1
, you genuinely disagree with the test but this is explained by 'standardization' (so make sure that you have 0 <= s < q
and if s > q/2
replace it by -s
, but make sure you encode -s
as an integer within [0,q)
, which means s
should in fact be replaced by q - s
).
-
seriously, you are a magnificent human being. While it is admittedly difficult to understand this, I think I have a grasp of it, and nevertheless do have some working code that produces the same results as the tests, and hopefully thereby uses the same encoding. I will post my code in order to expose whatever flaws may exist in the logic, or optimizations that could be made to it.– user31364Commented Jan 28, 2017 at 0:53
-
@BrettDoffing In my opinion, you should avoid cluttering your code with tests
0 <= x < q
. You probably have a methodmod
on your typeInteger
so thatx.mod(q)
always gives you an integer in the interval[0,q)
(whenx >= 0
it is simply the remainder of the division ofx
byq
). So ifx.mul(y)
is the normal integer product,x.mul(y).mod(q)
gives you the product inFq
etc. So compute(r,s)
as usual and before returning it you simply standardize it by calling a method which replaces the signature(r,s)
by the (valid) signature(r, -s.mod(q))
whens > q/2
. Commented Jan 28, 2017 at 5:40 -
Thanks for the advice. Swift uses a remainder, while my current big integer library is pretty small, with limited functionality, using remainder as well.– user31364Commented Jan 28, 2017 at 10:23
-
@BrettDoffing if your remainder function for big integers always returns a non-negative integer after dividing by q, then whatever (r,s) you were calculating before, take the reminders instead, and then do a last filtering to standardize before returning signature, and you should match the test vector perfectly :) Commented Jan 28, 2017 at 10:43
-
1The derivation of this answer is wrong.
s
and-s
are NOT equal mod q. The explanation is: During signature calculations we're multiplyings
with a curve pointG
. From the resulting point we're only using the x coordinate and discard the y coordinate. Because((-s)*G).x == (-(s*G)).x == (s*G).x
(negation of a point only inverts its y coord, x stays the same), boths
and-s
work interchangeably in the signature. For the canonical encoding (Low S
) ofs
we're using the smaller value of the two.– ensCommented Feb 24, 2018 at 21:02
While @SvenWilliamson has explained the answer, this is the code I have implemented get my desired results:
// As explained in @SvenWilliamson's answer - two integers s and s + q (s2) represent the same elements of Fq
let s2 = s + q
if 0 <= s && s < q {
if s > q/2 {
print((r, n - s))
} else {
print((r, s))
}
} else if 0 <= s2 && s2 < q {
if s2 > q/2 {
print((r, n - s2))
} else {
print((r, s2))
}
} else {
// ??
}