MiniVault (version 0.0.1)
index
c:\users\csu1\documents\perso\dev\python\minivault\minivault.py

A simple, lightweight vault implemented in pure Python, using RC6 for
securely storing and retrieving secrets in light-duty applications.
 
Features
--------
- One file per category; each file stores:
  - A per-category *data key* (random) encrypted under a master key derived from the
    provided master password (via `scrypt`).
  - A list of credentials where **only the password** field is encrypted with RC6-CBC
    using the category *data key*, and then Base64-encoded. Other fields remain plaintext.
- Start the vault with `PasswordVault.start(master_password, root_dir)`; this derives
  the master key, decrypts all category data keys into memory.
- Request a password with `vault.get_credentials(category, role)`; the function reads
  the category file, locates the credential by role, decrypts the password, and
  returns the full credential.
- Add or update credentials with `vault.put_credentials(...)`.
- Secure coding practices: scrypt KDF, IV per encryption, HMAC-SHA512 over critical
  sections, atomic file writes, strict file permissions, input validation, type hints,
  docstrings, and doctests for the RC6 library usage.
 
Requirements
------------
- Python 3.10+
- The `RC6Encryption` module with ECB/CBC block operations.
  The vault itself uses CBC mode for password encryption.
 
File Format (per category JSON)
-------------------------------
{
  "version": 1,
  "kdf": {"salt": "<b64>", "n": 2**14, "r": 8, "p": 1},
  "encrypted_data_key": {"iv": "<b64>", "ciphertext": "<b64>", "hmac": "<b64>"},
  "credentials": [
     {"role": "app-admin", "username": "alice", "password_b64": "<b64 RC6-CBC>", "notes": "..."},
     ...
  ]
}

 
Classes
       
builtins.dict(builtins.object)
CredentialRecord
builtins.object
CategoryState
PasswordVault

 
class CategoryState(builtins.object)
    CategoryState(data_key: bytes) -&gt; None
 
Holds in-memory decrypted data key for a category.
 
  Methods defined here:
__eq__(self, other)
Return self==value.
__init__(self, data_key: bytes) -> None
Initialize self.  See help(type(self)) for accurate signature.
__repr__(self)
Return repr(self).

Data descriptors defined here:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

Data and other attributes defined here:
__annotations__ = {'data_key': <class 'bytes'>}
__dataclass_fields__ = {'data_key': Field(name='data_key',type=<class 'bytes'>,defau...appingproxy({}),kw_only=False,_field_type=_FIELD)}
__dataclass_params__ = _DataclassParams(init=True,repr=True,eq=True,order=False,unsafe_hash=False,frozen=False)
__hash__ = None
__match_args__ = ('data_key',)

 
class CredentialRecord(builtins.dict)
    A single credential entry stored in a category file.
 
Fields
------
role: str
    Logical identifier of the credential (e.g., "db-admin", "webapp-user").
    Used as the lookup key when retrieving credentials.
username: str
    The username associated with this credential.
