Skip to content

Commit

Permalink
NIST Round 2 (#5)
Browse files Browse the repository at this point in the history
* switch to to_integer and from_integer

* switch to non bitsliced representation

* update NISTKATs

* change parameters and fix some problems with the latest Sage version

* new KATs + remove assert m % 32 == 0

* Versions

* Improve CI

* Fix

* Upgrade

* Upgrade 2

* explicitly mention round 2 in README

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update Makefile

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

* Update python-app.yml

---------

Co-authored-by: Ward Beullens <[email protected]>
Co-authored-by: Sofía Celi <[email protected]>
Co-authored-by: Sofía Celi <[email protected]>
  • Loading branch information
4 people authored Feb 3, 2025
1 parent 7db59d7 commit c975475
Show file tree
Hide file tree
Showing 13 changed files with 1,330 additions and 1,588 deletions.
32 changes: 23 additions & 9 deletions .github/workflows/python-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ name: Python application

on:
push:
branches: [ "main" ]
branches: [ "main", "MAYO1.1" ]
pull_request:
branches: [ "main" ]

Expand All @@ -19,18 +19,32 @@ jobs:

steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
- name: Set up Python 3.12
uses: actions/setup-python@v3
with:
python-version: "3.10"
python-version: "3.12"
- name: Install dependencies
run: |
sudo apt-get update
apt-cache policy sagemath
python -m pip install --upgrade pip
pip install flake8 pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
sudo apt-get install -y make
sudo apt-get install -y sagemath
sage --pip install pycryptodomex
curl -L -O "https://github.com/conda-forge/miniforge/releases/latest/download/Miniforge3-$(uname)-$(uname -m).sh"
bash Miniforge3-$(uname)-$(uname -m).sh -b
source ~/miniforge3/etc/profile.d/conda.sh
conda create -n sage sage python=3.12 -c conda-forge -y
conda run -n sage pip install flake8 pytest
if [ -f requirements.txt ]; then conda run -n sage pip install -r requirements.txt; fi
conda install -y -n sage make -c conda-forge
conda run -n sage sage -pip install pycryptodomex
- name: Check installed versions
run: |
# Print Python version
source $HOME/miniforge3/etc/profile.d/conda.sh
conda run -n sage python --version
conda run -n sage pip --version
conda run -n sage sage --version
- name: Test application
run: |
make test
source $HOME/miniforge3/etc/profile.d/conda.sh
conda activate sage
make test-conda
600 changes: 300 additions & 300 deletions KAT/PQCsignKAT_24_MAYO_1.rsp

Large diffs are not rendered by default.

600 changes: 300 additions & 300 deletions KAT/PQCsignKAT_24_MAYO_2.rsp

Large diffs are not rendered by default.

600 changes: 300 additions & 300 deletions KAT/PQCsignKAT_32_MAYO_3.rsp

Large diffs are not rendered by default.

600 changes: 300 additions & 300 deletions KAT/PQCsignKAT_40_MAYO_5.rsp

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
This code is part of a NIST submission for the PQC signatures call.
```

This is the sage implementation of our MAYO scheme. Learn about it on our [website](https://pqmayo.org/).
This is the sage implementation of the round-2 version fo the MAYO scheme. Learn about it on our [website](https://pqmayo.org/).

*Warning*: This code is a research prototype. Do not use it in production.

Expand All @@ -17,10 +17,10 @@ In order to natively build, run, test and benchmark the library, you will need t

```
Make
Python3 >= 3.9.7
pycryptodomex (please, install this version to avoid bugs with pycrypto.
Python3 >= 3.12
pycryptodomex >= 3.21 (please, install this version to avoid bugs with pycrypto.
Install it on sage by running 'sage --pip install pycryptodomex')
Sage
Sage >= 10.5
```

## Building and running
Expand Down
124 changes: 15 additions & 109 deletions mayo.sage
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,7 @@ try:
decode_matrix, \
decode_matrices, \
encode_matrices, \
partial_encode_matrices, \
partial_decode_matrices, \
upper, \
bitsliced_upper, \
bitsliced_matrices_add, \
bitsliced_matrices_matrix_mul, \
bitsliced_matrix_matrices_mul
upper
from sagelib.aes256_ctr_drbg \
import AES256_CTR_DRBG
from sagelib.aes128_ctr \
Expand All @@ -33,23 +27,6 @@ F16 = GF(16, names=('x',))
assert x**4 + x+1 == 0
R = F16['z']
(z,) = R._first_ngens(1)
# import itertools
# F.<x> = GF(16)
# R.<y> = F[]
# m = 72
# for c0 in F:
# if c0 in ZZ:
# continue
# f0 = y^m + c0
# for w in range(1,3):
# for js in itertools.combinations(list(range(1,m)), w):
# f = f0 + sum(y^j for j in js)
# if f.is_irreducible():
# print(f)

assert (z**64 + x**3*z**3 + x*z**2 + x**3).is_irreducible()
assert (z**96 + x*z**3 + x*z + x).is_irreducible()
assert (z**128 + x*z**4 + x**2*z**3 + x**3*z + x**2).is_irreducible()

# The parameters for the MAYO variants. They are:
# q (the size of the finite field F_q), m (the number of multivariate quadratic polynomials in the public key),
Expand All @@ -58,21 +35,21 @@ assert (z**128 + x*z**4 + x**2*z**3 + x**3*z + x**2).is_irreducible()
DEFAULT_PARAMETERS = {
"mayo_1": {
"name": "mayo1",
"n": 66,
"m": 64,
"n": 86,
"m": 78,
"o": 8,
"k": 9,
"k": 10,
"q": 16,
"sk_salt_bytes": 24,
"pk_bytes": 16,
"digest_bytes": 32,
"f": z**64 + x**3*z**3 + x*z**2 + x**3
"f": z**78 + z**2 + z + x**3
},
"mayo_2": {
"name": "mayo2",
"n": 78,
"n": 81,
"m": 64,
"o": 18,
"o": 17,
"k": 4,
"q": 16,
"sk_salt_bytes": 24,
Expand All @@ -82,27 +59,27 @@ DEFAULT_PARAMETERS = {
},
"mayo_3": {
"name": "mayo3",
"n": 99,
"m": 96,
"n": 118,
"m": 108,
"o": 10,
"k": 11,
"q": 16,
"sk_salt_bytes": 32,
"pk_bytes": 16,
"digest_bytes": 48,
"f": z**96 + x*z**3 + x*z + x
"f": z**108 + (x**2+x+1)*z**3 + z**2 + x**3
},
"mayo_5": {
"name": "mayo5",
"n": 133,
"m": 128,
"n": 154,
"m": 142,
"o": 12,
"k": 12,
"q": 16,
"sk_salt_bytes": 40,
"pk_bytes": 16,
"digest_bytes": 64,
"f": z**128 + x*z**4 + x**2*z**3 + x**3*z + x**2
"f": z**142 + z**3 + x**3*z**2 + x**2
},
}

Expand All @@ -127,8 +104,7 @@ class Mayo:
self.O_bytes = math.ceil((self.n - self.o)*self.o * self.q_bytes)
self.v_bytes = math.ceil((self.n - self.o) * self.q_bytes)
self.r_bytes = math.ceil(self.k*self.o*self.q_bytes)
self.P1_bytes = math.ceil(
self.m*math.comb((self.n-self.o+1), 2) * self.q_bytes)
self.P1_bytes = math.ceil(self.m*math.comb((self.n-self.o+1), 2) * self.q_bytes)
self.P2_bytes = math.ceil(self.m*(self.n - self.o)*self.o * self.q_bytes)
self.P3_bytes = math.ceil(self.m*math.comb((self.o+1), 2) * self.q_bytes)

Expand Down Expand Up @@ -194,6 +170,7 @@ class Mayo:
self.o, self.n-self.o, triangular=True) # {P_i^(1)}_(i in [m]) <- Decode_(P(1))(p[0 : P1_bytes])
p2 = decode_matrices(p[self.P1_bytes:self.P1_bytes+self.P2_bytes],
self.m, self.n-self.o, self.o, triangular=False) # {P_i^(2)}_(i in [m]) <- Decode_(P(2))(p[P1_bytes : P1_bytes + P2_bytes])

# for i from 0 to m − 1 do
# P(3) <- Upper(−O^(T)P_i^(1) O − O^(T)P_i^((2))
p3 = [matrix(F16, self.o, self.o) for _ in range(self.m)]
Expand All @@ -204,40 +181,6 @@ class Mayo:
csk = seed_sk # csk <- seedsk
return csk, cpk

def compact_key_gen_bitsliced(self):
"""
outputs a pair (csk, cpk) in B^{csk_bytes} x B^{cpk_bytes}, where csk and cpk
are compact representations of a secret key and public key
"""
seed_sk = self.random_bytes(self.sk_seed_bytes)

s = shake_256(seed_sk).digest(int(self.pk_seed_bytes + self.O_bytes))
seed_pk = s[:self.pk_seed_bytes]

o = decode_matrix(s[self.pk_seed_bytes:self.pk_seed_bytes +
self.O_bytes], self.n-self.o, self.o)

ctr = AES128_CTR(seed_pk, self.P1_bytes + self.P2_bytes)
p = ctr.aes_ctr_gen()

p1 = partial_decode_matrices(p[:self.P1_bytes], self.m, self.n -
self.o, self.n-self.o, triangular=True)

p2 = partial_decode_matrices(p[self.P1_bytes:self.P1_bytes+self.P2_bytes],
self.m, self.n-self.o, self.o, triangular=False)

p3 = [ [ None for _ in range(self.o)] for _ in range(self.o) ]

# compute p1o + p2
p1o_p2 = bitsliced_matrices_add(bitsliced_matrices_matrix_mul(p1,o),p2)
# compute p3
p3 = bitsliced_matrix_matrices_mul(o.transpose(), p1o_p2)
p3 = bitsliced_upper(p3)

cpk = seed_pk + partial_encode_matrices(p3, self.m, self.o, self.o, triangular=True)
csk = seed_sk
return csk, cpk

def expand_sk(self, csk):
"""
takes as input csk, the compact representation of a secret key, and outputs sk in B^{sk_bytes},
Expand Down Expand Up @@ -270,43 +213,6 @@ class Mayo:
esk = seed_sk + o_bytestring + p[0:self.P1_bytes] + encode_matrices(l, self.m, self.n-self.o, self.o, triangular=False)
return esk

def expand_sk_bitsliced(self, csk):
"""
takes as input csk, the compact representation of a secret key, and outputs sk in B^{sk_bytes},
an expanded representation of the secret key
"""
assert len(csk) == self.csk_bytes

seed_sk = csk
s = shake_256(seed_sk).digest(int(self.pk_seed_bytes + self.O_bytes))
seed_pk = s[:self.pk_seed_bytes]

o_bytestring = s[self.pk_seed_bytes:self.pk_seed_bytes + self.O_bytes]
o = decode_matrix(o_bytestring, self.n-self.o, self.o)

ctr = AES128_CTR(seed_pk, self.P1_bytes + self.P2_bytes)
p = ctr.aes_ctr_gen()

p1 = partial_decode_matrices(p[:self.P1_bytes], self.m, self.n -
self.o, self.n-self.o, triangular=True)

p2 = partial_decode_matrices(p[self.P1_bytes:self.P1_bytes+self.P2_bytes],
self.m, self.n-self.o, self.o, triangular=False)

# compute (p1 + p1^t)
p1_p1t = p1.copy()
for i in range(self.n-self.o):
p1_p1t[i][i] = (0,0,0,0)
for j in range(i+1,self.n-self.o):
p1_p1t[j][i] = p1_p1t[i][j]

# compute (p1 + p1^t)*o + p2
l = bitsliced_matrices_add(bitsliced_matrices_matrix_mul(p1_p1t, o), p2)

esk = seed_sk + o_bytestring + p[:self.P1_bytes] + partial_encode_matrices(l, self.m, self.n-self.o, self.o, triangular=False)

return esk

def expand_pk(self, cpk):
"""
takes as input cpk and outputs pk in B^{pk_bytes}
Expand Down
33 changes: 15 additions & 18 deletions parameter_check.sage
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,33 @@ print("Field with 16 elements is correct")
Kz = PolynomialRing(K,'z')
Kz.inject_variables()

# irreducible polynomials for m = 64, 96, and 128
F64 = z**64 + x**3*z**3 + x*z**2 + x**3
F96 = z**96 + x*z**3 + x*z + x
F128 = z**128 + x*z**4 + x**2*z**3 + x**3*z + x**2
# irreducible polynomials
F = {}
F[64] = z**64 + x**3*z**3 + x*z**2 + x**3
F[78] = z**78 + z**2 + z + x**3
F[108] = z**108 + (x**2+x+1)*z**3 + z**2 + x**3
F[142] = z**142 + z**3 + x**3*z**2 + x**2

assert (F64).is_irreducible()
assert (F96).is_irreducible()
assert (F128).is_irreducible()
print("Polynomials are irreducible")
for m in F:
print("F_"+str(m)+" is irreducible: ", F[m].is_irreducible())


for m,k in [(64, 1), (64, 2), (64, 4), (64, 8), (96, 10), (128, 11)]:
if m == 64:
F = F64
if m == 96:
F = F96
if m == 128:
F = F128
for m,k in [(78, 10), (64, 4), (108, 11), (142, 12)]:
Fm = F[m]

CM = companion_matrix(F)
CM = companion_matrix(Fm)

BM = matrix(K, k*m, k*m)
l = k*(k+1)/2-1
assert(l+1 < m)
for i in range(k):
for j in range(k-1,i-1,-1):
BM[ i*m:(i+1)*m, j*m:(j+1)*m ] = CM**l;
BM[ j*m:(j+1)*m, i*m:(i+1)*m ] = CM**l;
l -= 1

assert(l+1 < m)
assert(BM.rank() == m*k)
rk = BM.rank()
print("m,k,rank:", m,k, rk)
assert(rk == m*k)

print("Parameters are correct")
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1 @@
pycryptodomex==3.16.0
pycryptodomex==3.21.0
9 changes: 2 additions & 7 deletions test_kat.sage
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,7 @@ try:
decode_matrix, \
encode_matrix, \
decode_matrices, \
encode_matrices, \
bitslice_m_vec, \
unbitslice_m_vec, \
partial_encode_matrices, \
partial_decode_matrices, \
bitsliced_mul_add
encode_matrices
from sagelib.mayo \
import setupMayo, \
Mayo1, \
Expand Down Expand Up @@ -62,7 +57,7 @@ class TestDeterministicDRBGTestValues(unittest.TestCase):
Mayo.set_drbg_seed(seed)

# Assert keygen matches
_sk, _pk = Mayo.compact_key_gen_bitsliced()
_sk, _pk = Mayo.compact_key_gen()
self.assertEqual(pk, _pk)
self.assertEqual(sk, _sk)

Expand Down
Loading

0 comments on commit c975475

Please sign in to comment.