diff --git a/diff-instrumental.py b/diff-instrumental.py index 1bf1d935..d8fe376a 100644 --- a/diff-instrumental.py +++ b/diff-instrumental.py @@ -10,8 +10,8 @@ argv = sys.argv[1:] opts, args = getopt.getopt( - argv, "s:r:", - ["fail-under=", "max-difference=", "save=", "read="]) + argv, "s:r:", ["fail-under=", "max-difference=", "save=", "read="] +) if args: raise ValueError("Unexpected parameters: {0}".format(args)) for opt, arg in opts: @@ -20,9 +20,9 @@ elif opt == "-r" or opt == "--read": read_location = arg elif opt == "--fail-under": - fail_under = float(arg)/100.0 + fail_under = float(arg) / 100.0 elif opt == "--max-difference": - max_difference = float(arg)/100.0 + max_difference = float(arg) / 100.0 else: raise ValueError("Unknown option: {0}".format(opt)) @@ -34,7 +34,7 @@ continue fields = line.split() - hit, count = fields[1].split('/') + hit, count = fields[1].split("/") total_hits += int(hit) total_count += int(count) @@ -43,16 +43,16 @@ if read_location: with open(read_location, "r") as f: old_coverage = float(f.read()) - print("Old coverage: {0:6.2f}%".format(old_coverage*100)) + print("Old coverage: {0:6.2f}%".format(old_coverage * 100)) if save_location: with open(save_location, "w") as f: f.write("{0:1.40f}".format(coverage)) -print("Coverage: {0:6.2f}%".format(coverage*100)) +print("Coverage: {0:6.2f}%".format(coverage * 100)) if read_location: - print("Difference: {0:6.2f}%".format((old_coverage - coverage)*100)) + print("Difference: {0:6.2f}%".format((old_coverage - coverage) * 100)) if fail_under and coverage < fail_under: print("ERROR: Insufficient coverage.", file=sys.stderr) diff --git a/setup.py b/setup.py index a6abc4ec..f0ef9a6c 100755 --- a/setup.py +++ b/setup.py @@ -14,35 +14,33 @@ with io.open(readme_path, encoding="utf-8") as read_file: long_description = read_file.read() -setup(name="ecdsa", - version=versioneer.get_version(), - description="ECDSA cryptographic signature library (pure python)", - long_description=long_description, - long_description_content_type='text/markdown', - author="Brian Warner", - author_email="warner@lothar.com", - url="http://github.com/warner/python-ecdsa", - packages=["ecdsa"], - package_dir={"": "src"}, - license="MIT", - cmdclass=commands, - python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*", - classifiers=[ - "Programming Language :: Python", - "Programming Language :: Python :: 2", - "Programming Language :: Python :: 2.6", - "Programming Language :: Python :: 2.7", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.3", - "Programming Language :: Python :: 3.4", - "Programming Language :: Python :: 3.5", - "Programming Language :: Python :: 3.6", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - ], - install_requires=['six>=1.9.0'], - extras_require={ - 'gmpy2': 'gmpy2', - 'gmpy': 'gmpy', - }, - ) +setup( + name="ecdsa", + version=versioneer.get_version(), + description="ECDSA cryptographic signature library (pure python)", + long_description=long_description, + long_description_content_type="text/markdown", + author="Brian Warner", + author_email="warner@lothar.com", + url="http://github.com/warner/python-ecdsa", + packages=["ecdsa"], + package_dir={"": "src"}, + license="MIT", + cmdclass=commands, + python_requires=">=2.6, !=3.0.*, !=3.1.*, !=3.2.*", + classifiers=[ + "Programming Language :: Python", + "Programming Language :: Python :: 2", + "Programming Language :: Python :: 2.6", + "Programming Language :: Python :: 2.7", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.3", + "Programming Language :: Python :: 3.4", + "Programming Language :: Python :: 3.5", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + ], + install_requires=["six>=1.9.0"], + extras_require={"gmpy2": "gmpy2", "gmpy": "gmpy"}, +) diff --git a/speed.py b/speed.py index bb9bf1e8..19132e0d 100644 --- a/speed.py +++ b/speed.py @@ -2,28 +2,42 @@ import timeit from ecdsa.curves import curves + def do(setup_statements, statement): # extracted from timeit.py - t = timeit.Timer(stmt=statement, - setup="\n".join(setup_statements)) + t = timeit.Timer(stmt=statement, setup="\n".join(setup_statements)) # determine number so that 0.2 <= total time < 2.0 for i in range(1, 10): - number = 10**i + number = 10 ** i x = t.timeit(number) if x >= 0.2: break return x / number + prnt_form = ( "{name:>16}{sep:1} {siglen:>6} {keygen:>9{form}}{unit:1} " "{keygen_inv:>9{form_inv}} {sign:>9{form}}{unit:1} " "{sign_inv:>9{form_inv}} {verify:>9{form}}{unit:1} " - "{verify_inv:>9{form_inv}}") + "{verify_inv:>9{form_inv}}" +) -print(prnt_form.format(siglen="siglen", keygen="keygen", keygen_inv="keygen/s", - sign="sign", sign_inv="sign/s", verify="verify", - verify_inv="verify/s", name="", sep="", unit="", - form="", form_inv="")) +print( + prnt_form.format( + siglen="siglen", + keygen="keygen", + keygen_inv="keygen/s", + sign="sign", + sign_inv="sign/s", + verify="verify", + verify_inv="verify/s", + name="", + sep="", + unit="", + form="", + form_inv="", + ) +) for curve in [i.name for i in curves]: S1 = "import six; from ecdsa import SigningKey, %s" % curve @@ -38,24 +52,44 @@ def do(setup_statements, statement): # were changed to lazily calculate vk, we'd need to change this # benchmark to loop over S5 instead of S2 keygen = do([S1], S2) - sign = do([S1,S2,S3], S4) - verf = do([S1,S2,S3,S4,S5,S6], S7) + sign = do([S1, S2, S3], S4) + verf = do([S1, S2, S3, S4, S5, S6], S7) import ecdsa + c = getattr(ecdsa, curve) sig = ecdsa.SigningKey.generate(c).sign(six.b("msg")) - print(prnt_form.format( - name=curve, sep=":", siglen=len(sig), unit="s", keygen=keygen, - keygen_inv=1.0/keygen, sign=sign, sign_inv=1.0/sign, verify=verf, - verify_inv=1.0/verf, form=".5f", form_inv=".2f")) + print( + prnt_form.format( + name=curve, + sep=":", + siglen=len(sig), + unit="s", + keygen=keygen, + keygen_inv=1.0 / keygen, + sign=sign, + sign_inv=1.0 / sign, + verify=verf, + verify_inv=1.0 / verf, + form=".5f", + form_inv=".2f", + ) + ) -print('') +print("") -ecdh_form = ( - "{name:>16}{sep:1} {ecdh:>9{form}}{unit:1} " - "{ecdh_inv:>9{form_inv}}") +ecdh_form = "{name:>16}{sep:1} {ecdh:>9{form}}{unit:1} {ecdh_inv:>9{form_inv}}" -print(ecdh_form.format(ecdh="ecdh", ecdh_inv="ecdh/s", name="", sep="", - unit="", form="", form_inv="")) +print( + ecdh_form.format( + ecdh="ecdh", + ecdh_inv="ecdh/s", + name="", + sep="", + unit="", + form="", + form_inv="", + ) +) for curve in [i.name for i in curves]: S1 = "from ecdsa import SigningKey, ECDH, {0}".format(curve) @@ -64,6 +98,14 @@ def do(setup_statements, statement): S4 = "ecdh = ECDH(private_key=our, public_key=remote)" S5 = "ecdh.generate_sharedsecret_bytes()" ecdh = do([S1, S2, S3, S4], S5) - print(ecdh_form.format( - name=curve, sep=":", unit="s", form=".5f", form_inv=".2f", - ecdh=ecdh, ecdh_inv=1.0/ecdh)) + print( + ecdh_form.format( + name=curve, + sep=":", + unit="s", + form=".5f", + form_inv=".2f", + ecdh=ecdh, + ecdh_inv=1.0 / ecdh, + ) + ) diff --git a/src/ecdsa/__init__.py b/src/ecdsa/__init__.py index eef5fe38..4ae0a114 100644 --- a/src/ecdsa/__init__.py +++ b/src/ecdsa/__init__.py @@ -1,25 +1,76 @@ -from .keys import SigningKey, VerifyingKey, BadSignatureError, BadDigestError,\ - MalformedPointError -from .curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p,\ - SECP256k1, BRAINPOOLP160r1, BRAINPOOLP192r1, BRAINPOOLP224r1,\ - BRAINPOOLP256r1, BRAINPOOLP320r1, BRAINPOOLP384r1, BRAINPOOLP512r1 -from .ecdh import ECDH, NoKeyError, NoCurveError, InvalidCurveError, \ - InvalidSharedSecretError +from .keys import ( + SigningKey, + VerifyingKey, + BadSignatureError, + BadDigestError, + MalformedPointError, +) +from .curves import ( + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + SECP256k1, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, +) +from .ecdh import ( + ECDH, + NoKeyError, + NoCurveError, + InvalidCurveError, + InvalidSharedSecretError, +) from .der import UnexpectedDER # This code comes from http://github.com/warner/python-ecdsa from ._version import get_versions -__version__ = get_versions()['version'] + +__version__ = get_versions()["version"] del get_versions -__all__ = ["curves", "der", "ecdsa", "ellipticcurve", "keys", "numbertheory", - "test_pyecdsa", "util", "six"] +__all__ = [ + "curves", + "der", + "ecdsa", + "ellipticcurve", + "keys", + "numbertheory", + "test_pyecdsa", + "util", + "six", +] -_hush_pyflakes = [SigningKey, VerifyingKey, BadSignatureError, BadDigestError, - MalformedPointError, UnexpectedDER, InvalidCurveError, - NoKeyError, InvalidSharedSecretError, ECDH, NoCurveError, - NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1, - BRAINPOOLP160r1, BRAINPOOLP192r1, BRAINPOOLP224r1, - BRAINPOOLP256r1, BRAINPOOLP320r1, BRAINPOOLP384r1, - BRAINPOOLP512r1] +_hush_pyflakes = [ + SigningKey, + VerifyingKey, + BadSignatureError, + BadDigestError, + MalformedPointError, + UnexpectedDER, + InvalidCurveError, + NoKeyError, + InvalidSharedSecretError, + ECDH, + NoCurveError, + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + SECP256k1, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, +] del _hush_pyflakes diff --git a/src/ecdsa/_compat.py b/src/ecdsa/_compat.py index 965d8c47..d97c7649 100644 --- a/src/ecdsa/_compat.py +++ b/src/ecdsa/_compat.py @@ -14,6 +14,7 @@ def str_idx_as_int(string, index): if sys.version_info < (3, 0): + def normalise_bytes(buffer_object): """Cast the input into array of bytes.""" # flake8 runs on py3 where `buffer` indeed doesn't exist... @@ -22,6 +23,7 @@ def normalise_bytes(buffer_object): def hmac_compat(ret): return ret + else: if sys.version_info < (3, 4): # on python 3.3 hmac.hmac.update() accepts only bytes, on newer @@ -30,10 +32,12 @@ def hmac_compat(data): if not isinstance(data, bytes): return bytes(data) return data + else: + def hmac_compat(data): return data def normalise_bytes(buffer_object): """Cast the input into array of bytes.""" - return memoryview(buffer_object).cast('B') + return memoryview(buffer_object).cast("B") diff --git a/src/ecdsa/_rwlock.py b/src/ecdsa/_rwlock.py index e4ef78dc..010e4981 100644 --- a/src/ecdsa/_rwlock.py +++ b/src/ecdsa/_rwlock.py @@ -66,6 +66,7 @@ def writer_release(self): class _LightSwitch: """An auxiliary "light switch"-like object. The first thread turns on the "switch", the last one turns it off (see [1, sec. 4.2.2] for details).""" + def __init__(self): self.__counter = 0 self.__mutex = threading.Lock() diff --git a/src/ecdsa/_version.py b/src/ecdsa/_version.py index a539b3a2..a72288a9 100644 --- a/src/ecdsa/_version.py +++ b/src/ecdsa/_version.py @@ -1,4 +1,3 @@ - # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build @@ -58,17 +57,20 @@ class NotThisMethod(Exception): def register_vcs_handler(vcs, method): # decorator """Decorator to mark a method as the handler for a particular VCS.""" + def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f + return decorate -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, - env=None): +def run_command( + commands, args, cwd=None, verbose=False, hide_stderr=False, env=None +): """Call the given command(s).""" assert isinstance(commands, list) p = None @@ -76,10 +78,13 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) + p = subprocess.Popen( + [c] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None), + ) break except EnvironmentError: e = sys.exc_info()[1] @@ -116,16 +121,22 @@ def versions_from_parentdir(parentdir_prefix, root, verbose): for i in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} + return { + "version": dirname[len(parentdir_prefix) :], + "full-revisionid": None, + "dirty": False, + "error": None, + "date": None, + } else: rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: - print("Tried directories %s but none started with prefix %s" % - (str(rootdirs), parentdir_prefix)) + print( + "Tried directories %s but none started with prefix %s" + % (str(rootdirs), parentdir_prefix) + ) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") @@ -181,7 +192,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d @@ -190,7 +201,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r'\d', r)]) + tags = set([r for r in refs if re.search(r"\d", r)]) if verbose: print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: @@ -198,19 +209,26 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] + r = ref[len(tag_prefix) :] if verbose: print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} + return { + "version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": None, + "date": date, + } # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} + return { + "version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": "no suitable tags", + "date": None, + } @register_vcs_handler("git", "pieces_from_vcs") @@ -225,8 +243,9 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] - out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=True) + out, rc = run_command( + GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True + ) if rc != 0: if verbose: print("Directory %s not under git control" % root) @@ -234,10 +253,19 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long", - "--match", "%s*" % tag_prefix], - cwd=root) + describe_out, rc = run_command( + GITS, + [ + "describe", + "--tags", + "--dirty", + "--always", + "--long", + "--match", + "%s*" % tag_prefix, + ], + cwd=root, + ) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") @@ -260,17 +288,18 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] + git_describe = git_describe[: git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) + pieces["error"] = ( + "unable to parse git-describe output: '%s'" % describe_out + ) return pieces # tag @@ -279,10 +308,12 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) + pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( + full_tag, + tag_prefix, + ) return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] + pieces["closest-tag"] = full_tag[len(tag_prefix) :] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) @@ -293,13 +324,15 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): else: # HEX: no tags pieces["closest-tag"] = None - count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], - cwd=root) + count_out, rc = run_command( + GITS, ["rev-list", "HEAD", "--count"], cwd=root + ) pieces["distance"] = int(count_out) # total number of commits # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], - cwd=root)[0].strip() + date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[ + 0 + ].strip() pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) return pieces @@ -330,8 +363,7 @@ def render_pep440(pieces): rendered += ".dirty" else: # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) + rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered @@ -445,11 +477,13 @@ def render_git_describe_long(pieces): def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} + return { + "version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None, + } if not style or style == "default": style = "pep440" # the default @@ -469,9 +503,13 @@ def render(pieces, style): else: raise ValueError("unknown style '%s'" % style) - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} + return { + "version": rendered, + "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], + "error": None, + "date": pieces.get("date"), + } def get_versions(): @@ -485,8 +523,9 @@ def get_versions(): verbose = cfg.verbose try: - return git_versions_from_keywords(get_keywords(), cfg.tag_prefix, - verbose) + return git_versions_from_keywords( + get_keywords(), cfg.tag_prefix, verbose + ) except NotThisMethod: pass @@ -495,13 +534,16 @@ def get_versions(): # versionfile_source is the relative path from the top of the source # tree (where the .git directory might live) to this file. Invert # this to find the root from __file__. - for i in cfg.versionfile_source.split('/'): + for i in cfg.versionfile_source.split("/"): root = os.path.dirname(root) except NameError: - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to find root of source tree", - "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to find root of source tree", + "date": None, + } try: pieces = git_pieces_from_vcs(cfg.tag_prefix, root, verbose) @@ -515,6 +557,10 @@ def get_versions(): except NotThisMethod: pass - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, - "error": "unable to compute version", "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", + "date": None, + } diff --git a/src/ecdsa/curves.py b/src/ecdsa/curves.py index 173a2cda..9a103803 100644 --- a/src/ecdsa/curves.py +++ b/src/ecdsa/curves.py @@ -6,11 +6,26 @@ # orderlen was defined in this module previously, so keep it in __all__, # will need to mark it as deprecated later -__all__ = ["UnknownCurveError", "orderlen", "Curve", "NIST192p", - "NIST224p", "NIST256p", "NIST384p", "NIST521p", "curves", - "find_curve", "SECP256k1", "BRAINPOOLP160r1", "BRAINPOOLP192r1", - "BRAINPOOLP224r1", "BRAINPOOLP256r1", "BRAINPOOLP320r1", - "BRAINPOOLP384r1", "BRAINPOOLP512r1"] +__all__ = [ + "UnknownCurveError", + "orderlen", + "Curve", + "NIST192p", + "NIST224p", + "NIST256p", + "NIST384p", + "NIST521p", + "curves", + "find_curve", + "SECP256k1", + "BRAINPOOLP160r1", + "BRAINPOOLP192r1", + "BRAINPOOLP224r1", + "BRAINPOOLP256r1", + "BRAINPOOLP320r1", + "BRAINPOOLP384r1", + "BRAINPOOLP512r1", +] class UnknownCurveError(Exception): @@ -25,8 +40,8 @@ def __init__(self, name, curve, generator, oid, openssl_name=None): self.generator = generator self.order = generator.order() self.baselen = orderlen(self.order) - self.verifying_key_length = 2*self.baselen - self.signature_length = 2*self.baselen + self.verifying_key_length = 2 * self.baselen + self.signature_length = 2 * self.baselen self.oid = oid self.encoded_oid = der.encode_oid(*oid) @@ -35,94 +50,145 @@ def __repr__(self): # the NIST curves -NIST192p = Curve("NIST192p", ecdsa.curve_192, - ecdsa.generator_192, - (1, 2, 840, 10045, 3, 1, 1), "prime192v1") - - -NIST224p = Curve("NIST224p", ecdsa.curve_224, - ecdsa.generator_224, - (1, 3, 132, 0, 33), "secp224r1") - - -NIST256p = Curve("NIST256p", ecdsa.curve_256, - ecdsa.generator_256, - (1, 2, 840, 10045, 3, 1, 7), "prime256v1") - - -NIST384p = Curve("NIST384p", ecdsa.curve_384, - ecdsa.generator_384, - (1, 3, 132, 0, 34), "secp384r1") - - -NIST521p = Curve("NIST521p", ecdsa.curve_521, - ecdsa.generator_521, - (1, 3, 132, 0, 35), "secp521r1") - - -SECP256k1 = Curve("SECP256k1", ecdsa.curve_secp256k1, - ecdsa.generator_secp256k1, - (1, 3, 132, 0, 10), "secp256k1") - - -BRAINPOOLP160r1 = Curve("BRAINPOOLP160r1", - ecdsa.curve_brainpoolp160r1, - ecdsa.generator_brainpoolp160r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 1), - "brainpoolP160r1") - - -BRAINPOOLP192r1 = Curve("BRAINPOOLP192r1", - ecdsa.curve_brainpoolp192r1, - ecdsa.generator_brainpoolp192r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 3), - "brainpoolP192r1") - - -BRAINPOOLP224r1 = Curve("BRAINPOOLP224r1", - ecdsa.curve_brainpoolp224r1, - ecdsa.generator_brainpoolp224r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 5), - "brainpoolP224r1") - - -BRAINPOOLP256r1 = Curve("BRAINPOOLP256r1", - ecdsa.curve_brainpoolp256r1, - ecdsa.generator_brainpoolp256r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), - "brainpoolP256r1") - - -BRAINPOOLP320r1 = Curve("BRAINPOOLP320r1", - ecdsa.curve_brainpoolp320r1, - ecdsa.generator_brainpoolp320r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 9), - "brainpoolP320r1") - - -BRAINPOOLP384r1 = Curve("BRAINPOOLP384r1", - ecdsa.curve_brainpoolp384r1, - ecdsa.generator_brainpoolp384r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), - "brainpoolP384r1") - - -BRAINPOOLP512r1 = Curve("BRAINPOOLP512r1", - ecdsa.curve_brainpoolp512r1, - ecdsa.generator_brainpoolp512r1, - (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), - "brainpoolP512r1") - - -curves = [NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, SECP256k1, - BRAINPOOLP160r1, BRAINPOOLP192r1, BRAINPOOLP224r1, BRAINPOOLP256r1, - BRAINPOOLP320r1, BRAINPOOLP384r1, BRAINPOOLP512r1] +NIST192p = Curve( + "NIST192p", + ecdsa.curve_192, + ecdsa.generator_192, + (1, 2, 840, 10045, 3, 1, 1), + "prime192v1", +) + + +NIST224p = Curve( + "NIST224p", + ecdsa.curve_224, + ecdsa.generator_224, + (1, 3, 132, 0, 33), + "secp224r1", +) + + +NIST256p = Curve( + "NIST256p", + ecdsa.curve_256, + ecdsa.generator_256, + (1, 2, 840, 10045, 3, 1, 7), + "prime256v1", +) + + +NIST384p = Curve( + "NIST384p", + ecdsa.curve_384, + ecdsa.generator_384, + (1, 3, 132, 0, 34), + "secp384r1", +) + + +NIST521p = Curve( + "NIST521p", + ecdsa.curve_521, + ecdsa.generator_521, + (1, 3, 132, 0, 35), + "secp521r1", +) + + +SECP256k1 = Curve( + "SECP256k1", + ecdsa.curve_secp256k1, + ecdsa.generator_secp256k1, + (1, 3, 132, 0, 10), + "secp256k1", +) + + +BRAINPOOLP160r1 = Curve( + "BRAINPOOLP160r1", + ecdsa.curve_brainpoolp160r1, + ecdsa.generator_brainpoolp160r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 1), + "brainpoolP160r1", +) + + +BRAINPOOLP192r1 = Curve( + "BRAINPOOLP192r1", + ecdsa.curve_brainpoolp192r1, + ecdsa.generator_brainpoolp192r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 3), + "brainpoolP192r1", +) + + +BRAINPOOLP224r1 = Curve( + "BRAINPOOLP224r1", + ecdsa.curve_brainpoolp224r1, + ecdsa.generator_brainpoolp224r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 5), + "brainpoolP224r1", +) + + +BRAINPOOLP256r1 = Curve( + "BRAINPOOLP256r1", + ecdsa.curve_brainpoolp256r1, + ecdsa.generator_brainpoolp256r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), + "brainpoolP256r1", +) + + +BRAINPOOLP320r1 = Curve( + "BRAINPOOLP320r1", + ecdsa.curve_brainpoolp320r1, + ecdsa.generator_brainpoolp320r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 9), + "brainpoolP320r1", +) + + +BRAINPOOLP384r1 = Curve( + "BRAINPOOLP384r1", + ecdsa.curve_brainpoolp384r1, + ecdsa.generator_brainpoolp384r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), + "brainpoolP384r1", +) + + +BRAINPOOLP512r1 = Curve( + "BRAINPOOLP512r1", + ecdsa.curve_brainpoolp512r1, + ecdsa.generator_brainpoolp512r1, + (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), + "brainpoolP512r1", +) + + +curves = [ + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + SECP256k1, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, +] def find_curve(oid_curve): for c in curves: if c.oid == oid_curve: return c - raise UnknownCurveError("I don't know about the curve with oid %s." - "I only know about these: %s" % - (oid_curve, [c.name for c in curves])) + raise UnknownCurveError( + "I don't know about the curve with oid %s." + "I only know about these: %s" % (oid_curve, [c.name for c in curves]) + ) diff --git a/src/ecdsa/der.py b/src/ecdsa/der.py index ad75b37b..e48a8ed5 100644 --- a/src/ecdsa/der.py +++ b/src/ecdsa/der.py @@ -13,7 +13,7 @@ class UnexpectedDER(Exception): def encode_constructed(tag, value): - return int2byte(0xa0+tag) + encode_length(len(value)) + value + return int2byte(0xA0 + tag) + encode_length(len(value)) + value def encode_integer(r): @@ -23,13 +23,13 @@ def encode_integer(r): h = b("0") + h s = binascii.unhexlify(h) num = str_idx_as_int(s, 0) - if num <= 0x7f: + if num <= 0x7F: return b("\x02") + encode_length(len(s)) + s else: # DER integers are two's complement, so if the first byte is # 0x80-0xff then we need an extra 0x00 byte to prevent it from # looking negative. - return b("\x02") + encode_length(len(s)+1) + b("\x00") + s + return b("\x02") + encode_length(len(s) + 1) + b("\x00") + s # sentry object to check if an argument was specified (used to detect @@ -73,12 +73,13 @@ def encode_bitstring(s, unused=_sentry): :return: `s` encoded using DER :rtype: bytes """ - encoded_unused = b'' + encoded_unused = b"" len_extra = 0 if unused is _sentry: - warnings.warn("Legacy call convention used, unused= needs to be " - "specified", - DeprecationWarning) + warnings.warn( + "Legacy call convention used, unused= needs to be specified", + DeprecationWarning, + ) elif unused is not None: if not 0 <= unused <= 7: raise ValueError("unused must be integer between 0 and 7") @@ -99,36 +100,41 @@ def encode_octet_string(s): def encode_oid(first, second, *pieces): assert 0 <= first < 2 and 0 <= second <= 39 or first == 2 and 0 <= second - body = b''.join(chain([encode_number(40*first+second)], - (encode_number(p) for p in pieces))) - return b'\x06' + encode_length(len(body)) + body + body = b"".join( + chain( + [encode_number(40 * first + second)], + (encode_number(p) for p in pieces), + ) + ) + return b"\x06" + encode_length(len(body)) + body def encode_sequence(*encoded_pieces): total_len = sum([len(p) for p in encoded_pieces]) - return b('\x30') + encode_length(total_len) + b('').join(encoded_pieces) + return b("\x30") + encode_length(total_len) + b("").join(encoded_pieces) def encode_number(n): b128_digits = [] while n: - b128_digits.insert(0, (n & 0x7f) | 0x80) + b128_digits.insert(0, (n & 0x7F) | 0x80) n = n >> 7 if not b128_digits: b128_digits.append(0) - b128_digits[-1] &= 0x7f - return b('').join([int2byte(d) for d in b128_digits]) + b128_digits[-1] &= 0x7F + return b("").join([int2byte(d) for d in b128_digits]) def remove_constructed(string): s0 = str_idx_as_int(string, 0) - if (s0 & 0xe0) != 0xa0: - raise UnexpectedDER("wanted type 'constructed tag' (0xa0-0xbf), " - "got 0x%02x" % s0) - tag = s0 & 0x1f + if (s0 & 0xE0) != 0xA0: + raise UnexpectedDER( + "wanted type 'constructed tag' (0xa0-0xbf), got 0x%02x" % s0 + ) + tag = s0 & 0x1F length, llen = read_length(string[1:]) - body = string[1+llen:1+llen+length] - rest = string[1+llen+length:] + body = string[1 + llen : 1 + llen + length] + rest = string[1 + llen + length :] return tag, body, rest @@ -141,8 +147,8 @@ def remove_sequence(string): length, lengthlength = read_length(string[1:]) if length > len(string) - 1 - lengthlength: raise UnexpectedDER("Length longer than the provided buffer") - endseq = 1+lengthlength+length - return string[1+lengthlength:endseq], string[endseq:] + endseq = 1 + lengthlength + length + return string[1 + lengthlength : endseq], string[endseq:] def remove_octet_string(string): @@ -150,26 +156,28 @@ def remove_octet_string(string): n = str_idx_as_int(string, 0) raise UnexpectedDER("wanted type 'octetstring' (0x04), got 0x%02x" % n) length, llen = read_length(string[1:]) - body = string[1+llen:1+llen+length] - rest = string[1+llen+length:] + body = string[1 + llen : 1 + llen + length] + rest = string[1 + llen + length :] return body, rest def remove_object(string): if not string: raise UnexpectedDER( - "Empty string does not encode an object identifier") + "Empty string does not encode an object identifier" + ) if string[:1] != b"\x06": n = str_idx_as_int(string, 0) raise UnexpectedDER("wanted type 'object' (0x06), got 0x%02x" % n) length, lengthlength = read_length(string[1:]) - body = string[1+lengthlength:1+lengthlength+length] - rest = string[1+lengthlength+length:] + body = string[1 + lengthlength : 1 + lengthlength + length] + rest = string[1 + lengthlength + length :] if not body: raise UnexpectedDER("Empty object identifier") if len(body) != length: raise UnexpectedDER( - "Length of object identifier longer than the provided buffer") + "Length of object identifier longer than the provided buffer" + ) numbers = [] while body: n, ll = read_number(body) @@ -188,8 +196,9 @@ def remove_object(string): def remove_integer(string): if not string: - raise UnexpectedDER("Empty string is an invalid encoding of an " - "integer") + raise UnexpectedDER( + "Empty string is an invalid encoding of an integer" + ) if string[:1] != b"\x02": n = str_idx_as_int(string, 0) raise UnexpectedDER("wanted type 'integer' (0x02), got 0x%02x" % n) @@ -198,8 +207,8 @@ def remove_integer(string): raise UnexpectedDER("Length longer than provided buffer") if length == 0: raise UnexpectedDER("0-byte long encoding of integer") - numberbytes = string[1+llen:1+llen+length] - rest = string[1+llen+length:] + numberbytes = string[1 + llen : 1 + llen + length] + rest = string[1 + llen + length :] msb = str_idx_as_int(numberbytes, 0) if not msb < 0x80: raise UnexpectedDER("Negative integers are not supported") @@ -209,8 +218,10 @@ def remove_integer(string): # considered a negative number otherwise smsb = str_idx_as_int(numberbytes, 1) if smsb < 0x80: - raise UnexpectedDER("Invalid encoding of integer, unnecessary " - "zero padding bytes") + raise UnexpectedDER( + "Invalid encoding of integer, unnecessary " + "zero padding bytes" + ) return int(binascii.hexlify(numberbytes), 16), rest @@ -226,7 +237,7 @@ def read_number(string): raise UnexpectedDER("ran out of length bytes") number = number << 7 d = str_idx_as_int(string, llen) - number += (d & 0x7f) + number += d & 0x7F llen += 1 if not d & 0x80: break @@ -251,19 +262,19 @@ def read_length(string): num = str_idx_as_int(string, 0) if not (num & 0x80): # short form - return (num & 0x7f), 1 + return (num & 0x7F), 1 # else long-form: b0&0x7f is number of additional base256 length bytes, # big-endian - llen = num & 0x7f + llen = num & 0x7F if not llen: raise UnexpectedDER("Invalid length encoding, length of length is 0") - if llen > len(string)-1: + if llen > len(string) - 1: raise UnexpectedDER("Length of length longer than provided buffer") # verify that the encoding is minimal possible (DER requirement) msb = str_idx_as_int(string, 1) if not msb or llen == 1 and msb < 0x80: raise UnexpectedDER("Not minimal encoding of length") - return int(binascii.hexlify(string[1:1+llen]), 16), 1+llen + return int(binascii.hexlify(string[1 : 1 + llen]), 16), 1 + llen def remove_bitstring(string, expect_unused=_sentry): @@ -308,17 +319,19 @@ def remove_bitstring(string, expect_unused=_sentry): if not string: raise UnexpectedDER("Empty string does not encode a bitstring") if expect_unused is _sentry: - warnings.warn("Legacy call convention used, expect_unused= needs to be" - " specified", - DeprecationWarning) + warnings.warn( + "Legacy call convention used, expect_unused= needs to be" + " specified", + DeprecationWarning, + ) num = str_idx_as_int(string, 0) if string[:1] != b"\x03": raise UnexpectedDER("wanted bitstring (0x03), got 0x%02x" % num) length, llen = read_length(string[1:]) if not length: raise UnexpectedDER("Invalid length of bit string, can't be 0") - body = string[1+llen:1+llen+length] - rest = string[1+llen+length:] + body = string[1 + llen : 1 + llen + length] + rest = string[1 + llen + length :] if expect_unused is not _sentry: unused = str_idx_as_int(body, 0) if not 0 <= unused <= 7: @@ -337,6 +350,7 @@ def remove_bitstring(string, expect_unused=_sentry): body = (body, unused) return body, rest + # SEQUENCE([1, STRING(secexp), cont[0], OBJECT(curvename), cont[1], BINTSTRING) @@ -348,8 +362,8 @@ def remove_bitstring(string, expect_unused=_sentry): # ansi-X9-62 signatures(4) } # ecdsa-with-SHA1 OBJECT IDENTIFIER ::= { # id-ecSigType 1 } -## so 1,2,840,10045,4,1 -## so 0x42, .. .. +# so 1,2,840,10045,4,1 +# so 0x42, .. .. # Ecdsa-Sig-Value ::= SEQUENCE { # r INTEGER, @@ -366,19 +380,26 @@ def remove_bitstring(string, expect_unused=_sentry): # secp384r1 OBJECT IDENTIFIER ::= { # iso(1) identified-organization(3) certicom(132) curve(0) 34 } + def unpem(pem): if isinstance(pem, text_type): pem = pem.encode() - d = b("").join([l.strip() for l in pem.split(b("\n")) - if l and not l.startswith(b("-----"))]) + d = b("").join( + [ + l.strip() + for l in pem.split(b("\n")) + if l and not l.startswith(b("-----")) + ] + ) return base64.b64decode(d) def topem(der, name): b64 = base64.b64encode(der) lines = [("-----BEGIN %s-----\n" % name).encode()] - lines.extend([b64[start:start+64]+b("\n") - for start in range(0, len(b64), 64)]) + lines.extend( + [b64[start : start + 64] + b("\n") for start in range(0, len(b64), 64)] + ) lines.append(("-----END %s-----\n" % name).encode()) return b("").join(lines) diff --git a/src/ecdsa/ecdh.py b/src/ecdsa/ecdh.py index 88848f55..ecb1b680 100644 --- a/src/ecdsa/ecdh.py +++ b/src/ecdsa/ecdh.py @@ -7,8 +7,13 @@ from .keys import SigningKey, VerifyingKey -__all__ = ["ECDH", "NoKeyError", "NoCurveError", "InvalidCurveError", - "InvalidSharedSecretError"] +__all__ = [ + "ECDH", + "NoKeyError", + "NoCurveError", + "InvalidCurveError", + "InvalidSharedSecretError", +] class NoKeyError(Exception): @@ -41,7 +46,7 @@ class ECDH(object): Allows two parties, each having an elliptic-curve public-private key pair, to establish a shared secret over an insecure channel - """"" + """ def __init__(self, curve=None, private_key=None, public_key=None): """ @@ -58,7 +63,7 @@ def __init__(self, curve=None, private_key=None, public_key=None): :type private_key: SigningKey :param public_key: `their` public key for ECDH :type public_key: VerifyingKey - """ + """ self.curve = curve self.private_key = None self.public_key = None @@ -70,19 +75,26 @@ def __init__(self, curve=None, private_key=None, public_key=None): def _get_shared_secret(self, remote_public_key): if not self.private_key: raise NoKeyError( - "Private key needs to be set to create shared secret") + "Private key needs to be set to create shared secret" + ) if not self.public_key: raise NoKeyError( - "Public key needs to be set to create shared secret") - if not (self.private_key.curve == self.curve == remote_public_key.curve): + "Public key needs to be set to create shared secret" + ) + if not ( + self.private_key.curve == self.curve == remote_public_key.curve + ): raise InvalidCurveError( - "Curves for public key and private key is not equal.") + "Curves for public key and private key is not equal." + ) # shared secret = PUBKEYtheirs * PRIVATEKEYours - result = remote_public_key.pubkey.point * self.private_key.privkey.secret_multiplier + result = ( + remote_public_key.pubkey.point + * self.private_key.privkey.secret_multiplier + ) if result == INFINITY: - raise InvalidSharedSecretError( - "Invalid shared secret (INFINITY).") + raise InvalidSharedSecretError("Invalid shared secret (INFINITY).") return result.x() @@ -149,7 +161,8 @@ def load_private_key_bytes(self, private_key): if not self.curve: raise NoCurveError("Curve must be set prior to key load.") return self.load_private_key( - SigningKey.from_string(private_key, curve=self.curve)) + SigningKey.from_string(private_key, curve=self.curve) + ) def load_private_key_der(self, private_key_der): """ @@ -233,7 +246,8 @@ def load_received_public_key_bytes(self, public_key_str): :type public_key_str: :term:`bytes-like object` """ return self.load_received_public_key( - VerifyingKey.from_string(public_key_str, self.curve)) + VerifyingKey.from_string(public_key_str, self.curve) + ) def load_received_public_key_der(self, public_key_der): """ @@ -250,7 +264,9 @@ def load_received_public_key_der(self, public_key_der): :raises InvalidCurveError: public_key curve not the same as self.curve """ - return self.load_received_public_key(VerifyingKey.from_der(public_key_der)) + return self.load_received_public_key( + VerifyingKey.from_der(public_key_der) + ) def load_received_public_key_pem(self, public_key_pem): """ @@ -267,7 +283,9 @@ def load_received_public_key_pem(self, public_key_pem): :raises InvalidCurveError: public_key curve not the same as self.curve """ - return self.load_received_public_key(VerifyingKey.from_pem(public_key_pem)) + return self.load_received_public_key( + VerifyingKey.from_pem(public_key_pem) + ) def generate_sharedsecret_bytes(self): """ @@ -283,8 +301,8 @@ def generate_sharedsecret_bytes(self): :rtype: byte string """ return number_to_string( - self.generate_sharedsecret(), - self.private_key.curve.order) + self.generate_sharedsecret(), self.private_key.curve.order + ) def generate_sharedsecret(self): """ diff --git a/src/ecdsa/ecdsa.py b/src/ecdsa/ecdsa.py index 4e9bab08..d924ac1f 100644 --- a/src/ecdsa/ecdsa.py +++ b/src/ecdsa/ecdsa.py @@ -68,219 +68,229 @@ class InvalidPointError(RuntimeError): class Signature(object): - """ECDSA signature. - """ - def __init__(self, r, s): - self.r = r - self.s = s - - def recover_public_keys(self, hash, generator): - """Returns two public keys for which the signature is valid - hash is signed hash - generator is the used generator of the signature - """ - curve = generator.curve() - n = generator.order() - r = self.r - s = self.s - e = hash - x = r - - # Compute the curve point with x as x-coordinate - alpha = (pow(x, 3, curve.p()) + (curve.a() * x) + curve.b()) % curve.p() - beta = numbertheory.square_root_mod_prime(alpha, curve.p()) - y = beta if beta % 2 == 0 else curve.p() - beta - - # Compute the public key - R1 = ellipticcurve.PointJacobi(curve, x, y, 1, n) - Q1 = numbertheory.inverse_mod(r, n) * (s * R1 + (-e % n) * generator) - Pk1 = Public_key(generator, Q1) - - # And the second solution - R2 = ellipticcurve.PointJacobi(curve, x, -y, 1, n) - Q2 = numbertheory.inverse_mod(r, n) * (s * R2 + (-e % n) * generator) - Pk2 = Public_key(generator, Q2) - - return [Pk1, Pk2] + """ECDSA signature.""" + + def __init__(self, r, s): + self.r = r + self.s = s + + def recover_public_keys(self, hash, generator): + """Returns two public keys for which the signature is valid + hash is signed hash + generator is the used generator of the signature + """ + curve = generator.curve() + n = generator.order() + r = self.r + s = self.s + e = hash + x = r + + # Compute the curve point with x as x-coordinate + alpha = ( + pow(x, 3, curve.p()) + (curve.a() * x) + curve.b() + ) % curve.p() + beta = numbertheory.square_root_mod_prime(alpha, curve.p()) + y = beta if beta % 2 == 0 else curve.p() - beta + + # Compute the public key + R1 = ellipticcurve.PointJacobi(curve, x, y, 1, n) + Q1 = numbertheory.inverse_mod(r, n) * (s * R1 + (-e % n) * generator) + Pk1 = Public_key(generator, Q1) + + # And the second solution + R2 = ellipticcurve.PointJacobi(curve, x, -y, 1, n) + Q2 = numbertheory.inverse_mod(r, n) * (s * R2 + (-e % n) * generator) + Pk2 = Public_key(generator, Q2) + + return [Pk1, Pk2] class Public_key(object): - """Public key for ECDSA. - """ - - def __init__(self, generator, point, verify=True): - """ - Low level ECDSA public key object. - - :param generator: the Point that generates the group (the base point) - :param point: the Point that defines the public key - :param bool verify: if True check if point is valid point on curve - - :raises InvalidPointError: if the point parameters are invalid or - point does not lie on the curve - """ - - self.curve = generator.curve() - self.generator = generator - self.point = point - n = generator.order() - p = self.curve.p() - if not (0 <= point.x() < p) or not (0 <= point.y() < p): - raise InvalidPointError("The public point has x or y out of range.") - if verify and not self.curve.contains_point(point.x(), point.y()): - raise InvalidPointError("Point does not lie on the curve") - if not n: - raise InvalidPointError("Generator point must have order.") - # for curve parameters with base point with cofactor 1, all points - # that are on the curve are scalar multiples of the base point, so - # verifying that is not necessary. See Section 3.2.2.1 of SEC 1 v2 - if verify and self.curve.cofactor() != 1 and \ - not n * point == ellipticcurve.INFINITY: - raise InvalidPointError("Generator point order is bad.") - - def __eq__(self, other): - if isinstance(other, Public_key): - """Return True if the points are identical, False otherwise.""" - return self.curve == other.curve \ - and self.point == other.point - return NotImplemented - - def verifies(self, hash, signature): - """Verify that signature is a valid signature of hash. - Return True if the signature is valid. - """ - - # From X9.62 J.3.1. - - G = self.generator - n = G.order() - r = signature.r - s = signature.s - if r < 1 or r > n - 1: - return False - if s < 1 or s > n - 1: - return False - c = numbertheory.inverse_mod(s, n) - u1 = (hash * c) % n - u2 = (r * c) % n - if hasattr(G, "mul_add"): - xy = G.mul_add(u1, self.point, u2) - else: - xy = u1 * G + u2 * self.point - v = xy.x() % n - return v == r + """Public key for ECDSA.""" + + def __init__(self, generator, point, verify=True): + """Low level ECDSA public key object. + + :param generator: the Point that generates the group (the base point) + :param point: the Point that defines the public key + :param bool verify: if True check if point is valid point on curve + + :raises InvalidPointError: if the point parameters are invalid or + point does not lie on the curve + """ + + self.curve = generator.curve() + self.generator = generator + self.point = point + n = generator.order() + p = self.curve.p() + if not (0 <= point.x() < p) or not (0 <= point.y() < p): + raise InvalidPointError( + "The public point has x or y out of range." + ) + if verify and not self.curve.contains_point(point.x(), point.y()): + raise InvalidPointError("Point does not lie on the curve") + if not n: + raise InvalidPointError("Generator point must have order.") + # for curve parameters with base point with cofactor 1, all points + # that are on the curve are scalar multiples of the base point, so + # verifying that is not necessary. See Section 3.2.2.1 of SEC 1 v2 + if ( + verify + and self.curve.cofactor() != 1 + and not n * point == ellipticcurve.INFINITY + ): + raise InvalidPointError("Generator point order is bad.") + + def __eq__(self, other): + if isinstance(other, Public_key): + """Return True if the points are identical, False otherwise.""" + return self.curve == other.curve and self.point == other.point + return NotImplemented + + def verifies(self, hash, signature): + """Verify that signature is a valid signature of hash. + Return True if the signature is valid. + """ + + # From X9.62 J.3.1. + + G = self.generator + n = G.order() + r = signature.r + s = signature.s + if r < 1 or r > n - 1: + return False + if s < 1 or s > n - 1: + return False + c = numbertheory.inverse_mod(s, n) + u1 = (hash * c) % n + u2 = (r * c) % n + if hasattr(G, "mul_add"): + xy = G.mul_add(u1, self.point, u2) + else: + xy = u1 * G + u2 * self.point + v = xy.x() % n + return v == r class Private_key(object): - """Private key for ECDSA. - """ - - def __init__(self, public_key, secret_multiplier): - """public_key is of class Public_key; - secret_multiplier is a large integer. - """ - - self.public_key = public_key - self.secret_multiplier = secret_multiplier - - def __eq__(self, other): - if isinstance(other, Private_key): - """Return True if the points are identical, False otherwise.""" - return self.public_key == other.public_key \ - and self.secret_multiplier == other.secret_multiplier - return NotImplemented - - def sign(self, hash, random_k): - """Return a signature for the provided hash, using the provided - random nonce. It is absolutely vital that random_k be an unpredictable - number in the range [1, self.public_key.point.order()-1]. If - an attacker can guess random_k, he can compute our private key from a - single signature. Also, if an attacker knows a few high-order - bits (or a few low-order bits) of random_k, he can compute our private - key from many signatures. The generation of nonces with adequate - cryptographic strength is very difficult and far beyond the scope - of this comment. - - May raise RuntimeError, in which case retrying with a new - random value k is in order. - """ - - G = self.public_key.generator - n = G.order() - k = random_k % n - # Fix the bit-length of the random nonce, - # so that it doesn't leak via timing. - # This does not change that ks = k mod n - ks = k + n - kt = ks + n - if bit_length(ks) == bit_length(n): - p1 = kt * G - else: - p1 = ks * G - r = p1.x() % n - if r == 0: - raise RSZeroError("amazingly unlucky random number r") - s = (numbertheory.inverse_mod(k, n) - * (hash + (self.secret_multiplier * r) % n)) % n - if s == 0: - raise RSZeroError("amazingly unlucky random number s") - return Signature(r, s) + """Private key for ECDSA.""" + + def __init__(self, public_key, secret_multiplier): + """public_key is of class Public_key; + secret_multiplier is a large integer. + """ + + self.public_key = public_key + self.secret_multiplier = secret_multiplier + + def __eq__(self, other): + if isinstance(other, Private_key): + """Return True if the points are identical, False otherwise.""" + return ( + self.public_key == other.public_key + and self.secret_multiplier == other.secret_multiplier + ) + return NotImplemented + + def sign(self, hash, random_k): + """Return a signature for the provided hash, using the provided + random nonce. It is absolutely vital that random_k be an unpredictable + number in the range [1, self.public_key.point.order()-1]. If + an attacker can guess random_k, he can compute our private key from a + single signature. Also, if an attacker knows a few high-order + bits (or a few low-order bits) of random_k, he can compute our private + key from many signatures. The generation of nonces with adequate + cryptographic strength is very difficult and far beyond the scope + of this comment. + + May raise RuntimeError, in which case retrying with a new + random value k is in order. + """ + + G = self.public_key.generator + n = G.order() + k = random_k % n + # Fix the bit-length of the random nonce, + # so that it doesn't leak via timing. + # This does not change that ks = k mod n + ks = k + n + kt = ks + n + if bit_length(ks) == bit_length(n): + p1 = kt * G + else: + p1 = ks * G + r = p1.x() % n + if r == 0: + raise RSZeroError("amazingly unlucky random number r") + s = ( + numbertheory.inverse_mod(k, n) + * (hash + (self.secret_multiplier * r) % n) + ) % n + if s == 0: + raise RSZeroError("amazingly unlucky random number s") + return Signature(r, s) def int_to_string(x): - """Convert integer x into a string of bytes, as per X9.62.""" - assert x >= 0 - if x == 0: - return b('\0') - result = [] - while x: - ordinal = x & 0xFF - result.append(int2byte(ordinal)) - x >>= 8 + """Convert integer x into a string of bytes, as per X9.62.""" + assert x >= 0 + if x == 0: + return b("\0") + result = [] + while x: + ordinal = x & 0xFF + result.append(int2byte(ordinal)) + x >>= 8 - result.reverse() - return b('').join(result) + result.reverse() + return b("").join(result) def string_to_int(s): - """Convert a string of bytes into an integer, as per X9.62.""" - result = 0 - for c in s: - if not isinstance(c, int): - c = ord(c) - result = 256 * result + c - return result + """Convert a string of bytes into an integer, as per X9.62.""" + result = 0 + for c in s: + if not isinstance(c, int): + c = ord(c) + result = 256 * result + c + return result def digest_integer(m): - """Convert an integer into a string of bytes, compute + """Convert an integer into a string of bytes, compute its SHA-1 hash, and convert the result to an integer.""" - # - # I don't expect this function to be used much. I wrote - # it in order to be able to duplicate the examples - # in ECDSAVS. - # - from hashlib import sha1 - return string_to_int(sha1(int_to_string(m)).digest()) + # + # I don't expect this function to be used much. I wrote + # it in order to be able to duplicate the examples + # in ECDSAVS. + # + from hashlib import sha1 + + return string_to_int(sha1(int_to_string(m)).digest()) def point_is_valid(generator, x, y): - """Is (x,y) a valid public key based on the specified generator?""" + """Is (x,y) a valid public key based on the specified generator?""" - # These are the tests specified in X9.62. + # These are the tests specified in X9.62. - n = generator.order() - curve = generator.curve() - p = curve.p() - if not (0 <= x < p) or not (0 <= y < p): - return False - if not curve.contains_point(x, y): - return False - if curve.cofactor() != 1 and \ - not n * ellipticcurve.PointJacobi(curve, x, y, 1)\ - == ellipticcurve.INFINITY: - return False - return True + n = generator.order() + curve = generator.curve() + p = curve.p() + if not (0 <= x < p) or not (0 <= y < p): + return False + if not curve.contains_point(x, y): + return False + if ( + curve.cofactor() != 1 + and not n * ellipticcurve.PointJacobi(curve, x, y, 1) + == ellipticcurve.INFINITY + ): + return False + return True # NIST Curve P-192: @@ -288,13 +298,14 @@ def point_is_valid(generator, x, y): _r = 6277101735386680763835789423176059013767194773182842284081 # s = 0x3045ae6fc8422f64ed579528d38120eae12196d5L # c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65L -_b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 -_Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 -_Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 +_b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1 +_Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012 +_Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811 curve_192 = ellipticcurve.CurveFp(_p, -3, _b, 1) generator_192 = ellipticcurve.PointJacobi( - curve_192, _Gx, _Gy, 1, _r, generator=True) + curve_192, _Gx, _Gy, 1, _r, generator=True +) # NIST Curve P-224: @@ -302,64 +313,69 @@ def point_is_valid(generator, x, y): _r = 26959946667150639794667015087019625940457807714424391721682722368061 # s = 0xbd71344799d5c7fcdc45b59fa3b9ab8f6a948bc5L # c = 0x5b056c7e11dd68f40469ee7f3c7a7d74f7d121116506d031218291fbL -_b = 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4 -_Gx = 0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21 -_Gy = 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34 +_b = 0xB4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4 +_Gx = 0xB70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21 +_Gy = 0xBD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34 curve_224 = ellipticcurve.CurveFp(_p, -3, _b, 1) generator_224 = ellipticcurve.PointJacobi( - curve_224, _Gx, _Gy, 1, _r, generator=True) + curve_224, _Gx, _Gy, 1, _r, generator=True +) # NIST Curve P-256: _p = 115792089210356248762697446949407573530086143415290314195533631308867097853951 _r = 115792089210356248762697446949407573529996955224135760342422259061068512044369 # s = 0xc49d360886e704936a6678e1139d26b7819f7e90L # c = 0x7efba1662985be9403cb055c75d4f7e0ce8d84a9c5114abcaf3177680104fa0dL -_b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b -_Gx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296 -_Gy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 +_b = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B +_Gx = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296 +_Gy = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5 curve_256 = ellipticcurve.CurveFp(_p, -3, _b, 1) generator_256 = ellipticcurve.PointJacobi( - curve_256, _Gx, _Gy, 1, _r, generator=True) + curve_256, _Gx, _Gy, 1, _r, generator=True +) # NIST Curve P-384: _p = 39402006196394479212279040100143613805079739270465446667948293404245721771496870329047266088258938001861606973112319 _r = 39402006196394479212279040100143613805079739270465446667946905279627659399113263569398956308152294913554433653942643 # s = 0xa335926aa319a27a1d00896a6773a4827acdac73L # c = 0x79d1e655f868f02fff48dcdee14151ddb80643c1406d0ca10dfe6fc52009540a495e8042ea5f744f6e184667cc722483L -_b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef -_Gx = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7 -_Gy = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f +_b = 0xB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF +_Gx = 0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7 +_Gy = 0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F curve_384 = ellipticcurve.CurveFp(_p, -3, _b, 1) generator_384 = ellipticcurve.PointJacobi( - curve_384, _Gx, _Gy, 1, _r, generator=True) + curve_384, _Gx, _Gy, 1, _r, generator=True +) # NIST Curve P-521: _p = 6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151 _r = 6864797660130609714981900799081393217269435300143305409394463459185543183397655394245057746333217197532963996371363321113864768612440380340372808892707005449 # s = 0xd09e8800291cb85396cc6717393284aaa0da64baL # c = 0x0b48bfa5f420a34949539d2bdfc264eeeeb077688e44fbf0ad8f6d0edb37bd6b533281000518e19f1b9ffbe0fe9ed8a3c2200b8f875e523868c70c1e5bf55bad637L -_b = 0x051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00 -_Gx = 0xc6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66 -_Gy = 0x11839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650 +_b = 0x051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00 +_Gx = 0xC6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66 +_Gy = 0x11839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650 curve_521 = ellipticcurve.CurveFp(_p, -3, _b, 1) generator_521 = ellipticcurve.PointJacobi( - curve_521, _Gx, _Gy, 1, _r, generator=True) + curve_521, _Gx, _Gy, 1, _r, generator=True +) # Certicom secp256-k1 _a = 0x0000000000000000000000000000000000000000000000000000000000000000 _b = 0x0000000000000000000000000000000000000000000000000000000000000007 -_p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffc2f -_Gx = 0x79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798 -_Gy = 0x483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8 -_r = 0xfffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141 +_p = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F +_Gx = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 +_Gy = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 +_r = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 curve_secp256k1 = ellipticcurve.CurveFp(_p, _a, _b, 1) generator_secp256k1 = ellipticcurve.PointJacobi( - curve_secp256k1, _Gx, _Gy, 1, _r, generator=True) + curve_secp256k1, _Gx, _Gy, 1, _r, generator=True +) # Brainpool P-160-r1 _a = 0x340E7BE2A280EB74E2BE61BADA745D97E8F7C300 @@ -371,7 +387,8 @@ def point_is_valid(generator, x, y): curve_brainpoolp160r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) generator_brainpoolp160r1 = ellipticcurve.PointJacobi( - curve_brainpoolp160r1, _Gx, _Gy, 1, _q, generator=True) + curve_brainpoolp160r1, _Gx, _Gy, 1, _q, generator=True +) # Brainpool P-192-r1 _a = 0x6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF @@ -383,7 +400,8 @@ def point_is_valid(generator, x, y): curve_brainpoolp192r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) generator_brainpoolp192r1 = ellipticcurve.PointJacobi( - curve_brainpoolp192r1, _Gx, _Gy, 1, _q, generator=True) + curve_brainpoolp192r1, _Gx, _Gy, 1, _q, generator=True +) # Brainpool P-224-r1 _a = 0x68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43 @@ -395,7 +413,8 @@ def point_is_valid(generator, x, y): curve_brainpoolp224r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) generator_brainpoolp224r1 = ellipticcurve.PointJacobi( - curve_brainpoolp224r1, _Gx, _Gy, 1, _q, generator=True) + curve_brainpoolp224r1, _Gx, _Gy, 1, _q, generator=True +) # Brainpool P-256-r1 _a = 0x7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 @@ -407,7 +426,8 @@ def point_is_valid(generator, x, y): curve_brainpoolp256r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) generator_brainpoolp256r1 = ellipticcurve.PointJacobi( - curve_brainpoolp256r1, _Gx, _Gy, 1, _q, generator=True) + curve_brainpoolp256r1, _Gx, _Gy, 1, _q, generator=True +) # Brainpool P-320-r1 _a = 0x3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4 @@ -419,7 +439,8 @@ def point_is_valid(generator, x, y): curve_brainpoolp320r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) generator_brainpoolp320r1 = ellipticcurve.PointJacobi( - curve_brainpoolp320r1, _Gx, _Gy, 1, _q, generator=True) + curve_brainpoolp320r1, _Gx, _Gy, 1, _q, generator=True +) # Brainpool P-384-r1 _a = 0x7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826 @@ -431,7 +452,8 @@ def point_is_valid(generator, x, y): curve_brainpoolp384r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) generator_brainpoolp384r1 = ellipticcurve.PointJacobi( - curve_brainpoolp384r1, _Gx, _Gy, 1, _q, generator=True) + curve_brainpoolp384r1, _Gx, _Gy, 1, _q, generator=True +) # Brainpool P-512-r1 _a = 0x7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA @@ -443,4 +465,5 @@ def point_is_valid(generator, x, y): curve_brainpoolp512r1 = ellipticcurve.CurveFp(_p, _a, _b, 1) generator_brainpoolp512r1 = ellipticcurve.PointJacobi( - curve_brainpoolp512r1, _Gx, _Gy, 1, _q, generator=True) + curve_brainpoolp512r1, _Gx, _Gy, 1, _q, generator=True +) diff --git a/src/ecdsa/ellipticcurve.py b/src/ecdsa/ellipticcurve.py index 3420454d..db397100 100644 --- a/src/ecdsa/ellipticcurve.py +++ b/src/ecdsa/ellipticcurve.py @@ -37,10 +37,12 @@ try: from gmpy2 import mpz + GMPY = True except ImportError: try: from gmpy import mpz + GMPY = True except ImportError: GMPY = False @@ -53,727 +55,746 @@ @python_2_unicode_compatible class CurveFp(object): - """Elliptic Curve over the field of integers modulo a prime.""" + """Elliptic Curve over the field of integers modulo a prime.""" - if GMPY: - def __init__(self, p, a, b, h=None): - """ - The curve of points satisfying y^2 = x^3 + a*x + b (mod p). + if GMPY: - h is an integer that is the cofactor of the elliptic curve domain - parameters; it is the number of points satisfying the elliptic curve - equation divided by the order of the base point. It is used for selection - of efficient algorithm for public point verification. - """ - self.__p = mpz(p) - self.__a = mpz(a) - self.__b = mpz(b) - # h is not used in calculations and it can be None, so don't use - # gmpy with it - self.__h = h - else: - def __init__(self, p, a, b, h=None): - """ - The curve of points satisfying y^2 = x^3 + a*x + b (mod p). + def __init__(self, p, a, b, h=None): + """ + The curve of points satisfying y^2 = x^3 + a*x + b (mod p). + + h is an integer that is the cofactor of the elliptic curve domain + parameters; it is the number of points satisfying the elliptic curve + equation divided by the order of the base point. It is used for selection + of efficient algorithm for public point verification. + """ + self.__p = mpz(p) + self.__a = mpz(a) + self.__b = mpz(b) + # h is not used in calculations and it can be None, so don't use + # gmpy with it + self.__h = h - h is an integer that is the cofactor of the elliptic curve domain - parameters; it is the number of points satisfying the elliptic curve - equation divided by the order of the base point. It is used for selection - of efficient algorithm for public point verification. - """ - self.__p = p - self.__a = a - self.__b = b - self.__h = h + else: - def __eq__(self, other): - if isinstance(other, CurveFp): - """Return True if the curves are identical, False otherwise.""" - return self.__p == other.__p \ - and self.__a == other.__a \ - and self.__b == other.__b - return NotImplemented + def __init__(self, p, a, b, h=None): + """ + The curve of points satisfying y^2 = x^3 + a*x + b (mod p). + + h is an integer that is the cofactor of the elliptic curve domain + parameters; it is the number of points satisfying the elliptic curve + equation divided by the order of the base point. It is used for selection + of efficient algorithm for public point verification. + """ + self.__p = p + self.__a = a + self.__b = b + self.__h = h + + def __eq__(self, other): + if isinstance(other, CurveFp): + """Return True if the curves are identical, False otherwise.""" + return ( + self.__p == other.__p + and self.__a == other.__a + and self.__b == other.__b + ) + return NotImplemented - def __hash__(self): - return hash((self.__p, self.__a, self.__b)) + def __hash__(self): + return hash((self.__p, self.__a, self.__b)) - def p(self): - return self.__p + def p(self): + return self.__p - def a(self): - return self.__a + def a(self): + return self.__a - def b(self): - return self.__b + def b(self): + return self.__b - def cofactor(self): - return self.__h + def cofactor(self): + return self.__h - def contains_point(self, x, y): - """Is the point (x,y) on this curve?""" - return (y * y - ((x * x + self.__a) * x + self.__b)) % self.__p == 0 + def contains_point(self, x, y): + """Is the point (x,y) on this curve?""" + return (y * y - ((x * x + self.__a) * x + self.__b)) % self.__p == 0 - def __str__(self): - return "CurveFp(p=%d, a=%d, b=%d, h=%d)" % ( - self.__p, self.__a, self.__b, self.__h) + def __str__(self): + return "CurveFp(p=%d, a=%d, b=%d, h=%d)" % ( + self.__p, + self.__a, + self.__b, + self.__h, + ) class PointJacobi(object): - """ - Point on an elliptic curve. Uses Jacobi coordinates. + """ + Point on an elliptic curve. Uses Jacobi coordinates. - In Jacobian coordinates, there are three parameters, X, Y and Z. - They correspond to affine parameters 'x' and 'y' like so: + In Jacobian coordinates, there are three parameters, X, Y and Z. + They correspond to affine parameters 'x' and 'y' like so: - x = X / Z² - y = Y / Z³ - """ - def __init__(self, curve, x, y, z, order=None, generator=False): - """ - Initialise a point that uses Jacobi representation internally. - - :param CurveFp curve: curve on which the point resides - :param int x: the X parameter of Jacobi representation (equal to x when - converting from affine coordinates - :param int y: the Y parameter of Jacobi representation (equal to y when - converting from affine coordinates - :param int z: the Z parameter of Jacobi representation (equal to 1 when - converting from affine coordinates - :param int order: the point order, must be non zero when using - generator=True - :param bool generator: the point provided is a curve generator, as - such, it will be commonly used with scalar multiplication. This will - cause to precompute multiplication table for it - """ - self.__curve = curve - # since it's generally better (faster) to use scaled points vs unscaled - # ones, use writer-biased RWLock for locking: - self._scale_lock = RWLock() - if GMPY: - self.__x = mpz(x) - self.__y = mpz(y) - self.__z = mpz(z) - self.__order = order and mpz(order) - else: - self.__x = x - self.__y = y - self.__z = z - self.__order = order - self.__precompute = [] - if generator: - assert order - i = 1 - order *= 2 - doubler = PointJacobi(curve, x, y, z, order) - order *= 2 - self.__precompute.append((doubler.x(), doubler.y())) - - while i < order: - i *= 2 - doubler = doubler.double().scale() - self.__precompute.append((doubler.x(), doubler.y())) - - def __eq__(self, other): - """Compare two points with each-other.""" - try: - self._scale_lock.reader_acquire() - if other is INFINITY: - return not self.__y or not self.__z - x1, y1, z1 = self.__x, self.__y, self.__z - finally: - self._scale_lock.reader_release() - if isinstance(other, Point): - x2, y2, z2 = other.x(), other.y(), 1 - elif isinstance(other, PointJacobi): - try: - other._scale_lock.reader_acquire() - x2, y2, z2 = other.__x, other.__y, other.__z - finally: - other._scale_lock.reader_release() - else: - return NotImplemented - if self.__curve != other.curve(): - return False - p = self.__curve.p() - - zz1 = z1 * z1 % p - zz2 = z2 * z2 % p - - # compare the fractions by bringing them to the same denominator - # depend on short-circuit to save 4 multiplications in case of inequality - return (x1 * zz2 - x2 * zz1) % p == 0 and \ - (y1 * zz2 * z2 - y2 * zz1 * z1) % p == 0 - - def order(self): - """Return the order of the point. - - None if it is undefined. - """ - return self.__order + x = X / Z² + y = Y / Z³ + """ - def curve(self): - """Return curve over which the point is defined.""" - return self.__curve + def __init__(self, curve, x, y, z, order=None, generator=False): + """ + Initialise a point that uses Jacobi representation internally. + + :param CurveFp curve: curve on which the point resides + :param int x: the X parameter of Jacobi representation (equal to x when + converting from affine coordinates + :param int y: the Y parameter of Jacobi representation (equal to y when + converting from affine coordinates + :param int z: the Z parameter of Jacobi representation (equal to 1 when + converting from affine coordinates + :param int order: the point order, must be non zero when using + generator=True + :param bool generator: the point provided is a curve generator, as + such, it will be commonly used with scalar multiplication. This will + cause to precompute multiplication table for it + """ + self.__curve = curve + # since it's generally better (faster) to use scaled points vs unscaled + # ones, use writer-biased RWLock for locking: + self._scale_lock = RWLock() + if GMPY: + self.__x = mpz(x) + self.__y = mpz(y) + self.__z = mpz(z) + self.__order = order and mpz(order) + else: + self.__x = x + self.__y = y + self.__z = z + self.__order = order + self.__precompute = [] + if generator: + assert order + i = 1 + order *= 2 + doubler = PointJacobi(curve, x, y, z, order) + order *= 2 + self.__precompute.append((doubler.x(), doubler.y())) + + while i < order: + i *= 2 + doubler = doubler.double().scale() + self.__precompute.append((doubler.x(), doubler.y())) + + def __eq__(self, other): + """Compare two points with each-other.""" + try: + self._scale_lock.reader_acquire() + if other is INFINITY: + return not self.__y or not self.__z + x1, y1, z1 = self.__x, self.__y, self.__z + finally: + self._scale_lock.reader_release() + if isinstance(other, Point): + x2, y2, z2 = other.x(), other.y(), 1 + elif isinstance(other, PointJacobi): + try: + other._scale_lock.reader_acquire() + x2, y2, z2 = other.__x, other.__y, other.__z + finally: + other._scale_lock.reader_release() + else: + return NotImplemented + if self.__curve != other.curve(): + return False + p = self.__curve.p() + + zz1 = z1 * z1 % p + zz2 = z2 * z2 % p + + # compare the fractions by bringing them to the same denominator + # depend on short-circuit to save 4 multiplications in case of inequality + return (x1 * zz2 - x2 * zz1) % p == 0 and ( + y1 * zz2 * z2 - y2 * zz1 * z1 + ) % p == 0 + + def order(self): + """Return the order of the point. + + None if it is undefined. + """ + return self.__order - def x(self): - """ - Return affine x coordinate. + def curve(self): + """Return curve over which the point is defined.""" + return self.__curve - This method should be used only when the 'y' coordinate is not needed. - It's computationally more efficient to use `to_affine()` and then - call x() and y() on the returned instance. Or call `scale()` - and then x() and y() on the returned instance. - """ - try: - self._scale_lock.reader_acquire() - if self.__z == 1: - return self.__x - x = self.__x - z = self.__z - finally: - self._scale_lock.reader_release() - p = self.__curve.p() - z = numbertheory.inverse_mod(z, p) - return x * z**2 % p - - def y(self): - """ - Return affine y coordinate. + def x(self): + """ + Return affine x coordinate. - This method should be used only when the 'x' coordinate is not needed. - It's computationally more efficient to use `to_affine()` and then - call x() and y() on the returned instance. Or call `scale()` - and then x() and y() on the returned instance. - """ - try: - self._scale_lock.reader_acquire() - if self.__z == 1: - return self.__y - y = self.__y - z = self.__z - finally: - self._scale_lock.reader_release() - p = self.__curve.p() - z = numbertheory.inverse_mod(z, p) - return y * z**3 % p - - def scale(self): - """ - Return point scaled so that z == 1. + This method should be used only when the 'y' coordinate is not needed. + It's computationally more efficient to use `to_affine()` and then + call x() and y() on the returned instance. Or call `scale()` + and then x() and y() on the returned instance. + """ + try: + self._scale_lock.reader_acquire() + if self.__z == 1: + return self.__x + x = self.__x + z = self.__z + finally: + self._scale_lock.reader_release() + p = self.__curve.p() + z = numbertheory.inverse_mod(z, p) + return x * z ** 2 % p + + def y(self): + """ + Return affine y coordinate. - Modifies point in place, returns self. - """ - try: - self._scale_lock.reader_acquire() - if self.__z == 1: - return self - finally: - self._scale_lock.reader_release() - - try: - self._scale_lock.writer_acquire() - # scaling already scaled point is safe (as inverse of 1 is 1) and - # quick so we don't need to optimise for the unlikely event when - # two threads hit the lock at the same time - p = self.__curve.p() - z_inv = numbertheory.inverse_mod(self.__z, p) - zz_inv = z_inv * z_inv % p - self.__x = self.__x * zz_inv % p - self.__y = self.__y * zz_inv * z_inv % p - # we are setting the z last so that the check above will return true - # only after all values were already updated - self.__z = 1 - finally: - self._scale_lock.writer_release() - return self - - def to_affine(self): - """Return point in affine form.""" - if not self.__y or not self.__z: - return INFINITY - self.scale() - # after point is scaled, it's immutable, so no need to perform locking - return Point(self.__curve, self.__x, - self.__y, self.__order) - - @staticmethod - def from_affine(point, generator=False): - """Create from an affine point. - - :param bool generator: set to True to make the point to precalculate - multiplication table - useful for public point when verifying many - signatures (around 100 or so) or for generator points of a curve. - """ - return PointJacobi(point.curve(), point.x(), point.y(), 1, - point.order(), generator) - - # plese note that all the methods that use the equations from hyperelliptic - # are formatted in a way to maximise performance. - # Things that make code faster: multiplying instead of taking to the power - # (`xx = x * x; xxxx = xx * xx % p` is faster than `xxxx = x**4 % p` and - # `pow(x, 4, p)`), - # multiple assignments at the same time (`x1, x2 = self.x1, self.x2` is - # faster than `x1 = self.x1; x2 = self.x2`), - # similarly, sometimes the `% p` is skipped if it makes the calculation - # faster and the result of calculation is later reduced modulo `p` - - def _double_with_z_1(self, X1, Y1, p, a): - """Add a point to itself with z == 1.""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-mdbl-2007-bl - XX, YY = X1 * X1 % p, Y1 * Y1 % p - if not YY: - return 0, 0, 1 - YYYY = YY * YY % p - S = 2 * ((X1 + YY)**2 - XX - YYYY) % p - M = 3 * XX + a - T = (M * M - 2 * S) % p - # X3 = T - Y3 = (M * (S - T) - 8 * YYYY) % p - Z3 = 2 * Y1 % p - return T, Y3, Z3 - - def _double(self, X1, Y1, Z1, p, a): - """Add a point to itself, arbitrary z.""" - if Z1 == 1: - return self._double_with_z_1(X1, Y1, p, a) - if not Z1: - return 0, 0, 1 - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl - XX, YY = X1 * X1 % p, Y1 * Y1 % p - if not YY: - return 0, 0, 1 - YYYY = YY * YY % p - ZZ = Z1 * Z1 % p - S = 2 * ((X1 + YY)**2 - XX - YYYY) % p - M = (3 * XX + a * ZZ * ZZ) % p - T = (M * M - 2 * S) % p - # X3 = T - Y3 = (M * (S - T) - 8 * YYYY) % p - Z3 = ((Y1 + Z1)**2 - YY - ZZ) % p - - return T, Y3, Z3 - - def double(self): - """Add a point to itself.""" - if not self.__y: - return INFINITY - - p, a = self.__curve.p(), self.__curve.a() - - try: - self._scale_lock.reader_acquire() - X1, Y1, Z1 = self.__x, self.__y, self.__z - finally: - self._scale_lock.reader_release() - - X3, Y3, Z3 = self._double(X1, Y1, Z1, p, a) - - if not Y3 or not Z3: - return INFINITY - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def _add_with_z_1(self, X1, Y1, X2, Y2, p): - """add points when both Z1 and Z2 equal 1""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-mmadd-2007-bl - H = X2 - X1 - HH = H * H - I = 4 * HH % p - J = H * I - r = 2 * (Y2 - Y1) - if not H and not r: - return self._double_with_z_1(X1, Y1, p, self.__curve.a()) - V = X1 * I - X3 = (r**2 - J - 2 * V) % p - Y3 = (r * (V - X3) - 2 * Y1 * J) % p - Z3 = 2 * H % p - return X3, Y3, Z3 - - def _add_with_z_eq(self, X1, Y1, Z1, X2, Y2, p): - """add points when Z1 == Z2""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-zadd-2007-m - A = (X2 - X1)**2 % p - B = X1 * A % p - C = X2 * A - D = (Y2 - Y1)**2 % p - if not A and not D: - return self._double(X1, Y1, Z1, p, self.__curve.a()) - X3 = (D - B - C) % p - Y3 = ((Y2 - Y1) * (B - X3) - Y1 * (C - B)) % p - Z3 = Z1 * (X2 - X1) % p - return X3, Y3, Z3 - - def _add_with_z2_1(self, X1, Y1, Z1, X2, Y2, p): - """add points when Z2 == 1""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-madd-2007-bl - Z1Z1 = Z1 * Z1 % p - U2, S2 = X2 * Z1Z1 % p, Y2 * Z1 * Z1Z1 % p - H = (U2 - X1) % p - HH = H * H % p - I = 4 * HH % p - J = H * I - r = 2 * (S2 - Y1) % p - if not r and not H: - return self._double_with_z_1(X2, Y2, p, self.__curve.a()) - V = X1 * I - X3 = (r * r - J - 2 * V) % p - Y3 = (r * (V - X3) - 2 * Y1 * J) % p - Z3 = ((Z1 + H)**2 - Z1Z1 - HH) % p - return X3, Y3, Z3 - - def _add_with_z_ne(self, X1, Y1, Z1, X2, Y2, Z2, p): - """add points with arbitrary z""" - # after: - # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-2007-bl - Z1Z1 = Z1 * Z1 % p - Z2Z2 = Z2 * Z2 % p - U1 = X1 * Z2Z2 % p - U2 = X2 * Z1Z1 % p - S1 = Y1 * Z2 * Z2Z2 % p - S2 = Y2 * Z1 * Z1Z1 % p - H = U2 - U1 - I = 4 * H * H % p - J = H * I % p - r = 2 * (S2 - S1) % p - if not H and not r: - return self._double(X1, Y1, Z1, p, self.__curve.a()) - V = U1 * I - X3 = (r * r - J - 2 * V) % p - Y3 = (r * (V - X3) - 2 * S1 * J) % p - Z3 = ((Z1 + Z2)**2 - Z1Z1 - Z2Z2) * H % p - - return X3, Y3, Z3 - - def __radd__(self, other): - """Add other to self.""" - return self + other - - def _add(self, X1, Y1, Z1, X2, Y2, Z2, p): - """add two points, select fastest method.""" - if not Y1 or not Z1: - return X2, Y2, Z2 - if not Y2 or not Z2: - return X1, Y1, Z1 - if Z1 == Z2: - if Z1 == 1: - return self._add_with_z_1(X1, Y1, X2, Y2, p) - return self._add_with_z_eq(X1, Y1, Z1, X2, Y2, p) - if Z1 == 1: - return self._add_with_z2_1(X2, Y2, Z2, X1, Y1, p) - if Z2 == 1: - return self._add_with_z2_1(X1, Y1, Z1, X2, Y2, p) - return self._add_with_z_ne(X1, Y1, Z1, X2, Y2, Z2, p) - - def __add__(self, other): - """Add two points on elliptic curve.""" - if self == INFINITY: - return other - if other == INFINITY: - return self - if isinstance(other, Point): - other = PointJacobi.from_affine(other) - if self.__curve != other.__curve: - raise ValueError("The other point is on different curve") - - p = self.__curve.p() - try: - self._scale_lock.reader_acquire() - X1, Y1, Z1 = self.__x, self.__y, self.__z - finally: - self._scale_lock.reader_release() - try: - other._scale_lock.reader_acquire() - X2, Y2, Z2 = other.__x, other.__y, other.__z - finally: - other._scale_lock.reader_release() - X3, Y3, Z3 = self._add(X1, Y1, Z1, X2, Y2, Z2, p) - - if not Y3 or not Z3: - return INFINITY - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def __rmul__(self, other): - """Multiply point by an integer.""" - return self * other - - def _mul_precompute(self, other): - """Multiply point by integer with precomputation table.""" - X3, Y3, Z3, p = 0, 0, 1, self.__curve.p() - _add = self._add - for X2, Y2 in self.__precompute: - if other % 2: - if other % 4 >= 2: - other = (other + 1)//2 - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p) - else: - other = (other - 1)//2 - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) - else: - other //= 2 - - if not Y3 or not Z3: - return INFINITY - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - @staticmethod - def _naf(mult): - """Calculate non-adjacent form of number.""" - ret = [] - while mult: - if mult % 2: - nd = mult % 4 - if nd >= 2: - nd = nd - 4 - ret += [nd] - mult -= nd - else: - ret += [0] - mult //= 2 - return ret - - def __mul__(self, other): - """Multiply point by an integer.""" - if not self.__y or not other: - return INFINITY - if other == 1: - return self - if self.__order: - # order*2 as a protection for Minerva - other = other % (self.__order*2) - if self.__precompute: - return self._mul_precompute(other) - - self = self.scale() - # once scaled, point is immutable, not need to lock - X2, Y2 = self.__x, self.__y - X3, Y3, Z3 = 0, 0, 1 - p, a = self.__curve.p(), self.__curve.a() - _double = self._double - _add = self._add - # since adding points when at least one of them is scaled - # is quicker, reverse the NAF order - for i in reversed(self._naf(other)): - X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) - if i < 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p) - elif i > 0: - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) - - if not Y3 or not Z3: - return INFINITY - - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - @staticmethod - def _leftmost_bit(x): - """Return integer with the same magnitude as x but hamming weight of 1""" - assert x > 0 - result = 1 - while result <= x: - result = 2 * result - return result // 2 - - def mul_add(self, self_mul, other, other_mul): - """ + This method should be used only when the 'x' coordinate is not needed. + It's computationally more efficient to use `to_affine()` and then + call x() and y() on the returned instance. Or call `scale()` + and then x() and y() on the returned instance. + """ + try: + self._scale_lock.reader_acquire() + if self.__z == 1: + return self.__y + y = self.__y + z = self.__z + finally: + self._scale_lock.reader_release() + p = self.__curve.p() + z = numbertheory.inverse_mod(z, p) + return y * z ** 3 % p + + def scale(self): + """ + Return point scaled so that z == 1. + + Modifies point in place, returns self. + """ + try: + self._scale_lock.reader_acquire() + if self.__z == 1: + return self + finally: + self._scale_lock.reader_release() + + try: + self._scale_lock.writer_acquire() + # scaling already scaled point is safe (as inverse of 1 is 1) and + # quick so we don't need to optimise for the unlikely event when + # two threads hit the lock at the same time + p = self.__curve.p() + z_inv = numbertheory.inverse_mod(self.__z, p) + zz_inv = z_inv * z_inv % p + self.__x = self.__x * zz_inv % p + self.__y = self.__y * zz_inv * z_inv % p + # we are setting the z last so that the check above will return true + # only after all values were already updated + self.__z = 1 + finally: + self._scale_lock.writer_release() + return self + + def to_affine(self): + """Return point in affine form.""" + if not self.__y or not self.__z: + return INFINITY + self.scale() + # after point is scaled, it's immutable, so no need to perform locking + return Point(self.__curve, self.__x, self.__y, self.__order) + + @staticmethod + def from_affine(point, generator=False): + """Create from an affine point. + + :param bool generator: set to True to make the point to precalculate + multiplication table - useful for public point when verifying many + signatures (around 100 or so) or for generator points of a curve. + """ + return PointJacobi( + point.curve(), point.x(), point.y(), 1, point.order(), generator + ) + + # plese note that all the methods that use the equations from hyperelliptic + # are formatted in a way to maximise performance. + # Things that make code faster: multiplying instead of taking to the power + # (`xx = x * x; xxxx = xx * xx % p` is faster than `xxxx = x**4 % p` and + # `pow(x, 4, p)`), + # multiple assignments at the same time (`x1, x2 = self.x1, self.x2` is + # faster than `x1 = self.x1; x2 = self.x2`), + # similarly, sometimes the `% p` is skipped if it makes the calculation + # faster and the result of calculation is later reduced modulo `p` + + def _double_with_z_1(self, X1, Y1, p, a): + """Add a point to itself with z == 1.""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-mdbl-2007-bl + XX, YY = X1 * X1 % p, Y1 * Y1 % p + if not YY: + return 0, 0, 1 + YYYY = YY * YY % p + S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p + M = 3 * XX + a + T = (M * M - 2 * S) % p + # X3 = T + Y3 = (M * (S - T) - 8 * YYYY) % p + Z3 = 2 * Y1 % p + return T, Y3, Z3 + + def _double(self, X1, Y1, Z1, p, a): + """Add a point to itself, arbitrary z.""" + if Z1 == 1: + return self._double_with_z_1(X1, Y1, p, a) + if not Z1: + return 0, 0, 1 + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#doubling-dbl-2007-bl + XX, YY = X1 * X1 % p, Y1 * Y1 % p + if not YY: + return 0, 0, 1 + YYYY = YY * YY % p + ZZ = Z1 * Z1 % p + S = 2 * ((X1 + YY) ** 2 - XX - YYYY) % p + M = (3 * XX + a * ZZ * ZZ) % p + T = (M * M - 2 * S) % p + # X3 = T + Y3 = (M * (S - T) - 8 * YYYY) % p + Z3 = ((Y1 + Z1) ** 2 - YY - ZZ) % p + + return T, Y3, Z3 + + def double(self): + """Add a point to itself.""" + if not self.__y: + return INFINITY + + p, a = self.__curve.p(), self.__curve.a() + + try: + self._scale_lock.reader_acquire() + X1, Y1, Z1 = self.__x, self.__y, self.__z + finally: + self._scale_lock.reader_release() + + X3, Y3, Z3 = self._double(X1, Y1, Z1, p, a) + + if not Y3 or not Z3: + return INFINITY + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def _add_with_z_1(self, X1, Y1, X2, Y2, p): + """add points when both Z1 and Z2 equal 1""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-mmadd-2007-bl + H = X2 - X1 + HH = H * H + I = 4 * HH % p + J = H * I + r = 2 * (Y2 - Y1) + if not H and not r: + return self._double_with_z_1(X1, Y1, p, self.__curve.a()) + V = X1 * I + X3 = (r ** 2 - J - 2 * V) % p + Y3 = (r * (V - X3) - 2 * Y1 * J) % p + Z3 = 2 * H % p + return X3, Y3, Z3 + + def _add_with_z_eq(self, X1, Y1, Z1, X2, Y2, p): + """add points when Z1 == Z2""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-zadd-2007-m + A = (X2 - X1) ** 2 % p + B = X1 * A % p + C = X2 * A + D = (Y2 - Y1) ** 2 % p + if not A and not D: + return self._double(X1, Y1, Z1, p, self.__curve.a()) + X3 = (D - B - C) % p + Y3 = ((Y2 - Y1) * (B - X3) - Y1 * (C - B)) % p + Z3 = Z1 * (X2 - X1) % p + return X3, Y3, Z3 + + def _add_with_z2_1(self, X1, Y1, Z1, X2, Y2, p): + """add points when Z2 == 1""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-madd-2007-bl + Z1Z1 = Z1 * Z1 % p + U2, S2 = X2 * Z1Z1 % p, Y2 * Z1 * Z1Z1 % p + H = (U2 - X1) % p + HH = H * H % p + I = 4 * HH % p + J = H * I + r = 2 * (S2 - Y1) % p + if not r and not H: + return self._double_with_z_1(X2, Y2, p, self.__curve.a()) + V = X1 * I + X3 = (r * r - J - 2 * V) % p + Y3 = (r * (V - X3) - 2 * Y1 * J) % p + Z3 = ((Z1 + H) ** 2 - Z1Z1 - HH) % p + return X3, Y3, Z3 + + def _add_with_z_ne(self, X1, Y1, Z1, X2, Y2, Z2, p): + """add points with arbitrary z""" + # after: + # http://hyperelliptic.org/EFD/g1p/auto-shortw-jacobian.html#addition-add-2007-bl + Z1Z1 = Z1 * Z1 % p + Z2Z2 = Z2 * Z2 % p + U1 = X1 * Z2Z2 % p + U2 = X2 * Z1Z1 % p + S1 = Y1 * Z2 * Z2Z2 % p + S2 = Y2 * Z1 * Z1Z1 % p + H = U2 - U1 + I = 4 * H * H % p + J = H * I % p + r = 2 * (S2 - S1) % p + if not H and not r: + return self._double(X1, Y1, Z1, p, self.__curve.a()) + V = U1 * I + X3 = (r * r - J - 2 * V) % p + Y3 = (r * (V - X3) - 2 * S1 * J) % p + Z3 = ((Z1 + Z2) ** 2 - Z1Z1 - Z2Z2) * H % p + + return X3, Y3, Z3 + + def __radd__(self, other): + """Add other to self.""" + return self + other + + def _add(self, X1, Y1, Z1, X2, Y2, Z2, p): + """add two points, select fastest method.""" + if not Y1 or not Z1: + return X2, Y2, Z2 + if not Y2 or not Z2: + return X1, Y1, Z1 + if Z1 == Z2: + if Z1 == 1: + return self._add_with_z_1(X1, Y1, X2, Y2, p) + return self._add_with_z_eq(X1, Y1, Z1, X2, Y2, p) + if Z1 == 1: + return self._add_with_z2_1(X2, Y2, Z2, X1, Y1, p) + if Z2 == 1: + return self._add_with_z2_1(X1, Y1, Z1, X2, Y2, p) + return self._add_with_z_ne(X1, Y1, Z1, X2, Y2, Z2, p) + + def __add__(self, other): + """Add two points on elliptic curve.""" + if self == INFINITY: + return other + if other == INFINITY: + return self + if isinstance(other, Point): + other = PointJacobi.from_affine(other) + if self.__curve != other.__curve: + raise ValueError("The other point is on different curve") + + p = self.__curve.p() + try: + self._scale_lock.reader_acquire() + X1, Y1, Z1 = self.__x, self.__y, self.__z + finally: + self._scale_lock.reader_release() + try: + other._scale_lock.reader_acquire() + X2, Y2, Z2 = other.__x, other.__y, other.__z + finally: + other._scale_lock.reader_release() + X3, Y3, Z3 = self._add(X1, Y1, Z1, X2, Y2, Z2, p) + + if not Y3 or not Z3: + return INFINITY + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def __rmul__(self, other): + """Multiply point by an integer.""" + return self * other + + def _mul_precompute(self, other): + """Multiply point by integer with precomputation table.""" + X3, Y3, Z3, p = 0, 0, 1, self.__curve.p() + _add = self._add + for X2, Y2 in self.__precompute: + if other % 2: + if other % 4 >= 2: + other = (other + 1) // 2 + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p) + else: + other = (other - 1) // 2 + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) + else: + other //= 2 + + if not Y3 or not Z3: + return INFINITY + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + @staticmethod + def _naf(mult): + """Calculate non-adjacent form of number.""" + ret = [] + while mult: + if mult % 2: + nd = mult % 4 + if nd >= 2: + nd = nd - 4 + ret += [nd] + mult -= nd + else: + ret += [0] + mult //= 2 + return ret + + def __mul__(self, other): + """Multiply point by an integer.""" + if not self.__y or not other: + return INFINITY + if other == 1: + return self + if self.__order: + # order*2 as a protection for Minerva + other = other % (self.__order * 2) + if self.__precompute: + return self._mul_precompute(other) + + self = self.scale() + # once scaled, point is immutable, not need to lock + X2, Y2 = self.__x, self.__y + X3, Y3, Z3 = 0, 0, 1 + p, a = self.__curve.p(), self.__curve.a() + _double = self._double + _add = self._add + # since adding points when at least one of them is scaled + # is quicker, reverse the NAF order + for i in reversed(self._naf(other)): + X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) + if i < 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, -Y2, 1, p) + elif i > 0: + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) + + if not Y3 or not Z3: + return INFINITY + + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + @staticmethod + def _leftmost_bit(x): + """Return integer with the same magnitude as x but hamming weight of 1""" + assert x > 0 + result = 1 + while result <= x: + result = 2 * result + return result // 2 + + def mul_add(self, self_mul, other, other_mul): + """ Do two multiplications at the same time, add results. calculates self*self_mul + other*other_mul """ - if other is INFINITY or other_mul == 0: - return self * self_mul - if self_mul == 0: - return other * other_mul - if not isinstance(other, PointJacobi): - other = PointJacobi.from_affine(other) - # when the points have precomputed answers, then multiplying them alone - # is faster (as it uses NAF) - if self.__precompute and other.__precompute: - return self * self_mul + other * other_mul - - if self.__order: - self_mul = self_mul % self.__order - other_mul = other_mul % self.__order - - i = self._leftmost_bit(max(self_mul, other_mul))*2 - X3, Y3, Z3 = 0, 0, 1 - p, a = self.__curve.p(), self.__curve.a() - self = self.scale() - # after scaling, point is immutable, no need for locking - X1, Y1 = self.__x, self.__y - other = other.scale() - X2, Y2 = other.__x, other.__y - both = (self + other).scale() - X4, Y4 = both.__x, both.__y - _double = self._double - _add = self._add - while i > 1: - X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) - i = i // 2 - - if self_mul & i and other_mul & i: - X3, Y3, Z3 = _add(X3, Y3, Z3, X4, Y4, 1, p) - elif self_mul & i: - X3, Y3, Z3 = _add(X3, Y3, Z3, X1, Y1, 1, p) - elif other_mul & i: - X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) - - if not Y3 or not Z3: - return INFINITY - - return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) - - def __neg__(self): - """Return negated point.""" - try: - self._scale_lock.reader_acquire() - return PointJacobi(self.__curve, self.__x, -self.__y, self.__z, - self.__order) - finally: - self._scale_lock.reader_release() + if other is INFINITY or other_mul == 0: + return self * self_mul + if self_mul == 0: + return other * other_mul + if not isinstance(other, PointJacobi): + other = PointJacobi.from_affine(other) + # when the points have precomputed answers, then multiplying them alone + # is faster (as it uses NAF) + if self.__precompute and other.__precompute: + return self * self_mul + other * other_mul + + if self.__order: + self_mul = self_mul % self.__order + other_mul = other_mul % self.__order + + i = self._leftmost_bit(max(self_mul, other_mul)) * 2 + X3, Y3, Z3 = 0, 0, 1 + p, a = self.__curve.p(), self.__curve.a() + self = self.scale() + # after scaling, point is immutable, no need for locking + X1, Y1 = self.__x, self.__y + other = other.scale() + X2, Y2 = other.__x, other.__y + both = (self + other).scale() + X4, Y4 = both.__x, both.__y + _double = self._double + _add = self._add + while i > 1: + X3, Y3, Z3 = _double(X3, Y3, Z3, p, a) + i = i // 2 + + if self_mul & i and other_mul & i: + X3, Y3, Z3 = _add(X3, Y3, Z3, X4, Y4, 1, p) + elif self_mul & i: + X3, Y3, Z3 = _add(X3, Y3, Z3, X1, Y1, 1, p) + elif other_mul & i: + X3, Y3, Z3 = _add(X3, Y3, Z3, X2, Y2, 1, p) + + if not Y3 or not Z3: + return INFINITY + + return PointJacobi(self.__curve, X3, Y3, Z3, self.__order) + + def __neg__(self): + """Return negated point.""" + try: + self._scale_lock.reader_acquire() + return PointJacobi( + self.__curve, self.__x, -self.__y, self.__z, self.__order + ) + finally: + self._scale_lock.reader_release() class Point(object): - """A point on an elliptic curve. Altering x and y is forbidding, + """A point on an elliptic curve. Altering x and y is forbidding, but they can be read by the x() and y() methods.""" - def __init__(self, curve, x, y, order=None): - """curve, x, y, order; order (optional) is the order of this point.""" - self.__curve = curve - if GMPY: - self.__x = x and mpz(x) - self.__y = y and mpz(y) - self.__order = order and mpz(order) - else: - self.__x = x - self.__y = y - self.__order = order - # self.curve is allowed to be None only for INFINITY: - if self.__curve: - assert self.__curve.contains_point(x, y) - # for curves with cofactor 1, all points that are on the curve are scalar - # multiples of the base point, so performing multiplication is not - # necessary to verify that. See Section 3.2.2.1 of SEC 1 v2 - if curve and curve.cofactor() != 1 and order: - assert self * order == INFINITY - - def __eq__(self, other): - """Return True if the points are identical, False otherwise.""" - if isinstance(other, Point): - return self.__curve == other.__curve \ - and self.__x == other.__x \ - and self.__y == other.__y - return NotImplemented - - def __neg__(self): - return Point(self.__curve, self.__x, self.__curve.p() - self.__y) - - def __add__(self, other): - """Add one point to another point.""" - - # X9.62 B.3: - - if not isinstance(other, Point): + + def __init__(self, curve, x, y, order=None): + """curve, x, y, order; order (optional) is the order of this point.""" + self.__curve = curve + if GMPY: + self.__x = x and mpz(x) + self.__y = y and mpz(y) + self.__order = order and mpz(order) + else: + self.__x = x + self.__y = y + self.__order = order + # self.curve is allowed to be None only for INFINITY: + if self.__curve: + assert self.__curve.contains_point(x, y) + # for curves with cofactor 1, all points that are on the curve are scalar + # multiples of the base point, so performing multiplication is not + # necessary to verify that. See Section 3.2.2.1 of SEC 1 v2 + if curve and curve.cofactor() != 1 and order: + assert self * order == INFINITY + + def __eq__(self, other): + """Return True if the points are identical, False otherwise.""" + if isinstance(other, Point): + return ( + self.__curve == other.__curve + and self.__x == other.__x + and self.__y == other.__y + ) return NotImplemented - if other == INFINITY: - return self - if self == INFINITY: - return other - assert self.__curve == other.__curve - if self.__x == other.__x: - if (self.__y + other.__y) % self.__curve.p() == 0: - return INFINITY - else: - return self.double() - p = self.__curve.p() + def __neg__(self): + return Point(self.__curve, self.__x, self.__curve.p() - self.__y) + + def __add__(self, other): + """Add one point to another point.""" + + # X9.62 B.3: + + if not isinstance(other, Point): + return NotImplemented + if other == INFINITY: + return self + if self == INFINITY: + return other + assert self.__curve == other.__curve + if self.__x == other.__x: + if (self.__y + other.__y) % self.__curve.p() == 0: + return INFINITY + else: + return self.double() - l = ((other.__y - self.__y) * \ - numbertheory.inverse_mod(other.__x - self.__x, p)) % p + p = self.__curve.p() - x3 = (l * l - self.__x - other.__x) % p - y3 = (l * (self.__x - x3) - self.__y) % p + l = ( + (other.__y - self.__y) + * numbertheory.inverse_mod(other.__x - self.__x, p) + ) % p - return Point(self.__curve, x3, y3) + x3 = (l * l - self.__x - other.__x) % p + y3 = (l * (self.__x - x3) - self.__y) % p - def __mul__(self, other): - """Multiply a point by an integer.""" + return Point(self.__curve, x3, y3) - def leftmost_bit(x): - assert x > 0 - result = 1 - while result <= x: - result = 2 * result - return result // 2 + def __mul__(self, other): + """Multiply a point by an integer.""" - e = other - if e == 0 or (self.__order and e % self.__order == 0): - return INFINITY - if self == INFINITY: - return INFINITY - if e < 0: - return (-self) * (-e) + def leftmost_bit(x): + assert x > 0 + result = 1 + while result <= x: + result = 2 * result + return result // 2 - # From X9.62 D.3.2: + e = other + if e == 0 or (self.__order and e % self.__order == 0): + return INFINITY + if self == INFINITY: + return INFINITY + if e < 0: + return (-self) * (-e) - e3 = 3 * e - negative_self = Point(self.__curve, self.__x, -self.__y, self.__order) - i = leftmost_bit(e3) // 2 - result = self - # print_("Multiplying %s by %d (e3 = %d):" % (self, other, e3)) - while i > 1: - result = result.double() - if (e3 & i) != 0 and (e & i) == 0: - result = result + self - if (e3 & i) == 0 and (e & i) != 0: - result = result + negative_self - # print_(". . . i = %d, result = %s" % ( i, result )) - i = i // 2 + # From X9.62 D.3.2: - return result + e3 = 3 * e + negative_self = Point(self.__curve, self.__x, -self.__y, self.__order) + i = leftmost_bit(e3) // 2 + result = self + # print_("Multiplying %s by %d (e3 = %d):" % (self, other, e3)) + while i > 1: + result = result.double() + if (e3 & i) != 0 and (e & i) == 0: + result = result + self + if (e3 & i) == 0 and (e & i) != 0: + result = result + negative_self + # print_(". . . i = %d, result = %s" % ( i, result )) + i = i // 2 - def __rmul__(self, other): - """Multiply a point by an integer.""" + return result - return self * other + def __rmul__(self, other): + """Multiply a point by an integer.""" - def __str__(self): - if self == INFINITY: - return "infinity" - return "(%d,%d)" % (self.__x, self.__y) + return self * other - def double(self): - """Return a new point that is twice the old.""" + def __str__(self): + if self == INFINITY: + return "infinity" + return "(%d,%d)" % (self.__x, self.__y) - if self == INFINITY: - return INFINITY + def double(self): + """Return a new point that is twice the old.""" - # X9.62 B.3: + if self == INFINITY: + return INFINITY - p = self.__curve.p() - a = self.__curve.a() + # X9.62 B.3: - l = ((3 * self.__x * self.__x + a) * \ - numbertheory.inverse_mod(2 * self.__y, p)) % p + p = self.__curve.p() + a = self.__curve.a() - x3 = (l * l - 2 * self.__x) % p - y3 = (l * (self.__x - x3) - self.__y) % p + l = ( + (3 * self.__x * self.__x + a) + * numbertheory.inverse_mod(2 * self.__y, p) + ) % p - return Point(self.__curve, x3, y3) + x3 = (l * l - 2 * self.__x) % p + y3 = (l * (self.__x - x3) - self.__y) % p - def x(self): - return self.__x + return Point(self.__curve, x3, y3) - def y(self): - return self.__y + def x(self): + return self.__x - def curve(self): - return self.__curve + def y(self): + return self.__y - def order(self): - return self.__order + def curve(self): + return self.__curve + + def order(self): + return self.__order # This one point is the Point At Infinity for all purposes: diff --git a/src/ecdsa/keys.py b/src/ecdsa/keys.py index a8cefb94..0de59ebd 100644 --- a/src/ecdsa/keys.py +++ b/src/ecdsa/keys.py @@ -82,8 +82,13 @@ from ._compat import normalise_bytes -__all__ = ["BadSignatureError", "BadDigestError", "VerifyingKey", "SigningKey", - "MalformedPointError"] +__all__ = [ + "BadSignatureError", + "BadDigestError", + "VerifyingKey", + "SigningKey", + "MalformedPointError", +] class BadSignatureError(Exception): @@ -129,8 +134,9 @@ class VerifyingKey(object): def __init__(self, _error__please_use_generate=None): """Unsupported, please use one of the classmethods to initialise.""" if not _error__please_use_generate: - raise TypeError("Please use VerifyingKey.generate() to " - "construct me") + raise TypeError( + "Please use VerifyingKey.generate() to construct me" + ) self.curve = None self.default_hashfunc = None self.pubkey = None @@ -138,18 +144,19 @@ def __init__(self, _error__please_use_generate=None): def __repr__(self): pub_key = self.to_string("compressed") return "VerifyingKey.from_string({0!r}, {1!r}, {2})".format( - pub_key, self.curve, self.default_hashfunc().name) + pub_key, self.curve, self.default_hashfunc().name + ) def __eq__(self, other): """Return True if the points are identical, False otherwise.""" if isinstance(other, VerifyingKey): - return self.curve == other.curve \ - and self.pubkey == other.pubkey + return self.curve == other.curve and self.pubkey == other.pubkey return NotImplemented @classmethod - def from_public_point(cls, point, curve=NIST192p, hashfunc=sha1, - validate_point=True): + def from_public_point( + cls, point, curve=NIST192p, hashfunc=sha1, validate_point=True + ): """ Initialise the object from a Point object. @@ -180,8 +187,9 @@ def from_public_point(cls, point, curve=NIST192p, hashfunc=sha1, self.curve = curve self.default_hashfunc = hashfunc try: - self.pubkey = ecdsa.Public_key(curve.generator, point, - validate_point) + self.pubkey = ecdsa.Public_key( + curve.generator, point, validate_point + ) except ecdsa.InvalidPointError: raise MalformedPointError("Point does not lie on the curve") self.pubkey.order = curve.order @@ -189,7 +197,8 @@ def from_public_point(cls, point, curve=NIST192p, hashfunc=sha1, def precompute(self): self.pubkey.point = ellipticcurve.PointJacobi.from_affine( - self.pubkey.point, True) + self.pubkey.point, True + ) @staticmethod def _from_raw_encoding(string, curve): @@ -202,8 +211,8 @@ def _from_raw_encoding(string, curve): order = curve.order # real assert, from_string() should not call us with different length assert len(string) == curve.verifying_key_length - xs = string[:curve.baselen] - ys = string[curve.baselen:] + xs = string[: curve.baselen] + ys = string[curve.baselen :] if len(xs) != curve.baselen: raise MalformedPointError("Unexpected length of encoded x") if len(ys) != curve.baselen: @@ -216,10 +225,10 @@ def _from_raw_encoding(string, curve): @staticmethod def _from_compressed(string, curve): """Decode public point from compressed encoding.""" - if string[:1] not in (b('\x02'), b('\x03')): + if string[:1] not in (b("\x02"), b("\x03")): raise MalformedPointError("Malformed compressed point encoding") - is_even = string[:1] == b('\x02') + is_even = string[:1] == b("\x02") x = string_to_number(string[1:]) order = curve.order p = curve.curve.p() @@ -228,7 +237,8 @@ def _from_compressed(string, curve): beta = square_root_mod_prime(alpha, p) except SquareRootError as e: raise MalformedPointError( - "Encoding does not correspond to a point on curve", e) + "Encoding does not correspond to a point on curve", e + ) if is_even == bool(beta & 1): y = p - beta else: @@ -239,22 +249,26 @@ def _from_compressed(string, curve): def _from_hybrid(cls, string, curve, validate_point): """Decode public point from hybrid encoding.""" # real assert, from_string() should not call us with different types - assert string[:1] in (b('\x06'), b('\x07')) + assert string[:1] in (b("\x06"), b("\x07")) # primarily use the uncompressed as it's easiest to handle point = cls._from_raw_encoding(string[1:], curve) # but validate if it's self-consistent if we're asked to do that - if validate_point \ - and (point.y() & 1 and string[:1] != b('\x07') - or (not point.y() & 1) and string[:1] != b('\x06')): + if validate_point and ( + point.y() & 1 + and string[:1] != b("\x07") + or (not point.y() & 1) + and string[:1] != b("\x06") + ): raise MalformedPointError("Inconsistent hybrid point encoding") return point @classmethod - def from_string(cls, string, curve=NIST192p, hashfunc=sha1, - validate_point=True): + def from_string( + cls, string, curve=NIST192p, hashfunc=sha1, validate_point=True + ): """ Initialise the object from byte encoding of public key. @@ -288,22 +302,23 @@ def from_string(cls, string, curve=NIST192p, hashfunc=sha1, if sig_len == curve.verifying_key_length: point = cls._from_raw_encoding(string, curve) elif sig_len == curve.verifying_key_length + 1: - if string[:1] in (b('\x06'), b('\x07')): + if string[:1] in (b("\x06"), b("\x07")): point = cls._from_hybrid(string, curve, validate_point) - elif string[:1] == b('\x04'): + elif string[:1] == b("\x04"): point = cls._from_raw_encoding(string[1:], curve) else: raise MalformedPointError( - "Invalid X9.62 encoding of the public point") + "Invalid X9.62 encoding of the public point" + ) elif sig_len == curve.baselen + 1: point = cls._from_compressed(string, curve) else: raise MalformedPointError( "Length of string does not match lengths of " "any of the supported encodings of {0} " - "curve.".format(curve.name)) - return cls.from_public_point(point, curve, hashfunc, - validate_point) + "curve.".format(curve.name) + ) + return cls.from_public_point(point, curve, hashfunc, validate_point) @classmethod def from_pem(cls, string, hashfunc=sha1): @@ -360,31 +375,39 @@ def from_der(cls, string, hashfunc=sha1): # [[oid_ecPublicKey,oid_curve], point_str_bitstring] s1, empty = der.remove_sequence(string) if empty != b"": - raise der.UnexpectedDER("trailing junk after DER pubkey: %s" % - binascii.hexlify(empty)) + raise der.UnexpectedDER( + "trailing junk after DER pubkey: %s" % binascii.hexlify(empty) + ) s2, point_str_bitstring = der.remove_sequence(s1) # s2 = oid_ecPublicKey,oid_curve oid_pk, rest = der.remove_object(s2) oid_curve, empty = der.remove_object(rest) if empty != b"": - raise der.UnexpectedDER("trailing junk after DER pubkey objects: %s" % - binascii.hexlify(empty)) + raise der.UnexpectedDER( + "trailing junk after DER pubkey objects: %s" + % binascii.hexlify(empty) + ) if not oid_pk == oid_ecPublicKey: - raise der.UnexpectedDER("Unexpected object identifier in DER " - "encoding: {0!r}".format(oid_pk)) + raise der.UnexpectedDER( + "Unexpected object identifier in DER " + "encoding: {0!r}".format(oid_pk) + ) curve = find_curve(oid_curve) point_str, empty = der.remove_bitstring(point_str_bitstring, 0) if empty != b"": - raise der.UnexpectedDER("trailing junk after pubkey pointstring: %s" % - binascii.hexlify(empty)) + raise der.UnexpectedDER( + "trailing junk after pubkey pointstring: %s" + % binascii.hexlify(empty) + ) # raw encoding of point is invalid in DER files if len(point_str) == curve.verifying_key_length: raise der.UnexpectedDER("Malformed encoding of public point") return cls.from_string(point_str, curve, hashfunc=hashfunc) @classmethod - def from_public_key_recovery(cls, signature, data, curve, hashfunc=sha1, - sigdecode=sigdecode_string): + def from_public_key_recovery( + cls, signature, data, curve, hashfunc=sha1, sigdecode=sigdecode_string + ): """ Return keys that can be used as verifiers of the provided signature. @@ -414,13 +437,18 @@ def from_public_key_recovery(cls, signature, data, curve, hashfunc=sha1, data = normalise_bytes(data) digest = hashfunc(data).digest() return cls.from_public_key_recovery_with_digest( - signature, digest, curve, hashfunc=hashfunc, - sigdecode=sigdecode) + signature, digest, curve, hashfunc=hashfunc, sigdecode=sigdecode + ) @classmethod def from_public_key_recovery_with_digest( - cls, signature, digest, curve, - hashfunc=sha1, sigdecode=sigdecode_string): + cls, + signature, + digest, + curve, + hashfunc=sha1, + sigdecode=sigdecode_string, + ): """ Return keys that can be used as verifiers of the provided signature. @@ -457,8 +485,9 @@ def from_public_key_recovery_with_digest( pks = sig.recover_public_keys(digest_as_number, generator) # Transforms the ecdsa.Public_key object into a VerifyingKey - verifying_keys = [cls.from_public_point(pk.point, curve, hashfunc) - for pk in pks] + verifying_keys = [ + cls.from_public_point(pk.point, curve, hashfunc) for pk in pks + ] return verifying_keys def _raw_encode(self): @@ -473,17 +502,17 @@ def _compressed_encode(self): order = self.pubkey.order x_str = number_to_string(self.pubkey.point.x(), order) if self.pubkey.point.y() & 1: - return b('\x03') + x_str + return b("\x03") + x_str else: - return b('\x02') + x_str + return b("\x02") + x_str def _hybrid_encode(self): """Encode the public point into the hybrid form.""" raw_enc = self._raw_encode() if self.pubkey.point.y() & 1: - return b('\x07') + raw_enc + return b("\x07") + raw_enc else: - return b('\x06') + raw_enc + return b("\x06") + raw_enc def to_string(self, encoding="raw"): """ @@ -509,7 +538,7 @@ def to_string(self, encoding="raw"): if encoding == "raw": return self._raw_encode() elif encoding == "uncompressed": - return b('\x04') + self._raw_encode() + return b("\x04") + self._raw_encode() elif encoding == "hybrid": return self._hybrid_encode() else: @@ -557,14 +586,18 @@ def to_der(self, point_encoding="uncompressed"): if point_encoding == "raw": raise ValueError("raw point_encoding not allowed in DER") point_str = self.to_string(point_encoding) - return der.encode_sequence(der.encode_sequence(encoded_oid_ecPublicKey, - self.curve.encoded_oid), - # 0 is the number of unused bits in the - # bit string - der.encode_bitstring(point_str, 0)) - - def verify(self, signature, data, hashfunc=None, - sigdecode=sigdecode_string): + return der.encode_sequence( + der.encode_sequence( + encoded_oid_ecPublicKey, self.curve.encoded_oid + ), + # 0 is the number of unused bits in the + # bit string + der.encode_bitstring(point_str, 0), + ) + + def verify( + self, signature, data, hashfunc=None, sigdecode=sigdecode_string + ): """ Verify a signature made over provided data. @@ -604,8 +637,13 @@ def verify(self, signature, data, hashfunc=None, digest = hashfunc(data).digest() return self.verify_digest(signature, digest, sigdecode, True) - def verify_digest(self, signature, digest, sigdecode=sigdecode_string, - allow_truncate=False): + def verify_digest( + self, + signature, + digest, + sigdecode=sigdecode_string, + allow_truncate=False, + ): """ Verify a signature made over provided hash value. @@ -641,11 +679,12 @@ def verify_digest(self, signature, digest, sigdecode=sigdecode_string, # it, the decoders will do that digest = normalise_bytes(digest) if allow_truncate: - digest = digest[:self.curve.baselen] + digest = digest[: self.curve.baselen] if len(digest) > self.curve.baselen: - raise BadDigestError("this curve (%s) is too short " - "for your digest (%d)" % (self.curve.name, - 8 * len(digest))) + raise BadDigestError( + "this curve (%s) is too short " + "for your digest (%d)" % (self.curve.name, 8 * len(digest)) + ) number = string_to_number(digest) try: r, s = sigdecode(signature, self.pubkey.order) @@ -684,9 +723,11 @@ def __init__(self, _error__please_use_generate=None): def __eq__(self, other): """Return True if the points are identical, False otherwise.""" if isinstance(other, SigningKey): - return self.curve == other.curve \ - and self.verifying_key == other.verifying_key \ + return ( + self.curve == other.curve + and self.verifying_key == other.verifying_key and self.privkey == other.privkey + ) return NotImplemented @classmethod @@ -744,13 +785,16 @@ def from_secret_exponent(cls, secexp, curve=NIST192p, hashfunc=sha1): n = curve.order if not 1 <= secexp < n: raise MalformedPointError( - "Invalid value for secexp, expected integer between 1 and {0}" - .format(n)) + "Invalid value for secexp, expected integer between 1 and {0}".format( + n + ) + ) pubkey_point = curve.generator * secexp if hasattr(pubkey_point, "scale"): pubkey_point = pubkey_point.scale() - self.verifying_key = VerifyingKey.from_public_point(pubkey_point, curve, - hashfunc, False) + self.verifying_key = VerifyingKey.from_public_point( + pubkey_point, curve, hashfunc, False + ) pubkey = self.verifying_key.pubkey self.privkey = ecdsa.Private_key(pubkey, secexp) self.privkey.order = n @@ -785,8 +829,10 @@ def from_string(cls, string, curve=NIST192p, hashfunc=sha1): string = normalise_bytes(string) if len(string) != curve.baselen: raise MalformedPointError( - "Invalid length of private key, received {0}, expected {1}" - .format(len(string), curve.baselen)) + "Invalid length of private key, received {0}, expected {1}".format( + len(string), curve.baselen + ) + ) secexp = string_to_number(string) return cls.from_secret_exponent(secexp, curve, hashfunc) @@ -828,7 +874,9 @@ def from_pem(cls, string, hashfunc=sha1): # "EC PARAMETERS", we need just "EC PRIVATE KEY". if not PY2 and isinstance(string, str): string = string.encode() - privkey_pem = string[string.index(b("-----BEGIN EC PRIVATE KEY-----")):] + privkey_pem = string[ + string.index(b("-----BEGIN EC PRIVATE KEY-----")) : + ] return cls.from_der(der.unpem(privkey_pem), hashfunc) @classmethod @@ -878,21 +926,26 @@ def from_der(cls, string, hashfunc=sha1): string = normalise_bytes(string) s, empty = der.remove_sequence(string) if empty != b(""): - raise der.UnexpectedDER("trailing junk after DER privkey: %s" % - binascii.hexlify(empty)) + raise der.UnexpectedDER( + "trailing junk after DER privkey: %s" % binascii.hexlify(empty) + ) one, s = der.remove_integer(s) if one != 1: - raise der.UnexpectedDER("expected '1' at start of DER privkey," - " got %d" % one) + raise der.UnexpectedDER( + "expected '1' at start of DER privkey, got %d" % one + ) privkey_str, s = der.remove_octet_string(s) tag, curve_oid_str, s = der.remove_constructed(s) if tag != 0: - raise der.UnexpectedDER("expected tag 0 in DER privkey," - " got %d" % tag) + raise der.UnexpectedDER( + "expected tag 0 in DER privkey, got %d" % tag + ) curve_oid, empty = der.remove_object(curve_oid_str) if empty != b(""): - raise der.UnexpectedDER("trailing junk after DER privkey " - "curve_oid: %s" % binascii.hexlify(empty)) + raise der.UnexpectedDER( + "trailing junk after DER privkey " + "curve_oid: %s" % binascii.hexlify(empty) + ) curve = find_curve(curve_oid) # we don't actually care about the following fields @@ -908,7 +961,9 @@ def from_der(cls, string, hashfunc=sha1): # our from_string method likes fixed-length privkey strings if len(privkey_str) < curve.baselen: - privkey_str = b("\x00") * (curve.baselen - len(privkey_str)) + privkey_str + privkey_str = ( + b("\x00") * (curve.baselen - len(privkey_str)) + privkey_str + ) return cls.from_string(privkey_str, curve, hashfunc) def to_string(self): @@ -973,7 +1028,8 @@ def to_der(self, point_encoding="uncompressed"): der.encode_integer(1), der.encode_octet_string(self.to_string()), der.encode_constructed(0, self.curve.encoded_oid), - der.encode_constructed(1, der.encode_bitstring(encoded_vk, 0))) + der.encode_constructed(1, der.encode_bitstring(encoded_vk, 0)), + ) def get_verifying_key(self): """ @@ -987,9 +1043,13 @@ def get_verifying_key(self): """ return self.verifying_key - def sign_deterministic(self, data, hashfunc=None, - sigencode=sigencode_string, - extra_entropy=b''): + def sign_deterministic( + self, + data, + hashfunc=None, + sigencode=sigencode_string, + extra_entropy=b"", + ): """ Create signature over data using the deterministic RFC6679 algorithm. @@ -1026,12 +1086,21 @@ def sign_deterministic(self, data, hashfunc=None, digest = hashfunc(data).digest() return self.sign_digest_deterministic( - digest, hashfunc=hashfunc, sigencode=sigencode, - extra_entropy=extra_entropy, allow_truncate=True) - - def sign_digest_deterministic(self, digest, hashfunc=None, - sigencode=sigencode_string, - extra_entropy=b'', allow_truncate=False): + digest, + hashfunc=hashfunc, + sigencode=sigencode, + extra_entropy=extra_entropy, + allow_truncate=True, + ) + + def sign_digest_deterministic( + self, + digest, + hashfunc=None, + sigencode=sigencode_string, + extra_entropy=b"", + allow_truncate=False, + ): """ Create signature for digest using the deterministic RFC6679 algorithm. @@ -1079,21 +1148,34 @@ def simple_r_s(r, s, order): retry_gen = 0 while True: k = rfc6979.generate_k( - self.curve.generator.order(), secexp, hashfunc, digest, - retry_gen=retry_gen, extra_entropy=extra_entropy) + self.curve.generator.order(), + secexp, + hashfunc, + digest, + retry_gen=retry_gen, + extra_entropy=extra_entropy, + ) try: - r, s, order = self.sign_digest(digest, - sigencode=simple_r_s, - k=k, - allow_truncate=allow_truncate) + r, s, order = self.sign_digest( + digest, + sigencode=simple_r_s, + k=k, + allow_truncate=allow_truncate, + ) break except RSZeroError: retry_gen += 1 return sigencode(r, s, order) - def sign(self, data, entropy=None, hashfunc=None, - sigencode=sigencode_string, k=None): + def sign( + self, + data, + entropy=None, + hashfunc=None, + sigencode=sigencode_string, + k=None, + ): """ Create signature over data using the probabilistic ECDSA algorithm. @@ -1142,8 +1224,14 @@ def sign(self, data, entropy=None, hashfunc=None, h = hashfunc(data).digest() return self.sign_digest(h, entropy, sigencode, k, allow_truncate=True) - def sign_digest(self, digest, entropy=None, sigencode=sigencode_string, - k=None, allow_truncate=False): + def sign_digest( + self, + digest, + entropy=None, + sigencode=sigencode_string, + k=None, + allow_truncate=False, + ): """ Create signature over digest using the probabilistic ECDSA algorithm. @@ -1183,11 +1271,12 @@ def sign_digest(self, digest, entropy=None, sigencode=sigencode_string, """ digest = normalise_bytes(digest) if allow_truncate: - digest = digest[:self.curve.baselen] + digest = digest[: self.curve.baselen] if len(digest) > self.curve.baselen: - raise BadDigestError("this curve (%s) is too short " - "for your digest (%d)" % (self.curve.name, - 8 * len(digest))) + raise BadDigestError( + "this curve (%s) is too short " + "for your digest (%d)" % (self.curve.name, 8 * len(digest)) + ) number = string_to_number(digest) r, s = self.sign_number(number, entropy, k) return sigencode(r, s, self.privkey.order) diff --git a/src/ecdsa/numbertheory.py b/src/ecdsa/numbertheory.py index 0fa02cba..e5cc888d 100644 --- a/src/ecdsa/numbertheory.py +++ b/src/ecdsa/numbertheory.py @@ -13,18 +13,21 @@ from six import integer_types, PY2 from six.moves import reduce + try: xrange except NameError: xrange = range try: from gmpy2 import powmod + GMPY2 = True GMPY = False except ImportError: GMPY2 = False try: from gmpy import mpz + GMPY = True except ImportError: GMPY = False @@ -34,191 +37,199 @@ class Error(Exception): - """Base class for exceptions in this module.""" - pass + """Base class for exceptions in this module.""" + + pass class SquareRootError(Error): - pass + pass class NegativeExponentError(Error): - pass + pass def modular_exp(base, exponent, modulus): # pragma: no cover """Raise base to exponent, reducing by modulus""" # deprecated in 0.14 - warnings.warn("Function is unused in library code. If you use this code, " - "change to pow() builtin.", DeprecationWarning) + warnings.warn( + "Function is unused in library code. If you use this code, " + "change to pow() builtin.", + DeprecationWarning, + ) if exponent < 0: - raise NegativeExponentError("Negative exponents (%d) not allowed" - % exponent) + raise NegativeExponentError( + "Negative exponents (%d) not allowed" % exponent + ) return pow(base, exponent, modulus) def polynomial_reduce_mod(poly, polymod, p): - """Reduce poly by polymod, integer arithmetic modulo p. + """Reduce poly by polymod, integer arithmetic modulo p. - Polynomials are represented as lists of coefficients - of increasing powers of x.""" + Polynomials are represented as lists of coefficients + of increasing powers of x.""" - # This module has been tested only by extensive use - # in calculating modular square roots. + # This module has been tested only by extensive use + # in calculating modular square roots. - # Just to make this easy, require a monic polynomial: - assert polymod[-1] == 1 + # Just to make this easy, require a monic polynomial: + assert polymod[-1] == 1 - assert len(polymod) > 1 + assert len(polymod) > 1 - while len(poly) >= len(polymod): - if poly[-1] != 0: - for i in xrange(2, len(polymod) + 1): - poly[-i] = (poly[-i] - poly[-1] * polymod[-i]) % p - poly = poly[0:-1] + while len(poly) >= len(polymod): + if poly[-1] != 0: + for i in xrange(2, len(polymod) + 1): + poly[-i] = (poly[-i] - poly[-1] * polymod[-i]) % p + poly = poly[0:-1] - return poly + return poly def polynomial_multiply_mod(m1, m2, polymod, p): - """Polynomial multiplication modulo a polynomial over ints mod p. + """Polynomial multiplication modulo a polynomial over ints mod p. - Polynomials are represented as lists of coefficients - of increasing powers of x.""" + Polynomials are represented as lists of coefficients + of increasing powers of x.""" - # This is just a seat-of-the-pants implementation. + # This is just a seat-of-the-pants implementation. - # This module has been tested only by extensive use - # in calculating modular square roots. + # This module has been tested only by extensive use + # in calculating modular square roots. - # Initialize the product to zero: + # Initialize the product to zero: - prod = (len(m1) + len(m2) - 1) * [0] + prod = (len(m1) + len(m2) - 1) * [0] - # Add together all the cross-terms: + # Add together all the cross-terms: - for i in xrange(len(m1)): - for j in xrange(len(m2)): - prod[i + j] = (prod[i + j] + m1[i] * m2[j]) % p + for i in xrange(len(m1)): + for j in xrange(len(m2)): + prod[i + j] = (prod[i + j] + m1[i] * m2[j]) % p - return polynomial_reduce_mod(prod, polymod, p) + return polynomial_reduce_mod(prod, polymod, p) def polynomial_exp_mod(base, exponent, polymod, p): - """Polynomial exponentiation modulo a polynomial over ints mod p. - - Polynomials are represented as lists of coefficients - of increasing powers of x.""" + """Polynomial exponentiation modulo a polynomial over ints mod p. - # Based on the Handbook of Applied Cryptography, algorithm 2.227. + Polynomials are represented as lists of coefficients + of increasing powers of x.""" - # This module has been tested only by extensive use - # in calculating modular square roots. + # Based on the Handbook of Applied Cryptography, algorithm 2.227. - assert exponent < p + # This module has been tested only by extensive use + # in calculating modular square roots. - if exponent == 0: - return [1] + assert exponent < p - G = base - k = exponent - if k % 2 == 1: - s = G - else: - s = [1] + if exponent == 0: + return [1] - while k > 1: - k = k // 2 - G = polynomial_multiply_mod(G, G, polymod, p) + G = base + k = exponent if k % 2 == 1: - s = polynomial_multiply_mod(G, s, polymod, p) + s = G + else: + s = [1] - return s + while k > 1: + k = k // 2 + G = polynomial_multiply_mod(G, G, polymod, p) + if k % 2 == 1: + s = polynomial_multiply_mod(G, s, polymod, p) + + return s def jacobi(a, n): - """Jacobi symbol""" - - # Based on the Handbook of Applied Cryptography (HAC), algorithm 2.149. - - # This function has been tested by comparison with a small - # table printed in HAC, and by extensive use in calculating - # modular square roots. - - assert n >= 3 - assert n % 2 == 1 - a = a % n - if a == 0: - return 0 - if a == 1: - return 1 - a1, e = a, 0 - while a1 % 2 == 0: - a1, e = a1 // 2, e + 1 - if e % 2 == 0 or n % 8 == 1 or n % 8 == 7: - s = 1 - else: - s = -1 - if a1 == 1: - return s - if n % 4 == 3 and a1 % 4 == 3: - s = -s - return s * jacobi(n % a1, a1) + """Jacobi symbol""" + + # Based on the Handbook of Applied Cryptography (HAC), algorithm 2.149. + + # This function has been tested by comparison with a small + # table printed in HAC, and by extensive use in calculating + # modular square roots. + + assert n >= 3 + assert n % 2 == 1 + a = a % n + if a == 0: + return 0 + if a == 1: + return 1 + a1, e = a, 0 + while a1 % 2 == 0: + a1, e = a1 // 2, e + 1 + if e % 2 == 0 or n % 8 == 1 or n % 8 == 7: + s = 1 + else: + s = -1 + if a1 == 1: + return s + if n % 4 == 3 and a1 % 4 == 3: + s = -s + return s * jacobi(n % a1, a1) def square_root_mod_prime(a, p): - """Modular square root of a, mod p, p prime.""" + """Modular square root of a, mod p, p prime.""" - # Based on the Handbook of Applied Cryptography, algorithms 3.34 to 3.39. + # Based on the Handbook of Applied Cryptography, algorithms 3.34 to 3.39. - # This module has been tested for all values in [0,p-1] for - # every prime p from 3 to 1229. + # This module has been tested for all values in [0,p-1] for + # every prime p from 3 to 1229. - assert 0 <= a < p - assert 1 < p + assert 0 <= a < p + assert 1 < p - if a == 0: - return 0 - if p == 2: - return a + if a == 0: + return 0 + if p == 2: + return a - jac = jacobi(a, p) - if jac == -1: - raise SquareRootError("%d has no square root modulo %d" \ - % (a, p)) - - if p % 4 == 3: - return pow(a, (p + 1) // 4, p) - - if p % 8 == 5: - d = pow(a, (p - 1) // 4, p) - if d == 1: - return pow(a, (p + 3) // 8, p) - if d == p - 1: - return (2 * a * pow(4 * a, (p - 5) // 8, p)) % p - raise RuntimeError("Shouldn't get here.") - - if PY2: - # xrange on python2 can take integers representable as C long only - range_top = min(0x7fffffff, p) - else: - range_top = p - for b in xrange(2, range_top): - if jacobi(b * b - 4 * a, p) == -1: - f = (a, -b, 1) - ff = polynomial_exp_mod((0, 1), (p + 1) // 2, f, p) - assert ff[1] == 0 - return ff[0] - raise RuntimeError("No b found.") + jac = jacobi(a, p) + if jac == -1: + raise SquareRootError("%d has no square root modulo %d" % (a, p)) + + if p % 4 == 3: + return pow(a, (p + 1) // 4, p) + + if p % 8 == 5: + d = pow(a, (p - 1) // 4, p) + if d == 1: + return pow(a, (p + 3) // 8, p) + if d == p - 1: + return (2 * a * pow(4 * a, (p - 5) // 8, p)) % p + raise RuntimeError("Shouldn't get here.") + + if PY2: + # xrange on python2 can take integers representable as C long only + range_top = min(0x7FFFFFFF, p) + else: + range_top = p + for b in xrange(2, range_top): + if jacobi(b * b - 4 * a, p) == -1: + f = (a, -b, 1) + ff = polynomial_exp_mod((0, 1), (p + 1) // 2, f, p) + assert ff[1] == 0 + return ff[0] + raise RuntimeError("No b found.") if GMPY2: + def inverse_mod(a, m): """Inverse of a mod m.""" if a == 0: return 0 return powmod(a, -1, m) + + elif GMPY: + def inverse_mod(a, m): """Inverse of a mod m.""" # while libgmp likely does support inverses modulo, it is accessible @@ -237,7 +248,10 @@ def inverse_mod(a, m): lm, low, hm, high = hm - lm * r, high - low * r, lm, low return lm % m + + else: + def inverse_mod(a, m): """Inverse of a mod m.""" @@ -256,6 +270,7 @@ def inverse_mod(a, m): try: gcd2 = math.gcd except AttributeError: + def gcd2(a, b): """Greatest common divisor using Euclid's algorithm.""" while a: @@ -264,337 +279,532 @@ def gcd2(a, b): def gcd(*a): - """Greatest common divisor. + """Greatest common divisor. - Usage: gcd([ 2, 4, 6 ]) - or: gcd(2, 4, 6) - """ + Usage: gcd([ 2, 4, 6 ]) + or: gcd(2, 4, 6) + """ - if len(a) > 1: - return reduce(gcd2, a) - if hasattr(a[0], "__iter__"): - return reduce(gcd2, a[0]) - return a[0] + if len(a) > 1: + return reduce(gcd2, a) + if hasattr(a[0], "__iter__"): + return reduce(gcd2, a[0]) + return a[0] def lcm2(a, b): - """Least common multiple of two integers.""" + """Least common multiple of two integers.""" - return (a * b) // gcd(a, b) + return (a * b) // gcd(a, b) def lcm(*a): - """Least common multiple. + """Least common multiple. - Usage: lcm([ 3, 4, 5 ]) - or: lcm(3, 4, 5) - """ + Usage: lcm([ 3, 4, 5 ]) + or: lcm(3, 4, 5) + """ - if len(a) > 1: - return reduce(lcm2, a) - if hasattr(a[0], "__iter__"): - return reduce(lcm2, a[0]) - return a[0] + if len(a) > 1: + return reduce(lcm2, a) + if hasattr(a[0], "__iter__"): + return reduce(lcm2, a[0]) + return a[0] def factorization(n): - """Decompose n into a list of (prime,exponent) pairs.""" + """Decompose n into a list of (prime,exponent) pairs.""" - assert isinstance(n, integer_types) + assert isinstance(n, integer_types) - if n < 2: - return [] + if n < 2: + return [] - result = [] - d = 2 + result = [] + d = 2 - # Test the small primes: + # Test the small primes: - for d in smallprimes: - if d > n: - break - q, r = divmod(n, d) - if r == 0: - count = 1 - while d <= n: - n = q - q, r = divmod(n, d) - if r != 0: - break - count = count + 1 - result.append((d, count)) - - # If n is still greater than the last of our small primes, - # it may require further work: - - if n > smallprimes[-1]: - if is_prime(n): # If what's left is prime, it's easy: - result.append((n, 1)) - else: # Ugh. Search stupidly for a divisor: - d = smallprimes[-1] - while 1: - d = d + 2 # Try the next divisor. + for d in smallprimes: + if d > n: + break q, r = divmod(n, d) - if q < d: # n < d*d means we're done, n = 1 or prime. - break - if r == 0: # d divides n. How many times? - count = 1 - n = q - while d <= n: # As long as d might still divide n, - q, r = divmod(n, d) # see if it does. - if r != 0: - break - n = q # It does. Reduce n, increase count. - count = count + 1 - result.append((d, count)) - if n > 1: - result.append((n, 1)) - - return result + if r == 0: + count = 1 + while d <= n: + n = q + q, r = divmod(n, d) + if r != 0: + break + count = count + 1 + result.append((d, count)) + + # If n is still greater than the last of our small primes, + # it may require further work: + + if n > smallprimes[-1]: + if is_prime(n): # If what's left is prime, it's easy: + result.append((n, 1)) + else: # Ugh. Search stupidly for a divisor: + d = smallprimes[-1] + while 1: + d = d + 2 # Try the next divisor. + q, r = divmod(n, d) + if q < d: # n < d*d means we're done, n = 1 or prime. + break + if r == 0: # d divides n. How many times? + count = 1 + n = q + while d <= n: # As long as d might still divide n, + q, r = divmod(n, d) # see if it does. + if r != 0: + break + n = q # It does. Reduce n, increase count. + count = count + 1 + result.append((d, count)) + if n > 1: + result.append((n, 1)) + + return result def phi(n): # pragma: no cover - """Return the Euler totient function of n.""" - # deprecated in 0.14 - warnings.warn("Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/warner/python-ecdsa", - DeprecationWarning) - - assert isinstance(n, integer_types) - - if n < 3: - return 1 - - result = 1 - ff = factorization(n) - for f in ff: - e = f[1] - if e > 1: - result = result * f[0] ** (e - 1) * (f[0] - 1) - else: - result = result * (f[0] - 1) - return result + """Return the Euler totient function of n.""" + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/warner/python-ecdsa", + DeprecationWarning, + ) + + assert isinstance(n, integer_types) + + if n < 3: + return 1 + + result = 1 + ff = factorization(n) + for f in ff: + e = f[1] + if e > 1: + result = result * f[0] ** (e - 1) * (f[0] - 1) + else: + result = result * (f[0] - 1) + return result def carmichael(n): # pragma: no cover - """Return Carmichael function of n. + """Return Carmichael function of n. - Carmichael(n) is the smallest integer x such that - m**x = 1 mod n for all m relatively prime to n. - """ - # deprecated in 0.14 - warnings.warn("Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/warner/python-ecdsa", - DeprecationWarning) + Carmichael(n) is the smallest integer x such that + m**x = 1 mod n for all m relatively prime to n. + """ + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/warner/python-ecdsa", + DeprecationWarning, + ) - return carmichael_of_factorized(factorization(n)) + return carmichael_of_factorized(factorization(n)) def carmichael_of_factorized(f_list): # pragma: no cover - """Return the Carmichael function of a number that is - represented as a list of (prime,exponent) pairs. - """ - # deprecated in 0.14 - warnings.warn("Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/warner/python-ecdsa", - DeprecationWarning) + """Return the Carmichael function of a number that is + represented as a list of (prime,exponent) pairs. + """ + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/warner/python-ecdsa", + DeprecationWarning, + ) - if len(f_list) < 1: - return 1 + if len(f_list) < 1: + return 1 - result = carmichael_of_ppower(f_list[0]) - for i in xrange(1, len(f_list)): - result = lcm(result, carmichael_of_ppower(f_list[i])) + result = carmichael_of_ppower(f_list[0]) + for i in xrange(1, len(f_list)): + result = lcm(result, carmichael_of_ppower(f_list[i])) - return result + return result def carmichael_of_ppower(pp): # pragma: no cover - """Carmichael function of the given power of the given prime. - """ - # deprecated in 0.14 - warnings.warn("Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/warner/python-ecdsa", - DeprecationWarning) - - p, a = pp - if p == 2 and a > 2: - return 2**(a - 2) - else: - return (p - 1) * p**(a - 1) + """Carmichael function of the given power of the given prime.""" + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/warner/python-ecdsa", + DeprecationWarning, + ) + + p, a = pp + if p == 2 and a > 2: + return 2 ** (a - 2) + else: + return (p - 1) * p ** (a - 1) def order_mod(x, m): # pragma: no cover - """Return the order of x in the multiplicative group mod m. - """ - # deprecated in 0.14 - warnings.warn("Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/warner/python-ecdsa", - DeprecationWarning) + """Return the order of x in the multiplicative group mod m.""" + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/warner/python-ecdsa", + DeprecationWarning, + ) - # Warning: this implementation is not very clever, and will - # take a long time if m is very large. + # Warning: this implementation is not very clever, and will + # take a long time if m is very large. - if m <= 1: - return 0 + if m <= 1: + return 0 - assert gcd(x, m) == 1 + assert gcd(x, m) == 1 - z = x - result = 1 - while z != 1: - z = (z * x) % m - result = result + 1 - return result + z = x + result = 1 + while z != 1: + z = (z * x) % m + result = result + 1 + return result def largest_factor_relatively_prime(a, b): # pragma: no cover - """Return the largest factor of a relatively prime to b. - """ - # deprecated in 0.14 - warnings.warn("Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/warner/python-ecdsa", - DeprecationWarning) - - while 1: - d = gcd(a, b) - if d <= 1: - break - b = d + """Return the largest factor of a relatively prime to b.""" + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/warner/python-ecdsa", + DeprecationWarning, + ) + while 1: - q, r = divmod(a, d) - if r > 0: - break - a = q - return a + d = gcd(a, b) + if d <= 1: + break + b = d + while 1: + q, r = divmod(a, d) + if r > 0: + break + a = q + return a def kinda_order_mod(x, m): # pragma: no cover - """Return the order of x in the multiplicative group mod m', - where m' is the largest factor of m relatively prime to x. - """ - # deprecated in 0.14 - warnings.warn("Function is unused by library code. If you use this code, " - "please open an issue in " - "https://github.com/warner/python-ecdsa", - DeprecationWarning) + """Return the order of x in the multiplicative group mod m', + where m' is the largest factor of m relatively prime to x. + """ + # deprecated in 0.14 + warnings.warn( + "Function is unused by library code. If you use this code, " + "please open an issue in " + "https://github.com/warner/python-ecdsa", + DeprecationWarning, + ) - return order_mod(x, largest_factor_relatively_prime(m, x)) + return order_mod(x, largest_factor_relatively_prime(m, x)) def is_prime(n): - """Return True if x is prime, False otherwise. + """Return True if x is prime, False otherwise. - We use the Miller-Rabin test, as given in Menezes et al. p. 138. - This test is not exact: there are composite values n for which - it returns True. + We use the Miller-Rabin test, as given in Menezes et al. p. 138. + This test is not exact: there are composite values n for which + it returns True. - In testing the odd numbers from 10000001 to 19999999, - about 66 composites got past the first test, - 5 got past the second test, and none got past the third. - Since factors of 2, 3, 5, 7, and 11 were detected during - preliminary screening, the number of numbers tested by - Miller-Rabin was (19999999 - 10000001)*(2/3)*(4/5)*(6/7) - = 4.57 million. - """ + In testing the odd numbers from 10000001 to 19999999, + about 66 composites got past the first test, + 5 got past the second test, and none got past the third. + Since factors of 2, 3, 5, 7, and 11 were detected during + preliminary screening, the number of numbers tested by + Miller-Rabin was (19999999 - 10000001)*(2/3)*(4/5)*(6/7) + = 4.57 million. + """ - # (This is used to study the risk of false positives:) - global miller_rabin_test_count + # (This is used to study the risk of false positives:) + global miller_rabin_test_count - miller_rabin_test_count = 0 + miller_rabin_test_count = 0 - if n <= smallprimes[-1]: - if n in smallprimes: - return True - else: - return False - - if gcd(n, 2 * 3 * 5 * 7 * 11) != 1: - return False - - # Choose a number of iterations sufficient to reduce the - # probability of accepting a composite below 2**-80 - # (from Menezes et al. Table 4.4): - - t = 40 - n_bits = 1 + int(math.log(n, 2)) - for k, tt in ((100, 27), - (150, 18), - (200, 15), - (250, 12), - (300, 9), - (350, 8), - (400, 7), - (450, 6), - (550, 5), - (650, 4), - (850, 3), - (1300, 2), - ): - if n_bits < k: - break - t = tt - - # Run the test t times: - - s = 0 - r = n - 1 - while (r % 2) == 0: - s = s + 1 - r = r // 2 - for i in xrange(t): - a = smallprimes[i] - y = pow(a, r, n) - if y != 1 and y != n - 1: - j = 1 - while j <= s - 1 and y != n - 1: - y = pow(y, 2, n) - if y == 1: - miller_rabin_test_count = i + 1 - return False - j = j + 1 - if y != n - 1: - miller_rabin_test_count = i + 1 + if n <= smallprimes[-1]: + if n in smallprimes: + return True + else: + return False + + if gcd(n, 2 * 3 * 5 * 7 * 11) != 1: return False - return True + + # Choose a number of iterations sufficient to reduce the + # probability of accepting a composite below 2**-80 + # (from Menezes et al. Table 4.4): + + t = 40 + n_bits = 1 + int(math.log(n, 2)) + for k, tt in ( + (100, 27), + (150, 18), + (200, 15), + (250, 12), + (300, 9), + (350, 8), + (400, 7), + (450, 6), + (550, 5), + (650, 4), + (850, 3), + (1300, 2), + ): + if n_bits < k: + break + t = tt + + # Run the test t times: + + s = 0 + r = n - 1 + while (r % 2) == 0: + s = s + 1 + r = r // 2 + for i in xrange(t): + a = smallprimes[i] + y = pow(a, r, n) + if y != 1 and y != n - 1: + j = 1 + while j <= s - 1 and y != n - 1: + y = pow(y, 2, n) + if y == 1: + miller_rabin_test_count = i + 1 + return False + j = j + 1 + if y != n - 1: + miller_rabin_test_count = i + 1 + return False + return True def next_prime(starting_value): - "Return the smallest prime larger than the starting value." - - if starting_value < 2: - return 2 - result = (starting_value + 1) | 1 - while not is_prime(result): - result = result + 2 - return result - - -smallprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, - 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, - 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, - 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, - 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, - 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, - 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, - 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, - 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, - 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, - 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, - 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, - 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, - 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, - 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, - 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, - 983, 991, 997, 1009, 1013, 1019, 1021, 1031, 1033, - 1039, 1049, 1051, 1061, 1063, 1069, 1087, 1091, 1093, - 1097, 1103, 1109, 1117, 1123, 1129, 1151, 1153, 1163, - 1171, 1181, 1187, 1193, 1201, 1213, 1217, 1223, 1229] + """Return the smallest prime larger than the starting value.""" + + if starting_value < 2: + return 2 + result = (starting_value + 1) | 1 + while not is_prime(result): + result = result + 2 + return result + + +smallprimes = [ + 2, + 3, + 5, + 7, + 11, + 13, + 17, + 19, + 23, + 29, + 31, + 37, + 41, + 43, + 47, + 53, + 59, + 61, + 67, + 71, + 73, + 79, + 83, + 89, + 97, + 101, + 103, + 107, + 109, + 113, + 127, + 131, + 137, + 139, + 149, + 151, + 157, + 163, + 167, + 173, + 179, + 181, + 191, + 193, + 197, + 199, + 211, + 223, + 227, + 229, + 233, + 239, + 241, + 251, + 257, + 263, + 269, + 271, + 277, + 281, + 283, + 293, + 307, + 311, + 313, + 317, + 331, + 337, + 347, + 349, + 353, + 359, + 367, + 373, + 379, + 383, + 389, + 397, + 401, + 409, + 419, + 421, + 431, + 433, + 439, + 443, + 449, + 457, + 461, + 463, + 467, + 479, + 487, + 491, + 499, + 503, + 509, + 521, + 523, + 541, + 547, + 557, + 563, + 569, + 571, + 577, + 587, + 593, + 599, + 601, + 607, + 613, + 617, + 619, + 631, + 641, + 643, + 647, + 653, + 659, + 661, + 673, + 677, + 683, + 691, + 701, + 709, + 719, + 727, + 733, + 739, + 743, + 751, + 757, + 761, + 769, + 773, + 787, + 797, + 809, + 811, + 821, + 823, + 827, + 829, + 839, + 853, + 857, + 859, + 863, + 877, + 881, + 883, + 887, + 907, + 911, + 919, + 929, + 937, + 941, + 947, + 953, + 967, + 971, + 977, + 983, + 991, + 997, + 1009, + 1013, + 1019, + 1021, + 1031, + 1033, + 1039, + 1049, + 1051, + 1061, + 1063, + 1069, + 1087, + 1091, + 1093, + 1097, + 1103, + 1109, + 1117, + 1123, + 1129, + 1151, + 1153, + 1163, + 1171, + 1181, + 1187, + 1193, + 1201, + 1213, + 1217, + 1223, + 1229, +] miller_rabin_test_count = 0 diff --git a/src/ecdsa/rfc6979.py b/src/ecdsa/rfc6979.py index a4893812..9d508709 100644 --- a/src/ecdsa/rfc6979.py +++ b/src/ecdsa/rfc6979.py @@ -1,4 +1,4 @@ -''' +""" RFC 6979: Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA) @@ -7,7 +7,7 @@ Many thanks to Coda Hale for his implementation in Go language: https://github.com/codahale/rfc6979 -''' +""" import hmac from binascii import hexlify @@ -40,8 +40,8 @@ def bits2octets(data, order): # https://tools.ietf.org/html/rfc6979#section-3.2 -def generate_k(order, secexp, hash_func, data, retry_gen=0, extra_entropy=b''): - ''' +def generate_k(order, secexp, hash_func, data, retry_gen=0, extra_entropy=b""): + """ order - order of the DSA generator used in the signature secexp - secure exponent (private key) in numeric form hash_func - reference to the same hash function used for generating hash @@ -49,25 +49,27 @@ def generate_k(order, secexp, hash_func, data, retry_gen=0, extra_entropy=b''): retry_gen - int - how many good 'k' values to skip before returning extra_entropy - extra added data in binary form as per section-3.6 of rfc6979 - ''' + """ qlen = bit_length(order) holen = hash_func().digest_size rolen = (qlen + 7) / 8 - bx = (hmac_compat(number_to_string(secexp, order)), - hmac_compat(bits2octets(data, order)), - hmac_compat(extra_entropy)) + bx = ( + hmac_compat(number_to_string(secexp, order)), + hmac_compat(bits2octets(data, order)), + hmac_compat(extra_entropy), + ) # Step B - v = b'\x01' * holen + v = b"\x01" * holen # Step C - k = b'\x00' * holen + k = b"\x00" * holen # Step D k = hmac.new(k, digestmod=hash_func) - k.update(v + b'\x00') + k.update(v + b"\x00") for i in bx: k.update(i) k = k.digest() @@ -77,7 +79,7 @@ def generate_k(order, secexp, hash_func, data, retry_gen=0, extra_entropy=b''): # Step F k = hmac.new(k, digestmod=hash_func) - k.update(v + b'\x01') + k.update(v + b"\x01") for i in bx: k.update(i) k = k.digest() @@ -88,7 +90,7 @@ def generate_k(order, secexp, hash_func, data, retry_gen=0, extra_entropy=b''): # Step H while True: # Step H1 - t = b'' + t = b"" # Step H2 while len(t) < rolen: @@ -103,5 +105,5 @@ def generate_k(order, secexp, hash_func, data, retry_gen=0, extra_entropy=b''): return secret retry_gen -= 1 - k = hmac.new(k, v + b'\x00', hash_func).digest() + k = hmac.new(k, v + b"\x00", hash_func).digest() v = hmac.new(k, v, hash_func).digest() diff --git a/src/ecdsa/test_der.py b/src/ecdsa/test_der.py index e6cd593d..746d9277 100644 --- a/src/ecdsa/test_der.py +++ b/src/ecdsa/test_der.py @@ -1,8 +1,8 @@ - # compatibility with Python 2.6, for that we need unittest2 package, # which is not available on 3.3 or 3.4 import warnings from binascii import hexlify + try: import unittest2 as unittest except ImportError: @@ -13,8 +13,15 @@ import pytest from ._compat import str_idx_as_int from .curves import NIST256p, NIST224p -from .der import remove_integer, UnexpectedDER, read_length, encode_bitstring,\ - remove_bitstring, remove_object, encode_oid +from .der import ( + remove_integer, + UnexpectedDER, + read_length, + encode_bitstring, + remove_bitstring, + remove_object, + encode_oid, +) class TestRemoveInteger(unittest.TestCase): @@ -22,44 +29,44 @@ class TestRemoveInteger(unittest.TestCase): # interpreted as negative, check if those errors are detected def test_non_minimal_encoding(self): with self.assertRaises(UnexpectedDER): - remove_integer(b('\x02\x02\x00\x01')) + remove_integer(b("\x02\x02\x00\x01")) def test_negative_with_high_bit_set(self): with self.assertRaises(UnexpectedDER): - remove_integer(b('\x02\x01\x80')) + remove_integer(b("\x02\x01\x80")) def test_minimal_with_high_bit_set(self): - val, rem = remove_integer(b('\x02\x02\x00\x80')) + val, rem = remove_integer(b("\x02\x02\x00\x80")) self.assertEqual(val, 0x80) self.assertFalse(rem) def test_two_zero_bytes_with_high_bit_set(self): with self.assertRaises(UnexpectedDER): - remove_integer(b('\x02\x03\x00\x00\xff')) + remove_integer(b("\x02\x03\x00\x00\xff")) def test_zero_length_integer(self): with self.assertRaises(UnexpectedDER): - remove_integer(b('\x02\x00')) + remove_integer(b("\x02\x00")) def test_empty_string(self): with self.assertRaises(UnexpectedDER): - remove_integer(b('')) + remove_integer(b("")) def test_encoding_of_zero(self): - val, rem = remove_integer(b('\x02\x01\x00')) + val, rem = remove_integer(b("\x02\x01\x00")) self.assertEqual(val, 0) self.assertFalse(rem) def test_encoding_of_127(self): - val, rem = remove_integer(b('\x02\x01\x7f')) + val, rem = remove_integer(b("\x02\x01\x7f")) self.assertEqual(val, 127) self.assertFalse(rem) def test_encoding_of_128(self): - val, rem = remove_integer(b('\x02\x02\x00\x80')) + val, rem = remove_integer(b("\x02\x02\x00\x80")) self.assertEqual(val, 128) self.assertFalse(rem) @@ -70,37 +77,37 @@ class TestReadLength(unittest.TestCase): # form and lengths above that encoded with minimal number of bytes # necessary def test_zero_length(self): - self.assertEqual((0, 1), read_length(b('\x00'))) + self.assertEqual((0, 1), read_length(b("\x00"))) def test_two_byte_zero_length(self): with self.assertRaises(UnexpectedDER): - read_length(b('\x81\x00')) + read_length(b("\x81\x00")) def test_two_byte_small_length(self): with self.assertRaises(UnexpectedDER): - read_length(b('\x81\x7f')) + read_length(b("\x81\x7f")) def test_long_form_with_zero_length(self): with self.assertRaises(UnexpectedDER): - read_length(b('\x80')) + read_length(b("\x80")) def test_smallest_two_byte_length(self): - self.assertEqual((128, 2), read_length(b('\x81\x80'))) + self.assertEqual((128, 2), read_length(b("\x81\x80"))) def test_zero_padded_length(self): with self.assertRaises(UnexpectedDER): - read_length(b('\x82\x00\x80')) + read_length(b("\x82\x00\x80")) def test_two_three_byte_length(self): - self.assertEqual((256, 3), read_length(b'\x82\x01\x00')) + self.assertEqual((256, 3), read_length(b"\x82\x01\x00")) def test_empty_string(self): with self.assertRaises(UnexpectedDER): - read_length(b('')) + read_length(b("")) def test_length_overflow(self): with self.assertRaises(UnexpectedDER): - read_length(b('\x83\x01\x00')) + read_length(b("\x83\x01\x00")) class TestEncodeBitstring(unittest.TestCase): @@ -109,143 +116,145 @@ class TestEncodeBitstring(unittest.TestCase): def test_old_call_convention(self): """This is the old way to use the function.""" - warnings.simplefilter('always') + warnings.simplefilter("always") with pytest.warns(DeprecationWarning) as warns: - der = encode_bitstring(b'\x00\xff') + der = encode_bitstring(b"\x00\xff") self.assertEqual(len(warns), 1) - self.assertIn("unused= needs to be specified", - warns[0].message.args[0]) + self.assertIn( + "unused= needs to be specified", warns[0].message.args[0] + ) - self.assertEqual(der, b'\x03\x02\x00\xff') + self.assertEqual(der, b"\x03\x02\x00\xff") def test_new_call_convention(self): """This is how it should be called now.""" - warnings.simplefilter('always') + warnings.simplefilter("always") with pytest.warns(None) as warns: - der = encode_bitstring(b'\xff', 0) + der = encode_bitstring(b"\xff", 0) # verify that new call convention doesn't raise Warnings self.assertEqual(len(warns), 0) - self.assertEqual(der, b'\x03\x02\x00\xff') + self.assertEqual(der, b"\x03\x02\x00\xff") def test_implicit_unused_bits(self): """ Writing bit string with already included the number of unused bits. """ - warnings.simplefilter('always') + warnings.simplefilter("always") with pytest.warns(None) as warns: - der = encode_bitstring(b'\x00\xff', None) + der = encode_bitstring(b"\x00\xff", None) # verify that new call convention doesn't raise Warnings self.assertEqual(len(warns), 0) - self.assertEqual(der, b'\x03\x02\x00\xff') + self.assertEqual(der, b"\x03\x02\x00\xff") def test_explicit_unused_bits(self): - der = encode_bitstring(b'\xff\xf0', 4) + der = encode_bitstring(b"\xff\xf0", 4) - self.assertEqual(der, b'\x03\x03\x04\xff\xf0') + self.assertEqual(der, b"\x03\x03\x04\xff\xf0") def test_empty_string(self): - self.assertEqual(encode_bitstring(b'', 0), b'\x03\x01\x00') + self.assertEqual(encode_bitstring(b"", 0), b"\x03\x01\x00") def test_invalid_unused_count(self): with self.assertRaises(ValueError): - encode_bitstring(b'\xff\x00', 8) + encode_bitstring(b"\xff\x00", 8) def test_invalid_unused_with_empty_string(self): with self.assertRaises(ValueError): - encode_bitstring(b'', 1) + encode_bitstring(b"", 1) def test_non_zero_padding_bits(self): with self.assertRaises(ValueError): - encode_bitstring(b'\xff', 2) + encode_bitstring(b"\xff", 2) class TestRemoveBitstring(unittest.TestCase): def test_old_call_convention(self): """This is the old way to call the function.""" - warnings.simplefilter('always') + warnings.simplefilter("always") with pytest.warns(DeprecationWarning) as warns: - bits, rest = remove_bitstring(b'\x03\x02\x00\xff') + bits, rest = remove_bitstring(b"\x03\x02\x00\xff") self.assertEqual(len(warns), 1) - self.assertIn("expect_unused= needs to be specified", - warns[0].message.args[0]) + self.assertIn( + "expect_unused= needs to be specified", warns[0].message.args[0] + ) - self.assertEqual(bits, b'\x00\xff') - self.assertEqual(rest, b'') + self.assertEqual(bits, b"\x00\xff") + self.assertEqual(rest, b"") def test_new_call_convention(self): - warnings.simplefilter('always') + warnings.simplefilter("always") with pytest.warns(None) as warns: - bits, rest = remove_bitstring(b'\x03\x02\x00\xff', 0) + bits, rest = remove_bitstring(b"\x03\x02\x00\xff", 0) self.assertEqual(len(warns), 0) - self.assertEqual(bits, b'\xff') - self.assertEqual(rest, b'') + self.assertEqual(bits, b"\xff") + self.assertEqual(rest, b"") def test_implicit_unexpected_unused(self): - warnings.simplefilter('always') + warnings.simplefilter("always") with pytest.warns(None) as warns: - bits, rest = remove_bitstring(b'\x03\x02\x00\xff', None) + bits, rest = remove_bitstring(b"\x03\x02\x00\xff", None) self.assertEqual(len(warns), 0) - self.assertEqual(bits, (b'\xff', 0)) - self.assertEqual(rest, b'') + self.assertEqual(bits, (b"\xff", 0)) + self.assertEqual(rest, b"") def test_with_padding(self): - ret, rest = remove_bitstring(b'\x03\x02\x04\xf0', None) + ret, rest = remove_bitstring(b"\x03\x02\x04\xf0", None) - self.assertEqual(ret, (b'\xf0', 4)) - self.assertEqual(rest, b'') + self.assertEqual(ret, (b"\xf0", 4)) + self.assertEqual(rest, b"") def test_not_a_bitstring(self): with self.assertRaises(UnexpectedDER): - remove_bitstring(b'\x02\x02\x00\xff', None) + remove_bitstring(b"\x02\x02\x00\xff", None) def test_empty_encoding(self): with self.assertRaises(UnexpectedDER): - remove_bitstring(b'\x03\x00', None) + remove_bitstring(b"\x03\x00", None) def test_empty_string(self): with self.assertRaises(UnexpectedDER): - remove_bitstring(b'', None) + remove_bitstring(b"", None) def test_no_length(self): with self.assertRaises(UnexpectedDER): - remove_bitstring(b'\x03', None) + remove_bitstring(b"\x03", None) def test_unexpected_number_of_unused_bits(self): with self.assertRaises(UnexpectedDER): - remove_bitstring(b'\x03\x02\x00\xff', 1) + remove_bitstring(b"\x03\x02\x00\xff", 1) def test_invalid_encoding_of_unused_bits(self): with self.assertRaises(UnexpectedDER): - remove_bitstring(b'\x03\x03\x08\xff\x00', None) + remove_bitstring(b"\x03\x03\x08\xff\x00", None) def test_invalid_encoding_of_empty_string(self): with self.assertRaises(UnexpectedDER): - remove_bitstring(b'\x03\x01\x01', None) + remove_bitstring(b"\x03\x01\x01", None) def test_invalid_padding_bits(self): with self.assertRaises(UnexpectedDER): - remove_bitstring(b'\x03\x02\x01\xff', None) + remove_bitstring(b"\x03\x02\x01\xff", None) class TestStrIdxAsInt(unittest.TestCase): def test_str(self): - self.assertEqual(115, str_idx_as_int('str', 0)) + self.assertEqual(115, str_idx_as_int("str", 0)) def test_bytes(self): - self.assertEqual(115, str_idx_as_int(b'str', 0)) + self.assertEqual(115, str_idx_as_int(b"str", 0)) def test_bytearray(self): - self.assertEqual(115, str_idx_as_int(bytearray(b'str'), 0)) + self.assertEqual(115, str_idx_as_int(bytearray(b"str"), 0)) class TestEncodeOid(unittest.TestCase): @@ -257,21 +266,22 @@ def test_nist224p_oid(self): self.assertEqual(hexlify(NIST224p.encoded_oid), b("06052b81040021")) def test_nist256p_oid(self): - self.assertEqual(hexlify(NIST256p.encoded_oid), - b"06082a8648ce3d030107") + self.assertEqual( + hexlify(NIST256p.encoded_oid), b"06082a8648ce3d030107" + ) def test_large_second_subid(self): # from X.690, section 8.19.5 oid = encode_oid(2, 999, 3) - self.assertEqual(oid, b'\x06\x03\x88\x37\x03') + self.assertEqual(oid, b"\x06\x03\x88\x37\x03") def test_with_two_subids(self): oid = encode_oid(2, 999) - self.assertEqual(oid, b'\x06\x02\x88\x37') + self.assertEqual(oid, b"\x06\x02\x88\x37") def test_zero_zero(self): oid = encode_oid(0, 0) - self.assertEqual(oid, b'\x06\x01\x00') + self.assertEqual(oid, b"\x06\x01\x00") def test_with_wrong_types(self): with self.assertRaises((TypeError, AssertionError)): @@ -283,7 +293,7 @@ def test_with_small_first_large_second(self): def test_small_first_max_second(self): oid = encode_oid(1, 39) - self.assertEqual(oid, b'\x06\x01\x4f') + self.assertEqual(oid, b"\x06\x01\x4f") def test_with_invalid_first(self): with self.assertRaises(AssertionError): @@ -297,69 +307,69 @@ def setUpClass(cls): def test_pub_key_oid(self): oid, rest = remove_object(self.oid_ecPublicKey) - self.assertEqual(rest, b'') + self.assertEqual(rest, b"") self.assertEqual(oid, (1, 2, 840, 10045, 2, 1)) def test_with_extra_bytes(self): - oid, rest = remove_object(self.oid_ecPublicKey + b'more') - self.assertEqual(rest, b'more') + oid, rest = remove_object(self.oid_ecPublicKey + b"more") + self.assertEqual(rest, b"more") self.assertEqual(oid, (1, 2, 840, 10045, 2, 1)) def test_with_large_second_subid(self): # from X.690, section 8.19.5 - oid, rest = remove_object(b'\x06\x03\x88\x37\x03') - self.assertEqual(rest, b'') + oid, rest = remove_object(b"\x06\x03\x88\x37\x03") + self.assertEqual(rest, b"") self.assertEqual(oid, (2, 999, 3)) def test_with_padded_first_subid(self): with self.assertRaises(UnexpectedDER): - remove_object(b'\x06\x02\x80\x00') + remove_object(b"\x06\x02\x80\x00") def test_with_padded_second_subid(self): with self.assertRaises(UnexpectedDER): - remove_object(b'\x06\x04\x88\x37\x80\x01') + remove_object(b"\x06\x04\x88\x37\x80\x01") def test_with_missing_last_byte_of_multi_byte(self): with self.assertRaises(UnexpectedDER): - remove_object(b'\x06\x03\x88\x37\x83') + remove_object(b"\x06\x03\x88\x37\x83") def test_with_two_subids(self): - oid, rest = remove_object(b'\x06\x02\x88\x37') - self.assertEqual(rest, b'') + oid, rest = remove_object(b"\x06\x02\x88\x37") + self.assertEqual(rest, b"") self.assertEqual(oid, (2, 999)) def test_zero_zero(self): - oid, rest = remove_object(b'\x06\x01\x00') - self.assertEqual(rest, b'') + oid, rest = remove_object(b"\x06\x01\x00") + self.assertEqual(rest, b"") self.assertEqual(oid, (0, 0)) def test_empty_string(self): with self.assertRaises(UnexpectedDER): - remove_object(b'') + remove_object(b"") def test_missing_length(self): with self.assertRaises(UnexpectedDER): - remove_object(b'\x06') + remove_object(b"\x06") def test_empty_oid(self): with self.assertRaises(UnexpectedDER): - remove_object(b'\x06\x00') + remove_object(b"\x06\x00") def test_empty_oid_overflow(self): with self.assertRaises(UnexpectedDER): - remove_object(b'\x06\x01') + remove_object(b"\x06\x01") def test_with_wrong_type(self): with self.assertRaises(UnexpectedDER): - remove_object(b'\x04\x02\x88\x37') + remove_object(b"\x04\x02\x88\x37") def test_with_too_long_length(self): with self.assertRaises(UnexpectedDER): - remove_object(b'\x06\x03\x88\x37') + remove_object(b"\x06\x03\x88\x37") @st.composite -def st_oid(draw, max_value=2**512, max_size=50): +def st_oid(draw, max_value=2 ** 512, max_size=50): """ Hypothesis strategy that returns valid OBJECT IDENTIFIERs as tuples @@ -371,8 +381,11 @@ def st_oid(draw, max_value=2**512, max_size=50): second = draw(st.integers(min_value=0, max_value=39)) else: second = draw(st.integers(min_value=0, max_value=max_value)) - rest = draw(st.lists(st.integers(min_value=0, max_value=max_value), - max_size=max_size)) + rest = draw( + st.lists( + st.integers(min_value=0, max_value=max_value), max_size=max_size + ) + ) return (first, second) + tuple(rest) @@ -380,5 +393,5 @@ def st_oid(draw, max_value=2**512, max_size=50): def test_oids(ids): encoded_oid = encode_oid(*ids) decoded_oid, rest = remove_object(encoded_oid) - assert rest == b'' + assert rest == b"" assert decoded_oid == ids diff --git a/src/ecdsa/test_ecdh.py b/src/ecdsa/test_ecdh.py index 74c8bbab..caf68352 100644 --- a/src/ecdsa/test_ecdh.py +++ b/src/ecdsa/test_ecdh.py @@ -1,4 +1,3 @@ - import os import shutil import subprocess @@ -7,12 +6,13 @@ from .curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p from .curves import curves -from .ecdh import ECDH, InvalidCurveError, \ - InvalidSharedSecretError, NoKeyError +from .ecdh import ECDH, InvalidCurveError, InvalidSharedSecretError, NoKeyError from .keys import SigningKey, VerifyingKey -@pytest.mark.parametrize("vcurve", curves, ids=[curve.name for curve in curves]) +@pytest.mark.parametrize( + "vcurve", curves, ids=[curve.name for curve in curves] +) def test_ecdh_each(vcurve): ecdh1 = ECDH(curve=vcurve) ecdh2 = ECDH(curve=vcurve) @@ -64,7 +64,9 @@ def test_ecdh_invalid_shared_secret_curve(): ecdh1 = ECDH(curve=NIST256p) ecdh1.generate_private_key() - ecdh1.load_received_public_key(SigningKey.generate(NIST256p).get_verifying_key()) + ecdh1.load_received_public_key( + SigningKey.generate(NIST256p).get_verifying_key() + ) ecdh1.private_key.privkey.secret_multiplier = ecdh1.private_key.curve.order @@ -84,7 +86,7 @@ def test_ecdh_invalid_shared_secret_curve(): "42ea6dd9969dd2a61fea1aac7f8e98edcc896c6e55857cc0" "dfbe5d7c61fac88b11811bde328e8a0d12bf01a9d204b523", "803d8ab2e5b6e6fca715737c3a82f7ce3c783124f6d51cd0", - id="NIST192p-1" + id="NIST192p-1", ), pytest.param( NIST192p, @@ -92,7 +94,7 @@ def test_ecdh_invalid_shared_secret_curve(): "deb5712fa027ac8d2f22c455ccb73a91e17b6512b5e030e7" "7e2690a02cc9b28708431a29fb54b87b1f0c14e011ac2125", "c208847568b98835d7312cef1f97f7aa298283152313c29d", - id="NIST192p-2" + id="NIST192p-2", ), pytest.param( NIST192p, @@ -100,7 +102,7 @@ def test_ecdh_invalid_shared_secret_curve(): "4edaa8efc5a0f40f843663ec5815e7762dddc008e663c20f" "0a9f8dc67a3e60ef6d64b522185d03df1fc0adfd42478279", "87229107047a3b611920d6e3b2c0c89bea4f49412260b8dd", - id="NIST192p-3" + id="NIST192p-3", ), pytest.param( NIST192p, @@ -108,7 +110,7 @@ def test_ecdh_invalid_shared_secret_curve(): "8887c276edeed3e9e866b46d58d895c73fbd80b63e382e88" "04c5097ba6645e16206cfb70f7052655947dd44a17f1f9d5", "eec0bed8fc55e1feddc82158fd6dc0d48a4d796aaf47d46c", - id="NIST192p-4" + id="NIST192p-4", ), pytest.param( NIST192p, @@ -116,7 +118,7 @@ def test_ecdh_invalid_shared_secret_curve(): "0d045f30254adc1fcefa8a5b1f31bf4e739dd327cd18d594" "542c314e41427c08278a08ce8d7305f3b5b849c72d8aff73", "716e743b1b37a2cd8479f0a3d5a74c10ba2599be18d7e2f4", - id="NIST192p-5" + id="NIST192p-5", ), pytest.param( NIST192p, @@ -124,7 +126,7 @@ def test_ecdh_invalid_shared_secret_curve(): "fb35ca20d2e96665c51b98e8f6eb3d79113508d8bccd4516" "368eec0d5bfb847721df6aaff0e5d48c444f74bf9cd8a5a7", "f67053b934459985a315cb017bf0302891798d45d0e19508", - id="NIST192p-6" + id="NIST192p-6", ), pytest.param( NIST224p, @@ -132,7 +134,7 @@ def test_ecdh_invalid_shared_secret_curve(): "af33cd0629bc7e996320a3f40368f74de8704fa37b8fab69abaae280" "882092ccbba7930f419a8a4f9bb16978bbc3838729992559a6f2e2d7", "7d96f9a3bd3c05cf5cc37feb8b9d5209d5c2597464dec3e9983743e8", - id="NIST224p" + id="NIST224p", ), pytest.param( NIST256p, @@ -140,7 +142,7 @@ def test_ecdh_invalid_shared_secret_curve(): "700c48f77f56584c5cc632ca65640db91b6bacce3a4df6b42ce7cc838833d287" "db71e509e3fd9b060ddb20ba5c51dcc5948d46fbf640dfe0441782cab85fa4ac", "46fc62106420ff012e54a434fbdd2d25ccc5852060561e68040dd7778997bd7b", - id="NIST256p-1" + id="NIST256p-1", ), pytest.param( NIST256p, @@ -148,7 +150,7 @@ def test_ecdh_invalid_shared_secret_curve(): "809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7ae" "b29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3", "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67", - id="NIST256p-2" + id="NIST256p-2", ), pytest.param( NIST256p, @@ -156,7 +158,7 @@ def test_ecdh_invalid_shared_secret_curve(): "a2339c12d4a03c33546de533268b4ad667debf458b464d77443636440ee7fec3" "ef48a3ab26e20220bcda2c1851076839dae88eae962869a497bf73cb66faf536", "2d457b78b4614132477618a5b077965ec90730a8c81a1c75d6d4ec68005d67ec", - id="NIST256p-3" + id="NIST256p-3", ), pytest.param( NIST256p, @@ -164,7 +166,7 @@ def test_ecdh_invalid_shared_secret_curve(): "df3989b9fa55495719b3cf46dccd28b5153f7808191dd518eff0c3cff2b705ed" "422294ff46003429d739a33206c8752552c8ba54a270defc06e221e0feaf6ac4", "96441259534b80f6aee3d287a6bb17b5094dd4277d9e294f8fe73e48bf2a0024", - id="NIST256p-4" + id="NIST256p-4", ), pytest.param( NIST256p, @@ -172,7 +174,7 @@ def test_ecdh_invalid_shared_secret_curve(): "41192d2813e79561e6a1d6f53c8bc1a433a199c835e141b05a74a97b0faeb922" "1af98cc45e98a7e041b01cf35f462b7562281351c8ebf3ffa02e33a0722a1328", "19d44c8d63e8e8dd12c22a87b8cd4ece27acdde04dbf47f7f27537a6999a8e62", - id="NIST256p-5" + id="NIST256p-5", ), pytest.param( NIST256p, @@ -180,7 +182,7 @@ def test_ecdh_invalid_shared_secret_curve(): "33e82092a0f1fb38f5649d5867fba28b503172b7035574bf8e5b7100a3052792" "f2cf6b601e0a05945e335550bf648d782f46186c772c0f20d3cd0d6b8ca14b2f", "664e45d5bba4ac931cd65d52017e4be9b19a515f669bea4703542a2c525cd3d3", - id="NIST256p-6" + id="NIST256p-6", ), pytest.param( NIST384p, @@ -192,7 +194,7 @@ def test_ecdh_invalid_shared_secret_curve(): "1efedf243451915ed0905a32b060992b468c64766fc8437a", "5f9d29dc5e31a163060356213669c8ce132e22f57c9a04f4" "0ba7fcead493b457e5621e766c40a2e3d4d6a04b25e533f1", - id="NIST384p" + id="NIST384p", ), pytest.param( NIST521p, @@ -204,11 +206,11 @@ def test_ecdh_invalid_shared_secret_curve(): "bde99e0f6716939e632bc8986fa18dccd443a348b6c3e522497955a4f3c302f676", "005fc70477c3e63bc3954bd0df3ea0d1f41ee21746ed95fc5e1fdf90930d5e1366" "72d72cc770742d1711c3c3a4c334a0ad9759436a4d3c5bf6e74b9578fac148c831", - id="NIST521p" + id="NIST521p", ), ], ) -def test_ecdh_NIST(curve,privatekey,pubkey,secret): +def test_ecdh_NIST(curve, privatekey, pubkey, secret): ecdh = ECDH(curve=curve) ecdh.load_private_key_bytes(unhexlify(privatekey)) ecdh.load_received_public_key_bytes(unhexlify(pubkey)) @@ -223,19 +225,23 @@ def test_ecdh_NIST(curve,privatekey,pubkey,secret): "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" "bA==\n" - "-----END EC PRIVATE KEY-----\n") + "-----END EC PRIVATE KEY-----\n" +) der_local_private_key = ( "305f02010104185ec8420bd6ef9252a942e989043ca29f561fa525770eb1c5a00a06082a864" "8ce3d030101a13403320004b88177d084ef17f5e45639408028360f9f59b4a4d7264e62da06" - "51dce47a35a4c5b45cf51593423a8b557b9c2099f36c") + "51dce47a35a4c5b45cf51593423a8b557b9c2099f36c" +) pem_remote_public_key = ( "-----BEGIN PUBLIC KEY-----\n" "MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n" "Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n" - "-----END PUBLIC KEY-----\n") + "-----END PUBLIC KEY-----\n" +) der_remote_public_key = ( "3049301306072a8648ce3d020106082a8648ce3d03010103320004b88177d084ef17f5e4563" - "9408028360f9f59b4a4d7264e62da0651dce47a35a4c5b45cf51593423a8b557b9c2099f36c") + "9408028360f9f59b4a4d7264e62da0651dce47a35a4c5b45cf51593423a8b557b9c2099f36c" +) gshared_secret = "8f457e34982478d1c34b9cd2d0c15911b72dd60d869e2cea" @@ -266,23 +272,29 @@ class RunOpenSslError(Exception): def run_openssl(cmd): OPENSSL = "openssl" - p = subprocess.Popen([OPENSSL] + cmd.split(), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + p = subprocess.Popen( + [OPENSSL] + cmd.split(), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) stdout, ignored = p.communicate() if p.returncode != 0: raise RunOpenSslError( - "cmd '%s %s' failed: rc=%s, stdout/err was %s" % - (OPENSSL, cmd, p.returncode, stdout)) + "cmd '%s %s' failed: rc=%s, stdout/err was %s" + % (OPENSSL, cmd, p.returncode, stdout) + ) return stdout.decode() -OPENSSL_SUPPORTED_CURVES = set(c.split(':')[0].strip() for c in - run_openssl("ecparam -list_curves") - .split('\n')) +OPENSSL_SUPPORTED_CURVES = set( + c.split(":")[0].strip() + for c in run_openssl("ecparam -list_curves").split("\n") +) -@pytest.mark.parametrize("vcurve", curves, ids=[curve.name for curve in curves]) +@pytest.mark.parametrize( + "vcurve", curves, ids=[curve.name for curve in curves] +) def test_ecdh_with_openssl(vcurve): assert vcurve.openssl_name @@ -302,8 +314,12 @@ def test_ecdh_with_openssl(vcurve): if os.path.isdir("t"): shutil.rmtree("t") os.mkdir("t") - run_openssl("ecparam -name %s -genkey -out t/privkey1.pem" % vcurve.openssl_name) - run_openssl("ecparam -name %s -genkey -out t/privkey2.pem" % vcurve.openssl_name) + run_openssl( + "ecparam -name %s -genkey -out t/privkey1.pem" % vcurve.openssl_name + ) + run_openssl( + "ecparam -name %s -genkey -out t/privkey2.pem" % vcurve.openssl_name + ) run_openssl("ec -in t/privkey1.pem -pubout -out t/pubkey1.pem") ecdh1 = ECDH(curve=vcurve) @@ -331,8 +347,12 @@ def test_ecdh_with_openssl(vcurve): assert secret1 == secret2 try: - run_openssl("pkeyutl -derive -inkey t/privkey1.pem -peerkey t/pubkey2.pem -out t/secret1") - run_openssl("pkeyutl -derive -inkey t/privkey2.pem -peerkey t/pubkey1.pem -out t/secret2") + run_openssl( + "pkeyutl -derive -inkey t/privkey1.pem -peerkey t/pubkey2.pem -out t/secret1" + ) + run_openssl( + "pkeyutl -derive -inkey t/privkey2.pem -peerkey t/pubkey1.pem -out t/secret2" + ) except RunOpenSslError: pytest.skip("system openssl does not support `pkeyutl -derive`") return diff --git a/src/ecdsa/test_ecdsa.py b/src/ecdsa/test_ecdsa.py index 71c68913..e656b880 100644 --- a/src/ecdsa/test_ecdsa.py +++ b/src/ecdsa/test_ecdsa.py @@ -2,15 +2,26 @@ import sys import hypothesis.strategies as st from hypothesis import given, settings, note, example + try: import unittest2 as unittest except ImportError: import unittest import pytest -from .ecdsa import Private_key, Public_key, Signature, \ - generator_192, digest_integer, ellipticcurve, point_is_valid, \ - generator_224, generator_256, generator_384, generator_521, \ - generator_secp256k1 +from .ecdsa import ( + Private_key, + Public_key, + Signature, + generator_192, + digest_integer, + ellipticcurve, + point_is_valid, + generator_224, + generator_256, + generator_384, + generator_521, + generator_secp256k1, +) HYP_SETTINGS = {} @@ -22,6 +33,7 @@ class TestP192FromX9_62(unittest.TestCase): """Check test vectors from X9.62""" + @classmethod def setUpClass(cls): cls.d = 651056770906015076056810763456358567190100156695615665659 @@ -61,125 +73,137 @@ def test_rejection(self): class TestPublicKey(unittest.TestCase): - def test_equality_public_keys(self): gen = generator_192 - x = 0xc58d61f88d905293bcd4cd0080bcb1b7f811f2ffa41979f6 - y = 0x8804dc7a7c4c7f8b5d437f5156f3312ca7d6de8a0e11867f + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F point = ellipticcurve.Point(gen.curve(), x, y) pub_key1 = Public_key(gen, point) pub_key2 = Public_key(gen, point) self.assertEqual(pub_key1, pub_key2) - + def test_inequality_public_key(self): gen = generator_192 - x1 = 0xc58d61f88d905293bcd4cd0080bcb1b7f811f2ffa41979f6 - y1 = 0x8804dc7a7c4c7f8b5d437f5156f3312ca7d6de8a0e11867f + x1 = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y1 = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F point1 = ellipticcurve.Point(gen.curve(), x1, y1) - - x2 = 0x6a223d00bd22c52833409a163e057e5b5da1def2a197dd15 - y2 = 0x7b482604199367f1f303f9ef627f922f97023e90eae08abf + + x2 = 0x6A223D00BD22C52833409A163E057E5B5DA1DEF2A197DD15 + y2 = 0x7B482604199367F1F303F9EF627F922F97023E90EAE08ABF point2 = ellipticcurve.Point(gen.curve(), x2, y2) - + pub_key1 = Public_key(gen, point1) pub_key2 = Public_key(gen, point2) self.assertNotEqual(pub_key1, pub_key2) - + def test_inequality_public_key_not_implemented(self): gen = generator_192 - x = 0xc58d61f88d905293bcd4cd0080bcb1b7f811f2ffa41979f6 - y = 0x8804dc7a7c4c7f8b5d437f5156f3312ca7d6de8a0e11867f + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F point = ellipticcurve.Point(gen.curve(), x, y) pub_key = Public_key(gen, point) self.assertNotEqual(pub_key, None) class TestPrivateKey(unittest.TestCase): - @classmethod def setUpClass(cls): gen = generator_192 - x = 0xc58d61f88d905293bcd4cd0080bcb1b7f811f2ffa41979f6 - y = 0x8804dc7a7c4c7f8b5d437f5156f3312ca7d6de8a0e11867f + x = 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6 + y = 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F point = ellipticcurve.Point(gen.curve(), x, y) cls.pub_key = Public_key(gen, point) - + def test_equality_private_keys(self): pr_key1 = Private_key(self.pub_key, 100) pr_key2 = Private_key(self.pub_key, 100) self.assertEqual(pr_key1, pr_key2) - + def test_inequality_private_keys(self): pr_key1 = Private_key(self.pub_key, 100) pr_key2 = Private_key(self.pub_key, 200) self.assertNotEqual(pr_key1, pr_key2) - + def test_inequality_private_keys_not_implemented(self): pr_key = Private_key(self.pub_key, 100) self.assertNotEqual(pr_key, None) - + # Testing point validity, as per ECDSAVS.pdf B.2.2: P192_POINTS = [ - (generator_192, - 0xcd6d0f029a023e9aaca429615b8f577abee685d8257cc83a, - 0x00019c410987680e9fb6c0b6ecc01d9a2647c8bae27721bacdfc, - False), - - (generator_192, - 0x00017f2fce203639e9eaf9fb50b81fc32776b30e3b02af16c73b, - 0x95da95c5e72dd48e229d4748d4eee658a9a54111b23b2adb, - False), - - (generator_192, - 0x4f77f8bc7fccbadd5760f4938746d5f253ee2168c1cf2792, - 0x000147156ff824d131629739817edb197717c41aab5c2a70f0f6, - False), - - (generator_192, - 0xc58d61f88d905293bcd4cd0080bcb1b7f811f2ffa41979f6, - 0x8804dc7a7c4c7f8b5d437f5156f3312ca7d6de8a0e11867f, - True), - - (generator_192, - 0xcdf56c1aa3d8afc53c521adf3ffb96734a6a630a4a5b5a70, - 0x97c1c44a5fb229007b5ec5d25f7413d170068ffd023caa4e, - True), - - (generator_192, - 0x89009c0dc361c81e99280c8e91df578df88cdf4b0cdedced, - 0x27be44a529b7513e727251f128b34262a0fd4d8ec82377b9, - True), - - (generator_192, - 0x6a223d00bd22c52833409a163e057e5b5da1def2a197dd15, - 0x7b482604199367f1f303f9ef627f922f97023e90eae08abf, - True), - - (generator_192, - 0x6dccbde75c0948c98dab32ea0bc59fe125cf0fb1a3798eda, - 0x0001171a3e0fa60cf3096f4e116b556198de430e1fbd330c8835, - False), - - (generator_192, - 0xd266b39e1f491fc4acbbbc7d098430931cfa66d55015af12, - 0x193782eb909e391a3148b7764e6b234aa94e48d30a16dbb2, - False), - - (generator_192, - 0x9d6ddbcd439baa0c6b80a654091680e462a7d1d3f1ffeb43, - 0x6ad8efc4d133ccf167c44eb4691c80abffb9f82b932b8caa, - False), - - (generator_192, - 0x146479d944e6bda87e5b35818aa666a4c998a71f4e95edbc, - 0xa86d6fe62bc8fbd88139693f842635f687f132255858e7f6, - False), - - (generator_192, - 0xe594d4a598046f3598243f50fd2c7bd7d380edb055802253, - 0x509014c0c4d6b536e3ca750ec09066af39b4c8616a53a923, - False)] + ( + generator_192, + 0xCD6D0F029A023E9AACA429615B8F577ABEE685D8257CC83A, + 0x00019C410987680E9FB6C0B6ECC01D9A2647C8BAE27721BACDFC, + False, + ), + ( + generator_192, + 0x00017F2FCE203639E9EAF9FB50B81FC32776B30E3B02AF16C73B, + 0x95DA95C5E72DD48E229D4748D4EEE658A9A54111B23B2ADB, + False, + ), + ( + generator_192, + 0x4F77F8BC7FCCBADD5760F4938746D5F253EE2168C1CF2792, + 0x000147156FF824D131629739817EDB197717C41AAB5C2A70F0F6, + False, + ), + ( + generator_192, + 0xC58D61F88D905293BCD4CD0080BCB1B7F811F2FFA41979F6, + 0x8804DC7A7C4C7F8B5D437F5156F3312CA7D6DE8A0E11867F, + True, + ), + ( + generator_192, + 0xCDF56C1AA3D8AFC53C521ADF3FFB96734A6A630A4A5B5A70, + 0x97C1C44A5FB229007B5EC5D25F7413D170068FFD023CAA4E, + True, + ), + ( + generator_192, + 0x89009C0DC361C81E99280C8E91DF578DF88CDF4B0CDEDCED, + 0x27BE44A529B7513E727251F128B34262A0FD4D8EC82377B9, + True, + ), + ( + generator_192, + 0x6A223D00BD22C52833409A163E057E5B5DA1DEF2A197DD15, + 0x7B482604199367F1F303F9EF627F922F97023E90EAE08ABF, + True, + ), + ( + generator_192, + 0x6DCCBDE75C0948C98DAB32EA0BC59FE125CF0FB1A3798EDA, + 0x0001171A3E0FA60CF3096F4E116B556198DE430E1FBD330C8835, + False, + ), + ( + generator_192, + 0xD266B39E1F491FC4ACBBBC7D098430931CFA66D55015AF12, + 0x193782EB909E391A3148B7764E6B234AA94E48D30A16DBB2, + False, + ), + ( + generator_192, + 0x9D6DDBCD439BAA0C6B80A654091680E462A7D1D3F1FFEB43, + 0x6AD8EFC4D133CCF167C44EB4691C80ABFFB9F82B932B8CAA, + False, + ), + ( + generator_192, + 0x146479D944E6BDA87E5B35818AA666A4C998A71F4E95EDBC, + 0xA86D6FE62BC8FBD88139693F842635F687F132255858E7F6, + False, + ), + ( + generator_192, + 0xE594D4A598046F3598243F50FD2C7BD7D380EDB055802253, + 0x509014C0C4D6B536E3CA750EC09066AF39B4C8616A53A923, + False, + ), +] @pytest.mark.parametrize("generator,x,y,expected", P192_POINTS) @@ -193,184 +217,245 @@ def test_point_validity(generator, x, y, expected): # Trying signature-verification tests from ECDSAVS.pdf B.2.4: CURVE_192_KATS = [ - (generator_192, - int("0x84ce72aa8699df436059f052ac51b6398d2511e49631bcb7e71f89c499b9ee" - "425dfbc13a5f6d408471b054f2655617cbbaf7937b7c80cd8865cf02c8487d30" - "d2b0fbd8b2c4e102e16d828374bbc47b93852f212d5043c3ea720f086178ff79" - "8cc4f63f787b9c2e419efa033e7644ea7936f54462dc21a6c4580725f7f0e7d1" - "58", 16), - 0xd9dbfb332aa8e5ff091e8ce535857c37c73f6250ffb2e7ac, - 0x282102e364feded3ad15ddf968f88d8321aa268dd483ebc4, - 0x64dca58a20787c488d11d6dd96313f1b766f2d8efe122916, - 0x1ecba28141e84ab4ecad92f56720e2cc83eb3d22dec72479, - True), - - (generator_192, - int("0x94bb5bacd5f8ea765810024db87f4224ad71362a3c28284b2b9f39fab86db1" - "2e8beb94aae899768229be8fdb6c4f12f28912bb604703a79ccff769c1607f5a" - "91450f30ba0460d359d9126cbd6296be6d9c4bb96c0ee74cbb44197c207f6db3" - "26ab6f5a659113a9034e54be7b041ced9dcf6458d7fb9cbfb2744d999f7dfd63" - "f4", 16), - 0x3e53ef8d3112af3285c0e74842090712cd324832d4277ae7, - 0xcc75f8952d30aec2cbb719fc6aa9934590b5d0ff5a83adb7, - 0x8285261607283ba18f335026130bab31840dcfd9c3e555af, - 0x356d89e1b04541afc9704a45e9c535ce4a50929e33d7e06c, - True), - - (generator_192, - int("0xf6227a8eeb34afed1621dcc89a91d72ea212cb2f476839d9b4243c66877911" - "b37b4ad6f4448792a7bbba76c63bdd63414b6facab7dc71c3396a73bd7ee14cd" - "d41a659c61c99b779cecf07bc51ab391aa3252386242b9853ea7da67fd768d30" - "3f1b9b513d401565b6f1eb722dfdb96b519fe4f9bd5de67ae131e64b40e78c42" - "dd", 16), - 0x16335dbe95f8e8254a4e04575d736befb258b8657f773cb7, - 0x421b13379c59bc9dce38a1099ca79bbd06d647c7f6242336, - 0x4141bd5d64ea36c5b0bd21ef28c02da216ed9d04522b1e91, - 0x159a6aa852bcc579e821b7bb0994c0861fb08280c38daa09, - False), - - (generator_192, - int("0x16b5f93afd0d02246f662761ed8e0dd9504681ed02a253006eb36736b56309" - "7ba39f81c8e1bce7a16c1339e345efabbc6baa3efb0612948ae51103382a8ee8" - "bc448e3ef71e9f6f7a9676694831d7f5dd0db5446f179bcb737d4a526367a447" - "bfe2c857521c7f40b6d7d7e01a180d92431fb0bbd29c04a0c420a57b3ed26ccd" - "8a", 16), - 0xfd14cdf1607f5efb7b1793037b15bdf4baa6f7c16341ab0b, - 0x83fa0795cc6c4795b9016dac928fd6bac32f3229a96312c4, - 0x8dfdb832951e0167c5d762a473c0416c5c15bc1195667dc1, - 0x1720288a2dc13fa1ec78f763f8fe2ff7354a7e6fdde44520, - False), - - (generator_192, - int("0x08a2024b61b79d260e3bb43ef15659aec89e5b560199bc82cf7c65c77d3919" - "2e03b9a895d766655105edd9188242b91fbde4167f7862d4ddd61e5d4ab55196" - "683d4f13ceb90d87aea6e07eb50a874e33086c4a7cb0273a8e1c4408f4b846bc" - "eae1ebaac1b2b2ea851a9b09de322efe34cebe601653efd6ddc876ce8c2f2072" - "fb", 16), - 0x674f941dc1a1f8b763c9334d726172d527b90ca324db8828, - 0x65adfa32e8b236cb33a3e84cf59bfb9417ae7e8ede57a7ff, - 0x9508b9fdd7daf0d8126f9e2bc5a35e4c6d800b5b804d7796, - 0x36f2bf6b21b987c77b53bb801b3435a577e3d493744bfab0, - False), - - (generator_192, - int("0x1843aba74b0789d4ac6b0b8923848023a644a7b70afa23b1191829bbe4397c" - "e15b629bf21a8838298653ed0c19222b95fa4f7390d1b4c844d96e645537e0aa" - "e98afb5c0ac3bd0e4c37f8daaff25556c64e98c319c52687c904c4de7240a1cc" - "55cd9756b7edaef184e6e23b385726e9ffcba8001b8f574987c1a3fedaaa83ca" - "6d", 16), - 0x10ecca1aad7220b56a62008b35170bfd5e35885c4014a19f, - 0x04eb61984c6c12ade3bc47f3c629ece7aa0a033b9948d686, - 0x82bfa4e82c0dfe9274169b86694e76ce993fd83b5c60f325, - 0xa97685676c59a65dbde002fe9d613431fb183e8006d05633, - False), - - (generator_192, - int("0x5a478f4084ddd1a7fea038aa9732a822106385797d02311aeef4d0264f824f" - "698df7a48cfb6b578cf3da416bc0799425bb491be5b5ecc37995b85b03420a98" - "f2c4dc5c31a69a379e9e322fbe706bbcaf0f77175e05cbb4fa162e0da82010a2" - "78461e3e974d137bc746d1880d6eb02aa95216014b37480d84b87f717bb13f76" - "e1", 16), - 0x6636653cb5b894ca65c448277b29da3ad101c4c2300f7c04, - 0xfdf1cbb3fc3fd6a4f890b59e554544175fa77dbdbeb656c1, - 0xeac2ddecddfb79931a9c3d49c08de0645c783a24cb365e1c, - 0x3549fee3cfa7e5f93bc47d92d8ba100e881a2a93c22f8d50, - False), - - (generator_192, - int("0xc598774259a058fa65212ac57eaa4f52240e629ef4c310722088292d1d4af6" - "c39b49ce06ba77e4247b20637174d0bd67c9723feb57b5ead232b47ea452d5d7" - "a089f17c00b8b6767e434a5e16c231ba0efa718a340bf41d67ea2d295812ff1b" - "9277daacb8bc27b50ea5e6443bcf95ef4e9f5468fe78485236313d53d1c68f6b" - "a2", 16), - 0xa82bd718d01d354001148cd5f69b9ebf38ff6f21898f8aaa, - 0xe67ceede07fc2ebfafd62462a51e4b6c6b3d5b537b7caf3e, - 0x4d292486c620c3de20856e57d3bb72fcde4a73ad26376955, - 0xa85289591a6081d5728825520e62ff1c64f94235c04c7f95, - False), - - (generator_192, - int("0xca98ed9db081a07b7557f24ced6c7b9891269a95d2026747add9e9eb80638a" - "961cf9c71a1b9f2c29744180bd4c3d3db60f2243c5c0b7cc8a8d40a3f9a7fc91" - "0250f2187136ee6413ffc67f1a25e1c4c204fa9635312252ac0e0481d89b6d53" - "808f0c496ba87631803f6c572c1f61fa049737fdacce4adff757afed4f05beb6" - "58", 16), - 0x7d3b016b57758b160c4fca73d48df07ae3b6b30225126c2f, - 0x4af3790d9775742bde46f8da876711be1b65244b2b39e7ec, - 0x95f778f5f656511a5ab49a5d69ddd0929563c29cbc3a9e62, - 0x75c87fc358c251b4c83d2dd979faad496b539f9f2ee7a289, - False), - - (generator_192, - int("0x31dd9a54c8338bea06b87eca813d555ad1850fac9742ef0bbe40dad400e102" - "88acc9c11ea7dac79eb16378ebea9490e09536099f1b993e2653cd50240014c9" - "0a9c987f64545abc6a536b9bd2435eb5e911fdfde2f13be96ea36ad38df4ae9e" - "a387b29cced599af777338af2794820c9cce43b51d2112380a35802ab7e396c9" - "7a", 16), - 0x9362f28c4ef96453d8a2f849f21e881cd7566887da8beb4a, - 0xe64d26d8d74c48a024ae85d982ee74cd16046f4ee5333905, - 0xf3923476a296c88287e8de914b0b324ad5a963319a4fe73b, - 0xf0baeed7624ed00d15244d8ba2aede085517dbdec8ac65f5, - True), - - (generator_192, - int("0xb2b94e4432267c92f9fdb9dc6040c95ffa477652761290d3c7de312283f645" - "0d89cc4aabe748554dfb6056b2d8e99c7aeaad9cdddebdee9dbc099839562d90" - "64e68e7bb5f3a6bba0749ca9a538181fc785553a4000785d73cc207922f63e8c" - "e1112768cb1de7b673aed83a1e4a74592f1268d8e2a4e9e63d414b5d442bd045" - "6d", 16), - 0xcc6fc032a846aaac25533eb033522824f94e670fa997ecef, - 0xe25463ef77a029eccda8b294fd63dd694e38d223d30862f1, - 0x066b1d07f3a40e679b620eda7f550842a35c18b80c5ebe06, - 0xa0b0fb201e8f2df65e2c4508ef303bdc90d934016f16b2dc, - False), - - (generator_192, - int("0x4366fcadf10d30d086911de30143da6f579527036937007b337f7282460eae" - "5678b15cccda853193ea5fc4bc0a6b9d7a31128f27e1214988592827520b214e" - "ed5052f7775b750b0c6b15f145453ba3fee24a085d65287e10509eb5d5f602c4" - "40341376b95c24e5c4727d4b859bfe1483d20538acdd92c7997fa9c614f0f839" - "d7", 16), - 0x955c908fe900a996f7e2089bee2f6376830f76a19135e753, - 0xba0c42a91d3847de4a592a46dc3fdaf45a7cc709b90de520, - 0x1f58ad77fc04c782815a1405b0925e72095d906cbf52a668, - 0xf2e93758b3af75edf784f05a6761c9b9a6043c66b845b599, - False), - - (generator_192, - int("0x543f8af57d750e33aa8565e0cae92bfa7a1ff78833093421c2942cadf99866" - "70a5ff3244c02a8225e790fbf30ea84c74720abf99cfd10d02d34377c3d3b412" - "69bea763384f372bb786b5846f58932defa68023136cd571863b304886e95e52" - "e7877f445b9364b3f06f3c28da12707673fecb4b8071de06b6e0a3c87da160ce" - "f3", 16), - 0x31f7fa05576d78a949b24812d4383107a9a45bb5fccdd835, - 0x8dc0eb65994a90f02b5e19bd18b32d61150746c09107e76b, - 0xbe26d59e4e883dde7c286614a767b31e49ad88789d3a78ff, - 0x8762ca831c1ce42df77893c9b03119428e7a9b819b619068, - False), - - (generator_192, - int("0xd2e8454143ce281e609a9d748014dcebb9d0bc53adb02443a6aac2ffe6cb009f" - "387c346ecb051791404f79e902ee333ad65e5c8cb38dc0d1d39a8dc90add502357" - "2720e5b94b190d43dd0d7873397504c0c7aef2727e628eb6a74411f2e400c65670" - "716cb4a815dc91cbbfeb7cfe8c929e93184c938af2c078584da045e8f8d1", 16), - 0x66aa8edbbdb5cf8e28ceb51b5bda891cae2df84819fe25c0, - 0x0c6bc2f69030a7ce58d4a00e3b3349844784a13b8936f8da, - 0xa4661e69b1734f4a71b788410a464b71e7ffe42334484f23, - 0x738421cf5e049159d69c57a915143e226cac8355e149afe9, - False), - - (generator_192, - int("0x6660717144040f3e2f95a4e25b08a7079c702a8b29babad5a19a87654bc5c5af" - "a261512a11b998a4fb36b5d8fe8bd942792ff0324b108120de86d63f65855e5461" - "184fc96a0a8ffd2ce6d5dfb0230cbbdd98f8543e361b3205f5da3d500fdc8bac6d" - "b377d75ebef3cb8f4d1ff738071ad0938917889250b41dd1d98896ca06fb", 16), - 0xbcfacf45139b6f5f690a4c35a5fffa498794136a2353fc77, - 0x6f4a6c906316a6afc6d98fe1f0399d056f128fe0270b0f22, - 0x9db679a3dafe48f7ccad122933acfe9da0970b71c94c21c1, - 0x984c2db99827576c0a41a5da41e07d8cc768bc82f18c9da9, - False) - ] + ( + generator_192, + int( + "0x84ce72aa8699df436059f052ac51b6398d2511e49631bcb7e71f89c499b9ee" + "425dfbc13a5f6d408471b054f2655617cbbaf7937b7c80cd8865cf02c8487d30" + "d2b0fbd8b2c4e102e16d828374bbc47b93852f212d5043c3ea720f086178ff79" + "8cc4f63f787b9c2e419efa033e7644ea7936f54462dc21a6c4580725f7f0e7d1" + "58", + 16, + ), + 0xD9DBFB332AA8E5FF091E8CE535857C37C73F6250FFB2E7AC, + 0x282102E364FEDED3AD15DDF968F88D8321AA268DD483EBC4, + 0x64DCA58A20787C488D11D6DD96313F1B766F2D8EFE122916, + 0x1ECBA28141E84AB4ECAD92F56720E2CC83EB3D22DEC72479, + True, + ), + ( + generator_192, + int( + "0x94bb5bacd5f8ea765810024db87f4224ad71362a3c28284b2b9f39fab86db1" + "2e8beb94aae899768229be8fdb6c4f12f28912bb604703a79ccff769c1607f5a" + "91450f30ba0460d359d9126cbd6296be6d9c4bb96c0ee74cbb44197c207f6db3" + "26ab6f5a659113a9034e54be7b041ced9dcf6458d7fb9cbfb2744d999f7dfd63" + "f4", + 16, + ), + 0x3E53EF8D3112AF3285C0E74842090712CD324832D4277AE7, + 0xCC75F8952D30AEC2CBB719FC6AA9934590B5D0FF5A83ADB7, + 0x8285261607283BA18F335026130BAB31840DCFD9C3E555AF, + 0x356D89E1B04541AFC9704A45E9C535CE4A50929E33D7E06C, + True, + ), + ( + generator_192, + int( + "0xf6227a8eeb34afed1621dcc89a91d72ea212cb2f476839d9b4243c66877911" + "b37b4ad6f4448792a7bbba76c63bdd63414b6facab7dc71c3396a73bd7ee14cd" + "d41a659c61c99b779cecf07bc51ab391aa3252386242b9853ea7da67fd768d30" + "3f1b9b513d401565b6f1eb722dfdb96b519fe4f9bd5de67ae131e64b40e78c42" + "dd", + 16, + ), + 0x16335DBE95F8E8254A4E04575D736BEFB258B8657F773CB7, + 0x421B13379C59BC9DCE38A1099CA79BBD06D647C7F6242336, + 0x4141BD5D64EA36C5B0BD21EF28C02DA216ED9D04522B1E91, + 0x159A6AA852BCC579E821B7BB0994C0861FB08280C38DAA09, + False, + ), + ( + generator_192, + int( + "0x16b5f93afd0d02246f662761ed8e0dd9504681ed02a253006eb36736b56309" + "7ba39f81c8e1bce7a16c1339e345efabbc6baa3efb0612948ae51103382a8ee8" + "bc448e3ef71e9f6f7a9676694831d7f5dd0db5446f179bcb737d4a526367a447" + "bfe2c857521c7f40b6d7d7e01a180d92431fb0bbd29c04a0c420a57b3ed26ccd" + "8a", + 16, + ), + 0xFD14CDF1607F5EFB7B1793037B15BDF4BAA6F7C16341AB0B, + 0x83FA0795CC6C4795B9016DAC928FD6BAC32F3229A96312C4, + 0x8DFDB832951E0167C5D762A473C0416C5C15BC1195667DC1, + 0x1720288A2DC13FA1EC78F763F8FE2FF7354A7E6FDDE44520, + False, + ), + ( + generator_192, + int( + "0x08a2024b61b79d260e3bb43ef15659aec89e5b560199bc82cf7c65c77d3919" + "2e03b9a895d766655105edd9188242b91fbde4167f7862d4ddd61e5d4ab55196" + "683d4f13ceb90d87aea6e07eb50a874e33086c4a7cb0273a8e1c4408f4b846bc" + "eae1ebaac1b2b2ea851a9b09de322efe34cebe601653efd6ddc876ce8c2f2072" + "fb", + 16, + ), + 0x674F941DC1A1F8B763C9334D726172D527B90CA324DB8828, + 0x65ADFA32E8B236CB33A3E84CF59BFB9417AE7E8EDE57A7FF, + 0x9508B9FDD7DAF0D8126F9E2BC5A35E4C6D800B5B804D7796, + 0x36F2BF6B21B987C77B53BB801B3435A577E3D493744BFAB0, + False, + ), + ( + generator_192, + int( + "0x1843aba74b0789d4ac6b0b8923848023a644a7b70afa23b1191829bbe4397c" + "e15b629bf21a8838298653ed0c19222b95fa4f7390d1b4c844d96e645537e0aa" + "e98afb5c0ac3bd0e4c37f8daaff25556c64e98c319c52687c904c4de7240a1cc" + "55cd9756b7edaef184e6e23b385726e9ffcba8001b8f574987c1a3fedaaa83ca" + "6d", + 16, + ), + 0x10ECCA1AAD7220B56A62008B35170BFD5E35885C4014A19F, + 0x04EB61984C6C12ADE3BC47F3C629ECE7AA0A033B9948D686, + 0x82BFA4E82C0DFE9274169B86694E76CE993FD83B5C60F325, + 0xA97685676C59A65DBDE002FE9D613431FB183E8006D05633, + False, + ), + ( + generator_192, + int( + "0x5a478f4084ddd1a7fea038aa9732a822106385797d02311aeef4d0264f824f" + "698df7a48cfb6b578cf3da416bc0799425bb491be5b5ecc37995b85b03420a98" + "f2c4dc5c31a69a379e9e322fbe706bbcaf0f77175e05cbb4fa162e0da82010a2" + "78461e3e974d137bc746d1880d6eb02aa95216014b37480d84b87f717bb13f76" + "e1", + 16, + ), + 0x6636653CB5B894CA65C448277B29DA3AD101C4C2300F7C04, + 0xFDF1CBB3FC3FD6A4F890B59E554544175FA77DBDBEB656C1, + 0xEAC2DDECDDFB79931A9C3D49C08DE0645C783A24CB365E1C, + 0x3549FEE3CFA7E5F93BC47D92D8BA100E881A2A93C22F8D50, + False, + ), + ( + generator_192, + int( + "0xc598774259a058fa65212ac57eaa4f52240e629ef4c310722088292d1d4af6" + "c39b49ce06ba77e4247b20637174d0bd67c9723feb57b5ead232b47ea452d5d7" + "a089f17c00b8b6767e434a5e16c231ba0efa718a340bf41d67ea2d295812ff1b" + "9277daacb8bc27b50ea5e6443bcf95ef4e9f5468fe78485236313d53d1c68f6b" + "a2", + 16, + ), + 0xA82BD718D01D354001148CD5F69B9EBF38FF6F21898F8AAA, + 0xE67CEEDE07FC2EBFAFD62462A51E4B6C6B3D5B537B7CAF3E, + 0x4D292486C620C3DE20856E57D3BB72FCDE4A73AD26376955, + 0xA85289591A6081D5728825520E62FF1C64F94235C04C7F95, + False, + ), + ( + generator_192, + int( + "0xca98ed9db081a07b7557f24ced6c7b9891269a95d2026747add9e9eb80638a" + "961cf9c71a1b9f2c29744180bd4c3d3db60f2243c5c0b7cc8a8d40a3f9a7fc91" + "0250f2187136ee6413ffc67f1a25e1c4c204fa9635312252ac0e0481d89b6d53" + "808f0c496ba87631803f6c572c1f61fa049737fdacce4adff757afed4f05beb6" + "58", + 16, + ), + 0x7D3B016B57758B160C4FCA73D48DF07AE3B6B30225126C2F, + 0x4AF3790D9775742BDE46F8DA876711BE1B65244B2B39E7EC, + 0x95F778F5F656511A5AB49A5D69DDD0929563C29CBC3A9E62, + 0x75C87FC358C251B4C83D2DD979FAAD496B539F9F2EE7A289, + False, + ), + ( + generator_192, + int( + "0x31dd9a54c8338bea06b87eca813d555ad1850fac9742ef0bbe40dad400e102" + "88acc9c11ea7dac79eb16378ebea9490e09536099f1b993e2653cd50240014c9" + "0a9c987f64545abc6a536b9bd2435eb5e911fdfde2f13be96ea36ad38df4ae9e" + "a387b29cced599af777338af2794820c9cce43b51d2112380a35802ab7e396c9" + "7a", + 16, + ), + 0x9362F28C4EF96453D8A2F849F21E881CD7566887DA8BEB4A, + 0xE64D26D8D74C48A024AE85D982EE74CD16046F4EE5333905, + 0xF3923476A296C88287E8DE914B0B324AD5A963319A4FE73B, + 0xF0BAEED7624ED00D15244D8BA2AEDE085517DBDEC8AC65F5, + True, + ), + ( + generator_192, + int( + "0xb2b94e4432267c92f9fdb9dc6040c95ffa477652761290d3c7de312283f645" + "0d89cc4aabe748554dfb6056b2d8e99c7aeaad9cdddebdee9dbc099839562d90" + "64e68e7bb5f3a6bba0749ca9a538181fc785553a4000785d73cc207922f63e8c" + "e1112768cb1de7b673aed83a1e4a74592f1268d8e2a4e9e63d414b5d442bd045" + "6d", + 16, + ), + 0xCC6FC032A846AAAC25533EB033522824F94E670FA997ECEF, + 0xE25463EF77A029ECCDA8B294FD63DD694E38D223D30862F1, + 0x066B1D07F3A40E679B620EDA7F550842A35C18B80C5EBE06, + 0xA0B0FB201E8F2DF65E2C4508EF303BDC90D934016F16B2DC, + False, + ), + ( + generator_192, + int( + "0x4366fcadf10d30d086911de30143da6f579527036937007b337f7282460eae" + "5678b15cccda853193ea5fc4bc0a6b9d7a31128f27e1214988592827520b214e" + "ed5052f7775b750b0c6b15f145453ba3fee24a085d65287e10509eb5d5f602c4" + "40341376b95c24e5c4727d4b859bfe1483d20538acdd92c7997fa9c614f0f839" + "d7", + 16, + ), + 0x955C908FE900A996F7E2089BEE2F6376830F76A19135E753, + 0xBA0C42A91D3847DE4A592A46DC3FDAF45A7CC709B90DE520, + 0x1F58AD77FC04C782815A1405B0925E72095D906CBF52A668, + 0xF2E93758B3AF75EDF784F05A6761C9B9A6043C66B845B599, + False, + ), + ( + generator_192, + int( + "0x543f8af57d750e33aa8565e0cae92bfa7a1ff78833093421c2942cadf99866" + "70a5ff3244c02a8225e790fbf30ea84c74720abf99cfd10d02d34377c3d3b412" + "69bea763384f372bb786b5846f58932defa68023136cd571863b304886e95e52" + "e7877f445b9364b3f06f3c28da12707673fecb4b8071de06b6e0a3c87da160ce" + "f3", + 16, + ), + 0x31F7FA05576D78A949B24812D4383107A9A45BB5FCCDD835, + 0x8DC0EB65994A90F02B5E19BD18B32D61150746C09107E76B, + 0xBE26D59E4E883DDE7C286614A767B31E49AD88789D3A78FF, + 0x8762CA831C1CE42DF77893C9B03119428E7A9B819B619068, + False, + ), + ( + generator_192, + int( + "0xd2e8454143ce281e609a9d748014dcebb9d0bc53adb02443a6aac2ffe6cb009f" + "387c346ecb051791404f79e902ee333ad65e5c8cb38dc0d1d39a8dc90add502357" + "2720e5b94b190d43dd0d7873397504c0c7aef2727e628eb6a74411f2e400c65670" + "716cb4a815dc91cbbfeb7cfe8c929e93184c938af2c078584da045e8f8d1", + 16, + ), + 0x66AA8EDBBDB5CF8E28CEB51B5BDA891CAE2DF84819FE25C0, + 0x0C6BC2F69030A7CE58D4A00E3B3349844784A13B8936F8DA, + 0xA4661E69B1734F4A71B788410A464B71E7FFE42334484F23, + 0x738421CF5E049159D69C57A915143E226CAC8355E149AFE9, + False, + ), + ( + generator_192, + int( + "0x6660717144040f3e2f95a4e25b08a7079c702a8b29babad5a19a87654bc5c5af" + "a261512a11b998a4fb36b5d8fe8bd942792ff0324b108120de86d63f65855e5461" + "184fc96a0a8ffd2ce6d5dfb0230cbbdd98f8543e361b3205f5da3d500fdc8bac6d" + "b377d75ebef3cb8f4d1ff738071ad0938917889250b41dd1d98896ca06fb", + 16, + ), + 0xBCFACF45139B6F5F690A4C35A5FFFA498794136A2353FC77, + 0x6F4A6C906316A6AFC6D98FE1F0399D056F128FE0270B0F22, + 0x9DB679A3DAFE48F7CCAD122933ACFE9DA0970B71C94C21C1, + 0x984C2DB99827576C0A41A5DA41E07D8CC768BC82F18C9DA9, + False, + ), +] @pytest.mark.parametrize("gen,msg,qx,qy,r,s,expected", CURVE_192_KATS) @@ -379,13 +464,13 @@ def test_signature_validity(gen, msg, qx, qy, r, s, expected): `msg` = message, `qx` and `qy` represent the base point on elliptic curve of `gen`, `r` and `s` are the signature, and `expected` is True iff the signature is expected to be valid.""" - pubk = Public_key(gen, - ellipticcurve.Point(gen.curve(), qx, qy)) + pubk = Public_key(gen, ellipticcurve.Point(gen.curve(), qx, qy)) assert expected == pubk.verifies(digest_integer(msg), Signature(r, s)) -@pytest.mark.parametrize("gen,msg,qx,qy,r,s,expected", - [x for x in CURVE_192_KATS if x[6]]) +@pytest.mark.parametrize( + "gen,msg,qx,qy,r,s,expected", [x for x in CURVE_192_KATS if x[6]] +) def test_pk_recovery(gen, msg, r, s, qx, qy, expected): del expected sign = Signature(r, s) @@ -413,7 +498,8 @@ def st_random_gen_key_msg_nonce(draw): "generator_256": generator_256, "generator_secp256k1": generator_secp256k1, "generator_384": generator_384, - "generator_521": generator_521} + "generator_521": generator_521, + } name = draw(st.sampled_from(sorted(name_gen.keys()))) note("Generator used: {0}".format(name)) generator = name_gen[name] @@ -421,13 +507,17 @@ def st_random_gen_key_msg_nonce(draw): key = draw(st.integers(min_value=1, max_value=order)) msg = draw(st.integers(min_value=1, max_value=order)) - nonce = draw(st.integers(min_value=1, max_value=order+1) | - st.integers(min_value=order>>1, max_value=order)) + nonce = draw( + st.integers(min_value=1, max_value=order + 1) + | st.integers(min_value=order >> 1, max_value=order) + ) return generator, key, msg, nonce SIG_VER_SETTINGS = dict(HYP_SETTINGS) SIG_VER_SETTINGS["max_examples"] = 10 + + @settings(**SIG_VER_SETTINGS) @example((generator_224, 4, 1, 1)) @given(st_random_gen_key_msg_nonce()) diff --git a/src/ecdsa/test_ellipticcurve.py b/src/ecdsa/test_ellipticcurve.py index 924134ce..6b711fb3 100644 --- a/src/ecdsa/test_ellipticcurve.py +++ b/src/ecdsa/test_ellipticcurve.py @@ -1,24 +1,27 @@ import pytest from six import print_ + try: import unittest2 as unittest except ImportError: import unittest from hypothesis import given, settings import hypothesis.strategies as st + try: from hypothesis import HealthCheck - HC_PRESENT=True + + HC_PRESENT = True except ImportError: # pragma: no cover - HC_PRESENT=False + HC_PRESENT = False from .numbertheory import inverse_mod from .ellipticcurve import CurveFp, INFINITY, Point -HYP_SETTINGS={} +HYP_SETTINGS = {} if HC_PRESENT: # pragma: no branch - HYP_SETTINGS['suppress_health_check']=[HealthCheck.too_slow] - HYP_SETTINGS['deadline'] = 5000 + HYP_SETTINGS["suppress_health_check"] = [HealthCheck.too_slow] + HYP_SETTINGS["deadline"] = 5000 # NIST Curve P-192: @@ -26,9 +29,9 @@ r = 6277101735386680763835789423176059013767194773182842284081 # s = 0x3045ae6fc8422f64ed579528d38120eae12196d5 # c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 -b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 -Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 -Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 +b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1 +Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012 +Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811 c192 = CurveFp(p, -3, b) p192 = Point(c192, Gx, Gy, r) @@ -37,19 +40,19 @@ g_23 = Point(c_23, 13, 7, 7) -HYP_SLOW_SETTINGS=dict(HYP_SETTINGS) -HYP_SLOW_SETTINGS["max_examples"]=10 +HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) +HYP_SLOW_SETTINGS["max_examples"] = 10 @settings(**HYP_SLOW_SETTINGS) -@given(st.integers(min_value=1, max_value=r+1)) +@given(st.integers(min_value=1, max_value=r + 1)) def test_p192_mult_tests(multiple): inv_m = inverse_mod(multiple, r) p1 = p192 * multiple assert p1 * inv_m == p192 - - + + def add_n_times(point, n): ret = INFINITY i = 0 @@ -57,26 +60,26 @@ def add_n_times(point, n): yield ret ret = ret + point i += 1 - + # From X9.62 I.1 (p. 96): @pytest.mark.parametrize( "p, m, check", [(g_23, n, exp) for n, exp in enumerate(add_n_times(g_23, 8))], - ids=["g_23 test with mult {0}".format(i) for i in range(9)]) + ids=["g_23 test with mult {0}".format(i) for i in range(9)], +) def test_add_and_mult_equivalence(p, m, check): assert p * m == check - -class TestCurve(unittest.TestCase): +class TestCurve(unittest.TestCase): @classmethod def setUpClass(cls): cls.c_23 = CurveFp(23, 1, 1) - + def test_equality_curves(self): self.assertEqual(self.c_23, CurveFp(23, 1, 1)) - + def test_inequality_curves(self): c192 = CurveFp(p, -3, b) self.assertNotEqual(self.c_23, c192) @@ -97,92 +100,101 @@ def test_conflation_curves(self): class TestPoint(unittest.TestCase): - @classmethod def setUpClass(cls): cls.c_23 = CurveFp(23, 1, 1) cls.g_23 = Point(cls.c_23, 13, 7, 7) - + p = 6277101735386680763835789423207666416083908700390324961279 r = 6277101735386680763835789423176059013767194773182842284081 # s = 0x3045ae6fc8422f64ed579528d38120eae12196d5 # c = 0x3099d2bbbfcb2538542dcd5fb078b6ef5f3d6fe2c745de65 - b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 - Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 - Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 - + b = 0x64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1 + Gx = 0x188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012 + Gy = 0x07192B95FFC8DA78631011ED6B24CDD573F977A11E794811 + cls.c192 = CurveFp(p, -3, b) - cls.p192 = Point(cls.c192, Gx, Gy, r) - + cls.p192 = Point(cls.c192, Gx, Gy, r) + def test_p192(self): # Checking against some sample computations presented # in X9.62: d = 651056770906015076056810763456358567190100156695615665659 Q = d * self.p192 - self.assertEqual(Q.x(), 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5) + self.assertEqual( + Q.x(), 0x62B12D60690CDCF330BABAB6E69763B471F994DD702D16A5 + ) k = 6140507067065001063065065565667405560006161556565665656654 R = k * self.p192 - self.assertEqual(R.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD) - self.assertEqual(R.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835) + self.assertEqual( + R.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD + ) + self.assertEqual( + R.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 + ) u1 = 2563697409189434185194736134579731015366492496392189760599 u2 = 6266643813348617967186477710235785849136406323338782220568 temp = u1 * self.p192 + u2 * Q - self.assertEqual(temp.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD) - self.assertEqual(temp.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835) - + self.assertEqual( + temp.x(), 0x885052380FF147B734C330C43D39B2C4A89F29B0F749FEAD + ) + self.assertEqual( + temp.y(), 0x9CF9FA1CBEFEFB917747A3BB29C072B9289C2547884FD835 + ) + def test_double_infinity(self): p1 = INFINITY p3 = p1.double() self.assertEqual(p1, p3) self.assertEqual(p3.x(), p1.x()) - self.assertEqual(p3.y(), p3.y()) - + self.assertEqual(p3.y(), p3.y()) + def test_double(self): x1, y1, x3, y3 = (3, 10, 7, 12) - + p1 = Point(self.c_23, x1, y1) p3 = p1.double() self.assertEqual(p3.x(), x3) self.assertEqual(p3.y(), y3) - + def test_multiply(self): x1, y1, m, x3, y3 = (3, 10, 2, 7, 12) p1 = Point(self.c_23, x1, y1) p3 = p1 * m self.assertEqual(p3.x(), x3) self.assertEqual(p3.y(), y3) - + # Trivial tests from X9.62 B.3: def test_add(self): """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" - + x1, y1, x2, y2, x3, y3 = (3, 10, 9, 7, 17, 20) p1 = Point(self.c_23, x1, y1) p2 = Point(self.c_23, x2, y2) p3 = p1 + p2 self.assertEqual(p3.x(), x3) self.assertEqual(p3.y(), y3) - + def test_add_as_double(self): """We expect that on curve c, (x1,y1) + (x2, y2 ) = (x3, y3).""" - - x1, y1, x2, y2, x3, y3 = (3, 10, 3, 10, 7, 12) + + x1, y1, x2, y2, x3, y3 = (3, 10, 3, 10, 7, 12) p1 = Point(self.c_23, x1, y1) p2 = Point(self.c_23, x2, y2) p3 = p1 + p2 self.assertEqual(p3.x(), x3) self.assertEqual(p3.y(), y3) - + def test_equality_points(self): self.assertEqual(self.g_23, Point(self.c_23, 13, 7, 7)) - + def test_inequality_points(self): c = CurveFp(100, -3, 100) p = Point(c, 100, 100, 100) self.assertNotEqual(self.g_23, p) - + def test_inaquality_points_diff_types(self): - c = CurveFp(100, -3, 100) + c = CurveFp(100, -3, 100) self.assertNotEqual(self.g_23, c) diff --git a/src/ecdsa/test_jacobi.py b/src/ecdsa/test_jacobi.py index 35e52421..7c806f6f 100644 --- a/src/ecdsa/test_jacobi.py +++ b/src/ecdsa/test_jacobi.py @@ -1,4 +1,3 @@ - try: import unittest2 as unittest except ImportError: @@ -11,6 +10,7 @@ from .ecdsa import generator_256, curve_256, generator_224 from .numbertheory import inverse_mod + class TestJacobi(unittest.TestCase): def test___init__(self): curve = object() @@ -203,8 +203,10 @@ def test_precompute(self, mul): self.assertEqual(a, b) @settings(max_examples=10) - @given(st.integers(min_value=1, max_value=int(generator_256.order())), - st.integers(min_value=1, max_value=int(generator_256.order()))) + @given( + st.integers(min_value=1, max_value=int(generator_256.order())), + st.integers(min_value=1, max_value=int(generator_256.order())), + ) @example(3, 3) def test_add_scaled_points(self, a_mul, b_mul): j_g = PointJacobi.from_affine(generator_256) @@ -216,9 +218,11 @@ def test_add_scaled_points(self, a_mul, b_mul): self.assertEqual(c, j_g * (a_mul + b_mul)) @settings(max_examples=10) - @given(st.integers(min_value=1, max_value=int(generator_256.order())), - st.integers(min_value=1, max_value=int(generator_256.order())), - st.integers(min_value=1, max_value=int(curve_256.p()-1))) + @given( + st.integers(min_value=1, max_value=int(generator_256.order())), + st.integers(min_value=1, max_value=int(generator_256.order())), + st.integers(min_value=1, max_value=int(curve_256.p() - 1)), + ) def test_add_one_scaled_point(self, a_mul, b_mul, new_z): j_g = PointJacobi.from_affine(generator_256) a = PointJacobi.from_affine(j_g * a_mul) @@ -231,20 +235,23 @@ def test_add_one_scaled_point(self, a_mul, b_mul, new_z): new_zz = new_z * new_z % p b = PointJacobi( - curve_256, b.x() * new_zz % p, b.y() * new_zz * new_z % p, new_z) + curve_256, b.x() * new_zz % p, b.y() * new_zz * new_z % p, new_z + ) c = a + b self.assertEqual(c, j_g * (a_mul + b_mul)) @settings(max_examples=10) - @given(st.integers(min_value=1, max_value=int(generator_256.order())), - st.integers(min_value=1, max_value=int(generator_256.order())), - st.integers(min_value=1, max_value=int(curve_256.p()-1))) + @given( + st.integers(min_value=1, max_value=int(generator_256.order())), + st.integers(min_value=1, max_value=int(generator_256.order())), + st.integers(min_value=1, max_value=int(curve_256.p() - 1)), + ) @example(1, 1, 1) @example(3, 3, 3) - @example(2, int(generator_256.order()-2), 1) - @example(2, int(generator_256.order()-2), 3) + @example(2, int(generator_256.order() - 2), 1) + @example(2, int(generator_256.order() - 2), 3) def test_add_same_scale_points(self, a_mul, b_mul, new_z): j_g = PointJacobi.from_affine(generator_256) a = PointJacobi.from_affine(j_g * a_mul) @@ -257,23 +264,31 @@ def test_add_same_scale_points(self, a_mul, b_mul, new_z): new_zz = new_z * new_z % p a = PointJacobi( - curve_256, a.x() * new_zz % p, a.y() * new_zz * new_z % p, new_z) + curve_256, a.x() * new_zz % p, a.y() * new_zz * new_z % p, new_z + ) b = PointJacobi( - curve_256, b.x() * new_zz % p, b.y() * new_zz * new_z % p, new_z) + curve_256, b.x() * new_zz % p, b.y() * new_zz * new_z % p, new_z + ) c = a + b self.assertEqual(c, j_g * (a_mul + b_mul)) @settings(max_examples=14) - @given(st.integers(min_value=1, max_value=int(generator_256.order())), - st.integers(min_value=1, max_value=int(generator_256.order())), - st.lists(st.integers(min_value=1, max_value=int(curve_256.p()-1)), - min_size=2, max_size=2, unique=True)) + @given( + st.integers(min_value=1, max_value=int(generator_256.order())), + st.integers(min_value=1, max_value=int(generator_256.order())), + st.lists( + st.integers(min_value=1, max_value=int(curve_256.p() - 1)), + min_size=2, + max_size=2, + unique=True, + ), + ) @example(2, 2, [2, 1]) @example(2, 2, [2, 3]) - @example(2, int(generator_256.order()-2), [2, 3]) - @example(2, int(generator_256.order()-2), [2, 1]) + @example(2, int(generator_256.order() - 2), [2, 3]) + @example(2, int(generator_256.order() - 2), [2, 1]) def test_add_different_scale_points(self, a_mul, b_mul, new_z): j_g = PointJacobi.from_affine(generator_256) a = PointJacobi.from_affine(j_g * a_mul) @@ -291,12 +306,14 @@ def test_add_different_scale_points(self, a_mul, b_mul, new_z): curve_256, a.x() * new_zz0 % p, a.y() * new_zz0 * new_z[0] % p, - new_z[0]) + new_z[0], + ) b = PointJacobi( curve_256, b.x() * new_zz1 % p, b.y() * new_zz1 * new_z[1] % p, - new_z[1]) + new_z[1], + ) c = a + b @@ -330,10 +347,12 @@ def test_mul_add_precompute_large(self): b = PointJacobi.from_affine(j_g * 255, True) self.assertEqual(j_g * 256, j_g + b) - self.assertEqual(j_g * (0xff00 + 255 * 0xf0f0), - j_g * 0xff00 + b * 0xf0f0) - self.assertEqual(j_g * (0xff00 + 255 * 0xf0f0), - j_g.mul_add(0xff00, b, 0xf0f0)) + self.assertEqual( + j_g * (0xFF00 + 255 * 0xF0F0), j_g * 0xFF00 + b * 0xF0F0 + ) + self.assertEqual( + j_g * (0xFF00 + 255 * 0xF0F0), j_g.mul_add(0xFF00, b, 0xF0F0) + ) def test_mul_add_to_mul(self): j_g = PointJacobi.from_affine(generator_256) @@ -347,10 +366,10 @@ def test_mul_add(self): j_g = PointJacobi.from_affine(generator_256) w_a = generator_256 * 255 - w_b = generator_256 * (0xa8*0xf0) - j_b = j_g * 0xa8 + w_b = generator_256 * (0xA8 * 0xF0) + j_b = j_g * 0xA8 - ret = j_g.mul_add(255, j_b, 0xf0) + ret = j_g.mul_add(255, j_b, 0xF0) self.assertEqual(ret.to_affine(), w_a + w_b) @@ -359,7 +378,9 @@ def test_mul_add_large(self): b = PointJacobi.from_affine(j_g * 255) self.assertEqual(j_g * 256, j_g + b) - self.assertEqual(j_g * (0xff00 + 255 * 0xf0f0), - j_g * 0xff00 + b * 0xf0f0) - self.assertEqual(j_g * (0xff00 + 255 * 0xf0f0), - j_g.mul_add(0xff00, b, 0xf0f0)) + self.assertEqual( + j_g * (0xFF00 + 255 * 0xF0F0), j_g * 0xFF00 + b * 0xF0F0 + ) + self.assertEqual( + j_g * (0xFF00 + 255 * 0xF0F0), j_g.mul_add(0xFF00, b, 0xF0F0) + ) diff --git a/src/ecdsa/test_keys.py b/src/ecdsa/test_keys.py index 56e12842..4c8292e4 100644 --- a/src/ecdsa/test_keys.py +++ b/src/ecdsa/test_keys.py @@ -16,8 +16,14 @@ from .keys import VerifyingKey, SigningKey from .der import unpem -from .util import sigencode_string, sigencode_der, sigencode_strings, \ - sigdecode_string, sigdecode_der, sigdecode_strings +from .util import ( + sigencode_string, + sigencode_der, + sigencode_strings, + sigdecode_string, + sigdecode_der, + sigdecode_strings, +) class TestVerifyingKeyFromString(unittest.TestCase): @@ -28,9 +34,11 @@ class TestVerifyingKeyFromString(unittest.TestCase): @classmethod def setUpClass(cls): - cls.key_bytes = (b'\x04L\xa2\x95\xdb\xc7Z\xd7\x1f\x93\nz\xcf\x97\xcf' - b'\xd7\xc2\xd9o\xfe8}X!\xae\xd4\xfah\xfa^\rpI\xba\xd1' - b'Y\xfb\x92xa\xebo+\x9cG\xfav\xca') + cls.key_bytes = ( + b"\x04L\xa2\x95\xdb\xc7Z\xd7\x1f\x93\nz\xcf\x97\xcf" + b"\xd7\xc2\xd9o\xfe8}X!\xae\xd4\xfah\xfa^\rpI\xba\xd1" + b"Y\xfb\x92xa\xebo+\x9cG\xfav\xca" + ) cls.vk = VerifyingKey.from_string(cls.key_bytes) def test_bytes(self): @@ -38,10 +46,12 @@ def test_bytes(self): self.assertIsInstance(self.vk, VerifyingKey) self.assertEqual( self.vk.pubkey.point.x(), - 105419898848891948935835657980914000059957975659675736097) + 105419898848891948935835657980914000059957975659675736097, + ) self.assertEqual( self.vk.pubkey.point.y(), - 4286866841217412202667522375431381222214611213481632495306) + 4286866841217412202667522375431381222214611213481632495306, + ) def test_bytes_memoryview(self): vk = VerifyingKey.from_string(buffer(self.key_bytes)) @@ -59,46 +69,46 @@ def test_bytesarray_memoryview(self): self.assertEqual(self.vk.to_string(), vk.to_string()) def test_array_array_of_bytes(self): - arr = array.array('B', self.key_bytes) + arr = array.array("B", self.key_bytes) vk = VerifyingKey.from_string(arr) self.assertEqual(self.vk.to_string(), vk.to_string()) def test_array_array_of_bytes_memoryview(self): - arr = array.array('B', self.key_bytes) + arr = array.array("B", self.key_bytes) vk = VerifyingKey.from_string(buffer(arr)) self.assertEqual(self.vk.to_string(), vk.to_string()) def test_array_array_of_ints(self): - arr = array.array('I', self.key_bytes) + arr = array.array("I", self.key_bytes) vk = VerifyingKey.from_string(arr) self.assertEqual(self.vk.to_string(), vk.to_string()) def test_array_array_of_ints_memoryview(self): - arr = array.array('I', self.key_bytes) + arr = array.array("I", self.key_bytes) vk = VerifyingKey.from_string(buffer(arr)) self.assertEqual(self.vk.to_string(), vk.to_string()) def test_bytes_uncompressed(self): - vk = VerifyingKey.from_string(b'\x04' + self.key_bytes) + vk = VerifyingKey.from_string(b"\x04" + self.key_bytes) self.assertEqual(self.vk.to_string(), vk.to_string()) def test_bytearray_uncompressed(self): - vk = VerifyingKey.from_string(bytearray(b'\x04' + self.key_bytes)) + vk = VerifyingKey.from_string(bytearray(b"\x04" + self.key_bytes)) self.assertEqual(self.vk.to_string(), vk.to_string()) def test_bytes_compressed(self): - vk = VerifyingKey.from_string(b'\x02' + self.key_bytes[:24]) + vk = VerifyingKey.from_string(b"\x02" + self.key_bytes[:24]) self.assertEqual(self.vk.to_string(), vk.to_string()) def test_bytearray_compressed(self): - vk = VerifyingKey.from_string(bytearray(b'\x02' + self.key_bytes[:24])) + vk = VerifyingKey.from_string(bytearray(b"\x02" + self.key_bytes[:24])) self.assertEqual(self.vk.to_string(), vk.to_string()) @@ -108,6 +118,7 @@ class TestVerifyingKeyFromDer(unittest.TestCase): Verify that ecdsa.keys.VerifyingKey.from_der() can be used with bytes-like objects. """ + @classmethod def setUpClass(cls): prv_key_str = ( @@ -115,12 +126,14 @@ def setUpClass(cls): "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" "bA==\n" - "-----END EC PRIVATE KEY-----\n") + "-----END EC PRIVATE KEY-----\n" + ) key_str = ( "-----BEGIN PUBLIC KEY-----\n" "MEkwEwYHKoZIzj0CAQYIKoZIzj0DAQEDMgAEuIF30ITvF/XkVjlAgCg2D59ZtKTX\n" "Jk5i2gZR3OR6NaTFtFz1FZNCOotVe5wgmfNs\n" - "-----END PUBLIC KEY-----\n") + "-----END PUBLIC KEY-----\n" + ) cls.key_pem = key_str cls.key_bytes = unpem(key_str) @@ -167,13 +180,13 @@ def test_bytesarray_memoryview(self): self.assertEqual(self.vk.to_string(), vk.to_string()) def test_array_array_of_bytes(self): - arr = array.array('B', self.key_bytes) + arr = array.array("B", self.key_bytes) vk = VerifyingKey.from_der(arr) self.assertEqual(self.vk.to_string(), vk.to_string()) def test_array_array_of_bytes_memoryview(self): - arr = array.array('B', self.key_bytes) + arr = array.array("B", self.key_bytes) vk = VerifyingKey.from_der(buffer(arr)) self.assertEqual(self.vk.to_string(), vk.to_string()) @@ -193,6 +206,7 @@ class TestSigningKey(unittest.TestCase): Verify that ecdsa.keys.SigningKey.from_der() can be used with bytes-like objects. """ + @classmethod def setUpClass(cls): prv_key_str = ( @@ -200,43 +214,51 @@ def setUpClass(cls): "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" "bA==\n" - "-----END EC PRIVATE KEY-----\n") - cls.sk1 = SigningKey.from_pem(prv_key_str) - + "-----END EC PRIVATE KEY-----\n" + ) + cls.sk1 = SigningKey.from_pem(prv_key_str) + prv_key_str = ( "-----BEGIN EC PRIVATE KEY-----\n" "MHcCAQEEIKlL2EAm5NPPZuXwxRf4nXMk0A80y6UUbiQ17be/qFhRoAoGCCqGSM49\n" "AwEHoUQDQgAE4H3iRbG4TSrsSRb/gusPQB/4YcN8Poqzgjau4kfxBPyZimeRfuY/\n" "9g/wMmPuhGl4BUve51DsnKJFRr8psk0ieA==\n" - "-----END EC PRIVATE KEY-----\n") - cls.sk2 = SigningKey.from_pem(prv_key_str) - - def test_equality_on_signing_keys(self): - sk = SigningKey.from_secret_exponent(self.sk1.privkey.secret_multiplier, self.sk1.curve) + "-----END EC PRIVATE KEY-----\n" + ) + cls.sk2 = SigningKey.from_pem(prv_key_str) + + def test_equality_on_signing_keys(self): + sk = SigningKey.from_secret_exponent( + self.sk1.privkey.secret_multiplier, self.sk1.curve + ) self.assertEqual(self.sk1, sk) - + def test_inequality_on_signing_keys(self): self.assertNotEqual(self.sk1, self.sk2) - + def test_inequality_on_signing_keys_not_implemented(self): self.assertNotEqual(self.sk1, None) + # test VerifyingKey.verify() prv_key_str = ( "-----BEGIN EC PRIVATE KEY-----\n" "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" "bA==\n" - "-----END EC PRIVATE KEY-----\n") + "-----END EC PRIVATE KEY-----\n" +) key_bytes = unpem(prv_key_str) assert isinstance(key_bytes, bytes) sk = SigningKey.from_der(key_bytes) vk = sk.verifying_key -data = (b"some string for signing" - b"contents don't really matter" - b"but do include also some crazy values: " - b"\x00\x01\t\r\n\x00\x00\x00\xff\xf0") +data = ( + b"some string for signing" + b"contents don't really matter" + b"but do include also some crazy values: " + b"\x00\x01\t\r\n\x00\x00\x00\xff\xf0" +) assert len(data) % 4 == 0 sha1 = hashlib.sha1() sha1.update(data) @@ -255,11 +277,11 @@ def test_inequality_on_signing_keys_not_implemented(self): ("bytes memoryview", lambda x: buffer(x)), ("bytearray", lambda x: bytearray(x)), ("bytearray memoryview", lambda x: buffer(bytearray(x))), - ("array.array of bytes", lambda x: array.array('B', x)), - ("array.array of bytes memoryview", lambda x: buffer(array.array('B', x))), - ("array.array of ints", lambda x: array.array('I', x)), - ("array.array of ints memoryview", lambda x: buffer(array.array('I', x))) - ]: + ("array.array of bytes", lambda x: array.array("B", x)), + ("array.array of bytes memoryview", lambda x: buffer(array.array("B", x))), + ("array.array of ints", lambda x: array.array("I", x)), + ("array.array of ints memoryview", lambda x: buffer(array.array("I", x))), +]: if "ints" in modifier: conv = lambda x: x else: @@ -267,47 +289,60 @@ def test_inequality_on_signing_keys_not_implemented(self): for sig_format, signature, decoder, mod_apply in [ ("raw", sig_raw, sigdecode_string, lambda x: conv(x)), ("der", sig_der, sigdecode_der, lambda x: conv(x)), - ("strings", sig_strings, sigdecode_strings, lambda x: - tuple(conv(i) for i in x)) - ]: + ( + "strings", + sig_strings, + sigdecode_strings, + lambda x: tuple(conv(i) for i in x), + ), + ]: for method_name, vrf_mthd, vrf_data in [ ("verify", vk.verify, data), - ("verify_digest", vk.verify_digest, data_hash) - ]: - verifiers.append(pytest.param( - signature, decoder, mod_apply, fun, vrf_mthd, vrf_data, - id="{2}-{0}-{1}".format(modifier, sig_format, method_name))) + ("verify_digest", vk.verify_digest, data_hash), + ]: + verifiers.append( + pytest.param( + signature, + decoder, + mod_apply, + fun, + vrf_mthd, + vrf_data, + id="{2}-{0}-{1}".format(modifier, sig_format, method_name), + ) + ) + @pytest.mark.parametrize( - "signature,decoder,mod_apply,fun,vrf_mthd,vrf_data", - verifiers) + "signature,decoder,mod_apply,fun,vrf_mthd,vrf_data", verifiers +) def test_VerifyingKey_verify( - signature, decoder, mod_apply, fun, vrf_mthd, vrf_data): + signature, decoder, mod_apply, fun, vrf_mthd, vrf_data +): sig = mod_apply(signature) assert vrf_mthd(sig, fun(vrf_data), sigdecode=decoder) # test SigningKey.from_string() -prv_key_bytes = (b'^\xc8B\x0b\xd6\xef\x92R\xa9B\xe9\x89\x04<\xa2' - b'\x9fV\x1f\xa5%w\x0e\xb1\xc5') +prv_key_bytes = ( + b"^\xc8B\x0b\xd6\xef\x92R\xa9B\xe9\x89\x04<\xa2" + b"\x9fV\x1f\xa5%w\x0e\xb1\xc5" +) assert len(prv_key_bytes) == 24 converters = [] for modifier, convert in [ - ("bytes", lambda x: x), - ("bytes memoryview", buffer), - ("bytearray", bytearray), - ("bytearray memoryview", lambda x: buffer(bytearray(x))), - ("array.array of bytes", lambda x: array.array('B', x)), - ("array.array of bytes memoryview", - lambda x: buffer(array.array('B', x))), - ("array.array of ints", lambda x: array.array('I', x)), - ("array.array of ints memoryview", - lambda x: buffer(array.array('I', x))) - ]: - converters.append(pytest.param( - convert, - id=modifier)) + ("bytes", lambda x: x), + ("bytes memoryview", buffer), + ("bytearray", bytearray), + ("bytearray memoryview", lambda x: buffer(bytearray(x))), + ("array.array of bytes", lambda x: array.array("B", x)), + ("array.array of bytes memoryview", lambda x: buffer(array.array("B", x))), + ("array.array of ints", lambda x: array.array("I", x)), + ("array.array of ints memoryview", lambda x: buffer(array.array("I", x))), +]: + converters.append(pytest.param(convert, id=modifier)) + @pytest.mark.parametrize("convert", converters) def test_SigningKey_from_string(convert): @@ -323,7 +358,8 @@ def test_SigningKey_from_string(convert): "MF8CAQEEGF7IQgvW75JSqULpiQQ8op9WH6Uldw6xxaAKBggqhkjOPQMBAaE0AzIA\n" "BLiBd9CE7xf15FY5QIAoNg+fWbSk1yZOYtoGUdzkejWkxbRc9RWTQjqLVXucIJnz\n" "bA==\n" - "-----END EC PRIVATE KEY-----\n") + "-----END EC PRIVATE KEY-----\n" +) key_bytes = unpem(prv_key_str) assert isinstance(key_bytes, bytes) @@ -338,13 +374,14 @@ def test_SigningKey_from_der(convert): # test SigningKey.sign_deterministic() -extra_entropy=b'\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11' +extra_entropy = b"\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11" + @pytest.mark.parametrize("convert", converters) def test_SigningKey_sign_deterministic(convert): sig = sk.sign_deterministic( - convert(data), - extra_entropy=convert(extra_entropy)) + convert(data), extra_entropy=convert(extra_entropy) + ) vk.verify(sig, data) @@ -353,8 +390,8 @@ def test_SigningKey_sign_deterministic(convert): @pytest.mark.parametrize("convert", converters) def test_SigningKey_sign_digest_deterministic(convert): sig = sk.sign_digest_deterministic( - convert(data_hash), - extra_entropy=convert(extra_entropy)) + convert(data_hash), extra_entropy=convert(extra_entropy) + ) vk.verify(sig, data) diff --git a/src/ecdsa/test_malformed_sigs.py b/src/ecdsa/test_malformed_sigs.py index c1dca44a..4895ceab 100644 --- a/src/ecdsa/test_malformed_sigs.py +++ b/src/ecdsa/test_malformed_sigs.py @@ -1,11 +1,18 @@ from __future__ import with_statement, division import hashlib + try: from hashlib import algorithms_available except ImportError: # pragma: no cover algorithms_available = [ - "md5", "sha1", "sha224", "sha256", "sha384", "sha512"] + "md5", + "sha1", + "sha224", + "sha256", + "sha384", + "sha512", + ] from functools import partial import pytest import sys @@ -18,16 +25,23 @@ from .util import sigencode_der, sigencode_string from .util import sigdecode_der, sigdecode_string from .curves import curves, NIST256p -from .der import encode_integer, encode_bitstring, encode_octet_string, \ - encode_oid, encode_sequence, encode_constructed +from .der import ( + encode_integer, + encode_bitstring, + encode_octet_string, + encode_oid, + encode_sequence, + encode_constructed, +) example_data = b"some data to sign" """Since the data is hashed for processing, really any string will do.""" -hash_and_size = [(name, hashlib.new(name).digest_size) - for name in algorithms_available] +hash_and_size = [ + (name, hashlib.new(name).digest_size) for name in algorithms_available +] """Pairs of hash names and their output sizes. Needed for pairing with curves as we don't support hashes bigger than order sizes of curves.""" @@ -39,26 +53,33 @@ # for hypothesis strategy shrinking we want smallest curves and hashes first for curve in sorted(curves, key=lambda x: x.baselen): - for hash_alg in [name for name, size in - sorted(hash_and_size, key=lambda x: x[1]) - if 0 < size <= curve.baselen]: + for hash_alg in [ + name + for name, size in sorted(hash_and_size, key=lambda x: x[1]) + if 0 < size <= curve.baselen + ]: sk = SigningKey.generate( - curve, - hashfunc=partial(hashlib.new, hash_alg)) + curve, hashfunc=partial(hashlib.new, hash_alg) + ) keys_and_sigs.append( - ("{0} {1}".format(curve, hash_alg), - sk.verifying_key, - sk.sign(example_data, sigencode=sigencode_der))) + ( + "{0} {1}".format(curve, hash_alg), + sk.verifying_key, + sk.sign(example_data, sigencode=sigencode_der), + ) + ) # first make sure that the signatures can be verified @pytest.mark.parametrize( "verifying_key,signature", - [pytest.param(vk, sig, id=name) for name, vk, sig in keys_and_sigs]) + [pytest.param(vk, sig, id=name) for name, vk, sig in keys_and_sigs], +) def test_signatures(verifying_key, signature): - assert verifying_key.verify(signature, example_data, - sigdecode=sigdecode_der) + assert verifying_key.verify( + signature, example_data, sigdecode=sigdecode_der + ) @st.composite @@ -73,9 +94,9 @@ def st_fuzzed_sig(draw, keys_and_sigs): sig = bytearray(old_sig) # decide which bytes should be removed - to_remove = draw(st.lists( - st.integers(min_value=0, max_value=len(sig)-1), - unique=True)) + to_remove = draw( + st.lists(st.integers(min_value=0, max_value=len(sig) - 1), unique=True) + ) to_remove.sort() for i in reversed(to_remove): del sig[i] @@ -83,9 +104,12 @@ def st_fuzzed_sig(draw, keys_and_sigs): # decide which bytes of the original signature should be changed if sig: # pragma: no branch - xors = draw(st.dictionaries( - st.integers(min_value=0, max_value=len(sig)-1), - st.integers(min_value=1, max_value=255))) + xors = draw( + st.dictionaries( + st.integers(min_value=0, max_value=len(sig) - 1), + st.integers(min_value=1, max_value=255), + ) + ) for i, val in xors.items(): sig[i] ^= val note("xors: {0}".format(xors)) @@ -96,8 +120,9 @@ def st_fuzzed_sig(draw, keys_and_sigs): insert_data = draw(st.binary(max_size=256)) sig = sig[:insert_pos] + insert_data + sig[insert_pos:] - note("Inserted at position {0} bytes: {1!r}" - .format(insert_pos, insert_data)) + note( + "Inserted at position {0} bytes: {1!r}".format(insert_pos, insert_data) + ) sig = bytes(sig) # make sure that there was performed at least one mutation on the data @@ -112,11 +137,14 @@ def st_fuzzed_sig(draw, keys_and_sigs): # not supported in hypothesis 2.0.0 if sys.version_info >= (2, 7): # pragma: no branch from hypothesis import HealthCheck + # deadline=5s because NIST521p are slow to verify params["deadline"] = 5000 - params["suppress_health_check"] = [HealthCheck.data_too_large, - HealthCheck.filter_too_much, - HealthCheck.too_slow] + params["suppress_health_check"] = [ + HealthCheck.data_too_large, + HealthCheck.filter_too_much, + HealthCheck.too_slow, + ] slow_params = dict(params) slow_params["max_examples"] = 10 @@ -151,10 +179,14 @@ def st_random_der_ecdsa_sig_value(draw): # verifying that it doesn't accept them, so meh. # Test all numbers around the ones that can show up (around order) # way smaller and slightly bigger - r = draw(st.integers(min_value=0, max_value=order << 4) | - st.integers(min_value=order >> 2, max_value=order+1)) - s = draw(st.integers(min_value=0, max_value=order << 4) | - st.integers(min_value=order >> 2, max_value=order+1)) + r = draw( + st.integers(min_value=0, max_value=order << 4) + | st.integers(min_value=order >> 2, max_value=order + 1) + ) + s = draw( + st.integers(min_value=0, max_value=order << 4) + | st.integers(min_value=order >> 2, max_value=order + 1) + ) sig = encode_sequence(encode_integer(r), encode_integer(s)) @@ -195,7 +227,7 @@ def st_der_bit_string(draw, *args, **kwargs): if data: unused = draw(st.integers(min_value=0, max_value=7)) data = bytearray(data) - data[-1] &= - (2**unused) + data[-1] &= -(2 ** unused) data = bytes(data) else: unused = 0 @@ -214,7 +246,7 @@ def st_der_null(): """ Hypothesis strategy that returns DER NULL object. """ - return st.just(b'\x05\x00') + return st.just(b"\x05\x00") @st.composite @@ -226,9 +258,10 @@ def st_der_oid(draw): if first < 2: second = draw(st.integers(min_value=0, max_value=39)) else: - second = draw(st.integers(min_value=0, max_value=2**512)) - rest = draw(st.lists(st.integers(min_value=0, max_value=2**512), - max_size=50)) + second = draw(st.integers(min_value=0, max_value=2 ** 512)) + rest = draw( + st.lists(st.integers(min_value=0, max_value=2 ** 512), max_size=50) + ) return encode_oid(first, second, *rest) @@ -241,20 +274,26 @@ def st_der(): encoding of any of the above. """ return st.recursive( - st.just(b'') | st_der_integer(max_value=2**4096) | - st_der_bit_string(max_size=1024**2) | - st_der_octet_string(max_size=1024**2) | st_der_null() | st_der_oid(), - lambda children: - st.builds(lambda x: encode_octet_string(x), st.one_of(children)) | - st.builds(lambda x: encode_bitstring(x, 0), st.one_of(children)) | - st.builds(lambda x: encode_sequence(*x), - st.lists(children, max_size=200)) | - st.builds(lambda tag, x: - encode_constructed(tag, x), - st.integers(min_value=0, max_value=0x3f), - st.one_of(children)), - max_leaves=40 + st.just(b"") + | st_der_integer(max_value=2 ** 4096) + | st_der_bit_string(max_size=1024 ** 2) + | st_der_octet_string(max_size=1024 ** 2) + | st_der_null() + | st_der_oid(), + lambda children: st.builds( + lambda x: encode_octet_string(x), st.one_of(children) ) + | st.builds(lambda x: encode_bitstring(x, 0), st.one_of(children)) + | st.builds( + lambda x: encode_sequence(*x), st.lists(children, max_size=200) + ) + | st.builds( + lambda tag, x: encode_constructed(tag, x), + st.integers(min_value=0, max_value=0x3F), + st.one_of(children), + ), + max_leaves=40, + ) @settings(**params) @@ -268,16 +307,15 @@ def test_random_der_as_signature(params, der): @settings(**params) -@given(st.sampled_from(keys_and_sigs), st.binary(max_size=1024**2)) -@example( - keys_and_sigs[0], - encode_sequence(encode_integer(0), encode_integer(0))) +@given(st.sampled_from(keys_and_sigs), st.binary(max_size=1024 ** 2)) @example( - keys_and_sigs[0], - encode_sequence(encode_integer(1), encode_integer(1)) + b'\x00') + keys_and_sigs[0], encode_sequence(encode_integer(0), encode_integer(0)) +) @example( keys_and_sigs[0], - encode_sequence(*[encode_integer(1)] * 3)) + encode_sequence(encode_integer(1), encode_integer(1)) + b"\x00", +) +@example(keys_and_sigs[0], encode_sequence(*[encode_integer(1)] * 3)) def test_random_bytes_as_signature(params, der): """Check if random bytes are rejected as signature""" name, verifying_key, _ = params @@ -287,10 +325,16 @@ def test_random_bytes_as_signature(params, der): keys_and_string_sigs = [ - (name, verifying_key, - sigencode_string(*sigdecode_der(sig, verifying_key.curve.order), - order=verifying_key.curve.order)) - for name, verifying_key, sig in keys_and_sigs] + ( + name, + verifying_key, + sigencode_string( + *sigdecode_der(sig, verifying_key.curve.order), + order=verifying_key.curve.order + ), + ) + for name, verifying_key, sig in keys_and_sigs +] """ Name of the curve+hash combination, VerifyingKey and signature as a byte string. diff --git a/src/ecdsa/test_numbertheory.py b/src/ecdsa/test_numbertheory.py index 4cec4fd6..4912c578 100644 --- a/src/ecdsa/test_numbertheory.py +++ b/src/ecdsa/test_numbertheory.py @@ -2,6 +2,7 @@ from six import print_ from functools import reduce import operator + try: import unittest2 as unittest except ImportError: @@ -9,49 +10,59 @@ import hypothesis.strategies as st import pytest from hypothesis import given, settings, example + try: from hypothesis import HealthCheck - HC_PRESENT=True + + HC_PRESENT = True except ImportError: # pragma: no cover - HC_PRESENT=False -from .numbertheory import (SquareRootError, factorization, gcd, lcm, - jacobi, inverse_mod, - is_prime, next_prime, smallprimes, - square_root_mod_prime) - - -BIGPRIMES = (999671, - 999683, - 999721, - 999727, - 999749, - 999763, - 999769, - 999773, - 999809, - 999853, - 999863, - 999883, - 999907, - 999917, - 999931, - 999953, - 999959, - 999961, - 999979, - 999983) + HC_PRESENT = False +from .numbertheory import ( + SquareRootError, + factorization, + gcd, + lcm, + jacobi, + inverse_mod, + is_prime, + next_prime, + smallprimes, + square_root_mod_prime, +) + + +BIGPRIMES = ( + 999671, + 999683, + 999721, + 999727, + 999749, + 999763, + 999769, + 999773, + 999809, + 999853, + 999863, + 999883, + 999907, + 999917, + 999931, + 999953, + 999959, + 999961, + 999979, + 999983, +) @pytest.mark.parametrize( - "prime, next_p", - [(p, q) for p, q in zip(BIGPRIMES[:-1], BIGPRIMES[1:])]) + "prime, next_p", [(p, q) for p, q in zip(BIGPRIMES[:-1], BIGPRIMES[1:])] +) def test_next_prime(prime, next_p): assert next_prime(prime) == next_p -@pytest.mark.parametrize( - "val", - [-1, 0, 1]) +@pytest.mark.parametrize("val", [-1, 0, 1]) def test_next_prime_with_nums_less_2(val): assert next_prime(val) == 2 @@ -77,9 +88,12 @@ def test_square_root_mod_prime_for_small_primes(prime): def st_two_nums_rel_prime(draw): # 521-bit is the biggest curve we operate on, use 1024 for a bit # of breathing space - mod = draw(st.integers(min_value=2, max_value=2**1024)) - num = draw(st.integers(min_value=1, max_value=mod-1) - .filter(lambda x: gcd(x, mod) == 1)) + mod = draw(st.integers(min_value=2, max_value=2 ** 1024)) + num = draw( + st.integers(min_value=1, max_value=mod - 1).filter( + lambda x: gcd(x, mod) == 1 + ) + ) return num, mod @@ -87,15 +101,16 @@ def st_two_nums_rel_prime(draw): def st_primes(draw, *args, **kwargs): if "min_value" not in kwargs: # pragma: no branch kwargs["min_value"] = 1 - prime = draw(st.sampled_from(smallprimes) | - st.integers(*args, **kwargs) - .filter(is_prime)) + prime = draw( + st.sampled_from(smallprimes) + | st.integers(*args, **kwargs).filter(is_prime) + ) return prime @st.composite def st_num_square_prime(draw): - prime = draw(st_primes(max_value=2**1024)) + prime = draw(st_primes(max_value=2 ** 1024)) num = draw(st.integers(min_value=0, max_value=1 + prime // 2)) sq = num * num % prime return sq, prime @@ -106,21 +121,27 @@ def st_comp_with_com_fac(draw): """ Strategy that returns lists of numbers, all having a common factor. """ - primes = draw(st.lists(st_primes(max_value=2**512), min_size=1, - max_size=10)) + primes = draw( + st.lists(st_primes(max_value=2 ** 512), min_size=1, max_size=10) + ) # select random prime(s) that will make the common factor of composites - com_fac_primes = draw(st.lists(st.sampled_from(primes), - min_size=1, max_size=20)) + com_fac_primes = draw( + st.lists(st.sampled_from(primes), min_size=1, max_size=20) + ) com_fac = reduce(operator.mul, com_fac_primes, 1) # select at most 20 lists (returned numbers), # each having at most 30 primes (factors) including none (then the number # will be 1) comp_primes = draw( - st.integers(min_value=1, max_value=20). - flatmap(lambda n: st.lists(st.lists(st.sampled_from(primes), - max_size=30), - min_size=1, max_size=n))) + st.integers(min_value=1, max_value=20).flatmap( + lambda n: st.lists( + st.lists(st.sampled_from(primes), max_size=30), + min_size=1, + max_size=n, + ) + ) + ) return [reduce(operator.mul, nums, 1) * com_fac for nums in comp_primes] @@ -130,13 +151,21 @@ def st_comp_no_com_fac(draw): """ Strategy that returns lists of numbers that don't have a common factor. """ - primes = draw(st.lists(st_primes(max_value=2**512), - min_size=2, max_size=10, unique=True)) + primes = draw( + st.lists( + st_primes(max_value=2 ** 512), min_size=2, max_size=10, unique=True + ) + ) # first select the primes that will create the uncommon factor # between returned numbers - uncom_fac_primes = draw(st.lists( - st.sampled_from(primes), - min_size=1, max_size=len(primes)-1, unique=True)) + uncom_fac_primes = draw( + st.lists( + st.sampled_from(primes), + min_size=1, + max_size=len(primes) - 1, + unique=True, + ) + ) uncom_fac = reduce(operator.mul, uncom_fac_primes, 1) # then build composites from leftover primes @@ -148,10 +177,14 @@ def st_comp_no_com_fac(draw): # select at most 20 lists, each having at most 30 primes # selected from the leftover_primes list number_primes = draw( - st.integers(min_value=1, max_value=20). - flatmap(lambda n: st.lists(st.lists(st.sampled_from(leftover_primes), - max_size=30), - min_size=1, max_size=n))) + st.integers(min_value=1, max_value=20).flatmap( + lambda n: st.lists( + st.lists(st.sampled_from(leftover_primes), max_size=30), + min_size=1, + max_size=n, + ) + ) + ) numbers = [reduce(operator.mul, nums, 1) for nums in number_primes] @@ -162,13 +195,15 @@ def st_comp_no_com_fac(draw): HYP_SETTINGS = {} if HC_PRESENT: # pragma: no branch - HYP_SETTINGS['suppress_health_check']=[HealthCheck.filter_too_much, - HealthCheck.too_slow] + HYP_SETTINGS["suppress_health_check"] = [ + HealthCheck.filter_too_much, + HealthCheck.too_slow, + ] # the factorization() sometimes takes a long time to finish - HYP_SETTINGS['deadline'] = 5000 + HYP_SETTINGS["deadline"] = 5000 -HYP_SLOW_SETTINGS=dict(HYP_SETTINGS) +HYP_SLOW_SETTINGS = dict(HYP_SETTINGS) HYP_SLOW_SETTINGS["max_examples"] = 10 @@ -178,10 +213,12 @@ def test_gcd(self): assert gcd([3 * 5 * 7, 3 * 5 * 11, 3 * 5 * 13]) == 3 * 5 assert gcd(3) == 3 - @unittest.skipUnless(HC_PRESENT, - "Hypothesis 2.0.0 can't be made tolerant of hard to " - "meet requirements (like `is_prime()`), the test " - "case times-out on it") + @unittest.skipUnless( + HC_PRESENT, + "Hypothesis 2.0.0 can't be made tolerant of hard to " + "meet requirements (like `is_prime()`), the test " + "case times-out on it", + ) @settings(**HYP_SLOW_SETTINGS) @given(st_comp_with_com_fac()) def test_gcd_with_com_factor(self, numbers): @@ -190,18 +227,25 @@ def test_gcd_with_com_factor(self, numbers): for i in numbers: assert i % n == 0 - @unittest.skipUnless(HC_PRESENT, - "Hypothesis 2.0.0 can't be made tolerant of hard to " - "meet requirements (like `is_prime()`), the test " - "case times-out on it") + @unittest.skipUnless( + HC_PRESENT, + "Hypothesis 2.0.0 can't be made tolerant of hard to " + "meet requirements (like `is_prime()`), the test " + "case times-out on it", + ) @settings(**HYP_SLOW_SETTINGS) @given(st_comp_no_com_fac()) def test_gcd_with_uncom_factor(self, numbers): n = gcd(numbers) assert n == 1 - @given(st.lists(st.integers(min_value=1, max_value=2**8192), - min_size=1, max_size=20)) + @given( + st.lists( + st.integers(min_value=1, max_value=2 ** 8192), + min_size=1, + max_size=20, + ) + ) def test_gcd_with_random_numbers(self, numbers): n = gcd(numbers) for i in numbers: @@ -213,17 +257,24 @@ def test_lcm(self): assert lcm([3, 5 * 3, 7 * 3]) == 3 * 5 * 7 assert lcm(3) == 3 - @given(st.lists(st.integers(min_value=1, max_value=2**8192), - min_size=1, max_size=20)) + @given( + st.lists( + st.integers(min_value=1, max_value=2 ** 8192), + min_size=1, + max_size=20, + ) + ) def test_lcm_with_random_numbers(self, numbers): n = lcm(numbers) for i in numbers: assert n % i == 0 - @unittest.skipUnless(HC_PRESENT, - "Hypothesis 2.0.0 can't be made tolerant of hard to " - "meet requirements (like `is_prime()`), the test " - "case times-out on it") + @unittest.skipUnless( + HC_PRESENT, + "Hypothesis 2.0.0 can't be made tolerant of hard to " + "meet requirements (like `is_prime()`), the test " + "case times-out on it", + ) @settings(**HYP_SETTINGS) @given(st_num_square_prime()) def test_square_root_mod_prime(self, vals): @@ -233,7 +284,7 @@ def test_square_root_mod_prime(self, vals): assert calc * calc % prime == square @settings(**HYP_SETTINGS) - @given(st.integers(min_value=1, max_value=10**12)) + @given(st.integers(min_value=1, max_value=10 ** 12)) @example(265399 * 1526929) @example(373297 ** 2 * 553991) def test_factorization(self, num): diff --git a/src/ecdsa/test_pyecdsa.py b/src/ecdsa/test_pyecdsa.py index d83eb01d..e8e69b80 100644 --- a/src/ecdsa/test_pyecdsa.py +++ b/src/ecdsa/test_pyecdsa.py @@ -23,15 +23,30 @@ from . import util from .util import sigencode_der, sigencode_strings from .util import sigdecode_der, sigdecode_strings -from .util import number_to_string, encoded_oid_ecPublicKey, \ - MalformedSignature +from .util import number_to_string, encoded_oid_ecPublicKey, MalformedSignature from .curves import Curve, UnknownCurveError -from .curves import NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, \ - SECP256k1, BRAINPOOLP160r1, BRAINPOOLP192r1, BRAINPOOLP224r1, \ - BRAINPOOLP256r1, BRAINPOOLP320r1, BRAINPOOLP384r1, BRAINPOOLP512r1, \ - curves -from .ecdsa import curve_brainpoolp224r1, curve_brainpoolp256r1, \ - curve_brainpoolp384r1, curve_brainpoolp512r1 +from .curves import ( + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + SECP256k1, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, + curves, +) +from .ecdsa import ( + curve_brainpoolp224r1, + curve_brainpoolp256r1, + curve_brainpoolp384r1, + curve_brainpoolp512r1, +) from .ellipticcurve import Point from . import der from . import rfc6979 @@ -44,13 +59,17 @@ class SubprocessError(Exception): def run_openssl(cmd): OPENSSL = "openssl" - p = subprocess.Popen([OPENSSL] + cmd.split(), - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT) + p = subprocess.Popen( + [OPENSSL] + cmd.split(), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + ) stdout, ignored = p.communicate() if p.returncode != 0: - raise SubprocessError("cmd '%s %s' failed: rc=%s, stdout/err was %s" % - (OPENSSL, cmd, p.returncode, stdout)) + raise SubprocessError( + "cmd '%s %s' failed: rc=%s, stdout/err was %s" + % (OPENSSL, cmd, p.returncode, stdout) + ) return stdout.decode() @@ -76,7 +95,8 @@ def test_deterministic(self): pub = priv.get_verifying_key() k = rfc6979.generate_k( - SECP256k1.generator.order(), secexp, sha256, sha256(data).digest()) + SECP256k1.generator.order(), secexp, sha256, sha256(data).digest() + ) sig1 = priv.sign(data, k=k) self.assertTrue(pub.verify(sig1, data)) @@ -102,18 +122,27 @@ def test_lengths(self): self.assertEqual(len(pub.to_string()), default.verifying_key_length) sig = priv.sign(b("data")) self.assertEqual(len(sig), default.signature_length) - for curve in (NIST192p, NIST224p, NIST256p, NIST384p, NIST521p, - BRAINPOOLP160r1, BRAINPOOLP192r1, BRAINPOOLP224r1, - BRAINPOOLP256r1, BRAINPOOLP320r1, BRAINPOOLP384r1, - BRAINPOOLP512r1): + for curve in ( + NIST192p, + NIST224p, + NIST256p, + NIST384p, + NIST521p, + BRAINPOOLP160r1, + BRAINPOOLP192r1, + BRAINPOOLP224r1, + BRAINPOOLP256r1, + BRAINPOOLP320r1, + BRAINPOOLP384r1, + BRAINPOOLP512r1, + ): start = time.time() priv = SigningKey.generate(curve=curve) pub1 = priv.get_verifying_key() keygen_time = time.time() - start pub2 = VerifyingKey.from_string(pub1.to_string(), curve) self.assertEqual(pub1.to_string(), pub2.to_string()) - self.assertEqual(len(pub1.to_string()), - curve.verifying_key_length) + self.assertEqual(len(pub1.to_string()), curve.verifying_key_length) start = time.time() sig = priv.sign(b("data")) sign_time = time.time() - start @@ -127,8 +156,9 @@ def test_serialize(self): self.assertEqual(secexp1, secexp2) priv1 = SigningKey.from_secret_exponent(secexp1, curve) priv2 = SigningKey.from_secret_exponent(secexp2, curve) - self.assertEqual(hexlify(priv1.to_string()), - hexlify(priv2.to_string())) + self.assertEqual( + hexlify(priv1.to_string()), hexlify(priv2.to_string()) + ) self.assertEqual(priv1.to_pem(), priv2.to_pem()) pub1 = priv1.get_verifying_key() pub2 = priv2.get_verifying_key() @@ -139,8 +169,7 @@ def test_serialize(self): self.assertTrue(pub2.verify(sig1, data)) self.assertTrue(pub1.verify(sig2, data)) self.assertTrue(pub2.verify(sig2, data)) - self.assertEqual(hexlify(pub1.to_string()), - hexlify(pub2.to_string())) + self.assertEqual(hexlify(pub1.to_string()), hexlify(pub2.to_string())) def test_nonrandom(self): s = b("all the entropy in the entire world, compressed into one line") @@ -151,8 +180,10 @@ def not_much_entropy(numbytes): # we control the entropy source, these two keys should be identical: priv1 = SigningKey.generate(entropy=not_much_entropy) priv2 = SigningKey.generate(entropy=not_much_entropy) - self.assertEqual(hexlify(priv1.get_verifying_key().to_string()), - hexlify(priv2.get_verifying_key().to_string())) + self.assertEqual( + hexlify(priv1.get_verifying_key().to_string()), + hexlify(priv2.get_verifying_key().to_string()), + ) # likewise, signatures should be identical. Obviously you'd never # want to do this with keys you care about, because the secrecy of # the private key depends upon using different random numbers for @@ -162,10 +193,13 @@ def not_much_entropy(numbytes): self.assertEqual(hexlify(sig1), hexlify(sig2)) def assertTruePrivkeysEqual(self, priv1, priv2): - self.assertEqual(priv1.privkey.secret_multiplier, - priv2.privkey.secret_multiplier) - self.assertEqual(priv1.privkey.public_key.generator, - priv2.privkey.public_key.generator) + self.assertEqual( + priv1.privkey.secret_multiplier, priv2.privkey.secret_multiplier + ) + self.assertEqual( + priv1.privkey.public_key.generator, + priv2.privkey.public_key.generator, + ) def test_privkey_creation(self): s = b("all the entropy in the entire world, compressed into one line") @@ -267,15 +301,18 @@ def test_pubkey_strings(self): pub2 = VerifyingKey.from_der(pub1_der) self.assertTruePubkeysEqual(pub1, pub2) - self.assertRaises(der.UnexpectedDER, - VerifyingKey.from_der, pub1_der + b("junk")) + self.assertRaises( + der.UnexpectedDER, VerifyingKey.from_der, pub1_der + b("junk") + ) badpub = VerifyingKey.from_der(pub1_der) class FakeGenerator: def order(self): return 123456789 - badcurve = Curve("unknown", None, FakeGenerator(), (1, 2, 3, 4, 5, 6), None) + badcurve = Curve( + "unknown", None, FakeGenerator(), (1, 2, 3, 4, 5, 6), None + ) badpub.curve = badcurve badder = badpub.to_der() self.assertRaises(UnknownCurveError, VerifyingKey.from_der, badder) @@ -283,7 +320,9 @@ def order(self): pem = pub1.to_pem() self.assertEqual(type(pem), binary_type) self.assertTrue(pem.startswith(b("-----BEGIN PUBLIC KEY-----")), pem) - self.assertTrue(pem.strip().endswith(b("-----END PUBLIC KEY-----")), pem) + self.assertTrue( + pem.strip().endswith(b("-----END PUBLIC KEY-----")), pem + ) pub2 = VerifyingKey.from_pem(pem) self.assertTruePubkeysEqual(pub1, pub2) @@ -316,10 +355,11 @@ def test_sk_to_der_with_invalid_point_encoding(self): def test_vk_from_der_garbage_after_curve_oid(self): type_oid_der = encoded_oid_ecPublicKey - curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + \ - b('garbage') + curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) + b( + "garbage" + ) enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b'\x00\xff', None) + point_der = der.encode_bitstring(b"\x00\xff", None) to_decode = der.encode_sequence(enc_type_der, point_der) with self.assertRaises(der.UnexpectedDER): @@ -329,7 +369,7 @@ def test_vk_from_der_invalid_key_type(self): type_oid_der = der.encode_oid(*(1, 2, 3)) curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b'\x00\xff', None) + point_der = der.encode_bitstring(b"\x00\xff", None) to_decode = der.encode_sequence(enc_type_der, point_der) with self.assertRaises(der.UnexpectedDER): @@ -339,7 +379,7 @@ def test_vk_from_der_garbage_after_point_string(self): type_oid_der = encoded_oid_ecPublicKey curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b'\x00\xff', None) + b('garbage') + point_der = der.encode_bitstring(b"\x00\xff", None) + b("garbage") to_decode = der.encode_sequence(enc_type_der, point_der) with self.assertRaises(der.UnexpectedDER): @@ -349,7 +389,7 @@ def test_vk_from_der_invalid_bitstring(self): type_oid_der = encoded_oid_ecPublicKey curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b'\x08\xff', None) + point_der = der.encode_bitstring(b"\x08\xff", None) to_decode = der.encode_sequence(enc_type_der, point_der) with self.assertRaises(der.UnexpectedDER): @@ -359,7 +399,7 @@ def test_vk_from_der_with_invalid_length_of_encoding(self): type_oid_der = encoded_oid_ecPublicKey curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b'\xff'*64, 0) + point_der = der.encode_bitstring(b"\xff" * 64, 0) to_decode = der.encode_sequence(enc_type_der, point_der) with self.assertRaises(MalformedPointError): @@ -369,7 +409,7 @@ def test_vk_from_der_with_raw_encoding(self): type_oid_der = encoded_oid_ecPublicKey curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) enc_type_der = der.encode_sequence(type_oid_der, curve_oid_der) - point_der = der.encode_bitstring(b'\xff'*48, 0) + point_der = der.encode_bitstring(b"\xff" * 48, 0) to_decode = der.encode_sequence(enc_type_der, point_der) with self.assertRaises(der.UnexpectedDER): @@ -400,22 +440,22 @@ def test_signature_strings(self): def test_sig_decode_strings_with_invalid_count(self): with self.assertRaises(MalformedSignature): - sigdecode_strings([b('one'), b('two'), b('three')], 0xff) + sigdecode_strings([b("one"), b("two"), b("three")], 0xFF) def test_sig_decode_strings_with_wrong_r_len(self): with self.assertRaises(MalformedSignature): - sigdecode_strings([b('one'), b('two')], 0xff) + sigdecode_strings([b("one"), b("two")], 0xFF) def test_sig_decode_strings_with_wrong_s_len(self): with self.assertRaises(MalformedSignature): - sigdecode_strings([b('\xa0'), b('\xb0\xff')], 0xff) + sigdecode_strings([b("\xa0"), b("\xb0\xff")], 0xFF) def test_verify_with_too_long_input(self): sk = SigningKey.generate() vk = sk.verifying_key with self.assertRaises(BadDigestError): - vk.verify_digest(None, b('\x00') * 128) + vk.verify_digest(None, b("\x00") * 128) def test_sk_from_secret_exponent_with_wrong_sec_exponent(self): with self.assertRaises(MalformedPointError): @@ -423,11 +463,11 @@ def test_sk_from_secret_exponent_with_wrong_sec_exponent(self): def test_sk_from_string_with_wrong_len_string(self): with self.assertRaises(MalformedPointError): - SigningKey.from_string(b('\x01')) + SigningKey.from_string(b("\x01")) def test_sk_from_der_with_junk_after_sequence(self): ver_der = der.encode_integer(1) - to_decode = der.encode_sequence(ver_der) + b('garbage') + to_decode = der.encode_sequence(ver_der) + b("garbage") with self.assertRaises(der.UnexpectedDER): SigningKey.from_der(to_decode) @@ -441,33 +481,36 @@ def test_sk_from_der_with_wrong_version(self): def test_sk_from_der_invalid_const_tag(self): ver_der = der.encode_integer(1) - privkey_der = der.encode_octet_string(b('\x00\xff')) + privkey_der = der.encode_octet_string(b("\x00\xff")) curve_oid_der = der.encode_oid(*(1, 2, 3)) const_der = der.encode_constructed(1, curve_oid_der) - to_decode = der.encode_sequence(ver_der, privkey_der, const_der, - curve_oid_der) + to_decode = der.encode_sequence( + ver_der, privkey_der, const_der, curve_oid_der + ) with self.assertRaises(der.UnexpectedDER): SigningKey.from_der(to_decode) def test_sk_from_der_garbage_after_privkey_oid(self): ver_der = der.encode_integer(1) - privkey_der = der.encode_octet_string(b('\x00\xff')) - curve_oid_der = der.encode_oid(*(1, 2, 3)) + b('garbage') + privkey_der = der.encode_octet_string(b("\x00\xff")) + curve_oid_der = der.encode_oid(*(1, 2, 3)) + b("garbage") const_der = der.encode_constructed(0, curve_oid_der) - to_decode = der.encode_sequence(ver_der, privkey_der, const_der, - curve_oid_der) + to_decode = der.encode_sequence( + ver_der, privkey_der, const_der, curve_oid_der + ) with self.assertRaises(der.UnexpectedDER): SigningKey.from_der(to_decode) def test_sk_from_der_with_short_privkey(self): ver_der = der.encode_integer(1) - privkey_der = der.encode_octet_string(b('\x00\xff')) + privkey_der = der.encode_octet_string(b("\x00\xff")) curve_oid_der = der.encode_oid(*(1, 2, 840, 10045, 3, 1, 1)) const_der = der.encode_constructed(0, curve_oid_der) - to_decode = der.encode_sequence(ver_der, privkey_der, const_der, - curve_oid_der) + to_decode = der.encode_sequence( + ver_der, privkey_der, const_der, curve_oid_der + ) sk = SigningKey.from_der(to_decode) self.assertEqual(sk.privkey.secret_multiplier, 255) @@ -476,24 +519,29 @@ def test_sign_with_too_long_hash(self): sk = SigningKey.from_secret_exponent(12) with self.assertRaises(BadDigestError): - sk.sign_digest(b('\xff') * 64) + sk.sign_digest(b("\xff") * 64) def test_hashfunc(self): sk = SigningKey.generate(curve=NIST256p, hashfunc=sha256) data = b("security level is 128 bits") sig = sk.sign(data) - vk = VerifyingKey.from_string(sk.get_verifying_key().to_string(), - curve=NIST256p, hashfunc=sha256) + vk = VerifyingKey.from_string( + sk.get_verifying_key().to_string(), curve=NIST256p, hashfunc=sha256 + ) self.assertTrue(vk.verify(sig, data)) sk2 = SigningKey.generate(curve=NIST256p) sig2 = sk2.sign(data, hashfunc=sha256) - vk2 = VerifyingKey.from_string(sk2.get_verifying_key().to_string(), - curve=NIST256p, hashfunc=sha256) + vk2 = VerifyingKey.from_string( + sk2.get_verifying_key().to_string(), + curve=NIST256p, + hashfunc=sha256, + ) self.assertTrue(vk2.verify(sig2, data)) - vk3 = VerifyingKey.from_string(sk.get_verifying_key().to_string(), - curve=NIST256p) + vk3 = VerifyingKey.from_string( + sk.get_verifying_key().to_string(), curve=NIST256p + ) self.assertTrue(vk3.verify(sig, data, hashfunc=sha256)) def test_public_key_recovery(self): @@ -508,7 +556,9 @@ def test_public_key_recovery(self): signature = sk.sign(data) # Recover verifying keys - recovered_vks = VerifyingKey.from_public_key_recovery(signature, data, curve) + recovered_vks = VerifyingKey.from_public_key_recovery( + signature, data, curve + ) # Test if each pk is valid for recovered_vk in recovered_vks: @@ -517,11 +567,15 @@ def test_public_key_recovery(self): # Test if properties are equal self.assertEqual(vk.curve, recovered_vk.curve) - self.assertEqual(vk.default_hashfunc, recovered_vk.default_hashfunc) + self.assertEqual( + vk.default_hashfunc, recovered_vk.default_hashfunc + ) # Test if original vk is the list of recovered keys self.assertTrue( - vk.pubkey.point in [recovered_vk.pubkey.point for recovered_vk in recovered_vks]) + vk.pubkey.point + in [recovered_vk.pubkey.point for recovered_vk in recovered_vks] + ) def test_public_key_recovery_with_custom_hash(self): # Create keys @@ -535,9 +589,9 @@ def test_public_key_recovery_with_custom_hash(self): signature = sk.sign(data) # Recover verifying keys - recovered_vks = VerifyingKey.\ - from_public_key_recovery(signature, data, curve, - hashfunc=sha256) + recovered_vks = VerifyingKey.from_public_key_recovery( + signature, data, curve, hashfunc=sha256 + ) # Test if each pk is valid for recovered_vk in recovered_vks: @@ -549,84 +603,98 @@ def test_public_key_recovery_with_custom_hash(self): self.assertEqual(sha256, recovered_vk.default_hashfunc) # Test if original vk is the list of recovered keys - self.assertTrue(vk.pubkey.point in - [recovered_vk.pubkey.point for recovered_vk in recovered_vks]) + self.assertTrue( + vk.pubkey.point + in [recovered_vk.pubkey.point for recovered_vk in recovered_vks] + ) def test_encoding(self): sk = SigningKey.from_secret_exponent(123456789) vk = sk.verifying_key - exp = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3' - '\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4' - 'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*') + exp = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) self.assertEqual(vk.to_string(), exp) - self.assertEqual(vk.to_string('raw'), exp) - self.assertEqual(vk.to_string('uncompressed'), b('\x04') + exp) - self.assertEqual(vk.to_string('compressed'), b('\x02') + exp[:24]) - self.assertEqual(vk.to_string('hybrid'), b('\x06') + exp) + self.assertEqual(vk.to_string("raw"), exp) + self.assertEqual(vk.to_string("uncompressed"), b("\x04") + exp) + self.assertEqual(vk.to_string("compressed"), b("\x02") + exp[:24]) + self.assertEqual(vk.to_string("hybrid"), b("\x06") + exp) def test_decoding(self): sk = SigningKey.from_secret_exponent(123456789) vk = sk.verifying_key - enc = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3' - '\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4' - 'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*') + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) from_raw = VerifyingKey.from_string(enc) self.assertEqual(from_raw.pubkey.point, vk.pubkey.point) - from_uncompressed = VerifyingKey.from_string(b('\x04') + enc) + from_uncompressed = VerifyingKey.from_string(b("\x04") + enc) self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point) - from_compressed = VerifyingKey.from_string(b('\x02') + enc[:24]) + from_compressed = VerifyingKey.from_string(b("\x02") + enc[:24]) self.assertEqual(from_compressed.pubkey.point, vk.pubkey.point) - from_uncompressed = VerifyingKey.from_string(b('\x06') + enc) + from_uncompressed = VerifyingKey.from_string(b("\x06") + enc) self.assertEqual(from_uncompressed.pubkey.point, vk.pubkey.point) def test_decoding_with_malformed_uncompressed(self): - enc = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3' - '\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4' - 'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*') + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b('\x02') + enc) + VerifyingKey.from_string(b("\x02") + enc) def test_decoding_with_malformed_compressed(self): - enc = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3' - '\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4' - 'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*') + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b('\x01') + enc[:24]) + VerifyingKey.from_string(b("\x01") + enc[:24]) def test_decoding_with_inconsistent_hybrid(self): - enc = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3' - '\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4' - 'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*') + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b('\x07') + enc) + VerifyingKey.from_string(b("\x07") + enc) def test_decoding_with_point_not_on_curve(self): - enc = b('\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3' - '\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4' - 'z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*') + enc = b( + "\x0c\xe0\x1d\xe0d\x1c\x8eS\x8a\xc0\x9eK\xa8x !\xd5\xc2\xc3" + "\xfd\xc8\xa0c\xff\xfb\x02\xb9\xc4\x84)\x1a\x0f\x8b\x87\xa4" + "z\x8a#\xb5\x97\xecO\xb6\xa0HQ\x89*" + ) with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(enc[:47] + b('\x00')) + VerifyingKey.from_string(enc[:47] + b("\x00")) def test_decoding_with_point_at_infinity(self): # decoding it is unsupported, as it's not necessary to encode it with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b('\x00')) + VerifyingKey.from_string(b("\x00")) def test_not_lying_on_curve(self): - enc = number_to_string(NIST192p.curve.p(), NIST192p.curve.p()+1) + enc = number_to_string(NIST192p.curve.p(), NIST192p.curve.p() + 1) with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b('\x02') + enc) + VerifyingKey.from_string(b("\x02") + enc) def test_from_string_with_invalid_curve_too_short_ver_key_len(self): # both verifying_key_length and baselen are calculated internally @@ -637,7 +705,7 @@ def test_from_string_with_invalid_curve_too_short_ver_key_len(self): curve.baselen = 32 with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b('\x00')*16, curve) + VerifyingKey.from_string(b("\x00") * 16, curve) def test_from_string_with_invalid_curve_too_long_ver_key_len(self): # both verifying_key_length and baselen are calculated internally @@ -648,18 +716,19 @@ def test_from_string_with_invalid_curve_too_long_ver_key_len(self): curve.baselen = 16 with self.assertRaises(MalformedPointError): - VerifyingKey.from_string(b('\x00')*16, curve) + VerifyingKey.from_string(b("\x00") * 16, curve) -@pytest.mark.parametrize("val,even", - [(i, j) for i in range(256) for j in [True, False]]) +@pytest.mark.parametrize( + "val,even", [(i, j) for i in range(256) for j in [True, False]] +) def test_VerifyingKey_decode_with_small_values(val, even): enc = number_to_string(val, NIST192p.order) if even: - enc = b('\x02') + enc + enc = b("\x02") + enc else: - enc = b('\x03') + enc + enc = b("\x03") + enc # small values can both be actual valid public keys and not, verify that # only expected exceptions are raised if they are not @@ -673,8 +742,9 @@ def test_VerifyingKey_decode_with_small_values(val, even): params = [] for curve in curves: for enc in ["raw", "uncompressed", "compressed", "hybrid"]: - params.append(pytest.param(curve, enc, id="{0}-{1}".format( - curve.name, enc))) + params.append( + pytest.param(curve, enc, id="{0}-{1}".format(curve.name, enc)) + ) @pytest.mark.parametrize("curve,encoding", params) @@ -706,9 +776,10 @@ class OpenSSL(unittest.TestCase): # openssl ec -in privkey.pem -pubout -out pubkey.pem # openssl ec -in privkey.pem -pubout -outform DER -out pubkey.der - OPENSSL_SUPPORTED_CURVES = set(c.split(':')[0].strip() for c in - run_openssl("ecparam -list_curves") - .split('\n')) + OPENSSL_SUPPORTED_CURVES = set( + c.split(":")[0].strip() + for c in run_openssl("ecparam -list_curves").split("\n") + ) def get_openssl_messagedigest_arg(self, hash_name): v = run_openssl("version") @@ -724,83 +795,115 @@ def get_openssl_messagedigest_arg(self, hash_name): # vk: 3:OpenSSL->python 4:python->OpenSSL # sig: 5:OpenSSL->python 6:python->OpenSSL - @pytest.mark.skipif("prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1") + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) def test_from_openssl_nist192p(self): return self.do_test_from_openssl(NIST192p) - @pytest.mark.skipif("prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1") + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) def test_from_openssl_nist192p_sha256(self): return self.do_test_from_openssl(NIST192p, "SHA256") - @pytest.mark.skipif("secp224r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp224r1") + @pytest.mark.skipif( + "secp224r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp224r1", + ) def test_from_openssl_nist224p(self): return self.do_test_from_openssl(NIST224p) - @pytest.mark.skipif("prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1") + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) def test_from_openssl_nist256p(self): return self.do_test_from_openssl(NIST256p) - @pytest.mark.skipif("prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1") + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) def test_from_openssl_nist256p_sha384(self): return self.do_test_from_openssl(NIST256p, "SHA384") - @pytest.mark.skipif("prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1") + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) def test_from_openssl_nist256p_sha512(self): return self.do_test_from_openssl(NIST256p, "SHA512") - @pytest.mark.skipif("secp384r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp384r1") + @pytest.mark.skipif( + "secp384r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp384r1", + ) def test_from_openssl_nist384p(self): return self.do_test_from_openssl(NIST384p) - @pytest.mark.skipif("secp521r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp521r1") + @pytest.mark.skipif( + "secp521r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp521r1", + ) def test_from_openssl_nist521p(self): return self.do_test_from_openssl(NIST521p) - @pytest.mark.skipif("secp256k1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp256k1") + @pytest.mark.skipif( + "secp256k1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp256k1", + ) def test_from_openssl_secp256k1(self): return self.do_test_from_openssl(SECP256k1) - @pytest.mark.skipif("brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP160r1") + @pytest.mark.skipif( + "brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP160r1", + ) def test_from_openssl_brainpoolp160r1(self): return self.do_test_from_openssl(BRAINPOOLP160r1) - @pytest.mark.skipif("brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP192r1") + @pytest.mark.skipif( + "brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP192r1", + ) def test_from_openssl_brainpoolp192r1(self): return self.do_test_from_openssl(BRAINPOOLP192r1) - @pytest.mark.skipif("brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP224r1") + @pytest.mark.skipif( + "brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP224r1", + ) def test_from_openssl_brainpoolp224r1(self): return self.do_test_from_openssl(BRAINPOOLP224r1) - @pytest.mark.skipif("brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP256r1") + @pytest.mark.skipif( + "brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP256r1", + ) def test_from_openssl_brainpoolp256r1(self): return self.do_test_from_openssl(BRAINPOOLP256r1) - @pytest.mark.skipif("brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP320r1") + @pytest.mark.skipif( + "brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP320r1", + ) def test_from_openssl_brainpoolp320r1(self): return self.do_test_from_openssl(BRAINPOOLP320r1) - @pytest.mark.skipif("brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP384r1") + @pytest.mark.skipif( + "brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP384r1", + ) def test_from_openssl_brainpoolp384r1(self): return self.do_test_from_openssl(BRAINPOOLP384r1) - @pytest.mark.skipif("brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP512r1") + @pytest.mark.skipif( + "brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP512r1", + ) def test_from_openssl_brainpoolp512r1(self): return self.do_test_from_openssl(BRAINPOOLP512r1) @@ -817,106 +920,145 @@ def do_test_from_openssl(self, curve, hash_name="SHA1"): run_openssl("ec -in t/privkey.pem -pubout -out t/pubkey.pem") data = b("data") with open("t/data.txt", "wb") as e: - e.write(data) - run_openssl("dgst %s -sign t/privkey.pem -out t/data.sig t/data.txt" % mdarg) - run_openssl("dgst %s -verify t/pubkey.pem -signature t/data.sig t/data.txt" % mdarg) + e.write(data) + run_openssl( + "dgst %s -sign t/privkey.pem -out t/data.sig t/data.txt" % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig t/data.txt" + % mdarg + ) with open("t/pubkey.pem", "rb") as e: - pubkey_pem = e.read() + pubkey_pem = e.read() vk = VerifyingKey.from_pem(pubkey_pem) # 3 with open("t/data.sig", "rb") as e: - sig_der = e.read() - self.assertTrue(vk.verify(sig_der, data, # 5 - hashfunc=partial(hashlib.new, hash_name), - sigdecode=sigdecode_der)) + sig_der = e.read() + self.assertTrue( + vk.verify( + sig_der, + data, # 5 + hashfunc=partial(hashlib.new, hash_name), + sigdecode=sigdecode_der, + ) + ) with open("t/privkey.pem") as e: - fp = e.read() + fp = e.read() sk = SigningKey.from_pem(fp) # 1 - sig = sk.sign( - data, - hashfunc=partial(hashlib.new, hash_name), - ) - self.assertTrue(vk.verify(sig, - data, - hashfunc=partial(hashlib.new, hash_name))) + sig = sk.sign(data, hashfunc=partial(hashlib.new, hash_name),) + self.assertTrue( + vk.verify(sig, data, hashfunc=partial(hashlib.new, hash_name)) + ) - @pytest.mark.skipif("prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1") + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) def test_to_openssl_nist192p(self): self.do_test_to_openssl(NIST192p) - @pytest.mark.skipif("prime192v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime192v1") + @pytest.mark.skipif( + "prime192v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime192v1", + ) def test_to_openssl_nist192p_sha256(self): self.do_test_to_openssl(NIST192p, "SHA256") - @pytest.mark.skipif("secp224r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp224r1") + @pytest.mark.skipif( + "secp224r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp224r1", + ) def test_to_openssl_nist224p(self): self.do_test_to_openssl(NIST224p) - @pytest.mark.skipif("prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1") + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) def test_to_openssl_nist256p(self): self.do_test_to_openssl(NIST256p) - @pytest.mark.skipif("prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1") + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) def test_to_openssl_nist256p_sha384(self): self.do_test_to_openssl(NIST256p, "SHA384") - @pytest.mark.skipif("prime256v1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support prime256v1") + @pytest.mark.skipif( + "prime256v1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support prime256v1", + ) def test_to_openssl_nist256p_sha512(self): self.do_test_to_openssl(NIST256p, "SHA512") - @pytest.mark.skipif("secp384r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp384r1") + @pytest.mark.skipif( + "secp384r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp384r1", + ) def test_to_openssl_nist384p(self): self.do_test_to_openssl(NIST384p) - @pytest.mark.skipif("secp521r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp521r1") + @pytest.mark.skipif( + "secp521r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp521r1", + ) def test_to_openssl_nist521p(self): self.do_test_to_openssl(NIST521p) - @pytest.mark.skipif("secp256k1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support secp256k1") + @pytest.mark.skipif( + "secp256k1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support secp256k1", + ) def test_to_openssl_secp256k1(self): self.do_test_to_openssl(SECP256k1) - @pytest.mark.skipif("brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP160r1") + @pytest.mark.skipif( + "brainpoolP160r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP160r1", + ) def test_to_openssl_brainpoolp160r1(self): self.do_test_to_openssl(BRAINPOOLP160r1) - @pytest.mark.skipif("brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP192r1") + @pytest.mark.skipif( + "brainpoolP192r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP192r1", + ) def test_to_openssl_brainpoolp192r1(self): self.do_test_to_openssl(BRAINPOOLP192r1) - @pytest.mark.skipif("brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP224r1") + @pytest.mark.skipif( + "brainpoolP224r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP224r1", + ) def test_to_openssl_brainpoolp224r1(self): self.do_test_to_openssl(BRAINPOOLP224r1) - @pytest.mark.skipif("brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP256r1") + @pytest.mark.skipif( + "brainpoolP256r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP256r1", + ) def test_to_openssl_brainpoolp256r1(self): self.do_test_to_openssl(BRAINPOOLP256r1) - @pytest.mark.skipif("brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP320r1") + @pytest.mark.skipif( + "brainpoolP320r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP320r1", + ) def test_to_openssl_brainpoolp320r1(self): self.do_test_to_openssl(BRAINPOOLP320r1) - @pytest.mark.skipif("brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP384r1") + @pytest.mark.skipif( + "brainpoolP384r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP384r1", + ) def test_to_openssl_brainpoolp384r1(self): self.do_test_to_openssl(BRAINPOOLP384r1) - @pytest.mark.skipif("brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, - reason="system openssl does not support brainpoolP512r1") + @pytest.mark.skipif( + "brainpoolP512r1" not in OPENSSL_SUPPORTED_CURVES, + reason="system openssl does not support brainpoolP512r1", + ) def test_to_openssl_brainpoolp512r1(self): self.do_test_to_openssl(BRAINPOOLP512r1) @@ -933,27 +1075,42 @@ def do_test_to_openssl(self, curve, hash_name="SHA1"): vk = sk.get_verifying_key() data = b("data") with open("t/pubkey.der", "wb") as e: - e.write(vk.to_der()) # 4 + e.write(vk.to_der()) # 4 with open("t/pubkey.pem", "wb") as e: - e.write(vk.to_pem()) # 4 - sig_der = sk.sign(data, hashfunc=partial(hashlib.new, hash_name), - sigencode=sigencode_der) + e.write(vk.to_pem()) # 4 + sig_der = sk.sign( + data, + hashfunc=partial(hashlib.new, hash_name), + sigencode=sigencode_der, + ) with open("t/data.sig", "wb") as e: - e.write(sig_der) # 6 + e.write(sig_der) # 6 with open("t/data.txt", "wb") as e: - e.write(data) + e.write(data) with open("t/baddata.txt", "wb") as e: - e.write(data + b("corrupt")) - - self.assertRaises(SubprocessError, run_openssl, - "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/baddata.txt" % mdarg) - run_openssl("dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/data.txt" % mdarg) + e.write(data + b("corrupt")) + + self.assertRaises( + SubprocessError, + run_openssl, + "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/baddata.txt" + % mdarg, + ) + run_openssl( + "dgst %s -verify t/pubkey.der -keyform DER -signature t/data.sig t/data.txt" + % mdarg + ) with open("t/privkey.pem", "wb") as e: - e.write(sk.to_pem()) # 2 - run_openssl("dgst %s -sign t/privkey.pem -out t/data.sig2 t/data.txt" % mdarg) - run_openssl("dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" % mdarg) + e.write(sk.to_pem()) # 2 + run_openssl( + "dgst %s -sign t/privkey.pem -out t/data.sig2 t/data.txt" % mdarg + ) + run_openssl( + "dgst %s -verify t/pubkey.pem -signature t/data.sig2 t/data.txt" + % mdarg + ) class DER(unittest.TestCase): @@ -966,14 +1123,17 @@ def test_integer(self): # self.assertEqual(der.encode_integer(-1), b("\x02\x01\xff")) def s(n): - return der.remove_integer(der.encode_integer(n) + b("junk")) + return der.remove_integer(der.encode_integer(n) + b("junk")) + self.assertEqual(s(0), (0, b("junk"))) self.assertEqual(s(1), (1, b("junk"))) self.assertEqual(s(127), (127, b("junk"))) self.assertEqual(s(128), (128, b("junk"))) self.assertEqual(s(256), (256, b("junk"))) - self.assertEqual(s(1234567890123456789012345678901234567890), - (1234567890123456789012345678901234567890, b("junk"))) + self.assertEqual( + s(1234567890123456789012345678901234567890), + (1234567890123456789012345678901234567890, b("junk")), + ) def test_number(self): self.assertEqual(der.encode_number(0), b("\x00")) @@ -1022,28 +1182,41 @@ def test_trytryagain(self): tta = util.randrange_from_seed__trytryagain for i in range(1000): seed = "seed-%d" % i - for order in (2**8 - 2, 2**8 - 1, 2**8, 2**8 + 1, 2**8 + 2, - 2**16 - 1, 2**16 + 1): + for order in ( + 2 ** 8 - 2, + 2 ** 8 - 1, + 2 ** 8, + 2 ** 8 + 1, + 2 ** 8 + 2, + 2 ** 16 - 1, + 2 ** 16 + 1, + ): n = tta(seed, order) self.assertTrue(1 <= n < order, (1, n, order)) # this trytryagain *does* provide long-term stability - self.assertEqual(("%x" % (tta("seed", NIST224p.order))).encode(), - b("6fa59d73bf0446ae8743cf748fc5ac11d5585a90356417e97155c3bc")) + self.assertEqual( + ("%x" % (tta("seed", NIST224p.order))).encode(), + b("6fa59d73bf0446ae8743cf748fc5ac11d5585a90356417e97155c3bc"), + ) - @given(st.integers(min_value=0, max_value=10**200)) + @given(st.integers(min_value=0, max_value=10 ** 200)) def test_randrange(self, i): # util.randrange does not provide long-term stability: we might # change the algorithm in the future. entropy = util.PRNG("seed-%d" % i) - for order in (2**8 - 2, 2**8 - 1, 2**8, - 2**16 - 1, 2**16 + 1, - ): + for order in ( + 2 ** 8 - 2, + 2 ** 8 - 1, + 2 ** 8, + 2 ** 16 - 1, + 2 ** 16 + 1, + ): # that oddball 2**16+1 takes half our runtime n = util.randrange(order, entropy=entropy) self.assertTrue(1 <= n < order, (1, n, order)) def OFF_test_prove_uniformity(self): # pragma: no cover - order = 2**8 - 2 + order = 2 ** 8 - 2 counts = dict([(i, 0) for i in range(1, order)]) assert 0 not in counts assert order not in counts @@ -1064,22 +1237,33 @@ def _do(self, generator, secexp, hsh, hash_func, expected): self.assertEqual(expected, actual) def test_SECP256k1(self): - '''RFC doesn't contain test vectors for SECP256k1 used in bitcoin. - This vector has been computed by Golang reference implementation instead.''' + """RFC doesn't contain test vectors for SECP256k1 used in bitcoin. + This vector has been computed by Golang reference implementation instead.""" self._do( generator=SECP256k1.generator, secexp=int("9d0219792467d7d37b4d43298a7d0c05", 16), hsh=sha256(b("sample")).digest(), hash_func=sha256, - expected=int("8fa1f95d514760e498f28957b824ee6ec39ed64826ff4fecc2b5739ec45b91cd", 16)) + expected=int( + "8fa1f95d514760e498f28957b824ee6ec39ed64826ff4fecc2b5739ec45b91cd", + 16, + ), + ) def test_SECP256k1_2(self): self._do( generator=SECP256k1.generator, - secexp=int("cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", 16), + secexp=int( + "cca9fbcc1b41e5a95d369eaa6ddcff73b61a4efaa279cfc6567e8daa39cbaf50", + 16, + ), hsh=sha256(b("sample")).digest(), hash_func=sha256, - expected=int("2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", 16)) + expected=int( + "2df40ca70e639d89528a6b670d9d48d9165fdc0febc0974056bdce192b8e16a3", + 16, + ), + ) def test_SECP256k1_3(self): self._do( @@ -1087,15 +1271,21 @@ def test_SECP256k1_3(self): secexp=0x1, hsh=sha256(b("Satoshi Nakamoto")).digest(), hash_func=sha256, - expected=0x8F8A276C19F4149656B280621E358CCE24F5F52542772691EE69063B74F15D15) + expected=0x8F8A276C19F4149656B280621E358CCE24F5F52542772691EE69063B74F15D15, + ) def test_SECP256k1_4(self): self._do( generator=SECP256k1.generator, secexp=0x1, - hsh=sha256(b("All those moments will be lost in time, like tears in rain. Time to die...")).digest(), + hsh=sha256( + b( + "All those moments will be lost in time, like tears in rain. Time to die..." + ) + ).digest(), hash_func=sha256, - expected=0x38AA22D72376B4DBC472E06C3BA403EE0A394DA63FC58D88686C611ABA98D6B3) + expected=0x38AA22D72376B4DBC472E06C3BA403EE0A394DA63FC58D88686C611ABA98D6B3, + ) def test_SECP256k1_5(self): self._do( @@ -1103,24 +1293,36 @@ def test_SECP256k1_5(self): secexp=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364140, hsh=sha256(b("Satoshi Nakamoto")).digest(), hash_func=sha256, - expected=0x33A19B60E25FB6F4435AF53A3D42D493644827367E6453928554F43E49AA6F90) + expected=0x33A19B60E25FB6F4435AF53A3D42D493644827367E6453928554F43E49AA6F90, + ) def test_SECP256k1_6(self): self._do( generator=SECP256k1.generator, - secexp=0xf8b8af8ce3c7cca5e300d33939540c10d45ce001b8f252bfbc57ba0342904181, + secexp=0xF8B8AF8CE3C7CCA5E300D33939540C10D45CE001B8F252BFBC57BA0342904181, hsh=sha256(b("Alan Turing")).digest(), hash_func=sha256, - expected=0x525A82B70E67874398067543FD84C83D30C175FDC45FDEEE082FE13B1D7CFDF1) + expected=0x525A82B70E67874398067543FD84C83D30C175FDC45FDEEE082FE13B1D7CFDF1, + ) def test_1(self): # Basic example of the RFC, it also tests 'try-try-again' from Step H of rfc6979 self._do( - generator=Point(None, 0, 0, int("4000000000000000000020108A2E0CC0D99F8A5EF", 16)), + generator=Point( + None, + 0, + 0, + int("4000000000000000000020108A2E0CC0D99F8A5EF", 16), + ), secexp=int("09A4D6792295A7F730FC3F2B49CBC0F62E862272F", 16), - hsh=unhexlify(b("AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF")), + hsh=unhexlify( + b( + "AF2BDBE1AA9B6EC1E2ADE1D694F41FC71A831D0268E9891562113D8A62ADD1BF" + ) + ), hash_func=sha256, - expected=int("23AF4074C90A02B3FE61D286D5C87F425E6BDD81B", 16)) + expected=int("23AF4074C90A02B3FE61D286D5C87F425E6BDD81B", 16), + ) def test_2(self): self._do( @@ -1128,7 +1330,10 @@ def test_2(self): secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), hsh=sha1(b("sample")).digest(), hash_func=sha1, - expected=int("37D7CA00D2C7B0E5E412AC03BD44BA837FDD5B28CD3B0021", 16)) + expected=int( + "37D7CA00D2C7B0E5E412AC03BD44BA837FDD5B28CD3B0021", 16 + ), + ) def test_3(self): self._do( @@ -1136,7 +1341,10 @@ def test_3(self): secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), hsh=sha256(b("sample")).digest(), hash_func=sha256, - expected=int("32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496", 16)) + expected=int( + "32B1B6D7D42A05CB449065727A84804FB1A3E34D8F261496", 16 + ), + ) def test_4(self): self._do( @@ -1144,7 +1352,10 @@ def test_4(self): secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), hsh=sha512(b("sample")).digest(), hash_func=sha512, - expected=int("A2AC7AB055E4F20692D49209544C203A7D1F2C0BFBC75DB1", 16)) + expected=int( + "A2AC7AB055E4F20692D49209544C203A7D1F2C0BFBC75DB1", 16 + ), + ) def test_5(self): self._do( @@ -1152,7 +1363,10 @@ def test_5(self): secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), hsh=sha1(b("test")).digest(), hash_func=sha1, - expected=int("D9CF9C3D3297D3260773A1DA7418DB5537AB8DD93DE7FA25", 16)) + expected=int( + "D9CF9C3D3297D3260773A1DA7418DB5537AB8DD93DE7FA25", 16 + ), + ) def test_6(self): self._do( @@ -1160,7 +1374,10 @@ def test_6(self): secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), hsh=sha256(b("test")).digest(), hash_func=sha256, - expected=int("5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C", 16)) + expected=int( + "5C4CE89CF56D9E7C77C8585339B006B97B5F0680B4306C6C", 16 + ), + ) def test_7(self): self._do( @@ -1168,31 +1385,55 @@ def test_7(self): secexp=int("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16), hsh=sha512(b("test")).digest(), hash_func=sha512, - expected=int("0758753A5254759C7CFBAD2E2D9B0792EEE44136C9480527", 16)) + expected=int( + "0758753A5254759C7CFBAD2E2D9B0792EEE44136C9480527", 16 + ), + ) def test_8(self): self._do( generator=NIST521p.generator, - secexp=int("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", 16), + secexp=int( + "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", + 16, + ), hsh=sha1(b("sample")).digest(), hash_func=sha1, - expected=int("089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9", 16)) + expected=int( + "089C071B419E1C2820962321787258469511958E80582E95D8378E0C2CCDB3CB42BEDE42F50E3FA3C71F5A76724281D31D9C89F0F91FC1BE4918DB1C03A5838D0F9", + 16, + ), + ) def test_9(self): self._do( generator=NIST521p.generator, - secexp=int("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", 16), + secexp=int( + "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", + 16, + ), hsh=sha256(b("sample")).digest(), hash_func=sha256, - expected=int("0EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0", 16)) + expected=int( + "0EDF38AFCAAECAB4383358B34D67C9F2216C8382AAEA44A3DAD5FDC9C32575761793FEF24EB0FC276DFC4F6E3EC476752F043CF01415387470BCBD8678ED2C7E1A0", + 16, + ), + ) def test_10(self): self._do( generator=NIST521p.generator, - secexp=int("0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", 16), + secexp=int( + "0FAD06DAA62BA3B25D2FB40133DA757205DE67F5BB0018FEE8C86E1B68C7E75CAA896EB32F1F47C70855836A6D16FCC1466F6D8FBEC67DB89EC0C08B0E996B83538", + 16, + ), hsh=sha512(b("test")).digest(), hash_func=sha512, - expected=int("16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D", 16)) + expected=int( + "16200813020EC986863BEDFC1B121F605C1215645018AEA1A7B215A564DE9EB1B38A67AA1128B80CE391C4FB71187654AAA3431027BFC7F395766CA988C964DC56D", + 16, + ), + ) class ECDH(unittest.TestCase): @@ -1202,10 +1443,12 @@ def _do(self, curve, generator, dA, x_qA, y_qA, dB, x_qB, y_qB, x_Z, y_Z): Z = dA * qB self.assertEqual(Point(curve, x_qA, y_qA), qA) self.assertEqual(Point(curve, x_qB, y_qB), qB) - self.assertTrue((dA * qB) == - (dA * dB * generator) == - (dB * dA * generator) == - (dB * qA)) + self.assertTrue( + (dA * qB) + == (dA * dB * generator) + == (dB * dA * generator) + == (dB * qA) + ) self.assertEqual(Point(curve, x_Z, y_Z), Z) @@ -1216,93 +1459,177 @@ def test_brainpoolP224r1(self): self._do( curve=curve_brainpoolp224r1, generator=BRAINPOOLP224r1.generator, - dA=int("7C4B7A2C8A4BAD1FBB7D79CC0955DB7C6A4660CA64CC4778159B495E", - 16), - x_qA=int("B104A67A6F6E85E14EC1825E1539E8ECDBBF584922367DD88C6BDCF2", - 16), - y_qA=int("46D782E7FDB5F60CD8404301AC5949C58EDB26BC68BA07695B750A94", - 16), - dB=int("63976D4AAE6CD0F6DD18DEFEF55D96569D0507C03E74D6486FFA28FB", - 16), - x_qB=int("2A97089A9296147B71B21A4B574E1278245B536F14D8C2B9D07A874E", - 16), - y_qB=int("9B900D7C77A709A797276B8CA1BA61BB95B546FC29F862E44D59D25B", - 16), - x_Z=int("312DFD98783F9FB77B9704945A73BEB6DCCBE3B65D0F967DCAB574EB", - 16), - y_Z=int("6F800811D64114B1C48C621AB3357CF93F496E4238696A2A012B3C98", - 16)) + dA=int( + "7C4B7A2C8A4BAD1FBB7D79CC0955DB7C6A4660CA64CC4778159B495E", 16 + ), + x_qA=int( + "B104A67A6F6E85E14EC1825E1539E8ECDBBF584922367DD88C6BDCF2", 16 + ), + y_qA=int( + "46D782E7FDB5F60CD8404301AC5949C58EDB26BC68BA07695B750A94", 16 + ), + dB=int( + "63976D4AAE6CD0F6DD18DEFEF55D96569D0507C03E74D6486FFA28FB", 16 + ), + x_qB=int( + "2A97089A9296147B71B21A4B574E1278245B536F14D8C2B9D07A874E", 16 + ), + y_qB=int( + "9B900D7C77A709A797276B8CA1BA61BB95B546FC29F862E44D59D25B", 16 + ), + x_Z=int( + "312DFD98783F9FB77B9704945A73BEB6DCCBE3B65D0F967DCAB574EB", 16 + ), + y_Z=int( + "6F800811D64114B1C48C621AB3357CF93F496E4238696A2A012B3C98", 16 + ), + ) def test_brainpoolP256r1(self): self._do( curve=curve_brainpoolp256r1, generator=BRAINPOOLP256r1.generator, - dA=int("041EB8B1E2BC681BCE8E39963B2E9FC415B05283313DD1A8BCC055F11AE" - "49699", 16), - x_qA=int("78028496B5ECAAB3C8B6C12E45DB1E02C9E4D26B4113BC4F015F60C5C" - "CC0D206", 16), - y_qA=int("A2AE1762A3831C1D20F03F8D1E3C0C39AFE6F09B4D44BBE80CD100987" - "B05F92B", 16), - dB=int("06F5240EACDB9837BC96D48274C8AA834B6C87BA9CC3EEDD81F99A16B8D" - "804D3", 16), - x_qB=int("8E07E219BA588916C5B06AA30A2F464C2F2ACFC1610A3BE2FB240B635" - "341F0DB", 16), - y_qB=int("148EA1D7D1E7E54B9555B6C9AC90629C18B63BEE5D7AA6949EBBF47B2" - "4FDE40D", 16), - x_Z=int("05E940915549E9F6A4A75693716E37466ABA79B4BF2919877A16DD2CC2" - "E23708", 16), - y_Z=int("6BC23B6702BC5A019438CEEA107DAAD8B94232FFBBC350F3B137628FE6" - "FD134C", 16)) + dA=int( + "041EB8B1E2BC681BCE8E39963B2E9FC415B05283313DD1A8BCC055F11AE" + "49699", + 16, + ), + x_qA=int( + "78028496B5ECAAB3C8B6C12E45DB1E02C9E4D26B4113BC4F015F60C5C" + "CC0D206", + 16, + ), + y_qA=int( + "A2AE1762A3831C1D20F03F8D1E3C0C39AFE6F09B4D44BBE80CD100987" + "B05F92B", + 16, + ), + dB=int( + "06F5240EACDB9837BC96D48274C8AA834B6C87BA9CC3EEDD81F99A16B8D" + "804D3", + 16, + ), + x_qB=int( + "8E07E219BA588916C5B06AA30A2F464C2F2ACFC1610A3BE2FB240B635" + "341F0DB", + 16, + ), + y_qB=int( + "148EA1D7D1E7E54B9555B6C9AC90629C18B63BEE5D7AA6949EBBF47B2" + "4FDE40D", + 16, + ), + x_Z=int( + "05E940915549E9F6A4A75693716E37466ABA79B4BF2919877A16DD2CC2" + "E23708", + 16, + ), + y_Z=int( + "6BC23B6702BC5A019438CEEA107DAAD8B94232FFBBC350F3B137628FE6" + "FD134C", + 16, + ), + ) def test_brainpoolP384r1(self): self._do( curve=curve_brainpoolp384r1, generator=BRAINPOOLP384r1.generator, - dA=int("014EC0755B78594BA47FB0A56F6173045B4331E74BA1A6F47322E70D79D" - "828D97E095884CA72B73FDABD5910DF0FA76A", 16), - x_qA=int("45CB26E4384DAF6FB776885307B9A38B7AD1B5C692E0C32F012533277" - "8F3B8D3F50CA358099B30DEB5EE69A95C058B4E", 16), - y_qA=int("8173A1C54AFFA7E781D0E1E1D12C0DC2B74F4DF58E4A4E3AF7026C5D3" - "2DC530A2CD89C859BB4B4B768497F49AB8CC859", 16), - dB=int("6B461CB79BD0EA519A87D6828815D8CE7CD9B3CAA0B5A8262CBCD550A01" - "5C90095B976F3529957506E1224A861711D54", 16), - x_qB=int("01BF92A92EE4BE8DED1A911125C209B03F99E3161CFCC986DC7711383" - "FC30AF9CE28CA3386D59E2C8D72CE1E7B4666E8", 16), - y_qB=int("3289C4A3A4FEE035E39BDB885D509D224A142FF9FBCC5CFE5CCBB3026" - "8EE47487ED8044858D31D848F7A95C635A347AC", 16), - x_Z=int("04CC4FF3DCCCB07AF24E0ACC529955B36D7C807772B92FCBE48F3AFE9A" - "2F370A1F98D3FA73FD0C0747C632E12F1423EC", 16), - y_Z=int("7F465F90BD69AFB8F828A214EB9716D66ABC59F17AF7C75EE7F1DE22AB" - "5D05085F5A01A9382D05BF72D96698FE3FF64E", 16)) + dA=int( + "014EC0755B78594BA47FB0A56F6173045B4331E74BA1A6F47322E70D79D" + "828D97E095884CA72B73FDABD5910DF0FA76A", + 16, + ), + x_qA=int( + "45CB26E4384DAF6FB776885307B9A38B7AD1B5C692E0C32F012533277" + "8F3B8D3F50CA358099B30DEB5EE69A95C058B4E", + 16, + ), + y_qA=int( + "8173A1C54AFFA7E781D0E1E1D12C0DC2B74F4DF58E4A4E3AF7026C5D3" + "2DC530A2CD89C859BB4B4B768497F49AB8CC859", + 16, + ), + dB=int( + "6B461CB79BD0EA519A87D6828815D8CE7CD9B3CAA0B5A8262CBCD550A01" + "5C90095B976F3529957506E1224A861711D54", + 16, + ), + x_qB=int( + "01BF92A92EE4BE8DED1A911125C209B03F99E3161CFCC986DC7711383" + "FC30AF9CE28CA3386D59E2C8D72CE1E7B4666E8", + 16, + ), + y_qB=int( + "3289C4A3A4FEE035E39BDB885D509D224A142FF9FBCC5CFE5CCBB3026" + "8EE47487ED8044858D31D848F7A95C635A347AC", + 16, + ), + x_Z=int( + "04CC4FF3DCCCB07AF24E0ACC529955B36D7C807772B92FCBE48F3AFE9A" + "2F370A1F98D3FA73FD0C0747C632E12F1423EC", + 16, + ), + y_Z=int( + "7F465F90BD69AFB8F828A214EB9716D66ABC59F17AF7C75EE7F1DE22AB" + "5D05085F5A01A9382D05BF72D96698FE3FF64E", + 16, + ), + ) def test_brainpoolP512r1(self): self._do( curve=curve_brainpoolp512r1, generator=BRAINPOOLP512r1.generator, - dA=int("636B6BE0482A6C1C41AA7AE7B245E983392DB94CECEA2660A379CFE1595" - "59E357581825391175FC195D28BAC0CF03A7841A383B95C262B98378287" - "4CCE6FE333", 16), - x_qA=int("0562E68B9AF7CBFD5565C6B16883B777FF11C199161ECC427A39D17EC" - "2166499389571D6A994977C56AD8252658BA8A1B72AE42F4FB7532151" - "AFC3EF0971CCDA", 16), - y_qA=int("A7CA2D8191E21776A89860AFBC1F582FAA308D551C1DC6133AF9F9C3C" - "AD59998D70079548140B90B1F311AFB378AA81F51B275B2BE6B7DEE97" - "8EFC7343EA642E", 16), - dB=int("0AF4E7F6D52EDD52907BB8DBAB3992A0BB696EC10DF11892FF205B66D38" - "1ECE72314E6A6EA079CEA06961DBA5AE6422EF2E9EE803A1F236FB96A17" - "99B86E5C8B", 16), - x_qB=int("5A7954E32663DFF11AE24712D87419F26B708AC2B92877D6BFEE2BFC4" - "3714D89BBDB6D24D807BBD3AEB7F0C325F862E8BADE4F74636B97EAAC" - "E739E11720D323", 16), - y_qB=int("96D14621A9283A1BED84DE8DD64836B2C0758B11441179DC0C54C0D49" - "A47C03807D171DD544B72CAAEF7B7CE01C7753E2CAD1A861ECA55A719" - "54EE1BA35E04BE", 16), - x_Z=int("1EE8321A4BBF93B9CF8921AB209850EC9B7066D1984EF08C2BB7232362" - "08AC8F1A483E79461A00E0D5F6921CE9D360502F85C812BEDEE23AC5B2" - "10E5811B191E", 16), - y_Z=int("2632095B7B936174B41FD2FAF369B1D18DCADEED7E410A7E251F083109" - "7C50D02CFED02607B6A2D5ADB4C0006008562208631875B58B54ECDA5A" - "4F9FE9EAABA6", 16)) + dA=int( + "636B6BE0482A6C1C41AA7AE7B245E983392DB94CECEA2660A379CFE1595" + "59E357581825391175FC195D28BAC0CF03A7841A383B95C262B98378287" + "4CCE6FE333", + 16, + ), + x_qA=int( + "0562E68B9AF7CBFD5565C6B16883B777FF11C199161ECC427A39D17EC" + "2166499389571D6A994977C56AD8252658BA8A1B72AE42F4FB7532151" + "AFC3EF0971CCDA", + 16, + ), + y_qA=int( + "A7CA2D8191E21776A89860AFBC1F582FAA308D551C1DC6133AF9F9C3C" + "AD59998D70079548140B90B1F311AFB378AA81F51B275B2BE6B7DEE97" + "8EFC7343EA642E", + 16, + ), + dB=int( + "0AF4E7F6D52EDD52907BB8DBAB3992A0BB696EC10DF11892FF205B66D38" + "1ECE72314E6A6EA079CEA06961DBA5AE6422EF2E9EE803A1F236FB96A17" + "99B86E5C8B", + 16, + ), + x_qB=int( + "5A7954E32663DFF11AE24712D87419F26B708AC2B92877D6BFEE2BFC4" + "3714D89BBDB6D24D807BBD3AEB7F0C325F862E8BADE4F74636B97EAAC" + "E739E11720D323", + 16, + ), + y_qB=int( + "96D14621A9283A1BED84DE8DD64836B2C0758B11441179DC0C54C0D49" + "A47C03807D171DD544B72CAAEF7B7CE01C7753E2CAD1A861ECA55A719" + "54EE1BA35E04BE", + 16, + ), + x_Z=int( + "1EE8321A4BBF93B9CF8921AB209850EC9B7066D1984EF08C2BB7232362" + "08AC8F1A483E79461A00E0D5F6921CE9D360502F85C812BEDEE23AC5B2" + "10E5811B191E", + 16, + ), + y_Z=int( + "2632095B7B936174B41FD2FAF369B1D18DCADEED7E410A7E251F083109" + "7C50D02CFED02607B6A2D5ADB4C0006008562208631875B58B54ECDA5A" + "4F9FE9EAABA6", + 16, + ), + ) class RFC7027(ECDH): @@ -1312,128 +1639,209 @@ def test_brainpoolP256r1(self): self._do( curve=curve_brainpoolp256r1, generator=BRAINPOOLP256r1.generator, - dA=int("81DB1EE100150FF2EA338D708271BE38300CB54241D79950F77B0630398" - "04F1D", 16), - x_qA=int("44106E913F92BC02A1705D9953A8414DB95E1AAA49E81D9E85F929A8E" - "3100BE5", 16), - y_qA=int("8AB4846F11CACCB73CE49CBDD120F5A900A69FD32C272223F789EF10E" - "B089BDC", 16), - dB=int("55E40BC41E37E3E2AD25C3C6654511FFA8474A91A0032087593852D3E7D" - "76BD3", 16), - x_qB=int("8D2D688C6CF93E1160AD04CC4429117DC2C41825E1E9FCA0ADDD34E6F" - "1B39F7B", 16), - y_qB=int("990C57520812BE512641E47034832106BC7D3E8DD0E4C7F1136D70065" - "47CEC6A", 16), - x_Z=int("89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A" - "18BF2B", 16), - y_Z=int("49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E5963" - "2504DE", 16)) + dA=int( + "81DB1EE100150FF2EA338D708271BE38300CB54241D79950F77B0630398" + "04F1D", + 16, + ), + x_qA=int( + "44106E913F92BC02A1705D9953A8414DB95E1AAA49E81D9E85F929A8E" + "3100BE5", + 16, + ), + y_qA=int( + "8AB4846F11CACCB73CE49CBDD120F5A900A69FD32C272223F789EF10E" + "B089BDC", + 16, + ), + dB=int( + "55E40BC41E37E3E2AD25C3C6654511FFA8474A91A0032087593852D3E7D" + "76BD3", + 16, + ), + x_qB=int( + "8D2D688C6CF93E1160AD04CC4429117DC2C41825E1E9FCA0ADDD34E6F" + "1B39F7B", + 16, + ), + y_qB=int( + "990C57520812BE512641E47034832106BC7D3E8DD0E4C7F1136D70065" + "47CEC6A", + 16, + ), + x_Z=int( + "89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A" + "18BF2B", + 16, + ), + y_Z=int( + "49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E5963" + "2504DE", + 16, + ), + ) def test_brainpoolP384r1(self): self._do( curve=curve_brainpoolp384r1, generator=BRAINPOOLP384r1.generator, - dA=int("1E20F5E048A5886F1F157C74E91BDE2B98C8B52D58E5003D57053FC4B0B" - "D65D6F15EB5D1EE1610DF870795143627D042", 16), - x_qA=int("68B665DD91C195800650CDD363C625F4E742E8134667B767B1B476793" - "588F885AB698C852D4A6E77A252D6380FCAF068", 16), - y_qA=int("55BC91A39C9EC01DEE36017B7D673A931236D2F1F5C83942D049E3FA2" - "0607493E0D038FF2FD30C2AB67D15C85F7FAA59", 16), - dB=int("032640BC6003C59260F7250C3DB58CE647F98E1260ACCE4ACDA3DD869F7" - "4E01F8BA5E0324309DB6A9831497ABAC96670", 16), - x_qB=int("4D44326F269A597A5B58BBA565DA5556ED7FD9A8A9EB76C25F46DB69D" - "19DC8CE6AD18E404B15738B2086DF37E71D1EB4", 16), - y_qB=int("62D692136DE56CBE93BF5FA3188EF58BC8A3A0EC6C1E151A21038A42E" - "9185329B5B275903D192F8D4E1F32FE9CC78C48", 16), - x_Z=int("0BD9D3A7EA0B3D519D09D8E48D0785FB744A6B355E6304BC51C229FBBC" - "E239BBADF6403715C35D4FB2A5444F575D4F42", 16), - y_Z=int("0DF213417EBE4D8E40A5F76F66C56470C489A3478D146DECF6DF0D94BA" - "E9E598157290F8756066975F1DB34B2324B7BD", 16)) + dA=int( + "1E20F5E048A5886F1F157C74E91BDE2B98C8B52D58E5003D57053FC4B0B" + "D65D6F15EB5D1EE1610DF870795143627D042", + 16, + ), + x_qA=int( + "68B665DD91C195800650CDD363C625F4E742E8134667B767B1B476793" + "588F885AB698C852D4A6E77A252D6380FCAF068", + 16, + ), + y_qA=int( + "55BC91A39C9EC01DEE36017B7D673A931236D2F1F5C83942D049E3FA2" + "0607493E0D038FF2FD30C2AB67D15C85F7FAA59", + 16, + ), + dB=int( + "032640BC6003C59260F7250C3DB58CE647F98E1260ACCE4ACDA3DD869F7" + "4E01F8BA5E0324309DB6A9831497ABAC96670", + 16, + ), + x_qB=int( + "4D44326F269A597A5B58BBA565DA5556ED7FD9A8A9EB76C25F46DB69D" + "19DC8CE6AD18E404B15738B2086DF37E71D1EB4", + 16, + ), + y_qB=int( + "62D692136DE56CBE93BF5FA3188EF58BC8A3A0EC6C1E151A21038A42E" + "9185329B5B275903D192F8D4E1F32FE9CC78C48", + 16, + ), + x_Z=int( + "0BD9D3A7EA0B3D519D09D8E48D0785FB744A6B355E6304BC51C229FBBC" + "E239BBADF6403715C35D4FB2A5444F575D4F42", + 16, + ), + y_Z=int( + "0DF213417EBE4D8E40A5F76F66C56470C489A3478D146DECF6DF0D94BA" + "E9E598157290F8756066975F1DB34B2324B7BD", + 16, + ), + ) def test_brainpoolP512r1(self): self._do( curve=curve_brainpoolp512r1, generator=BRAINPOOLP512r1.generator, - dA=int("16302FF0DBBB5A8D733DAB7141C1B45ACBC8715939677F6A56850A38BD8" - "7BD59B09E80279609FF333EB9D4C061231FB26F92EEB04982A5F1D1764C" - "AD57665422", 16), - x_qA=int("0A420517E406AAC0ACDCE90FCD71487718D3B953EFD7FBEC5F7F27E28" - "C6149999397E91E029E06457DB2D3E640668B392C2A7E737A7F0BF044" - "36D11640FD09FD", 16), - y_qA=int("72E6882E8DB28AAD36237CD25D580DB23783961C8DC52DFA2EC138AD4" - "72A0FCEF3887CF62B623B2A87DE5C588301EA3E5FC269B373B60724F5" - "E82A6AD147FDE7", 16), - dB=int("230E18E1BCC88A362FA54E4EA3902009292F7F8033624FD471B5D8ACE49" - "D12CFABBC19963DAB8E2F1EBA00BFFB29E4D72D13F2224562F405CB8050" - "3666B25429", 16), - x_qB=int("9D45F66DE5D67E2E6DB6E93A59CE0BB48106097FF78A081DE781CDB31" - "FCE8CCBAAEA8DD4320C4119F1E9CD437A2EAB3731FA9668AB268D871D" - "EDA55A5473199F", 16), - y_qB=int("2FDC313095BCDD5FB3A91636F07A959C8E86B5636A1E930E8396049CB" - "481961D365CC11453A06C719835475B12CB52FC3C383BCE35E27EF194" - "512B71876285FA", 16), - x_Z=int("A7927098655F1F9976FA50A9D566865DC530331846381C87256BAF3226" - "244B76D36403C024D7BBF0AA0803EAFF405D3D24F11A9B5C0BEF679FE1" - "454B21C4CD1F", 16), - y_Z=int("7DB71C3DEF63212841C463E881BDCF055523BD368240E6C3143BD8DEF8" - "B3B3223B95E0F53082FF5E412F4222537A43DF1C6D25729DDB51620A83" - "2BE6A26680A2", 16)) + dA=int( + "16302FF0DBBB5A8D733DAB7141C1B45ACBC8715939677F6A56850A38BD8" + "7BD59B09E80279609FF333EB9D4C061231FB26F92EEB04982A5F1D1764C" + "AD57665422", + 16, + ), + x_qA=int( + "0A420517E406AAC0ACDCE90FCD71487718D3B953EFD7FBEC5F7F27E28" + "C6149999397E91E029E06457DB2D3E640668B392C2A7E737A7F0BF044" + "36D11640FD09FD", + 16, + ), + y_qA=int( + "72E6882E8DB28AAD36237CD25D580DB23783961C8DC52DFA2EC138AD4" + "72A0FCEF3887CF62B623B2A87DE5C588301EA3E5FC269B373B60724F5" + "E82A6AD147FDE7", + 16, + ), + dB=int( + "230E18E1BCC88A362FA54E4EA3902009292F7F8033624FD471B5D8ACE49" + "D12CFABBC19963DAB8E2F1EBA00BFFB29E4D72D13F2224562F405CB8050" + "3666B25429", + 16, + ), + x_qB=int( + "9D45F66DE5D67E2E6DB6E93A59CE0BB48106097FF78A081DE781CDB31" + "FCE8CCBAAEA8DD4320C4119F1E9CD437A2EAB3731FA9668AB268D871D" + "EDA55A5473199F", + 16, + ), + y_qB=int( + "2FDC313095BCDD5FB3A91636F07A959C8E86B5636A1E930E8396049CB" + "481961D365CC11453A06C719835475B12CB52FC3C383BCE35E27EF194" + "512B71876285FA", + 16, + ), + x_Z=int( + "A7927098655F1F9976FA50A9D566865DC530331846381C87256BAF3226" + "244B76D36403C024D7BBF0AA0803EAFF405D3D24F11A9B5C0BEF679FE1" + "454B21C4CD1F", + 16, + ), + y_Z=int( + "7DB71C3DEF63212841C463E881BDCF055523BD368240E6C3143BD8DEF8" + "B3B3223B95E0F53082FF5E412F4222537A43DF1C6D25729DDB51620A83" + "2BE6A26680A2", + 16, + ), + ) # https://tools.ietf.org/html/rfc4754#page-5 -@pytest.mark.parametrize("w, gwx, gwy, k, msg, md, r, s, curve", - [pytest.param( - "DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", - "2442A5CC0ECD015FA3CA31DC8E2BBC70BF42D60CBCA20085E0822CB04235E970", - "6FC98BD7E50211A4A27102FA3549DF79EBCB4BF246B80945CDDFE7D509BBFD7D", - "9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE", - b"abc", - sha256, - "CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C", - "86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315", - NIST256p, - id="ECDSA-256"), - pytest.param( - "0BEB646634BA87735D77AE4809A0EBEA865535DE4C1E1DCB692E84708E81A5AF" - "62E528C38B2A81B35309668D73524D9F", - "96281BF8DD5E0525CA049C048D345D3082968D10FEDF5C5ACA0C64E6465A97EA" - "5CE10C9DFEC21797415710721F437922", - "447688BA94708EB6E2E4D59F6AB6D7EDFF9301D249FE49C33096655F5D502FAD" - "3D383B91C5E7EDAA2B714CC99D5743CA", - "B4B74E44D71A13D568003D7489908D564C7761E229C58CBFA18950096EB7463B" - "854D7FA992F934D927376285E63414FA", - b'abc', - sha384, - "FB017B914E29149432D8BAC29A514640B46F53DDAB2C69948084E2930F1C8F7E" - "08E07C9C63F2D21A07DCB56A6AF56EB3", - "B263A1305E057F984D38726A1B46874109F417BCA112674C528262A40A629AF1" - "CBB9F516CE0FA7D2FF630863A00E8B9F", - NIST384p, - id="ECDSA-384"), - pytest.param( - "0065FDA3409451DCAB0A0EAD45495112A3D813C17BFD34BDF8C1209D7DF58491" - "20597779060A7FF9D704ADF78B570FFAD6F062E95C7E0C5D5481C5B153B48B37" - "5FA1", - "0151518F1AF0F563517EDD5485190DF95A4BF57B5CBA4CF2A9A3F6474725A35F" - "7AFE0A6DDEB8BEDBCD6A197E592D40188901CECD650699C9B5E456AEA5ADD190" - "52A8", - "006F3B142EA1BFFF7E2837AD44C9E4FF6D2D34C73184BBAD90026DD5E6E85317" - "D9DF45CAD7803C6C20035B2F3FF63AFF4E1BA64D1C077577DA3F4286C58F0AEA" - "E643", - "00C1C2B305419F5A41344D7E4359933D734096F556197A9B244342B8B62F46F9" - "373778F9DE6B6497B1EF825FF24F42F9B4A4BD7382CFC3378A540B1B7F0C1B95" - "6C2F", - b'abc', - sha512, - "0154FD3836AF92D0DCA57DD5341D3053988534FDE8318FC6AAAAB68E2E6F4339" - "B19F2F281A7E0B22C269D93CF8794A9278880ED7DBB8D9362CAEACEE54432055" - "2251", - "017705A7030290D1CEB605A9A1BB03FF9CDD521E87A696EC926C8C10C8362DF4" - "975367101F67D1CF9BCCBF2F3D239534FA509E70AAC851AE01AAC68D62F86647" - "2660", - NIST521p, - id="ECDSA-521") - ]) +@pytest.mark.parametrize( + "w, gwx, gwy, k, msg, md, r, s, curve", + [ + pytest.param( + "DC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F", + "2442A5CC0ECD015FA3CA31DC8E2BBC70BF42D60CBCA20085E0822CB04235E970", + "6FC98BD7E50211A4A27102FA3549DF79EBCB4BF246B80945CDDFE7D509BBFD7D", + "9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE", + b"abc", + sha256, + "CB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C", + "86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315", + NIST256p, + id="ECDSA-256", + ), + pytest.param( + "0BEB646634BA87735D77AE4809A0EBEA865535DE4C1E1DCB692E84708E81A5AF" + "62E528C38B2A81B35309668D73524D9F", + "96281BF8DD5E0525CA049C048D345D3082968D10FEDF5C5ACA0C64E6465A97EA" + "5CE10C9DFEC21797415710721F437922", + "447688BA94708EB6E2E4D59F6AB6D7EDFF9301D249FE49C33096655F5D502FAD" + "3D383B91C5E7EDAA2B714CC99D5743CA", + "B4B74E44D71A13D568003D7489908D564C7761E229C58CBFA18950096EB7463B" + "854D7FA992F934D927376285E63414FA", + b"abc", + sha384, + "FB017B914E29149432D8BAC29A514640B46F53DDAB2C69948084E2930F1C8F7E" + "08E07C9C63F2D21A07DCB56A6AF56EB3", + "B263A1305E057F984D38726A1B46874109F417BCA112674C528262A40A629AF1" + "CBB9F516CE0FA7D2FF630863A00E8B9F", + NIST384p, + id="ECDSA-384", + ), + pytest.param( + "0065FDA3409451DCAB0A0EAD45495112A3D813C17BFD34BDF8C1209D7DF58491" + "20597779060A7FF9D704ADF78B570FFAD6F062E95C7E0C5D5481C5B153B48B37" + "5FA1", + "0151518F1AF0F563517EDD5485190DF95A4BF57B5CBA4CF2A9A3F6474725A35F" + "7AFE0A6DDEB8BEDBCD6A197E592D40188901CECD650699C9B5E456AEA5ADD190" + "52A8", + "006F3B142EA1BFFF7E2837AD44C9E4FF6D2D34C73184BBAD90026DD5E6E85317" + "D9DF45CAD7803C6C20035B2F3FF63AFF4E1BA64D1C077577DA3F4286C58F0AEA" + "E643", + "00C1C2B305419F5A41344D7E4359933D734096F556197A9B244342B8B62F46F9" + "373778F9DE6B6497B1EF825FF24F42F9B4A4BD7382CFC3378A540B1B7F0C1B95" + "6C2F", + b"abc", + sha512, + "0154FD3836AF92D0DCA57DD5341D3053988534FDE8318FC6AAAAB68E2E6F4339" + "B19F2F281A7E0B22C269D93CF8794A9278880ED7DBB8D9362CAEACEE54432055" + "2251", + "017705A7030290D1CEB605A9A1BB03FF9CDD521E87A696EC926C8C10C8362DF4" + "975367101F67D1CF9BCCBF2F3D239534FA509E70AAC851AE01AAC68D62F86647" + "2660", + NIST521p, + id="ECDSA-521", + ), + ], +) def test_RFC4754_vectors(w, gwx, gwy, k, msg, md, r, s, curve): sk = SigningKey.from_string(unhexlify(w), curve) vk = VerifyingKey.from_string(unhexlify(gwx + gwy), curve) diff --git a/src/ecdsa/test_rw_lock.py b/src/ecdsa/test_rw_lock.py index de11d156..fae4689b 100644 --- a/src/ecdsa/test_rw_lock.py +++ b/src/ecdsa/test_rw_lock.py @@ -10,7 +10,9 @@ class Writer(threading.Thread): - def __init__(self, buffer_, rw_lock, init_sleep_time, sleep_time, to_write): + def __init__( + self, buffer_, rw_lock, init_sleep_time, sleep_time, to_write + ): """ @param buffer_: common buffer_ shared by the readers and writers @type buffer_: list @@ -87,8 +89,8 @@ def test_readers_nonexclusive_access(self): self.__start_and_join_threads(threads) ## The third reader should enter after the second one but it should - ## exit before the second one exits - ## (i.e. the readers should be in the critical section + ## exit before the second one exits + ## (i.e. the readers should be in the critical section ## at the same time) self.assertEqual([], threads[0].buffer_read) diff --git a/src/ecdsa/util.py b/src/ecdsa/util.py index 86f51ac3..a8c8349f 100644 --- a/src/ecdsa/util.py +++ b/src/ecdsa/util.py @@ -18,26 +18,33 @@ encoded_oid_ecPublicKey = der.encode_oid(*oid_ecPublicKey) if sys.version_info >= (3,): + def entropy_to_bits(ent_256): """Convert a bytestring to string of 0's and 1's""" - return bin(int.from_bytes(ent_256, 'big'))[2:].zfill(len(ent_256)*8) + return bin(int.from_bytes(ent_256, "big"))[2:].zfill(len(ent_256) * 8) + + else: + def entropy_to_bits(ent_256): """Convert a bytestring to string of 0's and 1's""" - return ''.join(bin(ord(x))[2:].zfill(8) for x in ent_256) + return "".join(bin(ord(x))[2:].zfill(8) for x in ent_256) if sys.version_info < (2, 7): # Can't add a method to a built-in type so we are stuck with this def bit_length(x): return len(bin(x)) - 2 + + else: + def bit_length(x): return x.bit_length() or 1 def orderlen(order): - return (1+len("%x" % order))//2 # bytes + return (1 + len("%x" % order)) // 2 # bytes def randrange(order, entropy=None): @@ -54,8 +61,8 @@ def randrange(order, entropy=None): assert order > 1 if entropy is None: entropy = os.urandom - upper_2 = bit_length(order-2) - upper_256 = upper_2//8 + 1 + upper_2 = bit_length(order - 2) + upper_256 = upper_2 // 8 + 1 while True: # I don't think this needs a counter with bit-wise randrange ent_256 = entropy(upper_256) ent_2 = entropy_to_bits(ent_256) @@ -84,7 +91,9 @@ def __call__(self, numbytes): def block_generator(self, seed): counter = 0 while True: - for byte in sha256(("prng-%d-%s" % (counter, seed)).encode()).digest(): + for byte in sha256( + ("prng-%d-%s" % (counter, seed)).encode() + ).digest(): yield byte counter += 1 @@ -124,6 +133,7 @@ def bits_and_bytes(order): # secexp = ecdsa.util.randrange_from_seed__trytryagain(sed, curve.order) # sk = SigningKey.from_secret_exponent(secexp, curve) + def randrange_from_seed__truncate_bytes(seed, order, hashmod=sha256): # hash the seed, then turn the digest into a number in [1,order), but # don't worry about trying to uniformly fill the range. This will lose, @@ -201,6 +211,7 @@ def string_to_number_fixedlen(string, order): # sigdecode= argument to VK.verify(), and control how the signature is packed # or unpacked. + def sigencode_strings(r, s, order): r_str = number_to_string(r, order) s_str = number_to_string(s, order) @@ -313,8 +324,8 @@ def sigdecode_string(signature, order): if not len(signature) == 2 * l: raise MalformedSignature( "Invalid length of signature, expected {0} bytes long, " - "provided string is {1} bytes long" - .format(2 * l, len(signature))) + "provided string is {1} bytes long".format(2 * l, len(signature)) + ) r = string_to_number_fixedlen(signature[:l], order) s = string_to_number_fixedlen(signature[l:], order) return r, s @@ -341,8 +352,10 @@ def sigdecode_strings(rs_strings, order): """ if not len(rs_strings) == 2: raise MalformedSignature( - "Invalid number of strings provided: {0}, expected 2" - .format(len(rs_strings))) + "Invalid number of strings provided: {0}, expected 2".format( + len(rs_strings) + ) + ) (r_str, s_str) = rs_strings r_str = normalise_bytes(r_str) s_str = normalise_bytes(s_str) @@ -350,13 +363,17 @@ def sigdecode_strings(rs_strings, order): if not len(r_str) == l: raise MalformedSignature( "Invalid length of first string ('r' parameter), " - "expected {0} bytes long, provided string is {1} bytes long" - .format(l, len(r_str))) + "expected {0} bytes long, provided string is {1} bytes long".format( + l, len(r_str) + ) + ) if not len(s_str) == l: raise MalformedSignature( "Invalid length of second string ('s' parameter), " - "expected {0} bytes long, provided string is {1} bytes long" - .format(l, len(s_str))) + "expected {0} bytes long, provided string is {1} bytes long".format( + l, len(s_str) + ) + ) r = string_to_number_fixedlen(r_str, order) s = string_to_number_fixedlen(s_str, order) return r, s @@ -391,11 +408,13 @@ def sigdecode_der(sig_der, order): # return der.encode_sequence(der.encode_integer(r), der.encode_integer(s)) rs_strings, empty = der.remove_sequence(sig_der) if empty != b"": - raise der.UnexpectedDER("trailing junk after DER sig: %s" % - binascii.hexlify(empty)) + raise der.UnexpectedDER( + "trailing junk after DER sig: %s" % binascii.hexlify(empty) + ) r, rest = der.remove_integer(rs_strings) s, empty = der.remove_integer(rest) if empty != b"": - raise der.UnexpectedDER("trailing junk after DER numbers: %s" % - binascii.hexlify(empty)) + raise der.UnexpectedDER( + "trailing junk after DER numbers: %s" % binascii.hexlify(empty) + ) return r, s diff --git a/tox.ini b/tox.ini index 4c2b030b..b994a36d 100644 --- a/tox.ini +++ b/tox.ini @@ -74,24 +74,16 @@ commands = {envpython} speed.py [testenv:codechecks] basepython = python3 deps = - pyflakes + black==19.10b0 flake8 commands = flake8 setup.py speed.py src + black --check --line-length 79 . [flake8] exclude = src/ecdsa/test*.py # We're just getting started. For now, ignore the following problems: -# E111: indentation is not a multiple of four -# E114: indentation is not a multiple of four (comment) -# E226: missing whitespace around arithmetic operator -# E231: missing whitespace after ',' -# E266: too many leading '#' for block comment -# E302: expected 2 blank lines, found 1 -# E305: expected 2 blank lines after class or function definition, found 1 +# E203: whitespace before ':' (this needs to be ignored for black compatibility) # E501: line too long -# E502: the backslash is redundant between brackets # E741: ambiguous variable name -# W391: blank line at end of file -# W503: line break before binary operator -ignore = E111,E114,E226,E231,E266,E302,E305,E501,E502,W391,E741,W503 +extend-ignore = E203,E501,E741 diff --git a/versioneer.py b/versioneer.py index f250cde5..0e49e951 100644 --- a/versioneer.py +++ b/versioneer.py @@ -1,4 +1,3 @@ - # Version: 0.17 """The Versioneer - like a rocketeer, but for versions. @@ -277,6 +276,7 @@ """ from __future__ import print_function + try: import configparser except ImportError: @@ -308,11 +308,13 @@ def get_root(): setup_py = os.path.join(root, "setup.py") versioneer_py = os.path.join(root, "versioneer.py") if not (os.path.exists(setup_py) or os.path.exists(versioneer_py)): - err = ("Versioneer was unable to run the project root directory. " - "Versioneer requires setup.py to be executed from " - "its immediate directory (like 'python setup.py COMMAND'), " - "or in a way that lets it use sys.argv[0] to find the root " - "(like 'python path/to/setup.py COMMAND').") + err = ( + "Versioneer was unable to run the project root directory. " + "Versioneer requires setup.py to be executed from " + "its immediate directory (like 'python setup.py COMMAND'), " + "or in a way that lets it use sys.argv[0] to find the root " + "(like 'python path/to/setup.py COMMAND')." + ) raise VersioneerBadRootError(err) try: # Certain runtime workflows (setup.py install/develop in a setuptools @@ -325,8 +327,10 @@ def get_root(): me_dir = os.path.normcase(os.path.splitext(me)[0]) vsr_dir = os.path.normcase(os.path.splitext(versioneer_py)[0]) if me_dir != vsr_dir: - print("Warning: build in %s is using versioneer.py from %s" - % (os.path.dirname(me), versioneer_py)) + print( + "Warning: build in %s is using versioneer.py from %s" + % (os.path.dirname(me), versioneer_py) + ) except NameError: pass return root @@ -348,6 +352,7 @@ def get(parser, name): if parser.has_option("versioneer", name): return parser.get("versioneer", name) return None + cfg = VersioneerConfig() cfg.VCS = VCS cfg.style = get(parser, "style") or "" @@ -364,6 +369,7 @@ def get(parser, name): class NotThisMethod(Exception): """Exception raised if a method is not valid for the current scenario.""" + # these dictionaries contain VCS-specific tools LONG_VERSION_PY = {} HANDLERS = {} @@ -371,17 +377,20 @@ class NotThisMethod(Exception): def register_vcs_handler(vcs, method): # decorator """Decorator to mark a method as the handler for a particular VCS.""" + def decorate(f): """Store f in HANDLERS[vcs][method].""" if vcs not in HANDLERS: HANDLERS[vcs] = {} HANDLERS[vcs][method] = f return f + return decorate -def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, - env=None): +def run_command( + commands, args, cwd=None, verbose=False, hide_stderr=False, env=None +): """Call the given command(s).""" assert isinstance(commands, list) p = None @@ -389,10 +398,13 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, try: dispcmd = str([c] + args) # remember shell=False, so use git.cmd on windows, not just git - p = subprocess.Popen([c] + args, cwd=cwd, env=env, - stdout=subprocess.PIPE, - stderr=(subprocess.PIPE if hide_stderr - else None)) + p = subprocess.Popen( + [c] + args, + cwd=cwd, + env=env, + stdout=subprocess.PIPE, + stderr=(subprocess.PIPE if hide_stderr else None), + ) break except EnvironmentError: e = sys.exc_info()[1] @@ -415,7 +427,11 @@ def run_command(commands, args, cwd=None, verbose=False, hide_stderr=False, print("stdout was %s" % stdout) return None, p.returncode return stdout, p.returncode -LONG_VERSION_PY['git'] = ''' + + +LONG_VERSION_PY[ + "git" +] = ''' # This file helps to compute a version number in source trees obtained from # git-archive tarball (such as those provided by githubs download-from-tag # feature). Distribution tarballs (built by setup.py sdist) and build @@ -990,7 +1006,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # starting in git-1.8.3, tags are listed as "tag: foo-1.0" instead of # just "foo-1.0". If we see a "tag: " prefix, prefer those. TAG = "tag: " - tags = set([r[len(TAG):] for r in refs if r.startswith(TAG)]) + tags = set([r[len(TAG) :] for r in refs if r.startswith(TAG)]) if not tags: # Either we're using git < 1.8.3, or there really are no tags. We use # a heuristic: assume all version tags have a digit. The old git %d @@ -999,7 +1015,7 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): # between branches and tags. By ignoring refnames without digits, we # filter out many common branch names like "release" and # "stabilization", as well as "HEAD" and "master". - tags = set([r for r in refs if re.search(r'\d', r)]) + tags = set([r for r in refs if re.search(r"\d", r)]) if verbose: print("discarding '%s', no digits" % ",".join(refs - tags)) if verbose: @@ -1007,19 +1023,26 @@ def git_versions_from_keywords(keywords, tag_prefix, verbose): for ref in sorted(tags): # sorting will prefer e.g. "2.0" over "2.0rc1" if ref.startswith(tag_prefix): - r = ref[len(tag_prefix):] + r = ref[len(tag_prefix) :] if verbose: print("picking %s" % r) - return {"version": r, - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": None, - "date": date} + return { + "version": r, + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": None, + "date": date, + } # no suitable tags, so version is "0+unknown", but full hex is still there if verbose: print("no suitable tags, using unknown + full revision id") - return {"version": "0+unknown", - "full-revisionid": keywords["full"].strip(), - "dirty": False, "error": "no suitable tags", "date": None} + return { + "version": "0+unknown", + "full-revisionid": keywords["full"].strip(), + "dirty": False, + "error": "no suitable tags", + "date": None, + } @register_vcs_handler("git", "pieces_from_vcs") @@ -1034,8 +1057,9 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if sys.platform == "win32": GITS = ["git.cmd", "git.exe"] - out, rc = run_command(GITS, ["rev-parse", "--git-dir"], cwd=root, - hide_stderr=True) + out, rc = run_command( + GITS, ["rev-parse", "--git-dir"], cwd=root, hide_stderr=True + ) if rc != 0: if verbose: print("Directory %s not under git control" % root) @@ -1043,10 +1067,19 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): # if there is a tag matching tag_prefix, this yields TAG-NUM-gHEX[-dirty] # if there isn't one, this yields HEX[-dirty] (no NUM) - describe_out, rc = run_command(GITS, ["describe", "--tags", "--dirty", - "--always", "--long", - "--match", "%s*" % tag_prefix], - cwd=root) + describe_out, rc = run_command( + GITS, + [ + "describe", + "--tags", + "--dirty", + "--always", + "--long", + "--match", + "%s*" % tag_prefix, + ], + cwd=root, + ) # --long was added in git-1.5.5 if describe_out is None: raise NotThisMethod("'git describe' failed") @@ -1069,17 +1102,18 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): dirty = git_describe.endswith("-dirty") pieces["dirty"] = dirty if dirty: - git_describe = git_describe[:git_describe.rindex("-dirty")] + git_describe = git_describe[: git_describe.rindex("-dirty")] # now we have TAG-NUM-gHEX or HEX if "-" in git_describe: # TAG-NUM-gHEX - mo = re.search(r'^(.+)-(\d+)-g([0-9a-f]+)$', git_describe) + mo = re.search(r"^(.+)-(\d+)-g([0-9a-f]+)$", git_describe) if not mo: # unparseable. Maybe git-describe is misbehaving? - pieces["error"] = ("unable to parse git-describe output: '%s'" - % describe_out) + pieces["error"] = ( + "unable to parse git-describe output: '%s'" % describe_out + ) return pieces # tag @@ -1088,10 +1122,12 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): if verbose: fmt = "tag '%s' doesn't start with prefix '%s'" print(fmt % (full_tag, tag_prefix)) - pieces["error"] = ("tag '%s' doesn't start with prefix '%s'" - % (full_tag, tag_prefix)) + pieces["error"] = "tag '%s' doesn't start with prefix '%s'" % ( + full_tag, + tag_prefix, + ) return pieces - pieces["closest-tag"] = full_tag[len(tag_prefix):] + pieces["closest-tag"] = full_tag[len(tag_prefix) :] # distance: number of commits since tag pieces["distance"] = int(mo.group(2)) @@ -1102,13 +1138,15 @@ def git_pieces_from_vcs(tag_prefix, root, verbose, run_command=run_command): else: # HEX: no tags pieces["closest-tag"] = None - count_out, rc = run_command(GITS, ["rev-list", "HEAD", "--count"], - cwd=root) + count_out, rc = run_command( + GITS, ["rev-list", "HEAD", "--count"], cwd=root + ) pieces["distance"] = int(count_out) # total number of commits # commit date: see ISO-8601 comment in git_versions_from_keywords() - date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], - cwd=root)[0].strip() + date = run_command(GITS, ["show", "-s", "--format=%ci", "HEAD"], cwd=root)[ + 0 + ].strip() pieces["date"] = date.strip().replace(" ", "T", 1).replace(" ", "", 1) return pieces @@ -1164,18 +1202,25 @@ def versions_from_parentdir(parentdir_prefix, root, verbose): for i in range(3): dirname = os.path.basename(root) if dirname.startswith(parentdir_prefix): - return {"version": dirname[len(parentdir_prefix):], - "full-revisionid": None, - "dirty": False, "error": None, "date": None} + return { + "version": dirname[len(parentdir_prefix) :], + "full-revisionid": None, + "dirty": False, + "error": None, + "date": None, + } else: rootdirs.append(root) root = os.path.dirname(root) # up a level if verbose: - print("Tried directories %s but none started with prefix %s" % - (str(rootdirs), parentdir_prefix)) + print( + "Tried directories %s but none started with prefix %s" + % (str(rootdirs), parentdir_prefix) + ) raise NotThisMethod("rootdir doesn't start with parentdir_prefix") + SHORT_VERSION_PY = """ # This file was generated by 'versioneer.py' (0.17) from # revision-control system data, or from the parent directory name of an @@ -1201,11 +1246,17 @@ def versions_from_file(filename): contents = f.read() except EnvironmentError: raise NotThisMethod("unable to read _version.py") - mo = re.search(r"version_json = '''\n(.*)''' # END VERSION_JSON", - contents, re.M | re.S) + mo = re.search( + r"version_json = '''\n(.*)''' # END VERSION_JSON", + contents, + re.M | re.S, + ) if not mo: - mo = re.search(r"version_json = '''\r\n(.*)''' # END VERSION_JSON", - contents, re.M | re.S) + mo = re.search( + r"version_json = '''\r\n(.*)''' # END VERSION_JSON", + contents, + re.M | re.S, + ) if not mo: raise NotThisMethod("no version_json in _version.py") return json.loads(mo.group(1)) @@ -1214,8 +1265,9 @@ def versions_from_file(filename): def write_to_version_file(filename, versions): """Write the given version number to the given _version.py file.""" os.unlink(filename) - contents = json.dumps(versions, sort_keys=True, - indent=1, separators=(",", ": ")) + contents = json.dumps( + versions, sort_keys=True, indent=1, separators=(",", ": ") + ) with open(filename, "w") as f: f.write(SHORT_VERSION_PY % contents) @@ -1247,8 +1299,7 @@ def render_pep440(pieces): rendered += ".dirty" else: # exception #1 - rendered = "0+untagged.%d.g%s" % (pieces["distance"], - pieces["short"]) + rendered = "0+untagged.%d.g%s" % (pieces["distance"], pieces["short"]) if pieces["dirty"]: rendered += ".dirty" return rendered @@ -1362,11 +1413,13 @@ def render_git_describe_long(pieces): def render(pieces, style): """Render the given version pieces into the requested style.""" if pieces["error"]: - return {"version": "unknown", - "full-revisionid": pieces.get("long"), - "dirty": None, - "error": pieces["error"], - "date": None} + return { + "version": "unknown", + "full-revisionid": pieces.get("long"), + "dirty": None, + "error": pieces["error"], + "date": None, + } if not style or style == "default": style = "pep440" # the default @@ -1386,9 +1439,13 @@ def render(pieces, style): else: raise ValueError("unknown style '%s'" % style) - return {"version": rendered, "full-revisionid": pieces["long"], - "dirty": pieces["dirty"], "error": None, - "date": pieces.get("date")} + return { + "version": rendered, + "full-revisionid": pieces["long"], + "dirty": pieces["dirty"], + "error": None, + "date": pieces.get("date"), + } class VersioneerBadRootError(Exception): @@ -1411,8 +1468,9 @@ def get_versions(verbose=False): handlers = HANDLERS.get(cfg.VCS) assert handlers, "unrecognized VCS '%s'" % cfg.VCS verbose = verbose or cfg.verbose - assert cfg.versionfile_source is not None, \ - "please set versioneer.versionfile_source" + assert ( + cfg.versionfile_source is not None + ), "please set versioneer.versionfile_source" assert cfg.tag_prefix is not None, "please set versioneer.tag_prefix" versionfile_abs = os.path.join(root, cfg.versionfile_source) @@ -1466,9 +1524,13 @@ def get_versions(verbose=False): if verbose: print("unable to compute version") - return {"version": "0+unknown", "full-revisionid": None, - "dirty": None, "error": "unable to compute version", - "date": None} + return { + "version": "0+unknown", + "full-revisionid": None, + "dirty": None, + "error": "unable to compute version", + "date": None, + } def get_version(): @@ -1517,6 +1579,7 @@ def run(self): print(" date: %s" % vers.get("date")) if vers["error"]: print(" error: %s" % vers["error"]) + cmds["version"] = cmd_version # we override "build_py" in both distutils and setuptools @@ -1549,14 +1612,17 @@ def run(self): # now locate _version.py in the new build/ directory and replace # it with an updated value if cfg.versionfile_build: - target_versionfile = os.path.join(self.build_lib, - cfg.versionfile_build) + target_versionfile = os.path.join( + self.build_lib, cfg.versionfile_build + ) print("UPDATING %s" % target_versionfile) write_to_version_file(target_versionfile, versions) + cmds["build_py"] = cmd_build_py if "cx_Freeze" in sys.modules: # cx_freeze enabled? from cx_Freeze.dist import build_exe as _build_exe + # nczeczulin reports that py2exe won't like the pep440-style string # as FILEVERSION, but it can be used for PRODUCTVERSION, e.g. # setup(console=[{ @@ -1577,17 +1643,21 @@ def run(self): os.unlink(target_versionfile) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % - {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) + f.write( + LONG + % { + "DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + } + ) + cmds["build_exe"] = cmd_build_exe del cmds["build_py"] - if 'py2exe' in sys.modules: # py2exe enabled? + if "py2exe" in sys.modules: # py2exe enabled? try: from py2exe.distutils_buildexe import py2exe as _py2exe # py3 except ImportError: @@ -1606,13 +1676,17 @@ def run(self): os.unlink(target_versionfile) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % - {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) + f.write( + LONG + % { + "DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + } + ) + cmds["py2exe"] = cmd_py2exe # we override different "sdist" commands for both environments @@ -1639,8 +1713,10 @@ def make_release_tree(self, base_dir, files): # updated value target_versionfile = os.path.join(base_dir, cfg.versionfile_source) print("UPDATING %s" % target_versionfile) - write_to_version_file(target_versionfile, - self._versioneer_generated_versions) + write_to_version_file( + target_versionfile, self._versioneer_generated_versions + ) + cmds["sdist"] = cmd_sdist return cmds @@ -1695,11 +1771,15 @@ def do_setup(): root = get_root() try: cfg = get_config_from_root(root) - except (EnvironmentError, configparser.NoSectionError, - configparser.NoOptionError) as e: + except ( + EnvironmentError, + configparser.NoSectionError, + configparser.NoOptionError, + ) as e: if isinstance(e, (EnvironmentError, configparser.NoSectionError)): - print("Adding sample versioneer config to setup.cfg", - file=sys.stderr) + print( + "Adding sample versioneer config to setup.cfg", file=sys.stderr + ) with open(os.path.join(root, "setup.cfg"), "a") as f: f.write(SAMPLE_CONFIG) print(CONFIG_ERROR, file=sys.stderr) @@ -1708,15 +1788,18 @@ def do_setup(): print(" creating %s" % cfg.versionfile_source) with open(cfg.versionfile_source, "w") as f: LONG = LONG_VERSION_PY[cfg.VCS] - f.write(LONG % {"DOLLAR": "$", - "STYLE": cfg.style, - "TAG_PREFIX": cfg.tag_prefix, - "PARENTDIR_PREFIX": cfg.parentdir_prefix, - "VERSIONFILE_SOURCE": cfg.versionfile_source, - }) - - ipy = os.path.join(os.path.dirname(cfg.versionfile_source), - "__init__.py") + f.write( + LONG + % { + "DOLLAR": "$", + "STYLE": cfg.style, + "TAG_PREFIX": cfg.tag_prefix, + "PARENTDIR_PREFIX": cfg.parentdir_prefix, + "VERSIONFILE_SOURCE": cfg.versionfile_source, + } + ) + + ipy = os.path.join(os.path.dirname(cfg.versionfile_source), "__init__.py") if os.path.exists(ipy): try: with open(ipy, "r") as f: @@ -1758,8 +1841,10 @@ def do_setup(): else: print(" 'versioneer.py' already in MANIFEST.in") if cfg.versionfile_source not in simple_includes: - print(" appending versionfile_source ('%s') to MANIFEST.in" % - cfg.versionfile_source) + print( + " appending versionfile_source ('%s') to MANIFEST.in" + % cfg.versionfile_source + ) with open(manifest_in, "a") as f: f.write("include %s\n" % cfg.versionfile_source) else: @@ -1808,6 +1893,7 @@ def scan_setup_py(): errors += 1 return errors + if __name__ == "__main__": cmd = sys.argv[1] if cmd == "setup":