2021-03-25 09:38:27 +00:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
import os
|
|
|
|
from sys import path
|
|
|
|
import subprocess
|
|
|
|
import getpass
|
|
|
|
import hashlib
|
|
|
|
import tempfile
|
|
|
|
|
|
|
|
|
|
|
|
path.append(".")
|
|
|
|
import OpenPGPpy
|
|
|
|
|
|
|
|
|
2022-01-10 19:36:59 +00:00
|
|
|
class BadTestResult(Exception):
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2021-03-25 09:38:27 +00:00
|
|
|
def sha256(data):
|
|
|
|
return hashlib.sha256(data).digest()
|
|
|
|
|
|
|
|
|
2022-01-10 19:36:59 +00:00
|
|
|
def rsa_2048_pubkey_to_der(pubkey):
|
|
|
|
# Add ASN1 DER header
|
|
|
|
# RSA 2048
|
|
|
|
header_modulus = "30820122300d06092a864886f70d01010105000382010f003082010a0282010100"
|
|
|
|
header_exponent = "0203"
|
|
|
|
|
|
|
|
modulus = pubkey[9:265].hex()
|
|
|
|
exponent = pubkey[267:].hex()
|
|
|
|
|
|
|
|
return bytes.fromhex(header_modulus + modulus + header_exponent + exponent)
|
|
|
|
|
|
|
|
|
|
|
|
def check_rsa_2048_signature(message, signature, pubkeyd):
|
|
|
|
pubkey = rsa_2048_pubkey_to_der(pubkeyd)
|
|
|
|
fpk = tempfile.NamedTemporaryFile(delete=False)
|
|
|
|
fpk.write(pubkey)
|
|
|
|
fpk.close()
|
|
|
|
fsig = tempfile.NamedTemporaryFile(delete=False)
|
|
|
|
fsig.write(signature)
|
|
|
|
fsig.close()
|
|
|
|
verify_cmd = (
|
|
|
|
f"openssl dgst -sha256 -keyform DER -verify {fpk.name} -signature {fsig.name}"
|
|
|
|
)
|
|
|
|
sigOK = False
|
|
|
|
try:
|
|
|
|
subprocess.run(
|
|
|
|
verify_cmd, input=message, shell=True, check=True, stdout=subprocess.PIPE
|
|
|
|
)
|
|
|
|
sigOK = True
|
|
|
|
except Exception:
|
|
|
|
print("[-] ERROR in signature verification:")
|
|
|
|
print(">>> openssl is required in path to check signatures")
|
|
|
|
sigOK = False
|
|
|
|
os.remove(fpk.name)
|
|
|
|
os.remove(fsig.name)
|
|
|
|
return sigOK
|
|
|
|
|
|
|
|
|
|
|
|
def encrypt_rsa_2048(message, pubkeyd):
|
|
|
|
pubkey = rsa_2048_pubkey_to_der(pubkeyd)
|
|
|
|
fpk = tempfile.NamedTemporaryFile(delete=False)
|
|
|
|
fpk.write(pubkey)
|
|
|
|
fpk.close()
|
|
|
|
fenc = tempfile.NamedTemporaryFile(delete=False)
|
|
|
|
fenc.close()
|
|
|
|
encrypt_cmd = (
|
|
|
|
f"openssl rsautl -keyform DER -pubin -inkey {fpk.name} -pkcs -encrypt -out {fenc.name}"
|
|
|
|
)
|
|
|
|
try:
|
|
|
|
subprocess.run(
|
|
|
|
encrypt_cmd, input=message, shell=True, check=True, stdout=subprocess.PIPE
|
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
print("[-] ERROR in encrpytion")
|
|
|
|
print(">>> openssl is required in path to encrypt message")
|
|
|
|
raise
|
|
|
|
os.remove(fpk.name)
|
|
|
|
fres = open(fenc.name, 'rb')
|
|
|
|
res = fres.read()
|
|
|
|
fres.close()
|
|
|
|
os.remove(fenc.name)
|
|
|
|
return bytes.fromhex("00") + res
|
|
|
|
|
|
|
|
|
|
|
|
def test_rsa_2048_signature(token, PIN1, PIN3, repeat):
|
|
|
|
print(f"[+] == Test RSA 2048 signature ({repeat} signatures) ==")
|
|
|
|
|
|
|
|
# Verify PIN3, required for the next command
|
|
|
|
print("[+] Verify PIN3")
|
|
|
|
token.verify_pin(3, PIN3)
|
|
|
|
|
|
|
|
# Setup RSA 2048 for SIG key
|
|
|
|
print("[+] Set SIG key to RSA 2048")
|
|
|
|
token.put_data("00C1", "010800001103")
|
|
|
|
|
|
|
|
# Generate key for SIG key
|
|
|
|
print("[+] Generate SIG key")
|
|
|
|
pubkey_card = token.gen_key("B600")
|
|
|
|
|
|
|
|
# Digest message and sign
|
|
|
|
message = "Hello SmartPGP! Take that message.".encode("ascii")
|
|
|
|
header_sha256 = "3031300D060960864801650304020105000420";
|
|
|
|
hash = bytes.fromhex(header_sha256) + sha256(message)
|
|
|
|
|
|
|
|
for i in range(repeat):
|
|
|
|
print(f"[+] Test #{i + 1}")
|
|
|
|
|
|
|
|
# Verify PIN1, required for signature
|
|
|
|
token.verify_pin(1, PIN1)
|
|
|
|
|
|
|
|
print("[+] Sign SHA256 hash")
|
|
|
|
sig_card = token.sign(hash)
|
|
|
|
|
|
|
|
print("[+] Verify signature")
|
|
|
|
if not check_rsa_2048_signature(message, sig_card, pubkey_card):
|
|
|
|
print("[-] BAD signature")
|
|
|
|
raise BadTestResult
|
|
|
|
|
|
|
|
|
|
|
|
def test_rsa_2048_decrypt(token, PIN1, PIN3, repeat):
|
|
|
|
print(f"[+] == Test RSA 2048 decrypt ({repeat} deciphers) ==")
|
|
|
|
|
|
|
|
# Verify PIN3, required for the next command
|
|
|
|
print("[+] Verify PIN3")
|
|
|
|
token.verify_pin(3, PIN3)
|
|
|
|
|
|
|
|
# Setup RSA 2048 for DEC key
|
|
|
|
print("[+] Set DEC key to RSA 2048")
|
|
|
|
token.put_data("00C2", "010800001103")
|
|
|
|
|
|
|
|
# Generate key for DEC key
|
|
|
|
print("[+] Generate DEC key")
|
|
|
|
pubkey_card = token.gen_key("B800")
|
|
|
|
|
|
|
|
# Verify PIN2, required for decrypt
|
|
|
|
token.verify_pin(2, PIN1)
|
|
|
|
|
|
|
|
# Cipher message
|
|
|
|
message = "Hello SmartPGP! Take that message.".encode("ascii")
|
|
|
|
|
|
|
|
print("[+] Prepare encrypted message")
|
|
|
|
encrypted = encrypt_rsa_2048(message, pubkey_card)
|
|
|
|
|
|
|
|
for i in range(repeat):
|
|
|
|
print(f"[+] Test #{i + 1}")
|
|
|
|
|
|
|
|
print("[+] Decipher message")
|
|
|
|
decrypted = token.decipher(encrypted)
|
|
|
|
|
|
|
|
print("[+] Verify message")
|
|
|
|
if message == decrypted:
|
|
|
|
print("[+] Good deciphering")
|
|
|
|
else:
|
|
|
|
print("[-] BAD deciphering")
|
|
|
|
raise BadTestResult
|
|
|
|
|
|
|
|
|
|
|
|
def test_rsa_2048(token, PIN1, PIN3, repeat):
|
|
|
|
print("[+] === Test RSA 2048 ===")
|
|
|
|
|
|
|
|
test_rsa_2048_signature(token, PIN1, PIN3, repeat)
|
|
|
|
|
|
|
|
test_rsa_2048_decrypt(token, PIN1, PIN3, repeat)
|
|
|
|
|
|
|
|
|
|
|
|
def ec_prime256v1_pubkey_to_der(pubkey):
|
2021-03-25 09:38:27 +00:00
|
|
|
# Add ASN1 DER header (EC parameters)
|
|
|
|
# ECP 256 r1 header
|
|
|
|
header_hex = "3059301306072A8648CE3D020106082A8648CE3D030107034200"
|
|
|
|
return bytes.fromhex(header_hex + pubkey.hex())
|
|
|
|
|
|
|
|
|
2022-01-10 19:36:59 +00:00
|
|
|
def check_ec_prime256v1_signature(message, signature, pubkeyd):
|
|
|
|
pubkey = ec_prime256v1_pubkey_to_der(pubkeyd)
|
2021-03-25 09:38:27 +00:00
|
|
|
fpk = tempfile.NamedTemporaryFile(delete=False)
|
|
|
|
fpk.write(pubkey)
|
|
|
|
fpk.close()
|
|
|
|
fsig = tempfile.NamedTemporaryFile(delete=False)
|
|
|
|
fsig.write(signature)
|
|
|
|
fsig.close()
|
|
|
|
verify_cmd = (
|
|
|
|
f"openssl dgst -sha256 -keyform DER -verify {fpk.name} -signature {fsig.name}"
|
|
|
|
)
|
|
|
|
sigOK = False
|
|
|
|
try:
|
|
|
|
subprocess.run(
|
2022-01-10 19:36:59 +00:00
|
|
|
verify_cmd, input=message, shell=True, check=True, stdout=subprocess.PIPE
|
2021-03-25 09:38:27 +00:00
|
|
|
)
|
|
|
|
sigOK = True
|
|
|
|
except Exception:
|
2022-01-10 19:36:59 +00:00
|
|
|
print("[-] ERROR in signature verification")
|
|
|
|
print(">>> openssl is required in path to check signatures")
|
2021-03-25 09:38:27 +00:00
|
|
|
sigOK = False
|
|
|
|
os.remove(fpk.name)
|
|
|
|
os.remove(fsig.name)
|
|
|
|
return sigOK
|
|
|
|
|
|
|
|
|
2022-01-10 19:36:59 +00:00
|
|
|
def encrypt_ec_prime256v1(pubkeyd):
|
|
|
|
pubkey = ec_prime256v1_pubkey_to_der(pubkeyd)
|
|
|
|
fpub = tempfile.NamedTemporaryFile(delete=False)
|
|
|
|
fpub.write(pubkey)
|
|
|
|
fpub.close()
|
|
|
|
|
|
|
|
fpriv2 = tempfile.NamedTemporaryFile(delete=False)
|
|
|
|
fpriv2.close()
|
|
|
|
generate_private_cmd = (
|
|
|
|
f"openssl genpkey -outform DER -out {fpriv2.name} -algorithm EC -pkeyopt ec_paramgen_curve:prime256v1 -pkeyopt ec_param_enc:named_curve"
|
|
|
|
)
|
2021-03-25 09:38:27 +00:00
|
|
|
try:
|
2022-01-10 19:36:59 +00:00
|
|
|
subprocess.run(
|
|
|
|
generate_private_cmd, shell=True, check=True, stdout=subprocess.PIPE
|
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
print("[-] ERROR in encrpytion")
|
|
|
|
print(">>> openssl is required in path to encrypt message")
|
|
|
|
raise
|
|
|
|
|
|
|
|
fpub2 = tempfile.NamedTemporaryFile(delete=False)
|
|
|
|
fpub2.close()
|
|
|
|
generate_pub_cmd = (
|
|
|
|
f"openssl pkey -pubout -inform DER -in {fpriv2.name} -outform DER -out {fpub2.name}"
|
|
|
|
)
|
2021-03-25 09:38:27 +00:00
|
|
|
try:
|
2022-01-10 19:36:59 +00:00
|
|
|
subprocess.run(
|
|
|
|
generate_pub_cmd, shell=True, check=True, stdout=subprocess.PIPE
|
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
print("[-] ERROR in encrpytion")
|
|
|
|
print(">>> openssl is required in path to encrypt message")
|
|
|
|
raise
|
|
|
|
|
|
|
|
fenc = tempfile.NamedTemporaryFile(delete=False)
|
|
|
|
fenc.close()
|
|
|
|
generate_ecdh_cmd = (
|
|
|
|
f"openssl pkeyutl -derive -out {fenc.name} -keyform DER -inkey {fpriv2.name} -peerform DER -peerkey {fpub.name}"
|
|
|
|
)
|
|
|
|
try:
|
|
|
|
subprocess.run(
|
|
|
|
generate_ecdh_cmd, shell=True, check=True, stdout=subprocess.PIPE
|
|
|
|
)
|
|
|
|
except Exception:
|
|
|
|
print("[-] ERROR in encrpytion")
|
|
|
|
print(">>> openssl is required in path to encrypt message")
|
|
|
|
raise
|
|
|
|
|
|
|
|
f = open(fpub2.name, 'rb')
|
|
|
|
pub2 = f.read()
|
|
|
|
pub2 = pub2[26:]
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
f = open(fenc.name, 'rb')
|
|
|
|
enc = f.read()
|
|
|
|
f.close()
|
|
|
|
|
|
|
|
os.remove(fpub.name)
|
|
|
|
os.remove(fpriv2.name)
|
|
|
|
os.remove(fpub2.name)
|
|
|
|
os.remove(fenc.name)
|
|
|
|
|
|
|
|
return (bytes.fromhex("A646") + bytes.fromhex("7F4943") + bytes.fromhex("8641") + pub2, enc)
|
|
|
|
|
|
|
|
|
|
|
|
def test_ec_prime256v1_signature(token, PIN1, PIN3, repeat):
|
|
|
|
print(f"[+] == Test EC prime256v1 signature ({repeat} signatures) ==")
|
|
|
|
|
|
|
|
# Verify PIN3, required for the next command
|
|
|
|
print("[+] Verify PIN3")
|
|
|
|
token.verify_pin(3, PIN3)
|
|
|
|
|
|
|
|
# Setup EC prime256v1 for SIG key
|
|
|
|
print("[+] Set SIG key to EC prime256v1")
|
|
|
|
token.put_data("00C1", "132A8648CE3D030107")
|
|
|
|
|
|
|
|
# Generate key for SIG key
|
|
|
|
print("[+] Generate SIG key")
|
|
|
|
pubkey_card = token.gen_key("B600")
|
|
|
|
pubkey_card = pubkey_card[-65:]
|
|
|
|
|
|
|
|
# Digest message and sign
|
2021-03-25 09:38:27 +00:00
|
|
|
message = "Hello SmartPGP! Take that message.".encode("ascii")
|
|
|
|
hash = sha256(message)
|
2022-01-10 19:36:59 +00:00
|
|
|
|
|
|
|
for i in range(repeat):
|
|
|
|
print(f"[+] Test #{i + 1}")
|
|
|
|
|
|
|
|
# Verify PIN1, required for signature
|
|
|
|
token.verify_pin(1, PIN1)
|
|
|
|
|
|
|
|
print("[+] Sign SHA256 hash")
|
|
|
|
sig_card = token.sign_ec_der(hash)
|
|
|
|
|
|
|
|
print("[+] Verify signature")
|
|
|
|
if not check_ec_prime256v1_signature(message, sig_card, pubkey_card):
|
|
|
|
print("[-] BAD signature")
|
|
|
|
raise BadTestResult
|
|
|
|
|
|
|
|
|
|
|
|
def test_ec_prime256v1_decrypt(token, PIN1, PIN3, repeat):
|
|
|
|
print(f"[+] == Test EC prime256v1 decrypt ({repeat} deciphers) ==")
|
|
|
|
|
|
|
|
# Verify PIN3, required for the next command
|
|
|
|
print("[+] Verify PIN3")
|
|
|
|
token.verify_pin(3, PIN3)
|
|
|
|
|
|
|
|
# Setup EC prime256v1 for DEC key
|
|
|
|
print("[+] Set DEC key to EC prime256v1")
|
|
|
|
token.put_data("00C2", "122A8648CE3D030107")
|
|
|
|
|
|
|
|
# Generate key for DEC key
|
|
|
|
print("[+] Generate DEC key")
|
|
|
|
pubkey_card = token.gen_key("B800")
|
|
|
|
pubkey_card = pubkey_card[-65:]
|
|
|
|
|
|
|
|
# Verify PIN2, required for decrypt
|
|
|
|
token.verify_pin(2, PIN1)
|
|
|
|
|
|
|
|
print("[+] Prepare ECDH")
|
|
|
|
(encrypted, secret) = encrypt_ec_prime256v1(pubkey_card)
|
|
|
|
|
|
|
|
for i in range(repeat):
|
|
|
|
print(f"[+] Test #{i + 1}")
|
|
|
|
|
|
|
|
print("[+] Decipher (compute ECDH secret)")
|
|
|
|
decrypted = token.decipher(encrypted)
|
|
|
|
|
|
|
|
print("[+] Verify message")
|
|
|
|
if secret == decrypted:
|
|
|
|
print("[+] Good deciphering")
|
2021-03-25 09:38:27 +00:00
|
|
|
else:
|
2022-01-10 19:36:59 +00:00
|
|
|
print("[-] BAD deciphering")
|
|
|
|
raise BadTestResult
|
|
|
|
|
|
|
|
|
|
|
|
def test_ec_prime256v1(token, PIN1, PIN3, repeat):
|
|
|
|
print("[+] === Test EC prime256v1 ===")
|
|
|
|
|
|
|
|
test_ec_prime256v1_signature(token, PIN1, PIN3, repeat)
|
|
|
|
|
|
|
|
test_ec_prime256v1_decrypt(token, PIN1, PIN3, repeat)
|
|
|
|
|
|
|
|
|
|
|
|
def main(rsa, ec, repeat):
|
|
|
|
# True/False == with/without debug
|
|
|
|
token = OpenPGPpy.OpenPGPcard(False)
|
|
|
|
|
|
|
|
PIN1 = "123456"
|
|
|
|
PIN3 = "12345678"
|
|
|
|
|
|
|
|
if rsa:
|
|
|
|
test_rsa_2048(token, PIN1, PIN3, repeat)
|
|
|
|
|
|
|
|
if ec:
|
|
|
|
test_ec_prime256v1(token, PIN1, PIN3, repeat)
|
2021-03-25 09:38:27 +00:00
|
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
try:
|
2022-01-10 19:36:59 +00:00
|
|
|
main(rsa=True, ec=False, repeat=200)
|
|
|
|
except OpenPGPpy.openpgp_card.ConnectionException:
|
|
|
|
print(f"[-] FAILED to find OpenPGP token")
|
|
|
|
exit(1)
|
2021-03-25 09:38:27 +00:00
|
|
|
except OpenPGPpy.PGPCardException as exc:
|
2022-01-10 19:36:59 +00:00
|
|
|
if exc.sw_code != 0x9000:
|
|
|
|
print(f"[-] FAILED with: 0x{exc.sw_code:02x}")
|
|
|
|
exit(2)
|
|
|
|
except BadTestResult:
|
|
|
|
print(f"[-] FAILED test")
|
|
|
|
exit(3)
|