diff --git a/.trunk/.gitignore b/.trunk/.gitignore new file mode 100644 index 0000000..072b680 --- /dev/null +++ b/.trunk/.gitignore @@ -0,0 +1,9 @@ +*out +*logs +*actions +*notifications +*tools +plugins +user_trunk.yaml +user.yaml +tmp \ No newline at end of file diff --git a/.trunk/configs/.isort.cfg b/.trunk/configs/.isort.cfg new file mode 100644 index 0000000..b9fb3f3 --- /dev/null +++ b/.trunk/configs/.isort.cfg @@ -0,0 +1,2 @@ +[settings] +profile=black diff --git a/.trunk/configs/.markdownlint.yaml b/.trunk/configs/.markdownlint.yaml new file mode 100644 index 0000000..fb94039 --- /dev/null +++ b/.trunk/configs/.markdownlint.yaml @@ -0,0 +1,10 @@ +# Autoformatter friendly markdownlint config (all formatting rules disabled) +default: true +blank_lines: false +bullet: false +html: false +indentation: false +line_length: false +spaces: false +url: false +whitespace: false diff --git a/.trunk/configs/.shellcheckrc b/.trunk/configs/.shellcheckrc new file mode 100644 index 0000000..8c7b1ad --- /dev/null +++ b/.trunk/configs/.shellcheckrc @@ -0,0 +1,7 @@ +enable=all +source-path=SCRIPTDIR +disable=SC2154 + +# If you're having issues with shellcheck following source, disable the errors via: +# disable=SC1090 +# disable=SC1091 diff --git a/.trunk/configs/.yamllint.yaml b/.trunk/configs/.yamllint.yaml new file mode 100644 index 0000000..4d44466 --- /dev/null +++ b/.trunk/configs/.yamllint.yaml @@ -0,0 +1,10 @@ +rules: + quoted-strings: + required: only-when-needed + extra-allowed: ["{|}"] + empty-values: + forbid-in-block-mappings: true + forbid-in-flow-mappings: true + key-duplicates: {} + octal-values: + forbid-implicit-octal: true diff --git a/.trunk/configs/ruff.toml b/.trunk/configs/ruff.toml new file mode 100644 index 0000000..f5a235c --- /dev/null +++ b/.trunk/configs/ruff.toml @@ -0,0 +1,5 @@ +# Generic, formatter-friendly config. +select = ["B", "D3", "E", "F"] + +# Never enforce `E501` (line length violations). This should be handled by formatters. +ignore = ["E501"] diff --git a/.trunk/trunk.yaml b/.trunk/trunk.yaml new file mode 100644 index 0000000..3519dd0 --- /dev/null +++ b/.trunk/trunk.yaml @@ -0,0 +1,42 @@ +# This file controls the behavior of Trunk: https://docs.trunk.io/cli +# To learn more about the format of this file, see https://docs.trunk.io/reference/trunk-yaml +version: 0.1 +cli: + version: 1.19.0 +# Trunk provides extensibility via plugins. (https://docs.trunk.io/plugins) +plugins: + sources: + - id: trunk + ref: v1.4.2 + uri: https://github.com/trunk-io/plugins +# Many linters and tools depend on runtimes - configure them here. (https://docs.trunk.io/runtimes) +runtimes: + enabled: + - go@1.21.0 + - node@18.12.1 + - python@3.10.8 +# This is the section where you manage your linters. (https://docs.trunk.io/check/configuration) +lint: + disabled: + - bandit + enabled: + - black@24.1.0 + - checkov@3.1.70 + - git-diff-check + - isort@5.13.2 + - markdownlint@0.38.0 + - osv-scanner@1.6.1 + - prettier@3.2.4 + - ruff@0.1.14 + - shellcheck@0.9.0 + - shfmt@3.6.0 + - trivy@0.48.3 + - trufflehog@3.64.0 + - yamllint@1.33.0 +actions: + disabled: + - trunk-announce + - trunk-check-pre-push + - trunk-fmt-pre-commit + enabled: + - trunk-upgrade-available diff --git a/file_encryptor.py b/file_encryptor.py index fa1e0c0..cc62799 100644 --- a/file_encryptor.py +++ b/file_encryptor.py @@ -15,15 +15,7 @@ if not os.path.isfile(filepath): print("File not found") sys.exit(1) - -# FIXME Do the below operations in chunks - - -# Read the file -with open(filepath, "rb") as f: - filebytes = f.read() -encodedbytes = hmacrypt.self_encrypt(filebytes, encoded=True) - -# Write the file -with open(filepath + ".enc", "wb") as f: - f.write(encodedbytes) \ No newline at end of file +hmacrypt.self_encrypt_file(filepath, filepath + ".enc") +print("Encrypted file: " + filepath + ".enc") +hmacrypt.self_decrypt_file(filepath + ".enc", filepath + ".dec.png") +print("Decrypted file: " + filepath + ".dec.png") \ No newline at end of file diff --git a/first_run.sh b/first_run.sh index 88d6665..cdf57da 100644 --- a/first_run.sh +++ b/first_run.sh @@ -1,3 +1,3 @@ #!/bin/bash -cd bins +cd bins || exit ./first_run diff --git a/src/bins/first_run b/src/bins/first_run index c473681..1b5d0c7 100755 --- a/src/bins/first_run +++ b/src/bins/first_run @@ -1,5 +1,5 @@ #!/bin/bash -cd routines +cd routines || exit ./1_hmac_secret_enumerate > .device ./2_hmac_secret_enroll ../.keyfile cd .. diff --git a/src/bins/hmac_secret_regenerate b/src/bins/hmac_secret_regenerate index d96b323..0352dc6 100755 --- a/src/bins/hmac_secret_regenerate +++ b/src/bins/hmac_secret_regenerate @@ -2,4 +2,4 @@ echo "Please note: the secret will be shown here and will NOT be saved. You MUST have your hardware key or equivalent to regenerate the secret. Saving your secret locally defeat the whole purpose of this tool." echo "--" -fido2-hmac-secret generate -f $1 +fido2-hmac-secret generate -f "$1" diff --git a/src/bins/routines/1_hmac_secret_enumerate b/src/bins/routines/1_hmac_secret_enumerate index f0fe77e..e859492 100755 --- a/src/bins/routines/1_hmac_secret_enumerate +++ b/src/bins/routines/1_hmac_secret_enumerate @@ -1,3 +1,4 @@ #!/bin/bash +# trunk-ignore(shellcheck/SC2312) HIDEVICE=$(fido2-hmac-secret enumerate | awk -F"dev" '{print $2}' | awk -F" " '{print $1}') -echo $HIDEVICE +echo "${HIDEVICE}" diff --git a/src/bins/routines/2_hmac_secret_enroll b/src/bins/routines/2_hmac_secret_enroll index b1b708a..d84a743 100755 --- a/src/bins/routines/2_hmac_secret_enroll +++ b/src/bins/routines/2_hmac_secret_enroll @@ -1,5 +1,5 @@ #!/bin/bash HIDEV=$(cat .device) echo "Please press the button or use the hardware key when prompted (usually AFTER setting your passphrase)" -fido2-hmac-secret enrol -d /dev$HIDEV -f $1 +fido2-hmac-secret enrol -d /dev"${HIDEV}" -f "$1" diff --git a/src/hmacrypt.py b/src/hmacrypt.py index b7197ba..47706be 100644 --- a/src/hmacrypt.py +++ b/src/hmacrypt.py @@ -1,7 +1,7 @@ from src.libs.seedable_rsa import generate_key, encrypt, decrypt import subprocess - +# INFO This method is the core of the whole process as it derives a RSA keypair from the stored secret and the hardware key def inferKeys(hidePrivate=False, savePublic=False, keyfilePath="src/bins/.keyfile"): """Infer keys from the secret stored in the hardware key""" hmac_secret_raw = subprocess.check_output(["src/bins/hmac_secret_regenerate", keyfilePath]) @@ -24,6 +24,9 @@ def inferKeys(hidePrivate=False, savePublic=False, keyfilePath="src/bins/.keyfil f.write(public_key) return private_key, public_key +# NOTE All the below methods generates keys on the fly to avoid persistance +# NOTE You should NEVER save the keypair to disk or even to a globlal variable +# NOTE Security is only guaranteed by the observance of the above rule # STRINGS @@ -39,13 +42,11 @@ def self_decrypt(encrypted): secret = decrypt(encrypted, private_key) return secret -# FILES - -# TODO Encrypt files in chunks to reduce memory usage +# SMALL FILES def self_encrypt_file(filepath, outpath): """Encrypt file with public key""" - private_key, public_key = inferKeys() + private_key, public_key = inferKeys(hidePrivate=True) with open(filepath, "rb") as f: filebytes = f.read() encrypted = encrypt(filebytes, public_key, encoded=True) @@ -63,6 +64,8 @@ def self_decrypt_file(filepath, outpath): f.write(decrypted) return outpath +# TODO LARGE FILES + # Self testing if __name__ == "__main__": diff --git a/src/libs/seedable_rsa.py b/src/libs/seedable_rsa.py index e673f15..b15fd6c 100644 --- a/src/libs/seedable_rsa.py +++ b/src/libs/seedable_rsa.py @@ -4,13 +4,13 @@ # Motivation: customization and actualization, also less dependencies # License: As per original repository +import base64 +import binascii +from struct import pack + from Crypto.Cipher import PKCS1_OAEP from Crypto.Hash import HMAC from Crypto.PublicKey import RSA -from struct import pack - -import base64 -import binascii def encrypt(message, public_key, encoded=False): @@ -33,7 +33,9 @@ def decrypt(encrypted_message, private_key): def generate_key(seed, nbytes=2048): # based on https://stackoverflow.com/questions/18264314/#answer-18266970 seed_128 = HMAC.new( - bytes(seed, 'utf-8') # + b'Application: 2nd key derivation' # Please note: this is not needed for the hardware key as the secret is already long enough (usually 128 bytes) + bytes( + seed, "utf-8" + ) # + b'Application: 2nd key derivation' # Please note: this is not needed for the hardware key as the secret is already long enough (usually 128 bytes) ).digest() class PRNG(object): @@ -45,8 +47,7 @@ def generate_key(seed, nbytes=2048): def __call__(self, n): while len(self.buffer) < n: - self.buffer += HMAC.new( - self.seed + pack("