1#!/usr/bin/env python
2#
3# Copyright (c) ZeroC, Inc. All rights reserved.
4#
5
6import sys, getopt, passlib.hash, passlib.hosts, getpass
7
8usePBKDF2 = any(sys.platform == p for p in ["win32", "darwin", "cygwin"])
9useCryptExt = any(sys.platform.startswith(p) for p in ["linux", "freebsd", "gnukfreebsd"])
10
11def usage():
12    print("Usage: icehashpassword [options]")
13    print("")
14    print("OPTIONS")
15
16    if usePBKDF2:
17        print("")
18        print("  -d MESSAGE_DIGEST_ALGORITHM, --digest=MESSAGE_DIGEST_ALGORITHM")
19        print("      The message digest algorithm to use with PBKDF2, valid values are (sha1, sha256, sha512).")
20        print("")
21        print("  -s SALT_SIZE, --salt=SALT_SIZE")
22        print("      Optional number of bytes to use when generating new salts.")
23        print("")
24    elif useCryptExt:
25        print("  -d MESSAGE_DIGEST_ALGORITHM, --digest=MESSAGE_DIGEST_ALGORITHM")
26        print("      The message digest algorithm to use with crypt function, valid values are (sha256, sha512).")
27        print("")
28    if usePBKDF2 or useCryptExt:
29        print("  -r ROUNDS, --rounds=ROUNDS")
30        print("      Optional number of rounds to use.")
31        print("")
32    print("  -h, --help" )
33    print("      Show this message.")
34    print("")
35
36def main():
37
38    digestAlgorithms = ()
39    shortArgs = "h"
40    longArgs = ["help"]
41    if usePBKDF2:
42        shortArgs += "d:s:r:"
43        longArgs += ["digest=", "salt=", "rounds="]
44        digestAlgorithms = ("sha1", "sha256", "sha512")
45    elif useCryptExt:
46        shortArgs += "d:r:"
47        longArgs += ["digest=", "rounds="]
48        digestAlgorithms = ("sha256", "sha512")
49
50    try:
51        opts, args = getopt.getopt(sys.argv[1:], shortArgs, longArgs)
52    except getopt.GetoptError as err:
53        print("")
54        print(str(err))
55        usage()
56        return 2
57
58    digest = None
59    salt = None
60    rounds = None
61
62    for o, a in opts:
63        if o in ("-h", "--help"):
64            usage()
65            return 0
66        elif o in ("-d", "--digest"):
67            if a in digestAlgorithms:
68                digest = a
69            else:
70                print("Unknown digest algorithm `" + a + "'")
71                return 2
72        elif o in ("-s", "--salt"):
73            try:
74                salt = int(a)
75            except ValueError as err:
76                print("Invalid salt size. Value must be an integer")
77                usage()
78                return 2
79        elif o in ("-r", "--rounds"):
80            try:
81                rounds = int(a)
82            except ValueError as err:
83                print("Invalid number of rounds. Value must be an integer")
84                usage()
85                return 2
86
87    passScheme = None
88    if usePBKDF2:
89        passScheme = passlib.hash.pbkdf2_sha256
90        if digest == "sha1":
91            passScheme = passlib.hash.pbkdf2_sha1
92        elif digest == "sha512":
93            passScheme = passlib.hash.pbkdf2_sha512
94    elif useCryptExt:
95        passScheme = passlib.hash.sha512_crypt
96        if digest == "sha256":
97            passScheme = passlib.hash.sha256_crypt
98    else:
99        #
100        # Fallback is the OS crypt function
101        #
102        passScheme = passlib.hosts.host_context
103
104    if rounds:
105        if not passScheme.min_rounds <= rounds <= passScheme.max_rounds:
106            print("Invalid number rounds for the digest algorithm. Value must be an integer between %s and %s" %
107                (passScheme.min_rounds, passScheme.max_rounds))
108            usage()
109            return 2
110    if salt:
111        if not passScheme.min_salt_size <= salt <= passScheme.max_salt_size:
112            print("Invalid salt size for the digest algorithm. Value must be an integer between %s and %s" %
113                (passScheme.min_salt_size, passScheme.max_salt_size))
114            usage()
115            return 2
116
117    args = []
118    if sys.stdout.isatty():
119        args.append(getpass.getpass("Password: "))
120    else:
121        args.append(sys.stdin.readline().strip())
122
123    opts = {}
124    if salt:
125        opts["salt_size"] = salt
126
127    if rounds:
128        opts["rounds"] = rounds
129
130    # passlib 1.7 renamed encrypt to hash
131    if hasattr(passScheme, "hash"):
132        print(passScheme.using(**opts).hash(*args))
133    else:
134        print(passScheme.encrypt(*args, **opts))
135
136    return 0
137
138if __name__ == '__main__':
139    sys.exit(main())
140