Довести DM v1 до соответствия коду и документации

This commit is contained in:
AidarKC
2026-07-08 17:28:39 +04:00
parent faada9dcdb
commit 98a2631590
19 changed files with 283 additions and 115 deletions
+1 -1
View File
@@ -1088,7 +1088,7 @@ async function init() {
let refBaseKey = String(payload.receiptRefBaseKey || '').trim();
if (!refBaseKey) {
try {
const ref = authService.parseReadReceiptPayload(parsed.payloadBytes);
const ref = parsed.readReceiptPayload || authService.parseReadReceiptPayload(parsed.payloadBytes);
refBaseKey = `${ref.refFromLogin}|${ref.refToLogin}|${ref.refTimeMs}|${ref.refNonce}`;
} catch {}
}
+55 -29
View File
@@ -330,6 +330,42 @@ function parseEncryptedDmBodyBytes(payloadBytes) {
};
}
function parseReadReceiptBodyBytes(payloadBytes) {
if (!(payloadBytes instanceof Uint8Array)) throw new Error('BAD_RECEIPT_PAYLOAD_LEN');
let o = 0;
const read = (n) => {
if (o + n > payloadBytes.length) throw new Error('BAD_RECEIPT_PAYLOAD_LEN');
const out = payloadBytes.slice(o, o + n);
o += n;
return out;
};
const readU8 = () => read(1)[0];
const readU32 = () => {
const part = read(4);
return new DataView(part.buffer, part.byteOffset, 4).getUint32(0, false);
};
const readU64 = () => {
const part = read(8);
return Number(new DataView(part.buffer, part.byteOffset, 8).getBigUint64(0, false));
};
const readAscii = (code) => {
const len = readU8();
const part = read(len);
for (let i = 0; i < part.length; i += 1) {
const c = part[i];
if (c < 0x20 || c > 0x7e) throw new Error(code);
}
return new TextDecoder().decode(part);
};
const refToLogin = readAscii('BAD_RECEIPT_TO_LOGIN');
const refFromLogin = readAscii('BAD_RECEIPT_FROM_LOGIN');
const refTimeMs = readU64();
const refNonce = readU32();
if (o !== payloadBytes.length) throw new Error('BAD_RECEIPT_PAYLOAD_LEN');
return { refToLogin, refFromLogin, refTimeMs, refNonce };
}
function parseSignedMessageBlockBytes(bytes) {
if (!(bytes instanceof Uint8Array)) throw new Error('Expected Uint8Array');
let o = 0;
@@ -374,11 +410,17 @@ function parseSignedMessageBlockBytes(bytes) {
read(DM_PREFIX_V1.length);
const formatVersionMajor = readU8();
const formatVersionMinor = readU8();
if (formatVersionMajor !== DM_FORMAT_VERSION_MAJOR || formatVersionMinor !== DM_FORMAT_VERSION_MINOR) {
throw new Error('BAD_FORMAT_VERSION');
}
const toLogin = readAscii();
const fromLogin = readAscii();
const timeMs = readU64();
const nonce = readU32();
const messageType = readU8();
if (messageType < DM_TYPE_INCOMING || messageType > DM_TYPE_CONVERSATION_DELETED_BY_RECIPIENT) {
throw new Error('BAD_MESSAGE_TYPE');
}
const revisionTimeMs = readU64();
const reencryptedAtMs = readU64();
const bodyLen = readU32();
@@ -392,9 +434,20 @@ function parseSignedMessageBlockBytes(bytes) {
let encryptedBodyPayload = null;
let readReceiptPayload = null;
if (messageType === DM_TYPE_INCOMING || messageType === DM_TYPE_OUTGOING_COPY) {
if (bodyBytes.length === 0) throw new Error('BAD_MESSAGE_LEN');
encryptedBodyPayload = parseEncryptedDmBodyBytes(bodyBytes);
} else if (messageType === DM_TYPE_READ_INCOMING || messageType === DM_TYPE_READ_OUTGOING_COPY) {
readReceiptPayload = null;
if (bodyBytes.length === 0) throw new Error('BAD_RECEIPT_PAYLOAD_LEN');
if (revisionTimeMs !== 0 || reencryptedAtMs !== 0) throw new Error('BAD_RECEIPT_REVISION');
readReceiptPayload = parseReadReceiptBodyBytes(bodyBytes);
} else if (messageType === DM_TYPE_MESSAGE_DELETED_BY_SENDER || messageType === DM_TYPE_MESSAGE_DELETED_BY_RECIPIENT) {
if (bodyBytes.length !== 0) throw new Error('BAD_DELETE_BODY');
if (revisionTimeMs <= 0) throw new Error('BAD_REVISION_TIME');
if (reencryptedAtMs !== 0) throw new Error('BAD_REENCRYPTED_TIME');
} else if (messageType === DM_TYPE_CONVERSATION_DELETED_BY_SENDER || messageType === DM_TYPE_CONVERSATION_DELETED_BY_RECIPIENT) {
if (bodyBytes.length !== 0) throw new Error('BAD_DELETE_BODY');
if (revisionTimeMs !== 0) throw new Error('BAD_REVISION_TIME');
if (reencryptedAtMs !== 0) throw new Error('BAD_REENCRYPTED_TIME');
}
return {
@@ -2278,34 +2331,7 @@ export class AuthService {
}
parseReadReceiptPayload(payloadBytes) {
if (!(payloadBytes instanceof Uint8Array)) throw new Error('Expected Uint8Array');
let o = 0;
const read = (n) => {
if (o + n > payloadBytes.length) throw new Error('BAD_RECEIPT_LEN');
const out = payloadBytes.slice(o, o + n);
o += n;
return out;
};
const readU8 = () => read(1)[0];
const readU32 = () => {
const part = read(4);
return new DataView(part.buffer, part.byteOffset, 4).getUint32(0, false);
};
const readU64 = () => {
const part = read(8);
return Number(new DataView(part.buffer, part.byteOffset, 8).getBigUint64(0, false));
};
const readAscii = () => {
const len = readU8();
const part = read(len);
return new TextDecoder().decode(part);
};
const refToLogin = readAscii();
const refFromLogin = readAscii();
const refTimeMs = readU64();
const refNonce = readU32();
if (o !== payloadBytes.length) throw new Error('BAD_RECEIPT_LEN');
return { refToLogin, refFromLogin, refTimeMs, refNonce };
return parseReadReceiptBodyBytes(payloadBytes);
}
async decryptSignedMessageContent({ parsed, blobB64 = '', login, storagePwd }) {