Skip to content

Commit

Permalink
sagemathgh-38374: Add framework for key exchange schemes and Diffie-H…
Browse files Browse the repository at this point in the history
…ellman

    
Motivated by (but does not yet close) sagemath#37305. Closes sagemath#11568.

This PR adds a basic framework to add public key exchange schemes to
Sage, and includes an implementation of the Diffie-Hellman primarily as
an example of this new framework.

Open to suggestions to improve the structure of the code. This code was
based on the existing code for public-key encryption in Sage.

### 📝 Checklist

<!-- Put an `x` in all the boxes that apply. -->

- [x] The title is concise and informative.
- [x] The description explains in detail what this PR is about.
- [x] I have linked a relevant issue or discussion.
- [x] I have created tests covering the changes.
- [x] I have updated the documentation and checked the documentation
preview.

### ⌛ Dependencies

None
    
URL: sagemath#38374
Reported by: Vincent Macri
Reviewer(s): grhkm21, grnx, Vincent Macri
  • Loading branch information
Release Manager committed Aug 1, 2024
2 parents cf2f19c + fc1a4dc commit db05c32
Show file tree
Hide file tree
Showing 7 changed files with 476 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/doc/en/reference/cryptography/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,8 @@ Cryptography
sage/crypto/lattice
sage/crypto/lwe

sage/crypto/key_exchange/catalog
sage/crypto/key_exchange/key_exchange_scheme
sage/crypto/key_exchange/diffie_hellman

.. include:: ../footer.txt
9 changes: 9 additions & 0 deletions src/doc/en/reference/references/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3846,6 +3846,10 @@ REFERENCES:
\M. Grötschel, \L Lovász, *Handbook of combinatorics*,
Vol. 1, Chapter 18, 1995
.. [KK2003] T. Kivinen and M. Kojo. *More Modular Exponential (MODP)
Diffie-Hellman groups for Internet Key Exchange (IKE)*, in RFC 3526.
Available at https://www.rfc-editor.org/rfc/rfc3526
.. [KKMMNN1992] S-J. Kang, M. Kashiwara, K. C. Misra, T. Miwa, T. Nakashima,
and A. Nakayashiki. *Affine crystals and vertex models*.
Int. J. Mod. Phys. A, **7** (suppl. 1A), (1992) pp. 449-484.
Expand Down Expand Up @@ -5356,6 +5360,11 @@ REFERENCES:
Mathematical Physics, Analysis and Geometry 1, 171-191 (1998).
:doi:`10.1023/A:1009724323513`
.. [PP2010] \C. Paar and J. Pelzl.
*Understanding Cryptography: A Textbook for Students and Practitioners*.
Springer Berlin, Heidelberg, 2010.
:doi:`10.1007/978-3-642-04101-3`
.. [PPW2013] \D. Perkinson, J. Perlman, and J. Wilmes.
*Primer for the algebraic geometry of sandpiles*.
Tropical and Non-Archimedean
Expand Down
4 changes: 4 additions & 0 deletions src/sage/crypto/all.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,8 @@
'lfsr_autocorrelation',
'lfsr_connection_polynomial',
])


import sage.crypto.key_exchange.catalog as key_exchange

del lazy_import
6 changes: 6 additions & 0 deletions src/sage/crypto/key_exchange/all.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from sage.misc.lazy_import import lazy_import

lazy_import('sage.crypto.key_exchange.diffie_hellman', 'DiffieHellman')
lazy_import('sage.crypto.key_exchange.key_exchange_scheme', 'KeyExchangeScheme')

del lazy_import
23 changes: 23 additions & 0 deletions src/sage/crypto/key_exchange/catalog.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
"""
Index of key exchange schemes
This catalogue includes implementations of key exchange schemes.
Let ``<tab>`` indicate pressing the :kbd:`Tab` key. So begin by typing
``key_exchange.<tab>`` to the see the currently implemented key exchange
schemes.
This catalogue includes the following key exchange schemes:
- :class:`sage.crypto.key_exchange.diffie_hellman.DiffieHellman`
To import these names into the global namespace, use::
sage: from sage.crypto.key_exchange.catalog import *
"""

from sage.misc.lazy_import import lazy_import

lazy_import('sage.crypto.key_exchange.diffie_hellman', 'DiffieHellman')

del lazy_import
325 changes: 325 additions & 0 deletions src/sage/crypto/key_exchange/diffie_hellman.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,325 @@
r"""
Diffie-Hellman Key Exchange Scheme
This module contains a toy implementation of the Diffie-Hellman key exchange
scheme.
AUTHORS:
- Vincent Macri (2024-07-30): initial version
"""
# ****************************************************************************
# Copyright (C) 2024 Vincent Macri <vincent.macri@ucalgary.ca>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# https://www.gnu.org/licenses/
# ****************************************************************************

