constantine/research/glv.nim

265 lines
7.6 KiB
Nim
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Research into the paper
# - Efficient and Secure Algorithms for GLV-Based Scalar
# Multiplication and their Implementation on GLV-GLS
# Curves (Extended Version)
# Armando Faz-Hernández, Patrick Longa, Ana H. Sánchez, 2013
# https://eprint.iacr.org/2013/158.pdf
import ../constantine/math/elliptic/ec_endomorphism_accel {.all.},
../constantine/platforms/abstractions,
../constantine/math/io/io_bigints,
../constantine/math/arithmetic
proc toString(glvSac: GLV_SAC): string =
for j in 0 ..< glvSac.M:
result.add "k" & $j & ": ["
for i in countdown(glvSac.LengthInDigits-1, 0):
result.add " " & (block:
case glvSac[j][i]
of 0: "0"
of 1: "1"
else:
raise newException(ValueError, "Unexpected encoded value: " & $glvSac[j][i])
)
result.add " ]\n"
iterator bits(u: SomeInteger): tuple[bitIndex: int32, bitValue: uint8] =
## bit iterator, starts from the least significant bit
var u = u
var idx = 0'i32
while u != 0:
yield (idx, uint8(u and 1))
u = u shr 1
inc idx
func buildLookupTable_naive[M: static int](
P: string,
endomorphisms: array[M-1, string],
lut: var array[1 shl (M-1), string],
) =
## Checking the LUT by building strings of endomorphisms additions
## This naively translates the lookup table algorithm
## Compute P[u] = P0 + u0 P1 +...+ um2 Pm1 for all 0≤u<2m1, where
## u= (um2,...,u0)_2.
## The number of additions done per entries is equal to the
## iteration variable `u` Hamming Weight
for u in 0 ..< 1 shl (M-1):
lut[u] = P
for u in 0 ..< 1 shl (M-1):
for idx, bit in bits(u):
if bit == 1:
lut[u] &= " + " & endomorphisms[idx]
func buildLookupTable_reuse[M: static int](
P: string,
endomorphisms: array[M-1, string],
lut: var array[1 shl (M-1), string],
) =
## Checking the LUT by building strings of endomorphisms additions
## This reuses previous table entries so that only one addition is done
## per new entries
lut[0] = P
for u in 1'u32 ..< 1 shl (M-1):
let msb = u.log2_vartime() # No undefined, u != 0
lut[u] = lut[u.clearBit(msb)] & " + " & endomorphisms[msb]
proc main_lut() =
const M = 4 # GLS-4 decomposition
const miniBitwidth = 4 # Bitwidth of the miniscalars resulting from scalar decomposition
var k: MultiScalar[M, miniBitwidth]
var kRecoded: GLV_SAC[M, miniBitwidth]
k[0].fromUint(11)
k[1].fromUint(6)
k[2].fromuint(14)
k[3].fromUint(3)
kRecoded.nDimMultiScalarRecoding(k)
echo "Recoded bytesize: ", sizeof(kRecoded)
echo kRecoded.toString()
var lut: array[1 shl (M-1), string]
let
P = "P0"
endomorphisms = ["P1", "P2", "P3"]
buildLookupTable_naive(P, endomorphisms, lut)
echo lut
doAssert lut[0] == "P0"
doAssert lut[1] == "P0 + P1"
doAssert lut[2] == "P0 + P2"
doAssert lut[3] == "P0 + P1 + P2"
doAssert lut[4] == "P0 + P3"
doAssert lut[5] == "P0 + P1 + P3"
doAssert lut[6] == "P0 + P2 + P3"
doAssert lut[7] == "P0 + P1 + P2 + P3"
var lut_reuse: array[1 shl (M-1), string]
buildLookupTable_reuse(P, endomorphisms, lut_reuse)
echo lut_reuse
doAssert lut == lut_reuse
main_lut()
echo "---------------------------------------------"
# This tests the multiplication against the Table 1
# of the paper
# Coef Decimal Binary GLV-SAC recoded
# | k0 | | 11 | | 0 1 0 1 1 | | 1 -1 1 -1 1 |
# | k1 | = | 6 | = | 0 0 1 1 0 | = | 1 -1 0 -1 0 |
# | k2 | | 14 | | 0 1 1 1 0 | | 1 0 0 -1 0 |
# | k3 | | 3 | | 0 0 0 1 1 | | 0 0 1 -1 1 |
# i | 3 2 1 0
# -------------------+----------------------------------------------------------------------
# 2Q | 2P0+2P1+2P2 2P0+2P1+4P2 6P0+4P1+8P2+2P3 10P0+6P1+14P2+2P3
# Q + sign_i T[ki] | P0+P1+2P2 3P0+2P1+4P2+P3 5P0+3P1+7P2+P3 11P0+6P1+14P2+3P3
type Endo = enum
P0
P1
P2
P3
func buildLookupTable_reuse[M: static int](
P: Endo,
endomorphisms: array[M-1, Endo],
lut: var array[1 shl (M-1), set[Endo]],
) =
## Checking the LUT by building strings of endomorphisms additions
## This reuses previous table entries so that only one addition is done
## per new entries
lut[0].incl P
for u in 1'u32 ..< 1 shl (M-1):
let msb = u.log2_vartime() # No undefined, u != 0
lut[u] = lut[u.clearBit(msb)] + {endomorphisms[msb]}
proc mainFullMul() =
const M = 4 # GLS-4 decomposition
const miniBitwidth = 4 # Bitwidth of the miniscalars resulting from scalar decomposition
const L = miniBitwidth + 1 # Bitwidth of the recoded scalars
var k: MultiScalar[M, L]
var kRecoded: GLV_SAC[M, L]
k[0].fromUint(11)
k[1].fromUint(6)
k[2].fromuint(14)
k[3].fromUint(3)
kRecoded.nDimMultiScalarRecoding(k)
echo kRecoded.toString()
var lut: array[1 shl (M-1), set[Endo]]
let
P = P0
endomorphisms = [P1, P2, P3]
buildLookupTable_reuse(P, endomorphisms, lut)
echo lut
var Q: array[Endo, int]
# Multiplication
assert bool k[0].isOdd()
# Q = sign_l-1 P[K_l-1]
let idx = kRecoded.tableIndex(L-1)
for p in lut[int(idx)]:
Q[p] = if kRecoded[0][L-1] == 0: 1 else: -1
# Loop
for i in countdown(L-2, 0):
# Q = 2Q
for val in Q.mitems: val *= 2
echo "2Q: ", Q
# Q = Q + sign_l-1 P[K_l-1]
let idx = kRecoded.tableIndex(i)
for p in lut[int(idx)]:
Q[p] += (if kRecoded[0][i] == 0: 1 else: -1)
echo "Q + sign_l-1 P[K_l-1]: ", Q
echo Q
mainFullMul()
echo "---------------------------------------------"
func buildLookupTable_m2w2(
lut: var array[8, array[2, int]],
) =
## Build a lookup table for GLV with 2-dimensional decomposition
## and window of size 2
# with [k0, k1] the mini-scalars with digits of size 2-bit
#
# 0 = 0b000 - encodes [0b01, 0b00] ≡ P0
lut[0] = [1, 0]
# 1 = 0b001 - encodes [0b01, 0b01] ≡ P0 - P1
lut[1] = [1, -1]
# 3 = 0b011 - encodes [0b01, 0b11] ≡ P0 + P1
lut[3] = [1, 1]
# 2 = 0b010 - encodes [0b01, 0b10] ≡ P0 + 2P1
lut[2] = [1, 2]
# 4 = 0b100 - encodes [0b00, 0b00] ≡ 3P0
lut[4] = [3, 0]
# 5 = 0b101 - encodes [0b00, 0b01] ≡ 3P0 + P1
lut[5] = [3, 1]
# 6 = 0b110 - encodes [0b00, 0b10] ≡ 3P0 + 2P1
lut[6] = [3, 2]
# 7 = 0b111 - encodes [0b00, 0b11] ≡ 3P0 + 3P1
lut[7] = [3, 3]
proc mainFullMulWindowed() =
const M = 2 # GLS-2 decomposition
const miniBitwidth = 8 # Bitwidth of the miniscalars resulting from scalar decomposition
const W = 2 # Window
const L = computeRecodedLength(miniBitwidth, W)
var k: MultiScalar[M, L]
var kRecoded: GLV_SAC[M, L]
k[0].fromUint(11)
k[1].fromUint(14)
kRecoded.nDimMultiScalarRecoding(k)
echo "Recoded bytesize: ", sizeof(kRecoded)
echo kRecoded.toString()
var lut: array[8, array[range[P0..P1], int]]
buildLookupTable_m2w2(lut)
echo lut
# Assumes k[0] is odd to simplify test
# and having to conditional substract at the end
assert bool k[0].isOdd()
var Q: array[Endo, int]
var isNeg: SecretBool
let idx = kRecoded.w2TableIndex((L div 2)-1, isNeg)
for p, coef in lut[int(idx)]:
# Unneeeded by construction
# let sign = if isNeg: -1 else: 1
Q[p] = coef
# Loop
for i in countdown((L div 2)-2, 0):
# Q = 4Q
for val in Q.mitems: val *= 4
echo "4Q: ", Q
# Q = Q + sign_l-1 P[K_l-1]
let idx = kRecoded.w2TableIndex(i, isNeg)
for p, coef in lut[int(idx)]:
let sign = (if bool isNeg: -1 else: 1)
Q[p] += sign * coef
echo "Q + sign_l-1 P[K_l-1]: ", Q
echo Q
mainFullMulWindowed()