securevault-pqc 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,110 @@
1
+ Metadata-Version: 2.4
2
+ Name: securevault-pqc
3
+ Version: 0.1.0
4
+ Summary: Post-quantum file encryption using ML-KEM-768 + X25519
5
+ Home-page: https://github.com/yourusername/securevault
6
+ Author: Reggy Mane
7
+ Author-email: your.email@example.com
8
+ Project-URL: Bug Tracker, https://github.com/yourusername/securevault/issues
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Information Technology
12
+ Classifier: Topic :: Security :: Cryptography
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Operating System :: OS Independent
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: liboqs-python>=0.8.0
23
+ Requires-Dist: cryptography>=41.0.0
24
+ Requires-Dist: click>=8.1.0
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: classifier
28
+ Dynamic: description
29
+ Dynamic: description-content-type
30
+ Dynamic: home-page
31
+ Dynamic: project-url
32
+ Dynamic: requires-dist
33
+ Dynamic: requires-python
34
+ Dynamic: summary
35
+
36
+ # SecureVault - Post-Quantum File Encryption
37
+ ***Protect your files from future quantum computers.***
38
+ ##
39
+
40
+ ![Python Version](https://img.shields.io/badge/python-3.9%2B-blue)
41
+ ![License](https://img.shields.io/badge/license-MIT-green)
42
+ ![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-lightgrey)
43
+ ![Cryptography](https://img.shields.io/badge/crypto-ML--KEM--768-purple)
44
+ ![NIST](https://img.shields.io/badge/NIST-Post--Quantum-orange)
45
+ ![Status](https://img.shields.io/badge/status-active-success)
46
+
47
+
48
+ ## The Problem
49
+ Quantum computers will break RSA/ECC encryption within 10-20 years.
50
+ Attackers are stealing encrypted data NOW to decrypt LATER.
51
+
52
+ This is what we called "**harvest now, decrypt later**" attacks.
53
+
54
+ ## The Solution
55
+ SecureVault uses ML-KEM-768 (NIST standardized post-quantum crypto)
56
+ in hybrid mode with X25519 for maximum security. Even if quantum computers break one layer, your data stays protected.
57
+
58
+ ## Features
59
+
60
+ - **Hybrid encryption**: X25519 + ML-KEM-768 (defense in depth²)
61
+ - **Password-protected keys**: PBKDF2 key derivation (100k iterations)
62
+ - **Cross-platform**: Works on Linux, macOS, and Windows
63
+ - **Future-proof**: Quantum-resistant for 30+ years
64
+ - **Easy CLI**: Simple commands, no crypto knowledge needed
65
+ - **Educational**: Shows security info about your files
66
+
67
+ ## Installation
68
+
69
+ ### Prerequisites
70
+ - Python 3.9 or higher
71
+ - pip
72
+
73
+ ### Setup
74
+ ```bash
75
+ # Clone the repo
76
+ git clone https://github.com/yourusername/securevault.git
77
+ cd securevault
78
+
79
+ # Install dependencies
80
+ pip install -r requirements.txt
81
+ ```
82
+
83
+ ## Quick Start
84
+ ```bash
85
+ # 1. Generate your keypair
86
+ python3 secure_vault.py keygen --password mypassword --output my_keys.key
87
+ # Creates: my_keys.key (private) and my_keys_public.key (public)
88
+
89
+ # 2. Encrypt a file
90
+ echo "Secret data" > secret.txt
91
+ python3 secure_vault.py encrypt secret.txt my_keys_public.key
92
+
93
+ # 3. Check security info
94
+ python3 secure_vault.py info secret.txt.vault
95
+
96
+ # 4. Decrypt it
97
+ python3 secure_vault.py decrypt secret.txt.vault my_keys.key --password mypassword
98
+ ```
99
+
100
+ ## Technical Details
101
+ - Hybrid: X25519 + ML-KEM-768
102
+ - File encryption: AES-256-GCM (Fernet)
103
+ - Key derivation: PBKDF2
104
+
105
+ ## Performance
106
+ [Benchmark results]
107
+
108
+ ## Why This Matters
109
+ [Quantum threat timeline]
110
+ [Use cases]
@@ -0,0 +1,75 @@
1
+ # SecureVault - Post-Quantum File Encryption
2
+ ***Protect your files from future quantum computers.***
3
+ ##
4
+
5
+ ![Python Version](https://img.shields.io/badge/python-3.9%2B-blue)
6
+ ![License](https://img.shields.io/badge/license-MIT-green)
7
+ ![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-lightgrey)
8
+ ![Cryptography](https://img.shields.io/badge/crypto-ML--KEM--768-purple)
9
+ ![NIST](https://img.shields.io/badge/NIST-Post--Quantum-orange)
10
+ ![Status](https://img.shields.io/badge/status-active-success)
11
+
12
+
13
+ ## The Problem
14
+ Quantum computers will break RSA/ECC encryption within 10-20 years.
15
+ Attackers are stealing encrypted data NOW to decrypt LATER.
16
+
17
+ This is what we called "**harvest now, decrypt later**" attacks.
18
+
19
+ ## The Solution
20
+ SecureVault uses ML-KEM-768 (NIST standardized post-quantum crypto)
21
+ in hybrid mode with X25519 for maximum security. Even if quantum computers break one layer, your data stays protected.
22
+
23
+ ## Features
24
+
25
+ - **Hybrid encryption**: X25519 + ML-KEM-768 (defense in depth²)
26
+ - **Password-protected keys**: PBKDF2 key derivation (100k iterations)
27
+ - **Cross-platform**: Works on Linux, macOS, and Windows
28
+ - **Future-proof**: Quantum-resistant for 30+ years
29
+ - **Easy CLI**: Simple commands, no crypto knowledge needed
30
+ - **Educational**: Shows security info about your files
31
+
32
+ ## Installation
33
+
34
+ ### Prerequisites
35
+ - Python 3.9 or higher
36
+ - pip
37
+
38
+ ### Setup
39
+ ```bash
40
+ # Clone the repo
41
+ git clone https://github.com/yourusername/securevault.git
42
+ cd securevault
43
+
44
+ # Install dependencies
45
+ pip install -r requirements.txt
46
+ ```
47
+
48
+ ## Quick Start
49
+ ```bash
50
+ # 1. Generate your keypair
51
+ python3 secure_vault.py keygen --password mypassword --output my_keys.key
52
+ # Creates: my_keys.key (private) and my_keys_public.key (public)
53
+
54
+ # 2. Encrypt a file
55
+ echo "Secret data" > secret.txt
56
+ python3 secure_vault.py encrypt secret.txt my_keys_public.key
57
+
58
+ # 3. Check security info
59
+ python3 secure_vault.py info secret.txt.vault
60
+
61
+ # 4. Decrypt it
62
+ python3 secure_vault.py decrypt secret.txt.vault my_keys.key --password mypassword
63
+ ```
64
+
65
+ ## Technical Details
66
+ - Hybrid: X25519 + ML-KEM-768
67
+ - File encryption: AES-256-GCM (Fernet)
68
+ - Key derivation: PBKDF2
69
+
70
+ ## Performance
71
+ [Benchmark results]
72
+
73
+ ## Why This Matters
74
+ [Quantum threat timeline]
75
+ [Use cases]
@@ -0,0 +1,388 @@
1
+ import oqs
2
+ from cryptography.hazmat.primitives.asymmetric import x25519
3
+ from cryptography.hazmat.primitives import serialization, hashes
4
+ from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
5
+ from cryptography.fernet import Fernet
6
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
7
+ import click
8
+ import os
9
+ import json
10
+ from pathlib import Path
11
+ import base64
12
+
13
+ def generate_hybrid_keypair():
14
+ # Generate X25519 key pair
15
+ private_key = x25519.X25519PrivateKey.generate()
16
+ public_key = private_key.public_key()
17
+
18
+ # Generate PQC key pair using oqs
19
+ kem = oqs.KeyEncapsulation('ML-KEM-768')
20
+ pqc_public_key = kem.generate_keypair()
21
+ pqc_secret_key = kem.export_secret_key()
22
+
23
+ # serialize keys
24
+ private_bytes = private_key.private_bytes(
25
+ encoding=serialization.Encoding.Raw,
26
+ format=serialization.PrivateFormat.Raw,
27
+ encryption_algorithm=serialization.NoEncryption()
28
+ )
29
+ public_bytes = public_key.public_bytes(
30
+ encoding=serialization.Encoding.Raw,
31
+ format=serialization.PublicFormat.Raw
32
+ )
33
+
34
+
35
+ # bundle keys together
36
+ return {
37
+ 'private_key': private_bytes,
38
+ 'public_key': public_bytes,
39
+ 'pqc_private_key': pqc_secret_key,
40
+ 'pqc_public_key': pqc_public_key,
41
+ 'algorithm': 'X25519+ML-KEM-768'
42
+ }
43
+
44
+ def save_keypair_to_file(keypair, filepath, password=None):
45
+ # Serialize keypair to JSON
46
+ keypair_serialized= {
47
+ 'private_key': base64.b64encode(keypair['private_key']).decode('utf-8'),
48
+ 'public_key': base64.b64encode(keypair['public_key']).decode('utf-8'),
49
+ 'pqc_private_key': base64.b64encode(keypair['pqc_private_key']).decode('utf-8'),
50
+ 'pqc_public_key': base64.b64encode(keypair['pqc_public_key']).decode('utf-8'),
51
+ 'algorithm': keypair['algorithm']
52
+ }
53
+
54
+ keypair_json = json.dumps(keypair_serialized).encode('utf-8')
55
+
56
+ if password:
57
+ salt = os.urandom(16)
58
+ kdf = PBKDF2HMAC(
59
+ algorithm=hashes.SHA256(),
60
+ length=32,
61
+ salt=salt,
62
+ iterations=100000,
63
+ )
64
+ key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
65
+ fernet = Fernet(key)
66
+ encrypted_dt = fernet.encrypt(keypair_json)
67
+ with open(filepath, 'wb') as f:
68
+ f.write(salt + encrypted_dt)
69
+
70
+ else:
71
+ with open(filepath, 'wb') as f:
72
+ f.write(keypair_json)
73
+
74
+
75
+
76
+
77
+
78
+ def load_keypair_from_file(filepath, password=None):
79
+ with open(filepath, 'rb') as f:
80
+ data = f.read()
81
+
82
+
83
+ #if encrypted
84
+ if password:
85
+ #derive key from password
86
+ salt = data[:16]
87
+ encrypted_dt = data[16:]
88
+ kdf = PBKDF2HMAC(
89
+ algorithm=hashes.SHA256(),
90
+ length=32,
91
+ salt=salt,
92
+ iterations=100000,
93
+ )
94
+ key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
95
+ fernet = Fernet(key)
96
+ decrypted_dt = fernet.decrypt(encrypted_dt)
97
+ keypair_json = decrypted_dt
98
+ else:
99
+ keypair_json = data
100
+ keypair_dict = json.loads(keypair_json)
101
+
102
+ return {
103
+ 'private_key': base64.b64decode(keypair_dict['private_key']),
104
+ 'public_key': base64.b64decode(keypair_dict['public_key']),
105
+ 'pqc_private_key': base64.b64decode(keypair_dict['pqc_private_key']),
106
+ 'pqc_public_key': base64.b64decode(keypair_dict['pqc_public_key']),
107
+ 'algorithm': keypair_dict['algorithm']
108
+ }
109
+
110
+
111
+ #yooo this is a lot harder than i thought lol
112
+
113
+
114
+ # def hybrid_encrypt_file(input_file, output_file, recipient_public_keys):
115
+
116
+ # #generate random file encryption key(32 bytes for AES-256)
117
+ # file_key = os.urandom(32)
118
+
119
+ # #encrypt file with AES (using Fernet)
120
+ # fernet = Fernet(base64.urlsafe_b64encode(file_key))
121
+ # with open(input_file, 'rb') as f:
122
+ # data = f.read()
123
+
124
+ # encrypted_dt = fernet.encrypt(data)
125
+ # #wrap file_key with classical crypto (x25519)
126
+ # private_key = x25519.X25519PrivateKey.generate()
127
+ # peer_public_key = x25519.X25519PublicKey.from_public_bytes(recipient_public_keys['public_key'])
128
+ # share_secret_classical = private_key.exchange(peer_public_key)
129
+
130
+ # # Create AESGCM instance with the shared secret
131
+ # #yo this was so hard to find an dunderstand
132
+ # aesgcm = AESGCM(share_secret_classical)
133
+ # classical_wrappped_key = aesgcm.encrypt(os.urandom(12), file_key, None)
134
+
135
+ # #wrap file_key with PQC crypto(ML-KEM-768)
136
+ # kem = oqs.KeyEncapsulation('ML-KEM-768')
137
+ # kem.import_public_key(recipient_public_keys['pqc_public_key'])
138
+ # pqc_ciphertext, pqc_shared_secret = kem.encap_secret(recipient_public_keys['pqc_public_key'])
139
+
140
+ # aesgcm_pqc = AESGCM(pqc_shared_secret)
141
+ # pqc_wrapped_key = aesgcm_pqc.encrypt(os.urandom(12), file_key, None)
142
+
143
+ # # package the whole thing into output file
144
+ # with open(output_file, 'wb') as f:
145
+ # f.write(encrypted_dt)
146
+
147
+
148
+ # return {
149
+ # 'encrypted_data': encrypted_dt.decode('utf-8'),
150
+ # 'classical_wrapped_key': classical_wrappped_key.decode('utf-8'),
151
+ # 'pqc_wrapped_key': pqc_wrapped_key.decode('utf-8'),
152
+ # 'pqc_ciphertext': pqc_ciphertext.decode('utf-8'),
153
+ # 'algorithm': 'hybrid-x25519-mlkem768'
154
+ # }
155
+
156
+ def hybrid_encrypt_file(input_file, output_file, recipient_public_keys):
157
+ """
158
+ Encrypt file using recipient's public keys with hybrid classical + PQC
159
+ """
160
+ # Step 1: Read the file
161
+ with open(input_file, 'rb') as f:
162
+ file_data = f.read()
163
+
164
+ # Step 2: Generate random symmetric key for file encryption
165
+ file_key = Fernet.generate_key()
166
+
167
+ # Step 3: Encrypt the file with symmetric key
168
+ fernet = Fernet(file_key)
169
+ encrypted_data = fernet.encrypt(file_data)
170
+
171
+ # Step 4: Wrap file_key with CLASSICAL crypto (X25519 + AES-GCM)
172
+ ephemeral_private = x25519.X25519PrivateKey.generate()
173
+ ephemeral_public = ephemeral_private.public_key()
174
+
175
+ recipient_public_classical = x25519.X25519PublicKey.from_public_bytes(
176
+ recipient_public_keys['classical_public_key']
177
+ )
178
+
179
+ shared_secret_classical = ephemeral_private.exchange(recipient_public_classical)
180
+
181
+ aesgcm = AESGCM(shared_secret_classical)
182
+ nonce_classical = os.urandom(12)
183
+
184
+ # Decode Fernet key to get raw 32 bytes
185
+ file_key_bytes = base64.urlsafe_b64decode(file_key)
186
+ classical_wrapped_key = aesgcm.encrypt(nonce_classical, file_key_bytes, None)
187
+
188
+ # Step 5: Wrap file_key with POST-QUANTUM crypto (ML-KEM-768)
189
+ kem = oqs.KeyEncapsulation('ML-KEM-768')
190
+ pqc_ciphertext, shared_secret_pqc = kem.encap_secret(recipient_public_keys['pqc_public_key'])
191
+
192
+ aesgcm_pqc = AESGCM(shared_secret_pqc)
193
+ nonce_pqc = os.urandom(12)
194
+ pqc_wrapped_key = aesgcm_pqc.encrypt(nonce_pqc, file_key_bytes, None)
195
+
196
+ # Step 6: Create encrypted package with all components
197
+ package = {
198
+ 'encrypted_data': base64.b64encode(encrypted_data).decode('utf-8'),
199
+ 'classical_wrapped_key': base64.b64encode(classical_wrapped_key).decode('utf-8'),
200
+ 'pqc_wrapped_key': base64.b64encode(pqc_wrapped_key).decode('utf-8'),
201
+ 'pqc_ciphertext': base64.b64encode(pqc_ciphertext).decode('utf-8'),
202
+ 'ephemeral_public_key': base64.b64encode(
203
+ ephemeral_public.public_bytes(
204
+ encoding=serialization.Encoding.Raw,
205
+ format=serialization.PublicFormat.Raw
206
+ )
207
+ ).decode('utf-8'),
208
+ 'nonce_classical': base64.b64encode(nonce_classical).decode('utf-8'),
209
+ 'nonce_pqc': base64.b64encode(nonce_pqc).decode('utf-8'),
210
+ 'algorithm': 'hybrid-x25519-mlkem768'
211
+ }
212
+
213
+ # Step 7: Write to output file as JSON
214
+ with open(output_file, 'w') as f:
215
+ json.dump(package, f, indent=2)
216
+
217
+ return package
218
+
219
+
220
+ def hybrid_decrypt_file(input_file, output_file, private_keys):
221
+ """
222
+ Decrypt file using your private keys
223
+ """
224
+ # Step 1: Load encrypted package from file
225
+ with open(input_file, 'r') as f:
226
+ package = json.load(f)
227
+
228
+ # Step 2: Base64 decode all the fields
229
+ encrypted_data = base64.b64decode(package['encrypted_data'])
230
+ classical_wrapped_key = base64.b64decode(package['classical_wrapped_key'])
231
+ pqc_wrapped_key = base64.b64decode(package['pqc_wrapped_key'])
232
+ pqc_ciphertext = base64.b64decode(package['pqc_ciphertext'])
233
+ ephemeral_public_bytes = base64.b64decode(package['ephemeral_public_key'])
234
+ nonce_classical = base64.b64decode(package['nonce_classical'])
235
+ nonce_pqc = base64.b64decode(package['nonce_pqc'])
236
+
237
+ # Step 3: Unwrap file_key using CLASSICAL crypto
238
+ private_key = x25519.X25519PrivateKey.from_private_bytes(
239
+ private_keys['private_key']
240
+ )
241
+ ephemeral_public = x25519.X25519PublicKey.from_public_bytes(ephemeral_public_bytes)
242
+
243
+ shared_secret_classical = private_key.exchange(ephemeral_public)
244
+
245
+ aesgcm = AESGCM(shared_secret_classical)
246
+ file_key_classical = aesgcm.decrypt(nonce_classical, classical_wrapped_key, None)
247
+
248
+ # Step 4: Unwrap file_key using POST-QUANTUM crypto
249
+ kem = oqs.KeyEncapsulation('ML-KEM-768', private_keys['pqc_private_key'])
250
+
251
+ shared_secret_pqc = kem.decap_secret(pqc_ciphertext)
252
+
253
+ aesgcm_pqc = AESGCM(shared_secret_pqc)
254
+ file_key_pqc = aesgcm_pqc.decrypt(nonce_pqc, pqc_wrapped_key, None)
255
+
256
+ # Step 5: VERIFY both unwraps produced same file_key
257
+ if file_key_classical != file_key_pqc:
258
+ raise ValueError("Security Error: Key mismatch between classical and PQC!")
259
+
260
+ # Step 6: Decrypt file with file_key
261
+ fernet = Fernet(base64.urlsafe_b64encode(file_key_classical))
262
+ decrypted_data = fernet.decrypt(encrypted_data)
263
+
264
+ # Step 7: Write to output file
265
+ with open(output_file, 'wb') as f:
266
+ f.write(decrypted_data)
267
+
268
+
269
+ # ==================== CLI COMMANDS ====================
270
+
271
+ @click.group()
272
+ def cli():
273
+ """SecureVault - Post-Quantum File Encryption"""
274
+ pass
275
+
276
+
277
+ @cli.command()
278
+ @click.option('--password', prompt=True, hide_input=True, confirmation_prompt=True,
279
+ help='Password to protect private key')
280
+ @click.option('--output', default='keypair.key', help='Output file for keypair')
281
+ def keygen(password, output):
282
+ """
283
+ Generate new hybrid keypair
284
+ """
285
+ keypair = generate_hybrid_keypair()
286
+
287
+ # Save private keypair (password protected)
288
+ save_keypair_to_file(keypair, output, password)
289
+
290
+ # Save public keys separately (NO password - anyone can use!)
291
+ public_key_file = output.replace('.key', '_public.key')
292
+ public_only = {
293
+ 'private_key': b'', # Empty, not needed for public key file
294
+ 'public_key': keypair['public_key'],
295
+ 'pqc_private_key': b'', # Empty, not needed
296
+ 'pqc_public_key': keypair['pqc_public_key'],
297
+ 'algorithm': keypair['algorithm']
298
+ }
299
+ save_keypair_to_file(public_only, public_key_file, password=None)
300
+
301
+ click.echo(click.style(f"✓ Private keypair saved to {output}", fg='green'))
302
+ click.echo(click.style(f"✓ Public keys saved to {public_key_file}", fg='green'))
303
+ click.echo(f"Algorithm: {keypair['algorithm']}")
304
+ click.echo(click.style("🔒 Classical: X25519", fg='cyan'))
305
+ click.echo(click.style("🔐 Post-Quantum: ML-KEM-768", fg='magenta'))
306
+ click.echo(click.style(f"\n📤 Share {public_key_file} with others!", fg='yellow'))
307
+
308
+ @cli.command()
309
+ @click.argument('input_file', type=click.Path(exists=True))
310
+ @click.argument('recipient_key', type=click.Path(exists=True))
311
+ @click.option('--output', help='Output encrypted file (default: input_file.vault)')
312
+ def encrypt(input_file, recipient_key, output): # ← REMOVED password parameter
313
+ """
314
+ Encrypt a file for a recipient using their PUBLIC keys (no password needed)
315
+ """
316
+ if output is None:
317
+ output = input_file + '.vault'
318
+
319
+ # Load recipient's keys WITHOUT password (we only need public keys!)
320
+ try:
321
+ recipient_keys = load_keypair_from_file(recipient_key, password=None)
322
+ except Exception as e:
323
+ click.echo(click.style("✗ Error: Could not load recipient's public key", fg='red'), err=True)
324
+ click.echo(click.style(" Make sure the key file is not password-protected, or use the public key file", fg='yellow'), err=True)
325
+ raise
326
+
327
+ recipient_public_keys = {
328
+ 'classical_public_key': recipient_keys['public_key'],
329
+ 'pqc_public_key': recipient_keys['pqc_public_key']
330
+ }
331
+
332
+ hybrid_encrypt_file(input_file, output, recipient_public_keys)
333
+
334
+ click.echo(click.style(f"✓ Encrypted {input_file} → {output}", fg='green'))
335
+ click.echo(click.style("🔒 Protected by classical AND post-quantum cryptography", fg='cyan'))
336
+ click.echo(click.style("🛡️ Quantum-resistant until 2050+", fg='magenta'))
337
+
338
+ @cli.command()
339
+ @click.argument('input_file', type=click.Path(exists=True))
340
+ @click.argument('private_key_file', type=click.Path(exists=True))
341
+ @click.option('--password', prompt=True, hide_input=True, help='Private key password')
342
+ @click.option('--output', help='Output decrypted file')
343
+ def decrypt(input_file, private_key_file, password, output):
344
+ """
345
+ Decrypt a file using your private key
346
+ """
347
+ private_keys = load_keypair_from_file(private_key_file, password)
348
+
349
+ if output is None:
350
+ output = input_file.replace('.vault', '')
351
+ if output == input_file: # No .vault extension found
352
+ output = input_file + '.decrypted'
353
+
354
+ hybrid_decrypt_file(input_file, output, private_keys)
355
+
356
+ click.echo(click.style(f"✓ Decrypted {input_file} → {output}", fg='green'))
357
+ click.echo(click.style("🔓 Verified with both classical and PQC keys", fg='cyan'))
358
+
359
+
360
+ @cli.command()
361
+ @click.argument('encrypted_file', type=click.Path(exists=True))
362
+ def info(encrypted_file):
363
+ """
364
+ Show security information about encrypted file
365
+ """
366
+ with open(encrypted_file, 'r') as f:
367
+ package = json.load(f)
368
+
369
+ click.echo(click.style("\n🔐 SecureVault Encrypted File Info", fg='cyan', bold=True))
370
+ click.echo("─" * 50)
371
+ click.echo(f"Algorithm: {click.style(package['algorithm'], fg='yellow')}")
372
+ click.echo(f"Classical protection: {click.style('✓ X25519', fg='green')}")
373
+ click.echo(f"Post-quantum protection: {click.style('✓ ML-KEM-768', fg='green')}")
374
+ click.echo(f"Quantum resistant: {click.style('YES', fg='green', bold=True)}")
375
+ click.echo(f"Safe until: {click.style('2050+ (estimated)', fg='magenta')}")
376
+
377
+ # File size info
378
+ encrypted_size = len(base64.b64decode(package['encrypted_data']))
379
+ click.echo(f"\nEncrypted data size: {click.style(f'{encrypted_size} bytes', fg='white')}")
380
+ click.echo("─" * 50)
381
+
382
+
383
+ if __name__ == '__main__':
384
+ cli()
385
+
386
+
387
+
388
+
@@ -0,0 +1,110 @@
1
+ Metadata-Version: 2.4
2
+ Name: securevault-pqc
3
+ Version: 0.1.0
4
+ Summary: Post-quantum file encryption using ML-KEM-768 + X25519
5
+ Home-page: https://github.com/yourusername/securevault
6
+ Author: Reggy Mane
7
+ Author-email: your.email@example.com
8
+ Project-URL: Bug Tracker, https://github.com/yourusername/securevault/issues
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: Intended Audience :: Information Technology
12
+ Classifier: Topic :: Security :: Cryptography
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Operating System :: OS Independent
20
+ Requires-Python: >=3.9
21
+ Description-Content-Type: text/markdown
22
+ Requires-Dist: liboqs-python>=0.8.0
23
+ Requires-Dist: cryptography>=41.0.0
24
+ Requires-Dist: click>=8.1.0
25
+ Dynamic: author
26
+ Dynamic: author-email
27
+ Dynamic: classifier
28
+ Dynamic: description
29
+ Dynamic: description-content-type
30
+ Dynamic: home-page
31
+ Dynamic: project-url
32
+ Dynamic: requires-dist
33
+ Dynamic: requires-python
34
+ Dynamic: summary
35
+
36
+ # SecureVault - Post-Quantum File Encryption
37
+ ***Protect your files from future quantum computers.***
38
+ ##
39
+
40
+ ![Python Version](https://img.shields.io/badge/python-3.9%2B-blue)
41
+ ![License](https://img.shields.io/badge/license-MIT-green)
42
+ ![Platform](https://img.shields.io/badge/platform-linux%20%7C%20macos%20%7C%20windows-lightgrey)
43
+ ![Cryptography](https://img.shields.io/badge/crypto-ML--KEM--768-purple)
44
+ ![NIST](https://img.shields.io/badge/NIST-Post--Quantum-orange)
45
+ ![Status](https://img.shields.io/badge/status-active-success)
46
+
47
+
48
+ ## The Problem
49
+ Quantum computers will break RSA/ECC encryption within 10-20 years.
50
+ Attackers are stealing encrypted data NOW to decrypt LATER.
51
+
52
+ This is what we called "**harvest now, decrypt later**" attacks.
53
+
54
+ ## The Solution
55
+ SecureVault uses ML-KEM-768 (NIST standardized post-quantum crypto)
56
+ in hybrid mode with X25519 for maximum security. Even if quantum computers break one layer, your data stays protected.
57
+
58
+ ## Features
59
+
60
+ - **Hybrid encryption**: X25519 + ML-KEM-768 (defense in depth²)
61
+ - **Password-protected keys**: PBKDF2 key derivation (100k iterations)
62
+ - **Cross-platform**: Works on Linux, macOS, and Windows
63
+ - **Future-proof**: Quantum-resistant for 30+ years
64
+ - **Easy CLI**: Simple commands, no crypto knowledge needed
65
+ - **Educational**: Shows security info about your files
66
+
67
+ ## Installation
68
+
69
+ ### Prerequisites
70
+ - Python 3.9 or higher
71
+ - pip
72
+
73
+ ### Setup
74
+ ```bash
75
+ # Clone the repo
76
+ git clone https://github.com/yourusername/securevault.git
77
+ cd securevault
78
+
79
+ # Install dependencies
80
+ pip install -r requirements.txt
81
+ ```
82
+
83
+ ## Quick Start
84
+ ```bash
85
+ # 1. Generate your keypair
86
+ python3 secure_vault.py keygen --password mypassword --output my_keys.key
87
+ # Creates: my_keys.key (private) and my_keys_public.key (public)
88
+
89
+ # 2. Encrypt a file
90
+ echo "Secret data" > secret.txt
91
+ python3 secure_vault.py encrypt secret.txt my_keys_public.key
92
+
93
+ # 3. Check security info
94
+ python3 secure_vault.py info secret.txt.vault
95
+
96
+ # 4. Decrypt it
97
+ python3 secure_vault.py decrypt secret.txt.vault my_keys.key --password mypassword
98
+ ```
99
+
100
+ ## Technical Details
101
+ - Hybrid: X25519 + ML-KEM-768
102
+ - File encryption: AES-256-GCM (Fernet)
103
+ - Key derivation: PBKDF2
104
+
105
+ ## Performance
106
+ [Benchmark results]
107
+
108
+ ## Why This Matters
109
+ [Quantum threat timeline]
110
+ [Use cases]
@@ -0,0 +1,9 @@
1
+ README.md
2
+ securevault.py
3
+ setup.py
4
+ securevault_pqc.egg-info/PKG-INFO
5
+ securevault_pqc.egg-info/SOURCES.txt
6
+ securevault_pqc.egg-info/dependency_links.txt
7
+ securevault_pqc.egg-info/entry_points.txt
8
+ securevault_pqc.egg-info/requires.txt
9
+ securevault_pqc.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ securevault = securevault:cli
@@ -0,0 +1,3 @@
1
+ liboqs-python>=0.8.0
2
+ cryptography>=41.0.0
3
+ click>=8.1.0
@@ -0,0 +1 @@
1
+ securevault
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,43 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ with open("README.md", "r", encoding="utf-8") as fh:
4
+ long_description = fh.read()
5
+
6
+ setup(
7
+ name="securevault-pqc", # Name on PyPI (securevault might be taken)
8
+ version="0.1.0",
9
+ author="Reggy Mane",
10
+ author_email="your.email@example.com",
11
+ description="Post-quantum file encryption using ML-KEM-768 + X25519",
12
+ long_description=long_description,
13
+ long_description_content_type="text/markdown",
14
+ url="https://github.com/yourusername/securevault",
15
+ project_urls={
16
+ "Bug Tracker": "https://github.com/yourusername/securevault/issues",
17
+ },
18
+ classifiers=[
19
+ "Development Status :: 4 - Beta",
20
+ "Intended Audience :: Developers",
21
+ "Intended Audience :: Information Technology",
22
+ "Topic :: Security :: Cryptography",
23
+ "License :: OSI Approved :: MIT License",
24
+ "Programming Language :: Python :: 3",
25
+ "Programming Language :: Python :: 3.9",
26
+ "Programming Language :: Python :: 3.10",
27
+ "Programming Language :: Python :: 3.11",
28
+ "Programming Language :: Python :: 3.12",
29
+ "Operating System :: OS Independent",
30
+ ],
31
+ py_modules=["securevault"],
32
+ python_requires=">=3.9",
33
+ install_requires=[
34
+ "liboqs-python>=0.8.0",
35
+ "cryptography>=41.0.0",
36
+ "click>=8.1.0",
37
+ ],
38
+ entry_points={
39
+ "console_scripts": [
40
+ "securevault=securevault:cli", # Creates 'securevault' command
41
+ ],
42
+ },
43
+ )