@@ -36,6 +36,7 @@
from Crypto.Util.number import long_to_bytes, bytes_to_long
from Crypto.Random import get_random_bytes as rng


def _mult_gf2(f1, f2):
"""Multiply two polynomials in GF(2)"""

@@ -94,14 +95,15 @@ def __init__(self, encoded_value):
else:
raise ValueError("The encoded value must be an integer or a 16 byte string")

def __eq__(self, other):
return self._value == other._value

def __int__(self):
"""Return the field element, encoded as a 128-bit integer."""

return self._value

def encode(self):
"""Return the field element, encoded as a 16 byte string."""

return long_to_bytes(self._value, 16)

def __mul__(self, factor):
@@ -115,14 +117,17 @@ def __mul__(self, factor):

if self.irr_poly in (f1, f2):
return _Element(0)

mask1 = 2 ** 128
v, z = f1, 0
while f2:
if f2 & 1:
z ^= v
# if f2 ^ 1: z ^= v
mask2 = int(bin(f2 & 1)[2:] * 128, base=2)
z = (mask2 & (z ^ v)) | ((mask1 - mask2 - 1) & z)
v <<= 1
if v & mask1:
v ^= self.irr_poly
# if v & mask1: v ^= self.irr_poly
mask3 = int(bin((v >> 128) & 1)[2:] * 128, base=2)
v = (mask3 & (v ^ self.irr_poly)) | ((mask1 - mask3 - 1) & v)
f2 >>= 1
return _Element(z)

@@ -135,6 +140,9 @@ def inverse(self):
# We use the Extended GCD algorithm
# http://en.wikipedia.org/wiki/Polynomial_greatest_common_divisor

if self._value == 0:
raise ValueError("Inversion of zero")

r0, r1 = self._value, self.irr_poly
s0, s1 = 1, 0
while r1 > 0:
@@ -143,113 +151,47 @@ def inverse(self):
s0, s1 = s1, s0 ^ _mult_gf2(q, s1)
return _Element(s0)

def __pow__(self, exponent):
result = _Element(self._value)
for _ in range(exponent - 1):
result = result * self
return result


class Shamir(object):
"""Shamir's secret sharing scheme.
This class implements the Shamir's secret sharing protocol
described in his original paper `"How to share a secret"`__.
All shares are points over a 2-dimensional curve. At least
*k* points (that is, shares) are required to reconstruct the curve,
and therefore the secret.
This implementation is primarilly meant to protect AES128 keys.
To that end, the secret is associated to a curve in
the field GF(2^128) defined by the irreducible polynomial
:math:`x^{128} + x^7 + x^2 + x + 1` (the same used in AES-GCM).
The shares are always 16 bytes long.
Data produced by this implementation are compatible to the popular
`ssss`_ tool if used with 128 bit security (parameter *"-s 128"*)
and no dispersion (parameter *"-D"*).
As an example, the following code shows how to protect a file meant
for 5 people, in such a way that 2 of the 5 are required to
reassemble it::
>>> from binascii import hexlify
>>> from Crypto.Cipher import AES
>>> from Crypto.Random import get_random_bytes
>>> from Crypto.Protocol.secret_sharing import Shamir
>>>
>>> key = get_random_bytes(16)
>>> shares = Shamir.split(2, 5, key)
>>> for idx, share in shares:
>>> print "Index #%d: %s" % (idx, hexlify(share))
>>>
>>> fi = open("clear_file.txt", "rb")
>>> fo = open("enc_file.txt", "wb")
>>>
>>> cipher = AES.new(key, AES.MODE_EAX)
>>> ct, tag = cipher.encrypt(fi.read()), cipher.digest()
>>> fo.write(nonce + tag + ct)
Each person can be given one share and the encrypted file.
When 2 people gather together with their shares, the can
decrypt the file::
>>> from binascii import unhexlify
>>> from Crypto.Cipher import AES
>>> from Crypto.Protocol.secret_sharing import Shamir
>>>
>>> shares = []
>>> for x in range(2):
>>> in_str = raw_input("Enter index and share separated by comma: ")
>>> idx, share = [ strip(s) for s in in_str.split(",") ]
>>> shares.append((idx, unhexlify(share)))
>>> key = Shamir.combine(shares)
>>>
>>> fi = open("enc_file.txt", "rb")
>>> nonce, tag = [ fi.read(16) for x in range(2) ]
>>> cipher = AES.new(key, AES.MODE_EAX, nonce)
>>> try:
>>> result = cipher.decrypt(fi.read())
>>> cipher.verify(tag)
>>> with open("clear_file2.txt", "wb") as fo:
>>> fo.write(result)
>>> except ValueError:
>>> print "The shares were incorrect"
.. attention::
Reconstruction does not guarantee that the result is authentic.
In particular, a malicious participant in the scheme has the
ability to force an algebric transformation on the result by
manipulating her share.
It is important to use the scheme in combination with an
authentication mechanism (the EAX cipher mode in the example).
.. __: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.80.8910&rep=rep1&type=pdf
.. _ssss: http://point-at-infinity.org/ssss/
A secret is split into ``n`` shares, and it is sufficient to collect
``k`` of them to reconstruct the secret.
"""

