| ... | @@ -144,98 +144,147 @@ pub const Edwards25519 = struct { | ... | @@ -144,98 +144,147 @@ pub const Edwards25519 = struct { |
| 144 | p.t.cMov(a.t, c); | 144 | p.t.cMov(a.t, c); |
| 145 | } | 145 | } |
| 146 | | 146 | |
| 147 | inline fn pcSelect(pc: [16]Edwards25519, b: u8) Edwards25519 { | 147 | inline fn pcSelect(comptime n: usize, pc: [n]Edwards25519, b: u8) Edwards25519 { |
| 148 | var t = Edwards25519.identityElement; | 148 | var t = Edwards25519.identityElement; |
| 149 | comptime var i: u8 = 0; | 149 | comptime var i: u8 = 1; |
| 150 | inline while (i < 16) : (i += 1) { | 150 | inline while (i < pc.len) : (i += 1) { |
| 151 | t.cMov(pc[i], ((@as(usize, b ^ i) -% 1) >> 8) & 1); | 151 | t.cMov(pc[i], ((@as(usize, b ^ i) -% 1) >> 8) & 1); |
| 152 | } | 152 | } |
| 153 | return t; | 153 | return t; |
| 154 | } | 154 | } |
| 155 | | 155 | |
| 156 | fn pcMul(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) !Edwards25519 { | 156 | fn nonAdjacentForm(s: [32]u8) [2 * 32]i8 { |
| | 157 | var e: [2 * 32]i8 = undefined; |
| | 158 | for (s) |x, i| { |
| | 159 | e[i * 2 + 0] = @as(i8, @truncate(u4, x)); |
| | 160 | e[i * 2 + 1] = @as(i8, @truncate(u4, x >> 4)); |
| | 161 | } |
| | 162 | // Now, e[0..63] is between 0 and 15, e[63] is between 0 and 7 |
| | 163 | var carry: i8 = 0; |
| | 164 | for (e[0..63]) |*x| { |
| | 165 | x.* += carry; |
| | 166 | carry = (x.* + 8) >> 4; |
| | 167 | x.* -= carry * 16; |
| | 168 | } |
| | 169 | e[63] += carry; |
| | 170 | // Now, e[*] is between -8 and 8, including e[63] |
| | 171 | return e; |
| | 172 | } |
| | 173 | |
| | 174 | // Scalar multiplication with a 4-bit window and the first 8 multiples. |
| | 175 | // This requires the scalar to be converted to non-adjacent form. |
| | 176 | // Based on real-world benchmarks, we only use this for multi-scalar multiplication. |
| | 177 | // NAF could be useful to half the size of precomputation tables, but we intentionally |
| | 178 | // avoid these to keep the standard library lightweight. |
| | 179 | fn pcMul(pc: [9]Edwards25519, s: [32]u8, comptime vartime: bool) !Edwards25519 { |
| | 180 | std.debug.assert(vartime); |
| | 181 | const e = nonAdjacentForm(s); |
| | 182 | var q = Edwards25519.identityElement; |
| | 183 | var pos: usize = 2 * 32 - 1; |
| | 184 | while (true) : (pos -= 1) { |
| | 185 | const slot = e[pos]; |
| | 186 | if (slot > 0) { |
| | 187 | q = q.add(pc[@intCast(usize, slot)]); |
| | 188 | } else if (slot < 0) { |
| | 189 | q = q.sub(pc[@intCast(usize, -slot)]); |
| | 190 | } |
| | 191 | if (pos == 0) break; |
| | 192 | q = q.dbl().dbl().dbl().dbl(); |
| | 193 | } |
| | 194 | try q.rejectIdentity(); |
| | 195 | return q; |
| | 196 | } |
| | 197 | |
| | 198 | // Scalar multiplication with a 4-bit window and the first 15 multiples. |
| | 199 | fn pcMul16(pc: [16]Edwards25519, s: [32]u8, comptime vartime: bool) !Edwards25519 { |
| 157 | var q = Edwards25519.identityElement; | 200 | var q = Edwards25519.identityElement; |
| 158 | var pos: usize = 252; | 201 | var pos: usize = 252; |
| 159 | while (true) : (pos -= 4) { | 202 | while (true) : (pos -= 4) { |
| 160 | q = q.dbl().dbl().dbl().dbl(); | 203 | const slot = @truncate(u4, (s[pos >> 3] >> @truncate(u3, pos))); |
| 161 | const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 0xf; | | |
| 162 | if (vartime) { | 204 | if (vartime) { |
| 163 | if (bit != 0) { | 205 | if (slot != 0) { |
| 164 | q = q.add(pc[bit]); | 206 | q = q.add(pc[slot]); |
| 165 | } | 207 | } |
| 166 | } else { | 208 | } else { |
| 167 | q = q.add(pcSelect(pc, bit)); | 209 | q = q.add(pcSelect(16, pc, slot)); |
| 168 | } | 210 | } |
| 169 | if (pos == 0) break; | 211 | if (pos == 0) break; |
| | 212 | q = q.dbl().dbl().dbl().dbl(); |
| 170 | } | 213 | } |
| 171 | try q.rejectIdentity(); | 214 | try q.rejectIdentity(); |
| 172 | return q; | 215 | return q; |
| 173 | } | 216 | } |
| 174 | | 217 | |
| 175 | fn precompute(p: Edwards25519) [16]Edwards25519 { | 218 | fn precompute(p: Edwards25519, comptime count: usize) [1 + count]Edwards25519 { |
| 176 | var pc: [16]Edwards25519 = undefined; | 219 | var pc: [1 + count]Edwards25519 = undefined; |
| 177 | pc[0] = Edwards25519.identityElement; | 220 | pc[0] = Edwards25519.identityElement; |
| 178 | pc[1] = p; | 221 | pc[1] = p; |
| 179 | var i: usize = 2; | 222 | var i: usize = 2; |
| 180 | while (i < 16) : (i += 1) { | 223 | while (i <= count) : (i += 1) { |
| 181 | pc[i] = pc[i - 1].add(p); | 224 | pc[i] = pc[i - 1].add(p); |
| 182 | } | 225 | } |
| 183 | return pc; | 226 | return pc; |
| 184 | } | 227 | } |
| 185 | | 228 | |
| | 229 | const basePointPc = comptime pc: { |
| | 230 | @setEvalBranchQuota(10000); |
| | 231 | break :pc precompute(Edwards25519.basePoint, 15); |
| | 232 | }; |
| | 233 | |
| 186 | /// Multiply an Edwards25519 point by a scalar without clamping it. | 234 | /// Multiply an Edwards25519 point by a scalar without clamping it. |
| 187 | /// Return error.WeakPublicKey if the resulting point is | 235 | /// Return error.WeakPublicKey if the resulting point is |
| 188 | /// the identity element. | 236 | /// the identity element. |
| 189 | pub fn mul(p: Edwards25519, s: [32]u8) !Edwards25519 { | 237 | pub fn mul(p: Edwards25519, s: [32]u8) !Edwards25519 { |
| 190 | var pc: [16]Edwards25519 = undefined; | 238 | const pc = if (p.is_base) basePointPc else pc: { |
| 191 | if (p.is_base) { | 239 | const xpc = precompute(p, 15); |
| 192 | @setEvalBranchQuota(10000); | 240 | xpc[4].rejectIdentity() catch |_| return error.WeakPublicKey; |
| 193 | pc = comptime precompute(Edwards25519.basePoint); | 241 | break :pc xpc; |
| 194 | } else { | 242 | }; |
| 195 | pc = precompute(p); | 243 | return pcMul16(pc, s, false); |
| 196 | pc[4].rejectIdentity() catch |_| return error.WeakPublicKey; | | |
| 197 | } | | |
| 198 | return pcMul(pc, s, false); | | |
| 199 | } | 244 | } |
| 200 | | 245 | |
| 201 | /// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME* | 246 | /// Multiply an Edwards25519 point by a *PUBLIC* scalar *IN VARIABLE TIME* |
| 202 | /// This can be used for signature verification. | 247 | /// This can be used for signature verification. |
| 203 | pub fn mulPublic(p: Edwards25519, s: [32]u8) !Edwards25519 { | 248 | pub fn mulPublic(p: Edwards25519, s: [32]u8) !Edwards25519 { |
| 204 | var pc: [16]Edwards25519 = undefined; | | |
| 205 | if (p.is_base) { | 249 | if (p.is_base) { |
| 206 | @setEvalBranchQuota(10000); | 250 | return pcMul16(basePointPc, s, true); |
| 207 | pc = comptime precompute(Edwards25519.basePoint); | | |
| 208 | } else { | 251 | } else { |
| 209 | pc = precompute(p); | 252 | const pc = precompute(p, 8); |
| 210 | pc[4].rejectIdentity() catch |_| return error.WeakPublicKey; | 253 | pc[4].rejectIdentity() catch |_| return error.WeakPublicKey; |
| | 254 | return pcMul(pc, s, true); |
| 211 | } | 255 | } |
| 212 | return pcMul(pc, s, true); | | |
| 213 | } | 256 | } |
| 214 | | 257 | |
| 215 | /// Multiscalar multiplication *IN VARIABLE TIME* for public data | 258 | /// Multiscalar multiplication *IN VARIABLE TIME* for public data |
| 216 | /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually | 259 | /// Computes ps0*ss0 + ps1*ss1 + ps2*ss2... faster than doing many of these operations individually |
| 217 | pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) !Edwards25519 { | 260 | pub fn mulMulti(comptime count: usize, ps: [count]Edwards25519, ss: [count][32]u8) !Edwards25519 { |
| 218 | var pcs: [count][16]Edwards25519 = undefined; | 261 | var pcs: [count][9]Edwards25519 = undefined; |
| 219 | for (ps) |p, i| { | 262 | for (ps) |p, i| { |
| 220 | if (p.is_base) { | 263 | if (p.is_base) { |
| 221 | @setEvalBranchQuota(10000); | 264 | @setEvalBranchQuota(10000); |
| 222 | pcs[i] = comptime precompute(Edwards25519.basePoint); | 265 | pcs[i] = comptime precompute(Edwards25519.basePoint, 8); |
| 223 | } else { | 266 | } else { |
| 224 | pcs[i] = precompute(p); | 267 | pcs[i] = precompute(p, 8); |
| 225 | pcs[i][4].rejectIdentity() catch |_| return error.WeakPublicKey; | 268 | pcs[i][4].rejectIdentity() catch |_| return error.WeakPublicKey; |
| 226 | } | 269 | } |
| 227 | } | 270 | } |
| | 271 | var es: [count][2 * 32]i8 = undefined; |
| | 272 | for (ss) |s, i| { |
| | 273 | es[i] = nonAdjacentForm(s); |
| | 274 | } |
| 228 | var q = Edwards25519.identityElement; | 275 | var q = Edwards25519.identityElement; |
| 229 | var pos: usize = 252; | 276 | var pos: usize = 2 * 32 - 1; |
| 230 | while (true) : (pos -= 4) { | 277 | while (true) : (pos -= 1) { |
| 231 | q = q.dbl().dbl().dbl().dbl(); | 278 | for (es) |e, i| { |
| 232 | for (ss) |s, i| { | 279 | const slot = e[pos]; |
| 233 | const bit = (s[pos >> 3] >> @truncate(u3, pos)) & 0xf; | 280 | if (slot > 0) { |
| 234 | if (bit != 0) { | 281 | q = q.add(pcs[i][@intCast(usize, slot)]); |
| 235 | q = q.add(pcs[i][bit]); | 282 | } else if (slot < 0) { |
| | 283 | q = q.sub(pcs[i][@intCast(usize, -slot)]); |
| 236 | } | 284 | } |
| 237 | } | 285 | } |
| 238 | if (pos == 0) break; | 286 | if (pos == 0) break; |
| | 287 | q = q.dbl().dbl().dbl().dbl(); |
| 239 | } | 288 | } |
| 240 | try q.rejectIdentity(); | 289 | try q.rejectIdentity(); |
| 241 | return q; | 290 | return q; |