cryptknox 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.
- cryptknox-0.1.0/LICENSE +7 -0
- cryptknox-0.1.0/PKG-INFO +195 -0
- cryptknox-0.1.0/README.md +181 -0
- cryptknox-0.1.0/cryptknox/Cli.py +244 -0
- cryptknox-0.1.0/cryptknox/Items.py +92 -0
- cryptknox-0.1.0/cryptknox/Storage.py +21 -0
- cryptknox-0.1.0/cryptknox/algorithm/Aes.py +54 -0
- cryptknox-0.1.0/cryptknox/algorithm/Generatepass.py +2 -0
- cryptknox-0.1.0/cryptknox/algorithm/__init__.py +0 -0
- cryptknox-0.1.0/cryptknox/lib/Arguments.py +31 -0
- cryptknox-0.1.0/cryptknox/lib/__init__.py +0 -0
- cryptknox-0.1.0/cryptknox.egg-info/PKG-INFO +195 -0
- cryptknox-0.1.0/cryptknox.egg-info/SOURCES.txt +18 -0
- cryptknox-0.1.0/cryptknox.egg-info/dependency_links.txt +1 -0
- cryptknox-0.1.0/cryptknox.egg-info/entry_points.txt +2 -0
- cryptknox-0.1.0/cryptknox.egg-info/requires.txt +2 -0
- cryptknox-0.1.0/cryptknox.egg-info/top_level.txt +1 -0
- cryptknox-0.1.0/pyproject.toml +28 -0
- cryptknox-0.1.0/requirements.txt +2 -0
- cryptknox-0.1.0/setup.cfg +4 -0
cryptknox-0.1.0/LICENSE
ADDED
cryptknox-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cryptknox
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A light weight secure CLI tool for storing, retrieving, deleting, and generating encrypted passwords.
|
|
5
|
+
Author-email: Sreejith <sreejitharee123@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/Sreejith-R-Mohan/cryptknox
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: cryptography==46.0.5
|
|
12
|
+
Requires-Dist: platformdirs==4.9.4
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# CryptKnox 🔐
|
|
16
|
+
|
|
17
|
+
**CryptKnox** is a lightweight **CLI-based encrypted password manager** written in Python.
|
|
18
|
+
It allows you to securely **store, retrieve, delete, and generate passwords** using a single master password.
|
|
19
|
+
|
|
20
|
+
All stored credentials are **encrypted locally using AES encryption**, ensuring your secrets remain protected.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
* 🔐 **AES Encryption** for secure password storage
|
|
27
|
+
* 🗂 **Store multiple accounts per service**
|
|
28
|
+
* 🔍 **Retrieve specific services or all stored credentials**
|
|
29
|
+
* ❌ **Delete credentials securely**
|
|
30
|
+
* 🔑 **Generate strong random passwords**
|
|
31
|
+
* 💻 **Simple CLI interface**
|
|
32
|
+
* 📦 **Installable via PyPI**
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
Install from PyPI:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install cryptknox
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
After installation you can run:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cryptknox --help
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
cryptknox --operation|-o [store|retrieve|delete|generate] [OPTIONS]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Operations
|
|
61
|
+
|
|
62
|
+
### Store Credentials
|
|
63
|
+
|
|
64
|
+
Store a password for a service.
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
cryptknox -o store -m MySecretPassword -s gmail -u user@gmail.com -p mypassword
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Options:
|
|
71
|
+
|
|
72
|
+
| Option | Description |
|
|
73
|
+
| ------------------------- | ----------------------------------- |
|
|
74
|
+
| `--operation`, `-o` | Operation (`store`) |
|
|
75
|
+
| `--master-password`, `-m` | Master password used for encryption |
|
|
76
|
+
| `--service`, `-s` | Service name (example: gmail) |
|
|
77
|
+
| `--username`, `-u` | Username for the service |
|
|
78
|
+
| `--password`, `-p` | Password to store |
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### Retrieve Credentials
|
|
83
|
+
|
|
84
|
+
Retrieve stored passwords.
|
|
85
|
+
|
|
86
|
+
Retrieve a specific service:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
cryptknox -o retrieve -m MySecretPassword -s gmail
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Retrieve all services:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
cryptknox -o retrieve -m MySecretPassword -s all
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### Delete Credentials
|
|
101
|
+
|
|
102
|
+
Delete stored credentials.
|
|
103
|
+
|
|
104
|
+
Delete a specific service entry:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
cryptknox -o delete -m MySecretPassword -s gmail
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Delete all entries:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
cryptknox -o delete -m MySecretPassword -s all
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### Generate Secure Password
|
|
119
|
+
|
|
120
|
+
Generate a random secure password.
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
cryptknox -o generate -l 12
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Options:
|
|
127
|
+
|
|
128
|
+
| Option | Description |
|
|
129
|
+
| ---------------- | ----------------------------------------------------- |
|
|
130
|
+
| `--length`, `-l` | Length of generated password (must be greater than 4) |
|
|
131
|
+
|
|
132
|
+
The generated password includes:
|
|
133
|
+
|
|
134
|
+
* uppercase letters
|
|
135
|
+
* lowercase letters
|
|
136
|
+
* numbers
|
|
137
|
+
* special characters
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Examples
|
|
142
|
+
|
|
143
|
+
Store credentials:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
cryptknox -o store -m MySecretPassword -s github -u myuser -p mypassword
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Retrieve all credentials:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
cryptknox -o retrieve -m MySecretPassword -s all
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Generate a strong password:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
cryptknox -o generate -l 16
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Delete credentials:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
cryptknox -o delete -m MySecretPassword -s github
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Security Notes
|
|
170
|
+
|
|
171
|
+
* All credentials are **encrypted before being stored**.
|
|
172
|
+
* Only the **master password can decrypt the vault**.
|
|
173
|
+
* The master password is **never stored**.
|
|
174
|
+
|
|
175
|
+
⚠️ If the master password is lost, the vault **cannot be recovered**.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Requirements
|
|
180
|
+
|
|
181
|
+
* Python **3.9+**
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
MIT License
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Author
|
|
192
|
+
|
|
193
|
+
Developed by **Sreejith**
|
|
194
|
+
|
|
195
|
+
GitHub: https://github.com/Sreejith-R-Mohan/cryptknox
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# CryptKnox 🔐
|
|
2
|
+
|
|
3
|
+
**CryptKnox** is a lightweight **CLI-based encrypted password manager** written in Python.
|
|
4
|
+
It allows you to securely **store, retrieve, delete, and generate passwords** using a single master password.
|
|
5
|
+
|
|
6
|
+
All stored credentials are **encrypted locally using AES encryption**, ensuring your secrets remain protected.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
* 🔐 **AES Encryption** for secure password storage
|
|
13
|
+
* 🗂 **Store multiple accounts per service**
|
|
14
|
+
* 🔍 **Retrieve specific services or all stored credentials**
|
|
15
|
+
* ❌ **Delete credentials securely**
|
|
16
|
+
* 🔑 **Generate strong random passwords**
|
|
17
|
+
* 💻 **Simple CLI interface**
|
|
18
|
+
* 📦 **Installable via PyPI**
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
Install from PyPI:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install cryptknox
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
After installation you can run:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
cryptknox --help
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## Usage
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
cryptknox --operation|-o [store|retrieve|delete|generate] [OPTIONS]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Operations
|
|
47
|
+
|
|
48
|
+
### Store Credentials
|
|
49
|
+
|
|
50
|
+
Store a password for a service.
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
cryptknox -o store -m MySecretPassword -s gmail -u user@gmail.com -p mypassword
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Options:
|
|
57
|
+
|
|
58
|
+
| Option | Description |
|
|
59
|
+
| ------------------------- | ----------------------------------- |
|
|
60
|
+
| `--operation`, `-o` | Operation (`store`) |
|
|
61
|
+
| `--master-password`, `-m` | Master password used for encryption |
|
|
62
|
+
| `--service`, `-s` | Service name (example: gmail) |
|
|
63
|
+
| `--username`, `-u` | Username for the service |
|
|
64
|
+
| `--password`, `-p` | Password to store |
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
### Retrieve Credentials
|
|
69
|
+
|
|
70
|
+
Retrieve stored passwords.
|
|
71
|
+
|
|
72
|
+
Retrieve a specific service:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
cryptknox -o retrieve -m MySecretPassword -s gmail
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Retrieve all services:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
cryptknox -o retrieve -m MySecretPassword -s all
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
### Delete Credentials
|
|
87
|
+
|
|
88
|
+
Delete stored credentials.
|
|
89
|
+
|
|
90
|
+
Delete a specific service entry:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
cryptknox -o delete -m MySecretPassword -s gmail
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Delete all entries:
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
cryptknox -o delete -m MySecretPassword -s all
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### Generate Secure Password
|
|
105
|
+
|
|
106
|
+
Generate a random secure password.
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
cryptknox -o generate -l 12
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Options:
|
|
113
|
+
|
|
114
|
+
| Option | Description |
|
|
115
|
+
| ---------------- | ----------------------------------------------------- |
|
|
116
|
+
| `--length`, `-l` | Length of generated password (must be greater than 4) |
|
|
117
|
+
|
|
118
|
+
The generated password includes:
|
|
119
|
+
|
|
120
|
+
* uppercase letters
|
|
121
|
+
* lowercase letters
|
|
122
|
+
* numbers
|
|
123
|
+
* special characters
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Examples
|
|
128
|
+
|
|
129
|
+
Store credentials:
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
cryptknox -o store -m MySecretPassword -s github -u myuser -p mypassword
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Retrieve all credentials:
|
|
136
|
+
|
|
137
|
+
```
|
|
138
|
+
cryptknox -o retrieve -m MySecretPassword -s all
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Generate a strong password:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
cryptknox -o generate -l 16
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Delete credentials:
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
cryptknox -o delete -m MySecretPassword -s github
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## Security Notes
|
|
156
|
+
|
|
157
|
+
* All credentials are **encrypted before being stored**.
|
|
158
|
+
* Only the **master password can decrypt the vault**.
|
|
159
|
+
* The master password is **never stored**.
|
|
160
|
+
|
|
161
|
+
⚠️ If the master password is lost, the vault **cannot be recovered**.
|
|
162
|
+
|
|
163
|
+
---
|
|
164
|
+
|
|
165
|
+
## Requirements
|
|
166
|
+
|
|
167
|
+
* Python **3.9+**
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## License
|
|
172
|
+
|
|
173
|
+
MIT License
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Author
|
|
178
|
+
|
|
179
|
+
Developed by **Sreejith**
|
|
180
|
+
|
|
181
|
+
GitHub: https://github.com/Sreejith-R-Mohan/cryptknox
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
#! /usr/bin/python
|
|
2
|
+
from .lib.Arguments import Arguments
|
|
3
|
+
from .algorithm.Aes import Aes
|
|
4
|
+
import sys,json
|
|
5
|
+
from .Items import Items
|
|
6
|
+
from .Storage import Storage
|
|
7
|
+
|
|
8
|
+
import secrets, string, random
|
|
9
|
+
class Cli:
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
allowed_options = ('--operation','-o','--master-password','-m','--service','-s','--username','-u','--password','-p','--length','-l','--help','-h')
|
|
13
|
+
@staticmethod
|
|
14
|
+
def help():
|
|
15
|
+
HELP_TEXT = """
|
|
16
|
+
CryptKnox – Encrypted CLI Password Manager
|
|
17
|
+
|
|
18
|
+
USAGE
|
|
19
|
+
cryptknox --operation|-o [store|retrieve|delete|generate] [OPTIONS]
|
|
20
|
+
|
|
21
|
+
OPERATIONS
|
|
22
|
+
|
|
23
|
+
store
|
|
24
|
+
Store a new password entry in the encrypted vault.
|
|
25
|
+
|
|
26
|
+
Required Options:
|
|
27
|
+
--master-password, -m Master password used to encrypt/decrypt the vault
|
|
28
|
+
--service, -s Service name (example: gmail)
|
|
29
|
+
--username, -u Username for the service
|
|
30
|
+
--password, -p Password to store
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
cryptknox -o store -m MySecretPassword -s gmail -u user@gmail.com -p mypassword
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
retrieve
|
|
37
|
+
Retrieve stored credentials from the vault.
|
|
38
|
+
|
|
39
|
+
Required Options:
|
|
40
|
+
--master-password, -m Master password used to decrypt the vault
|
|
41
|
+
--service, -s Service name or "all"
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
cryptknox -o retrieve -m MySecretPassword -s gmail
|
|
45
|
+
cryptknox -o retrieve -m MySecretPassword -s all
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
delete
|
|
49
|
+
Delete stored credentials from the vault.
|
|
50
|
+
|
|
51
|
+
Required Options:
|
|
52
|
+
--master-password, -m Master password used to decrypt the vault
|
|
53
|
+
--service, -s Service name or "all"
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
cryptknox -o delete -m MySecretPassword -s gmail
|
|
57
|
+
cryptknox -o delete -m MySecretPassword -s all
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
generate
|
|
61
|
+
Generate a secure random password.
|
|
62
|
+
|
|
63
|
+
Required Options:
|
|
64
|
+
--length, -l Length of password (must be greater than 4)
|
|
65
|
+
|
|
66
|
+
Example:
|
|
67
|
+
cryptknox -o generate -l 12
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
OPTIONS
|
|
71
|
+
|
|
72
|
+
--operation, -o Operation to perform
|
|
73
|
+
--master-password, -m Master password for vault encryption
|
|
74
|
+
--service, -s Service name (example: gmail) or "all"
|
|
75
|
+
--username, -u Username associated with the service
|
|
76
|
+
--password, -p Password to store
|
|
77
|
+
--length, -l Length of generated password
|
|
78
|
+
--help, -h Show this help message
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
EXAMPLES
|
|
82
|
+
|
|
83
|
+
Store credentials
|
|
84
|
+
cryptknox -o store -m MySecretPassword -s gmail -u user@gmail.com -p mypassword
|
|
85
|
+
|
|
86
|
+
Retrieve all credentials
|
|
87
|
+
cryptknox -o retrieve -m MySecretPassword -s all
|
|
88
|
+
|
|
89
|
+
Generate a password
|
|
90
|
+
cryptknox -o generate -l 16
|
|
91
|
+
|
|
92
|
+
Delete credentials
|
|
93
|
+
cryptknox -o delete -m MySecretPassword -s gmail
|
|
94
|
+
"""
|
|
95
|
+
print(HELP_TEXT)
|
|
96
|
+
|
|
97
|
+
def __init__(self,args):
|
|
98
|
+
self.args = Arguments(args)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def _get_required(self, long_opt, short_opt, name):
|
|
102
|
+
"""Helper function to fetch CLI option values"""
|
|
103
|
+
|
|
104
|
+
args = self.args
|
|
105
|
+
|
|
106
|
+
if not(args.hasOption(long_opt) or args.hasOption(short_opt)):
|
|
107
|
+
raise ValueError(f"Enter {name}")
|
|
108
|
+
|
|
109
|
+
val = args.getOptionValue(long_opt) or args.getOptionValue(short_opt)
|
|
110
|
+
|
|
111
|
+
if not val:
|
|
112
|
+
raise ValueError(f"Enter {name} value")
|
|
113
|
+
return val
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def run(self):
|
|
117
|
+
args = self.args
|
|
118
|
+
|
|
119
|
+
# if has option other than allowed options raise Exception
|
|
120
|
+
|
|
121
|
+
for i in args.options:
|
|
122
|
+
if i not in self.allowed_options:
|
|
123
|
+
self.help()
|
|
124
|
+
raise ValueError("Unknown arguments...")
|
|
125
|
+
|
|
126
|
+
# Get operation value
|
|
127
|
+
operation = self._get_required('--operation','-o','Operation')
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
storage = Storage()
|
|
131
|
+
items = Items()
|
|
132
|
+
|
|
133
|
+
if operation == 'store':
|
|
134
|
+
master_passwd = self._get_required('--master-password','-m','Master Operation')
|
|
135
|
+
service = self._get_required('--service','-s','Service')
|
|
136
|
+
uname = self._get_required('--username','-u',"Username")
|
|
137
|
+
passwd = self._get_required('--password','-p','Password')
|
|
138
|
+
|
|
139
|
+
# check if vault exists, if not load one else decrypt the vault.
|
|
140
|
+
|
|
141
|
+
file_data = storage.load_file()
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
aes = Aes(master_passwd)
|
|
145
|
+
if file_data:
|
|
146
|
+
try:
|
|
147
|
+
decrypt_vault = aes.decrypt(file_data)
|
|
148
|
+
dec_con = json.loads(decrypt_vault)
|
|
149
|
+
except ValueError as e:
|
|
150
|
+
print("Cannot able to decrypt the vault: Check master key", e)
|
|
151
|
+
else:
|
|
152
|
+
dec_con = items.create_new_entry()
|
|
153
|
+
|
|
154
|
+
# call add_new_entry in the vault
|
|
155
|
+
data = items.add_or_update_entry(dec_con,service,uname,passwd)
|
|
156
|
+
|
|
157
|
+
encrypted = aes.encrypt(json.dumps(data).encode())
|
|
158
|
+
|
|
159
|
+
storage.save_file(encrypted)
|
|
160
|
+
|
|
161
|
+
elif operation == 'retrieve':
|
|
162
|
+
master_passwd = self._get_required('--master-password','-m','Master Operation')
|
|
163
|
+
service = self._get_required('--service','-s','Service')
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
# get the file
|
|
167
|
+
data = storage.load_file()
|
|
168
|
+
|
|
169
|
+
# decrypt it
|
|
170
|
+
aes = Aes(master_passwd)
|
|
171
|
+
dec_con = aes.decrypt(data).decode()
|
|
172
|
+
# dump json
|
|
173
|
+
json_data = json.loads(dec_con)
|
|
174
|
+
# read it from the json using service Name
|
|
175
|
+
|
|
176
|
+
# will call the function which will display the service in a formatted way
|
|
177
|
+
|
|
178
|
+
items.show_entries(json_data, service)
|
|
179
|
+
|
|
180
|
+
elif operation == 'generate':
|
|
181
|
+
length = self._get_required('--length','-l','Length')
|
|
182
|
+
self.generate_pass(length)
|
|
183
|
+
|
|
184
|
+
elif operation == 'delete':
|
|
185
|
+
master_passwd = self._get_required('--master-password','-m','Master Operation')
|
|
186
|
+
service = self._get_required('--service','-s','Service')
|
|
187
|
+
uname = self._get_required('--username','-u','Username')
|
|
188
|
+
# get the file
|
|
189
|
+
data = storage.load_file()
|
|
190
|
+
|
|
191
|
+
# decrypt it
|
|
192
|
+
aes = Aes(master_passwd)
|
|
193
|
+
dec_con = aes.decrypt(data).decode()
|
|
194
|
+
print(dec_con)
|
|
195
|
+
# dump json
|
|
196
|
+
json_data = json.loads(dec_con)
|
|
197
|
+
items.delete_entry(json_data,service,uname)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def generate_pass(self, length):
|
|
202
|
+
# Since it is coming as a string from the command line
|
|
203
|
+
length = int(length)
|
|
204
|
+
if length < 4:
|
|
205
|
+
raise ValueError("Length must be greater than 4")
|
|
206
|
+
|
|
207
|
+
# This will ensure the password will contain 1 lowercase, 1 uppercase , 1 digit, 1 special character
|
|
208
|
+
password = [
|
|
209
|
+
secrets.choice(string.ascii_lowercase),
|
|
210
|
+
secrets.choice(string.ascii_uppercase),
|
|
211
|
+
secrets.choice(string.digits),
|
|
212
|
+
secrets.choice(string.punctuation)
|
|
213
|
+
]
|
|
214
|
+
|
|
215
|
+
all_char = string.ascii_lowercase + string.ascii_uppercase + string.digits + string.punctuation
|
|
216
|
+
|
|
217
|
+
for i in range(length - 4):
|
|
218
|
+
password.append(secrets.choice(all_char))
|
|
219
|
+
|
|
220
|
+
random.shuffle(password)
|
|
221
|
+
# Convert array to string
|
|
222
|
+
password = "".join(password)
|
|
223
|
+
|
|
224
|
+
print(password)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def main():
|
|
228
|
+
try:
|
|
229
|
+
if len(sys.argv)==1 or "--help" in sys.argv or "-h" in sys.argv:
|
|
230
|
+
Cli.help()
|
|
231
|
+
sys.exit(1)
|
|
232
|
+
if len(sys.argv) <= 2:
|
|
233
|
+
print("Oops:< Missing required arguments\n")
|
|
234
|
+
Cli.help()
|
|
235
|
+
sys.exit(1)
|
|
236
|
+
|
|
237
|
+
Cli(sys.argv).run()
|
|
238
|
+
|
|
239
|
+
except Exception as e:
|
|
240
|
+
print(f"Error: {e}")
|
|
241
|
+
sys.exit(1)
|
|
242
|
+
|
|
243
|
+
if __name__ == "__main__":
|
|
244
|
+
main()
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import json
|
|
2
|
+
class Items:
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def create_new_entry(self):
|
|
6
|
+
return {
|
|
7
|
+
"entries":{}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def add_or_update_entry(self, vault, service, uname, passwd):
|
|
12
|
+
vault["entries"].setdefault(service,{})
|
|
13
|
+
|
|
14
|
+
acc = vault["entries"][service]
|
|
15
|
+
|
|
16
|
+
# if acc already exist in vault, ask the user, to update/cancel else insert
|
|
17
|
+
|
|
18
|
+
if uname in acc:
|
|
19
|
+
choice = input(
|
|
20
|
+
f"Account {uname} already exists for {service}. Update password? (y/n)"
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
if choice.lower == 'y':
|
|
24
|
+
acc[uname] = passwd
|
|
25
|
+
print(f"Password Updated for {uname}")
|
|
26
|
+
else:
|
|
27
|
+
print("Operation Cancelled...")
|
|
28
|
+
else:
|
|
29
|
+
acc[uname] = passwd
|
|
30
|
+
print("New account added")
|
|
31
|
+
|
|
32
|
+
return vault
|
|
33
|
+
|
|
34
|
+
def delete_entry(self, vault, service, uname):
|
|
35
|
+
|
|
36
|
+
entries = vault.get("entries", {})
|
|
37
|
+
|
|
38
|
+
if service.lower() == "all":
|
|
39
|
+
|
|
40
|
+
confirm = input("Delete ALL entries? (y/n): ")
|
|
41
|
+
|
|
42
|
+
if confirm.lower() == "y":
|
|
43
|
+
entries.clear()
|
|
44
|
+
print("All entries deleted.")
|
|
45
|
+
else:
|
|
46
|
+
print("Operation cancelled.")
|
|
47
|
+
|
|
48
|
+
return vault
|
|
49
|
+
|
|
50
|
+
# delete specific account
|
|
51
|
+
if service not in entries:
|
|
52
|
+
print("Service not found")
|
|
53
|
+
return vault
|
|
54
|
+
|
|
55
|
+
if uname not in entries[service]:
|
|
56
|
+
print("Username not found")
|
|
57
|
+
return vault
|
|
58
|
+
|
|
59
|
+
del entries[service][uname]
|
|
60
|
+
|
|
61
|
+
print(f"{uname} deleted successfully")
|
|
62
|
+
|
|
63
|
+
# remove service if empty
|
|
64
|
+
if not entries[service]:
|
|
65
|
+
del entries[service]
|
|
66
|
+
|
|
67
|
+
return vault
|
|
68
|
+
|
|
69
|
+
def show_entries(self, vault, service="all"):
|
|
70
|
+
entries = vault.get("entries",{})
|
|
71
|
+
|
|
72
|
+
if service.lower == 'all':
|
|
73
|
+
if not entries:
|
|
74
|
+
print("Vault is empty...")
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
for sname, account in entries.items():
|
|
78
|
+
print(f"\nService {sname}")
|
|
79
|
+
for uname, passwd in account.items():
|
|
80
|
+
print(f"\nUsername : {uname} | Password : {passwd}")
|
|
81
|
+
|
|
82
|
+
else:
|
|
83
|
+
accounts = entries.get(service)
|
|
84
|
+
|
|
85
|
+
if not accounts:
|
|
86
|
+
print("Service not found.")
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
print(f"Service:{service}")
|
|
90
|
+
for uname, passwd in account.items():
|
|
91
|
+
print(f"\nUsername : {uname} | Password : {passwd}")
|
|
92
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from platformdirs import user_data_dir
|
|
3
|
+
|
|
4
|
+
class Storage:
|
|
5
|
+
def __init__(self):
|
|
6
|
+
self.APP_NAME = "cryptknox"
|
|
7
|
+
self.FILE_PATH = os.path.join(user_data_dir(self.APP_NAME),".vault.enc")
|
|
8
|
+
|
|
9
|
+
def save_file(self, data):
|
|
10
|
+
os.makedirs(os.path.dirname(self.FILE_PATH),exist_ok=True)
|
|
11
|
+
with open(self.FILE_PATH,'wb') as f:
|
|
12
|
+
f.write(data)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def load_file(self):
|
|
16
|
+
|
|
17
|
+
if not os.path.exists(self.FILE_PATH):
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
with open(self.FILE_PATH,'rb') as f:
|
|
21
|
+
return f.read()
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
2
|
+
from cryptography.hazmat.primitives.kdf.scrypt import Scrypt
|
|
3
|
+
import os
|
|
4
|
+
|
|
5
|
+
class Aes:
|
|
6
|
+
MAGIC = b'Cryptknox'
|
|
7
|
+
def __init__(self, key):
|
|
8
|
+
self.key = key
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def con_key(self,salt,bit_len):
|
|
12
|
+
kdf = Scrypt(
|
|
13
|
+
salt=salt,
|
|
14
|
+
length=bit_len//8, # Generate key based on the bit size(128,192,256)
|
|
15
|
+
n=2**14,
|
|
16
|
+
r=8,
|
|
17
|
+
p=1
|
|
18
|
+
)
|
|
19
|
+
return kdf.derive(self.key.encode())
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def encrypt(self,content,bit_length=256):
|
|
23
|
+
salt = os.urandom(16)
|
|
24
|
+
|
|
25
|
+
nonce = os.urandom(12)
|
|
26
|
+
|
|
27
|
+
key = self.con_key(salt,bit_length)
|
|
28
|
+
aesgcm = AESGCM(key)
|
|
29
|
+
enc = aesgcm.encrypt(nonce,content,self.MAGIC)
|
|
30
|
+
|
|
31
|
+
return self.MAGIC + salt + nonce + enc
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def decrypt(self,enc_content, bit_length= 256):
|
|
35
|
+
# Checking Magic Header
|
|
36
|
+
if enc_content[:len(self.MAGIC)] != self.MAGIC or len(enc_content) < 37:
|
|
37
|
+
raise ValueError("Invalid encrypted content...")
|
|
38
|
+
# Spliting the enc_content
|
|
39
|
+
salt = enc_content[9:25]
|
|
40
|
+
nonce = enc_content[25:37]
|
|
41
|
+
_enc = enc_content[37:]
|
|
42
|
+
|
|
43
|
+
key = self.con_key(salt,bit_length)
|
|
44
|
+
|
|
45
|
+
aesgcm = AESGCM(key)
|
|
46
|
+
|
|
47
|
+
decrypt = aesgcm.decrypt(nonce, _enc, self.MAGIC)
|
|
48
|
+
return decrypt
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
File without changes
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
class Arguments:
|
|
2
|
+
def __init__(self,args):
|
|
3
|
+
self.command = args[0] if args else None
|
|
4
|
+
self.options = []
|
|
5
|
+
self.optionValues = {}
|
|
6
|
+
self.args = args[1:]
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
i=0
|
|
10
|
+
while i < len(self.args):
|
|
11
|
+
arg = self.args[i]
|
|
12
|
+
if arg.startswith('-'):
|
|
13
|
+
self.options.append(arg)
|
|
14
|
+
if i+1 < len(self.args) and not self.args[i+1].startswith('-'):
|
|
15
|
+
self.optionValues[arg] = self.args[i+1]
|
|
16
|
+
i+=1
|
|
17
|
+
i+=1
|
|
18
|
+
|
|
19
|
+
# print(self.command)
|
|
20
|
+
# print(self.options)
|
|
21
|
+
# print(self.optionValues)
|
|
22
|
+
# print(self.args)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def hasOption(self,option):
|
|
27
|
+
return option in self.options
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def getOptionValue(self,option):
|
|
31
|
+
return self.optionValues.get(option)
|
|
File without changes
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cryptknox
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A light weight secure CLI tool for storing, retrieving, deleting, and generating encrypted passwords.
|
|
5
|
+
Author-email: Sreejith <sreejitharee123@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Repository, https://github.com/Sreejith-R-Mohan/cryptknox
|
|
8
|
+
Requires-Python: >=3.8
|
|
9
|
+
Description-Content-Type: text/markdown
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Requires-Dist: cryptography==46.0.5
|
|
12
|
+
Requires-Dist: platformdirs==4.9.4
|
|
13
|
+
Dynamic: license-file
|
|
14
|
+
|
|
15
|
+
# CryptKnox 🔐
|
|
16
|
+
|
|
17
|
+
**CryptKnox** is a lightweight **CLI-based encrypted password manager** written in Python.
|
|
18
|
+
It allows you to securely **store, retrieve, delete, and generate passwords** using a single master password.
|
|
19
|
+
|
|
20
|
+
All stored credentials are **encrypted locally using AES encryption**, ensuring your secrets remain protected.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Features
|
|
25
|
+
|
|
26
|
+
* 🔐 **AES Encryption** for secure password storage
|
|
27
|
+
* 🗂 **Store multiple accounts per service**
|
|
28
|
+
* 🔍 **Retrieve specific services or all stored credentials**
|
|
29
|
+
* ❌ **Delete credentials securely**
|
|
30
|
+
* 🔑 **Generate strong random passwords**
|
|
31
|
+
* 💻 **Simple CLI interface**
|
|
32
|
+
* 📦 **Installable via PyPI**
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
Install from PyPI:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install cryptknox
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
After installation you can run:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
cryptknox --help
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Usage
|
|
53
|
+
|
|
54
|
+
```
|
|
55
|
+
cryptknox --operation|-o [store|retrieve|delete|generate] [OPTIONS]
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Operations
|
|
61
|
+
|
|
62
|
+
### Store Credentials
|
|
63
|
+
|
|
64
|
+
Store a password for a service.
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
cryptknox -o store -m MySecretPassword -s gmail -u user@gmail.com -p mypassword
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Options:
|
|
71
|
+
|
|
72
|
+
| Option | Description |
|
|
73
|
+
| ------------------------- | ----------------------------------- |
|
|
74
|
+
| `--operation`, `-o` | Operation (`store`) |
|
|
75
|
+
| `--master-password`, `-m` | Master password used for encryption |
|
|
76
|
+
| `--service`, `-s` | Service name (example: gmail) |
|
|
77
|
+
| `--username`, `-u` | Username for the service |
|
|
78
|
+
| `--password`, `-p` | Password to store |
|
|
79
|
+
|
|
80
|
+
---
|
|
81
|
+
|
|
82
|
+
### Retrieve Credentials
|
|
83
|
+
|
|
84
|
+
Retrieve stored passwords.
|
|
85
|
+
|
|
86
|
+
Retrieve a specific service:
|
|
87
|
+
|
|
88
|
+
```
|
|
89
|
+
cryptknox -o retrieve -m MySecretPassword -s gmail
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Retrieve all services:
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
cryptknox -o retrieve -m MySecretPassword -s all
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
### Delete Credentials
|
|
101
|
+
|
|
102
|
+
Delete stored credentials.
|
|
103
|
+
|
|
104
|
+
Delete a specific service entry:
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
cryptknox -o delete -m MySecretPassword -s gmail
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Delete all entries:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
cryptknox -o delete -m MySecretPassword -s all
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
### Generate Secure Password
|
|
119
|
+
|
|
120
|
+
Generate a random secure password.
|
|
121
|
+
|
|
122
|
+
```
|
|
123
|
+
cryptknox -o generate -l 12
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Options:
|
|
127
|
+
|
|
128
|
+
| Option | Description |
|
|
129
|
+
| ---------------- | ----------------------------------------------------- |
|
|
130
|
+
| `--length`, `-l` | Length of generated password (must be greater than 4) |
|
|
131
|
+
|
|
132
|
+
The generated password includes:
|
|
133
|
+
|
|
134
|
+
* uppercase letters
|
|
135
|
+
* lowercase letters
|
|
136
|
+
* numbers
|
|
137
|
+
* special characters
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Examples
|
|
142
|
+
|
|
143
|
+
Store credentials:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
cryptknox -o store -m MySecretPassword -s github -u myuser -p mypassword
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Retrieve all credentials:
|
|
150
|
+
|
|
151
|
+
```
|
|
152
|
+
cryptknox -o retrieve -m MySecretPassword -s all
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Generate a strong password:
|
|
156
|
+
|
|
157
|
+
```
|
|
158
|
+
cryptknox -o generate -l 16
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Delete credentials:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
cryptknox -o delete -m MySecretPassword -s github
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Security Notes
|
|
170
|
+
|
|
171
|
+
* All credentials are **encrypted before being stored**.
|
|
172
|
+
* Only the **master password can decrypt the vault**.
|
|
173
|
+
* The master password is **never stored**.
|
|
174
|
+
|
|
175
|
+
⚠️ If the master password is lost, the vault **cannot be recovered**.
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## Requirements
|
|
180
|
+
|
|
181
|
+
* Python **3.9+**
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## License
|
|
186
|
+
|
|
187
|
+
MIT License
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Author
|
|
192
|
+
|
|
193
|
+
Developed by **Sreejith**
|
|
194
|
+
|
|
195
|
+
GitHub: https://github.com/Sreejith-R-Mohan/cryptknox
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
LICENSE
|
|
2
|
+
README.md
|
|
3
|
+
pyproject.toml
|
|
4
|
+
requirements.txt
|
|
5
|
+
cryptknox/Cli.py
|
|
6
|
+
cryptknox/Items.py
|
|
7
|
+
cryptknox/Storage.py
|
|
8
|
+
cryptknox.egg-info/PKG-INFO
|
|
9
|
+
cryptknox.egg-info/SOURCES.txt
|
|
10
|
+
cryptknox.egg-info/dependency_links.txt
|
|
11
|
+
cryptknox.egg-info/entry_points.txt
|
|
12
|
+
cryptknox.egg-info/requires.txt
|
|
13
|
+
cryptknox.egg-info/top_level.txt
|
|
14
|
+
cryptknox/algorithm/Aes.py
|
|
15
|
+
cryptknox/algorithm/Generatepass.py
|
|
16
|
+
cryptknox/algorithm/__init__.py
|
|
17
|
+
cryptknox/lib/Arguments.py
|
|
18
|
+
cryptknox/lib/__init__.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cryptknox
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "cryptknox"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "A light weight secure CLI tool for storing, retrieving, deleting, and generating encrypted passwords."
|
|
9
|
+
authors = [
|
|
10
|
+
{name = "Sreejith", email = "sreejitharee123@gmail.com"}
|
|
11
|
+
]
|
|
12
|
+
readme = "README.md"
|
|
13
|
+
requires-python = ">=3.8"
|
|
14
|
+
license = {text = "MIT"}
|
|
15
|
+
# 1. Tell setuptools that dependencies will be defined dynamically
|
|
16
|
+
dynamic = ["dependencies"]
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# 2. Point to the requirements file
|
|
20
|
+
[tool.setuptools.dynamic]
|
|
21
|
+
dependencies = {file = ["requirements.txt"]}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
[project.scripts]
|
|
25
|
+
fcryptx = "cryptknox.Cli:main"
|
|
26
|
+
|
|
27
|
+
[project.urls]
|
|
28
|
+
Repository = "https://github.com/Sreejith-R-Mohan/cryptknox"
|