diff --git a/dcrec/secp256k1/field.go b/dcrec/secp256k1/field.go index 9c633a08d9..1b7809ccee 100644 --- a/dcrec/secp256k1/field.go +++ b/dcrec/secp256k1/field.go @@ -391,32 +391,12 @@ func (f *FieldVal) Normalize() *FieldVal { // following determines if either or these conditions are true and does // the final reduction in constant time. // - // Note that the if/else statements here intentionally do the bitwise - // operators even when it won't change the value to ensure constant time - // between the branches. Also note that 'm' will be zero when neither - // of the aforementioned conditions are true and the value will not be - // changed when 'm' is zero. - m = 1 - if t9 == fieldMSBMask { - m &= 1 - } else { - m &= 0 - } - if t2&t3&t4&t5&t6&t7&t8 == fieldBaseMask { - m &= 1 - } else { - m &= 0 - } - if ((t0+977)>>fieldBase + t1 + 64) > fieldBaseMask { - m &= 1 - } else { - m &= 0 - } - if t9>>fieldMSBBits != 0 { - m |= 1 - } else { - m |= 0 - } + // Also note that 'm' will be zero when neither of the aforementioned + // conditions are true and the value will not be changed when 'm' is zero. + m = constantTimeEq(t9, fieldMSBMask) + m &= constantTimeEq(t8&t7&t6&t5&t4&t3&t2, fieldBaseMask) + m &= constantTimeGreater(t1+64+((t0+977)>>fieldBase), fieldBaseMask) + m |= t9 >> fieldMSBBits t0 = t0 + m*977 t1 = (t0 >> fieldBase) + t1 + (m << 6) t0 = t0 & fieldBaseMask diff --git a/dcrec/secp256k1/field_bench_test.go b/dcrec/secp256k1/field_bench_test.go index c96e1efaae..e7937ba646 100644 --- a/dcrec/secp256k1/field_bench_test.go +++ b/dcrec/secp256k1/field_bench_test.go @@ -12,8 +12,11 @@ import ( // BenchmarkFieldNormalize benchmarks how long it takes the internal field // to perform normalization (which includes modular reduction). func BenchmarkFieldNormalize(b *testing.B) { - // The normalize function is constant time so default value is fine. - f := new(FieldVal) + // The function is constant time so any value is fine. + f := &FieldVal{n: [10]uint32{ + 0x000148f6, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, + 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x00000007, + }} for i := 0; i < b.N; i++ { f.Normalize() } diff --git a/dcrec/secp256k1/field_test.go b/dcrec/secp256k1/field_test.go index 5abffb2866..b144051b5d 100644 --- a/dcrec/secp256k1/field_test.go +++ b/dcrec/secp256k1/field_test.go @@ -769,6 +769,73 @@ func TestFieldNormalize(t *testing.T) { name: "Value > P with redux > P at mag 1 due to 1st and 2nd words and carry to bit 256", raw: [10]uint32{0x03fffc30, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x07ffffff, 0x003fffff}, normalized: [10]uint32{0x00000001, 0x00000001, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000001}, + }, { + // --------------------------------------------------------------------- + // There are 3 main conditions that must be true if the final reduction + // is needed after the initial reduction to magnitude 1 when there was + // NOT a carry to bit 256 (in other words when the original value was < + // 2^256): + // 1) The final word of the reduced value is equal to the one of P + // 2) The 3rd through 9th words are equal to those of P + // 3) Either: + // - The 2nd word is greater than the one of P; or + // - The 2nd word is equal to that of P AND the 1st word is greater + // + // Therefore the eight possible combinations of those 3 main conditions + // can be thought of in binary where each bit starting from the left + // corresponds to the aforementioned conditions as such: + // 000, 001, 010, 011, 100, 101, 110, 111 + // + // For example, combination 6 is when both conditons 1 and 2 are true, + // but condition 3 is NOT true. + // + // The following tests hit each of these combinations and refer to each + // by its decimal equivalent for ease of reference. + // + // NOTE: The final combination (7) is already tested above since it only + // happens when the original value is already the normalized + // representation of P. + // --------------------------------------------------------------------- + + name: "Value < 2^256 final reduction combination 0", + raw: [10]uint32{0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03fffffe, 0x003ffffe}, + normalized: [10]uint32{0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03fffffe, 0x003ffffe}, + }, { + name: "Value < 2^256 final reduction combination 1 via 2nd word", + raw: [10]uint32{0x03fff85e, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03fffffe, 0x003ffffe}, + normalized: [10]uint32{0x03fff85e, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03fffffe, 0x003ffffe}, + }, { + name: "Value < 2^256 final reduction combination 1 via 1st word", + raw: [10]uint32{0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03fffffe, 0x003ffffe}, + normalized: [10]uint32{0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03fffffe, 0x003ffffe}, + }, { + name: "Value < 2^256 final reduction combination 2", + raw: [10]uint32{0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003ffffe}, + normalized: [10]uint32{0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003ffffe}, + }, { + name: "Value < 2^256 final reduction combination 3 via 2nd word", + raw: [10]uint32{0x03fff85e, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003ffffe}, + normalized: [10]uint32{0x03fff85e, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003ffffe}, + }, { + name: "Value < 2^256 final reduction combination 3 via 1st word", + raw: [10]uint32{0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003ffffe}, + normalized: [10]uint32{0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003ffffe}, + }, { + name: "Value < 2^256 final reduction combination 4", + raw: [10]uint32{0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03fffffe, 0x003fffff}, + normalized: [10]uint32{0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03fffffe, 0x003fffff}, + }, { + name: "Value < 2^256 final reduction combination 5 via 2nd word", + raw: [10]uint32{0x03fff85e, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03fffffe, 0x003fffff}, + normalized: [10]uint32{0x03fff85e, 0x03ffffc0, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03fffffe, 0x003fffff}, + }, { + name: "Value < 2^256 final reduction combination 5 via 1st word", + raw: [10]uint32{0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03fffffe, 0x003fffff}, + normalized: [10]uint32{0x03fffc2f, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03fffffe, 0x003fffff}, + }, { + name: "Value < 2^256 final reduction combination 6", + raw: [10]uint32{0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff}, + normalized: [10]uint32{0x03fff85e, 0x03ffffbf, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x03ffffff, 0x003fffff}, }} for _, test := range tests {