Last active
June 26, 2023 00:26
-
-
Save odudex/3c627700df658c1509e3ca673307a509 to your computer and use it in GitHub Desktop.
Sign files with Krux, airgapped and export openssl signature and public key
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
This python script is aimed to help and teach how Krux can be used to sign files and create PEM public keys so openssl can be used to verify | |
Requirements: | |
- opencv, qrcode | |
pip install opencv-python qrcode | |
- This script also calls a openssl bash command, so it is required to have verification functionality | |
""" | |
import argparse | |
import cv2 | |
import hashlib | |
from io import StringIO | |
import base64 | |
from qrcode import QRCode | |
import subprocess | |
parser = argparse.ArgumentParser( | |
prog="krux_file_signer", | |
description="This python script is aimed to help and teach how Krux can be used to sign files and create PEM public keys so openssl can be used to verify", | |
) | |
subparsers = parser.add_subparsers(help="sub-command help", dest="command") | |
signer = subparsers.add_parser("sign", help="sign a file") | |
signer.add_argument("--file", dest="file_to_sign", help="path to file to sign") | |
verifier = subparsers.add_parser("verify", help="verify signature") | |
verifier.add_argument("--file", dest="verify_file", help="path to file to verify") | |
verifier.add_argument("--sig-file", dest="sig_file", help="path to signature file") | |
verifier.add_argument("--pub-file", dest="pub_file", help="path to pubkey file") | |
def print_qr_code(data): | |
# Prints ascii QR code | |
qr_code = QRCode() | |
qr_code.add_data(data) | |
qr_string = StringIO() | |
qr_code.print_ascii(out=qr_string, invert=True) | |
print(qr_string.getvalue()) | |
def scan(): | |
"""Opens a scan window and uses cv2 to detect and decode a QR code, returning its data""" | |
vid = cv2.VideoCapture(0) | |
detector = cv2.QRCodeDetector() | |
qr_data = None | |
while True: | |
# Capture the video frame by frame | |
ret, frame = vid.read() | |
qr_data, bbox, straight_qrcode = detector.detectAndDecode(frame) | |
if len(qr_data) > 0: | |
break | |
# Display the resulting frame | |
cv2.imshow("frame", frame) | |
# the 'q' button is set as the | |
# quitting button you may use any | |
# desired button of your choice | |
if cv2.waitKey(1) & 0xFF == ord("q"): | |
break | |
# After the loop release the cap object | |
vid.release() | |
# Destroy all the windows | |
cv2.destroyAllWindows() | |
return qr_data | |
def verify(file_name, public_key_file, signature_file): | |
"""Uses openssl to verify the signature and public key""" | |
print("Verifying signature:") | |
subprocess.run( | |
"openssl sha256 <%s -binary | openssl pkeyutl -verify -pubin -inkey %s -sigfile %s" | |
% (file_name, public_key_file, signature_file), | |
shell=True, | |
) | |
args = parser.parse_args() | |
# If the sign command was given | |
if args.command == "sign" and args.file_to_sign is not None: | |
file_name = args.file_to_sign | |
try: | |
with open(file_name, "rb") as f: | |
bytes = f.read() # read file as bytes | |
readable_hash = hashlib.sha256(bytes).hexdigest() | |
except: | |
print("Unable to read target file") | |
# Prints the hash of the file | |
print("Hash of", file_name), ":" | |
print(readable_hash + "\n") | |
# Saves a hash file | |
hash_file = file_name + ".sha256sum.txt" | |
print("Saving a hash file:", hash_file) | |
with open(hash_file, "w") as f: | |
f.write(readable_hash) | |
print( | |
"To sign this file with Krux, load a 24 words key, use the Sign->Message feature and scan this QR code:" | |
) | |
# Prints the QR code | |
print_qr_code(readable_hash) | |
# Scans the signature QR code | |
_ = input("Press enter to scan signature") | |
signature = scan() | |
binary_signature = base64.b64decode(signature.encode()) | |
# Prints signature | |
print("Signature:", signature) | |
# Saves a signature file | |
signature_file = file_name + ".sig" | |
print("Saving a signature file:", signature_file) | |
with open(signature_file, "wb") as f: | |
f.write(binary_signature) | |
# Scans the public key | |
_ = input("Press enter to scan public key") | |
public_key = scan() | |
# Prints public key | |
print("Public key:", public_key) | |
# Saves a PEM public key file | |
public_key_data = "3036301006072A8648CE3D020106052B8104000A032200" | |
public_key_data += public_key | |
public_key_base64 = base64.b64encode(bytes.fromhex(public_key_data)).decode("utf-8") | |
pem_public_key = "-----BEGIN PUBLIC KEY-----\n" | |
pem_public_key += public_key_base64 + "\n" | |
pem_public_key += "-----END PUBLIC KEY-----" | |
public_key_file = "public_key.PEM" | |
print("Saving public key file:", public_key_file) | |
with open(public_key_file, "w") as f: | |
f.write(pem_public_key) | |
# Verify signature | |
verify(file_name, public_key_file, signature_file) | |
# Else if the verify command was given | |
elif ( | |
args.command == "verify" | |
and args.verify_file is not None | |
and args.sig_file is not None | |
and args.pub_file is not None | |
): | |
verify(args.verify_file, args.pub_file, args.sig_file) | |
# If command was not found | |
else: | |
parser.print_help() |
You can use argparse to manage arguments and/or subcommands;
made a little modification to fit; what do you think?
Thank you! copied your changes and did a few more to show help when wrong commands are given.
Maybe its worth to create a binary from it with pyinstaller, like @jreesun did, and then put it on repo?
I think this will help to use as a service on graphical user interface
i.e:
- krux_file_signer-linux
- krux_file_signer-win.exe
- krux_file_signer-mac
- krux_file_signer-mac-10
I think it is a good idea
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
You can use argparse to manage arguments and/or subcommands;
made a little modification to fit; what do you think?
usage: