From f6ae60147c58aee79a36c81b26f09980d71f7922 Mon Sep 17 00:00:00 2001 From: whaffman Date: Thu, 3 Jul 2025 12:31:54 +0200 Subject: [PATCH] Add initial implementation of OTP generation and management script --- .gitignore | 2 ++ ft_otp.py | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ key.hex | 1 + 3 files changed, 74 insertions(+) create mode 100644 .gitignore create mode 100644 key.hex diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4be3b2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.venv/ +ft_otp.key diff --git a/ft_otp.py b/ft_otp.py index e69de29..837c757 100644 --- a/ft_otp.py +++ b/ft_otp.py @@ -0,0 +1,71 @@ +import argparse +from cryptography.fernet import Fernet +from hashlib import sha256 +from base64 import urlsafe_b64encode + +def main(): + parser = argparse.ArgumentParser(description="Run the FT OTP script.") + parser.add_argument("-g", metavar="hexfile", type=str, help="Generate a new OTP key and save it to the specified file.") + parser.add_argument("-k", metavar="keyfile", type=str, help="Generate a OTP from the specified key file.") + args = parser.parse_args() + + if args.g: + # Call the function to generate a new OTP key + generate_otp_key(args.g) + elif args.k: + # Call the function to generate a OTP from the specified key file + generate_otp_from_key(args.k) + else: + print("No action specified. Use -g to generate a new OTP key or -k to generate a OTP from a key file.") + parser.print_help() + +def generate_otp_key(hexfile): + print(f"Generating OTP key from {hexfile}...") + machine_id = get_machine_hash() + with open(hexfile, 'rb') as f: + hex_key = f.read().strip() + fernet = Fernet(machine_id) + otp_key = fernet.encrypt(hex_key) + with open('./ft_otp.key', 'wb') as f: + f.write(otp_key) + +def generate_otp_from_key(keyfile): + import time + machine_id = get_machine_hash() + with open(keyfile, 'rb') as f: + otp_key = f.read().strip() + fernet = Fernet(machine_id) + otp = fernet.decrypt(otp_key) + counter = int(time.time() // 30) # Using time-based counter for HOTP + otp = HOTP(otp, counter) + # Print the generated OTP + print(f"Generated OTP: {otp}") + +def get_machine_hash() -> bytes: + """Retrieve the machine ID from /etc/machine-id.""" + try: + with open('/etc/machine-id', 'r') as f: + machine_id = f.read().strip() + sha256_hash = sha256(machine_id.encode()).digest() + return urlsafe_b64encode(sha256_hash) + except FileNotFoundError: + print("Machine ID file not found. Please ensure the system is properly configured.") + return None + + + +def HOTP(key, counter): + """Generate a one-time password using the HOTP algorithm. using RFC4226""" + import hmac + import struct + import hashlib + + counter = struct.pack('>Q', counter) + hmac_hash = hmac.new(key, counter, hashlib.sha1).digest() + offset = hmac_hash[-1] & 0x0F + otp = (struct.unpack('>I', hmac_hash[offset:offset + 4])[0] & 0x7FFFFFFF) % 1000000 + return str(otp).zfill(6) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/key.hex b/key.hex new file mode 100644 index 0000000..63aa3d4 --- /dev/null +++ b/key.hex @@ -0,0 +1 @@ +d779574a5815a54089b260694be7b29838e28a72e943bdd41e47656885118c7a3f29f6611ca1383f7e67e9868a80b8fd68c71be79b418e8984bdaef4991932e4