Skip to content

Encryption Architecture

Kosh uses a modern, hybrid encryption model combining Argon2id, Curve25519 ECDH, and ChaCha20-Poly1305 AEAD. This design ensures that stored credentials are encrypted securely, with forward secrecy properties for every entry.

Kosh separates encryption into two distinct layers:

  1. Vault Encryption — protects the vault metadata using a master password.
  2. Credential Encryption — each stored entry uses an ephemeral asymmetric key pair to derive a unique per-entry symmetric key.

This provides:

  • Strong master password protection
  • Forward secrecy (one leaked credential key doesn’t compromise others)
  • No long-term symmetric key stored on disk
  • Modern cryptographic primitives only

This document explains how keys are derived, how secrets are encrypted, and how decryption happens during retrieval.


The master password is never used directly. Instead, it is fed into Argon2id, a memory-hard KDF:

func GenerateSymmetricKey(secret, salt []byte) []byte {
return argon2.IDKey(secret, salt, keyTime, keyMemory, keyThreads, keyLength)
}

Parameters:

ParameterValue
Time cost1
Memory cost64 MB
Threads4
Output length32 bytes

Argon2id defends against GPU brute force attacks. The vault stores:

  • The salt
  • An encrypted “vault secret”
  • A nonce

During unlock (kosh add, kosh get, etc.), Kosh regenerates this key and attempts to decrypt the vault secret to verify correctness.


2. Asymmetric Encryption for Credentials (Curve25519)

Section titled “2. Asymmetric Encryption for Credentials (Curve25519)”

Each credential uses a fresh ephemeral key pair:

privateKey, publicKey := crypto.GenerateAsymmetricKeyPair()

Internally:

func GenerateAsymmetricKeyPair() (privateKey, publicKey []byte) {
privateKey = make([]byte, 32)
rand.Read(privateKey)
publicKey, _ = curve25519.X25519(privateKey, curve25519.Basepoint)
return privateKey, publicKey
}

The vault stores a long-term Curve25519 public key. To encrypt a credential:

  1. Generate ephemeral private key a.
  2. Compute shared secret S = X25519(a, vaultPublicKey).
  3. Hash it to a 32-byte encryption key:
key := sha256.Sum256(encryptionKey)

This ensures constant-length key material and avoids weak shared secrets.

  • Every credential gets a unique key.
  • Compromise of one credential does not leak others.
  • Vault’s private key is stored encrypted by the master password.

3. Symmetric Encryption (ChaCha20-Poly1305)

Section titled “3. Symmetric Encryption (ChaCha20-Poly1305)”

After deriving the per-credential key:

cipher, nonce := crypto.EncryptSecret(key[:], []byte(secret))

Under the hood:

aead, _ := chacha20poly1305.NewX(key)
nonce := random(aead.NonceSize())
cipher := aead.Seal(nil, nonce, secret, nil)

Properties:

  • AEAD: encryption + authentication
  • 192-bit nonce (extended variant via NewX)
  • Safe for random nonces

Stored fields per credential:

  • Ephemeral (public key)
  • Nonce
  • Secret (ciphertext)

When running:

Terminal window
kosh get <label> <user>

Steps:

  1. Prompt for master password.
  2. Derive unlock key with Argon2id.
  3. Decrypt vault secret (verifies password).
  4. Load credential: (EphemeralPub, Nonce, Cipher).
  5. Recompute shared secret:
shared, _ := curve25519.X25519(vaultPrivateKey, EphemeralPub)
key := sha256.Sum256(shared)
  1. Decrypt using ChaCha20-Poly1305:
secret, err := aead.Open(nil, nonce, cipher, nil)
  1. Copy plaintext password to OS clipboard.

Each credential uses a unique ephemeral ECDH key pair.

Argon2id with 64MB RAM requirement.

ChaCha20-Poly1305 prevents tampering.

Only the vault’s private key is stored, encrypted with the master password.

No external APIs; no key material leaves the device.


LayerStored DataPurpose
VaultSalt, encrypted Secret, Nonce, vault public keyMaster password verification & vault unlocking
CredentialEphemeral, Nonce, SecretPer-credential encryption using ephemeral ECDH

Kosh assumes:

  • Attacker has full access to the vault file
  • Attacker cannot guess master password within feasible time
  • System clipboard is trusted (OS-level)
  • User’s runtime environment is not compromised (no keyloggers or debugging injection)