The safety number is a derivation of the stable identifier and the public key of a user. Safety numbers are computed for both people in a conversation.
The real important code is this snipit
byte[] publicKey = getLogicalKeyBytes(unsortedIdentityKeys);
byte[] hash = ByteUtil.combine(ByteUtil.shortToByteArray(FINGERPRINT_VERSION), publicKey, stableIdentifier.getBytes());
for (int i=0;i<iterations;i++) {
digest.update(hash);
hash = digest.digest(publicKey);
}
What's happening in is we are taking the fingerprint version, public key, and stable identifier as starting inputs and hashing that once with SHA-512. The second iteration apends the public key to the hash we just produced, then hashes it a second time.
This process of adding the public key and repeating the hash continues for the number of indicated iterations.
Why do we need to do more iterations than the past?
This is due to a fundamental issue if hashing. Which is the possibility of hash collisions.
Let's say I'm an attacker (Eve). Alice wants to talk to Bob, so Signal sends her public key to Bob, but Eve intercepts the public key and substitutes her own. Normally there is an indication the key changed, and the Safety Number changes.
IF Eve had enough resources she could construct a public key which matched the safety number. To combat this threat we make it so that Eve would need to find a collision which occurs after 5200 rounds of hashing, with adding that same key every round.
This becomes computationally infeasible since each round of hashing makes finding a collision linearly more computationally expensive. The number of iterations currently picked usually is calculated on how long an attack of this style would take based in resources of the percieved threat.
I can't find any calculations from Signal as to specifically why they picked 5200.