Browse Source

in progress

0x4a52466c696e74 2 years ago
parent
commit
e74b210fe7
9 changed files with 1511 additions and 0 deletions
  1. 90 0
      bigint.go
  2. 472 0
      curve.go
  3. 1 0
      dhellman/curve.go
  4. 277 0
      elgamal.go
  5. 5 0
      go.mod
  6. 2 0
      go.sum
  7. 280 0
      point.go
  8. 97 0
      tools.go
  9. 287 0
      z_test.go

+ 90 - 0
bigint.go

@@ -0,0 +1,90 @@
+package curve
+
+import (
+	"math/big"
+)
+
+var (
+	valMin = big.NewInt(1187)
+	//valMin  = big.NewInt(1)
+	maxTest = big.NewInt(111187000)
+	//maxTest = big.NewInt(87)
+	max = Exp64(big.NewInt(2), 63)
+)
+
+func And(a, b *big.Int) *big.Int {
+	return new(big.Int).And(a, b)
+}
+
+func Exp(a, b, mod *big.Int) *big.Int {
+	return new(big.Int).Exp(a, b, mod)
+}
+
+func Exp64(a *big.Int, b int64) *big.Int {
+	return new(big.Int).Exp(a, big.NewInt(b), nil)
+}
+
+func Mul(a, b *big.Int) *big.Int {
+	return new(big.Int).Mul(a, b)
+}
+
+func Add(a, b *big.Int) *big.Int {
+	return new(big.Int).Add(a, b)
+}
+
+func Add64(a *big.Int, b int64) *big.Int {
+	return Add(a, big.NewInt(b))
+}
+
+func Mod(a, b *big.Int) *big.Int {
+	return new(big.Int).Mod(a, b)
+}
+
+func Mod64(a *big.Int, b int64) *big.Int {
+	return Mod(a, big.NewInt(b))
+}
+
+func Neg(a *big.Int) *big.Int {
+	return new(big.Int).Neg(a)
+}
+
+func Sub(a, b *big.Int) *big.Int {
+	return new(big.Int).Sub(a, b)
+}
+
+func Sub64(a *big.Int, b int64) *big.Int {
+	return Sub(a, big.NewInt(b))
+}
+
+func Cmp(a, b *big.Int) bool {
+	return a.Cmp(b) == 0
+}
+
+func Div(a, b *big.Int) *big.Int {
+	return new(big.Int).Div(a, b)
+}
+
+func Div64(a *big.Int, b int64) *big.Int {
+	return Div(a, big.NewInt(b))
+}
+
+func Rem(a, b *big.Int) *big.Int {
+	return new(big.Int).Rem(a, b)
+}
+
+func Split(x *big.Int, parts int, firstOffset, lastOffset *big.Int) [][]*big.Int {
+	res := make([][]*big.Int, parts)
+	div := Div64(x, int64(parts))
+	var last []*big.Int
+	for i := 0; i < parts; i++ {
+		res[i] = []*big.Int{
+			Mul(big.NewInt(int64(i)), div),
+			Sub64(Add(Mul(big.NewInt(int64(i)), div), div), 1),
+		}
+		last = res[i]
+	}
+	last[1] = Add(last[1], Add64(Mod64(x, int64(parts)), 1))
+	res[0][0] = Add(res[0][0], firstOffset)
+	last[1] = Sub(last[1], lastOffset)
+	return res
+}

+ 472 - 0
curve.go

@@ -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) {
+
+}
+*/

+ 1 - 0
dhellman/curve.go

@@ -0,0 +1 @@
+package dhellman

+ 277 - 0
elgamal.go