@staticmethod
def split(k, n, secret):
"""Split a secret into *n* shares.
def split(k, n, secret, ssss=False):
"""Split a secret into ``n`` shares.
The secret can be reconstructed later when *k* shares
out of the original *n* are recombined. Each share
must be kept confidential to the person it was
The secret can be reconstructed later using just ``k`` shares
out of the original ``n``.
Each share must be kept confidential to the person it was
assigned to.
Each share is associated to an index (starting from 1),
which must be presented when the secret is recombined.
Each share is associated to an index (starting from 1).
Args:
k (integer):
The number of shares that must be present in order to reconstruct
the secret.
The sufficient number of shares to reconstruct the secret (``k < n``).
n (integer):
The total number of shares to create (larger than *k*).
The number of shares that this method will create.
secret (byte string):
The 16 byte string (e.g. the AES128 key) to split.
A byte string of 16 bytes (e.g. the AES 128 key).
ssss (bool):
If ``True``, the shares can be used with the ``ssss`` utility.
Default: ``False``.
Return:
*n* tuples, each containing the unique index (an integer) and
the share (a byte string, 16 bytes long) meant for a
participant.
Return (tuples):
``n`` tuples. A tuple is meant for each participant and it contains two items:
1. the unique index (an integer)
2. the share (a byte string, 16 bytes)
"""

#
@@ -261,29 +203,34 @@ def split(k, n, secret):
#

coeffs = [_Element(rng(16)) for i in range(k - 1)]
coeffs.insert(0, _Element(secret))
coeffs.append(_Element(secret))

# Each share is y_i = p(x_i) where x_i is the public index
# associated to each of the n users.

def make_share(user, coeffs):
share, x, idx = [_Element(p) for p in (0, 1, user)]
def make_share(user, coeffs, ssss):
idx = _Element(user)
share = _Element(0)
for coeff in coeffs:
share += coeff * x
x *= idx
share = idx * share + coeff
if ssss:
share += _Element(user) ** len(coeffs)
return share.encode()

return [(i, make_share(i, coeffs)) for i in range(1, n + 1)]
return [(i, make_share(i, coeffs, ssss)) for i in range(1, n + 1)]

@staticmethod
def combine(shares):
def combine(shares, ssss=False):
"""Recombine a secret, if enough shares are presented.
Args:
shares (tuples):
At least *k* tuples, each containin the index (an integer) and
The *k* tuples, each containin the index (an integer) and
the share (a byte string, 16 bytes long) that were assigned to
a participant.
ssss (bool):
If ``True``, the shares were produced by the ``ssss`` utility.
Default: ``False``.
Return:
The original secret, as a byte string (16 bytes long).
@@ -299,26 +246,33 @@ def combine(shares):
# l_j(x) = \prod_{ \overset{0 \le m \le k-1}{m \ne j} }
# \frac{x - x_m}{x_j - x_m}
#
# However, in this case we are purely intersted in the constant
# However, in this case we are purely interested in the constant
# coefficient of L(x).
#

shares = [[_Element(y) for y in x] for x in shares]
k = len(shares)

gf_shares = []
for x in shares:
idx = _Element(x[0])
value = _Element(x[1])
if any(y[0] == idx for y in gf_shares):
raise ValueError("Duplicate share")
if ssss:
value += idx ** k
gf_shares.append((idx, value))

result = _Element(0)
k = len(shares)
for j in range(k):
x_j, y_j = shares[j]
x_j, y_j = gf_shares[j]

coeff_0_l = _Element(0)
while not int(coeff_0_l):
coeff_0_l = _Element(rng(16))
inv = coeff_0_l.inverse()
numerator = _Element(1)
denominator = _Element(1)

