Adds support for versioned profiles

* Add zkgroup library

* tsconfig.json: Prettier wants to mess it up. :0(

* Initial take on versioned profile fetches

* Fix up the logging in getProfiles() - warn instead of error

* Introduce new VERSIONED_PROFILE_FETCH flag

* Update zkgroup dependency to v0.5.0

* Fix lint-deps - new zkgroup library brought in new debug dep

* ts/zkgroup: Introduce some commonly-used helper functions

* Update to latest serverPublicParams

* Don't derive profileKeyVersion unless flag is set
This commit is contained in:
Scott Nonnenberg 2020-04-15 16:12:28 -07:00 committed by GitHub
parent 5f0c07eec2
commit c1dfe3e5b4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 591 additions and 25 deletions

View file

@ -1918,7 +1918,15 @@
}
const c = await ConversationController.getOrCreateAndWait(id, 'private');
const {
generateProfileKeyCredentialRequest,
getClientZkProfileOperations,
handleProfileKeyCredential,
} = Util.zkgroup;
const clientZkProfileCipher = getClientZkProfileOperations(
window.getServerPublicParams()
);
// Because we're no longer using Backbone-integrated saves, we need to manually
// clear the changed fields here so our hasChanged() check is useful.
c.changed = {};
@ -1926,7 +1934,36 @@
let profile;
try {
await c.deriveAccessKeyIfNeeded();
await Promise.all([
c.deriveAccessKeyIfNeeded(),
c.deriveProfileKeyVersionIfNeeded(),
]);
const profileKey = c.get('profileKey');
const uuid = c.get('uuid');
const profileKeyVersionHex = window.VERSIONED_PROFILE_FETCH
? c.get('profileKeyVersion')
: null;
const existingProfileKeyCredential = c.get('profileKeyCredential');
const weHaveVersion = Boolean(
profileKey && uuid && profileKeyVersionHex
);
let profileKeyCredentialRequestHex;
let profileCredentialRequestContext;
if (weHaveVersion && !existingProfileKeyCredential) {
window.log.info('Generating request...');
({
requestHex: profileKeyCredentialRequestHex,
context: profileCredentialRequestContext,
} = generateProfileKeyCredentialRequest(
clientZkProfileCipher,
uuid,
profileKey
));
}
const sendMetadata = c.getSendMetadata({ disableMeCheck: true }) || {};
const getInfo = sendMetadata[c.id] || {};
@ -1934,6 +1971,8 @@
try {
profile = await textsecure.messaging.getProfile(id, {
accessKey: getInfo.accessKey,
profileKeyVersion: profileKeyVersionHex,
profileKeyCredentialRequest: profileKeyCredentialRequestHex,
});
} catch (error) {
if (error.code === 401 || error.code === 403) {
@ -1941,13 +1980,19 @@
`Setting sealedSender to DISABLED for conversation ${c.idForLogging()}`
);
c.set({ sealedSender: SEALED_SENDER.DISABLED });
profile = await textsecure.messaging.getProfile(id);
profile = await textsecure.messaging.getProfile(id, {
profileKeyVersion: profileKeyVersionHex,
profileKeyCredentialRequest: profileKeyCredentialRequestHex,
});
} else {
throw error;
}
}
} else {
profile = await textsecure.messaging.getProfile(id);
profile = await textsecure.messaging.getProfile(id, {
profileKeyVersion: profileKeyVersionHex,
profileKeyCredentialRequest: profileKeyCredentialRequestHex,
});
}
const identityKey = window.Signal.Crypto.base64ToArrayBuffer(
@ -2014,10 +2059,18 @@
if (profile.capabilities) {
c.set({ capabilities: profile.capabilities });
}
if (profileCredentialRequestContext && profile.credential) {
const profileKeyCredential = handleProfileKeyCredential(
clientZkProfileCipher,
profileCredentialRequestContext,
profile.credential
);
c.set({ profileKeyCredential });
}
} catch (error) {
if (error.code !== 403 && error.code !== 404) {
window.log.error(
'getProfile error:',
window.log.warn(
'getProfile failure:',
id,
error && error.stack ? error.stack : error
);
@ -2030,8 +2083,8 @@
try {
await c.setProfileName(profile.name);
} catch (error) {
window.log.error(
'getProfile decryption error:',
window.log.warn(
'getProfile decryption failure:',
id,
error && error.stack ? error.stack : error
);
@ -2042,6 +2095,9 @@
await c.setProfileAvatar(profile.avatar);
} catch (error) {
if (error.code === 403 || error.code === 404) {
window.log.info(
`Clearing profile avatar for conversation ${c.idForLogging()}`
);
c.set({
profileAvatar: null,
});
@ -2120,6 +2176,8 @@
);
this.set({
profileKey,
profileKeyVersion: null,
profileKeyCredential: null,
accessKey: null,
profileName: null,
profileFamilyName: null,
@ -2127,7 +2185,10 @@
sealedSender: SEALED_SENDER.UNKNOWN,
});
await this.deriveAccessKeyIfNeeded();
await Promise.all([
this.deriveAccessKeyIfNeeded(),
this.deriveProfileKeyVersionIfNeeded(),
]);
window.Signal.Data.updateConversation(this.attributes, {
Conversation: Whisper.Conversation,
@ -2145,11 +2206,13 @@
}
this.set({
profileAvatar: null,
profileKey: null,
profileKeyVersion: null,
profileKeyCredential: null,
accessKey: null,
profileName: null,
profileFamilyName: null,
accessKey: null,
profileAvatar: null,
sealedSender: SEALED_SENDER.UNKNOWN,
});
@ -2177,6 +2240,28 @@
);
this.set({ accessKey });
},
async deriveProfileKeyVersionIfNeeded() {
const profileKey = this.get('profileKey');
if (!profileKey) {
return;
}
// We won't even save derived profile key versions if we haven't flipped this switch
if (!window.VERSIONED_PROFILE_FETCH) {
return;
}
const uuid = this.get('uuid');
if (!uuid || this.get('profileKeyVersion')) {
return;
}
const profileKeyVersion = Util.zkgroup.deriveProfileKeyVersion(
profileKey,
uuid
);
this.set({ profileKeyVersion });
},
hasMember(identifier) {
const cid = ConversationController.getConversationId(identifier);