@@ -0,0 +1,277 @@
+package curve
+
+import (
+	"context"
+	"errors"
+	"math/big"
+
+	"git.ali33.ru/fcg-xvii/go-tools/json"
+)
+
+func NewCrypt(conf json.Map, ctx context.Context) (res *Crypt, err error) {
+	var p *big.Int
+	var check bool
+	if conf.KeyExists("p") {
+		p, check = big.NewInt(0).SetString(conf.String("p", ""), 10)
+		if !check {
+			return nil, errors.New(`ожидается строка числового значения p > 0 в поле [ p ], например "1234"`)
+		}
+		if p.Cmp(intZero) <= 0 {
+			return nil, errors.New(`ожидается строка числового значения p > 0 в поле [ p ], например "1234"`)
+		}
+	}
+	res = new(Crypt)
+	err = res.Init(p, ctx)
+	return
+}
+
+type Crypt struct {
+	p *big.Int
+	g *big.Int
+}
+
+func (s *Crypt) IsValid() bool {
+	return s.p != nil
+}
+
+func (s *Crypt) Init(p *big.Int, ctx context.Context) (err error) {
+	if s.p != nil {
+		return errors.New("параметры криптографии инициированы были инициализированы ранее")
+	}
+	if p == nil {
+		//p = Random(valMin, max)
+		p = Random(valMin, maxTest)
+	}
+	s.p, s.g, err = SearchP(p, ctx)
+	return
+}
+
+func (s *Crypt) Keys(key *big.Int, ctx context.Context) *KeyPair {
+	if !s.IsValid() {
+		s.Init(nil, ctx)
+	}
+	priv := &KeyPrivate{
+		key: key,
+		c:   s,
+	}
+	pub := &KeyPublic{
+		key: Exp(s.g, priv.key, s.p),
+		c:   s,
+	}
+	res := &KeyPair{
+		priv: priv,
+		pub:  pub,
+	}
+	return res
+}
+
+func (s *Crypt) KeysGenerate(ctx context.Context) *KeyPair {
+	return s.Keys(Random(big.NewInt(1), Sub64(s.p, 1)), ctx)
+}
+
+func (s *Crypt) BrutforceKey(pub *KeyPublic, threads int, ctx context.Context) (res *KeyPair, err error) {
+	if threads < 0 {
+		threads = 1
+	} else if threads > 5 {
+		threads = 5
+	}
+	ch := make(chan *KeyPair)
+	cctx, cancel := context.WithCancel(ctx)
+	parts := Split(s.p, threads, big.NewInt(1), big.NewInt(1))
+	for i := 0; i < threads; i++ {
+		go func(part []*big.Int) {
+			cur, finish := intCopy(part[0]), part[1]
+			for {
+				select {
+				case <-cctx.Done():
+					return
+				case <-ctx.Done():
+					return
+				default:
+					if cur.Cmp(finish) <= 0 {
+						pair := s.Keys(cur, ctx)
+						if pair.pub.IsEqual(pub) {
+							ch <- pair
+							return
+						}
+						cur = Add64(cur, 1)
+					} else {
+						return
+					}
+				}
+			}
+		}(parts[i])
+	}
+	defer cancel()
+	select {
+	case res = <-ch:
+	case <-ctx.Done():
+		err = errors.New("Атака грубой силы не удалась - время вышло")
+	}
+	return
+}
+
+//////////////////////////////
+
+type KeyPair struct {
+	priv *KeyPrivate
+	pub  *KeyPublic
+}
+
+type KeyPrivate struct {
+	key *big.Int
+	c   *Crypt
+}
+
+func (s *KeyPrivate) MessageDecode(mes *Message) ([]byte, error) {
+	sl := Exp(mes.a, s.key, s.c.p)
+	if sl.ModInverse(sl, s.c.p) == nil {
+		return nil, errors.New("Ощиюбка расшифровки сообщения: некорректный приватный ключ")
+	}
+	data := make([]byte, len(mes.encrypted))
+	for i, e := range mes.encrypted {
+		part := Mul(sl, e)
+		data[i] = Mod(part, s.c.p).Bytes()[0]
+	}
+	return data, nil
+
+}
+
+type KeyPublic struct {
+	key *big.Int
+	c   *Crypt
+}
+
+func (s *KeyPublic) IsEqual(c *KeyPublic) bool {
+	return s.key.Cmp(c.key) == 0
+}
+
+func (s *KeyPublic) MessageEncode(data []byte) (res *Message) {
+	k := Random(big.NewInt(1), Sub64(s.c.p, 20))
+	k = SearchPrime(k)
+	//k := big.NewInt(2)
+	res = &Message{
+		a:         Exp(s.c.g, k, s.c.p),
+		encrypted: make([]*big.Int, len(data)),
+	}
+	sl := Exp(s.key, k, s.c.p)
+	for i, b := range data {
+		part := Mul(sl, big.NewInt(int64(b)))
+		res.encrypted[i] = Mod(part, s.c.p)
+	}
+	return
+}
+
+//////////////////////////////
+
+type Message struct {
+	a         *big.Int
+	encrypted []*big.Int
+}
+
+/*
+// PublicKey represents an ElGamal public key.
+type PublicKey struct {
+	G, P, Y *big.Int
+}
+
+// PrivateKey represents an ElGamal private key.
+type PrivateKey struct {
+	PublicKey
+	X *big.Int
+}
+
+// Encrypt encrypts the given message to the given public key. The result is a
+// pair of integers. Errors can result from reading random, or because msg is
+// too large to be encrypted to the public key.
+func Encrypt(random io.Reader, pub *PublicKey, msg []byte) (c1, c2 *big.Int, err error) {
+	pLen := (pub.P.BitLen() + 7) / 8
+	log.Println(pLen, len(msg))
+	if len(msg) > pLen-11 {
+		err = errors.New("elgamal: message too long")
+		return
+	}
+
+	// EM = 0x02 || PS || 0x00 || M
+	em := make([]byte, pLen-1)
+	em[0] = 2
+	ps, mm := em[1:len(em)-len(msg)-1], em[len(em)-len(msg):]
+	err = nonZeroRandomBytes(ps, random)
+	if err != nil {
+		return
+	}
+	em[len(em)-len(msg)-1] = 0
+	copy(mm, msg)
+
+	m := new(big.Int).SetBytes(em)
+
+	k, err := rand.Int(random, pub.P)
+	if err != nil {
+		return
+	}
+
+	log.Println("Y", pub.Y)
+	c1 = new(big.Int).Exp(pub.G, k, pub.P)
+	s := new(big.Int).Exp(pub.Y, k, pub.P)
+	c2 = s.Mul(s, m)
+	c2.Mod(c2, pub.P)
+
+	return
+}
+
+// Decrypt takes two integers, resulting from an ElGamal encryption, and
+// returns the plaintext of the message. An error can result only if the
+// ciphertext is invalid. Users should keep in mind that this is a padding
+// oracle and thus, if exposed to an adaptive chosen ciphertext attack, can
+// be used to break the cryptosystem.  See “Chosen Ciphertext Attacks
+// Against Protocols Based on the RSA Encryption Standard PKCS #1”, Daniel
+// Bleichenbacher, Advances in Cryptology (Crypto '98),
+func Decrypt(priv *PrivateKey, c1, c2 *big.Int) (msg []byte, err error) {
+	s := new(big.Int).Exp(c1, priv.X, priv.P)
+	if s.ModInverse(s, priv.P) == nil {
+		return nil, errors.New("elgamal: invalid private key")
+	}
+	s.Mul(s, c2)
+	s.Mod(s, priv.P)
+	em := s.Bytes()
+
+	firstByteIsTwo := subtle.ConstantTimeByteEq(em[0], 2)
+
+	// The remainder of the plaintext must be a string of non-zero random
+	// octets, followed by a 0, followed by the message.
+	//   lookingForIndex: 1 iff we are still looking for the zero.
+	//   index: the offset of the first zero byte.
+	var lookingForIndex, index int
+	lookingForIndex = 1
+
+	for i := 1; i < len(em); i++ {
+		equals0 := subtle.ConstantTimeByteEq(em[i], 0)
+		index = subtle.ConstantTimeSelect(lookingForIndex&equals0, i, index)
+		lookingForIndex = subtle.ConstantTimeSelect(equals0, 0, lookingForIndex)
+	}
+
+	if firstByteIsTwo != 1 || lookingForIndex != 0 || index < 9 {
+		return nil, errors.New("elgamal: decryption error")
+	}
+	return em[index+1:], nil
+}
+
+// nonZeroRandomBytes fills the given slice with non-zero random octets.
+func nonZeroRandomBytes(s []byte, rand io.Reader) (err error) {
+	_, err = io.ReadFull(rand, s)
+	if err != nil {
+		return
+	}
+
+	for i := 0; i < len(s); i++ {
+		for s[i] == 0 {
+			_, err = io.ReadFull(rand, s[i:i+1])
+			if err != nil {
+				return
+			}
+		}
+	}
+
+	return
+}
+*/

