diff --git a/java/client/src/main/java/org/signal/libsignal/net/CdsiLookup.java b/java/client/src/main/java/org/signal/libsignal/net/CdsiLookup.java index 5b0613be..69dcf650 100644 --- a/java/client/src/main/java/org/signal/libsignal/net/CdsiLookup.java +++ b/java/client/src/main/java/org/signal/libsignal/net/CdsiLookup.java @@ -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 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)); } } diff --git a/java/client/src/main/java/org/signal/libsignal/net/Network.java b/java/client/src/main/java/org/signal/libsignal/net/Network.java index c8a1f436..a0acc714 100644 --- a/java/client/src/main/java/org/signal/libsignal/net/Network.java +++ b/java/client/src/main/java/org/signal/libsignal/net/Network.java @@ -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 cdsiLookup( - String username, - String password, - CdsiLookupRequest request, - Duration timeout, - Consumer tokenConsumer) + String username, String password, CdsiLookupRequest request, Consumer 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()); diff --git a/java/client/src/main/java/org/signal/libsignal/net/Svr3.java b/java/client/src/main/java/org/signal/libsignal/net/Svr3.java index b8d949f0..74cf9f35 100644 --- a/java/client/src/main/java/org/signal/libsignal/net/Svr3.java +++ b/java/client/src/main/java/org/signal/libsignal/net/Svr3.java @@ -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(); * } * *

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 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 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); } } } diff --git a/java/client/src/test/java/org/signal/libsignal/net/CdsiLookupResponseTest.java b/java/client/src/test/java/org/signal/libsignal/net/CdsiLookupResponseTest.java index 443b4479..d969c760 100644 --- a/java/client/src/test/java/org/signal/libsignal/net/CdsiLookupResponseTest.java +++ b/java/client/src/test/java/org/signal/libsignal/net/CdsiLookupResponseTest.java @@ -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 expectedErrorType, String expectedMessage) { E e = assertThrows( - expectedErrorType, () -> Native.TESTING_CdsiLookupErrorConvert(errorDescription)); + "for " + errorDescription, + expectedErrorType, + () -> Native.TESTING_CdsiLookupErrorConvert(errorDescription)); assertEquals(e.getMessage(), expectedMessage); return e; } diff --git a/java/client/src/test/java/org/signal/libsignal/net/Svr3Test.java b/java/client/src/test/java/org/signal/libsignal/net/Svr3Test.java index fcda4c16..506c853e 100644 --- a/java/client/src/test/java/org/signal/libsignal/net/Svr3Test.java +++ b/java/client/src/test/java/org/signal/libsignal/net/Svr3Test.java @@ -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); - } - } } diff --git a/java/shared/java/org/signal/libsignal/internal/Native.java b/java/shared/java/org/signal/libsignal/internal/Native.java index 4940f6b5..0133619d 100644 --- a/java/shared/java/org/signal/libsignal/internal/Native.java +++ b/java/shared/java/org/signal/libsignal/internal/Native.java @@ -158,7 +158,7 @@ public final class Native { public static native void CdsiLookup_Destroy(long handle); public static native CompletableFuture CdsiLookup_complete(long asyncRuntime, long lookup); - public static native CompletableFuture CdsiLookup_new(long asyncRuntime, long connectionManager, String username, String password, long request, int timeoutMillis); + public static native CompletableFuture CdsiLookup_new(long asyncRuntime, long connectionManager, String username, String password, long request); public static native byte[] CdsiLookup_token(long lookup); public static native CompletableFuture 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 Svr3Backup(long asyncRuntime, long connectionManager, byte[] secret, String password, int maxTries, String username, String enclavePassword, int opTimeoutMs); + public static native CompletableFuture Svr3Backup(long asyncRuntime, long connectionManager, byte[] secret, String password, int maxTries, String username, String enclavePassword); - public static native CompletableFuture Svr3Restore(long asyncRuntime, long connectionManager, String password, byte[] shareSet, String username, String enclavePassword, int opTimeoutMs); + public static native CompletableFuture 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 TESTING_CdsiLookupResponseConvert(long asyncRuntime); diff --git a/node/Native.d.ts b/node/Native.d.ts index 8fc7c5ce..aa07e4f7 100644 --- a/node/Native.d.ts +++ b/node/Native.d.ts @@ -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, lookup: Wrapper): Promise; -export function CdsiLookup_new(asyncRuntime: Wrapper, connectionManager: Wrapper, username: string, password: string, request: Wrapper, timeoutMillis: number): Promise; +export function CdsiLookup_new(asyncRuntime: Wrapper, connectionManager: Wrapper, username: string, password: string, request: Wrapper): Promise; export function CdsiLookup_token(lookup: Wrapper): Buffer; export function ChatService_connect_auth(asyncRuntime: Wrapper, chat: Wrapper): Promise; export function ChatService_connect_unauth(asyncRuntime: Wrapper, chat: Wrapper): Promise; @@ -461,8 +461,8 @@ export function SignedPreKeyRecord_GetSignature(obj: Wrapper export function SignedPreKeyRecord_GetTimestamp(obj: Wrapper): Timestamp; export function SignedPreKeyRecord_New(id: number, timestamp: Timestamp, pubKey: Wrapper, privKey: Wrapper, signature: Buffer): SignedPreKeyRecord; export function SignedPreKeyRecord_Serialize(obj: Wrapper): Buffer; -export function Svr3Backup(asyncRuntime: Wrapper, connectionManager: Wrapper, secret: Buffer, password: string, maxTries: number, username: string, enclavePassword: string, opTimeoutMs: number): Promise; -export function Svr3Restore(asyncRuntime: Wrapper, connectionManager: Wrapper, password: string, shareSet: Buffer, username: string, enclavePassword: string, opTimeoutMs: number): Promise; +export function Svr3Backup(asyncRuntime: Wrapper, connectionManager: Wrapper, secret: Buffer, password: string, maxTries: number, username: string, enclavePassword: string): Promise; +export function Svr3Restore(asyncRuntime: Wrapper, connectionManager: Wrapper, password: string, shareSet: Buffer, username: string, enclavePassword: string): Promise; export function TESTING_CdsiLookupErrorConvert(errorDescription: string): void; export function TESTING_CdsiLookupResponseConvert(asyncRuntime: Wrapper): Promise; export function TESTING_ChatRequestGetBody(request: Wrapper): Buffer | null; diff --git a/node/ts/net.ts b/node/ts/net.ts index 69696d3e..b2406604 100644 --- a/node/ts/net.ts +++ b/node/ts/net.ts @@ -29,7 +29,6 @@ export type ServiceAuth = { export type CDSRequestOptionsType = { e164s: Array; acisAndAccessKeys: Array<{ aci: string; accessKey: string }>; - timeout: number; returnAcisWithoutUaks: boolean; }; @@ -140,7 +139,6 @@ export class Net { { e164s, acisAndAccessKeys, - timeout, returnAcisWithoutUaks, }: ReadonlyDeep ): Promise> { @@ -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, - opTimeoutMs: number + auth: Readonly ): Promise; /** @@ -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, - opTimeoutMs: number + auth: Readonly ): Promise; } @@ -297,8 +290,7 @@ class Svr3ClientImpl implements Svr3Client { what: Buffer, password: string, maxTries: number, - auth: Readonly, - opTimeoutMs: number + auth: Readonly ): Promise { 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, - opTimeoutMs: number + auth: Readonly ): Promise { return Native.Svr3Restore( this._asyncContext, @@ -324,8 +314,7 @@ class Svr3ClientImpl implements Svr3Client { password, shareSet, auth.username, - auth.password, - opTimeoutMs + auth.password ); } } diff --git a/node/ts/test/NetTest.ts b/node/ts/test/NetTest.ts index e00d4b25..d5fd6374 100644 --- a/node/ts/test/NetTest.ts +++ b/node/ts/test/NetTest.ts @@ -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); - }); }); }); diff --git a/rust/bridge/ffi/src/util.rs b/rust/bridge/ffi/src/util.rs index 47223415..76dfd029 100644 --- a/rust/bridge/ffi/src/util.rs +++ b/rust/bridge/ffi/src/util.rs @@ -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 { diff --git a/rust/bridge/shared/src/ffi/error.rs b/rust/bridge/shared/src/ffi/error.rs index 10bb758c..f5f5b186 100644 --- a/rust/bridge/shared/src/ffi/error.rs +++ b/rust/bridge/shared/src/ffi/error.rs @@ -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 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 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 => { diff --git a/rust/bridge/shared/src/jni/error.rs b/rust/bridge/shared/src/jni/error.rs index 59f81aff..dfb09995 100644 --- a/rust/bridge/shared/src/jni/error.rs +++ b/rust/bridge/shared/src/jni/error.rs @@ -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 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 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(_) diff --git a/rust/bridge/shared/src/jni/mod.rs b/rust/bridge/shared/src/jni/mod.rs index 84aff230..f3e78900 100644 --- a/rust/bridge/shared/src/jni/mod.rs +++ b/rust/bridge/shared/src/jni/mod.rs @@ -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, ), diff --git a/rust/bridge/shared/src/net.rs b/rust/bridge/shared/src/net.rs index b9bdccbe..ba357cc4 100644 --- a/rust/bridge/shared/src/net.rs +++ b/rust/bridge/shared/src/net.rs @@ -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, username: String, // hex-encoded uid enclave_password: String, // timestamp:otp(...) - op_timeout_ms: u32, // timeout spans both connecting and performing the operation ) -> Result, 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, 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()) } diff --git a/rust/bridge/shared/src/net/cdsi.rs b/rust/bridge/shared/src/net/cdsi.rs index f474cb93..d80e511d 100644 --- a/rust/bridge/shared/src/net/cdsi.rs +++ b/rust/bridge/shared/src/net/cdsi.rs @@ -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 { 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, diff --git a/rust/bridge/shared/src/node/error.rs b/rust/bridge/shared/src/node/error.rs index 8168aaa8..fd9b03cf 100644 --- a/rust/bridge/shared/src/node/error.rs +++ b/rust/bridge/shared/src/node/error.rs @@ -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); } diff --git a/rust/bridge/shared/src/testing/net.rs b/rust/bridge/shared/src/testing/net.rs index 2581824d..af82a706 100644 --- a/rust/bridge/shared/src/testing/net.rs +++ b/rust/bridge/shared/src/testing/net.rs @@ -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" }, }) } diff --git a/rust/net/examples/cdsi_lookup.rs b/rust/net/examples/cdsi_lookup.rs index c5b8db6c..8808dbff 100644 --- a/rust/net/examples/cdsi_lookup.rs +++ b/rust/net/examples/cdsi_lookup.rs @@ -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?; diff --git a/rust/net/src/cdsi.rs b/rust/net/src/cdsi.rs index 2b58985a..1f045554 100644 --- a/rust/net/src/cdsi.rs +++ b/rust/net/src/cdsi.rs @@ -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 for LookupError { impl From 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 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, } } } diff --git a/rust/net/src/enclave.rs b/rust/net/src/enclave.rs index ed11626a..3ff02b8c 100644 --- a/rust/net/src/enclave.rs +++ b/rust/net/src/enclave.rs @@ -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 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") } diff --git a/rust/net/src/infra/reconnect.rs b/rust/net/src/infra/reconnect.rs index ee362439..5a3a4c5a 100644 --- a/rust/net/src/infra/reconnect.rs +++ b/rust/net/src/infra/reconnect.rs @@ -36,7 +36,7 @@ pub(crate) enum ServiceState { /// 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, } } } diff --git a/rust/net/src/svr3.rs b/rust/net/src/svr3.rs index 61c7a5c5..7b516f62 100644 --- a/rust/net/src/svr3.rs +++ b/rust/net/src/svr3.rs @@ -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 for Error { @@ -184,7 +184,7 @@ impl From 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, } } } diff --git a/swift/Sources/LibSignalClient/Error.swift b/swift/Sources/LibSignalClient/Error.swift index fc4c2696..57b0ec77 100644 --- a/swift/Sources/LibSignalClient/Error.swift +++ b/swift/Sources/LibSignalClient/Error.swift @@ -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: diff --git a/swift/Sources/LibSignalClient/Net.swift b/swift/Sources/LibSignalClient/Net.swift index 05a2f919..7a8f477b 100644 --- a/swift/Sources/LibSignalClient/Net.swift +++ b/swift/Sources/LibSignalClient/Net.swift @@ -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? { diff --git a/swift/Sources/LibSignalClient/Svr3.swift b/swift/Sources/LibSignalClient/Svr3.swift index e273044f..87c86b43 100644 --- a/swift/Sources/LibSignalClient/Svr3.swift +++ b/swift/Sources/LibSignalClient/Svr3.swift @@ -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 ) } } diff --git a/swift/Sources/SignalFfi/signal_ffi.h b/swift/Sources/SignalFfi/signal_ffi.h index a7f0d4f9..3119abc6 100644 --- a/swift/Sources/SignalFfi/signal_ffi.h +++ b/swift/Sources/SignalFfi/signal_ffi.h @@ -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); diff --git a/swift/Tests/LibSignalClientTests/NetTests.swift b/swift/Tests/LibSignalClientTests/NetTests.swift index 2da1093c..4266337e 100644 --- a/swift/Tests/LibSignalClientTests/NetTests.swift +++ b/swift/Tests/LibSignalClientTests/NetTests.swift @@ -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