You are browsing as a guest. Sign up (or log in) to start making projects!

TokenShield

  • 5 Devlogs
  • 16 Total hours

I am building a terminal based password manager with integrated 2FA TOTP code authentication in python. It allows users to store passwords, generate random strong passwords and get the TOTP code for 2FA fast. Everything is encrypted with PBKDF2HMAC (SHA256).

Ship #1 Pending review

I created a terminal based password manager with integrated 2FA TOTP authentication written in Python. It can store passwords, generate random passwords, scan QR codes for 2FA setup and display current TOTP codes.
I started this project, because it annoyed me that everytime I had to Authenticate with TOTP, I needed to grab my phone.
With this, i just need to click a few times on my PC and i have my current TOTP Security Code.

The most challenging parts were implementing secure encryption, with the master password system, and adding TOTP authentification. I havent done anything before with encryption except in school a bit and i needed to look up, what methods are common what is good what not.

I am proud that i learned alot through this project. I could use a bit of my school knowledge which made me happy that moment and I like that it is done now.

(More details in the readme...) To test the project you need Python installed and have to install the required dependencies. After starting the program, they can create a master password and use the menu to add, search, and manage password entries.

  • 5 devlogs
  • 16h
Try project → See source code →
Open comments for this post

3h 30m 21s logged

Completed the 2FA implementation and added QR code support. You can now import the 2FA QR Code and you will see the current secret code.
The biggest challenge was reading QR codes right everytime. I didnt know that opencv only reads qr codes with a white border, so some qr code did work and some didnt. This is why i added:

white = [255, 255, 255]
img_with_border = cv2.copyMakeBorder(
img, 20, 20, 20, 20,
borderType=cv2.BORDER_CONSTANT,
value=white
)

Which adds a white border around the qr code.

0
0
3
Open comments for this post

4h 20m 49s logged

I started working in the implementation for 2FA totp code generator. This works because the totp qr code wont change for the account so you can implement it once in the password manager. I updated the tables to show a new 2FA column. The program can now generate a TOTP code if a secret is available. I also started adding QR code support so users can import their 2FA setup instead of entering the secret manually. Right now not every qr code is working and im trying to figure out why for like an hour…

current_code = generate_current_totp(details.get(“totp_secret”, “-”))

Also my muffin head wrote everything in german.. Needed to change it again…

0
0
1
Open comments for this post

3h 35m 36s logged

I added a dictionary where the .json file will be saved, so its not in the same folder as the .py.
For that i used the package PlatformDirs:

dirs = PlatformDirs(“TokenShield”, ensure_exists=True)

After texting i noticed, that there is no way in chaning the master password and no way in deleting a entry.
Entry Deletion (Option 4): Users can now completely remove an app entry from their vault. It also has a confirmation (y/n) to prevent accidental deletion

Master Password Change (Option 5): Added a way to update the master password. It decrypts the vault using the old password and generates a new one.

It was challenging to make the password change. I had to ensure the vault was fully decrypted first, then generate a brand new random (os.urandom(16)) for encryptiom and immediately re encrypt everything with the new key.

0
0
2
Open comments for this post

2h 33m 43s logged

I replaced all of the saving code that saved the pw in cleartext with real encryption to make it secure.

I used the cryptography library and Imported Fernet and hashing tools (PBKDF2HMAC) to do the encryption.

I also used Key Derivation and Added a function that takes the master password and hashes it 480.000 times and also with a random salt.

I uses AES-256 which takes a 256-bit and uses it to encrypt your vault data by transforming it into 128 bit blocks. Also gemini helped me with that.

kdf = PBKDF2HMAC(
algorithm=hashes.SHA256(),
length=32, # 32 bytes = 256 bits
salt=salt,
iterations=480000,
)
aes_key = base64.urlsafe_b64encode(kdf.derive(master_password.encode()))

0
0
4
Open comments for this post

2h 12m 28s logged

I started with a raw prototype. I built a simple terminal menu, a basic random password generator using Python’s secrets module, and a straightforward way to load and save unencrypted JSON data. It worked, but it was completely replaceable by any other online website…

Next, I integrated that you can add your own passwords, usernames and notes. I also The rich library to make the terminal look beautiful with custom tables. I also implemented the search feature so users could find specific accounts instead of just looking at everything, and I replaced the passwords in the main list (••••••••) and you need to search fot the specific programm. I think its better this way but others might disagree.

I added the getpass module so that when you type a password and spedifically the main password, the characters are completely hidden in the terminal.

Right now, the app is not fully encrypted yet, but I build a structure that will hopefulyl help me.

For the readability Instead of saving accounts directly at the top level of the JSON file, I moved them into a dedicated entries dictionary.

Master Password Integration: The app now requires a master password immediately upon boot. It checks this password against the JSON file before unlocking the menu.

Right now everything is just in the json file in clear text. i want to change that for the next devlog.
Right Now:

def load_vault(master_password: str) -> dict:
“”“Loads the vault and checks the password in plain text (Simulated).”””
if not os.path.exists(DB_FILE):
return {“cipher”: “VERSCHLÜSSELUNG_CIPHER”, “entries”: {}}

with open(DB_FILE, "r") as f:
    data = json.load(f)

# Checking the password in plain text for now
if data.get("passwort_schutz") != master_password:
    console.print("\n[bold red] Wrong password![/bold red]\n")
    return None

return {"cipher": data.get("cipher", "VERSCHLÜSSELUNG_CIPHER"), "entries": data.get("entries", {})}
0
0
3

Delete project?

Are you sure you want to permanently delete this project? This action cannot be undone.

All devlogs, followers, and associated data will be removed.

Followers

Loading…