Skip to content

Commit 0d26993

Browse files
committed
expand on ECC basics
1 parent 5151271 commit 0d26993

File tree

1 file changed

+77
-14
lines changed

1 file changed

+77
-14
lines changed

docs/source/basics.rst

Lines changed: 77 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
==========
2-
ECC basics
3-
==========
1+
======================
2+
Basics of ECC handling
3+
======================
44

55
The :term:`ECC`, as any asymmetric cryptography system, deals with private
66
keys and public keys. Private keys are generally used to create signatures,
@@ -14,12 +14,15 @@ The public keys on the other hand are widely distributed, and they don't
1414
have to be kept private. The primary purpose of them, is to allow
1515
checking if a given signature was made with the associated private key.
1616

17+
Number representations
18+
======================
19+
1720
On a more low level, the private key is a single number, usually the
1821
size of the curve size: a NIST P-256 private key will have a size of 256 bits,
1922
though as it needs to be selected randomly, it may be a slightly smaller
2023
number (255-bit, 248-bit, etc.).
2124
Public points are a pair of numbers. That pair specifies a point on an
22-
elliptic curve (a pair of points that satisfy the curve equation).
25+
elliptic curve (a pair of integers that satisfy the curve equation).
2326
Those two numbers are similarly close in size to the curve size, so both the
2427
``x`` and ``y`` coordinate of a NIST P-256 curve will also be around 256 bit in
2528
size.
@@ -31,8 +34,8 @@ size.
3134
size of the *prime* used for operations on points. While the *order* and
3235
the *prime* size are related and fairly close in size, it's possible
3336
to have a curve where either of them is larger by a bit (i.e.
34-
it's possible to have a curve that uses a 256 bit prime that has a 257 bit
35-
order).
37+
it's possible to have a curve that uses a 256 bit *prime* that has a 257 bit
38+
*order*).
3639

3740
Since normally computers work with much smaller numbers, like 32 bit or 64 bit,
3841
we need to use special approaches to represent numbers that are hundreds of
@@ -41,7 +44,7 @@ bits large.
4144
First is to decide if the numbers should be stored in a big
4245
endian format, or in little endian format. In big endian, the most
4346
significant bits are stored first, so a number like :math:`2^{16}` is saved
44-
as a three of byes: byte with value 1 and two bytes with value 0.
47+
as a three bytes: byte with value 1 and two bytes with value 0.
4548
In little endian format the least significant bits are stored first, so
4649
the number like :math:`2^{16}` would be stored as three bytes:
4750
first two bytes with value 0, than a byte with value 1.
@@ -57,13 +60,16 @@ sizes (so even if the number encoded is 1, but the curve used is 128 bit large,
5760
that number 1 still needs to be encoded with 16 bytes, with fifteen most
5861
significant bytes equal zero).
5962

63+
Public key encoding
64+
===================
65+
6066
Generally, public keys (i.e. points) are expressed as fixed size byte strings.
6167

