An stunnel like program that utilizes the Noise protocol.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

112 lines
3.2 KiB

  1. from twisted.trial import unittest
  2. from twisted.test import proto_helpers
  3. from noise.connection import NoiseConnection, Keypair
  4. from twisted.internet.protocol import Factory
  5. # XXX - shouldn't need to access the underlying primitives, but that's what
  6. # noiseprotocol module requires.
  7. from cryptography.hazmat.primitives.asymmetric import x448
  8. from cryptography.hazmat.primitives import serialization
  9. # Notes:
  10. # Using XK, so that the connecting party's identity is hidden and that the
  11. # server's party's key is known.
  12. import twisted.internet.protocol
  13. def genkeypair():
  14. '''Generates a keypair, and returns a tuple of (public, private).
  15. They are encoded as raw bytes, and sutible for use w/ Noise.'''
  16. key = x448.X448PrivateKey.generate()
  17. enc = serialization.Encoding.Raw
  18. pubformat = serialization.PublicFormat.Raw
  19. privformat = serialization.PrivateFormat.Raw
  20. encalgo = serialization.NoEncryption()
  21. pub = key.public_key().public_bytes(encoding=enc, format=pubformat)
  22. priv = key.private_bytes(encoding=enc, format=privformat, encryption_algorithm=encalgo)
  23. return pub, priv
  24. class TwistedNoiseServerProtocol(twisted.internet.protocol.Protocol):
  25. def connectionMade(self):
  26. # Initialize Noise
  27. noise = NoiseConnection.from_name(b'Noise_XK_448_ChaChaPoly_SHA256')
  28. self.noise = noise
  29. noise.set_as_responder()
  30. noise.set_keypair_from_private_bytes(Keypair.STATIC, self.factory.server_key)
  31. # Start Handshake
  32. noise.start_handshake()
  33. def dataReceived(self, data):
  34. if not self.noise.handshake_finished:
  35. self.noise.read_message(data)
  36. if not self.noise.handshake_finished:
  37. self.transport.write(self.noise.write_message())
  38. else:
  39. r = self.noise.decrypt(data)
  40. # echo it
  41. self.transport.write(self.noise.encrypt(r))
  42. class TwistedNoiseServerFactory(Factory):
  43. protocol = TwistedNoiseServerProtocol
  44. def __init__(self, server_key):
  45. self.server_key = server_key
  46. class TNServerTest(unittest.TestCase):
  47. def setUp(self):
  48. self.server_key_pair = genkeypair()
  49. factory = TwistedNoiseServerFactory(server_key=self.server_key_pair[1])
  50. self.proto = factory.buildProtocol(('127.0.0.1', 0))
  51. self.tr = proto_helpers.StringTransport()
  52. self.proto.makeConnection(self.tr)
  53. self.client_key_pair = genkeypair()
  54. def test_testprotocol(self):
  55. # Create client
  56. proto = NoiseConnection.from_name(b'Noise_XK_448_ChaChaPoly_SHA256')
  57. proto.set_as_initiator()
  58. # Setup required keys
  59. proto.set_keypair_from_private_bytes(Keypair.STATIC, self.client_key_pair[1])
  60. proto.set_keypair_from_public_bytes(Keypair.REMOTE_STATIC, self.server_key_pair[0])
  61. proto.set_keypair_from_private_bytes(Keypair.STATIC, self.client_key_pair[1])
  62. proto.start_handshake()
  63. # Send first message
  64. message = proto.write_message()
  65. self.proto.dataReceived(message)
  66. # Get response
  67. resp = self.tr.value()
  68. self.tr.clear()
  69. # And process it
  70. proto.read_message(resp)
  71. # Send second message
  72. message = proto.write_message()
  73. self.proto.dataReceived(message)
  74. # Finish handshake
  75. self.assertTrue(proto.handshake_finished)
  76. ptmsg = b'this is a test message'
  77. encmsg = proto.encrypt(ptmsg)
  78. self.proto.dataReceived(encmsg)
  79. # Get echo
  80. resp = self.tr.value()
  81. self.tr.clear()
  82. ptresp = proto.decrypt(resp)
  83. self.assertEqual(ptresp, ptmsg)