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 @@ -8,6 +8,11 @@ +ed25519: string, +curve25519: string, }; +const olmIdentityKeysValidator: TInterface = + tShape({ + ed25519: t.String, + curve25519: t.String, + }); export type OLMPrekey = { +curve25519: { @@ -47,6 +52,11 @@ +primaryIdentityPublicKeys: OLMIdentityKeys, +notificationIdentityPublicKeys: OLMIdentityKeys, }; +export const identityKeysBlobValidator: TInterface = + tShape({ + primaryIdentityPublicKeys: olmIdentityKeysValidator, + notificationIdentityPublicKeys: olmIdentityKeysValidator, + }); export type SignedIdentityKeysBlob = { +payload: string, 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 @@ -1,5 +1,17 @@ // @flow +import t, { type TInterface } from 'tcomb'; + +import { + identityKeysBlobValidator, + type IdentityKeysBlob, +} from './crypto-types.js'; +import { + type OlmSessionInitializationInfo, + olmSessionInitializationInfoValidator, +} from './request-types.js'; +import { tShape } from '../utils/validation-utils.js'; + export type UserLoginResponse = { +userId: string, +accessToken: string, @@ -19,9 +31,25 @@ +oneTimeNotifPrekey: ?string, }; +export type KeyserverKeys = { + +identityKeysBlob: IdentityKeysBlob, + +contentInitializationInfo: OlmSessionInitializationInfo, + +notifInitializationInfo: OlmSessionInitializationInfo, + +payloadSignature: string, + +socialProof: ?string, +}; +export const keyserverKeysValidator: TInterface = + tShape({ + identityKeysBlob: identityKeysBlobValidator, + contentInitializationInfo: olmSessionInitializationInfoValidator, + notifInitializationInfo: olmSessionInitializationInfoValidator, + payloadSignature: t.String, + socialProof: t.maybe(t.String), + }); + export interface IdentityServiceClient { +deleteUser: () => Promise; - +getKeyserverKeys: string => Promise; + +getKeyserverKeys: string => Promise; +registerUser?: ( username: string, password: string, diff --git a/native/identity-service/identity-service-context-provider.react.js b/native/identity-service/identity-service-context-provider.react.js --- a/native/identity-service/identity-service-context-provider.react.js +++ b/native/identity-service/identity-service-context-provider.react.js @@ -4,12 +4,14 @@ import { getOneTimeKeyArray } from 'lib/shared/crypto-utils.js'; import { IdentityClientContext } from 'lib/shared/identity-client-context.js'; -import type { - IdentityServiceClient, - OutboundKeyInfoResponse, - UserLoginResponse, +import { + type IdentityServiceClient, + type UserLoginResponse, + type KeyserverKeys, + keyserverKeysValidator, } from 'lib/types/identity-service-types.js'; import { ONE_TIME_KEYS_NUMBER } from 'lib/types/identity-service-types.js'; +import { assertWithValidator } from 'lib/utils/validation-utils.js'; import { getCommServicesAuthMetadataEmitter } from '../event-emitters/csa-auth-metadata-emitter.js'; import { commCoreModule, commRustModule } from '../native-modules.js'; @@ -68,7 +70,7 @@ const { deviceID, userID, accessToken } = await getAuthMetadata(); return commRustModule.deleteUser(userID, deviceID, accessToken); }, - getKeyserverKeys: async (keyserverID: string) => { + getKeyserverKeys: async (keyserverID: string): Promise => { const { deviceID, userID, accessToken } = await getAuthMetadata(); const result = await commRustModule.getKeyserverKeys( userID, @@ -76,18 +78,33 @@ accessToken, keyserverID, ); - const resultObject: OutboundKeyInfoResponse = JSON.parse(result); - if ( - !resultObject.payload || - !resultObject.payloadSignature || - !resultObject.contentPrekey || - !resultObject.contentPrekeySignature || - !resultObject.notifPrekey || - !resultObject.notifPrekeySignature - ) { - throw new Error('Invalid response from Identity service'); + const resultObject = JSON.parse(result); + const payload = resultObject?.payload; + + const keyserverKeys = { + identityKeysBlob: payload ? JSON.parse(payload) : null, + contentInitializationInfo: { + prekey: resultObject?.contentPrekey, + prekeySignature: resultObject?.contentPrekeySignature, + oneTimeKey: resultObject?.oneTimeContentPrekey, + }, + notifInitializationInfo: { + prekey: resultObject?.notifPrekey, + prekeySignature: resultObject?.notifPrekeySignature, + oneTimeKey: resultObject?.oneTimeNotifPrekey, + }, + payloadSignature: resultObject?.payloadSignature, + socialProof: resultObject?.socialProof, + }; + + if (!keyserverKeys.contentInitializationInfo.oneTimeKey) { + throw new Error('Missing content one time key'); } - return resultObject; + if (!keyserverKeys.notifInitializationInfo.oneTimeKey) { + throw new Error('Missing notif one time key'); + } + + return assertWithValidator(keyserverKeys, keyserverKeysValidator); }, registerUser: async (username: string, password: string) => { await commCoreModule.initializeCryptoAccount(); diff --git a/web/grpc/identity-service-client-wrapper.js b/web/grpc/identity-service-client-wrapper.js --- a/web/grpc/identity-service-client-wrapper.js +++ b/web/grpc/identity-service-client-wrapper.js @@ -1,11 +1,13 @@ // @flow import identityServiceConfig from 'lib/facts/identity-service.js'; -import type { - IdentityServiceAuthLayer, - IdentityServiceClient, - OutboundKeyInfoResponse, +import { + type IdentityServiceAuthLayer, + type IdentityServiceClient, + type KeyserverKeys, + keyserverKeysValidator, } from 'lib/types/identity-service-types.js'; +import { assertWithValidator } from 'lib/utils/validation-utils.js'; import { VersionInterceptor, AuthInterceptor } from './interceptor.js'; import * as IdentityAuthClient from '../protobufs/identity-auth-client.cjs'; @@ -79,41 +81,49 @@ await this.authClient.deleteUser(new Empty()); }; - getKeyserverKeys: (keyserverID: string) => Promise = - async (keyserverID: string) => { - const client = this.authClient; - if (!client) { - throw new Error('Identity service client is not initialized'); - } - - const request = new IdentityAuthStructs.OutboundKeysForUserRequest(); - request.setUserId(keyserverID); - const response = await client.getKeyserverKeys(request); - const keyserverInfo = response.getKeyserverInfo(); - if (!response.hasKeyserverInfo() || !keyserverInfo) { - return null; - } - - const identityInfo = keyserverInfo.getIdentityInfo(); - const contentPreKey = keyserverInfo.getContentPrekey(); - const notifPreKey = keyserverInfo.getNotifPrekey(); - - if (!identityInfo || !contentPreKey || !notifPreKey) { - return null; - } - - return { - payload: identityInfo.getPayload(), - payloadSignature: identityInfo.getPayloadSignature(), - socialProof: identityInfo.getSocialProof(), - contentPrekey: contentPreKey.getPrekey(), - contentPrekeySignature: contentPreKey.getPrekeySignature(), - notifPrekey: notifPreKey.getPrekey(), - notifPrekeySignature: notifPreKey.getPrekeySignature(), - oneTimeContentPrekey: keyserverInfo.getOneTimeContentPrekey(), - oneTimeNotifPrekey: keyserverInfo.getOneTimeNotifPrekey(), - }; + getKeyserverKeys: (keyserverID: string) => Promise = async ( + keyserverID: string, + ) => { + const client = this.authClient; + if (!client) { + throw new Error('Identity service client is not initialized'); + } + + const request = new IdentityAuthStructs.OutboundKeysForUserRequest(); + request.setUserId(keyserverID); + const response = await client.getKeyserverKeys(request); + + const keyserverInfo = response.getKeyserverInfo(); + const identityInfo = keyserverInfo?.getIdentityInfo(); + const contentPreKey = keyserverInfo?.getContentPrekey(); + const notifPreKey = keyserverInfo?.getNotifPrekey(); + const payload = identityInfo?.getPayload(); + + const keyserverKeys = { + identityKeysBlob: payload ? JSON.parse(payload) : null, + contentInitializationInfo: { + prekey: contentPreKey?.getPrekey(), + prekeySignature: contentPreKey?.getPrekeySignature(), + oneTimeKey: keyserverInfo?.getOneTimeContentPrekey(), + }, + notifInitializationInfo: { + prekey: notifPreKey?.getPrekey(), + prekeySignature: notifPreKey?.getPrekeySignature(), + oneTimeKey: keyserverInfo?.getOneTimeNotifPrekey(), + }, + payloadSignature: identityInfo?.getPayloadSignature(), + socialProof: identityInfo?.getSocialProof(), }; + + if (!keyserverKeys.contentInitializationInfo.oneTimeKey) { + throw new Error('Missing content one time key'); + } + if (!keyserverKeys.notifInitializationInfo.oneTimeKey) { + throw new Error('Missing notif one time key'); + } + + return assertWithValidator(keyserverKeys, keyserverKeysValidator); + }; } export { IdentityServiceClientWrapper };