auth.py 3.86 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
"""
Copyright  2016-2019 Maël Azimi <m.a@moul.re>

Silkaj is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Silkaj is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with Silkaj. If not, see <https://www.gnu.org/licenses/>.
"""

18
from silkaj.tools import message_exit
Moul's avatar
Moul committed
19
from click import command, option, pass_context, confirm
20
from getpass import getpass
Moul's avatar
Moul committed
21
from pathlib import Path
22
from re import compile, search
23
from duniterpy.key import SigningKey
24 25
from duniterpy.key import ScryptParams

Moul's avatar
Moul committed
26

27 28 29
@pass_context
def auth_method(ctx):
    if ctx.obj["AUTH_SEED"]:
Tortue95's avatar
Tortue95 committed
30
        return auth_by_seed()
31 32 33
    if ctx.obj["AUTH_FILE"]:
        return auth_by_auth_file()
    if ctx.obj["AUTH_WIF"]:
34
        return auth_by_wif()
35
    else:
36
        return auth_by_scrypt()
Tortue95's avatar
Tortue95 committed
37

Moul's avatar
Moul committed
38

39 40 41 42
@command("authfile", help="Generate authentication file")
@option("--file", default="authfile", show_default=True, help="Path file")
def generate_auth_file(file):
    key = auth_method()
Moul's avatar
Moul committed
43 44 45 46 47 48 49 50 51 52
    authfile = Path(file)
    if authfile.is_file():
        confirm(
            "Would you like to erase "
            + file
            + " by an authfile corresponding to following pubkey `"
            + key.pubkey
            + "`?",
            abort=True,
        )
53
    key.save_seedhex_file(file)
Moul's avatar
Moul committed
54 55 56
    print(
        "Authentication file 'authfile' generated and stored in current\
 folder for following public key:",
57
        key.pubkey,
Moul's avatar
Moul committed
58
    )
Tortue95's avatar
Tortue95 committed
59

Moul's avatar
Moul committed
60

61 62 63
@pass_context
def auth_by_auth_file(ctx):
    file = ctx.obj["AUTH_FILE_PATH"]
Moul's avatar
Moul committed
64 65
    authfile = Path(file)
    if not authfile.is_file():
Moul's avatar
Moul committed
66
        message_exit('Error: the file "' + file + '" does not exist')
Moul's avatar
Moul committed
67
    filetxt = authfile.open("r").read()
Moul's avatar
Moul committed
68 69 70 71
    regex_seed = compile("^[0-9a-fA-F]{64}$")
    regex_gannonce = compile(
        "^pub: [1-9A-HJ-NP-Za-km-z]{43,44}\nsec: [1-9A-HJ-NP-Za-km-z]{88,90}.*$"
    )
Tortue95's avatar
Tortue95 committed
72
    # Seed Format
73
    if search(regex_seed, filetxt):
74
        return SigningKey.from_seedhex_file(file)
Tortue95's avatar
Tortue95 committed
75
    # gannonce.duniter.org Format
76
    elif search(regex_gannonce, filetxt):
77
        return SigningKey.from_pubsec_file(file)
Tortue95's avatar
Tortue95 committed
78
    else:
79
        message_exit("Error: the format of the file is invalid")
Tortue95's avatar
Tortue95 committed
80

Tortue95's avatar
Tortue95 committed
81

Tortue95's avatar
Tortue95 committed
82
def auth_by_seed():
83 84 85 86 87
    seedhex = getpass("Please enter your seed on hex format: ")
    try:
        return SigningKey.from_seedhex(seedhex)
    except Exception as error:
        message_exit(error)
Tortue95's avatar
Tortue95 committed
88 89


90 91
@pass_context
def auth_by_scrypt(ctx):
92 93
    salt = getpass("Please enter your Scrypt Salt (Secret identifier): ")
    password = getpass("Please enter your Scrypt password (masked): ")
Tortue95's avatar
Tortue95 committed
94

95 96 97
    if ctx.obj["AUTH_SCRYPT_PARAMS"]:
        n, r, p = ctx.obj["AUTH_SCRYPT_PARAMS"].split(",")

98
        if n.isnumeric() and r.isnumeric() and p.isnumeric():
99
            n, r, p = int(n), int(r), int(p)
Jean-Yves's avatar
Jean-Yves committed
100
            if n <= 0 or n > 65536 or r <= 0 or r > 512 or p <= 0 or p > 32:
101
                message_exit("Error: the values of Scrypt parameters are not good")
102
            scrypt_params = ScryptParams(n, r, p)
103
        else:
104
            message_exit("one of n, r or p is not a number")
105
    else:
106
        scrypt_params = None
Tortue95's avatar
Tortue95 committed
107

108 109 110 111
    try:
        return SigningKey.from_credentials(salt, password, scrypt_params)
    except ValueError as error:
        message_exit(error)
112 113 114


def auth_by_wif():
115 116 117 118 119 120 121 122
    wif_hex = getpass("Enter your WIF or Encrypted WIF address (masked): ")
    password = getpass(
        "(Leave empty in case WIF format) Enter the Encrypted WIF password (masked): "
    )
    try:
        return SigningKey.from_wif_or_ewif_hex(wif_hex, password)
    except Exception as error:
        message_exit(error)