for m in range(k):
x_m = shares[m][0]
x_m = gf_shares[m][0]
if m != j:
t = x_m * (x_j + x_m).inverse()
coeff_0_l *= t
result += y_j * coeff_0_l * inv
numerator *= x_m
denominator *= x_j + x_m
result += y_j * numerator * denominator.inverse()
return result.encode()
@@ -1,20 +1,22 @@
from typing import Union, List, Tuple
from typing import Union, List, Tuple, Optional

def _mult_gf2(f1: int, f2: int) -> int : ...
def _div_gf2(a: int, b: int) -> int : ...

class _Element(object):
irr_poly: int
def __init__(self, encoded_value: Union[int, bytes]) -> None: ...
def __eq__(self, other) -> bool: ...
def __int__(self) -> int: ...
def encode(self) -> bytes: ...
def __mul__(self, factor: int) -> _Element: ...
def __add__(self, term: _Element) -> _Element: ...
def inverse(self) -> _Element: ...
def __pow__(self, exponent) -> _Element: ...

class Shamir(object):
@staticmethod
def split(k: int, n: int, secret: bytes) -> List[Tuple[int, bytes]]: ...
def split(k: int, n: int, secret: bytes, ssss: Optional[bool]) -> List[Tuple[int, bytes]]: ...
@staticmethod
def combine(shares: List[Tuple[int, bytes]]) -> bytes: ...
def combine(shares: List[Tuple[int, bytes]], ssss: Optional[bool]) -> bytes: ...

@@ -249,6 +249,26 @@ def test_output_param(self):
self.assertEqual(pt, output)
self.assertEqual(res, None)


def test_output_param_same_buffer(self):

pt = b'5' * 16
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
ct = cipher.encrypt(pt)

pt_ba = bytearray(pt)
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
res = cipher.encrypt(pt_ba, output=pt_ba)
self.assertEqual(ct, pt_ba)
self.assertEqual(res, None)

ct_ba = bytearray(ct)
cipher = AES.new(b'4'*16, self.aes_mode, iv=self.iv_128)
res = cipher.decrypt(ct_ba, output=ct_ba)
self.assertEqual(pt, ct_ba)
self.assertEqual(res, None)


def test_output_param_memoryview(self):

pt = b'5' * 16
@@ -120,6 +120,9 @@ def test_unaligned_data_64(self):
def test_output_param(self):
pass

def test_output_param_same_buffer(self):
pass

def test_output_param_memoryview(self):
pass

@@ -33,6 +33,7 @@

"""Self-test for Math.Numbers"""

import sys
import unittest

from Crypto.SelfTest.st_common import list_test_cases
@@ -749,8 +750,10 @@ def setUp(self):

tests += list_test_cases(TestIntegerGMP)
except (ImportError, OSError) as e:
import sys
sys.stdout.write("Skipping GMP tests (%s)\n" % str(e) )
if sys.platform == "win32":
sys.stdout.write("Skipping GMP tests on Windows\n")
else:
sys.stdout.write("Skipping GMP tests (%s)\n" % str(e) )

try:
from Crypto.Math._IntegerCustom import IntegerCustom
@@ -761,7 +764,6 @@ def setUp(self):

tests += list_test_cases(TestIntegerCustomModexp)
except (ImportError, OSError) as e:
import sys
sys.stdout.write("Skipping custom modexp tests (%s)\n" % str(e) )

tests += list_test_cases(testIntegerRandom)
@@ -141,21 +141,89 @@ def test1(self):

def test2(self):
# Test recombine

# These shares were obtained with ssss v0.5:
# ssss-split -t 2 -n 3 -s 128 -D -x
secret = b("000102030405060708090a0b0c0d0e0f")
shares = (
(1,"0b8cbb92e2a750defa563537d72942a2"),
(2,"171a7120c941abb4ecb77472ba459753"),
(3,"1c97c8b12fe3fd6d1ee84b4e6161dbfe")
)

bin_shares = []
for share in shares:
bin_shares.append((share[0], unhexlify(b(share[1]))))
result = Shamir.combine(bin_shares)
self.assertEqual(hexlify(result), secret)
from itertools import permutations

