Update Poly1305 to match the DJB approach for Fp arithmetic. This improves performance and correctness. Also fix wrong output when passing an empty string.
149 lines
4.7 KiB
Lua
149 lines
4.7 KiB
Lua
--- The Poly1305 one-time authenticator.
|
|
--
|
|
-- @module poly1305
|
|
--
|
|
|
|
local expect = require "cc.expect".expect
|
|
local random = require "ccryptolib.random"
|
|
|
|
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.
|
|
--
|
|
function mod.mac(key, message)
|
|
expect(1, key, "string")
|
|
assert(#key == 32, "key length must be 32")
|
|
expect(2, message, "string")
|
|
|
|
-- Pad message.
|
|
local pbplen = #message - 15
|
|
if #message % 16 ~= 0 or #message == 0 then
|
|
message = message .. "\1"
|
|
message = message .. ("\0"):rep(-#message % 16)
|
|
end
|
|
|
|
-- Decode r.
|
|
local R0, R1, R2, R3 = ("<I4I4I4I4"):unpack(key)
|
|
|
|
-- Clamp and shift.
|
|
R0 = R0 % 2 ^ 28
|
|
R1 = (R1 - R1 % 4) % 2 ^ 28 * 2 ^ 32
|
|
R2 = (R2 - R2 % 4) % 2 ^ 28 * 2 ^ 64
|
|
R3 = (R3 - R3 % 4) % 2 ^ 28 * 2 ^ 96
|
|
|
|
-- Split.
|
|
local r0 = R0 % 2 ^ 18 local r1 = R0 - r0
|
|
local r2 = R1 % 2 ^ 50 local r3 = R1 - r2
|
|
local r4 = R2 % 2 ^ 82 local r5 = R2 - r4
|
|
local r6 = R3 % 2 ^ 112 local r7 = R3 - r6
|
|
|
|
-- Generate scaled key.
|
|
local S1 = 5 / 2 ^ 130 * R1
|
|
local S2 = 5 / 2 ^ 130 * R2
|
|
local S3 = 5 / 2 ^ 130 * R3
|
|
|
|
-- Split.
|
|
local s2 = S1 % 2 ^ -80 local s3 = S1 - s2
|
|
local s4 = S2 % 2 ^ -48 local s5 = S2 - s4
|
|
local s6 = S3 % 2 ^ -16 local s7 = S3 - s6
|
|
|
|
local h0, h1, h2, h3, h4, h5, h6, h7 = 0, 0, 0, 0, 0, 0, 0, 0
|
|
|
|
for i = 1, #message, 16 do
|
|
-- Decode message block.
|
|
local m0, m1, m2, m3 = ("<I4I4I4I4"):unpack(message, i)
|
|
|
|
-- Shift message and add.
|
|
local x0 = h0 + h1 + m0
|
|
local x2 = h2 + h3 + m1 * 2 ^ 32
|
|
local x4 = h4 + h5 + m2 * 2 ^ 64
|
|
local x6 = h6 + h7 + m3 * 2 ^ 96
|
|
|
|
-- Apply per-block padding when applicable.
|
|
if i <= pbplen then x6 = x6 + 2 ^ 128 end
|
|
|
|
-- Multiply
|
|
h0 = x0 * r0 + x2 * s6 + x4 * s4 + x6 * s2
|
|
h1 = x0 * r1 + x2 * s7 + x4 * s5 + x6 * s3
|
|
h2 = x0 * r2 + x2 * r0 + x4 * s6 + x6 * s4
|
|
h3 = x0 * r3 + x2 * r1 + x4 * s7 + x6 * s5
|
|
h4 = x0 * r4 + x2 * r2 + x4 * r0 + x6 * s6
|
|
h5 = x0 * r5 + x2 * r3 + x4 * r1 + x6 * s7
|
|
h6 = x0 * r6 + x2 * r4 + x4 * r2 + x6 * r0
|
|
h7 = x0 * r7 + x2 * r5 + x4 * r3 + x6 * r1
|
|
|
|
-- Carry.
|
|
local y0 = h0 + 3 * 2 ^ 69 - 3 * 2 ^ 69 h0 = h0 - y0 h1 = h1 + y0
|
|
local y1 = h1 + 3 * 2 ^ 83 - 3 * 2 ^ 83 h1 = h1 - y1 h2 = h2 + y1
|
|
local y2 = h2 + 3 * 2 ^ 101 - 3 * 2 ^ 101 h2 = h2 - y2 h3 = h3 + y2
|
|
local y3 = h3 + 3 * 2 ^ 115 - 3 * 2 ^ 115 h3 = h3 - y3 h4 = h4 + y3
|
|
local y4 = h4 + 3 * 2 ^ 133 - 3 * 2 ^ 133 h4 = h4 - y4 h5 = h5 + y4
|
|
local y5 = h5 + 3 * 2 ^ 147 - 3 * 2 ^ 147 h5 = h5 - y5 h6 = h6 + y5
|
|
local y6 = h6 + 3 * 2 ^ 163 - 3 * 2 ^ 163 h6 = h6 - y6 h7 = h7 + y6
|
|
local y7 = h7 + 3 * 2 ^ 181 - 3 * 2 ^ 181 h7 = h7 - y7
|
|
|
|
-- Reduce carry overflow into first limb.
|
|
h0 = h0 + 5 / 2 ^ 130 * y7
|
|
end
|
|
|
|
-- Carry canonically.
|
|
local c0 = h0 % 2 ^ 16 h1 = h0 - c0 + h1
|
|
local c1 = h1 % 2 ^ 32 h2 = h1 - c1 + h2
|
|
local c2 = h2 % 2 ^ 48 h3 = h2 - c2 + h3
|
|
local c3 = h3 % 2 ^ 64 h4 = h3 - c3 + h4
|
|
local c4 = h4 % 2 ^ 80 h5 = h4 - c4 + h5
|
|
local c5 = h5 % 2 ^ 96 h6 = h5 - c5 + h6
|
|
local c6 = h6 % 2 ^ 112 h7 = h6 - c6 + h7
|
|
local c7 = h7 % 2 ^ 130
|
|
|
|
-- Reduce carry overflow.
|
|
h0 = c0 + 5 / 2 ^ 130 * (h7 - c7)
|
|
c0 = h0 % 2 ^ 16
|
|
c1 = h0 - c0 + c1
|
|
|
|
-- Canonicalize.
|
|
if c7 == 0xffff * 2 ^ 112
|
|
and c6 == 0xffff * 2 ^ 96
|
|
and c5 == 0xffff * 2 ^ 80
|
|
and c4 == 0xffff * 2 ^ 64
|
|
and c3 == 0xffff * 2 ^ 48
|
|
and c2 == 0xffff * 2 ^ 32
|
|
and c1 == 0xffff * 2 ^ 16
|
|
and c0 >= 0xfffa
|
|
then
|
|
c7, c6, c5, c4, c3, c2, c1, c0 = 0, 0, 0, 0, 0, 0, 0, c0 - 0xfffa
|
|
end
|
|
|
|
-- Decode s.
|
|
local s0, s1, s2, s3 = ("<I4I4I4I4"):unpack(key, 17)
|
|
|
|
-- Add.
|
|
local t0 = s0 + c0 + c1 local u0 = t0 % 2 ^ 32
|
|
local t1 = t0 - u0 + s1 * 2 ^ 32 + c2 + c3 local u1 = t1 % 2 ^ 64
|
|
local t2 = t1 - u1 + s2 * 2 ^ 64 + c4 + c5 local u2 = t2 % 2 ^ 96
|
|
local t3 = t2 - u2 + s3 * 2 ^ 96 + c6 + c7 local u3 = t3 % 2 ^ 128
|
|
|
|
-- Encode.
|
|
return ("<I4I4I4I4"):pack(u0, u1 / 2 ^ 32, u2 / 2 ^ 64, u3 / 2 ^ 96)
|
|
end
|
|
|
|
local mac = mod.mac
|
|
|
|
--- Verifies a Poly1305 tag.
|
|
--
|
|
-- @tparam string key The key used to generate the tag.
|
|
-- @tparam string message The message to authenticate.
|
|
-- @tparam string tag The tag to check.
|
|
-- @treturn boolean Whether the tag is valid or not.
|
|
--
|
|
function mod.verify(key, message, tag)
|
|
local kaux = random.random(32)
|
|
return mac(kaux, tag) == mac(kaux, mac(key, message))
|
|
end
|
|
|
|
return mod
|