6268
While public keys can be saved as two integers, one to represent the
6369
``x`` coordinate and one to represent ``y`` coordinate, that actually
6470
provides a lot of redundancy. Because of the specifics of elliptic curves,
6571
for every valid ``x`` value there are only two valid ``y`` values.
66-
Moreover, if you have an ``x`` values, you can compute those two possible
72+
Moreover, if you have an ``x`` value, you can compute those two possible
6773
``y`` values (if they exist).
6874
As such, it's possible to save just the ``x`` coordinate and the sign
6975
of the ``y`` coordinate (as the two possible values are negatives of
@@ -79,11 +85,15 @@ That gives us few options to represent the public point, the most common are:
7985
3. As a fixed-length big-endian integer representing the ``x`` coordinate
8086
prefixed with the byte representing the combined type of the encoding
8187
and the sign of the ``y`` coordinate, so called :term:`compressed`
82-
point representation.
88+
point representation (the type is then represented by a 0x02 or a 0x03
89+
byte).
90+
91+
Interoperable file formats
92+
==========================
8393

8494
Now, while we can save the byte strings as-is and "remember" which curve
8595
was used to generate those private and public keys, interoperability usually
86-
requires us to also save information about the curve together with the
96+
requires to also save information about the curve together with the
8797
corresponding key. Here too there are many ways to do it:
8898
save the parameters of the used curve explicitly, use the name of the
8999
well-known curve as a string, use a numerical identifier of the well-known
@@ -93,7 +103,60 @@ For public keys the most interoperable format is the one described
93103
in RFC5912 (look for SubjectPublicKeyInfo structure).
94104
For private keys, the RFC5915 format (also known as the ssleay format)
95105
and the PKCS#8 format (described in RFC5958) are the most popular.
96-
All of those specify a binary encoding, called DER, which can use
97-
bytes with any values. For some uses it's useful to limit byte use
98-
to printable characters, then the PEM formatting of the DER-encoded data
99-
can be used.
106+
107+
All three formats effectively support two ways of providing the information
108+
about the curve used: by specifying the curve parameters explicitly or
109+
by specifying the curve using ASN.1 OBJECT IDENTIFIER (OID), which is
110+
called ``named_curve``. ASN.1 OIDs are a hierarchical system of representing
111+
types of objects, for example, NIST P-256 curve is identified by the
112+
1.2.840.10045.3.1.7 OID (in dotted-decimal formatting of the OID, also
113+
known by the ``prime256v1`` OID node name or short name). Those OIDs
114+
uniquely, identify a particular curve, but the receiver needs to know
115+
which numerical OID maps to which curve parameters. Thus the prospect of
116+
using the explicit encoding, where all the needed parameters are provided
117+
is tempting, the downside is that curve parameters may specify a *weak*
118+
curve, which is easy to attack and break (that is to deduce the private key
119+
from the public key). To verify curve parameters is complex and computationally
120+
expensive, thus generally protocols use few specific curves and require
121+
all implementations to carry the parameters of them. As such, use of
122+
``named_curve`` parameters is generally recommended.
123+
124+
All of the mentioned formats specify a binary encoding, called DER. That
125+
encoding uses bytes with all possible numerical values, which means it's not
126+
possible to embed it directly in text files. For uses where it's useful to
127+
limit bytes to printable characters, so that the keys can be embedded in text
128+
files or text-only protocols (like email), the PEM formatting of the
129+
DER-encoded data can be used. The PEM formatting is just a base64 encoding
130+
with appropriate header and footer.
131+
132+
Signature formats
133+
=================
134+
135+
Finally, ECDSA signatures at the lowest level are a pair of numbers, usually
136+
called ``r`` and ``s``. While they are the ``x`` coordinates of special
137+
points on the curve, they are saved modulo *order* of the curve, not
138+
modulo *prime* of the curve (as a coordinate needs to be).
139+
140+
That again means we have multiple ways of encoding those two numbers.
141+
The two most popular formats are to save them as a concatenation of big-endian
142+
integers of fixed size (determined by the curve *order*) or as a DER
143+
structure with two INTEGERS.
144+
The first of those is called the :term:``raw encoding`` inside the Python
145+
ecdsa library.
146+
147+
As ASN.1 signature format requires the encoding of INTEGERS, and DER INTEGERs
148+
must use the fewest possible number of bytes, a numerically small value of
149+
``r`` or ``s`` will require fewer
150+
bytes to represent in the DER structure. Thus, DER encoding isn't fixed
151+
size for a given curve, but has a maximum possible size.
152+
153+
.. note::
154+
155+
As DER INTEGER uses so-called two's complement representation of
156+
numbers, the most significant bit of the most significant byte
157+
represents the *sign* of the number. If that bit is set, then the
158+
number is considered to be negative. Thus, to represent a number like
159+
255, which in binary representation is 0b11111111 (i.e. a byte with all
160+
bits set high), the DER encoding of it will require two bytes, one
161+
zero byte to make sure the sign bit is 0, and a byte with value 255 to
162+
encode the numerical value of the integer.

0 commit comments

Comments
 (0)