|
1 From 0027ed1872cdec08defe3b097c7123eaaf149e30 Mon Sep 17 00:00:00 2001 |
|
2 From: Russ Cox <[email protected]> |
|
3 Date: Wed, 9 Dec 2015 11:49:53 -0500 |
|
4 Subject: [PATCH 69/73] [release-branch.go1.5] math/big: fix carry propagation |
|
5 in Int.Exp Montgomery code |
|
6 |
|
7 Fixes #13515. |
|
8 |
|
9 Change-Id: I7dd5fbc816e5ea135f7d81f6735e7601f636fe4f |
|
10 Reviewed-on: https://go-review.googlesource.com/17672 |
|
11 Reviewed-by: Robert Griesemer <[email protected]> |
|
12 Reviewed-on: https://go-review.googlesource.com/18585 |
|
13 --- |
|
14 src/math/big/nat.go | 29 +++++++++++---- |
|
15 src/math/big/nat_test.go | 95 +++++++++++++++++++++++++++++++++++++++++------- |
|
16 2 files changed, 103 insertions(+), 21 deletions(-) |
|
17 |
|
18 diff --git a/src/math/big/nat.go b/src/math/big/nat.go |
|
19 index 6545bc1..c7362e6 100644 |
|
20 --- a/src/math/big/nat.go |
|
21 +++ b/src/math/big/nat.go |
|
22 @@ -216,23 +216,36 @@ func basicMul(z, x, y nat) { |
|
23 } |
|
24 } |
|
25 |
|
26 -// montgomery computes x*y*2^(-n*_W) mod m, |
|
27 -// assuming k = -1/m mod 2^_W. |
|
28 +// montgomery computes z mod m = x*y*2**(-n*_W) mod m, |
|
29 +// assuming k = -1/m mod 2**_W. |
|
30 // z is used for storing the result which is returned; |
|
31 // z must not alias x, y or m. |
|
32 +// See Gueron, "Efficient Software Implementations of Modular Exponentiation". |
|
33 +// https://eprint.iacr.org/2011/239.pdf |
|
34 +// In the terminology of that paper, this is an "Almost Montgomery Multiplication": |
|
35 +// x and y are required to satisfy 0 <= z < 2**(n*_W) and then the result |
|
36 +// z is guaranteed to satisfy 0 <= z < 2**(n*_W), but it may not be < m. |
|
37 func (z nat) montgomery(x, y, m nat, k Word, n int) nat { |
|
38 - var c1, c2 Word |
|
39 + // This code assumes x, y, m are all the same length, n. |
|
40 + // (required by addMulVVW and the for loop). |
|
41 + // It also assumes that x, y are already reduced mod m, |
|
42 + // or else the result will not be properly reduced. |
|
43 + if len(x) != n || len(y) != n || len(m) != n { |
|
44 + panic("math/big: mismatched montgomery number lengths") |
|
45 + } |
|
46 + var c1, c2, c3 Word |
|
47 z = z.make(n) |
|
48 z.clear() |
|
49 for i := 0; i < n; i++ { |
|
50 d := y[i] |
|
51 - c1 += addMulVVW(z, x, d) |
|
52 + c2 = addMulVVW(z, x, d) |
|
53 t := z[0] * k |
|
54 - c2 = addMulVVW(z, m, t) |
|
55 - |
|
56 + c3 = addMulVVW(z, m, t) |
|
57 copy(z, z[1:]) |
|
58 - z[n-1] = c1 + c2 |
|
59 - if z[n-1] < c1 { |
|
60 + cx := c1 + c2 |
|
61 + cy := cx + c3 |
|
62 + z[n-1] = cy |
|
63 + if cx < c2 || cy < c3 { |
|
64 c1 = 1 |
|
65 } else { |
|
66 c1 = 0 |
|
67 diff --git a/src/math/big/nat_test.go b/src/math/big/nat_test.go |
|
68 index 7ac3cb8..dce7de5 100644 |
|
69 --- a/src/math/big/nat_test.go |
|
70 +++ b/src/math/big/nat_test.go |
|
71 @@ -341,25 +341,57 @@ var montgomeryTests = []struct { |
|
72 "0xffffffffffffffffffffffffffffffffffffffffffffffffe", |
|
73 "0xffffffffffffffffffffffffffffffffffffffffffffffffe", |
|
74 "0xfffffffffffffffffffffffffffffffffffffffffffffffff", |
|
75 - 0x0000000000000000, |
|
76 - "0xffffffffffffffffffffffffffffffffffffffffff", |
|
77 - "0xffffffffffffffffffffffffffffffffff", |
|
78 + 1, |
|
79 + "0x1000000000000000000000000000000000000000000", |
|
80 + "0x10000000000000000000000000000000000", |
|
81 }, |
|
82 { |
|
83 - "0x0000000080000000", |
|
84 - "0x00000000ffffffff", |
|
85 + "0x000000000ffffff5", |
|
86 + "0x000000000ffffff0", |
|
87 "0x0000000010000001", |
|
88 0xff0000000fffffff, |
|
89 - "0x0000000088000000", |
|
90 - "0x0000000007800001", |
|
91 + "0x000000000bfffff4", |
|
92 + "0x0000000003400001", |
|
93 + }, |
|
94 + { |
|
95 + "0x0000000080000000", |
|
96 + "0x00000000ffffffff", |
|
97 + "0x1000000000000001", |
|
98 + 0xfffffffffffffff, |
|
99 + "0x0800000008000001", |
|
100 + "0x0800000008000001", |
|
101 }, |
|
102 { |
|
103 - "0xffffffffffffffffffffffffffffffff00000000000022222223333333333444444444", |
|
104 - "0xffffffffffffffffffffffffffffffff999999999999999aaabbbbbbbbcccccccccccc", |
|
105 + "0x0000000080000000", |
|
106 + "0x0000000080000000", |
|
107 + "0xffffffff00000001", |
|
108 + 0xfffffffeffffffff, |
|
109 + "0xbfffffff40000001", |
|
110 + "0xbfffffff40000001", |
|
111 + }, |
|
112 + { |
|
113 + "0x0000000080000000", |
|
114 + "0x0000000080000000", |
|
115 + "0x00ffffff00000001", |
|
116 + 0xfffffeffffffff, |
|
117 + "0xbfffff40000001", |
|
118 + "0xbfffff40000001", |
|
119 + }, |
|
120 + { |
|
121 + "0x0000000080000000", |
|
122 + "0x0000000080000000", |
|
123 + "0x0000ffff00000001", |
|
124 + 0xfffeffffffff, |
|
125 + "0xbfff40000001", |
|
126 + "0xbfff40000001", |
|
127 + }, |
|
128 + { |
|
129 + "0x3321ffffffffffffffffffffffffffff00000000000022222623333333332bbbb888c0", |
|
130 + "0x3321ffffffffffffffffffffffffffff00000000000022222623333333332bbbb888c0", |
|
131 "0x33377fffffffffffffffffffffffffffffffffffffffffffff0000000000022222eee1", |
|
132 0xdecc8f1249812adf, |
|
133 - "0x22bb05b6d95eaaeca2bb7c05e51f807bce9064b5fbad177161695e4558f9474e91cd79", |
|
134 - "0x14beb58d230f85b6d95eaaeca2bb7c05e51f807bce9064b5fb45669afa695f228e48cd", |
|
135 + "0x04eb0e11d72329dc0915f86784820fc403275bf2f6620a20e0dd344c5cd0875e50deb5", |
|
136 + "0x0d7144739a7d8e11d72329dc0915f86784820fc403275bf2f61ed96f35dd34dbb3d6a0", |
|
137 }, |
|
138 { |
|
139 "0x10000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffff00000000000022222223333333333444444444", |
|
140 @@ -372,10 +404,27 @@ var montgomeryTests = []struct { |
|
141 } |
|
142 |
|
143 func TestMontgomery(t *testing.T) { |
|
144 + one := NewInt(1) |
|
145 + _B := new(Int).Lsh(one, _W) |
|
146 for i, test := range montgomeryTests { |
|
147 x := natFromString(test.x) |
|
148 y := natFromString(test.y) |
|
149 m := natFromString(test.m) |
|
150 + for len(x) < len(m) { |
|
151 + x = append(x, 0) |
|
152 + } |
|
153 + for len(y) < len(m) { |
|
154 + y = append(y, 0) |
|
155 + } |
|
156 + |
|
157 + if x.cmp(m) > 0 { |
|
158 + _, r := nat(nil).div(nil, x, m) |
|
159 + t.Errorf("#%d: x > m (0x%s > 0x%s; use 0x%s)", i, x.utoa(16), m.utoa(16), r.utoa(16)) |
|
160 + } |
|
161 + if y.cmp(m) > 0 { |
|
162 + _, r := nat(nil).div(nil, x, m) |
|
163 + t.Errorf("#%d: y > m (0x%s > 0x%s; use 0x%s)", i, y.utoa(16), m.utoa(16), r.utoa(16)) |
|
164 + } |
|
165 |
|
166 var out nat |
|
167 if _W == 32 { |
|
168 @@ -384,11 +433,31 @@ func TestMontgomery(t *testing.T) { |
|
169 out = natFromString(test.out64) |
|
170 } |
|
171 |
|
172 - k0 := Word(test.k0 & _M) // mask k0 to ensure that it fits for 32-bit systems. |
|
173 + // t.Logf("#%d: len=%d\n", i, len(m)) |
|
174 + |
|
175 + // check output in table |
|
176 + xi := &Int{abs: x} |
|
177 + yi := &Int{abs: y} |
|
178 + mi := &Int{abs: m} |
|
179 + p := new(Int).Mod(new(Int).Mul(xi, new(Int).Mul(yi, new(Int).ModInverse(new(Int).Lsh(one, uint(len(m))*_W), mi))), mi) |
|
180 + if out.cmp(p.abs.norm()) != 0 { |
|
181 + t.Errorf("#%d: out in table=0x%s, computed=0x%s", i, out.utoa(16), p.abs.norm().utoa(16)) |
|
182 + } |
|
183 + |
|
184 + // check k0 in table |
|
185 + k := new(Int).Mod(&Int{abs: m}, _B) |
|
186 + k = new(Int).Sub(_B, k) |
|
187 + k = new(Int).Mod(k, _B) |
|
188 + k0 := Word(new(Int).ModInverse(k, _B).Uint64()) |
|
189 + if k0 != Word(test.k0) { |
|
190 + t.Errorf("#%d: k0 in table=%#x, computed=%#x\n", i, test.k0, k0) |
|
191 + } |
|
192 + |
|
193 + // check montgomery with correct k0 produces correct output |
|
194 z := nat(nil).montgomery(x, y, m, k0, len(m)) |
|
195 z = z.norm() |
|
196 if z.cmp(out) != 0 { |
|
197 - t.Errorf("#%d got %s want %s", i, z.decimalString(), out.decimalString()) |
|
198 + t.Errorf("#%d: got 0x%s want 0x%s", i, z.utoa(16), out.utoa(16)) |
|
199 } |
|
200 } |
|
201 } |
|
202 -- |
|
203 2.6.1 |
|
204 |