test_vectors = (
(2, "d9fe73909bae28b3757854c0af7ad405",
"1-594ae8964294174d95c33756d2504170",
"2-d897459d29da574eb40e93ec552ffe6e",
"3-5823de9bf0e068b054b5f07a28056b1b",
"4-db2c1f8bff46d748f795da995bd080cb"),
(2, "bf4f902d9a7efafd1f3ffd9291fd5de9",
"1-557bd3b0748064b533469722d1cc7935",
"2-6b2717164783c66d47cd28f2119f14d0",
"3-8113548ba97d58256bb4424251ae300c",
"4-179e9e5a218483ddaeda57539139cf04"),
(3, "ec96aa5c14c9faa699354cf1da74e904",
"1-64579fbf1908d66f7239bf6e2b4e41e1",
"2-6cd9428df8017b52322561e8c672ae3e",
"3-e418776ef5c0579bd9299277374806dd",
"4-ab3f77a0107398d23b323e581bb43f5d",
"5-23fe42431db2b41bd03ecdc7ea8e97ac"),
(3, "44cf249b68b80fcdc27b47be60c2c145",
"1-d6515a3905cd755119b86e311c801e31",
"2-16693d9ac9f10c254036ced5f8917fa3",
"3-84f74338a48476b99bf5e75a84d3a0d1",
"4-3fe8878dc4a5d35811cf3cbcd33dbe52",
"5-ad76f92fa9d0a9c4ca0c1533af7f6132"),
(5, "5398717c982db935d968eebe53a47f5a",
"1-be7be2dd4c068e7ef576aaa1b1c11b01",
"2-f821f5848441cb98b3eb467e2733ee21",
"3-25ee52f53e203f6e29a0297b5ab486b5",
"4-fc9fb58ef74dab947fbf9acd9d5d83cd",
"5-b1949cce46d81552e65f248d3f74cc5c",
"6-d64797f59977c4d4a7956ad916da7699",
"7-ab608a6546a8b9af8820ff832b1135c7"),
(5, "4a78db90fbf35da5545d2fb728e87596",
"1-08daf9a25d8aa184cfbf02b30a0ed6a0",
"2-dda28261e36f0b14168c2cf153fb734e",
"3-e9fdec5505d674a57f9836c417c1ecaa",
"4-4dce5636ae06dee42d2c82e65f06c735",
"5-3963dc118afc2ba798fa1d452b28ef00",
"6-6dfe6ff5b09e94d2f84c382b12f42424",
"7-6faea9d4d4a4e201bf6c90b9000630c3"),
(10, "eccbf6d66d680b49b073c4f1ddf804aa",
"01-7d8ac32fe4ae209ead1f3220fda34466",
"02-f9144e76988aad647d2e61353a6e96d5",
"03-b14c3b80179203363922d60760271c98",
"04-770bb2a8c28f6cee89e00f4d5cc7f861",
"05-6e3d7073ea368334ef67467871c66799",
"06-248792bc74a98ce024477c13c8fb5f8d",
"07-fcea4640d2db820c0604851e293d2487",
"08-2776c36fb714bb1f8525a0be36fc7dba",
"09-6ee7ac8be773e473a4bf75ee5f065762",
"10-33657fc073354cf91d4a68c735aacfc8",
"11-7645c65094a5868bf225c516fdee2d0c",
"12-840485aacb8226631ecd9c70e3018086"),
(10, "377e63bdbb5f7d4dc58a483d035212bb",
"01-32c53260103be431c843b1a633afe3bd",
"02-0107eb16cb8695084d452d2cc50bc7d6",
"03-df1e5c66cd755287fb0446faccd72a06",
"04-361bbcd5d40797f49dfa1898652da197",
"05-160d3ad1512f7dec7fd9344aed318591",
"06-659af6d95df4f25beca4fb9bfee3b7e8",
"07-37f3b208977bad50b3724566b72bfa9d",
"08-6c1de2dfc69c2986142c26a8248eb316",
"09-5e19220837a396bd4bc8cd685ff314c3",
"10-86e7b864fb0f3d628e46d50c1ba92f1c",
"11-065d0082c80b1aea18f4abe0c49df72e",
"12-84a09430c1d20ea9f388f3123c3733a3"),
)

def get_share(p):
pos = p.find('-')
return int(p[:pos]), unhexlify(p[pos + 1:])

for tv in test_vectors:
k = tv[0]
secret = unhexlify(tv[1])
max_perms = 10
for perm, shares_idx in enumerate(permutations(range(2, len(tv)), k)):
if perm > max_perms:
break
shares = [ get_share(tv[x]) for x in shares_idx ]
result = Shamir.combine(shares, True)
self.assertEqual(secret, result)

def test3(self):
# Loopback split/recombine
@@ -169,8 +237,21 @@ def test3(self):
secret3 = Shamir.combine([ shares[0], shares[2] ])
self.assertEqual(secret, secret3)

secret4 = Shamir.combine(shares)
self.assertEqual(secret, secret4) # One share too many
def test4(self):
# Loopback split/recombine (SSSS)
secret = unhexlify(b("000102030405060708090a0b0c0d0e0f"))

shares = Shamir.split(2, 3, secret, ssss=True)

secret2 = Shamir.combine(shares[:2], ssss=True)
self.assertEqual(secret, secret2)

def test5(self):
# Detect duplicate shares
secret = unhexlify(b("000102030405060708090a0b0c0d0e0f"))

shares = Shamir.split(2, 3, secret)
self.assertRaises(ValueError, Shamir.combine, (shares[0], shares[0]))


def get_tests(config={}):
@@ -109,8 +109,12 @@ def sign(self, msg_hash):
for x in sig_pair])
else:
# Dss-sig ::= SEQUENCE {
# r OCTET STRING,
# s OCTET STRING
# r INTEGER,
# s INTEGER
# }
# Ecdsa-Sig-Value ::= SEQUENCE {
# r INTEGER,
# s INTEGER
# }
output = DerSequence(sig_pair).encode()

@@ -353,14 +357,14 @@ def new(key, mode, encoding='binary', randfunc=None):
- *'binary'* (default), the signature is the raw concatenation
of ``r`` and ``s``. It is defined in the IEEE P.1363 standard.
For DSA, the size in bytes of the signature is ``N/4``
(e.g. 64 bytes for ``N=256``).
For DSA, the size in bytes of the signature is ``N/4`` bytes
(e.g. 64 for ``N=256``).
For ECDSA, the signature is always twice the length of a point
coordinate (e.g. 64 bytes for P-256).
- *'der'*, the signature is an ASN.1 SEQUENCE with two
INTEGERs (``r`` and ``s``) encoded with DER.
- *'der'*, the signature is a ASN.1 DER SEQUENCE
with two INTEGERs (``r`` and ``s``). It is defined in RFC3279_.
The size of the signature is variable.
:type encoding: string
@@ -374,6 +378,7 @@ def new(key, mode, encoding='binary', randfunc=None):
.. _FIPS 186-4: http://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
.. _NIST SP 800 Part 1 Rev 4: http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-57pt1r4.pdf
.. _RFC6979: http://tools.ietf.org/html/rfc6979
.. _RFC3279: https://tools.ietf.org/html/rfc3279#section-2.2.2
"""

# The goal of the 'mode' parameter is to avoid to
@@ -169,10 +169,15 @@ def VoidPointer():
from ctypes import Array as _Array

null_pointer = None
cached_architecture = []

def load_lib(name, cdecl):
import platform
bits, linkage = platform.architecture()
if not cached_architecture:
# platform.architecture() creates a subprocess, so caching the
# result makes successive imports faster.
import platform
cached_architecture[:] = platform.architecture()
bits, linkage = cached_architecture
if "." not in name and not linkage.startswith("Win"):
full_name = find_library(name)
if full_name is None:
@@ -1,6 +1,6 @@
__all__ = ['Cipher', 'Hash', 'Protocol', 'PublicKey', 'Util', 'Signature',
'IO', 'Math']

version_info = (3, 9, '7')
version_info = (3, 9, '8')

__version__ = ".".join([str(x) for x in version_info])
@@ -424,7 +424,9 @@ def create_cryptodome_lib():
# ECC
Extension("Crypto.PublicKey._ec_ws",
include_dirs=['src/'],
sources=['src/modexp_utils.c', 'src/siphash.c', 'src/ec_ws.c', 'src/mont.c'],
sources=['src/modexp_utils.c', 'src/siphash.c', 'src/ec_ws.c',
'src/mont.c', 'src/p256_table.c', 'src/p384_table.c',
'src/p521_table.c'],
),

# Math
@@ -19,23 +19,14 @@ modexp_in: modexp.c mont.c modexp_utils.c siphash.c
modexp: modexp.c mont.c modexp_utils.c siphash.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DPROFILE

p256_table.c: make_p256_table
./make_p256_table > $@
p256_table.c: make_ecc_table.py
python make_ecc_table.py p256 5 p256_table

p384_table.c: make_p384_table
./make_p384_table > $@
p384_table.c: make_ecc_table.py
python make_ecc_table.py p384 5 p384_table

p521_table.c: make_p521_table
./make_p521_table > $@

make_p256_table: make_p256_table.c ec_ws.c mont.c modexp_utils.c siphash.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DMAKE_TABLE

make_p384_table: make_p384_table.c ec_ws.c mont.c modexp_utils.c siphash.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DMAKE_TABLE

make_p521_table: make_p521_table.c ec_ws.c mont.c modexp_utils.c siphash.c
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=64 -DMAKE_TABLE
p521_table.c: make_ecc_table.py
python make_ecc_table.py p521 4 p521_table

clean::
rm -f ec_ws_p256 ec_ws_p384 ec_ws_p521 mont.o modexp make_p256_table make_p384_table make_p521_table
rm -f ec_ws_p256 ec_ws_p384 ec_ws_p521 mont.o modexp
@@ -37,6 +37,10 @@
#include "mont.h"
#include "ec.h"

#include "p256_table.h"
#include "p384_table.h"
#include "p521_table.h"

FAKE_INIT(ec_ws)

#ifdef MAIN
@@ -566,11 +570,6 @@ STATIC int ec_scalar(uint64_t *x3, uint64_t *y3, uint64_t *z3,
return res;
}

#ifndef MAKE_TABLE
#include "p256_table.c"
#include "p384_table.c"
#include "p521_table.c"

STATIC void free_g_p256(ProtMemory **prot_g)
{
unsigned i;
@@ -870,8 +869,6 @@ STATIC int ec_scalar_g_p521(uint64_t *x3, uint64_t *y3, uint64_t *z3,
return 0;
}

#endif

/*
* Create an Elliptic Curve context for Weierstress curves y²=x³+ax+b with a=-3
*
@@ -922,7 +919,6 @@ EXPORT_SYM int ec_ws_new_context(EcContext **pec_ctx,
}
bytes_to_words(ec_ctx->order, order_words, order, len);

#ifndef MAKE_TABLE
/* Scramble lookup table for special generators */
switch (ctx->modulus_type) {
case ModulusP256: {
@@ -952,7 +948,6 @@ EXPORT_SYM int ec_ws_new_context(EcContext **pec_ctx,
case ModulusGeneric:
break;
}
#endif

return 0;

@@ -968,7 +963,6 @@ EXPORT_SYM void ec_free_context(EcContext *ec_ctx)
{
if (NULL == ec_ctx)
return;
#ifndef MAKE_TABLE
switch (ec_ctx->mont_ctx->modulus_type) {
case ModulusP256:
free_g_p256(ec_ctx->prot_g);
@@ -982,7 +976,6 @@ EXPORT_SYM void ec_free_context(EcContext *ec_ctx)
case ModulusGeneric:
break;
}
#endif
free(ec_ctx->b);
free(ec_ctx->order);
mont_context_free(ec_ctx->mont_ctx);
@@ -1305,7 +1298,6 @@ EXPORT_SYM int ec_ws_scalar(EcPoint *ecp, const uint8_t *k, size_t len, uint64_t
goto cleanup;
}

#ifndef MAKE_TABLE
switch (ctx->modulus_type) {
case ModulusP256: {
/** Coordinates in Montgomery form **/
@@ -1388,7 +1380,6 @@ EXPORT_SYM int ec_ws_scalar(EcPoint *ecp, const uint8_t *k, size_t len, uint64_t
case ModulusGeneric:
break;
}
#endif

if (seed != 0) {
uint8_t *blind_scalar=NULL;
@@ -0,0 +1,163 @@
#!/usr/bin/python

import argparse

declaration = """\
/* This file was automatically generated, do not edit */
#include "common.h"
extern const unsigned {0}_n_tables;
extern const unsigned {0}_window_size;
extern const unsigned {0}_points_per_table;
extern const uint64_t {0}_tables[{1}][{2}][2][{3}];
"""

definition = """\
/* This file was automatically generated, do not edit */
#include "common.h"
const unsigned {0}_n_tables = {1};
const unsigned {0}_window_size = {2};
const unsigned {0}_points_per_table = {3};
/* {4} */
/* Table size: {5} kbytes */
const uint64_t {0}_tables[{1}][{3}][2][{6}] = {{\
"""

point = """\
{{ /* Point #{0} */
{{ {1} }},
{{ {2} }}
}}{3}\
"""

parser = argparse.ArgumentParser()
parser.add_argument("curve")
parser.add_argument("window_size", type=int)
parser.add_argument("basename")
args = parser.parse_args()

if args.curve == "p256":
bits = 256
p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff
Gx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296
Gy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5
msg = "Affine coordinates in Montgomery form"
elif args.curve == "p384":
bits = 384
p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff
Gx = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760aB7
Gy = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5F
msg = "Affine coordinates in Montgomery form"
elif args.curve == "p521":
bits = 521
p = 0x000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
Gx = 0x000000c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66
Gy = 0x0000011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650
msg = "Affine coordinates in plain form (not Montgomery)"
else:
raise ValueError("Unsupported curve: " + args.curve)


c_file = open(args.basename + ".c", "wt")
h_file = open(args.basename + ".h", "wt")

words = (bits + 63) // 64
window_size = args.window_size
points_per_table = 2**window_size
n_tables = (bits + window_size - 1) // window_size
byte_size = n_tables * points_per_table * 2 * (bits // 64) * (64 // 8) // 1024
G = Gx, Gy


def double(X1, Y1):
if X1 == 0 and Y1 == 0:
return (0, 0)

XX = pow(X1, 2, p)
w = -3 + 3 * XX
Y1Y1 = pow(Y1, 2, p)
R = 2 * Y1Y1
sss = 4 * Y1 * R
RR = pow(R, 2, p)
B = pow(X1 + R, 2, p) - XX - RR
h = pow(w, 2, p) - 2 * B
X3 = 2 * h * Y1 % p
Y3 = w * (B - h) - 2 * RR % p
Z3 = sss

Z3inv = pow(Z3, p - 2, p)
x3 = X3 * Z3inv % p
y3 = Y3 * Z3inv % p
return (x3, y3)


def add(X1, Y1, X2, Y2):
if X1 == 0 and Y1 == 0:
return (X2, Y2)
if X1 == X2 and Y1 == Y2:
return double(X1, Y1)
if X1 == X2 and (Y1 + Y2) % p == 0:
return (0, 0)

u = Y2 - Y1
uu = pow(u, 2, p)
v = X2 - X1
vv = pow(v, 2, p)
vvv = v * vv % p
R = vv * X1 % p
A = uu - vvv - 2 * R
X3 = v * A % p
Y3 = (u * (R - A) - vvv * Y1) % p
Z3 = vvv

Z3inv = pow(Z3, p - 2, p)
x3 = X3 * Z3inv % p
y3 = Y3 * Z3inv % p
return (x3, y3)


def get64(z, words):
"""Return a C string with the number encoded into 64-bit words"""

# Convert to Montgomery form, but only if it's not P521
if words != 9:
R = 2**(words * 64)
x = z * R % p
else:
x = z

result = []
for _ in range(words):
masked = x & ((1 << 64) - 1)
result.append("0x%016XULL" % masked)
x >>= 64
return ",".join(result)


# Create table with points 0, G, 2G, 3G, .. (2**window_size-1)G
window = [(0, 0)]
for _ in range(points_per_table - 1):
new_point = add(*window[-1], *G)
window.append(new_point)

print(declaration.format(args.curve, n_tables, points_per_table, words), file=h_file)
print(definition.format(args.curve, n_tables, window_size, points_per_table, msg,
byte_size, words), file=c_file)

for i in range(n_tables):
print(" { /* Table #%u */" % i, file=c_file)
for j, w in enumerate(window):
endc = "" if (j == points_per_table - 1) else ","
print(point.format(j, get64(w[0], words), get64(w[1], words), endc),
file=c_file)
endc = "" if (i == n_tables - 1) else ","
print(" }%s" % endc, file=c_file)

# Move from G to G*2^{w}
for j in range(window_size):
G = double(*G)

# Update window
for j in range(1, points_per_table):
window[j] = add(*window[j-1], *G)

print("};", file=c_file)

This file was deleted.

This file was deleted.

This file was deleted.

@@ -1,11 +1,11 @@
/* This file was automatically generated, do not edit */
#include "common.h"
static const unsigned p256_n_tables = 52;
static const unsigned p256_window_size = 5;
static const unsigned p256_points_per_table = 32;
const unsigned p256_n_tables = 52;
const unsigned p256_window_size = 5;
const unsigned p256_points_per_table = 32;
/* Affine coordinates in Montgomery form */
/* Table size: 106496 kbytes */
static const uint64_t p256_tables[52][32][2][4] = {
/* Table size: 104 kbytes */
const uint64_t p256_tables[52][32][2][4] = {
{ /* Table #0 */
{ /* Point #0 */
{ 0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL },
@@ -0,0 +1,7 @@
/* This file was automatically generated, do not edit */
#include "common.h"
extern const unsigned p256_n_tables;
extern const unsigned p256_window_size;
extern const unsigned p256_points_per_table;
extern const uint64_t p256_tables[52][32][2][4];

@@ -1,11 +1,11 @@
/* This file was automatically generated, do not edit */
#include "common.h"
static const unsigned p384_n_tables = 77;
static const unsigned p384_window_size = 5;
static const unsigned p384_points_per_table = 32;
const unsigned p384_n_tables = 77;
const unsigned p384_window_size = 5;
const unsigned p384_points_per_table = 32;
/* Affine coordinates in Montgomery form */
/* Table size: 236544 kbytes */
static const uint64_t p384_tables[77][32][2][6] = {
/* Table size: 231 kbytes */
const uint64_t p384_tables[77][32][2][6] = {
{ /* Table #0 */
{ /* Point #0 */
{ 0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL },
@@ -0,0 +1,7 @@
/* This file was automatically generated, do not edit */
#include "common.h"
extern const unsigned p384_n_tables;
extern const unsigned p384_window_size;
extern const unsigned p384_points_per_table;
extern const uint64_t p384_tables[77][32][2][6];

@@ -1,11 +1,11 @@
/* This file was automatically generated, do not edit */
#include "common.h"
static const unsigned p521_n_tables = 131;
static const unsigned p521_window_size = 4;
static const unsigned p521_points_per_table = 16;
const unsigned p521_n_tables = 131;
const unsigned p521_window_size = 4;
const unsigned p521_points_per_table = 16;
/* Affine coordinates in plain form (not Montgomery) */
/* Table size: 301824 kbytes */
static const uint64_t p521_tables[131][16][2][9] = {
/* Table size: 262 kbytes */
const uint64_t p521_tables[131][16][2][9] = {
{ /* Table #0 */
{ /* Point #0 */
{ 0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL,0x0000000000000000ULL },
@@ -0,0 +1,7 @@
/* This file was automatically generated, do not edit */
#include "common.h"
extern const unsigned p521_n_tables;
extern const unsigned p521_window_size;
extern const unsigned p521_points_per_table;
extern const uint64_t p521_tables[131][16][2][9];

@@ -134,22 +134,44 @@ static int CFB_transcrypt(CfbModeState *cfbState,
if (0 != result)
return result;

/* Reset */
/* The next input to the cipher is:
* - the old input shifted left by the segment length
* - filled on the right with the enough ciphertext
*
* If the segment length equals the block length,
* the next input is simply the ciphertext block.
*
* If the segment length equals 1, only the leftmost
* byte of the ciphertext block is used.
*/
memmove(next_iv, next_iv + segment_len, block_len - segment_len);

/*
* The rest of the next input is copied when enough ciphertext is
* available (we need segment_len bytes).
*/
cfbState->usedKeyStream = 0;
}

keyStream = cfbState->keyStream + cfbState->usedKeyStream;
segment = next_iv + block_len - segment_len + cfbState->usedKeyStream;
keyStreamToUse = MIN(segment_len - cfbState->usedKeyStream, data_len);

for (i=0; i<keyStreamToUse; i++, cfbState->usedKeyStream++) {
segment = next_iv + (block_len - (segment_len - cfbState->usedKeyStream));

if (direction == DirDecrypt) {
memcpy(segment, in, keyStreamToUse);
}

for (i=0; i<keyStreamToUse; i++) {
*out++ = *keyStream++ ^ *in++;
}

memcpy(segment, (direction == DirEncrypt ? out : in) - keyStreamToUse, keyStreamToUse);
if (direction == DirEncrypt) {
memcpy(segment, out - keyStreamToUse, keyStreamToUse);
}

data_len -= keyStreamToUse;
cfbState->usedKeyStream += keyStreamToUse;
}

return 0;
@@ -44,10 +44,21 @@ build/modexp_utils.o: ../modexp_utils.c

# ECC

build/tests_ec_ws_64: test_ec_ws.c ../ec_ws.c build/mont_64.o $(UTILS)
TABLES = build/p256_table.o build/p384_table.o build/p521_table.o

build/p256_table.o: ../p256_table.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $^

build/p384_table.o: ../p384_table.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $^

build/p521_table.o: ../p521_table.c
$(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $^

build/tests_ec_ws_64: test_ec_ws.c ../ec_ws.c build/mont_64.o $(TABLES) $(UTILS)
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^

build/tests_ec_ws_32: test_ec_ws.c ../ec_ws.c build/mont_32.o $(UTILS)
build/tests_ec_ws_32: test_ec_ws.c ../ec_ws.c build/mont_32.o $(TABLES) $(UTILS)
$(CC) $(CFLAGS) $(CPPFLAGS) -o $@ $^ -DSYS_BITS=32 -UHAVE_UINT128

# addmul128