diff --git a/builder/Dockerfile_Debian b/builder/Dockerfile_Debian index 24cbe83..de38d0d 100644 --- a/builder/Dockerfile_Debian +++ b/builder/Dockerfile_Debian @@ -6,7 +6,7 @@ MAINTAINER Andres Gomez Ramirez @kuronosec RUN apt-get update && \ apt-get install -y build-essential libssl3 libssl-dev pcscd libxcb-xinerama0 libpcre3 \ - libffi-dev libnss3-dev dh-make alien nodejs npm + libffi-dev libnss3-dev dh-make alien nodejs npm libsqlcipher-dev pkg-config python3-dev RUN npm install -g snarkjs@latest diff --git a/builder/build_linux.sh b/builder/build_linux.sh index 4b36b17..5883c12 100755 --- a/builder/build_linux.sh +++ b/builder/build_linux.sh @@ -2,7 +2,7 @@ set -xe -VERSION=0.6.3 +VERSION=0.7.0 NAME='zk-firma-digital' PACKAGE='zk-firma-digital' ARCH='amd64' diff --git a/builder/build_mac.sh b/builder/build_mac.sh index c6872c6..7345b7b 100755 --- a/builder/build_mac.sh +++ b/builder/build_mac.sh @@ -5,7 +5,7 @@ set -xe source /etc/profile export LC_ALL="en_US.UTF-8" -VERSION='0.6.2' +VERSION='0.7.0' cd src pip install -r requirements.txt diff --git a/src/authentication_window.py b/src/authentication_window.py index d7332ae..6a4f73c 100755 --- a/src/authentication_window.py +++ b/src/authentication_window.py @@ -116,7 +116,7 @@ def on_submit_generate_credential(self): return # If the certificates were stored in disk then provide the option # to verify them - if not os.path.exists(self.config.certificate_path): + if not os.path.exists(os.path.join(self.config.credentials_path, "identity_wallet.db")): QMessageBox.information(self, self.tr("Certificado"), self.tr("No se pudo obtener el certificado")) self.generate_credential_button.setEnabled(True) self.generate_credential_button.setStyleSheet("background-color : green") diff --git a/src/certificate.py b/src/certificate.py index 63a7fb3..b84011e 100644 --- a/src/certificate.py +++ b/src/certificate.py @@ -11,6 +11,7 @@ from PyKCS11 import * from configuration import Configuration from pathlib import Path +from storage import Storage # This class interacts with the Smart Card and extracts the autentication certificate class Certificate: @@ -46,6 +47,8 @@ def __init__(self, pin): else: print("Unknown operating system") + self.storage = Storage(self.pin, self.credentials_path) + def get_certificates(self): """ Try to read the smart card to get the stored public certificates @@ -82,14 +85,21 @@ def get_certificates(self): if "AUTENTICACION" in common_name: result.append(common_name) - if not os.path.exists(self.credentials_path): - os.makedirs(self.credentials_path) + if not self.storage.exists(): + self.storage.create_DB() # cert is an instance of x509.Certificate - with open(self.config.certificate_path, 'wb') as f: - pprint.pprint(cert.native["tbs_certificate"]["subject"]) - der_bytes = cert.dump() - pem_bytes = pem.armor('CERTIFICATE', der_bytes) - f.write(pem_bytes) + pprint.pprint(cert.native["tbs_certificate"]["subject"]) + der_bytes = cert.dump() + pem_bytes = pem.armor('CERTIFICATE', der_bytes) + try: + self.storage.insert_identity( + "Firma Digital AUTENTICACION", + pem_bytes, + common_name + ) + self.storage.close_database() + except Exception as e: + logging.info("Firma Digital AUTENTICACION already exists", e) return True, result diff --git a/src/main.py b/src/main.py index ccc5889..88166b2 100755 --- a/src/main.py +++ b/src/main.py @@ -151,7 +151,7 @@ def on_submit_generate_credential(self): return # If the certificates were stored in disk then provide the option # to verify them - if not os.path.exists(self.config.certificate_path): + if not os.path.exists(os.path.join(self.config.credentials_path, "identity_wallet.db")): QMessageBox.information(self, self.tr("Certificado"), self.tr("No se pudo obtener el certificado")) self.generate_credential_button.setEnabled(True) self.generate_credential_button.setStyleSheet("background-color : green") diff --git a/src/requirements.txt b/src/requirements.txt index bcf47b7..b8f9fee 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -8,3 +8,4 @@ https://github.com/wbond/oscrypto/archive/d5f3437ed24257895ae1edd9e503cfb352e635 web3==7.4.0 cryptography pyinstaller +pysqlcipher3 \ No newline at end of file diff --git a/src/storage.py b/src/storage.py new file mode 100644 index 0000000..aeba7d5 --- /dev/null +++ b/src/storage.py @@ -0,0 +1,78 @@ +import os +import logging +from pysqlcipher3 import dbapi2 as sqlite + +class Storage: + def __init__(self, passphrase, db_path): + self.passphrase = passphrase + self.db_path = os.path.join(db_path, "identity_wallet.db") + + # Connect to encrypted DB or create one + self.conn = sqlite.connect(self.db_path) + self.cursor = self.conn.cursor() + + def create_DB(self): + # Set up encryption + self.cursor.execute(f"PRAGMA key='{self.passphrase}';") + + # Create table for identities + self.cursor.execute(''' + CREATE TABLE IF NOT EXISTS identities ( + id INTEGER PRIMARY KEY, + did TEXT NOT NULL, + data TEXT NOT NULL, + metadata TEXT + ); + ''') + + def exists(self): + if not os.path.exists(self.db_path): + return False + + try: + # Apply the decryption key + self.cursor.execute(f"PRAGMA key = '{self.passphrase}';") + + # Optional: Check if PRAGMA works (will fail if key is wrong) + self.cursor.execute("PRAGMA cipher_integrity_check;") + result = self.cursor.fetchone() + if result is None or result != "ok": + logging.info("❌ Integrity check failed") + return False + + # Check if 'identities' table exists + self.cursor.execute(''' + SELECT name FROM sqlite_master + WHERE type='table' AND name='identities'; + ''') + exists = self.cursor.fetchone() is not None + self.conn.close() + return exists + except Exception as e: + logging.info("❌ Error while validating DB:", e) + return False + + def insert_identity(self, did, data, metadata): + # Apply the decryption key + self.cursor.execute(f"PRAGMA key = '{self.passphrase}';") + # Insert identity + self.cursor.execute(''' + INSERT INTO identities (did, data, metadata) + VALUES (?, ?, ?) + ''', (did, data, metadata)) + + def get_identity(self, did): + # Apply the decryption key + self.cursor.execute(f"PRAGMA key = '{self.passphrase}';") + # Get specific identity + self.cursor.execute(''' + SELECT data FROM identities + WHERE did = ? + ''', (did,)) + identity = self.cursor.fetchone()[0] + return identity + + def close_database(self): + self.conn.commit() + self.conn.close() + diff --git a/src/verification.py b/src/verification.py index a4235cb..124a7fd 100644 --- a/src/verification.py +++ b/src/verification.py @@ -9,6 +9,7 @@ from certvalidator import CertificateValidator, ValidationContext, errors from configuration import Configuration from signature import Signature +from storage import Storage # This class helps to validate the certificate extracted from the smart card # to see if it actually was signed by the goverment chain of trust @@ -24,6 +25,7 @@ def __init__(self, pin, nullifier_seed=0, signal_hash=None): # We have a folder with the goverment certificates self.root_CA_path = self.config.root_CA_path + self.storage = Storage(self.pin, self.credentials_path) # Actually carry our the signature verification process def verify_certificate(self, user_certificate_path): @@ -34,10 +36,10 @@ def verify_certificate(self, user_certificate_path): trust_roots.append(der_bytes) # Load user certificate - with open(user_certificate_path, 'rb') as f: - end_entity_cert = f.read() - if pem.detect(end_entity_cert): - _, _, end_entity_cert = pem.unarmor(end_entity_cert) + end_entity_cert = self.storage.get_identity("Firma Digital AUTENTICACION") + self.storage.close_database() + if pem.detect(end_entity_cert): + _, _, end_entity_cert = pem.unarmor(end_entity_cert) user_cert = x509.Certificate.load(end_entity_cert) context = ValidationContext(trust_roots=trust_roots)