FileZilla 3.26.0-rc1 has been released with support for encrypted passwords protected by a master password. This post explains how it works:
Attacker category A:
- A passive attacker that can merely read all files stored on the user's computer.
Attacker category B:
- Passive attackers that can read active program memory
- Active attackers that can modify files
- Passive attackers monitoring input devices
- If user opts to use plaintext protocols: Passive attackers monitoring network traffic
Note that it is theoretically impossible to protect against attackers of category B unless expensive special-purpose hardware is used.
The master password functionality has been designed to protect the user's passwords against attacker category A. It does not and cannot protect against attackers of category B. Incidentally, this functionality is indistinguishable from not storing passwords at all from a security perspective.
When configuring a master password (P_master
), a random 32 byte salt (S_m
) is generated. From the password and the salt, a X25519 private key (M_priv
) is generated using PBKDF2_HMAC_SHA256:
M_priv' = PBKDF2_HMAC_SHA256(P_master, S_m, 32, 100000)
is obtained by clearing the three least significant bits of the first byte and the most significant bit of the last byte and by setting the second most significant bit of the last byte to 1.
The corresponding public key (M_pub
M_pub = X25519(M_priv, 9)
as well as S_m
are remembered for later use.
When saving a password (P
), first a random 32 byte salt (S_e
) is generated as well as a random ephemeral X25519 private key (E_priv
) and its public counterpart (E_pub
). Using Elliptic-Curve Diffie Hellman, a shared secret (R
) is derived:
R = X25519(E_priv, M_pub)
From R, M_pub, E_pub, S_e and S_m
, both an AES key (K
) and a nonce (IV
) are derived:
K = SHA256(S_e || 0 || S || E_pub || M_pub || S_m)
IV = SHA256(S_e || 1 || S || E_pub || M_pub || S_m)
The password is then encrypted with AES256-CTR and stored together with E_pub
C = E_pub || S_e || AES256-CTR(K, IV, P)
When decrypting a password (C
), it is first split into E_pub
such that C = E_pub || S_e || C1
Next, using P_master
and the stored S_m
, M_priv is calculated as during setup.
Using ECDH, the shared secret R
R = X25519(M_priv, E_pub)
are calculated as before and the password decrypted:
P = AES256-CTR(K, IV, C1)
RFC 7748 for X25519 and its application for ECDH
RFC 2898 for PBKDF2
Wikipedia article about AES
Wikipedia article about Counter mode