|
@@ -0,0 +1,472 @@
|
|
|
+package curve
|
|
|
+
|
|
|
+import (
|
|
|
+ "errors"
|
|
|
+ "fmt"
|
|
|
+ "log"
|
|
|
+ "math/big"
|
|
|
+)
|
|
|
+
|
|
|
+var (
|
|
|
+ MinP = big.NewInt(11)
|
|
|
+)
|
|
|
+
|
|
|
+// 4a³ + 27b² = 0 - проверка кривой на сингулярность
|
|
|
+
|
|
|
+func New(a, b *big.Int) (*Curve, error) {
|
|
|
+ c := &Curve{
|
|
|
+ a: new(big.Int).Set(a),
|
|
|
+ b: new(big.Int).Set(b),
|
|
|
+ }
|
|
|
+ return c, c.IsSingular()
|
|
|
+}
|
|
|
+
|
|
|
+type Curve struct {
|
|
|
+ a *big.Int // константа a
|
|
|
+ b *big.Int // константа b
|
|
|
+ p *big.Int // размер конечного поля
|
|
|
+ g *Point // базовая точка подгруппы
|
|
|
+ n *big.Int // порядок подгруппы
|
|
|
+ h *big.Int // кофактор подгруппы
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Curve) A() (res *big.Int) {
|
|
|
+ return intCopy(s.a)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Curve) B() (res *big.Int) {
|
|
|
+ return intCopy(s.b)
|
|
|
+}
|
|
|
+
|
|
|
+// Проверка, установлен ли размер конечного поля
|
|
|
+func (s *Curve) IsValidP() (err error) {
|
|
|
+ if err = s.IsValidConstants(); err == nil {
|
|
|
+ if s.p == nil {
|
|
|
+ err = errors.New("размер конечного поля не установлен")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Curve) IsValidG() (err error) {
|
|
|
+ if err = s.IsValidP(); err == nil {
|
|
|
+ if s.g == nil {
|
|
|
+ err = errors.New("базовая точка подгруппы g не определена")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Curve) IsValidN() (err error) {
|
|
|
+ if err = s.IsValidG(); err == nil {
|
|
|
+ if s.n == nil {
|
|
|
+ err = errors.New("порядок подгруппы не определен")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// Возвращает строку уравнения кривой
|
|
|
+func (s *Curve) FormulaString() string {
|
|
|
+ return fmt.Sprintf(
|
|
|
+ "y² = x³ + %sx + %s",
|
|
|
+ mustBigInt(s.a),
|
|
|
+ mustBigInt(s.b),
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+// Проверка кривой на сингулярность (4a³ + 27b² = 0)
|
|
|
+func (s *Curve) IsSingular() (err error) {
|
|
|
+ if err = s.IsValidConstants(); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ l := new(big.Int).Mul(new(big.Int).Exp(s.a, big.NewInt(3), nil), big.NewInt(4))
|
|
|
+ r := new(big.Int).Mul(new(big.Int).Exp(s.b, big.NewInt(2), nil), big.NewInt(27))
|
|
|
+ if l.Add(l, r).Cmp(big.NewInt(0)) == 0 {
|
|
|
+ err = fmt.Errorf("кривая [ %s ] сингулярна и не рекомендуется к использованию", s.FormulaString())
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// Проверка констант уравнения на валидность
|
|
|
+func (s *Curve) IsValidConstants() error {
|
|
|
+ if s.a == nil {
|
|
|
+ return errors.New("константа [ a ] не определена")
|
|
|
+ }
|
|
|
+ if s.b == nil {
|
|
|
+ return errors.New("константа [ b ] не определена")
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+// Установка размера конечного поля
|
|
|
+func (s *Curve) SetP(p *big.Int) (err error) {
|
|
|
+ if s.p != nil {
|
|
|
+ return errors.New("резмер конечного поля уже определен")
|
|
|
+ }
|
|
|
+ if err = s.IsValidConstants(); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if !IsPrime(p) {
|
|
|
+ err = fmt.Errorf("число [ %s ] не является простым. Размер конечного поля должен быть простым числом", p)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ s.p = p
|
|
|
+ /*if !IsPrime(Div(Sub(p, 1), 2)) {
|
|
|
+
|
|
|
+ }*/
|
|
|
+ /*
|
|
|
+ if p.Cmp(MinP) < 0 {
|
|
|
+ err = fmt.Errorf("слишком маленький размер конечного поля [ %s ]", p)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ */
|
|
|
+ //s.p = new(big.Int).Set(p)
|
|
|
+ // сброс подгруппы
|
|
|
+ s.n = nil
|
|
|
+ s.h = nil
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Curve) Point(x, y *big.Int) *Point {
|
|
|
+ return NewPoint(x, y, s)
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Curve) InverseMod(k, p *big.Int) (res *big.Int, err error) {
|
|
|
+ if err = s.IsValidP(); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if k == nil {
|
|
|
+ err = errors.New("[ k ] не определено")
|
|
|
+ return
|
|
|
+ } else if rk := k.Cmp(intZero); rk == 0 {
|
|
|
+ err = errors.New("[ k ] == 0. Деление на 0 невозможно")
|
|
|
+ return
|
|
|
+ } else if rk < 0 {
|
|
|
+ // k ** -1 = p - (-k) ** -1 (mod p)
|
|
|
+ // point - self.inverseMod(-k, point)
|
|
|
+ var iMod *big.Int
|
|
|
+ if iMod, err = s.InverseMod(Neg(k), p); err == nil {
|
|
|
+ res = Sub(p, iMod)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ s, olds := big.NewInt(0), big.NewInt(1)
|
|
|
+ t, oldt := big.NewInt(1), big.NewInt(0)
|
|
|
+ r, oldr := intCopy(p), intCopy(k)
|
|
|
+
|
|
|
+ for r.Cmp(intZero) != 0 {
|
|
|
+ quot := Div(oldr, r)
|
|
|
+ oldr, r = r, Sub(oldr, Mul(quot, r))
|
|
|
+ olds, s = s, Sub(olds, Mul(quot, s))
|
|
|
+ oldt, t = t, Sub(oldt, Mul(quot, t))
|
|
|
+ }
|
|
|
+ // gcd, x, y = old_r, old_s, old_t
|
|
|
+ res = Mod(olds, p)
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// Определение координат пары зеркальных точек по x
|
|
|
+func (s *Curve) points(dx *big.Int) (p1, p2 *Point, err error) {
|
|
|
+ dxx := intCopy(dx)
|
|
|
+ // Инициализируем результирующие точки пустыми
|
|
|
+ p1, p2 = s.Point(nil, nil), s.Point(nil, nil)
|
|
|
+ var dy *big.Int
|
|
|
+ // Операции, обратной делению по модулю, не существует, поэтому
|
|
|
+ // для определения координаты по y, необходимо, путем перебора с подстановкой y,
|
|
|
+ // проверить равенство левой части уравнения, разделенного по модулю на p с
|
|
|
+ // правой частью, так же разделенной по модулю на p
|
|
|
+ for i := big.NewInt(0); i.Cmp(s.p) < 0; i.Add(i, big.NewInt(1)) {
|
|
|
+ // i ** 2 % s.p == (dx ** 3 + s.a * dx + self.b) % self.p > 0
|
|
|
+ l := Mod(Exp64(i, 2), s.p)
|
|
|
+ r := Mod(
|
|
|
+ Add(
|
|
|
+ Exp64(dx, 3),
|
|
|
+ Add(
|
|
|
+ Mul(s.a, dx),
|
|
|
+ s.b,
|
|
|
+ ),
|
|
|
+ ),
|
|
|
+ s.p,
|
|
|
+ )
|
|
|
+ if l.Cmp(r) == 0 {
|
|
|
+ dy = i
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if dy == nil {
|
|
|
+ err = fmt.Errorf("не удалось найти пару зеркальных точек на кривой после x [ %s ]", dxx)
|
|
|
+ } else {
|
|
|
+ p1.x, p1.y = dx, dy
|
|
|
+ p2.x = intCopy(p1.x)
|
|
|
+ p2.y = Mod(Neg(p1.y), s.p)
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Curve) searhClosePoints(x *big.Int) (p1, p2 *Point, err error) {
|
|
|
+ cx := intCopy(x)
|
|
|
+ if x.Cmp(s.p) >= 0 {
|
|
|
+ x = Sub(s.p, big.NewInt(1))
|
|
|
+ } else if x.Cmp(intZero) < 0 {
|
|
|
+ x = big.NewInt(0)
|
|
|
+ }
|
|
|
+ //kx := Sub64(x, 1)
|
|
|
+ kx := intCopy(x)
|
|
|
+ for {
|
|
|
+ //log.Println("xkx", x, kx)
|
|
|
+ if x.Cmp(s.p) >= 0 && kx.Cmp(intZero) <= 0 {
|
|
|
+ err = fmt.Errorf("не удалось найти точки, близкие к [ %s ]", cx)
|
|
|
+ break
|
|
|
+ } else {
|
|
|
+ // Поиск вправо
|
|
|
+ if x.Cmp(s.p) <= 0 {
|
|
|
+ if p1, p2, err = s.points(x); err == nil {
|
|
|
+ if p1.IsInCurve() == nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ x = Add64(x, 1)
|
|
|
+ }
|
|
|
+ // Поиск влево
|
|
|
+ if kx.Cmp(intZero) < 0 {
|
|
|
+ if p1, p2, err = s.points(kx); err == nil {
|
|
|
+ if p1.IsInCurve() == nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ kx = Sub64(kx, 1)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Curve) SetG(g *Point) (err error) {
|
|
|
+ if s.g != nil {
|
|
|
+ err = errors.New("базовая точка подгруппы g уже определена")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if err = s.IsValidP(); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if err = g.IsInCurve(); err == nil && g.curve == s {
|
|
|
+ s.g = g
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Curve) SetGRandom() (err error) {
|
|
|
+ if s.g != nil {
|
|
|
+ err = errors.New("базовая точка подгруппы g уже определена")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if err = s.IsValidP(); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ x := Random(
|
|
|
+ Div(s.p, big.NewInt(2)),
|
|
|
+ Sub(s.p, big.NewInt(10)),
|
|
|
+ )
|
|
|
+ //log.Println(x)
|
|
|
+ p1, p2, err := s.searhClosePoints(x)
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if err = p1.IsInCurve(); err == nil {
|
|
|
+ s.g = p1
|
|
|
+ } else if err = p2.IsInCurve(); err == nil {
|
|
|
+ s.g = p2
|
|
|
+ } else {
|
|
|
+ err = errors.New("не удалось найти точку g, пренадлежащую кривой")
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// Поиск порядка подгруппы
|
|
|
+func (s *Curve) SetN() (err error) {
|
|
|
+ if s.n != nil {
|
|
|
+ return errors.New("порядок подгруппы уже определен")
|
|
|
+ }
|
|
|
+ if err = s.IsValidP(); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ dx, dy := intCopy(s.g.x), Mod(Neg(s.g.y), s.p)
|
|
|
+ tmpG := s.g.Copy()
|
|
|
+ s.n = big.NewInt(1)
|
|
|
+ for {
|
|
|
+ s.n.Add(s.n, big.NewInt(1))
|
|
|
+ if tmpG, err = tmpG.Add(s.g); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if tmpG.x.Cmp(dx) == 0 && tmpG.y.Cmp(dy) == 0 {
|
|
|
+ s.n.Add(s.n, big.NewInt(1))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Curve) KeyPub(priv *big.Int) (pub *Point, err error) {
|
|
|
+ if err = s.IsValidN(); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ pub, err = s.g.Mul(priv)
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (s *Curve) RandomKeyPair() (priv *big.Int, pub *Point, err error) {
|
|
|
+ if err = s.IsValidN(); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ priv = Random(big.NewInt(1), Sub64(s.n, 1))
|
|
|
+ pub, err = s.g.Mul(priv)
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+// Вычисление общего секрета
|
|
|
+func (s *Curve) PointSecret(mPriv *big.Int, fPub *Point) (res *Point, err error) {
|
|
|
+ if err = s.IsValidN(); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ res, err = fPub.Mul(mPriv)
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+type ELKeyPriv struct {
|
|
|
+ d *big.Int
|
|
|
+}
|
|
|
+
|
|
|
+type ELKeyPub struct {
|
|
|
+ e1 *Point
|
|
|
+ e2 *Point
|
|
|
+}
|
|
|
+
|
|
|
+type ELKeyPair struct {
|
|
|
+ curve *Curve
|
|
|
+ priv *ELKeyPriv
|
|
|
+ pub *ELKeyPub
|
|
|
+ dTable map[byte]*Point
|
|
|
+}
|
|
|
+
|
|
|
+func (s *ELKeyPair) setupDataTable() error {
|
|
|
+ if s.dTable != nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ s.dTable = make(map[byte]*Point)
|
|
|
+ dx := big.NewInt(0)
|
|
|
+ //var points []*Point
|
|
|
+ //var p *Point
|
|
|
+ for i := byte(0); i <= 254; i++ {
|
|
|
+ p, _, err := s.curve.searhClosePoints(dx)
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // check already exists - todo
|
|
|
+ dx = Add64(p.x, 1)
|
|
|
+ s.dTable[i] = p
|
|
|
+ log.Println(i, p)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+type ELMessage struct {
|
|
|
+ c1 *Point
|
|
|
+ cd []*Point
|
|
|
+}
|
|
|
+
|
|
|
+// c1 = r x e1
|
|
|
+// c2 = P + r x e2
|
|
|
+func (s *ELKeyPair) EncodeMessage(data []byte) (mes *ELMessage, err error) {
|
|
|
+ r := Random(big.NewInt(1), Sub64(s.curve.p, 1))
|
|
|
+ c1, err := s.pub.e1.Mul(r)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ mes = &ELMessage{
|
|
|
+ c1: c1,
|
|
|
+ cd: make([]*Point, len(data)),
|
|
|
+ }
|
|
|
+ for i, d := range data {
|
|
|
+ p := s.dTable[d]
|
|
|
+ c2, err := s.pub.e2.Mul(r)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ cd, err := p.Add(c2)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ mes.cd[i] = cd
|
|
|
+ }
|
|
|
+ return mes, nil
|
|
|
+}
|
|
|
+
|
|
|
+// p = c2 – (d x c1)
|
|
|
+func (s *ELKeyPair) DecodeMessage(mes *ELMessage) ([]byte, error) {
|
|
|
+ res := make([]byte, len(mes.cd))
|
|
|
+ part, err := mes.c1.Mul(s.priv.d)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ part, _ = part.Neg()
|
|
|
+ for _, c2 := range mes.cd {
|
|
|
+ p, err := c2.Add(part)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ log.Println(p)
|
|
|
+ }
|
|
|
+ return res, nil
|
|
|
+}
|
|
|
+
|
|
|
+// https://intuit.ru/studies/professional_retraining/940/courses/408/lecture/9373?page=6
|
|
|
+func (s *Curve) ELKeyPair() (*ELKeyPair, error) {
|
|
|
+ e1, _, err := s.searhClosePoints(Random(big.NewInt(1), Sub64(s.p, 1)))
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ d := Random(big.NewInt(1), Sub64(s.n, 1))
|
|
|
+ e2, err := e1.Mul(d)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ res := &ELKeyPair{
|
|
|
+ curve: s,
|
|
|
+ priv: &ELKeyPriv{
|
|
|
+ d: d,
|
|
|
+ },
|
|
|
+ pub: &ELKeyPub{
|
|
|
+ e1: e1,
|
|
|
+ e2: e2,
|
|
|
+ },
|
|
|
+ }
|
|
|
+ return res, res.setupDataTable()
|
|
|
+}
|
|
|
+
|
|
|
+/*
|
|
|
+func (s *Curve) HEncode(b byte, pub *Point) (cm1, cm2 *Point, err error) {
|
|
|
+ var p1 *Point
|
|
|
+ if p1, _, err = s.points(big.NewInt(int64(b))); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ c1 = pub.Add()
|
|
|
+}
|
|
|
+*/
|
|
|
+
|
|
|
+/*
|
|
|
+// 𝐶𝑚 = (𝑘 × 𝐺, 𝑃𝑚 + 𝑘 × 𝑃𝐵)
|
|
|
+func (s *Curve) HellmanEncode(b byte, pub *Point) (cm1, cm2 *Point, err error) {
|
|
|
+ k := Random(big.NewInt(1), Sub64(s.n, 1))
|
|
|
+ if cm1, err = s.g.Mul(k); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ cm2, err = pub.Mul(Add64(k, int64(b)))
|
|
|
+ return
|
|
|
+}
|
|
|
+*/
|
|
|
+
|
|
|
+/*
|
|
|
+func (s *Curve) HellmanDecode(cm1, cm2 *Point, priv *big.Int) {
|
|
|
+
|
|
|
+}
|
|
|
+*/
|