1 package org
.asamk
.signal
.manager
.util
;
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
.ECKeyPair
;
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
.backup
.MediaRootBackupKey
;
19 import java
.security
.SecureRandom
;
20 import java
.util
.ArrayList
;
21 import java
.util
.Base64
;
22 import java
.util
.List
;
24 import static org
.asamk
.signal
.manager
.config
.ServiceConfig
.PREKEY_BATCH_SIZE
;
25 import static org
.asamk
.signal
.manager
.config
.ServiceConfig
.PREKEY_MAXIMUM_ID
;
27 public class KeyUtils
{
29 private static final SecureRandom secureRandom
= new SecureRandom();
34 public static IdentityKeyPair
getIdentityKeyPair(byte[] publicKeyBytes
, byte[] privateKeyBytes
) {
36 final var publicKey
= new IdentityKey(publicKeyBytes
);
37 final var privateKey
= new ECPrivateKey(privateKeyBytes
);
39 return new IdentityKeyPair(publicKey
, privateKey
);
40 } catch (InvalidKeyException e
) {
41 throw new AssertionError(e
);
45 public static IdentityKeyPair
generateIdentityKeyPair() {
46 var djbKeyPair
= ECKeyPair
.generate();
47 var djbIdentityKey
= new IdentityKey(djbKeyPair
.getPublicKey());
48 var djbPrivateKey
= djbKeyPair
.getPrivateKey();
50 return new IdentityKeyPair(djbIdentityKey
, djbPrivateKey
);
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
= ECKeyPair
.generate();
58 var record = new PreKeyRecord(preKeyId
, keyPair
);
65 public static SignedPreKeyRecord
generateSignedPreKeyRecord(
66 final int signedPreKeyId
,
67 final ECPrivateKey privateKey
69 var keyPair
= ECKeyPair
.generate();
71 signature
= privateKey
.calculateSignature(keyPair
.getPublicKey().serialize());
72 return new SignedPreKeyRecord(signedPreKeyId
, System
.currentTimeMillis(), keyPair
, signature
);
75 public static List
<KyberPreKeyRecord
> generateKyberPreKeyRecords(final int offset
, final ECPrivateKey privateKey
) {
76 var records
= new ArrayList
<KyberPreKeyRecord
>(PREKEY_BATCH_SIZE
);
77 for (var i
= 0; i
< PREKEY_BATCH_SIZE
; i
++) {
78 var preKeyId
= (offset
+ i
) % PREKEY_MAXIMUM_ID
;
79 records
.add(generateKyberPreKeyRecord(preKeyId
, privateKey
));
84 public static KyberPreKeyRecord
generateKyberPreKeyRecord(final int preKeyId
, final ECPrivateKey privateKey
) {
85 KEMKeyPair keyPair
= KEMKeyPair
.generate(KEMKeyType
.KYBER_1024
);
86 byte[] signature
= privateKey
.calculateSignature(keyPair
.getPublicKey().serialize());
88 return new KyberPreKeyRecord(preKeyId
, System
.currentTimeMillis(), keyPair
, signature
);
91 public static ProfileKey
createProfileKey() {
93 return new ProfileKey(getSecretBytes(32));
94 } catch (InvalidInputException e
) {
95 throw new AssertionError("Profile key is guaranteed to be 32 bytes here");
99 public static String
createPassword() {
100 return getSecret(18);
103 public static byte[] createStickerUploadKey() {
104 return getSecretBytes(32);
107 public static MediaRootBackupKey
createMediaRootBackupKey() {
108 return new MediaRootBackupKey(getSecretBytes(32));
111 public static byte[] createRawStorageId() {
112 return getSecretBytes(16);
115 private static String
getSecret(int size
) {
116 var secret
= getSecretBytes(size
);
117 return Base64
.getEncoder().encodeToString(secret
);
120 public static byte[] getSecretBytes(int size
) {
121 var secret
= new byte[size
];
122 secureRandom
.nextBytes(secret
);
126 public static int getRandomInt(int bound
) {
127 return secureRandom
.nextInt(bound
);
130 public static PreKeyCollection
generatePreKeysForType(final SignalAccount
.AccountData
<?
> accountData
) {
131 final var keyPair
= accountData
.getIdentityKeyPair();
132 final var preKeyMetadata
= accountData
.getPreKeyMetadata();
134 final var nextSignedPreKeyId
= preKeyMetadata
.getNextSignedPreKeyId();
135 final var signedPreKey
= generateSignedPreKeyRecord(nextSignedPreKeyId
, keyPair
.getPrivateKey());
137 final var privateKey
= keyPair
.getPrivateKey();
138 final var kyberPreKeyIdOffset
= preKeyMetadata
.getNextKyberPreKeyId();
139 final var lastResortKyberPreKey
= generateKyberPreKeyRecord(kyberPreKeyIdOffset
, privateKey
);
141 return new PreKeyCollection(keyPair
.getPublicKey(), signedPreKey
, lastResortKyberPreKey
);