From 13193649d3dbfec9eaae0e3d2ff16b888dc28980 Mon Sep 17 00:00:00 2001 From: Fedor Indutny <79877362+indutny-signal@users.noreply.github.com> Date: Fri, 21 Jul 2023 01:19:32 +0200 Subject: [PATCH] Use combined username link API --- package.json | 4 +-- ts/services/username.ts | 42 ++++++++++++++++++------------- ts/test-mock/pnp/username_test.ts | 15 +++++------ ts/textsecure/WebAPI.ts | 26 ++++++++++++++++--- yarn.lock | 18 ++++++------- 5 files changed, 63 insertions(+), 42 deletions(-) diff --git a/package.json b/package.json index dffd5f931e0d..13b43d64048d 100644 --- a/package.json +++ b/package.json @@ -89,7 +89,7 @@ "@popperjs/core": "2.11.6", "@react-spring/web": "9.5.5", "@signalapp/better-sqlite3": "8.4.3", - "@signalapp/libsignal-client": "0.28.0", + "@signalapp/libsignal-client": "0.29.0", "@signalapp/ringrtc": "2.29.1", "@types/fabric": "4.5.3", "backbone": "1.4.0", @@ -190,7 +190,7 @@ "@electron/fuses": "1.5.0", "@formatjs/intl": "2.6.7", "@mixer/parallel-prettier": "2.0.3", - "@signalapp/mock-server": "3.2.0", + "@signalapp/mock-server": "3.2.1", "@storybook/addon-a11y": "6.5.6", "@storybook/addon-actions": "6.5.6", "@storybook/addon-controls": "6.5.6", diff --git a/ts/services/username.ts b/ts/services/username.ts index 3576b28b9967..57792f0ab809 100644 --- a/ts/services/username.ts +++ b/ts/services/username.ts @@ -183,16 +183,26 @@ export async function confirmUsername( strictAssert(usernames.hash(username).equals(hash), 'username hash mismatch'); try { - await server.confirmUsername({ - hash, - proof, - abortSignal, + const { entropy, encryptedUsername } = + usernames.createUsernameLink(username); + + await window.storage.remove('usernameLink'); + + const { usernameLinkHandle: serverIdString } = await server.confirmUsername( + { + hash, + proof, + encryptedUsername, + abortSignal, + } + ); + + await window.storage.put('usernameLink', { + entropy, + serverId: uuidToBytes(serverIdString), }); await updateUsernameAndSyncProfile(username); - - // TODO: DESKTOP-5687 - await resetLink(username); } catch (error) { if (error instanceof HTTPError) { if (error.code === 413 || error.code === 429) { @@ -245,17 +255,15 @@ export async function resetLink(username: string): Promise { throw new Error('Username has changed on another device'); } - const link = usernames.createUsernameLink(username); + const { entropy, encryptedUsername } = usernames.createUsernameLink(username); await window.storage.remove('usernameLink'); const { usernameLinkHandle: serverIdString } = - await server.replaceUsernameLink({ - encryptedUsername: link.encryptedUsername, - }); + await server.replaceUsernameLink({ encryptedUsername }); await window.storage.put('usernameLink', { - entropy: link.entropy, + entropy, serverId: uuidToBytes(serverIdString), }); @@ -285,10 +293,8 @@ export async function resolveUsernameByLinkBase64( serverId ); - const link = new usernames.UsernameLink( - Buffer.from(entropy), - Buffer.from(usernameLinkEncryptedValue) - ); - - return link.decryptUsername(); + return usernames.decryptUsernameLink({ + entropy: Buffer.from(entropy), + encryptedUsername: Buffer.from(usernameLinkEncryptedValue), + }); } diff --git a/ts/test-mock/pnp/username_test.ts b/ts/test-mock/pnp/username_test.ts index e7f5b2f87e4c..9f2766d4a423 100644 --- a/ts/test-mock/pnp/username_test.ts +++ b/ts/test-mock/pnp/username_test.ts @@ -222,18 +222,15 @@ describe('pnp/username', function needsName() { const linkUuid = bufferToUuid(Buffer.from(usernameLink.serverId)); - const encryptedLinkBase64 = await server.lookupByUsernameLink(linkUuid); - if (!encryptedLinkBase64) { + const encryptedLink = await server.lookupByUsernameLink(linkUuid); + if (!encryptedLink) { throw new Error('Could not find link on the sever'); } - const encryptedLink = Buffer.from(encryptedLinkBase64, 'base64'); - - const link = new usernames.UsernameLink( - Buffer.from(usernameLink.entropy), - encryptedLink - ); - const linkUsername = link.decryptUsername(); + const linkUsername = usernames.decryptUsernameLink({ + entropy: Buffer.from(usernameLink.entropy), + encryptedUsername: encryptedLink, + }); assert.strictEqual(linkUsername, username); state = newState; diff --git a/ts/textsecure/WebAPI.ts b/ts/textsecure/WebAPI.ts index a4b2b575e2ab..e8543bf3d7f7 100644 --- a/ts/textsecure/WebAPI.ts +++ b/ts/textsecure/WebAPI.ts @@ -831,6 +831,7 @@ export type ReplaceUsernameLinkOptionsType = Readonly<{ export type ConfirmUsernameOptionsType = Readonly<{ hash: Uint8Array; proof: Uint8Array; + encryptedUsername: Uint8Array; abortSignal?: AbortSignal; }>; @@ -843,6 +844,13 @@ export type ReserveUsernameResultType = z.infer< typeof reserveUsernameResultZod >; +const confirmUsernameResultZod = z.object({ + usernameLinkHandle: z.string(), +}); +export type ConfirmUsernameResultType = z.infer< + typeof confirmUsernameResultZod +>; + const replaceUsernameLinkResultZod = z.object({ usernameLinkHandle: z.string(), }); @@ -851,7 +859,9 @@ export type ReplaceUsernameLinkResultType = z.infer< >; const resolveUsernameLinkResultZod = z.object({ - usernameLinkEncryptedValue: z.string().transform(x => Bytes.fromBase64(x)), + usernameLinkEncryptedValue: z + .string() + .transform(x => Bytes.fromBase64(fromWebSafeBase64(x))), }); export type ResolveUsernameLinkResultType = z.infer< typeof resolveUsernameLinkResultZod @@ -1020,7 +1030,9 @@ export type WebAPIType = { reserveUsername: ( options: ReserveUsernameOptionsType ) => Promise; - confirmUsername(options: ConfirmUsernameOptionsType): Promise; + confirmUsername( + options: ConfirmUsernameOptionsType + ): Promise; replaceUsernameLink: ( options: ReplaceUsernameLinkOptionsType ) => Promise; @@ -1916,17 +1928,21 @@ export function initialize({ async function confirmUsername({ hash, proof, + encryptedUsername, abortSignal, }: ConfirmUsernameOptionsType) { - await _ajax({ + const response = await _ajax({ call: 'confirmUsername', httpType: 'PUT', jsonData: { usernameHash: toWebSafeBase64(Bytes.toBase64(hash)), zkProof: toWebSafeBase64(Bytes.toBase64(proof)), + encryptedUsername: toWebSafeBase64(Bytes.toBase64(encryptedUsername)), }, + responseType: 'json', abortSignal, }); + return confirmUsernameResultZod.parse(response); } async function replaceUsernameLink({ @@ -1938,7 +1954,9 @@ export function initialize({ httpType: 'PUT', responseType: 'json', jsonData: { - usernameLinkEncryptedValue: Bytes.toBase64(encryptedUsername), + usernameLinkEncryptedValue: toWebSafeBase64( + Bytes.toBase64(encryptedUsername) + ), }, }) ); diff --git a/yarn.lock b/yarn.lock index f1c91c6fe0d8..dd7136c217e2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2276,20 +2276,20 @@ bindings "^1.5.0" tar "^6.1.0" -"@signalapp/libsignal-client@0.28.0", "@signalapp/libsignal-client@^0.28.0": - version "0.28.0" - resolved "https://registry.yarnpkg.com/@signalapp/libsignal-client/-/libsignal-client-0.28.0.tgz#b1553a4b56fc01afe5e9b2785abd5c680f46ebc4" - integrity sha512-Vl3vt9hBdPW2/cwuf8+ZMwxmlAlnuBSgsKebRPfDOboLWDRlQRq+tstlwfBFU0e/2ixgY95Wulu46I1cl6H40g== +"@signalapp/libsignal-client@0.29.0", "@signalapp/libsignal-client@^0.29.0": + version "0.29.0" + resolved "https://registry.yarnpkg.com/@signalapp/libsignal-client/-/libsignal-client-0.29.0.tgz#36c467645551e023924371f2d894085dfbd3e59c" + integrity sha512-p+FoCV0wORPoZNixyib/kxPL1+7OdNNjAxCKbiuN+1zx948YDZxVrttJv1FylPrLiTUpF+AloZr88elnUq4ZJA== dependencies: node-gyp-build "^4.2.3" uuid "^8.3.0" -"@signalapp/mock-server@3.2.0": - version "3.2.0" - resolved "https://registry.yarnpkg.com/@signalapp/mock-server/-/mock-server-3.2.0.tgz#9371dc2002a1a8aa25ac815944a443cdc0a1b7c5" - integrity sha512-4rpAAH5tV8eoIikb6FozMmrCKr+pqaP9JNyfZQ5YLPanAiTE9iER7WJex0HJ9PhscgswbzWIPFuRyhm/qPocVQ== +"@signalapp/mock-server@3.2.1": + version "3.2.1" + resolved "https://registry.yarnpkg.com/@signalapp/mock-server/-/mock-server-3.2.1.tgz#36fd3e72b44dbcb6c82fdb27bbf56f8393e8f065" + integrity sha512-irT4U3e8Lve9HODGIlXx+vElONaPNe7Ks9QRoSPSR4o0DbUX/g+zUxfEu7gjEi0CQI8OKxcJHe7e5DbE8mvXng== dependencies: - "@signalapp/libsignal-client" "^0.28.0" + "@signalapp/libsignal-client" "^0.29.0" debug "^4.3.2" long "^4.0.0" micro "^9.3.4"