diff --git a/lib/types/identity-service-types.js b/lib/types/identity-service-types.js --- a/lib/types/identity-service-types.js +++ b/lib/types/identity-service-types.js @@ -82,4 +82,15 @@ +username: string, }; +export type IdentityDeviceKeyUpload = { + +keyPayload: string, + +keyPayloadSignature: string, + +contentPrekey: string, + +contentPrekeySignature: string, + +notifPrekey: string, + +notifPrekeySignature: string, + +contentOneTimeKeys: $ReadOnlyArray, + +notifOneTimeKeys: $ReadOnlyArray, +}; + export const ONE_TIME_KEYS_NUMBER = 10; diff --git a/web/account/account-hooks.js b/web/account/account-hooks.js --- a/web/account/account-hooks.js +++ b/web/account/account-hooks.js @@ -20,6 +20,10 @@ OLMIdentityKeys, NotificationsOlmDataType, } from 'lib/types/crypto-types.js'; +import { + ONE_TIME_KEYS_NUMBER, + type IdentityDeviceKeyUpload, +} from 'lib/types/identity-service-types.js'; import type { OlmSessionInitializationInfo } from 'lib/types/request-types.js'; import { useDispatch } from 'lib/utils/redux-utils.js'; @@ -174,6 +178,65 @@ }, [getOrCreateCryptoStore]); } +function useGetDeviceKeyUpload(): () => Promise { + const getOrCreateCryptoStore = useGetOrCreateCryptoStore(); + // `getSignedIdentityKeysBlob()` will initialize OLM, so no need to do it + // again + const getSignedIdentityKeysBlob = useGetSignedIdentityKeysBlob(); + + return React.useCallback(async () => { + const signedIdentityKeysBlob = await getSignedIdentityKeysBlob(); + const { primaryAccount, notificationAccount } = + await getOrCreateCryptoStore(); + + const primaryOLMAccount = new olm.Account(); + const notificationOLMAccount = new olm.Account(); + primaryOLMAccount.unpickle( + primaryAccount.picklingKey, + primaryAccount.pickledAccount, + ); + notificationOLMAccount.unpickle( + notificationAccount.picklingKey, + notificationAccount.pickledAccount, + ); + + const contentPrekey = primaryOLMAccount.prekey(); + const contentPrekeySignature = primaryOLMAccount.prekey_signature(); + + if (!contentPrekeySignature) { + throw new Error('invalid_prekey'); + } + + const notifPrekey = notificationOLMAccount.prekey(); + const notifPrekeySignature = notificationOLMAccount.prekey_signature(); + + if (!notifPrekeySignature) { + throw new Error('invalid_prekey'); + } + + primaryOLMAccount.generate_one_time_keys(ONE_TIME_KEYS_NUMBER); + const contentOneTimeKeys = getOneTimeKeyValuesFromBlob( + primaryOLMAccount.one_time_keys(), + ); + + notificationOLMAccount.generate_one_time_keys(ONE_TIME_KEYS_NUMBER); + const notifOneTimeKeys = getOneTimeKeyValuesFromBlob( + notificationOLMAccount.one_time_keys(), + ); + + return { + keyPayload: signedIdentityKeysBlob.payload, + keyPayloadSignature: signedIdentityKeysBlob.signature, + contentPrekey, + contentPrekeySignature, + notifPrekey, + notifPrekeySignature, + contentOneTimeKeys, + notifOneTimeKeys, + }; + }, [getOrCreateCryptoStore, getSignedIdentityKeysBlob]); +} + function NotificationsSessionCreatorProvider(props: Props): React.Node { const getOrCreateCryptoStore = useGetOrCreateCryptoStore(); const currentCryptoStore = useSelector(state => state.cryptoStore); @@ -331,4 +394,5 @@ NotificationsSessionCreatorProvider, useWebNotificationsSessionCreator, GetOrCreateCryptoStoreProvider, + useGetDeviceKeyUpload, };