000x000 il y a 2 ans
Parent
commit
ead5839448
6 fichiers modifiés avec 151 ajouts et 289 suppressions
  1. BIN
      __pycache__/crypt.cpython-38.pyc
  2. BIN
      __pycache__/point.cpython-38.pyc
  3. 0 237
      check.py
  4. 48 0
      crypt.py
  5. 76 41
      curve.py
  6. 27 11
      point.py

BIN
__pycache__/crypt.cpython-38.pyc


BIN
__pycache__/point.cpython-38.pyc


+ 0 - 237
check.py

@@ -1,237 +0,0 @@
-import collections
-import hashlib
-
-
-class VerificationFailed(Exception):
-
-    pass
-
-
-EllipticCurve = collections.namedtuple('EllipticCurve', 'seed p a b')
-
-
-# All the following curves except the last one were taken from the OpenSSL
-# source code (crypto/ec/ec_curve.c). The last four are fake curves that should
-# not pass seed validation.
-
-curves = {
-    'prime192v1': EllipticCurve(
-        seed=0x3045ae6fc8422f64ed579528d38120eae12196d5,
-        p=0xfffffffffffffffffffffffffffffffeffffffffffffffff,
-        a=0xfffffffffffffffffffffffffffffffefffffffffffffffc,
-        b=0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1,
-    ),
-    'secp224r1': EllipticCurve(
-        seed=0xbd71344799d5c7fcdc45b59fa3b9ab8f6a948bc5,
-        p=0xffffffffffffffffffffffffffffffff000000000000000000000001,
-        a=0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe,
-        b=0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4,
-    ),
-    'secp384r1': EllipticCurve(
-        seed=0xa335926aa319a27a1d00896a6773a4827acdac73,
-        p=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff,
-        a=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc,
-        b=0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef,
-    ),
-    'secp521r1': EllipticCurve(
-        seed=0xd09e8800291cb85396cc6717393284aaa0da64ba,
-        p=0x01ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff,
-        a=0x01fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc,
-        b=0x0051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00,
-    ),
-    'prime192v2': EllipticCurve(
-        seed=0x31a92ee2029fd10d901b113e990710f0d21ac6b6,
-        p=0xfffffffffffffffffffffffffffffffeffffffffffffffff,
-        a=0xfffffffffffffffffffffffffffffffefffffffffffffffc,
-        b=0xcc22d6dfb95c6b25e49c0d6364a4e5980c393aa21668d953,
-    ),
-    'prime192v3': EllipticCurve(
-        seed=0xc469684435deb378c4b65ca9591e2a5763059a2e,
-        p=0xfffffffffffffffffffffffffffffffeffffffffffffffff,
-        a=0xfffffffffffffffffffffffffffffffefffffffffffffffc,
-        b=0x22123dc2395a05caa7423daeccc94760a7d462256bd56916,
-    ),
-    'prime239v1': EllipticCurve(
-        seed=0xe43bb460f0b80cc0c0b075798e948060f8321b7d,
-        p=0x7fffffffffffffffffffffff7fffffffffff8000000000007fffffffffff,
-        a=0x7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc,
-        b=0x6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a,
-    ),
-    'prime239v2': EllipticCurve(
-        seed=0xe8b4011604095303ca3b8099982be09fcb9ae616,
-        p=0x7fffffffffffffffffffffff7fffffffffff8000000000007fffffffffff,
-        a=0x7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc,
-        b=0x617fab6832576cbbfed50d99f0249c3fee58b94ba0038c7ae84c8c832f2c,
-    ),
-    'prime239v3': EllipticCurve(
-        seed=0x7d7374168ffe3471b60a857686a19475d3bfa2ff,
-        p=0x7fffffffffffffffffffffff7fffffffffff8000000000007fffffffffff,
-        a=0x7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc,
-        b=0x255705fa2a306654b1f4cb03d6a750a30c250102d4988717d9ba15ab6d3e,
-    ),
-    'prime256v1': EllipticCurve(
-        seed=0xc49d360886e704936a6678e1139d26b7819f7e90,
-        p=0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff,
-        a=0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc,
-        b=0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b,
-    ),
-    'secp112r1': EllipticCurve(
-        seed=0x00f50b028e4d696e676875615175290472783fb1,
-        p=0xdb7c2abf62e35e668076bead208b,
-        a=0xdb7c2abf62e35e668076bead2088,
-        b=0x659ef8ba043916eede8911702b22,
-    ),
-    'secp112r2': EllipticCurve(
-        seed=0x002757a1114d696e6768756151755316c05e0bd4,
-        p=0xdb7c2abf62e35e668076bead208b,
-        a=0x6127c24c05f38a0aaaf65c0ef02c,
-        b=0x51def1815db5ed74fcc34c85d709,
-    ),
-    'secp128r1': EllipticCurve(
-        seed=0x000e0d4d696e6768756151750cc03a4473d03679,
-        p=0xfffffffdffffffffffffffffffffffff,
-        a=0xfffffffdfffffffffffffffffffffffc,
-        b=0xe87579c11079f43dd824993c2cee5ed3,
-    ),
-    'secp128r2': EllipticCurve(
-        seed=0x004d696e67687561517512d8f03431fce63b88f4,
-        p=0xfffffffdffffffffffffffffffffffff,
-        a=0xd6031998d1b3bbfebf59cc9bbff9aee1,
-        b=0x5eeefca380d02919dc2c6558bb6d8a5d,
-    ),
-    'secp160r1': EllipticCurve(
-        seed=0x1053cde42c14d696e67687561517533bf3f83345,
-        p=0x00ffffffffffffffffffffffffffffffff7fffffff,
-        a=0x00ffffffffffffffffffffffffffffffff7ffffffc,
-        b=0x001c97befc54bd7a8b65acf89f81d4d4adc565fa45,
-    ),
-    'secp160r2': EllipticCurve(
-        seed=0xb99b99b099b323e02709a4d696e6768756151751,
-        p=0x00fffffffffffffffffffffffffffffffeffffac73,
-        a=0x00fffffffffffffffffffffffffffffffeffffac70,
-        b=0x00b4e134d3fb59eb8bab57274904664d5af50388ba,
-    ),
-    # This is prime192v1 with a wrong value for seed.
-    'wrong192v1': EllipticCurve(
-        seed=0x123,
-        p=0xfffffffffffffffffffffffffffffffeffffffffffffffff,
-        a=0xfffffffffffffffffffffffffffffffefffffffffffffffc,
-        b=0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1,
-    ),
-    # This is prime192v1 with a wrong value for p.
-    'wrong192v2': EllipticCurve(
-        seed=0x3045ae6fc8422f64ed579528d38120eae12196d5,
-        p=0x123,
-        a=0xfffffffffffffffffffffffffffffffefffffffffffffffc,
-        b=0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1,
-    ),
-    # This is prime192v1 with a wrong value for a.
-    'wrong192v3': EllipticCurve(
-        seed=0x3045ae6fc8422f64ed579528d38120eae12196d5,
-        p=0xfffffffffffffffffffffffffffffffeffffffffffffffff,
-        a=0x123,
-        b=0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1,
-    ),
-    # This is prime192v1 with a wrong value for b.
-    'wrong192v4': EllipticCurve(
-        seed=0x3045ae6fc8422f64ed579528d38120eae12196d5,
-        p=0xfffffffffffffffffffffffffffffffeffffffffffffffff,
-        a=0xfffffffffffffffffffffffffffffffefffffffffffffffc,
-        b=0x123,
-    ),
-}
-
-
-def verify_curve(curve):
-    """
-    Проверяет, были ли сгенерированы параметры a и b данной кривой
-    из семени.
-    Вызывает исключение VerificationFailed в случае сбоя проверки.
-    """
-    # Далее следует реализация алгоритма проверки
-    # описан в "Алгоритме цифровой подписи на эллиптических кривых (ECDSA)",
-    # от Сертиком. Есть лишь несколько отличий от исходного алгоритма
-    # и реализация:
-    #
-    # * несколько имен переменных изменены для ясности;
-    # * документ от Certicom допускает произвольные начальные числа с битовой длиной
-    # >= 160; здесь нас интересуют только семена длиной ровно 160 бит.
-
-    if curve.seed.bit_length() > 160:
-        raise VerificationFailed('seed too long')
-
-    seed_bytes = curve.seed.to_bytes(length=160 // 8, byteorder='big')
-
-    # Определите t, s и v, как указано в документе.
-    t = curve.p.bit_length()
-    s = (t - 1) // 160
-    v = t - 160 * s
-
-    # 1. Вычислите h = SHA-1(seed_bytes) и пусть c0 обозначает битовую строку
-    # длина v битов, полученная путем взятия v крайних правых битов h.
-    h = hashlib.sha1(seed_bytes).digest()
-    h = int.from_bytes(h, byteorder='big')
-
-    c0 = h & ((1 << v) - 1)
-    print(h, c0)
-
-    # 2. Пусть w[0] обозначает строку битов длины v бит, полученную установкой
-    # крайний левый бит c0 в 0.
-    #
-    # Примечание: здесь мы используем 160 бит вместо v бит, как того требует документ.
-    # Мы делаем это для того, чтобы сделать код проще и потому что он не делает никаких ошибок.
-    # разница (см. шаг 6).
-    w0 = c0 & ((1 << v - 1) - 1)
-    w = [w0.to_bytes(length=160 // 8, byteorder='big')]
-
-    # 3. Пусть z будет целым числом, двоичное представление которого задается 160-битной строкой
-    # seed_bytes.
-    z = curve.seed
-
-    # 4. Для i от 1 до s делаем:
-    for i in range(1, s + 1):
-        # 4.1 Пусть s_i будет 160-битной строкой, которая представляет собой двоичное расширение
-        # целое число (z+i)%(2**g).
-        z_i = ((z + i) % (2 ** 160))
-        s_i = z_i.to_bytes(length=160 // 8, byteorder='big')
-
-        # 4.2 Вычисление w_i = SHA-1(s_i).
-        w_i = hashlib.sha1(s_i).digest()
-        w.append(w_i)
-
-    # 5. Пусть w будет битовой строкой, полученной конкатенацией w_0,w_1,...,w_s.
-    w = b''.join(w)
-
-    # 6. Пусть c будет целым числом, целочисленное разложение которого задается w.
-    #
-    # На шаге 2 мы сказали, что использовали более длинную битовую длину для первого элемента
-    # w. Это правильно, потому что результирующий c не меняется: используя 160
-    # бит вместо v бит эквивалентно добавлению нескольких нулей слева от c.
-    c = int.from_bytes(w, 'big')
-
-    # Если b ** 2 * c == a ** 3 (mod p), то accept; в противном случае отклонить.
-    if (curve.b * curve.b * c - curve.a * curve.a * curve.a) % curve.p != 0:
-        raise VerificationFailed('curve verification failed')
-
-
-# Check all the curves defined above.
-# Should produce the following output:
-#
-#     prime192v1: ok
-#     prime192v2: ok
-#     ...
-#     secp384r1: ok
-#     secp521r1: ok
-#     wrong192v1: failed
-#     wrong192v2: failed
-#     wrong192v3: failed
-#     wrong192v4: failed
-for name in sorted(curves):
-    curve = curves[name]
-    print(name, end=': ')
-    try:
-        verify_curve(curve)
-    except VerificationFailed:
-        print('failed')
-    else:
-        print('ok')

+ 48 - 0
crypt.py

@@ -0,0 +1,48 @@
+# pycrypto необходим для AES
+# pip3 install pycrypto
+
+from Crypto.Cipher import AES
+import base64
+
+# Выравнивание шифруемой строки
+def paddingSetup(text):
+    nLength = 16 - (len(text.encode('utf-8')) % 16)
+    text += chr(nLength) * nLength
+    return text
+
+
+# Удаление символов выравнивания
+def paddingRemove(text):
+    textLen = len(text)
+    if textLen == 0:
+        return text
+    asc = ord(text[-1])
+    if asc >= 16:
+        return text
+    return text[0:-asc]
+
+# Алгоритм симметричного шифрования AES
+# Ключем шифрования является точка секрета, полученная из приватного и публичного ключа
+class CipherAES:
+
+    def __init__(self, curve, secretPoint):
+        # в качестве ключа используется хэш точки секрета
+        self.key = secretPoint.md5()
+        # в качестве инициализирующего вектора - урезанный хэш базовой точки подгруппы эллиптической кривой
+        self.iv = curve.g.md5()
+
+    # инициализация шировальщика
+    def cipher(self):
+        return AES.new(key=self.key, mode=AES.MODE_CBC, IV=self.iv)
+
+    # Шифровка сообщения    
+    def encrypt(self, text):
+        data = self.cipher().encrypt(paddingSetup(text))
+        return str(base64.b64encode(data), 'utf-8')
+
+    # Расшифровка сообщения
+    def decrypt(self, encryptedText):
+        data = base64.b64decode(encryptedText)
+        bytes = self.cipher().decrypt(data)
+        blank = str(bytes, 'utf-8').strip()
+        return paddingRemove(blank)

+ 76 - 41
curve.py

@@ -1,19 +1,7 @@
 import random
 from point import Point
+from crypt import CipherAES
 
-'''
-'secp256k1',
-p=0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
-a=0,
-b=7,
-g=(0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8),
-# Subgroup order.
-n=0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141,
-# Subgroup cofactor.
-h=1,
-'''
-
-# y2 = x3 + ax + b
 class Curve:
     
     def __init__(self, p, a, b, gx, gy, n, h):
@@ -22,7 +10,7 @@ class Curve:
         # коэффиценты уравнения a и b
         self.a = a
         self.b = b
-        # базовая точка
+        # базовая точка, определяющая подгруппу
         self.g = Point(
             curve = self,
             x = gx,
@@ -40,10 +28,10 @@ class Curve:
             y = y
         )
 
-    # Обратное деление по модулю p кривой
+    # Обратное деление по модулю p
     def inverseMod(self, k, point):
         if k == 0:
-            raise ZeroDivisionError('division by zero')
+            raise ZeroDivisionError('Деление на 0 невозможно!!!')
         if k < 0:
             # k ** -1 = p - (-k) ** -1  (mod p)
             return point - self.inverseMod(-k, point)
@@ -57,8 +45,6 @@ class Curve:
             old_s, s = s, old_s - quotient * s
             old_t, t = t, old_t - quotient * t
         gcd, x, y = old_r, old_s, old_t
-        #assert gcd == 1
-        #assert (k * x) % p == 1
         return x % point
 
     def randomKeypair(self):
@@ -66,38 +52,87 @@ class Curve:
         keyPub = self.g * keyPriv
         return keyPriv, keyPub
 
+    # Создание публичного ключа из приватного
     def keyPub(self, keyPriv):
+        # Результат - умножение точки подгруппы на приватный ключ
         return self.g * keyPriv
 
+    # Вычисление общего секретного ключа, по которому производится шифрование и расшифровка сообщений
+    def pointSecret(self, myKeyPrivate, friendKeyPublic):
+        return friendKeyPublic * myKeyPrivate
+
+    # Шифровка сообщения секретным ключем
+    def encodeMessage(self, secretPoint, text):
+        cipher = CipherAES(self, secretPoint)
+        return cipher.encrypt(text)
+
+    # Расшифровка сообщения секретным ключем
+    def decodeMessage(self, secretPoint, encodedText):
+        cipher = CipherAES(self, secretPoint)
+        return cipher.decrypt(encodedText)
+        
+
 #################################
 
-c = Curve(
-    p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
-    a = 0,
-    b = 7,
-    gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
-    gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,
-    n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141,
-    h = 1
-)
+# Тестирование
+if __name__ == '__main__':
+
+    # Создание объекта эллиптической кривой (кривая из исходников openssl)
+    cur = Curve(
+        # Размер конечного поля
+        p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f,
+        # Коэффиценты
+        a = 0,
+        b = 7,
+        # Координаты точки, определяющей подгруппу
+        gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798,
+        gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8,
+        # Размер подгруппы
+        n = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141,
+        # Кофактор подгруппы
+        h = 1
+    )
 
-# 0x774ae7f858a9411e5ef4246b70c65aac5649980be5c17891bbec17895da008cb, 
-# 0xd984a032eb6b5e190243dd56d7b7b365372db1e2dff9d6a8301d74c9c953c61b
-#pub = c.keyPub(11)
+    # В зашифрованном обмене данными принимают участие 2 пользователя.
+    # Далее - п1 (пользователь 1) и п2 (пользователь 2)
+
+    # Каждый из пользовательей должен создать свою пару ключей (приватный и публичный) исходя из параметров одной эллиптической кривой
+
+    # Создание пары ключей п1
+    p1KeyPriv, p1KeyPub = cur.randomKeypair()
+    # Создание пары ключей п2
+    p2KeyPriv, p2KeyPub = cur.randomKeypair()
+
+    # Предположим, что п1 и п2 обменялись публичными ключами
+    # Далее каждый из них может рассчитать общий секретный ключ, используя свой приватный ключ и полученный публичный ключ контрагента
+    # Секретный ключ идентичен для п1 и п2 и используется в качестве ключа в алгоритме симметричного шифрования, который используется
+    # для шифровки и расшифровки сообщений
+    
+    # Расчет секретного ключа п1
+    p1KeySecret = cur.pointSecret(p1KeyPriv, p2KeyPub)
 
-#print(pub.show())
+    # Расчет секретного ключа п2
+    p2KeySecret = cur.pointSecret(p2KeyPriv, p1KeyPub)
 
-#exit(0)
+    # Покажем, что полученные секретные ключи идентичны у п1 и п2
+    print(p1KeySecret.isEqual(p2KeySecret))
 
-aPriv, aPub = c.randomKeypair()
-bPriv, bPub = c.randomKeypair()
+    # Далее п1 отправляет зашифрованное сообщение п2
+    p1MessageEncrypted = cur.encodeMessage(p1KeySecret, 'Привет, как дела?')
+    #Покажем зашифрованное сообщение
+    print("п1: ", p1MessageEncrypted)
 
-print(hex(aPriv))
-print(aPub.show())
+    # п2 расшифровывает полученное сообщение
+    p1MessageDecrypted = cur.decodeMessage(p2KeySecret, p1MessageEncrypted)
+    # Покажем расшифрованное сообщение
+    print("п1: ", p1MessageDecrypted)
 
-#aS = scalar_mult(alice_private_key, bob_public_key)
-aS = bPub * aPriv
-bS = aPub * bPriv
+    # п2 отправляет зашифрованным сообщением п1
+    p2MessageEncrypted = cur.encodeMessage(p2KeySecret, 'Спасибо, у меня всё хорошо :)')
+    #Покажем зашифрованное сообщение
+    print("п2: ", p2MessageEncrypted)
 
-print(aS.show())
-print(bS.show())
+    # п1 расшифровывает полученное сообщение
+    p2MessageDecrypted = cur.decodeMessage(p1KeySecret, p2MessageEncrypted)
+    # Покажем расшифрованное сообщение
+    print("п2: ", p2MessageDecrypted)

+ 27 - 11
point.py

@@ -1,3 +1,4 @@
+import hashlib
 
 class Point:
 
@@ -7,7 +8,7 @@ class Point:
         self.x = x
         self.y = y
 
-    # Копирование точки
+    # Копирование объекта точки
     def copy(self):
         return Point(
             curve = self.curve,
@@ -15,11 +16,13 @@ class Point:
             y = self.y,
         )
 
+    # HEX формат строки с координатами
     def show(self):
         if self.isNone():
             return "None"
         return "(0x{:x}, 0x{:x})".format(self.x, self.y)
 
+    # Числовой формат строки с координатами
     def showCoords(self):
         if self.isNone():
             return "None"
@@ -33,7 +36,7 @@ class Point:
             y = y
         )
 
-    # Иницуиализация новой точки с неопределенными координатами
+    # Инициализация новой точки с неопределенными координатами
     def pointNull(self):
         return Point(
             x = None,
@@ -42,7 +45,7 @@ class Point:
         )
 
     # Проверка, определена ли точка.
-    # ТОчка считается неопределенной, если одна из координат или кривая не определены
+    # Точка считается неопределенной, если одна из координат или объект эллиптической кривой не определены
     def isNone(self):
         return self.x is None or self.y is None or self.curve is None
 
@@ -50,7 +53,7 @@ class Point:
     def coords(self):
         return self.x, self.y
 
-    # Проверка пренадлежности точки к кривой
+    # Проверка пренадлежности точки к эллиптической кривой
     def isInCurve(self):
         x, y = self.coords()
         c = self.curve
@@ -63,7 +66,7 @@ class Point:
         x1, y1 = self.coords()
         x2, y2 = point.coords()
         if self.x == point.x:
-            # точки равны.
+            # точки равны
             c = self.curve
             m = (3 * x1 * x1 + cur.a) * cur.inverseMod(2 * y1, cur.p)
         else:
@@ -71,10 +74,8 @@ class Point:
             m = (y1 - y2) * cur.inverseMod(x1 - x2, cur.p)
         return m
 
-    # Сложение
+    # Скалярное сложение точек
     def __add__(self, point):
-        #assert is_on_curve(point1)
-        #assert is_on_curve(point2)
         if self.isNone():
             return point.copy()
         if point.isNone():
@@ -106,9 +107,8 @@ class Point:
 
     # Умножение
     def __mul__(self, k):
-        #assert is_on_curve(point)
         if self.isNone() or k % self.curve.n == 0:
-            return s.pointNull()
+            return self.pointNull()
 
         if k < 0:
             # k * point = -k * (-point)
@@ -124,7 +124,23 @@ class Point:
             # Double.
             addend = addend + addend
             k >>= 1
-        #assert is_on_curve(result)
         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