Browse Source

implement an x25519 class and add various tests for it

main
John-Mark Gurney 2 years ago
parent
commit
8501f5ab27
1 changed files with 83 additions and 1 deletions
  1. +83
    -1
      lora_comms.py

+ 83
- 1
lora_comms.py View File

@@ -25,6 +25,7 @@
import os
import unittest

from binascii import a2b_hex
from ctypes import Structure, POINTER, CFUNCTYPE, pointer, sizeof
from ctypes import c_uint8, c_uint16, c_ssize_t, c_size_t, c_uint64, c_int
from ctypes import CDLL
@@ -149,6 +150,55 @@ def x25519_base(scalar, clamp):

return bytes(out)

class X25519:
'''Class to wrap the x25519 functions into something a bit more
usable. This provides better key ingestion and better support
for other key formats.

Use either the gen method to generate a random key, or the frombytes
method.

a = X25519.gen()
b = X25519.gen()

a.dh(b.getpub()) == b.dh(a.getpub())

That is, each party generates a key, sends their public part to the
other party, and then uses their received public part as an argument
to the dh method. The resulting value will be shared between the
two parties.
'''

def __init__(self, key):
self.privkey = key
self.pubkey = x25519_base(key, 1)

def dh(self, pub):
'''Perform a DH operation using the public part pub.'''

return x25519_wrap(self.pubkey, self.privkey, pub, 1)

def getpub(self):
'''Get the public part of the key. This is to be sent
to the other party for key exchange.'''

return self.pubkey

def getpriv(self):
return self.privkey

@classmethod
def gen(cls):
'''Generate a random X25519 key.'''

return cls(x25519_genkey())

@classmethod
def frombytes(cls, key):
'''Generate an X25519 key from 32 bytes.'''

return cls(key)

def comms_process_wrap(state, input):
'''A wrapper around comms_process that converts the argument
into the buffer, and the returns the message as a bytes string.
@@ -164,7 +214,39 @@ def comms_process_wrap(state, input):
return outbuf._from()

class TestX25519(unittest.TestCase):
def test_basic(self):
PUBLIC_BYTES = EC_PUBLIC_BYTES
PRIVATE_BYTES = EC_PRIVATE_BYTES

def test_class(self):
key = X25519.gen()

pubkey = key.getpub()
privkey = key.getpriv()

apubkey = x25519_base(privkey, 1)

self.assertEqual(apubkey, pubkey)
self.assertEqual(X25519.frombytes(privkey).getpub(), pubkey)

with self.assertRaises(ValueError):
X25519(b'0'*31)

def test_rfc7748_6_1(self):
# KAT from https://datatracker.ietf.org/doc/html/rfc7748#section-6.1
apriv = a2b_hex('77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a')

akey = X25519(apriv)
self.assertEqual(akey.getpub(), a2b_hex('8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a'))

bpriv = a2b_hex('5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb')
bkey = X25519(bpriv)
self.assertEqual(bkey.getpub(), a2b_hex('de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f'))

ss = a2b_hex('4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742')
self.assertEqual(akey.dh(bkey.getpub()), ss)
self.assertEqual(bkey.dh(akey.getpub()), ss)

def test_basic_ops(self):
aprivkey = x25519_genkey()
apubkey = x25519_base(aprivkey, 1)



Loading…
Cancel
Save