from sage.misc.superseded import experimental

from sage.crypto.key_exchange.key_exchange_scheme import KeyExchangeScheme

from sage.arith.misc import is_prime
from sage.misc.prandom import randint
from sage.rings.integer import Integer
from sage.rings.finite_rings.finite_field_constructor import GF
from sage.rings.finite_rings.finite_field_prime_modn import \
FiniteField_prime_modn
from sage.rings.finite_rings.integer_mod import IntegerMod_abstract
from sage.structure.proof.proof import WithProof

from typing import Union

class DiffieHellman(KeyExchangeScheme):

@experimental(37305)
def __init__(self, p: Integer, g: Union[Integer, IntegerMod_abstract],
proof: bool = True) -> None:
r"""
Create an instance of the Diffie-Hellman key exchange scheme using the
given prime ``p`` and base ``g``.
INPUT:
- ``p`` -- prime integer defining the field `\GF{p}` that the key
exchanges will be performed over, must be at least 5
- ``g`` -- base for the key exchange, (coerceable to) an element of
`\GF{p}` from `2` to `p - 2`
- ``proof`` -- (default: ``True``) whether to require a proof that
``p`` is prime. If ``False``, a probabilistic test can be used for
checking that ``p`` is prime. This should be set to ``False``
when using large (cryptographic size) primes, otherwise checking
primality will take too long.
.. WARNING::
This is a toy implementation for educational use only! Do not use
this implementation, or any cryptographic features of Sage, in any
setting where security is needed!
REFERENCES:
For more information, see Section 8.1 of [PP2010]_.
EXAMPLES::
sage: DH = key_exchange.DiffieHellman(13, 2)
doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation.
See https://github.com/sagemath/sage/issues/37305 for details.
This is an example of a full key exchange using a cryptographically
large prime. This is the prime from the 8192-bit MODP group in RFC 3526
(see [KK2003]_)::
sage: p = 2^8192 - 2^8128 - 1 + 2^64 * (round(2^8062 * pi) + 4743158)
sage: DH = key_exchange.DiffieHellman(p, 2, proof=False)
sage: alice_sk = DH.generate_secret_key()
sage: alice_pk = DH.generate_public_key(alice_sk)
sage: bob_sk = DH.generate_secret_key()
sage: bob_pk = DH.generate_public_key(bob_sk)
sage: alice_shared_secret = DH.compute_shared_secret(bob_pk, alice_sk)
sage: bob_shared_secret = DH.compute_shared_secret(alice_pk, bob_sk)
sage: alice_shared_secret == bob_shared_secret
True
TESTS::
sage: DH = key_exchange.DiffieHellman(3, 2)
Traceback (most recent call last):
...
ValueError: p must be at least 5
sage: DH = key_exchange.DiffieHellman(5, 0)
Traceback (most recent call last):
...
ValueError: g cannot be 0, 1, or p - 1 (mod p)
sage: DH = key_exchange.DiffieHellman(5, 1)
Traceback (most recent call last):
...
ValueError: g cannot be 0, 1, or p - 1 (mod p)
sage: DH = key_exchange.DiffieHellman(5, 4)
Traceback (most recent call last):
...
ValueError: g cannot be 0, 1, or p - 1 (mod p)
"""

if p < 5:
raise ValueError('p must be at least 5')

if proof:
# The modn implementation checks that ``p`` is prime
self._field = GF(p, impl='modn')
else:
with WithProof('arithmetic', False):
self._field = GF(p, impl='modn')

self._p = p
self._g = self._field(g)

# While these values won't cause mathematical problems, they do
# completely break the security of the Diffie-Hellman scheme.
# g = 0 makes every secret key and shared secret 0
# g = 1 makes every secret key and shared secret 1
# g = -1 makes every secret key and shared secret 1 or -1
if self._g == 0 or self._g == 1 or self._g == p - 1:
raise ValueError('g cannot be 0, 1, or p - 1 (mod p)')

def field(self) -> FiniteField_prime_modn:
"""
Return the field this ``DiffieHellman`` instance is working over.
EXAMPLES::
sage: DH = key_exchange.DiffieHellman(5, 2)
sage: DH.field()
Finite Field of size 5
"""
return self._field

