continued reorganisation
This commit is contained in:
parent
6f8a7322f2
commit
0a2d9a5694
22 changed files with 190 additions and 61 deletions
10
README
Normal file
10
README
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#### Running
|
||||||
|
The original file `bcrypt.ctf.py` can run on any version of python3.
|
||||||
|
`bcrypter.py` however is only compatible with Python 3.14.0a4 or above.
|
||||||
|
Mostly just cause I dislike `typing.Self`, and Python 3.14 now has the
|
||||||
|
wonderful `__annotate__` object method (see [PEP 649](https://peps.python.org/pep-0649/)
|
||||||
|
& [PEP 749](https://peps.python.org/pep-0749/)) which solves my issues
|
||||||
|
on type hint semantics and static type analysis.
|
||||||
|
|
||||||
|
Beyond versioning, `bcrypter.py` has no dependencies beyond the stdlib.
|
||||||
|
Enjoy :)
|
||||||
|
|
@ -1,61 +0,0 @@
|
||||||
from math import gcd
|
|
||||||
from random import randint, randbytes
|
|
||||||
|
|
||||||
def debug_hashes_eq(x: bytes, R_table: dict[int, int]) -> bool:
|
|
||||||
return hashfn(x, R_table) == bcrypt(x)
|
|
||||||
|
|
||||||
def debug_test_random_hashes(trials: int,
|
|
||||||
max_bytes: int = 16,
|
|
||||||
quiet: bool = False) -> bytes | None:
|
|
||||||
R_table = precompute_R(0, 255)
|
|
||||||
for i in range(trials):
|
|
||||||
# generate random bytes
|
|
||||||
num_bytes = randint(0, max_bytes)
|
|
||||||
x = randbytes(num_bytes)
|
|
||||||
|
|
||||||
# test the modified bcrypt with the original
|
|
||||||
hash_test = hashfn(x, R_table)
|
|
||||||
hash_bcrypt = bcrypt(x)
|
|
||||||
if hash_test != hash_bcrypt:
|
|
||||||
if not quiet:
|
|
||||||
print(f'Your hashfn sucks, big mistake bucko!! (iter: {i})')
|
|
||||||
print(hash_test)
|
|
||||||
print(hash_bcrypt)
|
|
||||||
print([str(b) for b in x])
|
|
||||||
return x
|
|
||||||
|
|
||||||
if not quiet:
|
|
||||||
print('Impeccable hashfn holy moly!!')
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> None:
|
|
||||||
print(f'gcd(H,K): {gcd(H,K)}')
|
|
||||||
print(f'gcd(H,M): {gcd(H,M)}')
|
|
||||||
print(f'gcd(K,M): {gcd(K,M)}')
|
|
||||||
if debug_test_random_hashes(10000) != None:
|
|
||||||
R_table = precompute_R(0, 255)
|
|
||||||
x = bytes(input('x: '), 'utf-8')
|
|
||||||
hash_test = hashfn(x, R_table)
|
|
||||||
hash_bcrypt = bcrypt(x)
|
|
||||||
print(f'hashfn: {hash_test}')
|
|
||||||
print(f'bcrypt: {hash_bcrypt}')
|
|
||||||
|
|
||||||
# a = bytes(input("A: "), 'utf-8')
|
|
||||||
# b = bytes(input("B: "), 'utf-8')
|
|
||||||
|
|
||||||
# if a != b and hashfn(a) == hashfn(b):
|
|
||||||
# print('*** YOU WIN ***')
|
|
||||||
# elif a == b:
|
|
||||||
# print('Idiot those are the same')
|
|
||||||
# else:
|
|
||||||
# print("Trivially false!")
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
try:
|
|
||||||
main()
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print('\n[!] Received SIGINT')
|
|
||||||
except EOFError:
|
|
||||||
print('\n[!] Reached EOF')
|
|
||||||
46
bcrypter/cli/cmd.py
Normal file
46
bcrypter/cli/cmd.py
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
from bcrypter.lib.result import Result
|
||||||
|
|
||||||
|
class Command:
|
||||||
|
NAME = '[Abstract]Command'
|
||||||
|
ARGS = []
|
||||||
|
FLAGS = []
|
||||||
|
OPTIONS = []
|
||||||
|
def __init__(self, args: list[string]) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def parse(cls: Command, cmd: list[str]) -> Result[Command]:
|
||||||
|
for arg in cmd[1:]:
|
||||||
|
# flag or option
|
||||||
|
if arg.startswith('--'):
|
||||||
|
match = cls._match_arg(arg[2:])
|
||||||
|
if match
|
||||||
|
|
||||||
|
'''
|
||||||
|
Check whether FLAGS and OPTIONS are defined consistently
|
||||||
|
(ie no duplicate names or parsing ambiguity)
|
||||||
|
'''
|
||||||
|
@classmethod
|
||||||
|
def _is_well_defined(cls: Command) -> Result[None]:
|
||||||
|
raise NotImplementedException('Command.is_consistent()')
|
||||||
|
|
||||||
|
'''
|
||||||
|
Attempt to match an arg to its flag or option
|
||||||
|
NOTE: _match_arg() assumes _is_well_defined() == True
|
||||||
|
'''
|
||||||
|
@classmethod
|
||||||
|
def _match_arg(cls: Command, arg: str) -> Result[None]:
|
||||||
|
for opt in chain(cls.FLAGS, cls.OPTIONS):
|
||||||
|
if opt.matches(arg):
|
||||||
|
return Result.succeed(None)
|
||||||
|
return Result.fail()
|
||||||
|
|
||||||
|
|
||||||
|
class Builtin(Command):
|
||||||
|
self.NAME = '[Abstract]Builtin'
|
||||||
|
def __init__(self,
|
||||||
|
repl_builtins: list[Builtin],
|
||||||
|
repl_cmds: list[Command]) -> None:
|
||||||
|
super().__init__()
|
||||||
|
self._repl_builtins = repl_builtins
|
||||||
|
self._repl_cmds = repl_cmds
|
||||||
21
bcrypter/cli/opt.py
Normal file
21
bcrypter/cli/opt.py
Normal file
|
|
@ -0,0 +1,21 @@
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class OptType(Enum):
|
||||||
|
AbstractOpt # only used by Opt
|
||||||
|
Flag
|
||||||
|
Option
|
||||||
|
|
||||||
|
class Opt:
|
||||||
|
_TYPE: OptType = OptType.AbstractOpt
|
||||||
|
def __init__(self, *args) -> None:
|
||||||
|
pass
|
||||||
|
|
||||||
|
class Flag(Opt):
|
||||||
|
_TYPE: OptType = OptType.Flag
|
||||||
|
def __init__(self, *args) -> None:
|
||||||
|
super().__init__(*args)
|
||||||
|
|
||||||
|
class Option(Opt):
|
||||||
|
_TYPE: OptType = OptType.Option
|
||||||
|
def __init__(self, *args) -> None:
|
||||||
|
super().__init__(*args)
|
||||||
42
bcrypter/cli/repl.py
Normal file
42
bcrypter/cli/repl.py
Normal file
|
|
@ -0,0 +1,42 @@
|
||||||
|
import readline # GNU readline (ie allows input() history buffer)
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
|
from bcrypter.cli.builtins import *
|
||||||
|
from bcrypter.cli.commands import *
|
||||||
|
from bcrypter.lib.result import Result
|
||||||
|
from bcrypter.exceptions import CmdDeclarationError
|
||||||
|
|
||||||
|
class REPL:
|
||||||
|
_PROMPT = '>> '
|
||||||
|
_DEFAULT_HISTORY_FILE = '.bcrypter_history'
|
||||||
|
|
||||||
|
_BUILTINS = [
|
||||||
|
BuiltinHelp(),
|
||||||
|
]
|
||||||
|
_COMMANDS = [
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, history_file: str = _DEFAULT_HISTORY_FILE) -> None:
|
||||||
|
for cmd in chain(REPL._BUILTINS, REPL._COMMANDS):
|
||||||
|
result = cmd._is_consistent():
|
||||||
|
if result.is_err():
|
||||||
|
raise CmdDeclarationError(result.message)
|
||||||
|
self._history_file = history_file
|
||||||
|
readline.read_history_file(self._history_file)
|
||||||
|
|
||||||
|
def __del__(self) -> None:
|
||||||
|
readline.write_history_file(self._history_file)
|
||||||
|
|
||||||
|
def prompt(self) -> str:
|
||||||
|
return input(REPL._PROMPT)
|
||||||
|
|
||||||
|
'''
|
||||||
|
Parse and execute a string command
|
||||||
|
'''
|
||||||
|
def exec(self, cmd: str) -> Result[Command]:
|
||||||
|
cmd = cmd.strip().split()
|
||||||
|
if not len(cmd):
|
||||||
|
return Result.warn('No command given')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
30
bcrypter/debug.py
Normal file
30
bcrypter/debug.py
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
from math import gcd
|
||||||
|
from random import randint, randbytes
|
||||||
|
|
||||||
|
def main() -> None:
|
||||||
|
if debug_test_random_hashes(10000) != None:
|
||||||
|
R_table = precompute_R(0, 255)
|
||||||
|
x = bytes(input('x: '), 'utf-8')
|
||||||
|
hash_test = hashfn(x, R_table)
|
||||||
|
hash_bcrypt = bcrypt(x)
|
||||||
|
print(f'hashfn: {hash_test}')
|
||||||
|
print(f'bcrypt: {hash_bcrypt}')
|
||||||
|
|
||||||
|
# a = bytes(input("A: "), 'utf-8')
|
||||||
|
# b = bytes(input("B: "), 'utf-8')
|
||||||
|
|
||||||
|
# if a != b and hashfn(a) == hashfn(b):
|
||||||
|
# print('*** YOU WIN ***')
|
||||||
|
# elif a == b:
|
||||||
|
# print('Idiot those are the same')
|
||||||
|
# else:
|
||||||
|
# print("Trivially false!")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
print('\n[!] Received SIGINT')
|
||||||
|
except EOFError:
|
||||||
|
print('\n[!] Reached EOF')
|
||||||
13
bcrypter/debug/constants.py
Normal file
13
bcrypter/debug/constants.py
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
'''
|
||||||
|
This file provides various methods for determining
|
||||||
|
properties, connections, etc for bcrypt's constants.
|
||||||
|
'''
|
||||||
|
|
||||||
|
from math import gcd
|
||||||
|
|
||||||
|
from bcrypt.hash.rev import H, K, M
|
||||||
|
|
||||||
|
def disint_gcds() -> None:
|
||||||
|
print(f'gcd(H,K): {gcd(H,K)}')
|
||||||
|
print(f'gcd(H,M): {gcd(H,M)}')
|
||||||
|
print(f'gcd(K,M): {gcd(K,M)}')
|
||||||
2
bcrypter/exceptions.py
Normal file
2
bcrypter/exceptions.py
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
class CmdDeclarationError(Exception):
|
||||||
|
pass
|
||||||
0
bcrypter/hash/__init__.py
Normal file
0
bcrypter/hash/__init__.py
Normal file
26
bcrypter/lib/result.py
Normal file
26
bcrypter/lib/result.py
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
from dataclass import dataclass
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
|
class ResultState(Enum):
|
||||||
|
WARNING,
|
||||||
|
SUCCESS,
|
||||||
|
FAILURE,
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class Result[T]:
|
||||||
|
state: ResultState
|
||||||
|
value: T | None
|
||||||
|
message: str
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def warn(cls: Result, message: str, value: T | None = NOne) -> Result:
|
||||||
|
cls(ResultState.WARNING, value, message)
|
||||||
|
@classmethod
|
||||||
|
def succeed(cls: Result, value: T, message: str = 'Ok') -> Result:
|
||||||
|
cls(ResultState.SUCCESS, value, message)
|
||||||
|
@classmethod
|
||||||
|
def fail(cls: Result, message: str, value: T | None = None) -> Result:
|
||||||
|
cls(ResultState.WARNING, value, message)
|
||||||
|
|
||||||
|
def is_ok(self) -> bool: return not self.is_err()
|
||||||
|
def is_err(self) -> bool: return self.state == ResultState.FAILURE
|
||||||
Loading…
Add table
Add a link
Reference in a new issue