summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHubert Kario <hkario@redhat.com>2022-07-09 13:28:29 +0200
committerGitHub <noreply@github.com>2022-07-09 13:28:29 +0200
commit3a8bc4e6dcd2c2c209f0ade440a70fd6485eaec7 (patch)
tree14748c0b8196950e323bd4a54b90eb97019f5603
parent29a3cd05f60644ac1962f2cc5227f8bb4344a73a (diff)
parent1943ef37a1ae0ae82965b3d894af857a35665669 (diff)
downloadecdsa-3a8bc4e6dcd2c2c209f0ade440a70fd6485eaec7.tar.gz
Merge pull request #290 from tomato42/docs-updates
Docs updates
-rw-r--r--docs/source/basics.rst162
-rw-r--r--docs/source/conf.py3
-rw-r--r--docs/source/ec_arithmetic.rst137
-rw-r--r--docs/source/ecdsa.eddsa.rst7
-rw-r--r--docs/source/ecdsa.rst1
-rw-r--r--docs/source/glossary.rst92
-rw-r--r--docs/source/index.rst41
-rw-r--r--docs/source/modules.rst4
-rw-r--r--docs/source/quickstart.rst178
-rw-r--r--src/ecdsa/ellipticcurve.py5
-rw-r--r--src/ecdsa/keys.py129
11 files changed, 662 insertions, 97 deletions
diff --git a/docs/source/basics.rst b/docs/source/basics.rst
new file mode 100644
index 0000000..b71e925
--- /dev/null
+++ b/docs/source/basics.rst
@@ -0,0 +1,162 @@
+======================
+Basics of ECC handling
+======================
+
+The :term:`ECC`, as any asymmetric cryptography system, deals with private
+keys and public keys. Private keys are generally used to create signatures,
+and are kept, as the name suggest, private. That's because possession of a
+private key allows creating a signature that can be verified with a public key.
+If the public key is associated with an identity (like a person or an
+institution), possession of the private key will allow to impersonate
+that identity.
+
+The public keys on the other hand are widely distributed, and they don't
+have to be kept private. The primary purpose of them, is to allow
+checking if a given signature was made with the associated private key.
+
+Number representations
+======================
+
+On a more low level, the private key is a single number, usually the
+size of the curve size: a NIST P-256 private key will have a size of 256 bits,
+though as it needs to be selected randomly, it may be a slightly smaller
+number (255-bit, 248-bit, etc.).
+Public points are a pair of numbers. That pair specifies a point on an
+elliptic curve (a pair of integers that satisfy the curve equation).
+Those two numbers are similarly close in size to the curve size, so both the
+``x`` and ``y`` coordinate of a NIST P-256 curve will also be around 256 bit in
+size.
+
+.. note::
+ To be more precise, the size of the private key is related to the
+ curve *order*, i.e. the number of points on a curve. The coordinates
+ of the curve depend on the *field* of the curve, which usually means the
+ size of the *prime* used for operations on points. While the *order* and
+ the *prime* size are related and fairly close in size, it's possible
+ to have a curve where either of them is larger by a bit (i.e.
+ it's possible to have a curve that uses a 256 bit *prime* that has a 257 bit
+ *order*).
+
+Since normally computers work with much smaller numbers, like 32 bit or 64 bit,
+we need to use special approaches to represent numbers that are hundreds of
+bits large.
+
+First is to decide if the numbers should be stored in a big
+endian format, or in little endian format. In big endian, the most
+significant bits are stored first, so a number like :math:`2^{16}` is saved
+as a three bytes: byte with value 1 and two bytes with value 0.
+In little endian format the least significant bits are stored first, so
+the number like :math:`2^{16}` would be stored as three bytes:
+first two bytes with value 0, than a byte with value 1.
+
+For :term:`ECDSA` big endian encoding is usually used, for :term:`EdDSA`
+little endian encoding is usually used.
+
+Secondly, we need to decide if the numbers need to be stored as fixed length
+strings (zero padded if necessary), or if they should be stored with
+minimal number of bytes necessary.
+That depends on the format and place it's used, some require strict
+sizes (so even if the number encoded is 1, but the curve used is 128 bit large,
+that number 1 still needs to be encoded with 16 bytes, with fifteen most
+significant bytes equal zero).
+
+Public key encoding
+===================
+
+Generally, public keys (i.e. points) are expressed as fixed size byte strings.
+
+While public keys can be saved as two integers, one to represent the
+``x`` coordinate and one to represent ``y`` coordinate, that actually
+provides a lot of redundancy. Because of the specifics of elliptic curves,
+for every valid ``x`` value there are only two valid ``y`` values.
+Moreover, if you have an ``x`` value, you can compute those two possible
+``y`` values (if they exist).
+As such, it's possible to save just the ``x`` coordinate and the sign
+of the ``y`` coordinate (as the two possible values are negatives of
+each-other: :math:`y_1 == -y_2`).
+
+That gives us few options to represent the public point, the most common are:
+
+1. As a concatenation of two fixed-length big-endian integers, so called
+ :term:`raw encoding`.
+2. As a concatenation of two fixed-length big-endian integers prefixed with
+ the type of the encoding, so called :term:`uncompressed` point
+ representation (the type is represented by a 0x04 byte).
+3. As a fixed-length big-endian integer representing the ``x`` coordinate
+ prefixed with the byte representing the combined type of the encoding
+ and the sign of the ``y`` coordinate, so called :term:`compressed`
+ point representation (the type is then represented by a 0x02 or a 0x03
+ byte).
+
+Interoperable file formats
+==========================
+
+Now, while we can save the byte strings as-is and "remember" which curve
+was used to generate those private and public keys, interoperability usually
+requires to also save information about the curve together with the
+corresponding key. Here too there are many ways to do it:
+save the parameters of the used curve explicitly, use the name of the
+well-known curve as a string, use a numerical identifier of the well-known
+curve, etc.
+
+For public keys the most interoperable format is the one described
+in RFC5912 (look for SubjectPublicKeyInfo structure).
+For private keys, the RFC5915 format (also known as the ssleay format)
+and the PKCS#8 format (described in RFC5958) are the most popular.
+
+All three formats effectively support two ways of providing the information
+about the curve used: by specifying the curve parameters explicitly or
+by specifying the curve using ASN.1 OBJECT IDENTIFIER (OID), which is
+called ``named_curve``. ASN.1 OIDs are a hierarchical system of representing
+types of objects, for example, NIST P-256 curve is identified by the
+1.2.840.10045.3.1.7 OID (in dotted-decimal formatting of the OID, also
+known by the ``prime256v1`` OID node name or short name). Those OIDs
+uniquely, identify a particular curve, but the receiver needs to know
+which numerical OID maps to which curve parameters. Thus the prospect of
+using the explicit encoding, where all the needed parameters are provided
+is tempting, the downside is that curve parameters may specify a *weak*
+curve, which is easy to attack and break (that is to deduce the private key
+from the public key). To verify curve parameters is complex and computationally
+expensive, thus generally protocols use few specific curves and require
+all implementations to carry the parameters of them. As such, use of
+``named_curve`` parameters is generally recommended.
+
+All of the mentioned formats specify a binary encoding, called DER. That
+encoding uses bytes with all possible numerical values, which means it's not
+possible to embed it directly in text files. For uses where it's useful to
+limit bytes to printable characters, so that the keys can be embedded in text
+files or text-only protocols (like email), the PEM formatting of the
+DER-encoded data can be used. The PEM formatting is just a base64 encoding
+with appropriate header and footer.
+
+Signature formats
+=================
+
+Finally, ECDSA signatures at the lowest level are a pair of numbers, usually
+called ``r`` and ``s``. While they are the ``x`` coordinates of special
+points on the curve, they are saved modulo *order* of the curve, not
+modulo *prime* of the curve (as a coordinate needs to be).
+
+That again means we have multiple ways of encoding those two numbers.
+The two most popular formats are to save them as a concatenation of big-endian
+integers of fixed size (determined by the curve *order*) or as a DER
+structure with two INTEGERS.
+The first of those is called the :term:``raw encoding`` inside the Python
+ecdsa library.
+
+As ASN.1 signature format requires the encoding of INTEGERS, and DER INTEGERs
+must use the fewest possible number of bytes, a numerically small value of
+``r`` or ``s`` will require fewer
+bytes to represent in the DER structure. Thus, DER encoding isn't fixed
+size for a given curve, but has a maximum possible size.
+
+.. note::
+
+ As DER INTEGER uses so-called two's complement representation of
+ numbers, the most significant bit of the most significant byte
+ represents the *sign* of the number. If that bit is set, then the
+ number is considered to be negative. Thus, to represent a number like
+ 255, which in binary representation is 0b11111111 (i.e. a byte with all
+ bits set high), the DER encoding of it will require two bytes, one
+ zero byte to make sure the sign bit is 0, and a byte with value 255 to
+ encode the numerical value of the integer.
diff --git a/docs/source/conf.py b/docs/source/conf.py
index df4de1b..0eaea41 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -61,6 +61,9 @@ html_theme = "sphinx_rtd_theme"
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ["_static"]
+# Example configuration for intersphinx: refer to the Python standard library.
+intersphinx_mapping = {"https://docs.python.org/": None}
+
autodoc_default_options = {
"undoc-members": True,
"inherited-members": True,
diff --git a/docs/source/ec_arithmetic.rst b/docs/source/ec_arithmetic.rst
new file mode 100644
index 0000000..36d0ad4
--- /dev/null
+++ b/docs/source/ec_arithmetic.rst
@@ -0,0 +1,137 @@
+=========================
+Elliptic Curve arithmetic
+=========================
+
+The python-ecdsa also provides generic API for performing operations on
+elliptic curve points.
+
+.. warning::
+
+ This is documentation of a very low-level API, if you want to
+ handle keys or signatures you should look at documentation of
+ the :py:mod:`~ecdsa.keys` module.
+
+Short Weierstrass curves
+========================
+
+There are two low-level implementations for
+:term:`short Weierstrass curves <short Weierstrass curve>`:
+:py:class:`~ecdsa.ellipticcurve.Point` and
+:py:class:`~ecdsa.ellipticcurve.PointJacobi`.
+
+Both of them use the curves specified using the
+:py:class:`~ecdsa.ellipticcurve.CurveFp` object.
+
+You can either provide your own curve parameters or use one of the predefined
+curves.
+For example, to define a curve :math:`x^2 = x^3 + x + 4 \text{ mod } 5` use
+code like this:
+
+.. code:: python
+
+ from ecdsa.ellipticcurve import CurveFp
+ custom_curve = CurveFp(5, 1, 4)
+
+The predefined curves are specified in the :py:mod:`~ecdsa.ecdsa` module,
+but it's much easier to use the helper functions (and proper names)
+from the :py:mod:`~ecdsa.curves` module.
+
+For example, to get the curve parameters for the NIST P-256 curve use this
+code:
+
+.. code:: python
+
+ from ecdsa.curves import NIST256p
+ curve = NIST256p.curve
+
+.. tip::
+
+ You can also use :py:class:`~ecdsa.curves.Curve` to get the curve
+ parameters from a PEM or DER file. You can also use
+ :py:func:`~ecdsa.curves.curve_by_name` to get a curve by specifying its
+ name.
+ Or use the
+ :py:func:`~ecdsa.curves.find_curve` to get a curve by specifying its
+ ASN.1 object identifier (OID).
+
+Affine coordinates
+------------------
+
+After taking hold of curve parameters you can create a point on the
+curve. The :py:class:`~ecdsa.ellipticcurve.Point` uses affine coordinates,
+i.e. the :math:`x` and :math:`y` from the curve equation directly.
+
+To specify a point (1, 1) on the ``custom_curve`` you can use this code:
+
+.. code:: python
+
+ from ecdsa.ellipticcurve import Point
+ point_a = Point(custom_curve, 1, 1)
+
+Then it's possible to either perform scalar multiplication:
+
+.. code:: python
+
+ point_b = point_a * 3
+
+Or specify other points and perform addition:
+
+.. code:: python
+
+ point_b = Point(custom_curve, 3, 2)
+ point_c = point_a + point_b
+
+To get the affine coordinates of the point, call the ``x()`` and ``y()``
+methods of the object:
+
+.. code:: python
+
+ print("x: {0}, y: {1}".format(point_c.x(), point_c.y()))
+
+Projective coordinates
+----------------------
+
+When using the Jacobi coordinates, the point is defined by 3 integers,
+which are related to the :math:`x` and :math:`y` in the following way:
+
+.. math::
+
+ x = X/Z^2 \\
+ y = Y/Z^3
+
+That means that if you have point in affine coordinates, it's possible
+to convert them to Jacobi by simply assuming :math:`Z = 1`.
+
+So the same points can be specified as so:
+
+.. code:: python
+
+ from ecdsa.ellipticcurve import PointJacobi
+ point_a = PointJacobi(custom_curve, 1, 1, 1)
+ point_b = PointJacobi(custom_curve, 3, 2, 1)
+
+
+.. note::
+
+ Unlike the :py:class:`~ecdsa.ellipticcurve.Point`, the
+ :py:class:`~ecdsa.ellipticcurve.PointJacobi` does **not** check if the
+ coordinates specify a valid point on the curve as that operation is
+ computationally expensive for Jacobi coordinates.
+ If you want to verify if they specify a valid
+ point, you need to convert the point to affine coordinates and use the
+ :py:meth:`~ecdsa.ellipticcurve.CurveFp.contains_point` method.
+
+Then all the operations work exactly the same as with regular
+:py:class:`~ecdsa.ellipticcurve.Point` implementation.
+While it's not possible to get the internal :math:`X`, :math:`Y`, and :math:`Z`
+coordinates, it's possible to get the affine projection just like with
+the regular implementation:
+
+.. code:: python
+
+ point_c = point_a + point_b
+ print("x: {0}, y: {1}".format(point_c.x(), point_c.y()))
+
+All the other operations, like scalar multiplication or point addition work
+on projective points the same as with affine representation, but they
+are much more effective computationally.
diff --git a/docs/source/ecdsa.eddsa.rst b/docs/source/ecdsa.eddsa.rst
new file mode 100644
index 0000000..1b1c3f4
--- /dev/null
+++ b/docs/source/ecdsa.eddsa.rst
@@ -0,0 +1,7 @@
+ecdsa.eddsa module
+==================
+
+.. automodule:: ecdsa.eddsa
+ :members:
+ :undoc-members:
+ :show-inheritance:
diff --git a/docs/source/ecdsa.rst b/docs/source/ecdsa.rst
index 2b5b548..00b4936 100644
--- a/docs/source/ecdsa.rst
+++ b/docs/source/ecdsa.rst
@@ -16,6 +16,7 @@ Submodules
ecdsa.der
ecdsa.ecdh
ecdsa.ecdsa
+ ecdsa.eddsa
ecdsa.ellipticcurve
ecdsa.errors
ecdsa.keys
diff --git a/docs/source/glossary.rst b/docs/source/glossary.rst
new file mode 100644
index 0000000..5486673
--- /dev/null
+++ b/docs/source/glossary.rst
@@ -0,0 +1,92 @@
+.. _glossary:
+
+Glossary
+========
+
+.. glossary::
+ :sorted:
+
+ ECC
+ Elliptic Curve Cryptography, a term for all the different ways of using
+ elliptic curves in cryptography. Also combined term for :term:`ECDSA`,
+ :term:`EdDSA`, :term:`ECDH`.
+
+ ECDSA
+ Elliptic Curve Digital Signature Algorithm
+
+ EdDSA
+ Edwards curve based Digital Signature Algorithm, the alternative
+ digital signature algorithm that's used for Curve25519 or Curve448
+
+ ECDH
+ Elliptic Curve Diffie-Hellman
+
+ raw encoding
+ Conversion of public, private keys and signatures (which in
+ mathematical sense are integers or pairs of integers) to strings of
+ bytes that does not use any special tags or encoding rules.
+ For any given curve, all keys of the same type or signatures will be
+ encoded to byte strings of the same length. In more formal sense,
+ the integers are encoded as big-endian, constant length byte strings,
+ where the string length is determined by the curve order (e.g.
+ for NIST256p the order is 256 bits long, so the private key will be 32
+ bytes long while public key will be 64 bytes long). The encoding of a
+ single integer is zero-padded on the left if the numerical value is
+ low. In case of public keys and signatures, which are comprised of two
+ integers, the integers are simply concatenated.
+
+ uncompressed
+ The most common formatting specified in PKIX standards. Specified in
+ X9.62 and SEC1 standards. The only difference between it and
+ :term:`raw encoding` is the prepending of a 0x04 byte. Thus an
+ uncompressed NIST256p public key encoding will be 65 bytes long.
+
+ compressed
+ The public point representation that uses half of bytes of the
+ :term:`uncompressed` encoding (rounded up). It uses the first byte of
+ the encoding to specify the sign of the y coordinate and encodes the
+ x coordinate as-is. The first byte of the encoding is equal to
+ 0x02 or 0x03. Compressed encoding of NIST256p public key will be 33
+ bytes long.
+
+ hybrid
+ A combination of :term:`uncompressed` and :term:`compressed` encodings.
+ Both x and y coordinates are stored just as in :term:`compressed`
+ encoding, but the first byte reflects the sign of the y coordinate. The
+ first byte of the encoding will be equal to 0x06 or 0x7. Hybrid
+ encoding of NIST256p public key will be 65 bytes long.
+
+ PEM
+ The acronym stands for Privacy Enhanced Mail, but currently it is used
+ primarily as the way to encode :term:`DER` objects into text that can
+ be either easily copy-pasted or transferred over email.
+ It uses headers like ``-----BEGIN <type of contents>-----`` and footers
+ like ``-----END <type of contents>-----`` to separate multiple
+ types of objects in the same file or the object from the surrounding
+ comments. The actual object stored is base64 encoded.
+
+ DER
+ Distinguished Encoding Rules, the way to encode :term:`ASN.1` objects
+ deterministically and uniquely into byte strings.
+
+ ASN.1
+ Abstract Syntax Notation 1 is a standard description language for
+ specifying serialisation and deserialisation of data structures in a
+ portable and cross-platform way.
+
+ bytes-like object
+ All the types that implement the buffer protocol. That includes
+ ``str`` (only on python2), ``bytes``, ``bytearray``, ``array.array``
+ and ``memoryview`` of those objects.
+ Please note that ``array.array`` serialisation (converting it to byte
+ string) is endianess dependant! Signature computed over ``array.array``
+ of integers on a big-endian system will not be verified on a
+ little-endian system and vice-versa.
+
+ set-like object
+ All the types that support the ``in`` operator, like ``list``,
+ ``tuple``, ``set``, ``frozenset``, etc.
+
+ short Weierstrass curve
+ A curve with the curve equation: :math:`x^2=y^3+ax+b`. Most popular
+ curves use equation of this format (e.g. NIST256p).
diff --git a/docs/source/index.rst b/docs/source/index.rst
index 9aa5b3f..915071a 100644
--- a/docs/source/index.rst
+++ b/docs/source/index.rst
@@ -6,10 +6,50 @@
Welcome to python-ecdsa's documentation!
========================================
+``ecdsa`` implements
+`elliptic-curve cryptography (ECC) <https://en.wikipedia.org/wiki/Elliptic-curve_cryptography>`_,
+more specifically the
+`Elliptic Curve Digital Signature Algorithm (ECDSA) <https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm>`_,
+`Edwards-curve Digital Signature Algorithm (EdDSA) <https://en.wikipedia.org/wiki/EdDSA>`_
+and the
+`Elliptic Curve Diffie-Hellman (ECDH) <https://en.wikipedia.org/wiki/Elliptic-curve_Diffie%E2%80%93Hellman>`_
+algorithms.
+All of those algorithms are used in many protocols in practice, like
+in
+`TLS <https://en.wikipedia.org/wiki/Transport_Layer_Security>`_
+or
+`SSH <https://en.wikipedia.org/wiki/Secure_Shell_Protocol>`_.
+
+This library provides key generation, signing, verifying, and shared secret
+derivation for five
+popular NIST "Suite B" GF(p) (*prime field*) curves, with key lengths of 192,
+224, 256, 384, and 521 bits. The "short names" for these curves, as known by
+the OpenSSL tool (``openssl ecparam -list_curves``), are: ``prime192v1``,
+``secp224r1``, ``prime256v1``, ``secp384r1``, and ``secp521r1``. It includes
+the
+256-bit curve ``secp256k1`` used by Bitcoin. There is also support for the
+regular (non-twisted) variants of Brainpool curves from 160 to 512 bits. The
+"short names" of those curves are: ``brainpoolP160r1``, ``brainpoolP192r1``,
+``brainpoolP224r1``, ``brainpoolP256r1``, ``brainpoolP320r1``,
+``brainpoolP384r1``,
+``brainpoolP512r1``. Few of the small curves from SEC standard are also
+included (mainly to speed-up testing of the library), those are:
+``secp112r1``, ``secp112r2``, ``secp128r1``, and ``secp160r1``.
+Key generation, siging and verifying is also supported for Ed25519 and Ed448
+curves.
+No other curves are included, but it is not too hard to add support for more
+curves over prime fields.
+
.. toctree::
:maxdepth: 2
:caption: Contents:
+ :hidden:
+ quickstart
+ basics
+ ec_arithmetic
+ glossary
+ modules
Indices and tables
@@ -17,4 +57,5 @@ Indices and tables
* :ref:`genindex`
* :ref:`modindex`
+* :ref:`glossary`
* :ref:`search`
diff --git a/docs/source/modules.rst b/docs/source/modules.rst
index d74a83e..f5c495b 100644
--- a/docs/source/modules.rst
+++ b/docs/source/modules.rst
@@ -1,5 +1,5 @@
-src
-===
+python-ecdsa API
+================
.. toctree::
:maxdepth: 4
diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst
new file mode 100644
index 0000000..e83a6a6
--- /dev/null
+++ b/docs/source/quickstart.rst
@@ -0,0 +1,178 @@
+===============
+Getting started
+===============
+
+The library has just one mandatory dependency: ``six``.
+If you install ``python-ecdsa`` through pip, it should automatically
+install ``six`` too.
+
+To install it you can run the following command:
+
+.. code:: bash
+
+ pip install ecdsa
+
+The high level API provided by the library is primarily in the
+:py:class:`~ecdsa.keys` module.
+There you will find the :py:class:`~ecdsa.keys.SigningKey` (the class
+that enables handling of the private keys) and the
+:py:class:`~ecdsa.keys.VerifyingKey` (the class that enables handling of
+the public keys).
+
+To handle shared key derivation, the :py:class:`~ecdsa.ecdh.ECDH` class
+is used.
+
+Finally, in case use of custom elliptic curves is necessary, the
+:py:class:`~ecdsa.curves.Curve` class may be needed.
+
+Key generation
+==============
+
+To generate a key, import the :py:class:`~ecdsa.keys.SigningKey` and
+call the :py:func:`~ecdsa.keys.SigningKey.generate` function in it:
+
+.. code:: python
+
+ from ecdsa.keys import SigningKey
+
+ key = SigningKey.generate()
+
+By default, that will create a key that uses the NIST P-192 curve. To
+select a more secure curve, like NIST P-256, import it from the
+:py:mod:`ecdsa.curves` or from the :py:mod:`ecdsa` module:
+
+.. code:: python
+
+ from ecdsa import SigningKey, NIST256p
+
+ key = SigningKey.generate(curve=NIST256p)
+
+Private key storage and retrieval
+=================================
+
+To store a key as string or file, you can serialise it using many formats,
+in general we recommend the PKCS#8 PEM encoding.
+
+If you have a :py:class:`~ecdsa.keys.SigningKey` object in ``key`` and
+want to save it to a file like ``priv_key.pem`` you can run the following
+code:
+
+.. code:: python
+
+ with open("priv_key.pem", "wb") as f:
+ f.write(key.to_pem(format="pkcs8"))
+
+.. warning::
+
+ Not specifying the ``format=pkcs8`` will create a file that uses the legacy
+ ``ssleay`` file format which is most commonly used by applications
+ that use OpenSSL, as that was originally the only format supported by it.
+ For a long time though OpenSSL supports the PKCS# 8 format too.
+
+To read that file back, you can run code like this:
+
+.. code:: python
+
+ from ecdsa import SigningKey
+
+ with open("priv_key.pem") as f:
+ key = SigningKey.from_pem(f.read())
+
+.. tip::
+
+ As the format is self-describing, the parser will automatically detect
+ if the provided file is in the ``ssleay`` or the ``pkcs8`` format
+ and process it accordingly.
+
+Public key derivation
+=====================
+
+To get the public key associated with the given private key, either
+call the :py:func:`~ecdsa.keys.SigningKey.get_verifying_key` method or
+access the ``verifying_key`` attribute in
+:py:class:`~ecdsa.keys.SigningKey` directly:
+
+.. code:: python
+
+ from ecdsa import SigningKey, NIST256p
+
+ private_key = SigningKey.generate(curve=NIST256p)
+
+ public_key = private_key.verifying_key
+
+Public key storage and retrieval
+================================
+
+Similarly to private keys, public keys can be stored in files:
+
+.. code:: python
+
+ from ecdsa import SigningKey
+
+ private_key = SigningKey.generate()
+
+ public_key = private_key.verifying_key
+
+ with open("pub_key.pem", "wb") as f:
+ f.write(public_key.to_pem())
+
+And read from files:
+
+.. code:: python
+
+ from ecdsa import VerifyingKey
+
+ with open("pub_key.pem") as f:
+ public_key = VerifyingKey.from_pem(f.read())
+
+Signing
+=======
+
+To sign a byte string stored in variable ``message`` using SigningKey in
+``private_key``, SHA-256, get a signature in the DER format and save it to a
+file, you can use the following code:
+
+.. code:: python
+
+ from hashlib import sha256
+ from ecdsa.util import sigencode_der
+
+ sig = private_key.sign_deterministic(
+ message,
+ hashfunc=sha256,
+ sigencode=sigencode_der
+ )
+
+ with open("message.sig", "wb") as f:
+ f.write(sig)
+
+.. note::
+
+ As cryptographic hashes (SHA-256, SHA3-256, etc.) operate on *bytes* not
+ text strings, any text needs to be serialised into *bytes* before it can
+ be signed. This is because encoding of string "text" results in very
+ different bytes when it's encoded using UTF-8 and when it's encoded using
+ UCS-2.
+
+Verifying
+=========
+
+To verify a signature of a byte string in ``message`` using a VerifyingKey
+in ``public_key``, SHA-256 and a DER signature in a ``message.sig`` file,
+you can use the following code:
+
+.. code:: python
+
+ from hashlib import sha256
+ from ecdsa import BadSignatureError
+ from ecdsa.util import sigdecode_der
+
+ with open("message.sig", "rb") as f:
+ sig = f.read()
+
+ try:
+ ret = public_key.verify(sig, message, sha256, sigdecode=sigdecode_der)
+ assert ret
+ print("Valid signature")
+ except BadSignatureError:
+ print("Incorrect signature")
diff --git a/src/ecdsa/ellipticcurve.py b/src/ecdsa/ellipticcurve.py
index f358fbd..d6f7146 100644
--- a/src/ecdsa/ellipticcurve.py
+++ b/src/ecdsa/ellipticcurve.py
@@ -56,7 +56,10 @@ from .util import orderlen, string_to_number, number_to_string
@python_2_unicode_compatible
class CurveFp(object):
- """Short Weierstrass Elliptic Curve over a prime field."""
+ """
+ :term:`Short Weierstrass Elliptic Curve <short Weierstrass curve>` over a
+ prime field.
+ """
if GMPY: # pragma: no branch
diff --git a/src/ecdsa/keys.py b/src/ecdsa/keys.py
index b606f57..2b7d316 100644
--- a/src/ecdsa/keys.py
+++ b/src/ecdsa/keys.py
@@ -1,73 +1,5 @@
"""
Primary classes for performing signing and verification operations.
-
-.. glossary::
-
- raw encoding
- Conversion of public, private keys and signatures (which in
- mathematical sense are integers or pairs of integers) to strings of
- bytes that does not use any special tags or encoding rules.
- For any given curve, all keys of the same type or signatures will be
- encoded to byte strings of the same length. In more formal sense,
- the integers are encoded as big-endian, constant length byte strings,
- where the string length is determined by the curve order (e.g.
- for NIST256p the order is 256 bits long, so the private key will be 32
- bytes long while public key will be 64 bytes long). The encoding of a
- single integer is zero-padded on the left if the numerical value is
- low. In case of public keys and signatures, which are comprised of two
- integers, the integers are simply concatenated.
-
- uncompressed
- The most common formatting specified in PKIX standards. Specified in
- X9.62 and SEC1 standards. The only difference between it and
- :term:`raw encoding` is the prepending of a 0x04 byte. Thus an
- uncompressed NIST256p public key encoding will be 65 bytes long.
-
- compressed
- The public point representation that uses half of bytes of the
- :term:`uncompressed` encoding (rounded up). It uses the first byte of
- the encoding to specify the sign of the y coordinate and encodes the
- x coordinate as-is. The first byte of the encoding is equal to
- 0x02 or 0x03. Compressed encoding of NIST256p public key will be 33
- bytes long.
-
- hybrid
- A combination of :term:`uncompressed` and :term:`compressed` encodings.
- Both x and y coordinates are stored just as in :term:`compressed`
- encoding, but the first byte reflects the sign of the y coordinate. The
- first byte of the encoding will be equal to 0x06 or 0x7. Hybrid
- encoding of NIST256p public key will be 65 bytes long.
-
- PEM
- The acronym stands for Privacy Enhanced Email, but currently it is used
- primarily as the way to encode :term:`DER` objects into text that can
- be either easily copy-pasted or transferred over email.
- It uses headers like ``-----BEGIN <type of contents>-----`` and footers
- like ``-----END <type of contents>-----`` to separate multiple
- types of objects in the same file or the object from the surrounding
- comments. The actual object stored is base64 encoded.
-
- DER
- Distinguished Encoding Rules, the way to encode :term:`ASN.1` objects
- deterministically and uniquely into byte strings.
-
- ASN.1
- Abstract Syntax Notation 1 is a standard description language for
- specifying serialisation and deserialisation of data structures in a
- portable and cross-platform way.
-
- bytes-like object
- All the types that implement the buffer protocol. That includes
- ``str`` (only on python2), ``bytes``, ``bytesarray``, ``array.array``
- and ``memoryview`` of those objects.
- Please note that ``array.array`` serialisation (converting it to byte
- string) is endianess dependant! Signature computed over ``array.array``
- of integers on a big-endian system will not be verified on a
- little-endian system and vice-versa.
-
- set-like object
- All the types that support the ``in`` operator, like ``list``,
- ``tuple``, ``set``, ``frozenset``, etc.
"""
import binascii
@@ -704,7 +636,7 @@ class VerifyingKey(object):
:type signature: sigdecode method dependent
:param data: data signed by the `signature`, will be hashed using
`hashfunc`, if specified, or default hash function
- :type data: bytes like object
+ :type data: :term:`bytes-like object`
:param hashfunc: The default hash function that will be used for
verification, needs to implement the same interface as hashlib.sha1
:type hashfunc: callable
@@ -758,7 +690,7 @@ class VerifyingKey(object):
:param signature: encoding of the signature
:type signature: sigdecode method dependent
:param digest: raw hash value that the signature authenticates.
- :type digest: bytes like object
+ :type digest: :term:`bytes-like object`
:param sigdecode: Callable to define the way the signature needs to
be decoded to an object, needs to handle `signature` as the
first parameter, the curve order (an int) as the second and return
@@ -801,7 +733,7 @@ class SigningKey(object):
"""
Class for handling keys that can create signatures (private keys).
- :ivar `~ecdsa.curves.Curve` ~.curve: The Curve over which all the
+ :ivar `~ecdsa.curves.Curve` curve: The Curve over which all the
cryptographic operations will take place
:ivar default_hashfunc: the function that will be used for hashing the
data. Should implement the same API as :py:class:`hashlib.sha1`
@@ -947,7 +879,7 @@ class SigningKey(object):
In Python 3, the expected type is `bytes`.
:param string: the raw encoding of the private key
- :type string: bytes like object
+ :type string: :term:`bytes-like object`
:param curve: The curve on which the point needs to reside
:type curve: ~ecdsa.curves.Curve
:param hashfunc: The default hash function that will be used for
@@ -1080,7 +1012,7 @@ class SigningKey(object):
in them will not be detected.
:param string: binary string with DER-encoded private ECDSA key
- :type string: bytes like object
+ :type string: :term:`bytes-like object`
:param valid_curve_encodings: list of allowed encoding formats
for curve parameters. By default (``None``) all are supported:
``named_curve`` and ``explicit``.
@@ -1383,7 +1315,7 @@ class SigningKey(object):
of data is necessary.
:param data: data to be hashed and computed signature over
- :type data: bytes like object
+ :type data: :term:`bytes-like object`
:param hashfunc: hash function to use for computing the signature,
if unspecified, the default hash function selected during
object initialisation will be used (see
@@ -1402,7 +1334,7 @@ class SigningKey(object):
:param extra_entropy: additional data that will be fed into the random
number generator used in the RFC6979 process. Entirely optional.
Ignored with EdDSA.
- :type extra_entropy: bytes like object
+ :type extra_entropy: :term:`bytes-like object`
:return: encoded signature over `data`
:rtype: bytes or sigencode function dependent type
@@ -1442,24 +1374,26 @@ class SigningKey(object):
hashing of data is necessary.
:param digest: hash of data that will be signed
- :type digest: bytes like object
+ :type digest: :term:`bytes-like object`
:param hashfunc: hash function to use for computing the random "k"
value from RFC6979 process,
if unspecified, the default hash function selected during
object initialisation will be used (see
- `VerifyingKey.default_hashfunc`). The object needs to implement
- the same interface as hashlib.sha1.
+ :attr:`.VerifyingKey.default_hashfunc`). The object needs to
+ implement
+ the same interface as :func:`~hashlib.sha1` from :py:mod:`hashlib`.
:type hashfunc: callable
:param sigencode: function used to encode the signature.
The function needs to accept three parameters: the two integers
that are the signature and the order of the curve over which the
signature was computed. It needs to return an encoded signature.
- See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der`
+ See :func:`~ecdsa.util.sigencode_string` and
+ :func:`~ecdsa.util.sigencode_der`
as examples of such functions.
:type sigencode: callable
:param extra_entropy: additional data that will be fed into the random
number generator used in the RFC6979 process. Entirely optional.
- :type extra_entropy: bytes like object
+ :type extra_entropy: :term:`bytes-like object`
:param bool allow_truncate: if True, the provided digest can have
bigger bit-size than the order of the curve, the extra bits (at
the end of the digest) will be truncated. Use it when signing
@@ -1524,28 +1458,35 @@ class SigningKey(object):
method instead of this one.
:param data: data that will be hashed for signing
- :type data: bytes like object
- :param callable entropy: randomness source, os.urandom by default.
- Ignored with EdDSA.
- :param hashfunc: hash function to use for hashing the provided `data`.
+ :type data: :term:`bytes-like object`
+ :param callable entropy: randomness source, :func:`os.urandom` by
+ default. Ignored with EdDSA.
+ :param hashfunc: hash function to use for hashing the provided
+ ``data``.
If unspecified the default hash function selected during
object initialisation will be used (see
- `VerifyingKey.default_hashfunc`).
- Should behave like hashlib.sha1. The output length of the
+ :attr:`.VerifyingKey.default_hashfunc`).
+ Should behave like :func:`~hashlib.sha1` from :py:mod:`hashlib`.
+ The output length of the
hash (in bytes) must not be longer than the length of the curve
order (rounded up to the nearest byte), so using SHA256 with
NIST256p is ok, but SHA256 with NIST192p is not. (In the 2**-96ish
unlikely event of a hash output larger than the curve order, the
hash will effectively be wrapped mod n).
- Use hashfunc=hashlib.sha1 to match openssl's -ecdsa-with-SHA1 mode,
- or hashfunc=hashlib.sha256 for openssl-1.0.0's -ecdsa-with-SHA256.
+ If you want to explicitly allow use of large hashes with small
+ curves set the ``allow_truncate`` to ``True``.
+ Use ``hashfunc=hashlib.sha1`` to match openssl's
+ ``-ecdsa-with-SHA1`` mode,
+ or ``hashfunc=hashlib.sha256`` for openssl-1.0.0's
+ ``-ecdsa-with-SHA256``.
Ignored for EdDSA
:type hashfunc: callable
:param sigencode: function used to encode the signature.
The function needs to accept three parameters: the two integers
that are the signature and the order of the curve over which the
signature was computed. It needs to return an encoded signature.
- See `ecdsa.util.sigencode_string` and `ecdsa.util.sigencode_der`
+ See :func:`~ecdsa.util.sigencode_string` and
+ :func:`~ecdsa.util.sigencode_der`
as examples of such functions.
Ignored for EdDSA
:type sigencode: callable
@@ -1553,17 +1494,17 @@ class SigningKey(object):
In typical use cases, it should be set to None (the default) to
allow its generation from an entropy source.
Ignored for EdDSA.
- :param bool allow_truncate: if True, the provided digest can have
+ :param bool allow_truncate: if ``True``, the provided digest can have
bigger bit-size than the order of the curve, the extra bits (at
the end of the digest) will be truncated. Use it when signing
SHA-384 output using NIST256p or in similar situations. True by
default.
Ignored for EdDSA.
- :raises RSZeroError: in the unlikely event when "r" parameter or
- "s" parameter of the created signature is equal 0, as that would
+ :raises RSZeroError: in the unlikely event when *r* parameter or
+ *s* parameter of the created signature is equal 0, as that would
leak the key. Caller should try a better entropy source, retry with
- different 'k', or use the
+ different ``k``, or use the
:func:`~SigningKey.sign_deterministic` in such case.
:return: encoded signature of the hash of `data`
@@ -1597,7 +1538,7 @@ class SigningKey(object):
instead of this one.
:param digest: hash value that will be signed
- :type digest: bytes like object
+ :type digest: :term:`bytes-like object`
:param callable entropy: randomness source, os.urandom by default
:param sigencode: function used to encode the signature.
The function needs to accept three parameters: the two integers