Skip to content

Algorithmic address+password generation #697

@link2xt

Description

@link2xt

Currently the username (address) and password is generated by making a request to /new URL.

Server-side script then generates a new username and password:

def create_newemail_dict(config: Config):
user = "".join(random.choices(ALPHANUMERIC, k=config.username_max_length))
password = "".join(
secrets.choice(ALPHANUMERIC_PUNCT)
for _ in range(config.password_min_length + 3)
)
return dict(email=f"{user}@{config.mail_domain}", password=f"{password}")

There are multiple problems with this approach:

  1. HTTPS request may fail even if IMAP and SMTP connection could work.
  2. HTTPS requests are only made when the user registers a new account or manually visits the web page. So a passive observer can see when new user accounts are created.
  3. If username expires because the user has not used it for a long time, it can be taken over.
  4. If the server is reinstalled, username can be taken over.
  5. This encourages users to manually select "nice" address with a weak password they somewhat randomly type on the keyboard (https://support.delta.chat/t/how-to-create-an-account-without-an-account-creation-qr-code/3911), but the password cannot be changed (https://support.delta.chat/t/changing-the-password-of-a-chatmail-account/4122)
    We don't want to support changing the password, this will be solved by having the ability to create a new address (with a new password) and migrate to it.

HTTPS requests exist for historical reasons. chatmail has evolved from mailadm scripts that started as Python scripts to create accounts on postfix+dovecot server and a request to an URL with a token actually created an account.

I propose to specify an algorithm for password and address generation so it can be implemented directly in the client.

Something like:

  1. Generate a random password with 256 bits of randomness. base64-encode it.
  2. Calculate the address by taking the "chatmail:" | domain | ':' | password string and hashing it. Concatenating with the domain makes sure that passwords cannot be reused between chatmail servers with different domains.
  3. Truncate the hash to 32 byte (256 bit) output of the hash. Add 3 random bytes or remove the last two bytes to avoid padding and base32-encode it. Add @domain to get the address.

One downside of this scheme is that username now is not completely random, but depends on the password and we need to ensure that this scheme in secure.

We cannot have short addresses to make it impossible to bruteforce by running this procedure a lot of times and looking for collisions. We can still save the password hash when the user logs in for the first time so even if another password for the same login is somehow found, it will not be accepted as long as the user does not expire.

I also thought about having domain separation, but maybe there are other non-obvious changes that need to be done.

For backwards compatibility we will still support usernames that have a password file with a hash on the server, but will not create new ones. If they expire, they cannot be taken over because new one will not be created.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status

    Closed PRs & Issues

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions