talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

password_encryption.txt (2922B)


      1 Unless --with-password-encryption=old is active, account passwords are
      2 stored encrypted using AES-256-CBC, using following scheme:
      3 ----------------------------------------------------------------------
      4 
      5 Encryption/decryption key is derived from either PASSCRYPT_KEY, or
      6 user-selected master passphrase, using PBKDF2, using salt from
      7 'master_passphrase_salt', and number of rounds (iterations) from
      8 'master_passphrase_pbkdf2_rounds'.
      9 
     10 IV (initialization vector) for the cipher is filled with random bytes.
     11 
     12 
     13 Encryption
     14 ----------
     15 We prepare a buffer long enough to fit the NULL-terminated password string
     16 plus one cipher block in it, with one block of random data at the beginning,
     17 followed by the password we want to encrypt (in UTF-8), rest is padded
     18 with zero bytes.
     19 
     20 The minimal buffer size is 128+blocksize, and if the password (including
     21 the trailing NULL byte) is longer than 128 bytes, the size is increased by
     22 another 128 bytes until it is long enough to fit the password plus one
     23 cipher block. This is to make it harder to guess the password length from
     24 length of the encrypted string. So for example, if the password (again,
     25 including the trailing NULL byte) is 129 characters long, our buffer will
     26 be 256+blocksize bytes long.
     27 
     28 We encrypt the buffer using the encryption key and IV mentioned above,
     29 resulting in ciphertext of the same length as the buffer.
     30 
     31 We base64-encode the ciphertext, and store it as:
     32 "{algorithm,rounds}encodedciphertext"
     33 
     34 "rounds" is an integer value set to number of PBKDF2 rounds used to
     35 generate the key derivation used as encryption key.
     36 
     37 
     38 Decryption
     39 ----------
     40 We strip the "{algorithm}" (after verifying that it matches what we
     41 expect) and base64-decode the remaining ciphertext.
     42 
     43 We decrypt the ciphertext using decryption key and IV mentioned above,
     44 resulting in plaintext of the same length as the ciphertext.
     45 
     46 We discard the first block from plaintext, and the rest is a
     47 zero-terminated string with our password in UTF-8.
     48 
     49 
     50 Why the random block at the beginning?
     51 --------------------------------------
     52 We are taking advantage of property of CBC mode where decryption with
     53 a wrong IV results in only first block being garbled. Therefore we
     54 prepend a random block to our plaintext before encryption, and discard
     55 first block from plaintext after decryption.
     56 
     57 
     58 Master passphrase
     59 -----------------
     60 This can be any string user chooses. We store its 64 bytes long key
     61 derivation (KD), using PBKDF2 with HMAC-SHA1, and later check correctness
     62 of user-entered passphrase by making same KD from it and comparing it
     63 to the stored one. Only if the two KDs match, the passphrase is accepted
     64 and remembered for session, thus giving access to account or plugin
     65 passwords.
     66 
     67 Salt used for PBKDF2 is stored in 'master_passphrase_salt', encoded
     68 as base64. It consists of 64 randomly generated bytes.
     69 
     70 Number of rounds for PBKDF2 is stored in hidden preference
     71 'master_passphrase_pbkdf2_rounds'.