Rework comments to new annotation style

This commit is contained in:
Miguel Oliveira 2023-06-08 01:15:16 -03:00
parent 6fbbab378a
commit cb620cfb0a
23 changed files with 451 additions and 575 deletions

View file

@ -1,7 +1,4 @@
--- The ChaCha20Poly1305AEAD authenticated encryption with associated data (AEAD) construction.
--
-- @module aead
--
local expect = require "cc.expect".expect
local lassert = require "ccryptolib.internal.util".lassert
@ -14,15 +11,13 @@ local u4x4, fmt4x4 = packing.compileUnpack("<I4I4I4I4")
local bxor = bit32.bxor
--- Encrypts a message.
--
-- @tparam string key A 32-byte random key.
-- @tparam string nonce A 12-byte per-message unique nonce.
-- @tparam string message The message to be encrypted.
-- @tparam string aad Arbitrary associated data to authenticate on decryption.
-- @tparam[opt=20] number rounds The number of ChaCha20 rounds to use.
-- @treturn string The ciphertext.
-- @treturn string The 16-byte authentication tag.
--
--- @param key string A 32-byte random key.
--- @param nonce string A 12-byte per-message unique nonce.
--- @param message string The message to be encrypted.
--- @param aad string aad Arbitrary associated data to also authenticate.
--- @param rounds number? The number of ChaCha20 rounds to use. Defaults to 20.
--- @return string ctx The ciphertext.
--- @return string tag The 16-byte authentication tag.
local function encrypt(key, nonce, message, aad, rounds)
expect(1, key, "string")
lassert(#key == 32, "key length must be 32", 2)
@ -53,16 +48,13 @@ local function encrypt(key, nonce, message, aad, rounds)
end
--- Decrypts a message.
--
-- @tparam string key The key used on encryption.
-- @tparam string nonce The nonce used on encryption.
-- @tparam string ciphertext The ciphertext to be decrypted.
-- @tparam string aad The arbitrary associated data used on encryption.
-- @tparam string tag The authentication tag returned on encryption.
-- @tparam[opt=20] number rounds The number of rounds used on encryption.
-- @treturn[1] string The decrypted plaintext.
-- @treturn[2] nil If authentication has failed.
--
--- @param key string The key used on encryption.
--- @param nonce string The nonce used on encryption.
--- @param ciphertext string The ciphertext to be decrypted.
--- @param aad string The arbitrary associated data used on encryption.
--- @param tag string The authentication tag returned on encryption.
--- @param rounds number The number of rounds used on encryption.
--- @return string? msg The decrypted plaintext. Or nil on auth failure.
local function decrypt(key, nonce, tag, ciphertext, aad, rounds)
expect(1, key, "string")
lassert(#key == 32, "key length must be 32", 2)

View file

@ -1,7 +1,4 @@
--- The BLAKE3 cryptographic hash function.
--
-- @module blake3
--
local expect = require "cc.expect".expect
local lassert = require "ccryptolib.internal.util".lassert
@ -208,11 +205,9 @@ end
local mod = {}
--- Hashes data using BLAKE3.
--
-- @tparam string message The input message.
-- @tparam[opt=32] number len The desired hash length, in bytes.
-- @treturn string The hash.
--
--- @param message string The input message.
--- @param len number? The desired hash length, in bytes. Defaults to 32.
--- @return string hash The hash.
function mod.digest(message, len)
expect(1, message, "string")
len = expect(2, len, "number", "nil") or 32
@ -222,12 +217,10 @@ function mod.digest(message, len)
end
--- Performs a keyed hash.
--
-- @tparam string key A 32-byte random key.
-- @tparam string message The input message.
-- @tparam[opt=32] number len The desired hash length, in bytes.
-- @treturn string The keyed hash.
--
--- @param key string A 32-byte random key.
--- @param message string The input message.
--- @param len number? The desired hash length, in bytes. Defaults to 32.
--- @return string hash The keyed hash.
function mod.digestKeyed(key, message, len)
expect(1, key, "string")
lassert(#key == 32, "key length must be 32", 2)
@ -239,14 +232,15 @@ function mod.digestKeyed(key, message, len)
end
--- Makes a context-based key derivation function (KDF).
--
-- @tparam string context The context for the KDF.
-- @treturn function(material:string [, len:number]):string The KDF.
--
--- @param context string The context for the KDF.
--- @return fun(material: string, len: number?): string kdf The KDF.
function mod.deriveKey(context)
expect(1, context, "string")
local iv = {u8x4(fmt8x4, blake3(IV, DERIVE_KEY_CONTEXT, context, 32), 1)}
--- Derives a key.
--- @param material string The keying material.
--- @param len number? The desired hash length, in bytes. Defaults to 32.
return function(material, len)
expect(1, material, "string")
len = expect(2, len, "number", "nil") or 32

View file

@ -1,7 +1,4 @@
--- The ChaCha20 stream cipher.
--
-- @module chacha20
--
local expect = require "cc.expect".expect
local lassert = require "ccryptolib.internal.util".lassert
@ -17,14 +14,12 @@ local u16x4 = packing.compileUnpack(fmt16x4)
local mod = {}
--- Encrypts/Decrypts data using ChaCha20.
--
-- @tparam string key A 32-byte random key.
-- @tparam string nonce A 12-byte per-message unique nonce.
-- @tparam string message A plaintext or ciphertext.
-- @tparam[opt=20] number rounds The number of ChaCha20 rounds to use.
-- @tparam[opt=1] number offset The block offset to generate the keystream at.
-- @treturn string The resulting ciphertext or plaintext.
--
--- @param key string A 32-byte random key.
--- @param nonce string A 12-byte per-message unique nonce.
--- @param message string A plaintext or ciphertext.
--- @param rounds number? The number of ChaCha20 rounds to use. Defaults to 20.
--- @param offset number? The block offset to generate the keystream at. Defaults to 1.
--- @return string out The resulting ciphertext or plaintext.
function mod.crypt(key, nonce, message, rounds, offset)
expect(1, key, "string")
lassert(#key == 32, "key length must be 32", 2)

View file

@ -1,7 +1,4 @@
--- The Ed25519 digital signature scheme.
--
-- @module ed25519
--
local expect = require "cc.expect".expect
local lassert = require "ccryptolib.internal.util".lassert
@ -13,10 +10,8 @@ local random = require "ccryptolib.random"
local mod = {}
--- Computes a public key from a secret key.
--
-- @tparam string sk A random 32-byte secret key.
-- @treturn string The matching 32-byte public key.
--
--- @param sk string A random 32-byte secret key.
--- @return string pk The matching 32-byte public key.
function mod.publicKey(sk)
expect(1, sk, "string")
assert(#sk == 32, "secret key length must be 32")
@ -28,12 +23,10 @@ function mod.publicKey(sk)
end
--- Signs a message.
--
-- @tparam string sk The signer's secret key.
-- @tparam string pk The signer's public key.
-- @tparam string msg The message to be signed.
-- @treturn string The 64-byte signature on the message.
--
--- @param sk string The signer's secret key.
--- @param pk string The signer's public key.
--- @param msg string The message to be signed.
--- @return string sig The 64-byte signature on the message.
function mod.sign(sk, pk, msg)
expect(1, sk, "string")
lassert(#sk == 32, "secret key length must be 32", 2)
@ -62,21 +55,19 @@ function mod.sign(sk, pk, msg)
end
--- Verifies a signature on a message.
--
-- @tparam string pk The signer's public key.
-- @tparam string msg The signed message.
-- @tparam string sig The signature.
-- @treturn boolean Whether the signature is valid or not.
--
--- @param pk string The signer's public key.
--- @param msg string The signed message.
--- @param sig string The alleged signature.
--- @return boolean valid Whether the signature is valid or not.
function mod.verify(pk, msg, sig)
expect(1, pk, "string")
lassert(#pk == 32, "public key length must be 32", 2)
lassert(#pk == 32, "public key length must be 32", 2) --- @cast pk String32
expect(2, msg, "string")
expect(3, sig, "string")
lassert(#sig == 64, "signature length must be 64", 2)
local y = ed.decode(pk)
if not y then return nil end
if not y then return false end
local rStr = sig:sub(1, 32)
local sStr = sig:sub(33)

View file

@ -1,19 +1,16 @@
--- Point arithmetic on the Curve25519 Montgomery curve.
--
-- :::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.
-- :::
--
-- <br />
--
-- @module[kind=internal] internal.curve25519
--
local fp = require "ccryptolib.internal.fp"
local ed = require "ccryptolib.internal.edwards25519"
local random = require "ccryptolib.random"
--- @class MtPoint A point class on Curve25519, in XZ coordinates.
--- @field [1] number[] The X coordinate.
--- @field [2] number[] The Z coordinate.
--- Doubles a point.
--- @param P1 MtPoint The point to double.
--- @return MtPoint P2 P1 + P1.
local function double(P1)
local x1, z1 = P1[1], P1[2]
local a = fp.add(x1, z1)
@ -26,6 +23,11 @@ local function double(P1)
return {x3, z3}
end
--- Computes differential addition on two points.
--- @param DP MtPoint P1 - P2.
--- @param P1 MtPoint The first point to add.
--- @param P2 MtPoint The second point to add.
--- @return MtPoint P3 P1 + P2.
local function dadd(DP, P1, P2)
local dx, dz = DP[1], DP[2]
local x1, z1 = P1[1], P1[2]
@ -42,13 +44,11 @@ local function dadd(DP, P1, P2)
end
--- Performs a step on the Montgomery ladder.
--
-- @param C A - B.
-- @param A The first point.
-- @param B The second point.
-- @return 2A
-- @return A + B
--
--- @param DP MtPoint P1 - P2.
--- @param P1 MtPoint The first point.
--- @param P2 MtPoint The second point.
--- @return MtPoint P3 2A
--- @return MtPoint P4 A + B
local function step(DP, P1, P2)
local dx, dz = DP[1], DP[2]
local x1, z1 = P1[1], P1[2]
@ -85,50 +85,46 @@ local function ladder(DP, bits)
end
--- Performs a scalar multiplication operation with multiplication by 8.
--
-- @tparam point P The base point.
-- @tparam {number...} bits The scalar multiplier, in little-endian bits.
-- @treturn point The product, multiplied by 8.
--
--- @param P MtPoint The base point.
--- @param bits number[] The scalar multiplier, in little-endian bits.
--- @return MtPoint product The product, multiplied by 8.
local function ladder8(P, bits)
-- Randomize.
local rf = fp.decode(random.random(32))
local rf = fp.decode(random.random(32) --[[@as String32, length is given]])
P = {fp.mul(P[1], rf), fp.mul(P[2], rf)}
-- Multiply.
return double(double(double(ladder(P, bits))))
end
--- Scales a point's coordinates.
--- @param P MtPoint The input point.
--- @return MtPoint Q The same point P, but with Z = 1.
local function scale(P)
return {fp.mul(P[1], fp.invert(P[2])), fp.num(1)}
end
--- Encodes a point.
--
-- @tparam point P1 The scaled point to encode.
-- @treturn string The 32-byte encoded point.
--
--- Encodes a scaled point.
--- @param P MtPoint The scaled point to encode.
--- @return string encoded P, encoded into a 32-byte string.
local function encode(P)
return fp.encode(P[1])
end
--- Decodes a point.
--
-- @tparam string str A 32-byte encoded point.
-- @treturn point The decoded point.
--
--- @param str String32 A 32-byte encoded point.
--- @return MtPoint pt The decoded point.
local function decode(str)
return {fp.decode(str), fp.num(1)}
end
--- Decodes an Edwards25519 encoded point into Curve25519, ignoring the sign.
--
-- There is a single exception: The identity point (0, 1), which gets mapped
-- into the 2-torsion point (0, 0), which isn't the identity of Curve25519.
--
-- @tparam string str A 32-byte encoded Edwards25519 point.
-- @treturn point The decoded point, mapped into Curve25519.
--
---
--- There is a single exception: The identity point (0, 1), which gets mapped
--- into the 2-torsion point (0, 0), which isn't the identity of Curve25519.
---
--- @param str String32 A 32-byte encoded Edwards25519 point.
--- @return MtPoint pt The decoded point, mapped into Curve25519.
local function decodeEd(str)
local y = fp.decode(str)
local n = fp.carry(fp.add(fp.num(1), y))
@ -141,10 +137,8 @@ local function decodeEd(str)
end
--- Performs a scalar multiplication by the base point G.
--
-- @tparam {number...} bits The scalar multiplier, in little-endian bits.
-- @return The product point.
--
--- @param bits number[] The scalar multiplier, in little-endian bits.
--- @return MtPoint product The product point.
local function mulG(bits)
-- Multiply by G on Edwards25519.
local P = ed.mulG(bits)
@ -159,17 +153,17 @@ local function mulG(bits)
end
--- Computes a twofold product from a ruleset.
--
-- @tparam point P The base point.
-- @tparam {{number...}, {number...}} The ruleset generated by scalars m, n.
-- @treturn[1] point [8m]P
-- @treturn[1] point [8n]P
-- @treturn[1] point [8m]P - [8n]P
-- @treturn[2] nil If any of the three results is equal to O.
--
---
--- Returns nil if any of the results would be equal to the identity.
---
--- @param P MtPoint The base point.
--- @param ruleset __TYPE_TODO The ruleset generated by scalars m, n.
--- @return MtPoint? A [8m]P.
--- @return MtPoint? B [8n]P.
--- @return MtPoint? C [8m]P - [8n]P.
local function prac(P, ruleset)
-- Randomize.
local rf = fp.decode(random.random(32))
local rf = fp.decode(random.random(32) --[[@as String32, length is given]])
local A = {fp.mul(P[1], rf), fp.mul(P[2], rf)}
-- Start the base at [8]P.
@ -184,7 +178,7 @@ local function prac(P, ruleset)
-- Reject rulesets where m = n.
local rules = ruleset[2]
if #rules == 0 then return nil end
if #rules == 0 then return end
-- Evaluate the first rule.
-- Since e = d, this means A - B = C = O. Differential addition fails when

View file

@ -1,30 +1,31 @@
--- Point arithmetic on the Edwards25519 Edwards curve.
--
-- :::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.
-- :::
--
-- <br />
--
-- @module[kind=internal] internal.edwards25519
--
local fp = require "ccryptolib.internal.fp"
local unpack = unpack or table.unpack
--- @class EdPoint A point on Edwards25519, in extended coordinates.
--- @field [1] number[] The X coordinate.
--- @field [2] number[] The Y coordinate.
--- @field [3] number[] The Z coordinate.
--- @field [4] number[] The T coordinate.
--- @class NsPoint A point on Edwards25519, in Niels' coordinates.
--- @field [1] number[] Preprocessed Y + X.
--- @field [2] number[] Preprocessed Y - X.
--- @field [3] number[] Preprocessed 2Z.
--- @field [4] number[] Preprocessed 2DT.
local D = fp.mul(fp.num(-121665), fp.invert(fp.num(121666)))
local K = fp.kmul(D, 2)
--- @type EdPoint
local O = {fp.num(0), fp.num(1), fp.num(1), fp.num(0)}
local G = nil
--- Doubles a point.
--
-- @tparam point P1 The point to double.
-- @treturn point Twice P1.
--
--- @param P1 EdPoint The point to double.
--- @return EdPoint P2 P1 + P1.
local function double(P1)
-- Unsoundness: fp.sub(g, e), and fp.sub(d, i) break fp.sub's contract since
-- it doesn't accept an fp2. Although not ideal, in practice this doesn't
@ -48,14 +49,12 @@ local function double(P1)
end
--- Adds two points.
--
-- @tparam point P1 The first summand point.
-- @tparam niels N1 The second summand point, in Niels form. See @{niels}.
-- @treturn point The sum.
--
local function add(P1, N1)
--- @param P1 EdPoint The first summand point.
--- @param N2 NsPoint The second summand point.
--- @return EdPoint P3 P1 + P2, where N2 = niels(P2).
local function add(P1, N2)
local P1x, P1y, P1z, P1t = unpack(P1)
local N1p, N1m, N1z, N1t = unpack(N1)
local N1p, N1m, N1z, N1t = unpack(N2)
local a = fp.sub(P1y, P1x)
local b = fp.mul(a, N1m)
local c = fp.add(P1y, P1x)
@ -73,9 +72,13 @@ local function add(P1, N1)
return {P3x, P3y, P3z, P3t}
end
local function sub(P1, N1)
--- Subtracts one point from another.
--- @param P1 EdPoint The first summand point.
--- @param N2 NsPoint The second summand point.
--- @return EdPoint P3 P1 - P2, where N2 = niels(P2).
local function sub(P1, N2)
local P1x, P1y, P1z, P1t = unpack(P1)
local N1p, N1m, N1z, N1t = unpack(N1)
local N1p, N1m, N1z, N1t = unpack(N2)
local a = fp.sub(P1y, P1x)
local b = fp.mul(a, N1p)
local c = fp.add(P1y, P1x)
@ -94,10 +97,8 @@ local function sub(P1, N1)
end
--- Computes the Niels representation of a point.
--
-- @tparam point P1
-- @treturn niels P1's Niels representation.
--
--- @param P1 EdPoint The input point.
--- @return NsPoint N1 Niels' precomputation applied to P1.
local function niels(P1)
local P1x, P1y, P1z, P1t = unpack(P1)
local N3p = fp.add(P1y, P1x)
@ -107,6 +108,9 @@ local function niels(P1)
return {N3p, N3m, N3z, N3t}
end
--- Scales a point.
--- @param P1 EdPoint The input point.
--- @return EdPoint P2 The same point as P1, but with Z = 1.
local function scale(P1)
local P1x, P1y, P1z = unpack(P1)
local zInv = fp.invert(P1z)
@ -117,11 +121,9 @@ local function scale(P1)
return {P3x, P3y, P3z, P3t}
end
--- Encodes a point.
--
-- @tparam point P1 The scaled point to encode.
-- @treturn string The 32-byte encoded point.
--
--- Encodes a scaled point.
--- @param P1 EdPoint The scaled point to encode.
--- @return string out P1 encoded as a 32-byte string.
local function encode(P1)
P1 = scale(P1)
local P1x, P1y = unpack(P1)
@ -131,11 +133,8 @@ local function encode(P1)
end
--- Decodes a point.
--
-- @tparam string str A 32-byte encoded point.
-- @treturn[1] point The decoded point.
-- @treturn[2] nil If the string did not represent a valid encoded point.
--
--- @param str String32 A 32-byte encoded point.
--- @return EdPoint? P1 The decoded point, or nil if it isn't on the curve.
local function decode(str)
local P3y = fp.decode(str)
local a = fp.square(P3y)
@ -153,8 +152,12 @@ local function decode(str)
return {P3x, P3y, P3z, P3t}
end
G = decode("Xfffffffffffffffffffffffffffffff")
G = decode("Xfffffffffffffffffffffffffffffff") --[[@as EdPoint, G is valid]]
--- Transforms little-endian bits into a signed radix-2^w form.
--- @param bits number[]
--- @param w number Log2 of the radix, must be at least 1.
--- @return number[]
local function signedRadixW(bits, w)
-- TODO Find a more elegant way of doing this.
local wPow = 2 ^ w
@ -176,6 +179,10 @@ local function signedRadixW(bits, w)
return out
end
--- Computes a multiplication table for radix-2^w form multiplication.
--- @param P EdPoint The base point.
--- @param w number Log2 of the radix, must be at least 1.
--- @return NsPoint[][]
local function radixWTable(P, w)
local out = {}
for i = 1, math.ceil(256 / w) do
@ -190,10 +197,21 @@ local function radixWTable(P, w)
return out
end
--- The radix logarithm of the precomputed table for G.
local G_W = 5
--- The precomputed multiplication table for G.
local G_TABLE = radixWTable(G, G_W)
local function WNAF(bits, w)
--- Transforms little-endian bits into a signed radix-2^w non-adjacent form.
---
--- The returned array contains a 0 whenever a single doubling is needed, or an
--- odd integer when an addition with a multiple of the base is needed.
---
--- @param bits number[]
--- @param w number Log2 of the radix, must be at least 1.
--- @return number[]
local function wNaf(bits, w)
-- TODO Find a more elegant way of doing this.
local wPow = 2 ^ w
local wPowh = wPow / 2
@ -220,6 +238,10 @@ local function WNAF(bits, w)
return out
end
--- Computes a multiplication table for wNAF form multiplication.
--- @param P EdPoint The base point.
--- @param w number Log2 of the radix, must be at least 1.
--- @return NsPoint[]
local function WNAFTable(P, w)
local dP = double(P)
local out = {niels(P)}
@ -230,10 +252,8 @@ local function WNAFTable(P, w)
end
--- Performs a scalar multiplication by the base point G.
--
-- @tparam {number...} bits The scalar multiplier, in little-endian bits.
-- @treturn point The product.
--
--- @param bits number[] The scalar multiplicand little-endian bits.
--- @return EdPoint
local function mulG(bits)
local sw = signedRadixW(bits, G_W)
local R = O
@ -249,13 +269,11 @@ local function mulG(bits)
end
--- Performs a scalar multiplication operation.
--
-- @tparam point P The base point.
-- @tparam {number...} bits The scalar multiplier, in little-endian bits.
-- @treturn point The product.
--
--- @param P EdPoint The base point.
--- @param bits number[] The scalar multiplicand little-endian bits.
--- @return EdPoint
local function mul(P, bits)
local naf = WNAF(bits, 5)
local naf = wNaf(bits, 5)
local tbl = WNAFTable(P, 5)
local R = O
for i = #naf, 1, -1 do

View file

@ -1,21 +1,19 @@
--- Arithmetic on Curve25519's base 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.
-- :::
--
-- <br />
--
-- @module[kind=internal] internal.fp
--
local packing = require "ccryptolib.internal.packing"
local unpack = unpack or table.unpack
local ufp, fmtfp = packing.compileUnpack("<I3I3I2I3I3I2I3I3I2I3I3I2")
--- @class Fq An element of the field of integers modulo 2²⁵⁵ - 19.
--- @class FpR2: Fq An Fp element with limbs inside twice the standard range.
--- @class FpR1: FpR2 An Fp element with limbs inside the standard range. See
--- the Curve25519 polynomial representation for more info around this.
--- The modular square root of -1.
--- @type FpR1
local I = {
0958640 * 2 ^ 0,
0826664 * 2 ^ 22,
@ -32,19 +30,15 @@ local I = {
}
--- Converts a Lua number to an element.
--
-- @tparam number n A number n in [0..2²²).
-- @treturn fp1
--
--- @param n number A number n in [0..2²²).
--- @return FpR1 out The number as an element.
local function num(n)
return {n, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
end
--- Negates an element.
--
-- @tparam fp1 a
-- @treturn fp1 -a.
--
--- @param a FpR1
--- @return FpR1 out -a.
local function neg(a)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11 = unpack(a)
return {
@ -64,11 +58,9 @@ local function neg(a)
end
--- Adds two elements.
--
-- @tparam fp1 a
-- @tparam fp1 b
-- @treturn fp2 a + b.
--
--- @param a FpR1
--- @param b FpR1
--- @return FpR2 out a + b.
local function add(a, b)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11 = unpack(a)
local b00, b01, b02, b03, b04, b05, b06, b07, b08, b09, b10, b11 = unpack(b)
@ -89,11 +81,9 @@ local function add(a, b)
end
--- Subtracts an element from another.
--
-- @tparam fp1 a
-- @tparam fp1 b
-- @treturn fp2 a - b.
--
--- @param a FpR1
--- @param b FpR1
--- @return FpR2 out a - b.
local function sub(a, b)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11 = unpack(a)
local b00, b01, b02, b03, b04, b05, b06, b07, b08, b09, b10, b11 = unpack(b)
@ -113,13 +103,9 @@ local function sub(a, b)
}
end
--- Carries an element.
--
-- Also performs a small reduction modulo p.
--
-- @tparam fp2 a
-- @treturn fp1 a' ≡ a (mod p).
--
--- Carries an element. Also performs a small reduction modulo p.
--- @param a FpR2 The element to carry.
--- @return FpR1 out The same element as a but in a tighter range.
local function carry(a)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11 = unpack(a)
local c00, c01, c02, c03, c04, c05, c06, c07, c08, c09, c10, c11
@ -157,14 +143,14 @@ local function carry(a)
end
--- Returns the canoncal representative of a modp number.
--
-- Some elements can be represented by two different arrays of floats. This
-- returns the canonical element of the represented equivalence class. We define
-- an element as canonical if it's the smallest nonnegative number in its class.
--
-- @tparam fp2 a
-- @treturn fp1 A canonical element a' ≡ a (mod p).
--
---
--- Some elements can be represented by two different arrays of floats. This
--- returns the canonical element of the represented equivalence class. We
--- define an element as canonical if it's the smallest nonnegative number in
--- its class.
---
--- @param a FpR2
--- @return FpR1 out A canonical element a' ≡ a (mod p).
local function canonicalize(a)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11 = unpack(a)
local c00, c01, c02, c03, c04, c05, c06, c07, c08, c09, c10, c11
@ -205,11 +191,9 @@ local function canonicalize(a)
end
--- Returns whether two elements are the same.
--
-- @tparam fp1 a
-- @tparam fp1 b
-- @treturn boolean Whether the two elements are the same mod p.
--
--- @param a FpR1
--- @param b FpR1
--- @return boolean eq Whether a ≡ b (mod p).
local function eq(a, b)
local c = canonicalize(sub(a, b))
for i = 1, 12 do if c[i] ~= 0 then return false end end
@ -217,11 +201,9 @@ local function eq(a, b)
end
--- Multiplies two elements.
--
-- @tparam fp2 a
-- @tparam fp2 b
-- @treturn fp1 c ≡ a × b (mod p).
--
--- @param a FpR2
--- @param b FpR2
--- @return FpR1 c An element such that c ≡ a × b (mod p).
local function mul(a, b)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11 = unpack(a)
local b00, b01, b02, b03, b04, b05, b06, b07, b08, b09, b10, b11 = unpack(b)
@ -421,10 +403,8 @@ local function mul(a, b)
end
--- Squares an element.
--
-- @tparam fp2 a
-- @treturn fp1 c ≡ a² (mod p).
--
--- @param a FpR2
--- @return FpR1 b An element such that b ≡ a² (mod p).
local function square(a)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11 = unpack(a)
local d00, d01, d02, d03, d04, d05, d06, d07, d08, d09, d10
@ -571,11 +551,9 @@ local function square(a)
end
--- Multiplies an element by a number.
--
-- @tparam fp2 a
-- @tparam number k A number k in [0..2²²).
-- @treturn fp1 c ≡ a × k (mod p).
--
--- @param a FpR2
--- @param k number A number in [0..2²²).
--- @return FpR1 c An element such that c ≡ a × k (mod p).
local function kmul(a, k)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11 = unpack(a)
local c00, c01, c02, c03, c04, c05, c06, c07, c08, c09, c10, c11
@ -627,24 +605,20 @@ local function kmul(a, k)
end
--- Squares an element n times.
--
-- @tparam fp2 a
-- @tparam number n A positive integer.
-- @treturn fp1 c ≡ a ^ 2 ^ n (mod p).
--
--- @param a FpR2
--- @param n number The number of times to square a.
--- @return FpR1 c A number c such that c ≡ a ^ 2 ^ n (mod p).
local function nsquare(a, n)
for _ = 1, n do a = square(a) end
return a
end
--- Computes the inverse of an element.
--
-- Computation of the inverse requires 11 multiplications and 252 squarings.
--
-- @tparam fp2 a
-- @treturn[1] fp1 c ≡ a⁻¹ (mod p), if a ≠ 0.
-- @treturn[2] fp1 c ≡ 0 (mod p), if a = 0.
--
---
--- Performance: 11 multiplications and 252 squarings.
---
--- @param a FpR2
--- @return FpR1 c An element such that c ≡ a⁻¹ (mod p), or 0 if c doesn't exist.
local function invert(a)
local a2 = square(a)
local a9 = mul(a, nsquare(a2, 2))
@ -662,15 +636,13 @@ local function invert(a)
return mul(nsquare(x250, 5), a11)
end
--- Returns an element x that satisfies v * x² = u.
--
-- Note that when v = 0, the returned element can take any value.
--
-- @tparam fp2 u
-- @tparam fp2 v
-- @treturn[1] fp1 x.
-- @treturn[2] nil if there is no solution.
--
--- Returns an element x that satisfies vx² = u.
---
--- Note that when v = 0, the returned element can take any value.
---
--- @param u FpR2
--- @param v FpR2
--- @return FpR1? x An element such that vx² ≡ u (mod p), if it exists.
local function sqrtDiv(u, v)
u = carry(u)
@ -711,11 +683,11 @@ local function sqrtDiv(u, v)
end
end
--- @class String32: string A string with length equal to 32 bytes.
--- Encodes an element in little-endian.
--
-- @tparam fp2 a
-- @treturn string A 32-byte string. Always represents the canonical element.
--
--- @param a FpR1
--- @return String32 out The 32-byte canonical encoding of a.
local function encode(a)
a = canonicalize(a)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10, a11 = unpack(a)
@ -744,14 +716,12 @@ local function encode(a)
putBytes(3) acc = acc + a11 / 2 ^ 232
putBytes(3)
return string.char(unpack(bytes))
return string.char(unpack(bytes)) --[[@as String32, putBytes sums to 32]]
end
--- Decodes an element in little-endian.
--
-- @tparam string b A 32-byte string. The most-significant bit is discarded.
-- @treturn fp1 The decoded element. May not be canonical.
--
--- @param b String32 A 32-byte string, the most-significant bit is discarded.
--- @return FpR1 out The decoded element. It may not be canonical.
local function decode(b)
local w00, w01, w02, w03, w04, w05, w06, w07, w08, w09, w10, w11 =
ufp(fmtfp, b, 1)
@ -774,11 +744,9 @@ local function decode(b)
}
end
--- Checks if two elements are equal.
--
-- @tparam fp2 a
-- @treturn boolean Whether a ≡ 0 (mod p).
--
--- Checks if the given element is equal to 0.
--- @param a FpR2
--- @return boolean eqz Whether a ≡ 0 (mod p).
local function eqz(a)
local c = canonicalize(a)
local c00, c01, c02, c03, c04, c05, c06, c07, c08, c09, c10, c11 = unpack(c)

View file

@ -90,14 +90,12 @@ 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
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
return (mp.carry(c)) -- cc < q implies that the carry number is 0.
end
--- Adds two scalars mod q.
@ -170,15 +168,6 @@ local function demontgomery(a)
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²⁴).
@ -378,12 +367,8 @@ local function makeRuleset(a, b)
end
return {
num = num,
add = add,
neg = neg,
sub = sub,
montgomery = montgomery,
demontgomery = demontgomery,
mul = mul,
encode = encode,
decode = decode,

View file

@ -12,12 +12,19 @@
local unpack = unpack or table.unpack
--- A little-endian big integer of width 11 in (-2⁵²..2⁵²).
--- @class MpSW11L52
--- A little-endian big integer of width 11 in (-2²⁴, 2²⁴).
--- @class MpSW11L24: MpSW11L52
--- A little-endian big integer of width 11 in [0..2²⁴).
--- @class MpUW11L24: MpSW11L24
--- Carries a number in base 2²⁴ into a signed limb form.
--
-- @tparam {number...} a A number a in (-2²⁸⁸..2²⁸⁸) as 11 limbs in
-- [-2⁵²..2⁵²].
-- @treturn {number...} a as 12 limbs in (-2²⁴..2²⁴).
--
--- @param a MpSW11L52
--- @return MpSW11L24 low The carried low limbs.
--- @return number carry The overflowed carry.
local function carryWeak(a)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10 = unpack(a)
@ -45,16 +52,13 @@ local function carryWeak(a)
a08 - h08,
a09 - h09,
a10 - h10,
h10 * 2 ^ -24,
}
}, h10 * 2 ^ -24
end
--- Carries a number in base 2²⁴.
--
-- @tparam {number...} a A number a in [0..2²⁸⁸) as 11 limbs in
-- [-2⁵²..2⁵²].
-- @treturn {number...} a as 12 limbs in [0..2²⁴).
--
--- @param a MpSW11L52
--- @return MpUW11L24 low The low 11 limbs of the output.
--- @return number carry The overflow carry.
local function carry(a)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10 = unpack(a)
@ -71,15 +75,13 @@ local function carry(a)
local l10 = a10 % 2 ^ 24
local h10 = (a10 - l10) * 2 ^ -24
return {l00, l01, l02, l03, l04, l05, l06, l07, l08, l09, l10, h10}
return {l00, l01, l02, l03, l04, l05, l06, l07, l08, l09, l10}, h10
end
--- Adds two numbers.
--
-- @tparam {number...} a An array of 11 limbs in (k..l).
-- @tparam {number...} b An array of 11 limbs in (m..n).
-- @treturn {number...} a + b as 11 limbs in ((k + m)..(l + n)).
--
--- @param a MpSW11L24
--- @param b MpSW11L24
--- @return MpSW11L52 c a + b
local function add(a, b)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10 = unpack(a)
local b00, b01, b02, b03, b04, b05, b06, b07, b08, b09, b10 = unpack(b)
@ -100,11 +102,9 @@ local function add(a, b)
end
--- Subtracts a number from another.
--
-- @tparam {number...} a An array of 11 limbs in (k..l).
-- @tparam {number...} b An array of 11 limbs in (m..n).
-- @treturn {number...} a + b as 11 limbs in ((k - m)..(l - n)).
--
--- @param a MpSW11L24
--- @param b MpSW11L24
--- @return MpSW11L52 c a - b
local function sub(a, b)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10 = unpack(a)
local b00, b01, b02, b03, b04, b05, b06, b07, b08, b09, b10 = unpack(b)
@ -125,17 +125,15 @@ local function sub(a, b)
end
--- Computes the lower half of a product between two numbers.
--
-- @tparam {number...} a A nonnegative integer as 11 limbs in [0..2²⁴).
-- @tparam {number...} b A nonnegative integer as 11 limbs in [0..2²⁴).
-- @treturn {number...} c ≡ a × b (mod 2²⁶⁴) as 11 limbs in [0..2²⁴).
-- @treturn number ⌊a × b ÷ 2²⁶⁴⌋.
--
--- @param a MpUW11L24
--- @param b MpUW11L24
--- @return MpUW11L24 c a × b (mod 2²⁶⁴)
--- @return number carry ⌊a × b ÷ 2²⁶⁴⌋
local function lmul(a, b)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10 = unpack(a)
local b00, b01, b02, b03, b04, b05, b06, b07, b08, b09, b10 = unpack(b)
local out = carry {
return carry {
a00 * b00,
a01 * b00 + a00 * b01,
a02 * b00 + a01 * b01 + a00 * b02,
@ -148,28 +146,21 @@ local function lmul(a, b)
a09 * b00 + a08 * b01 + a07 * b02 + a06 * b03 + a05 * b04 + a04 * b05 + a03 * b06 + a02 * b07 + a01 * b08 + a00 * b09,
a10 * b00 + a09 * b01 + a08 * b02 + a07 * b03 + a06 * b04 + a05 * b05 + a04 * b06 + a03 * b07 + a02 * b08 + a01 * b09 + a00 * b10,
}
-- Strip overflow.
local of = out[12]
out[12] = nil
return out, of
end
--- Computes the a product between two numbers.
--
-- @tparam {number...} a An array of 11 limbs in [0..2²⁴).
-- @tparam {number...} b An array of 11 limbs in [0..2²⁴).
-- @treturn {number...} The first 11 limbs of a × b in [0..2²⁴).
-- @treturn {number...} The last 11 limbs of a × b in [0..2²⁴).
--
--- @param a MpUW11L24
--- @param b MpUW11L24
--- @return MpUW11L24 low The low 11 limbs of a × b.
--- @return MpUW11L24 high The high 11 limbs of a × b.
local function mul(a, b)
local low, of = lmul(a, b)
local _, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10 = unpack(a)
local _, b01, b02, b03, b04, b05, b06, b07, b08, b09, b10 = unpack(b)
local high = carry {
-- The carry is always 0.
return low, (carry {
of + a10 * b01 + a09 * b02 + a08 * b03 + a07 * b04 + a06 * b05 + a05 * b06 + a04 * b07 + a03 * b08 + a02 * b09 + a01 * b10,
a10 * b02 + a09 * b03 + a08 * b04 + a07 * b05 + a06 * b06 + a05 * b07 + a04 * b08 + a03 * b09 + a02 * b10,
a10 * b03 + a09 * b04 + a08 * b05 + a07 * b06 + a06 * b07 + a05 * b08 + a04 * b09 + a03 * b10,
@ -181,40 +172,31 @@ local function mul(a, b)
a10 * b09 + a09 * b10,
a10 * b10,
0
}
-- Strip overflow (it's always 0).
high[12] = nil
return low, high
})
end
--- Computes a double-width sum of two numbers.
--
-- @tparam {number...} a0 The low part of a as 11 limbs in [0..2²⁴).
-- @tparam {number...} a1 The high part of a as 11 limbs in [0..2²⁴).
-- @tparam {number...} b0 The low part of b as 11 limbs in [0..2²⁴).
-- @tparam {number...} b1 The high part of b as 11 limbs in [0..2²⁴).
-- @treturn {number...} The low part of a + b as 11 limbs in [0..2²⁴).
-- @treturn {number...} The high part of a + b as 12 limbs in [0..2²⁴).
--
--- @param a0 MpUW11L24 The low 11 limbs of a.
--- @param a1 MpUW11L24 The high 11 limbs of a.
--- @param b0 MpUW11L24 The low 11 limbs of b.
--- @param b1 MpUW11L24 The high 11 limbs of b.
--- @return MpUW11L24 c0 The low 11 limbs of a + b.
--- @return MpUW11L24 c1 The high 11 limbs of a + b.
--- @return number The carry.
local function dwadd(a0, a1, b0, b1)
local low = carry(add(a0, b0))
local low, c = carry(add(a0, b0))
local high = add(a1, b1)
high[1] = high[1] + low[12]
low[12] = nil
high[1] = high[1] + c
return low, carry(high)
end
--- Computes half of a number.
--
-- @tparam {number...} a An even positive integer as 11 limbs in (-2²⁴..2²⁴).
-- @treturn {number...} a ÷ 2 as 11 limbs in (-2²⁴..2²⁴).
--
--- @param a MpSW11L24 The number to halve, must be even.
--- @return MpSW11L24 c a ÷ 2
local function half(a)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10 = unpack(a)
local out = carryWeak {
return (carryWeak {
a00 * 0.5 + a01 * 2 ^ 23,
a02 * 2 ^ 23,
a03 * 2 ^ 23,
@ -226,18 +208,12 @@ local function half(a)
a09 * 2 ^ 23,
a10 * 2 ^ 23,
0,
}
out[12] = nil
return out
})
end
--- Computes a third of a number.
--
-- @tparam {number...} a A positive multiple of 3 as 11 limbs in (-2²⁶..2²⁶).
-- @treturn {number...} a ÷ 3 as 11 limbs in (-2²⁴..2²⁴).
--
--- @param a MpSW11L24 The number to divide, must be a multiple of 3.
--- @return MpSW11L24 c a ÷ 3
local function third(a)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10 = unpack(a)
@ -253,7 +229,9 @@ local function third(a)
local d09 = a09 * 0xaaaaaa + d08
local d10 = a10 * 0xaaaaaa + d09
local out = carryWeak {
-- We compute the modular division mod 2²⁶⁴. The carry isn't 0 but it isn't
-- part of a ÷ 3 either.
return (carryWeak {
a00 + d00,
a01 + d01,
a02 + d02,
@ -265,39 +243,27 @@ local function third(a)
a08 + d08,
a09 + d09,
a10 + d10,
}
-- We compute the modular division mod 2²⁶⁴. out[12] isn't 0 but it's not
-- part of a ÷ 3 either.
out[12] = nil
return out
})
end
--- Computes a number modulo 2.
--
-- @tparam {number...} a A number as 11 limbs in (-2²⁶, 2²⁶).
-- @treturn number a mod 2.
--
--- @param a MpSW11L24
--- @return number c a mod 2.
local function mod2(a)
return a[1] % 2
end
--- Computes a number modulo 3.
--
-- @tparam {number...} a A number as 11 limbs in (-2²⁶, 2²⁶).
-- @treturn number a mod 3.
--
--- @param a MpSW11L24
--- @return number c a mod 3.
local function mod3(a)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10 = unpack(a)
return (a00 + a01 + a02 + a03 + a04 + a05 + a06 + a07 + a08 + a09 + a10) % 3
end
--- Computes a double representing the most-significant bits of a number.
--
-- @tparam {number...} a A number as 11 limbs in (-2⁴⁸..2⁴⁸).
-- @treturn number A floating-point approximation for the value of a.
--
--- @param a MpSW11L52
--- @return number c A floating-point approximation for the value of a.
local function approx(a)
local a00, a01, a02, a03, a04, a05, a06, a07, a08, a09, a10 = unpack(a)
return a00
@ -314,11 +280,9 @@ local function approx(a)
end
--- Compares two numbers for ordering.
--
-- @tparam {number...} a A number as 11 limbs in (-2²⁵..2²⁵).
-- @tparam {number...} b A number as 11 limbs in (-2²⁵..2²⁵).
-- @treturn number Some number x with x < 0 iff a < b and x = 0 iff a = b.
--
--- @param a MpSW11L24
--- @param b MpSW11L24
--- @return number ord Some number with ord < 0 iff a < b and ord = 0 iff a = b.
local function cmp(a, b)
return approx(sub(a, b))
end

View file

@ -1,22 +1,11 @@
--- High-performance binary packing of integers.
--
-- :::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.
-- :::
--
-- <br />
--
-- :::warning
-- For performance reasons, **the generated functions do not check types,
-- lengths, nor ranges**. You must ensure that the passed arguments are
-- well-formed and respect the format string yourself.
-- :::
--
-- <br />
--
-- @module[kind=internal] internal.packing
--
---
--- Remark (and warning):
--- For performance reasons, **the generated functions do not check types,
--- lengths, nor ranges**. You must ensure that the passed arguments are
--- well-formed and respect the format string yourself.
---
--- <br />
local fmt = string.format
@ -119,14 +108,17 @@ if not string.pack or pcall(string.dump, string.pack) then
local packCache = {}
local unpackCache = {}
--- (`string.pack == nil`) Compiles a binary packing function.
-- @tparam string fmt A string matched by `^([><])I[I%d]+$`.
-- @treturn function A high-performance function that behaves like an unsafe
-- version of `string.pack` for the given format string. Note that the third
-- argument isn't optional.
-- @treturn string fmt
-- @throws If the string is invalid or has an invalid integral size.
-- @throws If the compiled function is too large.
-- I CAN'T EVEN WITH THIS EXTENSION, WHY CAN'T IT HANDLE MORE THAN A SINGLE
-- LINE OF RETURN DESCRIPTION? LOOK AT IT!!! THE COMMENT GOES OVER THERE ------------------------------------------------------------------> look! ↓ ↓ ↓
--- (string.pack is nil) Compiles a binary packing function.
---
--- Errors if the format string is invalid or has an invalid integral size,
--- or if the compiled function turns out too large.
---
--- @param fmt string A string matched by `^([><])I[I%d]+$`.
--- @return fun(_ignored: any, ...: any): string pack A function that behaves like an unsafe version of `string.pack` for the given format string.
--- @return string fmt
function mod.compilePack(fmt)
if not packCache[fmt] then
packCache[fmt] = compile(fmt, mkPack)
@ -134,13 +126,14 @@ if not string.pack or pcall(string.dump, string.pack) then
return packCache[fmt], fmt
end
--- (`string.pack == nil`) Compiles a binary unpacking function.
-- @tparam string fmt A string matched by `^([><])I[I%d]+$`.
-- @treturn function A high-performance function that behaves like an unsafe
-- version of `string.unpack` for the given format string.
-- @treturn string fmt
-- @throws If the string is invalid or has an invalid integral size.
-- @throws If the compiled function is too large.
--- (string.pack is nil) Compiles a binary unpacking function.
---
--- Errors if the format string is invalid or has an invalid integral size,
--- or if the compiled function turns out too large.
---
--- @param fmt string A string matched by `^([><])I[I%d]+$`.
--- @return fun(_ignored: any, str: string, pos: number) unpack A function that behaves like an unsafe version of `string.unpack` for the given format string. Note that the third argument isn't optional.
--- @return string fmt
function mod.compileUnpack(fmt)
if not unpackCache[fmt] then
unpackCache[fmt] = compile(fmt, mkUnpack)
@ -150,16 +143,16 @@ if not string.pack or pcall(string.dump, string.pack) then
return mod
else
--- (`string.pack ~= nil`) Compiles a binary packing function.
-- @tparam string fmt
-- @treturn function `string.pack`
-- @treturn string fmt
--- (string.pack isn't nil) It's string.pack! It returns string.pack!
--- @param fmt string
--- @return fun(fmt: string, ...: any): string pack string.pack!
--- @return string fmt
mod.compilePack = function(fmt) return string.pack, fmt end
--- (`string.pack ~= nil`) Compiles a binary unpacking function.
-- @tparam string fmt
-- @treturn function `string.unpack`
-- @treturn string fmt
--- (string.pack isn't nil) It's string.unpack! It returns string.unpack!
--- @param fmt string
--- @return fun(fmt: string, str: string, pos: number) unpack string.unpack!
--- @return string fmt
mod.compileUnpack = function(fmt) return string.unpack, fmt end
end

View file

@ -1,14 +1,4 @@
--- The SHA512 cryptographic hash function.
--
-- :::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.
-- :::
--
-- <br />
--
-- @module[kind=internal] internal.sha512
--
local expect = require "cc.expect".expect
local packing = require "ccryptolib.internal.packing"
@ -59,10 +49,8 @@ local K = {
}
--- Hashes data bytes using SHA512.
--
-- @tparam string data The input data.
-- @treturn string The 64-byte hash value.
--
--- @param data string The input data.
--- @return string hash The 64-byte hash value.
local function digest(data)
expect(1, data, "string")

View file

@ -4,12 +4,10 @@ local function lassert(val, err, level)
end
--- Converts a little-endian array from one power-of-two base to another.
--
-- @tparam {number...} a The array to convert, in little-endian.
-- @tparam number base1 The base to convert from. Must be a power of 2.
-- @tparam number base2 The base to convert to. Must be a power of 2.
-- @treturn {number...}
--
--- @param a number[] The array to convert, in little-endian.
--- @param base1 number The base to convert from. Must be a power of 2.
--- @param base2 number The base to convert to. Must be a power of 2.
--- @return number[]
local function rebaseLE(a, base1, base2) -- TODO Write contract properly.
local out = {}
local outlen = 1
@ -33,10 +31,8 @@ local function rebaseLE(a, base1, base2) -- TODO Write contract properly.
end
--- Decodes bits with X25519/Ed25519 exponent clamping.
--
-- @taparm string str The 32-byte encoded exponent.
-- @treturn {number...} The decoded clamped bits.
--
--- @param str string The 32-byte encoded exponent.
--- @return number[] bits The decoded clamped bits.
local function bits(str)
-- Decode.
local bytes = {str:byte(1, 32)}
@ -61,10 +57,8 @@ local function bits(str)
end
--- Decodes bits with X25519/Ed25519 exponent clamping and division by 8.
--
-- @taparm string str The 32-byte encoded exponent.
-- @treturn {number...} The decoded clamped bits, divided by 8.
--
--- @param str string The 32-byte encoded exponent.
--- @return number[] bits The decoded clamped bits, divided by 8.
local function bits8(str)
return {unpack(bits(str), 4)}
end

View file

@ -1,7 +1,4 @@
--- The Poly1305 one-time authenticator.
--
-- @module poly1305
--
local expect = require "cc.expect".expect
local lassert = require "ccryptolib.internal.util".lassert
@ -13,11 +10,9 @@ local p4x4 = packing.compilePack(fmt4x4)
local mod = {}
--- Computes a Poly1305 message authentication code.
--
-- @tparam string key A 32-byte single-use random key.
-- @tparam string message The message to authenticate.
-- @treturn string The 16-byte authentication tag.
--
--- @param key string A 32-byte single-use random key.
--- @param message string The message to authenticate.
--- @return string tag The 16-byte authentication tag.
function mod.mac(key, message)
expect(1, key, "string")
lassert(#key == 32, "key length must be 32", 2)

View file

@ -22,9 +22,7 @@ local initialized = false
local mod = {}
--- Mixes entropy into the generator, and marks it as initialized.
--
-- @tparam string seed The seed data.
--
--- @param seed string The seed data.
function mod.init(seed)
expect(1, seed, "string")
state = blake3.digestKeyed(state, seed)
@ -32,18 +30,14 @@ function mod.init(seed)
end
--- Mixes extra entropy into the generator state.
--
-- @tparam string seed The additional entropy to mix.
--
--- @param data string The additional entropy to mix.
function mod.mix(data)
state = blake3.digestKeyed(state, data)
end
--- Generates random bytes.
--
-- @tparam number len The desired output length.
-- @throws If the generator hasn't been initialized.
--
--- @param len number The desired output length.
--- @return string bytes
function mod.random(len)
expect(1, len, "number")
lassert(initialized, "attempt to use an uninitialized random generator", 2)

View file

@ -1,7 +1,4 @@
--- The SHA256 cryptographic hash function.
--
-- @module sha256
--
local expect = require "cc.expect".expect
local lassert = require "ccryptolib.internal.util".lassert
@ -79,10 +76,8 @@ local function compress(h, w)
end
--- Hashes data using SHA256.
--
-- @tparam string data Input bytes.
-- @treturn string The 32-byte hash value.
--
--- @param data string Input bytes.
--- @return string hash The 32-byte hash value.
local function digest(data)
expect(1, data, "string")
@ -101,12 +96,10 @@ local function digest(data)
end
--- Hashes a password using PBKDF2-HMAC-SHA256.
--
-- @tparam password string The password to hash.
-- @tparam salt string The password's salt.
-- @tparam iter number The number of iterations to perform.
-- @treturn string The 32-byte derived key.
--
--- @param password string The password to hash.
--- @param salt string The password's salt.
--- @param iter number The number of iterations to perform.
--- @return string dk The 32-byte derived key.
local function pbkdf2(password, salt, iter)
expect(1, password, "string")
expect(2, salt, "string")

View file

@ -1,7 +1,4 @@
--- The X25519 key exchange scheme.
--
-- @module x25519
--
local expect = require "cc.expect".expect
local lassert = require "ccryptolib.internal.util".lassert
@ -11,10 +8,8 @@ local c25 = require "ccryptolib.internal.curve25519"
local mod = {}
--- Computes the public key from a secret key.
--
-- @tparam string sk A random 32-byte secret key.
-- @treturn string The matching public key.
--
--- @param sk string A random 32-byte secret key.
--- @return string pk The matching public key.
function mod.publicKey(sk)
expect(1, sk, "string")
assert(#sk == 32, "secret key length must be 32")
@ -22,25 +17,27 @@ function mod.publicKey(sk)
end
--- Performs the key exchange.
--
-- @tparam string sk A secret key.
-- @tparam string pk A public key, usually derived from a second secret key.
-- @treturn string The 32-byte shared secret between both keys.
--
--- @param sk string A Curve25519 secret key.
--- @param pk string A public key, usually derived from someone else's secret key.
--- @return string ss The 32-byte shared secret between both keys.
function mod.exchange(sk, pk)
expect(1, sk, "string")
lassert(#sk == 32, "secret key length must be 32", 2)
expect(2, pk, "string")
lassert(#pk == 32, "public key length must be 32", 2)
lassert(#pk == 32, "public key length must be 32", 2) --- @cast pk String32
return c25.encode(c25.scale(c25.ladder8(c25.decode(pk), util.bits8(sk))))
end
--- Same as @{exchange}, but decodes the public key as an Edwards25519 point.
--- Performs the key exchange, but decoding the public key as an Edwards25519
--- point, using the birational map.
--- @param sk string A Curve25519 secret key
--- @param pk string An Edwards25519 public key, usually derived from someone else's secret key.
--- @return string ss The 32-byte shared secret between both keys.
function mod.exchangeEd(sk, pk)
expect(1, sk, "string")
lassert(#sk == 32, "secret key length must be 32", 2)
expect(2, pk, "string")
lassert(#pk == 32, "public key length must be 32", 2)
lassert(#pk == 32, "public key length must be 32", 2) --- @cast pk String32
return c25.encode(c25.scale(c25.ladder8(c25.decodeEd(pk), util.bits8(sk))))
end

View file

@ -8,6 +8,8 @@ local sha512 = require "ccryptolib.internal.sha512"
local random = require "ccryptolib.random"
--- Masks an exchange secret key.
--- @param sk string A random 32-byte Curve25519 secret key.
--- @return string msk A masked secret key.
local function maskX(sk)
expect(1, sk, "string")
lassert(#sk == 32, "secret key length must be 32", 2)
@ -19,6 +21,8 @@ local function maskX(sk)
end
--- Masks a signature secret key.
--- @param sk string A random 32-byte Edwards25519 secret key.
--- @return string msk A masked secret key.
function maskS(sk)
expect(1, sk, "string")
lassert(#sk == 32, "secret key length must be 32", 2)
@ -26,27 +30,29 @@ function maskS(sk)
end
--- Rerandomizes the masking on a masked key.
local function remask(sk)
expect(1, sk, "string")
lassert(#sk == 64, "masked secret key length must be 64", 2)
--- @param msk string A masked secret key.
--- @return string msk The same secret key, but with another mask.
local function remask(msk)
expect(1, msk, "string")
lassert(#msk == 64, "masked secret key length must be 64", 2)
local newMask = random.random(32)
local xr = fq.decode(sk:sub(1, 32))
local r = fq.decodeClamped(sk:sub(33))
local xr = fq.decode(msk:sub(1, 32))
local r = fq.decodeClamped(msk:sub(33))
local s = fq.decodeClamped(newMask)
local xs = fq.add(xr, fq.sub(r, s))
return fq.encode(xs) .. newMask
end
--- Returns the ephemeral exchange secret key of this masked key.
--
-- This is the second secret key in the "double key exchange" in @{exchange},
-- the first being the key that has been masked. The ephemeral key changes every
-- time @{remask} is called.
--
local function ephemeralSk(sk)
expect(1, sk, "string")
lassert(#sk == 64, "masked secret key length must be 64", 2)
return sk:sub(33)
--- This is the second secret key in the "double key exchange" in @{exchange},
--- the first being the key that has been masked. The ephemeral key changes
--- every time @{remask} is called.
--- @param msk string A masked secret key.
--- @return string esk The ephemeral half of the masked secret key.
local function ephemeralSk(msk)
expect(1, msk, "string")
lassert(#msk == 64, "masked secret key length must be 64", 2)
return msk:sub(33)
end
local function exchangeOnPoint(sk, P)
@ -108,54 +114,69 @@ local function exchangeOnPoint(sk, P)
end
--- Returns the X25519 public key of this masked key.
local function publicKeyX(sk)
expect(1, sk, "string")
lassert(#sk == 64, "masked secret key length must be 64", 2)
return (exchangeOnPoint(sk, c25.G))
--- @param msk string A masked secret key.
local function publicKeyX(msk)
expect(1, msk, "string")
lassert(#msk == 64, "masked secret key length must be 64", 2)
return (exchangeOnPoint(msk, c25.G))
end
--- Returns the Ed25519 public key of this masked key.
local function publicKeyS(sk)
expect(1, sk, "string")
lassert(#sk == 64, "masked secret key length must be 64", 2)
local xr = fq.decode(sk:sub(1, 32))
local r = fq.decodeClamped(sk:sub(33))
--- @param msk string A masked secret key.
--- @return string pk The Ed25519 public key matching this masked key.
local function publicKeyS(msk)
expect(1, msk, "string")
lassert(#msk == 64, "masked secret key length must be 64", 2)
local xr = fq.decode(msk:sub(1, 32))
local r = fq.decodeClamped(msk:sub(33))
local y = ed.add(ed.mulG(fq.bits(xr)), ed.niels(ed.mulG(fq.bits(r))))
return ed.encode(ed.scale(y))
end
--- Performs a double key exchange.
--
-- Returns 0 if the input public key has small order or if it isn't in the base
-- curve. This is different from standard X25519, which performs the exchange
-- even on the twist.
--
-- May incorrectly return 0 with negligible chance if the mask happens to match
-- the masked key. I haven't checked if clamping prevents that from happening.
--
---
--- Returns 0 if the input public key has small order or if it isn't in the base
--- curve. This is different from standard X25519, which performs the exchange
--- even on the twist.
---
--- May incorrectly return 0 with negligible chance if the mask happens to match
--- the masked key. I haven't checked if clamping prevents that from happening.
---
--- @param sk string A masked secret key.
--- @param pk string An X25519 public key.
--- @return string sss The shared secret between the public key and the static half of the masked key.
--- @return string sse The shared secret betwen the public key and the ephemeral half of the masked key.
local function exchangeX(sk, pk)
expect(1, sk, "string")
lassert(#sk == 64, "masked secret key length must be 64", 2)
expect(2, pk, "string")
lassert(#pk == 32, "public key length must be 32", 2)
lassert(#pk == 32, "public key length must be 32", 2) --- @cast pk String32
return exchangeOnPoint(sk, c25.decode(pk))
end
--- Performs an exchange against an Ed25519 key.
--
-- This is done by converting the key into X25519 before passing it to the
-- regular exchange. Using this function on the result of @{signaturePk} leads
-- to the same value as using @{exchange} on the result of @{exchangePk}.
--
---
--- This is done by converting the key into X25519 before passing it to the
--- regular exchange. Using this function on the result of @{signaturePk} leads
--- to the same value as using @{exchange} on the result of @{exchangePk}.
---
--- @param sk string A masked secret key.
--- @param pk string An Ed25519 public key.
--- @return string sss The shared secret between the public key and the static half of the masked key.
--- @return string sse The shared secret betwen the public key and the ephemeral half of the masked key.
local function exchangeS(sk, pk)
expect(1, sk, "string")
lassert(#sk == 64, "masked secret key length must be 64", 2)
expect(2, pk, "string")
lassert(#pk == 32, "public key length must be 32", 2)
lassert(#pk == 32, "public key length must be 32", 2) --- @cast pk String32
return exchangeOnPoint(sk, c25.decodeEd(pk))
end
--- Signs a message using Ed25519.
--- @param sk string A masked secret key.
--- @param pk string The Ed25519 public key matching the secret key.
--- @param msg string A message to sign.
--- @return string sig The signature on the message.
local function sign(sk, pk, msg)
expect(1, sk, "string")
lassert(#sk == 64, "masked secret key length must be 64", 2)

View file

@ -16,15 +16,15 @@ describe("aead.encrypt", function()
-- Types
expect.error(aead.encrypt, nil, nonce, msg, aad, rounds)
:eq("bad argument #1 (expected string, got nil)")
:eq("bad argument #1 (string expected, got nil)")
expect.error(aead.encrypt, key, nil, msg, aad, rounds)
:eq("bad argument #2 (expected string, got nil)")
:eq("bad argument #2 (string expected, got nil)")
expect.error(aead.encrypt, key, nonce, nil, aad, rounds)
:eq("bad argument #3 (expected string, got nil)")
:eq("bad argument #3 (string expected, got nil)")
expect.error(aead.encrypt, key, nonce, msg, nil, rounds)
:eq("bad argument #4 (expected string, got nil)")
:eq("bad argument #4 (string expected, got nil)")
expect.error(aead.encrypt, key, nonce, msg, aad, {})
:eq("bad argument #5 (expected number, got table)")
:eq("bad argument #5 (number expected, got table)")
-- String lengths
expect.error(aead.encrypt, key .. "a", nonce, msg, aad, rounds)
@ -155,17 +155,17 @@ describe("aead.decrypt", function()
-- Types
expect.error(aead.decrypt, nil, nonce, tag, ctx, aad, rounds)
:eq("bad argument #1 (expected string, got nil)")
:eq("bad argument #1 (string expected, got nil)")
expect.error(aead.decrypt, key, nil, tag, ctx, aad, rounds)
:eq("bad argument #2 (expected string, got nil)")
:eq("bad argument #2 (string expected, got nil)")
expect.error(aead.decrypt, key, nonce, nil, ctx, aad, rounds)
:eq("bad argument #3 (expected string, got nil)")
:eq("bad argument #3 (string expected, got nil)")
expect.error(aead.decrypt, key, nonce, tag, nil, aad, rounds)
:eq("bad argument #4 (expected string, got nil)")
:eq("bad argument #4 (string expected, got nil)")
expect.error(aead.decrypt, key, nonce, tag, ctx, nil, rounds)
:eq("bad argument #5 (expected string, got nil)")
:eq("bad argument #5 (string expected, got nil)")
expect.error(aead.decrypt, key, nonce, tag, ctx, aad, {})
:eq("bad argument #6 (expected number, got table)")
:eq("bad argument #6 (number expected, got table)")
-- String lengths
expect.error(aead.decrypt, key .. "a", nonce, tag, ctx, aad, rounds)

View file

@ -17,9 +17,9 @@ describe("blake3.digest", function()
it("validates arguments", function()
-- Types
expect.error(blake3.digest, nil)
:eq("bad argument #1 (expected string, got nil)")
:eq("bad argument #1 (string expected, got nil)")
expect.error(blake3.digest, "", {})
:eq("bad argument #2 (expected number, got table)")
:eq("bad argument #2 (number expected, got table)")
-- Length
expect.error(blake3.digest, "", 0.5)
@ -51,11 +51,11 @@ describe("blake3.digestKeyed", function()
-- Types
expect.error(blake3.digestKeyed, nil, "")
:eq("bad argument #1 (expected string, got nil)")
:eq("bad argument #1 (string expected, got nil)")
expect.error(blake3.digestKeyed, key, nil)
:eq("bad argument #2 (expected string, got nil)")
:eq("bad argument #2 (string expected, got nil)")
expect.error(blake3.digestKeyed, key, "", {})
:eq("bad argument #3 (expected number, got table)")
:eq("bad argument #3 (number expected, got table)")
-- String lengths
expect.error(blake3.digestKeyed, key .. "a", "")
@ -90,11 +90,11 @@ describe("blake3.deriveKey", function()
it("validates arguments", function()
-- Types
expect.error(blake3.deriveKey, nil)
:eq("bad argument #1 (expected string, got nil)")
:eq("bad argument #1 (string expected, got nil)")
expect.error(blake3.deriveKey(""), nil)
:eq("bad argument #1 (expected string, got nil)")
:eq("bad argument #1 (string expected, got nil)")
expect.error(blake3.deriveKey(""), "", {})
:eq("bad argument #2 (expected number, got table)")
:eq("bad argument #2 (number expected, got table)")
-- Length
expect.error(blake3.deriveKey(""), "", 0.5)

View file

@ -16,15 +16,15 @@ describe("chacha20.crypt", function()
-- Types
expect.error(chacha20.crypt, nil, nonce, msg, rounds, offset)
:eq("bad argument #1 (expected string, got nil)")
:eq("bad argument #1 (string expected, got nil)")
expect.error(chacha20.crypt, key, nil, msg, rounds, offset)
:eq("bad argument #2 (expected string, got nil)")
:eq("bad argument #2 (string expected, got nil)")
expect.error(chacha20.crypt, key, nonce, nil, rounds, offset)
:eq("bad argument #3 (expected string, got nil)")
:eq("bad argument #3 (string expected, got nil)")
expect.error(chacha20.crypt, key, nonce, msg, {}, offset)
:eq("bad argument #4 (expected number, got table)")
:eq("bad argument #4 (number expected, got table)")
expect.error(chacha20.crypt, key, nonce, msg, nil, {})
:eq("bad argument #5 (expected number, got table)")
:eq("bad argument #5 (number expected, got table)")
-- String lengths
expect.error(chacha20.crypt, key .. "a", nonce, msg, rounds, offset)

View file

@ -13,9 +13,9 @@ describe("poly1305.mac", function()
-- Types
expect.error(poly1305.mac, nil, msg)
:eq("bad argument #1 (expected string, got nil)")
:eq("bad argument #1 (string expected, got nil)")
expect.error(poly1305.mac, key, nil)
:eq("bad argument #2 (expected string, got nil)")
:eq("bad argument #2 (string expected, got nil)")
-- Key length
expect.error(poly1305.mac, key .. "a", msg)

View file

@ -12,7 +12,7 @@ local longMsg = require "spec.vec.sha256.long"
describe("sha256.digest", function()
it("validates arguments", function()
expect.error(sha256.digest, nil)
:eq("bad argument #1 (expected string, got nil)")
:eq("bad argument #1 (string expected, got nil)")
end)
it("passes the NIST SHAVS byte-oriented short messages test", function()

View file

@ -9,10 +9,10 @@ local sha512 = require "ccryptolib.internal.sha512"
local shortMsg = require "spec.vec.sha512.short"
local longMsg = require "spec.vec.sha512.long"
describe("sha256.digest", function()
describe("sha512.digest", function()
it("validates arguments", function()
expect.error(sha512.digest, nil)
:eq("bad argument #1 (expected string, got nil)")
:eq("bad argument #1 (string expected, got nil)")
end)
it("passes the NIST SHAVS byte-oriented short messages test", function()