test_cramauth.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. Tests for L{twisted.cred}'s implementation of CRAM-MD5.
  5. """
  6. from __future__ import division, absolute_import
  7. from hmac import HMAC
  8. from binascii import hexlify
  9. from twisted.trial.unittest import TestCase
  10. from twisted.cred.credentials import CramMD5Credentials
  11. from twisted.cred.credentials import IUsernameHashedPassword
  12. class CramMD5CredentialsTests(TestCase):
  13. """
  14. Tests for L{CramMD5Credentials}.
  15. """
  16. def test_idempotentChallenge(self):
  17. """
  18. The same L{CramMD5Credentials} will always provide the same challenge,
  19. no matter how many times it is called.
  20. """
  21. c = CramMD5Credentials()
  22. chal = c.getChallenge()
  23. self.assertEqual(chal, c.getChallenge())
  24. def test_checkPassword(self):
  25. """
  26. When a valid response (which is a hex digest of the challenge that has
  27. been encrypted by the user's shared secret) is set on the
  28. L{CramMD5Credentials} that created the challenge, and C{checkPassword}
  29. is called with the user's shared secret, it will return L{True}.
  30. """
  31. c = CramMD5Credentials()
  32. chal = c.getChallenge()
  33. c.response = hexlify(HMAC(b'secret', chal).digest())
  34. self.assertTrue(c.checkPassword(b'secret'))
  35. def test_noResponse(self):
  36. """
  37. When there is no response set, calling C{checkPassword} will return
  38. L{False}.
  39. """
  40. c = CramMD5Credentials()
  41. self.assertFalse(c.checkPassword(b'secret'))
  42. def test_wrongPassword(self):
  43. """
  44. When an invalid response is set on the L{CramMD5Credentials} (one that
  45. is not the hex digest of the challenge, encrypted with the user's shared
  46. secret) and C{checkPassword} is called with the user's correct shared
  47. secret, it will return L{False}.
  48. """
  49. c = CramMD5Credentials()
  50. chal = c.getChallenge()
  51. c.response = hexlify(HMAC(b'thewrongsecret', chal).digest())
  52. self.assertFalse(c.checkPassword(b'secret'))
  53. def test_setResponse(self):
  54. """
  55. When C{setResponse} is called with a string that is the username and
  56. the hashed challenge separated with a space, they will be set on the
  57. L{CramMD5Credentials}.
  58. """
  59. c = CramMD5Credentials()
  60. chal = c.getChallenge()
  61. c.setResponse(b" ".join(
  62. (b"squirrel",
  63. hexlify(HMAC(b'supersecret', chal).digest()))))
  64. self.assertTrue(c.checkPassword(b'supersecret'))
  65. self.assertEqual(c.username, b"squirrel")
  66. def test_interface(self):
  67. """
  68. L{CramMD5Credentials} implements the L{IUsernameHashedPassword}
  69. interface.
  70. """
  71. self.assertTrue(
  72. IUsernameHashedPassword.implementedBy(CramMD5Credentials))