gbkomi 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.
gbkomi-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: gbkomi
3
+ Version: 0.1.0
4
+ Summary: Secure encrypted token storage for Python
5
+ Author-email: Your Name <qqqwwweeeshah@gmail.com>
6
+ License: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: cryptography>=42.0.0
10
+ Requires-Dist: argon2-cffi>=23.1.0
11
+
12
+ # gbkomi
13
+
14
+ Secure encrypted token storage for Python using Argon2id + AES-256-GCM.
15
+
16
+ ## Features
17
+
18
+ - Store secrets in encrypted `.gbkomi` files
19
+ - Password-based encryption
20
+ - CLI for managing secrets
21
+ - Authenticated encryption with AES-GCM
22
+ - Strong key derivation with Argon2id
23
+
24
+ ## Install
25
+ ```bash
26
+ pip install gbkomi
gbkomi-0.1.0/README.md ADDED
@@ -0,0 +1,15 @@
1
+ # gbkomi
2
+
3
+ Secure encrypted token storage for Python using Argon2id + AES-256-GCM.
4
+
5
+ ## Features
6
+
7
+ - Store secrets in encrypted `.gbkomi` files
8
+ - Password-based encryption
9
+ - CLI for managing secrets
10
+ - Authenticated encryption with AES-GCM
11
+ - Strong key derivation with Argon2id
12
+
13
+ ## Install
14
+ ```bash
15
+ pip install gbkomi
@@ -0,0 +1,27 @@
1
+ [build-system]
2
+ requires = ["setuptools>=68", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "gbkomi"
7
+ version = "0.1.0"
8
+ description = "Secure encrypted token storage for Python"
9
+ readme = "README.md"
10
+ requires-python = ">=3.9"
11
+ license = { text = "MIT" }
12
+ authors = [
13
+ { name = "Your Name", email = "qqqwwweeeshah@gmail.com" }
14
+ ]
15
+ dependencies = [
16
+ "cryptography>=42.0.0",
17
+ "argon2-cffi>=23.1.0"
18
+ ]
19
+
20
+ [project.scripts]
21
+ gbkomi = "gbkomi.cli:main"
22
+
23
+ [tool.setuptools]
24
+ package-dir = {"" = "src"}
25
+
26
+ [tool.setuptools.packages.find]
27
+ where = ["src"]
gbkomi-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,2 @@
1
+ __all__ = ["create_vault", "load_vault"]
2
+ __version__ = "0.1.0"
@@ -0,0 +1,65 @@
1
+ import argparse
2
+ import getpass
3
+ import sys
4
+
5
+ from .vault import create_vault, set_secret, get_secret, delete_secret, list_secrets
6
+
7
+
8
+ def prompt_password(confirm: bool = False) -> str:
9
+ pwd = getpass.getpass("Password: ")
10
+ if confirm:
11
+ pwd2 = getpass.getpass("Confirm password: ")
12
+ if pwd != pwd2:
13
+ print("Passwords do not match.", file=sys.stderr)
14
+ sys.exit(1)
15
+ return pwd
16
+
17
+
18
+ def main():
19
+ parser = argparse.ArgumentParser(prog="gbkomi")
20
+ sub = parser.add_subparsers(dest="command", required=True)
21
+
22
+ p_init = sub.add_parser("init")
23
+ p_init.add_argument("path")
24
+
25
+ p_set = sub.add_parser("set")
26
+ p_set.add_argument("path")
27
+ p_set.add_argument("key")
28
+ p_set.add_argument("value")
29
+
30
+ p_get = sub.add_parser("get")
31
+ p_get.add_argument("path")
32
+ p_get.add_argument("key")
33
+
34
+ p_list = sub.add_parser("list")
35
+ p_list.add_argument("path")
36
+
37
+ p_del = sub.add_parser("delete")
38
+ p_del.add_argument("path")
39
+ p_del.add_argument("key")
40
+
41
+ args = parser.parse_args()
42
+
43
+ if args.command == "init":
44
+ pwd = prompt_password(confirm=True)
45
+ create_vault(args.path, pwd)
46
+ print(f"Vault created: {args.path}")
47
+
48
+ elif args.command == "set":
49
+ pwd = prompt_password()
50
+ set_secret(args.path, pwd, args.key, args.value)
51
+ print(f"Saved: {args.key}")
52
+
53
+ elif args.command == "get":
54
+ pwd = prompt_password()
55
+ print(get_secret(args.path, pwd, args.key))
56
+
57
+ elif args.command == "list":
58
+ pwd = prompt_password()
59
+ for key in list_secrets(args.path, pwd):
60
+ print(key)
61
+
62
+ elif args.command == "delete":
63
+ pwd = prompt_password()
64
+ delete_secret(args.path, pwd, args.key)
65
+ print(f"Deleted: {args.key}")
@@ -0,0 +1,33 @@
1
+ import os
2
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
3
+ from cryptography.hazmat.primitives.kdf.argon2 import Argon2id
4
+
5
+
6
+ def derive_key(password: str, salt: bytes) -> bytes:
7
+ kdf = Argon2id(
8
+ salt=salt,
9
+ length=32,
10
+ iterations=3,
11
+ lanes=4,
12
+ memory_cost=65536,
13
+ )
14
+ return kdf.derive(password.encode("utf-8"))
15
+
16
+
17
+ def encrypt_bytes(data: bytes, password: str) -> dict:
18
+ salt = os.urandom(16)
19
+ nonce = os.urandom(12)
20
+ key = derive_key(password, salt)
21
+ aesgcm = AESGCM(key)
22
+ ciphertext = aesgcm.encrypt(nonce, data, None)
23
+ return {
24
+ "salt": salt,
25
+ "nonce": nonce,
26
+ "ciphertext": ciphertext,
27
+ }
28
+
29
+
30
+ def decrypt_bytes(salt: bytes, nonce: bytes, ciphertext: bytes, password: str) -> bytes:
31
+ key = derive_key(password, salt)
32
+ aesgcm = AESGCM(key)
33
+ return aesgcm.decrypt(nonce, ciphertext, None)
@@ -0,0 +1,10 @@
1
+ class GBKomiError(Exception):
2
+ """Base exception for gbkomi."""
3
+
4
+
5
+ class VaultDecryptionError(GBKomiError):
6
+ """Raised when vault decryption fails."""
7
+
8
+
9
+ class VaultFormatError(GBKomiError):
10
+ """Raised when vault format is invalid."""
@@ -0,0 +1,79 @@
1
+ import json
2
+ import base64
3
+ from pathlib import Path
4
+
5
+ from .crypto import encrypt_bytes, decrypt_bytes
6
+ from .exceptions import VaultDecryptionError, VaultFormatError
7
+
8
+
9
+ MAGIC = "GBK1"
10
+
11
+
12
+ def _b64e(data: bytes) -> str:
13
+ return base64.b64encode(data).decode("utf-8")
14
+
15
+
16
+ def _b64d(data: str) -> bytes:
17
+ return base64.b64decode(data.encode("utf-8"))
18
+
19
+
20
+ def create_vault(path: str, password: str) -> None:
21
+ payload = {}
22
+ save_vault(path, payload, password)
23
+
24
+
25
+ def load_vault(path: str, password: str) -> dict:
26
+ p = Path(path)
27
+ raw = json.loads(p.read_text(encoding="utf-8"))
28
+
29
+ if raw.get("magic") != MAGIC:
30
+ raise VaultFormatError("Invalid vault file format.")
31
+
32
+ try:
33
+ plaintext = decrypt_bytes(
34
+ _b64d(raw["salt"]),
35
+ _b64d(raw["nonce"]),
36
+ _b64d(raw["ciphertext"]),
37
+ password,
38
+ )
39
+ except Exception as e:
40
+ raise VaultDecryptionError("Unable to decrypt vault. Wrong password or corrupted file.") from e
41
+
42
+ return json.loads(plaintext.decode("utf-8"))
43
+
44
+
45
+ def save_vault(path: str, data: dict, password: str) -> None:
46
+ plaintext = json.dumps(data, ensure_ascii=False, separators=(",", ":")).encode("utf-8")
47
+ encrypted = encrypt_bytes(plaintext, password)
48
+
49
+ payload = {
50
+ "magic": MAGIC,
51
+ "version": 1,
52
+ "salt": _b64e(encrypted["salt"]),
53
+ "nonce": _b64e(encrypted["nonce"]),
54
+ "ciphertext": _b64e(encrypted["ciphertext"]),
55
+ }
56
+
57
+ Path(path).write_text(json.dumps(payload, indent=2), encoding="utf-8")
58
+
59
+
60
+ def set_secret(path: str, password: str, key: str, value: str) -> None:
61
+ data = load_vault(path, password)
62
+ data[key] = value
63
+ save_vault(path, data, password)
64
+
65
+
66
+ def get_secret(path: str, password: str, key: str) -> str:
67
+ data = load_vault(path, password)
68
+ return data[key]
69
+
70
+
71
+ def delete_secret(path: str, password: str, key: str) -> None:
72
+ data = load_vault(path, password)
73
+ data.pop(key, None)
74
+ save_vault(path, data, password)
75
+
76
+
77
+ def list_secrets(path: str, password: str) -> list[str]:
78
+ data = load_vault(path, password)
79
+ return sorted(data.keys())
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: gbkomi
3
+ Version: 0.1.0
4
+ Summary: Secure encrypted token storage for Python
5
+ Author-email: Your Name <qqqwwweeeshah@gmail.com>
6
+ License: MIT
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: cryptography>=42.0.0
10
+ Requires-Dist: argon2-cffi>=23.1.0
11
+
12
+ # gbkomi
13
+
14
+ Secure encrypted token storage for Python using Argon2id + AES-256-GCM.
15
+
16
+ ## Features
17
+
18
+ - Store secrets in encrypted `.gbkomi` files
19
+ - Password-based encryption
20
+ - CLI for managing secrets
21
+ - Authenticated encryption with AES-GCM
22
+ - Strong key derivation with Argon2id
23
+
24
+ ## Install
25
+ ```bash
26
+ pip install gbkomi
@@ -0,0 +1,13 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/gbkomi/__init__.py
4
+ src/gbkomi/cli.py
5
+ src/gbkomi/crypto.py
6
+ src/gbkomi/exceptions.py
7
+ src/gbkomi/vault.py
8
+ src/gbkomi.egg-info/PKG-INFO
9
+ src/gbkomi.egg-info/SOURCES.txt
10
+ src/gbkomi.egg-info/dependency_links.txt
11
+ src/gbkomi.egg-info/entry_points.txt
12
+ src/gbkomi.egg-info/requires.txt
13
+ src/gbkomi.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ gbkomi = gbkomi.cli:main
@@ -0,0 +1,2 @@
1
+ cryptography>=42.0.0
2
+ argon2-cffi>=23.1.0
@@ -0,0 +1 @@
1
+ gbkomi