Files
bluessh/x25519.lua
T
2026-08-09 19:55:34 +03:00

361 lines
14 KiB
Lua

-- local curve25519 = require("curve25519")
-- curve25519(private_key, peer_public_value)
if not load("return (1>>1) == 0 and (1<<1) == 2 and (1&1) == 1 and (1|0) == 1") then
error("bitwise operators not available; need Lua 5.3+")
end
local kBottom25Bits = 0x1ffffff
local kBottom26Bits = 0x3ffffff
local kTop39Bits = 0xfffffffffe000000
local kTop38Bits = 0xfffffffffc000000
local function load_3(i, offset)
return i[1 + offset] | (i[2 + offset] << 8) | (i[3 + offset] << 16)
end
local function load_4(i, offset)
return i[1 + offset] | (i[2 + offset] << 8) | (i[3 + offset] << 16) | (i[4 + offset] << 24)
end
local function arshift(x, n)
n = n & 63
if n == 0 then return x end
if x >= 0 then
return x >> n
else
return (x >> n) | (~0 << (64 - n))
end
end
local function fe_frombytes(s)
local h = {
load_4(s, 0),
load_3(s, 4) << 6,
load_3(s, 7) << 5,
load_3(s, 10) << 3,
load_3(s, 13) << 2,
load_4(s, 16),
load_3(s, 20) << 7,
load_3(s, 23) << 5,
load_3(s, 26) << 4,
(load_3(s, 29) & 0x7fffff) << 2
}
local carry
carry = h[10] + (1 << 24); h[1] = h[1] + arshift(carry, 25) * 19; h[10] = h[10] - (carry & kTop39Bits)
carry = h[2] + (1 << 24); h[3] = h[3] + arshift(carry, 25); h[2] = h[2] - (carry & kTop39Bits)
carry = h[4] + (1 << 24); h[5] = h[5] + arshift(carry, 25); h[4] = h[4] - (carry & kTop39Bits)
carry = h[6] + (1 << 24); h[7] = h[7] + arshift(carry, 25); h[6] = h[6] - (carry & kTop39Bits)
carry = h[8] + (1 << 24); h[9] = h[9] + arshift(carry, 25); h[8] = h[8] - (carry & kTop39Bits)
carry = h[1] + (1 << 25); h[2] = h[2] + arshift(carry, 26); h[1] = h[1] - (carry & kTop38Bits)
carry = h[3] + (1 << 25); h[4] = h[4] + arshift(carry, 26); h[3] = h[3] - (carry & kTop38Bits)
carry = h[5] + (1 << 25); h[6] = h[6] + arshift(carry, 26); h[5] = h[5] - (carry & kTop38Bits)
carry = h[7] + (1 << 25); h[8] = h[8] + arshift(carry, 26); h[7] = h[7] - (carry & kTop38Bits)
carry = h[9] + (1 << 25); h[10] = h[10] + arshift(carry, 26); h[9] = h[9] - (carry & kTop38Bits)
return h
end
local function fe_tobytes(h)
local q
q = (19 * h[10] + (1 << 24)) >> 25
q = arshift(h[1] + q, 26)
q = arshift(h[2] + q, 25)
q = arshift(h[3] + q, 26)
q = arshift(h[4] + q, 25)
q = arshift(h[5] + q, 26)
q = arshift(h[6] + q, 25)
q = arshift(h[7] + q, 26)
q = arshift(h[8] + q, 25)
q = arshift(h[9] + q, 26)
q = arshift(h[10] + q, 25)
h[1] = h[1] + 19 * q
h[2] = h[2] + arshift(h[1], 26)
h[1] = h[1] & kBottom26Bits
h[3] = h[3] + arshift(h[2], 25)
h[2] = h[2] & kBottom25Bits
h[4] = h[4] + arshift(h[3], 26)
h[3] = h[3] & kBottom26Bits
h[5] = h[5] + arshift(h[4], 25)
h[4] = h[4] & kBottom25Bits
h[6] = h[6] + arshift(h[5], 26)
h[5] = h[5] & kBottom26Bits
h[7] = h[7] + arshift(h[6], 25)
h[6] = h[6] & kBottom25Bits
h[8] = h[8] + arshift(h[7], 26)
h[7] = h[7] & kBottom26Bits
h[9] = h[9] + arshift(h[8], 25)
h[8] = h[8] & kBottom25Bits
h[10] = h[10] + arshift(h[9], 26)
h[9] = h[9] & kBottom26Bits
h[10] = h[10] & kBottom25Bits
return {
(h[1] >> 0) & 0xff,
(h[1] >> 8) & 0xff,
(h[1] >> 16) & 0xff,
((h[1] >> 24) | (h[2] << 2)) & 0xff,
(h[2] >> 6) & 0xff,
(h[2] >> 14) & 0xff,
((h[2] >> 22) | (h[3] << 3)) & 0xff,
(h[3] >> 5) & 0xff,
(h[3] >> 13) & 0xff,
((h[3] >> 21) | (h[4] << 5)) & 0xff,
(h[4] >> 3) & 0xff,
(h[4] >> 11) & 0xff,
((h[4] >> 19) | (h[5] << 6)) & 0xff,
(h[5] >> 2) & 0xff,
(h[5] >> 10) & 0xff,
(h[5] >> 18) & 0xff,
(h[6] >> 0) & 0xff,
(h[6] >> 8) & 0xff,
(h[6] >> 16) & 0xff,
((h[6] >> 24) | (h[7] << 1)) & 0xff,
(h[7] >> 7) & 0xff,
(h[7] >> 15) & 0xff,
((h[7] >> 23) | (h[8] << 3)) & 0xff,
(h[8] >> 5) & 0xff,
(h[8] >> 13) & 0xff,
((h[8] >> 21) | (h[9] << 4)) & 0xff,
(h[9] >> 4) & 0xff,
(h[9] >> 12) & 0xff,
((h[9] >> 20) | (h[10] << 6)) & 0xff,
(h[10] >> 2) & 0xff,
(h[10] >> 10) & 0xff,
(h[10] >> 18) & 0xff
}
end
local function fe_add(f, g)
local h = {}
for i = 1, 10 do
h[i] = (f[i] + g[i])
end
return h
end
local function fe_addto(f, g)
for i = 1, 10 do
f[i] = (f[i] + g[i])
end
end
local function fe_sub(f, g)
local h = {}
for i = 1, 10 do
h[i] = (f[i] - g[i])
end
return h
end
local function fe_subto(f, g)
for i = 1, 10 do
f[i] = (f[i] - g[i])
end
end
local function fe_cswap(f, g, b)
b = 0 - b
for i = 1, 10 do
local x = f[i] ~ g[i]
x = x & b
f[i] = f[i] ~ x
g[i] = g[i] ~ x
end
end
local function fe_mul121666(f)
local h = {
f[1] * 121666,
f[2] * 121666,
f[3] * 121666,
f[4] * 121666,
f[5] * 121666,
f[6] * 121666,
f[7] * 121666,
f[8] * 121666,
f[9] * 121666,
f[10] * 121666
}
local carry
carry = h[10] + (1 << 24); h[1] = h[1] + arshift(carry, 25) * 19; h[10] = h[10] - (carry & kTop39Bits)
carry = h[2] + (1 << 24); h[3] = h[3] + arshift(carry, 25); h[2] = h[2] - (carry & kTop39Bits)
carry = h[4] + (1 << 24); h[5] = h[5] + arshift(carry, 25); h[4] = h[4] - (carry & kTop39Bits)
carry = h[6] + (1 << 24); h[7] = h[7] + arshift(carry, 25); h[6] = h[6] - (carry & kTop39Bits)
carry = h[8] + (1 << 24); h[9] = h[9] + arshift(carry, 25); h[8] = h[8] - (carry & kTop39Bits)
carry = h[1] + (1 << 25); h[2] = h[2] + arshift(carry, 26); h[1] = h[1] - (carry & kTop38Bits)
carry = h[3] + (1 << 25); h[4] = h[4] + arshift(carry, 26); h[3] = h[3] - (carry & kTop38Bits)
carry = h[5] + (1 << 25); h[6] = h[6] + arshift(carry, 26); h[5] = h[5] - (carry & kTop38Bits)
carry = h[7] + (1 << 25); h[8] = h[8] + arshift(carry, 26); h[7] = h[7] - (carry & kTop38Bits)
carry = h[9] + (1 << 25); h[10] = h[10] + arshift(carry, 26); h[9] = h[9] - (carry & kTop38Bits)
return h
end
local function fe_mul(f, g)
local h = {
f[1] * g[1] + f[2] * g[10] * 38 + f[3] * g[9] * 19 + f[4] * g[8] * 38 + f[5] * g[7] * 19 + f[6] * g[6] * 38 + f[7] * g[5] * 19 + f[8] * g[4] * 38 + f[9] * g[3] * 19 + f[10] * g[2] * 38,
f[1] * g[2] + f[2] * g[1] + f[3] * g[10] * 19 + f[4] * g[9] * 19 + f[5] * g[8] * 19 + f[6] & g[7] * 19 + f[7] * g[6] * 19 + f[8] * g[5] * 19 + f[9] * g[4] * 19 + f[10] * g[3] * 19,
f[1] * g[3] + f[2] * g[2] * 2 + f[3] * g[1] + f[4] * g[10] * 38 + f[5] * g[9] * 19 + f[6] * g[8] * 38 + f[7] * g[7] * 19 + f[8] * g[6] * 38 + f[9] * g[5] * 19 + f[10] * g[4] * 38,
f[1] * g[4] + f[2] * g[3] + f[3] * g[2] + f[4] * g[1] + f[5] * g[10] * 19 + f[6] * g[9] * 19 + f[7] * g[8] * 19 + f[8] * g[7] * 19 + f[9] * g[6] * 19 + f[10] * g[5] * 19,
f[1] * g[5] + f[2] * g[4] * 2 + f[3] * g[3] + f[4] * g[2] * 2 + f[5] * g[1] + f[6] * g[10] * 38 + f[7] * g[9] * 19 + f[8] * g[8] * 38 + f[9] * g[7] * 19 + f[10] * g[6] * 38,
f[1] * g[6] + f[2] * g[5] + f[3] * g[4] + f[4] * g[3] + f[5] * g[2] + f[6] * g[1] + f[7] * g[10] * 19 + f[8] * g[9] * 19 + f[9] * g[8] * 19 + f[10] * g[7] * 19,
f[1] * g[7] + f[2] * g[6] * 2 + f[3] * g[5] + f[4] * g[4] * 2 + f[5] * g[3] + f[6] * g[2] * 2 + f[7] * g[1] + f[8] * g[10] * 38 + f[9] * g[9] * 19 + f[10] * g[8] * 38,
f[1] * g[8] + f[2] * g[7] + f[3] * g[6] + f[4] * g[5] + f[5] * g[4] + f[6] * g[3] + f[7] * g[2] + f[8] * g[1] + f[9] * g[10] * 19 + f[10] * g[9] * 19,
f[1] * g[9] + f[2] * g[8] * 2 + f[3] * g[7] + f[4] * g[6] * 2 + f[5] * g[5] + f[6] * g[4] * 2 + f[7] * g[3] + f[8] * g[2] * 2 + f[9] * g[1] + f[10] * g[10] * 38,
f[1] * g[10] + f[2] * g[9] + f[3] * g[8] + f[4] * g[7] + f[5] * g[6] + f[6] * g[5] + f[7] * g[4] + f[8] * g[3] + f[9] * g[2] + f[10] * g[1]
}
local carry
carry = h[1] + (1 << 25); h[2] = h[2] + arshift(carry, 26); h[1] = h[1] - (carry & kTop38Bits)
carry = h[5] + (1 << 25); h[6] = h[6] + arshift(carry, 26); h[5] = h[5] - (carry & kTop38Bits)
carry = h[2] + (1 << 24); h[3] = h[3] + arshift(carry, 25); h[2] = h[2] - (carry & kTop39Bits)
carry = h[6] + (1 << 24); h[7] = h[7] + arshift(carry, 25); h[6] = h[6] - (carry & kTop39Bits)
carry = h[3] + (1 << 25); h[4] = h[4] + arshift(carry, 26); h[3] = h[3] - (carry & kTop38Bits)
carry = h[7] + (1 << 25); h[8] = h[8] + arshift(carry, 26); h[7] = h[7] - (carry & kTop38Bits)
carry = h[4] + (1 << 24); h[5] = h[5] + arshift(carry, 25); h[4] = h[4] - (carry & kTop39Bits)
carry = h[8] + (1 << 24); h[9] = h[9] + arshift(carry, 25); h[8] = h[8] - (carry & kTop39Bits)
carry = h[5] + (1 << 25); h[6] = h[6] + arshift(carry, 26); h[5] = h[5] - (carry & kTop38Bits)
carry = h[9] + (1 << 25); h[10] = h[10] + arshift(carry, 26); h[9] = h[9] - (carry & kTop38Bits)
carry = h[10] + (1 << 24); h[1] = h[1] + arshift(carry, 25) * 19; h[10] = h[10] - (carry & kTop39Bits)
carry = h[1] + (1 << 25); h[2] = h[2] + arshift(carry, 26); h[1] = h[1] - (carry & kTop38Bits)
return h
end
local function fe_sq(f)
local h = {
f[1] * f[1] + f[2] * f[10] * 76 + f[3] * f[9] * 38 + f[4] * f[8] * 76 + f[5] * f[7] * 38 + f[6] * f[6] * 38,
f[1] * f[2] * 2 + f[3] * f[10] * 38 + f[4] * f[9] * 38 + f[5] * f[8] * 38 + f[6] * f[7] * 38,
f[1] * f[3] * 2 + f[2] * f[2] * 2 + f[4] * f[10] * 76 + f[5] * f[9] * 38 + f[6] * f[8] * 76 + f[7] * f[7] * 19,
f[1] * f[4] * 2 + f[2] * f[3] * 2 + f[5] * f[10] * 38 + f[6] * f[9] * 38 + f[7] * f[8] * 38,
f[1] * f[5] * 2 + f[2] * f[4] * 4 + f[3] * f[3] + f[6] * f[10] * 76 + f[7] * f[9] * 38 + f[8] * f[8] * 38,
f[1] * f[6] * 2 + f[2] * f[5] * 2 + f[3] * f[4] * 2 + f[7] * f[10] * 38 + f[8] * f[9] * 38,
f[1] * f[7] * 2 + f[2] * f[6] * 4 + f[3] * f[5] * 2 + f[4] * f[4] * 2 + f[8] * f[10] * 76 + f[9] * f[9] * 19,
f[1] * f[8] * 2 + f[2] * f[7] * 2 + f[3] * f[6] * 2 + f[4] * f[5] * 2 + f[9] * f[10] * 38,
f[1] * f[9] * 2 + f[2] * f[8] * 4 + f[3] * f[7] * 2 + f[4] * f[6] * 4 + f[5] * f[5] + f[10] * f[10] * 38,
f[1] * f[10] * 2 + f[2] * f[9] * 2 + f[3] * f[8] * 2 + f[4] * f[7] * 2 + f[5] * f[6] * 2
}
local carry
carry = h[1] + (1 << 25); h[2] = h[2] + arshift(carry, 26); h[1] = h[1] - (carry & kTop38Bits)
carry = h[5] + (1 << 25); h[6] = h[6] + arshift(carry, 26); h[5] = h[5] - (carry & kTop38Bits)
carry = h[2] + (1 << 24); h[3] = h[3] + arshift(carry, 25); h[2] = h[2] - (carry & kTop39Bits)
carry = h[6] + (1 << 24); h[7] = h[7] + arshift(carry, 25); h[6] = h[6] - (carry & kTop39Bits)
carry = h[3] + (1 << 25); h[4] = h[4] + arshift(carry, 26); h[3] = h[3] - (carry & kTop38Bits)
carry = h[7] + (1 << 25); h[8] = h[8] + arshift(carry, 26); h[7] = h[7] - (carry & kTop38Bits)
carry = h[4] + (1 << 24); h[5] = h[5] + arshift(carry, 25); h[4] = h[4] - (carry & kTop39Bits)
carry = h[8] + (1 << 24); h[9] = h[9] + arshift(carry, 25); h[8] = h[8] - (carry & kTop39Bits)
carry = h[5] + (1 << 25); h[6] = h[6] + arshift(carry, 26); h[5] = h[5] - (carry & kTop38Bits)
carry = h[9] + (1 << 25); h[10] = h[10] + arshift(carry, 26); h[9] = h[9] - (carry & kTop38Bits)
carry = h[10] + (1 << 24); h[1] = h[1] + arshift(carry, 25) * 19; h[10] = h[10] - (carry & kTop39Bits)
carry = h[1] + (1 << 25); h[2] = h[2] + arshift(carry, 26); h[1] = h[1] - (carry & kTop38Bits)
return h
end
local function fe_invert(z)
local t0 = fe_sq(z)
local t1 = fe_sq(t0)
t1 = fe_sq(t1)
t1 = fe_mul(z, t1)
t0 = fe_mul(t0, t1)
local t2 = fe_sq(t0)
t1 = fe_mul(t1, t2)
t2 = fe_sq(t1)
for _ = 1, 4 do
t2 = fe_sq(t2)
end
t1 = fe_mul(t2, t1)
t2 = fe_sq(t1)
for _ = 1, 9 do
t2 = fe_sq(t2)
end
t2 = fe_mul(t2, t1)
local t3 = fe_sq(t2)
for _ = 1, 19 do
t3 = fe_sq(t3)
end
t2 = fe_mul(t3, t2)
for _ = 0, 9 do
t2 = fe_sq(t2)
end
t1 = fe_mul(t2, t1)
t2 = fe_sq(t1)
for _ = 1, 49 do
t2 = fe_sq(t2)
end
t2 = fe_mul(t2, t1)
t3 = fe_sq(t2)
for _ = 1, 99 do
t3 = fe_sq(t3);
end
t2 = fe_mul(t3, t2)
t2 = fe_sq(t2)
for _ = 1, 49 do
t2 = fe_sq(t2)
end
t1 = fe_mul(t2, t1)
t1 = fe_sq(t1)
for _ = 1, 4 do
t1 = fe_sq(t1)
end
return fe_mul(t1, t0)
end
local function x25519_scalar_mult_generic(scalar, point)
local x1 = fe_frombytes(point)
local x2 = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
local z2 = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
local x3 = { table.unpack(x1) }
local z3 = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
local swap = 0
local e = { table.unpack(scalar) }
e[1] = e[1] & 248
e[32] = e[32] & 127
e[32] = e[32] | 64
for pos = 254, 0, -1 do
local b = 1 & (e[pos // 8 + 1] >> (pos & 7))
swap = swap ~ b
fe_cswap(x2, x3, swap)
fe_cswap(z2, z3, swap)
swap = b
local tmp0 = fe_sub(x3, z3)
local tmp1 = fe_sub(x2, z2)
fe_addto(x2, z2)
z2 = fe_add(x3, z3)
z3 = fe_mul(tmp0, x2)
z2 = fe_mul(z2, tmp1)
tmp0 = fe_sq(tmp1)
tmp1 = fe_sq(x2)
x3 = fe_add(z3, z2);
z2 = fe_sub(z3, z2);
x2 = fe_mul(tmp1, tmp0)
fe_subto(tmp1, tmp0);
z2 = fe_sq(z2)
z3 = fe_mul121666(tmp1)
x3 = fe_sq(x3)
fe_addto(tmp0, z3)
z3 = fe_mul(x1, z2)
z2 = fe_mul(tmp1, tmp0)
end
-- safety or something
for i = 1, 32 do
e[i] = 0
end
return fe_tobytes(fe_mul(fe_invert(z2), x2))
end
return x25519_scalar_mult_generic