mirror of
				https://github.com/9001/copyparty.git
				synced 2025-11-03 21:22:47 -07:00 
			
		
		
		
	* fix toast/tooltip colors on splashpage * properly warn if --ah-cli or --ah-gen is used without --ah-alg * support ^D during --ah-cli * improve flavor texts
		
			
				
	
	
		
			150 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			150 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
# coding: utf-8
 | 
						|
from __future__ import print_function, unicode_literals
 | 
						|
 | 
						|
import argparse
 | 
						|
import base64
 | 
						|
import hashlib
 | 
						|
import sys
 | 
						|
import threading
 | 
						|
 | 
						|
from .__init__ import unicode
 | 
						|
 | 
						|
 | 
						|
class PWHash(object):
 | 
						|
    def __init__(self, args: argparse.Namespace):
 | 
						|
        self.args = args
 | 
						|
 | 
						|
        try:
 | 
						|
            alg, ac = args.ah_alg.split(",")
 | 
						|
        except:
 | 
						|
            alg = args.ah_alg
 | 
						|
            ac = {}
 | 
						|
 | 
						|
        if alg == "none":
 | 
						|
            alg = ""
 | 
						|
 | 
						|
        self.alg = alg
 | 
						|
        self.ac = ac
 | 
						|
        if not alg:
 | 
						|
            self.on = False
 | 
						|
            self.hash = unicode
 | 
						|
            return
 | 
						|
 | 
						|
        self.on = True
 | 
						|
        self.salt = args.ah_salt.encode("utf-8")
 | 
						|
        self.cache: dict[str, str] = {}
 | 
						|
        self.mutex = threading.Lock()
 | 
						|
        self.hash = self._cache_hash
 | 
						|
 | 
						|
        if alg == "sha2":
 | 
						|
            self._hash = self._gen_sha2
 | 
						|
        elif alg == "scrypt":
 | 
						|
            self._hash = self._gen_scrypt
 | 
						|
        elif alg == "argon2":
 | 
						|
            self._hash = self._gen_argon2
 | 
						|
        else:
 | 
						|
            t = "unsupported password hashing algorithm [{}], must be one of these: argon2 scrypt sha2 none"
 | 
						|
            raise Exception(t.format(alg))
 | 
						|
 | 
						|
    def _cache_hash(self, plain: str) -> str:
 | 
						|
        with self.mutex:
 | 
						|
            try:
 | 
						|
                return self.cache[plain]
 | 
						|
            except:
 | 
						|
                pass
 | 
						|
 | 
						|
            if not plain:
 | 
						|
                return ""
 | 
						|
 | 
						|
            if len(plain) > 255:
 | 
						|
                raise Exception("password too long")
 | 
						|
 | 
						|
            if len(self.cache) > 9000:
 | 
						|
                self.cache = {}
 | 
						|
 | 
						|
            ret = self._hash(plain)
 | 
						|
            self.cache[plain] = ret
 | 
						|
            return ret
 | 
						|
 | 
						|
    def _gen_sha2(self, plain: str) -> str:
 | 
						|
        its = int(self.ac[0]) if self.ac else 424242
 | 
						|
        bplain = plain.encode("utf-8")
 | 
						|
        ret = b"\n"
 | 
						|
        for _ in range(its):
 | 
						|
            ret = hashlib.sha512(self.salt + bplain + ret).digest()
 | 
						|
 | 
						|
        return "+" + base64.urlsafe_b64encode(ret[:24]).decode("utf-8")
 | 
						|
 | 
						|
    def _gen_scrypt(self, plain: str) -> str:
 | 
						|
        cost = 2 << 13
 | 
						|
        its = 2
 | 
						|
        blksz = 8
 | 
						|
        para = 4
 | 
						|
        try:
 | 
						|
            cost = 2 << int(self.ac[0])
 | 
						|
            its = int(self.ac[1])
 | 
						|
            blksz = int(self.ac[2])
 | 
						|
            para = int(self.ac[3])
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
 | 
						|
        ret = plain.encode("utf-8")
 | 
						|
        for _ in range(its):
 | 
						|
            ret = hashlib.scrypt(ret, salt=self.salt, n=cost, r=blksz, p=para, dklen=24)
 | 
						|
 | 
						|
        return "+" + base64.urlsafe_b64encode(ret).decode("utf-8")
 | 
						|
 | 
						|
    def _gen_argon2(self, plain: str) -> str:
 | 
						|
        from argon2.low_level import Type as ArgonType
 | 
						|
        from argon2.low_level import hash_secret
 | 
						|
 | 
						|
        time_cost = 3
 | 
						|
        mem_cost = 256
 | 
						|
        parallelism = 4
 | 
						|
        version = 19
 | 
						|
        try:
 | 
						|
            time_cost = int(self.ac[0])
 | 
						|
            mem_cost = int(self.ac[1])
 | 
						|
            parallelism = int(self.ac[2])
 | 
						|
            version = int(self.ac[3])
 | 
						|
        except:
 | 
						|
            pass
 | 
						|
 | 
						|
        bplain = plain.encode("utf-8")
 | 
						|
 | 
						|
        bret = hash_secret(
 | 
						|
            secret=bplain,
 | 
						|
            salt=self.salt,
 | 
						|
            time_cost=time_cost,
 | 
						|
            memory_cost=mem_cost * 1024,
 | 
						|
            parallelism=parallelism,
 | 
						|
            hash_len=24,
 | 
						|
            type=ArgonType.ID,
 | 
						|
            version=version,
 | 
						|
        )
 | 
						|
        ret = bret.split(b"$")[-1].decode("utf-8")
 | 
						|
        return "+" + ret.replace("/", "_").replace("+", "-")
 | 
						|
 | 
						|
    def stdin(self) -> None:
 | 
						|
        while True:
 | 
						|
            ln = sys.stdin.readline().strip()
 | 
						|
            if not ln:
 | 
						|
                break
 | 
						|
            print(self.hash(ln))
 | 
						|
 | 
						|
    def cli(self) -> None:
 | 
						|
        import getpass
 | 
						|
 | 
						|
        while True:
 | 
						|
            try:
 | 
						|
                p1 = getpass.getpass("password> ")
 | 
						|
                p2 = getpass.getpass("again or just hit ENTER> ")
 | 
						|
            except EOFError:
 | 
						|
                return
 | 
						|
 | 
						|
            if p2 and p1 != p2:
 | 
						|
                print("\033[31minputs don't match; try again\033[0m", file=sys.stderr)
 | 
						|
                continue
 | 
						|
            print(self.hash(p1))
 | 
						|
            print()
 |