password_b64: str
    The encrypted password, stored as Base64 text. The raw value is
    RC6-CBC encrypted (with PKCS#7 padding) using the per-category
    data key. It must be decoded and decrypted at runtime before use.
notes: str, optional
    Human-readable notes or metadata about this credential.
    Not used in cryptographic operations, purely informational.
 
Notes
-----
- Only ``password_b64`` is encrypted at rest. All other fields are
  plaintext in the JSON file.
- This TypedDict is marked ``total=False``, which means all fields
  are optional from the type checker’s perspective. However, in
  practice ``role``, ``username`` and ``password_b64`` are always
  expected to be present in persisted files.
 
 
Method resolution order:
CredentialRecord
builtins.dict
builtins.object

Data descriptors defined here:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

Data and other attributes defined here:
__annotations__ = {'notes': <class 'str'>, 'password_b64': <class 'str'>, 'role': <class 'str'>, 'username': <class 'str'>}
__optional_keys__ = frozenset({'notes', 'password_b64', 'role', 'username'})
__orig_bases__ = (<function TypedDict>,)
__required_keys__ = frozenset()
__total__ = False

Methods inherited from builtins.dict:
__contains__(self, key, /)
True if the dictionary has the specified key, else False.
__delitem__(self, key, /)
Delete self[key].
__eq__(self, value, /)
Return self==value.
__ge__(self, value, /)
Return self>=value.
__getattribute__(self, name, /)
Return getattr(self, name).
__getitem__(...)
x.__getitem__(y) <==> x[y]
__gt__(self, value, /)
Return self>value.
__init__(self, /, *args, **kwargs)
Initialize self.  See help(type(self)) for accurate signature.
__ior__(self, value, /)
Return self|=value.
__iter__(self, /)
Implement iter(self).
__le__(self, value, /)
Return self<=value.
__len__(self, /)
Return len(self).
__lt__(self, value, /)
Return self<value.
__ne__(self, value, /)
Return self!=value.
__or__(self, value, /)
Return self|value.
__repr__(self, /)
Return repr(self).
__reversed__(self, /)
Return a reverse iterator over the dict keys.
__ror__(self, value, /)
Return value|self.
__setitem__(self, key, value, /)
Set self[key] to value.
__sizeof__(...)
D.__sizeof__() -> size of D in memory, in bytes
clear(...)
D.clear() -> None.  Remove all items from D.
copy(...)
D.copy() -> a shallow copy of D
get(self, key, default=None, /)
Return the value for key if key is in the dictionary, else default.
items(...)
D.items() -> a set-like object providing a view on D's items
keys(...)
D.keys() -> a set-like object providing a view on D's keys
pop(...)
D.pop(k[,d]) -> v, remove specified key and return the corresponding value.
 
If the key is not found, return the default if given; otherwise,
raise a KeyError.
popitem(self, /)
Remove and return a (key, value) pair as a 2-tuple.
 
Pairs are returned in LIFO (last-in, first-out) order.
Raises KeyError if the dict is empty.
setdefault(self, key, default=None, /)
Insert key with a value of default if key is not in the dictionary.
 
Return the value for key if key is in the dictionary, else default.
update(...)
D.update([E, ]**F) -> None.  Update D from dict/iterable E and F.
If E is present and has a .keys() method, then does:  for k in E: D[k] = E[k]
If E is present and lacks a .keys() method, then does:  for k, v in E: D[k] = v
In either case, this is followed by: for k in F:  D[k] = F[k]
values(...)
D.values() -> an object providing a view on D's values

Class methods inherited from builtins.dict:
__class_getitem__(...) from typing._TypedDictMeta
See PEP 585
fromkeys(iterable, value=None, /) from typing._TypedDictMeta
Create a new dictionary with keys from iterable and values set to value.

Static methods inherited from builtins.dict:
__new__(*args, **kwargs) from builtins.type
Create and return a new object.  See help(type) for accurate signature.

Data and other attributes inherited from builtins.dict:
__hash__ = None

 
class PasswordVault(builtins.object)
    PasswordVault(root_dir: pathlib.Path)
 
A small password vault that encrypts only the password fields using RC6.
 
Use :meth:`PasswordVault.start` to initialize from a root directory, passing a
master password. The constructor is not public because we want to constrain
initialization through the key-derivation step.
 
All persistent data lives in files under *root_dir*, one JSON file per category.
 
Notes on Security
-----------------
- Passwords are encrypted with RC6 in CBC mode with PKCS#7 padding.
- Per-category data keys are protected using a key derived from the master password
  by scrypt (N=2**14, r=8, p=1). The encrypted data key (encrypted_data_key) is integrity-protected
  with HMAC-SHA512.
 
  Methods defined here:
__init__(self, root_dir: pathlib.Path)
Initialize self.  See help(type(self)) for accurate signature.
create_new_category(self, category: str, master_password: str) -> None
This method creates a new category using master password.
get_credentials(self, category: str, role: str) -> MiniVault.CredentialRecord
Return the credential for (*category*, *role*), decrypting the password.
 
Raises ``FileNotFoundError`` if the category file does not exist, or ``KeyError``
if the role cannot be found.
put_credentials(self, category: str, role: str, username: str, password: str, notes: str | None = None) -> None
Create or update a credential entry in *category*.
 
Only the *password* is RC6-encrypted and Base64-encoded at rest.

Class methods defined here:
start(master_password: str, root_dir: str | os.PathLike[str]) -> ~PasswordVault from builtins.type
Start the vault by deriving the master key, decrypting category data keys,
and erasing the master password from memory.
 
Parameters
----------
master_password : str
    The user-provided master password.
root_dir : str | PathLike
    Directory containing category files.

Data descriptors defined here:
__dict__
dictionary for instance variables (if defined)
__weakref__
list of weak references to the object (if defined)

Data and other attributes defined here:
__annotations__ = {'_categories': typing.Dict[str, MiniVault.CategoryState], '_master_key': typing.Optional[bytes], '_root': <class 'pathlib.Path'>}

 
Functions
       
compare_digest(a, b, /)
Return 'a == b'.
 
This function uses an approach designed to prevent
timing analysis, making it appropriate for cryptography.
 
a and b must both be of the same type: either str (ASCII only),
or any bytes-like object.
 
Note: If a and b are of different lengths, or if an error occurs,
a timing attack could theoretically reveal information about the
types and lengths of a and b--but not their values.
os_open = open(path, flags, mode=511, *, dir_fd=None)
Open a file for low level IO.  Returns a file descriptor (integer).
 
If dir_fd is not None, it should be a file descriptor open to a directory,
  and path should be relative; path will then be relative to that directory.
dir_fd may not be implemented on your platform.
  If it is unavailable, using it will raise a NotImplementedError.
pkcs5_7padding(data: bytes) -> bytes
Apply PKCS#5/PKCS#7 padding to *data* to a multiple of 16 bytes.
 
Parameters
----------
data : bytes
    Input data.
 
Returns
-------
bytes
    Padded data.
remove_pkcs_padding(data: bytes) -> bytes
Remove PKCS#5/PKCS#7 padding from *data*.
 
Raises ``ValueError`` if padding is malformed.
replace(src, dst, *, src_dir_fd=None, dst_dir_fd=None)
Rename a file or directory, overwriting the destination.
 
If either src_dir_fd or dst_dir_fd is not None, it should be a file
  descriptor open to a directory, and the respective path string (src or dst)
  should be relative; the path will then be relative to that directory.
src_dir_fd and dst_dir_fd, may not be implemented on your platform.
  If they are unavailable, using them will raise a NotImplementedError.
scrypt(password, *, salt=None, n=None, r=None, p=None, maxmem=0, dklen=64)
scrypt password-based key derivation function.
sha512 = openssl_sha512(string=b'', *, usedforsecurity=True)
Returns a sha512 hash object; optionally initialized with a string
test()

 
Data
        Any = typing.Any
BLOCK_SIZE = 16
Dict = typing.Dict
List = typing.List
O_CREAT = 256
O_TRUNC = 512
O_WRONLY = 1
Optional = typing.Optional
Tuple = typing.Tuple
__author_email__ = 'mauricelambert434@gmail.com'
__copyright__ = '\nMiniVault Copyright (C) 2025 Maurice Lambert\n...ome to redistribute it\nunder certain conditions.\n'
__description__ = '\nA simple, lightweight vault implemented in pure...d retrieving secrets in light-duty applications.\n'
__license__ = 'GPL-3.0 License'
__maintainer__ = 'Maurice Lambert'
__maintainer_email__ = 'mauricelambert434@gmail.com'
__url__ = 'https://github.com/mauricelambert/MiniVault'
copyright = '\nMiniVault Copyright (C) 2025 Maurice Lambert\n...ome to redistribute it\nunder certain conditions.\n'
environ = environ({'ALLUSERSPROFILE': 'C:\\ProgramData', '...3.10_qbz5n2kfra8p0\\LocalCache\\local-packages'})
license = 'GPL-3.0 License'

 
Author
        Maurice Lambert