passy.py 1.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. #!/usr/bin/env python
  2. import hmac
  3. import string
  4. import getpass
  5. import sys
  6. import itertools
  7. from hashlib import sha1
  8. __author__ = "Adam Rutkowski, adam@mtod.org"
  9. """
  10. Do not store passwords - generate them when needed.
  11. (http://news.ycombinator.com/item?id=2430808)
  12. usage:
  13. OSX:
  14. alias p='passy.py | pbcopy'
  15. Linux:
  16. alias p='passy.py | xsel --clipboard --input'
  17. alias p='passy.py | xclip -selection clipboard'
  18. """
  19. def get_chars(key):
  20. """Prepare a set of characters to build passwords with"""
  21. charset = "".join([string.ascii_letters, string.digits,
  22. string.punctuation])
  23. nums = map(int, filter(lambda x: x.isdigit(), sha1(key[::-1]).hexdigest()))
  24. chars = ""
  25. index = 0
  26. for n in itertools.cycle(nums):
  27. index += n
  28. chars += charset[index % len(charset)]
  29. if len(set(chars)) == 32:
  30. return "".join(set(chars))
  31. if len(chars) > 256:
  32. return "".join(chars[:32])
  33. def encode(domain, key):
  34. """Passy alogrithm by Stephen Waits as seen on
  35. https://bitbucket.org/swaits/passyweb"""
  36. chars = get_chars(key + domain)
  37. assert len(chars) == 32
  38. key = hmac.new(domain, key).hexdigest()
  39. key += sha1(key).hexdigest()
  40. recoded = map(lambda x: chars[ord(x) % 32], key.decode('hex'))
  41. password = ""
  42. pass_valid = [False] * 4
  43. for i, r_key in enumerate(recoded):
  44. password += r_key
  45. if r_key.isalpha():
  46. if r_key.isupper():
  47. pass_valid[0] = True
  48. else:
  49. pass_valid[1] = True
  50. elif r_key.isdigit():
  51. pass_valid[2] = True
  52. else:
  53. pass_valid[3] = True
  54. if (i >= 16 and all(pass_valid)):
  55. return password
  56. return password
  57. if __name__ == '__main__':
  58. try:
  59. domain = sys.argv[1]
  60. except IndexError:
  61. sys.stderr.write("Argument required\n")
  62. sys.exit(1)
  63. key = getpass.getpass()
  64. sys.stdout.write(encode(domain, key))