]> Cypherpunks.ru repositories - pygost.git/blobdiff - pygost/gost3410.py
Unify docstring's leading space presence
[pygost.git] / pygost / gost3410.py
index c9b905e225ed06e63fb70c0c55f4f15b96934121..433c8188f96144dd7457a308d1cf4c274b805376 100644 (file)
@@ -1,11 +1,10 @@
 # coding: utf-8
 # PyGOST -- Pure Python GOST cryptographic functions library
 # coding: utf-8
 # PyGOST -- Pure Python GOST cryptographic functions library
-# Copyright (C) 2015-2016 Sergey Matveev <stargrave@stargrave.org>
+# Copyright (C) 2015-2020 Sergey Matveev <stargrave@stargrave.org>
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
 #
 # This program is free software: you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
+# the Free Software Foundation, version 3 of the License.
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
 #
 # This program is distributed in the hope that it will be useful,
 # but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -14,7 +13,7 @@
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
-""" GOST R 34.10 public-key signature function.
+"""GOST R 34.10 public-key signature function.
 
 This is implementation of GOST R 34.10-2001 (:rfc:`5832`), GOST R
 34.10-2012 (:rfc:`7091`). The difference between 2001 and 2012 is the
 
 This is implementation of GOST R 34.10-2001 (:rfc:`5832`), GOST R
 34.10-2012 (:rfc:`7091`). The difference between 2001 and 2012 is the
@@ -23,123 +22,61 @@ key, digest and signature lengths.
 
 from os import urandom
 
 
 from os import urandom
 
-from pygost.gost3411_94 import GOST341194
 from pygost.utils import bytes2long
 from pygost.utils import hexdec
 from pygost.utils import long2bytes
 from pygost.utils import modinvert
 
 
 from pygost.utils import bytes2long
 from pygost.utils import hexdec
 from pygost.utils import long2bytes
 from pygost.utils import modinvert
 
 
-SIZE_3410_2001 = 32
-SIZE_3410_2012 = 64
-
-
-DEFAULT_CURVE = "GostR3410_2001_CryptoPro_A_ParamSet"
-# Curve parameters are the following: p, q, a, b, x, y
-CURVE_PARAMS = {
-    "GostR3410_2001_ParamSet_cc": (
-        "C0000000000000000000000000000000000000000000000000000000000003C7",
-        "5fffffffffffffffffffffffffffffff606117a2f4bde428b7458a54b6e87b85",
-        "C0000000000000000000000000000000000000000000000000000000000003c4",
-        "2d06B4265ebc749ff7d0f1f1f88232e81632e9088fd44b7787d5e407e955080c",
-        "0000000000000000000000000000000000000000000000000000000000000002",
-        "a20e034bf8813ef5c18d01105e726a17eb248b264ae9706f440bedc8ccb6b22c",
-    ),
-    "GostR3410_2001_TestParamSet": (
-        "8000000000000000000000000000000000000000000000000000000000000431",
-        "8000000000000000000000000000000150FE8A1892976154C59CFC193ACCF5B3",
-        "0000000000000000000000000000000000000000000000000000000000000007",
-        "5FBFF498AA938CE739B8E022FBAFEF40563F6E6A3472FC2A514C0CE9DAE23B7E",
-        "0000000000000000000000000000000000000000000000000000000000000002",
-        "08E2A8A0E65147D4BD6316030E16D19C85C97F0A9CA267122B96ABBCEA7E8FC8",
-    ),
-    "GostR3410_2001_CryptoPro_A_ParamSet": (
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97",
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893",
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94",
-        "00000000000000000000000000000000000000000000000000000000000000a6",
-        "0000000000000000000000000000000000000000000000000000000000000001",
-        "8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14",
-    ),
-    "GostR3410_2001_CryptoPro_B_ParamSet": (
-        "8000000000000000000000000000000000000000000000000000000000000C99",
-        "800000000000000000000000000000015F700CFFF1A624E5E497161BCC8A198F",
-        "8000000000000000000000000000000000000000000000000000000000000C96",
-        "3E1AF419A269A5F866A7D3C25C3DF80AE979259373FF2B182F49D4CE7E1BBC8B",
-        "0000000000000000000000000000000000000000000000000000000000000001",
-        "3FA8124359F96680B83D1C3EB2C070E5C545C9858D03ECFB744BF8D717717EFC",
-    ),
-    "GostR3410_2001_CryptoPro_C_ParamSet": (
-        "9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D759B",
-        "9B9F605F5A858107AB1EC85E6B41C8AA582CA3511EDDFB74F02F3A6598980BB9",
-        "9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D7598",
-        "000000000000000000000000000000000000000000000000000000000000805a",
-        "0000000000000000000000000000000000000000000000000000000000000000",
-        "41ECE55743711A8C3CBF3783CD08C0EE4D4DC440D4641A8F366E550DFDB3BB67",
-    ),
-    "GostR3410_2001_CryptoPro_XchA_ParamSet": (
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97",
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893",
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94",
-        "00000000000000000000000000000000000000000000000000000000000000a6",
-        "0000000000000000000000000000000000000000000000000000000000000001",
-        "8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14",
-    ),
-    "GostR3410_2001_CryptoPro_XchB_ParamSet": (
-        "9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D759B",
-        "9B9F605F5A858107AB1EC85E6B41C8AA582CA3511EDDFB74F02F3A6598980BB9",
-        "9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D7598",
-        "000000000000000000000000000000000000000000000000000000000000805a",
-        "0000000000000000000000000000000000000000000000000000000000000000",
-        "41ECE55743711A8C3CBF3783CD08C0EE4D4DC440D4641A8F366E550DFDB3BB67",
-    ),
-    "GostR3410_2012_TC26_ParamSetA": (
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7",
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275",
-        "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4",
-        "E8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760",
-        "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003",
-        "7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4",
-    ),
-    "GostR3410_2012_TC26_ParamSetB": (
-        "8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006F",
-        "800000000000000000000000000000000000000000000000000000000000000149A1EC142565A545ACFDB77BD9D40CFA8B996712101BEA0EC6346C54374F25BD",
-        "8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006C",
-        "687D1B459DC841457E3E06CF6F5E2517B97C7D614AF138BCBF85DC806C4B289F3E965D2DB1416D217F8B276FAD1AB69C50F78BEE1FA3106EFB8CCBC7C5140116",
-        "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002",
-        "1A8F7EDA389B094C2C071E3647A8940F3C123B697578C213BE6DD9E6C8EC7335DCB228FD1EDF4A39152CBCAAF8C0398828041055F94CEEEC7E21340780FE41BD"
-    ),
-}
-for c, params in CURVE_PARAMS.items():
-    CURVE_PARAMS[c] = [hexdec(param) for param in params]
+def point_size(point):
+    """Determine is it either 256 or 512 bit point
+    """
+    return (512 // 8) if point.bit_length() > 256 else (256 // 8)
 
 
 class GOST3410Curve(object):
 
 
 class GOST3410Curve(object):
-    """ GOST 34.10 validated curve
-
-    >>> p, q, a, b, x, y = CURVE_PARAMS["GostR3410_2001_TestParamSet"]
-    >>> curve = GOST3410Curve(p, q, a, b, x, y)
-    >>> priv = bytes2long(urandom(32))
-    >>> signature = sign(curve, priv, GOST341194(data).digest())
-    >>> pubX, pubY = public_key(curve, priv)
-    >>> verify(curve, pubX, pubY, GOST341194(data).digest(), signature)
+    """GOST 34.10 validated curve
+
+    >>> curve = CURVES["id-GostR3410-2001-TestParamSet"]
+    >>> prv = prv_unmarshal(urandom(32))
+    >>> signature = sign(curve, prv, GOST341194(data).digest())
+    >>> pub = public_key(curve, prv)
+    >>> verify(curve, pub, GOST341194(data).digest(), signature)
     True
     True
+
+    :param long p: characteristic of the underlying prime field
+    :param long q: elliptic curve subgroup order
+    :param long a, b: coefficients of the equation of the elliptic curve in
+                      the canonical form
+    :param long x, y: the coordinate of the point P (generator of the
+                      subgroup of order q) of the elliptic curve in
+                      the canonical form
+    :param long e, d: coefficients of the equation of the elliptic curve in
+                      the twisted Edwards form
     """
     """
-    def __init__(self, p, q, a, b, x, y):
-        self.p = bytes2long(p)
-        self.q = bytes2long(q)
-        self.a = bytes2long(a)
-        self.b = bytes2long(b)
-        self.x = bytes2long(x)
-        self.y = bytes2long(y)
+    def __init__(self, p, q, a, b, x, y, cofactor=1, e=None, d=None):
+        self.p = p
+        self.q = q
+        self.a = a
+        self.b = b
+        self.x = x
+        self.y = y
+        self.cofactor = cofactor
+        self.e = e
+        self.d = d
         r1 = self.y * self.y % self.p
         r2 = ((self.x * self.x + self.a) * self.x + self.b) % self.p
         r1 = self.y * self.y % self.p
         r2 = ((self.x * self.x + self.a) * self.x + self.b) % self.p
-        if r2 < 0:
-            r2 += self.p
-        if r1 != r2:
+        if r1 != self.pos(r2):
             raise ValueError("Invalid parameters")
             raise ValueError("Invalid parameters")
+        self._st = None
 
 
-    def _pos(self, v):
+    @property
+    def point_size(self):
+        return point_size(self.p)
+
+    def pos(self, v):
+        """Make positive number
+        """
         if v < 0:
             return v + self.p
         return v
         if v < 0:
             return v + self.p
         return v
@@ -149,11 +86,11 @@ class GOST3410Curve(object):
             # double
             t = ((3 * p1x * p1x + self.a) * modinvert(2 * p1y, self.p)) % self.p
         else:
             # double
             t = ((3 * p1x * p1x + self.a) * modinvert(2 * p1y, self.p)) % self.p
         else:
-            tx = self._pos(p2x - p1x) % self.p
-            ty = self._pos(p2y - p1y) % self.p
+            tx = self.pos(p2x - p1x) % self.p
+            ty = self.pos(p2y - p1y) % self.p
             t = (ty * modinvert(tx, self.p)) % self.p
             t = (ty * modinvert(tx, self.p)) % self.p
-        tx = self._pos(t * t - p1x - p2x) % self.p
-        ty = self._pos(t * (p1x - tx) - p1y) % self.p
+        tx = self.pos(t * t - p1x - p2x) % self.p
+        ty = self.pos(t * (p1x - tx) - p1y) % self.p
         return tx, ty
 
     def exp(self, degree, x=None, y=None):
         return tx, ty
 
     def exp(self, degree, x=None, y=None):
@@ -161,9 +98,9 @@ class GOST3410Curve(object):
         y = y or self.y
         tx = x
         ty = y
         y = y or self.y
         tx = x
         ty = y
-        degree -= 1
         if degree == 0:
             raise ValueError("Bad degree value")
         if degree == 0:
             raise ValueError("Bad degree value")
+        degree -= 1
         while degree != 0:
             if degree & 1 == 1:
                 tx, ty = self._add(tx, ty, x, y)
         while degree != 0:
             if degree & 1 == 1:
                 tx, ty = self._add(tx, ty, x, y)
@@ -171,69 +108,157 @@ class GOST3410Curve(object):
             x, y = self._add(x, y, x, y)
         return tx, ty
 
             x, y = self._add(x, y, x, y)
         return tx, ty
 
+    def st(self):
+        """Compute s/t parameters for twisted Edwards curve points conversion
+        """
+        if self.e is None or self.d is None:
+            raise ValueError("non twisted Edwards curve")
+        if self._st is not None:
+            return self._st
+        self._st = (
+            self.pos(self.e - self.d) * modinvert(4, self.p) % self.p,
+            (self.e + self.d) * modinvert(6, self.p) % self.p,
+        )
+        return self._st
 
 
-def public_key(curve, private_key):
-    """ Generate public key from the private one
 
 
-    :param GOST3410Curve curve: curve to use
-    :param long private_key: private key
-    :return: public key's parts, X and Y
-    :rtype: (long, long)
-    """
-    return curve.exp(private_key)
+CURVES = {
+    "GostR3410_2001_ParamSet_cc": GOST3410Curve(
+        p=bytes2long(hexdec("C0000000000000000000000000000000000000000000000000000000000003C7")),
+        q=bytes2long(hexdec("5fffffffffffffffffffffffffffffff606117a2f4bde428b7458a54b6e87b85")),
+        a=bytes2long(hexdec("C0000000000000000000000000000000000000000000000000000000000003c4")),
+        b=bytes2long(hexdec("2d06B4265ebc749ff7d0f1f1f88232e81632e9088fd44b7787d5e407e955080c")),
+        x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000002")),
+        y=bytes2long(hexdec("a20e034bf8813ef5c18d01105e726a17eb248b264ae9706f440bedc8ccb6b22c")),
+    ),
+    "id-GostR3410-2001-TestParamSet": GOST3410Curve(
+        p=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000431")),
+        q=bytes2long(hexdec("8000000000000000000000000000000150FE8A1892976154C59CFC193ACCF5B3")),
+        a=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000007")),
+        b=bytes2long(hexdec("5FBFF498AA938CE739B8E022FBAFEF40563F6E6A3472FC2A514C0CE9DAE23B7E")),
+        x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000002")),
+        y=bytes2long(hexdec("08E2A8A0E65147D4BD6316030E16D19C85C97F0A9CA267122B96ABBCEA7E8FC8")),
+    ),
+    "id-GostR3410-2001-CryptoPro-A-ParamSet": GOST3410Curve(
+        p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97")),
+        q=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF6C611070995AD10045841B09B761B893")),
+        a=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD94")),
+        b=bytes2long(hexdec("00000000000000000000000000000000000000000000000000000000000000a6")),
+        x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000001")),
+        y=bytes2long(hexdec("8D91E471E0989CDA27DF505A453F2B7635294F2DDF23E3B122ACC99C9E9F1E14")),
+    ),
+    "id-GostR3410-2001-CryptoPro-B-ParamSet": GOST3410Curve(
+        p=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000C99")),
+        q=bytes2long(hexdec("800000000000000000000000000000015F700CFFF1A624E5E497161BCC8A198F")),
+        a=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000C96")),
+        b=bytes2long(hexdec("3E1AF419A269A5F866A7D3C25C3DF80AE979259373FF2B182F49D4CE7E1BBC8B")),
+        x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000001")),
+        y=bytes2long(hexdec("3FA8124359F96680B83D1C3EB2C070E5C545C9858D03ECFB744BF8D717717EFC")),
+    ),
+    "id-GostR3410-2001-CryptoPro-C-ParamSet": GOST3410Curve(
+        p=bytes2long(hexdec("9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D759B")),
+        q=bytes2long(hexdec("9B9F605F5A858107AB1EC85E6B41C8AA582CA3511EDDFB74F02F3A6598980BB9")),
+        a=bytes2long(hexdec("9B9F605F5A858107AB1EC85E6B41C8AACF846E86789051D37998F7B9022D7598")),
+        b=bytes2long(hexdec("000000000000000000000000000000000000000000000000000000000000805a")),
+        x=bytes2long(hexdec("0000000000000000000000000000000000000000000000000000000000000000")),
+        y=bytes2long(hexdec("41ECE55743711A8C3CBF3783CD08C0EE4D4DC440D4641A8F366E550DFDB3BB67")),
+    ),
+    "id-tc26-gost-3410-2012-256-paramSetA": GOST3410Curve(
+        p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFD97")),
+        q=bytes2long(hexdec("400000000000000000000000000000000FD8CDDFC87B6635C115AF556C360C67")),
+        a=bytes2long(hexdec("C2173F1513981673AF4892C23035A27CE25E2013BF95AA33B22C656F277E7335")),
+        b=bytes2long(hexdec("295F9BAE7428ED9CCC20E7C359A9D41A22FCCD9108E17BF7BA9337A6F8AE9513")),
+        x=bytes2long(hexdec("91E38443A5E82C0D880923425712B2BB658B9196932E02C78B2582FE742DAA28")),
+        y=bytes2long(hexdec("32879423AB1A0375895786C4BB46E9565FDE0B5344766740AF268ADB32322E5C")),
+        cofactor=4,
+        e=0x01,
+        d=bytes2long(hexdec("0605F6B7C183FA81578BC39CFAD518132B9DF62897009AF7E522C32D6DC7BFFB")),
+    ),
+    "id-tc26-gost-3410-2012-512-paramSetTest": GOST3410Curve(
+        p=bytes2long(hexdec("4531ACD1FE0023C7550D267B6B2FEE80922B14B2FFB90F04D4EB7C09B5D2D15DF1D852741AF4704A0458047E80E4546D35B8336FAC224DD81664BBF528BE6373")),
+        q=bytes2long(hexdec("4531ACD1FE0023C7550D267B6B2FEE80922B14B2FFB90F04D4EB7C09B5D2D15DA82F2D7ECB1DBAC719905C5EECC423F1D86E25EDBE23C595D644AAF187E6E6DF")),
+        a=7,
+        b=bytes2long(hexdec("1CFF0806A31116DA29D8CFA54E57EB748BC5F377E49400FDD788B649ECA1AC4361834013B2AD7322480A89CA58E0CF74BC9E540C2ADD6897FAD0A3084F302ADC")),
+        x=bytes2long(hexdec("24D19CC64572EE30F396BF6EBBFD7A6C5213B3B3D7057CC825F91093A68CD762FD60611262CD838DC6B60AA7EEE804E28BC849977FAC33B4B530F1B120248A9A")),
+        y=bytes2long(hexdec("2BB312A43BD2CE6E0D020613C857ACDDCFBF061E91E5F2C3F32447C259F39B2C83AB156D77F1496BF7EB3351E1EE4E43DC1A18B91B24640B6DBB92CB1ADD371E")),
+    ),
+    "id-tc26-gost-3410-12-512-paramSetA": GOST3410Curve(
+        p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7")),
+        q=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF27E69532F48D89116FF22B8D4E0560609B4B38ABFAD2B85DCACDB1411F10B275")),
+        a=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC4")),
+        b=bytes2long(hexdec("E8C2505DEDFC86DDC1BD0B2B6667F1DA34B82574761CB0E879BD081CFD0B6265EE3CB090F30D27614CB4574010DA90DD862EF9D4EBEE4761503190785A71C760")),
+        x=bytes2long(hexdec("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003")),
+        y=bytes2long(hexdec("7503CFE87A836AE3A61B8816E25450E6CE5E1C93ACF1ABC1778064FDCBEFA921DF1626BE4FD036E93D75E6A50E3A41E98028FE5FC235F5B889A589CB5215F2A4")),
+    ),
+    "id-tc26-gost-3410-12-512-paramSetB": GOST3410Curve(
+        p=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006F")),
+        q=bytes2long(hexdec("800000000000000000000000000000000000000000000000000000000000000149A1EC142565A545ACFDB77BD9D40CFA8B996712101BEA0EC6346C54374F25BD")),
+        a=bytes2long(hexdec("8000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006C")),
+        b=bytes2long(hexdec("687D1B459DC841457E3E06CF6F5E2517B97C7D614AF138BCBF85DC806C4B289F3E965D2DB1416D217F8B276FAD1AB69C50F78BEE1FA3106EFB8CCBC7C5140116")),
+        x=bytes2long(hexdec("00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002")),
+        y=bytes2long(hexdec("1A8F7EDA389B094C2C071E3647A8940F3C123B697578C213BE6DD9E6C8EC7335DCB228FD1EDF4A39152CBCAAF8C0398828041055F94CEEEC7E21340780FE41BD")),
+    ),
+    "id-tc26-gost-3410-2012-512-paramSetC": GOST3410Curve(
+        p=bytes2long(hexdec("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDC7")),
+        q=bytes2long(hexdec("3FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC98CDBA46506AB004C33A9FF5147502CC8EDA9E7A769A12694623CEF47F023ED")),
+        a=bytes2long(hexdec("DC9203E514A721875485A529D2C722FB187BC8980EB866644DE41C68E143064546E861C0E2C9EDD92ADE71F46FCF50FF2AD97F951FDA9F2A2EB6546F39689BD3")),
+        b=bytes2long(hexdec("B4C4EE28CEBC6C2C8AC12952CF37F16AC7EFB6A9F69F4B57FFDA2E4F0DE5ADE038CBC2FFF719D2C18DE0284B8BFEF3B52B8CC7A5F5BF0A3C8D2319A5312557E1")),
+        x=bytes2long(hexdec("E2E31EDFC23DE7BDEBE241CE593EF5DE2295B7A9CBAEF021D385F7074CEA043AA27272A7AE602BF2A7B9033DB9ED3610C6FB85487EAE97AAC5BC7928C1950148")),
+        y=bytes2long(hexdec("F5CE40D95B5EB899ABBCCFF5911CB8577939804D6527378B8C108C3D2090FF9BE18E2D33E3021ED2EF32D85822423B6304F726AA854BAE07D0396E9A9ADDC40F")),
+        cofactor=4,
+        e=0x01,
+        d=bytes2long(hexdec("9E4F5D8C017D8D9F13A5CF3CDF5BFE4DAB402D54198E31EBDE28A0621050439CA6B39E0A515C06B304E2CE43E79E369E91A0CFC2BC2A22B4CA302DBB33EE7550")),
+    ),
+}
+CURVES["id-GostR3410-2001-CryptoPro-XchA-ParamSet"] = CURVES["id-GostR3410-2001-CryptoPro-A-ParamSet"]
+CURVES["id-GostR3410-2001-CryptoPro-XchB-ParamSet"] = CURVES["id-GostR3410-2001-CryptoPro-C-ParamSet"]
+CURVES["id-tc26-gost-3410-2012-256-paramSetB"] = CURVES["id-GostR3410-2001-CryptoPro-A-ParamSet"]
+CURVES["id-tc26-gost-3410-2012-256-paramSetC"] = CURVES["id-GostR3410-2001-CryptoPro-B-ParamSet"]
+CURVES["id-tc26-gost-3410-2012-256-paramSetD"] = CURVES["id-GostR3410-2001-CryptoPro-C-ParamSet"]
+DEFAULT_CURVE = CURVES["id-GostR3410-2001-CryptoPro-A-ParamSet"]
 
 
 
 
-def kek(curve, private_key, ukm, pubkey):
-    """ Make Diffie-Hellman computation
+def public_key(curve, prv):
+    """Generate public key from the private one
 
     :param GOST3410Curve curve: curve to use
 
     :param GOST3410Curve curve: curve to use
-    :param long private_key: private key
-    :param ukm: UKM value (VKO-factor)
-    :type ukm: bytes, 8 bytes
-    :param pubkey: public key's part
-    :type pubkey: (long, long)
-    :return: Key Encryption Key (shared key)
-    :rtype: bytes, 32 bytes
-
-    Shared Key Encryption Key computation is based on
-    :rfc:`4357` VKO GOST 34.10-2001 with little-endian
-    hash output.
+    :param long prv: private key
+    :returns: public key's parts, X and Y
+    :rtype: (long, long)
     """
     """
-    key = curve.exp(private_key, pubkey[0], pubkey[1])
-    key = curve.exp(bytes2long(24 * b"\x00" + ukm), key[0], key[1])
-    return GOST341194(
-        (long2bytes(key[1]) + long2bytes(key[0]))[::-1],
-        "GostR3411_94_CryptoProParamSet"
-    ).digest()[::-1]
+    return curve.exp(prv)
 
 
 
 
-def sign(curve, private_key, digest, size=SIZE_3410_2001):
-    """ Calculate signature for provided digest
+def sign(curve, prv, digest, rand=None):
+    """Calculate signature for provided digest
 
     :param GOST3410Curve curve: curve to use
 
     :param GOST3410Curve curve: curve to use
-    :param long private_key: private key
+    :param long prv: private key
     :param digest: digest for signing
     :param digest: digest for signing
-    :type digest: bytes, 32 bytes
-    :param size: signature size
-    :type size: 32 (for 34.10-2001) or 64 (for 34.10-2012)
-    :return: signature
-    :rtype: bytes, 64 bytes
+    :type digest: bytes, 32 or 64 bytes
+    :param rand: optional predefined random data used for k/r generation
+    :type rand: bytes, 32 or 64 bytes
+    :returns: signature, BE(S) || BE(R)
+    :rtype: bytes, 64 or 128 bytes
     """
     """
-    if len(digest) != size:
-        raise ValueError("Invalid digest length")
+    size = curve.point_size
     q = curve.q
     e = bytes2long(digest) % q
     if e == 0:
         e = 1
     while True:
     q = curve.q
     e = bytes2long(digest) % q
     if e == 0:
         e = 1
     while True:
-        k = bytes2long(urandom(size)) % q
+        if rand is None:
+            rand = urandom(size)
+        elif len(rand) != size:
+            raise ValueError("rand length != %d" % size)
+        k = bytes2long(rand) % q
         if k == 0:
             continue
         r, _ = curve.exp(k)
         r %= q
         if r == 0:
             continue
         if k == 0:
             continue
         r, _ = curve.exp(k)
         r %= q
         if r == 0:
             continue
-        d = private_key * r
+        d = prv * r
         k *= e
         s = (d + k) % q
         if s == 0:
         k *= e
         s = (d + k) % q
         if s == 0:
@@ -242,22 +267,18 @@ def sign(curve, private_key, digest, size=SIZE_3410_2001):
     return long2bytes(s, size) + long2bytes(r, size)
 
 
     return long2bytes(s, size) + long2bytes(r, size)
 
 
-def verify(curve, pubkeyX, pubkeyY, digest, signature, size=SIZE_3410_2001):
-    """ Verify provided digest with the signature
+def verify(curve, pub, digest, signature):
+    """Verify provided digest with the signature
 
     :param GOST3410Curve curve: curve to use
 
     :param GOST3410Curve curve: curve to use
-    :param long pubkeyX: public key's X
-    :param long pubkeyY: public key's Y
+    :type pub: (long, long)
     :param digest: digest needed to check
     :param digest: digest needed to check
-    :type digest: bytes, 32 bytes
+    :type digest: bytes, 32 or 64 bytes
     :param signature: signature to verify with
     :param signature: signature to verify with
-    :type signature: bytes, 64 bytes
-    :param size: signature size
-    :type size: 32 (for 34.10-2001) or 64 (for 34.10-2012)
+    :type signature: bytes, 64 or 128 bytes
     :rtype: bool
     """
     :rtype: bool
     """
-    if len(digest) != size:
-        raise ValueError("Invalid digest length")
+    size = curve.point_size
     if len(signature) != size * 2:
         raise ValueError("Invalid signature length")
     q = curve.q
     if len(signature) != size * 2:
         raise ValueError("Invalid signature length")
     q = curve.q
@@ -273,7 +294,7 @@ def verify(curve, pubkeyX, pubkeyY, digest, signature, size=SIZE_3410_2001):
     z1 = s * v % q
     z2 = q - r * v % q
     p1x, p1y = curve.exp(z1)
     z1 = s * v % q
     z2 = q - r * v % q
     p1x, p1y = curve.exp(z1)
-    q1x, q1y = curve.exp(z2, pubkeyX, pubkeyY)
+    q1x, q1y = curve.exp(z2, pub[0], pub[1])
     lm = q1x - p1x
     if lm < 0:
         lm += p
     lm = q1x - p1x
     if lm < 0:
         lm += p
@@ -288,3 +309,56 @@ def verify(curve, pubkeyX, pubkeyY, digest, signature, size=SIZE_3410_2001):
     lm %= q
     # This is not constant time comparison!
     return lm == r
     lm %= q
     # This is not constant time comparison!
     return lm == r
+
+
+def prv_unmarshal(prv):
+    """Unmarshal little-endian private key
+
+    :param bytes prv: serialized private key
+    :rtype: long
+    """
+    return bytes2long(prv[::-1])
+
+
+def pub_marshal(pub):
+    """Marshal public key
+
+    :type pub: (long, long)
+    :rtype: bytes
+    :returns: LE(X) || LE(Y)
+    """
+    size = point_size(pub[0])
+    return (long2bytes(pub[1], size) + long2bytes(pub[0], size))[::-1]
+
+
+def pub_unmarshal(pub):
+    """Unmarshal public key
+
+    :param pub: LE(X) || LE(Y)
+    :type pub: bytes
+    :rtype: (long, long)
+    """
+    size = len(pub) // 2
+    pub = pub[::-1]
+    return (bytes2long(pub[size:]), bytes2long(pub[:size]))
+
+
+def uv2xy(curve, u, v):
+    """Convert twisted Edwards curve U,V coordinates to Weierstrass X,Y
+    """
+    s, t = curve.st()
+    k1 = (s * (1 + v)) % curve.p
+    k2 = curve.pos(1 - v)
+    x = t + k1 * modinvert(k2, curve.p)
+    y = k1 * modinvert(u * k2, curve.p)
+    return x % curve.p, y % curve.p
+
+
+def xy2uv(curve, x, y):
+    """Convert Weierstrass X,Y coordinates to twisted Edwards curve U,V
+    """
+    s, t = curve.st()
+    xmt = curve.pos(x - t)
+    u = xmt * modinvert(y, curve.p)
+    v = curve.pos(xmt - s) * modinvert(xmt + s, curve.p)
+    return u % curve.p, v % curve.p