1 package org
.asamk
.signal
.manager
.helper
;
3 import org
.asamk
.signal
.manager
.api
.IncorrectPinException
;
4 import org
.asamk
.signal
.manager
.api
.Pair
;
5 import org
.signal
.libsignal
.protocol
.InvalidKeyException
;
6 import org
.slf4j
.Logger
;
7 import org
.slf4j
.LoggerFactory
;
8 import org
.whispersystems
.signalservice
.api
.KeyBackupService
;
9 import org
.whispersystems
.signalservice
.api
.KeyBackupServicePinException
;
10 import org
.whispersystems
.signalservice
.api
.SvrNoDataException
;
11 import org
.whispersystems
.signalservice
.api
.SvrPinData
;
12 import org
.whispersystems
.signalservice
.api
.kbs
.MasterKey
;
13 import org
.whispersystems
.signalservice
.api
.kbs
.PinHashUtil
;
14 import org
.whispersystems
.signalservice
.internal
.contacts
.crypto
.UnauthenticatedResponseException
;
15 import org
.whispersystems
.signalservice
.internal
.contacts
.entities
.TokenResponse
;
16 import org
.whispersystems
.signalservice
.internal
.push
.AuthCredentials
;
17 import org
.whispersystems
.signalservice
.internal
.push
.LockedException
;
19 import java
.io
.IOException
;
20 import java
.util
.Collection
;
21 import java
.util
.stream
.Stream
;
23 public class PinHelper
{
25 private final static Logger logger
= LoggerFactory
.getLogger(PinHelper
.class);
27 private final KeyBackupService keyBackupService
;
28 private final Collection
<KeyBackupService
> fallbackKeyBackupServices
;
31 final KeyBackupService keyBackupService
, final Collection
<KeyBackupService
> fallbackKeyBackupServices
33 this.keyBackupService
= keyBackupService
;
34 this.fallbackKeyBackupServices
= fallbackKeyBackupServices
;
37 public void setRegistrationLockPin(
38 String pin
, MasterKey masterKey
39 ) throws IOException
{
40 final var pinChangeSession
= keyBackupService
.newPinChangeSession();
41 final var hashedPin
= PinHashUtil
.hashPin(pin
, pinChangeSession
.hashSalt());
44 pinChangeSession
.setPin(hashedPin
, masterKey
);
45 } catch (UnauthenticatedResponseException e
) {
46 throw new IOException(e
);
48 pinChangeSession
.enableRegistrationLock(masterKey
);
51 public void migrateRegistrationLockPin(String pin
, MasterKey masterKey
) throws IOException
{
52 setRegistrationLockPin(pin
, masterKey
);
54 for (final var keyBackupService
: fallbackKeyBackupServices
) {
56 final var pinChangeSession
= keyBackupService
.newPinChangeSession();
57 pinChangeSession
.removePin();
58 } catch (Exception e
) {
59 logger
.warn("Failed to remove PIN from fallback KBS: {}", e
.getMessage());
64 public void removeRegistrationLockPin() throws IOException
{
65 final var pinChangeSession
= keyBackupService
.newPinChangeSession();
66 pinChangeSession
.disableRegistrationLock();
68 pinChangeSession
.removePin();
69 } catch (UnauthenticatedResponseException e
) {
70 throw new IOException(e
);
74 public SvrPinData
getRegistrationLockData(
75 String pin
, LockedException e
76 ) throws IOException
, IncorrectPinException
{
77 var basicStorageCredentials
= e
.getSvr1Credentials();
78 if (basicStorageCredentials
== null) {
83 return getRegistrationLockData(pin
, basicStorageCredentials
);
84 } catch (SvrNoDataException ex
) {
85 throw new IOException(e
);
86 } catch (KeyBackupServicePinException ex
) {
87 throw new IncorrectPinException(ex
.getTriesRemaining());
91 private SvrPinData
getRegistrationLockData(
92 String pin
, AuthCredentials authCredentials
93 ) throws IOException
, SvrNoDataException
, KeyBackupServicePinException
{
94 final var basicStorageCredentials
= authCredentials
.asBasic();
95 var tokenResponsePair
= getTokenResponse(basicStorageCredentials
);
96 final var tokenResponse
= tokenResponsePair
.first();
97 final var keyBackupService
= tokenResponsePair
.second();
99 var registrationLockData
= restoreMasterKey(pin
, basicStorageCredentials
, tokenResponse
, keyBackupService
);
100 if (registrationLockData
== null) {
101 throw new AssertionError("Failed to restore master key");
103 return registrationLockData
;
106 private Pair
<TokenResponse
, KeyBackupService
> getTokenResponse(String basicStorageCredentials
) throws IOException
{
107 final var keyBackupServices
= Stream
.concat(Stream
.of(keyBackupService
), fallbackKeyBackupServices
.stream())
109 for (final var keyBackupService
: keyBackupServices
) {
110 var tokenResponse
= keyBackupService
.getToken(basicStorageCredentials
);
111 if (tokenResponse
!= null && tokenResponse
.getTries() > 0) {
112 return new Pair
<>(tokenResponse
, keyBackupService
);
115 throw new IOException("KBS Account locked, maximum pin attempts reached.");
118 private SvrPinData
restoreMasterKey(
120 String basicStorageCredentials
,
121 TokenResponse tokenResponse
,
122 final KeyBackupService keyBackupService
123 ) throws IOException
, SvrNoDataException
, KeyBackupServicePinException
{
124 if (pin
== null) return null;
126 if (basicStorageCredentials
== null) {
127 throw new AssertionError("Cannot restore KBS key, no storage credentials supplied");
130 var session
= keyBackupService
.newRegistrationSession(basicStorageCredentials
, tokenResponse
);
133 var hashedPin
= PinHashUtil
.hashPin(pin
, session
.hashSalt());
134 var kbsData
= session
.restorePin(hashedPin
);
135 if (kbsData
== null) {
136 throw new AssertionError("Null not expected");
139 } catch (UnauthenticatedResponseException
| InvalidKeyException e
) {
140 throw new IOException(e
);