The problem

Users may choose to use a different pseudonym for a different identity, and this should not only be kept consistent in their communication, but is a core privacy issue. A gamer’s leisuretime persona, FurryBob420, for example, should not leak information when she sends professional mails as Alice Spivak to her child’s school. The previous implementation always kept one canonical username (updated in various ways, but still potentially leakable between identities when not specified in an input identity struct), which made such leakage possible.

This description aims to partially specify new means to allow consistency of usernames by identity. (From the user standpoint at the app level, the p≡p application user identifies communication partners by using the contact management of her/his messaging app, so any mapping of such names to identities, particularly for the own user, will be done there (see caveat below regarding partner users).


  • msg: a p≡p message struct
  • addressees: all identities of msg.to and msg.cc as listed on the msg struct
  • inner message: the rfc822 message contained inside the attachments section of an encrypted incoming or outgoing p≡p message’s payload
  • transported message: in case of non-legacy p≡p messages, the inner (actual) message before encryption or after decryption; for other messages (OpenPGP, unencrypted, legacy p≡p), there is no outer message, so this will just be the normal message transmitted
  • src: the input message for encrypt_message() or decrypt_message()
  • dst: the output message for encrypt_message() or decrypt_message()
  • src.from: in decrypt_message(), the from identity that is derived directly from the outer (or only) message
  • dst.from: in decrypt_message(), the calculated from identity; this may, for transport or privacy reasons, differ completely from src.from when coming from the inner message, or it may be equivalent/the same as src.from, in the case of legacy/non-p≡p messages. This is the identity that will be delivered back to the caller of decrypt_message()
  • set: (in regard to person.username and identity.username) - save to the management database
  • deliver: return from a function call

Privacy and security goals

  • Avoidance of third-parties being able to map a pseudonym to other pseudonyms or trace a pseudonym back to an actual person, where feasible
  • No external party should be able to set/reset an own username/pseudonym
  • No external party, other than the verified communications partner, should be able to set/reset the pseudonym for an identity once a reliable channel has been established
  • FIXME: This sentence does not make sense. Have asked question in query and will rewrite it once the author replies - until then, I presume this falls under the first sentence ;) The p≡p User may want to communicate to the Communication Partner using their chosen Pseudonym to not uncover the username in Contact Management.


Database Storage

  • Generally, the username from contact management is stored in person.username in the management database. However, since any call to update_identity may set this value, some care needs to be taken to ensure this is either always supposed to be true, or actually IS true.
  • Depending on the message direction and whether this is an own user, the pseudonym from the transported message is stored in identity.username in the management database

API calls


  • update_identity() sets people.username in all cases.
  • update_identity() sets identity.username only if it is empty.


myself()sets identity.username.


  • In the case that the message rating is at least reliable, decrypt_message() sets identity.username from whatever the dst.from identity (calculated output identity) is
  • decrypt_message() sets identity.username from src.from in case the message is unprotected and the channel is insecure.
  • decrypt_message() sets identity.username from src.from in case the message is unprotected and the channel is secure only if identity.username is empty.
  • decrypt_message() delivers identity.username for all addressees of dst in case of protected message or src otherwise, without storing them.We can avoid me calling update_identity all over the place here by putting a special get_identity_username function - less DB intensive than update_identity and won’t set anything. But in case there is not one in the DB, do we deliver src., or nothing?


encrypt_message() sets identity.username for all addressees of src only where it is empty, respectively. See “known issues”

Usage of username

When rendering an identity for the Transported Message in case identity.username is set this is used, otherwise if people.username is set this is used, otherwise no username is used.

Known issues

  • We always choose to use the username sent to us by the most verifiable assertion of identity received by a partner so far. In this scenario, there is little the user can do to keep the partner from setting them up to send undesired username information attached to the partner’s identity to others. Take, for example, the following scenario: hussidente@huss_for_pres.cat wants everyone to think DarthMama thinks he should not be elected president, which is, of course, not true. And Hussidente KNOWS DarthMama will mail lots of people with him in CC:. DarthMama’s pseudonym for Hussidente is “Hussidente Maximus”. Hussidente mails DarthMama a mail with from from name “DON’T VOTE FOR HUSS EVER - I’LL GIVE YOU EUR 100 NOT TO!” DarthMama sends out a mail to a group of colleagues, and suddenly has promised them all 100 Euros not to vote for Hussidente. One can think of worse examples, of course - you could cause someone to inadvertantly abuse you in front of others to get them in trouble.
  • When rendering an identity for the Outer Message no username should be used, but identity.username will be used if set in the first version, because otherwise pEp for Thunderbird cannot work. This must be marked in source code and a bug ticket must be created and the ticket number must be mentioned in source. (p≡p generally protects messages by only allowing confidential information in the inner message.)
  • Conformance to this spec is fragile - calls to update_identity() will have to be managed closely. Please note that adding such calls to decrypt_message() in particular will change behaviour; tests will be written to detect such problems once specification problems above are resolved.