1 package org
.asamk
.signal
.manager
.util
;
3 import org
.signal
.libsignal
.protocol
.IdentityKey
;
4 import org
.signal
.libsignal
.protocol
.IdentityKeyPair
;
5 import org
.signal
.libsignal
.protocol
.InvalidKeyException
;
6 import org
.signal
.libsignal
.protocol
.ecc
.Curve
;
7 import org
.signal
.libsignal
.protocol
.ecc
.ECPrivateKey
;
8 import org
.signal
.libsignal
.protocol
.kem
.KEMKeyPair
;
9 import org
.signal
.libsignal
.protocol
.kem
.KEMKeyType
;
10 import org
.signal
.libsignal
.protocol
.state
.KyberPreKeyRecord
;
11 import org
.signal
.libsignal
.protocol
.state
.PreKeyRecord
;
12 import org
.signal
.libsignal
.protocol
.state
.SignedPreKeyRecord
;
13 import org
.signal
.libsignal
.zkgroup
.InvalidInputException
;
14 import org
.signal
.libsignal
.zkgroup
.profiles
.ProfileKey
;
15 import org
.whispersystems
.signalservice
.api
.kbs
.MasterKey
;
17 import java
.security
.SecureRandom
;
18 import java
.util
.ArrayList
;
19 import java
.util
.Base64
;
20 import java
.util
.List
;
22 import static org
.asamk
.signal
.manager
.config
.ServiceConfig
.PREKEY_MAXIMUM_ID
;
24 public class KeyUtils
{
26 private static final SecureRandom secureRandom
= new SecureRandom();
31 public static IdentityKeyPair
getIdentityKeyPair(byte[] publicKeyBytes
, byte[] privateKeyBytes
) {
33 IdentityKey publicKey
= new IdentityKey(publicKeyBytes
);
34 ECPrivateKey privateKey
= Curve
.decodePrivatePoint(privateKeyBytes
);
36 return new IdentityKeyPair(publicKey
, privateKey
);
37 } catch (InvalidKeyException e
) {
38 throw new AssertionError(e
);
42 public static IdentityKeyPair
generateIdentityKeyPair() {
43 var djbKeyPair
= Curve
.generateKeyPair();
44 var djbIdentityKey
= new IdentityKey(djbKeyPair
.getPublicKey());
45 var djbPrivateKey
= djbKeyPair
.getPrivateKey();
47 return new IdentityKeyPair(djbIdentityKey
, djbPrivateKey
);
50 public static List
<PreKeyRecord
> generatePreKeyRecords(final int offset
, final int batchSize
) {
51 var records
= new ArrayList
<PreKeyRecord
>(batchSize
);
52 for (var i
= 0; i
< batchSize
; i
++) {
53 var preKeyId
= (offset
+ i
) % PREKEY_MAXIMUM_ID
;
54 var keyPair
= Curve
.generateKeyPair();
55 var record = new PreKeyRecord(preKeyId
, keyPair
);
62 public static SignedPreKeyRecord
generateSignedPreKeyRecord(
63 final IdentityKeyPair identityKeyPair
, final int signedPreKeyId
65 var keyPair
= Curve
.generateKeyPair();
68 signature
= Curve
.calculateSignature(identityKeyPair
.getPrivateKey(), keyPair
.getPublicKey().serialize());
69 } catch (InvalidKeyException e
) {
70 throw new AssertionError(e
);
72 return new SignedPreKeyRecord(signedPreKeyId
, System
.currentTimeMillis(), keyPair
, signature
);
75 public static List
<KyberPreKeyRecord
> generateKyberPreKeyRecords(
76 final int offset
, final int batchSize
, final ECPrivateKey privateKey
78 var records
= new ArrayList
<KyberPreKeyRecord
>(batchSize
);
79 for (var i
= 0; i
< batchSize
; i
++) {
80 var preKeyId
= (offset
+ i
) % PREKEY_MAXIMUM_ID
;
81 records
.add(generateKyberPreKeyRecord(preKeyId
, privateKey
));
86 public static KyberPreKeyRecord
generateKyberPreKeyRecord(final int preKeyId
, final ECPrivateKey privateKey
) {
87 KEMKeyPair keyPair
= KEMKeyPair
.generate(KEMKeyType
.KYBER_1024
);
88 byte[] signature
= privateKey
.calculateSignature(keyPair
.getPublicKey().serialize());
90 return new KyberPreKeyRecord(preKeyId
, System
.currentTimeMillis(), keyPair
, signature
);
93 public static ProfileKey
createProfileKey() {
95 return new ProfileKey(getSecretBytes(32));
96 } catch (InvalidInputException e
) {
97 throw new AssertionError("Profile key is guaranteed to be 32 bytes here");
101 public static String
createPassword() {
102 return getSecret(18);
105 public static byte[] createStickerUploadKey() {
106 return getSecretBytes(32);
109 public static MasterKey
createMasterKey() {
110 return MasterKey
.createNew(secureRandom
);
113 private static String
getSecret(int size
) {
114 var secret
= getSecretBytes(size
);
115 return Base64
.getEncoder().encodeToString(secret
);
118 public static byte[] getSecretBytes(int size
) {
119 var secret
= new byte[size
];
120 secureRandom
.nextBytes(secret
);
124 public static int getRandomInt(int bound
) {
125 return secureRandom
.nextInt(bound
);