The function of the keyserver is to:
- Make public keys required for peer-identification available: Who has sent me a message?
- Make public keys required for content-confidentiality available: How do I encrypt a message?
- Make addresses required to contact a peer available: Where do I send a message?
- Increase authenticity of identities: Why should I trust a public key to belong to a specific person?
Goals:
- Allow independent verification of keyserver integrity by any number of users.
- Support short-lived encryption keys and address: Forward Security.
- Assure that all identifiers are unique.
- Allow the use of short, human-readable pseudonyms as sufficiently secure anchors of key authenticity.
- Prevent full enumaration of user pseudonyms even if all public data is available.
- Allow users to prove if the keyserver is ill-behaving.
These functions are implemented by three interrelated components:
- The Key Repository: Here long-term public keys and public recipient addresses of the system's participants are stored and available for lookup. The primary function that is accomplished by the Key Repository is to provide peer-identification and link the various keys published by a user. In addition, it provides the necessary data for addressing and initial content confidentiality in case other components are temporarily insufficient to accomplish these.
- The KeyInit Repository: Here temporary/short-lived public keys and public recipeint addresses of the participants are stored and available for consumption (lookup then delete). This provides addressing and content-confidentiality functions.
- The Key Hashchain: Every change of long-term public identity information in the Key Repository is recorded in the permanent Key Hashchain. It guarantees the order of events and assures—in combination with a specific usage scheme—key authenticity. Furthermore the Key Hashchain serves as index to facilitate Key Repository lookups. The Key Hashchain is public and at least partially known to each participant of the system.
In short:
- The Key Hashchain establishes the link between a claimed pseudonym and a long term public signature key of that pseudonym.
- The Key Hashchain indexes the Key Repository which holds the long term public signature key.
- The KeyInit Repository holds the temporary public encryption keys and addresses of the pseudonym, verified by the public signature key.
Given a pseudonym for which the key is to be looked up, the user searches the Key Hashchain in chronological order, testing if the entry matches the pseudonym. In case of a match, the user requests the key referenced by the entry from the keyserver. The user continues the search on the Key Hashchain until the last-known entry has been processed. Should there be more than one matching entry, the user needs to verify that the entry N has been signed by the key published by entry N-1. If the verification matches for the whole set of entries, the lookup has been successful. The user may increase the assurance concerning the integrity and authenticity of the Key Hashchain by exchanging information about the last known entries with known peers. This establishes trust in that the Key Hashchain has not been falsified, reorderd or selectively created, giving the user a high assurance that the pseudonym indeed links to one specific key (key authenticity).
The user creates a human-readable/human-memorizable pseudonym and verifies that no Key Hashchain entry matches it already (that is, he verifies that the pseudonym is not yet taken). He then creates a signature key pair and signs the pseudonym and public key with the private key. The resulting message is sent to the Keyserver. After verifying that the pseudonym is not yet taken, the keyserver enters the pseudonym and key into the Key Hashchain and Key Repository, respectively. The Keyserver then replies with a signed statement confirming to the user that the pseudonym has been registered in the hashchain. At this point the user can verify the publishing of the pseudonym in the Key Hashchain and optionally wait some time before announcing his pseudonym to others. If this order—creation, publication, keyserver confirmation, verification, announcement—is observed, the authenticity of the pseudonym-key relationship is assured.
The Keyserver is to provide the following public JSON-RPC API:
KeyRepository.Capabilities()
Return keyserver capabilities:
- methods implemented
- domains served
- Key Repository URIs
- KeyInit Repository URIs
- Key Hashchain URIs
- last Key Hashchain entry
- public wallet key of keyserver
- public signature key(s) of keyserver
Reply is signed by current keyserver signature key.
KeyRepository.FetchUID(UIDIndex)
Return the encrypted UID message specified by UIDIndex from the Key Repository.
KeyInitRepository.FetchKeyInit(SigKeyHash)
Return the current encrypted KeyInit message specified by SigKeyHash from the KeyInit Repository.
KeyHashchain.FetchHashChain(startPosition [,endPosition])
Return the Key Hashchain entries between startPosition and optional endPosition. If endPosition is omitted, only return one entry at startPosition. If position at startPosition does not exist yet, return last entry.
KeyHashchain.FetchLastHashChain()
Return last Hashchain entry.
KeyRepository.GetLink(domain)
Return the Identity and Position of the first AUTHORITATIVE entry for the Chainlink to the domain. (see below: "Linking chains and key repositories")
KeyRepository.CreateUID(UIDMessage[,Token])
Add the UID to the Key Hashchain and Key Repository if the identity is not known yet. Return a signed confirmation consisting of UID message and Key Hashchain entry where the UID was added.
KeyRepository.UpdateUID(UIDMessage[,Token])
Update the UID message for an identity if the signature(s) on the new UIDMessage match the keys present in the previous UIDMessage for the same identity. Return a signed confirmation consisting of UID message and Key Hashchain entry where the UID was added.
KeyInitRepository.AddKeyInit(SigPubKey, KeyInits[,Token])
Add one or more KeyInit messages to the KeyInit Repository if the messages are signed by the corresponding signature public key. Return a signed confirmation consisting of the KeyInit.
KeyHashchain.LookupUID(pseudonym)
Return all Key Hashchain entries referring to pseudonym.
KeyInitRepository.FlushKeyInit(SigPubKey, Nonce, Signature)
Flush all keys in the KeyInit Repository for this SigPubKey. Call must be signed/authenticated by a Signature over Nonce (which is the current 64bit unixtime).
The pseudonym is of the format localpart@domain
(e.g.,
[email protected]
). The pseudonym may only contain printable lowercase
characters of the latin alphabet, the digits 2
-9
and the special characters
dash (-
), at (@
), and dot (.
).
For comparison-reasons, all occurrences of the digit 1
must be translated
to the lower-case letter l
(Lima), all occurrences of the digit 0
must
be translated to the lower-case letter o
(Oscar), and all occurrences of the
lower-case letter j
(Juliett) must be translated to the lower-case letter
i
(India).
The digit translation serves the purpose of reducing the cases of mistaken
spelling in copy&paste-scenarios in which an attacker tries to slip a
similar-looking name past the attention of the user.
Messages are encoded as JSON according to the global encoding rules (see overview):
- All optional fields are present but set to the zero value.
- Keys are converted to uppercase and may not include any whitespace.
- No indention is used, no unnecessary whitespace for element separation.
- All binary data is encoded as base64 with full padding.
- Fields are ordered lexicographically by field name/key.
- In cases where lists of cryptographic keys for different ciphersuites are given, they are encoded as array and ordered by the hash of binary cryptographic key (before base64 encoding).
Messages share certain common fields:
VERSION
: The protocol version, as string. E.g. "0.1a".MSGCOUNT
: Integer that must increase for each message of the same type for the same user. Encoded as JSON integer.NOTAFTER
: 64bit unixtime after which the key(s) offered by the message should not be used anymore. Encoded as JSON integer.NOTBEFORE
: 64bit unixtime before which the key(s) offered by the message should not be used yet. Encoded as JSON integer.
Mute supports the use of multiple ciphersuites. This makes it necessary to communicate multiple keys per message to give the peer a choice to operate from. Lists of keys are arrays ordered by the hash of the included keys, each entry consisting of:
struct KeyEntry{
CIPHERSUITE. Ciphersuite for which the key may be used. Example:
"ECIES25519 HKDF AES-CTR256 SHA512-HMAC ED25519 ECDHE25519"
FUNCTION. Function for which the key may be used in the ciphersuite. Example:
"ECIES25519"
HASH. SHA512 hash of PUBKEY.
PUBKEY. The public key.
}
During encoding, the binary value of HASH
can be used to determine the order
of the array containing multiple keys. HASH
and PUBKEY
will be encoded
base64.
The key repository maintains a publicly accessible repository of valid signature
key entries for peer identification purposes. To add a key, the user generates a
UIDMessage
and sends it to the Key Repository.
After validation the UIDMessage
is added to the Key Repository and the Key
HashChain, and a signed confirmation is returned to the user.
This message contains the necessary keys and other data to serve as peer identification and optional fallback for addressing and content confidentiality. It is self-signed by the user and counter-signed by the keyserver after being added to the Key Repository.
Format:
To be sent from user to server.
struct UIDMessage {
struct UIDContent {
VERSION: The protocol version, as string. E.g. "0.1a".
MSGCOUNT: Integer that must increase for each message of the same type for
the same user. Encoded as JSON integer.
NOTAFTER: 64bit unixtime after which the key(s) offered by the message
should not be used anymore. Encoded as JSON integer.
NOTBEFORE: 64bit unixtime before which the key(s) offered by the message
should not be used yet. Encoded as JSON integer.
MIXADDRESS: Fully qualified address of Mix to use as last hop to user.
String. Must be "NULL", if FORWARDSEC is not "optional".
NYMADDRESS: A valid NymAddress. Base64. OPTIONAL.
Must be "NULL", if FORWARDSEC is not "optional".
IDENTITY: Identity/Pseudonym claimed. Including domain. String.
SIGKEY: Entry of type KeyEntry (see above). Used to sign the UIDContent and
to authenticate future UIDMessages for this Identity.
PUBKEYS: Array of KeyEntry. For static key content confidentiality.
(Must be ECDH/DH capable)
SIGESCROW: Entry of type KeyEntry (see above). Used to optionally
authenticate future UIDMessages for this Identity. Failover for
a lost SIGKEY. May be zero-value.
LASTENTRY: Last known Key Hashchain entry. String.
REPOURIS: URIs of KeyInit Repositories to be used to publish KeyInit
messages. Array of strings.
struct PREFERENCES {
FORWARDSEC: Forward Security preference. Must be "strict", "mandatory" or
"optional". "strict" and "mandatory" force the peer to
initiate a message via a forward secure mechanism, "optional"
allows for degrading the first message to be not forward
secure.
CIPHERSUITES: List of ciphersuites, ordered from most preferred to least
preferred. Arra of strings. May be zero-value.
[future preferences may be added]
}
struct CHAINLINK {
URI: URI(s) of the foreign key hashchain. Array of strings. May be zero-value.
LAST: Last entry of the foreign key hashchain. String.
May be zero-value if URI is zero.
AUTHORITATIVE: Boolean true/false.
DOMAINS: List of domains that are served currently. Array of strings.
Must be zero unless AUTHORITATIVE is true and URI is set.
IDENTITY: Own Identity in the foreign key hashchain. String. May be
zero-value if URI is zero. Currently unused (must be zero).
}
}
ESCROWSIGNATURE: Signature over UIDContent by previous SIGESCROW (see above).
Base64 encoded. May be zero-value.
USERSIGNATURE: Signature over UIDContent by previous SIGKEY (see above).
Base64 encoded. May be zero-value.
SELFSIGNATURE: Signature over UIDContent by current SIGKEY (see above).
Base64 encoded.
LINKAUTHORITY: Signature over UIDContent by key server SIGESCROW in the case
of AUTHORITATIVE keyserver links (see below: "Linking chains and
key repositories"). Base64 encoded. Must be zero unless an
AUTHORITATIVE link entry.
}
Successful reply from server;
struct UIDMessageReply {
struct Entry {
UIDMessageEncrypted: Encrypted version of UIDMessage.
See below: "Storing UIDMessages."
HASHCHAINENTRY: Corresponding Key Hashchain Entry. String.
HASHCHAINPOS: Position of Key Hashchain Entry. Integer.
}
SERVERSIGNATURE: Signature over Entry by Keyserver's signature key.
}
Key Server verifies:
-
Verify that the
UIDMessage
is well-formed. -
Verify that the
MIXADDRESS
is allowed by configuration of the Key Server. -
Verify that the domain of the
IDENTITY
is allowed by configuration of the Key Server. -
Verify that the localpart of the
IDENTITY
is allowed by the configuration (allows blocking of special identities likeroot@
andadmin@
). -
Verify that the
SELFSIGNATURE
is a valid signature ofUIDContent
by the includedSIGKEY
. -
Verify
CHAINLINK
(see below: "Linking chains and key repositories"). -
Verify that
UIDContent.LASTENTRY
is reasonably fresh and valid for this keyserver. -
If
IDENTITY
is unknown, add to Key Hashchain and add to Key Repository (see below: "Storing UIDMessages"). End. -
If
IDENTITY
is known, continue: -
Verify that
MSGCOUNT
has been increased by exactly 1 (one) from previousUIDMessage
. -
Verify that
NOTBEFORE
and NOTAFTER are valid. -
If
ESCROWSIGNATURE
is not zero, verify thatESCROWSIGNATURE
is a valid signature overUIDContent
by the previously knownSIGESCROW
. -
If
SIGESCROW
differs from previousSIGESCROW
, verify thatESCROWSIGNATURE
is not zero. -
If
USERSIGNATURE
is not zero, verify thatUSERSIGNATURE
is a valid signature overUIDContent
by the previously knownSIGKEY
. -
Verify
USERSIGNATURE
orESCROWSIGNATURE
are not zero (this is a xor,USERSIGNATURE
andESCROWSIGNATURE
cannot be both not zero). -
Add to Key Hashchain and add to Key Repository (see below: "Storing UIDMessages"). End.
The USERSIGNATURE
/ESCROWSIGNATURE
verification allows for the updating of
the SIGKEY
without needing cooperation by the ESCROW
. It also allows the
updating of the SIGKEY
when it has been lost. Furthermore, it prevents the
changing of the SIGESCROW
key unless the previous SIGESCROW
key is
available. This allows for secure paper-storage of a very long-term
backup/failover key to recover control over the identity in case of key loss (or
key control loss).
Definitions:
ROLLOVER
: Time after which the hashchain should be considered "non-AUTHORITATIVE" by the client. One year.
Server:
- New entries should not have a
UIDMessage.UIDContent.NOTAFTER
that is more thanROLLOVER
in the future. - A found
IDENTITY
is only considered "known" if it is not older thanUIDMessage.UIDContent.NOTAFTER - ROLLOVER * 3
. - A found
IDENTITY
can be updated if it is not older thanUIDMessage.UIDContent.NOTAFTER - ROLLOVER * 2
Client:
- A found
IDENTITY
is only considered "known" if it is not older thanUIDMessage.UIDContent.NOTAFTER - ROLLOVER
The Key Repository stores an encrypted form of the UIDMessageReply
and makes
it accessible to external users:
- Calculate hash:
UIDHash = SHA256(UIDMessage)
- Calculate hash:
UIDIndex = SHA256(UIDHash)
- Create
nonce=Random(blocksize)
- Encrypt
UIDMessage
:
UIDMessageEncrypted = UIDIndex | nonce | aes_ctr(nonce, key=UIDHash, UIDMessage)
- Create Key Hashchain entry:
hcEntry, hcPos = hashchain_append(Identity, UIDHash, UIDIndex) // see below: Hashchain operation
- Construct
Entry
fromhcEntry
,hcPos
,UIDMessageEncrypted
- Sign
Entry
by Key Server's key pkey:serverSig = sign(pkey, Entry)
- Construct
UIDMessageReply
fromEntry
andserverSig
. - Store new UIDMessageReply accessible by
UIDIndex
. (User must knowUIDIndex
to fetch encryptedUIDMessageReply
) - Return
UIDMessageReply
to user.
- Maintain a key-value database "Identity"-"Key HashChain Entry" to facilitate
quick checks if an identity is already taken, and if yes, what
UIDMessage
it points to. - Maintain a key-value database "Identity"-"ECDSA PubKey" to quickly check signatures in KeyInit and Key/UID update situations.
- Maintain a chronologically ordered log of Key Hashchain positions for each
identity, if the
LookupUID
method is supported.
The Key Hashchain is constructed as follows.
Key Server computes:
Given: Identity
, UIDHash
and UIDIndex
(see above: "Storing UIDMessages"),
previous entries.
- Set
TYPE = 0x01 // Type field for future extensions
- Create random
NONCE
64bit - Compute
k1, k2 = CKDF(NONCE)
- Create
HASH(k1 | identity) = HashID
- Create
HASH(k2 | identity) = IDKEY
- Create
AES_256_CBC(IDKEY, UIDHash) = CrUID
- Create
HASH(entry[n]) := Hash(TYPE | NONCE | HashID | CrUID | UIDIndex | Hash(entry[n-1])
- Publish:
HASH(entry[n]) | TYPE | NONCE | HashID | CrUID | UIDIndex
Client computes:
Given: Identity, Hashchain entries
- For each entry n:
Compute: k1, k2 = CKDF(NONCE)
Compute: HashIDTest = HASH(k1 | Identity)
If NOT: HashID == HashIDTest: Continue
Compute: IDKEY = HASH(k2 | Identity)
Fetch from Key Repository: UIDMessageReply = GET(UIDIndex)
Decrypt UIDHash = AES_256_CBC_Decrypt( IDKEY, CrUID)
Decrypt UIDMessageReply.UIDMessage with UIDHash
Verify UIDMessage, memorize ECDSA Key
- If no further entry can be found, the latest
UIDMessage
entry has been found.
- A single hashchain entry is
3 * HASH + AES_BLOCK + NONCE
in size:3 * 256 + 256 + 64 bits == 1088 bits == 136 bytes
. - Start the chain with the
UIDMessage
of the Keyserver itself (containing the Keyserver signature public key, self-signed reply). - Keyservers should keep a private cache of identity -> entry mappings to prevent exhaustive searches. The cache is not distributed.
UIDMessages and messages sent between peers should contain the last known
hashchain entry. This allows verification of the chain by consensus. In paranoid
cases the user should wait for peers to send him a last known entry after his
own last UIDMessage
entry before making his pseudonym known. Since
UIDMessageReplies are signed, we can demonstrate the performance of the Key
Server to third parties. Verification of single Identities should always be an
exhaustive search over the Hashchain. In addition, users should verify the
UIDContent.LASTENTRY
to be reasonably fresh (and valid) in the chronological
context of the entry so that delayed entries can be detected in cooperation with
the keyserver.
The UIDMessage CHAINLINK struct
serves as a means to linke multiple Key
Repositories and Key Hashchains together for the following purposes:
- Verification binding: Hashchains that verify each other by adding the last entry of another hashchain into their own, increasing the security against modification for the linked hashchain.
- Connect identities across multiple Key Servers.
- AUTHORITATIVE link: Allows Keyservers to operate in a federated way to support finding Identities served by other KeyServers.
Entries in the CHAINLINK
URI must be ordered lexicographically. No more than
five entries are permitted except in the AUTHORITATIVE Link scenario.
This limitation serves to limit space for preimage attacks.
The URI of the CHAINLINK
is set to the URI(s) of the Origin Key Server to be
bound into the destination hashchain.
The LAST
entry is the last key hashchain entry on the Origin Key Server at the
time of request.
If AUTHORITATIVE
is true, the entry is verified by the destination keyserver by
making the appropriate calls to the origin keyserver.
The reply of such a call may be off by up to an hour to allow for fast-growing chains to link.
DOMAINS
and IDENTITY
must be zero-value.
The URI of the CHAINLINK
is set to the URI(s) of the Origin Key Server to be
bound into the destination hashchain.
The LAST
entry is the last key hashchain entry on the Origin Key Server at the
time of request.
IDENTITY
is set to the origin keyserver identity of this entry.
DOMAINS
must be zero-value.
If AUTHORITATIVE
is true, the Key Server must get the current UIDMessage
for
the IDENTITY
from the Origin Key Server and verify that the SIGKEY
and
SIGESCROW
of the connecting UIDMessage
(this message) are the same as for
the current entry of the IDENTITY
on the Origin Key Server.
If AUTHORITATIVE
is false, the Key Server must verify that the identity was
connected successfully before (with AUTHORITATIVE
true).
This allows a peer to be identified over multiple keyservers and thus increase
assurance. It also allows for identities to be moved from one keyserver to
another or to be separated.
The URI of the CHAINLINK
is set to the URI(s) of the Origin Key Server to be
bound into the destination hashchain.
The LAST
entry is the last key hashchain entry on the Origin Key Server at the
time of request.
AUTHORITATIVE
must be true.
DOMAINS
is a list of domains handled by the Origin Key Server. IDENTITY
is
set to the identity of the origin key server itself (it's signature key).
AUTHORITATIVE Links are Connected Identities and require the same verification
process. In addition, AUTHORITATIVE links that change (add or delete) domains
claimed by the Origin Key Server need to be manually verified by the Key Server
Operator.
For this the LINKAUTHORITY
signature over the whole UIDMessage
must be made
by the destination key server SIGESCROW
key.
DOMAINS
made known by this operation may not be claimed by future links unless
the chain of identities can be verified.
KeyInit messages allow the asynchronous setup of Forward Secure communication sessions by making the ephemeral key of the peer always available through a third party (the KeyInit repository).
KeyInit messages contain the necessary information to setup a session:
Mixaddress, Nymaddress and ephemeral keys. This information is encrypted to make
it unusable to users that do not know about the Identity. This also allows for
KeyInit Messages to be public and thus increase plausible deniability for peers.
KeyInit messages are created by the user and uploaded to the Key Server. The Key
Server returns a KeyInit message to an inquiring peer when given a UIDIndex
(see above: "Key HashChain operation") and depending on NOTAFTER
/NOTBEFORE
settings of the KeyInit messages.
struct KeyInit{
struct Contents{
VERSION: The protocol version, as string. E.g. "0.1a".
MSGCOUNT: Integer that must increase for each message of the same type for
the same user. Encoded as JSON integer.
NOTAFTER: 64bit unixtime after which the key(s) offered by the message
should not be used anymore. Encoded as JSON integer.
NOTBEFORE: 64bit unixtime before which the key(s) offered by the message
should not be used yet. Encoded as JSON integer.
FALLBACK: Boolean. true/false. Determines if the key may serve as a
fallback key.
SIGKEYHASH: SHA512(UIDMessage.UIDContent.SIGKEY.HASH)
REPOURI: URI of this KeyInit Repository. String
SESSIONANCHOR: Encrypted SessionAnchor Struct. See below.
SESSIONANCHORHASH: SHA512 of SessionAnchor Struct before encryption.
}
SIGNATURE: Signature of Contents by UIDMessage.UIDContent.SIGKEY
}
struct SessionAnchor{
MIXADDRESS: Fully qualified address of Mix to use as last hop to user. String.
NYMADDRESS: A valid NymAddress. Base64.
MUST BE "NULL" IF UIDMessage.UIDContent.NYMADDRESS == NULL
PFKEYS: Array of KeyEntry. For ephemeral/forward secure key agreement.
}
SESSIONANCHOR = AES256_CTR(key=UIDMessage.UIDContent.SIGKEY.HASH, SessionAnchor)
The Key Server must verify that
(on call of AddKeyInit(Identity, KeyInit[,Token])
):
- The
REPOURI
points to this KeyInit Repository. SIGKEYHASH
corresponds to theSIGKEY
of theIdentity
.- That the
SIGNATURE
was made withUIDMessage.UIDContent.SIGKEY
over Contents. - That the
MSGCOUNT
has been increased (by any number). - That
NOTAFTER
andNOTBEFORE
are valid.
Client verifies in addition:
- That
SESSIONANCHORHASH
matches decryptedSESSIONANCHOR
.
A peer can request a KeyInit
message from the KeyInit repository by
UIDIndex
. The order in which KeyInit
messages are returned and deleted
depends on the NOTAFTER
/NOTBEFORE
settings as well as the FALLBACK
setting. The server is to delete KeyInit messages that have reached NOTAFTER
and not return them anymore. KeyInit
messages that have not reached
NOTBEFORE
are not returned.
No Fallback:
From the KeyInit messages that have reached NOTBEFORE
but have not yet reached
NOTAFTER
the ones with FALLBACK
set to false are returned first,
ordered by their remaining lifetime (time until NOTAFTER
is reached). Keys
with FALLBACK==false
are always deleted immediately.
Fallback:
If no more keys with FALLBACK==false
are available, the KeyServer will start
returning FALLBACK==true keys
that have reached NOTBEFORE
but have not yet
reached NOTAFTER
.
Out of these keys a random one is selected and returned, biased by remaining
lifetime (the less lifetime remains, the more likely the key is returned).
Fallback keys are deleted only if other valid keys remain and based on a biased
coin-flip (the less lifetime remains, the more likely the key is deleted after
it has been returned).
Biased selection/deletion:
- Calculate the remaining litetime of the key in question:
n = NOTAFTER-NOW()
- Calculate the maximum lifetime of all keys eligible for selection:
m = max(lifetime)
- Generate random value
0 < r < m : r = random(0,m)
- If
r > n
, the key is a deletion candidate.
If no more KeyInit messages are available from the KeyInit repository but the
FORWARDSEC
(see below) setting of the user is "mandatory", then the peer has
to initiate a session via synchronous prekeying
(see message protocol).
If the KeyInit message returned by the KeyInit repository is the fallback
message and the FORWARDSEC
setting of the user is "strict", then the peer must
either retry until he receives a unique message or he must initiate a session
via synchronous prekeying.
If the KeyInit message returned is shared (FALLBACK==true
) and FOWARDWARDSEC
setting is "optional", the sender may use the KeyInit Message.
FORWARDSEC: UIDMessage.UIDContent.PREFERENCES.FORWARDSEC
- Users should update the KeyInit message frequently, but not faster than
2 * MixMax
. - After a
KeyInit
message has expired, the user should purge the corresponding private key quickly, but not before waiting2 * MixMax
and then fetching and processing messages from the storage account. - The keyserver may define a maximum number of
KeyInit
messages stored perUIDIndex
and a maximum frequency for downloading/deleting them.