2 """Generate Password from Salts and Passphrase.
4 The salts come from command line option and the passphrase from stdin.
5 Print ``hash(salts + passphrase)'' for your password.
6 You can generate different passwords for many domains from one passphrase.
8 usage: genpasswd [options] [string...]
11 $ genpasswd example.com # domain to login
12 Passphrase:My Passphrase # without echoback
13 Vn/aHPNgXbieJCkSGYiAA7y9GwM # got your password for example.com
15 $ genpasswd example.net # domain to login
16 Passphrase:My Passphrase # without echoback
17 /+/G4MzuaiSo9dHE/c0+GgPi6Nc # got your password for example.net
20 # Copyright (c) 2009,2012 Satoshi Fukutomi <info@fuktommy.com>.
21 # All rights reserved.
23 # Redistribution and use in source and binary forms, with or without
24 # modification, are permitted provided that the following conditions
26 # 1. Redistributions of source code must retain the above copyright
27 # notice, this list of conditions and the following disclaimer.
28 # 2. Redistributions in binary form must reproduce the above copyright
29 # notice, this list of conditions and the following disclaimer in the
30 # documentation and/or other materials provided with the distribution.
32 # THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
33 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
34 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
35 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
36 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
37 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
38 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
39 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
40 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
41 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
51 from getpass
import getpass
55 """Parse command line argments.
57 usage
= 'usage: %prog [options] [string...]'
58 parser
= optparse
.OptionParser(usage
=usage
)
59 parser
.add_option('-a', '--alphanum', dest
='alphanum_mode',
60 default
=False, action
='store_true',
61 help='password includes alphabet and number only')
62 parser
.add_option('-c', '--clipboard', dest
='clipboard_mode',
63 default
=False, action
='store_true',
64 help='copy password to clipboard (require putclip command)')
65 parser
.add_option('-f', '--file', dest
='saltfile', metavar
='FILE',
66 help='additional salt file')
67 parser
.add_option('-s', '--size', type='int', dest
='size',
69 parser
.add_option('--test', action
='callback', callback
=_test
,
70 help='run test and exit')
71 return parser
.parse_args(argv
)
74 def generate_password(salt
, options
):
75 """Generate password(hash) from salt.
77 digest
= hashlib
.sha1(':'.join(salt
)).digest()
78 passwd
= base64
.encodestring(digest
).replace('=', '').strip()
80 if options
.alphanum_mode
:
81 passwd
= passwd
.replace('+', '').replace('/', '')
82 if options
.size
is not None:
83 passwd
= passwd
[:options
.size
]
87 def clipboard_command():
88 """Select clipboard command for platform.
90 if sys
.platform
.startswith('linux'):
91 return 'xsel --input --clipboard'
92 elif sys
.platform
== 'darwin':
95 raise Exception('I do not know your clipboard command.')
97 def windows_put_clipboard(string
):
98 """Put string to clipboard on Windows.
102 import win32clipboard
103 win32clipboard
.OpenClipboard()
104 win32clipboard
.SetClipboardText(string
)
105 win32clipboard
.CloseClipboard()
107 def put_clipboard(string
):
108 """Put string to clipboard.
111 windows_put_clipboard(string
)
115 if sys
.platform
== 'cygwin':
116 open('/dev/clipboard', 'wb').write(string
)
118 cmd
= clipboard_command()
119 pipe
= subprocess
.Popen(cmd
, shell
=True, stdin
=subprocess
.PIPE
)
120 pipe
.stdin
.write(string
)
124 class GeneratePasswordTest(unittest
.TestCase
):
125 def test_generate(self
):
128 argv
= ['foo', 'bar']
129 options
, salt
= parse_args(argv
)
130 result
= generate_password(salt
, options
)
131 self
.assertEquals('VNy+Z9IdXrOUk9Rtia4fQS071t4', result
)
133 def test_generate_alpha(self
):
134 """Set alpha_num_mode on.
136 argv
= ['foo', 'bar', '-a']
137 options
, salt
= parse_args(argv
)
138 result
= generate_password(salt
, options
)
139 self
.assertEquals('VNyZ9IdXrOUk9Rtia4fQS071t4', result
)
141 def test_generate_size(self
):
144 argv
= ['foo', 'bar', '-s', '6']
145 options
, salt
= parse_args(argv
)
146 result
= generate_password(salt
, options
)
147 self
.assertEquals('VNy+Z9', result
)
149 def test_generate_alpha_size(self
):
150 """Set size and alpha_num_mode.
152 Password size is set size.
154 argv
= ['foo', 'bar', '-a', '-s', '6']
155 options
, salt
= parse_args(argv
)
156 result
= generate_password(salt
, options
)
157 self
.assertEquals('VNyZ9I', result
)
160 def _test(option
, opt_str
, value
, parser
, *args
, **kwargs
):
161 suite
= unittest
.TestSuite()
162 suite
.addTest(unittest
.makeSuite(GeneratePasswordTest
))
163 result
= unittest
.TextTestRunner(verbosity
=2).run(suite
)
164 if result
.errors
or result
.failures
:
171 options
, salt
= parse_args(sys
.argv
[1:])
173 salt
.append(open(options
.saltfile
, 'rb').read())
174 passphrase
= getpass('Passphrase:')
175 salt
.append(passphrase
)
176 passwd
= generate_password(salt
, options
)
177 if options
.clipboard_mode
:
178 print 'put password to clipboard.'
179 put_clipboard(passwd
)
184 if __name__
== '__main__':