MikroTikPatch/toyecc/PubKeyOps.py

167 lines
5.7 KiB
Python
Raw Normal View History

2024-06-15 02:18:14 +03:00
#
# toyecc - A small Elliptic Curve Cryptography Demonstration.
# Copyright (C) 2011-2022 Johannes Bauer
#
# This file is part of toyecc.
#
# toyecc 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; this program is ONLY licensed under
# version 3 of the License, later versions are explicitly excluded.
#
# toyecc is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with toyecc; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# Johannes Bauer <JohannesBauer@gmx.de>
#
import hashlib
from .FieldElement import FieldElement
from .AffineCurvePoint import AffineCurvePoint
from .Random import secure_rand_int_between
from . import Tools
from .PrivKeyOps import PrivKeyOpLoad
from .ASN1 import parse_asn1_public_key
from .CurveDB import CurveDB
from .CurveQuirks import CurveQuirkSigningHashFunction
class PubKeyOpECDSAExploitReusedNonce(object):
def ecdsa_exploit_reused_nonce(self, msg1, sig1, msg2, sig2):
"""Given two different messages msg1 and msg2 and their corresponding
signatures sig1, sig2, try to calculate the private key that was used
for signing if during signature generation no unique nonces were
used."""
assert(isinstance(msg1, bytes))
assert(isinstance(msg2, bytes))
assert(msg1 != msg2)
assert(sig1.r == sig2.r)
# Hash the messages
dig1 = hashlib.new(sig1.hashalg)
dig1.update(msg1)
dig1 = dig1.digest()
dig2 = hashlib.new(sig2.hashalg)
dig2.update(msg2)
dig2 = dig2.digest()
# Calculate hashes of messages
e1 = Tools.ecdsa_msgdigest_to_int(dig1, self.point.curve.n)
e2 = Tools.ecdsa_msgdigest_to_int(dig2, self.point.curve.n)
# Take them modulo n
e1 = FieldElement(e1, self.point.curve.n)
e2 = FieldElement(e2, self.point.curve.n)
(s1, s2) = (FieldElement(sig1.s, self.point.curve.n), FieldElement(sig2.s, self.point.curve.n))
r = sig1.r
# Recover (supposedly) random nonce
nonce = (e1 - e2) // (s1 - s2)
# Recover private key
priv = ((nonce * s1) - e1) // r
return { "nonce": nonce, "privatekey": priv }
class PubKeyOpECDSAVerify(object):
def ecdsa_verify_hash(self, message_digest, signature):
"""Verify ECDSA signature over the hash of a message (the message
digest)."""
assert(isinstance(message_digest, bytes))
assert(0 < signature.r < self.curve.n)
assert(0 < signature.s < self.curve.n)
# Convert message digest to integer value
e = Tools.ecdsa_msgdigest_to_int(message_digest, self.curve.n)
(r, s) = (signature.r, FieldElement(signature.s, self.curve.n))
w = s.inverse()
u1 = int(e * w)
u2 = int(r * w)
pt = (u1 * self.curve.G) + (u2 * self.point)
x1 = int(pt.x) % self.curve.n
return x1 == r
def ecdsa_verify(self, message, signature):
"""Verify an ECDSA signature over a message."""
assert(isinstance(message, bytes))
digest_fnc = hashlib.new(signature.hashalg)
digest_fnc.update(message)
message_digest = digest_fnc.digest()
return self.ecdsa_verify_hash(message_digest, signature)
class PubKeyOpEDDSAVerify(object):
def eddsa_verify(self, message, signature):
"""Verify an EdDSA signature over a message."""
if not self.curve.has_quirk(CurveQuirkSigningHashFunction):
raise Exception("Unable to determine EdDSA signature function.")
quirk = self.curve.get_quirk(CurveQuirkSigningHashFunction)
h = Tools.bytestoint_le(quirk.hashdata(signature.R.eddsa_encode() + self.point.eddsa_encode() + message))
return (signature.s * self.curve.G) == signature.R + (h * self.point)
class PubKeyOpEDDSAEncode(object):
def eddsa_encode(self):
"""Encodes a EdDSA-encoded public key to its serialized (bytes)
form."""
return self.point.eddsa_encode()
@classmethod
def eddsa_decode(cls, curve, encoded_pubkey):
"""Decodes a EdDSA-encoded public key from its serialized (bytes)
form."""
pubkey = AffineCurvePoint.eddsa_decode(curve, encoded_pubkey)
return cls(pubkey)
class PubKeyOpECIESEncrypt(object):
def ecies_encrypt(self, r = None):
"""Generates a shared secret which can be used to symetrically encrypt
data that only the holder of the corresponding private key can read.
The output are two points, R and S: R is the public point that is
transmitted together with the message while S is the point which
resembles the shared secret. The receiver can use R together with her
private key to reconstruct S. A random nonce r can be supplied for this
function. If it isn't supplied, it is randomly chosen."""
# Chose a random number
if r is None:
r = secure_rand_int_between(1, self.curve.n - 1)
R = r * self.curve.G
S = r * self.point
# Return the publicly transmitted R and the symmetric key S
return { "R": R, "S": S }
class PubKeyOpLoad(object):
@classmethod
def load_derdata(cls, derdata):
"""Loads an EC public key from a DER-encoded ASN.1 bytes object."""
asn1 = parse_asn1_public_key(derdata)
curve = CurveDB().get_curve_from_asn1(asn1["algorithm"]["parameters"])
point = AffineCurvePoint.deserialize_uncompressed(Tools.bits_to_bytes(asn1["subjectPublicKey"]), curve)
return cls(point)
@classmethod
def load_pem(cls, pemfilename):
"""Loads an EC public key from a PEM-encoded 'PUBLIC KEY' file."""
return cls.load_derdata(Tools.load_pem_data(pemfilename, "PUBLIC KEY"))
@classmethod
def load_der(cls, derfilename):
"""Loads an EC public key from a DER-encoded ASN.1 file."""
with open(derfilename, "rb") as f:
data = f.read()
return cls.load_derdata(data)