+ 5 - 0
go.mod

@@ -0,0 +1,5 @@
+module git.ali33.ru/fcg-xvii/curve
+
+go 1.18
+
+require git.ali33.ru/fcg-xvii/go-tools v0.0.0-20221003202615-4f03edf5cf03

+ 2 - 0
go.sum

@@ -0,0 +1,2 @@
+git.ali33.ru/fcg-xvii/go-tools v0.0.0-20221003202615-4f03edf5cf03 h1:1ZnIuj6Z2f/Rv8TDSQuDBBwWCezEE5aZSBjo88tSIBE=
+git.ali33.ru/fcg-xvii/go-tools v0.0.0-20221003202615-4f03edf5cf03/go.mod h1:YbBhWFFNNQIKcRisQFnpVaN5KA+XHGImSU1Z/MuntqU=

+ 280 - 0
point.go

@@ -0,0 +1,280 @@
+package curve
+
+import (
+	"errors"
+	"fmt"
+	"math/big"
+)
+
+func NewPoint(x, y *big.Int, curve *Curve) *Point {
+	return &Point{
+		curve: curve,
+		x:     intCopy(x),
+		y:     intCopy(y),
+	}
+}
+
+type Point struct {
+	x     *big.Int
+	y     *big.Int
+	curve *Curve
+}
+
+func (s *Point) String() string {
+	return fmt.Sprintf("(%v, %v)", s.x, s.y)
+}
+
+func (s *Point) PointNull() *Point {
+	return &Point{
+		curve: s.curve,
+	}
+}
+
+// Проверка, валидна ли точка.
+// Точка считается валидной, если определены x, y и объект кривой
+func (s *Point) IsValid() (err error) {
+	if s.x == nil {
+		err = errors.New("координата [ x ] точки не определена")
+	} else if s.y == nil {
+		err = errors.New("координата [ y ] точки не определена")
+	} else if s.curve == nil {
+		err = errors.New("кривая точки не определена")
+	} else {
+		err = s.curve.IsValidP()
+	}
+	return
+}
+
+func (s *Point) InitXY(x, y *big.Int) *Point {
+	return &Point{
+		x:     intCopy(x),
+		y:     intCopy(y),
+		curve: s.curve,
+	}
+}
+
+// Копирование точки
+func (s *Point) Copy() *Point {
+	res := &Point{
+		curve: s.curve,
+	}
+	if s.x != nil {
+		res.x = new(big.Int).Set(s.x)
+	}
+	if s.y != nil {
+		res.y = new(big.Int).Set(s.y)
+	}
+	return res
+}
+
+func (s *Point) ShowHex() string {
+	return fmt.Sprintf(
+		"0x%x, 0x%x",
+		mustBigInt(s.x),
+		mustBigInt(s.y),
+	)
+}
+
+func (s *Point) Show() string {
+	return fmt.Sprintf(
+		"%s, %s",
+		mustBigInt(s.x),
+		mustBigInt(s.y),
+	)
+}
+
+func (s *Point) Coords() (x, y *big.Int) {
+	if s.x != nil {
+		x = new(big.Int).Set(s.x)
+	}
+	if s.y != nil {
+		y = new(big.Int).Set(s.y)
+	}
+	return
+}
+
+// Проверка принадлежности точки кривой.
+// Точка должна удовлетворять уровнению (y * y - x * x * x - c.a * x - c.b) % c.p == 0
+func (s *Point) IsInCurve() error {
+	if err := s.IsValid(); err != nil {
+		return err
+	}
+	if err := s.curve.IsValidP(); err != nil {
+		return err
+	}
+	x, y := s.Coords()
+	c := s.curve
+	y2 := Exp64(y, 2)
+	x3 := Exp64(x, 3)
+	cax := Mul(c.a, x)
+	res := Sub(Sub(Sub(y2, x3), cax), c.b)
+	res = Rem(res, c.p)
+	if res.Cmp(big.NewInt(0)) != 0 {
+		return fmt.Errorf("точка [ %s ] не пренадлежит кривой", s.Show())
+	}
+	return nil
+}
+
+// Вычисление наклона прямой, проходящей через 2 точки эллиптической кривой
+func (s *Point) GetIncline(pt *Point) (m *big.Int, err error) {
+	if err = s.IsValid(); err != nil {
+		return
+	}
+	if err = pt.IsValid(); err != nil {
+		return
+	}
+	m, cur := intCopy(intZero), s.curve
+	x1, y1 := s.Coords()
+	x2, y2 := pt.Coords()
+	var iMod *big.Int
+	if Cmp(x1, x2) { // !!! (points compare)
+		// m = (3 * x1 * x1 + cur.a) * cur.inverseMod(2 * y1, cur.p)
+		if iMod, err = cur.InverseMod(Mul(big.NewInt(2), y1), cur.p); err != nil {
+			return
+		}
+		m = Mul(
+			Add(
+				Mul(big.NewInt(3), Exp64(x1, 2)),
+				cur.a,
+			),
+			iMod,
+		)
+	} else {
+		// m = (y1 - y2) * cur.inverseMod(x1 - x2, cur.p)
+		if iMod, err = cur.InverseMod(Sub(x1, x2), cur.p); err != nil {
+			return
+		}
+		//log.Println("imod", iMod)
+		m = Mul(
+			Sub(y1, y2),
+			iMod,
+		)
+	}
+	return
+}
+
+func (s *Point) Add(pt *Point) (rpt *Point, err error) {
+	if err = s.IsValid(); err != nil {
+		err, rpt = nil, pt.Copy()
+	} else if err = pt.IsValid(); err != nil {
+		err, rpt = nil, s.Copy()
+	} else if s.x.Cmp(pt.x) == 0 && s.y.Cmp(pt.y) != 0 {
+		rpt = s.PointNull()
+	} else {
+		var m *big.Int
+		if m, err = s.GetIncline(pt); err != nil {
+			return
+		}
+		// rx = m * m - s.x - pt.x
+		//rxry 567678238 13525501245905
+		//log.Println("MMMMMMM", m, s.x, s.y, pt.x, pt.y)
+		rx := Sub(Sub(Mul(m, m), s.x), pt.x)
+		// ry = s.y + m * (rx - s.x)
+		ry := Add(s.y, Mul(m, Sub(rx, s.x)))
+		//log.Println("rxry", rx, ry)
+		rpt = s.InitXY(
+			//x = rx % s.curve.p,
+			Mod(rx, s.curve.p),
+			//y = -ry % s.curve.p,
+			Mod(Neg(ry), s.curve.p),
+		)
+	}
+	return
+}
+
+// Унарный минус
+func (s *Point) Neg() (pt *Point, err error) {
+	if err = s.IsValid(); err != nil {
+		return
+	}
+	pt = s.InitXY(
+		intCopy(s.x),
+		Mod(Neg(s.y), s.curve.p),
+	)
+	return
+}
+
+func (s *Point) Compare(pt *Point) (check bool, err error) {
+	if err = s.IsValid(); err != nil {
+		return
+	}
+	if err = pt.IsValid(); err != nil {
+		return
+	}
+	check = s.x.Cmp(pt.x) == 0
+	return
+}
+
+func (s *Point) Mul(k *big.Int) (pt *Point, err error) {
+
+	if err = s.IsValid(); err == nil {
+		if err = s.curve.IsValidN(); err != nil {
+			return
+		}
+	}
+	if k.Cmp(big.NewInt(0)) < 0 {
+		// k * point = -k * (-point)
+		if pt, err = s.Neg(); err == nil {
+			pt, err = pt.Mul(Neg(k))
+		}
+	} else {
+		pt = s.PointNull()
+		addend := s.Copy()
+		k = intCopy(k)
+		for k.Cmp(intZero) != 0 {
+			if And(k, big.NewInt(1)).Cmp(intZero) > 0 {
+				if pt, err = pt.Add(addend); err != nil {
+					return
+				}
+			}
+			if addend, err = addend.Add(addend); err != nil {
+				return
+			}
+			// k >>= 1
+			k.Rsh(k, 1)
+		}
+	}
+	return
+}
+
+/*
+    # Умножение
+   def __mul__(self, k):
+       if self.isNone() or k % self.curve.n == 0:
+           return self.pointNull()
+
+       if k < 0:
+           # k * point = -k * (-point)
+           return -self * -k
+
+       res = self.pointNull()
+       addend = self.copy()
+
+       while k:
+           if k & 1:
+               # Add.
+               res = res + addend
+           # Double.
+           addend = addend + addend
+           k >>= 1
+       return res
+
+   # Вычисление хэша точки (числовая строка суммы координат)
+   def md5(self):
+       src = str(self.x + self.y)
+       return hashlib.md5(src.encode('utf-8')).digest()
+
+   # Вычисление хэша по оси x
+   def md5X(self):
+       return hashlib.md5(str(self.x).encode('utf-8')).digest()
+
+   # Вычисление хэша по оси y
+   def md5Y(self):
+       return hashlib.md5(str(self.y).encode('utf-8')).digest()
+
+   # Проверка совпадения координат 2х точек по осям x и y
+   def isEqual(self, point):
+       x1, y1 = self.coords()
+       x2, y2 = point.coords()
+       return x1 == x2 and y1 == y2
+*/

+ 97 - 0
tools.go

@@ -0,0 +1,97 @@
+package curve
+
+import (
+	"context"
+	"errors"
+	"math/big"
+	"math/rand"
+	"time"
+)
+
+var (
+	rnd       = rand.New(rand.NewSource(time.Now().UnixNano())) // Генератор случайных чисел
+	intZero   = big.NewInt(0)
+	intMax, _ = new(big.Int).SetString("9223372036854775807", 10)
+)
+
+func intCopy(val *big.Int) (res *big.Int) {
+	if val != nil {
+		res = new(big.Int).Set(val)
+	}
+	return
+}
+
+// Возвращает число или 0 при пустом указателе
+func mustBigInt(val *big.Int) (res *big.Int) {
+	res = val
+	if res == nil {
+		res = big.NewInt(0)
+	}
+	return res
+}
+
+func IsPrime(val *big.Int) bool {
+	return val.ProbablyPrime(100)
+}
+
+func SearchP(min *big.Int, ctx context.Context) (p *big.Int, g *big.Int, err error) {
+	ch := make(chan struct{})
+	go func() {
+		defer func() {
+			close(ch)
+		}()
+		p = intCopy(min)
+		var check bool
+		for {
+			select {
+			case <-ctx.Done():
+				err = errors.New("Поиск поля и первообразного корня - время вышло")
+				return
+			default:
+				if g, check = CheckP(p); check {
+					ch <- struct{}{}
+					return
+				}
+				p = Add64(p, 1)
+			}
+		}
+	}()
+	<-ch
+	return
+}
+
+func CheckP(val *big.Int) (res *big.Int, check bool) {
+	if IsPrime(val) {
+		//log.Println("PRIME", val, Div64(Sub64(val, 1), 2))
+		res = Div64(Sub64(val, 1), 2)
+		check = IsPrime(res)
+	}
+	return
+}
+
+func SearchPrime(val *big.Int) *big.Int {
+	rVal := new(big.Int).Set(val)
+	//log.Println(rVal)
+	for {
+		if rVal.ProbablyPrime(100) {
+			break
+		}
+		rVal.Add(rVal, big.NewInt(1))
+		//log.Println(rVal)
+	}
+	return rVal
+}
+
+func Random(min, max *big.Int) *big.Int {
+	if min == nil {
+		min = intZero
+	}
+	if max == nil {
+		max = intMax
+	}
+	res := new(big.Int).Rand(rnd, max)
+	for res.Cmp(min) == -1 {
+		res = new(big.Int).Rand(rnd, max)
+	}
+	return res
+}

+ 287 - 0
z_test.go

@@ -0,0 +1,287 @@
+package curve
+
+import (
+	"context"
+	"log"
+	"math"
+	"math/big"
+	"testing"
+	"time"
+
+	"git.ali33.ru/fcg-xvii/go-tools/json"
+)
+
+func TestSearchP(t *testing.T) {
+	min := big.NewInt(1)
+	var g *big.Int
+	var err error
+	mmin := big.NewInt(0)
+	mg := big.NewInt(0)
+	for {
+		ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Millisecond*50))
+		if min, g, err = SearchP(min, ctx); err != nil {
+			log.Println(mmin, mg)
+			t.Fatal(err)
+		}
+		mmin, mg = min, g
+		cancel()
+		//log.Println(min, g)
+		min = Add64(min, 1)
+	}
+}
+
+func TestFormula(t *testing.T) {
+	a, _ := big.NewInt(0).SetString("10", 10)
+	b, _ := big.NewInt(0).SetString("15", 10)
+	c, err := New(a, b)
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(c.FormulaString())
+}
+
+func TestSingular(t *testing.T) {
+	a := big.NewInt(0)
+	b := big.NewInt(0)
+	c, err := New(a, b)
+	if err == nil {
+		t.Fatal(c.FormulaString(), ", не сингулярна")
+	}
+	t.Log(err)
+}
+
+func TestSearchPrime(t *testing.T) {
+	a := big.NewInt(101010101010)
+	b := SearchPrime(a)
+	t.Log(a, b)
+}
+
+func TestBigRandom(t *testing.T) {
+	for i := 0; i < 10; i++ {
+		t.Log(Random(big.NewInt(15), big.NewInt(20)))
+	}
+}
+
+func TestPointShow(t *testing.T) {
+	p := &Point{
+		y: big.NewInt(105465465463543),
+	}
+	t.Log(p.Show())
+	t.Log(p.ShowHex())
+}
+
+func TestCurveG(t *testing.T) {
+	curve, err := New(
+		big.NewInt(2),
+		big.NewInt(4),
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	p := SearchPrime(big.NewInt(2000000))
+	log.Println("P", p)
+	if err = curve.SetP(p); err != nil {
+		t.Fatal(err)
+	}
+	//log.Println(curve.a, curve.b, curve.p)
+	//p1, _, err := curve.searhClosePoints(big.NewInt(1000000))
+	//log.Println("==============================")
+	//log.Println(p1.Show())
+	//log.Println(p2.Show())
+	//log.Println("==============================")
+	//return
+	//log.Println(p1.Show(), p2.Show(), err)
+	if err = curve.SetGRandom(); err != nil {
+		t.Fatal(err)
+	}
+	t.Log("G", curve.g.Show())
+	if err = curve.SetN(); err != nil {
+		t.Fatal(err)
+	}
+	t.Log(curve.n)
+}
+
+func TestKeyPairs(t *testing.T) {
+	curve, err := New(
+		big.NewInt(2),
+		big.NewInt(4),
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	p := SearchPrime(big.NewInt(int64(math.Pow(2, 10))))
+	log.Println("P", p)
+	if err = curve.SetP(p); err != nil {
+		t.Fatal(err)
+	}
+	if err = curve.SetGRandom(); err != nil {
+		t.Fatal(err)
+	}
+	t.Log("G", curve.g.Show())
+	if err = curve.SetN(); err != nil {
+		t.Fatal(err)
+	}
+	t.Log("N", curve.n)
+	priv1, pub1, err := curve.RandomKeyPair()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(priv1, pub1)
+	priv2, pub2, err := curve.RandomKeyPair()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(priv2, pub2)
+	log.Println(curve.PointSecret(priv1, pub2))
+	log.Println(curve.PointSecret(priv2, pub1))
+
+	p1, err := curve.ELKeyPair()
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(p1.priv.d)
+	t.Log(p1.pub.e1, p1.pub.e2)
+
+	mes, err := p1.EncodeMessage([]byte{5})
+	if err != nil {
+		t.Fatal(err)
+	}
+	log.Println(mes.c1)
+	log.Println(mes.cd)
+	p1.DecodeMessage(mes)
+}
+
+/*
+func TestGe(t *testing.T) {
+	curve, err := New(
+		big.NewInt(2),
+		big.NewInt(4),
+	)
+	if err != nil {
+		t.Fatal(err)
+	}
+	p := SearchPrime(big.NewInt(int64(math.Pow(2, 10))))
+	log.Println("P", p)
+	if err = curve.SetP(p); err != nil {
+		t.Fatal(err)
+	}
+	if err = curve.SetGRandom(); err != nil {
+		t.Fatal(err)
+	}
+	t.Log("G", curve.g.Show())
+	if err = curve.SetN(); err != nil {
+		t.Fatal(err)
+	}
+	t.Log("N", curve.n)
+	v1, vp1 := curve.GKeyPair()
+	v2, vp2 := curve.GKeyPair()
+	t.Log(v1, vp1)
+	t.Log(v2, vp2)
+	e1, e2 := curve.GEncode(5, vp2)
+	t.Log(e1, e2)
+	curve.GDecode(e1, e2, v2)
+}
+*/
+
+/*
+func TestEncryptDecrypt(t *testing.T) {
+	p := SearchPrime(big.NewInt(int64(math.Pow(2, 10))))
+	g := Div64(Sub64(p, 1), 2)
+	priv := &PrivateKey{
+		PublicKey: PublicKey{
+			G: g,
+			P: p,
+		},
+		X: big.NewInt(20),
+	}
+	priv.Y = new(big.Int).Exp(priv.G, priv.X, priv.P)
+
+	message := []byte{5}
+	c1, c2, err := Encrypt(rand.Reader, &priv.PublicKey, message)
+	if err != nil {
+		t.Errorf("error encrypting: %s", err)
+	}
+	message2, err := Decrypt(priv, c1, c2)
+	if err != nil {
+		t.Errorf("error decrypting: %s", err)
+	}
+	if !bytes.Equal(message2, message) {
+		t.Errorf("decryption failed, got: %x, want: %x", message2, message)
+	}
+	log.Println(message, message2)
+}
+*/
+
+func TestJSON(t *testing.T) {
+	p := big.NewInt(0).SetBytes([]byte{10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10})
+	t.Log(p)
+	jm := json.Map{
+		"p": p,
+	}
+	jm.LogPretty()
+	pl := jm.Value("p", big.NewInt(0))
+	log.Printf("%T %s", pl, pl)
+	src := []byte(`{
+        "p": "13"
+	}`)
+	var jjm json.Map
+	json.Unmarshal(src, &jjm)
+	//pll := jjm.Value("p", big.NewInt(0))
+	pll, _ := big.NewInt(0).SetString(jjm.StringVal("p", ""), 10)
+	log.Printf("%T %s", pll, pll)
+}
+
+func TestCrypt(t *testing.T) {
+	jm := json.Map{}
+	ctx, _ := context.WithDeadline(context.Background(), time.Now().Add(time.Millisecond*1000))
+	cr, err := NewCrypt(jm, ctx)
+	if err != nil {
+		t.Fatal(err)
+	}
+	c1 := cr.KeysGenerate(context.Background())
+	c2 := cr.KeysGenerate(context.Background())
+	log.Println(c1.priv.key, c1.pub.key)
+	log.Println(c2.priv.key, c2.pub.key)
+	text := "Hello, World!!! ;)"
+	log.Println(text)
+	log.Println([]byte(text))
+	mes := c2.pub.MessageEncode([]byte(text))
+	//mes := c2.pub.MessageEncode([]byte{15, 10})
+	log.Println(mes.a, mes.encrypted)
+	dData, err := c2.priv.MessageDecode(mes)
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(string(dData))
+}
+
+func TestCryptBrutforceKey(t *testing.T) {
+	ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(time.Millisecond*1000))
+	defer cancel()
+	cr, err := NewCrypt(json.Map{}, ctx)
+	if err != nil {
+		t.Fatal(err)
+	}
+	k1 := cr.KeysGenerate(context.Background())
+	mes := k1.pub.MessageEncode([]byte("Hello, WORLD!!! ;)"))
+	log.Println(k1.priv.key, k1.pub.key)
+	cctx, _ := context.WithDeadline(context.Background(), time.Now().Add(time.Second*10))
+	pair, err := cr.BrutforceKey(k1.pub, 4, cctx)
+	if err != nil {
+		t.Fatal(err)
+	}
+	log.Println(pair.priv.key, pair.pub.key)
+	dec, err := pair.priv.MessageDecode(mes)
+	if err != nil {
+		t.Fatal(err)
+	}
+	t.Log(string(dec))
+}
+
+func TestSplit(t *testing.T) {
+	x := big.NewInt(1000)
+	parts := 2
+	firstOffset, lastOffset := big.NewInt(1), big.NewInt(2)
+	res := Split(x, parts, firstOffset, lastOffset)
+	log.Println(res)
+}