Additional protocol changes for CDS v2
This commit is contained in:
parent
b35d330c0a
commit
bb15cfc622
5 changed files with 66 additions and 20 deletions
11
ts/Curve.ts
11
ts/Curve.ts
|
@ -91,6 +91,13 @@ export function createKeyPair(incomingKey: Uint8Array): KeyPairType {
|
|||
};
|
||||
}
|
||||
|
||||
export function prefixPublicKey(pubKey: Uint8Array): Uint8Array {
|
||||
return Bytes.concatenate([
|
||||
new Uint8Array([0x05]),
|
||||
validatePubKeyFormat(pubKey),
|
||||
]);
|
||||
}
|
||||
|
||||
export function calculateAgreement(
|
||||
pubKey: Uint8Array,
|
||||
privKey: Uint8Array
|
||||
|
@ -98,9 +105,7 @@ export function calculateAgreement(
|
|||
const privKeyBuffer = Buffer.from(privKey);
|
||||
|
||||
const pubKeyObj = client.PublicKey.deserialize(
|
||||
Buffer.from(
|
||||
Bytes.concatenate([new Uint8Array([0x05]), validatePubKeyFormat(pubKey)])
|
||||
)
|
||||
Buffer.from(prefixPublicKey(pubKey))
|
||||
);
|
||||
const privKeyObj = client.PrivateKey.deserialize(privKeyBuffer);
|
||||
const sharedSecret = privKeyObj.agree(pubKeyObj);
|
||||
|
|
|
@ -20,8 +20,22 @@ enum State {
|
|||
Closed,
|
||||
}
|
||||
|
||||
export type CDSRequestOptionsType = Readonly<{
|
||||
e164s: ReadonlyArray<string>;
|
||||
auth: CDSAuthType;
|
||||
timeout?: number;
|
||||
}>;
|
||||
|
||||
export type CDSAuthType = Readonly<{
|
||||
username: string;
|
||||
password: string;
|
||||
}>;
|
||||
|
||||
const HANDSHAKE_TIMEOUT = 10 * durations.SECOND;
|
||||
const REQUEST_TIMEOUT = 10 * durations.SECOND;
|
||||
const VERSION = new Uint8Array([0x01]);
|
||||
const USERNAME_LENGTH = 32;
|
||||
const PASSWORD_LENGTH = 31;
|
||||
|
||||
export class CDSSocket extends EventEmitter {
|
||||
private state = State.Handshake;
|
||||
|
@ -82,19 +96,30 @@ export class CDSSocket extends EventEmitter {
|
|||
|
||||
public async request({
|
||||
e164s,
|
||||
auth,
|
||||
timeout = REQUEST_TIMEOUT,
|
||||
}: {
|
||||
e164s: ReadonlyArray<string>;
|
||||
timeout?: number;
|
||||
}): Promise<ReadonlyArray<UUIDStringType | null>> {
|
||||
}: CDSRequestOptionsType): Promise<ReadonlyArray<UUIDStringType | null>> {
|
||||
await this.finishedHandshake;
|
||||
strictAssert(
|
||||
this.state === State.Established,
|
||||
'Connection not established'
|
||||
);
|
||||
|
||||
const username = Bytes.fromString(auth.username);
|
||||
const password = Bytes.fromString(auth.password);
|
||||
strictAssert(
|
||||
username.length === USERNAME_LENGTH,
|
||||
'Invalid username length'
|
||||
);
|
||||
strictAssert(
|
||||
password.length === PASSWORD_LENGTH,
|
||||
'Invalid password length'
|
||||
);
|
||||
|
||||
const request = Bytes.concatenate([
|
||||
new Uint8Array([0x01]),
|
||||
VERSION,
|
||||
username,
|
||||
password,
|
||||
...e164s.map(e164 => {
|
||||
// Long.fromString handles numbers with or without a leading '+'
|
||||
return new Uint8Array(Long.fromString(e164).toBytesBE());
|
||||
|
|
|
@ -6,10 +6,12 @@ import { HsmEnclaveClient, PublicKey } from '@signalapp/signal-client';
|
|||
import type { connection as WebSocket } from 'websocket';
|
||||
|
||||
import * as Bytes from '../Bytes';
|
||||
import { prefixPublicKey } from '../Curve';
|
||||
import type { AbortableProcess } from '../util/AbortableProcess';
|
||||
import * as log from '../logging/log';
|
||||
import type { UUIDStringType } from '../types/UUID';
|
||||
import { CDSSocket } from './CDSSocket';
|
||||
import type { CDSRequestOptionsType } from './CDSSocket';
|
||||
import { connect as connectWebSocket } from './WebSocket';
|
||||
|
||||
export type CDSSocketManagerOptionsType = Readonly<{
|
||||
|
@ -30,7 +32,7 @@ export class CDSSocketManager {
|
|||
|
||||
constructor(private readonly options: CDSSocketManagerOptionsType) {
|
||||
this.publicKey = PublicKey.deserialize(
|
||||
Buffer.from(Bytes.fromHex(options.publicKey))
|
||||
Buffer.from(prefixPublicKey(Bytes.fromHex(options.publicKey)))
|
||||
);
|
||||
this.codeHash = Buffer.from(Bytes.fromHex(options.codeHash));
|
||||
if (options.proxyUrl) {
|
||||
|
@ -38,19 +40,15 @@ export class CDSSocketManager {
|
|||
}
|
||||
}
|
||||
|
||||
public async request({
|
||||
e164s,
|
||||
timeout,
|
||||
}: {
|
||||
e164s: ReadonlyArray<string>;
|
||||
timeout?: number;
|
||||
}): Promise<ReadonlyArray<UUIDStringType | null>> {
|
||||
public async request(
|
||||
options: CDSRequestOptionsType
|
||||
): Promise<ReadonlyArray<UUIDStringType | null>> {
|
||||
log.info('CDSSocketManager: connecting socket');
|
||||
const socket = await this.connect().getResult();
|
||||
log.info('CDSSocketManager: connected socket');
|
||||
|
||||
try {
|
||||
return await socket.request({ e164s, timeout });
|
||||
return await socket.request(options);
|
||||
} finally {
|
||||
log.info('CDSSocketManager: closing socket');
|
||||
socket.close(3000, 'Normal');
|
||||
|
|
|
@ -504,6 +504,7 @@ const URL_CALLS = {
|
|||
deliveryCert: 'v1/certificate/delivery',
|
||||
devices: 'v1/devices',
|
||||
directoryAuth: 'v1/directory/auth',
|
||||
directoryAuthV2: 'v2/directory/auth',
|
||||
discovery: 'v1/discovery',
|
||||
getGroupAvatarUpload: 'v1/groups/avatar/form',
|
||||
getGroupCredentials: 'v1/certificate/group',
|
||||
|
@ -556,6 +557,7 @@ const WEBSOCKET_CALLS = new Set<keyof typeof URL_CALLS>([
|
|||
|
||||
// Directory
|
||||
'directoryAuth',
|
||||
'directoryAuthV2',
|
||||
|
||||
// Storage
|
||||
'storageToken',
|
||||
|
@ -2475,6 +2477,17 @@ export function initialize({
|
|||
})) as { username: string; password: string };
|
||||
}
|
||||
|
||||
async function getDirectoryAuthV2(): Promise<{
|
||||
username: string;
|
||||
password: string;
|
||||
}> {
|
||||
return (await _ajax({
|
||||
call: 'directoryAuthV2',
|
||||
httpType: 'GET',
|
||||
responseType: 'json',
|
||||
})) as { username: string; password: string };
|
||||
}
|
||||
|
||||
function validateAttestationQuote({
|
||||
serverStaticPublic,
|
||||
quote: quoteBytes,
|
||||
|
@ -2864,7 +2877,12 @@ export function initialize({
|
|||
async function getUuidsForE164sV2(
|
||||
e164s: ReadonlyArray<string>
|
||||
): Promise<Dictionary<UUIDStringType | null>> {
|
||||
const uuids = await cdsSocketManager.request({ e164s });
|
||||
const auth = await getDirectoryAuthV2();
|
||||
|
||||
const uuids = await cdsSocketManager.request({
|
||||
auth,
|
||||
e164s,
|
||||
});
|
||||
|
||||
return zipObject(e164s, uuids);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue