From 1fd5770d42a2d8fe548e1903a23db78cd4244a2f Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 20 Dec 2015 02:59:17 +0100 Subject: [PATCH] Add client test for ECDSA-NIST256P-CHALLENGE. --- irctest/authentication.py | 7 +++-- irctest/client_tests/test_sasl.py | 43 +++++++++++++++++++++++++++++-- irctest/controllers/limnoria.py | 4 +++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/irctest/authentication.py b/irctest/authentication.py index b9d8dfa..e8afd43 100644 --- a/irctest/authentication.py +++ b/irctest/authentication.py @@ -5,9 +5,12 @@ import collections class Mechanisms(enum.Enum): @classmethod def as_string(cls, mech): - return {cls.plain: 'PLAIN'}[mech] + return {cls.plain: 'PLAIN', + cls.ecdsa_nist256p_challenge: 'ECDSA-NIST256P-CHALLENGE', + }[mech] plain = 1 + ecdsa_nist256p_challenge = 2 Authentication = collections.namedtuple('Authentication', - 'mechanisms username password certificate') + 'mechanisms username password ecdsa_key') Authentication.__new__.__defaults__ = ([Mechanisms.plain], None, None, None) diff --git a/irctest/client_tests/test_sasl.py b/irctest/client_tests/test_sasl.py index af3786e..a6ff39b 100644 --- a/irctest/client_tests/test_sasl.py +++ b/irctest/client_tests/test_sasl.py @@ -1,9 +1,22 @@ +import ecdsa import base64 from irctest import cases from irctest import authentication from irctest.irc_utils.message_parser import Message -class CapTestCase(cases.BaseClientTestCase, cases.ClientNegociationHelper): +ECDSA_KEY = """ +-----BEGIN EC PARAMETERS----- +BggqhkjOPQMBBw== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHcCAQEEIIJueQ3W2IrGbe9wKdOI75yGS7PYZSj6W4tg854hlsvmoAoGCCqGSM49 +AwEHoUQDQgAEAZmaVhNSMmV5r8FXPvKuMnqDKyIA9pDHN5TNMfiF3mMeikGgK10W +IRX9cyi2wdYg9mUUYyh9GKdBCYHGUJAiCA== +-----END EC PRIVATE KEY----- +""" +ENCODED_FOOBAR = """gqpuPoR0vaTIKiEQyqrj48RIT13R3hJuJI2Os7ySXwLWbjizpdmGPNtJdv396FkvZVPwZpC4cCfabhEby4ytGA==""" + +class SaslTestCase(cases.BaseClientTestCase, cases.ClientNegociationHelper): def testPlain(self): auth = authentication.Authentication( mechanisms=[authentication.Mechanisms.plain], @@ -35,7 +48,6 @@ class CapTestCase(cases.BaseClientTestCase, cases.ClientNegociationHelper): self.assertEqual(m, Message([], None, 'AUTHENTICATE', ['PLAIN'])) self.sendLine('904 {} :SASL auth failed'.format(self.nick)) m = self.getMessage() - print(m) def testPlainLarge(self): @@ -90,6 +102,33 @@ class CapTestCase(cases.BaseClientTestCase, cases.ClientNegociationHelper): m = self.negotiateCapabilities(['sasl'], False) self.assertEqual(m, Message([], None, 'CAP', ['END'])) + def testEcdsa(self): + auth = authentication.Authentication( + mechanisms=[authentication.Mechanisms.ecdsa_nist256p_challenge], + username='jilles', + ecdsa_key=ECDSA_KEY, + ) + m = self.negotiateCapabilities(['sasl'], auth=auth) + self.assertEqual(m, Message([], None, 'AUTHENTICATE', ['ECDSA-NIST256P-CHALLENGE'])) + self.sendLine('AUTHENTICATE +') + m = self.getMessage() + self.assertEqual(m, Message([], None, 'AUTHENTICATE', + ['amlsbGVz'])) # jilles + self.sendLine('AUTHENTICATE Zm9vYmFy') # foobar + m = self.getMessage() + self.assertMessageEqual(m, command='AUTHENTICATE') + sk = ecdsa.SigningKey.from_pem(ECDSA_KEY) + vk = sk.get_verifying_key() + signature = base64.b64decode(m.params[0]) + try: + vk.verify(signature, b'foobar') + except ecdsa.BadSignatureError: + raise AssertionError('Bad signature') + self.sendLine('900 * * foo :You are now logged in.') + self.sendLine('903 * :SASL authentication successful') + m = self.negotiateCapabilities(['sasl'], False) + self.assertEqual(m, Message([], None, 'CAP', ['END'])) + class Irc302SaslTestCase(cases.BaseClientTestCase, cases.ClientNegociationHelper): def testPlainNotAvailable(self): auth = authentication.Authentication( diff --git a/irctest/controllers/limnoria.py b/irctest/controllers/limnoria.py index 70580ac..1b096ab 100644 --- a/irctest/controllers/limnoria.py +++ b/irctest/controllers/limnoria.py @@ -12,6 +12,7 @@ supybot.networks: testnet supybot.networks.testnet.servers: {hostname}:{port} supybot.networks.testnet.sasl.username: {username} supybot.networks.testnet.sasl.password: {password} +supybot.networks.testnet.sasl.ecdsa_key: {directory}/ecdsa_key.pem supybot.networks.testnet.sasl.mechanisms: {mechanisms} """ @@ -30,6 +31,9 @@ class LimnoriaController(BaseClientController, DirectoryBasedController): if auth: mechanisms = ' '.join(map(authentication.Mechanisms.as_string, auth.mechanisms)) + if auth.ecdsa_key: + with self.open_file('ecdsa_key.pem') as fd: + fd.write(auth.ecdsa_key) else: mechanisms = '' with self.open_file('bot.conf') as fd: