]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/util/KeyUtils.java
Reformat files
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / util / KeyUtils.java
1 package org.asamk.signal.manager.util;
2
3 import org.asamk.signal.manager.storage.SignalAccount;
4 import org.signal.libsignal.protocol.IdentityKey;
5 import org.signal.libsignal.protocol.IdentityKeyPair;
6 import org.signal.libsignal.protocol.InvalidKeyException;
7 import org.signal.libsignal.protocol.ecc.Curve;
8 import org.signal.libsignal.protocol.ecc.ECPrivateKey;
9 import org.signal.libsignal.protocol.kem.KEMKeyPair;
10 import org.signal.libsignal.protocol.kem.KEMKeyType;
11 import org.signal.libsignal.protocol.state.KyberPreKeyRecord;
12 import org.signal.libsignal.protocol.state.PreKeyRecord;
13 import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
14 import org.signal.libsignal.zkgroup.InvalidInputException;
15 import org.signal.libsignal.zkgroup.profiles.ProfileKey;
16 import org.whispersystems.signalservice.api.account.PreKeyCollection;
17 import org.whispersystems.signalservice.api.kbs.MasterKey;
18
19 import java.security.SecureRandom;
20 import java.util.ArrayList;
21 import java.util.Base64;
22 import java.util.List;
23
24 import static org.asamk.signal.manager.config.ServiceConfig.PREKEY_BATCH_SIZE;
25 import static org.asamk.signal.manager.config.ServiceConfig.PREKEY_MAXIMUM_ID;
26
27 public class KeyUtils {
28
29 private static final SecureRandom secureRandom = new SecureRandom();
30
31 private KeyUtils() {
32 }
33
34 public static IdentityKeyPair getIdentityKeyPair(byte[] publicKeyBytes, byte[] privateKeyBytes) {
35 try {
36 IdentityKey publicKey = new IdentityKey(publicKeyBytes);
37 ECPrivateKey privateKey = Curve.decodePrivatePoint(privateKeyBytes);
38
39 return new IdentityKeyPair(publicKey, privateKey);
40 } catch (InvalidKeyException e) {
41 throw new AssertionError(e);
42 }
43 }
44
45 public static IdentityKeyPair generateIdentityKeyPair() {
46 var djbKeyPair = Curve.generateKeyPair();
47 var djbIdentityKey = new IdentityKey(djbKeyPair.getPublicKey());
48 var djbPrivateKey = djbKeyPair.getPrivateKey();
49
50 return new IdentityKeyPair(djbIdentityKey, djbPrivateKey);
51 }
52
53 public static List<PreKeyRecord> generatePreKeyRecords(final int offset) {
54 var records = new ArrayList<PreKeyRecord>(PREKEY_BATCH_SIZE);
55 for (var i = 0; i < PREKEY_BATCH_SIZE; i++) {
56 var preKeyId = (offset + i) % PREKEY_MAXIMUM_ID;
57 var keyPair = Curve.generateKeyPair();
58 var record = new PreKeyRecord(preKeyId, keyPair);
59
60 records.add(record);
61 }
62 return records;
63 }
64
65 public static SignedPreKeyRecord generateSignedPreKeyRecord(
66 final int signedPreKeyId,
67 final ECPrivateKey privateKey
68 ) {
69 var keyPair = Curve.generateKeyPair();
70 byte[] signature;
71 try {
72 signature = Curve.calculateSignature(privateKey, keyPair.getPublicKey().serialize());
73 } catch (InvalidKeyException e) {
74 throw new AssertionError(e);
75 }
76 return new SignedPreKeyRecord(signedPreKeyId, System.currentTimeMillis(), keyPair, signature);
77 }
78
79 public static List<KyberPreKeyRecord> generateKyberPreKeyRecords(final int offset, final ECPrivateKey privateKey) {
80 var records = new ArrayList<KyberPreKeyRecord>(PREKEY_BATCH_SIZE);
81 for (var i = 0; i < PREKEY_BATCH_SIZE; i++) {
82 var preKeyId = (offset + i) % PREKEY_MAXIMUM_ID;
83 records.add(generateKyberPreKeyRecord(preKeyId, privateKey));
84 }
85 return records;
86 }
87
88 public static KyberPreKeyRecord generateKyberPreKeyRecord(final int preKeyId, final ECPrivateKey privateKey) {
89 KEMKeyPair keyPair = KEMKeyPair.generate(KEMKeyType.KYBER_1024);
90 byte[] signature = privateKey.calculateSignature(keyPair.getPublicKey().serialize());
91
92 return new KyberPreKeyRecord(preKeyId, System.currentTimeMillis(), keyPair, signature);
93 }
94
95 public static ProfileKey createProfileKey() {
96 try {
97 return new ProfileKey(getSecretBytes(32));
98 } catch (InvalidInputException e) {
99 throw new AssertionError("Profile key is guaranteed to be 32 bytes here");
100 }
101 }
102
103 public static String createPassword() {
104 return getSecret(18);
105 }
106
107 public static byte[] createStickerUploadKey() {
108 return getSecretBytes(32);
109 }
110
111 public static MasterKey createMasterKey() {
112 return MasterKey.createNew(secureRandom);
113 }
114
115 public static byte[] createRawStorageId() {
116 return getSecretBytes(16);
117 }
118
119 private static String getSecret(int size) {
120 var secret = getSecretBytes(size);
121 return Base64.getEncoder().encodeToString(secret);
122 }
123
124 public static byte[] getSecretBytes(int size) {
125 var secret = new byte[size];
126 secureRandom.nextBytes(secret);
127 return secret;
128 }
129
130 public static int getRandomInt(int bound) {
131 return secureRandom.nextInt(bound);
132 }
133
134 public static PreKeyCollection generatePreKeysForType(final SignalAccount.AccountData<?> accountData) {
135 final var keyPair = accountData.getIdentityKeyPair();
136 final var preKeyMetadata = accountData.getPreKeyMetadata();
137
138 final var nextSignedPreKeyId = preKeyMetadata.getNextSignedPreKeyId();
139 final var signedPreKey = generateSignedPreKeyRecord(nextSignedPreKeyId, keyPair.getPrivateKey());
140
141 final var privateKey = keyPair.getPrivateKey();
142 final var kyberPreKeyIdOffset = preKeyMetadata.getNextKyberPreKeyId();
143 final var lastResortKyberPreKey = generateKyberPreKeyRecord(kyberPreKeyIdOffset, privateKey);
144
145 return new PreKeyCollection(keyPair.getPublicKey(), signedPreKey, lastResortKyberPreKey);
146 }
147 }