--- Arithmetic on Curve25519's scalar field.
--
-- :::note Internal Module
-- This module is meant for internal use within the library. Its API is unstable
-- and subject to change without major version bumps.
-- :::
--
--
--
-- @module[kind=internal] internal.fq
--
local mp = require "ccryptolib.internal.mp"
local util = require "ccryptolib.internal.util"
local unpack = unpack or table.unpack
--- The scalar field's order, q.
local Q = {
16110573,
06494812,
14047250,
10680220,
14612958,
00000020,
00000000,
00000000,
00000000,
00000000,
00004096,
}
--- The first Montgomery precomputed constant, -q⁻¹ mod 2²⁶⁴.
local T0 = {
05537307,
01942290,
16765621,
16628356,
10618610,
07072433,
03735459,
01369940,
15276086,
13038191,
13409718,
}
--- The second Montgomery precomputed constant, 2⁵²⁸ mod q.
local T1 = {
11711996,
01747860,
08326961,
03814718,
01859974,
13327461,
16105061,
07590423,
04050668,
08138906,
00000283,
}
local T8 = {
01130678,
05563041,
03870191,
01622646,
01247520,
12151703,
16693196,
09337410,
04700637,
07308819,
00002083,
}
local ZERO = mp.num(0)
--- Reduces a number modulo q.
--
-- @tparam {number...} a A number a < 2q as 11 limbs in [0..2²⁵).
-- @treturn {number...} a mod q as 11 limbs in [0..2²⁴).
--
local function reduce(a)
local c = mp.sub(a, Q)
-- Return carry(a) if a < q.
if mp.approx(c) < 0 then return mp.carry(a) end
-- c >= q means c - q >= 0.
-- Since q < 2²⁸⁸, c < 2q means c - q < q < 2²⁸⁸.
-- c's limbs fit in (-2²⁶..2²⁶), since subtraction adds at most one bit.
local cc = mp.carry(c)
cc[12] = nil -- cc < q implies that cc[12] = 0.
return cc
end
--- Adds two scalars mod q.
--
-- If the two operands are in Montgomery form, returns the correct result also
-- in Montgomery form, since (2²⁶⁴ × a) + (2²⁶⁴ × b) ≡ 2²⁶⁴ × (a + b) (mod q).
--
-- @tparam {number...} a A number a < q as 11 limbs in [0..2²⁴).
-- @tparam {number...} b A number b < q as 11 limbs in [0..2²⁴).
-- @treturn {number...} a + b mod q as 11 limbs in [0..2²⁴).
--
local function add(a, b)
return reduce(mp.add(a, b))
end
--- Negates a scalar mod q.
--
-- @tparam {number...} a A number a < q as 11 limbs in [0..2²⁴).
-- @treturn {number...} -a mod q as 11 limbs in [0..2²⁴).
--
local function neg(a)
return reduce(mp.sub(Q, a))
end
--- Subtracts scalars mod q.
--
-- If the two operands are in Montgomery form, returns the correct result also
-- in Montgomery form, since (2²⁶⁴ × a) - (2²⁶⁴ × b) ≡ 2²⁶⁴ × (a - b) (mod q).
--
-- @tparam {number...} a A number a < q as 11 limbs in [0..2²⁴).
-- @tparam {number...} b A number b < q as 11 limbs in [0..2²⁴).
-- @treturn {number...} a - b mod q as 11 limbs in [0..2²⁴).
--
local function sub(a, b)
return add(a, neg(b))
end
--- Given two scalars a and b, computes 2⁻²⁶⁴ × a × b mod q.
--
-- @tparam {number...} a A number a as 11 limbs in [0..2²⁴).
-- @tparam {number...} b A number b < q as 11 limbs in [0..2²⁴).
-- @treturn 2⁻²⁶⁴ × a × b mod q as 11 limbs in [0..2²⁴).
--
local function mul(a, b)
local t0, t1 = mp.mul(a, b)
local mq0, mq1 = mp.mul(mp.lmul(t0, T0), Q)
local _, s1 = mp.dwadd(t0, t1, mq0, mq1)
return reduce(s1)
end
--- Converts a scalar into Montgomery form.
--
-- @tparam {number...} a A number a as 11 limbs in [0..2²⁴).
-- @treturn {number...} 2²⁶⁴ × a mod q as 11 limbs in [0..2²⁴).
--
local function montgomery(a)
-- 0 ≤ a < 2²⁶⁴ and 0 ≤ T1 < q.
return mul(a, T1)
end
--- Converts a scalar from Montgomery form.
--
-- @tparam {number...} a A number a < q as 11 limbs in [0..2²⁴).
-- @treturn {number...} 2⁻²⁶⁴ × a mod q as 11 limbs in [0..2²⁴).
--
local function demontgomery(a)
-- It's REDC all over again except b is 1.
local mq0, mq1 = mp.mul(mp.lmul(a, T0), Q)
local _, s1 = mp.dwadd(a, ZERO, mq0, mq1)
return reduce(s1)
end
--- Converts a Lua number to a scalar.
--
-- @tparam number n A number n in [0..2²⁴).
-- @treturn {number...} 2²⁶⁴ × n mod q as 11 limbs in [0..2²⁴).
--
local function num(n)
return montgomery({n, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
end
--- Encodes a scalar.
--
-- @tparam {number...} a A number 2²⁶⁴ × a mod q as 11 limbs in [0..2²⁴).
-- @treturn string The 32-byte string encoding of a.
--
local function encode(a)
return ("