Introduction
In the ongoing debate over digital privacy and government access to encrypted data, Apple and Google have raised concerns about Canada's Bill C-22, which proposes lawful-access legislation. This tutorial will guide you through creating a secure encryption system that demonstrates the principles of encryption, key management, and access control that are central to this debate. You'll build a practical encryption tool that implements modern cryptographic techniques while understanding the balance between security and lawful access requirements.
Prerequisites
- Basic understanding of Python programming
- Python 3.6 or higher installed on your system
- Knowledge of basic cryptography concepts (symmetric and asymmetric encryption)
- Installed Python packages:
pycryptodome,hashlib,os,base64
Step-by-Step Instructions
1. Set Up Your Development Environment
First, create a new Python project directory and install the required dependencies:
mkdir encryption_project
cd encryption_project
pip install pycryptodome
This creates a clean workspace and installs the necessary cryptographic library for our implementation.
2. Create the Main Encryption Class
Start by creating the core encryption functionality that will handle both symmetric and asymmetric encryption:
import os
import hashlib
from Crypto.Cipher import AES, PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Random import get_random_bytes
import base64
class SecureEncryption:
def __init__(self):
self.key = None
self.public_key = None
self.private_key = None
def generate_keys(self):
"""Generate RSA key pair for asymmetric encryption"""
key = RSA.generate(2048)
self.private_key = key
self.public_key = key.publickey()
return self.public_key
def encrypt_symmetric(self, data, key=None):
"""Encrypt data using AES symmetric encryption"""
if key is None:
key = get_random_bytes(32) # 256-bit key
cipher = AES.new(key, AES.MODE_EAX)
ciphertext, tag = cipher.encrypt_and_digest(data.encode())
# Return encrypted data and nonce
return {
'ciphertext': base64.b64encode(ciphertext).decode(),
'nonce': base64.b64encode(cipher.nonce).decode(),
'tag': base64.b64encode(tag).decode(),
'key': base64.b64encode(key).decode()
}
def decrypt_symmetric(self, encrypted_data, key):
"""Decrypt data using AES symmetric encryption"""
key = base64.b64decode(key)
nonce = base64.b64decode(encrypted_data['nonce'])
tag = base64.b64decode(encrypted_data['tag'])
ciphertext = base64.b64decode(encrypted_data['ciphertext'])
cipher = AES.new(key, AES.MODE_EAX, nonce=nonce)
plaintext = cipher.decrypt_and_verify(ciphertext, tag)
return plaintext.decode()
def encrypt_asymmetric(self, data):
"""Encrypt data using RSA asymmetric encryption"""
if not self.public_key:
raise Exception("Public key not generated")
cipher = PKCS1_OAEP.new(self.public_key)
encrypted_data = cipher.encrypt(data.encode())
return base64.b64encode(encrypted_data).decode()
This foundation establishes both symmetric and asymmetric encryption methods, which represent the core technologies at the heart of the privacy vs. access debate.
3. Implement Key Management System
Now add a secure key management system that demonstrates how access control could be implemented:
def generate_key_with_access_control(self, access_level=1):
"""Generate a key with access level control"""
key = get_random_bytes(32)
# Simulate access control by storing key metadata
return {
'key': base64.b64encode(key).decode(),
'access_level': access_level,
'created_at': os.time()
}
def validate_access(self, access_level, required_level=2):
"""Validate if access level is sufficient"""
return access_level >= required_level
This simulates how governments might implement access control mechanisms while maintaining security.
4. Create Access Control Framework
Implement the judicial oversight concept by creating an access control framework:
def request_access(self, key_data, judicial_order=False):
"""Simulate a judicial access request"""
if judicial_order:
print("Judicial order received - granting access")
return True
else:
print("Standard access request - denied")
return False
def secure_decrypt(self, encrypted_data, access_level=1, judicial_order=False):
"""Decrypt with access control and judicial oversight"""
if not self.validate_access(access_level):
if not judicial_order:
raise PermissionError("Insufficient access level")
else:
print("Access granted through judicial order")
# Decrypt using symmetric key
return self.decrypt_symmetric(encrypted_data, encrypted_data['key'])
This demonstrates the core tension: how to balance security with lawful access requirements.
5. Test the Encryption System
Create a test script to verify the functionality:
def main():
# Initialize encryption system
crypto = SecureEncryption()
# Generate keys
public_key = crypto.generate_keys()
print("Keys generated successfully")
# Test symmetric encryption
message = "This is a confidential message"
encrypted = crypto.encrypt_symmetric(message)
print("Message encrypted")
# Test decryption
decrypted = crypto.decrypt_symmetric(encrypted, encrypted['key'])
print(f"Decrypted: {decrypted}")
# Test access control
try:
crypto.secure_decrypt(encrypted, access_level=1)
except PermissionError as e:
print(f"Access denied: {e}")
# Test judicial access
access_granted = crypto.request_access(encrypted, judicial_order=True)
if access_granted:
decrypted = crypto.secure_decrypt(encrypted, access_level=1, judicial_order=True)
print(f"Decrypted with judicial access: {decrypted}")
if __name__ == "__main__":
main()
This test demonstrates how the system would work in practice, showing both normal operation and how judicial oversight could be implemented.
6. Add Logging and Audit Trail
Enhance the system with logging to track access requests:
import logging
from datetime import datetime
def setup_logging(self):
"""Setup logging for access audit"""
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('access_log.txt'),
logging.StreamHandler()
]
)
def log_access_request(self, access_level, judicial_order=False):
"""Log access request for audit"""
logging.info(f"Access request - Level: {access_level}, Judicial Order: {judicial_order}")
def secure_decrypt_with_logging(self, encrypted_data, access_level=1, judicial_order=False):
"""Decrypt with logging"""
self.log_access_request(access_level, judicial_order)
if not self.validate_access(access_level):
if not judicial_order:
raise PermissionError("Insufficient access level")
else:
logging.info("Access granted through judicial order")
return self.decrypt_symmetric(encrypted_data, encrypted_data['key'])
This logging mechanism represents how access could be monitored in real-world implementations.
Summary
This tutorial demonstrated how to build a secure encryption system that illustrates the core concepts behind the privacy vs. access debate highlighted in the Canada Bill C-22 discussion. You've learned how to implement both symmetric and asymmetric encryption, create access control mechanisms, and simulate judicial oversight requirements. The system shows how encryption can maintain security while providing controlled access pathways, which is exactly what Apple and Google have been advocating for in their push for judicial oversight in lawful-access legislation.
The practical implementation demonstrates that secure systems can be designed to meet both privacy requirements and lawful access needs, striking a balance that protects user data while allowing for appropriate government oversight when required.



