diff --git a/internal/fq.lua b/internal/fq.lua index 9265080..5feae29 100644 --- a/internal/fq.lua +++ b/internal/fq.lua @@ -280,6 +280,11 @@ local function bits(a) return util.rebaseLE(demontgomery(a), 2 ^ 24, 2) end +--- Clones a scalar. +-- +-- @tparam {number...} a The scalar to clone. +-- @treturn {number...} The exact same value but as a different object. +-- local function clone(a) return {unpack(a)} end diff --git a/internal/maddq.lua b/internal/maddq.lua index df9127f..f016fc6 100644 --- a/internal/maddq.lua +++ b/internal/maddq.lua @@ -1,6 +1,26 @@ +--- Additive arithmetic masking modulo q (unstable, for internal use only). +-- +-- Representing secret scalars in Cobalt is potentially dangerous since the VM +-- leaks information through timing. To remedy this, we use _masking_, which is +-- a randomized representation of a scalar s as several values {s₁, s₂, s₃, ...} +-- such that s₁ + s₂ + s₃ + ... ≡ s (mod q). +-- +-- Using masking, we can perform arithmetic on the masked value without needing +-- to render the raw secrets in memory. After these operations, we can unwrap +-- the result which, depending on the operations, is harder to measure. +-- +-- @module internal.maddq +-- + local fq = require "ccryptolib.internal.fq" local random = require "ccryptolib.random" +--- Builds a masked scalar from a regular one. +-- +-- @tparam {number...} val The input scalar. +-- @tparam number order How many values to use, must be at least 2. +-- @treturn {{number...}...} The masked scalar. +-- local function new(val, order) local out = {} local sum = fq.num(0) @@ -14,36 +34,68 @@ local function new(val, order) return out end +--- Unwraps a masked scalar, getting the raw unmasked value. +-- +-- @tparam {{number...}...} arr The input masked scalar. +-- @treturn {number...} The unmasked scalar. +-- local function unwrap(arr) local sum = fq.num(0) for i = 1, #arr do sum = fq.add(sum, arr[i]) end return sum end +--- Encodes a masked scalar into a string. +-- +-- @tparam {{number...}...} arr The input masked scalar. +-- @treturn string The encoded value. +-- local function encode(arr) local out = {} for i = 1, #arr do out[i] = fq.encode(arr[i]) end return table.concat(out) end +--- Decodes a masked scalar from a string. +-- +-- @tparam string str The encoded scalar. Length must be a multiple of 32. +-- @treturn {{number...}...} The decoded value. +-- local function decode(str) local out = {} for i = 1, #str / 32 do out[i] = fq.decode(str:sub(i * 32 - 31, i * 32)) end return out end +--- Rerandomizes a masked scalar's representation. +-- +-- @tparam {{number...}...} arr The input masked scalar. +-- @treturn {{number...}...} The rerandomized representation of the input. +-- local function remask(arr) local out = new(fq.num(0), #arr) for i = 1, #arr do out[i] = fq.add(out[i], arr[i]) end return out end +--- Multiplies a masked scalar by a regular scalar. +-- +-- @tparam {{number...}...} arr The input masked scalar. +-- @tparam {number...} k The scalar to multiply by. +-- @treturn {{number...}...} The masked product of both values. +-- local function mul(arr, k) local out = {} for i = 1, #arr do out[i] = fq.mul(arr[i], k) end return out end +--- Adds a regular scalar to a masked scalar. +-- +-- @tparam {{number...}...} The input masked scalar. +-- @tparam {number...} v The scalar to add to. +-- @treturn {{number...}...} The masked sum of both values. +-- local function add(arr, v) local out = {} for i = 1, #arr do out[i] = fq.clone(arr[i]) end