nonce.py 2.8 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. __all__ = [
  2. 'split',
  3. 'mkNonce',
  4. 'checkTimestamp',
  5. ]
  6. from openid import cryptutil
  7. from time import strptime, strftime, gmtime, time
  8. from calendar import timegm
  9. import string
  10. NONCE_CHARS = string.ascii_letters + string.digits
  11. # Keep nonces for five hours (allow five hours for the combination of
  12. # request time and clock skew). This is probably way more than is
  13. # necessary, but there is not much overhead in storing nonces.
  14. SKEW = 60 * 60 * 5
  15. time_fmt = '%Y-%m-%dT%H:%M:%SZ'
  16. time_str_len = len('0000-00-00T00:00:00Z')
  17. def split(nonce_string):
  18. """Extract a timestamp from the given nonce string
  19. @param nonce_string: the nonce from which to extract the timestamp
  20. @type nonce_string: str
  21. @returns: A pair of a Unix timestamp and the salt characters
  22. @returntype: (int, str)
  23. @raises ValueError: if the nonce does not start with a correctly
  24. formatted time string
  25. """
  26. timestamp_str = nonce_string[:time_str_len]
  27. try:
  28. timestamp = timegm(strptime(timestamp_str, time_fmt))
  29. except AssertionError: # Python 2.2
  30. timestamp = -1
  31. if timestamp < 0:
  32. raise ValueError('time out of range')
  33. return timestamp, nonce_string[time_str_len:]
  34. def checkTimestamp(nonce_string, allowed_skew=SKEW, now=None):
  35. """Is the timestamp that is part of the specified nonce string
  36. within the allowed clock-skew of the current time?
  37. @param nonce_string: The nonce that is being checked
  38. @type nonce_string: str
  39. @param allowed_skew: How many seconds should be allowed for
  40. completing the request, allowing for clock skew.
  41. @type allowed_skew: int
  42. @param now: The current time, as a Unix timestamp
  43. @type now: int
  44. @returntype: bool
  45. @returns: Whether the timestamp is correctly formatted and within
  46. the allowed skew of the current time.
  47. """
  48. try:
  49. stamp, _ = split(nonce_string)
  50. except ValueError:
  51. return False
  52. else:
  53. if now is None:
  54. now = time()
  55. # Time after which we should not use the nonce
  56. past = now - allowed_skew
  57. # Time that is too far in the future for us to allow
  58. future = now + allowed_skew
  59. # the stamp is not too far in the future and is not too far in
  60. # the past
  61. return past <= stamp <= future
  62. def mkNonce(when=None):
  63. """Generate a nonce with the current timestamp
  64. @param when: Unix timestamp representing the issue time of the
  65. nonce. Defaults to the current time.
  66. @type when: int
  67. @returntype: str
  68. @returns: A string that should be usable as a one-way nonce
  69. @see: time
  70. """
  71. salt = cryptutil.randomString(6, NONCE_CHARS)
  72. if when is None:
  73. t = gmtime()
  74. else:
  75. t = gmtime(when)
  76. time_str = strftime(time_fmt, t)
  77. return time_str + salt