Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion builder/Dockerfile_Debian
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 1 addition & 1 deletion builder/build_linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

set -xe

VERSION=0.6.3
VERSION=0.7.0
NAME='zk-firma-digital'
PACKAGE='zk-firma-digital'
ARCH='amd64'
Expand Down
2 changes: 1 addition & 1 deletion builder/build_mac.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion src/authentication_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
24 changes: 17 additions & 7 deletions src/certificate.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
2 changes: 1 addition & 1 deletion src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand Down
1 change: 1 addition & 0 deletions src/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ https://github.com/wbond/oscrypto/archive/d5f3437ed24257895ae1edd9e503cfb352e635
web3==7.4.0
cryptography
pyinstaller
pysqlcipher3
78 changes: 78 additions & 0 deletions src/storage.py
Original file line number Diff line number Diff line change
@@ -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()

10 changes: 6 additions & 4 deletions src/verification.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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):
Expand All @@ -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)
Expand Down