diff --git a/lib/components/prekeys-handler.react.js b/lib/components/prekeys-handler.react.js new file mode 100644 --- /dev/null +++ b/lib/components/prekeys-handler.react.js @@ -0,0 +1,40 @@ +// @flow + +import invariant from 'invariant'; +import * as React from 'react'; + +import { isLoggedIn } from '../selectors/user-selectors.js'; +import { IdentityClientContext } from '../shared/identity-client-context.js'; +import { getConfig } from '../utils/config.js'; +import { useSelector } from '../utils/redux-utils.js'; + +// Time after which rotation is started +const PREKEY_ROTATION_TIMEOUT = 3 * 1000; // in milliseconds + +function PrekeysHandler(): null { + const loggedIn = useSelector(isLoggedIn); + + const identityContext = React.useContext(IdentityClientContext); + invariant(identityContext, 'Identity context should be set'); + + React.useEffect(() => { + if (!loggedIn) { + return undefined; + } + + const timeoutID = setTimeout(async () => { + try { + const { olmAPI } = getConfig(); + await olmAPI.validateAndUploadPrekeys(identityContext); + } catch (e) { + console.log('Prekey validation error: ', e.message); + } + }, PREKEY_ROTATION_TIMEOUT); + + return () => clearTimeout(timeoutID); + }, [identityContext, loggedIn]); + + return null; +} + +export default PrekeysHandler; diff --git a/lib/types/crypto-types.js b/lib/types/crypto-types.js --- a/lib/types/crypto-types.js +++ b/lib/types/crypto-types.js @@ -2,6 +2,7 @@ import t, { type TInterface } from 'tcomb'; +import { type IdentityClientContextType } from '../shared/identity-client-context.js'; import { tShape } from '../utils/validation-utils.js'; export type OLMIdentityKeys = { @@ -131,4 +132,7 @@ initialEncryptedContent: string, ) => Promise, +getOneTimeKeys: (numberOfKeys: number) => Promise, + +validateAndUploadPrekeys: ( + identityContext: IdentityClientContextType, + ) => Promise, }; diff --git a/native/components/prekeys-handler.react.js b/native/components/prekeys-handler.react.js deleted file mode 100644 --- a/native/components/prekeys-handler.react.js +++ /dev/null @@ -1,46 +0,0 @@ -// @flow - -import * as React from 'react'; - -import { isLoggedIn } from 'lib/selectors/user-selectors.js'; -import { useSelector } from 'lib/utils/redux-utils.js'; - -import { commCoreModule } from '../native-modules.js'; - -// Time after which rotation is started -const PREKEY_ROTATION_TIMEOUT = 3 * 1000; // in milliseconds - -function PrekeysHandler(): null { - const loggedIn = useSelector(isLoggedIn); - - React.useEffect(() => { - if (!loggedIn) { - return undefined; - } - - const timeoutID = setTimeout(async () => { - const authMetadata = await commCoreModule.getCommServicesAuthMetadata(); - const { userID, deviceID, accessToken } = authMetadata; - if (!userID || !deviceID || !accessToken) { - return; - } - - try { - await commCoreModule.initializeCryptoAccount(); - await commCoreModule.validateAndUploadPrekeys( - userID, - deviceID, - accessToken, - ); - } catch (e) { - console.log('Prekey validation error: ', e.message); - } - }, PREKEY_ROTATION_TIMEOUT); - - return () => clearTimeout(timeoutID); - }, [loggedIn]); - - return null; -} - -export default PrekeysHandler; diff --git a/native/crypto/olm-api.js b/native/crypto/olm-api.js --- a/native/crypto/olm-api.js +++ b/native/crypto/olm-api.js @@ -1,6 +1,7 @@ // @flow import { getOneTimeKeyValues } from 'lib/shared/crypto-utils.js'; +import { type IdentityClientContextType } from 'lib/shared/identity-client-context.js'; import type { OneTimeKeysResultValues, OlmAPI, @@ -37,6 +38,25 @@ notificationsOneTimeKeys: getOneTimeKeyValues(notificationsOneTimeKeys), }; }, + async validateAndUploadPrekeys( + identityContext: IdentityClientContextType, + ): Promise { + let authMetadata; + try { + authMetadata = await identityContext.getAuthMetadata(); + } catch (e) { + return; + } + const { userID, deviceID, accessToken } = authMetadata; + if (!userID || !deviceID || !accessToken) { + return; + } + await commCoreModule.validateAndUploadPrekeys( + userID, + deviceID, + accessToken, + ); + }, }; export { olmAPI }; diff --git a/native/root.react.js b/native/root.react.js --- a/native/root.react.js +++ b/native/root.react.js @@ -29,6 +29,7 @@ import IntegrityHandler from 'lib/components/integrity-handler.react.js'; import KeyserverConnectionsHandler from 'lib/components/keyserver-connections-handler.js'; import { MediaCacheProvider } from 'lib/components/media-cache-provider.react.js'; +import PrekeysHandler from 'lib/components/prekeys-handler.react.js'; import { StaffContextProvider } from 'lib/components/staff-provider.react.js'; import { CallKeyserverEndpointProvider } from 'lib/keyserver-conn/call-keyserver-endpoint-provider.react.js'; import { TunnelbrokerProvider } from 'lib/tunnelbroker/tunnelbroker-context.js'; @@ -44,7 +45,6 @@ import AccessTokenHandler from './components/access-token-handler.react.js'; import { FeatureFlagsProvider } from './components/feature-flags-provider.react.js'; import PersistedStateGate from './components/persisted-state-gate.js'; -import PrekeysHandler from './components/prekeys-handler.react.js'; import VersionSupportedChecker from './components/version-supported.react.js'; import ConnectedStatusBar from './connected-status-bar.react.js'; import { SQLiteDataHandler } from './data/sqlite-data-handler.js'; diff --git a/web/crypto/olm-api.js b/web/crypto/olm-api.js --- a/web/crypto/olm-api.js +++ b/web/crypto/olm-api.js @@ -3,13 +3,19 @@ import olm from '@commapp/olm'; import type { Account, Session } from '@commapp/olm'; +import { type IdentityClientContextType } from 'lib/shared/identity-client-context.js'; import { type OlmAPI, olmEncryptedMessageTypes, type OLMIdentityKeys, type OneTimeKeysResultValues, } from 'lib/types/crypto-types.js'; -import { getAccountOneTimeKeys } from 'lib/utils/olm-utils.js'; +import { + getAccountOneTimeKeys, + getAccountPrekeysSet, + shouldForgetPrekey, + shouldRotatePrekey, +} from 'lib/utils/olm-utils.js'; // methods below are just mocks to SQLite API // implement proper methods tracked in ENG-6462 @@ -87,6 +93,50 @@ return { contentOneTimeKeys, notificationsOneTimeKeys }; }, + async validateAndUploadPrekeys( + identityContext: IdentityClientContextType, + ): Promise { + const authMetadata = await identityContext.getAuthMetadata(); + const { userID, deviceID, accessToken } = authMetadata; + if (!userID || !deviceID || !accessToken) { + return; + } + + const contentAccount = getOlmAccount(); + if (shouldRotatePrekey(contentAccount)) { + contentAccount.generate_prekey(); + } + if (shouldForgetPrekey(contentAccount)) { + contentAccount.forget_old_prekey(); + } + await storeOlmAccount(contentAccount); + + if (!contentAccount.unpublished_prekey()) { + return; + } + + const notifAccount = getOlmAccount(); + const { prekey: notifPrekey, prekeySignature: notifPrekeySignature } = + getAccountPrekeysSet(notifAccount); + const { prekey: contentPrekey, prekeySignature: contentPrekeySignature } = + getAccountPrekeysSet(contentAccount); + + if (!notifPrekeySignature || !contentPrekeySignature) { + throw new Error('Prekey signature is missing'); + } + + if (!identityContext.identityClient.publishWebPrekeys) { + throw new Error('Publish prekeys method unimplemented'); + } + await identityContext.identityClient.publishWebPrekeys({ + contentPrekey, + contentPrekeySignature, + notifPrekey, + notifPrekeySignature, + }); + contentAccount.mark_keys_as_published(); + await storeOlmAccount(contentAccount); + }, }; export { olmAPI }; diff --git a/web/root.js b/web/root.js --- a/web/root.js +++ b/web/root.js @@ -11,6 +11,7 @@ import IntegrityHandler from 'lib/components/integrity-handler.react.js'; import KeyserverConnectionsHandler from 'lib/components/keyserver-connections-handler.js'; +import PrekeysHandler from 'lib/components/prekeys-handler.react.js'; import { CallKeyserverEndpointProvider } from 'lib/keyserver-conn/call-keyserver-endpoint-provider.react.js'; import { reduxLoggerMiddleware } from 'lib/utils/action-logger.js'; @@ -52,6 +53,7 @@ +