Linphone Instant Messaging Encryption (LIME)

Message encryption/decryption

Random material used

Sending and receiving messages are treated separately. There is key material for sending messages to a peer and separated one for message reception from the same peer.
On each side, stored in ZRTP cache.

Secret :

  • sender/receiver key(256 bits, actually 192 bits for key, 64 bits for Initial Vector)
  • sender/receiver Session Id(256 bits)

Public :

  • sender/receiver Session Index (32 bits)
  • end of key validity (64 bits UTC time)
  • ZID (ZRTP unique ID) (96 bits)

ZRTP cache is an XML file with the following structure (only LIME relevant component are listed here)

<peerZID/> (unique)
<sip Uri/> (can be more than one)
<sender Key, Session Id, Session Index/> (unique)
<receiver Key, Session Id, Session Index/> (unique)
<key validity limit (UTC time)/> (unique)


Message is encrypted using AES-192 in GCM mode(SCIMP use CCM mode which is very similar, GCM is supposed to be lighter on CPU) with authentication tag(sender/receiver ZID and sender Session Index are authenticated). Result message is an XML doc :

<sender ZID/>
<session Index/>
<auth tag(16 bytes) and cipher text in b64 coding/>


Message is decrypted, sender/receiver ZID and Session Index are authenticated (see AES GCM for details).

Key derivation

After any encryption or decryption, the key used is derived using a one way hash function so any attacker able to reach a key can't decrypt past messages.

Key derivation function(identical to the one defined in SCIMP paper)  :
HMAC_SHA256(Key, 0x0000001||"MessageKey"||0x00||SessionId||SessionIndex||0x00000100)

Session Index of derived key is incremented by one.

Lost messages/Key derivations sync

On message reception and before decryption, the receiver checks, using the session Id in the message and the local one, that the key derivation are still in sync with peer. If session index received is ahead the one locally stored, local image of key is derived until correct session index is reached(a limit is set at build time to not go into key derivation process if the session index is too far).

Shared secret expiration date

In order to prevent messages being encrypted forever with a key associated to an old or lost device, each peer(ZID) sending key have an expiration date. This expiration date is reset each time a ZRTP exchange occurs(voice/video call) and each time this peer(ZID hence device) send us a valid encrypted chat message. When a key has expired, any valid incoming message from peer will make it valid again for the preset time period: expiration applies to sending key only, not reception's one.

Validity period extent can be set in the linphone config file using, in the [sip] section, the lime_key_validity setting.

The config setting accept either:

  • an integer interpreted as a number of seconds
  • a list of integer values with suffix from Y,M,W,d,h,m,s respectively year, month, week, day, hour, minute and second (matching {[0-9]+[Y,M,W,d,h,m,s]?}* ). Month is set to always be a 30 days period and year a 365 days period.

The default value of this setting is 0 which implies no expiration date for the sending key.

Any change in the extension of the validity period setting will be executive after the next ZRTP call to the matching device or message received from it.

ZID/sip URI matching

ZID is attached to a device and created randomly at first run of a ZRTP session on that device.
Sip Uri defines a sip account.

One ZID can be associated to several sip URI (multiple SIP accounts on the same device)
One sip URI can be associated to multiple ZID (account being used on several devices)

Message sending

Parse the ZRTP cache to find any <peer> element having the correct <sip Uri>. We may found :

  • None :Message encryption is not possible
  • One : Encrypt using the sender key material(if not expired) and send the messages
  • Several : Create a message with multiple <msg> elements, one for each possible peer ZID encrypted with the relevant sender key material.

Message reception

Retrieve sender ZID from message and parse our cache to find a peer with matching ZID. If no matching ZID is found, unable to decrypt messages.

Retrieve from message a <msg> element having a <receiver ZID> matching our. If not found, unable to decrypt message, otherwise decrypt it and update the reception key and sender key expiration date.

Key material generation/re-sync

At each ZRTP enabled call, some new key material is created and stored in the cache along other ZRTP related secrets.

The extra secret shared material generation is defined in section 4.5.2 of ZRTP's RFC as exported key. Key, Session Id and Session Index are generated for sender and receiver using this mechanism at each call.