passgen.py 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. #!/usr/bin/env python
  2. #
  3. # This is a port of classical passgen (provided by jxself) into the python
  4. # language.
  5. #
  6. # Usage: passgen.py [service-specific string]
  7. #
  8. # Use this program if you do not have access to a graphical environment or if
  9. # you can't install PyGTK. Otherwise, run main.py
  10. #
  11. # Copyright (C) 2016 - kzimmermann <https://quitter.se/kzimmermann>
  12. #
  13. # This program is free software: you can redistribute it and/or modify
  14. # it under the terms of the GNU General Public License as published by
  15. # the Free Software Foundation, either version 3 of the License, or
  16. # (at your option) any later version.
  17. #
  18. # This program is distributed in the hope that it will be useful,
  19. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  21. # GNU General Public License for more details.
  22. #
  23. # You should have received a copy of the GNU General Public License
  24. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  25. #
  26. import sys
  27. import base64 as b64
  28. import hashlib
  29. from getpass import getpass
  30. #---- Standard configuration variables:
  31. seclevel = 7 # how many times tokens are hashed
  32. #---- Functions:
  33. def secure_hash(token):
  34. """
  35. Hashes multiple times a given string so as to provide something that should
  36. not be easy to reverse. Standardized as seven times, but feel free to
  37. change it to as much as your own paranoia needs.
  38. Returns a SHA-256 hash.
  39. """
  40. temp = str(token)
  41. for i in xrange(seclevel):
  42. temp = hashlib.sha256(temp).hexdigest()
  43. return temp
  44. def generate(salt, string):
  45. """
  46. Generates a 32-char long password based on the new algorithm.
  47. Arguments are a password seed (called a salt) and a service-specific string
  48. so all passwords generated will be different.
  49. The 32 characters are sampled from a different position depending on the
  50. salt used. This should always work provided that the salt isn't too long.
  51. """
  52. # So that the sampling is not that obvious ;)
  53. start = len("%s%s" % (salt, string))
  54. temp = secure_hash("%s%s" % (salt, string))
  55. return b64.b64encode(temp)[start:start+32]
  56. #----------
  57. # Hey, if we can create passwords, why not create a username as well to tie
  58. # everything together? That would be one less thing to memorize!
  59. def generate_name(salt, string):
  60. """
  61. Generates a 12-char long username from the hashed combination. WIP.
  62. SHA-256 is always 64-characters in length, so we could take a nice slice
  63. all the way until the 56th character.
  64. """
  65. start = len("%s%s" % (salt, string))
  66. return secure_hash("%s%s" % (salt, string))[start:start+12]
  67. def main():
  68. """
  69. Main interaction when module has not been imported
  70. It now can be used in a script, like this:
  71. passgen.py SALT SERVICE
  72. passgen.py -u SALT SERVICE (for usernames)
  73. Which is what `passgen` will be using to generate its own.
  74. """
  75. if "-u" in sys.argv:
  76. # for usernames
  77. print generate_name(sys.argv[2], sys.argv[3])
  78. sys.exit(0)
  79. if len(sys.argv) < 3: # no arguments passed, or a single SERVICE string
  80. while True:
  81. seed = getpass("Enter your salt: ")
  82. seed_confirm = getpass("Confirm: ")
  83. if seed == seed_confirm:
  84. break
  85. else:
  86. print "Salts don't match. Try again."
  87. if len(sys.argv) == 1:
  88. string = raw_input("Enter your string: ")
  89. elif len(sys.argv) == 2:
  90. string = sys.argv[1]
  91. print "Suggested username: %s" % generate_name(seed, string)
  92. print generate(seed, string)
  93. raw_input("Copy this, clear your clipboard and press Enter. ")
  94. # Clear the screen to make difficult (but not impossible) to view the
  95. # password again.
  96. for i in xrange(500):
  97. print "\n\n\n\n\n"
  98. else:
  99. # running inside a script like `passgen SALT SERVICE`
  100. # the script must catch this to some variable to prevent leaking
  101. print generate(sys.argv[1], sys.argv[2])
  102. if __name__ == "__main__":
  103. main()
  104. sys.exit(0)