def prime(self) -> Integer:
"""
Return the prime ``p`` for this ``DiffieHellman`` instance.
EXAMPLES::
sage: DH = key_exchange.DiffieHellman(7, 3)
sage: DH.prime()
7
"""
return self._p

def generator(self) -> IntegerMod_abstract:
"""
Return the generator ``g`` for this ``DiffieHellman`` instance.
EXAMPLES::
sage: DH = key_exchange.DiffieHellman(7, 3)
sage: DH.generator()
3
"""
return self._g

def parameters(self) -> tuple[Integer, IntegerMod_abstract]:
"""
Get the parameters ``(p, g)`` for this ``DiffieHellman`` instance.
EXAMPLES::
sage: DH = key_exchange.DiffieHellman(7, 3)
sage: DH.parameters()
(7, 3)
"""
return (self._p, self._g)

def generate_secret_key(self) -> Integer:
"""
Generate a random Diffie-Hellman secret key.
TESTS:
sage: DH = key_exchange.DiffieHellman(7, 2)
sage: keys = [DH.generate_secret_key() for i in range(10)]
sage: all(2 <= i <= 5 for i in keys)
True
"""
return randint(2, self._p - 2)

def generate_public_key(self, secret_key: Integer) -> IntegerMod_abstract:
"""
Generate a Diffie-Hellman public key using the given secret key.
INPUT:
- ``secret_key`` -- the secret key to generate the public key with
EXAMPLES::
sage: DH = key_exchange.DiffieHellman(13, 2)
sage: DH.generate_public_key(4)
3
"""
return self._g**secret_key

def compute_shared_secret(self, pk: IntegerMod_abstract,
sk: Integer) -> IntegerMod_abstract:
"""
Compute the shared secret using the given public key and secret keys.
INPUT:
- ``pk`` -- public key
- ``sk`` -- secret key
EXAMPLES::
sage: DH = key_exchange.DiffieHellman(17, 3)
sage: DH.compute_shared_secret(13, 11)
4
"""
return self._field(pk**sk)

def subgroup_size(self) -> Integer:
"""
Calculates the size of the subgroup of `\\GF{p}` generated by
``self.generator()``.
EXAMPLES:
This is an example of a ``DiffieHellman`` instance where the subgroup
size is `(p - 1) / 2`::
sage: DH = key_exchange.DiffieHellman(47, 2)
sage: DH.subgroup_size()
23
This is an example of a ``DiffieHellman`` instance where the subgroup
size is `p - 1`::
sage: DH = key_exchange.DiffieHellman(47, 5)
sage: DH.subgroup_size()
46
"""
return self._g.multiplicative_order()

def __len__(self) -> int:
"""
Calculates the size of the subgroup of `\\GF{p}` generated by
``self.generator()``. This is a wrapper around `subgroup_size`.
TESTS::
sage: DH = key_exchange.DiffieHellman(53, 9)
sage: len(DH)
26
"""
return int(self.subgroup_size())

def __eq__(self, other) -> bool:
"""
Check if two ``DiffieHellman`` instances have the same parameter set.
TESTS::
sage: DH1 = key_exchange.DiffieHellman(5, 2)
sage: DH2 = key_exchange.DiffieHellman(5, 2)
sage: DH1 == DH2
True
sage: DH1 == 5
False
"""
if isinstance(other, DiffieHellman):
return self.parameters() == other.parameters()
return False

def __hash__(self) -> int:
"""
Compute the hash value of a ``DiffieHellman`` instance.
TESTS::
sage: DH1 = key_exchange.DiffieHellman(7, 3)
sage: DH2 = key_exchange.DiffieHellman(7, 3)
sage: s = set([DH1, DH2])
sage: len(s)
1
"""
return hash((self._p, self._g))

def _repr_(self) -> str:
"""
Get the string representation of the ``DiffieHellman`` instance.
TESTS::
sage: DH = key_exchange.DiffieHellman(7, 3)
sage: DH
Diffie-Hellman key exchange over Finite Field of size 7 with generator 3
"""
return ('Diffie-Hellman key exchange over '
f'{self._field} '
'with generator '
f'{self._g}')

def _latex_(self) -> str:
r"""
Get the LaTeX representation of the ``DiffieHellman`` instance.
TESTS::
sage: DH = key_exchange.DiffieHellman(7, 3)
sage: latex(DH)
\text{Diffie-Hellman key exchange over }\Bold{F}_{7}\text{ with generator }3
"""
return ('\\text{Diffie-Hellman key exchange over }'
f'{self._field._latex_()}'
'\\text{ with generator }'
f'{self._g}')
Loading

0 comments on commit db05c32

Please sign in to comment.