RFC 6189
ZRTP: Media Path Key Agreement for Unicast Secure RTP
RFC 6189: ZRTP: Media Path Key Agreement for Unicast Secure RTP ๐ฆ
okay bestie, this one is RFC 6189 and it's giving ZRTP: Media Path Key Agreement for Unicast Secure RTP ๐. The authors are P. Zimmermann from the Zfone Project, A. Johnston (Ed.) from Avaya, and J. Callas from Apple, Inc. It was published in April 2011 as an Informational document, with ISSN 2070-1721. The header doesn't say it obsoletes or updates anything, so it's doing its own thing ๐
Abstract โจ
so the abstract is basically the whole pitch ๐. ZRTP is a protocol for a media path Diffie-Hellman exchange, and its job is to agree on a session key and parameters so you can set up unicast Secure Real-time Transport Protocol (SRTP) sessions for Voice over IP (VoIP) apps. It's called media path keying because it runs multiplexed on the same port as RTP, and it doesn't need the signaling protocol to support it ๐ฆ. It also doesn't assume a Public Key Infrastructure (PKI), so end devices don't need the complicated certificates ๐.
For the media session, ZRTP gives you confidentiality and protection against man-in-the-middle (MiTM) attacks. If the signaling protocol already provides end-to-end integrity protection, you also get authentication โจ. ZRTP can use a Session Description Protocol (SDP) attribute to handle discovery and authentication through the signaling channel ๐ง. For best effort SRTP, it uses normal RTP/AVP (Audio-Visual Profile) profiles ๐ธ. It secures media sessions that include a voice stream, and it can secure sessions without voice too, using an optional digital signature ๐ฅ.
Status of This Memo ๐
this part is the boring legal bit, but it matters ๐. This document is not an Internet Standards Track specification. It's published for informational purposes.
It's a product of the Internet Engineering Task Force (IETF) and represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). Not every IESG-approved document is a candidate for any level of Internet Standard, see Section 2 of RFC 5741 ๐.
Info about the current status of this document, any errata, and how to give feedback is at http://www.rfc-editor.org/info/rfc6189 ๐ง.
Copyright Notice ๐
Copyright (c) 2011 IETF Trust and the persons identified as the document authors. All rights reserved ๐.
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (http://trustee.ietf.org/license-info) in effect on the date of publication. Please review these carefully, because they describe your rights and restrictions for this document ๐ซถ. Code Components extracted from this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they come without warranty as described in the Simplified BSD License โญ.
Table of Contents ๐
1. Introduction ....................................................4
2. Terminology .....................................................5
3. Overview ........................................................6
3.1. Key Agreement Modes ........................................7
3.1.1. Diffie-Hellman Mode Overview ........................7
3.1.2. Preshared Mode Overview .............................9
3.1.3. Multistream Mode Overview ...........................9
4. Protocol Description ...........................................10
4.1. Discovery .................................................10
4.1.1. Protocol Version Negotiation .......................11
4.1.2. Algorithm Negotiation ..............................13
4.2. Commit Contention .........................................14
4.3. Matching Shared Secret Determination ......................15
4.3.1. Calculation and Comparison of Hashes of
Shared Secrets .....................................17
4.3.2. Handling a Shared Secret Cache Mismatch ............18
4.4. DH and Non-DH Key Agreements ..............................19
4.4.1. Diffie-Hellman Mode ................................19
4.4.1.1. Hash Commitment in Diffie-Hellman Mode ....20
4.4.1.2. Responder Behavior in
Diffie-Hellman Mode .......................21
4.4.1.3. Initiator Behavior in
Diffie-Hellman Mode .......................22
4.4.1.4. Shared Secret Calculation for DH Mode .....22
4.4.2. Preshared Mode .....................................25
4.4.2.1. Commitment in Preshared Mode ..............25
4.4.2.2. Initiator Behavior in Preshared Mode ......26
4.4.2.3. Responder Behavior in Preshared Mode ......26
4.4.2.4. Shared Secret Calculation for
Preshared Mode ............................27
4.4.3. Multistream Mode ...................................28
4.4.3.1. Commitment in Multistream Mode ............29
4.4.3.2. Shared Secret Calculation for
Multistream Mode ..........................29
4.5. Key Derivations ...........................................31
4.5.1. The ZRTP Key Derivation Function ...................31
4.5.2. Deriving ZRTPSess Key and SAS in DH or
Preshared Modes ....................................32
4.5.3. Deriving the Rest of the Keys from s0 ..............33
4.6. Confirmation ..............................................35
4.6.1. Updating the Cache of Shared Secrets ...............35
4.6.1.1. Cache Update Following a Cache Mismatch ...36
4.7. Termination ...............................................37
4.7.1. Termination via Error Message ......................37
4.7.2. Termination via GoClear Message ....................37
4.7.2.1. Key Destruction for GoClear Message .......39
4.7.3. Key Destruction at Termination .....................40
4.8. Random Number Generation ..................................40
4.9. ZID and Cache Operation ...................................41
4.9.1. Cacheless Implementations ..........................42
5. ZRTP Messages ..................................................42
5.1. ZRTP Message Formats ......................................44
5.1.1. Message Type Block .................................44
5.1.2. Hash Type Block ....................................45
5.1.2.1. Negotiated Hash and MAC Algorithm .........46
5.1.2.2. Implicit Hash and MAC Algorithm ...........47
5.1.3. Cipher Type Block ..................................47
5.1.4. Auth Tag Type Block ................................48
5.1.5. Key Agreement Type Block ...........................49
5.1.6. SAS Type Block .....................................51
5.1.7. Signature Type Block ...............................52
5.2. Hello Message .............................................53
5.3. HelloACK Message ..........................................56
5.4. Commit Message ............................................56
5.5. DHPart1 Message ...........................................60
5.6. DHPart2 Message ...........................................62
5.7. Confirm1 and Confirm2 Messages ............................63
5.8. Conf2ACK Message ..........................................66
5.9. Error Message .............................................66
5.10. ErrorACK Message .........................................68
5.11. GoClear Message ..........................................68
5.12. ClearACK Message .........................................69
5.13. SASrelay Message .........................................69
5.14. RelayACK Message .........................................72
5.15. Ping Message .............................................72
5.16. PingACK Message ..........................................73
6. Retransmissions ................................................74
7. Short Authentication String ....................................77
7.1. SAS Verified Flag .........................................78
7.2. Signing the SAS ...........................................79
7.2.1. OpenPGP Signatures .................................81
7.2.2. ECDSA Signatures with X.509v3 Certs ................82
7.2.3. Signing the SAS without a PKI ......................83
7.3. Relaying the SAS through a PBX ............................84
7.3.1. PBX Enrollment and the PBX Enrollment Flag .........87
8. Signaling Interactions .........................................89
8.1. Binding the Media Stream to the Signaling Layer
via the Hello Hash ........................................90
8.1.1. Integrity-Protected Signaling Enables
Integrity-Protected DH Exchange ....................92
8.2. Deriving the SRTP Secret (srtps) from the
Signaling Layer ...........................................93
8.3. Codec Selection for Secure Media ..........................94
9. False ZRTP Packet Rejection ....................................95
10. Intermediary ZRTP Devices .....................................97
11. The ZRTP Disclosure Flag ......................................98
11.1. Guidelines on Proper Implementation of the
Disclosure Flag .........................................100
12. Mapping between ZID and AOR (SIP URI) ........................100
13. IANA Considerations ..........................................102
14. Media Security Requirements ..................................102
15. Security Considerations ......................................104
15.1. Self-Healing Key Continuity Feature .....................107
16. Acknowledgments ..............................................108
17. References ...................................................109
17.1. Normative References ....................................109
17.2. Informative References ..................................111
1. Introduction โจ
okay bestie, so ZRTP is a key agreement protocol ๐ that does a Diffie-Hellman key exchange during call setup, right in the media path. It rides on the same port as the Real-time Transport Protocol (RTP) [RFC3550] media stream, and that stream was already set up by a signaling protocol like Session Initiation Protocol (SIP) [RFC3261]. The shared secret it creates gets turned into keys and salt for a Secure RTP (SRTP) [RFC3711] session ๐. ZRTP borrows ideas from [PGPfone], and there's a reference implementation over in [Zfone] if you want to poke at it ๐.
ZRTP has some seriously iconic cryptographic features that a lot of other media encryption approaches just don't have ๐ . It uses a public key algorithm, but it doesn't need a public key infrastructure (PKI) ๐ โโ๏ธ. In fact, it doesn't use persistent public keys at all. It uses ephemeral Diffie-Hellman (DH) with hash commitment, and it can detect man-in-the-middle (MiTM) attacks by showing a short authentication string (SAS) that the users read out and compare verbally over the phone ๐. It also has Perfect Forward Secrecy, which means the keys get destroyed at the end of the call. So nobody can go back later, once key material leaks, and mess with that old call ๐ฆ.
BUT even if the users are too lazy to bother with short authentication strings, ZRTP still gets reasonable authentication against a MiTM attack ๐ญ. That comes from a form of key continuity. It caches some key material to use in the next call, and that gets mixed in with the next call's DH shared secret. So it has key continuity properties that are analogous to Secure SHell (SSH) โจ. All of this happens without a PKI, key certification, trust models, certificate authorities, or the key management complexity that bedevils the email encryption world ๐. It also doesn't rely on SIP signaling for key management, and it doesn't rely on any servers at all. Everything is purely peer-to-peer over the RTP packet stream ๐.
ZRTP can be used and discovered without being declared or indicated in the signaling path. That gives you a best effort SRTP capability ๐. It also keeps implementations simpler and minimizes interdependency between the signaling and media layers ๐ง. However, when ZRTP is indicated in the signaling through the zrtp-hash SDP attribute, it gets extra useful properties ๐. Sending a hash of the ZRTP Hello message in the signaling gives you a useful binding between the signaling and media paths, which is explained in Section 8.1 ๐. When that's done through a signaling path with end-to-end integrity protection, the DH exchange is automatically protected from a MiTM attack, which is explained in Section 8.1.1 ๐ก๏ธ.
ZRTP is designed for unicast media sessions that have a voice media stream ๐ค. For multiparty secure conferencing, separate ZRTP sessions may be negotiated between each party and the conference bridge. For sessions without a voice media stream, MiTM protection may come from the mechanisms in Sections 8.1.1 or 7.2. In terms of the RTP topologies defined in [RFC5117], ZRTP is designed for Point-to-Point topologies only ๐.
2. Terminology
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in [RFC2119] ๐โจ.
In this document, a "call" is synonymous with a "session" ๐๐ธ.
3. Overview
This section gives you a description of how ZRTP works ๐ง. This description is non-normative, but it's included to help you build understanding of the protocol ๐ก.
ZRTP is negotiated the same way a conventional RTP session is negotiated, in an offer/answer exchange using the standard RTP/AVP profile ๐ค. The ZRTP protocol begins after two endpoints have used a signaling protocol, like SIP, and are ready to exchange media ๐ถ. If Interactive Connectivity Establishment (ICE) [RFC5245] is being used, ZRTP begins after ICE has finished its connectivity checks โ .
ZRTP is multiplexed on the same ports as RTP ๐. It uses a unique header that makes it clearly differentiable from RTP or Session Traversal Utilities for NAT (STUN) ๐ฆ.
ZRTP support can be discovered in the signaling path by the presence of a ZRTP SDP attribute ๐. But even when that doesn't show up in the signaling, an endpoint can still send ZRTP Hello messages to see if a response comes back ๐. If no response is received, no more ZRTP messages will be sent during this session. This is safe because ZRTP was designed to look clearly different from RTP, and to have a structure similar to STUN packets. Those STUN packets can be received during an ICE exchange, sometimes by endpoints that don't support ZRTP ๐.
Both ZRTP endpoints begin the ZRTP exchange by sending a ZRTP Hello message to the other endpoint ๐. The Hello message is there to confirm that the endpoint supports the protocol, and to see which algorithms the two ZRTP endpoints have in common ๐.
The Hello message contains the SRTP configuration options and the ZID. Each instance of ZRTP has a unique 96-bit random ZRTP ID, or ZID, that is generated once at installation time ๐. ZIDs are discovered during the Hello message exchange. The received ZID is then used to look up retained shared secrets from previous ZRTP sessions with that endpoint ๐๏ธ.
A response to a ZRTP Hello message is a ZRTP HelloACK message. The HelloACK message just acknowledges receipt of the Hello ๐ . Since RTP commonly uses best effort UDP transport, ZRTP has retransmission timers in case datagrams get lost ๐ฐ๏ธ. There are two timers, and both use exponential backoff mechanisms. One timer handles retransmissions of Hello messages. The other handles retransmissions of all other messages after a HelloACK has been received ๐.
If an integrity-protected signaling channel is available, a hash of the Hello message can be sent. This allows rejection of false ZRTP Hello messages injected by an attacker ๐ซ๐พ.
Hello and other ZRTP messages also contain a hash image that's used to link the messages together. This allows rejection of false ZRTP messages injected during an exchange ๐๐.
3.1. Key Agreement Modes
After both endpoints exchange Hello and HelloACK messages, the key agreement exchange can begin with the ZRTP Commit message ๐. ZRTP supports a number of key agreement modes, including both Diffie-Hellman and non-Diffie-Hellman modes, as described in the following sections ๐.
The Commit message may be sent immediately after both endpoints have completed the Hello/HelloACK discovery handshake. Or it may be deferred until later in the call, after the participants have some unencrypted conversation ๐ฌ. The Commit message may be manually activated by a user interface element, like a GO SECURE button, which becomes enabled after the Hello/HelloACK discovery phase ๐. This copies the user experience of a number of secure phones in the Public Switched Telephone Network (PSTN) world [comsec] ๐ฑ. That said, it is expected that most simple ZRTP user agents will skip those buttons and go straight to secure mode by sending a Commit message right after the Hello/HelloACK handshake โก.
3.1.1. Diffie-Hellman Mode Overview
An example ZRTP call flow is shown in Figure 1 ๐. The order of the Hello/HelloACK exchanges in F1/F2 and F3/F4 may be reversed. That means either Alice or Bob might send the first Hello message ๐. The endpoint that sends the Commit message is considered the initiator of the ZRTP session, and it drives the key agreement exchange ๐. The Diffie-Hellman public values are exchanged in the DHPart1 and DHPart2 messages ๐. Then the SRTP keys and salts are calculated ๐งฎ.
The initiator needs to generate its ephemeral key pair before sending the Commit, and the responder generates its key pair before sending DHPart1 ๐๏ธ.
Alice Bob
| |
| Alice and Bob establish a media session. |
| They initiate ZRTP on media ports |
| |
| F1 Hello (version, options, Alice's ZID) |
|-------------------------------------------------->|
| HelloACK F2 |
|<--------------------------------------------------|
| Hello (version, options, Bob's ZID) F3 |
|<--------------------------------------------------|
| F4 HelloACK |
|-------------------------------------------------->|
| |
| Bob acts as the initiator. |
| |
| Commit (Bob's ZID, options, hash value) F5 |
|<--------------------------------------------------|
| F6 DHPart1 (pvr, shared secret hashes) |
|-------------------------------------------------->|
| DHPart2 (pvi, shared secret hashes) F7 |
|<--------------------------------------------------|
| |
| Alice and Bob generate SRTP session key. |
| |
| F8 Confirm1 (MAC, D,A,V,E flags, sig) |
|-------------------------------------------------->|
| Confirm2 (MAC, D,A,V,E flags, sig) F9 |
|<--------------------------------------------------|
| F10 Conf2ACK |
|-------------------------------------------------->|
| SRTP begins |
|<=================================================>|
| |
Figure 1: Establishment of an SRTP Session Using ZRTP
ZRTP authentication uses a Short Authentication String (SAS), which is ideally displayed for the human user ๐. Alternatively, the SAS can be authenticated by exchanging an optional digital signature (sig) over the SAS in the Confirm1 or Confirm2 messages (described in Section 7.2) โ๏ธ.
The ZRTP Confirm1 and Confirm2 messages are sent for a bunch of reasons, and not the least of them is that they confirm all the key agreement calculations were successful, so the encryption will actually work ๐. They also carry other information, like the Disclosure flag (D), the Allow Clear flag (A), the SAS Verified flag (V), and the Private Branch Exchange (PBX) Enrollment flag (E) ๐ข. All of those flags are encrypted, to shield them from a passive observer ๐ต๏ธโโ๏ธ๐.
3.1.2. Preshared Mode Overview โจ
okay so in Preshared mode ๐ฆ, endpoints can skip the DH calculation if they already have a shared secret from a previous ZRTP session. It's indicated in the Commit message and the call flow ends up looking just like Multistream mode ๐. The big difference is the starting keying material: Preshared mode uses a previously cached shared secret, rs1, instead of an active ZRTP Session key ๐.
this mode is lowkey perfect for slow processor endpoints, because you don't have to do a DH calculation every single session ๐ข. It's also a great way to rapidly re-establish a session that just got torn down or interrupted, with no need to do another DH calculation โก.
Preshared mode has forward secrecy properties ๐. If an opponent captures a phone's cache, the cached shared secrets can't be used to recover earlier encrypted calls, because the secrets get replaced with new ones in each new call, same as in DH mode ๐ . BUT, and this is the tea ๐ต, a passive wiretapper sitting in the media path can use the captured secrets to decrypt the next call, as long as that next call is in Preshared mode ๐ณ. In DH mode, the wiretapper would need to be an active MiTM to pull that off ๐ต๏ธโโ๏ธ. The silver lining: if the wiretapper misses the next call, they can't wiretap any further calls ๐. So it still keeps most of the self-healing properties (Section 15.1) of key continuity that DH mode gives you ๐ฉนโจ.
3.1.3. Multistream Mode Overview โจ
Multistream mode is another key agreement method, used when two endpoints already have an established SRTP media stream between them with an active ZRTP Session key ๐. ZRTP can derive multiple SRTP keys from one single DH exchange ๐ง . For example, an established secure voice call that adds a video stream uses Multistream mode to start the video stream quickly, without a second DH exchange ๐น๐.
When Multistream mode is indicated in the Commit message, the call flow looks similar to Figure 1, except no DH calculation happens on either endpoint and the DHPart1 and DHPart2 messages get omitted ๐. The Confirm1, Confirm2, and Conf2ACK messages are still sent ๐จ. Since the cache isn't affected during this mode, you can run multiple Multistream ZRTP exchanges in parallel between the same two endpoints ๐ง.
When you add more media streams to an existing call, only Multistream mode is used ๐. Only one DH operation happens, and it's just for the first media stream ๐.
4. Protocol Description ๐
This section kicks off the normative description of the protocol ๐.
ZRTP MUST be multiplexed on the same ports as the RTP media packets ๐ฅ.
To support best effort encryption from the Media Security Requirements [RFC5479], ZRTP uses normal RTP/AVP profile (AVP) media lines in the initial offer/answer exchange ๐. The ZRTP SDP attribute a=zrtp-hash, defined in Section 8, SHOULD be used in all offers and answers to show that the endpoint supports the ZRTP protocol ๐ซถ.
ZRTP can be utilized by endpoints that don't have a common signaling protocol but both support SRTP and rely on a gateway for conversion. So it's not always possible for the signaling protocol to relay the zrtp-hash the way SIP can ๐ธ.
The Secure RTP/AVP (SAVP) profile MAY be used in subsequent offer/answer exchanges after a successful ZRTP exchange has resulted in an SRTP session, or if it's known that the other endpoint supports this profile ๐ฆ. Other profiles MAY also be used ๐ .
The RTP/SAVP profile has caused failures when negotiating best effort SRTP, because of the limitations on negotiating profiles using SDP ๐ญ. That's why ZRTP supports the RTP/AVP profile and includes its own discovery mechanisms ๐งโโ๏ธ.
In all key agreement modes, the initiator SHOULD NOT send RTP media after sending the Commit message ๐ โโ๏ธ. It MUST NOT send SRTP media before receiving either the Conf2ACK or the first SRTP media (with a valid SRTP auth tag) from the responder ๐. The responder SHOULD NOT send RTP media after receiving the Commit message, and MUST NOT send SRTP media before receiving the Confirm2 message ๐.
4.1. Discovery ๐
During the ZRTP discovery phase, a ZRTP endpoint finds out whether the other endpoint supports ZRTP, plus the algorithms and options it supports ๐ต๏ธ. This info travels in a Hello message, described in Section 5.2 ๐ฌ.
ZRTP endpoints SHOULD include the SDP attribute a=zrtp-hash in offers and answers, as defined in Section 8 โจ.
The Hello message includes the ZRTP version, Hash Type, Cipher Type, SRTP authentication tag type, Key Agreement Type, and Short Authentication String (SAS) algorithms that are supported ๐ง. It also has a hash image, described in Section 9 ๐ผ๏ธ. Each endpoint also sends and discovers ZIDs ๐. The received ZID gets used later in the protocol as an index into a cache of shared secrets that were negotiated earlier and kept between the two parties ๐๏ธ๐.
A Hello message can be sent at any time, but it's usually sent at the start of an RTP session to check whether the other endpoint supports ZRTP and whether the SRTP implementations are compatible ๐ท. A Hello message gets retransmitted using timer T1 and an exponential backoff mechanism (detailed in Section 6) until a HelloACK message or a Commit message is received โฐ๐.
The use of the a=zrtp-hash SDP attribute to authenticate the Hello message is described in Section 8.1 ๐.
If a Hello message, or any other ZRTP message, shows there's a synchronization source (SSRC) collision, an Error message (Section 5.9) MUST be sent with the Error Code indicating SSRC collision, and the ZRTP negotiation MUST be terminated ๐จ๐. Both endpoints SHOULD follow the procedures of RFC 3550, Section 8.2 [RFC3550], to resolve this condition ๐ ๏ธ. If it gets resolved, a new ZRTP secure session SHOULD be negotiated ๐.
4.1.1. Protocol Version Negotiation ๐
This spec defines ZRTP version 1.10 ๐ท๏ธ. New versions of ZRTP may show up in the future, so this section defines a protocol version negotiation too ๐ฎ.
Each party declares which version of the ZRTP protocol they support, via the version field in the Hello message (Section 5.2) ๐ฃ. If both parties have the same version number in their Hello messages, they can move on with the rest of the protocol ๐ค. To help both parties get to that version agreement in their Hello messages, ZRTP should use information from the signaling layer, if it's available ๐ก. If a ZRTP endpoint supports more than one version of the protocol, it SHOULD declare all of them in a list of SIP SDP a=zrtp-hash attributes (defined in Section 8), with separate hashes and separate ZRTP version numbers in each item on the list ๐๐
.
Both parties should check the list of ZRTP version numbers the other party sent in the SIP SDP a=zrtp-hash attributes ๐. Both parties SHOULD pick the highest version number that shows up in both parties' lists of a=zrtp-hash version numbers, and use that version for their Hello messages ๐. If both parties use SIP signaling this way, their initial Hello messages will have the same ZRTP version number, as long as they both have at least one supported protocol version in common ๐ฅฐ. Before the ZRTP key agreement can proceed, an endpoint MUST have sent and received Hellos with the same protocol version ๐.
Using the signaling layer to negotiate the protocol version number is the best move ๐ซ. The catch: the a=zrtp-hash SDP attribute isn't always in the SIP packet, as explained in Section 8.1 ๐. If there's no guidance from the signaling layer, an endpoint MUST send the highest supported version in its initial Hello messages ๐. If the two parties send different protocol version numbers in their Hello messages, they can agree on a common version, if one exists ๐ค. They go through the following rules over and over until both have matching version fields in their Hello messages and the key agreement can proceed ๐:
- If an endpoint receives a Hello message with an unsupported version number that's higher than the endpoint's current Hello message version, the received Hello message MUST be ignored ๐. The endpoint keeps retransmitting Hello messages on the standard retry schedule (Section 6) โณ.
- If an endpoint receives a Hello message with a version number lower than the endpoint's current Hello message, and the endpoint supports a version that's less than or equal to the received version number, the endpoint MUST stop retransmitting the old version number and MUST start sending a Hello message with the highest supported version number that's less than or equal to the received version number ๐โจ.
- If an endpoint receives a Hello message with an unsupported version number that's lower than the endpoint's current Hello message, the endpoint MUST send an Error message (Section 5.9) indicating failure to support this ZRTP version ๐ โโ๏ธ.
The above comparisons get iterated until the version numbers match, or until it exits on a failure to match ๐.
Here's the example, verbatim ๐:
For example, assume that Alice supports protocol versions 1.10 and
2.00, and Bob supports versions 1.10 and 1.20. Alice initially
sends a Hello with version 2.00, and Bob initially sends a Hello
with version 1.20. Bob ignores Alice's 2.00 Hello and continues
to send his 1.20 Hellos. Alice detects that Bob does not support
2.00 and she stops sending her 2.00 Hellos and starts sending a
stream of 1.10 Hellos. Bob sees the 1.10 Hello from Alice and
stops sending his 1.20 Hellos and switches to sending 1.10 Hellos.
At that point, they have converged on using version 1.10 and the
protocol proceeds on that basis.
When comparing protocol versions, a ZRTP endpoint MUST include only the first three octets of the version field in the comparison ๐ข. The final octet is ignored, because it isn't significant for interoperability ๐คทโโ๏ธ. For example, "1.1 ", "1.10", "1.11", or "1.1a" are all regarded as a version match, because they'd all be interoperable versions ๐.
Changes in protocol version numbers are expected to be infrequent after version 1.10 ๐. Supporting multiple versions adds code complexity and may introduce security weaknesses in the implementation ๐จ. The old adage about keeping it simple applies especially to implementing security protocols ๐ง ๐ . Endpoints SHOULD NOT support protocol versions earlier than version 1.10 ๐ โโ๏ธ๐.
4.1.2. Algorithm Negotiation
okay so this part is about how the two parties agree on the same DH key size and algorithm ๐ before a Commit message even gets sent, and they do it in a totally deterministic way, so both sides land on the exact same answer with zero vibes-based guessing โจ
Each Hello message lists the algorithms in order of preference for that ZRTP endpoint ๐ . Then each endpoint crosses out anything that isn't on the other side's list too, so you end up with a shortlist of algorithms in common. That shortlist might or might not be in the same order as the other endpoint's, which is very plot-twisty ๐ Next, each endpoint compares the first item on its own shortlist with the first item on the other endpoint's shortlist, and it SHOULD choose the faster of the two. Here's the example:
- Alice's full list:
DH2k, DH3k, EC25 - Bob's full list:
EC38, EC25, DH3k - Alice's intersecting list:
DH3k, EC25 - Bob's intersecting list:
EC25, DH3k - Alice's first choice is
DH3k, and Bob's first choice isEC25. - Thus, both parties choose
EC25(ECDH-256) because it's faster ๐ฅ
To decide which DH algorithm is faster, there's a ranking from fastest to slowest: DH-2048, ECDH-256, DH-3072, ECDH-384, ECDH-521. All of these are defined in Section 5.1.5 ๐ฆ
If both endpoints follow this method, they may each start their DH calculations as soon as they receive the Hello message, and there will be no need for either endpoint to discard their DH calculation if the other endpoint becomes the initiator. main character energy for sure โจ
This method is only used to negotiate the DH key size ๐ธ For the rest of the algorithm choices, it's simply whatever the initiator picks from the algorithms in common. Also, the DH key size influences the Hash Type and the size of the symmetric cipher key, as explained in Section 5.1.5.
Unfavorable choices will never be made by this method, because each endpoint leaves off of its list any choices that are too slow or not secure enough to meet its security policy. iconic, honestly ๐
4.2. Commit Contention
After both parties have received compatible Hello messages, a Commit message (Section 5.4) can be sent to kick off the ZRTP key exchange ๐ The endpoint that sends the Commit is called the initiator, and the receiver of the Commit is called the responder.
If both sides send Commit messages to start a secure session at the same time, there are some rules to break the tie ๐ฅ
- If one Commit is for a DH mode while the other is for Preshared mode, then the Preshared Commit MUST be discarded and the DH Commit proceeds.
- If the two Commits are both Preshared mode, and one party has set the MiTM (M) flag in the Hello message and the other has not, the Commit message from the party who set the (M) flag MUST be discarded, and the one who has not set the (M) flag becomes the initiator, regardless of the nonce values. In other words, for Preshared mode, the phone is the initiator and the PBX is the responder ๐
- If the two Commits are either both DH modes or both non-DH modes, then the Commit message with the lowest
hvi(hash value of initiator) value (for DH Commits), or lowest nonce value (for non-DH Commits), MUST be discarded and the other side is the initiator, and the protocol proceeds with the initiator's Commit. The twohvior nonce values are compared as large unsigned integers in network byte order. the math is giving precision ๐งฎ
If one Commit is for Multistream mode while the other is for non-Multistream (DH or Preshared) mode, a software error has occurred and the ZRTP negotiation should be terminated ๐ This should never occur because of the constraints on Multistream mode described in Section 4.4.3.
In the event that Commit messages are sent by both ZRTP endpoints at the same time, but are received in different media streams, the same resolution rules apply as if they were received on the same stream. The media stream where the Commit was received or sent will proceed through the ZRTP exchange, while the media stream with the discarded Commit has to wait for the other ZRTP exchange to finish ๐
If a commit contention forces a DH Commit message to be discarded, the responder's DH public value should only be discarded if it does not match the initiator's DH key size. This won't happen if both endpoints choose a common key size via the method described in Section 4.1.2 โจ
4.3. Matching Shared Secret Determination
okay this section is where things get juicy ๐ It explains how ZRTP endpoints generate and/or use the set of shared secrets s1, auxsecret, and pbxsecret through the exchange of the DHPart1 and DHPart2 messages. It does not cover the Diffie-Hellman calculations, though. It only covers how the two parties figure out whether they already have shared secrets in common in their caches ๐
Each ZRTP endpoint keeps a long-term cache of shared secrets it has previously negotiated with the other party ๐๏ธ The ZID of the other party, received in the other party's Hello message, is used as an index into this cache to find the set of shared secrets, if any exist. This cache entry may contain previously retained shared secrets, rs1 and rs2, which give ZRTP its key continuity features. If the other party is a PBX, the cache may also contain a trusted MiTM PBX shared secret, called pbxsecret, defined in Section 7.3.1.
The DHPart1 and DHPart2 messages contain a list of hashes of these shared secrets. This lets the two endpoints compare those hashes with what's in their caches, so they can detect whether the two sides share any secrets that can be used in the calculation of the session key ๐ How this shared secret cache gets used is described in Section 4.9.
If no secret of a given type is available, a random value is generated and used for that secret, which guarantees a mismatch in the hash comparisons in the DHPart1 and DHPart2 messages. This stops an eavesdropper from knowing which types of shared secrets are available between the endpoints ๐๐
Section 4.3.1 refers to the auxiliary shared secret auxsecret. The auxsecret shared secret may be defined by the VoIP user agent out-of-band from the ZRTP protocol. In some cases, it may be provided by the signaling layer as srtps, which is defined in Section 8.2. If it is not provided by the signaling layer, the auxsecret shared secret may be manually provisioned in other application-specific ways that are out of band, such as computed from a hashed pass phrase by prior agreement between the two parties or supplied by a hardware token. Or, it may be a family key used by an institution to which the two parties both belong. It's a generalized mechanism for providing a shared secret that's agreed to between the two parties, out of scope of the ZRTP protocol. It is expected that most typical ZRTP endpoints will rarely use auxsecret (so it's kind of a niche little helper) ๐ท
For both the initiator and the responder, the shared secrets s1, s2, and s3 will be calculated so that they can all be used later to calculate s0 in Section 4.4.1.4. Here's how both parties calculate s1, s2, and s3 ๐ง
The shared secret s1 will be either the initiator's rs1 or the initiator's rs2, depending on which of them can be found in the responder's cache. If the initiator's rs1 matches the responder's rs1 or rs2, then s1 MUST be set to the initiator's rs1. If and only if that match fails, then if the initiator's rs2 matches the responder's rs1 or rs2, then s1 MUST be set to the initiator's rs2. If that match also fails, then s1 MUST be set to null. The complexity of the s1 calculation is there to recover from any loss of cache sync from an earlier aborted session, due to the Two Generals' Problem [Byzantine] ๐ญ
The shared secret s2 MUST be set to the value of auxsecret if and only if both parties have matching values for auxsecret, as determined by comparing the hashes of auxsecret sent in the DH messages. If they don't match, s2 MUST be set to null.
The shared secret s3 MUST be set to the value of pbxsecret if and only if both parties have matching values for pbxsecret, as determined by comparing the hashes of pbxsecret sent in the DH messages. If they don't match, s3 MUST be set to null.
If s1, s2, or s3 have null values, they are assumed to have a zero length for the purposes of hashing them later during the s0 calculation in Section 4.4.1.4.
The comparison of hashes of rs1, rs2, auxsecret, and pbxsecret is described in Section 4.3.1 ๐
4.3.1. Calculation and Comparison of Hashes of Shared Secrets
Both parties calculate a set of non-invertible hashes (implemented via the MAC defined in Section 5.1.2.1) of shared secrets that may be present in each of their caches ๐ These hashes are truncated to the leftmost 64 bits:
rs1IDr = MAC(rs1, "Responder")
rs2IDr = MAC(rs2, "Responder")
auxsecretIDr = MAC(auxsecret, Responder's H3)
pbxsecretIDr = MAC(pbxsecret, "Responder")
rs1IDi = MAC(rs1, "Initiator")
rs2IDi = MAC(rs2, "Initiator")
auxsecretIDi = MAC(auxsecret, Initiator's H3)
pbxsecretIDi = MAC(pbxsecret, "Initiator")
The responder sends rs1IDr, rs2IDr, auxsecretIDr, and pbxsecretIDr in the DHPart1 message. The initiator sends rs1IDi, rs2IDi, auxsecretIDi, and pbxsecretIDi in the DHPart2 message ๐จ
The responder uses the locally computed rs1IDi, rs2IDi, auxsecretIDi, and pbxsecretIDi to compare against the corresponding fields in the received DHPart2 message. The initiator uses the locally computed rs1IDr, rs2IDr, auxsecretIDr, and pbxsecretIDr to compare against the corresponding fields in the received DHPart1 message ๐ช
From these comparisons, s1, s2, and s3 are calculated per the methods described in Section 4.3. The secrets that match are kept, while the secrets that don't match get replaced with a null, which is assumed to have a zero length for the purposes of hashing them later. The resulting s1, s2, and s3 values are used later to calculate s0 in Section 4.4.1.4 โจ
For example, say two ZRTP endpoints share rs1 and pbxsecret (defined in Section 7.3.1). During the comparison, rs1ID and pbxsecretID will match, but auxsecretID won't. As a result, s1 = rs1, s2 will be null, and s3 = pbxsecret bestie, the cache-matching logic is literally so clean ๐๐
4.3.2. Handling a Shared Secret Cache Mismatch โจ
okay bestie, a shared secret cache mismatch is what we call it when we expected a match because rs1 is already sitting in our local cache, but when we work out s1 using the method from Section 4.3, we get a null value. ๐
If one party has a cached shared secret and the other doesn't, that means one of two things is going on. Either there's a MiTM attack ๐ or one of the legit parties lost their cached shared secret by some mishap. Maybe they accidentally deleted their cache, or it got lost or messed up when they restored their disk from an earlier backup copy. ๐พ The party with the surviving cache entry can easily detect that a cache mismatch happened, because they expect their own cached secret to match the other party's, and it doesn't. It's also possible for both parties to notice this if both of them have surviving cached secrets that fell out of sync, maybe because one of them restored from a disk backup. ๐ซถ
If either party discovers a cache mismatch, the user agent who makes this discovery must treat this as a possible security event and MUST alert their own user ๐จ that there's a heightened risk of a MiTM attack, and that the user should verbally compare the SAS with the other party to make sure no MiTM attack has occurred. If a cache mismatch is detected and it's not possible to compare the SAS, either because the user interface doesn't support it or because one or both endpoints are unmanned devices, and no other SAS comparison mechanism is available, the session MAY be terminated.
The session need not be terminated on a cache mismatch event if:
- the mechanism described in Section 8.1.1 is available, which allows authentication of the DH exchange without human assistance, or
- any mechanism is available to determine if the SAS matches. This would require either circumstances that allow human verbal comparisons of the SAS or by use of the OPTIONAL digital signature feature on the SAS hash, as described in Section 7.2.
Even if the user interface doesn't permit an SAS comparison, the human user MUST be warned and may elect to proceed with the call at their own risk. ๐ฌ
If and only if a cache mismatch event occurs, the cache update mechanism in Section 4.6.1 is affected, requiring the user to verify the SAS before the cache is updated. The user will thus be alerted of this security condition on every call until the SAS is verified.
This is described in Section 4.6.1.1.
Here's a non-normative example of a cache-mismatch alert message from a ZRTP user agent (specifically, [Zfone]), designed for a desktop PC graphical user interface environment. It is by no means required that the alert be this detailed:
We expected the other party to have a shared secret cached from a
previous call, but they don't have it. This may mean your partner
simply lost his cache of shared secrets, but it could also mean
someone is trying to wiretap you. To resolve this question you
must check the authentication string with your partner. If it
doesn't match, it indicates the presence of a wiretapper.
And if the alert is read out by a robot voice instead of shown in a GUI, brevity may be more important ๐ค:
Something's wrong. You must check the authentication string with
your partner. If it doesn't match, it indicates the presence of a
wiretapper.
A mismatch of auxsecret is handled differently than a mismatch of rs1. An auxsecret mismatch is defined to mean that auxsecret exists locally, but we computed a null value for s2 (per the method described in Section 4.3). This mismatch should be made visible to whichever user has auxsecret defined. The mismatch should be made visible to both users if they both have auxsecret defined but they fail to match. The severity of the user notification is implementation dependent. Aborting the session is not required. If auxsecret matches, it should not excuse a mismatch of rs1, which still requires a strong warning to the user. โ ๏ธ
4.4. DH and Non-DH Key Agreements ๐
The next step is generating a secret for deriving SRTP keying material. ZRTP uses Diffie-Hellman and two non-Diffie-Hellman modes, which are described in the following subsections.
4.4.1. Diffie-Hellman Mode ๐
The whole point of the Diffie-Hellman exchange (either Finite Field Diffie-Hellman or Elliptic Curve Diffie-Hellman) is for the two ZRTP endpoints to generate a brand new shared secret, s0. โจ On top of that, the endpoints figure out if they have any cached or previously stored shared secrets in common, and they use those as part of the calculation of the session keys.
Because the DH exchange affects the state of the retained shared secret cache, only one in-process ZRTP DH exchange may occur at a time between two ZRTP endpoints. Otherwise, race conditions and cache integrity problems will result. ๐ When multiple media streams are established in parallel between the same pair of ZRTP endpoints (determined by the ZIDs in the Hello messages), only one can be processed. Once that exchange completes with Confirm2 and Conf2ACK messages, another ZRTP DH exchange can begin. This constraint doesn't apply when Multistream mode key agreement is used, since the cached shared secrets aren't affected.
4.4.1.1. Hash Commitment in Diffie-Hellman Mode ๐ง
From the intersection of the algorithms in the sent and received Hello messages, the initiator chooses a hash, cipher, auth tag, Key Agreement Type, and SAS Type to be used.
A Diffie-Hellman mode is selected by setting the Key Agreement Type in the Commit to one of the DH or Elliptic Curve Diffie-Hellman (ECDH) values from the table in Section 5.1.5. In this mode, the key agreement begins with the initiator choosing a fresh random Diffie-Hellman (DH) secret value (svi) based on the chosen Key Agreement Type value, and then computing the public value. (Note that to speed up processing, this computation can be done in advance.) For guidance on generating random numbers, see Section 4.8.
For Finite Field Diffie-Hellman, the value for the DH generator g, the DH prime p, and the length of the DH secret value, svi, are defined in Section 5.1.5.
pvi = g^svi mod p
Here, g and p are determined by the Key Agreement Type value. The DH public value pvi value is formatted as a big-endian octet string and fixed to the bit-length of the DH prime. Leading zeros MUST NOT be truncated. ๐
โโ๏ธ
For Elliptic Curve DH, pvi is calculated and formatted according to the ECDH specification in Section 5.1.5, which refers in detail to certain sections of NIST SP 800-56A [NIST-SP800-56A].
The hash commitment is performed by the initiator of the ZRTP exchange. The hash value of the initiator, hvi, includes a hash of the entire DHPart2 message as shown in Figure 9 (which includes the Diffie-Hellman public value, pvi), and the responder's Hello message (where '||' means concatenation). The hvi hash is truncated to 256 bits:
hvi = hash(initiator's DHPart2 message ||
responder's Hello message)
Note that the Hello message includes the fields shown in Figure 3.
The information from the responder's Hello message is included in the hash calculation to prevent a bid-down attack by modification of the responder's Hello message. ๐ก๏ธ
The initiator sends the hvi in the Commit message.
The use of hash commitment in the DH exchange constrains the attacker to only one guess to generate the correct Short Authentication String (SAS) (Section 7) in his attack, which means the SAS can be quite short. A 16-bit SAS, for example, gives the attacker only one chance out of 65536 of not being detected. ๐ฏ Without this hash commitment feature, a MiTM attacker would acquire both the pvi and pvr public values from the two parties before having to choose his own two DH public values for his MiTM attack. He could then use that information to quickly perform a bunch of trial DH calculations for both sides until he finds two with a matching SAS. To raise the cost of this birthday attack, the SAS would have to be much longer. The Short Authentication String would have to become a Long Authentication String, which would be unacceptable to the user. ๐ฉ A hash commitment precludes this attack by forcing the MiTM to choose his own two DH public values before learning the public values of either of the two parties. ๐
4.4.1.2. Responder Behavior in Diffie-Hellman Mode ๐
Upon receipt of the Commit message, the responder generates its own fresh random DH secret value, svr, and computes the public value. (Note that to speed up processing, this computation can be done in advance, with no need to discard it if both endpoints chose the same algorithm via Section 4.1.2.) For guidance on random number generation, see Section 4.8.
For Finite Field Diffie-Hellman, the value for the DH generator g, the DH prime p, and the length of the DH secret value, svr, are defined in Section 5.1.5.
pvr = g^svr mod p
The pvr value is formatted as a big-endian octet string, fixed to the bit-length of the DH prime. Leading zeros MUST NOT be truncated.
For Elliptic Curve DH, pvr is calculated and formatted according to the ECDH specification in Section 5.1.5, which refers in detail to certain sections of NIST SP 800-56A.
Upon receipt of the DHPart2 message, the responder checks that the initiator's DH public value is not equal to 1 or p-1. An attacker might inject a false DHPart2 message with a value of 1 or p-1 for g^svi mod p, which would cause a disastrously weak final DH result to be computed. ๐ฑ If pvi is 1 or p-1, the user SHOULD be alerted of the attack and the protocol exchange MUST be terminated. Otherwise, the responder computes its own value for the hash commitment using the DH public value (pvi) received in the DHPart2 message and its own Hello message, and compares the result with the hvi received in the Commit message. If they are different, a MiTM attack is taking place and the user is alerted and the protocol exchange terminated. ๐ซ
The responder then calculates the Diffie-Hellman result:
DHResult = pvi^svr mod p
4.4.1.3. Initiator Behavior in Diffie-Hellman Mode ๐ธ
Upon receipt of the DHPart1 message, the initiator checks that the responder's DH public value is not equal to 1 or p-1. An attacker might inject a false DHPart1 message with a value of 1 or p-1 for g^svr mod p, which would cause a disastrously weak final DH result to be computed. ๐ If pvr is 1 or p-1, the user should be alerted of the attack and the protocol exchange MUST be terminated.
The initiator then sends a DHPart2 message containing the initiator's DH public value and the set of calculated shared secret IDs as defined in Section 4.3.1.
The initiator calculates the same Diffie-Hellman result using:
DHResult = pvr^svi mod p
4.4.1.4. Shared Secret Calculation for DH Mode โจ
okay so both parties calculate a hash of the ZRTP messages sent and received in the current exchange, in this exact order ๐
total_hash = hash(Hello of responder || Commit || DHPart1 ||
DHPart2)
ngl only the ZRTP messages (Figures 3, 5, 8, and 9) go into the total_hash, not the entire ZRTP packets ๐
For both the initiator and responder, the DHResult is a big-endian octet string that's always the same width as the DH prime, and leading zeros MUST NOT be truncated ๐ซถ For example, with a 3072-bit p, DHResult is 384 octets, with the first octet being the most significant. DHResult can also come from an ECDH calculation, which is covered in Section 5.1.5 ๐ฆ
Key | Size of
Agreement | DHResult
------------------------
DH-3072 | 384 octets
------------------------
DH-2048 | 256 octets
------------------------
ECDH P-256 | 32 octets
------------------------
ECDH P-384 | 48 octets
------------------------
The authors believe the final shared secret, s0, is calculated in line with the recommendations in Sections 5.8.1 and 6.1.2.1 of NIST SP 800-56A [NIST-SP800-56A] ๐ธ They do this by hashing together a bunch of items: the DHResult, the ZIDs of the initiator (ZIDi) and the responder (ZIDr), the total_hash, and the set of non-null shared secrets from Section 4.3.
In Section 5.8.1 of [NIST-SP800-56A], NIST wants certain parameters hashed together in a specific order: Z, AlgorithmID, PartyUInfo, PartyVInfo, SuppPubInfo, and SuppPrivInfo ๐ Here's how that maps to ours: our DHResult is Z, "ZRTP-HMAC-KDF" is AlgorithmID, our ZIDi and ZIDr are PartyUInfo and PartyVInfo, our total_hash is SuppPubInfo, and the three shared secrets s1, s2, and s3 are SuppPrivInfo. NIST also wants a 32-bit big-endian integer counter included each time the hash is computed. We set it to the fixed value of 1 because we only compute the hash once ๐ฅ NIST calls the final hash output DerivedKeyingMaterial, which is our s0 here.
s0 = hash(counter || DHResult || "ZRTP-HMAC-KDF" || ZIDi ||
ZIDr || total_hash || len(s1) || s1 || len(s2) ||
s2 || len(s3) || s3)
Quick heads up: the temporary values s1, s2, and s3 were calculated using the methods from Section 4.3. DHResult, s1, s2, and s3 MUST all be erased from memory right after they're used to calculate s0 ๐
The length of the DHResult field is implicitly agreed on by the negotiated DH prime size. The length of total_hash is implicitly set by the negotiated hash algorithm. All the explicit length fields, len(), in the hash above are 32-bit big-endian integers that give the length in octets of the field right after them. Some members of the shared secret set (s1, s2, and s3) may have a length of zero if they're null (not shared), and each one is still preceded by a 4-octet length field. For example, if s2 is null, len(s2) is 0x00000000, s2 itself is left out of the hash calculation, and len(s3) comes right after len(s2) ๐ง Including ZIDi and ZIDr might be redundant, since they're already implicitly part of the total_hash. We include them anyway so we follow NIST SP 800-56A. The fixed-length string "ZRTP-HMAC-KDF" (not null-terminated) tells you what the resulting s0 is for: it's the key derivation key for the ZRTP HMAC-based key derivation function (KDF) defined in Section 4.5.1 and used in Section 4.5.3 ๐
The authors believe ZRTP DH mode fully complies with two relevant NIST documents about key derivation ๐ First, Section 5.8.1 of [NIST-SP800-56A] calculates what NIST calls DerivedKeyingMaterial, which ZRTP calls s0. This s0 then acts as the key derivation key (NIST calls it KI) in the key derivation function described in Sections 5 and 5.1 of [NIST-SP800-108], and that's how all the rest of the subkeys ZRTP needs get derived. For ECDH mode, the authors also believe the s0 calculation complies with Section 3.1 of the National Security Agency's (NSA's) Suite B Implementer's Guide to NIST SP 800-56A [NSA-Suite-B-Guide-56A] โญ
The ZRTP key derivation function (KDF) (Section 4.5.1) requires a KDF Context field (following [NIST-SP800-108] guidelines), which should include the ZIDi, ZIDr, and a nonce value known to both parties. The total_hash counts as a nonce value, because its calculation included nonce material from the initiator's Commit message and the responder's Hello message ๐ฆ
KDF_Context = (ZIDi || ZIDr || total_hash)
At this point in DH mode, the two endpoints move on to deriving ZRTPSess and the rest of the keys in Section 4.5.2, now that there's a defined s0 ๐
4.4.2. Preshared Mode
Preshared mode can be used to generate SRTP keys and salts without a DH calculation. Instead, it relies on a shared secret left over from earlier DH calculations between the endpoints ๐
This key agreement mode is great for quickly re-establishing a secure session between two parties who recently started and ended a secure session that already did a DH key agreement. You skip another lengthy DH calculation, which can be really nice on slow processors in resource-limited environments ๐ธ Preshared mode MUST NOT be used for adding additional media streams to an existing call. Multistream mode MUST be used for that instead ๐
In the most severe resource-limited environments, Preshared mode may be useful with processors that can't do a DH calculation in an ergonomically acceptable time limit. Shared key material can be manually provisioned between two such endpoints in advance and still allow a limited subset of functionality. A "better than nothing" implementation like that would have to be regarded as non-compliant with the ZRTP specification, but it could still interoperate in Preshared (and, if applicable, Multistream) mode with a compliant ZRTP endpoint ๐ฅน
Because Preshared mode changes the state of the retained shared secret cache, only one in-process ZRTP Preshared exchange may happen at a time between two ZRTP endpoints. This rule is explained in more detail in Section 4.4.1 and applies for the same reasons as in DH mode ๐
Preshared mode is only included in this specification to meet the R-REUSE requirement in the Media Security Requirements [RFC5479] document. A series of preshared-keyed calls between two ZRTP endpoints should use a DH key exchange every so often. Preshared mode is only used if a cached shared secret was established in an earlier session by a DH exchange, as discussed in Section 4.9 ๐
4.4.2.1. Commitment in Preshared Mode
Preshared mode is selected by setting the Key Agreement Type to Preshared in the Commit message. This gives the same call flow as Multistream mode. The main difference is that Preshared mode uses a previously cached shared secret, rs1, as the initial keying material instead of an active ZRTP Session key, ZRTPSess โจ
Preshared mode depends on having a reliable shared secret in its cache. Before Preshared mode is used, the initial DH exchange that produced the shared secret SHOULD have used at least one of these anti-MiTM mechanisms: 1) a verbal comparison of the SAS, shown by the SAS Verified flag, or 2) an end-to-end integrity-protected delivery of the a=zrtp-hash in the signaling (Section 8.1.1), or 3) a digital signature on the sashash (Section 7.2) ๐ก๏ธ
4.4.2.2. Initiator Behavior in Preshared Mode
The Commit message (Figure 7) is sent by the initiator of the ZRTP exchange. From the algorithms the sent and received Hello messages have in common, the initiator picks a hash, cipher, auth tag, Key Agreement Type, and SAS Type to use ๐ฆ
To put together a Preshared commit, we must first build a temporary preshared_key. It's built from one of several possible combinations of cached key material, depending on what's available in the shared secret cache. If rs1 isn't available in the initiator's cache, then Preshared mode MUST NOT be used ๐
preshared_key = hash(len(rs1) || rs1 || len(auxsecret) || auxsecret ||
len(pbxsecret) || pbxsecret)
All the explicit length fields, len(), in the hash above are 32-bit big-endian integers that give the length in octets of the field right after them. Some members of the shared secret set (rs1, auxsecret, and pbxsecret) may have a length of zero if they're null (not available), and each one is preceded by a 4-octet length field. For example, if auxsecret is null, len(auxsecret) is 0x00000000, auxsecret itself is left out of the hash calculation, and len(pbxsecret) comes right after len(auxsecret) ๐ง
In place of hvi in the Commit message, the initiator inserts two smaller fields:
- A random nonce of length 4 words (16 octets).
- A
keyID = MAC(preshared_key, "Prsh")truncated to 64 bits.
Note: Since the nonce is used to calculate different SRTP key and salt pairs for each session, a duplication will result in the same key and salt being generated for the two sessions, which would have disastrous security consequences. ๐ญ๐จ
4.4.2.3. Responder Behavior in Preshared Mode ๐
okay so the responder uses the keyID it got to dig through its cache for matching key material ๐. it works out a preshared_key value and a keyID value with the same formula the initiator used, depending on what's actually in its local cache. if the keyID it calculates doesn't match the keyID in the Commit, it tries again with a different subset of shared keys. it drops auxsecret, pbxsecret, or both from the hash calculation and keeps going until it finds a matching preshared_key or runs out of options ๐. quick reminder: rs2 is not included in the process ๐
if it finds the right matching shared key material, it uses that to derive s0 and a new ZRTPSess key. this is covered in the next section on shared secret calculation, Section 4.4.2.4 โจ
if the responder figures out it doesn't have a cached shared secret from an earlier DH exchange, or it can't match the keyID hash from the initiator with any combo of its shared keys, it SHOULD respond with its own DH Commit message ๐. this flips the roles, so the responder becomes the initiator, because a DH Commit must always "trump" a Preshared Commit message, as described in Section 4.2 ๐. the key exchange then carries on in DH mode. BUT if a severely resource-limited responder doesn't have the compute to reply with a DH Commit in a reasonable time, it MAY respond with a ZRTP Error message (Section 5.9) saying that no shared secret is available ๐ฅบ
if both sides send Preshared Commit messages at the same time to start a secure session, the contention gets resolved, the initiator/responder roles are settled according to Section 4.2, and the protocol keeps going ๐ซถ
in Preshared mode, both the DHPart1 and DHPart2 messages are skipped ๐ . after the responder gets the Commit from the initiator, it sends the Confirm1 message once it has calculated this stream's SRTP keys, as described below ๐ธ
4.4.2.4. Shared Secret Calculation for Preshared Mode ๐ฆ
Preshared mode needs the s0 and ZRTPSess keys derived from the preshared_key, and this has to be done in a way that guarantees uniqueness for each session ๐. it's done by mixing nonce material from both parties: the explicit nonce in the initiator's Preshared Commit message (Figure 7) and the H3 field in the responder's Hello message (Figure 3). so both parties force the resulting shared secret to be unique for each session โจ
a hash of the received and sent ZRTP messages in the current ZRTP exchange for the current media stream gets calculated:
total_hash = hash(Hello of responder || Commit)
note that only the ZRTP messages (Figures 3 and 7), not the entire ZRTP packets, are included in the total_hash ๐ฆ
the ZRTP key derivation function (KDF) (Section 4.5.1) needs a KDF Context field (following [NIST-SP800-108] guidelines), which should include the ZIDi, ZIDr, and a nonce value known to both parties. the total_hash counts as a nonce value, since its computation included nonce material from the initiator's Commit message and the responder's Hello message ๐ง
KDF_Context = (ZIDi || ZIDr || total_hash)
the s0 key comes from the ZRTP key derivation function (Section 4.5.1) using preshared_key and the nonces implicitly included in the total_hash. the nonces also make sure KDF_Context is unique for each session, which is critical for security ๐ก๏ธ
s0 = KDF(preshared_key, "ZRTP PSK", KDF_Context,
negotiated hash length)
the preshared_key MUST be erased as soon as it has been used to calculate s0 ๐๏ธ
at this point in Preshared mode, the two endpoints move on to deriving ZRTPSess and the rest of the keys in Section 4.5.2, now that there's a defined s0 ๐
4.4.3. Multistream Mode ๐
the Multistream key agreement mode can generate SRTP keys and salts for extra media streams set up between a pair of endpoints ๐ถ. Multistream mode cannot be used unless there's already an active SRTP session established between the endpoints, which means a ZRTP Session key is active. that ZRTP Session key can generate keys and salts without doing another DH calculation โจ. in this mode, the retained shared secret cache isn't used or updated, so multiple ZRTP Multistream mode exchanges can be processed in parallel between two endpoints ๐ง
multistream mode is also used to resume a secure call that went clear using a GoClear message, as described in Section 4.7.2.1 ๐
when adding extra media streams to an existing call, Multistream mode MUST be used ๐. the first media stream MUST use either DH mode or Preshared mode. only one DH exchange or Preshared exchange is performed, just for the first media stream. the DH exchange or Preshared exchange MUST be completed for the first media stream before Multistream mode is used to add any other media streams ๐ฆ. in a Multistream session, a ZRTP endpoint MUST use the same ZID for all media streams, matching the ZID used in the first media stream ๐
4.4.3.1. Commitment in Multistream Mode ๐
multistream mode gets selected when the initiator sets the Key Agreement Type to "Mult" in the Commit message (Figure 6) ๐ฏ. the Cipher Type, Auth Tag Length, and Hash in Multistream mode SHOULD be set by the initiator to the same values as in the initial DH Mode Commit. the SAS Type gets ignored, since there's no SAS authentication in this mode ๐
Note: This requirement is needed since some endpoints cannot support different SRTP algorithms for different media streams. However, in the case of Multistream mode being used to go secure after a GoClear, the requirement to use the same SRTP algorithms is relaxed if there are no other active SRTP sessions.
in place of hvi in the Commit, a random nonce of length 4 words (16 octets) is chosen ๐ฒ. its value MUST be unique for all nonce values chosen for active ZRTP sessions between a pair of endpoints. if a Commit comes in with a reused nonce value, the ZRTP exchange MUST be immediately terminated ๐จ
Note: Since the nonce is used to calculate different SRTP key and salt pairs for each media stream, a duplication will result in the same key and salt being generated for the two media streams, which would have disastrous security consequences. ๐
if a Commit is received selecting Multistream mode, but the responder doesn't have a ZRTP Session Key available, the exchange MUST be terminated. otherwise, the responder moves on to the next section on shared secret calculation, Section 4.4.3.2 ๐ซ
if both sides send Multistream Commit messages at the same time, the contention is resolved, the initiator/responder roles are settled according to Section 4.2, and the protocol keeps going ๐ค
in Multistream mode, both the DHPart1 and DHPart2 messages are skipped. after receiving the Commit message from the initiator, the responder sends the Confirm1 message once it has calculated this stream's SRTP keys, as described below ๐
4.4.3.2. Shared Secret Calculation for Multistream Mode ๐
in Multistream mode, each media stream needs a set of keys derived from the ZRTPSess key, and this must be done in a way that guarantees uniqueness for each media stream ๐. it's done by using nonce material from both parties: the explicit nonce in the initiator's Multistream Commit message (Figure 6) and the H3 field in the responder's Hello message (Figure 3). so both parties force the resulting shared secret to be unique for each media stream ๐
a hash of the received and sent ZRTP messages in the current ZRTP exchange for the current media stream gets calculated:
total_hash = hash(Hello of responder || Commit)
this refers to the Hello and Commit messages for the current media stream, which is using Multistream mode, not the original media stream that included a full DH key agreement ๐. note that only the ZRTP messages (Figures 3 and 6), not the entire ZRTP packets, are included in the hash
the ZRTP key derivation function (KDF) (Section 4.5.1) needs a KDF Context field (per [NIST-SP800-108] guidelines), which should include the ZIDi, ZIDr, and a nonce value known to both parties. the total_hash counts as a nonce value, since its computation included nonce material from the initiator's Commit message and the responder's Hello message ๐ง
KDF_Context = (ZIDi || ZIDr || total_hash)
the current stream's SRTP keys and salts for the initiator and responder are calculated using the ZRTP Session Key ZRTPSess and the nonces implicitly included in the total_hash. the nonces also ensure that KDF_Context will be unique for each media stream, which is critical for security ๐. for each additional media stream, a separate s0 is derived from ZRTPSess via the ZRTP key derivation function (Section 4.5.1):
s0 = KDF(ZRTPSess, "ZRTP MSK", KDF_Context,
negotiated hash length)
note that the ZRTPSess key was previously derived from material that also includes a different and more inclusive total_hash from the entire packet sequence that performed the original DH exchange for the first media stream in this ZRTP session ๐งฉ
at this point in Multistream mode, the two endpoints begin key derivations in Section 4.5.3 ๐
4.5. Key Derivations โจ
4.5.1. The ZRTP Key Derivation Function
okay bestie, whenever ZRTP needs to cook up keys from a shared secret, it uses an HMAC-based key derivation function, or KDF ๐ It shows up all over Section 4.5.3 and a few other spots too. The HMAC inside the KDF is built on whatever hash algorithm got negotiated, per Section 5.1.2 ๐
The authors say the ZRTP KDF is in full compliance with the recommendations in NIST SP 800-108 [NIST-SP800-108] โจ Section 7.5 of that NIST doc talks about "key separation," which is a security requirement for keys derived from the same key derivation key. The keys shall be separate in the sense that the compromise of some derived keys will not degrade the security strength of any of the other derived keys or the security strength of the key derivation key. Strong preimage resistance is provided too ๐ญ๐ซถ
The ZRTP KDF runs the NIST pseudorandom function (PRF) in counter mode, with only a single iteration of the counter ๐ง The NIST PRF is based on the HMAC function. The ZRTP KDF never has to generate more than 256 bits (or 384 bits for Suite B applications) of output key material, so only a single invocation of the HMAC function is needed. Literally one and done ๐
Here's how the ZRTP KDF is defined, per Sections 5 and 5.1 of [NIST-SP800-108]:
KDF(KI, Label, Context, L) = HMAC(KI, i || Label ||
0x00 || Context || L)
The HMAC in the KDF is keyed by KI, which is a secret key derivation key that is unknown to the wiretapper (for example, s0). The HMAC is computed on a concatenated set of nonsecret fields, defined like this ๐ The first field is a 32-bit big-endian integer counter (i) that NIST requires to be included in the HMAC each time it's computed. ZRTP sets it to the fixed value 0x000001 because the HMAC only gets computed once. Label is a string of nonzero octets that identifies the purpose of the derived keying material. The octet 0x00 is a delimiter that NIST requires. The NIST KDF formula has a "Context" field that includes ZIDi, ZIDr, and some optional nonce material known to both parties. L is a 32-bit big-endian positive integer, not to exceed the length in bits of the output of the HMAC. The output of the KDF is truncated to the leftmost L bits. If SHA-384 is the negotiated hash algorithm, the HMAC would be HMAC-SHA-384, so the maximum value of L would be 384, the negotiated hash length ๐
Heads up: the ZRTP KDF is NOT the same thing as the SRTP KDF defined in [RFC3711]. Don't mix them up, bestie ๐
4.5.2. Deriving ZRTPSess Key and SAS in DH or Preshared Modes
Both DH mode and Preshared mode (but not Multistream mode) come to this common point in the protocol to derive ZRTPSess and the SAS from s0, using the ZRTP Key Derivation Function (Section 4.5.1) ๐ By now, s0 has been calculated, and so has KDF_Context. These calculations are only done for the first media stream, not for Multistream mode.
The ZRTPSess key gets used for just two things: 1) generating the additional s0 keys (Section 4.4.3.2) for adding more media streams to this session in Multistream mode, and 2) generating the pbxsecret (Section 7.3.1) that may be cached for use in future sessions ๐ฆ The ZRTPSess key sticks around for the whole call signaling session between the two ZRTP endpoints. So if there are two separate calls between the endpoints (SIP-speak for separate SIP dialogs), a ZRTP Session Key MUST NOT be used across the two call signaling sessions. ZRTPSess MUST be destroyed no later than the end of the call signaling session. Gone. Erased. Not tea ๐
ZRTPSess = KDF(s0, "ZRTP Session Key", KDF_Context,
negotiated hash length)
Note that KDF_Context is unique for each media stream, but only the first media stream is permitted to calculate ZRTPSess ๐
There is only one Short Authentication String (SAS) (Section 7) computed per call, and it applies to every media stream derived from a single DH key agreement in a ZRTP session. KDF_Context is unique for each media stream, but only the first media stream is permitted to calculate sashash โจ
sashash = KDF(s0, "SAS", KDF_Context, 256)
sasvalue = sashash [truncated to leftmost 32 bits]
Even though the SAS is exposed to both parties, the rest of the keying material stays protected by the key separation properties of the KDF (Section 4.5.1) ๐ฅ
ZRTP-enabled VoIP clients might need other kinds of communication too, like text chat, instant messaging, or file transfers. Those might need encryption as well, and they'd benefit from reusing the ZRTP key exchange that's already done for the VoIP part of the call ๐ In that case, more key material MAY be derived and "exported" from the ZRTP protocol and handed over as a shared secret to the VoIP client for those non-VoIP purposes. The application can then use this exported key in its own application-specific ways, which is outside the scope of the ZRTP protocol.
ExportedKey = KDF(s0, "Exported key", KDF_Context,
negotiated hash length)
Only one ExportedKey is computed per call. KDF_Context is unique for each media stream, but only the first media stream is permitted to calculate ExportedKey ๐ง
The application may use this exported key to derive other subkeys for various non-ZRTP purposes, through a KDF using separate KDF label strings that the application defines itself. That key or its derived subkeys can be used for encryption, or to authenticate other key exchanges the application carries out, and they're protected by ZRTP's MiTM defense umbrella โ๏ธ The exported key and its descendants can be used for as long as the application needs them, kept in a separate crypto context that may outlast the VoIP session ๐
At this point in DH mode or Preshared mode, the two endpoints move on to the key derivations in Section 4.5.3, now that there's a defined s0 and ZRTPSess key ๐
4.5.3. Deriving the Rest of the Keys from s0
DH mode, Multistream mode, and Preshared mode all come to this common point in the protocol to derive a set of keys from s0 ๐ At this point, it can be assumed that s0 has been calculated, along with the ZRTPSess key and KDF_Context. Each media stream has its own separate s0 key.
Subkeys aren't pulled straight out of s0, the way NIST SP 800-56A does it. To boost key separation, ZRTP uses s0 to key a Key Derivation Function (Section 4.5.1) based on [NIST-SP800-108] ๐ธ Since s0 already included total_hash in its derivation, it's redundant to use total_hash again in the KDF Context in all the invocations of the KDF keyed by s0. But NIST SP 800-108 always requires KDF Context to be defined for the KDF, and some KDF invocations need nonce material (especially in Multistream mode and Preshared mode). So total_hash gets included as a nonce in the KDF Context ๐ซถ
Separate SRTP master keys and master salts are derived for each direction, for each media stream. Unless something says otherwise, ZRTP uses SRTP with no Master Key Identifier (MKI), 32-bit authentication using HMAC-SHA1, AES-CM with a 128 or 256-bit key length, a 112-bit session salt key length, a 2^48 key derivation rate, and an SRTP prefix length of 0 ๐ Secure RTCP (SRTCP) is used too. Its keys come from the same master keys and salts as SRTP, using the mechanisms in [RFC3711], and there's no need for a separate ZRTP negotiation for RTCP ๐ฆ
The ZRTP initiator encrypts and the ZRTP responder decrypts packets using srtpkeyi and srtpsalti. The ZRTP responder encrypts and the ZRTP initiator decrypts packets using srtpkeyr and srtpsaltr ๐ The SRTP key and salt values get truncated (taking the leftmost bits) to the length set by the chosen SRTP profile. They're generated like this:
srtpkeyi = KDF(s0, "Initiator SRTP master key", KDF_Context,
negotiated AES key length)
srtpsalti = KDF(s0, "Initiator SRTP master salt", KDF_Context, 112)
srtpkeyr = KDF(s0, "Responder SRTP master key", KDF_Context,
negotiated AES key length)
srtpsaltr = KDF(s0, "Responder SRTP master salt", KDF_Context, 112)
The MAC keys are the same length as the output of the underlying hash function in the KDF, so they're generated without any truncation ๐ They're only used by ZRTP, not by SRTP. The initiator and responder need different MAC keys so that GoClear messages in each direction are unique and can't be cached by an attacker and reflected back at the endpoint. Iconic defensive move ๐ญ
mackeyi = KDF(s0, "Initiator HMAC key", KDF_Context,
negotiated hash length)
mackeyr = KDF(s0, "Responder HMAC key", KDF_Context,
negotiated hash length)
ZRTP keys are also generated for the initiator and responder to use when they encrypt the Confirm1 and Confirm2 messages. They're truncated to the same size as the negotiated SRTP key size โจ
zrtpkeyi = KDF(s0, "Initiator ZRTP key", KDF_Context,
negotiated AES key length)
zrtpkeyr = KDF(s0, "Responder ZRTP key", KDF_Context,
negotiated AES key length)
All key material gets destroyed as soon as it's no longer needed, and no later than the end of the call ๐๏ธ s0 is erased in Section 4.6.1, and the rest of the session key material is erased in Sections 4.7.2.1 and 4.7.3. Clean girl energy ๐
4.6. Confirmation
The Confirm1 and Confirm2 messages (Figure 10) carry the cache expiration interval (defined in Section 4.9) for the newly generated retained shared secret ๐ The flagoctet is an 8-bit unsigned integer made up of these flags: the PBX Enrollment flag (E) defined in Section 7.3.1, the SAS Verified flag (V) defined in Section 7.1, the Allow Clear flag (A) defined in Section 4.7.2, and the Disclosure flag (D) defined in Section 11 ๐
flagoctet = (E * 2^3) + (V * 2^2) + (A * 2^1) + (D * 2^0)
Part of the Confirm1 and Confirm2 messages get encrypted using full-block Cipher Feedback Mode, and they contain a 128-bit random Cipher FeedBack (CFB) Initialization Vector (IV) ๐ฆ The Confirm1 and Confirm2 messages also carry a MAC covering the encrypted part of the message. That MAC covers a string of zeros, the signature length, the flag octet, the cache expiration interval, the signature type block (if present), and the signature (Section 7.2) (if present) ๐ง For the responder:
confirm_mac = MAC(mackeyr, encrypted part of Confirm1)
For the initiator:
confirm_mac = MAC(mackeyi, encrypted part of Confirm2)
The mackeyi and mackeyr keys are computed in Section 4.5.3 ๐
The exchange is completed when the responder sends either the Conf2ACK message or the responder's first SRTP media packet (with a valid SRTP auth tag) ๐ฅ The initiator MUST treat the first valid SRTP media from the responder as equivalent to receiving a Conf2ACK. The responder may respond to Confirm2 with SRTP media, Conf2ACK, or both, in whichever order the responder chooses (or whichever order the "cloud" chooses to deliver them) ๐ธ
4.6. Confirmation โจ
Okay so once the Confirm messages have been exchanged, both parties need to update their retained shared secret rs1 in their caches, but only if all of this is true ๐
- This key exchange is either DH or Preshared mode, not Multistream mode, which does not update the cache.
- Depending on the values of the cache expiration intervals that are received in the two Confirm messages, there are some scenarios that do not update the cache, as explained in Section 4.9.
- The responder MUST receive the initiator's Confirm2 message before updating the responder's cache.
- The initiator MUST receive either the responder's Conf2ACK message or the responder's SRTP media (with a valid SRTP auth tag) before updating the initiator's cache.
The cache update can also be affected by a cache mismatch, which is covered in Section 4.6.1.1 ๐ต๏ธโโ๏ธ
For DH mode only, before updating the retained shared secret rs1 in the cache, each party first throws away their old rs2 and copies their old rs1 into rs2 ๐ซถ The old rs1 gets saved to rs2 because a session could get interrupted after one party has updated their own rs1 but before the other party has enough info to update theirs. If that happens, they can regain cache sync in the next session by using rs2 (per Section 4.3). This is a mitigation for the super famous Two Generals' Problem [Byzantine]. ๐ธ The old rs1 value is not saved in Preshared mode.
For DH mode and Preshared mode, both parties compute a brand new rs1 value from s0 using the ZRTP key derivation function (Section 4.5.1):
rs1 = KDF(s0, "retained secret", KDF_Context, 256)
Note that KDF_Context is unique for each media stream, but only the first media stream is permitted to update rs1. ๐ฆ
Each media stream has its own s0. At this point in the protocol, for each media stream, the corresponding s0 MUST be erased. ๐๏ธ
4.6.1.1. Cache Update Following a Cache Mismatch
If a shared secret cache mismatch (as defined in Section 4.3.2) gets detected in the current session, it's a possible MiTM attack, bestie ๐ญ However, there may be evidence to the contrary if either one of the following conditions is met:
- Successful use of the mechanism described in Section 8.1.1, but only if it's fully supported by end-to-end integrity-protected delivery of the
a=zrtp-hashin the signaling via SIP Identity [RFC4474] or, even better, Dan Wing's SIP Identity using Media Path [SIP-IDENTITY]. This allows authentication of the DH exchange without human assistance. ๐ - A good signature is received and verified using the digital signature feature on the SAS hash, as described in Section 7.2, if this feature is supported. โ๏ธ
If there's a cache mismatch and none of that mitigating evidence shows up, the cache update MUST be delayed in the current session until the user verbally compares the SAS with their partner during the call and confirms a successful SAS verify through their user interface, as described in Section 7.1. ๐ฃ๏ธ If the session ends before that happens, the cache update is not performed, and the rs1/rs2 values stay unmodified in the cache. Regardless of whether a cache mismatch occurs, s0 must still be erased. ๐
If no cache entry exists, like in the initial call, the cache update is handled in the normal fashion. ๐
4.7. Termination ๐ฌ
A ZRTP session is normally terminated at the end of a call, but it may be terminated early by either the Error message or the GoClear message. ๐
4.7.1. Termination via Error Message
The Error message (Section 5.9) is used to terminate an in-progress ZRTP exchange because of an error ๐ The Error message contains an integer Error Code for debugging purposes. Terminating a ZRTP key agreement exchange results in no updates to the cached shared secrets, plus deletion of all crypto context for that media stream. The ZRTP Session key, ZRTPSess, is only deleted if all ZRTP media streams that are using it are terminated.
Because no key agreement has been reached, the Error message cannot use the same MAC protection as the GoClear message. A denial of service is possible by injecting fake Error messages. ๐ฑ (However, even if the Error message were somehow designed with integrity protection, it would raise other questions. What would a badly formed Error message mean if it were sent to report a badly formed message? A good message?) ๐ค
4.7.2. Termination via GoClear Message
The GoClear message (Section 5.11) is used to switch from SRTP to RTP, usually because the user chose to do that by pressing a button ๐ The GoClear uses a MAC of the Message Type Block sent in the GoClear message, computed with the mackey derived from the shared secret. This MAC is truncated to the leftmost 64 bits. When the initiator sends it:
clear_mac = MAC(mackeyi, "GoClear ")
When the responder sends it:
clear_mac = MAC(mackeyr, "GoClear ")
Both of these MACs are calculated across the 8-octet "GoClear " Message Type Block, including the trailing space. โจ
A GoClear message that does not receive a ClearACK response must be resent. If a GoClear message arrives with a bad MAC, the ClearACK MUST NOT be sent and the GoClear MUST NOT be acted on by the recipient, but it MAY be processed as a security exception, perhaps by logging or alerting the user. ๐จ
A ZRTP endpoint MAY choose to accept GoClear messages after the session has switched to SRTP, which lets the session revert to RTP. ๐ This is indicated in the Confirm1 or Confirm2 messages (Figure 10) by setting the Allow Clear flag (A). If an endpoint sets the Allow Clear (A) flag in their Confirm message, it's saying it supports receiving GoClear messages. ๐
A ZRTP endpoint that receives a GoClear MUST authenticate the message by checking the clear_mac. If the message authenticates, the endpoint stops sending SRTP packets and generates a ClearACK in response. It MUST also delete all the crypto key material for all the SRTP media streams, as defined in Section 4.7.2.1. ๐งน
Until confirmation from the user is received (for example, clicking a button, or pressing a dual-tone multi-frequency (DTMF) key), the ZRTP endpoint MUST NOT resume sending RTP packets. The endpoint then shows the user an indication that the media session has switched to clear mode, and waits for confirmation from the user. This blocks the flow of sensitive discourse until the user is forced to notice they're no longer protected by encryption ๐ To keep pinholes from closing or NAT bindings from expiring, the ClearACK message MAY be resent at regular intervals (for example, every 5 seconds) while waiting for confirmation from the user. After the user's confirmation of the notification is received, sending RTP packets may begin. ๐
After sending a GoClear message, the ZRTP endpoint stops sending SRTP packets. When a ClearACK is received, the ZRTP endpoint deletes the crypto context for the SRTP session, as defined in Section 4.7.2.1, and may then resume sending RTP packets. ๐ฆ
If a ClearACK doesn't arrive before the retransmissions of GoClear run out, the key material is deleted, as defined in Section 4.7.2.1. ๐
After the users have moved from SRTP media back to RTP media (clear mode), they may decide later to return to secure mode by manual activation, usually by pressing a GO SECURE button ๐ In that case, a new secure session is started by the party that presses the button, by sending a new Commit message, which leads to a new session key negotiation. It's not necessary to send another Hello message, because the two parties already did that at the start of the call and have already discovered each other's ZRTP capabilities. It's possible for users to toggle back and forth between clear and secure modes multiple times in the same session, just like they could back in the old days of secure PSTN phones ๐๐
4.7.2.1. Key Destruction for GoClear Message
All SRTP session key material MUST be erased by the receiver of the GoClear message upon receiving a properly authenticated GoClear. The same key destruction MUST be done by the sender of the GoClear message, upon receiving the ClearACK. This must be done for the key material for all of the media streams. ๐งผ
All key material that would have been erased at the end of the SIP session MUST be erased, as described in Section 4.7.3, with the single exception of ZRTPSess. ZRTPSess gets destroyed differently from the other key material. Both parties replace ZRTPSess with a KDF-derived, non-invertible function of itself:
ZRTPSess = KDF(ZRTPSess, "New ZRTP Session", (ZIDi || ZIDr),
negotiated hash length)
ZRTPSess will be replaced twice if a session generates separate GoClear messages for both audio and video streams, and the two endpoints don't need to do the replacements in the same order. ๐
The destruction of key material meets the requirements of Perfect Forward Secrecy (PFS), but it still preserves a new version of ZRTPSess. That way, the user can later re-initiate secure mode during the same session without doing another Diffie-Hellman calculation using Multistream mode, which requires and assumes the existence of ZRTPSess with the same value at both ZRTP endpoints. A new key negotiation after a GoClear SHOULD use a Multistream Commit message. ๐
Note: Multistream mode is preferred over a Diffie-Hellman mode since it doesn't require generating a new hash chain and a new signaling exchange to swap new Hello Hash values. ๐ง
Later, at the end of the entire call, ZRTPSess is finally destroyed along with the other key material, as described in Section 4.7.3. ๐
4.7.3. Key Destruction at Termination
All SRTP session key material MUST be erased by both parties at the end of the call. In particular, the destroyed key material includes the SRTP session keys and salts, the SRTP master keys and salts, and all material sufficient to reconstruct the SRTP keys and salts, including ZRTPSess and s0 (although s0 should have been destroyed earlier, in Section 4.6.1). This must be done for the key material for all of the media streams. The only exceptions are the cached shared secrets needed for future sessions, including rs1, rs2, and pbxsecret. ๐๐
4.8. Random Number Generation ๐ฒ
okay bestie, the ZRTP protocol uses random numbers for its cryptographic key material ๐ especially the DH secret exponents and nonces, and those have to be freshly generated with each session. Whenever a random number is needed, all of the following criteria must be satisfied:
Random numbers MUST be freshly generated, meaning that they must not have been used in a previous calculation. ๐
When generating a random number k of L bits in length, k MUST be chosen with equal probability from the range of [1 < k < 2^L]. ๐ฏ
It MUST be derived from a physical entropy source, such as radio frequency (RF) noise, acoustic noise, thermal noise, high-resolution timings of environmental events, or other unpredictable physical sources of entropy. One possible source of entropy for a VoIP client would be microphone noise ๐ค For a detailed explanation of cryptographic grade random numbers and guidance for collecting suitable entropy, see [RFC4086] and Chapter 10 of "Practical Cryptography" [Ferguson]. The raw entropy must be distilled and processed through a deterministic random-bit generator (DRBG). Examples of DRBGs may be found in [NIST-SP800-90], in [Ferguson], and in [RFC5869]. Failure to use true entropy from the physical environment as a basis for generating random cryptographic key material would lead to a disastrous loss of security ๐ so please, no shortcuts.
4.9. ZID and Cache Operation ๐๏ธ
Each instance of ZRTP has a unique 96-bit random ZRTP ID, or ZID, that is generated once at installation time โจ It is used to look up retained shared secrets in a local cache. A single global ZID for a single installation is the simplest way to implement ZIDs. However, it is specifically not precluded for an implementation to use multiple ZIDs, up to the limit of a separate one per callee. This then turns it into a long-lived "association ID" that does not apply to any other associations between a different pair of parties. It is a goal of this protocol to permit both options to interoperate freely ๐ค A PBX acting as a trusted man in the middle will also generate a single ZID and use that ZID for all endpoints behind it, as described in Section 10.
There is no protocol mechanism to invalidate a previously used ZID. An endpoint wishing to change ZIDs would simply generate a new one and begin using it ๐
The ZID should not be hard coded or hard defined in the firmware of a product. It should be randomly generated by the software and stored at installation or initialization time. It should be randomly generated rather than allocated from a preassigned range of ZID values, because 96 bits should be enough to avoid birthday collisions in realistic scenarios ๐
Each time a new s0 is calculated, a new retained shared secret rs1 is generated and stored in the cache, indexed by the ZID of the other endpoint. This cache updating is described in Section 4.6.1. For the new retained shared secret, each endpoint chooses a cache expiration value that is an unsigned 32-bit integer of the number of seconds that this secret should be retained in the cache. The time interval is relative to when the Confirm1 message is sent or received โฑ๏ธ
The cache intervals are exchanged in the Confirm1 and Confirm2 messages (Figure 10). The actual cache interval used by both endpoints is the minimum of the values from the Confirm1 and Confirm2 messages. A value of 0 seconds means the newly computed shared secret SHOULD NOT be stored in the cache, and if a cache entry already exists from an earlier call, the stored cache interval should be set to 0. This means if either Confirm message contains a null cache expiration interval, and there is no cache entry already defined, no new cache entry is created. A value of 0xffffffff means the secret should be cached indefinitely and is the recommended value ๐ If the ZRTP exchange is Multistream mode, the field in the Confirm1 and Confirm2 is set to 0xffffffff and is ignored; the cache is not updated.
The expiration interval need not be used to force the deletion of a shared secret from the cache when the interval has expired. It just means the shared secret MAY be deleted from that cache at any point after the interval has expired without causing the other party to note it as an unexpected security event when the next key negotiation occurs between the same two parties. This means there need not be perfectly synchronized deletion of expired secrets from the two caches, and makes it easy to avoid a race condition that might otherwise be caused by clock skew ๐ง
If the expiration interval is not properly agreed to by both endpoints, it may later result in false alarms of MiTM attacks, due to apparent cache mismatches (Section 4.3.2). Not me getting a false alarm over a timing mismatch ๐ญ
The relationship between a ZID and a SIP AOR is explained in Section 12.
4.9.1. Cacheless Implementations ๐
It is possible to implement a simplified but nonetheless useful (and still compliant) profile of the ZRTP protocol that does not support any caching of shared secrets. In this case, the users would have to rely exclusively on the verbal SAS comparison for every call. That is, unless MiTM protection is provided by the mechanisms in Section 8.1.1 or 7.2, which introduce their own forms of complexity.
If a ZRTP endpoint does not support the caching of shared secrets, it MUST set the cache expiration interval to zero, and MUST set the SAS Verified (V) flag (Section 7.1) to false. In addition, because the ZID serves mainly as a cache index, the ZID would not be required to maintain the same value across separate SIP sessions, although there is no reason why it should not ๐
Cacheless operation would sacrifice the key continuity (Section 15.1) features, as well as Preshared mode (Section 4.4.2). Further, if the pbxsecret is also not cached, there would be no PBX trusted MiTM (Section 7.3) features, including the PBX security enrollment (Section 7.3.1) mechanism ๐ฅฒ
5. ZRTP Messages ๐จ
All ZRTP messages use the message format defined in Figure 2. All word lengths referenced in this specification are 32 bits, or 4 octets. All integer fields are carried in network byte order, that is, most-significant byte (octet) first, commonly known as big-endian ๐
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 0 0 1|Not Used (set to zero) | Sequence Number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Magic Cookie 'ZRTP' (0x5a525450) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Identifier |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| ZRTP Message (length depends on Message Type) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| CRC (1 word) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 2: ZRTP Packet Format
The Sequence Number is a count that is incremented for each ZRTP packet sent. The count is initialized to a random value. This is useful in estimating ZRTP packet loss and also detecting when ZRTP packets arrive out of sequence ๐ข
The ZRTP Magic Cookie is a 32-bit string that uniquely identifies a ZRTP packet and has the value 0x5a525450 โจ
Source Identifier is the SSRC number of the RTP stream to which this ZRTP packet relates. For cases of forking or forwarding, RTP, and hence ZRTP, may arrive at the same port from several different sources -- each of these sources will have a different SSRC and may initiate an independent ZRTP protocol session. SSRC collisions would be disruptive to ZRTP. SSRC collision handling procedures are described in Section 4.1 ๐
This format is clearly identifiable as non-RTP due to the first two bits being zero, which looks like RTP version 0, which is not a valid RTP version number. It is clearly distinguishable from STUN since the Magic Cookies are different. The 12 unused bits are set to zero and MUST be ignored when received. In early versions of this spec, ZRTP messages were encapsulated in RTP header extensions, which made ZRTP an eponymous variant of RTP. In later versions, the packet format changed to make it syntactically distinguishable from RTP ๐ฆ
The ZRTP messages are defined in Figures 3 to 17 and are of variable length.
The ZRTP protocol uses a 32-bit Cyclic Redundancy Check (CRC) as defined in RFC 4960, Appendix B [RFC4960], in each ZRTP packet to detect transmission errors. ZRTP packets are typically transported by UDP, which carries its own built-in 16-bit checksum for integrity, but ZRTP does not rely on it. This is because of the effect of an undetected transmission error in a ZRTP message. For example, an undetected error in the DH exchange could appear to be an active man-in-the-middle attack. A false announcement of this by ZRTP clients can be psychologically distressing. The probability of such a false alarm hinges on a mere 16-bit checksum that usually protects UDP packets, so more error detection is needed. For these reasons, this belt-and-suspenders approach is used to minimize the chance of a transmission error affecting the ZRTP key agreement ๐ก๏ธ
The CRC is calculated across the entire ZRTP packet shown in Figure 2, including the ZRTP header and the ZRTP message, but not including the CRC field. If a ZRTP message fails the CRC check, it is silently discarded ๐๏ธ
5.1. ZRTP Message Formats ๐
ZRTP messages are designed to simplify endpoint parsing requirements and to reduce the opportunities for buffer overflow attacks (a good goal of any security extension should be to not introduce new attack vectors) ๐ซถ
ZRTP uses a block of 8 octets (2 words) to encode the Message Type. 4-octet (1 word) blocks are used to encode Hash Type, Cipher Type, Key Agreement Type, and Authentication Tag Type. The values in the blocks are ASCII strings that are extended with spaces (0x20) to make them the desired length. Currently defined block values are listed in Tables 1-6.
Additional block values may be defined and used.
ZRTP uses this ASCII encoding to simplify debugging and make it "Wireshark (Ethereal) friendly" ๐ฆโจ
5.1.1. Message Type Block โจ
okay bestie, so there are 16 Message Type Blocks defined ๐ and they're basically the whole squad of ZRTP message primitives. ZRTP endpoints MUST support the Hello, HelloACK, Commit, DHPart1, DHPart2, Confirm1, Confirm2, Conf2ACK, SASrelay, RelayACK, Error, ErrorACK, and PingACK message types. ZRTP endpoints MAY support the GoClear, ClearACK, and Ping messages ๐ฆ And to generate a PingACK message, you have to parse a Ping message first, so it's literally a package deal ๐ Additional messages may be defined in extensions to ZRTP, so the list can keep growing โจ
Message Type Block | Meaning
---------------------------------------------------
"Hello " | Hello Message
---------------------------------------------------
"HelloACK" | HelloACK Message
---------------------------------------------------
"Commit " | Commit Message
---------------------------------------------------
"DHPart1 " | DHPart1 Message
---------------------------------------------------
"DHPart2 " | DHPart2 Message
---------------------------------------------------
"Confirm1" | Confirm1 Message
---------------------------------------------------
"Confirm2" | Confirm2 Message
---------------------------------------------------
"Conf2ACK" | Conf2ACK Message
---------------------------------------------------
"Error " | Error Message
---------------------------------------------------
"ErrorACK" | ErrorACK Message
---------------------------------------------------
"GoClear " | GoClear Message
---------------------------------------------------
"ClearACK" | ClearACK Message
---------------------------------------------------
"SASrelay" | SASrelay Message
---------------------------------------------------
"RelayACK" | RelayACK Message
---------------------------------------------------
"Ping " | Ping Message
---------------------------------------------------
"PingACK " | PingACK Message
---------------------------------------------------
Table 1. Message Type Block Values
5.1.2. Hash Type Block ๐ธ
okay so the hash algorithm and its related MAC algorithm get negotiated through the Hash Type Block, which shows up in the Hello message (Section 5.2) and the Commit message (Section 5.4) ๐
All ZRTP endpoints MUST support a Hash Type of SHA-256 [FIPS-180-3]. SHA-384 SHOULD be supported and MUST be supported if ECDH-384 is used. Additional Hash Types MAY be used, such as the NIST SHA-3 hash [SHA-3] when it becomes available. Quick reminder: the Hash Type is the hash algorithm used throughout the ZRTP key exchange, NOT the one used in the SRTP Authentication Tag ๐ซถ
The choice of the negotiated Hash Type is coupled to the Key Agreement Type, as explained in Section 5.1.5. It's giving connected ๐ฅ
Hash Type Block | Meaning
----------------------------------------------------------
"S256" | SHA-256 Hash defined in FIPS 180-3
----------------------------------------------------------
"S384" | SHA-384 Hash defined in FIPS 180-3
----------------------------------------------------------
"N256" | NIST SHA-3 256-bit hash (when published)
----------------------------------------------------------
"N384" | NIST SHA-3 384-bit hash (when published)
----------------------------------------------------------
Table 2. Hash Type Block Values
At the time of this writing, the NIST SHA-3 hashes [SHA-3] are not yet available ๐ญ NIST is expected to publish SHA-3 in 2012, as a successor to the SHA-2 hashes in [FIPS-180-3].
5.1.2.1. Negotiated Hash and MAC Algorithm ๐
ZRTP makes use of message authentication codes (MACs) that are keyed hashes based on the negotiated Hash Type. For the SHA-2 and SHA-3 hashes, the negotiated MAC is the HMAC based on the negotiated hash. This MAC function is also used in the ZRTP key derivation function (Section 4.5.1) ๐ง
The HMAC function is defined in [FIPS-198-1]. A discussion of the general security of the HMAC construction may be found in [RFC2104]. Test vectors for HMAC-SHA-256 and HMAC-SHA-384 may be found in [RFC4231] ๐
The negotiated Hash Type does not apply to the hash used in the digital signature defined in Section 7.2. For example, even if the negotiated Hash Type is SHA-256, the digital signature may use SHA-384 if an Elliptic Curve Digital Signature Algorithm (ECDSA) P-384 signature key is used. Digital signatures are optional in ZRTP โจ
Except for the aforementioned digital signatures, and the special cases noted in Section 5.1.2.2, all the other hashes and MACs used throughout the ZRTP protocol will use the negotiated Hash Type ๐
A future hash may include its own built-in MAC, not based on the HMAC construct, for example, the Skein hash function [Skein]. If NIST chooses such a hash as the SHA-3 winner, Hash Types "N256", and "N384" will still use the related HMAC as the negotiated MAC. If an implementer wishes to use Skein and its built-in MAC as the negotiated MAC, new Hash Types must be used ๐
5.1.2.2. Implicit Hash and MAC Algorithm ๐
okay so most of the hash and MAC usage is decided by the negotiated Hash Type (Section 5.1.2), but some hashes and MACs have to be precomputed before negotiations even start. That means they can't get negotiated during the ZRTP exchange, so they're implicitly predetermined to use SHA-256 [FIPS-180-3] and HMAC-SHA-256 ๐
These are the hashes and MACs that MUST use the Implicit hash and MAC algorithm:
- The hash chain H0-H3 defined in Section 9.
- The MACs that are keyed by this hash chain, as defined in Section 8.1.1.
- The Hello Hash in the a=zrtp-hash attribute defined in Section 8.1.
ZRTP defines a method for negotiating different ZRTP protocol versions (Section 4.1.1). SHA-256 is the Implicit Hash and HMAC-SHA-256 is the Implicit MAC for ZRTP protocol version 1.10. Future ZRTP protocol versions may, if appropriate, use another hash algorithm as the Implicit Hash, such as the NIST SHA-3 hash [SHA-3], when it becomes available. For example, a future SIP packet may list two a=zrtp-hash SDP attributes, one based on SHA-256 for ZRTP version 1.10, and another based on SHA-3 for ZRTP version 2.00 ๐ฆ
5.1.3. Cipher Type Block ๐
okay the block cipher algorithm gets negotiated via the Cipher Type Block, found in the Hello message (Section 5.2) and the Commit message (Section 5.4) ๐ธ
All ZRTP endpoints MUST support AES-128 (AES1) and MAY support AES-192 (AES2), AES-256 (AES3), or other Cipher Types. The Advanced Encryption Standard is defined in [FIPS-197] โจ
The use of AES-128 in SRTP is defined by [RFC3711]. The use of AES-192 and AES-256 in SRTP is defined by [RFC6188]. The choice of the AES key length is coupled to the Key Agreement Type, as explained in Section 5.1.5 ๐ฅ
Other block ciphers may be supported that have the same block size and key sizes as AES. If implemented, they may be used anywhere in ZRTP or SRTP in place of the AES, in the same modes of operation and key size. Notably, in counter mode to replace AES-CM in [RFC3711] and [RFC6188], as well as in CFB mode to encrypt a portion of the Confirm message (Figure 10) and SASrelay message (Figure 16). ZRTP endpoints MAY support the TwoFish [TwoFish] block cipher ๐ง
Cipher Type Block | Meaning
-------------------------------------------------
"AES1" | AES with 128-bit keys
-------------------------------------------------
"AES2" | AES with 192-bit keys
-------------------------------------------------
"AES3" | AES with 256-bit keys
-------------------------------------------------
"2FS1" | TwoFish with 128-bit keys
-------------------------------------------------
"2FS2" | TwoFish with 192-bit keys
-------------------------------------------------
"2FS3" | TwoFish with 256-bit keys
-------------------------------------------------
Table 3. Cipher Type Block Values
5.1.4. Auth Tag Type Block ๐
okay so all ZRTP endpoints MUST support HMAC-SHA1 authentication tags for SRTP, with both 32-bit and 80-bit length tags as defined in [RFC3711] ๐
ZRTP endpoints MAY support 32-bit and 64-bit SRTP authentication tags based on the Skein hash function [Skein]. The Skein-512-MAC key length is fixed at 256 bits for this application, and the output length is adjustable. The Skein MAC is defined in Sections 2.6 and 4.3 of [Skein] and is not based on the HMAC construct. Reference implementations for Skein may be found at [Skein1]. A Skein-based MAC is significantly more efficient than HMAC-SHA1, especially for short SRTP payloads ๐ฆ
The Skein MAC key is computed by the SRTP key derivation function, which is also referred to as the AES-CM PRF, or pseudorandom function. This is defined either in [RFC3711] or in [RFC6188], depending on the selected SRTP AES key length. To compute a Skein MAC key, the SRTP PRF output for the authentication key is left untruncated at 256 bits, instead of the usual truncated length of 160 bits (the key length used by HMAC-SHA1) ๐
Auth Tag Type Block | Meaning
----------------------------------------------------------
"HS32" | 32-bit authentication tag based on
| HMAC-SHA1 as defined in RFC 3711.
----------------------------------------------------------
"HS80" | 80-bit authentication tag based on
| HMAC-SHA1 as defined in RFC 3711.
----------------------------------------------------------
"SK32" | 32-bit authentication tag based on
| Skein-512-MAC as defined in [Skein],
| with 256-bit key, 32-bit MAC length.
----------------------------------------------------------
"SK64" | 64-bit authentication tag based on
| Skein-512-MAC as defined in [Skein],
| with 256-bit key, 64-bit MAC length.
----------------------------------------------------------
Table 4. Auth Tag Type Values
Implementers should be aware that AES-GCM and AES-CCM for SRTP are expected to become available when [SRTP-AES-GCM] is published as an RFC. If an implementer wishes to use these modes when they become available, new Auth Tag Types must be added ๐ซถ
5.1.5. Key Agreement Type Block โจ
okay bestie, this block is where the two endpoints pick how they agree on a key ๐ All ZRTP endpoints MUST support DH3k, SHOULD support Preshared, and MAY support EC25, EC38, and DH2k. ๐
If an endpoint runs multiple concurrent media streams (think audio AND video at the same time) it MUST support Multistream (Section 4.4.3) mode ๐ And if an endpoint supports the GoClear message (Section 4.7.2), it SHOULD support Multistream too. That's so the two parties can go back to the secure state after going Clear, as explained in Section 4.7.2.1 ๐ซถ
For Finite Field Diffie-Hellman, ZRTP endpoints MUST use the DH parameters defined in [RFC3526]. DH3k uses the 3072-bit modular exponentiation group (MODP), and DH2k uses the 2048-bit MODP group. The DH generator g is 2. The random Diffie-Hellman secret exponent SHOULD be twice as long as the AES key length. So if AES-128 is used, the DH secret value SHOULD be 256 bits long, and if AES-256 is used, the secret value SHOULD be 512 bits long ๐งฎโจ
If Elliptic Curve DH is used, the ECDH algorithm and key generation come from [NIST-SP800-56A]. The curves are from [NSA-Suite-B], which uses the same curves as ECDSA defined by [FIPS-186-3], and you can also find them in RFC 5114, Sections 2.6 through 2.8 [RFC5114]. ECDH test vectors live in RFC 5114, appendices A.6 through A.8 [RFC5114]. The validation procedures come from [NIST-SP800-56A], Section 5.6.2.6, method 3, Elliptic Curve Cryptography (ECC) Partial Validation. Both the X and Y coordinates of the point on the curve are sent, in the first and second half of the ECDH public value, respectively. The ECDH result returns only the X coordinate, as specified in SP 800-56A. If you want useful strategies for implementing ECC, check out [RFC6090] ๐
The choice of the negotiated hash algorithm (Section 5.1.2) is coupled to the choice of Key Agreement Type. If ECDH-384 (EC38) is chosen as the key agreement, the negotiated hash algorithm MUST be either SHA-384 or the corresponding SHA-3 successor. no skipping this one, bestie ๐ โโ๏ธ
The AES key length is coupled to the Key Agreement Type too. If EC38 is chosen, AES-256 (AES3) SHOULD be used, but AES-192 MAY be used. If DH3k or EC25 is chosen, any AES key size MAY be used. Note that SRTP as defined in [RFC3711] only supports AES-128 ๐ฅน
DH2k is intended to provide acceptable security for low power applications, or for applications that require faster key negotiations. NIST asserts in Table 4 of [NIST-SP800-131A] that DH-2048 is safe to use through 2013. The security of DH2k can be augmented by implementing ZRTP's key continuity features (Section 15.1). DH2k SHOULD use AES-128. If an implementor must use slow hardware, DH2k should precede DH3k in the Hello message ๐ข
ECDH-521 SHOULD NOT be used, due to disruptive computational delays. ๐ญ These delays may lead to exhaustion of the retransmission schedule, unless both endpoints have very fast hardware. Note that ECDH-521 is not part of NSA Suite B. it's giving "no thanks" energy ๐
ZRTP also defines two non-DH modes, Multistream and Preshared, in which the SRTP key is derived from a shared secret and some nonce material. ๐ง
The table below lists the pv length in words and the DHPart1 and DHPart2 message length in words for each Key Agreement Type Block ๐
Key Agreement | pv | message | Meaning
Type Block | words | words |
-----------------------------------------------------------
"DH3k" | 96 | 117 | DH mode with p=3072 bit prime
| | | per RFC 3526, Section 4.
-----------------------------------------------------------
"DH2k" | 64 | 85 | DH mode with p=2048 bit prime
| | | per RFC 3526, Section 3.
-----------------------------------------------------------
"EC25" | 16 | 37 | Elliptic Curve DH, P-256
| | | per RFC 5114, Section 2.6
-----------------------------------------------------------
"EC38" | 24 | 45 | Elliptic Curve DH, P-384
| | | per RFC 5114, Section 2.7
-----------------------------------------------------------
"EC52" | 33 | 54 | Elliptic Curve DH, P-521
| | | per RFC 5114, Section 2.8
| | | (deprecated - do not use)
-----------------------------------------------------------
"Prsh" | - | - | Preshared Non-DH mode
-----------------------------------------------------------
"Mult" | - | - | Multistream Non-DH mode
-----------------------------------------------------------
Table 5. Key Agreement Type Block Values
5.1.6. SAS Type Block ๐ธ
The SAS Type decides how the SAS gets shown to the user, so the user can read it out and verbally compare it with their partner over the voice channel. This is how a MiTM attack gets caught ๐ต๏ธโโ๏ธ๐ฅ
All ZRTP endpoints MUST support the base32 and MAY support the base256 rendering schemes for the Short Authentication String, plus other SAS rendering schemes. See Section 4.5.2 for how the sasvalue is computed and Section 7 for how the SAS is used.
SAS Type Block | Meaning
---------------------------------------------------
"B32 " | Short Authentication String using
| base32 encoding
---------------------------------------------------
"B256" | Short Authentication String using
| base256 encoding (PGP Word List)
---------------------------------------------------
Table 6. SAS Type Block Values
For the SAS Type of "B256", the most-significant (leftmost) 16 bits of the 32-bit sasvalue are rendered in network byte order using the PGP Word List [pgpwordlist] [Juola1][Juola2] ๐
For the SAS Type of "B32 ", the most-significant (leftmost) 20 bits of the 32-bit sasvalue are rendered as a form of base32 encoding. The leftmost 20 bits of the sasvalue results in four base32 characters that are rendered, most-significant quintet first, to both ZRTP endpoints. Here is a normative pseudocode implementation of the base32 function ๐ง
char[4] base32(uint32 bits)
{ int i, n, shift;
char result[4];
for (i=0,shift=27; i!=4; ++i,shift-=5)
{ n = (bits>>shift) & 31;
result[i] = "ybndrfg8ejkmcpqxot1uwisza345h769"[n];
}
return result;
}
This base32 encoding scheme differs from RFC 4648, and was designed (by Bryce Wilcox-O'Hearn) to represent bit sequences in a form that is convenient for human users to manipulate with minimal ambiguity. The unusually permuted character ordering was designed for other applications that use bit sequences that do not end on quintet boundaries. the permutation is literally doing the most ๐ฆ
5.1.7. Signature Type Block ๐๏ธ
The Signature Type Block says which signature algorithm gets used to sign the SAS, as discussed in Section 7.2. The 4-octet Signature Type Block, along with the accompanying signature block, are OPTIONAL and may be present in the Confirm message (Figure 10) or the SASrelay message (Figure 16). The signature types are in the table below ๐
Signature | Meaning
Type Block |
------------------------------------------------
"PGP " | OpenPGP Signature, per RFC 4880
|
------------------------------------------------
"X509" | ECDSA, with X.509v3 cert
| per RFC 5759 and FIPS-186-3
------------------------------------------------
Table 7. Signature Type Block Values
Additional details on the signature and signing key format may be found in Section 7.2. OpenPGP signatures (Signature Type "PGP ") are discussed in Section 7.2.1. The ECDSA curves are over prime fields only, drawn from Appendix D.1.2 of [FIPS-186-3]. X.509v3 ECDSA Signatures (Signature Type "X509") are discussed in Section 7.2.2 โจ
5.2. Hello Message โจ
okay so the Hello message is the first thing you send to say hi ๐ and it has the layout shown in Figure 3 ๐
every ZRTP message starts with the preamble value 0x505a, then comes a 16-bit length counted in 32-bit words ๐ that length covers only the ZRTP message (preamble and length included), not the ZRTP packet header or CRC ๐ง right after the length you get the 8-octet Message Type โจ
next up is a 4-character string for the version (ver) of the ZRTP protocol, which is "1.10" for this spec ๐ then there's the Client Identifier (cid), which is 4 words long and tells everyone which vendor and release of ZRTP software you're running ๐ธ the 256-bit hash image H3 is defined in Section 9 ๐ฅ and the ZID is the 96-bit unique identifier for the ZRTP endpoint, which is defined in Section 4.9 ๐ฆ
the next four bits are three flag bits, and they're giving main character energy ๐
- The Signature-capable flag (S) says this Hello came from an endpoint that can parse and verify digital signatures, as described in Section 7.2 ๐ if signatures aren't supported, the (S) flag MUST be set to zero ๐
- The MiTM flag (M) is a Boolean that's set to true if and only if this Hello is sent from a device (usually a PBX) that can send an SASrelay message (Section 5.13) ๐
- The Passive flag (P) is a Boolean that's normally false ๐ซถ it's set to true if and only if the Hello comes from a device that's configured to never send a Commit message (Section 5.4). that means it can't start secure sessions, but it can still act as a responder โจ
the next 8 bits are unused, and they SHOULD be set to zero when sent and MUST be ignored on receipt ๐ no drama, just vibes ๐
then there's a list of supported Hash algorithms, Cipher algorithms, SRTP Auth Tag Types, Key Agreement Types, and SAS Types ๐ each list has a count: hc=hash count, cc=cipher count, ac=auth tag count, kc=key agreement count, and sc=sas count. the values for these algorithms are in Tables 2, 3, 4, 5, and 6 ๐ a count of zero means only the mandatory-to-implement algorithms are supported ๐ฆ mandatory algorithms MAY be included in the list ๐ซถ the order of the list shows the endpoint's preferences ๐ and if a mandatory algorithm isn't in the list, it gets implicitly added to the end for preference ๐ญ
the 64-bit MAC at the very end covers the whole message (not including the MAC itself), and it uses the MAC algorithm from Section 5.1.2.2 ๐ the MAC key is the sender's H2 (defined in Section 9), which means the receiver can't check the MAC until it learns the sender's H2 later in the protocol ๐ธ wild, right? ๐
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="Hello " (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| version="1.10" (1 word) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Client Identifier (4 words) |
| |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Hash image H3 (8 words) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| ZID (3 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0|S|M|P| unused (zeros)| hc | cc | ac | kc | sc |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| hash algorithms (0 to 7 values) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cipher algorithms (0 to 7 values) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| auth tag types (0 to 7 values) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Key Agreement Types (0 to 7 values) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SAS Types (0 to 7 values) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| MAC (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 3: Hello Message Format ๐ง
5.3. HelloACK Message ๐
the HelloACK is the "got it!" message ๐ it's used to stop retransmissions of a Hello message ๐ซถ a HelloACK gets sent no matter whether the version number in the Hello is supported or whether the algorithm list is supported ๐ receiving a HelloACK is what stops the Hello from being retransmitted ๐ the format is in the figure below โจ also, an Initiator can send a Commit message in place of a HelloACK, but only if that Commit is ready to go promptly ๐ฅ
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=3 words |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="HelloACK" (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 4: HelloACK Message Format ๐ฆ
5.4. Commit Message ๐
okay so the Commit message is the one that kicks off the key agreement process ๐โจ. it only gets sent once BOTH sides have received a Hello message, which means you need a Hello and a HelloACK before you can send it ๐. there are three subtypes (Figures 5, 6, and 7 are the vibes, see below) ๐ฆ
the Commit message starts with the Message Type Block, then comes the 256-bit hash image H2 (defined in Section 9) ๐ธ. next up is the initiator's ZID, which is the 96-bit-long unique identifier for the ZRTP endpoint, and it MUST have the same value as the one used in the Hello message ๐ . it has to match, bestie, no exceptions ๐ญ
then there's a list of the algorithms the initiator picked: hash, cipher, auth tag type, key agreement, and sas type ๐ง. for a DH Commit, the hash value hvi is a hash of the DHPart2 of the Initiator and the Responder's Hello message, as explained in Section 4.4.1.1 ๐
the 64-bit MAC at the very end covers the whole message (everything except the MAC itself) ๐ฅ. it's computed with the MAC algorithm from Section 5.1.2.2, and the MAC key is the sender's H1 (defined in Section 9) ๐ซถ. the twist is that the receiving party can't actually check the MAC yet, because it won't know the sender's H1 value until later in the protocol ๐ iykyk, it's giving suspense ๐
Figure 5 is the DH Commit, and it's giving main character energy ๐
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=29 words |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="Commit " (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Hash image H2 (8 words) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| ZID (3 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| hash algorithm |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cipher algorithm |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| auth tag type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Key Agreement Type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SAS Type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| hvi (8 words) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| MAC (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 5: DH Commit Message Format
Figure 6 is the Multistream Commit, and it's the Key Agreement Type = "Mult" glow-up ๐ง๐
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=25 words |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="Commit " (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Hash image H2 (8 words) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| ZID (3 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| hash algorithm |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cipher algorithm |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| auth tag type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Key Agreement Type = "Mult" |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SAS Type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| nonce (4 words) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| MAC (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 6: Multistream Commit Message Format
and last but not least, Figure 7 is the Preshared Commit ๐ it's the Key Agreement Type = "Prsh" era, with a keyID slot tucked in before the MAC ๐งธ
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=27 words |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="Commit " (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Hash image H2 (8 words) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| ZID (3 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| hash algorithm |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cipher algorithm |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| auth tag type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Key Agreement Type = "Prsh" |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| SAS Type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| nonce (4 words) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| keyID (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| MAC (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 7: Preshared Commit Message Format
5.5. DHPart1 Message ๐
okay so the DHPart1 message ๐ is the one that kicks off the DH exchange โจ. The Responder sends it if it gets a valid Commit message from the Initiator. The length of the pvr value and the length of the whole DHPart1 message both depend on which Key Agreement Type was picked ๐ฆ. You can find that info in the table in Section 5.1.5, and Multistream and Preshared modes don't send DHPart1 or DHPart2 at all ๐ .
The 256-bit hash image H1 is defined in Section 9 ๐ธ.
Next up are four parameters that are non-invertible hashes (computed in Section 4.3.1) of possible shared secrets, which get used when making the ZRTP secret s0 ๐. The first two, rs1IDr and rs2IDr, are hashes of the responder's two retained shared secrets, truncated to 64 bits ๐ซถ. Then there's auxsecretIDr, a hash of the responder's auxsecret (defined in Section 4.3), also truncated to 64 bits. The last one is a hash of the trusted MiTM PBX shared secret pbxsecret, which is defined in Section 7.3.1 ๐.
The 64-bit MAC at the very end covers the whole message, minus the MAC itself ๐ง. It uses the MAC algorithm from Section 5.1.2.2. The MAC key is the sender's H0 (defined in Section 9), so the receiving party can't check the MAC until it learns the sender's H0 later in the protocol ๐ญ. Wild, right? ๐
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=depends on KA Type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="DHPart1 " (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Hash image H1 (8 words) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rs1IDr (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rs2IDr (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| auxsecretIDr (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| pbxsecretIDr (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| pvr (length depends on KA Type) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| MAC (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 8: DHPart1 Message Format ๐
5.6. DHPart2 Message ๐ฆ
the DHPart2 message โจ (shown in Figure 9) is the one that wraps up the DH exchange ๐. The Initiator sends it after it gets a valid DHPart1 message from the Responder ๐ . Same deal as before: the length of the pvi value and the length of the DHPart2 message depend on the Key Agreement Type, and the table in Section 5.1.5 has the details ๐. Multistream and Preshared modes skip both DHPart1 and DHPart2 entirely ๐.
The 256-bit hash image H1 is defined in Section 9 ๐ธ.
The next four parameters are non-invertible hashes (computed in Section 4.3.1) of possible shared secrets used to generate the ZRTP secret s0 ๐. The first two, rs1IDi and rs2IDi, are hashes of the initiator's two retained shared secrets, truncated to 64 bits ๐ซถ. Next comes auxsecretIDi, a hash of the initiator's auxsecret (defined in Section 4.3), truncated to 64 bits. The final parameter is a hash of the trusted MiTM PBX shared secret pbxsecret, defined in Section 7.3.1 ๐.
The 64-bit MAC at the end covers the whole message except the MAC itself ๐ง. It uses the MAC algorithm defined in Section 5.1.2.2. The MAC key is the sender's H0 (defined in Section 9), so the receiving party can't verify the MAC until it knows the sender's H0 value later in the protocol ๐ญ.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=depends on KA Type |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="DHPart2 " (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Hash image H1 (8 words) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rs1IDi (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rs2IDi (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| auxsecretIDi (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| pbxsecretIDi (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| pvi (length depends on KA Type) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| MAC (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 9: DHPart2 Message Format ๐
5.7. Confirm1 and Confirm2 Messages โจ
okay so this is where the Confirm messages come in and they're giving security ๐๐ . The Responder sends Confirm1 after it gets a valid DHPart2 message, once the SRTP session key and parameters are all negotiated. The Initiator replies with Confirm2 to that Confirm1. Here's the layout (Figure 10 ๐).
The message starts with the Message Type Block, either "Confirm1" or "Confirm2" ๐. Next up is the confirm_mac, which is a MAC computed over the encrypted part of the message (the bit wrapped in "====" in Figure 10). This confirm_mac gets keyed and computed the way Section 4.6 lays out. After that come 16 octets holding the CFB Initialization Vector, and the rest of the message is encrypted with CFB and protected by that confirm_mac ๐.
The first thing inside the encrypted region is the hash preimage H0, which Section 9 explains in full ๐ง.
Next are 15 bits that aren't used for anything ๐ซฅ. They SHOULD be set to zero when sent, and they MUST be ignored when received in Confirm1 or Confirm2 messages.
Then come 9 bits for the signature length ๐. If there's no SAS signature (Section 7.2), all of those bits are zero. The length is counted in words and includes the signature type block. If the signature's octet count isn't a multiple of 4, zeros get added to pad it to a word boundary. If there's no signature at all, the whole Confirm1 or Confirm2 message comes out to 19 words total ๐.
After that is an 8-bit flags field ๐ฉ. Any undefined flags are set to zero and ignored. Four flags are defined right now:
- The PBX Enrollment flag (E) is a Boolean bit, described in Section 7.3.1.
- The SAS Verified flag (V) is a Boolean bit, described in Section 7.1.
- The Allow Clear flag (A) is a Boolean bit, described in Section 4.7.2.
- The Disclosure Flag (D) is a Boolean bit, described in Section 11.
The cache expiration interval gets its own explanation over in Section 4.9 ๐ฐ๏ธ.
If the signature length (in words) isn't zero, a signature type block shows up along with a signature block ๐๏ธ. The signature block carries the signature plus the key (or a link to the key) that was used to make it (Section 7.2).
Now for the encryption part ๐. CFB mode [NIST-SP800-38A] runs with a feedback length of 128 bits and a full cipher block. The final block gets truncated so it matches the exact length of the encrypted data. The CFB Initialization Vector is a 128-bit random nonce, and the block cipher algorithm and key size match the negotiated block cipher from Section 5.1.3 for media encryption. CFB encrypts everything in Confirm1 from right after the CFB IV all the way to the end of the message (that encrypted region is the stuff in "====" in Figure 10) โจ.
Last detail: the Responder uses zrtpkeyr to encrypt Confirm1, and the Initiator uses zrtpkeyi to encrypt Confirm2 ๐ซถ.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=variable |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="Confirm1" or "Confirm2" (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| confirm_mac (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| CFB Initialization Vector (4 words) |
| |
| |
+===============================================================+
| |
| Hash preimage H0 (8 words) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Unused (15 bits of zeros) | sig len (9 bits)|0 0 0 0|E|V|A|D|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| cache expiration interval (1 word) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| optional signature type block (1 word if present) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| optional signature block (variable length) |
| . . . |
| |
| |
+===============================================================+
Figure 10: Confirm1 and Confirm2 Message Format
5.8. Conf2ACK Message
the Conf2ACK is the reply ๐. The Responder sends it when it gets a valid Confirm2 message. Here's the format (the figure below ๐). Once a Conf2ACK arrives, the Confirm2 message stops getting retransmitted ๐. Also, the first SRTP media from the Responder (the one with a valid SRTP auth tag) stops those Confirm2 retransmissions too, so that's a second way to end them ๐ฆ.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=3 words |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="Conf2ACK" (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 11: Conf2ACK Message Format
5.9. Error Message
the Error message is the "something went wrong, we're stopping" message ๐จ. It's sent to end an in-process ZRTP key agreement exchange because of an error. The format is below, and Section 4.7.1 covers how it gets used ๐.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=4 words |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="Error " (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Integer Error Code (1 word) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 12: Error Message Format
here are the defined hexadecimal Error Codes, all in one handy table ๐.
Error Code | Meaning
-----------------------------------------------------------
0x10 | Malformed packet (CRC OK, but wrong structure)
-----------------------------------------------------------
0x20 | Critical software error
-----------------------------------------------------------
0x30 | Unsupported ZRTP version
-----------------------------------------------------------
0x40 | Hello components mismatch
-----------------------------------------------------------
0x51 | Hash Type not supported
-----------------------------------------------------------
0x52 | Cipher Type not supported
-----------------------------------------------------------
0x53 | Public key exchange not supported
-----------------------------------------------------------
0x54 | SRTP auth tag not supported
-----------------------------------------------------------
0x55 | SAS rendering scheme not supported
-----------------------------------------------------------
0x56 | No shared secret available, DH mode required
-----------------------------------------------------------
0x61 | DH Error: bad pvi or pvr ( == 1, 0, or p-1)
-----------------------------------------------------------
0x62 | DH Error: hvi != hashed data
-----------------------------------------------------------
0x63 | Received relayed SAS from untrusted MiTM
-----------------------------------------------------------
0x70 | Auth Error: Bad Confirm pkt MAC
-----------------------------------------------------------
0x80 | Nonce reuse
-----------------------------------------------------------
0x90 | Equal ZIDs in Hello
-----------------------------------------------------------
0x91 | SSRC collision
-----------------------------------------------------------
0xA0 | Service unavailable
-----------------------------------------------------------
0xB0 | Protocol timeout error
-----------------------------------------------------------
0x100 | GoClear message received, but not allowed
Table 8. ZRTP Error Codes
5.10. ErrorACK Message
the ErrorACK is sent back in response to an Error message ๐. Receiving one stops retransmission of the Error message. Here's the format ๐.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=3 words |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="ErrorACK" (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 13: ErrorACK Message Format
5.11. GoClear Message
okay the GoClear message is the optional one ๐. Support for it is OPTIONAL in the protocol, and it's used to switch from SRTP back to RTP ๐. The clear_mac authenticates it, so bogus GoClear messages from an attacker can be spotted and tossed out ๐๏ธ. Section 4.7.2 explains how GoClear gets used. Here's the format ๐.
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=5 words |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="GoClear " (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| clear_mac (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 14: GoClear Message Format
5.12. ClearACK Message โจ
Okay bestie, the ClearACK is the little "got it, cleared" reply ๐ It's OPTIONAL to support in the protocol, and it gets sent to acknowledge a GoClear. Only send one if the clear_mac from that GoClear actually authenticates. If it doesn't check out, nothing comes back at all ๐ Here's what it looks like ๐
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=3 words |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="ClearACK" (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 15: ClearACK Message Format ๐
5.13. SASrelay Message ๐
Okay, the SASrelay is giving middleman energy ๐ต๏ธโโ๏ธ It gets sent by a trusted MiTM, most often a PBX. It's not a reply to some packet. The trusted MiTM starts it on its own (Section 7.3). It can only show up after the rest of the ZRTP key negotiations are done, meaning after the Confirm messages and their ACKs. It also has to wait until the trusted MiTM has finished key negotiations with the other party, because the other party's SAS is the thing getting relayed ๐ฅ It gets sent with retry logic until a RelayACK message (Section 5.14) shows up, or the retry schedule runs out.
If a device (usually a PBX) sends an SASrelay message, it MUST have already declared itself as a MiTM device by setting the MiTM (M) flag in the Hello message (Section 5.2). If the receiver never got a Hello with the MiTM (M) flag set, the Relayed SAS SHOULD NOT be rendered. A RelayACK still gets sent in that case, but no Error message goes out ๐
The SASrelay layout is in Figure 16 โจ It carries the Message Type Block "SASrelay". Then comes a MAC computed over the encrypted part of the message (the bit wrapped in "====" in Figure 16). That MAC is keyed the same way as the confirm_mac in the Confirm messages (see Section 4.6). The next 16 octets hold the CFB Initialization Vector, and the rest of the message gets encrypted with CFB and protected by the MAC ๐
The next 15 bits aren't used. They SHOULD be set to zero when sent, and they MUST be ignored when received in SASrelay messages.
The next 9 bits hold the signature length. The trusted MiTM MAY compute a digital signature on the SAS hash (as described in Section 7.2), using a persistent signing key that it owns ๐ If there's no SAS signature, all of those bits are zero. The signature length is counted in words and includes the signature type block. If the signature octet count isn't a multiple of 4, zeros get added to pad it out to a word boundary. And if there's no signature block at all, the whole SASrelay message comes out to 19 words total ๐
The next 8 bits are flags. Undefined flags are set to zero and ignored. Three flags are defined right now: the Disclosure Flag (D), which is a Boolean bit defined in Section 11; the Allow Clear flag (A), a Boolean bit defined in Section 4.7.2; and the SAS Verified flag (V), a Boolean bit defined in Section 7.1. These are updated versions of the same flags that came in the Confirm message. They've been refreshed to reflect the new flag info the PBX relayed from the other party ๐
The next 32-bit word holds the SAS rendering scheme for the relayed sashash. It's the same rendering scheme the other party used on the other side of the trusted MiTM. Section 7.3 explains how the PBX decides whether the ZRTP client thinks of the PBX as a trusted MiTM ๐ If the PBX decides the ZRTP client trusts it, the next 8 words carry the sashash relayed from the other party. The first 32-bit word of that sashash is the sasvalue, which can be shown to the user using the specified SAS rendering scheme. If this SASrelay is going to a ZRTP client that doesn't trust this MiTM, the recipient ignores the sashash, and the PBX should set it to zeros.
If the signature length (in words) isn't zero, a signature type block shows up along with a signature block. The signature block holds the signature plus either the key or a link to the key that was used to make the signature (Section 7.2) ๐งพ
CFB mode [NIST-SP800-38A] gets applied with a feedback length of 128 bits, a full cipher block, and the final block truncated to match the exact length of the encrypted data. The CFB Initialization Vector is a 128-bit random nonce ๐ฒ The block cipher and key size match the negotiated block cipher (Section 5.1.3) used for media encryption. CFB encrypts everything from right after the CFB IV all the way to the end of the message (the encrypted region is the bit wrapped in "====" in Figure 16).
Whether the encryption key is zrtpkeyi or zrtpkeyr depends on the role the trusted MiTM took during the ZRTP key negotiation. If it was the initiator, it's zrtpkeyi. If it was the responder, it's zrtpkeyr ๐ธ
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=variable |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="SASrelay" (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| MAC (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| CFB Initialization Vector (4 words) |
| |
| |
+===============================================================+
| Unused (15 bits of zeros) | sig len (9 bits)|0 0 0 0|0|V|A|D|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| rendering scheme of relayed SAS (1 word) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Trusted MiTM relayed sashash (8 words) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| optional signature type block (1 word if present) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| optional signature block (variable length) |
| . . . |
| |
| |
+===============================================================+
Figure 16: SASrelay Message Format ๐ฆ
5.14. RelayACK Message ๐ซถ
The RelayACK is the reply to a valid SASrelay message ๐ Its layout is in the figure below. Once a RelayACK arrives, the sender stops retransmitting the SASrelay, so it's basically the "all good, stop sending" text ๐
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=3 words |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="RelayACK" (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 17: RelayACK Message Format โจ
5.15. Ping Message ๐ง
Okay, the Ping and PingACK messages are kind of their own little world ๐ They have nothing to do with the rest of the ZRTP protocol. No ZRTP endpoint is required to generate a Ping message, but every ZRTP endpoint MUST respond to a Ping message with a PingACK message ๐
Even though Ping and PingACK don't affect the rest of ZRTP, they're in the spec because they make "bump-in-the-wire" ZRTP proxies (Section 10) so much easier to design (notably, [Zfone]) ๐ฅ They let proxies work out which ZRTP endpoints are tied to which media streams without needing help from the signaling layer. Main character proxy energy ๐
Before sending a ZRTP Hello message, a ZRTP proxy MAY send a Ping message to sort out which RTP media streams are connected to particular ZRTP endpoints. Ping messages only come from ZRTP proxies. If neither party is a ZRTP proxy, you won't run into any Ping messages at all ๐ How Ping retransmission works is covered in Section 6.
The Ping message (Figure 18) carries an "EndpointHash", which is defined in Section 5.16 ๐ฆ
The Ping message also has a version number that says which version of PingACK it's asking for. If the Ping responder supports that version, you'll get back a PingACK matching that version's format. Otherwise, you might get a PingACK with a lower version number ๐
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=6 words |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="Ping " (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| version="1.10" (1 word) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| EndpointHash (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 18: Ping Message Format ๐
5.16. PingACK Message ๐ฆ
okay so a PingACK is only ever sent as a reply to a Ping, bestie ๐ A ZRTP endpoint MUST respond to a Ping with a PingACK message. The version you want is written right inside the Ping, and if that version is supported, the PingACK MUST match it. If the Ping asks for a version we don't support, the PingACK SHOULD come back in the format of the highest version the Ping responder knows about. Only version "1.10" is supported in this spec ๐
The PingACK carries its own 64-bit EndpointHash, which is different from the EndpointHash in the other party's Ping ๐ง It is REQUIRED that two participants in a call are extremely unlikely to share an EndpointHash, and that an EndpointHash stays the same value between calls. For a normal ZRTP endpoint, like a ZRTP-enabled VoIP client, the EndpointHash can just be the truncated ZID โจ For a ZRTP endpoint like a PBX with a bunch of endpoints behind it, every endpoint needs its own distinct value ๐ The recommendation is a truncated hash of the ZRTP endpoint's ZID, concatenated with something unique about the actual endpoint or phone behind the PBX. That could be the SIP URI of the phone, the PBX extension number, or the local IP address of the phone, whichever one is easiest to get in your app environment ๐
EndpointHash = hash(ZID || SIP URI of the endpoint)
EndpointHash = hash(ZID || PBX extension number of the endpoint)
EndpointHash = hash(ZID || local IP address of the endpoint)
Any of these formulas gives you uniqueness, whether the ZRTP connection ends at a VoIP client, or whether a PBX is terminating it for multiple VoIP phones in a conference call. All of those phones share the PBX's ZID, but each one has its own IP address behind the PBX ๐ธ Also, there is no requirement that both parties use the same hash function, so don't stress about that one ๐
The PingACK contains the EndpointHash of the PingACK sender AND the EndpointHash of the Ping sender ๐ซถ The Source Identifier (SSRC) from the ZRTP header of the Ping packet (Figure 2) gets copied into the PingACK message body (Figure 19). Important detail: that SSRC is NOT the SSRC of the PingACK sender ๐
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|0 1 0 1 0 0 0 0 0 1 0 1 1 0 1 0| length=9 words |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Message Type Block="PingACK " (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| version="1.10" (1 word) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| EndpointHash of PingACK Sender (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| EndpointHash of Received Ping (2 words) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Source Identifier (SSRC) of Received Ping (1 word) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 19: PingACK Message Format
6. Retransmissions โจ
okay this part is about the resend schedule ๐ฅ ZRTP runs two retransmission timers, T1 and T2 ๐ T1 is for resending Hello messages, the ones where we might not know yet whether the other side even speaks ZRTP. T2 handles resends for all the other ZRTP messages ๐
All message retransmissions MUST be identical to the initial message, including the nonces, public values, and so on ๐ Otherwise the hashes of the message sequences might not agree, and that would be a whole mess ๐ญ
Real talk: RTP packet loss right at the start of an RTP session can be extremely high. The whole ZRTP exchange happens during that exact window, so the retransmission scheme is aggressive by design ๐ฆ Apart from DHPart1 and DHPart2, ZRTP packets are small, so this should have minimal effect on the bandwidth of the media session ๐
ZRTP endpoints MUST NOT exceed the bandwidth of the resulting media session, as determined by the offer/answer exchange in the signaling layer ๐ โโ๏ธ
The Ping message (Section 5.15) may follow the same retransmission schedule as the Hello message, but this is not required in this specification. Ping retransmission is subject to application-specific ZRTP proxy heuristics ๐ง
Hello messages get retransmitted at an interval that starts at T1 seconds and doubles after every retransmission, capping at 200 ms โจ T1 has a recommended initial value of 50 ms. A Hello message is retransmitted 20 times before giving up, so the whole Hello retry schedule runs out after 3.75 seconds (50 + 100 + 18*200 ms) ๐ A Hello's retransmissions end as soon as a HelloACK or Commit message comes in ๐
The post-Hello messages only get retransmitted by the session initiator. So only Commit, DHPart2, and Confirm2 get resent, if the matching responder message (DHPart1, Confirm1, or Conf2ACK) never shows up ๐ซถ Also, Confirm2 retransmission can stop early once the responder sends the first SRTP media (with a valid SRTP auth tag) ๐ธ
GoClear, Error, and SASrelay messages can be started and retransmitted by either party, and answered by the other party, no matter who the overall session initiator is ๐ They get retransmitted if the matching response (ClearACK, ErrorACK, or RelayACK) doesn't arrive ๐ฅ
Non-Hello (and non-Ping) messages get retransmitted at an interval that starts at T2 seconds and doubles after every retransmission, capping at 1200 ms. T2 has a recommended initial value of 150 ms ๐ Each non-Hello message gets retransmitted 10 times before giving up, so the full schedule runs out after 9.45 seconds (150 + 300 + 600 + 7*1200 ms) ๐ญ Only the initiator does retransmissions. Every message has a response that stops its retransmissions, as the table below shows ๐ Higher T2 values mean retransmissions will most likely only happen when packets actually get lost ๐ฆ
Message Acknowledgement Message
------- -----------------------
Hello HelloACK or Commit
Commit DHPart1 or Confirm1
DHPart2 Confirm1
Confirm2 Conf2ACK or SRTP media
GoClear ClearACK
Error ErrorACK
SASrelay RelayACK
Ping PingACK
Table 9. Retransmitted ZRTP Messages and Responses
The retry schedule has to handle more than just packet loss. It also needs to cover slow or heavily loaded peers that need extra time to finish their DH calculations ๐ง The following mitigations are recommended:
- Slow or heavily loaded ZRTP endpoints that are at risk of taking too long to perform their DH calculation SHOULD use a HelloACK message instead of a Commit message to reply to a Hello from the other party. ๐ข
- If a ZRTP endpoint has evidence that the other party is a ZRTP endpoint, by receiving a Hello message or Ping message, or by receiving a Hello Hash in the signaling layer, it SHOULD extend its own Hello retry schedule to span at least 12 seconds of retries. If this extended Hello retry schedule is exhausted without receiving a HelloACK or Commit message, a late Commit message from the peer SHOULD still be accepted. ๐ซถ
These recommended retransmission intervals are designed for a typical broadband Internet connection ๐ On high-latency channels, like some mobile phone environments or geostationary satellites, a different retransmission schedule may be used. The initial value for the T1 or T2 timer SHOULD be increased to be no less than the round-trip time the channel provides โจ It should also account for the time needed to send the whole message and the whole reply, plus a reasonable estimate for the DH calculation ๐ฐ๏ธ
ZRTP has its own retransmission schedule because it rides along with RTP, usually over UDP ๐ In unusual cases RTP can run over a non-UDP transport, like TCP or DCCP, which has its own built-in retransmission mechanism ๐ It can be hard for a ZRTP endpoint to tell that TCP is in use when media relays are involved ๐ The ZRTP endpoint might only be sending UDP, but a media relay along the path could convert UDP to TCP for part of the journey ๐ณ Or, if the ZRTP endpoint is sending TCP, the relay might be converting TCP to UDP ๐ There have been empirical observations of this in the wild ๐ When TCP is in use, ZRTP and TCP might together generate some extra retransmissions. It's tempting to avoid that by switching off the ZRTP retransmission schedule on a TCP channel, but that would risk breaking the protocol, because the path may not be TCP all the way to the remote ZRTP endpoint ๐จ A ZRTP exchange only takes a few packets, so trying to optimize away the extra retransmissions in that case isn't worth the risk ๐ โโ๏ธ
After receiving a Commit message, but before receiving a Confirm2 message, if a ZRTP responder receives no ZRTP messages for more than 10 seconds, the responder MAY send a protocol timeout Error message and terminate the ZRTP protocol โฐ
7. Short Authentication String โจ
okay so this section is all about the SAS (Short Authentication String) in ZRTP, and it's literally the thing that keeps your call private ๐๐
. The SAS can get compared by the humans reading it out loud, or the endpoints can check an OPTIONAL digital signature (described in Section 7.2) that's sent in the Confirm1 or Confirm2 messages ๐.
the hash commitment in the DH exchange (Section 4.4.1.1) means the attacker only gets one guess to make the right SAS, so the SAS can be super short ๐ฆ. a 16-bit SAS, for example, gives the attacker just one chance out of 65536 to slip by unnoticed ๐ญ. how the hash commitment makes the SAS that short is explained in Section 4.4.1.1, so go check that if you're curious ๐ง.
there's only one SAS value per call ๐. it's the one for the first media stream that gets set up, and it's calculated in Section 4.5.2. that same SAS covers every media stream in the same session, so you don't need a new one for each stream ๐.
the SAS SHOULD be rendered to the user for authentication ๐. how it shows up on both endpoints' screens depends on the SAS Type that got agreed on in the Commit message. see Section 5.1.6 for the full rundown of how the SAS gets shown to the user ๐
.
the SAS isn't a secret ๐คทโโ๏ธ, but both ends have to compare it and make sure it matches ๐. the two users do the comparing with their actual voices, their actual ears, and their actual brains ๐ง . if it doesn't match, that's a sign of a MiTM attack ๐จ.
it is worse than useless and absolutely unsafe to rely on a robot voice from the remote endpoint to compare the SAS ๐, because a MiTM can totally forge a robot voice. the SAS verbal comparison can only happen with a real live human on the other end ๐ซถ.
7.1. SAS Verified Flag
the SAS Verified flag (V) gets set once the user says the SAS comparison went through successfully ๐ธ. it's exchanged securely in the Confirm1 and Confirm2 messages (Figure 10) of the next session โจ. so basically each party sends the SAS Verified flag from the previous session inside the Confirm message of the current one ๐. it's totally reasonable for a ZRTP endpoint to never set the SAS Verified flag, since that would mean adding extra UI complexity so the user can set it ๐. the flag isn't required to be set ๐, but if the client software has access to it, it can tell the user that the SAS verify was done in a previous session ๐.
no matter whether there's a UI element for the user to set the SAS Verified flag, it's still worth caching a shared secret ๐, because that cuts down on the attacker's chances in the next call ๐ฅ.
okay now the drama ๐ญ. if the users do the SAS comparison and it actually fails to match, that means there's a very resourceful MiTM ๐. if it's the first call, the MiTM was there from the very first call, which is impressive enough ๐. if it happens in a later call, the MiTM also has to know the cached shared secret, because you couldn't have carried out any voice traffic at all unless the session key was correctly computed and is also known to the attacker ๐ซ . that means the MiTM had to be present in all the previous sessions since the very first shared secret got created ๐ณ. that's an absolutely iconic level of resourceful attacker ๐. and if at any point they stop participating as a MiTM on one of your calls, the protocol will notice the cached shared secret isn't valid anymore ๐จ, because it was really two different shared secrets the whole time, one between Alice and the attacker and the other between the attacker and Bob ๐ . so the continuity of the cached shared secrets lets us catch the MiTM when they sneak into the ongoing relationship, and also when they leave ๐ฆ. and if the attacker tries to stay in a long lineage of calls but skips even one DH MiTM attack, they're permanently excluded ๐ โโ๏ธ. they can't resync with the chain of cached shared secrets ever again ๐.
a user interface element (like a checkbox or a button) is needed so the user can tell the software the SAS verify was successful ๐. that makes the software set the SAS Verified flag (V), which, together with our cached shared secret, means the SAS procedure isn't needed in the next call ๐. you can also add another UI element so the user can say they actually detected a SAS mismatch, which points to a MiTM attack ๐จ. then the software can take the right action, clearing the SAS Verified flag and erasing the cached shared secret from this session ๐งน. whether that extra UI complexity is worth it is up to the implementer ๐.
if the SAS matches, that means there's no MiTM, which also means it's now safe to trust a cached shared secret for later calls โจ. but if inattentive users don't bother checking the SAS, we honestly don't know whether there's a MiTM or not ๐ฌ. so even if we set up a new cached shared secret, there's a risk that the potential attacker gets more chances to keep inserting themselves into the call until somebody finally gets around to checking the SAS ๐. if the SAS matches, though, no attacker was present in any previous session since we started propagating cached shared secrets ๐ฅน, because this session and all the previous ones were also authenticated through a continuous lineage of shared secrets ๐.
7.2. Signing the SAS
in most apps, it's desirable to avoid the extra complexity of a PKI-backed digital signature ๐ โโ๏ธ, which is exactly why ZRTP is designed not to require one ๐ . still, sometimes it's hard to get two humans to verbally compare the SAS ๐ฉ, or an app is already using an existing PKI and wants to use it to level up ZRTP ๐.
so to handle those cases, ZRTP has an OPTIONAL signature feature ๐, which lets the SAS be checked without any human participation ๐ค. the SAS MAY be signed, and the signature can be sent inside the Confirm1, Confirm2 (Figure 10), or SASrelay (Figure 16) messages ๐. the signature type (Section 5.1.7), the length of the signature, and the key used to create it (or a link to that key) all get sent along with the signature ๐. the signature is calculated across the entire SAS hash result (sashash), which is what the sasvalue was derived from ๐งฎ. the signatures exchanged in the encrypted Confirm1, Confirm2, or SASrelay messages MAY be used to authenticate the ZRTP exchange ๐ซถ. a signature may only be sent in the initial media stream of a DH or ECDH ZRTP exchange ๐ธ, and not in Multistream mode ๐ซ.
the signature gets sent, but the thing being signed, the sashash, doesn't get sent with it in the Confirm message ๐, because both parties already calculated the sashash independently โจ. that's not the case for the SASrelay message, which has to relay the sashash ๐. to avoid pointless signature calculations ๐ง , a signature SHOULD NOT be sent if the other ZRTP endpoint didn't set the (S) flag in the Hello message (Section 5.2) ๐
โโ๏ธ.
the hash algorithm used in the digital signature is independent of the hash used in the sashash ๐งฉ. the sashash is determined by the negotiated Hash Type (Section 5.1.2), while the hash used by the signature is defined separately by the digital signature algorithm ๐ซ. for example, the sashash might be based on SHA-256, while the digital signature might use SHA-384, if an ECDSA P-384 key is used ๐ฆ.
the sashash is always truncated to 256 bits ๐. if it's shorter than the signature hash, the security isn't weakened, because the hash commitment stops the attacker from searching for sashash collisions ๐.
ECDSA algorithms can be used with either OpenPGP-formatted keys or X.509v3 certificates ๐. if the ZRTP key exchange is ECDH and the SAS is signed, then the signature SHOULD be ECDSA ๐ , and it SHOULD use the same size curve as the ECDH exchange if an ECDSA key of that size is available ๐.
if a ZRTP endpoint supports incoming signatures (you can tell by the (S) flag being set in the Hello message) โจ, it SHOULD be able to parse signatures from the other endpoint in OpenPGP format ๐, and it MUST be able to parse them in X.509v3 format ๐ซถ. if the incoming signature is in an unsupported format, or the trust model doesn't lead to a trusted introducer or a trusted certificate authority (CA) ๐ค, another authentication method can be used if one's available ๐, like the SAS compare or a cached shared secret from a previous session ๐. if none of those methods are available, it's up to the ZRTP user agent and the user to decide whether to go ahead with the call, after the user gets informed ๐โโ๏ธ.
both ECDSA and DSA [FIPS-186-3] have a feature that lets most of the signature calculation happen in advance of the session โก, which cuts down latency during call setup ๐. that's super useful for low-power mobile handsets ๐.
ECDSA is preferred because it has compact keys as well as compact signatures ๐ฆ. if the signature along with its public key certificate is not compact enough, the Confirm message can get too long for the maximum transmission unit (MTU) size, and then UDP fragmentation might happen ๐ฑ. some firewalls and NATs throw away fragmented UDP packets ๐๏ธ, and that would make the ZRTP exchange fail ๐. it is RECOMMENDED that a ZRTP endpoint avoid sending signatures if they would cause UDP fragmentation ๐. for a discussion of MTU size and PMTU discovery, see [RFC1191] and [RFC1981] ๐.
from a packet-size perspective, ECDSA and DSA both produce equally compact signatures for a given signature strength ๐. DSA keys are much bigger than ECDSA keys ๐, but for OpenPGP signatures, the public key doesn't get sent along with the signature ๐.
all signatures generated MUST use only NIST-approved hash algorithms ๐, and they MUST avoid using SHA1 ๐ซ. this applies to both OpenPGP and X.509v3 signatures ๐. NIST-approved hash algorithms are found in [FIPS-180-3] or its SHA-3 successor ๐. all ECDSA curves used throughout this spec are over prime fields, drawn from Appendix D.1.2 of [FIPS-186-3] โจ.
7.2.1. OpenPGP Signatures โจ
okay so if the SAS Signature Type (Section 5.1.7) says OpenPGP ("PGP ", trailing space and all ๐
), the signature fields are laid out like this ๐
The first field after the 4-octet Signature Type Block is the OpenPGP signature. Its format and the algorithms that make it come from [RFC4880]. It's a complete OpenPGP version 4 signature in binary form (not Radix-64), as RFC 4880, Section 5.2.3 describes, wrapped in the full OpenPGP packet syntax. You can parse the length straight out of the signature, and it depends on the type and length of the signing key ๐ฅ
If OpenPGP signatures are supported, an implementation SHOULD NOT generate signatures with any other signature algorithm except DSA or ECDSA (ECDSA is a reserved algorithm type in RFC 4880). It MAY accept other signature types from the other party, though ๐ฆ DSA signatures with keys shorter than 2048 bits or longer than 3072 bits MUST NOT be generated. no exceptions bestie ๐ โโ๏ธ
Implementers should be aware that ECDSA signatures for OpenPGP are expected to show up once the work in progress [ECC-OpenPGP] becomes an RFC ๐ Any use of ECDSA signatures in ZRTP SHOULD NOT generate signatures with ECDSA key sizes other than P-224, P-256, and P-384, as defined in [FIPS-186-3] ๐
RFC 4880, Section 5.2.3.18, explains how to embed a URI for the preferred key server inside an OpenPGP signature. The URI should be fully specified so the recipient can get the public key of the signing key that made the signature. This URI MUST be present ๐ซถ It's up to the recipient to fetch that public key and decide whether it's valid, using the OpenPGP trust model discussed in [RFC4880] ๐ง
The contents of Figure 20 sit inside the encrypted region of the Confirm message (Figure 10) or the SASrelay message (Figure 16) ๐ธ
The total length of everything in Figure 20, key server URI included, must not exceed 511 32-bit words (2044 octets). That length, in words, goes in the signature length field of whichever Confirm or SASrelay message carries the signature. It's desirable to avoid UDP fragmentation, so the URI should be kept short ๐
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Signature Type Block = "PGP " (1 word) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| OpenPGP signature |
| (variable length) |
| . . . |
| |
+===============================================================+
Figure 20: OpenPGP Signature Format
7.2.2. ECDSA Signatures with X.509v3 Certs ๐
If the SAS Signature Type (Section 5.1.7) is "X509", the ECDSA signature fields are arranged like this ๐
The first field after the 4-octet Signature Type Block is the DER encoded X.509v3 certificate (the signed public key) of the ECDSA signing key that made the signature. The format of this certificate is specified by the NSA's Suite B Certificate and CRL Profile [RFC5759] ๐
Right after the X.509v3 certificate, at the next word boundary, comes the ECDSA signature itself. Its size depends on the size and type of the public key in that certificate. The format of the signature and the algorithms that create it are specified by [FIPS-186-3]. It's the ECDSA signature output parameters (r, s) in binary form, concatenated, in network byte order, with no truncation of leading zeros. The first half is r and the second half is s ๐ง If ECDSA P-256 is specified, the signature fills 16 words (64 octets), 32 octets each for r and s. If ECDSA P-384 is specified, it fills 24 words (96 octets), 48 octets each for r and s โจ
It's up to the recipient to use the information in the certificate and path discovery mechanisms to trace the chain back to the root CA. It is recommended that end user certificates issued for secure telephony contain appropriate path discovery links to make this easier ๐ฆ
Figure 21 shows a certificate and an ECDSA signature. All of this material lies inside the encrypted region of the Confirm message (Figure 10) or the SASrelay message (Figure 16) ๐
The total length of everything in Figure 21, including the X.509v3 certificate, must not exceed 511 32-bit words (2044 octets). That length, in words, goes in the signature length field of the Confirm or SASrelay message carrying the signature. It's desirable to avoid UDP fragmentation, so the certificate material should be kept much smaller than that limit. End user certs issued for this purpose should minimize extraneous material such as legal notices ๐
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Signature Type Block = "X509" (1 word) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| Signing key's X.509v3 certificate |
| (variable length) |
| . . . |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| ECDSA P-256 or P-384 signature |
| (16 words or 24 words) |
| . . . |
| |
+===============================================================+
Figure 21: X.509v3 ECDSA Signature Format
7.2.3. Signing the SAS without a PKI ๐
It's not strictly necessary to use a PKI to back the public key that signs the SAS ๐ For example, you can use a self-signed X.509v3 certificate or an OpenPGP key that no other key signed. In that case, the same key continuity technique used by SSH [RFC4251] may be used. The public key is cached locally the first time it's encountered, and when that same public key shows up again in later sessions, it's deemed not to be a MiTM attack. If there's no MiTM attack in the first session, there can't be one in any later session. This is exactly how SSH does it ๐ซง
Of course, the security rests on the assumption that the MiTM did not attack in the first session. That assumption seems to hold most of the time in the SSH world ๐ฌ The user would have to be warned the first time a public key is encountered, just like in SSH. If possible, the SAS should be checked before the user consents to caching the new public key. If the SAS matches in the first session, there's no MiTM, and it's safe to cache the public key. If no SAS comparison is possible, it's up to the user, or the application, to decide whether to take a leap of faith and proceed. That's how SSH works most of the time, since SSH users never get the chance to verbally compare an SAS with anyone ๐
For a phone that is SIP-registered to a PBX, the phone may be provisioned with the PBX's public key using a trusted automated provisioning process. Even without a PKI, the phone knows that key is the correct one, because a trusted provisioning mechanism loaded it in. This makes it easy for the phone to reach several automated services a PBX commonly offers, like voice mail or a conference bridge, where no human at the PBX is around to do a verbal SAS compare ๐๐ธ The same provisioning may be used to preload the pbxsecret into the phone, which is discussed in Section 7.3.1 ๐
7.3. Relaying the SAS through a PBX ๐โจ
ZRTP is designed to use end-to-end encryption ๐, and the whole verbal comparison of the short authentication string (SAS) depends on that assumption. ๐ But in some PBX environments, like Asterisk, the PBX can end up acting as a trusted MiTM ๐. That means there are two back-to-back ZRTP connections, each with its own session keys and its own SAS, which is kinda chaotic.
Picture this ๐: Bob has a ZRTP-enabled VoIP phone registered with his company's PBX, so his phone counts as an extension of the PBX. Alice's phone isn't associated with the PBX, so she dials the PBX from outside, and a ZRTP connection gets negotiated between her phone and the PBX. Then she picks Bob's extension from the company directory in the PBX. The PBX calls Bob's phone (which could be offsite, many miles away through the Internet ๐), and a separate ZRTP connection gets negotiated between the PBX and Bob's phone. The two ZRTP sessions have different session keys and different SASs, so the SAS would be totally useless for Alice and Bob to compare verbally ๐ญ. They might even think a wiretapper is lurking because of the SAS mismatch, and that causes undue alarm ๐จ.
ZRTP has a fix for this ๐ฆ. The PBX relays the Alice/PBX SAS to Bob in a special SASrelay message, as defined in Section 5.13. That message is sent after the PBX/Bob ZRTP negotiation is complete, after the Confirm messages. Only the PBX gets to relay the SAS, because it's acting as a special trusted MiTM (trusted by whoever receives the SASrelay message). The SASrelay message protects the relayed SAS from tampering with an included MAC, similar to how the Confirm message is protected ๐. Bob's phone only accepts the relayed SAS for rendering because it was previously configured to trust the PBX. That special trust relationship can be set up through a special security enrollment procedure (Section 7.3.1). After enrollment, Bob treats the PBX as a special trusted MiTM. The result is that Alice's SAS gets rendered on Bob's phone, so Alice and Bob can verbally compare them and prevent a MiTM attack from any other untrusted MiTM ๐.
A real "bad-guy" MiTM can't use this feature to pull off a MiTM attack and relay Alice's SAS to Bob, because Bob never did a special registration ritual with the bad guy ๐ โโ๏ธ. The relayed SAS wouldn't get rendered by Bob's phone, since it didn't come from a trusted PBX. The special trust relationship is recognized because of a special shared secret set up in advance between Bob and his PBX, called pbxsecret (defined in Section 7.3.1), also known as the trusted MiTM key ๐๏ธ.
The trusted MiTM key can be stored in a special cache at the time of the initial enrollment (which only happens once for Bob's phone ๐ ). Bob's phone associates this key with the ZID of the PBX, and the PBX associates it with the ZID of Bob's phone. Once enrollment has established and stored the trusted MiTM key, it can be detected during later ZRTP session negotiations between the PBX and Bob's phone. That's because the PBX and the phone MUST pass the hash of the trusted MiTM key in the DH message, and then it gets used as part of the key agreement to calculate s0 โจ.
The PBX can work out whether a phone's ZRTP user agent trusts it. A shared trusted MiTM key showing up in the key negotiation sequence means the phone was enrolled with this PBX, so it trusts the PBX to act as a trusted MiTM ๐ค. During a key agreement with two other ZRTP endpoints, the PBX might share a trusted MiTM key with both endpoints, with only one endpoint, or with neither. If the PBX shares a trusted MiTM key with neither endpoint, the PBX MUST NOT relay the SAS. If it shares one with only one endpoint, the PBX MUST relay the SAS from one party to the other by sending an SASrelay message to the endpoint it shares a trusted MiTM key with. If it has a (separate) shared trusted MiTM key with each of the endpoints, the PBX MUST relay the SAS to only one endpoint, not both ๐.
Note: When a PBX shares trusted MiTM keys with both endpoints, it doesn't matter which endpoint gets the relayed SAS, as long as only one endpoint receives it.
The relayed SAS fields contain the SAS rendering type and the complete sashash ๐. The receiver absolutely MUST NOT render the relayed SAS if it doesn't come from a specially trusted ZRTP endpoint ๐ซ. ZRTP's security depends on not rendering a relayed SAS from an untrusted MiTM, since a MiTM attacker could be the one doing the relaying. Check out the SASrelay message definition (Figure 16) for more details ๐.
To make sure Alice and Bob both use the same SAS rendering scheme after the keys are negotiated, the PBX also sends the SASrelay message to the unenrolled party (the one that doesn't see this PBX as a trusted MiTM) ๐ฏ. That message conveys the SAS rendering scheme, but not the sashash, which gets set to zero. The unenrolled party ignores the relayed SAS field, but still uses the specified SAS rendering scheme.
It's possible to route a call through two ZRTP-enabled PBXs with this scheme ๐ธ. Say Alice is a ZRTP endpoint who trusts her local PBX in Atlanta, and Bob is a ZRTP endpoint who trusts his local PBX in Biloxi. The call goes from Alice to the Atlanta PBX, then to the Biloxi PBX, then to Bob. Atlanta relays the Atlanta-Biloxi SAS to Alice, because Alice is enrolled with Atlanta, and Biloxi relays the Atlanta-Biloxi SAS to Bob, because Bob is enrolled with Biloxi. In this example the two PBXs aren't assumed to be enrolled with each other. Alice and Bob both view and verbally compare the same relayed SAS, the Atlanta-Biloxi SAS ๐. No more than two trusted MiTM nodes can be traversed with this relaying scheme โ.
This behavior extends to two PBXs that are enrolled with each other, through this rule ๐ง: when a PBX shares trusted MiTM keys with both endpoints (meaning both are enrolled with this PBX), and one of them is another PBX (shown by the M-flag) while the other is a non-PBX, the MiTM PBX MUST always relay the PBX-to-PBX SAS to the non-PBX endpoint.
A ZRTP endpoint phone that trusts a PBX to act as a trusted MiTM is basically handing its own policy decisions about algorithm negotiation over to the PBX ๐ง.
When a PBX sits between two ZRTP endpoints and terminates their media streams at the PBX, the PBX presents its own ZID to both parties. That hides each party's ZID from the other ๐ซฅ. For example, if several different calls get routed through such a PBX to several different ZRTP-enabled phones behind it, the calling party only ever sees one ZID in every case: the ZID of the PBX itself.
The next section describes the initial enrollment procedure that sets up a special shared secret, a trusted MiTM key, between a PBX and a phone, so the phone learns to recognize the PBX as a trusted MiTM ๐.
7.3.1. PBX Enrollment and the PBX Enrollment Flag ๐
Both the PBX and the endpoint need to know when enrollment is happening ๐ฃ. One way to do this is to set up an enrollment extension on the PBX, which a newly configured endpoint calls to establish a ZRTP session. The PBX then plays audio media offering the user a chance to configure his phone to trust this PBX as a trusted MiTM ๐ง. The PBX calculates and stores the trusted MiTM shared secret in its cache, associated with this phone and indexed by the phone's ZID. The trusted MiTM PBX shared secret is derived from ZRTPSess through the ZRTP key derivation function (Section 4.5.1), like this:
pbxsecret = KDF(ZRTPSess, "Trusted MiTM key", (ZIDi || ZIDr), 256)
The pbxsecret is calculated for the whole ZRTP session, not for each stream within a session. That means the KDF Context field in this case doesn't include any stream-specific nonce material ๐งฉ.
The PBX signals enrollment by setting the PBX Enrollment flag (E) in the Confirm message (Figure 10) ๐ฉ. This flag triggers the ZRTP endpoint's user interface to ask the user whether they want to trust this PBX, and then calculate and store the pbxsecret in the cache. If the user decides yes by activating the right user interface element (a menu item, checkbox, or button), his ZRTP user agent calculates pbxsecret with the same formula and saves it in a special cache entry linked to this PBX ๐พ.
During a PBX enrollment, the GoClear features are disabled ๐ . If the PBX sets the (E) flag, it MUST NOT set the Allow Clear (A) flag. So (E) implies not (A). If a received Confirm message has the (E) flag set, the (A) flag MUST be disregarded and treated as false ๐ซ .
If the user decides not to enroll (maybe they dialed a wrong number, or they just don't feel comfy with this PBX yet), they can hang up and skip saving the pbxsecret in their cache ๐. The PBX will still have it saved in its cache, but that does no harm. The SASrelay scheme doesn't depend on the PBX trusting the phone. It only depends on the phone trusting the PBX. The phone (and the user behind it) is the one at risk if the PBX abuses its MiTM privileges ๐ฌ.
An endpoint MUST NOT store the pbxsecret in the cache without explicit user authorization ๐.
After enrollment, the PBX and the ZRTP-enabled phone both share a secret that lets the phone recognize the PBX as a trusted MiTM in future calls โจ. So when a future call from an outside ZRTP-enabled caller gets relayed through the PBX to this phone, the phone renders a relayed SAS from the PBX. If a SASrelay message comes from a MiTM that doesn't know the pbxsecret, the phone treats it as a bad-guy MiTM and refuses to render the relayed SAS ๐. No matter which party starts a future call through the PBX, the enrolled phone or the outside phone, the PBX relays the SAS to the enrolled phone ๐ฒ.
This enrollment procedure is mainly meant for phones already associated with the PBX, like enterprise phones "behind" it ๐ข. It isn't meant for the countless outside phones that aren't registered to this PBX's SIP server. It should be treated as part of the installation and provisioning process for a new phone in the organization ๐ ๏ธ.
There are more streamlined ways to configure ZRTP user agents to trust a PBX ๐. In large-scale deployments, the pbxsecret may be configured into the phone by an automated provisioning process, which can be less of a hassle for users and less error-prone. This specification doesn't require a manual enrollment process. Any process that results in a pbxsecret being computed and shared between the PBX and the phone will do, as long as the user is made aware that this puts the PBX in a position to wiretap the calls ๐.
It is recommended that a ZRTP client not proceed with the PBX enrollment procedure without evidence that a MiTM attack isn't happening during the enrollment session ๐ต๏ธโโ๏ธ. It would be especially damaging if a MiTM tricked the client into enrolling with the wrong PBX ๐ฑ. That would let the malevolent MiTM wiretap all future calls without arousing suspicion, because it would look trusted. Bestie, that's a whole nightmare ๐.
8. Signaling Interactions โจ
okay so this section is all about how ZRTP, SIP, and SDP vibe together ๐ and bestie, ZRTP doesn't need to be coupled with SIP signaling at all ๐ฆ. It can be a "bump in the wire" or a "bump in the stack," where RTP coming from the SIP User Agent (UA) gets converted to ZRTP ๐ง. In those setups the SIP UA has zero clue ZRTP exists ๐. So the signaling path discovery stuff in this section should not be definitive -- it's a hint, not a promise ๐ . Even if there's no sign of ZRTP support in an offer or answer, a ZRTP endpoint SHOULD still send Hello messages ๐ซถ.
ZRTP endpoints that do control the signaling path toss a ZRTP SDP attribute into their SDP offers and answers ๐. The attribute, a=zrtp-hash, says "hey, I support ZRTP" and carries a hash of the Hello message ๐ธ. That hash is computed according to Section 8.1 โจ.
Besides the perks from Section 8.1, this attribute is honestly iconic for a bunch of other stuff ๐. Signaling elements can use it to figure out when ZRTP might be used by endpoints ๐. It's also clutch when endpoints support multiple SRTP key management methods, because the ZRTP attribute makes sure those methods work with each other instead of against each other ๐ฅ. Like, if only one endpoint does ZRTP but both support some other method to key SRTP, then that other method is what gets used ๐. When they run in parallel, an SRTP secret carried in an a=keymgt [RFC4567] or a=crypto [RFC4568] attribute can act as a shared secret for the srtps computation defined in Section 8.2 ๐ฆ. And the ZRTP attribute also tells an intermediary ZRTP device not to act as a ZRTP endpoint, which we'll get into in Section 10 ๐.
The a=zrtp-hash attribute can only show up at the media level in the SDP, since Hello messages in different media streams always have unique hashes ๐.
The ABNF for the ZRTP attribute looks like this:
zrtp-attribute = "a=zrtp-hash:" zrtp-version zrtp-hash-value
zrtp-version = token
zrtp-hash-value = 1*(HEXDIG)
Here's an example of the ZRTP attribute in an initial SDP offer or answer used at the media level, using the <allOneLine> convention defined in RFC 4475, Section 2.1 [RFC4475] ๐ :
v=0
o=bob 2890844527 2890844527 IN IP4 client.biloxi.example.com
s=
c=IN IP4 client.biloxi.example.com
t=0 0
m=audio 3456 RTP/AVP 97 33
a=rtpmap:97 iLBC/8000
a=rtpmap:33 no-op/8000
<allOneLine>
a=zrtp-hash:1.10 fe30efd02423cb054e50efd0248742ac7a52c8f91bc2
df881ae642c371ba46df
</allOneLine>
A mechanism for carrying this same zrtp-hash info in the Jingle signaling protocol is defined in [XEP-0262] โจ.
It should be safe to send ZRTP messages even when the signaling gives no evidence the other party supports it ๐, because ZRTP was designed to look clearly different from RTP, kind of like the STUN packets sent during an ICE exchange ๐.
8.1. Binding the Media Stream to the Signaling Layer via the Hello Hash ๐
Tying the media stream to the signaling channel helps stop a third party from sneaking in fake media packets ๐ญ. If the signaling layer has info that ties it to the media stream, false media streams can get rejected ๐ซถ.
Here's the move: the entire Hello message (Figure 3) gets hashed, using the hash algorithm defined in Section 5.1.2.2 ๐ฆ. The ZRTP packet framing from Figure 2 is not part of the hash ๐
. The resulting hash image is handed to the signaling layer without truncation, where it's sent as a hexadecimal value in the SIP channel through the SDP attribute a=zrtp-hash, defined in this spec ๐ธ. Assuming Section 5.1.2.2 defines a 256-bit hash length, the a=zrtp-hash field carries 64 hexadecimal digits ๐ฅ. Each media stream (audio or video) gets its own Hello message, so each one needs its own a=zrtp-hash SDP attribute ๐. The recipient of the SIP/SDP message can then use that hash image to detect and reject false Hello messages in the media channel, and also figure out which media stream belongs to this SIP call โจ. Every Hello message hashes uniquely, because it contains the H3 field derived from a random nonce, defined in Section 9 ๐.
The Hello Hash as an SDP attribute is not a REQUIRED feature ๐, because some ZRTP endpoints can't add SDP attributes to the signaling. For example, if ZRTP lives in a hardware bump-in-the-wire device, it might only be able to modify the media packets and not the SIP packets, especially if the SIP packets are integrity protected and can't be changed on the wire ๐. If the SDP has no hash image of the ZRTP Hello message, the recipient's ZRTP user agent can't check it, so it won't be able to reject Hello messages based on this hash ๐ญ.
Once the Hello Hash has helped identify the ZRTP Hello message as belonging to this particular SIP call, the rest of the ZRTP message sequence is protected from false packet injection by other protection mechanisms, like the hash chaining mechanism defined in Section 9 ๐.
An attacker who controls only the signaling layer, like an uncooperative VoIP service provider, might be able to deny service by corrupting the hash of the Hello message in the SDP attribute ๐. That would force ZRTP to reject perfectly good Hello messages ๐ญ. If there's reason to believe this is happening, the ZRTP endpoint MAY allow Hello messages that don't match the hash image in the SDP attribute ๐.
Even without the SIP integrity protection, including the a=zrtp-hash SDP attribute, combined with the hash chaining mechanism defined in Section 9, meets the R-ASSOC requirement in the Media Security Requirements [RFC5479] ๐. That requirement says:
...a mechanism for associating key management messages with both the signaling traffic that initiated the session and with protected media traffic. It is useful to associate key management messages with call signaling messages, as this allows the SDP offerer to avoid performing CPU-consuming operations (e.g., Diffie-Hellman or public key operations) with attackers that have not seen the signaling messages.
The a=zrtp-hash SDP attribute gets especially useful if the SDP is integrity-protected end-to-end by SIP Identity [RFC4474], or even better, Dan Wing's SIP Identity using Media Path [SIP-IDENTITY] ๐ฅ. That opens up a way to stop MiTM attacks independent of ZRTP's SAS mechanism, as explained in Section 8.1.1 ๐.
8.1.1. Integrity-Protected Signaling Enables Integrity-Protected DH Exchange ๐ฆ
If and only if the signaling path and the SDP are protected by some form of end-to-end integrity protection, like one of the mechanisms above, so that delivery of the a=zrtp-hash attribute can't be tampered with by a third party, and there's good reason to trust the signaling layer to protect the end user's interests, then it's possible to authenticate the key exchange and prevent a MiTM attack ๐ญ. This can be done without the users having to verbally compare the SAS, by using the hash chaining mechanism defined in Section 9 to provide a series of MAC keys that protect the entire ZRTP key exchange ๐. So an end-to-end integrity-protected signaling layer automatically enables an integrity-protected Diffie-Hellman exchange in ZRTP, which in turn means immunity from a MiTM attack ๐. Here's how it works ๐ง.
The integrity-protected SIP SDP contains a hash commitment to the entire Hello message ๐ธ. The Hello message contains H3, which provides a hash commitment for the rest of the hash chain H0-H2 (Section 9) โจ. The Hello message is protected by a 64-bit MAC, keyed by H2 ๐. The Commit message is protected by a 64-bit MAC, keyed by H1 ๐ซถ. The DHPart1 or DHPart2 messages are protected by a 64-bit MAC, keyed by H0 ๐ฆ. The MAC protecting the Confirm messages is computed by a different MAC key, derived from the resulting key agreement ๐ . Each message's MAC gets checked when the MAC key shows up in the next message ๐ฅ. If a bad MAC is discovered, it MUST be treated as a security exception indicating a MiTM attack, perhaps by logging or alerting the user, and it MUST NOT be treated as a random error ๐จ. Random errors are already caught and quietly rejected by bad CRCs (Figure 2) ๐.
The Hello message has to be assembled before any hash algorithms are negotiated, so an implicit predetermined hash algorithm and MAC algorithm (both defined in Section 5.1.2.2) must be used ๐. All of the MACs mentioned above, keyed by the hashes in that hash chain, MUST be computed with the MAC algorithm defined in Section 5.1.2.2, with the MAC truncated to 64 bits ๐.
The Media Security Requirements [RFC5479] R-EXISTING requirement can be fully met by leveraging a certificate-backed PKI in the signaling layer to integrity protect delivery of the a=zrtp-hash SDP attribute ๐. That protects ZRTP against a MiTM attack without requiring the user to check the SAS, without adding any explicit signatures or signature keys to the ZRTP key exchange, and without any extra public key operations or extra packets ๐ฆ.
Without an end-to-end integrity-protection mechanism in the signaling layer to guarantee delivery of the a=zrtp-hash SDP attribute without modification by a third party, these MACs alone will NOT prevent a MiTM attack ๐ญ. In that case, ZRTP's built-in SAS mechanism still has to be used to authenticate the key exchange ๐
. At the time of this writing, very few deployed VoIP clients offer a fully implemented SIP stack that provides end-to-end integrity protection for delivery of SDP attributes ๐. End-to-end signaling integrity also gets more problematic if E.164 numbers [RFC3824] are used in SIP ๐. So real-world ZRTP endpoints will keep depending on SAS authentication for quite some time ๐ธ. Even after SIP user agents with integrity-protected SDP attribute delivery are widely available, many users will still face the fact that the signaling path may be controlled by institutions that don't have the end user's best interests at heart ๐ค. In those cases, SAS authentication stays the gold standard for the prudent user ๐โจ.
Even without SIP integrity protection, the Media Security Requirements [RFC5479] R-ACT-ACT requirement can be met by ZRTP's SAS mechanism ๐. ZRTP can benefit from an integrity-protected SIP layer, but it's honestly fortunate that ZRTP's self-contained MiTM defenses don't actually require one ๐ฆ. ZRTP can bypass the delays and problems that SIP integrity faces, like E.164 number usage and the complexity of building and maintaining a PKI ๐.
In contrast, DTLS-SRTP [RFC5764] appears to depend heavily on end-to-end integrity protection in the SIP layer ๐ฌ. Further, DTLS-SRTP has to bear the extra cost of its own signature calculation, on top of the signature calculation the SIP layer uses for its integrity protection ๐. ZRTP needs no signature calculation of its own to leverage the one the SIP layer already does ๐ซถ๐.
8.2. Deriving the SRTP Secret (srtps) from the Signaling Layer ๐
Okay so the shared secret math in Section 4.3 can use an SRTP secret (srtps), if the signaling layer provides one โจ
Ideally there's only ONE SRTP key negotiation protocol in the room, and that one should be ZRTP ๐ But ngl, sometimes the signaling layer already negotiated its own SRTP master key and salt, using SDP Security Descriptions (SDES [RFC4568]) or [RFC4567]. When that happens, the key material can get passed from signaling down to the ZRTP layer and mixed into ZRTP's own shared secret calculations. No security is lost because media encryption still doesn't depend on signaling ๐ง
ZRTP cooks up srtps from the SRTP master key and salt that signaling handed over, then truncates the result to 256 bits, like this:
srtps = KDF(SRTP master key, "SRTP Secret", (ZIDi || ZIDr ||
SRTP master salt), 256)
Honestly, srtps should be rarely computed or used in typical ZRTP endpoints ๐
Because ZRTP is meant to be the sole SRTP key negotiator, it needs zero help from [RFC4568] or [RFC4567]. If srtps does get computed, it's stored in the auxiliary shared secret auxsecret, which is defined in Section 4.3 and used in Section 4.3.1.
8.3. Codec Selection for Secure Media ๐
Codec selection happens in the signaling layer ๐ฆ If signaling figures out that both endpoints support ZRTP, that should steer codec choice toward avoiding variable bitrate (VBR) codecs, because those leak information.
Here's the tea: when voice is compressed with a VBR codec, the packet lengths change depending on the kinds of sounds being compressed. That leaks a lot about the content, even if the packets are encrypted, no matter which encryption protocol you use [Wright1]. It is RECOMMENDED that VBR codecs be avoided in encrypted calls. It's totally fine if the codec adapts its bitrate to the available channel bandwidth. The vulnerable ones are only the codecs that change their bitrate depending on the type of sound being compressed ๐
Lowkey, voice activity detection (VAD) also seems to leak info about the convo, just to a lesser extent than VBR does. You can mitigate that by lengthening the VAD hangover time by a random amount between 1 and 2 seconds, if your app can handle it. Only short bursts of speech would actually benefit from a longer hangover.
The security problems of VBR and VAD are covered in depth by the guidelines in [VBR-AUDIO]. It is RECOMMENDED that ZRTP endpoints follow these guidelines ๐
9. False ZRTP Packet Rejection ๐ซ
An attacker who isn't in the media path might try to sneak in fake ZRTP packets ๐ Their goal could be a denial-of-service attack, or sliding their own media stream into the call. VoIP is basically a DoS magnet by nature, so it needs protocol features that reject those attacks.
Fake SRTP packets can be easily rejected with the SRTP auth tag field, but that only works after a key agreement is completed. During ZRTP key negotiation, we need other ways to reject fakes. One is total_hash in the final shared secret calculation, but that only catches fakes AFTER you've already done the super expensive Diffie-Hellman math ๐ฉ
Lots of work has gone into analyzing denial-of-service attacks, especially from attackers outside the media path. Those attackers could inject false ZRTP packets to force a ZRTP endpoint into an endless series of pointless, expensive DH calculations. To detect and reject false packets cheaply and quickly, the moment they arrive, ZRTP uses a one-way hash chain, which is just a series of successive hash images. Before each session, these values get computed:
H0 = 256-bit random nonce (different for each party)
H1 = hash (H0)
H2 = hash (H1)
H3 = hash (H2)
This one-way hash chain MUST use the hash algorithm defined in Section 5.1.2.2, truncated to 256 bits. Each 256-bit hash image is the preimage of the next one, and the images get sent in reverse order through the ZRTP packet sequence. H3 rides in the Hello message, H2 in the Commit message, H1 in the DHPart1 or DHPart2 messages, and H0 in the Confirm1 or Confirm2 messages ๐ The initial random H0 nonces that each party generates MUST be unpredictable to an attacker and unique within a ZRTP session. That also forces the derived hash images H1-H3 to be unique and unpredictable too.
The recipient checks for the correct hash preimage by hashing the packet and comparing the result with the hash image from the preceding packet. Packets with an incorrect hash preimage MUST NOT be used by the recipient, but they MAY be processed as security exceptions, maybe by logging or alerting the user. As long as those bogus packets go unused and correct packets keep arriving, the protocol SHOULD be allowed to run to completion, which makes this denial-of-service attack totally ineffective ๐
Note: H2 is sent in the Commit message, and the initiator never receives a Commit message. So the initiator computes the responder's missing H2 by hashing the responder's H1 ๐ง Both parties do an analogous interpolation to handle the skipped DHPart1 and DHPart2 messages in Preshared (Section 3.1.2) or Multistream (Section 3.1.3) modes.
The hash images alone don't protect the rest of the packet they live in ๐ซถ So this scheme assumes the attacker can't modify packet contents from a legitimate party. That's a reasonable assumption for someone outside the media path, and it covers an important range of denial-of-service attacks. For the remaining attacks that involve packet modification, other mechanisms take over, like total_hash in the final shared secret calculation and the hash commitment in the Commit message.
Hello messages injected by an attacker can be detected and rejected by including a hash of the Hello message in the signaling, as described in Section 8. This needs every Hello message to be unique, and the H3 hash image already meets that requirement โ
If and only if an integrity-protected signaling channel is available, the MACs keyed by this hash chaining scheme can authenticate the entire ZRTP key exchange. That prevents a MiTM attack without relying on the users verbally comparing the SAS. See Section 8.1.1 for details ๐
Some ZRTP user agents let the user manually switch to clear mode (via the GoClear message) in the middle of a secure call, then later start secure mode again. Many consumer client products omit this feature, but the ones that allow it may return to secure mode in the same media stream. The same chain of hash images gets reused and so becomes ineffective the second time around, but no real harm is done. The new SRTP session keys are derived partly from a cached shared secret, which was safely protected from the MiTM during the earlier DH exchange in the same session ๐
10. Intermediary ZRTP Devices ๐ธ
This section is about a ZRTP endpoint that's actually an intermediary. For example, picture a device that proxies both signaling and media between endpoints. There are three possible ways such a device could support ZRTP ๐ฆ
First, an intermediary device can act transparently to the ZRTP protocol. To do this, a device MUST pass non-RTP protocols multiplexed on the same port as RTP (so ZRTP and STUN can get through). This is the RECOMMENDED behavior for intermediaries, since ZRTP and SRTP work best when they're done end-to-end โจ
Second, an intermediary device could implement ZRTP and act as a ZRTP endpoint on behalf of non-ZRTP endpoints behind it. It could figure out on a call-by-call basis whether the endpoint behind it supports ZRTP, based on the presence or absence of the ZRTP SDP attribute flag (a=zrtp-hash). For non-ZRTP endpoints, the intermediary could act as the ZRTP endpoint using its own ZID and cache. This approach SHOULD only be used when some other security method protects the confidentiality of the media between the intermediary and the inside endpoint, such as IPsec or physical security ๐
Third, the NOT RECOMMENDED option is for the intermediary to attempt to back-to-back the ZRTP protocol. The only exception is when the intermediary is a trusted element providing services to one of the endpoints, e.g., a Private Branch Exchange or PBX. In this mode, the intermediary tries to act as a ZRTP endpoint toward both endpoints of the media session. This approach MUST NOT be used except as described in Section 7.3, because it will always result in a detected MiTM attack, generate alarms on both endpoints, and likely terminate the session immediately ๐จ The PBX MUST uses a single ZID for all endpoints behind it.
With centralized media mixing, the SAS won't match when the humans compare it ๐ญ Signaling can sometimes reveal this through the presence of the isfocus feature tag [RFC4579]. So when the isfocus feature tag is present, the DH exchange can be authenticated either by the mechanism in Section 8.1.1, or by validating signatures (Section 7.2) in the Confirm or SASrelay messages.
Picture an audio conference call with three participants, Alice, Bob, and Carol, hosted on a conference bridge in Dallas ๐ง There are three ZRTP encrypted media streams, one between each participant and Dallas, and each has a different SAS. Each participant can validate their SAS with the conference bridge using signatures optionally present in the Confirm messages (described in Section 7.2). Or, if the signaling path has end-to-end integrity protection, each DH exchange gets automatic MiTM protection through the mechanism in Section 8.1.1 ๐
SIP feature tags can also help detect when a session is with an automaton, like an Interactive Voice Response (IVR), voicemail system, or speech recognition system ๐ค In those cases, the display of SAS strings to users should be disabled.
One more thing: an intermediary acting as a ZRTP endpoint might still receive ZRTP Hello and other messages from the inside endpoint. That could happen if there's another inline ZRTP device that doesn't include the ZRTP SDP attribute flag. An intermediary acting as a ZRTP endpoint that receives ZRTP Hello and other messages from the inside endpoint MUST NOT pass those ZRTP messages on ๐ โโ๏ธ
11. The ZRTP Disclosure Flag โจ
Okay bestie, ZRTP has no back doors built into the protocol spec ๐ โโ๏ธ๐ The designers really wanted to discourage back doors in ZRTP-enabled products. BUT ngl, even though the protocol itself is back-door free, a ZRTP implementer could still make a rogue product with a back door that lives outside the protocol ๐ฌ For example, they could build a product that takes the SRTP session key generated by ZRTP and discloses it out-of-band to a third party. They might even have a legit business reason to do that for some customers ๐คทโโ๏ธ
Some environments really do need to monitor or record calls. Think stock brokerage houses that want to stop insider trading, or super high-security places that need to keep an eye on their own phone calls ๐ต๏ธโโ๏ธ We've all heard the automated message saying "This call may be monitored for quality assurance" ๐ A ZRTP endpoint in a setup like that might quietly disclose the session key to whoever is monitoring the call. And ZRTP-enabled products that do these out-of-band disclosures of the session key can seriously mess with public confidence in the protocol, unless we do everything we can in the protocol to alert the other user that it's happening ๐ญ
If one of the parties is using a product designed to disclose their session key, ZRTP requires them to confess this to the other party ๐ซถ They send a message to the other party's ZRTP client, and that client can properly alert its user, maybe by rendering it in a graphical user interface. The disclosing party does this by sending a Disclosure flag (D) in Confirm1 and Confirm2 messages, as described in Section 5.7 ๐
Here's the key bit: the point is to have the Disclosure flag identify products that are designed to disclose their session keys. It is NOT meant to identify which particular calls are compromised on a call-by-call basis ๐ This is an important legal distinction, because most government-sanctioned wiretap regulations require a VoIP service provider to not reveal which particular calls are wiretapped. But there is nothing illegal about revealing that a product is designed to be wiretap-friendly. So the ZRTP protocol mandates that such a product "outs" itself ๐
You might be using a ZRTP-enabled product with no back doors at all, but your own GUI says the call is (mostly) secure, except the other party is using a product designed in a way that it may have disclosed the session key for monitoring purposes ๐ You might ask them what brand of secure phone they're using and make a mental note not to buy that brand yourself ๐ If we build a protocol environment that makes back-doored phones confess their nature, word will spread fast, and the "invisible hand" of the free market will act ๐ The free market has effectively dealt with this kind of thing before.
Of course, a ZRTP implementer can lie about their product having a back door. But the ZRTP standard mandates that ZRTP-compliant products MUST adhere to the requirement that a back door be confessed by sending the Disclosure flag to the other party ๐ฅ
Yes, there will be inevitable comparisons to Steve Bellovin's April fool joke from 2003, when he submitted RFC 3514 [RFC3514], which defined the "Evil bit" in the IPv4 header, for packets with "evil intent" ๐ But we think a similar idea can actually have some merit for securing VoIP ๐ง Sure, you can always imagine some implementer who isn't fazed by the rules and lies anyway. But they would have lied even without the Disclosure flag. There are good reasons to believe it will improve the overall percentage of implementations that at least tell us if they put a back door in their products, and may even get some of them to decide not to add a back door at all ๐ From a civic hygiene perspective, we are better off with the Disclosure flag in the protocol.
If an endpoint stores or logs SRTP keys, or information that can be used to reconstruct or recover SRTP keys after they are no longer in use (i.e., the session is active), or otherwise discloses or passes SRTP keys, or information that can be used to reconstruct or recover SRTP keys, to another application or device, the Disclosure flag D MUST be set in the Confirm1 or Confirm2 message ๐ฃ
11.1. Guidelines on Proper Implementation of the Disclosure Flag
Some implementers have asked for guidance on the Disclosure flag, so here we go ๐ซถ Some people have wrongly thought that a connection secured with ZRTP can't be used in a call center, with voluntary voice recording, or even with a voicemail system ๐ On the flip side, some potential ZRTP users have overestimated the protection ZRTP can give them ๐ These guidelines clear up both concerns.
The ZRTP Disclosure flag only governs the ZRTP/SRTP stream itself. It does NOT govern the underlying RTP media stream, or the actual media itself ๐ง So a PBX that uses ZRTP can still offer conference calls, call monitoring, call recording, voicemail, or other PBX features, and still say it does not disclose the ZRTP key material. A video system can offer DVR features and still say it does not disclose the ZRTP key material ๐ผ When the ZRTP Disclosure flag is not set, it only means that the ZRTP cryptographic key material stays within the bounds of the ZRTP subsystem ๐
If an application needs to disclose the ZRTP cryptographic key material, the easiest way to comply with the protocol is to set the flag to the proper value ๐ The next easiest way is to overestimate disclosure. For example, a call center that commonly records calls might set the Disclosure flag even though all its recording is analog recording of a call (and therefore outside the ZRTP scope), because it sets an expectation with clients that their calls might be recorded ๐๏ธ
Also note that the ZRTP Disclosure Flag does not require an implementation to prevent hacking or malware ๐ป Malware that leaks ZRTP cryptographic key material does not create a liability for the implementer for non-compliance with the ZRTP specification ๐คทโโ๏ธ
A ZRTP user should know that ZRTP is NOT a panacea against unauthorized recording ๐ซ ZRTP does not, and cannot, protect against an untrustworthy partner who holds a microphone up to the speaker ๐ค It doesn't protect against someone else being in the room. It doesn't protect against analog wiretaps in the phone or in the room. It doesn't mean your partner hasn't been hacked with spyware. It doesn't mean the software has no flaws ๐ What it does mean is that the ZRTP subsystem is not knowingly leaking ZRTP cryptographic key material ๐ธ
12. Mapping between ZID and AOR (SIP URI) ๐ฆ
Remember how the ZID manages the local cache of shared secrets? That's explained in Section 4.9 ๐ง A particular ZID is tied to a particular ZRTP endpoint, typically a VoIP client. Now, one SIP URI (also called an Address-of-Record, or AOR) can live on several different soft VoIP clients, desktop phones, and mobile handsets, and each of those will have a different ZID ๐ฑ A single VoIP client can also have several SIP URIs configured in its profiles, but still only one ZID. So there's no one-to-one mapping between a ZID and a SIP URI ๐ โโ๏ธ A single SIP URI can be associated with several ZIDs, and a single ZID can be associated with several SIP URIs on the same client ๐
And it gets better: ZRTP doesn't care which signaling protocol you use ๐ It works equally well with SIP, Jingle, H.323, or any proprietary signaling protocol. So a ZRTP ZID has very little to do with SIP, per se, which means it has very little to do with a SIP URI ๐
Even though a ZID is tied to a device and not a human, a ZRTP endpoint is often mainly controlled by one particular human. A mobile phone is a classic example ๐ฑ To get the full benefit of the key continuity features, a local cache entry (and therefore a ZID) should be associated with some kind of name for the remote party ๐ซถ That name could be a human name, or it could be more precise by specifying which ZRTP endpoint they're using. For example, "Jon Callas", "Jon Callas on his iPhone", "Jon on his iPad", or "Alice on her office phone" ๐ These name strings can be stored in the local cache, indexed by ZID, and may have been provided initially by the local user by hand. Or the local cache entry may contain a pointer to an entry in the local address book ๐ When a secure session is established, if a prior session has created a cache entry, and the new session has a matching cache entry indexed by the same ZID, and the SAS has been previously verified, then the person's name stored in that cache entry should be displayed โจ
If the remote ZID comes from a PBX, the displayed name would be the name of that PBX, which might be the name of the company that owns the PBX ๐ข
If it's desirable to associate some key material with a particular AOR, digital signatures (Section 7.2) may be used, with public key certificates that tie the signature key to an AOR ๐ If more than one ZRTP endpoint shares the same AOR, they may all use the same signature key and provide the same public key certificate with their signatures ๐
13. IANA Considerations ๐
This specification defines a new SDP [RFC4566] attribute in Section 8 ๐ Here's the registration, copied exactly:
Contact name: Philip Zimmermann <prz@mit.edu>
Attribute name: "zrtp-hash"
Type of attribute: Media level
Subject to charset: Not
Purpose of attribute: The 'zrtp-hash' indicates that a UA supports
the ZRTP protocol and provides a hash of the
ZRTP Hello message. The ZRTP protocol
version number is also specified.
Allowed attribute values: Hex
14. Media Security Requirements โจ
okay so this section is literally about how ZRTP checks off every single RTP security requirement from [RFC5479] ๐ and the best part is it does it without needing any other protocols or extensions. compare that to DTLS-SRTP [RFC5764], which needs extra protocols and mechanisms just to get going ๐. here's the whole checklist, bestie:
R-FORK-RETARGETis met since ZRTP is a media path key agreement protocol. ๐ฆR-DISTINCTis met since ZRTP uses ZIDs and allows multiple independent ZRTP exchanges to proceed. ๐ซถR-HERFPis met since ZRTP is a media path key agreement protocol. ๐R-REUSEis met using the Multistream and Preshared modes. ๐R-AVOID-CLIPPINGis met since ZRTP is a media path key agreement protocol. โจR-RTP-CHECKis met since the ZRTP packet format does not pass the RTP validity check. ๐งR-ASSOCis met using thea=zrtp-hashSDP attribute in INVITEs and responses (Section 8.1). ๐R-NEGOTIATEis met using the Commit message. ๐คR-PSTNis met since ZRTP can be implemented in Gateways. ๐R-PFSis met using ZRTP Diffie-Hellman key agreement methods. ๐ธR-COMPUTEis met using the Hello/Commit ZRTP exchange. ๐R-CERTSis met using the verbal comparison of the SAS. ๐ฃ๏ธR-FIPSis met since ZRTP uses only FIPS-approved algorithms in all relevant categories. The authors believe ZRTP is compliant with [NIST-SP800-56A], [NIST-SP800-108], [FIPS-198-1], [FIPS-180-3], [NIST-SP800-38A], [FIPS-197], and [NSA-Suite-B], which should meet the FIPS-140 validation requirements set by [FIPS-140-2-Annex-A] and [FIPS-140-2-Annex-D]. the paperwork is giving serious main character energy ๐๐R-DOSis met since ZRTP does not introduce any new denial-of-service attacks. ๐ก๏ธR-EXISTINGis met since ZRTP can support the use of certificates or keys. ๐R-AGILITYis met since the set of hash, cipher, SRTP authentication tag type, key agreement method, SAS type, and signature type can all be extended and negotiated. ๐ so flexible it's unrealR-DOWNGRADEis met since ZRTP has protection against downgrade attacks. ๐ซโฌ๏ธR-PASS-MEDIAis met since ZRTP prevents a passive adversary with access to the media path from gaining access to keying material used to protect SRTP media packets. ๐๐ซR-PASS-SIGis met since ZRTP prevents a passive adversary with access to the signaling path from gaining access to keying material used to protect SRTP media packets. ๐ โโ๏ธR-SIG-MEDIAis met using thea=zrtp-hashSDP attribute in INVITEs and responses. ๐R-ID-BINDINGis met using thea=zrtp-hashSDP attribute (Section 8.1). ๐ชชR-ACT-ACTis met using thea=zrtp-hashSDP attribute in INVITEs and responses. ๐ญR-BEST-SECUREis met since ZRTP utilizes the RTP/AVP profile and hence best effort SRTP in every case. ๐R-OTHER-SIGNALINGis met since ZRTP can utilize modes in which there is no dependency on the signaling path. ๐ซงR-RECORDINGis met using the ZRTP Disclosure flag. ๐๏ธR-TRANSCODERis met if the transcoder operates as a trusted MitM (i.e., a PBX). ๐งโ๐ผR-ALLOW-RTPis met due to ZRTP's best effort encryption. ๐ฅฐ
15. Security Considerations โจ
okay bestie, this whole document is about securely keying SRTP sessions, so security shows up in every section ๐ no escaping it, it's giving main character in every chapter ๐ค
so most secure phones rely on a Diffie-Hellman exchange to agree on a common session key. but DH is susceptible to a MiTM attack, so it's common practice to provide a way to authenticate the DH exchange ๐ต๏ธโโ๏ธ in some military systems they do this by depending on digital signatures backed by a centrally managed PKI. and honestly? a decade of industry experience has shown that deploying centrally managed PKIs can be a painful and often futile experience ๐ PKIs are just too messy and need too much activation energy to get going. setting one up requires somebody to run it, which is not practical for an equipment provider. a service provider, like a carrier, might venture down this path, but even then you have to deal with cross-carrier authentication, certificate revocation lists, and other complexities ๐ตโ๐ซ so it is much simpler to avoid PKIs altogether, especially when developing secure commercial products ๐
which is why it's more common for commercial secure phones in the PSTN world to augment the DH exchange with a Short Authentication String (SAS) combined with a hash commitment at the start of the key exchange, to shorten the length of SAS material that must be read aloud ๐ no PKI is required for this approach to authenticating the DH exchange. the AT&T TSD 3600, Eric Blossom's COMSEC secure phones [comsec], [PGPfone], and the GSMK CryptoPhone are all examples of products that took this simpler lightweight approach ๐ฑโจ the main problem with this approach? inattentive users who may not execute the voice authentication procedure ๐
some questions have been raised about voice spoofing during the short authentication string (SAS) comparison. but it's a mistake to think this is simply an exercise in voice impersonation (perhaps this could be called the "Rich Little" attack) ๐ค sure, there are digital signal processing techniques for changing a person's voice, but that does not mean a MiTM attacker can safely break into a phone conversation and inject his own SAS at just the right moment. he doesn't know exactly when or in what manner the users will choose to read aloud the SAS, or in what context they will bring it up or say it, or even which of the two speakers will say it, or if indeed they both will say it ๐ณ plus some methods of rendering the SAS involve using a list of words such as the PGP word list[Juola2], kind of like how pilots use the NATO phonetic alphabet to convey information โ๏ธ this can make it even more complicated for the attacker, because these words can be worked into the conversation in unpredictable ways. and if the session also includes video (an increasingly common usage scenario), the MiTM may be further deterred by the difficulty of making the lips sync with the voice-spoofed SAS ๐ the PGP word list is designed to make each word phonetically distinct, which also tends to create distinctive lip movements ๐ remember that the attacker places a very high value on not being detected, and if he makes a mistake, he doesn't get to do it over ๐ค๐ฅ
a question has been raised about the safety of the SAS procedure for people who don't know each other's voices, because it may allow an attack from a MiTM even if he lacks voice impersonation capabilities. this is not as much of a problem as it seems, though, because it isn't necessary that users recognize each other by their voice ๐ โโ๏ธ it is only necessary that they detect that the voice used for the SAS procedure doesn't match the voice in the rest of the phone conversation ๐
special consideration must be given to secure phone calls with automated systems that cannot perform a verbal SAS comparison between two humans (e.g., a voice mail system) ๐ผ if a well-functioning PKI is available to all parties, it is recommended that credentials be provisioned at the automated system sufficient to use one of the automatic MiTM detection mechanisms from Section 8.1.1 or Section 7.2. or rely on a previously established cached shared secret (pbxsecret or rs1 or both), backed by a human-executed SAS comparison during an initial call. note that it is worse than useless and absolutely unsafe to rely on a robot voice from the remote endpoint to compare the SAS, because a robot voice can be trivially forged by a MiTM ๐ค๐ซ however, a robot voice may be safe to use strictly locally for a different purpose. a ZRTP user agent may render its locally computed SAS to the local user via a robot voice if no visual display is available, provided the user can readily determine that the robot voice is generated locally, not from the remote endpoint ๐ง that's the one exception, and it's a good one
a popular and field-proven approach to MiTM protection is used by SSH (Secure Shell) [RFC4251], which Peter Gutmann likes to call the "baby duck" security model ๐ฆ๐ผ SSH establishes a relationship by exchanging public keys in the initial session, when we assume no attacker is present, and this makes it possible to authenticate all subsequent sessions. a successful MiTM attacker has to have been present in all sessions all the way back to the first one, which is assumed to be difficult for the attacker ๐ฎโ๐จ ZRTP's key continuity features are actually better than SSH, at least for VoIP, for reasons described in Section 15.1. all of this is accomplished without resorting to a centrally managed PKI ๐
we use an analogous baby duck security model to authenticate the DH exchange in ZRTP ๐ฆ๐ we don't need to exchange persistent public keys. we can simply cache a shared secret and re-use it to authenticate a long series of DH exchanges for secure phone calls over a long period of time. if we verbally compare just one SAS, and then cache a shared secret for later calls to use for authentication, no new voice authentication rituals need to be executed. we just have to remember we did one already ๐ง โจ
okay now the spicy part. if one party ever loses this cached shared secret, it is no longer available for authentication of DH exchanges ๐ฌ this cache mismatch situation is easy to detect by the party that still has a surviving shared secret cache entry. if it fails to match, either there is a MiTM attack or one side has lost their shared secret cache entry. the user agent that discovers the cache mismatch must alert the user that a cache mismatch has been detected, and that he must do a verbal comparison of the SAS to distinguish if the mismatch is because of a MiTM attack or because of the other party losing her cache (normative language is in Section 4.3.2). voice confirmation is absolutely essential in this situation ๐จ from that point on, the two parties start over with a new cached shared secret. then they can go back to omitting the voice authentication on later calls ๐ฅณ
precautions must be observed when using a trusted MiTM device such as a trusted PBX, as described in Section 7.3 โ ๏ธ make sure you really trust that this PBX will never be compromised before establishing it as a trusted MiTM, because it is in a position to wiretap calls for any phone that trusts it. it is "licensed" to be in a position to wiretap ๐ซฃ you are safer to try to arrange the connection topology to route the media directly between the two ZRTP peers, not through a trusted PBX. real end-to-end encryption is preferred ๐
the security of the SAS mechanism depends on the user verifying it verbally with his peer at the other endpoint. there is some risk the user will not be so diligent and may ignore the SAS ๐ฅฑ for a discussion on how users become habituated to security warnings in the PKI certificate world, see [Sunshine]. part of the problems discussed in that paper come from the habituation syndrome common to most warning messages, and part of them come from the fact that users simply don't understand trust models ๐ fortunately, ZRTP doesn't need a trust model to use the SAS mechanism, so it's easier for the user to grasp the idea of comparing the SAS verbally with the other party. it's easier than understanding a trust model, at least ๐ซถ also, the verbal comparison of the SAS gets both users involved, and they will notice a mismatch of the SAS ๐ฏโโ๏ธ and the ZRTP user agent will know when the SAS has been previously verified because of the SAS verified flag (V) (Section 7.1), and only ask the user to verify it when needed. after it has been verified once, the key continuity features make it unnecessary to verify it again ๐โจ
15.1. Self-Healing Key Continuity Feature โจ
okay bestie, ZRTP's key continuity stuff is kinda like SSH [RFC4251] ๐, but there's one big difference ๐. SSH caches public signature keys that never change, and it uses a permanent private signature key that has to be guarded from disclosure. If someone steals your SSH private signature key, they can impersonate you in all future sessions and mount a successful MiTM attack any time they want ๐.
ZRTP caches symmetric key material used to compute secret session keys, and those values change with every session ๐ฆ. If someone steals your ZRTP shared secret cache, they only get one chance to mount a MiTM attack, in the very next session. If they miss that chance, the retained shared secret gets refreshed with a new value, and the window of vulnerability heals itself. So they're locked out of any future chances to MiTM you ๐ โโ๏ธ. This is the "self-healing" feature if any cached key material gets compromised, and it's giving main character recovery ๐ .
A MiTM attacker always has to be in the media path. That's a big operational burden for the attacker in lots of VoIP setups, because being in the media path for every call is often way harder than being in the signaling path ๐. This will likely create coverage gaps in the attacker's opportunities to mount a MiTM attack. ZRTP's self-healing key continuity features are better than SSH at exploiting any temporary gaps in those opportunities ๐ง. So ZRTP quickly recovers from any disclosure of cached key material โจ.
In systems that use a persistent private signature key, like SSH, the stored signature key is usually protected from disclosure by encryption that needs a user-supplied high-entropy passphrase ๐. This setup might be fine for a diligent user at a desktop computer in an office with a full ASCII keyboard ๐ฅ๏ธ. But typing a high-entropy passphrase on a mobile phone's numeric keypad while driving a car would be prohibitively inconvenient and unsafe ๐๐. Users will reject any scheme that requires a passphrase on a platform like that, which means mobile phones carry an elevated risk of compromise of stored key material. They'd especially benefit from the self-healing aspects of ZRTP's key continuity features ๐ฑ๐ซถ.
The infamous Debian OpenSSL weak key vulnerability [dsa-1571] (discovered and patched in May 2008) is a real-world example of why ZRTP's self-healing scheme is a good way to do key continuity ๐ญ. The Debian bug produced a lot of weak SSH (and TLS/SSL) keys, which kept compromising security even after the bug was patched. ZRTP's key continuity scheme is different ๐: it adds new entropy to the cached key material with every call, so old deficiencies in entropy get washed away with each new session ๐ซง.
It's worth noting that adding shared secret entropy from previous sessions can extend the strength of the new session key to AES-256 levels ๐ฅ. This works even if the new session uses Diffie-Hellman keys no larger than DH-3072 or ECDH-256, as long as the cached shared secrets were first established when the wiretapper wasn't present. This is why AES-256 MAY be used with the smaller DH key sizes in Section 5.1.5, despite the key strength comparisons in Table 2 of [NIST-SP800-57-Part1] ๐ค.
Caching shared symmetric key material is also less CPU intensive than using digital signatures ๐. That can matter a lot for low-power mobile platforms ๐ฑโก.
Unlike the long-lived, non-updated key material used by SSH, the dynamically updated shared secrets of ZRTP may lose sync if traditional backup/restore mechanisms are used ๐ฌ. This limitation is a consequence of the otherwise beneficial aspects of this approach to key continuity. It's partially mitigated by ZRTP's built-in cache backup logic (Section 4.6.1) ๐.
16. Acknowledgments ๐ซถ
The authors would like to thank Bryce "Zooko" Wilcox-O'Hearn and Colin Plumb for their contributions to the design of this protocol ๐ธ. Also, huge thanks to Hal Finney, Viktor Krikun, Werner Dittmann, Dan Wing, Sagar Pai, David McGrew, Colin Perkins, Dan Harkins, David Black, Tim Polk, Richard Harris, Roni Even, Jon Peterson, and Robert Sparks for their helpful comments and suggestions ๐. Thanks to Lily Chen at NIST for her help making sure everything complied with NIST SP800-56A and SP800-108 ๐.
The use of one-way hash chains to key HMACs in ZRTP is similar to Adrian Perrig's TESLA protocol [TESLA] ๐.
17. References ๐
17.1. Normative References ๐
[RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC: Keyed-
Hashing for Message Authentication", RFC 2104,
February 1997.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[RFC3526] Kivinen, T. and M. Kojo, "More Modular Exponential (MODP)
Diffie-Hellman groups for Internet Key Exchange (IKE)",
RFC 3526, May 2003.
[RFC3550] Schulzrinne, H., Casner, S., Frederick, R., and V.
Jacobson, "RTP: A Transport Protocol for Real-Time
Applications", STD 64, RFC 3550, July 2003.
[RFC3711] Baugher, M., McGrew, D., Naslund, M., Carrara, E., and K.
Norrman, "The Secure Real-time Transport Protocol (SRTP)",
RFC 3711, March 2004.
[RFC4231] Nystrom, M., "Identifiers and Test Vectors for HMAC-SHA-
224, HMAC-SHA-256, HMAC-SHA-384, and HMAC-SHA-512",
RFC 4231, December 2005.
[RFC4566] Handley, M., Jacobson, V., and C. Perkins, "SDP: Session
Description Protocol", RFC 4566, July 2006.
[RFC4880] Callas, J., Donnerhacke, L., Finney, H., Shaw, D., and R.
Thayer, "OpenPGP Message Format", RFC 4880, November 2007.
[RFC4960] Stewart, R., "Stream Control Transmission Protocol",
RFC 4960, September 2007.
[RFC5114] Lepinski, M. and S. Kent, "Additional Diffie-Hellman
Groups for Use with IETF Standards", RFC 5114,
January 2008.
[RFC5479] Wing, D., Fries, S., Tschofenig, H., and F. Audet,
"Requirements and Analysis of Media Security Management
Protocols", RFC 5479, April 2009.
[RFC5759] Solinas, J. and L. Zieglar, "Suite B Certificate and
Certificate Revocation List (CRL) Profile", RFC 5759,
January 2010.
[RFC6188] McGrew, D., "The Use of AES-192 and AES-256 in Secure
RTP", RFC 6188, March 2011.
[FIPS-140-2-Annex-A]
"Annex A: Approved Security Functions for FIPS PUB 140-2",
NIST FIPS PUB 140-2 Annex A, January 2011.
[FIPS-140-2-Annex-D]
"Annex D: Approved Key Establishment Techniques for FIPS
PUB 140-2", NIST FIPS PUB 140-2 Annex D, January 2011.
[FIPS-180-3]
"Secure Hash Standard (SHS)", NIST FIPS PUB 180-3, October
2008.
[FIPS-186-3]
"Digital Signature Standard (DSS)", NIST FIPS PUB 186-
3, June 2009.
[FIPS-197] "Advanced Encryption Standard (AES)", NIST FIPS PUB
197, November 2001.
[FIPS-198-1]
"The Keyed-Hash Message Authentication Code (HMAC)", NIST
FIPS PUB 198-1, July 2008.
[NIST-SP800-38A]
Dworkin, M., "Recommendation for Block Cipher Modes of
Operation", NIST Special Publication 800-38A, 2001
Edition.
[NIST-SP800-56A]
Barker, E., Johnson, D., and M. Smid, "Recommendation for
Pair-Wise Key Establishment Schemes Using Discrete
Logarithm Cryptography", NIST Special Publication 800-
56A Revision 1, March 2007.
[NIST-SP800-90]
Barker, E. and J. Kelsey, "Recommendation for Random
Number Generation Using Deterministic Random Bit
Generators", NIST Special Publication 800-90 (Revised),
March 2007.
[NIST-SP800-108]
Chen, L., "Recommendation for Key Derivation Using
Pseudorandom Functions", NIST Special Publication 800-
108, October 2009.
[NSA-Suite-B]
"NSA Suite B Cryptography", NSA Information Assurance
Directorate, NSA Suite B Cryptography.
[NSA-Suite-B-Guide-56A]
"Suite B Implementer's Guide to NIST SP 800-56A", Suite B
Implementer's Guide to NIST SP 800-56A, 28 July 2009.
[TwoFish] Schneier, B., Kelsey, J., Whiting, D., Hall, C., and N.
Ferguson, "Twofish: A 128-Bit Block Cipher", June 1998,
<http://www.schneier.com/paper-twofish-paper.html>.
[Skein] Ferguson, N., Lucks, S., Schneier, B., Whiting, D.,
Bellare, M., Kohno, T., Callas, J., and J. Walker, "The
Skein Hash Function Family, Version 1.3 - 1 Oct 2010", <ht
tp://www.skein-hash.info/sites/default/files/
skein1.3.pdf>.
[pgpwordlist]
"PGP Word List", December 2010, <http://en.wikipedia.org/
w/index.php?title=PGP_word_list&oldid=400752943>.
17.2. Informative References ๐โจ
okay bestie, these are the informative refs, aka the extra reading list that's not strictly required but explains where all the good ideas came from ๐ค๐ pulled up exactly as written so nothing gets lost in translation ๐ซถ
[RFC1191] Mogul, J. and S. Deering, "Path MTU discovery", RFC 1191,
November 1990.
[RFC1981] McCann, J., Deering, S., and J. Mogul, "Path MTU Discovery
for IP version 6", RFC 1981, August 1996.
[RFC3261] Rosenberg, J., Schulzrinne, H., Camarillo, G., Johnston,
A., Peterson, J., Sparks, R., Handley, M., and E.
Schooler, "SIP: Session Initiation Protocol", RFC 3261,
June 2002.
[RFC3514] Bellovin, S., "The Security Flag in the IPv4 Header",
RFC 3514, April 1 2003.
[RFC3824] Peterson, J., Liu, H., Yu, J., and B. Campbell, "Using
E.164 numbers with the Session Initiation Protocol (SIP)",
RFC 3824, June 2004.
[RFC4086] Eastlake, D., Schiller, J., and S. Crocker, "Randomness
Requirements for Security", BCP 106, RFC 4086, June 2005.
[RFC4251] Ylonen, T. and C. Lonvick, "The Secure Shell (SSH)
Protocol Architecture", RFC 4251, January 2006.
[RFC4474] Peterson, J. and C. Jennings, "Enhancements for
Authenticated Identity Management in the Session
Initiation Protocol (SIP)", RFC 4474, August 2006.
[RFC4475] Sparks, R., Hawrylyshen, A., Johnston, A., Rosenberg, J.,
and H. Schulzrinne, "Session Initiation Protocol (SIP)
Torture Test Messages", RFC 4475, May 2006.
[RFC4567] Arkko, J., Lindholm, F., Naslund, M., Norrman, K., and E.
Carrara, "Key Management Extensions for Session
Description Protocol (SDP) and Real Time Streaming
Protocol (RTSP)", RFC 4567, July 2006.
[RFC4568] Andreasen, F., Baugher, M., and D. Wing, "Session
Description Protocol (SDP) Security Descriptions for Media
Streams", RFC 4568, July 2006.
[RFC4579] Johnston, A. and O. Levin, "Session Initiation Protocol
(SIP) Call Control - Conferencing for User Agents",
BCP 119, RFC 4579, August 2006.
[RFC5117] Westerlund, M. and S. Wenger, "RTP Topologies", RFC 5117,
January 2008.
[RFC5245] Rosenberg, J., "Interactive Connectivity Establishment
(ICE): A Protocol for Network Address Translator (NAT)
Traversal for Offer/Answer Protocols", RFC 5245,
April 2010.
[RFC5764] McGrew, D. and E. Rescorla, "Datagram Transport Layer
Security (DTLS) Extension to Establish Keys for the Secure
Real-time Transport Protocol (SRTP)", RFC 5764, May 2010.
[RFC5869] Krawczyk, H. and P. Eronen, "HMAC-based Extract-and-Expand
Key Derivation Function (HKDF)", RFC 5869, May 2010.
[RFC6090] McGrew, D., Igoe, K., and M. Salter, "Fundamental Elliptic
Curve Cryptography Algorithms", RFC 6090, February 2011.
[SRTP-AES-GCM]
McGrew, D., "AES-GCM and AES-CCM Authenticated Encryption
in Secure RTP (SRTP)", Work in Progress, January 2011.
[ECC-OpenPGP]
Jivsov, A., "ECC in OpenPGP", Work in Progress,
March 2011.
[VBR-AUDIO]
Perkins, C. and J. Valin, "Guidelines for the use of
Variable Bit Rate Audio with Secure RTP", Work
in Progress, December 2010.
[SIP-IDENTITY]
Wing, D. and H. Kaplan, "SIP Identity using Media Path",
Work in Progress, February 2008.
[NIST-SP800-57-Part1]
Barker, E., Barker, W., Burr, W., Polk, W., and M. Smid,
"Recommendation for Key Management - Part 1: General
(Revised)", NIST Special Publication 800-57 - Part
1 Revised March 2007.
[NIST-SP800-131A]
Barker, E. and A. Roginsky, "Recommendation for the
Transitioning of Cryptographic Algorithms and Key
Lengths", NIST Special Publication 800-131A January 2011.
[SHA-3] "Cryptographic Hash Algorithm Competition", NIST Computer
Security Resource Center Cryptographic Hash Project.
[Skein1] "The Skein Hash Function Family - Web site",
<http://www.skein-hash.info/>.
[XEP-0262] Saint-Andre, P., "Use of ZRTP in Jingle RTP Sessions", XSF
XEP 0262, August 2010.
[Ferguson] Ferguson, N. and B. Schneier, "Practical Cryptography",
Wiley Publishing, 2003.
[Juola1] Juola, P. and P. Zimmermann, "Whole-Word Phonetic
Distances and the PGPfone Alphabet", Proceedings of the
International Conference of Spoken Language Processing
(ICSLP-96), 1996.
[Juola2] Juola, P., "Isolated Word Confusion Metrics and the
PGPfone Alphabet", Proceedings of New Methods in Language
Processing, 1996.
[PGPfone] Zimmermann, P., "PGPfone", July 1996,
<http://philzimmermann.com/docs/pgpfone10b7.pdf>.
[Zfone] Zimmermann, P., "Zfone Project", 2006,
<http://www.philzimmermann.com/zfone>.
[Byzantine]
"The Two Generals' Problem", March 2011, <http://
en.wikipedia.org/w/
index.php?title=Two_Generals%27_Problem&oldid=417855753>.
[TESLA] Perrig, A., Canetti, R., Tygar, J., and D. Song, "The
TESLA Broadcast Authentication Protocol", October 2002, <h
ttp://www.ece.cmu.edu/~adrian/projects/tesla-cryptobytes/
tesla-cryptobytes.pdf>.
[comsec] Blossom, E., "The VP1 Protocol for Voice Privacy Devices
Version 1.2", <http://www.comsec.com/vp1-protocol.pdf>.
[Wright1] Wright, C., Ballard, L., Coull, S., Monrose, F., and G.
Masson, "Spot me if you can: Uncovering spoken phrases in
encrypted VoIP conversations", Proceedings of the 2008
IEEE Symposium on Security and Privacy 2008,
<http://cs.jhu.edu/~cwright/oakland08.pdf>.
[Sunshine] Sunshine, J., Egelman, S., Almuhimedi, H., Atri, N., and
L. Cranor, "Crying Wolf: An Empirical Study of SSL Warning
Effectiveness", USENIX Security Symposium 2009,
<http://lorrie.cranor.org/pubs/sslwarnings.pdf>.
[dsa-1571] "Debian Security Advisory - OpenSSL predictable random
number generator", May 2008,
<http://www.debian.org/security/2008/dsa-1571>.
Authors' Addresses ๐
these are the humans behind the whole thing, so give them their flowers ๐๐ซถ
Philip Zimmermann
Zfone Project
Santa Cruz, California
EMail: prz@mit.edu
URI: http://philzimmermann.com
Alan Johnston (editor)
Avaya
St. Louis, MO 63124
EMail: alan.b.johnston@gmail.com
Jon Callas
Apple, Inc.
EMail: jon@callas.org
๐ฆ where this page comes from
- ietf: rfc6189 2011-04
- ๐ girlified by claude-haiku-5-5 on : a language model's rewrite of the original, under its terms