mirror of
https://github.com/signalapp/libsignal.git
synced 2024-09-20 03:52:17 +02:00
Remove enclave operation timeout arguments
The enclave interactions have internal progress monitoring in the form of websocket PING/PONG frames, so the timeout parameters aren't necessary for broken connection detection.
This commit is contained in:
parent
819606dab9
commit
10a6d8b744
@ -5,10 +5,7 @@
|
||||
|
||||
package org.signal.libsignal.net;
|
||||
|
||||
import static org.signal.libsignal.net.DurationExt.timeoutMillis;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import org.signal.libsignal.internal.CompletableFuture;
|
||||
import org.signal.libsignal.internal.Native;
|
||||
@ -16,11 +13,7 @@ import org.signal.libsignal.internal.NativeHandleGuard;
|
||||
|
||||
class CdsiLookup implements NativeHandleGuard.Owner {
|
||||
public static CompletableFuture<CdsiLookup> start(
|
||||
Network network,
|
||||
String username,
|
||||
String password,
|
||||
CdsiLookupRequest request,
|
||||
Duration timeout)
|
||||
Network network, String username, String password, CdsiLookupRequest request)
|
||||
throws IOException, InterruptedException, ExecutionException {
|
||||
|
||||
CdsiLookupRequest.NativeRequest nativeRequest = request.makeNative();
|
||||
@ -33,8 +26,7 @@ class CdsiLookup implements NativeHandleGuard.Owner {
|
||||
connectionManager.nativeHandle(),
|
||||
username,
|
||||
password,
|
||||
nativeRequest.getHandle(),
|
||||
timeoutMillis(timeout))
|
||||
nativeRequest.getHandle())
|
||||
.thenApply((Long nativeHandle) -> new CdsiLookup(nativeHandle, network));
|
||||
}
|
||||
}
|
||||
|
@ -6,7 +6,6 @@
|
||||
package org.signal.libsignal.net;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.function.Consumer;
|
||||
import org.signal.libsignal.internal.CompletableFuture;
|
||||
@ -47,13 +46,9 @@ public class Network {
|
||||
}
|
||||
|
||||
public CompletableFuture<CdsiLookupResponse> cdsiLookup(
|
||||
String username,
|
||||
String password,
|
||||
CdsiLookupRequest request,
|
||||
Duration timeout,
|
||||
Consumer<byte[]> tokenConsumer)
|
||||
String username, String password, CdsiLookupRequest request, Consumer<byte[]> tokenConsumer)
|
||||
throws IOException, InterruptedException, ExecutionException {
|
||||
return CdsiLookup.start(this, username, password, request, timeout)
|
||||
return CdsiLookup.start(this, username, password, request)
|
||||
.thenCompose(
|
||||
(CdsiLookup lookup) -> {
|
||||
tokenConsumer.accept(lookup.getToken());
|
||||
|
@ -5,9 +5,6 @@
|
||||
|
||||
package org.signal.libsignal.net;
|
||||
|
||||
import static org.signal.libsignal.net.DurationExt.timeoutMillis;
|
||||
|
||||
import java.time.Duration;
|
||||
import org.signal.libsignal.internal.CompletableFuture;
|
||||
import org.signal.libsignal.internal.Native;
|
||||
import org.signal.libsignal.internal.NativeHandleGuard;
|
||||
@ -26,8 +23,8 @@ import org.signal.libsignal.internal.NativeHandleGuard;
|
||||
* // Instantiate EnclaveAuth with the username and password obtained from the Chat Server.
|
||||
* EnclaveAuth auth = new EnclaveAuth(USERNAME, ENCLAVE_PASSWORD);
|
||||
* // Store a value in SVR3. Here 10 is the number of permitted restore attempts.
|
||||
* byte[] shareSet = net.svr3().backup(SECRET_TO_BE_STORED, PASSWORD, 10, auth, TIMEOUT).get();
|
||||
* byte[] restoredSecret = net.svr3().restore(PASSWORD, shareSet, auth, TIMEOUT).get();
|
||||
* byte[] shareSet = net.svr3().backup(SECRET_TO_BE_STORED, PASSWORD, 10, auth).get();
|
||||
* byte[] restoredSecret = net.svr3().restore(PASSWORD, shareSet, auth).get();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Please note that the methods of this class return {@link
|
||||
@ -62,8 +59,6 @@ public final class Svr3 {
|
||||
* and password obtained from the Chat Server. The password is an OTP which is generally good
|
||||
* for about 15 minutes, therefore it can be reused for the subsequent calls to either backup
|
||||
* or restore that are not too far apart in time.
|
||||
* @param timeout The maximum wall time libsignal is allowed to spend communicating with SVR3
|
||||
* service.
|
||||
* @return an instance of {@link org.signal.libsignal.internal.CompletableFuture} which-when
|
||||
* awaited-will return a byte array with a serialized masked share set. It is supposed to be
|
||||
* an opaque blob for the clients and therefore no assumptions should be made about its
|
||||
@ -71,14 +66,14 @@ public final class Svr3 {
|
||||
* along with the password. Please note that masked share set does not have to be treated as
|
||||
* secret.
|
||||
* @throws {@link org.signal.libsignal.net.NetworkException} in case of network related errors,
|
||||
* including timeouts and failed auth.
|
||||
* including connect timeout and failed auth.
|
||||
* @throws {@link org.signal.libsignal.attest.AttestationFailedException} when an attempt to
|
||||
* validate the server attestation document fails.
|
||||
* @throws {@link org.signal.libsignal.sgxsession.SgxCommunicationFailureException} when a Noise
|
||||
* connection error happens.
|
||||
*/
|
||||
public final CompletableFuture<byte[]> backup(
|
||||
byte[] what, String password, int maxTries, EnclaveAuth auth, Duration timeout) {
|
||||
byte[] what, String password, int maxTries, EnclaveAuth auth) {
|
||||
try (NativeHandleGuard asyncRuntime = new NativeHandleGuard(this.network.getAsyncContext());
|
||||
NativeHandleGuard connectionManager =
|
||||
new NativeHandleGuard(this.network.getConnectionManager())) {
|
||||
@ -90,8 +85,7 @@ public final class Svr3 {
|
||||
password,
|
||||
maxTries,
|
||||
auth.username,
|
||||
auth.password,
|
||||
timeoutMillis(timeout));
|
||||
auth.password);
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,12 +111,10 @@ public final class Svr3 {
|
||||
* and password obtained from the Chat Server. The password is an OTP which is generally good
|
||||
* for about 15 minutes, therefore it can be reused for the subsequent calls to either backup
|
||||
* or restore that are not too far apart in time.
|
||||
* @param timeout The maximum wall time libsignal is allowed to spend communicating with SVR3
|
||||
* service.
|
||||
* @return an instance of {@link org.signal.libsignal.internal.CompletableFuture} which-when
|
||||
* awaited-will return a byte array with the restored secret.
|
||||
* @throws {@link org.signal.libsignal.net.NetworkException} in case of network related errors,
|
||||
* including timeouts and failed auth.
|
||||
* including connection timeouts and failed auth.
|
||||
* @throws {@link org.signal.libsignal.svr.DataMissingException} when the maximum restore attempts
|
||||
* number has been exceeded or if the value has never been backed up.
|
||||
* @throws {@link org.signal.libsignal.svr.RestoreFailedException} when the combination of the
|
||||
@ -136,7 +128,7 @@ public final class Svr3 {
|
||||
* connection error happens.
|
||||
*/
|
||||
public final CompletableFuture<byte[]> restore(
|
||||
String password, byte[] shareSet, EnclaveAuth auth, Duration timeout) {
|
||||
String password, byte[] shareSet, EnclaveAuth auth) {
|
||||
try (NativeHandleGuard asyncRuntime = new NativeHandleGuard(this.network.getAsyncContext());
|
||||
NativeHandleGuard connectionManager =
|
||||
new NativeHandleGuard(this.network.getConnectionManager())) {
|
||||
@ -147,8 +139,7 @@ public final class Svr3 {
|
||||
password,
|
||||
shareSet,
|
||||
auth.username,
|
||||
auth.password,
|
||||
timeoutMillis(timeout));
|
||||
auth.password);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,7 +80,7 @@ public class CdsiLookupResponseTest {
|
||||
assertLookupErrorIs("ConnectDnsFailed", IOException.class, "DNS lookup failed");
|
||||
assertLookupErrorIs(
|
||||
"WebSocketIdleTooLong", NetworkException.class, "channel was idle for too long");
|
||||
assertLookupErrorIs("Timeout", NetworkException.class, "timeout");
|
||||
assertLookupErrorIs("ConnectionTimedOut", NetworkException.class, "connect timed out");
|
||||
assertLookupErrorIs("ServerCrashed", CdsiProtocolException.class, "Server error: crashed");
|
||||
}
|
||||
|
||||
@ -88,7 +88,9 @@ public class CdsiLookupResponseTest {
|
||||
String errorDescription, Class<E> expectedErrorType, String expectedMessage) {
|
||||
E e =
|
||||
assertThrows(
|
||||
expectedErrorType, () -> Native.TESTING_CdsiLookupErrorConvert(errorDescription));
|
||||
"for " + errorDescription,
|
||||
expectedErrorType,
|
||||
() -> Native.TESTING_CdsiLookupErrorConvert(errorDescription));
|
||||
assertEquals(e.getMessage(), expectedMessage);
|
||||
return e;
|
||||
}
|
||||
|
@ -8,7 +8,6 @@ package org.signal.libsignal.net;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import org.junit.Assume;
|
||||
import org.junit.Before;
|
||||
@ -25,7 +24,6 @@ public class Svr3Test {
|
||||
Hex.fromStringCondensedAssert(
|
||||
"d2ae1668ac8a2bfd6170498332babad7cd72b9314631559a361310eee0a8adc6");
|
||||
private final String ENCLAVE_SECRET = System.getenv("ENCLAVE_SECRET");
|
||||
private final Duration TIMEOUT = Duration.ofSeconds(10);
|
||||
|
||||
private EnclaveAuth auth;
|
||||
|
||||
@ -53,8 +51,8 @@ public class Svr3Test {
|
||||
Network net = new Network(Network.Environment.STAGING);
|
||||
byte[] restored =
|
||||
net.svr3()
|
||||
.backup(STORED_SECRET, "password", 2, this.auth, TIMEOUT)
|
||||
.thenCompose(shareSet -> net.svr3().restore("password", shareSet, this.auth, TIMEOUT))
|
||||
.backup(STORED_SECRET, "password", 2, this.auth)
|
||||
.thenCompose(shareSet -> net.svr3().restore("password", shareSet, this.auth))
|
||||
.get();
|
||||
assertEquals(Hex.toStringCondensed(STORED_SECRET), Hex.toStringCondensed(restored));
|
||||
}
|
||||
@ -63,11 +61,11 @@ public class Svr3Test {
|
||||
public void noMoreTries() throws Exception {
|
||||
Network net = new Network(Network.Environment.STAGING);
|
||||
// Backup and first restore should succeed
|
||||
byte[] shareSet = net.svr3().backup(STORED_SECRET, "password", 1, this.auth, TIMEOUT).get();
|
||||
net.svr3().restore("password", shareSet, this.auth, TIMEOUT).get();
|
||||
byte[] shareSet = net.svr3().backup(STORED_SECRET, "password", 1, this.auth).get();
|
||||
net.svr3().restore("password", shareSet, this.auth).get();
|
||||
try {
|
||||
// The next attempt should fail
|
||||
net.svr3().restore("password", shareSet, this.auth, TIMEOUT).get();
|
||||
net.svr3().restore("password", shareSet, this.auth).get();
|
||||
} catch (ExecutionException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
assertTrue("Unexpected exception: " + cause, cause instanceof DataMissingException);
|
||||
@ -77,9 +75,9 @@ public class Svr3Test {
|
||||
@Test
|
||||
public void failedRestore() throws Exception {
|
||||
Network net = new Network(Network.Environment.STAGING);
|
||||
byte[] shareSet = net.svr3().backup(STORED_SECRET, "password", 1, this.auth, TIMEOUT).get();
|
||||
byte[] shareSet = net.svr3().backup(STORED_SECRET, "password", 1, this.auth).get();
|
||||
try {
|
||||
net.svr3().restore("wrong password", shareSet, this.auth, TIMEOUT).get();
|
||||
net.svr3().restore("wrong password", shareSet, this.auth).get();
|
||||
} catch (ExecutionException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
assertTrue("Unexpected exception: " + cause, cause instanceof RestoreFailedException);
|
||||
@ -91,14 +89,14 @@ public class Svr3Test {
|
||||
Network net = new Network(Network.Environment.STAGING);
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> net.svr3().backup(STORED_SECRET, "password", 0, this.auth, TIMEOUT).get());
|
||||
() -> net.svr3().backup(STORED_SECRET, "password", 0, this.auth).get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void badSecret() throws Exception {
|
||||
Network net = new Network(Network.Environment.STAGING);
|
||||
try {
|
||||
net.svr3().backup(new byte[31], "password", 1, this.auth, TIMEOUT).get();
|
||||
net.svr3().backup(new byte[31], "password", 1, this.auth).get();
|
||||
} catch (ExecutionException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
assertTrue("Unexpected exception: " + cause, cause instanceof AssertionError);
|
||||
@ -108,25 +106,13 @@ public class Svr3Test {
|
||||
@Test
|
||||
public void badShareSet() throws Exception {
|
||||
Network net = new Network(Network.Environment.STAGING);
|
||||
byte[] shareSet = net.svr3().backup(STORED_SECRET, "password", 1, this.auth, TIMEOUT).get();
|
||||
byte[] shareSet = net.svr3().backup(STORED_SECRET, "password", 1, this.auth).get();
|
||||
shareSet[0] ^= 0xff;
|
||||
try {
|
||||
net.svr3().restore("password", shareSet, this.auth, TIMEOUT).get();
|
||||
net.svr3().restore("password", shareSet, this.auth).get();
|
||||
} catch (ExecutionException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
assertTrue("Unexpected exception: " + cause, cause instanceof SvrException);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeout() throws Exception {
|
||||
final Duration SHORT_TIMEOUT = Duration.ofMillis(100);
|
||||
Network net = new Network(Network.Environment.STAGING);
|
||||
try {
|
||||
net.svr3().backup(STORED_SECRET, "password", 1, this.auth, SHORT_TIMEOUT).get();
|
||||
} catch (ExecutionException ex) {
|
||||
Throwable cause = ex.getCause();
|
||||
assertTrue("Unexpected exception: " + cause, cause instanceof NetworkException);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -158,7 +158,7 @@ public final class Native {
|
||||
|
||||
public static native void CdsiLookup_Destroy(long handle);
|
||||
public static native CompletableFuture<Object> CdsiLookup_complete(long asyncRuntime, long lookup);
|
||||
public static native CompletableFuture<Long> CdsiLookup_new(long asyncRuntime, long connectionManager, String username, String password, long request, int timeoutMillis);
|
||||
public static native CompletableFuture<Long> CdsiLookup_new(long asyncRuntime, long connectionManager, String username, String password, long request);
|
||||
public static native byte[] CdsiLookup_token(long lookup);
|
||||
|
||||
public static native CompletableFuture<Object> ChatService_connect_auth(long asyncRuntime, long chat);
|
||||
@ -598,9 +598,9 @@ public final class Native {
|
||||
|
||||
public static native long Svr2Client_New(byte[] mrenclave, byte[] attestationMsg, long currentTimestamp) throws Exception;
|
||||
|
||||
public static native CompletableFuture<byte[]> Svr3Backup(long asyncRuntime, long connectionManager, byte[] secret, String password, int maxTries, String username, String enclavePassword, int opTimeoutMs);
|
||||
public static native CompletableFuture<byte[]> Svr3Backup(long asyncRuntime, long connectionManager, byte[] secret, String password, int maxTries, String username, String enclavePassword);
|
||||
|
||||
public static native CompletableFuture<byte[]> Svr3Restore(long asyncRuntime, long connectionManager, String password, byte[] shareSet, String username, String enclavePassword, int opTimeoutMs);
|
||||
public static native CompletableFuture<byte[]> Svr3Restore(long asyncRuntime, long connectionManager, String password, byte[] shareSet, String username, String enclavePassword);
|
||||
|
||||
public static native void TESTING_CdsiLookupErrorConvert(String errorDescription) throws Exception;
|
||||
public static native CompletableFuture<Object> TESTING_CdsiLookupResponseConvert(long asyncRuntime);
|
||||
|
6
node/Native.d.ts
vendored
6
node/Native.d.ts
vendored
@ -166,7 +166,7 @@ export function CallLinkSecretParams_DeriveFromRootKey(rootKey: Buffer): Buffer;
|
||||
export function CallLinkSecretParams_GetPublicParams(paramsBytes: Buffer): Buffer;
|
||||
export function Cds2ClientState_New(mrenclave: Buffer, attestationMsg: Buffer, currentTimestamp: Timestamp): SgxClientState;
|
||||
export function CdsiLookup_complete(asyncRuntime: Wrapper<TokioAsyncContext>, lookup: Wrapper<CdsiLookup>): Promise<LookupResponse>;
|
||||
export function CdsiLookup_new(asyncRuntime: Wrapper<TokioAsyncContext>, connectionManager: Wrapper<ConnectionManager>, username: string, password: string, request: Wrapper<LookupRequest>, timeoutMillis: number): Promise<CdsiLookup>;
|
||||
export function CdsiLookup_new(asyncRuntime: Wrapper<TokioAsyncContext>, connectionManager: Wrapper<ConnectionManager>, username: string, password: string, request: Wrapper<LookupRequest>): Promise<CdsiLookup>;
|
||||
export function CdsiLookup_token(lookup: Wrapper<CdsiLookup>): Buffer;
|
||||
export function ChatService_connect_auth(asyncRuntime: Wrapper<TokioAsyncContext>, chat: Wrapper<Chat>): Promise<DebugInfo>;
|
||||
export function ChatService_connect_unauth(asyncRuntime: Wrapper<TokioAsyncContext>, chat: Wrapper<Chat>): Promise<DebugInfo>;
|
||||
@ -461,8 +461,8 @@ export function SignedPreKeyRecord_GetSignature(obj: Wrapper<SignedPreKeyRecord>
|
||||
export function SignedPreKeyRecord_GetTimestamp(obj: Wrapper<SignedPreKeyRecord>): Timestamp;
|
||||
export function SignedPreKeyRecord_New(id: number, timestamp: Timestamp, pubKey: Wrapper<PublicKey>, privKey: Wrapper<PrivateKey>, signature: Buffer): SignedPreKeyRecord;
|
||||
export function SignedPreKeyRecord_Serialize(obj: Wrapper<SignedPreKeyRecord>): Buffer;
|
||||
export function Svr3Backup(asyncRuntime: Wrapper<TokioAsyncContext>, connectionManager: Wrapper<ConnectionManager>, secret: Buffer, password: string, maxTries: number, username: string, enclavePassword: string, opTimeoutMs: number): Promise<Buffer>;
|
||||
export function Svr3Restore(asyncRuntime: Wrapper<TokioAsyncContext>, connectionManager: Wrapper<ConnectionManager>, password: string, shareSet: Buffer, username: string, enclavePassword: string, opTimeoutMs: number): Promise<Buffer>;
|
||||
export function Svr3Backup(asyncRuntime: Wrapper<TokioAsyncContext>, connectionManager: Wrapper<ConnectionManager>, secret: Buffer, password: string, maxTries: number, username: string, enclavePassword: string): Promise<Buffer>;
|
||||
export function Svr3Restore(asyncRuntime: Wrapper<TokioAsyncContext>, connectionManager: Wrapper<ConnectionManager>, password: string, shareSet: Buffer, username: string, enclavePassword: string): Promise<Buffer>;
|
||||
export function TESTING_CdsiLookupErrorConvert(errorDescription: string): void;
|
||||
export function TESTING_CdsiLookupResponseConvert(asyncRuntime: Wrapper<TokioAsyncContext>): Promise<LookupResponse>;
|
||||
export function TESTING_ChatRequestGetBody(request: Wrapper<HttpRequest>): Buffer | null;
|
||||
|
@ -29,7 +29,6 @@ export type ServiceAuth = {
|
||||
export type CDSRequestOptionsType = {
|
||||
e164s: Array<string>;
|
||||
acisAndAccessKeys: Array<{ aci: string; accessKey: string }>;
|
||||
timeout: number;
|
||||
returnAcisWithoutUaks: boolean;
|
||||
};
|
||||
|
||||
@ -140,7 +139,6 @@ export class Net {
|
||||
{
|
||||
e164s,
|
||||
acisAndAccessKeys,
|
||||
timeout,
|
||||
returnAcisWithoutUaks,
|
||||
}: ReadonlyDeep<CDSRequestOptionsType>
|
||||
): Promise<CDSResponseType<string, string>> {
|
||||
@ -167,8 +165,7 @@ export class Net {
|
||||
this._connectionManager,
|
||||
username,
|
||||
password,
|
||||
request,
|
||||
timeout
|
||||
request
|
||||
);
|
||||
|
||||
return await Native.CdsiLookup_complete(this._asyncContext, {
|
||||
@ -193,8 +190,8 @@ export class Net {
|
||||
* // Instantiate ServiceAuth with the username and password obtained from the Chat Server.
|
||||
* const auth = { username: USERNAME, password: ENCLAVE_PASSWORD };
|
||||
* // Store a value in SVR3. Here 10 is the number of permitted restore attempts.
|
||||
* const shareSet = await SVR3.backup(SECRET_TO_BE_STORED, PASSWORD, 10, auth, TIMEOUT);
|
||||
* const restoredSecret = await SVR3.restore( PASSWORD, shareSet, auth, TIMEOUT);
|
||||
* const shareSet = await SVR3.backup(SECRET_TO_BE_STORED, PASSWORD, 10, auth);
|
||||
* const restoredSecret = await SVR3.restore( PASSWORD, shareSet, auth);
|
||||
* ```
|
||||
*/
|
||||
export interface Svr3Client {
|
||||
@ -215,8 +212,6 @@ export interface Svr3Client {
|
||||
* generally good for about 15 minutes, therefore it can be reused for the
|
||||
* subsequent calls to either backup or restore that are not too far apart in
|
||||
* time.
|
||||
* @param opTimeoutMs - The maximum wall time libsignal is allowed to spend
|
||||
* communicating with SVR3 service.
|
||||
* @returns A `Promise` which--when awaited--will return a byte array with a
|
||||
* serialized masked share set. It is supposed to be an opaque blob for the
|
||||
* clients and therefore no assumptions should be made about its contents.
|
||||
@ -224,20 +219,20 @@ export interface Svr3Client {
|
||||
* secret along with the password. Please note that masked share set does not
|
||||
* have to be treated as secret.
|
||||
*
|
||||
* The returned `Promise` can also fail due to the network issues (including the
|
||||
* timeout), problems establishing the Noise connection to the enclaves, or
|
||||
* invalid arguments' values. {@link IoError} errors can, in general, be
|
||||
* retried, although there is already a retry-with-backoff mechanism inside
|
||||
* libsignal used to connect to the SVR3 servers. Other exceptions are caused
|
||||
* by the bad input or data missing on the server. They are therefore
|
||||
* non-actionable and are guaranteed to be thrown again when retried.
|
||||
* The returned `Promise` can also fail due to the network issues (including a
|
||||
* connection timeout), problems establishing the Noise connection to the
|
||||
* enclaves, or invalid arguments' values. {@link IoError} errors can, in
|
||||
* general, be retried, although there is already a retry-with-backoff
|
||||
* mechanism inside libsignal used to connect to the SVR3 servers. Other
|
||||
* exceptions are caused by the bad input or data missing on the server. They
|
||||
* are therefore non-actionable and are guaranteed to be thrown again when
|
||||
* retried.
|
||||
*/
|
||||
backup(
|
||||
what: Buffer,
|
||||
password: string,
|
||||
maxTries: number,
|
||||
auth: Readonly<ServiceAuth>,
|
||||
opTimeoutMs: number
|
||||
auth: Readonly<ServiceAuth>
|
||||
): Promise<Buffer>;
|
||||
|
||||
/**
|
||||
@ -255,18 +250,17 @@ export interface Svr3Client {
|
||||
* generally good for about 15 minutes, therefore it can be reused for the
|
||||
* subsequent calls to either backup or restore that are not too far apart in
|
||||
* time.
|
||||
* @param opTimeoutMs - The maximum wall time libsignal is allowed to spend
|
||||
* communicating with SVR3 service.
|
||||
* @returns A `Promise` which--when awaited--will return a byte array with the
|
||||
* restored secret.
|
||||
*
|
||||
* The returned `Promise` can also fail due to the network issues (including the
|
||||
* timeout), problems establishing the Noise connection to the enclaves, or
|
||||
* invalid arguments' values. {@link IoError} errors can, in general, be
|
||||
* retried, although there is already a retry-with-backoff mechanism inside
|
||||
* libsignal used to connect to the SVR3 servers. Other exceptions are caused
|
||||
* by the bad input or data missing on the server. They are therefore
|
||||
* non-actionable and are guaranteed to be thrown again when retried.
|
||||
* The returned `Promise` can also fail due to the network issues (including
|
||||
* the connection timeout), problems establishing the Noise connection to the
|
||||
* enclaves, or invalid arguments' values. {@link IoError} errors can, in
|
||||
* general, be retried, although there is already a retry-with-backoff
|
||||
* mechanism inside libsignal used to connect to the SVR3 servers. Other
|
||||
* exceptions are caused by the bad input or data missing on the server. They
|
||||
* are therefore non-actionable and are guaranteed to be thrown again when
|
||||
* retried.
|
||||
*
|
||||
* - {@link SvrDataMissingError} is returned when the maximum restore attempts
|
||||
* number has been exceeded or if the value has never been backed up.
|
||||
@ -280,8 +274,7 @@ export interface Svr3Client {
|
||||
restore(
|
||||
password: string,
|
||||
shareSet: Buffer,
|
||||
auth: Readonly<ServiceAuth>,
|
||||
opTimeoutMs: number
|
||||
auth: Readonly<ServiceAuth>
|
||||
): Promise<Buffer>;
|
||||
}
|
||||
|
||||
@ -297,8 +290,7 @@ class Svr3ClientImpl implements Svr3Client {
|
||||
what: Buffer,
|
||||
password: string,
|
||||
maxTries: number,
|
||||
auth: Readonly<ServiceAuth>,
|
||||
opTimeoutMs: number
|
||||
auth: Readonly<ServiceAuth>
|
||||
): Promise<Buffer> {
|
||||
return Native.Svr3Backup(
|
||||
this._asyncContext,
|
||||
@ -307,16 +299,14 @@ class Svr3ClientImpl implements Svr3Client {
|
||||
password,
|
||||
maxTries,
|
||||
auth.username,
|
||||
auth.password,
|
||||
opTimeoutMs
|
||||
auth.password
|
||||
);
|
||||
}
|
||||
|
||||
async restore(
|
||||
password: string,
|
||||
shareSet: Buffer,
|
||||
auth: Readonly<ServiceAuth>,
|
||||
opTimeoutMs: number
|
||||
auth: Readonly<ServiceAuth>
|
||||
): Promise<Buffer> {
|
||||
return Native.Svr3Restore(
|
||||
this._asyncContext,
|
||||
@ -324,8 +314,7 @@ class Svr3ClientImpl implements Svr3Client {
|
||||
password,
|
||||
shareSet,
|
||||
auth.username,
|
||||
auth.password,
|
||||
opTimeoutMs
|
||||
auth.password
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -196,7 +196,7 @@ describe('cdsi lookup', () => {
|
||||
ErrorCode.IoError,
|
||||
'websocket error: channel was idle for too long',
|
||||
],
|
||||
['Timeout', ErrorCode.IoError, 'lookup timed out'],
|
||||
['ConnectionTimedOut', ErrorCode.IoError, 'connect attempt timed out'],
|
||||
['ServerCrashed', ErrorCode.IoError, 'server error: crashed'],
|
||||
];
|
||||
cases.forEach((testCase) => {
|
||||
@ -213,7 +213,6 @@ describe('cdsi lookup', () => {
|
||||
});
|
||||
|
||||
describe('SVR3', () => {
|
||||
const TIMEOUT = 5000;
|
||||
const USERNAME = randomBytes(16).toString('hex');
|
||||
const SVR3 = new Net(Environment.Staging).svr3;
|
||||
|
||||
@ -232,14 +231,14 @@ describe('SVR3', () => {
|
||||
|
||||
it('maxTries must be positive', () => {
|
||||
const secret = randomBytes(32);
|
||||
return expect(SVR3.backup(secret, 'password', 0, AUTH, TIMEOUT)).to
|
||||
.eventually.be.rejected;
|
||||
return expect(SVR3.backup(secret, 'password', 0, AUTH)).to.eventually.be
|
||||
.rejected;
|
||||
});
|
||||
|
||||
it('Secret must be 32 bytes', () => {
|
||||
const secret = randomBytes(42);
|
||||
return expect(SVR3.backup(secret, 'password', 1, AUTH, TIMEOUT)).to
|
||||
.eventually.be.rejected;
|
||||
return expect(SVR3.backup(secret, 'password', 1, AUTH)).to.eventually.be
|
||||
.rejected;
|
||||
});
|
||||
});
|
||||
|
||||
@ -248,7 +247,7 @@ describe('SVR3', () => {
|
||||
const auth = make_auth();
|
||||
const shareSet = Buffer.alloc(0);
|
||||
return expect(
|
||||
SVR3.restore('password', shareSet, auth, TIMEOUT)
|
||||
SVR3.restore('password', shareSet, auth)
|
||||
).to.eventually.be.rejectedWith(LibSignalErrorBase);
|
||||
});
|
||||
|
||||
@ -256,7 +255,7 @@ describe('SVR3', () => {
|
||||
const auth = make_auth();
|
||||
const shareSet = Buffer.from([42]);
|
||||
return expect(
|
||||
SVR3.restore('password', shareSet, auth, TIMEOUT)
|
||||
SVR3.restore('password', shareSet, auth)
|
||||
).to.eventually.be.rejectedWith(LibSignalErrorBase);
|
||||
});
|
||||
});
|
||||
@ -274,21 +273,16 @@ describe('SVR3', () => {
|
||||
it('Backup and restore work in staging', async () => {
|
||||
const auth = make_auth();
|
||||
const secret = randomBytes(32);
|
||||
const shareSet = await SVR3.backup(secret, 'password', 10, auth, TIMEOUT);
|
||||
const restoredSecret = await SVR3.restore(
|
||||
'password',
|
||||
shareSet,
|
||||
auth,
|
||||
TIMEOUT
|
||||
);
|
||||
const shareSet = await SVR3.backup(secret, 'password', 10, auth);
|
||||
const restoredSecret = await SVR3.restore('password', shareSet, auth);
|
||||
expect(restoredSecret).to.eql(secret);
|
||||
}).timeout(10000);
|
||||
|
||||
it('Restore with wrong password', async () => {
|
||||
const auth = make_auth();
|
||||
const secret = randomBytes(32);
|
||||
const shareSet = await SVR3.backup(secret, 'password', 10, auth, TIMEOUT);
|
||||
return expect(SVR3.restore('wrong password', shareSet, auth, TIMEOUT))
|
||||
const shareSet = await SVR3.backup(secret, 'password', 10, auth);
|
||||
return expect(SVR3.restore('wrong password', shareSet, auth))
|
||||
.to.eventually.be.rejectedWith(LibSignalErrorBase)
|
||||
.and.have.property('code', ErrorCode.SvrRestoreFailed);
|
||||
}).timeout(10000);
|
||||
@ -296,33 +290,24 @@ describe('SVR3', () => {
|
||||
it('Restore with corrupted share set', async () => {
|
||||
const auth = make_auth();
|
||||
const secret = randomBytes(32);
|
||||
const shareSet = await SVR3.backup(secret, 'password', 10, auth, TIMEOUT);
|
||||
const shareSet = await SVR3.backup(secret, 'password', 10, auth);
|
||||
// The first byte is the serialization format version, changing that
|
||||
// _will_ fail (checked in the other test). Changing the actual share set
|
||||
// value makes a more interesting test case.
|
||||
shareSet[1] ^= 0xff;
|
||||
return expect(
|
||||
SVR3.restore('password', shareSet, auth, TIMEOUT)
|
||||
SVR3.restore('password', shareSet, auth)
|
||||
).to.eventually.be.rejectedWith(LibSignalErrorBase);
|
||||
}).timeout(10000);
|
||||
|
||||
it('Exceed maxTries', async () => {
|
||||
const auth = make_auth();
|
||||
const secret = randomBytes(32);
|
||||
const shareSet = await SVR3.backup(secret, 'password', 1, auth, TIMEOUT);
|
||||
await SVR3.restore('password', shareSet, auth, TIMEOUT);
|
||||
return expect(SVR3.restore('password', shareSet, auth, TIMEOUT))
|
||||
const shareSet = await SVR3.backup(secret, 'password', 1, auth);
|
||||
await SVR3.restore('password', shareSet, auth);
|
||||
return expect(SVR3.restore('password', shareSet, auth))
|
||||
.to.eventually.be.rejectedWith(LibSignalErrorBase)
|
||||
.and.have.property('code', ErrorCode.SvrDataMissing);
|
||||
});
|
||||
|
||||
it('Timeout', async () => {
|
||||
const auth = make_auth();
|
||||
const secret = randomBytes(32);
|
||||
const SHORT_TIMEOUT = 100;
|
||||
return expect(SVR3.backup(secret, 'password', 10, auth, SHORT_TIMEOUT))
|
||||
.to.eventually.be.rejectedWith(LibSignalErrorBase)
|
||||
.and.have.property('code', ErrorCode.IoError);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -79,7 +79,7 @@ pub enum SignalErrorCode {
|
||||
#[allow(dead_code)]
|
||||
UnsupportedMediaInput = 132,
|
||||
|
||||
Timeout = 133,
|
||||
ConnectionTimedOut = 133,
|
||||
NetworkProtocol = 134,
|
||||
RateLimited = 135,
|
||||
WebSocket = 136,
|
||||
@ -320,7 +320,7 @@ impl From<&SignalFfiError> for SignalErrorCode {
|
||||
}
|
||||
}
|
||||
SignalFfiError::WebSocket(_) => SignalErrorCode::WebSocket,
|
||||
SignalFfiError::Timeout => SignalErrorCode::Timeout,
|
||||
SignalFfiError::ConnectionTimedOut => SignalErrorCode::ConnectionTimedOut,
|
||||
SignalFfiError::NetworkProtocol(_) => SignalErrorCode::NetworkProtocol,
|
||||
SignalFfiError::CdsiInvalidToken => SignalErrorCode::CdsiInvalidToken,
|
||||
SignalFfiError::RateLimited {
|
||||
|
@ -37,7 +37,7 @@ pub enum SignalFfiError {
|
||||
UsernameLinkError(UsernameLinkError),
|
||||
Io(IoError),
|
||||
WebSocket(#[from] WebSocketServiceError),
|
||||
Timeout,
|
||||
ConnectionTimedOut,
|
||||
NetworkProtocol(String),
|
||||
CdsiInvalidToken,
|
||||
RateLimited {
|
||||
@ -76,7 +76,7 @@ impl fmt::Display for SignalFfiError {
|
||||
SignalFfiError::UsernameProofError(e) => write!(f, "{}", e),
|
||||
SignalFfiError::UsernameLinkError(e) => write!(f, "{}", e),
|
||||
SignalFfiError::Io(e) => write!(f, "IO error: {}", e),
|
||||
SignalFfiError::Timeout => write!(f, "Operation timed out"),
|
||||
SignalFfiError::ConnectionTimedOut => write!(f, "Connect timed out"),
|
||||
SignalFfiError::WebSocket(e) => write!(f, "WebSocket error: {e}"),
|
||||
SignalFfiError::CdsiInvalidToken => write!(f, "CDSI request token was invalid"),
|
||||
SignalFfiError::NetworkProtocol(message) => write!(f, "Protocol error: {}", message),
|
||||
@ -199,7 +199,7 @@ impl From<libsignal_net::cdsi::LookupError> for SignalFfiError {
|
||||
LookupError::AttestationError(e) => SignalFfiError::Sgx(e),
|
||||
LookupError::ConnectTransport(e) => SignalFfiError::Io(e.into()),
|
||||
LookupError::WebSocket(e) => SignalFfiError::WebSocket(e),
|
||||
LookupError::Timeout => SignalFfiError::Timeout,
|
||||
LookupError::ConnectionTimedOut => SignalFfiError::ConnectionTimedOut,
|
||||
LookupError::ParseError
|
||||
| LookupError::Protocol
|
||||
| LookupError::InvalidResponse
|
||||
@ -224,11 +224,11 @@ impl From<Svr3Error> for SignalFfiError {
|
||||
match err {
|
||||
Svr3Error::Connect(e) => match e {
|
||||
WebSocketConnectError::Transport(e) => SignalFfiError::Io(e.into()),
|
||||
WebSocketConnectError::Timeout => SignalFfiError::Timeout,
|
||||
WebSocketConnectError::Timeout => SignalFfiError::ConnectionTimedOut,
|
||||
WebSocketConnectError::WebSocketError(e) => WebSocketServiceError::from(e).into(),
|
||||
},
|
||||
Svr3Error::Service(e) => SignalFfiError::WebSocket(e),
|
||||
Svr3Error::Timeout => SignalFfiError::Timeout,
|
||||
Svr3Error::ConnectionTimedOut => SignalFfiError::ConnectionTimedOut,
|
||||
Svr3Error::AttestationError(inner) => SignalFfiError::Sgx(inner),
|
||||
Svr3Error::Protocol(inner) => SignalFfiError::NetworkProtocol(inner.to_string()),
|
||||
Svr3Error::RequestFailed(_) | Svr3Error::RestoreFailed | Svr3Error::DataMissing => {
|
||||
|
@ -49,7 +49,7 @@ pub enum SignalJniError {
|
||||
WebSocket(#[from] WebSocketServiceError),
|
||||
ChatService(ChatServiceError),
|
||||
InvalidUri(InvalidUri),
|
||||
Timeout,
|
||||
ConnectTimedOut,
|
||||
Bridge(BridgeLayerError),
|
||||
#[cfg(feature = "testing-fns")]
|
||||
TestingError {
|
||||
@ -97,7 +97,7 @@ impl fmt::Display for SignalJniError {
|
||||
SignalJniError::ChatService(e) => write!(f, "{}", e),
|
||||
SignalJniError::InvalidUri(e) => write!(f, "{}", e),
|
||||
SignalJniError::WebSocket(e) => write!(f, "{e}"),
|
||||
SignalJniError::Timeout => write!(f, "timeout"),
|
||||
SignalJniError::ConnectTimedOut => write!(f, "connect timed out"),
|
||||
SignalJniError::Svr3(e) => write!(f, "{}", e),
|
||||
SignalJniError::Bridge(e) => write!(f, "{}", e),
|
||||
#[cfg(feature = "testing-fns")]
|
||||
@ -251,7 +251,7 @@ impl From<libsignal_net::cdsi::LookupError> for SignalJniError {
|
||||
fn from(e: libsignal_net::cdsi::LookupError) -> SignalJniError {
|
||||
use libsignal_net::cdsi::LookupError;
|
||||
SignalJniError::Cdsi(match e {
|
||||
LookupError::Timeout => return SignalJniError::Timeout,
|
||||
LookupError::ConnectionTimedOut => return SignalJniError::ConnectTimedOut,
|
||||
LookupError::AttestationError(e) => return e.into(),
|
||||
LookupError::ConnectTransport(e) => return IoError::from(e).into(),
|
||||
LookupError::WebSocket(e) => return e.into(),
|
||||
@ -290,11 +290,11 @@ impl From<Svr3Error> for SignalJniError {
|
||||
fn from(err: Svr3Error) -> Self {
|
||||
match err {
|
||||
Svr3Error::Connect(inner) => match inner {
|
||||
WebSocketConnectError::Timeout => SignalJniError::Timeout,
|
||||
WebSocketConnectError::Timeout => SignalJniError::ConnectTimedOut,
|
||||
WebSocketConnectError::Transport(e) => SignalJniError::Io(e.into()),
|
||||
WebSocketConnectError::WebSocketError(e) => WebSocketServiceError::from(e).into(),
|
||||
},
|
||||
Svr3Error::Timeout => SignalJniError::Timeout,
|
||||
Svr3Error::ConnectionTimedOut => SignalJniError::ConnectTimedOut,
|
||||
Svr3Error::Service(inner) => inner.into(),
|
||||
Svr3Error::AttestationError(inner) => inner.into(),
|
||||
Svr3Error::Protocol(_)
|
||||
|
@ -581,7 +581,7 @@ where
|
||||
jni_class_name!(org.signal.libsignal.net.CdsiProtocolException),
|
||||
error,
|
||||
),
|
||||
SignalJniError::WebSocket(_) | SignalJniError::Timeout => (
|
||||
SignalJniError::WebSocket(_) | SignalJniError::ConnectTimedOut => (
|
||||
jni_class_name!(org.signal.libsignal.net.NetworkException),
|
||||
error,
|
||||
),
|
||||
|
@ -30,7 +30,6 @@ use libsignal_net::infra::tcp_ssl::TcpSslTransportConnector;
|
||||
use libsignal_net::infra::{make_ws_config, EndpointConnection};
|
||||
use libsignal_net::svr::{self, SvrConnection};
|
||||
use libsignal_net::svr3::{self, OpaqueMaskedShareSet, PpssOps as _};
|
||||
use libsignal_net::utils::timeout;
|
||||
use libsignal_net::{chat, env};
|
||||
use rand::rngs::OsRng;
|
||||
use tokio::sync::mpsc;
|
||||
@ -168,29 +167,24 @@ async fn Svr3Backup(
|
||||
max_tries: AsType<NonZeroU32, u32>,
|
||||
username: String, // hex-encoded uid
|
||||
enclave_password: String, // timestamp:otp(...)
|
||||
op_timeout_ms: u32, // timeout spans both connecting and performing the operation
|
||||
) -> Result<Vec<u8>, svr3::Error> {
|
||||
let secret = secret
|
||||
.as_ref()
|
||||
.try_into()
|
||||
.expect("can only backup 32 bytes");
|
||||
let mut rng = OsRng;
|
||||
let share_set = timeout(
|
||||
Duration::from_millis(op_timeout_ms.into()),
|
||||
svr::Error::Timeout.into(),
|
||||
svr3_connect(connection_manager, username, enclave_password)
|
||||
.map_err(|err| err.into())
|
||||
.and_then(|connections| {
|
||||
Svr3Env::backup(
|
||||
connections,
|
||||
&password,
|
||||
secret,
|
||||
max_tries.into_inner(),
|
||||
&mut rng,
|
||||
)
|
||||
}),
|
||||
)
|
||||
.await?;
|
||||
let share_set = svr3_connect(connection_manager, username, enclave_password)
|
||||
.map_err(|err| err.into())
|
||||
.and_then(|connections| {
|
||||
Svr3Env::backup(
|
||||
connections,
|
||||
&password,
|
||||
secret,
|
||||
max_tries.into_inner(),
|
||||
&mut rng,
|
||||
)
|
||||
})
|
||||
.await?;
|
||||
Ok(share_set.serialize().expect("can serialize the share set"))
|
||||
}
|
||||
|
||||
@ -201,18 +195,13 @@ async fn Svr3Restore(
|
||||
share_set: Box<[u8]>,
|
||||
username: String, // hex-encoded uid
|
||||
enclave_password: String, // timestamp:otp(...)
|
||||
op_timeout_ms: u32, // timeout spans both connecting and performing the operation
|
||||
) -> Result<Vec<u8>, svr3::Error> {
|
||||
let mut rng = OsRng;
|
||||
let share_set = OpaqueMaskedShareSet::deserialize(&share_set)?;
|
||||
let restored_secret = timeout(
|
||||
Duration::from_millis(op_timeout_ms.into()),
|
||||
svr::Error::Timeout.into(),
|
||||
svr3_connect(connection_manager, username, enclave_password)
|
||||
.map_err(|err| err.into())
|
||||
.and_then(|connections| Svr3Env::restore(connections, &password, share_set, &mut rng)),
|
||||
)
|
||||
.await?;
|
||||
let restored_secret = svr3_connect(connection_manager, username, enclave_password)
|
||||
.map_err(|err| err.into())
|
||||
.and_then(|connections| Svr3Env::restore(connections, &password, share_set, &mut rng))
|
||||
.await?;
|
||||
Ok(restored_secret.to_vec())
|
||||
}
|
||||
|
||||
|
@ -4,14 +4,12 @@
|
||||
//
|
||||
|
||||
use std::convert::TryInto as _;
|
||||
use std::time::Duration;
|
||||
|
||||
use libsignal_bridge_macros::{bridge_fn, bridge_io};
|
||||
use libsignal_net::auth::Auth;
|
||||
use libsignal_net::cdsi::{
|
||||
self, AciAndAccessKey, CdsiConnection, ClientResponseCollector, LookupResponse, Token, E164,
|
||||
};
|
||||
use libsignal_net::utils::timeout;
|
||||
use libsignal_protocol::{Aci, SignalProtocolError};
|
||||
|
||||
use crate::net::{ConnectionManager, TokioAsyncContext};
|
||||
@ -29,7 +27,7 @@ pub enum CdsiError {
|
||||
/// Invalid response received from the server
|
||||
InvalidResponse,
|
||||
/// Retry later
|
||||
RateLimited { retry_after: Duration },
|
||||
RateLimited { retry_after: std::time::Duration },
|
||||
/// Failed to parse the response from the server
|
||||
ParseError,
|
||||
/// Request token was invalid
|
||||
@ -109,7 +107,6 @@ async fn CdsiLookup_new(
|
||||
username: String,
|
||||
password: String,
|
||||
request: &LookupRequest,
|
||||
timeout_millis: u32,
|
||||
) -> Result<CdsiLookup, cdsi::LookupError> {
|
||||
let request = std::mem::take(&mut *request.0.lock().expect("not poisoned"));
|
||||
let auth = Auth { username, password };
|
||||
@ -120,12 +117,7 @@ async fn CdsiLookup_new(
|
||||
auth,
|
||||
)
|
||||
.await?;
|
||||
let (token, remaining_response) = timeout(
|
||||
Duration::from_millis(timeout_millis.into()),
|
||||
cdsi::LookupError::Timeout,
|
||||
connected.send_request(request),
|
||||
)
|
||||
.await?;
|
||||
let (token, remaining_response) = connected.send_request(request).await?;
|
||||
|
||||
Ok(CdsiLookup {
|
||||
token,
|
||||
|
@ -461,7 +461,7 @@ impl SignalNodeError for libsignal_net::cdsi::LookupError {
|
||||
Self::AttestationError(e) => return e.throw(cx, module, operation_name),
|
||||
Self::InvalidArgument { server_reason: _ } => (None, None),
|
||||
Self::InvalidToken => (Some("CdsiInvalidToken"), None),
|
||||
Self::Timeout
|
||||
Self::ConnectionTimedOut
|
||||
| Self::ConnectTransport(_)
|
||||
| Self::WebSocket(_)
|
||||
| Self::Protocol
|
||||
@ -485,7 +485,9 @@ impl SignalNodeError for libsignal_net::svr3::Error {
|
||||
operation_name: &str,
|
||||
) -> JsResult<'a, JsValue> {
|
||||
let name = match self {
|
||||
Svr3Error::Service(_) | Svr3Error::Timeout | Svr3Error::Connect(_) => Some(IO_ERROR),
|
||||
Svr3Error::Service(_) | Svr3Error::ConnectionTimedOut | Svr3Error::Connect(_) => {
|
||||
Some(IO_ERROR)
|
||||
}
|
||||
Svr3Error::AttestationError(inner) => {
|
||||
return inner.throw(cx, module, operation_name);
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ enum TestingCdsiLookupError {
|
||||
Parse,
|
||||
ConnectDnsFailed,
|
||||
WebSocketIdleTooLong,
|
||||
Timeout,
|
||||
ConnectionTimedOut,
|
||||
ServerCrashed,
|
||||
}
|
||||
|
||||
@ -83,7 +83,7 @@ const _: () = {
|
||||
LookupError::ParseError => TestingCdsiLookupError::Parse,
|
||||
LookupError::ConnectTransport(_) => TestingCdsiLookupError::ConnectDnsFailed,
|
||||
LookupError::WebSocket(_) => TestingCdsiLookupError::WebSocketIdleTooLong,
|
||||
LookupError::Timeout => TestingCdsiLookupError::Timeout,
|
||||
LookupError::ConnectionTimedOut => TestingCdsiLookupError::ConnectionTimedOut,
|
||||
LookupError::Server { reason } => TestingCdsiLookupError::ServerCrashed,
|
||||
}
|
||||
}
|
||||
@ -124,7 +124,7 @@ fn TESTING_CdsiLookupErrorConvert(
|
||||
TestingCdsiLookupError::WebSocketIdleTooLong => LookupError::WebSocket(
|
||||
libsignal_net::infra::ws::WebSocketServiceError::ChannelIdleTooLong,
|
||||
),
|
||||
TestingCdsiLookupError::Timeout => LookupError::Timeout,
|
||||
TestingCdsiLookupError::ConnectionTimedOut => LookupError::ConnectionTimedOut,
|
||||
TestingCdsiLookupError::ServerCrashed => LookupError::Server { reason: "crashed" },
|
||||
})
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ async fn cdsi_lookup(
|
||||
let connected = CdsiConnection::connect(endpoint, transport_connector, auth).await?;
|
||||
let (_token, remaining_response) = libsignal_net::utils::timeout(
|
||||
timeout,
|
||||
LookupError::Timeout,
|
||||
LookupError::ConnectionTimedOut,
|
||||
connected.send_request(request),
|
||||
)
|
||||
.await?;
|
||||
|
@ -285,8 +285,8 @@ pub enum LookupError {
|
||||
ConnectTransport(TransportConnectError),
|
||||
/// websocket error: {0}
|
||||
WebSocket(WebSocketServiceError),
|
||||
/// lookup timed out
|
||||
Timeout,
|
||||
/// connect attempt timed out
|
||||
ConnectionTimedOut,
|
||||
/// request was invalid: {server_reason}
|
||||
InvalidArgument { server_reason: String },
|
||||
/// server error: {reason}
|
||||
@ -306,9 +306,10 @@ impl From<AttestedConnectionError> for LookupError {
|
||||
|
||||
impl From<crate::enclave::Error> for LookupError {
|
||||
fn from(value: crate::enclave::Error) -> Self {
|
||||
use crate::enclave::Error;
|
||||
match value {
|
||||
crate::svr::Error::WebSocketConnect(err) => match err {
|
||||
WebSocketConnectError::Timeout => Self::Timeout,
|
||||
Error::WebSocketConnect(err) => match err {
|
||||
WebSocketConnectError::Timeout => Self::ConnectionTimedOut,
|
||||
WebSocketConnectError::Transport(e) => Self::ConnectTransport(e),
|
||||
WebSocketConnectError::WebSocketError(e) => {
|
||||
if let tungstenite::Error::Http(response) = &e {
|
||||
@ -328,10 +329,10 @@ impl From<crate::enclave::Error> for LookupError {
|
||||
Self::WebSocket(e.into())
|
||||
}
|
||||
},
|
||||
crate::svr::Error::AttestationError(err) => Self::AttestationError(err),
|
||||
crate::svr::Error::WebSocket(err) => Self::WebSocket(err),
|
||||
crate::svr::Error::Protocol => Self::Protocol,
|
||||
crate::svr::Error::Timeout => Self::Timeout,
|
||||
Error::AttestationError(err) => Self::AttestationError(err),
|
||||
Error::WebSocket(err) => Self::WebSocket(err),
|
||||
Error::Protocol => Self::Protocol,
|
||||
Error::ConnectionTimedOut => Self::ConnectionTimedOut,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -214,8 +214,8 @@ pub enum Error {
|
||||
Protocol,
|
||||
/// Enclave attestation failed: {0}
|
||||
AttestationError(attest::enclave::Error),
|
||||
/// Timeout
|
||||
Timeout,
|
||||
/// Connection timeout
|
||||
ConnectionTimedOut,
|
||||
}
|
||||
|
||||
impl LogSafeDisplay for Error {}
|
||||
@ -257,7 +257,7 @@ impl<E: EnclaveKind + NewHandshake, C: ConnectionManager> EnclaveEndpointConnect
|
||||
unreachable!("new service connector should not be in cooldown")
|
||||
}
|
||||
ServiceState::Error(e) => Err(Error::WebSocketConnect(e)),
|
||||
ServiceState::TimedOut => Err(Error::Timeout),
|
||||
ServiceState::ConnectionTimedOut => Err(Error::ConnectionTimedOut),
|
||||
ServiceState::Inactive => {
|
||||
unreachable!("can't be returned by the initializer")
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ pub(crate) enum ServiceState<T, CE, SE> {
|
||||
/// Last connection attempt resulted in an error.
|
||||
Error(CE),
|
||||
/// Last connection attempt timed out.
|
||||
TimedOut,
|
||||
ConnectionTimedOut,
|
||||
}
|
||||
|
||||
/// Represents the logic needed to establish a connection over some transport.
|
||||
@ -217,7 +217,7 @@ where
|
||||
}
|
||||
ConnectionAttemptOutcome::TimedOut => {
|
||||
log::debug!("connection attempt timed out");
|
||||
ServiceState::TimedOut
|
||||
ServiceState::ConnectionTimedOut
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -263,7 +263,7 @@ where
|
||||
ServiceState::Active(service, status) if !status.is_stopped() => Ok(mapper(service)),
|
||||
ServiceState::Inactive => Err(StateError::Inactive),
|
||||
ServiceState::Cooldown(_)
|
||||
| ServiceState::TimedOut
|
||||
| ServiceState::ConnectionTimedOut
|
||||
| ServiceState::Error(_)
|
||||
| ServiceState::Active(_, _) => Err(StateError::ServiceUnavailable),
|
||||
}
|
||||
@ -352,7 +352,7 @@ where
|
||||
// because we just checked that we'll wake before the deadline
|
||||
tokio::time::sleep_until(*next_attempt_time).await;
|
||||
}
|
||||
ServiceState::TimedOut => {
|
||||
ServiceState::ConnectionTimedOut => {
|
||||
// keep trying until we hit our own timeout deadline
|
||||
log::info!("Connection attempt timed out");
|
||||
if Instant::now() >= deadline {
|
||||
@ -374,7 +374,7 @@ where
|
||||
ServiceState::Active(service, service_state)
|
||||
}
|
||||
Ok(result) => result,
|
||||
Err(_) => ServiceState::TimedOut,
|
||||
Err(_) => ServiceState::ConnectionTimedOut,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -143,8 +143,8 @@ pub enum Error {
|
||||
/// This could mean either the data was never backed-up or we ran out of attempts to restore
|
||||
/// it.
|
||||
DataMissing,
|
||||
/// Timeout
|
||||
Timeout,
|
||||
/// Connect timed out
|
||||
ConnectionTimedOut,
|
||||
}
|
||||
|
||||
impl From<DeserializeError> for Error {
|
||||
@ -184,7 +184,7 @@ impl From<super::svr::Error> for Error {
|
||||
SvrError::WebSocket(inner) => Self::Service(inner),
|
||||
SvrError::Protocol => Self::Protocol("General SVR protocol error".to_string()),
|
||||
SvrError::AttestationError(inner) => Self::AttestationError(inner),
|
||||
SvrError::Timeout => Self::Timeout,
|
||||
SvrError::ConnectionTimedOut => Self::ConnectionTimedOut,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ public enum SignalError: Error {
|
||||
case unsupportedMediaInput(String)
|
||||
case callbackError(String)
|
||||
case webSocketError(String)
|
||||
case timeoutError(String)
|
||||
case connectionTimeoutError(String)
|
||||
case networkProtocolError(String)
|
||||
case cdsiInvalidToken(String)
|
||||
case rateLimitedError(retryAfter: TimeInterval, message: String)
|
||||
@ -168,8 +168,8 @@ internal func checkError(_ error: SignalFfiErrorRef?) throws {
|
||||
throw SignalError.callbackError(errStr)
|
||||
case SignalErrorCodeWebSocket:
|
||||
throw SignalError.webSocketError(errStr)
|
||||
case SignalErrorCodeTimeout:
|
||||
throw SignalError.timeoutError(errStr)
|
||||
case SignalErrorCodeConnectionTimedOut:
|
||||
throw SignalError.connectionTimeoutError(errStr)
|
||||
case SignalErrorCodeNetworkProtocol:
|
||||
throw SignalError.networkProtocolError(errStr)
|
||||
case SignalErrorCodeCdsiInvalidToken:
|
||||
|
@ -32,18 +32,17 @@ public class Net {
|
||||
self.svr3 = Svr3Client(self.asyncContext, self.connectionManager)
|
||||
}
|
||||
|
||||
/// Like ``cdsiLookup(auth:request:timeout:)`` but with the parameters to ``CdsiLookupRequest`` broken out.
|
||||
/// Like ``cdsiLookup(auth:request:)`` but with the parameters to ``CdsiLookupRequest`` broken out.
|
||||
public func cdsiLookup(
|
||||
auth: Auth,
|
||||
prevE164s: [String],
|
||||
e164s: [String],
|
||||
acisAndAccessKeys: [AciAndAccessKey],
|
||||
returnAcisWithoutUaks: Bool,
|
||||
token: Data?,
|
||||
timeout: TimeInterval
|
||||
token: Data?
|
||||
) async throws -> CdsiLookup {
|
||||
let request = try CdsiLookupRequest(e164s: e164s, prevE164s: prevE164s, acisAndAccessKeys: acisAndAccessKeys, token: token, returnAcisWithoutUaks: returnAcisWithoutUaks)
|
||||
return try await self.cdsiLookup(auth: auth, request: request, timeout: timeout)
|
||||
return try await self.cdsiLookup(auth: auth, request: request)
|
||||
}
|
||||
|
||||
/// Starts a new CDSI lookup request.
|
||||
@ -56,7 +55,6 @@ public class Net {
|
||||
/// - Parameters:
|
||||
/// - auth: The information to use when authenticating with the CDSI server.
|
||||
/// - request: The CDSI request to be sent to the server.
|
||||
/// - timeout: The amount of time to wait for the initial connection before giving up.
|
||||
///
|
||||
/// - Returns:
|
||||
/// An object representing the in-progress request. If this method
|
||||
@ -77,7 +75,7 @@ public class Net {
|
||||
///
|
||||
/// // Start the request.
|
||||
/// let net = Net(env: Net.Environment.production)
|
||||
/// let lookup = try await net.cdsiLookup(auth: auth, request: request, timeout: TimeInterval(10))
|
||||
/// let lookup = try await net.cdsiLookup(auth: auth, request: request)
|
||||
///
|
||||
/// // Save the token for future lookups.
|
||||
/// let savedToken = lookup.token
|
||||
@ -90,15 +88,13 @@ public class Net {
|
||||
/// ```
|
||||
public func cdsiLookup(
|
||||
auth: Auth,
|
||||
request: CdsiLookupRequest,
|
||||
timeout: TimeInterval
|
||||
request: CdsiLookupRequest
|
||||
) async throws -> CdsiLookup {
|
||||
let timeoutMs = durationToMillis(timeout)
|
||||
let handle: OpaquePointer = try await invokeAsyncFunction { promise, context in
|
||||
self.asyncContext.withNativeHandle { asyncContext in
|
||||
self.connectionManager.withNativeHandle { connectionManager in
|
||||
request.withNativeHandle { request in
|
||||
signal_cdsi_lookup_new(promise, context, asyncContext, connectionManager, auth.username, auth.password, request, timeoutMs)
|
||||
signal_cdsi_lookup_new(promise, context, asyncContext, connectionManager, auth.username, auth.password, request)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -204,7 +200,7 @@ public class CdsiLookupRequest: NativeHandleOwner {
|
||||
|
||||
/// CDSI lookup in progress.
|
||||
///
|
||||
/// Returned by ``Net/cdsiLookup(auth:request:timeout:)`` when a request is successfully initiated.
|
||||
/// Returned by ``Net/cdsiLookup(auth:request:)`` when a request is successfully initiated.
|
||||
public class CdsiLookup {
|
||||
class NativeCdsiLookup: NativeHandleOwner {
|
||||
override internal class func destroyNativeHandle(_ handle: OpaquePointer) -> SignalFfiErrorRef? {
|
||||
|
@ -25,16 +25,14 @@ import SignalFfi
|
||||
/// SECRET_TO_BE_STORED,
|
||||
/// password: PASSWORD,
|
||||
/// maxTries: 10,
|
||||
/// auth: auth,
|
||||
/// timeout: TIMEOUT
|
||||
/// auth: auth
|
||||
/// )
|
||||
/// // Attempt to retrieve the secret from SVR3 provided the masked share set
|
||||
/// // and a password.
|
||||
/// let restoredSecret = try await net.svr3.restore(
|
||||
/// password: PASSWORD,
|
||||
/// shareSet: shareSet,
|
||||
/// auth: auth,
|
||||
/// timeout: TIMEOUT
|
||||
/// auth: auth
|
||||
/// )
|
||||
/// ~~~
|
||||
public class Svr3Client {
|
||||
@ -56,7 +54,7 @@ public class Svr3Client {
|
||||
/// - password: User-provided password that will be used to derive the
|
||||
/// encryption key for the secret.
|
||||
/// - maxTries: Maximum allowed number of restore attempts (successful
|
||||
/// or not). Each call to ``restore(password:shareSet:auth:timeout:)``
|
||||
/// or not). Each call to ``restore(password:shareSet:auth:)``
|
||||
/// that reaches the server will decrement the counter. Must be
|
||||
/// positive.
|
||||
/// - auth: An instance of ``Auth`` containing the username and password
|
||||
@ -64,8 +62,6 @@ public class Svr3Client {
|
||||
/// generally good for about 15 minutes, therefore it can be reused for
|
||||
/// the subsequent calls to either `backup` or `restore` that are not
|
||||
/// too far apart in time.
|
||||
/// - timeout: The maximum wall time libsignal is allowed to spend
|
||||
/// communicating with SVR3 service.
|
||||
///
|
||||
/// - Returns:
|
||||
/// A byte array containing a serialized masked share set. It is supposed
|
||||
@ -78,15 +74,15 @@ public class Svr3Client {
|
||||
/// - Throws:
|
||||
/// On error, throws a ``SignalError``. Expected error cases are
|
||||
/// - `SignalError.networkError` for a network-level connectivity issue,
|
||||
/// including timeouts.
|
||||
/// including connection timeout.
|
||||
/// - `SignalError.networkProtocolError` for an SVR3 or attested
|
||||
/// connection protocol issue.
|
||||
///
|
||||
/// ## Notes:
|
||||
/// - Error messages are expected to be log-safe and not contain any
|
||||
/// sensitive data.
|
||||
/// - Failures caused by the network issues (including the timeouts) can,
|
||||
/// in general, be retried, although there is already a
|
||||
/// - Failures caused by the network issues (including a connection
|
||||
/// timeout) can, in general, be retried, although there is already a
|
||||
/// retry-with-backoff mechanism inside libsignal used to connect to the
|
||||
/// SVR3 servers. Other exceptions are caused by the bad input or data
|
||||
/// missing on the server. They are therefore non-actionable and are
|
||||
@ -95,10 +91,8 @@ public class Svr3Client {
|
||||
_ secret: some ContiguousBytes,
|
||||
password: String,
|
||||
maxTries: UInt32,
|
||||
auth: Auth,
|
||||
timeout: TimeInterval
|
||||
auth: Auth
|
||||
) async throws -> [UInt8] {
|
||||
let timeoutMs = durationToMillis(timeout)
|
||||
let output = try await invokeAsyncFunction(returning: SignalOwnedBuffer.self) { promise, context in
|
||||
self.asyncContext.withNativeHandle { asyncContext in
|
||||
self.connectionManager.withNativeHandle { connectionManager in
|
||||
@ -112,8 +106,7 @@ public class Svr3Client {
|
||||
password,
|
||||
maxTries,
|
||||
auth.username,
|
||||
auth.password,
|
||||
timeoutMs
|
||||
auth.password
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -131,14 +124,12 @@ public class Svr3Client {
|
||||
/// - password: User-provided password that will be used to derive the
|
||||
/// encryption key for the secret.
|
||||
/// - shareSet: A serialized masked share set returned by
|
||||
/// ``backup(_:password:maxTries:auth:timeout:)``.
|
||||
/// ``backup(_:password:maxTries:auth:)``.
|
||||
/// - auth: An instance of ``Auth`` containing the username and password
|
||||
/// obtained from the Chat Server. The password is an OTP which is
|
||||
/// generally good for about 15 minutes, therefore it can be reused for
|
||||
/// the subsequent calls to either backup or restore that are not too
|
||||
/// far apart in time.
|
||||
/// - timeout: The maximum wall time libsignal is allowed to spend
|
||||
/// communicating with SVR3 service.
|
||||
///
|
||||
/// - Returns:
|
||||
/// A byte array containing the restored secret.
|
||||
@ -146,7 +137,7 @@ public class Svr3Client {
|
||||
/// - Throws:
|
||||
/// On error, throws a ``SignalError``. Expected error cases are
|
||||
/// - `SignalError.networkError` for a network-level connectivity issue,
|
||||
/// including timeouts.
|
||||
/// including connection timeouts.
|
||||
/// - `SignalError.networkProtocolError` for an SVR3 or attested
|
||||
/// connection protocol issue.
|
||||
/// - `SignalError.svrDataMissing` when either the maximum number of
|
||||
@ -158,8 +149,8 @@ public class Svr3Client {
|
||||
/// ## Notes:
|
||||
/// - Error messages are expected to be log-safe and not contain any
|
||||
/// sensitive data.
|
||||
/// - Failures caused by the network issues (including the timeouts) can,
|
||||
/// in general, be retried, although there is already a
|
||||
/// - Failures caused by the network issues (including a connection
|
||||
/// timeout) can, in general, be retried, although there is already a
|
||||
/// retry-with-backoff mechanism inside libsignal used to connect to the
|
||||
/// SVR3 servers. Other exceptions are caused by the bad input or data
|
||||
/// missing on the server. They are therefore non-actionable and are
|
||||
@ -167,10 +158,8 @@ public class Svr3Client {
|
||||
public func restore(
|
||||
password: String,
|
||||
shareSet: some ContiguousBytes,
|
||||
auth: Auth,
|
||||
timeout: TimeInterval
|
||||
auth: Auth
|
||||
) async throws -> [UInt8] {
|
||||
let timeoutMs = durationToMillis(timeout)
|
||||
let output = try await invokeAsyncFunction(returning: SignalOwnedBuffer.self) { promise, context in
|
||||
self.asyncContext.withNativeHandle { asyncContext in
|
||||
self.connectionManager.withNativeHandle { connectionManager in
|
||||
@ -183,8 +172,7 @@ public class Svr3Client {
|
||||
password,
|
||||
shareSetBuffer,
|
||||
auth.username,
|
||||
auth.password,
|
||||
timeoutMs
|
||||
auth.password
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -184,7 +184,7 @@ typedef enum {
|
||||
SignalErrorCodeIoError = 130,
|
||||
SignalErrorCodeInvalidMediaInput = 131,
|
||||
SignalErrorCodeUnsupportedMediaInput = 132,
|
||||
SignalErrorCodeTimeout = 133,
|
||||
SignalErrorCodeConnectionTimedOut = 133,
|
||||
SignalErrorCodeNetworkProtocol = 134,
|
||||
SignalErrorCodeRateLimited = 135,
|
||||
SignalErrorCodeWebSocket = 136,
|
||||
@ -1329,9 +1329,9 @@ SignalFfiError *signal_create_otp(const char **out, const char *username, Signal
|
||||
|
||||
SignalFfiError *signal_create_otp_from_base64(const char **out, const char *username, const char *secret);
|
||||
|
||||
SignalFfiError *signal_svr3_backup(SignalCPromiseOwnedBufferOfc_uchar promise, const void *promise_context, const SignalTokioAsyncContext *async_runtime, const SignalConnectionManager *connection_manager, SignalBorrowedBuffer secret, const char *password, uint32_t max_tries, const char *username, const char *enclave_password, uint32_t op_timeout_ms);
|
||||
SignalFfiError *signal_svr3_backup(SignalCPromiseOwnedBufferOfc_uchar promise, const void *promise_context, const SignalTokioAsyncContext *async_runtime, const SignalConnectionManager *connection_manager, SignalBorrowedBuffer secret, const char *password, uint32_t max_tries, const char *username, const char *enclave_password);
|
||||
|
||||
SignalFfiError *signal_svr3_restore(SignalCPromiseOwnedBufferOfc_uchar promise, const void *promise_context, const SignalTokioAsyncContext *async_runtime, const SignalConnectionManager *connection_manager, const char *password, SignalBorrowedBuffer share_set, const char *username, const char *enclave_password, uint32_t op_timeout_ms);
|
||||
SignalFfiError *signal_svr3_restore(SignalCPromiseOwnedBufferOfc_uchar promise, const void *promise_context, const SignalTokioAsyncContext *async_runtime, const SignalConnectionManager *connection_manager, const char *password, SignalBorrowedBuffer share_set, const char *username, const char *enclave_password);
|
||||
|
||||
SignalFfiError *signal_chat_destroy(SignalChat *p);
|
||||
|
||||
@ -1353,7 +1353,7 @@ SignalFfiError *signal_lookup_request_destroy(SignalLookupRequest *p);
|
||||
|
||||
SignalFfiError *signal_cdsi_lookup_destroy(SignalCdsiLookup *p);
|
||||
|
||||
SignalFfiError *signal_cdsi_lookup_new(SignalCPromiseCdsiLookup promise, const void *promise_context, const SignalTokioAsyncContext *async_runtime, const SignalConnectionManager *connection_manager, const char *username, const char *password, const SignalLookupRequest *request, uint32_t timeout_millis);
|
||||
SignalFfiError *signal_cdsi_lookup_new(SignalCPromiseCdsiLookup promise, const void *promise_context, const SignalTokioAsyncContext *async_runtime, const SignalConnectionManager *connection_manager, const char *username, const char *password, const SignalLookupRequest *request);
|
||||
|
||||
SignalFfiError *signal_cdsi_lookup_token(SignalOwnedBuffer *out, const SignalCdsiLookup *lookup);
|
||||
|
||||
|
@ -92,9 +92,9 @@ final class NetTests: XCTestCase {
|
||||
XCTAssertEqual(message, "WebSocket error: channel was idle for too long")
|
||||
}
|
||||
do {
|
||||
try failWithError("Timeout")
|
||||
} catch SignalError.timeoutError(let message) {
|
||||
XCTAssertEqual(message, "Operation timed out")
|
||||
try failWithError("ConnectionTimedOut")
|
||||
} catch SignalError.connectionTimeoutError(let message) {
|
||||
XCTAssertEqual(message, "Connect timed out")
|
||||
}
|
||||
do {
|
||||
try failWithError("ServerCrashed")
|
||||
@ -116,7 +116,7 @@ final class NetTests: XCTestCase {
|
||||
)
|
||||
let net = Net(env: .staging)
|
||||
|
||||
let lookup = try await net.cdsiLookup(auth: auth, request: request, timeout: TimeInterval(0))
|
||||
let lookup = try await net.cdsiLookup(auth: auth, request: request)
|
||||
let response = try await lookup.complete()
|
||||
for entry in response.entries {
|
||||
_ = entry.aci
|
||||
@ -130,8 +130,6 @@ final class Svr3Tests: TestCaseBase {
|
||||
private let username = randomBytes(16).hexString
|
||||
private let storedSecret = randomBytes(32)
|
||||
|
||||
private let defaultTimeout = TimeInterval(10)
|
||||
|
||||
func getEnclaveSecret() throws -> String {
|
||||
guard let enclaveSecret = ProcessInfo.processInfo.environment["ENCLAVE_SECRET"] else {
|
||||
throw XCTSkip("requires ENCLAVE_SECRET")
|
||||
@ -147,15 +145,13 @@ final class Svr3Tests: TestCaseBase {
|
||||
self.storedSecret,
|
||||
password: "password",
|
||||
maxTries: 10,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
|
||||
let restoredSecret = try await net.svr3.restore(
|
||||
password: "password",
|
||||
shareSet: shareSet,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
XCTAssertEqual(restoredSecret, self.storedSecret)
|
||||
}
|
||||
@ -168,16 +164,14 @@ final class Svr3Tests: TestCaseBase {
|
||||
self.storedSecret,
|
||||
password: "password",
|
||||
maxTries: 10,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
|
||||
do {
|
||||
_ = try await net.svr3.restore(
|
||||
password: "invalid password",
|
||||
shareSet: shareSet,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
XCTFail("Should have thrown")
|
||||
} catch SignalError.svrRestoreFailed(_) {
|
||||
@ -195,8 +189,7 @@ final class Svr3Tests: TestCaseBase {
|
||||
self.storedSecret,
|
||||
password: "password",
|
||||
maxTries: 10,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
// Invert a byte somewhere inside the share set
|
||||
shareSet[42] ^= 0xFF
|
||||
@ -205,8 +198,7 @@ final class Svr3Tests: TestCaseBase {
|
||||
_ = try await net.svr3.restore(
|
||||
password: "password",
|
||||
shareSet: shareSet,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
XCTFail("Should have thrown")
|
||||
} catch SignalError.svrRestoreFailed(_) {
|
||||
@ -224,23 +216,20 @@ final class Svr3Tests: TestCaseBase {
|
||||
self.storedSecret,
|
||||
password: "password",
|
||||
maxTries: 1,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
// First restore should succeed, but use up all the available tries
|
||||
_ = try await net.svr3.restore(
|
||||
password: "password",
|
||||
shareSet: shareSet,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
|
||||
do {
|
||||
_ = try await net.svr3.restore(
|
||||
password: "password",
|
||||
shareSet: shareSet,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
XCTFail("Should have thrown")
|
||||
} catch SignalError.svrDataMissing(_) {
|
||||
@ -258,16 +247,14 @@ final class Svr3Tests: TestCaseBase {
|
||||
self.storedSecret,
|
||||
password: "password",
|
||||
maxTries: 1,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
// First restore fails **and** decrements the tries left counter
|
||||
do {
|
||||
_ = try await net.svr3.restore(
|
||||
password: "invalid password",
|
||||
shareSet: shareSet,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
XCTFail("Should have thrown")
|
||||
} catch SignalError.svrRestoreFailed(_) {
|
||||
@ -280,8 +267,7 @@ final class Svr3Tests: TestCaseBase {
|
||||
_ = try await net.svr3.restore(
|
||||
password: "password",
|
||||
shareSet: shareSet,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
XCTFail("Should have thrown")
|
||||
} catch SignalError.svrDataMissing(_) {
|
||||
@ -300,8 +286,7 @@ final class Svr3Tests: TestCaseBase {
|
||||
self.storedSecret,
|
||||
password: "password",
|
||||
maxTries: 0,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
XCTFail("Should have thrown")
|
||||
} catch SignalError.invalidArgument(_) {
|
||||
@ -320,8 +305,7 @@ final class Svr3Tests: TestCaseBase {
|
||||
randomBytes(42),
|
||||
password: "password",
|
||||
maxTries: 0,
|
||||
auth: auth,
|
||||
timeout: self.defaultTimeout
|
||||
auth: auth
|
||||
)
|
||||
XCTFail("Should have thrown")
|
||||
} catch SignalError.invalidArgument(_) {
|
||||
@ -330,27 +314,6 @@ final class Svr3Tests: TestCaseBase {
|
||||
XCTFail("Unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
func testBackupTimeout() async throws {
|
||||
let auth = try Auth(username: self.username, enclaveSecret: self.getEnclaveSecret())
|
||||
let net = Net(env: .staging)
|
||||
|
||||
do {
|
||||
_ = try await net.svr3.backup(
|
||||
self.storedSecret,
|
||||
password: "password",
|
||||
maxTries: 1,
|
||||
auth: auth,
|
||||
timeout: TimeInterval(0.01)
|
||||
)
|
||||
XCTFail("Should have thrown")
|
||||
} catch SignalError.timeoutError(let message) {
|
||||
// Make sure the logged message will provide enough details
|
||||
XCTAssertTrue(message.contains("Operation timed out"), "Unexpected message: '\(message)'")
|
||||
} catch {
|
||||
XCTFail("Unexpected error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user