mirror of
https://github.com/henrydcase/nobs.git
synced 2024-11-26 17:11:22 +00:00
Kris Kwiatkowski
7efbbf4745
Implementation of Commutative Supersingular Isogeny Diffie Hellman, based on "A faster way to CSIDH" paper (2018/782). * For fast isogeny calculation, implementation converts a curve from Montgomery to Edwards. All calculations are done on Edwards curve and then converted back to Montgomery. * As multiplication in a field Fp511 is most expensive operation the implementation contains multiple multiplications. It has most performant, assembly implementation which uses BMI2 and ADOX/ADCX instructions for modern CPUs. It also contains slower implementation which will run on older CPUs * Benchmarks (Intel SkyLake): BenchmarkGeneratePrivate 6459 172213 ns/op 0 B/op 0 allocs/op BenchmarkGenerateKeyPair 25 45800356 ns/op 0 B/op 0 allocs/op BenchmarkValidate 297 3915983 ns/op 0 B/op 0 allocs/op BenchmarkValidateRandom 184683 6231 ns/op 0 B/op 0 allocs/op BenchmarkValidateGenerated 25 48481306 ns/op 0 B/op 0 allocs/op BenchmarkDerive 19 60928763 ns/op 0 B/op 0 allocs/op BenchmarkDeriveGenerated 8 137342421 ns/op 0 B/op 0 allocs/op BenchmarkXMul 2311 494267 ns/op 1 B/op 0 allocs/op BenchmarkXAdd 2396754 501 ns/op 0 B/op 0 allocs/op BenchmarkXDbl 2072690 571 ns/op 0 B/op 0 allocs/op BenchmarkIsom 78004 15171 ns/op 0 B/op 0 allocs/op BenchmarkFp512Sub 224635152 5.33 ns/op 0 B/op 0 allocs/op BenchmarkFp512Mul 246633255 4.90 ns/op 0 B/op 0 allocs/op BenchmarkCSwap 233228547 5.10 ns/op 0 B/op 0 allocs/op BenchmarkAddRdc 87348240 12.6 ns/op 0 B/op 0 allocs/op BenchmarkSubRdc 95112787 11.7 ns/op 0 B/op 0 allocs/op BenchmarkModExpRdc 25436 46878 ns/op 0 B/op 0 allocs/op BenchmarkMulBmiAsm 19527573 60.1 ns/op 0 B/op 0 allocs/op BenchmarkMulGeneric 7117650 164 ns/op 0 B/op 0 allocs/op * Go code has very similar performance when compared to C implementation. Results from sidh_torturer (4e2996e12d68364761064341cbe1d1b47efafe23) github.com:henrydcase/sidh-torture/csidh | TestName |Go | C | |------------------|----------|----------| |TestSharedSecret | 57.95774 | 57.91092 | |TestKeyGeneration | 62.23614 | 58.12980 | |TestSharedSecret | 55.28988 | 57.23132 | |TestKeyGeneration | 61.68745 | 58.66396 | |TestSharedSecret | 63.19408 | 58.64774 | |TestKeyGeneration | 62.34022 | 61.62539 | |TestSharedSecret | 62.85453 | 68.74503 | |TestKeyGeneration | 52.58518 | 58.40115 | |TestSharedSecret | 50.77081 | 61.91699 | |TestKeyGeneration | 59.91843 | 61.09266 | |TestSharedSecret | 59.97962 | 62.98151 | |TestKeyGeneration | 64.57525 | 56.22863 | |TestSharedSecret | 56.40521 | 55.77447 | |TestKeyGeneration | 67.85850 | 58.52604 | |TestSharedSecret | 60.54290 | 65.14052 | |TestKeyGeneration | 65.45766 | 58.42823 | On average Go implementation is 2% faster.
157 lines
2.8 KiB
Go
157 lines
2.8 KiB
Go
package csidh
|
|
|
|
import (
|
|
"fmt"
|
|
"math/big"
|
|
mrand "math/rand"
|
|
)
|
|
|
|
// Commonly used variables
|
|
var (
|
|
// Number of interations
|
|
numIter = 10
|
|
// Modulus
|
|
modulus, _ = new(big.Int).SetString(fp2S(p), 16)
|
|
// Zero in fp
|
|
zeroFp512 = fp{}
|
|
// One in fp
|
|
oneFp512 = fp{1, 0, 0, 0, 0, 0, 0, 0}
|
|
)
|
|
|
|
// Converts dst to Montgomery if "toMont==true" or from Montgomery domain otherwise.
|
|
func toMont(dst *big.Int, toMont bool) {
|
|
var bigP, bigR big.Int
|
|
|
|
intSetU64(&bigP, p[:])
|
|
bigR.SetUint64(1)
|
|
bigR.Lsh(&bigR, 512)
|
|
|
|
if !toMont {
|
|
bigR.ModInverse(&bigR, &bigP)
|
|
}
|
|
dst.Mul(dst, &bigR)
|
|
dst.Mod(dst, &bigP)
|
|
}
|
|
|
|
func fp2S(v fp) string {
|
|
var str string
|
|
for i := 0; i < 8; i++ {
|
|
str = fmt.Sprintf("%016x", v[i]) + str
|
|
}
|
|
return str
|
|
}
|
|
|
|
// zeroize fp
|
|
func zero(v *fp) {
|
|
for i := range *v {
|
|
v[i] = 0
|
|
}
|
|
}
|
|
|
|
// returns random value in a range (0,p)
|
|
func randomFp() fp {
|
|
var u fp
|
|
for i := 0; i < 8; i++ {
|
|
u[i] = mrand.Uint64()
|
|
}
|
|
return u
|
|
}
|
|
|
|
// x<y: <0
|
|
// x>y: >0
|
|
// x==y: 0
|
|
func cmp512(x, y *fp) int {
|
|
if len(*x) == len(*y) {
|
|
for i := len(*x) - 1; i >= 0; i-- {
|
|
if x[i] < y[i] {
|
|
return -1
|
|
} else if x[i] > y[i] {
|
|
return 1
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
return len(*x) - len(*y)
|
|
}
|
|
|
|
// return x==y for fp
|
|
func ceqFp(l, r *fp) bool {
|
|
for idx := range l {
|
|
if l[idx] != r[idx] {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// return x==y for point
|
|
func ceqpoint(l, r *point) bool {
|
|
return ceqFp(&l.x, &r.x) && ceqFp(&l.z, &r.z)
|
|
}
|
|
|
|
// return x==y
|
|
func ceq512(x, y *fp) bool {
|
|
return cmp512(x, y) == 0
|
|
}
|
|
|
|
// Converts src to big.Int. Function assumes that src is a slice of uint64
|
|
// values encoded in little-endian byte order.
|
|
func intSetU64(dst *big.Int, src []uint64) *big.Int {
|
|
var tmp big.Int
|
|
|
|
dst.SetUint64(0)
|
|
for i := range src {
|
|
tmp.SetUint64(src[i])
|
|
tmp.Lsh(&tmp, uint(i*64))
|
|
dst.Add(dst, &tmp)
|
|
}
|
|
return dst
|
|
}
|
|
|
|
// Converts src to an array of uint64 values encoded in little-endian
|
|
// byte order.
|
|
func intGetU64(src *big.Int) []uint64 {
|
|
var tmp, mod big.Int
|
|
dst := make([]uint64, (src.BitLen()/64)+1)
|
|
|
|
u64 := uint64(0)
|
|
u64--
|
|
mod.SetUint64(u64)
|
|
for i := 0; i < (src.BitLen()/64)+1; i++ {
|
|
tmp.Set(src)
|
|
tmp.Rsh(&tmp, uint(i)*64)
|
|
tmp.And(&tmp, &mod)
|
|
dst[i] = tmp.Uint64()
|
|
}
|
|
return dst
|
|
}
|
|
|
|
// Returns projective coordinate X of normalized EC 'point' (point.x / point.z).
|
|
func toNormX(point *point) big.Int {
|
|
var bigP, bigDnt, bigDor big.Int
|
|
|
|
intSetU64(&bigP, p[:])
|
|
intSetU64(&bigDnt, point.x[:])
|
|
intSetU64(&bigDor, point.z[:])
|
|
|
|
bigDor.ModInverse(&bigDor, &bigP)
|
|
bigDnt.Mul(&bigDnt, &bigDor)
|
|
bigDnt.Mod(&bigDnt, &bigP)
|
|
return bigDnt
|
|
}
|
|
|
|
// Converts string to fp element in Montgomery domain of cSIDH-512
|
|
func toFp(num string) fp {
|
|
var tmp big.Int
|
|
var ok bool
|
|
var ret fp
|
|
|
|
_, ok = tmp.SetString(num, 0)
|
|
if !ok {
|
|
panic("Can't parse a number")
|
|
}
|
|
toMont(&tmp, true)
|
|
copy(ret[:], intGetU64(&tmp))
|
|
return ret
|
|
}
|