signal-desktop/ts/util/zkgroup.ts

368 lines
10 KiB
TypeScript
Raw Normal View History

// Copyright 2020-2021 Signal Messenger, LLC
2020-10-30 20:34:04 +00:00
// SPDX-License-Identifier: AGPL-3.0-only
2022-07-08 20:46:25 +00:00
import type {
ProfileKeyCredentialRequestContext,
PniCredentialRequestContext,
} from '@signalapp/libsignal-client/zkgroup';
import {
2022-07-08 20:46:25 +00:00
AuthCredentialWithPni,
2020-09-09 02:25:05 +00:00
ClientZkAuthOperations,
ClientZkGroupCipher,
ClientZkProfileOperations,
2020-09-09 02:25:05 +00:00
GroupMasterKey,
GroupSecretParams,
ProfileKey,
2020-09-09 02:25:05 +00:00
ProfileKeyCiphertext,
2022-07-08 20:46:25 +00:00
ExpiringProfileKeyCredential,
2020-09-09 02:25:05 +00:00
ProfileKeyCredentialPresentation,
2022-07-08 20:46:25 +00:00
ExpiringProfileKeyCredentialResponse,
PniCredential,
PniCredentialResponse,
PniCredentialPresentation,
ServerPublicParams,
2020-09-09 02:25:05 +00:00
UuidCiphertext,
NotarySignature,
} from '@signalapp/libsignal-client/zkgroup';
2021-10-26 22:59:08 +00:00
import { UUID } from '../types/UUID';
import type { UUIDStringType } from '../types/UUID';
export * from '@signalapp/libsignal-client/zkgroup';
2020-09-09 02:25:05 +00:00
// Scenarios
export function decryptGroupBlob(
clientZkGroupCipher: ClientZkGroupCipher,
2021-06-22 14:46:42 +00:00
ciphertext: Uint8Array
): Uint8Array {
return clientZkGroupCipher.decryptBlob(Buffer.from(ciphertext));
2020-09-09 02:25:05 +00:00
}
2022-07-08 20:46:25 +00:00
export function decodeProfileKeyCredentialPresentation(
2021-06-22 14:46:42 +00:00
presentationBuffer: Uint8Array
2022-07-08 20:46:25 +00:00
): { profileKey: Uint8Array; userId: Uint8Array } {
2020-09-09 02:25:05 +00:00
const presentation = new ProfileKeyCredentialPresentation(
Buffer.from(presentationBuffer)
2020-09-09 02:25:05 +00:00
);
2022-07-08 20:46:25 +00:00
const userId = presentation.getUuidCiphertext().serialize();
const profileKey = presentation.getProfileKeyCiphertext().serialize();
return {
profileKey,
userId,
};
}
export function decryptPniCredentialPresentation(
clientZkGroupCipher: ClientZkGroupCipher,
presentationBuffer: Uint8Array
): { profileKey: Uint8Array; pni: UUIDStringType; aci: UUIDStringType } {
const presentation = new PniCredentialPresentation(
Buffer.from(presentationBuffer)
);
const pniCiphertext = presentation.getPniCiphertext();
const aciCiphertext = presentation.getAciCiphertext();
const aci = clientZkGroupCipher.decryptUuid(aciCiphertext);
const pni = clientZkGroupCipher.decryptUuid(pniCiphertext);
2020-09-09 02:25:05 +00:00
const profileKeyCiphertext = presentation.getProfileKeyCiphertext();
const profileKey = clientZkGroupCipher.decryptProfileKey(
profileKeyCiphertext,
2022-07-08 20:46:25 +00:00
aci
2020-09-09 02:25:05 +00:00
);
return {
profileKey: profileKey.serialize(),
2022-07-08 20:46:25 +00:00
aci: UUID.cast(aci),
pni: UUID.cast(pni),
2020-09-09 02:25:05 +00:00
};
}
export function decryptProfileKey(
clientZkGroupCipher: ClientZkGroupCipher,
2021-06-22 14:46:42 +00:00
profileKeyCiphertextBuffer: Uint8Array,
2021-10-26 22:59:08 +00:00
uuid: UUIDStringType
2021-06-22 14:46:42 +00:00
): Uint8Array {
2020-09-09 02:25:05 +00:00
const profileKeyCiphertext = new ProfileKeyCiphertext(
Buffer.from(profileKeyCiphertextBuffer)
2020-09-09 02:25:05 +00:00
);
const profileKey = clientZkGroupCipher.decryptProfileKey(
profileKeyCiphertext,
uuid
);
return profileKey.serialize();
2020-09-09 02:25:05 +00:00
}
export function decryptUuid(
clientZkGroupCipher: ClientZkGroupCipher,
2021-06-22 14:46:42 +00:00
uuidCiphertextBuffer: Uint8Array
2020-09-09 02:25:05 +00:00
): string {
const uuidCiphertext = new UuidCiphertext(Buffer.from(uuidCiphertextBuffer));
2020-09-09 02:25:05 +00:00
return clientZkGroupCipher.decryptUuid(uuidCiphertext);
}
export function deriveProfileKeyVersion(
profileKeyBase64: string,
2021-10-26 22:59:08 +00:00
uuid: UUIDStringType
): string {
const profileKeyArray = Buffer.from(profileKeyBase64, 'base64');
const profileKey = new ProfileKey(profileKeyArray);
const profileKeyVersion = profileKey.getProfileKeyVersion(uuid);
return profileKeyVersion.toString();
}
export function deriveGroupPublicParams(
2021-06-22 14:46:42 +00:00
groupSecretParamsBuffer: Uint8Array
): Uint8Array {
2020-09-09 02:25:05 +00:00
const groupSecretParams = new GroupSecretParams(
Buffer.from(groupSecretParamsBuffer)
2020-09-09 02:25:05 +00:00
);
return groupSecretParams.getPublicParams().serialize();
2020-09-09 02:25:05 +00:00
}
2021-06-22 14:46:42 +00:00
export function deriveGroupID(groupSecretParamsBuffer: Uint8Array): Uint8Array {
2020-09-09 02:25:05 +00:00
const groupSecretParams = new GroupSecretParams(
Buffer.from(groupSecretParamsBuffer)
2020-09-09 02:25:05 +00:00
);
return groupSecretParams.getPublicParams().getGroupIdentifier().serialize();
2020-09-09 02:25:05 +00:00
}
export function deriveGroupSecretParams(
2021-06-22 14:46:42 +00:00
masterKeyBuffer: Uint8Array
): Uint8Array {
const masterKey = new GroupMasterKey(Buffer.from(masterKeyBuffer));
2020-09-09 02:25:05 +00:00
const groupSecretParams = GroupSecretParams.deriveFromMasterKey(masterKey);
return groupSecretParams.serialize();
2020-09-09 02:25:05 +00:00
}
export function encryptGroupBlob(
clientZkGroupCipher: ClientZkGroupCipher,
2021-06-22 14:46:42 +00:00
plaintext: Uint8Array
): Uint8Array {
return clientZkGroupCipher.encryptBlob(Buffer.from(plaintext));
2020-09-09 02:25:05 +00:00
}
export function encryptUuid(
clientZkGroupCipher: ClientZkGroupCipher,
2022-07-08 20:46:25 +00:00
uuidPlaintext: UUID
2021-06-22 14:46:42 +00:00
): Uint8Array {
2022-07-08 20:46:25 +00:00
const uuidCiphertext = clientZkGroupCipher.encryptUuid(
uuidPlaintext.toString()
);
2020-09-09 02:25:05 +00:00
return uuidCiphertext.serialize();
}
export function generateProfileKeyCredentialRequest(
clientZkProfileCipher: ClientZkProfileOperations,
2021-10-26 22:59:08 +00:00
uuid: UUIDStringType,
profileKeyBase64: string
): { context: ProfileKeyCredentialRequestContext; requestHex: string } {
const profileKeyArray = Buffer.from(profileKeyBase64, 'base64');
const profileKey = new ProfileKey(profileKeyArray);
2021-11-11 22:43:05 +00:00
const context =
clientZkProfileCipher.createProfileKeyCredentialRequestContext(
uuid,
profileKey
);
const request = context.getRequest();
const requestArray = request.serialize();
return {
context,
requestHex: requestArray.toString('hex'),
};
}
2022-07-08 20:46:25 +00:00
export function generatePNICredentialRequest(
clientZkProfileCipher: ClientZkProfileOperations,
aci: UUIDStringType,
pni: UUIDStringType,
profileKeyBase64: string
): { context: PniCredentialRequestContext; requestHex: string } {
const profileKeyArray = Buffer.from(profileKeyBase64, 'base64');
const profileKey = new ProfileKey(profileKeyArray);
const context = clientZkProfileCipher.createPniCredentialRequestContext(
aci,
pni,
profileKey
);
const request = context.getRequest();
const requestArray = request.serialize();
return {
context,
requestHex: requestArray.toString('hex'),
};
}
2020-09-09 02:25:05 +00:00
export function getAuthCredentialPresentation(
clientZkAuthOperations: ClientZkAuthOperations,
authCredentialBase64: string,
groupSecretParamsBase64: string
2021-06-22 14:46:42 +00:00
): Uint8Array {
2022-07-08 20:46:25 +00:00
const authCredential = new AuthCredentialWithPni(
Buffer.from(authCredentialBase64, 'base64')
2020-09-09 02:25:05 +00:00
);
const secretParams = new GroupSecretParams(
Buffer.from(groupSecretParamsBase64, 'base64')
2020-09-09 02:25:05 +00:00
);
2022-07-08 20:46:25 +00:00
const presentation =
clientZkAuthOperations.createAuthCredentialWithPniPresentation(
secretParams,
authCredential
);
return presentation.serialize();
2020-09-09 02:25:05 +00:00
}
2020-10-06 17:06:34 +00:00
export function createProfileKeyCredentialPresentation(
clientZkProfileCipher: ClientZkProfileOperations,
profileKeyCredentialBase64: string,
groupSecretParamsBase64: string
2021-06-22 14:46:42 +00:00
): Uint8Array {
const profileKeyCredentialArray = Buffer.from(
profileKeyCredentialBase64,
'base64'
2020-10-06 17:06:34 +00:00
);
2022-07-08 20:46:25 +00:00
const profileKeyCredential = new ExpiringProfileKeyCredential(
2020-10-06 17:06:34 +00:00
profileKeyCredentialArray
);
const secretParams = new GroupSecretParams(
Buffer.from(groupSecretParamsBase64, 'base64')
2020-10-06 17:06:34 +00:00
);
2021-11-11 22:43:05 +00:00
const presentation =
2022-07-08 20:46:25 +00:00
clientZkProfileCipher.createExpiringProfileKeyCredentialPresentation(
2021-11-11 22:43:05 +00:00
secretParams,
profileKeyCredential
);
2020-10-06 17:06:34 +00:00
return presentation.serialize();
2020-10-06 17:06:34 +00:00
}
2022-07-08 20:46:25 +00:00
export function createPNICredentialPresentation(
clientZkProfileCipher: ClientZkProfileOperations,
pniCredentialBase64: string,
groupSecretParamsBase64: string
): Uint8Array {
const pniCredentialArray = Buffer.from(pniCredentialBase64, 'base64');
const pniCredential = new PniCredential(pniCredentialArray);
const secretParams = new GroupSecretParams(
Buffer.from(groupSecretParamsBase64, 'base64')
);
const presentation = clientZkProfileCipher.createPniCredentialPresentation(
secretParams,
pniCredential
);
return presentation.serialize();
}
2020-09-09 02:25:05 +00:00
export function getClientZkAuthOperations(
serverPublicParamsBase64: string
): ClientZkAuthOperations {
const serverPublicParams = new ServerPublicParams(
Buffer.from(serverPublicParamsBase64, 'base64')
2020-09-09 02:25:05 +00:00
);
return new ClientZkAuthOperations(serverPublicParams);
}
export function getClientZkGroupCipher(
groupSecretParamsBase64: string
): ClientZkGroupCipher {
const serverPublicParams = new GroupSecretParams(
Buffer.from(groupSecretParamsBase64, 'base64')
2020-09-09 02:25:05 +00:00
);
return new ClientZkGroupCipher(serverPublicParams);
}
export function getClientZkProfileOperations(
serverPublicParamsBase64: string
): ClientZkProfileOperations {
const serverPublicParams = new ServerPublicParams(
Buffer.from(serverPublicParamsBase64, 'base64')
2020-09-09 02:25:05 +00:00
);
return new ClientZkProfileOperations(serverPublicParams);
}
export function handleProfileKeyCredential(
clientZkProfileCipher: ClientZkProfileOperations,
context: ProfileKeyCredentialRequestContext,
responseBase64: string
2022-07-08 20:46:25 +00:00
): { credential: string; expiration: number } {
const response = new ExpiringProfileKeyCredentialResponse(
Buffer.from(responseBase64, 'base64')
2020-09-09 02:25:05 +00:00
);
2021-11-11 22:43:05 +00:00
const profileKeyCredential =
2022-07-08 20:46:25 +00:00
clientZkProfileCipher.receiveExpiringProfileKeyCredential(
context,
response
);
const credentialArray = profileKeyCredential.serialize();
2022-07-08 20:46:25 +00:00
return {
credential: credentialArray.toString('base64'),
expiration: profileKeyCredential.getExpirationTime().getTime(),
};
}
export function handleProfileKeyPNICredential(
clientZkProfileCipher: ClientZkProfileOperations,
context: PniCredentialRequestContext,
responseBase64: string
): string {
const response = new PniCredentialResponse(
Buffer.from(responseBase64, 'base64')
);
const pniCredential = clientZkProfileCipher.receivePniCredential(
context,
response
);
const credentialArray = pniCredential.serialize();
return credentialArray.toString('base64');
}
2021-07-19 19:26:06 +00:00
export function deriveProfileKeyCommitment(
profileKeyBase64: string,
2021-10-26 22:59:08 +00:00
uuid: UUIDStringType
2021-07-19 19:26:06 +00:00
): string {
const profileKeyArray = Buffer.from(profileKeyBase64, 'base64');
2021-07-19 19:26:06 +00:00
const profileKey = new ProfileKey(profileKeyArray);
return profileKey.getCommitment(uuid).contents.toString('base64');
2021-07-19 19:26:06 +00:00
}
export function verifyNotarySignature(
serverPublicParamsBase64: string,
message: Uint8Array,
signature: Uint8Array
): void {
const serverPublicParams = new ServerPublicParams(
Buffer.from(serverPublicParamsBase64, 'base64')
);
const notarySignature = new NotarySignature(Buffer.from(signature));
serverPublicParams.verifySignature(Buffer.from(message), notarySignature);
}