This repository has been archived by the owner on Feb 12, 2022. It is now read-only.
/
NumericFingerprintGenerator.java
137 lines (117 loc) · 5.33 KB
/
NumericFingerprintGenerator.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/**
* Copyright (C) 2016 Open Whisper Systems
*
* Licensed according to the LICENSE file in this repository.
*/
package org.whispersystems.libsignal.fingerprint;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.util.ByteUtil;
import org.whispersystems.libsignal.util.IdentityKeyComparator;
import java.io.ByteArrayOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
public class NumericFingerprintGenerator implements FingerprintGenerator {
private static final int FINGERPRINT_VERSION = 0;
private final int iterations;
/**
* Construct a fingerprint generator for 60 digit numerics.
*
* @param iterations The number of internal iterations to perform in the process of
* generating a fingerprint. This needs to be constant, and synchronized
* across all clients.
*
* The higher the iteration count, the higher the security level:
*
* - 1024 ~ 109.7 bits
* - 1400 > 110 bits
* - 5200 > 112 bits
*/
public NumericFingerprintGenerator(int iterations) {
this.iterations = iterations;
}
/**
* Generate a scannable and displayable fingerprint.
*
* @param version The version of fingerprint you are generating.
* @param localStableIdentifier The client's "stable" identifier.
* @param localIdentityKey The client's identity key.
* @param remoteStableIdentifier The remote party's "stable" identifier.
* @param remoteIdentityKey The remote party's identity key.
* @return A unique fingerprint for this conversation.
*/
@Override
public Fingerprint createFor(int version,
byte[] localStableIdentifier,
final IdentityKey localIdentityKey,
byte[] remoteStableIdentifier,
final IdentityKey remoteIdentityKey)
{
return createFor(version,
localStableIdentifier,
new LinkedList<IdentityKey>() {{
add(localIdentityKey);
}},
remoteStableIdentifier,
new LinkedList<IdentityKey>() {{
add(remoteIdentityKey);
}});
}
/**
* Generate a scannable and displayable fingerprint for logical identities that have multiple
* physical keys.
*
* Do not trust the output of this unless you've been through the device consistency process
* for the provided localIdentityKeys.
*
* @param version The version of fingerprint you are generating.
* @param localStableIdentifier The client's "stable" identifier.
* @param localIdentityKeys The client's collection of physical identity keys.
* @param remoteStableIdentifier The remote party's "stable" identifier.
* @param remoteIdentityKeys The remote party's collection of physical identity key.
* @return A unique fingerprint for this conversation.
*/
public Fingerprint createFor(int version,
byte[] localStableIdentifier,
List<IdentityKey> localIdentityKeys,
byte[] remoteStableIdentifier,
List<IdentityKey> remoteIdentityKeys)
{
byte[] localFingerprint = getFingerprint(iterations, localStableIdentifier, localIdentityKeys);
byte[] remoteFingerprint = getFingerprint(iterations, remoteStableIdentifier, remoteIdentityKeys);
DisplayableFingerprint displayableFingerprint = new DisplayableFingerprint(localFingerprint,
remoteFingerprint);
ScannableFingerprint scannableFingerprint = new ScannableFingerprint(version,
localFingerprint,
remoteFingerprint);
return new Fingerprint(displayableFingerprint, scannableFingerprint);
}
private byte[] getFingerprint(int iterations, byte[] stableIdentifier, List<IdentityKey> unsortedIdentityKeys) {
try {
MessageDigest digest = MessageDigest.getInstance("SHA-512");
byte[] publicKey = getLogicalKeyBytes(unsortedIdentityKeys);
byte[] hash = ByteUtil.combine(ByteUtil.shortToByteArray(FINGERPRINT_VERSION),
publicKey, stableIdentifier);
for (int i=0;i<iterations;i++) {
digest.update(hash);
hash = digest.digest(publicKey);
}
return hash;
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
private byte[] getLogicalKeyBytes(List<IdentityKey> identityKeys) {
ArrayList<IdentityKey> sortedIdentityKeys = new ArrayList<>(identityKeys);
Collections.sort(sortedIdentityKeys, new IdentityKeyComparator());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
for (IdentityKey identityKey : sortedIdentityKeys) {
byte[] publicKeyBytes = identityKey.getPublicKey().serialize();
baos.write(publicKeyBytes, 0, publicKeyBytes.length);
}
return baos.toByteArray();
}
}