Affected application versions (known as of 6/4/21)
- 5.13.0 and below (iOS)
- 5.3.0 and below (macOS)
- 5.3.0 and below (Windows)
- 5.10.8 and below (Android)
- 5.3.0 and below (Linux)
Initial test versions (5/13/21)
- 5.11.0 and below (iOS)
- 5.1.0 and below (macOS)
- 5.1.0 and below (Windows)
- 5.9.7 and below (Android)
- 5.1.0 and below (Linux)
Signal provides a free, cross-platform private messenger app. Folks in all kinds of unsafe situations rely on Signal, as a highly visible and popular app which the security and privacy professional communities endorse. Journalists rely on Signal to ensure confidential communication with their sources.
What privacy guarantees does one really have though if you can't prove the identity of who you're communicating with?
Mid-May, I got a new phone. At the time I understood that with *any change* to the device or installation of either party in a chat with message history, the Signal chat "safety number" changes. This used to be but (following an involved email back-and-forth with the Signal team over the course of a month) is no longer reflected in the Signal support documentation.
When a safety number changes, Signal shows a message to both parties in the conversation. The most recent alert I recall seeing prior to this adventure (which I believe was initially received April 14, about a month before I changed phones) looks like this:
Expecting similar alerts to be sent out to my existing chat threads upon phone changeover, I messaged a few of my more recent chats.
Signal has a pretty convenient iOS device transfer method to help migrate everything over (I later discovered it not only transfers your chat threads and settings, it also transfers your key material) by simply scanning a QR code on your new device using the old device. It worked beautifully. But then - nada más. I went to check docs to see if I had missed something obvious.
In the Signal user support documentation:
What *are* safety numbers anyway?
As far as I know, the idea of safety numbers as implemented in Signal doesn't have a publicly available product-level nor technical specification, unlike some of the other algorithm and protocol components.
Backing up a few steps for folks who aren't familiar (anyone who knows Signal can obviously skip this), here's a bit more on how safety numbers work.
Let's say there are two participants in a Signal chat, Alice and Bob. This chat has a single unique safety number which both parties can check in the app.
This number is a human-readable representation of Alice and Bob's shared public key material. Slightly more technically put, it's a combination of Alice and Bob's individual cryptographic fingerprints in decimal format. There's also a QR code version in the mobile app so it's easy for folks to compare.
Here's a safety number in the Android app:
The safety number for Alice and Bob *should eventually* change whenever Alice *or* Bob change their Signal installation (example: uninstalling and reinstalling the app to prevent a third party seeing the chat history on Alice's phone). This allows Alice and Bob to verify the privacy of their communication over Signal as desired.
Alice and Bob can also mark their safety number verified in a way that is supposed to make sending messages to their chat always require manual approval right in the UI.
Again however there had been no safety number alerts in the UI to any of my existing chat threads. We later found out that it's an intentional product decision by Signal staff to not have device "quick start" transfers cause safety number changes, even though this is inconsistent with the app behavior on every other kind of device or installation change and inconsistent with the documentation.
Signal enables users to have multiple devices on the same account through the Sesame device and session management algorithm. The need for users to verify each other to get any real privacy guarantee gets mentioned multiple times in the Sesame doc.
Here's another callout about the requirement for identity checking.
To reproduce the issue, I transferred Signal to my old device again. John Jackson observed that the safety number for our chat stayed the same before and after transfer, where he should have been alerted and the safety number should have changed:
I additionally checked the chat safety number on another device associated with my Signal account and saw it had also not changed there:
Further Investigation: App Uninstallation
Later with Rob, Sick Codes, and others we observed that safety numbers also did not change for multiple user pairs across all device types Signal currently supports (Linux, macOS, Android, iOS, Windows) when at least one party deleted the app and reinstalled it on one of their linked devices. This meant the issue was not something isolated to communicating with my user account.
Requesting a CVE ID and notifying vendor
At this point we felt we had two fairly well-tested cases where the documentation did not reflect the observed behavior:
- chat safety numbers not changing on device transfer
- chat safety numbers not changing on Signal uninstall/reinstall on same device
We requested a CVE and emailed firstname.lastname@example.org a first draft we proposed for the CVE follow-on writeup with this summary:
Missing cryptographic step in Signal Private Messenger ("Signal") across multiple platforms allows an attacker to masquerade as a victim Signal user to the victim's contacts. Signal at time of writing does not rotate the safety key ("safety number") between a user pair upon re-installation of the application, nor on transfer of application data from one device to another using a method such as iOS Quick Start, despite clear indication in the Signal documentation this must occur in order to let the user's contacts know the user's device or installation has changed. Failure of key rotation results in lack of non-repudiation of communications and indeterminate potential for impersonation and man-in-the-middle attacks.
Safety number verification
Then, I started to wonder if safety numbers actually changed under any circumstances.
Rob and I determined that the "you have not marked person as verified" functionality also did not force a safety number change when Signal was uninstalled and reinstalled on Rob's device, most likely as well due to whatever underlying issue was causing safety numbers to not change on uninstallation and reinstallation:
Prior to verification
Following Rob deleting and reinstalling the app
Clearing data then uninstalling does actually cause safety number change on reinstall
Sick Codes and I proved the chat safety number did actually change when upon clearing data in some flavour of the desktop app. Clearing my data then uninstalling Signal on macOS caused me to lose all chat threads and contents of group chats on the macOS device. Chat threads were all still present across my other devices until I resynced my phone to the cleared desktop app, at which point all messages from before clearing my data were gone from my phone as well.
Before clearing data on desktop app
After clearing data
I accidentally went through a CAPTCHA loop twice trying to get everything put back together properly, but it was worth it to prove safety number changes can happen sometimes if you really try.
Some time passed before Signal requested further information. We provided many of the screenshots included here and detailed some ways we thought the issues could be abused to cause Signal users harm. We felt it was clear the app behaviour and documentation did not match when we reported, but Signal staff surprisingly said they were unable to reproduce.
After several more emails spaced out over multiple days Signal staff requested a call. As everyone mentioned in this blog works full time elsewhere and this is our side gig, it was very kind of Signal staff to agree to take a call after Pacific Time working hours, but the call was ultimately not very useful from our perspective. We arrived at deadlock and pretty much nothing else productive came from our long email chain. Eventually, Signal staff stopped replying after telling us they planned to update the customer docs and not change anything else. We later found after dismissing our report, Signal not only had updated the customer docs, but had started rolling out patches for the issues.
Though as far as we understand it wasn't taken into consideration, we provided Signal via email after the call with what we considered to be our ideal timeline and outcome. We would have liked to see disclosure at each step by Signal in order to show accountability to users and the security and privacy communities. We felt this request was appropriate considering the Signal app is a privacy-critical product, people in unsafe situations may rely on it for communications, and it seemed to us that some of Signal's privacy guarantees weren't being met.
Mobile apps' version bump
We tried to obtain independent verification from Ax Sharma this past week, but he unfortunately wasn't able to reproduce the reinstallation lack of safety number change on iOS and Android on Friday (4 June). This tipped us off to something maybe having quietly been changed in the Signal codebase since we were consistently able to reproduce the issue across multiple user/device matchups for several weeks beforehand.
Turns out, there is a freshly baked new version out in the Android and iOS app stores as of Friday. Even if unintentionally included, it seems we have our fix for at least the uninstall/reinstall safety number change issue on mobile:
Safety number now changes on mobile for uninstall / reinstall!
Where before you could uninstall the app, wait awhile, reinstall it, and just get dropped back into all your chats with all message history available, now you have to follow a registration workflow, the chat safety number changes, and the party on the other end gets a UI alert. Further, on iOS any previous messages are no longer available on app reinstallation.
From uninstalling and reinstalling on iOS the evening of 4 June:
Before (seen on John's phone)
New prompt on iOS
After (seen on Kelly's phone)
After (seen on John's phone)
Desktop Electron app
Upon learning from Ax that uninstall/reinstall now showed a safety number change and proving that for ourselves, we wanted to see if the same was true for the desktop app, which is more or less the same Electron/NodeJS codebase across macOS, Linux, and Windows. We noticed that despite a similar version bump, the desktop app on macOS still did not produce a safety number update or show alerts after uninstall/reinstall for a chat:
I was unable to start the reinstalled 5.3.0 dmg I had saved in my downloads and had to grab 5.4.0:
I wanted to also see if the device transfer issue was also fixed. I got an interesting interstitial which wouldn't let me complete transfer til I updated the Signal version on my old device, but even after update my contacts didn't see safety number changes.
This time, I verified the safety number for my chat thread with John was the same from both my own devices as well as doing the transfer. John or anyone else communicating with me are still unable to tell anything has changed at all after transfer from my old to my new phone or vice versa.
Before (new phone)
After (back on old phone again)
Why are we doing this?
We don't want anyone to get hurt by way of trusting privacy guarantees which may be more conditional than they appear from the docs!
If Bob notices the chat safety number with Alice has changed and then Alice sends a bunch of suspect-sounding messages or asks to meet in person and Bob has never met Alice in person before, for example, Bob should be wary. After Alice for example is forced to provide device passcode or unlock their device with their fingerprint or face, Alice's device could be cloned over to a new device by way of quick transfer functionality without Alice's consent, and the messages could be coming from the cloned device rather than Alice's actual device.
- 12 May 2021: vulnerability discovered
- 13 May 2021: CVE requested from Mitre
- 13 May 2021: vendor notified via security@ email address
- 14 May 2021: vendor requested additional information
- 14 May 2021: researchers responded
- 15 May 2021: vendor requested additional information
- 15 May 2021: researchers responded
- 18 May 2021: researchers requested response
- 18 May 2021: vendor denied vulnerability
- 19 May 2021: researchers responded
- 22 May 2021: vendor requested video call
- 24 May 2021: video call with vendor engineering manager, Kelly Kaoudis, and John Jackson to discuss
- 25 May 2021: researchers provided sketch of ideal timeline for disclosure to vendor
- 27 May 2021: vendor notified researchers of planned support page update and lack of plans to mitigate vulnerability or lack of clarity in technical documentation
- 29 May 2021: researchers discover additional issue
- 2 June 2021: vendor notified researchers of support page update
- 2 June 2021: researchers requested vendor preferred timeline for issue remediation for both initial and second vulnerabilities
- 2 June 2021: researchers approached Ax Sharma for independent verification and possible writeup
- 4 June 2021: Ax Sharma unable to reproduce by uninstalling and reinstalling Signal on Android and iOS
- 4 June 2021: researchers determine safety numbers now change in Android and iOS when uninstalling and reinstalling Signal, but not on macOS nor when performing device transfer between two iOS devices (original device transfer issue remains unpatched)
- Signal on GitHub
- Wayback 2021-05-22: Signal Support Center Safety Number Documentation
- Wayback 2021-06-04: Signal Support Center Safety Number Documentation
- Wayback 2021-05-04: Signal blog "Safety Number Updates"
- Wayback 2021-05-04: Signal blog "Verified Safety Number Updates"
- Wayback 2021-06-04: Signal blog "iOS Device Transfer"
- Wayback 2021-06-03: Signal Sesame Specification
- Wayback 2021-06-03: Signal X3DH Specification
Additional Thanks To
Ax Sharma, Amber Harrington, exp1r3d for their independent assistance testing!