diff --git a/ts/components/conversation/message/message-content/MessageAvatar.tsx b/ts/components/conversation/message/message-content/MessageAvatar.tsx index 220091961..85e71eb6c 100644 --- a/ts/components/conversation/message/message-content/MessageAvatar.tsx +++ b/ts/components/conversation/message/message-content/MessageAvatar.tsx @@ -6,7 +6,7 @@ import { MessageRenderingProps } from '../../../../models/messageType'; import { findCachedBlindedMatchOrLookItUp } from '../../../../session/apis/open_group_api/sogsv3/knownBlindedkeys'; import { getConversationController } from '../../../../session/conversations'; import { getSodiumRenderer } from '../../../../session/crypto'; -import { PubKey } from '../../../../session/types'; +import { KeyPrefixType, PubKey } from '../../../../session/types'; import { openConversationWithMessages } from '../../../../state/ducks/conversations'; import { updateUserDetailsModal } from '../../../../state/ducks/modalDialog'; import { @@ -61,7 +61,7 @@ export const MessageAvatar = (props: Props) => { const userName = authorName || authorProfileName || sender; const onMessageAvatarClick = useCallback(async () => { - if (isPublic && !PubKey.hasBlindedPrefix(sender)) { + if (isPublic && !PubKey.isBlinded(sender)) { // public chat but session id not blinded. disable showing user details if we do not have an active convo with that user. // an unactive convo with that user means that we never chatted with that id directyly, but only through a sogs const convoWithSender = getConversationController().get(sender); @@ -84,6 +84,11 @@ export const MessageAvatar = (props: Props) => { } if (isPublic && selectedConvoKey) { + if (sender.startsWith(KeyPrefixType.blinded25)) { + window.log.info('onMessageAvatarClick: blinded25 convo click are disabled currently...'); + + return; + } const convoOpen = getConversationController().get(selectedConvoKey); const room = OpenGroupData.getV2OpenGroupRoom(convoOpen.id); let privateConvoToOpen = sender; diff --git a/ts/components/menu/Menu.tsx b/ts/components/menu/Menu.tsx index 695d0acf9..f00ae0b7a 100644 --- a/ts/components/menu/Menu.tsx +++ b/ts/components/menu/Menu.tsx @@ -357,7 +357,7 @@ export const CopyMenuItem = (): JSX.Element | null => { export const MarkAllReadMenuItem = (): JSX.Element | null => { const convoId = useConvoIdFromContext(); const isIncomingRequest = useIsIncomingRequest(convoId); - if (!isIncomingRequest && !PubKey.hasBlindedPrefix(convoId)) { + if (!isIncomingRequest && !PubKey.isBlinded(convoId)) { return ( markAllReadByConvoId(convoId)}>{window.i18n('markAllAsRead')} ); @@ -379,7 +379,7 @@ export const BlockMenuItem = (): JSX.Element | null => { const isPrivate = useIsPrivate(convoId); const isIncomingRequest = useIsIncomingRequest(convoId); - if (!isMe && isPrivate && !isIncomingRequest && !PubKey.hasBlindedPrefix(convoId)) { + if (!isMe && isPrivate && !isIncomingRequest && !PubKey.isBlinded(convoId)) { const blockTitle = isBlocked ? window.i18n('unblock') : window.i18n('block'); const blockHandler = isBlocked ? () => unblockConvoById(convoId) diff --git a/ts/hooks/useParamSelector.ts b/ts/hooks/useParamSelector.ts index 74ef78556..ba26cf208 100644 --- a/ts/hooks/useParamSelector.ts +++ b/ts/hooks/useParamSelector.ts @@ -101,7 +101,7 @@ export function useIsBlinded(convoId?: string) { if (!convoId) { return false; } - return Boolean(PubKey.hasBlindedPrefix(convoId)); + return Boolean(PubKey.isBlinded(convoId)); } export function useHasNickname(convoId?: string) { diff --git a/ts/models/conversation.ts b/ts/models/conversation.ts index 9c745ac1f..1e793508e 100644 --- a/ts/models/conversation.ts +++ b/ts/models/conversation.ts @@ -532,8 +532,8 @@ export class ConversationModel extends Backbone.Model { ); const hasIncomingMessages = incomingMessageCount > 0; - if (this.id.startsWith('15')) { - window.log.info('Sending a blinded message to this user: ', this.id); + if (PubKey.isBlinded(this.id)) { + window.log.info('Sending a blinded message react to this user: ', this.id); await this.sendBlindedMessageRequest(chatMessageParams); return; } @@ -1674,7 +1674,7 @@ export class ConversationModel extends Backbone.Model { ); const hasIncomingMessages = incomingMessageCount > 0; - if (this.id.startsWith('15')) { + if (PubKey.isBlinded(this.id)) { window.log.info('Sending a blinded message to this user: ', this.id); await this.sendBlindedMessageRequest(chatMessageParams); return; @@ -1778,7 +1778,7 @@ export class ConversationModel extends Backbone.Model { const ourSignKeyBytes = await UserUtils.getUserED25519KeyPairBytes(); const groupUrl = this.getSogsOriginMessage(); - if (!PubKey.hasBlindedPrefix(this.id)) { + if (!PubKey.isBlinded(this.id)) { window?.log?.warn('sendBlindedMessageRequest - convo is not a blinded one'); return; } @@ -1826,7 +1826,7 @@ export class ConversationModel extends Backbone.Model { } this.set({ active_at: Date.now(), isApproved: true }); - + // TODO we need to add support for sending blinded25 message request in addition to the legacy blinded15 await getMessageQueue().sendToOpenGroupV2BlindedRequest({ encryptedContent: encryptedMsg, roomInfos: roomInfo, diff --git a/ts/models/message.ts b/ts/models/message.ts index f11167efd..592839993 100644 --- a/ts/models/message.ts +++ b/ts/models/message.ts @@ -1279,7 +1279,7 @@ export function findAndFormatContact(pubkey: string): FindAndFormatContactType { if ( pubkey === UserUtils.getOurPubKeyStrFromCache() || - (pubkey && pubkey.startsWith('15') && isUsAnySogsFromCache(pubkey)) + (pubkey && PubKey.isBlinded(pubkey) && isUsAnySogsFromCache(pubkey)) ) { profileName = window.i18n('you'); isMe = true; diff --git a/ts/node/migration/sessionMigrations.ts b/ts/node/migration/sessionMigrations.ts index 382cf9c18..365de0f9d 100644 --- a/ts/node/migration/sessionMigrations.ts +++ b/ts/node/migration/sessionMigrations.ts @@ -1676,7 +1676,7 @@ function updateToSessionSchemaVersion31(currentVersion: number, db: BetterSqlite // this filter is based on the `isContactToStoreInWrapper` function. Note, blocked contacts won't be added to the wrapper at first, but will on the first start const contactsToWriteInWrapper = db .prepare( - `SELECT * FROM ${CONVERSATIONS_TABLE} WHERE type = 'private' AND active_at > 0 AND priority <> ${CONVERSATION_PRIORITIES.hidden} AND (didApproveMe OR isApproved) AND id <> '$us' AND id NOT LIKE '15%' ;` + `SELECT * FROM ${CONVERSATIONS_TABLE} WHERE type = 'private' AND active_at > 0 AND priority <> ${CONVERSATION_PRIORITIES.hidden} AND (didApproveMe OR isApproved) AND id <> '$us' AND id NOT LIKE '15%' AND id NOT LIKE '25%' ;` ) .all({ us: publicKeyHex, diff --git a/ts/session/apis/open_group_api/sogsv3/knownBlindedkeys.ts b/ts/session/apis/open_group_api/sogsv3/knownBlindedkeys.ts index bc23882ce..89297d34d 100644 --- a/ts/session/apis/open_group_api/sogsv3/knownBlindedkeys.ts +++ b/ts/session/apis/open_group_api/sogsv3/knownBlindedkeys.ts @@ -145,8 +145,8 @@ export function tryMatchBlindWithStandardKey( throw new Error('standardKey must be a standard key (starting with 05)'); } - if (!blindedSessionId.startsWith(KeyPrefixType.blinded)) { - throw new Error('blindedKey must be a blinded key (starting with 15)'); + if (!PubKey.isBlinded(blindedSessionId)) { + throw new Error('blindedKey must be a blinded key (starting with 15 or 25)'); } // We don't want to stop iterating even if an error happens while looking for a blind/standard match. @@ -204,7 +204,7 @@ function findNotCachedBlindingMatch( // we iterate only over the convos private, approved, and which have an unblinded id. const foundConvoMatchingBlindedPubkey = getConversationController() .getConversations() - .filter(m => m.isPrivate() && m.isApproved() && !PubKey.hasBlindedPrefix(m.id)) + .filter(m => m.isPrivate() && m.isApproved() && !PubKey.isBlinded(m.id)) .find(m => { return tryMatchBlindWithStandardKey(m.id, blindedId, serverPublicKey, sodium); }); @@ -214,13 +214,13 @@ function findNotCachedBlindingMatch( /** * This function returns true if the given blindedId matches our own blinded id on any pysogs. - * If the given pubkey is not blinded, it returns true if it is our naked SessionID. + * If the given pubkey is not blinded, it returns true if it is our naked SessionID. * It can be used to replace mentions with the @You syntax and for the quotes too */ export function isUsAnySogsFromCache(blindedOrNakedId: string): boolean { const usUnblinded = UserUtils.getOurPubKeyStrFromCache(); - if (!PubKey.hasBlindedPrefix(blindedOrNakedId)) { + if (!PubKey.isBlinded(blindedOrNakedId)) { return blindedOrNakedId === usUnblinded; } const found = assertLoaded().find( @@ -268,7 +268,7 @@ function findNotCachedBlindedConvoFromUnblindedKey( serverPublicKey: string, sodium: LibSodiumWrappers ): Array { - if (PubKey.hasBlindedPrefix(unblindedID)) { + if (PubKey.isBlinded(unblindedID)) { throw new Error( 'findNotCachedBlindedConvoFromUnblindedKey unblindedID is supposed to be unblinded!' ); @@ -279,7 +279,7 @@ function findNotCachedBlindedConvoFromUnblindedKey( const foundConvosForThisServerPk = getConversationController() .getConversations() - .filter(m => m.isPrivate() && PubKey.hasBlindedPrefix(m.id) && m.isActive()) + .filter(m => m.isPrivate() && PubKey.isBlinded(m.id) && m.isActive()) .filter(m => { return tryMatchBlindWithStandardKey(unblindedID, m.id, serverPublicKey, sodium); }) || []; @@ -302,7 +302,7 @@ export async function findCachedBlindedMatchOrLookItUp( serverPubKey: string, sodium: LibSodiumWrappers ): Promise { - if (!PubKey.hasBlindedPrefix(blindedId)) { + if (!PubKey.isBlinded(blindedId)) { return blindedId; } const found = getCachedNakedKeyFromBlinded(blindedId, serverPubKey); @@ -333,7 +333,7 @@ export function findCachedBlindedIdFromUnblinded( unblindedId: string, serverPubKey: string ): string | undefined { - if (PubKey.hasBlindedPrefix(unblindedId)) { + if (PubKey.isBlinded(unblindedId)) { throw new Error('findCachedBlindedIdFromUnblinded needs an unblindedID'); } const found = assertLoaded().find( @@ -351,7 +351,7 @@ export async function findCachedOurBlindedPubkeyOrLookItUp( ): Promise { const ourNakedSessionID = UserUtils.getOurPubKeyStrFromCache(); - if (PubKey.hasBlindedPrefix(ourNakedSessionID)) { + if (PubKey.isBlinded(ourNakedSessionID)) { throw new Error('findCachedBlindedIdFromUnblindedOrLookItUp needs a unblindedID'); } let found = findCachedBlindedIdFromUnblinded(ourNakedSessionID, serverPubKey); @@ -402,7 +402,7 @@ export function findCachedBlindedMatchOrLookupOnAllServers( unblindedId: string, sodium: LibSodiumWrappers ): Array { - if (PubKey.hasBlindedPrefix(unblindedId)) { + if (PubKey.isBlinded(unblindedId)) { throw new Error('findCachedBlindedMatchOrLookupOnAllServers needs an unblindedId'); } diff --git a/ts/session/apis/open_group_api/sogsv3/sogsBlinding.ts b/ts/session/apis/open_group_api/sogsv3/sogsBlinding.ts index 8e4aba24e..2f3318a6d 100644 --- a/ts/session/apis/open_group_api/sogsv3/sogsBlinding.ts +++ b/ts/session/apis/open_group_api/sogsv3/sogsBlinding.ts @@ -63,7 +63,8 @@ async function getOpenGroupHeaders(data: { const blindingValues = getBlindingValues(serverPK, signingKeys, sodium); ka = blindingValues.secretKey; kA = blindingValues.publicKey; - pubkey = `${KeyPrefixType.blinded}${toHex(kA)}`; + // TODO we will need to add the support of blinded25 here + pubkey = `${KeyPrefixType.blinded15}${toHex(kA)}`; } else { pubkey = `${KeyPrefixType.unblinded}${toHex(signingKeys.pubKeyBytes)}`; } @@ -151,7 +152,8 @@ const getBlindedPubKey = ( sodium: LibSodiumWrappers ): string => { const blindedPubKeyBytes = getBlindingValues(serverPK, signingKeys, sodium); - return `${KeyPrefixType.blinded}${to_hex(blindedPubKeyBytes.publicKey)}`; + // TODO we will need to add the support of blinded25 here + return `${KeyPrefixType.blinded15}${to_hex(blindedPubKeyBytes.publicKey)}`; }; const getBlindingValues = ( diff --git a/ts/session/conversations/ConversationController.ts b/ts/session/conversations/ConversationController.ts index 6257b5599..b4b41d294 100644 --- a/ts/session/conversations/ConversationController.ts +++ b/ts/session/conversations/ConversationController.ts @@ -187,7 +187,7 @@ export class ConversationController { 'getConversationController().deleteBlindedContact() needs complete initial fetch' ); } - if (!PubKey.hasBlindedPrefix(blindedId)) { + if (!PubKey.isBlinded(blindedId)) { throw new Error('deleteBlindedContact allow accepts blinded id'); } window.log.info(`deleteBlindedContact with ${blindedId}`); diff --git a/ts/session/sending/MessageQueue.ts b/ts/session/sending/MessageQueue.ts index b7401fb78..af20d5391 100644 --- a/ts/session/sending/MessageQueue.ts +++ b/ts/session/sending/MessageQueue.ts @@ -139,7 +139,8 @@ export class MessageQueue { recipientBlindedId: string; }) { try { - if (!PubKey.hasBlindedPrefix(recipientBlindedId)) { + // TODO we will need to add the support for blinded25 messages requests + if (!PubKey.isBlinded(recipientBlindedId)) { throw new Error('sendToOpenGroupV2BlindedRequest needs a blindedId'); } const { serverTimestamp, serverId } = await MessageSender.sendToOpenGroupV2BlindedRequest( diff --git a/ts/session/types/PubKey.ts b/ts/session/types/PubKey.ts index 44654ebfb..987250d1c 100644 --- a/ts/session/types/PubKey.ts +++ b/ts/session/types/PubKey.ts @@ -10,9 +10,15 @@ export enum KeyPrefixType { */ standard = '05', /** - * used for participants in open groups + * used for participants in open groups (legacy blinding logic) */ - blinded = '15', + blinded15 = '15', + + /** + * used for participants in open groups (new blinding logic) + */ + blinded25 = '25', + /** * used for participants in open groups */ @@ -32,7 +38,7 @@ export class PubKey { public static readonly PREFIX_GROUP_TEXTSECURE = '__textsecure_group__!'; // prettier-ignore private static readonly regex: RegExp = new RegExp( - `^(${PubKey.PREFIX_GROUP_TEXTSECURE})?(${KeyPrefixType.standard}|${KeyPrefixType.blinded}|${KeyPrefixType.unblinded}|${KeyPrefixType.groupV3})?(${PubKey.HEX}{64}|${PubKey.HEX}{32})$` + `^(${PubKey.PREFIX_GROUP_TEXTSECURE})?(${KeyPrefixType.standard}|${KeyPrefixType.blinded15}|${KeyPrefixType.blinded25}|${KeyPrefixType.unblinded}|${KeyPrefixType.groupV3})?(${PubKey.HEX}{64}|${PubKey.HEX}{32})$` ); /** * If you want to update this regex. Be sure that those are matches ; @@ -146,18 +152,19 @@ export class PubKey { /** * @param keyWithOrWithoutPrefix Key with or without prefix - * @returns If key is the correct length and has a supported prefix 05 or 15 + * @returns If key is the correct length and has a supported prefix 05, 15, 25 */ public static isValidPrefixAndLength(keyWithOrWithoutPrefix: string): boolean { return ( keyWithOrWithoutPrefix.length === 66 && - (keyWithOrWithoutPrefix.startsWith(KeyPrefixType.blinded) || + (keyWithOrWithoutPrefix.startsWith(KeyPrefixType.blinded15) || + keyWithOrWithoutPrefix.startsWith(KeyPrefixType.blinded25) || keyWithOrWithoutPrefix.startsWith(KeyPrefixType.standard)) ); } /** - * This removes the 05 or 15 prefix from a Pubkey which have it and have a length of 66 + * This removes the 05, 15 or 25 prefix from a Pubkey which have it and have a length of 66 * @param keyWithOrWithoutPrefix the key with or without the prefix */ public static removePrefixIfNeeded(keyWithOrWithoutPrefix: string): string { @@ -230,8 +237,8 @@ export class PubKey { return fromHexToArray(PubKey.removePrefixIfNeeded(this.key)); } - public static hasBlindedPrefix(key: string) { - return key.startsWith(KeyPrefixType.blinded); + public static isBlinded(key: string) { + return key.startsWith(KeyPrefixType.blinded15) || key.startsWith(KeyPrefixType.blinded25); } public static isClosedGroupV3(key: string) { diff --git a/ts/session/utils/libsession/libsession_utils_contacts.ts b/ts/session/utils/libsession/libsession_utils_contacts.ts index a5d16d5df..00186a241 100644 --- a/ts/session/utils/libsession/libsession_utils_contacts.ts +++ b/ts/session/utils/libsession/libsession_utils_contacts.ts @@ -29,9 +29,7 @@ const mappedContactWrapperValues = new Map(); * We want to sync the message request status so we need to allow a contact even if it's not approved, did not approve us and is not blocked. */ function isContactToStoreInWrapper(convo: ConversationModel): boolean { - return ( - !convo.isMe() && convo.isPrivate() && convo.isActive() && !PubKey.hasBlindedPrefix(convo.id) - ); + return !convo.isMe() && convo.isPrivate() && convo.isActive() && !PubKey.isBlinded(convo.id); } /** diff --git a/ts/session/utils/sync/syncUtils.ts b/ts/session/utils/sync/syncUtils.ts index cf0d7f3d9..e1d3ace6c 100644 --- a/ts/session/utils/sync/syncUtils.ts +++ b/ts/session/utils/sync/syncUtils.ts @@ -204,7 +204,7 @@ const getValidContacts = (convos: Array) => { c.getRealSessionUsername() && c.isPrivate() && c.isApproved() && - !PubKey.hasBlindedPrefix(c.get('id')) + !PubKey.isBlinded(c.get('id')) ); const contacts = contactsModels.map(c => { diff --git a/ts/state/selectors/conversations.ts b/ts/state/selectors/conversations.ts index c7c1a3d61..97a4f4904 100644 --- a/ts/state/selectors/conversations.ts +++ b/ts/state/selectors/conversations.ts @@ -785,7 +785,7 @@ export const getMessageQuoteProps = createSelector( const isFromMe = isUsAnySogsFromCache(author) || false; // NOTE the quote lookup map always stores our messages using the unblinded pubkey - if (isFromMe && PubKey.hasBlindedPrefix(author)) { + if (isFromMe && PubKey.isBlinded(author)) { author = UserUtils.getOurPubKeyStrFromCache(); } diff --git a/ts/test/session/unit/sogsv3/knownBlindedKeys_test.ts b/ts/test/session/unit/sogsv3/knownBlindedKeys_test.ts index 3618c0eb4..022848ae7 100644 --- a/ts/test/session/unit/sogsv3/knownBlindedKeys_test.ts +++ b/ts/test/session/unit/sogsv3/knownBlindedKeys_test.ts @@ -418,7 +418,7 @@ describe('knownBlindedKeys', () => { it('throws if blindedSessionId is not standard', () => { expect(() => { tryMatchBlindWithStandardKey(realSessionId, realSessionId, serverPublicKey, sodium); - }).to.throw('blindedKey must be a blinded key (starting with 15)'); + }).to.throw('blindedKey must be a blinded key (starting with 15 or 25)'); }); it('returns true if those keys are not matching blind & naked', () => { diff --git a/ts/webworker/workers/node/util/util.worker.ts b/ts/webworker/workers/node/util/util.worker.ts index 54e4daf54..f0ab356e9 100644 --- a/ts/webworker/workers/node/util/util.worker.ts +++ b/ts/webworker/workers/node/util/util.worker.ts @@ -141,7 +141,8 @@ async function verifySignature( } const messageData = fromBase64ToUint8Array(messageBase64); const signature = fromBase64ToUint8Array(signatureBase64); - const isBlindedSender = senderPubKey.startsWith('15'); + // blinded15 or blinded25 are the same for the verifySignature logic + const isBlindedSender = senderPubKey.startsWith('15') || senderPubKey.startsWith('25'); const pubkeyWithoutPrefix = senderPubKey.slice(2); const pubkeyBytes = fromHexToArray(pubkeyWithoutPrefix);