├── rubenesque ├── codecs │ ├── __init__.py │ ├── cfrg.py │ ├── jwk.py │ └── sec.py ├── signatures │ ├── __init__.py │ └── ecdsa.py ├── curves │ ├── prime.py │ ├── mdc.py │ ├── __init__.py │ ├── base.py │ ├── weierstrass.py │ ├── edwards.py │ ├── cfrg.py │ ├── sec.py │ └── brainpool.py ├── lcodec.py ├── math.py └── __init__.py ├── .gitignore ├── AUTHORS ├── pytest.ini ├── LICENSE ├── setup.py └── README.md /rubenesque/codecs/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /rubenesque/signatures/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | .cache/ 3 | *.pyc 4 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Nathaniel McCallum 2 | -------------------------------------------------------------------------------- /pytest.ini: -------------------------------------------------------------------------------- 1 | [pytest] 2 | addopts = --doctest-modules rubenesque --cov=rubenesque --cov-report term-missing 3 | doctest_optionflags = IGNORE_EXCEPTION_DETAIL ALLOW_BYTES 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015, Red Hat, Inc. 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /rubenesque/curves/prime.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2015, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | import abc 28 | 29 | from .base import Point 30 | 31 | 32 | class Point(Point): 33 | prime = 1 34 | 35 | @classmethod 36 | def bits(cls): 37 | return (cls.prime - 1).bit_length() 38 | 39 | def __neg__(self): 40 | if self.is_identity: 41 | return self 42 | 43 | return self.__class__.create( 44 | self.primary, 45 | self.prime - self.secondary 46 | ) 47 | -------------------------------------------------------------------------------- /rubenesque/lcodec.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2016, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | from binascii import hexlify, unhexlify 28 | 29 | 30 | if not hasattr(__builtins__, "long"): 31 | long = int 32 | 33 | 34 | def lenc(v, l, be=True): 35 | r"""Encode a long integer to bytes. 36 | 37 | >>> lenc(0xff, 1, True) 38 | b'\xff' 39 | >>> lenc(0xff, 1, False) 40 | b'\xff' 41 | >>> lenc(0xff, 2, True) 42 | b'\x00\xff' 43 | >>> lenc(0xff, 2, False) 44 | b'\xff\x00' 45 | """ 46 | fmt = "%%0%dX" % (l * 2) 47 | v = unhexlify(fmt % v) 48 | return v if be else v[::-1] 49 | 50 | 51 | def ldec(v, be=True): 52 | """Decode a long integer from bytes. 53 | 54 | >>> assert ldec(b'\\xff', True) == 255 55 | >>> assert ldec(b'\\xff', False) == 255 56 | >>> assert ldec(b'\\x00\\xff', True) == 255 57 | >>> assert ldec(b'\\x00\\xff', False) == 65280 58 | """ 59 | return long(hexlify(v if be else v[::-1]), 16) 60 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2015, Red Hat, Inc. 2 | # All rights reserved. 3 | # 4 | # Permission is hereby granted, free of charge, to any person obtaining a copy 5 | # of this software and associated documentation files (the "Software"), to deal 6 | # in the Software without restriction, including without limitation the rights 7 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | # copies of the Software, and to permit persons to whom the Software is 9 | # furnished to do so, subject to the following conditions: 10 | # 11 | # The above copyright notice and this permission notice shall be included in 12 | # all copies or substantial portions of the Software. 13 | # 14 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | # THE SOFTWARE. 21 | 22 | import os 23 | from distutils.core import setup 24 | 25 | 26 | def read(fname): 27 | fname = os.path.join(os.path.dirname(__file__), fname) 28 | with open(fname) as f: 29 | return f.read() 30 | 31 | 32 | setup( 33 | name="rubenesque", 34 | version="1", 35 | author="Nathaniel McCallum", 36 | author_email="npmccallum@redhat.com", 37 | description=("A pure-python implementation of elliptic curves."), 38 | license="BSD", 39 | keywords="cryptography elliptic curves", 40 | url="http://github.com/npmccallum/python-rubenesque", 41 | packages=[ 42 | 'rubenesque', 43 | 'rubenesque.codecs', 44 | 'rubenesque.curves', 45 | 'rubenesque.signatures', 46 | ], 47 | long_description=read('README.md'), 48 | requires=[], 49 | classifiers=[ 50 | "Development Status :: 2 - Pre-Alpha", 51 | "Intended Audience :: Developers", 52 | "License :: OSI Approved :: BSD License", 53 | "Operating System :: OS Independent", 54 | "Programming Language :: Python :: 2", 55 | "Programming Language :: Python :: 3", 56 | "Topic :: Security :: Cryptography" 57 | ], 58 | ) 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Rubenesque 2 | ========== 3 | 4 | Rubenesque is an implementation of several standard elliptic curve groups 5 | in pure Python 3 used for cryptographic purposes. The classes contained in 6 | this source code should be sufficient to implement your own cryptographic 7 | primitives. 8 | 9 | However, please note that, due to the nature of the Python programming 10 | language, this code cannot guarantee either performance or safety from 11 | side-channel attacks. Using this library in situations where such properties 12 | are required is not advised and may actively lead to security compromise. 13 | Although this project will accept patches to improve performance, we will 14 | not consider performance or side-channel avoidance as valid bugs. 15 | 16 | Rubenesque does, however, strive to be correct and standards compliant. This 17 | makes it useful in a variety of scenarios; especially use cases such as 18 | offline cryptography or unit tests against servers programmed in lower level 19 | languages. 20 | 21 | Rubenesque currently implements the following curves: 22 | 23 | * brainpoolP160r1 24 | * brainpoolP192r1 25 | * brainpoolP224r1 26 | * brainpoolP256r1 27 | * brainpoolP320r1 28 | * brainpoolP384r1 29 | * brainpoolP512r1 30 | * edwards25519 31 | * edwards448 32 | * MDC201601 33 | * secp192r1 34 | * secp224r1 35 | * secp256r1 36 | * secp384r1 37 | * secp521r1 38 | 39 | Here is a simple example of doing ECDH with SEC P256 using SEC1 encoding. 40 | 41 | Both sides prepare for the exchange by loading the same curve and encoding: 42 | ``` 43 | >>> from rubenesque.codecs.sec import encode, decode 44 | >>> import rubenesque.curves 45 | >>> secp256r1 = rubenesque.curves.find('secp256r1') 46 | ``` 47 | 48 | Alice generates her private and public keys: 49 | ``` 50 | >>> alice_prv = secp256r1.private_key() 51 | >>> alice_pub = secp256r1.generator() * alice_prv 52 | >>> alice_enc = encode(alice_pub) 53 | ``` 54 | 55 | Bob does the same: 56 | ``` 57 | >>> bob_prv = secp256r1.private_key() 58 | >>> bob_pub = secp256r1.generator() * bob_prv 59 | >>> bob_enc = encode(bob_pub) 60 | ``` 61 | After exchanging their encoded keys, Alice computes the session key: 62 | ``` 63 | >>> alice_ses = decode(secp256r1, bob_enc) * alice_prv 64 | ``` 65 | 66 | Bob does the same: 67 | ``` 68 | >>> bob_ses = decode(secp256r1, alice_enc) * bob_prv 69 | ``` 70 | 71 | Notice that Bob and Alice share the same session key, but not private key: 72 | ``` 73 | >>> alice_prv == bob_prv 74 | False 75 | >>> alice_ses == bob_ses 76 | True 77 | ``` 78 | 79 | 80 | License 81 | ======== 82 | Rubenesque is licensed under the BSD 2-Clause license. 83 | -------------------------------------------------------------------------------- /rubenesque/curves/mdc.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2016, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | from .edwards import Point 28 | 29 | 30 | class MDC201601(Point): 31 | """ 32 | >>> from . import find 33 | >>> cls = find("MDC201601") 34 | 35 | Test basic math: 36 | >>> cls().is_identity 37 | True 38 | >>> (-cls()).is_identity 39 | True 40 | >>> cls().is_valid 41 | False 42 | >>> (cls.generator() * 0).is_identity 43 | True 44 | >>> cls.generator() * 1 == cls.generator() 45 | True 46 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 47 | True 48 | >>> cls.generator() + cls.generator() == cls.generator() * 2 49 | True 50 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 51 | True 52 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 53 | True 54 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 55 | True 56 | 57 | >>> MDC201601.generator() 58 | MDC201601(B681886A7F903B83D85B421E03CBCF6350D72ABB8D2713E2232C25BFEE68363B, CA6734E1B59C0B0359814DCF6563DA421DA8BC3D81A93A3A7E73C355BD2864B5) 59 | """ 60 | 61 | d = 39384817741350628573161184301225915800358770588933756071948264625804612259721 62 | order = 27278090819240297610677772592287387918930509574048068887630978293185521973243 63 | prime = 109112363276961190442711090369149551676330307646118204517771511330536253156371 64 | cofactor = 4 65 | 66 | @classmethod 67 | def generator(cls): 68 | return cls( 69 | 82549803222202399340024462032964942512025856818700414254726364205096731424315, 70 | 91549545637415734422658288799119041756378259523097147807813396915125932811445 71 | ) 72 | -------------------------------------------------------------------------------- /rubenesque/curves/__init__.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2015, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | from . import base 28 | from . import brainpool 29 | from . import cfrg 30 | from . import sec 31 | from . import mdc 32 | 33 | 34 | def find(id): 35 | """Returns a point class for the given curve identifier 36 | 37 | Identifiers can be the class name: 38 | >>> find('secp256r1') 39 | 40 | >>> find('edwards25519') 41 | 42 | 43 | Identifiers can be a common alias: 44 | >>> find('P-256') 45 | 46 | >>> find('ed25519') 47 | 48 | 49 | Identifiers can be OIDs: 50 | >>> find("1.2.840.10045.3.1.1") 51 | 52 | 53 | >>> find('snoopyCurve') 54 | Traceback (most recent call last): 55 | ... 56 | NameError: Unknown curve 'snoopyCurve' 57 | """ 58 | 59 | def _inner(name, cls=base.Point): 60 | if cls.__name__ == name: 61 | return cls 62 | 63 | if name in cls.aliases: 64 | return cls 65 | 66 | for c in cls.__subclasses__(): 67 | cc = _inner(name, c) 68 | if cc is not None: 69 | return cc 70 | 71 | return None 72 | 73 | cls = _inner(id) 74 | if cls is None: 75 | raise NameError("Unknown curve '%s'" % id) 76 | return cls 77 | 78 | def supported(): 79 | """Returns a list of the names of supported curves. 80 | 81 | >>> tuple(sorted(supported())) 82 | ('MDC201601', 'brainpoolP160r1', 'brainpoolP192r1', 'brainpoolP224r1', 'brainpoolP256r1', 'brainpoolP320r1', 'brainpoolP384r1', 'brainpoolP512r1', 'edwards25519', 'edwards448', 'secp192r1', 'secp224r1', 'secp256r1', 'secp384r1', 'secp521r1') 83 | """ 84 | 85 | def _inner(cls=base.Point): 86 | if not cls.__subclasses__(): 87 | yield cls.__name__ 88 | else: 89 | for c in cls.__subclasses__(): 90 | for n in _inner(c): 91 | yield n 92 | 93 | return _inner() 94 | -------------------------------------------------------------------------------- /rubenesque/math.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2015, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | 28 | def legendre(n, p): 29 | """Compute the Legendre Symbol 30 | 31 | >>> legendre(27, 7) 32 | -1 33 | >>> legendre(28, 7) 34 | 0 35 | >>> legendre(29, 7) 36 | 1 37 | """ 38 | return {0: 0, p - 1: -1}.get(pow(n, (p - 1) // 2, p), 1) 39 | 40 | 41 | def sqrt(n, p): 42 | """Compute the square root using Tonelli-Shanks 43 | 44 | >>> sqrt(0, 13) 45 | 0 46 | >>> sqrt(1, 13) 47 | 1 48 | >>> sqrt(2, 13) 49 | 0 50 | >>> sqrt(3, 13) 51 | 9 52 | >>> sqrt(4, 13) 53 | 11 54 | >>> sqrt(5, 13) 55 | 0 56 | >>> sqrt(6, 13) 57 | 0 58 | >>> sqrt(7, 13) 59 | 0 60 | >>> sqrt(8, 13) 61 | 0 62 | >>> sqrt(9, 13) 63 | 3 64 | >>> sqrt(10, 13) 65 | 7 66 | >>> sqrt(11, 13) 67 | 0 68 | >>> sqrt(12, 13) 69 | 8 70 | """ 71 | if legendre(n, p) != 1: 72 | return 0 73 | if n == 0: 74 | return 0 75 | if p == 2: 76 | return n 77 | 78 | s = 0 79 | q = p - 1 80 | while q & 1 == 0: 81 | q >>= 1 82 | s += 1 83 | 84 | if s == 1: 85 | return pow(n, (p + 1) // 4, p) 86 | 87 | z = 2 88 | while legendre(z, p) != -1: 89 | z += 1 90 | 91 | r = pow(n, (q + 1) // 2, p) 92 | t = pow(n, q, p) 93 | c = pow(z, q, p) 94 | m = s 95 | 96 | while True: 97 | if t == 1: 98 | return r 99 | for i in range(m): 100 | if pow(t, 2 ** i, p) == 1: 101 | b = pow(c, 2 ** (m - i - 1), p) 102 | r = r * b % p 103 | c = b * b % p 104 | t = t * c % p 105 | m = i 106 | break 107 | 108 | assert False 109 | 110 | 111 | def egcd(a, b): 112 | """ 113 | >>> egcd(3, 7) 114 | (1, -2, 1) 115 | """ 116 | if a == 0: 117 | return (b, 0, 1) 118 | 119 | g, y, x = egcd(b % a, a) 120 | return (g, x - (b // a) * y, y) 121 | 122 | 123 | def inv(n, m): 124 | """Calculate the multiplicitive inverse 125 | 126 | >>> inv(7, 13) 127 | 2 128 | """ 129 | g, x, y = egcd(n, m) 130 | return x % m if g == 1 else None 131 | -------------------------------------------------------------------------------- /rubenesque/codecs/cfrg.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2015, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | """ 28 | Implements codec according to the protocol discussed on CFRG: 29 | http://www.ietf.org/mail-archive/web/cfrg/current/msg07256.html 30 | """ 31 | 32 | from ..lcodec import lenc, ldec 33 | 34 | 35 | def encode(point): 36 | """ 37 | >>> from ..curves.sec import secp224r1, secp521r1 38 | >>> from ..curves.cfrg import edwards25519, edwards448 39 | 40 | >>> encode(secp224r1.generator()) 41 | b'!\\x1d\\\\\\x11\\xd6\\x8024"\\x11\\xc2V\\xd3\\xc1\\x03J\\xb9\\x90\\x132\\x7f\\xbf\\xb4k\\xbd\\x0c\\x0e\\xb7\\x00' 42 | 43 | >>> encode(secp521r1.generator()) 44 | b"f\\xbd\\xe5\\xc21~~\\xf9\\x9bBj\\x85\\xc1\\xb3H3\\xde\\xa8\\xff\\xa2'\\xc1\\x1d\\xfe(Y\\xe7\\xefw^K\\xa1\\xba=Mk`\\xaf(\\xf8!\\xb5?\\x059\\x81d\\x9cB\\xb4\\x95#f\\xcb>\\x9e\\xcd\\xe9\\x04\\x04\\xb7\\x06\\x8e\\x85\\xc6\\x00" 45 | 46 | >>> encode(edwards25519.generator()) 47 | b'Xfffffffffffffffffffffffffffffff' 48 | 49 | >>> encode(edwards448.generator()) 50 | b'\\xfe\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x7f\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x7f\\x00' 51 | """ 52 | 53 | l = point.bits() // 8 + 1 54 | b = (point.secondary & 1) << (l * 8 - 1) 55 | return lenc(point.primary | b, l, False) 56 | 57 | 58 | def decode(cls, bytes): 59 | """ 60 | >>> from ..curves.sec import secp224r1, secp521r1 61 | >>> from ..curves.cfrg import edwards25519, edwards448 62 | 63 | >>> g5 = secp224r1.generator() * 5 64 | >>> decode(secp224r1, encode(g5)) == g5 65 | True 66 | 67 | >>> g5 = secp521r1.generator() * 5 68 | >>> decode(secp521r1, encode(g5)) == g5 69 | True 70 | 71 | >>> g5 = edwards25519.generator() * 5 72 | >>> decode(edwards25519, encode(g5)) == g5 73 | True 74 | 75 | >>> g5 = edwards448.generator() * 5 76 | >>> decode(edwards448, encode(g5)) == g5 77 | True 78 | """ 79 | 80 | l = cls.bits() // 8 + 1 81 | assert len(bytes) == l 82 | 83 | p = ldec(bytes, False) 84 | b = (p >> (l * 8 - 1)) & 1 85 | p &= ~(1 << (l * 8 - 1)) 86 | 87 | point = cls.recover(p, b) 88 | assert point.is_valid 89 | return point 90 | -------------------------------------------------------------------------------- /rubenesque/__init__.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2015, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | """ 28 | Rubenesque is an implementation of several standard elliptic curve groups 29 | in pure Python 3 used for cryptographic purposes. The classes contained in 30 | this source code should be sufficient to implement your own cryptographic 31 | primitives. 32 | 33 | However, please note that, due to the nature of the Python programming 34 | language, this code cannot guarantee either performance or safety from 35 | side-channel attacks. Using this library in situations where such properties 36 | are required is not advised and may actively lead to security compromise. 37 | Although this project will accept patches to improve performance, we will 38 | not consider performance or side-channel avoidance as valid bugs. 39 | 40 | Rubenesque does, however, strive to be correct and standards compliant. This 41 | makes it useful in a variety of scenarios; especially use cases such as 42 | offline cryptography or unit tests against servers programmed in lower level 43 | languages. 44 | 45 | Rubenesque is licensed under the BSD 2-Clause license. 46 | 47 | Rubenesque currently implements the following curves: 48 | * brainpoolP160r1 49 | * brainpoolP192r1 50 | * brainpoolP224r1 51 | * brainpoolP256r1 52 | * brainpoolP320r1 53 | * brainpoolP384r1 54 | * brainpoolP512r1 55 | * edwards25519 56 | * edwards448 57 | * MDC201601 58 | * secp192r1 59 | * secp224r1 60 | * secp256r1 61 | * secp384r1 62 | * secp521r1 63 | 64 | Here is a simple example of doing ECDH with SEC P256 using SEC1 encoding. 65 | 66 | Both sides prepare for the exchange by loading the same curve and encoding: 67 | >>> from rubenesque.codecs.sec import encode, decode 68 | >>> import rubenesque.curves 69 | >>> secp256r1 = rubenesque.curves.find('secp256r1') 70 | 71 | Alice generates her private and public keys: 72 | >>> alice_prv = secp256r1.private_key() 73 | >>> alice_pub = secp256r1.generator() * alice_prv 74 | >>> alice_enc = encode(alice_pub) 75 | 76 | Bob does the same: 77 | >>> bob_prv = secp256r1.private_key() 78 | >>> bob_pub = secp256r1.generator() * bob_prv 79 | >>> bob_enc = encode(bob_pub) 80 | 81 | After exchanging their encoded keys, Alice computes the session key: 82 | >>> alice_ses = decode(secp256r1, bob_enc) * alice_prv 83 | 84 | Bob does the same: 85 | >>> bob_ses = decode(secp256r1, alice_enc) * bob_prv 86 | 87 | Notice that Bob and Alice share the same session key, but not private key: 88 | >>> alice_prv == bob_prv 89 | False 90 | >>> alice_ses == bob_ses 91 | True 92 | """ 93 | 94 | -------------------------------------------------------------------------------- /rubenesque/codecs/jwk.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2016, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | from ..lcodec import lenc, ldec 28 | from ..curves import find 29 | import base64 30 | 31 | def b64u_enc(x): 32 | return base64.b64encode(x, b"-_").decode("UTF-8").rstrip("=") 33 | 34 | def b64u_dec(x): 35 | x += "=" * (4 - len(x) % 4) 36 | return base64.b64decode(x.encode("UTF-8"), b"-_") 37 | 38 | def encode(point, prv=None): 39 | """Encodes a point to a dictionary representing a JSON Web Token. 40 | 41 | >>> from ..curves.sec import secp256r1 42 | >>> prv = 95868137618030166809364817078804351319836184172769340930264256928620637034634 43 | >>> secp256r1.generator() * prv 44 | secp256r1(808D060082C176EED3E776A4AC598CC8672C1779F974EECC9B03411CA5B9495D, 48B5BFC527DFCE53D6AC7115237D031CCFF87A0570B77350A9E503EE7305A69B) 45 | 46 | >>> jwk = { "kty": "EC", "crv": "P-256"} 47 | >>> jwk["x"] = "gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0" 48 | >>> jwk["y"] = "SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps" 49 | >>> encode(secp256r1.generator() * prv) == jwk 50 | True 51 | 52 | >>> jwk["d"] = "0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo" 53 | >>> encode(secp256r1.generator() * prv, prv) == jwk 54 | True 55 | """ 56 | assert not point.is_identity 57 | 58 | NAMES = { 59 | "secp256r1": "P-256", 60 | "secp384r1": "P-384", 61 | "secp521r1": "P-521", 62 | } 63 | 64 | x = lenc(point.x, (point.bits() + 7) // 8) 65 | y = lenc(point.y, (point.bits() + 7) // 8) 66 | 67 | jwk = { 68 | "kty": "EC", 69 | "crv": NAMES.get(point.__class__.__name__, point.__class__.__name__), 70 | "x": b64u_enc(x), 71 | "y": b64u_enc(y), 72 | } 73 | 74 | if prv is not None: 75 | d = lenc(prv, (point.bits() + 7) // 8) 76 | jwk["d"] = b64u_enc(d) 77 | 78 | return jwk 79 | 80 | 81 | def decode(jwk): 82 | """Decodes a JSON Web Token to a Point and a private key. 83 | 84 | >>> jwk = { "kty": "EC", "crv": "P-256"} 85 | >>> jwk["x"] = "gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0" 86 | >>> jwk["y"] = "SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps" 87 | >>> pub, prv = decode(jwk) 88 | >>> prv is None 89 | True 90 | >>> pub 91 | secp256r1(808D060082C176EED3E776A4AC598CC8672C1779F974EECC9B03411CA5B9495D, 48B5BFC527DFCE53D6AC7115237D031CCFF87A0570B77350A9E503EE7305A69B) 92 | 93 | >>> jwk["d"] = "0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo" 94 | >>> pub, prv = decode(jwk) 95 | >>> prv == 95868137618030166809364817078804351319836184172769340930264256928620637034634 96 | True 97 | >>> pub 98 | secp256r1(808D060082C176EED3E776A4AC598CC8672C1779F974EECC9B03411CA5B9495D, 48B5BFC527DFCE53D6AC7115237D031CCFF87A0570B77350A9E503EE7305A69B) 99 | """ 100 | assert jwk["kty"] == "EC" 101 | 102 | crv = find(jwk["crv"]) 103 | x = ldec(b64u_dec(jwk["x"])) 104 | y = ldec(b64u_dec(jwk["y"])) 105 | d = jwk.get("d", None) 106 | 107 | if d is not None: 108 | d = ldec(b64u_dec(d)) 109 | 110 | return (crv(x, y), d) 111 | -------------------------------------------------------------------------------- /rubenesque/curves/base.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # pylint: disable=line-too-long 3 | # 4 | # Copyright (c) 2015, Red Hat, Inc. 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright notice, this 11 | # list of conditions and the following disclaimer. 12 | # 13 | # * Redistributions in binary form must reproduce the above copyright notice, 14 | # this list of conditions and the following disclaimer in the documentation 15 | # and/or other materials provided with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | import abc 29 | import os 30 | 31 | from ..math import inv 32 | from ..lcodec import ldec 33 | 34 | 35 | if not hasattr(abc, "ABC"): 36 | abc.ABC = abc.ABCMeta(str('ABC'), (), {}) 37 | 38 | 39 | class Point(abc.ABC): 40 | generator = None 41 | cofactor = 1 42 | aliases = () 43 | order = 0 44 | 45 | @classmethod 46 | @abc.abstractmethod 47 | def bits(cls): 48 | "The bit length of points on the curve" 49 | 50 | @classmethod 51 | @abc.abstractmethod 52 | def generator(cls): 53 | "The generator (a.k.a. base point)" 54 | 55 | @classmethod 56 | def create(cls, primary, secondary): 57 | "Creates a point using the primary and secondary coordinates" 58 | return cls(primary, secondary) 59 | 60 | @classmethod 61 | @abc.abstractmethod 62 | def recover(cls, primary, bit): 63 | "Recovers a point using the primary coordinate and a secondary bit" 64 | 65 | @classmethod 66 | def private_key(cls, min=1): 67 | "Generates a random integer suitable for use as a private key" 68 | bytes = (cls.order.bit_length() + 7) // 8 69 | mask = 2 ** cls.order.bit_length() - 1 70 | 71 | r = 0 72 | while r < min or r >= cls.order: 73 | r = ldec(os.urandom(bytes)) & mask 74 | 75 | return r 76 | 77 | @abc.abstractmethod 78 | def __init__(self, x=None, y=None, *args, **kwargs): 79 | "Creates a new point with the specified coordinates" 80 | 81 | @abc.abstractproperty 82 | def is_identity(self): 83 | "Whether or not this point represents the neutral element" 84 | 85 | @abc.abstractproperty 86 | def is_valid(self): 87 | "Whether or not this point represents the neutral element" 88 | 89 | @abc.abstractproperty 90 | def x(self): 91 | "The x coordinate" 92 | 93 | @abc.abstractproperty 94 | def y(self): 95 | "The y coordinate" 96 | 97 | @property 98 | def primary(self): 99 | "The primary coordinate" 100 | return self.x 101 | 102 | @property 103 | def secondary(self): 104 | "The secondary coordinate" 105 | return self.y 106 | 107 | @abc.abstractmethod 108 | def __add__(self, other): 109 | "Adds two points" 110 | 111 | @abc.abstractmethod 112 | def __eq__(self, other): 113 | "Tests equality between two points" 114 | 115 | @abc.abstractmethod 116 | def __neg__(self): 117 | "Invert a point" 118 | 119 | def __mul__(self, multiplier): 120 | if multiplier == 0: 121 | return self.__class__() 122 | 123 | q = self.__class__() 124 | p = self 125 | for o in range(multiplier.bit_length(), -1, -1): 126 | if multiplier & (1 << o): 127 | q += p 128 | p += p 129 | else: 130 | p += q 131 | q += q 132 | 133 | return q 134 | 135 | def __div__(self, divisor): 136 | return self * inv(divisor, self.order) 137 | __floordiv__ = __div__ 138 | __truediv__ = __div__ 139 | 140 | def __sub__(self, other): 141 | return self + -other 142 | 143 | def __repr__(self): 144 | if self.is_identity: 145 | return "%s(∞)" % self.__class__.__name__ 146 | 147 | l = (self.__class__.bits() + 7) // 8 * 2 148 | t = "%s(%%0%dX, %%0%dX)" % (self.__class__.__name__, l, l) 149 | return t % (self.x, self.y) 150 | -------------------------------------------------------------------------------- /rubenesque/curves/weierstrass.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2015, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | """ 28 | This module implements Weierstrass curves following the methods given by 29 | RFC 6090. In particular, we use homogeneous coordinates to avoid unnecessary 30 | inversions. 31 | """ 32 | 33 | import abc 34 | 35 | from ..math import sqrt, inv 36 | from .prime import Point 37 | 38 | 39 | class Point(Point): 40 | a = 0 41 | b = 0 42 | 43 | @classmethod 44 | def __weierstrass(cls, x): 45 | return (pow(x, 3, cls.prime) 46 | + cls.a * x % cls.prime 47 | + cls.b) % cls.prime 48 | 49 | @classmethod 50 | def recover(cls, primary, bit): 51 | s = sqrt(cls.__weierstrass(primary), cls.prime) 52 | assert s != 0 53 | 54 | secondary = s if s & 1 == bit else ((cls.prime - s) % cls.prime) 55 | return cls(primary, secondary) 56 | 57 | def __init__(self, x=None, y=None, z=1): 58 | assert (x is None and y is None) \ 59 | or (x is not None and y is not None) 60 | 61 | self.__x = 0 if x is None else x 62 | self.__y = 1 if y is None else y 63 | self.__z = 0 if y is None else z 64 | 65 | def __normalize(self): 66 | if self.__z in (0, 1): 67 | return 68 | 69 | p = self.__class__.prime 70 | self.__x = self.__x * inv(self.__z, p) % p 71 | self.__y = self.__y * inv(self.__z, p) % p 72 | self.__z = 1 73 | 74 | @property 75 | def is_identity(self): 76 | return self.__z == 0 77 | 78 | @property 79 | def is_valid(self): 80 | if self.is_identity: 81 | return False 82 | 83 | l = self.__class__.__weierstrass(self.x) 84 | r = self.y * self.y % self.__class__.prime 85 | return l == r 86 | 87 | @property 88 | def x(self): 89 | self.__normalize() 90 | return self.__x if self.__z == 1 else None 91 | 92 | @property 93 | def y(self): 94 | self.__normalize() 95 | return self.__y if self.__z == 1 else None 96 | 97 | def __add__(self, other): 98 | assert isinstance(other, self.__class__) 99 | 100 | p = self.__class__.prime 101 | 102 | if self.is_identity or other.is_identity: 103 | return self if other.is_identity else other 104 | 105 | u = (other.__y * self.__z % p - self.__y * other.__z % p) % p 106 | v = (other.__x * self.__z % p - self.__x * other.__z % p) % p 107 | 108 | if u != 0 and v == 0: 109 | X3 = 0 110 | Y3 = 1 111 | Z3 = 0 112 | 113 | elif u == 0 and v == 0: 114 | XX = self.__x * self.__x % p 115 | YY = self.__y * self.__y % p 116 | ZZ = self.__z * self.__z % p 117 | YZ = self.__y * self.__z % p 118 | YYZ = YY * self.__z % p 119 | 120 | w = (3 * XX % p + self.__class__.a * ZZ % p) % p 121 | ww = w * w % p 122 | www = w * ww % p 123 | 124 | X3 = (ww - 8 * self.__x % p * YYZ % p) % p 125 | X3 = 2 * YZ % p * X3 % p 126 | Y3 = (3 * w % p * self.__x % p - 2 * YYZ % p) % p 127 | Y3 = (4 * YYZ % p * Y3 % p - www) % p 128 | Z3 = 8 * YYZ % p * ZZ % p * self.__y % p 129 | 130 | else: 131 | uu = u * u % p 132 | uuu = u * uu % p 133 | vv = v * v % p 134 | vvv = v * vv % p 135 | 136 | X1vv = self.__x * vv % p 137 | 138 | X3 = (self.__z * uu % p - 2 * X1vv % p) % p 139 | X3 = v * ((other.__z * X3 % p - vvv) % p) % p 140 | Y3 = 3 * u % p * X1vv % p 141 | Y3 = ((Y3 - self.__y * vvv % p) % p - self.__z * uuu % p) % p 142 | Y3 = (other.__z * Y3 % p + u * vvv % p) % p 143 | Z3 = vvv * self.__z % p * other.__z % p 144 | 145 | return self.__class__(X3, Y3, Z3) 146 | 147 | def __eq__(self, other): 148 | p = self.__class__.prime 149 | x = other.__x * self.__z % p == self.__x * other.__z % p 150 | y = other.__y * self.__z % p == self.__y * other.__z % p 151 | return x and y 152 | -------------------------------------------------------------------------------- /rubenesque/curves/edwards.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2015, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | import abc 28 | 29 | from ..math import sqrt, inv 30 | from .prime import Point 31 | 32 | 33 | class Point(Point): 34 | a = 1 35 | d = 1 36 | 37 | @classmethod 38 | def create(cls, primary, secondary): 39 | return cls(secondary, primary) 40 | 41 | @classmethod 42 | def recover(cls, primary, bit): 43 | pp = primary * primary % cls.prime 44 | a = (pp - 1) % cls.prime 45 | b = (cls.d * pp % cls.prime - cls.a) % cls.prime 46 | s = sqrt(a * inv(b, cls.prime), cls.prime) 47 | assert s != 0 48 | 49 | secondary = s if s & 1 == bit else ((cls.prime - s) % cls.prime) 50 | return cls(secondary, primary) 51 | 52 | def __init__(self, x=None, y=None, z=1, t=None): 53 | assert (y is None and x is None) \ 54 | or (y is not None and x is not None) 55 | 56 | self.__x = 0 if x is None else x 57 | self.__y = 1 if y is None else y 58 | self.__z = z 59 | self.__t = t if t is not None else ( 60 | self.__x * self.__y % self.__class__.prime 61 | * self.__z % self.__class__.prime 62 | ) 63 | 64 | def __normalize(self): 65 | if self.__z in (0, 1): 66 | return 67 | 68 | p = self.__class__.prime 69 | self.__x = self.__x * inv(self.__z, p) % p 70 | self.__y = self.__y * inv(self.__z, p) % p 71 | self.__z = 1 72 | self.__t = self.__x * self.__y 73 | 74 | @property 75 | def is_identity(self): 76 | return self.__z == 0 or self.__x == 0 and self.__y == self.__z 77 | 78 | @property 79 | def is_valid(self): 80 | if self.is_identity: 81 | return False 82 | 83 | p = self.__class__.prime 84 | 85 | yy = self.y * self.y % p 86 | xx = self.x * self.x % p 87 | 88 | l = (self.__class__.a * xx % p + yy) % p 89 | r = (1 + self.__class__.d * xx % p * yy % p) % p 90 | 91 | return l == r 92 | 93 | @property 94 | def x(self): 95 | self.__normalize() 96 | return self.__x if self.__z == 1 else None 97 | 98 | @property 99 | def y(self): 100 | self.__normalize() 101 | return self.__y if self.__z == 1 else None 102 | 103 | @property 104 | def primary(self): 105 | return self.y 106 | 107 | @property 108 | def secondary(self): 109 | return self.x 110 | 111 | def __eq__(self, other): 112 | p = self.__class__.prime 113 | x = other.__x * self.__z % p == self.__x * other.__z % p 114 | y = other.__y * self.__z % p == self.__y * other.__z % p 115 | return x and y 116 | 117 | def __add__(self, other): 118 | assert isinstance(other, self.__class__) 119 | 120 | if self.is_identity or other.is_identity: 121 | return self if other.is_identity else other 122 | 123 | p = self.__class__.prime 124 | 125 | if self == other: 126 | # https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd 127 | A = self.__x * self.__x % p 128 | B = self.__y * self.__y % p 129 | C = 2 * self.__z % p * self.__z % p 130 | D = self.__class__.a * A % p 131 | E = ((pow((self.__x + self.__y) % p, 2, p) - A) % p - B) % p 132 | G = (D + B) % p 133 | F = (G - C) % p 134 | H = (D - B) % p 135 | else: 136 | # https://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#addition-add-2008-hwcd-2 137 | A = self.__x * other.__x % p 138 | B = self.__y * other.__y % p 139 | C = self.__z * other.__t % p 140 | D = self.__t * other.__z % p 141 | E = (D + C) % p 142 | F = ((((self.__x - self.__y) % p) * ((other.__x + other.__y) % p) % p + B) % p - A) % p 143 | G = (B + self.__class__.a * A % p) % p 144 | H = (D - C) % p 145 | 146 | X3 = E * F % p 147 | Y3 = G * H % p 148 | T3 = E * H % p 149 | Z3 = F * G % p 150 | return self.__class__(X3, Y3, Z3, T3) 151 | -------------------------------------------------------------------------------- /rubenesque/curves/cfrg.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # pylint: disable=line-too-long 3 | # 4 | # Copyright (c) 2015, Red Hat, Inc. 5 | # All rights reserved. 6 | # 7 | # Redistribution and use in source and binary forms, with or without 8 | # modification, are permitted provided that the following conditions are met: 9 | # 10 | # * Redistributions of source code must retain the above copyright notice, this 11 | # list of conditions and the following disclaimer. 12 | # 13 | # * Redistributions in binary form must reproduce the above copyright notice, 14 | # this list of conditions and the following disclaimer in the documentation 15 | # and/or other materials provided with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 20 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 21 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 23 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 24 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | 28 | from .edwards import Point 29 | 30 | 31 | class edwards25519(Point): 32 | """ 33 | >>> from . import find 34 | >>> cls = find("edwards25519") 35 | >>> find("ed25519") 36 | 37 | 38 | Test basic math: 39 | >>> cls().is_identity 40 | True 41 | >>> (-cls()).is_identity 42 | True 43 | >>> cls().is_valid 44 | False 45 | >>> (cls.generator() * 0).is_identity 46 | True 47 | >>> cls.generator() * 1 == cls.generator() 48 | True 49 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 50 | True 51 | >>> cls.generator() + cls.generator() == cls.generator() * 2 52 | True 53 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 54 | True 55 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 56 | True 57 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 58 | True 59 | 60 | 61 | >>> edwards25519.generator() 62 | edwards25519(216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A, 6666666666666666666666666666666666666666666666666666666666666658) 63 | 64 | >>> edwards25519.generator() * 0x449a44ba44226a50185afcc10a4c1462dd5e46824b15163b9d7c52f06be346a0 65 | edwards25519(1229EF6103B0AB43012C2DA513CC07CDB72A5DD0C37B3813D781B195A1BA7166, 1CC8FD645E6144B78000B7AEAE4A382FBAB72A08F9B6FB8A92C1BF3D7A032DBB) 66 | 67 | >>> edwards25519.generator() + edwards25519.generator() * 0x449a44ba44226a50185afcc10a4c1462dd5e46824b15163b9d7c52f06be346a0 68 | edwards25519(3318E20DB2054DE4687BADC30CC6BDC7406069B0DC3CD9F65E2ECBA364E4A077, 4641AE96F530129FA2107E9B79ED5893AD03C4EDDBFDB444ABC76FF629C6A973) 69 | """ 70 | 71 | a = -1 72 | d = 0x52036cee2b6ffe738cc740797779e89800700a4d4141d8ab75eb4dca135978a3 73 | order = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed 74 | prime = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed 75 | aliases = ("ed25519", ) 76 | cofactor = 8 77 | 78 | @classmethod 79 | def generator(cls): 80 | return cls( 81 | 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a, 82 | 0x6666666666666666666666666666666666666666666666666666666666666658 83 | ) 84 | 85 | 86 | class edwards448(Point): 87 | """ 88 | >>> from . import find 89 | >>> cls = find("edwards448") 90 | >>> find("ed448") 91 | 92 | 93 | Test basic math: 94 | >>> cls().is_identity 95 | True 96 | >>> (-cls()).is_identity 97 | True 98 | >>> cls().is_valid 99 | False 100 | >>> (cls.generator() * 0).is_identity 101 | True 102 | >>> cls.generator() * 1 == cls.generator() 103 | True 104 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 105 | True 106 | >>> cls.generator() + cls.generator() == cls.generator() * 2 107 | True 108 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 109 | True 110 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 111 | True 112 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 113 | True 114 | 115 | >>> edwards448.generator() 116 | edwards448(79A70B2B70400553AE7C9DF416C792C61128751AC92969240C25A07D728BDC93E21F7787ED6972249DE732F38496CD11698713093E9C04FC, 7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE) 117 | 118 | >>> edwards448.generator() * 0 119 | edwards448(∞) 120 | >>> edwards448.generator() * 1 121 | edwards448(79A70B2B70400553AE7C9DF416C792C61128751AC92969240C25A07D728BDC93E21F7787ED6972249DE732F38496CD11698713093E9C04FC, 7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE) 122 | >>> edwards448.generator() + edwards448.generator() * 0 123 | edwards448(79A70B2B70400553AE7C9DF416C792C61128751AC92969240C25A07D728BDC93E21F7787ED6972249DE732F38496CD11698713093E9C04FC, 7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE) 124 | 125 | TODO: More test vectors needed... 126 | """ 127 | 128 | d = 0xd78b4bdc7f0daf19f24f38c29373a2ccad46157242a50f37809b1da3412a12e79ccc9c81264cfe9ad080997058fb61c4243cc32dbaa156b9 129 | order = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3 130 | prime = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff 131 | aliases = ("ed448", ) 132 | cofactor = 4 133 | 134 | @classmethod 135 | def generator(cls): 136 | return cls( 137 | 0x79a70b2b70400553ae7c9df416c792c61128751ac92969240c25a07d728bdc93e21f7787ed6972249de732f38496cd11698713093e9c04fc, 138 | 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffff7ffffffffffffffffffffffffffffffffffffffffffffffffffffffe 139 | ) 140 | -------------------------------------------------------------------------------- /rubenesque/signatures/ecdsa.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2015, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | from ..math import inv 28 | from ..lcodec import ldec 29 | from ..curves import weierstrass 30 | 31 | 32 | def sign(cls, prv, hsh, testk=None): 33 | """ 34 | Test values are from RFC 4754 35 | 36 | >>> from rubenesque.curves.sec import secp256r1, secp384r1, secp521r1 37 | >>> from hashlib import sha256, sha384, sha512 38 | 39 | 40 | >>> h = sha256(b'abc').digest() 41 | >>> w = 0xDC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F 42 | >>> k = 0x9E56F509196784D963D1C0A401510EE7ADA3DCC5DEE04B154BF61AF1D5A6DECE 43 | >>> a, b = sign(secp256r1, w, h, k) 44 | >>> a == 91891732277435504623317252591401134119269651937436268244947074415855034340732 45 | True 46 | >>> b == 61052045556022437972545958544857058931480303283285058697354104476845267944213 47 | True 48 | 49 | 50 | >>> h = sha384(b'abc').digest() 51 | >>> w = 0x0BEB646634BA87735D77AE4809A0EBEA865535DE4C1E1DCB692E84708E81A5AF62E528C38B2A81B35309668D73524D9F 52 | >>> k = 0xB4B74E44D71A13D568003D7489908D564C7761E229C58CBFA18950096EB7463B854D7FA992F934D927376285E63414FA 53 | >>> a, b = sign(secp384r1, w, h, k) 54 | >>> a == 38633327193540170134142746196640777001610571660897807027400558946617577572929271756342333545057439198888737488858803 55 | True 56 | >>> b == 27456607455725262996851015520164417800230371254197279025501184027627966373822115908168123099250660377810276830841759 57 | True 58 | 59 | 60 | >>> h = sha512(b'abc').digest() 61 | >>> w = 0x0065FDA3409451DCAB0A0EAD45495112A3D813C17BFD34BDF8C1209D7DF5849120597779060A7FF9D704ADF78B570FFAD6F062E95C7E0C5D5481C5B153B48B375FA1 62 | >>> k = 0x00C1C2B305419F5A41344D7E4359933D734096F556197A9B244342B8B62F46F9373778F9DE6B6497B1EF825FF24F42F9B4A4BD7382CFC3378A540B1B7F0C1B956C2F 63 | >>> a, b = sign(secp521r1, w, h, k) 64 | >>> a == 4571916881931522509684395052881421199225740839880572005887744882418558877708584528579508021653311389814949506911518722326141413326277238174509287981280731729 65 | True 66 | >>> b == 5028224013397087880963813951950174050813517229556618035427576214486083823861825258909909585187151444995522339222016536225541889721370155815222553876665673312 67 | True 68 | """ 69 | assert issubclass(cls, weierstrass.Point) 70 | assert prv >= 1 and prv < cls.order 71 | 72 | z = ldec(hsh) & (2 ** cls.bits() - 1) 73 | while True: 74 | k = cls.private_key() if testk is None else testk 75 | r = (cls.generator() * k).primary % cls.order 76 | s = inv(k, cls.order) * (z + r * prv % cls.order) % cls.order 77 | if r != 0 and s != 0: 78 | return (r, s) 79 | 80 | 81 | def verify(pub, hsh, r, s): 82 | """ 83 | Test values are from RFC 4754 84 | 85 | >>> from rubenesque.curves.sec import secp256r1, secp384r1, secp521r1 86 | >>> from hashlib import sha256, sha384, sha512 87 | 88 | 89 | >>> h = sha256(b'abc').digest() 90 | >>> w = 0xDC51D3866A15BACDE33D96F992FCA99DA7E6EF0934E7097559C27F1614C88A7F 91 | >>> r = 0xCB28E0999B9C7715FD0A80D8E47A77079716CBBF917DD72E97566EA1C066957C 92 | >>> s = 0x86FA3BB4E26CAD5BF90B7F81899256CE7594BB1EA0C89212748BFF3B3D5B0315 93 | >>> verify(secp256r1.generator() * w, h, r, s) 94 | True 95 | >>> verify(secp256r1.generator() * secp256r1.order, h, r, s) 96 | False 97 | >>> verify(secp256r1.generator() * w, h, r, 0) 98 | False 99 | >>> verify(secp256r1.generator() * w, h, 0, s) 100 | False 101 | 102 | 103 | >>> h = sha384(b'abc').digest() 104 | >>> w = 0x0BEB646634BA87735D77AE4809A0EBEA865535DE4C1E1DCB692E84708E81A5AF62E528C38B2A81B35309668D73524D9F 105 | >>> r = 0xFB017B914E29149432D8BAC29A514640B46F53DDAB2C69948084E2930F1C8F7E08E07C9C63F2D21A07DCB56A6AF56EB3 106 | >>> s = 0xB263A1305E057F984D38726A1B46874109F417BCA112674C528262A40A629AF1CBB9F516CE0FA7D2FF630863A00E8B9F 107 | >>> verify(secp384r1.generator() * w, h, r, s) 108 | True 109 | >>> verify(secp384r1.generator() * secp384r1.order, h, r, s) 110 | False 111 | >>> verify(secp384r1.generator() * w, h, r, 0) 112 | False 113 | >>> verify(secp384r1.generator() * w, h, 0, s) 114 | False 115 | 116 | 117 | >>> h = sha512(b'abc').digest() 118 | >>> w = 0x0065FDA3409451DCAB0A0EAD45495112A3D813C17BFD34BDF8C1209D7DF5849120597779060A7FF9D704ADF78B570FFAD6F062E95C7E0C5D5481C5B153B48B375FA1 119 | >>> r = 0x0154FD3836AF92D0DCA57DD5341D3053988534FDE8318FC6AAAAB68E2E6F4339B19F2F281A7E0B22C269D93CF8794A9278880ED7DBB8D9362CAEACEE544320552251 120 | >>> s = 0x017705A7030290D1CEB605A9A1BB03FF9CDD521E87A696EC926C8C10C8362DF4975367101F67D1CF9BCCBF2F3D239534FA509E70AAC851AE01AAC68D62F866472660 121 | >>> verify(secp521r1.generator() * w, h, r, s) 122 | True 123 | >>> verify(secp521r1.generator() * secp521r1.order, h, r, s) 124 | False 125 | >>> verify(secp521r1.generator() * w, h, r, 0) 126 | False 127 | >>> verify(secp521r1.generator() * w, h, 0, s) 128 | False 129 | """ 130 | if not isinstance(pub, weierstrass.Point): 131 | return False 132 | 133 | if not pub.is_valid: 134 | return False 135 | 136 | if not (pub * pub.order).is_identity: 137 | return False 138 | 139 | if r < 1 or r >= pub.order: 140 | return False 141 | 142 | if s < 1 or s >= pub.order: 143 | return False 144 | 145 | z = ldec(hsh) & (2 ** pub.bits() - 1) 146 | w = inv(s, pub.order) 147 | u1 = z * w % pub.order 148 | u2 = r * w % pub.order 149 | p = pub.generator() * u1 + pub * u2 150 | 151 | return r == p.primary 152 | -------------------------------------------------------------------------------- /rubenesque/codecs/sec.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2015, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | from ..lcodec import lenc, ldec 28 | 29 | 30 | def encode(point, compressed=True): 31 | """ 32 | >>> from ..curves.mdc import MDC201601 33 | >>> from ..curves.sec import secp224r1, secp521r1 34 | >>> from ..curves.cfrg import edwards25519, edwards448 35 | 36 | >>> encode(MDC201601.generator()) 37 | b'\\x03\\xcag4\\xe1\\xb5\\x9c\\x0b\\x03Y\\x81M\\xcfec\\xdaB\\x1d\\xa8\\xbc=\\x81\\xa9::~s\\xc3U\\xbd(d\\xb5' 38 | 39 | >>> encode(MDC201601.generator(), False) 40 | b"\\x04\\xcag4\\xe1\\xb5\\x9c\\x0b\\x03Y\\x81M\\xcfec\\xdaB\\x1d\\xa8\\xbc=\\x81\\xa9::~s\\xc3U\\xbd(d\\xb5\\xb6\\x81\\x88j\\x7f\\x90;\\x83\\xd8[B\\x1e\\x03\\xcb\\xcfcP\\xd7*\\xbb\\x8d'\\x13\\xe2#,%\\xbf\\xeeh6;" 41 | 42 | >>> encode(secp224r1.generator()) 43 | b'\\x02\\xb7\\x0e\\x0c\\xbdk\\xb4\\xbf\\x7f2\\x13\\x90\\xb9J\\x03\\xc1\\xd3V\\xc2\\x11"42\\x80\\xd6\\x11\\\\\\x1d!' 44 | 45 | >>> encode(secp224r1.generator(), False) 46 | b'\\x04\\xb7\\x0e\\x0c\\xbdk\\xb4\\xbf\\x7f2\\x13\\x90\\xb9J\\x03\\xc1\\xd3V\\xc2\\x11"42\\x80\\xd6\\x11\\\\\\x1d!\\xbd7c\\x88\\xb5\\xf7#\\xfbL"\\xdf\\xe6\\xcdCu\\xa0Z\\x07GdD\\xd5\\x81\\x99\\x85\\x00~4' 47 | 48 | >>> encode(secp521r1.generator()) 49 | b"\\x02\\x00\\xc6\\x85\\x8e\\x06\\xb7\\x04\\x04\\xe9\\xcd\\x9e>\\xcbf#\\x95\\xb4B\\x9cd\\x819\\x05?\\xb5!\\xf8(\\xaf`kM=\\xba\\xa1K^w\\xef\\xe7Y(\\xfe\\x1d\\xc1'\\xa2\\xff\\xa8\\xde3H\\xb3\\xc1\\x85jB\\x9b\\xf9~~1\\xc2\\xe5\\xbdf" 50 | 51 | >>> encode(secp521r1.generator(), False) 52 | b"\\x04\\x00\\xc6\\x85\\x8e\\x06\\xb7\\x04\\x04\\xe9\\xcd\\x9e>\\xcbf#\\x95\\xb4B\\x9cd\\x819\\x05?\\xb5!\\xf8(\\xaf`kM=\\xba\\xa1K^w\\xef\\xe7Y(\\xfe\\x1d\\xc1'\\xa2\\xff\\xa8\\xde3H\\xb3\\xc1\\x85jB\\x9b\\xf9~~1\\xc2\\xe5\\xbdf\\x01\\x189)jx\\x9a;\\xc0\\x04\\\\\\x8a_\\xb4,}\\x1b\\xd9\\x98\\xf5DIW\\x9bDh\\x17\\xaf\\xbd\\x17'>f,\\x97\\xeer\\x99^\\xf4&@\\xc5P\\xb9\\x01?\\xad\\x07a5>> encode(edwards25519.generator()) 55 | b'\\x02fffffffffffffffffffffffffffffffX' 56 | 57 | >>> encode(edwards25519.generator(), False) 58 | b'\\x04fffffffffffffffffffffffffffffffX!i6\\xd3\\xcdnS\\xfe\\xc0\\xa4\\xe21\\xfd\\xd6\\xdc\\\\i,\\xc7`\\x95%\\xa7\\xb2\\xc9V-`\\x8f%\\xd5\\x1a' 59 | 60 | >>> encode(edwards448.generator()) 61 | b'\\x02\\x7f\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x7f\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe' 62 | 63 | >>> encode(edwards448.generator(), False) 64 | b'\\x04\\x7f\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x7f\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfey\\xa7\\x0b+p@\\x05S\\xae|\\x9d\\xf4\\x16\\xc7\\x92\\xc6\\x11(u\\x1a\\xc9)i$\\x0c%\\xa0}r\\x8b\\xdc\\x93\\xe2\\x1fw\\x87\\xedir$\\x9d\\xe72\\xf3\\x84\\x96\\xcd\\x11i\\x87\\x13\\t>\\x9c\\x04\\xfc' 65 | """ 66 | assert not point.is_identity 67 | 68 | l = (point.bits() + 7) // 8 69 | p = lenc(point.primary, l) 70 | 71 | if compressed: 72 | return lenc(point.secondary & 1 | 2, 1) + p 73 | else: 74 | return b'\x04' + p + lenc(point.secondary, l) 75 | 76 | 77 | def decode(cls, bytes): 78 | """ 79 | >>> from ..curves.mdc import MDC201601 80 | >>> from ..curves.sec import secp224r1, secp521r1 81 | >>> from ..curves.cfrg import edwards25519, edwards448 82 | 83 | >>> decode(MDC201601, encode(MDC201601.generator())) 84 | MDC201601(B681886A7F903B83D85B421E03CBCF6350D72ABB8D2713E2232C25BFEE68363B, CA6734E1B59C0B0359814DCF6563DA421DA8BC3D81A93A3A7E73C355BD2864B5) 85 | 86 | >>> decode(MDC201601, encode(MDC201601.generator(), False)) 87 | MDC201601(B681886A7F903B83D85B421E03CBCF6350D72ABB8D2713E2232C25BFEE68363B, CA6734E1B59C0B0359814DCF6563DA421DA8BC3D81A93A3A7E73C355BD2864B5) 88 | 89 | >>> decode(secp224r1, encode(secp224r1.generator())) 90 | secp224r1(B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21, BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34) 91 | 92 | >>> decode(secp224r1, encode(secp224r1.generator(), False)) 93 | secp224r1(B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21, BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34) 94 | 95 | >>> decode(secp521r1, encode(secp521r1.generator())) 96 | secp521r1(00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66, 011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650) 97 | 98 | >>> decode(secp521r1, encode(secp521r1.generator(), False)) 99 | secp521r1(00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66, 011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650) 100 | 101 | >>> decode(edwards25519, encode(edwards25519.generator())) 102 | edwards25519(216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A, 6666666666666666666666666666666666666666666666666666666666666658) 103 | 104 | >>> decode(edwards25519, encode(edwards25519.generator(), False)) 105 | edwards25519(216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A, 6666666666666666666666666666666666666666666666666666666666666658) 106 | 107 | >>> decode(edwards448, encode(edwards448.generator())) 108 | edwards448(79A70B2B70400553AE7C9DF416C792C61128751AC92969240C25A07D728BDC93E21F7787ED6972249DE732F38496CD11698713093E9C04FC, 7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE) 109 | 110 | >>> decode(edwards448, encode(edwards448.generator(), False)) 111 | edwards448(79A70B2B70400553AE7C9DF416C792C61128751AC92969240C25A07D728BDC93E21F7787ED6972249DE732F38496CD11698713093E9C04FC, 7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE) 112 | """ 113 | assert bytes.startswith((b'\x02', b'\x03', b'\x04')) 114 | 115 | l = (cls.bits() + 7) // 8 116 | z = bytes[0] if isinstance(bytes[0], int) else ord(bytes[0]) 117 | p = ldec(bytes[1:l + 1]) 118 | if z == 4: 119 | s = ldec(bytes[l + 1:2 * l + 1]) 120 | point = cls.create(p, s) 121 | else: 122 | point = cls.recover(p, z & 1) 123 | 124 | assert point.is_valid 125 | return point 126 | -------------------------------------------------------------------------------- /rubenesque/curves/sec.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2015, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | from .weierstrass import Point 28 | 29 | 30 | class secp192r1(Point): 31 | """ 32 | >>> from . import find 33 | >>> cls = find("secp192r1") 34 | >>> find("1.2.840.10045.3.1.1") 35 | 36 | 37 | Test basic math: 38 | >>> cls().is_identity 39 | True 40 | >>> (-cls()).is_identity 41 | True 42 | >>> cls().is_valid 43 | False 44 | >>> (cls.generator() * 0).is_identity 45 | True 46 | >>> cls.generator() * 1 == cls.generator() 47 | True 48 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 49 | True 50 | >>> cls.generator() + cls.generator() == cls.generator() * 2 51 | True 52 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 53 | True 54 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 55 | True 56 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 57 | True 58 | 59 | >>> a = secp192r1.generator() * 0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D2282F 60 | >>> a 61 | secp192r1(DAFEBF5828783F2AD35534631588A3F629A70FB16982A888, 229425F266C25F05B94D8443EBE4796FA6CCE505A3816C54) 62 | >>> b = secp192r1.generator() * 0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22830 63 | >>> b 64 | secp192r1(188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012, F8E6D46A003725879CEFEE1294DB32298C06885EE186B7EE) 65 | >>> c = a * 0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22830 66 | >>> c 67 | secp192r1(DAFEBF5828783F2AD35534631588A3F629A70FB16982A888, DD6BDA0D993DA0FA46B27BBC141B868F59331AFA5C7E93AB) 68 | >>> b * 0xFFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D2282F == c 69 | True 70 | """ 71 | 72 | a = 0xfffffffffffffffffffffffffffffffefffffffffffffffc 73 | b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 74 | order = 0xffffffffffffffffffffffff99def836146bc9b1b4d22831 75 | prime = 0xfffffffffffffffffffffffffffffffeffffffffffffffff 76 | aliases = ("1.2.840.10045.3.1.1", ) 77 | 78 | @classmethod 79 | def generator(cls): 80 | return cls( 81 | 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012, 82 | 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 83 | ) 84 | 85 | 86 | class secp224r1(Point): 87 | """ 88 | >>> from . import find 89 | >>> cls = find("secp224r1") 90 | >>> find("1.3.132.0.33") 91 | 92 | 93 | Test basic math: 94 | >>> cls().is_identity 95 | True 96 | >>> (-cls()).is_identity 97 | True 98 | >>> cls().is_valid 99 | False 100 | >>> (cls.generator() * 0).is_identity 101 | True 102 | >>> cls.generator() * 1 == cls.generator() 103 | True 104 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 105 | True 106 | >>> cls.generator() + cls.generator() == cls.generator() * 2 107 | True 108 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 109 | True 110 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 111 | True 112 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 113 | True 114 | 115 | >>> a = secp224r1.generator() * 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3B 116 | >>> a 117 | secp224r1(706A46DC76DCB76798E60E6D89474788D16DC18032D268FD1A704FA6, E3D4895843DA188FD58FB0567976D7B50359D6B78530C8F62D1B1746) 118 | >>> b = secp224r1.generator() * 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3C 119 | >>> b 120 | secp224r1(B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21, 42C89C774A08DC04B3DD201932BC8A5EA5F8B89BBB2A7E667AFF81CD) 121 | >>> c = b * 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3B 122 | >>> c 123 | secp224r1(706A46DC76DCB76798E60E6D89474788D16DC18032D268FD1A704FA6, 1C2B76A7BC25E7702A704FA986892849FCA629487ACF3709D2E4E8BB) 124 | >>> a * 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3C == c 125 | True 126 | """ 127 | 128 | a = 0xfffffffffffffffffffffffffffffffefffffffffffffffffffffffe 129 | b = 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4 130 | order = 0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d 131 | prime = 0xffffffffffffffffffffffffffffffff000000000000000000000001 132 | aliases = ("1.3.132.0.33", ) 133 | 134 | @classmethod 135 | def generator(cls): 136 | return cls( 137 | 0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21, 138 | 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34 139 | ) 140 | 141 | 142 | class secp256r1(Point): 143 | """ 144 | >>> from . import find 145 | >>> cls = find("secp256r1") 146 | >>> find("1.2.840.10045.3.1.7") 147 | 148 | >>> find("P256") 149 | 150 | >>> find("P-256") 151 | 152 | 153 | Test basic math: 154 | >>> cls().is_identity 155 | True 156 | >>> (-cls()).is_identity 157 | True 158 | >>> cls().is_valid 159 | False 160 | >>> (cls.generator() * 0).is_identity 161 | True 162 | >>> cls.generator() * 1 == cls.generator() 163 | True 164 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 165 | True 166 | >>> cls.generator() + cls.generator() == cls.generator() * 2 167 | True 168 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 169 | True 170 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 171 | True 172 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 173 | True 174 | 175 | >>> a = secp256r1.generator() * 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F 176 | >>> a 177 | secp256r1(7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978, F888AAEE24712FC0D6C26539608BCF244582521AC3167DD661FB4862DD878C2E) 178 | >>> b = secp256r1.generator() * 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632550 179 | >>> b 180 | secp256r1(6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296, B01CBD1C01E58065711814B583F061E9D431CCA994CEA1313449BF97C840AE0A) 181 | >>> c = a * 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632550 182 | >>> c 183 | secp256r1(7CF27B188D034F7E8A52380304B51AC3C08969E277F21B35A60B48FC47669978, 07775510DB8ED040293D9AC69F7430DBBA7DADE63CE982299E04B79D227873D1) 184 | >>> b * 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC63254F == c 185 | True 186 | """ 187 | 188 | a = 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc 189 | b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b 190 | order = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 191 | prime = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff 192 | aliases = ("1.2.840.10045.3.1.7", "P256", "P-256") 193 | 194 | @classmethod 195 | def generator(cls): 196 | return cls( 197 | 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296, 198 | 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 199 | ) 200 | 201 | 202 | class secp384r1(Point): 203 | """ 204 | >>> from . import find 205 | >>> cls = find("secp384r1") 206 | >>> find("1.3.132.0.34") 207 | 208 | >>> find("P384") 209 | 210 | >>> find("P-384") 211 | 212 | 213 | Test basic math: 214 | >>> cls().is_identity 215 | True 216 | >>> (-cls()).is_identity 217 | True 218 | >>> cls().is_valid 219 | False 220 | >>> (cls.generator() * 0).is_identity 221 | True 222 | >>> cls.generator() * 1 == cls.generator() 223 | True 224 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 225 | True 226 | >>> cls.generator() + cls.generator() == cls.generator() * 2 227 | True 228 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 229 | True 230 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 231 | True 232 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 233 | True 234 | 235 | >>> a = secp384r1.generator() * 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52971 236 | >>> a 237 | secp384r1(08D999057BA3D2D969260045C55B97F089025959A6F434D651D207D19FB96E9E4FE0E86EBE0E64F85B96A9C75295DF61, 717F0E05A4E4C312484017200292458B4D8A278A43933BC16FB1AFA0DA954BD9A002BC15B2C61DD29EAFE190F56BF17F) 238 | >>> b = secp384r1.generator() * 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52972 239 | >>> b 240 | secp384r1(AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7, C9E821B569D9D390A26167406D6D23D6070BE242D765EB831625CEEC4A0F473EF59F4E30E2817E6285BCE2846F15F1A0) 241 | >>> c = a * 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52972 242 | >>> c 243 | secp384r1(08D999057BA3D2D969260045C55B97F089025959A6F434D651D207D19FB96E9E4FE0E86EBE0E64F85B96A9C75295DF61, 8E80F1FA5B1B3CEDB7BFE8DFFD6DBA74B275D875BC6CC43E904E505F256AB4255FFD43E94D39E22D61501E700A940E80) 244 | >>> b * 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52971 == c 245 | True 246 | """ 247 | 248 | a = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc 249 | b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef 250 | order = 0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973 251 | prime = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff 252 | aliases = ("1.3.132.0.34", "P384", "P-384") 253 | 254 | @classmethod 255 | def generator(cls): 256 | return cls( 257 | 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7, 258 | 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f 259 | ) 260 | 261 | 262 | class secp521r1(Point): 263 | """ 264 | >>> from . import find 265 | >>> cls = find("secp521r1") 266 | >>> find("1.3.132.0.35") 267 | 268 | >>> find("P521") 269 | 270 | >>> find("P-521") 271 | 272 | 273 | Test basic math: 274 | >>> cls().is_identity 275 | True 276 | >>> (-cls()).is_identity 277 | True 278 | >>> cls().is_valid 279 | False 280 | >>> (cls.generator() * 0).is_identity 281 | True 282 | >>> cls.generator() * 1 == cls.generator() 283 | True 284 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 285 | True 286 | >>> cls.generator() + cls.generator() == cls.generator() * 2 287 | True 288 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 289 | True 290 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 291 | True 292 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 293 | True 294 | 295 | >>> a = secp521r1.generator() * 0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386407 296 | >>> a 297 | secp521r1(00433C219024277E7E682FCB288148C282747403279B1CCC06352C6E5505D769BE97B3B204DA6EF55507AA104A3A35C5AF41CF2FA364D60FD967F43E3933BA6D783D, 010B44733807924D98FF580C1311112C0F4A394AEF83B25688BF54DE5D66F93BD2444C1C882160DAE0946C6C805665CDB70B1503416A123F0B08E41CA9299E0BE4FD) 298 | >>> b = secp521r1.generator() * 0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386408 299 | >>> b 300 | secp521r1(00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66, 00E7C6D6958765C43FFBA375A04BD382E426670ABBB6A864BB97E85042E8D8C199D368118D66A10BD9BF3AAF46FEC052F89ECAC38F795D8D3DBF77416B89602E99AF) 301 | >>> c = a * 0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386408 302 | >>> c 303 | secp521r1(00433C219024277E7E682FCB288148C282747403279B1CCC06352C6E5505D769BE97B3B204DA6EF55507AA104A3A35C5AF41CF2FA364D60FD967F43E3933BA6D783D, 00F4BB8CC7F86DB26700A7F3ECEEEED3F0B5C6B5107C4DA97740AB21A29906C42DBBB3E377DE9F251F6B93937FA99A3248F4EAFCBE95EDC0F4F71BE356D661F41B02) 304 | >>> b * 0x1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386407 == c 305 | True 306 | """ 307 | 308 | a = 0x000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc 309 | b = 0x00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00 310 | order = 0x000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409 311 | prime = 0x000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff 312 | aliases = ("1.3.132.0.35", "P521", "P-521") 313 | 314 | @classmethod 315 | def generator(cls): 316 | return cls( 317 | 0x000000c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66, 318 | 0x0000011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650 319 | ) 320 | -------------------------------------------------------------------------------- /rubenesque/curves/brainpool.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=line-too-long 2 | # 3 | # Copyright (c) 2015, Red Hat, Inc. 4 | # All rights reserved. 5 | # 6 | # Redistribution and use in source and binary forms, with or without 7 | # modification, are permitted provided that the following conditions are met: 8 | # 9 | # * Redistributions of source code must retain the above copyright notice, this 10 | # list of conditions and the following disclaimer. 11 | # 12 | # * Redistributions in binary form must reproduce the above copyright notice, 13 | # this list of conditions and the following disclaimer in the documentation 14 | # and/or other materials provided with the distribution. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | 27 | from .weierstrass import Point 28 | 29 | 30 | class brainpoolP160r1(Point): 31 | """ 32 | >>> from . import find 33 | >>> cls = find("brainpoolP160r1") 34 | >>> find("1.3.36.3.3.2.8.1.1.1") 35 | 36 | 37 | Test basic math: 38 | >>> cls().is_identity 39 | True 40 | >>> (-cls()).is_identity 41 | True 42 | >>> cls().is_valid 43 | False 44 | >>> (cls.generator() * 0).is_identity 45 | True 46 | >>> cls.generator() * 1 == cls.generator() 47 | True 48 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 49 | True 50 | >>> cls.generator() + cls.generator() == cls.generator() * 2 51 | True 52 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 53 | True 54 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 55 | True 56 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 57 | True 58 | 59 | The following test values are from https://tools.ietf.org/html/draft-merkle-ikev2-ke-brainpool-00 60 | >>> a = brainpoolP160r1.generator() * 0x8BF7BC5CBE8AC8B34940C2C5652D6AE4EC9F53CE 61 | >>> a 62 | brainpoolP160r1(651DA24C7FF64DD863F8F650E53F07B8EC943C39, D1C68C656E44034D0DAD60A1589FD49594E7C2A4) 63 | >>> b = brainpoolP160r1.generator() * 0xB6F7160F0DE61CCEAAC528A32BD7AD942E8017B2 64 | >>> b 65 | brainpoolP160r1(DF9F259AEA6DFA1F28B16B8FEC52044CC1DFBA35, DF72AEA65A5E3EF69166DA161ABE00FC9C81C4D0) 66 | >>> a * 0xB6F7160F0DE61CCEAAC528A32BD7AD942E8017B2 67 | brainpoolP160r1(D78792AC4CBE3390DDD6557060066BC25579CA97, 3A3DAB50421585FB9DE9D87BB3BBBAFE3379A571) 68 | >>> b * 0x8BF7BC5CBE8AC8B34940C2C5652D6AE4EC9F53CE 69 | brainpoolP160r1(D78792AC4CBE3390DDD6557060066BC25579CA97, 3A3DAB50421585FB9DE9D87BB3BBBAFE3379A571) 70 | """ 71 | 72 | a = 0x340E7BE2A280EB74E2BE61BADA745D97E8F7C300 73 | b = 0x1E589A8595423412134FAA2DBDEC95C8D8675E58 74 | order = 0xE95E4A5F737059DC60DF5991D45029409E60FC09 75 | prime = 0xE95E4A5F737059DC60DFC7AD95B3D8139515620F 76 | aliases = ("1.3.36.3.3.2.8.1.1.1", ) 77 | 78 | @classmethod 79 | def generator(cls): 80 | return cls( 81 | 0xBED5AF16EA3F6A4F62938C4631EB5AF7BDBCDBC3, 82 | 0x1667CB477A1A8EC338F94741669C976316DA6321 83 | ) 84 | 85 | 86 | class brainpoolP192r1(Point): 87 | """ 88 | >>> from . import find 89 | >>> cls = find("brainpoolP192r1") 90 | >>> find("1.3.36.3.3.2.8.1.1.3") 91 | 92 | 93 | Test basic math: 94 | >>> cls().is_identity 95 | True 96 | >>> (-cls()).is_identity 97 | True 98 | >>> cls().is_valid 99 | False 100 | >>> (cls.generator() * 0).is_identity 101 | True 102 | >>> cls.generator() * 1 == cls.generator() 103 | True 104 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 105 | True 106 | >>> cls.generator() + cls.generator() == cls.generator() * 2 107 | True 108 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 109 | True 110 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 111 | True 112 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 113 | True 114 | 115 | The following test values are from https://tools.ietf.org/html/draft-merkle-ikev2-ke-brainpool-00 116 | >>> a = brainpoolP192r1.generator() * 0x4B32E699290E3F052A99C7F0CFFD1C5707898015C743FE2A 117 | >>> a 118 | brainpoolP192r1(31DB86048351629CFCA68541D65D909A0F1DC8E77AC440B2, 7FB2925E1FDC609CF2252F0469836BA4F216ADF8A812893D) 119 | >>> b = brainpoolP192r1.generator() * 0x6916B2A336B3305A256238EA2BAB549D9C893F47D07964B8 120 | >>> b 121 | brainpoolP192r1(15B795C67C5E47510116B0DE419C45835C209AE77D971536, 0D1ADD9881EF6115EDF5B5CEF854B42E435CD9260A5719FB) 122 | >>> a * 0x6916B2A336B3305A256238EA2BAB549D9C893F47D07964B8 123 | brainpoolP192r1(B946A6914877922A40F6D588D47FC7D44691C346FD384570, AAD4E9CBC5BB6C7C308A95F445287580A09EBC93624FB24E) 124 | >>> b * 0x4B32E699290E3F052A99C7F0CFFD1C5707898015C743FE2A 125 | brainpoolP192r1(B946A6914877922A40F6D588D47FC7D44691C346FD384570, AAD4E9CBC5BB6C7C308A95F445287580A09EBC93624FB24E) 126 | """ 127 | 128 | a = 0x6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF 129 | b = 0x469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9 130 | order = 0xC302F41D932A36CDA7A3462F9E9E916B5BE8F1029AC4ACC1 131 | prime = 0xC302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297 132 | aliases = ("1.3.36.3.3.2.8.1.1.3", ) 133 | 134 | @classmethod 135 | def generator(cls): 136 | return cls( 137 | 0xC0A0647EAAB6A48753B033C56CB0F0900A2F5C4853375FD6, 138 | 0x14B690866ABD5BB88B5F4828C1490002E6773FA2FA299B8F 139 | ) 140 | 141 | 142 | class brainpoolP224r1(Point): 143 | """ 144 | >>> from . import find 145 | >>> cls = find("brainpoolP224r1") 146 | >>> find("1.3.36.3.3.2.8.1.1.5") 147 | 148 | 149 | Test basic math: 150 | >>> cls().is_identity 151 | True 152 | >>> (-cls()).is_identity 153 | True 154 | >>> cls().is_valid 155 | False 156 | >>> (cls.generator() * 0).is_identity 157 | True 158 | >>> cls.generator() * 1 == cls.generator() 159 | True 160 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 161 | True 162 | >>> cls.generator() + cls.generator() == cls.generator() * 2 163 | True 164 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 165 | True 166 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 167 | True 168 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 169 | True 170 | 171 | The following test values are from https://tools.ietf.org/html/rfc6954 172 | >>> a = brainpoolP224r1.generator() * 0x39F155483CEE191FBECFE9C81D8AB1A03CDA6790E7184ACE44BCA161 173 | >>> a 174 | brainpoolP224r1(A9C21A569759DA95E0387041184261440327AFE33141CA04B82DC92E, 98A0F75FBBF61D8E58AE5511B2BCDBE8E549B31E37069A2825F590C1) 175 | >>> b = brainpoolP224r1.generator() * 0x6060552303899E2140715816C45B57D9B42204FB6A5BF5BEAC10DB00 176 | >>> b 177 | brainpoolP224r1(034A56C550FF88056144E6DD56070F54B0135976B5BF77827313F36B, 75165AD99347DC86CAAB1CBB579E198EAF88DC35F927B358AA683681) 178 | >>> a * 0x6060552303899E2140715816C45B57D9B42204FB6A5BF5BEAC10DB00 179 | brainpoolP224r1(1A4BFE705445120C8E3E026699054104510D119757B74D5FE2462C66, BB6802AC01F8B7E91B1A1ACFB9830A95C079CEC48E52805DFD7D2AFE) 180 | >>> b * 0x39F155483CEE191FBECFE9C81D8AB1A03CDA6790E7184ACE44BCA161 181 | brainpoolP224r1(1A4BFE705445120C8E3E026699054104510D119757B74D5FE2462C66, BB6802AC01F8B7E91B1A1ACFB9830A95C079CEC48E52805DFD7D2AFE) 182 | """ 183 | 184 | a = 0x68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43 185 | b = 0x2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B 186 | order = 0xD7C134AA264366862A18302575D0FB98D116BC4B6DDEBCA3A5A7939F 187 | prime = 0xD7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF 188 | aliases = ("1.3.36.3.3.2.8.1.1.5", ) 189 | 190 | @classmethod 191 | def generator(cls): 192 | return cls( 193 | 0x0D9029AD2C7E5CF4340823B2A87DC68C9E4CE3174C1E6EFDEE12C07D, 194 | 0x58AA56F772C0726F24C6B89E4ECDAC24354B9E99CAA3F6D3761402CD 195 | ) 196 | 197 | 198 | class brainpoolP256r1(Point): 199 | """ 200 | >>> from . import find 201 | >>> cls = find("brainpoolP256r1") 202 | >>> find("1.3.36.3.3.2.8.1.1.7") 203 | 204 | 205 | Test basic math: 206 | >>> cls().is_identity 207 | True 208 | >>> (-cls()).is_identity 209 | True 210 | >>> cls().is_valid 211 | False 212 | >>> (cls.generator() * 0).is_identity 213 | True 214 | >>> cls.generator() * 1 == cls.generator() 215 | True 216 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 217 | True 218 | >>> cls.generator() + cls.generator() == cls.generator() * 2 219 | True 220 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 221 | True 222 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 223 | True 224 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 225 | True 226 | 227 | The following test values are from https://tools.ietf.org/html/rfc6954 228 | >>> a = brainpoolP256r1.generator() * 0x81DB1EE100150FF2EA338D708271BE38300CB54241D79950F77B063039804F1D 229 | >>> a 230 | brainpoolP256r1(44106E913F92BC02A1705D9953A8414DB95E1AAA49E81D9E85F929A8E3100BE5, 8AB4846F11CACCB73CE49CBDD120F5A900A69FD32C272223F789EF10EB089BDC) 231 | >>> b = brainpoolP256r1.generator() * 0x55E40BC41E37E3E2AD25C3C6654511FFA8474A91A0032087593852D3E7D76BD3 232 | >>> b 233 | brainpoolP256r1(8D2D688C6CF93E1160AD04CC4429117DC2C41825E1E9FCA0ADDD34E6F1B39F7B, 990C57520812BE512641E47034832106BC7D3E8DD0E4C7F1136D7006547CEC6A) 234 | >>> a * 0x55E40BC41E37E3E2AD25C3C6654511FFA8474A91A0032087593852D3E7D76BD3 235 | brainpoolP256r1(89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A18BF2B, 49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E59632504DE) 236 | >>> b * 0x81DB1EE100150FF2EA338D708271BE38300CB54241D79950F77B063039804F1D 237 | brainpoolP256r1(89AFC39D41D3B327814B80940B042590F96556EC91E6AE7939BCE31F3A18BF2B, 49C27868F4ECA2179BFD7D59B1E3BF34C1DBDE61AE12931648F43E59632504DE) 238 | """ 239 | 240 | a = 0x7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9 241 | b = 0x26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6 242 | order = 0xA9FB57DBA1EEA9BC3E660A909D838D718C397AA3B561A6F7901E0E82974856A7 243 | prime = 0xA9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377 244 | aliases = ("1.3.36.3.3.2.8.1.1.7", ) 245 | 246 | @classmethod 247 | def generator(cls): 248 | return cls( 249 | 0x8BD2AEB9CB7E57CB2C4B482FFC81B7AFB9DE27E1E3BD23C23A4453BD9ACE3262, 250 | 0x547EF835C3DAC4FD97F8461A14611DC9C27745132DED8E545C1D54C72F046997 251 | ) 252 | 253 | 254 | class brainpoolP320r1(Point): 255 | """ 256 | >>> from . import find 257 | >>> cls = find("brainpoolP320r1") 258 | >>> find("1.3.36.3.3.2.8.1.1.9") 259 | 260 | 261 | Test basic math: 262 | >>> cls().is_identity 263 | True 264 | >>> (-cls()).is_identity 265 | True 266 | >>> cls().is_valid 267 | False 268 | >>> (cls.generator() * 0).is_identity 269 | True 270 | >>> cls.generator() * 1 == cls.generator() 271 | True 272 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 273 | True 274 | >>> cls.generator() + cls.generator() == cls.generator() * 2 275 | True 276 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 277 | True 278 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 279 | True 280 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 281 | True 282 | 283 | The following test values are from https://tools.ietf.org/html/draft-merkle-ikev2-ke-brainpool-00 284 | >>> a = brainpoolP320r1.generator() * 0x7CD9C454BA907F7617E262A7FD73764C4A3157C13F82279EF9F062BE5D49A8E390B66A4DCEDFA867 285 | >>> a 286 | brainpoolP320r1(BC43666C00E4B943FE1C785DD8AA842A42AB54B0B49819F960F77694193CD3AFA71B6B3C826C7734, 69E998892C0764468023C8E3A7B8F219A1446042BE175D4476B2FDFD85B22EAD2F29101A1242A578) 287 | >>> b = brainpoolP320r1.generator() * 0xB832A73DA5F671E80D87F09372544801F6812224B19A4BC1B37AA7DB0842E6DD3CA11DE0F802BFED 288 | >>> b 289 | brainpoolP320r1(B1246229429354D1D687BCA48BCCD6FC733B146DAC03642A0AD4B896F5D8BCBD2F4BCA16776E4526, A41683898F9A76EF36EA2DC7B74D419E55CF3664721890D6A2B2FB8CEB7C113167ED137A358EE37F) 290 | >>> a * 0xB832A73DA5F671E80D87F09372544801F6812224B19A4BC1B37AA7DB0842E6DD3CA11DE0F802BFED 291 | brainpoolP320r1(730314D906B2F21DC11BE05031B028D665696BEEC7139328CDF70C718BE5D208659BB96743A88067, C338B5B7A3FB62EDE9BAA9C06DF9BC36D4B5F0D35EFDF79249913E6DC4DB6DBC7BA9B74E59C840F1) 292 | >>> b * 0x7CD9C454BA907F7617E262A7FD73764C4A3157C13F82279EF9F062BE5D49A8E390B66A4DCEDFA867 293 | brainpoolP320r1(730314D906B2F21DC11BE05031B028D665696BEEC7139328CDF70C718BE5D208659BB96743A88067, C338B5B7A3FB62EDE9BAA9C06DF9BC36D4B5F0D35EFDF79249913E6DC4DB6DBC7BA9B74E59C840F1) 294 | """ 295 | 296 | a = 0x3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4 297 | b = 0x520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6 298 | order = 0xD35E472036BC4FB7E13C785ED201E065F98FCFA5B68F12A32D482EC7EE8658E98691555B44C59311 299 | prime = 0xD35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27 300 | aliases = ("1.3.36.3.3.2.8.1.1.9", ) 301 | 302 | @classmethod 303 | def generator(cls): 304 | return cls( 305 | 0x43BD7E9AFB53D8B85289BCC48EE5BFE6F20137D10A087EB6E7871E2A10A599C710AF8D0D39E20611, 306 | 0x14FDD05545EC1CC8AB4093247F77275E0743FFED117182EAA9C77877AAAC6AC7D35245D1692E8EE1 307 | ) 308 | 309 | 310 | class brainpoolP384r1(Point): 311 | """ 312 | >>> from . import find 313 | >>> cls = find("brainpoolP384r1") 314 | >>> find("1.3.36.3.3.2.8.1.1.11") 315 | 316 | 317 | Test basic math: 318 | >>> cls().is_identity 319 | True 320 | >>> (-cls()).is_identity 321 | True 322 | >>> cls().is_valid 323 | False 324 | >>> (cls.generator() * 0).is_identity 325 | True 326 | >>> cls.generator() * 1 == cls.generator() 327 | True 328 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 329 | True 330 | >>> cls.generator() + cls.generator() == cls.generator() * 2 331 | True 332 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 333 | True 334 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 335 | True 336 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 337 | True 338 | 339 | The following test values are from https://tools.ietf.org/html/rfc6954 340 | >>> a = brainpoolP384r1.generator() * 0x1E20F5E048A5886F1F157C74E91BDE2B98C8B52D58E5003D57053FC4B0BD65D6F15EB5D1EE1610DF870795143627D042 341 | >>> a 342 | brainpoolP384r1(68B665DD91C195800650CDD363C625F4E742E8134667B767B1B476793588F885AB698C852D4A6E77A252D6380FCAF068, 55BC91A39C9EC01DEE36017B7D673A931236D2F1F5C83942D049E3FA20607493E0D038FF2FD30C2AB67D15C85F7FAA59) 343 | >>> b = brainpoolP384r1.generator() * 0x032640BC6003C59260F7250C3DB58CE647F98E1260ACCE4ACDA3DD869F74E01F8BA5E0324309DB6A9831497ABAC96670 344 | >>> b 345 | brainpoolP384r1(4D44326F269A597A5B58BBA565DA5556ED7FD9A8A9EB76C25F46DB69D19DC8CE6AD18E404B15738B2086DF37E71D1EB4, 62D692136DE56CBE93BF5FA3188EF58BC8A3A0EC6C1E151A21038A42E9185329B5B275903D192F8D4E1F32FE9CC78C48) 346 | >>> a * 0x032640BC6003C59260F7250C3DB58CE647F98E1260ACCE4ACDA3DD869F74E01F8BA5E0324309DB6A9831497ABAC96670 347 | brainpoolP384r1(0BD9D3A7EA0B3D519D09D8E48D0785FB744A6B355E6304BC51C229FBBCE239BBADF6403715C35D4FB2A5444F575D4F42, 0DF213417EBE4D8E40A5F76F66C56470C489A3478D146DECF6DF0D94BAE9E598157290F8756066975F1DB34B2324B7BD) 348 | >>> b * 0x1E20F5E048A5886F1F157C74E91BDE2B98C8B52D58E5003D57053FC4B0BD65D6F15EB5D1EE1610DF870795143627D042 349 | brainpoolP384r1(0BD9D3A7EA0B3D519D09D8E48D0785FB744A6B355E6304BC51C229FBBCE239BBADF6403715C35D4FB2A5444F575D4F42, 0DF213417EBE4D8E40A5F76F66C56470C489A3478D146DECF6DF0D94BAE9E598157290F8756066975F1DB34B2324B7BD) 350 | """ 351 | 352 | a = 0x7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826 353 | b = 0x04A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11 354 | order = 0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B31F166E6CAC0425A7CF3AB6AF6B7FC3103B883202E9046565 355 | prime = 0x8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53 356 | aliases = ("1.3.36.3.3.2.8.1.1.11", ) 357 | 358 | @classmethod 359 | def generator(cls): 360 | return cls( 361 | 0x1D1C64F068CF45FFA2A63A81B7C13F6B8847A3E77EF14FE3DB7FCAFE0CBD10E8E826E03436D646AAEF87B2E247D4AF1E, 362 | 0x8ABE1D7520F9C2A45CB1EB8E95CFD55262B70B29FEEC5864E19C054FF99129280E4646217791811142820341263C5315 363 | ) 364 | 365 | 366 | class brainpoolP512r1(Point): 367 | """ 368 | >>> from . import find 369 | >>> cls = find("brainpoolP512r1") 370 | >>> find("1.3.36.3.3.2.8.1.1.13") 371 | 372 | 373 | Test basic math: 374 | >>> cls().is_identity 375 | True 376 | >>> (-cls()).is_identity 377 | True 378 | >>> cls().is_valid 379 | False 380 | >>> (cls.generator() * 0).is_identity 381 | True 382 | >>> cls.generator() * 1 == cls.generator() 383 | True 384 | >>> cls.generator() + cls.generator() * 0 == cls.generator() 385 | True 386 | >>> cls.generator() + cls.generator() == cls.generator() * 2 387 | True 388 | >>> cls.generator() * 2 + cls.generator() == cls.generator() * 3 389 | True 390 | >>> cls.generator() * 2 - cls.generator() == cls.generator() 391 | True 392 | >>> cls.generator() * 6 / 3 == cls.generator() * 2 393 | True 394 | 395 | The following test values are from https://tools.ietf.org/html/rfc6954 396 | >>> a = brainpoolP512r1.generator() * 0x16302FF0DBBB5A8D733DAB7141C1B45ACBC8715939677F6A56850A38BD87BD59B09E80279609FF333EB9D4C061231FB26F92EEB04982A5F1D1764CAD57665422 397 | >>> a 398 | brainpoolP512r1(0A420517E406AAC0ACDCE90FCD71487718D3B953EFD7FBEC5F7F27E28C6149999397E91E029E06457DB2D3E640668B392C2A7E737A7F0BF04436D11640FD09FD, 72E6882E8DB28AAD36237CD25D580DB23783961C8DC52DFA2EC138AD472A0FCEF3887CF62B623B2A87DE5C588301EA3E5FC269B373B60724F5E82A6AD147FDE7) 399 | >>> b = brainpoolP512r1.generator() * 0x230E18E1BCC88A362FA54E4EA3902009292F7F8033624FD471B5D8ACE49D12CFABBC19963DAB8E2F1EBA00BFFB29E4D72D13F2224562F405CB80503666B25429 400 | >>> b 401 | brainpoolP512r1(9D45F66DE5D67E2E6DB6E93A59CE0BB48106097FF78A081DE781CDB31FCE8CCBAAEA8DD4320C4119F1E9CD437A2EAB3731FA9668AB268D871DEDA55A5473199F, 2FDC313095BCDD5FB3A91636F07A959C8E86B5636A1E930E8396049CB481961D365CC11453A06C719835475B12CB52FC3C383BCE35E27EF194512B71876285FA) 402 | >>> a * 0x230E18E1BCC88A362FA54E4EA3902009292F7F8033624FD471B5D8ACE49D12CFABBC19963DAB8E2F1EBA00BFFB29E4D72D13F2224562F405CB80503666B25429 403 | brainpoolP512r1(A7927098655F1F9976FA50A9D566865DC530331846381C87256BAF3226244B76D36403C024D7BBF0AA0803EAFF405D3D24F11A9B5C0BEF679FE1454B21C4CD1F, 7DB71C3DEF63212841C463E881BDCF055523BD368240E6C3143BD8DEF8B3B3223B95E0F53082FF5E412F4222537A43DF1C6D25729DDB51620A832BE6A26680A2) 404 | >>> b * 0x16302FF0DBBB5A8D733DAB7141C1B45ACBC8715939677F6A56850A38BD87BD59B09E80279609FF333EB9D4C061231FB26F92EEB04982A5F1D1764CAD57665422 405 | brainpoolP512r1(A7927098655F1F9976FA50A9D566865DC530331846381C87256BAF3226244B76D36403C024D7BBF0AA0803EAFF405D3D24F11A9B5C0BEF679FE1454B21C4CD1F, 7DB71C3DEF63212841C463E881BDCF055523BD368240E6C3143BD8DEF8B3B3223B95E0F53082FF5E412F4222537A43DF1C6D25729DDB51620A832BE6A26680A2) 406 | """ 407 | 408 | a = 0x7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA 409 | b = 0x3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723 410 | order = 0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA70330870553E5C414CA92619418661197FAC10471DB1D381085DDADDB58796829CA90069 411 | prime = 0xAADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3 412 | aliases = ("1.3.36.3.3.2.8.1.1.13", ) 413 | 414 | @classmethod 415 | def generator(cls): 416 | return cls( 417 | 0x81AEE4BDD82ED9645A21322E9C4C6A9385ED9F70B5D916C1B43B62EEF4D0098EFF3B1F78E2D0D48D50D1687B93B97D5F7C6D5047406A5E688B352209BCB9F822, 418 | 0x7DDE385D566332ECC0EABFA9CF7822FDF209F70024A57B1AA000C55B881F8111B2DCDE494A5F485E5BCA4BD88A2763AED1CA2B2FA8F0540678CD1E0F3AD80892 419 | ) 420 | --------------------------------------------------------------------------------