]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java
Add support for SVR2
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / helper / PinHelper.java
1 package org.asamk.signal.manager.helper;
2
3 import org.asamk.signal.manager.api.IncorrectPinException;
4 import org.slf4j.Logger;
5 import org.slf4j.LoggerFactory;
6 import org.whispersystems.signalservice.api.KeyBackupService;
7 import org.whispersystems.signalservice.api.kbs.MasterKey;
8 import org.whispersystems.signalservice.api.kbs.PinHashUtil;
9 import org.whispersystems.signalservice.api.svr.SecureValueRecovery;
10 import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV1;
11 import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV2;
12 import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
13 import org.whispersystems.signalservice.internal.push.AuthCredentials;
14 import org.whispersystems.signalservice.internal.push.LockedException;
15
16 import java.io.IOException;
17 import java.util.Collection;
18
19 public class PinHelper {
20
21 private final static Logger logger = LoggerFactory.getLogger(PinHelper.class);
22
23 private final KeyBackupService keyBackupService;
24 private final SecureValueRecoveryV1 secureValueRecoveryV1;
25 private final SecureValueRecoveryV2 secureValueRecoveryV2;
26 private final Collection<KeyBackupService> fallbackKeyBackupServices;
27
28 public PinHelper(
29 final KeyBackupService keyBackupService,
30 final Collection<KeyBackupService> fallbackKeyBackupServices,
31 SecureValueRecoveryV2 secureValueRecoveryV2
32 ) {
33 this.keyBackupService = keyBackupService;
34 this.fallbackKeyBackupServices = fallbackKeyBackupServices;
35 this.secureValueRecoveryV1 = new SecureValueRecoveryV1(keyBackupService);
36 this.secureValueRecoveryV2 = secureValueRecoveryV2;
37 }
38
39 public void setRegistrationLockPin(
40 String pin, MasterKey masterKey
41 ) throws IOException {
42 final var pinChangeSession = keyBackupService.newPinChangeSession();
43 final var hashedPin = PinHashUtil.hashPin(pin, pinChangeSession.hashSalt());
44
45 try {
46 pinChangeSession.setPin(hashedPin, masterKey);
47 } catch (UnauthenticatedResponseException e) {
48 throw new IOException(e);
49 }
50 pinChangeSession.enableRegistrationLock(masterKey);
51
52 final var backupResponse = secureValueRecoveryV2.setPin(pin, masterKey).execute();
53 if (backupResponse instanceof SecureValueRecovery.BackupResponse.Success) {
54 } else if (backupResponse instanceof SecureValueRecovery.BackupResponse.ServerRejected) {
55 logger.warn("Backup svr2 failed: ServerRejected");
56 } else if (backupResponse instanceof SecureValueRecovery.BackupResponse.EnclaveNotFound) {
57 logger.warn("Backup svr2 failed: EnclaveNotFound");
58 } else if (backupResponse instanceof SecureValueRecovery.BackupResponse.ExposeFailure) {
59 logger.warn("Backup svr2 failed: ExposeFailure");
60 } else if (backupResponse instanceof SecureValueRecovery.BackupResponse.ApplicationError error) {
61 throw new IOException(error.getException());
62 } else if (backupResponse instanceof SecureValueRecovery.BackupResponse.NetworkError error) {
63 throw error.getException();
64 } else {
65 throw new AssertionError("Unexpected response");
66 }
67 }
68
69 public void migrateRegistrationLockPin(String pin, MasterKey masterKey) throws IOException {
70 setRegistrationLockPin(pin, masterKey);
71
72 for (final var keyBackupService : fallbackKeyBackupServices) {
73 try {
74 final var pinChangeSession = keyBackupService.newPinChangeSession();
75 pinChangeSession.removePin();
76 } catch (Exception e) {
77 logger.warn("Failed to remove PIN from fallback KBS: {}", e.getMessage());
78 }
79 }
80 }
81
82 public void removeRegistrationLockPin() throws IOException {
83 final var pinChangeSession = keyBackupService.newPinChangeSession();
84 pinChangeSession.disableRegistrationLock();
85 try {
86 pinChangeSession.removePin();
87 } catch (UnauthenticatedResponseException e) {
88 throw new IOException(e);
89 }
90
91 final var deleteResponse = secureValueRecoveryV2.deleteData();
92 if (deleteResponse instanceof SecureValueRecovery.DeleteResponse.Success) {
93 } else if (deleteResponse instanceof SecureValueRecovery.DeleteResponse.ServerRejected) {
94 logger.warn("Delete svr2 failed: ServerRejected");
95 } else if (deleteResponse instanceof SecureValueRecovery.DeleteResponse.EnclaveNotFound) {
96 logger.warn("Delete svr2 failed: EnclaveNotFound");
97 } else if (deleteResponse instanceof SecureValueRecovery.DeleteResponse.ApplicationError error) {
98 throw new IOException(error.getException());
99 } else if (deleteResponse instanceof SecureValueRecovery.DeleteResponse.NetworkError error) {
100 throw error.getException();
101 } else {
102 throw new AssertionError("Unexpected response");
103 }
104 }
105
106 public SecureValueRecovery.RestoreResponse.Success getRegistrationLockData(
107 String pin, LockedException e
108 ) throws IOException, IncorrectPinException {
109 var svr1Credentials = e.getSvr1Credentials();
110 if (svr1Credentials != null) {
111 return getRegistrationLockData(secureValueRecoveryV1, svr1Credentials, pin);
112 }
113
114 var svr2Credentials = e.getSvr2Credentials();
115 if (svr2Credentials != null) {
116 return getRegistrationLockData(secureValueRecoveryV2, svr2Credentials, pin);
117 }
118
119 return null;
120 }
121
122 public SecureValueRecovery.RestoreResponse.Success getRegistrationLockData(
123 SecureValueRecovery secureValueRecovery, AuthCredentials authCredentials, String pin
124 ) throws IOException, IncorrectPinException {
125 final var restoreResponse = secureValueRecovery.restoreDataPreRegistration(authCredentials, pin);
126
127 if (restoreResponse instanceof SecureValueRecovery.RestoreResponse.Success s) {
128 return s;
129 } else if (restoreResponse instanceof SecureValueRecovery.RestoreResponse.PinMismatch pinMismatch) {
130 throw new IncorrectPinException(pinMismatch.getTriesRemaining());
131 } else if (restoreResponse instanceof SecureValueRecovery.RestoreResponse.ApplicationError error) {
132 throw new IOException(error.getException());
133 } else if (restoreResponse instanceof SecureValueRecovery.RestoreResponse.NetworkError error) {
134 throw error.getException();
135 } else {
136 throw new AssertionError("Unexpected response");
137 }
138 }
139 }