Skip to content

Instantly share code, notes, and snippets.

@odudex
Last active June 26, 2023 00:26
Show Gist options
  • Save odudex/3c627700df658c1509e3ca673307a509 to your computer and use it in GitHub Desktop.
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 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()
@qlrd
Copy link

qlrd commented Jun 25, 2023

You can use argparse to manage arguments and/or subcommands;

made a little modification to fit; what do you think?

usage:

python krux_file_signer.py --help
python krux_file_signer.py sign --help
python krux_file_signer.py verify --help
python krux_file_signer.py sign --file <file>
python krux_file_signer.py verify --file <file> --sig-file <sig_file> --pub-file <pub_file>

@odudex
Copy link
Author

odudex commented Jun 25, 2023

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.

@qlrd
Copy link

qlrd commented Jun 25, 2023

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

@odudex
Copy link
Author

odudex commented Jun 26, 2023

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