From f4b9d19f2ece20aede7a5bb27facc914843e0d5d Mon Sep 17 00:00:00 2001 From: gstarovo Date: Thu, 15 Feb 2024 14:18:35 +0100 Subject: [PATCH 1/4] changes in ecc.py in encoding and decoding --- tlslite/utils/ecc.py | 56 ++++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/tlslite/utils/ecc.py b/tlslite/utils/ecc.py index 484362ea..48635d51 100644 --- a/tlslite/utils/ecc.py +++ b/tlslite/utils/ecc.py @@ -11,25 +11,23 @@ def decodeX962Point(data, curve=ecdsa.NIST256p): """Decode a point from a X9.62 encoding""" - parser = Parser(data) - encFormat = parser.get(1) - if encFormat != 4: - raise DecodeError("Not an uncompressed point encoding") - bytelength = getPointByteSize(curve) - xCoord = bytesToNumber(parser.getFixBytes(bytelength)) - yCoord = bytesToNumber(parser.getFixBytes(bytelength)) - if parser.getRemainingLength(): + try: + abstractPoint = ecdsa.ellipticcurve.AbstractPoint().from_bytes(curve.curve, data) + point = ecdsa.ellipticcurve.Point(curve.curve, + abstractPoint[0], + abstractPoint[1]).from_bytes(curve.curve, data) + except: raise DecodeError("Invalid length of point encoding for curve") - return ecdsa.ellipticcurve.Point(curve.curve, xCoord, yCoord) + + return point def encodeX962Point(point): """Encode a point in X9.62 format""" - bytelength = numBytes(point.curve().p()) + writer = Writer() writer.add(4, 1) - writer.bytes += numberToByteArray(point.x(), bytelength) - writer.bytes += numberToByteArray(point.y(), bytelength) + writer.bytes += point.to_bytes() return writer.bytes def getCurveByName(curveName): @@ -52,20 +50,22 @@ def getCurveByName(curveName): def getPointByteSize(point): """Convert the point or curve bit size to bytes""" - curveMap = {ecdsa.NIST256p.curve: 256//8, - ecdsa.NIST384p.curve: 384//8, - ecdsa.NIST521p.curve: (521+7)//8, - ecdsa.SECP256k1.curve: 256//8, - ecdsa.BRAINPOOLP256r1.curve: 256//8, - ecdsa.BRAINPOOLP384r1.curve: 384//8, - ecdsa.BRAINPOOLP512r1.curve: 512//8} - if ecdsaAllCurves: - curveMap[ecdsa.NIST224p.curve] = 224//8 - curveMap[ecdsa.NIST192p.curve] = 192//8 + print(len(point.to_bytes())) + return ecdsa.ellipticcurve.bytes_to_int(point.to_bytes()) + # curveMap = {ecdsa.NIST256p.curve: 256//8, + # ecdsa.NIST384p.curve: 384//8, + # ecdsa.NIST521p.curve: (521+7)//8, + # ecdsa.SECP256k1.curve: 256//8, + # ecdsa.BRAINPOOLP256r1.curve: 256//8, + # ecdsa.BRAINPOOLP384r1.curve: 384//8, + # ecdsa.BRAINPOOLP512r1.curve: 512//8} + # if ecdsaAllCurves: + # curveMap[ecdsa.NIST224p.curve] = 224//8 + # curveMap[ecdsa.NIST192p.curve] = 192//8 - if hasattr(point, 'curve'): - if callable(point.curve): - return curveMap[point.curve()] - else: - return curveMap[point.curve] - raise ValueError("Parameter must be a curve or point on curve") + # if hasattr(point, 'curve'): + # if callable(point.curve): + # return curveMap[point.curve()] + # else: + # return curveMap[point.curve] + # raise ValueError("Parameter must be a curve or point on curve") From 8baec4ce0f2a628120254a6b696fe85dcfe0fe4d Mon Sep 17 00:00:00 2001 From: gstarovo Date: Wed, 21 Feb 2024 13:19:18 +0100 Subject: [PATCH 2/4] deleted encode,decode function from ecc.py; corresponding changes implemented in keyexchnage.py --- tlslite/keyexchange.py | 19 ++-- tlslite/utils/ecc.py | 60 ++++------- unit_tests/test_tlslite_keyexchange.py | 3 +- unit_tests/test_tlslite_utils_ecc.py | 142 +------------------------ 4 files changed, 32 insertions(+), 192 deletions(-) diff --git a/tlslite/keyexchange.py b/tlslite/keyexchange.py index bea1644a..6bfbaa6f 100644 --- a/tlslite/keyexchange.py +++ b/tlslite/keyexchange.py @@ -13,8 +13,7 @@ from .messages import ServerKeyExchange, ClientKeyExchange, CertificateVerify from .constants import SignatureAlgorithm, HashAlgorithm, CipherSuite, \ ExtensionType, GroupName, ECCurveType, SignatureScheme -from .utils.ecc import decodeX962Point, encodeX962Point, getCurveByName, \ - getPointByteSize +from .utils.ecc import getCurveByName, getPointByteSize from .utils.rsakey import RSAKey from .utils.cryptomath import bytesToNumber, getRandomBytes, powMod, \ numBits, numberToByteArray, divceil, numBytes, secureHash @@ -23,7 +22,7 @@ from .utils.x25519 import x25519, x448, X25519_G, X448_G, X25519_ORDER_SIZE, \ X448_ORDER_SIZE from .utils.compat import int_types -from .utils.codec import DecodeError +from .utils.codec import DecodeError, Writer class KeyExchange(object): @@ -999,7 +998,7 @@ def get_random_private_key(self): return getRandomBytes(X448_ORDER_SIZE) else: curve = getCurveByName(GroupName.toStr(self.group)) - return ecdsa.util.randrange(curve.generator.order()) + return ecdsa.keys.SigningKey.generate(curve) def _get_fun_gen_size(self): """Return the function and generator for X25519/X448 KEX.""" @@ -1015,7 +1014,11 @@ def calc_public_value(self, private): return fun(private, generator) else: curve = getCurveByName(GroupName.toStr(self.group)) - return encodeX962Point(curve.generator * private) + point = curve.generator * private + writer = Writer() + writer.add(4, 1) + writer.bytes += point.to_bytes() + return writer.bytes def calc_shared_key(self, private, peer_share): """Calculate the shared key,""" @@ -1029,8 +1032,10 @@ def calc_shared_key(self, private, peer_share): else: curve = getCurveByName(GroupName.toRepr(self.group)) try: - ecdhYc = decodeX962Point(peer_share, - curve) + abstractPoint = ecdsa.ellipticcurve.AbstractPoint().from_bytes(curve.curve, peer_share) + ecdhYc = ecdsa.ellipticcurve.Point(curve.curve, + abstractPoint[0], + abstractPoint[1]).from_bytes(curve.curve, peer_share) except (AssertionError, DecodeError): raise TLSIllegalParameterException("Invalid ECC point") diff --git a/tlslite/utils/ecc.py b/tlslite/utils/ecc.py index 48635d51..1d2d1111 100644 --- a/tlslite/utils/ecc.py +++ b/tlslite/utils/ecc.py @@ -5,31 +5,9 @@ from .codec import Parser, Writer, DecodeError from .cryptomath import bytesToNumber, numberToByteArray, numBytes -from .compat import ecdsaAllCurves +from .compat import ecdsaAllCurves, bytes_to_int import ecdsa - -def decodeX962Point(data, curve=ecdsa.NIST256p): - """Decode a point from a X9.62 encoding""" - try: - abstractPoint = ecdsa.ellipticcurve.AbstractPoint().from_bytes(curve.curve, data) - point = ecdsa.ellipticcurve.Point(curve.curve, - abstractPoint[0], - abstractPoint[1]).from_bytes(curve.curve, data) - except: - raise DecodeError("Invalid length of point encoding for curve") - - return point - - -def encodeX962Point(point): - """Encode a point in X9.62 format""" - - writer = Writer() - writer.add(4, 1) - writer.bytes += point.to_bytes() - return writer.bytes - def getCurveByName(curveName): """Return curve identified by curveName""" curveMap = {'secp256r1':ecdsa.NIST256p, @@ -50,22 +28,20 @@ def getCurveByName(curveName): def getPointByteSize(point): """Convert the point or curve bit size to bytes""" - print(len(point.to_bytes())) - return ecdsa.ellipticcurve.bytes_to_int(point.to_bytes()) - # curveMap = {ecdsa.NIST256p.curve: 256//8, - # ecdsa.NIST384p.curve: 384//8, - # ecdsa.NIST521p.curve: (521+7)//8, - # ecdsa.SECP256k1.curve: 256//8, - # ecdsa.BRAINPOOLP256r1.curve: 256//8, - # ecdsa.BRAINPOOLP384r1.curve: 384//8, - # ecdsa.BRAINPOOLP512r1.curve: 512//8} - # if ecdsaAllCurves: - # curveMap[ecdsa.NIST224p.curve] = 224//8 - # curveMap[ecdsa.NIST192p.curve] = 192//8 - - # if hasattr(point, 'curve'): - # if callable(point.curve): - # return curveMap[point.curve()] - # else: - # return curveMap[point.curve] - # raise ValueError("Parameter must be a curve or point on curve") + curveMap = {ecdsa.NIST256p.curve: 256//8, + ecdsa.NIST384p.curve: 384//8, + ecdsa.NIST521p.curve: (521+7)//8, + ecdsa.SECP256k1.curve: 256//8, + ecdsa.BRAINPOOLP256r1.curve: 256//8, + ecdsa.BRAINPOOLP384r1.curve: 384//8, + ecdsa.BRAINPOOLP512r1.curve: 512//8} + if ecdsaAllCurves: + curveMap[ecdsa.NIST224p.curve] = 224//8 + curveMap[ecdsa.NIST192p.curve] = 192//8 + + if hasattr(point, 'curve'): + if callable(point.curve): + return curveMap[point.curve()] + else: + return curveMap[point.curve] + raise ValueError("Parameter must be a curve or point on curve") diff --git a/unit_tests/test_tlslite_keyexchange.py b/unit_tests/test_tlslite_keyexchange.py index 755e4f09..92eca0f6 100644 --- a/unit_tests/test_tlslite_keyexchange.py +++ b/unit_tests/test_tlslite_keyexchange.py @@ -34,8 +34,7 @@ from tlslite.handshakehashes import HandshakeHashes from tlslite import VerifierDB from tlslite.extensions import SupportedGroupsExtension, SNIExtension -from tlslite.utils.ecc import getCurveByName, decodeX962Point, encodeX962Point,\ - getPointByteSize +from tlslite.utils.ecc import getCurveByName, getPointByteSize from tlslite.utils.compat import a2b_hex import ecdsa from operator import mul diff --git a/unit_tests/test_tlslite_utils_ecc.py b/unit_tests/test_tlslite_utils_ecc.py index 4c0acb50..7e3d4f3d 100644 --- a/unit_tests/test_tlslite_utils_ecc.py +++ b/unit_tests/test_tlslite_utils_ecc.py @@ -10,149 +10,9 @@ except ImportError: import unittest -from tlslite.utils.ecc import decodeX962Point, encodeX962Point, getCurveByName,\ - getPointByteSize +from tlslite.utils.ecc import getCurveByName,getPointByteSize import ecdsa -class TestEncoder(unittest.TestCase): - def test_encode_P_256_point(self): - point = ecdsa.NIST256p.generator * 200 - - self.assertEqual(encodeX962Point(point), - bytearray(b'\x04' - # x coordinate - b'\x3a\x53\x5b\xd0\xbe\x46\x6f\xf3\xd8\x56' - b'\xa0\x77\xaa\xd9\x50\x4f\x16\xaa\x5d\x52' - b'\x28\xfc\xd7\xc2\x77\x48\x85\xee\x21\x3f' - b'\x3b\x34' - # y coordinate - b'\x66\xab\xa8\x18\x5b\x33\x41\xe0\xc2\xe3' - b'\xd1\xb3\xae\x69\xe4\x7d\x0f\x01\xd4\xbb' - b'\xd7\x06\xd9\x57\x8b\x0b\x65\xd6\xd3\xde' - b'\x1e\xfe' - )) - - def test_encode_P_256_point_with_zero_first_byte_on_x(self): - point = ecdsa.NIST256p.generator * 379 - - self.assertEqual(encodeX962Point(point), - bytearray(b'\x04' - b'\x00\x55\x43\x89\x4a\xf3\xd0\x0e\xd7\xd7' - b'\x40\xab\xdb\xd7\x5c\x96\xb0\x68\x77\xb7' - b'\x87\xdb\x5f\x70\xee\xa7\x8b\x90\xa8\xd7' - b'\xc0\x0a' - b'\xbb\x4c\x85\xa3\xd8\xea\x29\xef\xaa\xfa' - b'\x24\x40\x69\x12\xdd\x84\xd5\xb1\x4d\xc3' - b'\x2b\xf6\x56\xef\x6c\x6b\xd5\x8a\x5d\x94' - b'\x3f\x92' - )) - - def test_encode_P_256_point_with_zero_first_byte_on_y(self): - point = ecdsa.NIST256p.generator * 43 - - self.assertEqual(encodeX962Point(point), - bytearray(b'\x04' - b'\x98\x6a\xe2\x50\x6f\x1f\xf1\x04\xd0\x42' - b'\x30\x86\x1d\x8f\x4b\x49\x8f\x4b\xc4\xc6' - b'\xd0\x09\xb3\x0f\x75\x44\xdc\x12\x9b\x82' - b'\xd2\x8d' - b'\x00\x3c\xcc\xc0\xa6\x46\x0e\x0a\xe3\x28' - b'\xa4\xd9\x7d\x3c\x7b\x61\xd8\x6f\xc6\x28' - b'\x9c\x18\x9f\x25\x25\x11\x0c\x44\x1b\xb0' - b'\x7e\x97' - )) - - def test_encode_P_256_point_with_two_zero_first_bytes_on_x(self): - point = ecdsa.NIST256p.generator * 40393 - - self.assertEqual(encodeX962Point(point), - bytearray(b'\x04' - b'\x00\x00\x3f\x5f\x17\x8a\xa0\x70\x6c\x42' - b'\x31\xeb\x6e\x54\x95\xaa\x16\x42\xc5\xb8' - b'\xa9\x94\x12\x7c\x89\x46\x5f\x22\x99\x4a' - b'\x42\xf9' - b'\xc2\x48\xb3\x37\x59\x9f\x0c\x2f\x29\x77' - b'\x2e\x25\x6f\x1d\x55\x49\xc8\x9b\xa9\xe5' - b'\x73\x13\x82\xcd\x1e\x3c\xc0\x9d\x10\xd0' - b'\x0b\x55')) - - def test_encode_P_521_point(self): - point = ecdsa.NIST521p.generator * 200 - - self.assertEqual(encodeX962Point(point), - bytearray(b'\x04' - b'\x00\x3e\x2a\x2f\x9f\xd5\x9f\xc3\x8d\xfb' - b'\xde\x77\x26\xa0\xbf\xc6\x48\x2a\x6b\x2a' - b'\x86\xf6\x29\xb8\x34\xa0\x6c\x3d\x66\xcd' - b'\x79\x8d\x9f\x86\x2e\x89\x31\xf7\x10\xc7' - b'\xce\x89\x15\x9f\x35\x8b\x4a\x5c\x5b\xb3' - b'\xd2\xcc\x9e\x1b\x6e\x94\x36\x23\x6d\x7d' - b'\x6a\x5e\x00\xbc\x2b\xbe' - b'\x01\x56\x7a\x41\xcb\x48\x8d\xca\xd8\xe6' - b'\x3a\x3f\x95\xb0\x8a\xf6\x99\x2a\x69\x6a' - b'\x37\xdf\xc6\xa1\x93\xff\xbc\x3f\x91\xa2' - b'\x96\xf3\x3c\x66\x15\x57\x3c\x1c\x06\x7f' - b'\x0a\x06\x4d\x18\xbd\x0c\x81\x4e\xf7\x2a' - b'\x8f\x76\xf8\x7f\x9b\x7d\xff\xb2\xf4\x26' - b'\x36\x43\x43\x86\x11\x89')) - -class TestDecoder(unittest.TestCase): - def test_decode_P_256_point(self): - point = ecdsa.NIST256p.generator * 379 - data = bytearray(b'\x04' - b'\x00\x55\x43\x89\x4a\xf3\xd0\x0e\xd7\xd7' - b'\x40\xab\xdb\xd7\x5c\x96\xb0\x68\x77\xb7' - b'\x87\xdb\x5f\x70\xee\xa7\x8b\x90\xa8\xd7' - b'\xc0\x0a' - b'\xbb\x4c\x85\xa3\xd8\xea\x29\xef\xaa\xfa' - b'\x24\x40\x69\x12\xdd\x84\xd5\xb1\x4d\xc3' - b'\x2b\xf6\x56\xef\x6c\x6b\xd5\x8a\x5d\x94' - b'\x3f\x92' - ) - - decoded_point = decodeX962Point(data, ecdsa.NIST256p) - - self.assertEqual(point, decoded_point) - - def test_decode_P_521_point(self): - - data = bytearray(b'\x04' - b'\x01\x7d\x8a\x5d\x11\x03\x4a\xaf\x01\x26' - b'\x5f\x2d\xd6\x2d\x76\xeb\xd8\xbe\x4e\xfb' - b'\x3b\x4b\xd2\x05\x5a\xed\x4c\x6d\x20\xc7' - b'\xf3\xd7\x08\xab\x21\x9e\x34\xfd\x14\x56' - b'\x3d\x47\xd0\x02\x65\x15\xc2\xdd\x2d\x60' - b'\x66\xf9\x15\x64\x55\x7a\xae\x56\xa6\x7a' - b'\x28\x51\x65\x26\x5c\xcc' - b'\x01\xd4\x19\x56\xfa\x14\x6a\xdb\x83\x1c' - b'\xb6\x1a\xc4\x4b\x40\xb1\xcb\xcc\x9e\x4f' - b'\x57\x2c\xb2\x72\x70\xb9\xef\x38\x15\xae' - b'\x87\x1f\x85\x40\x94\xda\x69\xed\x97\xeb' - b'\xdc\x72\x25\x25\x61\x76\xb2\xde\xed\xa2' - b'\xb0\x5c\xca\xc4\x83\x8f\xfb\x54\xae\xe0' - b'\x07\x45\x0b\xbf\x7c\xfc') - - point = decodeX962Point(data, ecdsa.NIST521p) - self.assertIsNotNone(point) - - self.assertEqual(encodeX962Point(point), data) - - def test_decode_with_missing_data(self): - data = bytearray(b'\x04' - b'\x00\x55\x43\x89\x4a\xf3\xd0\x0e\xd7\xd7' - b'\x40\xab\xdb\xd7\x5c\x96\xb0\x68\x77\xb7' - b'\x87\xdb\x5f\x70\xee\xa7\x8b\x90\xa8\xd7' - b'\xc0\x0a' - b'\xbb\x4c\x85\xa3\xd8\xea\x29\xef\xaa\xfa' - b'\x24\x40\x69\x12\xdd\x84\xd5\xb1\x4d\xc3' - b'\x2b\xf6\x56\xef\x6c\x6b\xd5\x8a\x5d\x94' - #b'\x3f\x92' - ) - - # XXX will change later as decoder in tlslite-ng needs to be updated - with self.assertRaises(SyntaxError): - decodeX962Point(data, ecdsa.NIST256p) - class TestCurveLookup(unittest.TestCase): def test_with_correct_name(self): curve = getCurveByName('secp256r1') From 74c6fc54a710edbcb89ba16db13c2593c43f4c64 Mon Sep 17 00:00:00 2001 From: gstarovo Date: Wed, 21 Feb 2024 13:37:38 +0100 Subject: [PATCH 3/4] cleaned unused imports in ecc.py --- tlslite/utils/ecc.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tlslite/utils/ecc.py b/tlslite/utils/ecc.py index 1d2d1111..1b026859 100644 --- a/tlslite/utils/ecc.py +++ b/tlslite/utils/ecc.py @@ -3,10 +3,9 @@ # See the LICENSE file for legal information regarding use of this file. """Methods for dealing with ECC points""" -from .codec import Parser, Writer, DecodeError -from .cryptomath import bytesToNumber, numberToByteArray, numBytes -from .compat import ecdsaAllCurves, bytes_to_int import ecdsa +from .compat import ecdsaAllCurves + def getCurveByName(curveName): """Return curve identified by curveName""" From 4a974e52b2ad7b512e76f224e308fb536db05b09 Mon Sep 17 00:00:00 2001 From: gstarovo Date: Wed, 21 Feb 2024 16:37:29 +0100 Subject: [PATCH 4/4] generating of private key in keyexchange.py returned to the previous state --- tlslite/keyexchange.py | 2 +- unit_tests/test_tlslite_keyexchange.py | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/tlslite/keyexchange.py b/tlslite/keyexchange.py index 6bfbaa6f..2f8b287b 100644 --- a/tlslite/keyexchange.py +++ b/tlslite/keyexchange.py @@ -998,7 +998,7 @@ def get_random_private_key(self): return getRandomBytes(X448_ORDER_SIZE) else: curve = getCurveByName(GroupName.toStr(self.group)) - return ecdsa.keys.SigningKey.generate(curve) + return ecdsa.util.randrange(curve.generator.order()) def _get_fun_gen_size(self): """Return the function and generator for X25519/X448 KEX.""" diff --git a/unit_tests/test_tlslite_keyexchange.py b/unit_tests/test_tlslite_keyexchange.py index 92eca0f6..73666550 100644 --- a/unit_tests/test_tlslite_keyexchange.py +++ b/unit_tests/test_tlslite_keyexchange.py @@ -27,7 +27,7 @@ from tlslite.x509 import X509 from tlslite.x509certchain import X509CertChain from tlslite.utils.keyfactory import parsePEMKey -from tlslite.utils.codec import Parser +from tlslite.utils.codec import Parser, Writer from tlslite.utils.cryptomath import bytesToNumber, getRandomBytes, powMod, \ numberToByteArray, isPrime, numBits from tlslite.mathtls import makeX, makeU, makeK, goodGroupParameters @@ -1952,8 +1952,15 @@ def test_ECDHE_key_exchange(self): curve = getCurveByName(curveName) generator = curve.generator cln_Xc = ecdsa.util.randrange(generator.order()) - cln_Ys = decodeX962Point(srv_key_ex.ecdh_Ys, curve) - cln_Yc = encodeX962Point(generator * cln_Xc) + abstractPoint = ecdsa.ellipticcurve.AbstractPoint().from_bytes(curve.curve, srv_key_ex.ecdh_Ys) + cln_Ys = ecdsa.ellipticcurve.Point(curve.curve, + abstractPoint[0], + abstractPoint[1]).from_bytes(curve.curve, srv_key_ex.ecdh_Ys) + point = generator * cln_Xc + writer = Writer() + writer.add(4, 1) + writer.bytes += point.to_bytes() + cln_Yc = writer.bytes cln_key_ex = ClientKeyExchange(self.cipher_suite, (3, 3)) cln_key_ex.createECDH(cln_Yc) @@ -1980,9 +1987,12 @@ def test_ECDHE_key_exchange_with_invalid_CKE(self): curve = getCurveByName(curveName) generator = curve.generator cln_Xc = ecdsa.util.randrange(generator.order()) - cln_Ys = decodeX962Point(srv_key_ex.ecdh_Ys, curve) - cln_Yc = encodeX962Point(generator * cln_Xc) - + point = generator * cln_Xc + writer = Writer() + writer.add(4, 1) + writer.bytes += point.to_bytes() + cln_Yc = writer.bytes + cln_key_ex = ClientKeyExchange(self.cipher_suite, (3, 3)) cln_key_ex.createECDH(cln_Yc)