ceremonyclient/nekryptology/pkg/core/iqc/classgroup.go
2023-07-05 00:32:28 -05:00

569 lines
13 KiB
Go
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.

//
// Copyright (c) 2019 harmony-one
//
// SPDX-License-Identifier: MIT
//
package iqc
import (
"math/big"
)
type ClassGroup struct {
a *big.Int
b *big.Int
c *big.Int
d *big.Int
}
func NewClassGroup(a, b, c *big.Int) *ClassGroup {
return &ClassGroup{a: a, b: b, c: c}
}
func (cg *ClassGroup) Clone() *ClassGroup {
return &ClassGroup{a: cg.a, b: cg.b, c: cg.c}
}
func NewClassGroupFromAbDiscriminant(a, b, discriminant *big.Int) *ClassGroup {
//z = b*b-discriminant
z := new(big.Int).Sub(new(big.Int).Mul(b, b), discriminant)
//z = z // 4a
c := FloorDivision(z, new(big.Int).Mul(a, big.NewInt(4)))
return NewClassGroup(a, b, c)
}
func NewClassGroupFromBytesDiscriminant(buf []byte, discriminant *big.Int) (*ClassGroup, bool) {
int_size_bits := discriminant.BitLen()
//add additional one byte for sign
int_size := (int_size_bits + 16) >> 4
//make sure the input byte buffer size matches with discriminant's
if len(buf) != int_size*2 {
return nil, false
}
a := decodeTwosComplement(buf[:int_size])
b := decodeTwosComplement(buf[int_size:])
return NewClassGroupFromAbDiscriminant(a, b, discriminant), true
}
func IdentityForDiscriminant(d *big.Int) *ClassGroup {
return NewClassGroupFromAbDiscriminant(big.NewInt(1), big.NewInt(1), d)
}
func (group *ClassGroup) Normalized() *ClassGroup {
a := new(big.Int).Set(group.a)
b := new(big.Int).Set(group.b)
c := new(big.Int).Set(group.c)
//if b > -a && b <= a:
if (b.Cmp(new(big.Int).Neg(a)) == 1) && (b.Cmp(a) < 1) {
return group
}
//r = (a - b) // (2 * a)
r := new(big.Int).Sub(a, b)
r = FloorDivision(r, new(big.Int).Mul(a, big.NewInt(2)))
//b, c = b + 2 * r * a, a * r * r + b * r + c
t := new(big.Int).Mul(big.NewInt(2), r)
t.Mul(t, a)
oldB := new(big.Int).Set(b)
b.Add(b, t)
x := new(big.Int).Mul(a, r)
x.Mul(x, r)
y := new(big.Int).Mul(oldB, r)
c.Add(c, x)
c.Add(c, y)
return NewClassGroup(a, b, c)
}
func (group *ClassGroup) Reduced() *ClassGroup {
g := group.Normalized()
a := new(big.Int).Set(g.a)
b := new(big.Int).Set(g.b)
c := new(big.Int).Set(g.c)
//while a > c or (a == c and b < 0):
for (a.Cmp(c) == 1) || ((a.Cmp(c) == 0) && (b.Sign() == -1)) {
//s = (c + b) // (c + c)
s := new(big.Int).Add(c, b)
s = FloorDivision(s, new(big.Int).Add(c, c))
//a, b, c = c, -b + 2 * s * c, c * s * s - b * s + a
oldA := new(big.Int).Set(a)
oldB := new(big.Int).Set(b)
a = new(big.Int).Set(c)
b.Neg(b)
x := new(big.Int).Mul(big.NewInt(2), s)
x.Mul(x, c)
b.Add(b, x)
c.Mul(c, s)
c.Mul(c, s)
oldB.Mul(oldB, s)
c.Sub(c, oldB)
c.Add(c, oldA)
}
return NewClassGroup(a, b, c).Normalized()
}
func (group *ClassGroup) identity() *ClassGroup {
return NewClassGroupFromAbDiscriminant(big.NewInt(1), big.NewInt(1), group.Discriminant())
}
func (group *ClassGroup) Discriminant() *big.Int {
if group.d == nil {
d := new(big.Int).Set(group.b)
d.Mul(d, d)
a := new(big.Int).Set(group.a)
a.Mul(a, group.c)
a.Mul(a, big.NewInt(4))
d.Sub(d, a)
group.d = d
}
return group.d
}
func (group *ClassGroup) Multiply(other *ClassGroup) *ClassGroup {
//a1, b1, c1 = self.reduced()
x := group.Reduced()
//a2, b2, c2 = other.reduced()
y := other.Reduced()
//g = (b2 + b1) // 2
g := new(big.Int).Add(x.b, y.b)
g = FloorDivision(g, big.NewInt(2))
//h = (b2 - b1) // 2
h := new(big.Int).Sub(y.b, x.b)
h = FloorDivision(h, big.NewInt(2))
//w = mod.gcd(a1, a2, g)
w1 := allInputValueGCD(y.a, g)
w := allInputValueGCD(x.a, w1)
//j = w
j := new(big.Int).Set(w)
//r = 0
r := big.NewInt(0)
//s = a1 // w
s := FloorDivision(x.a, w)
//t = a2 // w
t := FloorDivision(y.a, w)
//u = g // w
u := FloorDivision(g, w)
//k_temp, constant_factor = mod.solve_mod(t * u, h * u + s * c1, s * t)
b := new(big.Int).Mul(h, u)
sc := new(big.Int).Mul(s, x.c)
b.Add(b, sc)
k_temp, constant_factor, solvable := SolveMod(new(big.Int).Mul(t, u), b, new(big.Int).Mul(s, t))
if !solvable {
return nil
}
//n, constant_factor_2 = mod.solve_mod(t * constant_factor, h - t * k_temp, s)
n, _, solvable := SolveMod(new(big.Int).Mul(t, constant_factor), new(big.Int).Sub(h, new(big.Int).Mul(t, k_temp)), s)
if !solvable {
return nil
}
//k = k_temp + constant_factor * n
k := new(big.Int).Add(k_temp, new(big.Int).Mul(constant_factor, n))
//l = (t * k - h) // s
l := FloorDivision(new(big.Int).Sub(new(big.Int).Mul(t, k), h), s)
//m = (t * u * k - h * u - s * c1) // (s * t)
tuk := new(big.Int).Mul(t, u)
tuk.Mul(tuk, k)
hu := new(big.Int).Mul(h, u)
tuk.Sub(tuk, hu)
tuk.Sub(tuk, sc)
st := new(big.Int).Mul(s, t)
m := FloorDivision(tuk, st)
//a3 = s * t - r * u
ru := new(big.Int).Mul(r, u)
a3 := st.Sub(st, ru)
//b3 = (j * u + m * r) - (k * t + l * s)
ju := new(big.Int).Mul(j, u)
mr := new(big.Int).Mul(m, r)
ju = ju.Add(ju, mr)
kt := new(big.Int).Mul(k, t)
ls := new(big.Int).Mul(l, s)
kt = kt.Add(kt, ls)
b3 := ju.Sub(ju, kt)
//c3 = k * l - j * m
kl := new(big.Int).Mul(k, l)
jm := new(big.Int).Mul(j, m)
c3 := kl.Sub(kl, jm)
return NewClassGroup(a3, b3, c3).Reduced()
}
func (group *ClassGroup) Pow(n int64) *ClassGroup {
x := group.Clone()
items_prod := group.identity()
for n > 0 {
if n&1 == 1 {
items_prod = items_prod.Multiply(x)
if items_prod == nil {
return nil
}
}
x = x.Square()
if x == nil {
return nil
}
n >>= 1
}
return items_prod
}
func (group *ClassGroup) BigPow(n *big.Int) *ClassGroup {
x := group.Clone()
items_prod := group.identity()
p := new(big.Int).Set(n)
for p.Sign() > 0 {
if p.Bit(0) == 1 {
items_prod = items_prod.Multiply(x)
if items_prod == nil {
return nil
}
}
x = x.Square()
if x == nil {
return nil
}
p.Rsh(p, 1)
}
return items_prod
}
func (group *ClassGroup) Square() *ClassGroup {
u, _, solvable := SolveMod(group.b, group.c, group.a)
if !solvable {
return nil
}
//A = a
A := new(big.Int).Mul(group.a, group.a)
//B = b 2aµ,
au := new(big.Int).Mul(group.a, u)
B := new(big.Int).Sub(group.b, new(big.Int).Mul(au, big.NewInt(2)))
//C = µ ^ 2 - (bµc)//a
C := new(big.Int).Mul(u, u)
m := new(big.Int).Mul(group.b, u)
m = new(big.Int).Sub(m, group.c)
m = FloorDivision(m, group.a)
C = new(big.Int).Sub(C, m)
return NewClassGroup(A, B, C).Reduced()
}
func (group *ClassGroup) SquareUsingMultiply() *ClassGroup {
//a1, b1, c1 = self.reduced()
x := group.Reduced()
//g = b1
g := x.b
//h = 0
h := big.NewInt(0)
//w = mod.gcd(a1, g)
w := allInputValueGCD(x.a, g)
//j = w
j := new(big.Int).Set(w)
//r = 0
r := big.NewInt(0)
//s = a1 // w
s := FloorDivision(x.a, w)
//t = s
t := s
//u = g // w
u := FloorDivision(g, w)
//k_temp, constant_factor = mod.solve_mod(t * u, h * u + s * c1, s * t)
b := new(big.Int).Mul(h, u)
sc := new(big.Int).Mul(s, x.c)
b.Add(b, sc)
k_temp, constant_factor, solvable := SolveMod(new(big.Int).Mul(t, u), b, new(big.Int).Mul(s, t))
if !solvable {
return nil
}
//n, constant_factor_2 = mod.solve_mod(t * constant_factor, h - t * k_temp, s)
n, _, solvable := SolveMod(new(big.Int).Mul(t, constant_factor), new(big.Int).Sub(h, new(big.Int).Mul(t, k_temp)), s)
if !solvable {
return nil
}
//k = k_temp + constant_factor * n
k := new(big.Int).Add(k_temp, new(big.Int).Mul(constant_factor, n))
//l = (t * k - h) // s
l := FloorDivision(new(big.Int).Sub(new(big.Int).Mul(t, k), h), s)
//m = (t * u * k - h * u - s * c1) // (s * t)
tuk := new(big.Int).Mul(t, u)
tuk.Mul(tuk, k)
hu := new(big.Int).Mul(h, u)
tuk.Sub(tuk, hu)
tuk.Sub(tuk, sc)
st := new(big.Int).Mul(s, t)
m := FloorDivision(tuk, st)
//a3 = s * t - r * u
ru := new(big.Int).Mul(r, u)
a3 := st.Sub(st, ru)
//b3 = (j * u + m * r) - (k * t + l * s)
ju := new(big.Int).Mul(j, u)
mr := new(big.Int).Mul(m, r)
ju = ju.Add(ju, mr)
kt := new(big.Int).Mul(k, t)
ls := new(big.Int).Mul(l, s)
kt = kt.Add(kt, ls)
b3 := ju.Sub(ju, kt)
//c3 = k * l - j * m
kl := new(big.Int).Mul(k, l)
jm := new(big.Int).Mul(j, m)
c3 := kl.Sub(kl, jm)
return NewClassGroup(a3, b3, c3).Reduced()
}
// Serialize encodes a, b based on discriminant's size
// using one more byte for sign if nessesary
func (group *ClassGroup) Serialize() []byte {
r := group.Reduced()
int_size_bits := group.Discriminant().BitLen()
int_size := (int_size_bits + 16) >> 4
buf := make([]byte, int_size*2)
copy(buf[:int_size], signBitFill(encodeTwosComplement(r.a), int_size))
copy(buf[int_size:], signBitFill(encodeTwosComplement(r.b), int_size))
return buf
}
func (group *ClassGroup) Equal(other *ClassGroup) bool {
g := group.Reduced()
o := other.Reduced()
return (g.a.Cmp(o.a) == 0 && g.b.Cmp(o.b) == 0 && g.c.Cmp(o.c) == 0)
}
func FloorDivision(x, y *big.Int) *big.Int {
var r big.Int
q, _ := new(big.Int).QuoRem(x, y, &r)
if (r.Sign() == 1 && y.Sign() == -1) || (r.Sign() == -1 && y.Sign() == 1) {
q.Sub(q, big.NewInt(1))
}
return q
}
var bigOne = big.NewInt(1)
func decodeTwosComplement(bytes []byte) *big.Int {
if bytes[0]&0x80 == 0 {
// non-negative
return new(big.Int).SetBytes(bytes)
}
setyb := make([]byte, len(bytes))
for i := range bytes {
setyb[i] = bytes[i] ^ 0xff
}
n := new(big.Int).SetBytes(setyb)
return n.Sub(n.Neg(n), bigOne)
}
func encodeTwosComplement(n *big.Int) []byte {
if n.Sign() > 0 {
bytes := n.Bytes()
if bytes[0]&0x80 == 0 {
return bytes
}
// add one more byte for positive sign
buf := make([]byte, len(bytes)+1)
copy(buf[1:], bytes)
return buf
}
if n.Sign() < 0 {
// A negative number has to be converted to two's-complement form. So we
// invert and subtract 1. If the most-significant-bit isn't set then
// we'll need to pad the beginning with 0xff in order to keep the number
// negative.
nMinus1 := new(big.Int).Neg(n)
nMinus1.Sub(nMinus1, bigOne)
bytes := nMinus1.Bytes()
if len(bytes) == 0 {
// sneaky -1 value
return []byte{0xff}
}
for i := range bytes {
bytes[i] ^= 0xff
}
if bytes[0]&0x80 != 0 {
return bytes
}
// add one more byte for negative sign
buf := make([]byte, len(bytes)+1)
buf[0] = 0xff
copy(buf[1:], bytes)
return buf
}
return []byte{}
}
func signBitFill(bytes []byte, targetLen int) []byte {
if len(bytes) >= targetLen {
return bytes
}
buf := make([]byte, targetLen)
offset := targetLen - len(bytes)
if bytes[0]&0x80 != 0 {
for i := 0; i < offset; i++ {
buf[i] = 0xff
}
}
copy(buf[offset:], bytes)
return buf
}
func EncodeBigIntBigEndian(a *big.Int) []byte {
int_size_bits := a.BitLen()
int_size := (int_size_bits + 16) >> 3
return signBitFill(encodeTwosComplement(a), int_size)
}
//Return r, s, t such that gcd(a, b) = r = a * s + b * t
func extendedGCD(a, b *big.Int) (r, s, t *big.Int) {
//r0, r1 = a, b
r0 := new(big.Int).Set(a)
r1 := new(big.Int).Set(b)
//s0, s1, t0, t1 = 1, 0, 0, 1
s0 := big.NewInt(1)
s1 := big.NewInt(0)
t0 := big.NewInt(0)
t1 := big.NewInt(1)
//if r0 > r1:
//r0, r1, s0, s1, t0, t1 = r1, r0, t0, t1, s0, s1
if r0.Cmp(r1) == 1 {
oldR0 := new(big.Int).Set(r0)
r0 = r1
r1 = oldR0
oldS0 := new(big.Int).Set(s0)
s0 = t0
oldS1 := new(big.Int).Set(s1)
s1 = t1
t0 = oldS0
t1 = oldS1
}
//while r1 > 0:
for r1.Sign() == 1 {
//q, r = divmod(r0, r1)
r := big.NewInt(1)
bb := new(big.Int).Set(b)
q, r := bb.DivMod(r0, r1, r)
//r0, r1, s0, s1, t0, t1 = r1, r, s1, s0 - q * s1, t1, t0 - q * t1
r0 = r1
r1 = r
oldS0 := new(big.Int).Set(s0)
s0 = s1
s1 = new(big.Int).Sub(oldS0, new(big.Int).Mul(q, s1))
oldT0 := new(big.Int).Set(t0)
t0 = t1
t1 = new(big.Int).Sub(oldT0, new(big.Int).Mul(q, t1))
}
return r0, s0, t0
}
//wrapper around big.Int GCD to allow all input values for GCD
//as Golang big.Int GCD requires both a, b > 0
//If a == b == 0, GCD sets r = 0.
//If a == 0 and b != 0, GCD sets r = |b|
//If a != 0 and b == 0, GCD sets r = |a|
//Otherwise r = GCD(|a|, |b|)
func allInputValueGCD(a, b *big.Int) (r *big.Int) {
if a.Sign() == 0 {
return new(big.Int).Abs(b)
}
if b.Sign() == 0 {
return new(big.Int).Abs(a)
}
return new(big.Int).GCD(nil, nil, new(big.Int).Abs(a), new(big.Int).Abs(b))
}
//Solve ax == b mod m for x.
//Return s, t where x = s + k * t for integer k yields all solutions.
func SolveMod(a, b, m *big.Int) (s, t *big.Int, solvable bool) {
//g, d, e = extended_gcd(a, m)
//TODO: golang 1.x big.int GCD requires both a > 0 and m > 0, so we can't use it :(
//d := big.NewInt(0)
//e := big.NewInt(0)
//g := new(big.Int).GCD(d, e, a, m)
g, d, _ := extendedGCD(a, m)
//q, r = divmod(b, g)
r := big.NewInt(1)
bb := new(big.Int).Set(b)
q, r := bb.DivMod(b, g, r)
//TODO: replace with utils.GetLogInstance().Error(...)
//if r != 0:
if r.Cmp(big.NewInt(0)) != 0 {
//panic(fmt.Sprintf("no solution to %s x = %s mod %s", a.String(), b.String(), m.String()))
return nil, nil, false
}
//assert b == q * g
//return (q * d) % m, m // g
q.Mul(q, d)
s = q.Mod(q, m)
t = FloorDivision(m, g)
return s, t, true
}