chore: use hex/base64/utf from @fuman/utils
This commit is contained in:
parent
baef78403e
commit
66786064e3
77 changed files with 408 additions and 846 deletions
|
@ -12,7 +12,8 @@
|
||||||
"build": "pnpm run -w build-package convert"
|
"build": "pnpm run -w build-package convert"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/core": "workspace:^"
|
"@mtcute/core": "workspace:^",
|
||||||
|
"@fuman/utils": "workspace:^"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/test": "workspace:^"
|
"@mtcute/test": "workspace:^"
|
||||||
|
|
|
@ -75,11 +75,13 @@ export const DC_MAPPING_TEST: Record<number, DcOptions> = {
|
||||||
id: 1,
|
id: 1,
|
||||||
ipAddress: '149.154.175.10',
|
ipAddress: '149.154.175.10',
|
||||||
port: 80,
|
port: 80,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
media: {
|
media: {
|
||||||
id: 1,
|
id: 1,
|
||||||
ipAddress: '149.154.175.10',
|
ipAddress: '149.154.175.10',
|
||||||
port: 80,
|
port: 80,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
2: {
|
2: {
|
||||||
|
@ -87,11 +89,13 @@ export const DC_MAPPING_TEST: Record<number, DcOptions> = {
|
||||||
id: 2,
|
id: 2,
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
port: 443,
|
port: 443,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
media: {
|
media: {
|
||||||
id: 2,
|
id: 2,
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
port: 443,
|
port: 443,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
3: {
|
3: {
|
||||||
|
@ -99,11 +103,13 @@ export const DC_MAPPING_TEST: Record<number, DcOptions> = {
|
||||||
id: 3,
|
id: 3,
|
||||||
ipAddress: '149.154.175.117',
|
ipAddress: '149.154.175.117',
|
||||||
port: 443,
|
port: 443,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
media: {
|
media: {
|
||||||
id: 3,
|
id: 3,
|
||||||
ipAddress: '149.154.175.117',
|
ipAddress: '149.154.175.117',
|
||||||
port: 443,
|
port: 443,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { GRAMJS_SESSION } from './__fixtures__/session.js'
|
import { GRAMJS_SESSION } from './__fixtures__/session.js'
|
||||||
import { convertFromGramjsSession, convertToGramjsSession } from './convert.js'
|
import { convertFromGramjsSession, convertToGramjsSession } from './convert.js'
|
||||||
|
@ -7,7 +7,7 @@ import { convertFromGramjsSession, convertToGramjsSession } from './convert.js'
|
||||||
describe('gramjs/convert', () => {
|
describe('gramjs/convert', () => {
|
||||||
it('should correctly convert from gramjs sessions', () => {
|
it('should correctly convert from gramjs sessions', () => {
|
||||||
expect(convertFromGramjsSession(GRAMJS_SESSION)).toEqual({
|
expect(convertFromGramjsSession(GRAMJS_SESSION)).toEqual({
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'ad286dc1184bc61bfc8ed8942c1a2ef5bce1d5c25f6a069c1606fb3b8c722261'
|
'ad286dc1184bc61bfc8ed8942c1a2ef5bce1d5c25f6a069c1606fb3b8c722261'
|
||||||
+ '1cff7d73c649bf0c49807f3253542ba88f8687490ad0902e42e708a437eafe32'
|
+ '1cff7d73c649bf0c49807f3253542ba88f8687490ad0902e42e708a437eafe32'
|
||||||
+ '552d9d594629aae72cb55db784b3ae60b59035f925306515da861f8dcc66cf98'
|
+ '552d9d594629aae72cb55db784b3ae60b59035f925306515da861f8dcc66cf98'
|
||||||
|
@ -39,7 +39,7 @@ describe('gramjs/convert', () => {
|
||||||
it('should correctly convert to gramjs sessions', () => {
|
it('should correctly convert to gramjs sessions', () => {
|
||||||
expect(
|
expect(
|
||||||
convertToGramjsSession({
|
convertToGramjsSession({
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'ad286dc1184bc61bfc8ed8942c1a2ef5bce1d5c25f6a069c1606fb3b8c722261'
|
'ad286dc1184bc61bfc8ed8942c1a2ef5bce1d5c25f6a069c1606fb3b8c722261'
|
||||||
+ '1cff7d73c649bf0c49807f3253542ba88f8687490ad0902e42e708a437eafe32'
|
+ '1cff7d73c649bf0c49807f3253542ba88f8687490ad0902e42e708a437eafe32'
|
||||||
+ '552d9d594629aae72cb55db784b3ae60b59035f925306515da861f8dcc66cf98'
|
+ '552d9d594629aae72cb55db784b3ae60b59035f925306515da861f8dcc66cf98'
|
||||||
|
@ -55,15 +55,16 @@ describe('gramjs/convert', () => {
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
ipv6: false,
|
ipv6: false,
|
||||||
port: 443,
|
port: 443,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
media: {
|
media: {
|
||||||
id: 2,
|
id: 2,
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
ipv6: false,
|
ipv6: false,
|
||||||
port: 443,
|
port: 443,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
testMode: true,
|
|
||||||
version: 3,
|
version: 3,
|
||||||
}),
|
}),
|
||||||
).toEqual(GRAMJS_SESSION)
|
).toEqual(GRAMJS_SESSION)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { GRAMJS_SESSION } from './__fixtures__/session.js'
|
import { GRAMJS_SESSION } from './__fixtures__/session.js'
|
||||||
import { parseGramjsSession } from './parse.js'
|
import { parseGramjsSession } from './parse.js'
|
||||||
|
@ -11,7 +11,7 @@ describe('gramjs/parse', () => {
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
port: 443,
|
port: 443,
|
||||||
ipv6: false,
|
ipv6: false,
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'ad286dc1184bc61bfc8ed8942c1a2ef5bce1d5c25f6a069c1606fb3b8c722261'
|
'ad286dc1184bc61bfc8ed8942c1a2ef5bce1d5c25f6a069c1606fb3b8c722261'
|
||||||
+ '1cff7d73c649bf0c49807f3253542ba88f8687490ad0902e42e708a437eafe32'
|
+ '1cff7d73c649bf0c49807f3253542ba88f8687490ad0902e42e708a437eafe32'
|
||||||
+ '552d9d594629aae72cb55db784b3ae60b59035f925306515da861f8dcc66cf98'
|
+ '552d9d594629aae72cb55db784b3ae60b59035f925306515da861f8dcc66cf98'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { MtArgumentError } from '@mtcute/core'
|
import { MtArgumentError } from '@mtcute/core'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
|
||||||
import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
||||||
|
import { base64, utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import type { TelethonSession } from '../telethon/types.js'
|
import type { TelethonSession } from '../telethon/types.js'
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ export function parseGramjsSession(session: string): TelethonSession {
|
||||||
|
|
||||||
session = session.slice(1)
|
session = session.slice(1)
|
||||||
|
|
||||||
const data = getPlatform().base64Decode(session)
|
const data = base64.decode(session)
|
||||||
const dv = dataViewFromBuffer(data)
|
const dv = dataViewFromBuffer(data)
|
||||||
|
|
||||||
const dcId = dv.getUint8(0)
|
const dcId = dv.getUint8(0)
|
||||||
|
@ -20,7 +20,7 @@ export function parseGramjsSession(session: string): TelethonSession {
|
||||||
const ipSize = dv.getUint16(1)
|
const ipSize = dv.getUint16(1)
|
||||||
let pos = 3 + ipSize
|
let pos = 3 + ipSize
|
||||||
|
|
||||||
const ip = getPlatform().utf8Decode(data.subarray(3, pos))
|
const ip = utf8.decoder.decode(data.subarray(3, pos))
|
||||||
const port = dv.getUint16(pos)
|
const port = dv.getUint16(pos)
|
||||||
pos += 2
|
pos += 2
|
||||||
const authKey = data.subarray(pos, pos + 256)
|
const authKey = data.subarray(pos, pos + 256)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { GRAMJS_SESSION } from './__fixtures__/session.js'
|
import { GRAMJS_SESSION } from './__fixtures__/session.js'
|
||||||
import { serializeGramjsSession } from './serialize.js'
|
import { serializeGramjsSession } from './serialize.js'
|
||||||
|
@ -12,7 +12,7 @@ describe('gramjs/serialize', () => {
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
port: 443,
|
port: 443,
|
||||||
ipv6: false,
|
ipv6: false,
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'ad286dc1184bc61bfc8ed8942c1a2ef5bce1d5c25f6a069c1606fb3b8c722261'
|
'ad286dc1184bc61bfc8ed8942c1a2ef5bce1d5c25f6a069c1606fb3b8c722261'
|
||||||
+ '1cff7d73c649bf0c49807f3253542ba88f8687490ad0902e42e708a437eafe32'
|
+ '1cff7d73c649bf0c49807f3253542ba88f8687490ad0902e42e708a437eafe32'
|
||||||
+ '552d9d594629aae72cb55db784b3ae60b59035f925306515da861f8dcc66cf98'
|
+ '552d9d594629aae72cb55db784b3ae60b59035f925306515da861f8dcc66cf98'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { MtArgumentError } from '@mtcute/core'
|
import { MtArgumentError } from '@mtcute/core'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
|
||||||
import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
||||||
|
import { base64, utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import type { TelethonSession } from '../telethon/types.js'
|
import type { TelethonSession } from '../telethon/types.js'
|
||||||
|
|
||||||
|
@ -9,7 +9,7 @@ export function serializeGramjsSession(session: TelethonSession): string {
|
||||||
throw new MtArgumentError('authKey must be 256 bytes long')
|
throw new MtArgumentError('authKey must be 256 bytes long')
|
||||||
}
|
}
|
||||||
|
|
||||||
const ipEncoded = getPlatform().utf8Encode(session.ipAddress)
|
const ipEncoded = utf8.encoder.encode(session.ipAddress)
|
||||||
|
|
||||||
const u8 = new Uint8Array(261 + ipEncoded.length)
|
const u8 = new Uint8Array(261 + ipEncoded.length)
|
||||||
const dv = dataViewFromBuffer(u8)
|
const dv = dataViewFromBuffer(u8)
|
||||||
|
@ -24,5 +24,5 @@ export function serializeGramjsSession(session: TelethonSession): string {
|
||||||
pos += 2
|
pos += 2
|
||||||
u8.set(session.authKey, pos)
|
u8.set(session.authKey, pos)
|
||||||
|
|
||||||
return `1${getPlatform().base64Encode(u8)}`
|
return `1${base64.encode(u8)}`
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { u8HexDecode } from '@mtcute/test'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { MTKRUTO_SESSION } from './__fixtures__/session.js'
|
import { MTKRUTO_SESSION } from './__fixtures__/session.js'
|
||||||
import { convertFromMtkrutoSession, convertToMtkrutoSession } from './convert.js'
|
import { convertFromMtkrutoSession, convertToMtkrutoSession } from './convert.js'
|
||||||
|
@ -7,7 +7,7 @@ import { convertFromMtkrutoSession, convertToMtkrutoSession } from './convert.js
|
||||||
describe('mtkruto/convert', () => {
|
describe('mtkruto/convert', () => {
|
||||||
it('should correctly convert from mtkruto sessions', () => {
|
it('should correctly convert from mtkruto sessions', () => {
|
||||||
expect(convertFromMtkrutoSession(MTKRUTO_SESSION)).toEqual({
|
expect(convertFromMtkrutoSession(MTKRUTO_SESSION)).toEqual({
|
||||||
authKey: u8HexDecode(
|
authKey: hex.decode(
|
||||||
'58420a6b4ec287ef73a00d36e260cea6cbf6d135b8630ba845144ea928b8d584'
|
'58420a6b4ec287ef73a00d36e260cea6cbf6d135b8630ba845144ea928b8d584'
|
||||||
+ '026c3ddce272a7cfb05c148bb599f9fa7fa5e6dce4d5aa84f4ce26f8a7f02e64'
|
+ '026c3ddce272a7cfb05c148bb599f9fa7fa5e6dce4d5aa84f4ce26f8a7f02e64'
|
||||||
+ '3f47fadf23e406a079c460fa84a94259a3a2251acca412c67c56a2d1967f598f'
|
+ '3f47fadf23e406a079c460fa84a94259a3a2251acca412c67c56a2d1967f598f'
|
||||||
|
@ -37,7 +37,7 @@ describe('mtkruto/convert', () => {
|
||||||
it('should correctly convert to mtkruto sessions', () => {
|
it('should correctly convert to mtkruto sessions', () => {
|
||||||
expect(
|
expect(
|
||||||
convertToMtkrutoSession({
|
convertToMtkrutoSession({
|
||||||
authKey: u8HexDecode(
|
authKey: hex.decode(
|
||||||
'58420a6b4ec287ef73a00d36e260cea6cbf6d135b8630ba845144ea928b8d584'
|
'58420a6b4ec287ef73a00d36e260cea6cbf6d135b8630ba845144ea928b8d584'
|
||||||
+ '026c3ddce272a7cfb05c148bb599f9fa7fa5e6dce4d5aa84f4ce26f8a7f02e64'
|
+ '026c3ddce272a7cfb05c148bb599f9fa7fa5e6dce4d5aa84f4ce26f8a7f02e64'
|
||||||
+ '3f47fadf23e406a079c460fa84a94259a3a2251acca412c67c56a2d1967f598f'
|
+ '3f47fadf23e406a079c460fa84a94259a3a2251acca412c67c56a2d1967f598f'
|
||||||
|
@ -53,15 +53,16 @@ describe('mtkruto/convert', () => {
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
ipv6: false,
|
ipv6: false,
|
||||||
port: 443,
|
port: 443,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
media: {
|
media: {
|
||||||
id: 2,
|
id: 2,
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
ipv6: false,
|
ipv6: false,
|
||||||
port: 443,
|
port: 443,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
testMode: true,
|
|
||||||
version: 3,
|
version: 3,
|
||||||
}),
|
}),
|
||||||
).toEqual(MTKRUTO_SESSION)
|
).toEqual(MTKRUTO_SESSION)
|
||||||
|
|
|
@ -14,7 +14,6 @@ export function convertFromMtkrutoSession(session: MtkrutoSession | string): Str
|
||||||
|
|
||||||
return {
|
return {
|
||||||
version: 3,
|
version: 3,
|
||||||
testMode: session.isTest,
|
|
||||||
primaryDcs: (session.isTest ? DC_MAPPING_TEST : DC_MAPPING_PROD)[session.dcId],
|
primaryDcs: (session.isTest ? DC_MAPPING_TEST : DC_MAPPING_PROD)[session.dcId],
|
||||||
authKey: session.authKey,
|
authKey: session.authKey,
|
||||||
}
|
}
|
||||||
|
@ -27,7 +26,7 @@ export function convertToMtkrutoSession(session: StringSessionData | string): st
|
||||||
|
|
||||||
return serializeMtkrutoSession({
|
return serializeMtkrutoSession({
|
||||||
dcId: session.primaryDcs.main.id,
|
dcId: session.primaryDcs.main.id,
|
||||||
isTest: session.testMode,
|
isTest: session.primaryDcs.main.testMode ?? false,
|
||||||
authKey: session.authKey,
|
authKey: session.authKey,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { u8HexDecode } from '@mtcute/test'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { MTKRUTO_SESSION } from './__fixtures__/session.js'
|
import { MTKRUTO_SESSION } from './__fixtures__/session.js'
|
||||||
import { parseMtkrutoSession } from './parse.js'
|
import { parseMtkrutoSession } from './parse.js'
|
||||||
|
@ -9,7 +9,7 @@ describe('mtkruto/parse', () => {
|
||||||
expect(parseMtkrutoSession(MTKRUTO_SESSION)).toEqual({
|
expect(parseMtkrutoSession(MTKRUTO_SESSION)).toEqual({
|
||||||
dcId: 2,
|
dcId: 2,
|
||||||
isTest: true,
|
isTest: true,
|
||||||
authKey: u8HexDecode(
|
authKey: hex.decode(
|
||||||
'58420a6b4ec287ef73a00d36e260cea6cbf6d135b8630ba845144ea928b8d584'
|
'58420a6b4ec287ef73a00d36e260cea6cbf6d135b8630ba845144ea928b8d584'
|
||||||
+ '026c3ddce272a7cfb05c148bb599f9fa7fa5e6dce4d5aa84f4ce26f8a7f02e64'
|
+ '026c3ddce272a7cfb05c148bb599f9fa7fa5e6dce4d5aa84f4ce26f8a7f02e64'
|
||||||
+ '3f47fadf23e406a079c460fa84a94259a3a2251acca412c67c56a2d1967f598f'
|
+ '3f47fadf23e406a079c460fa84a94259a3a2251acca412c67c56a2d1967f598f'
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { MtArgumentError } from '@mtcute/core'
|
import { MtArgumentError } from '@mtcute/core'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
|
||||||
import { TlBinaryReader } from '@mtcute/core/utils.js'
|
import { TlBinaryReader } from '@mtcute/core/utils.js'
|
||||||
|
import { base64 } from '@fuman/utils'
|
||||||
|
|
||||||
import { telegramRleDecode } from '../utils/rle.js'
|
import { telegramRleDecode } from '../utils/rle.js'
|
||||||
|
|
||||||
import type { MtkrutoSession } from './types.js'
|
import type { MtkrutoSession } from './types.js'
|
||||||
|
|
||||||
export function parseMtkrutoSession(session: string): MtkrutoSession {
|
export function parseMtkrutoSession(session: string): MtkrutoSession {
|
||||||
const data = telegramRleDecode(getPlatform().base64Decode(session, true))
|
const data = telegramRleDecode(base64.decode(session, true))
|
||||||
const reader = TlBinaryReader.manual(data)
|
const reader = TlBinaryReader.manual(data)
|
||||||
|
|
||||||
let dcIdStr = reader.string()
|
let dcIdStr = reader.string()
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { u8HexDecode } from '@mtcute/test'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { MTKRUTO_SESSION } from './__fixtures__/session.js'
|
import { MTKRUTO_SESSION } from './__fixtures__/session.js'
|
||||||
import { serializeMtkrutoSession } from './serialize.js'
|
import { serializeMtkrutoSession } from './serialize.js'
|
||||||
|
@ -10,7 +10,7 @@ describe('mtkruto/serialize', () => {
|
||||||
serializeMtkrutoSession({
|
serializeMtkrutoSession({
|
||||||
dcId: 2,
|
dcId: 2,
|
||||||
isTest: true,
|
isTest: true,
|
||||||
authKey: u8HexDecode(
|
authKey: hex.decode(
|
||||||
'58420a6b4ec287ef73a00d36e260cea6cbf6d135b8630ba845144ea928b8d584'
|
'58420a6b4ec287ef73a00d36e260cea6cbf6d135b8630ba845144ea928b8d584'
|
||||||
+ '026c3ddce272a7cfb05c148bb599f9fa7fa5e6dce4d5aa84f4ce26f8a7f02e64'
|
+ '026c3ddce272a7cfb05c148bb599f9fa7fa5e6dce4d5aa84f4ce26f8a7f02e64'
|
||||||
+ '3f47fadf23e406a079c460fa84a94259a3a2251acca412c67c56a2d1967f598f'
|
+ '3f47fadf23e406a079c460fa84a94259a3a2251acca412c67c56a2d1967f598f'
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
|
||||||
import { TlBinaryWriter } from '@mtcute/core/utils.js'
|
import { TlBinaryWriter } from '@mtcute/core/utils.js'
|
||||||
|
import { base64 } from '@fuman/utils'
|
||||||
|
|
||||||
import { telegramRleEncode } from '../utils/rle.js'
|
import { telegramRleEncode } from '../utils/rle.js'
|
||||||
|
|
||||||
|
@ -13,5 +13,5 @@ export function serializeMtkrutoSession(session: MtkrutoSession): string {
|
||||||
writer.string(dcIdStr)
|
writer.string(dcIdStr)
|
||||||
writer.bytes(session.authKey)
|
writer.bytes(session.authKey)
|
||||||
|
|
||||||
return getPlatform().base64Encode(telegramRleEncode(writer.result()), true)
|
return base64.encode(telegramRleEncode(writer.result()), true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { PYROGRAM_TEST_SESSION_OLD } from './__fixtures__/session_old.js'
|
import { PYROGRAM_TEST_SESSION_OLD } from './__fixtures__/session_old.js'
|
||||||
import { convertFromPyrogramSession, convertToPyrogramSession } from './convert.js'
|
import { convertFromPyrogramSession, convertToPyrogramSession } from './convert.js'
|
||||||
|
@ -7,7 +7,7 @@ import { convertFromPyrogramSession, convertToPyrogramSession } from './convert.
|
||||||
describe('pyrogram/convert', () => {
|
describe('pyrogram/convert', () => {
|
||||||
it('should correctly convert from pyrogram sessions', () => {
|
it('should correctly convert from pyrogram sessions', () => {
|
||||||
expect(convertFromPyrogramSession(PYROGRAM_TEST_SESSION_OLD)).toEqual({
|
expect(convertFromPyrogramSession(PYROGRAM_TEST_SESSION_OLD)).toEqual({
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'1674732db80d690b4d5890d887a4bd5b0b4c810b7c331990b049158a940fdaeb'
|
'1674732db80d690b4d5890d887a4bd5b0b4c810b7c331990b049158a940fdaeb'
|
||||||
+ 'd46178f50ddcce753699f0497ad6de9655f454bc5a3030524036dee4ffe3db7c'
|
+ 'd46178f50ddcce753699f0497ad6de9655f454bc5a3030524036dee4ffe3db7c'
|
||||||
+ '73b526c378a184a0fafa14e9679c170b632e0b412c174f99e96b216214f78263'
|
+ '73b526c378a184a0fafa14e9679c170b632e0b412c174f99e96b216214f78263'
|
||||||
|
@ -43,7 +43,7 @@ describe('pyrogram/convert', () => {
|
||||||
it('should correctly convert to pyrogram sessions', () => {
|
it('should correctly convert to pyrogram sessions', () => {
|
||||||
expect(
|
expect(
|
||||||
convertToPyrogramSession({
|
convertToPyrogramSession({
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'1674732db80d690b4d5890d887a4bd5b0b4c810b7c331990b049158a940fdaeb'
|
'1674732db80d690b4d5890d887a4bd5b0b4c810b7c331990b049158a940fdaeb'
|
||||||
+ 'd46178f50ddcce753699f0497ad6de9655f454bc5a3030524036dee4ffe3db7c'
|
+ 'd46178f50ddcce753699f0497ad6de9655f454bc5a3030524036dee4ffe3db7c'
|
||||||
+ '73b526c378a184a0fafa14e9679c170b632e0b412c174f99e96b216214f78263'
|
+ '73b526c378a184a0fafa14e9679c170b632e0b412c174f99e96b216214f78263'
|
||||||
|
@ -58,11 +58,13 @@ describe('pyrogram/convert', () => {
|
||||||
id: 2,
|
id: 2,
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
port: 443,
|
port: 443,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
media: {
|
media: {
|
||||||
id: 2,
|
id: 2,
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
port: 443,
|
port: 443,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
self: {
|
self: {
|
||||||
|
@ -71,7 +73,6 @@ describe('pyrogram/convert', () => {
|
||||||
userId: 5000801609,
|
userId: 5000801609,
|
||||||
usernames: [],
|
usernames: [],
|
||||||
},
|
},
|
||||||
testMode: true,
|
|
||||||
version: 3,
|
version: 3,
|
||||||
}),
|
}),
|
||||||
).toEqual(PYROGRAM_TEST_SESSION_OLD)
|
).toEqual(PYROGRAM_TEST_SESSION_OLD)
|
||||||
|
|
|
@ -14,7 +14,6 @@ export function convertFromPyrogramSession(session: PyrogramSession | string): S
|
||||||
|
|
||||||
return {
|
return {
|
||||||
version: 3,
|
version: 3,
|
||||||
testMode: session.isTest,
|
|
||||||
primaryDcs: (session.isTest ? DC_MAPPING_TEST : DC_MAPPING_PROD)[session.dcId],
|
primaryDcs: (session.isTest ? DC_MAPPING_TEST : DC_MAPPING_PROD)[session.dcId],
|
||||||
authKey: session.authKey,
|
authKey: session.authKey,
|
||||||
self: {
|
self: {
|
||||||
|
@ -39,7 +38,7 @@ export function convertToPyrogramSession(
|
||||||
return serializePyrogramSession({
|
return serializePyrogramSession({
|
||||||
apiId: params?.apiId,
|
apiId: params?.apiId,
|
||||||
isBot: session.self?.isBot ?? false,
|
isBot: session.self?.isBot ?? false,
|
||||||
isTest: session.testMode,
|
isTest: session.primaryDcs.main.testMode ?? false,
|
||||||
userId: session.self?.userId ?? 0,
|
userId: session.self?.userId ?? 0,
|
||||||
dcId: session.primaryDcs.main.id,
|
dcId: session.primaryDcs.main.id,
|
||||||
authKey: session.authKey,
|
authKey: session.authKey,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { PYROGRAM_TEST_SESSION } from './__fixtures__/session.js'
|
import { PYROGRAM_TEST_SESSION } from './__fixtures__/session.js'
|
||||||
import { PYROGRAM_TEST_SESSION_OLD } from './__fixtures__/session_old.js'
|
import { PYROGRAM_TEST_SESSION_OLD } from './__fixtures__/session_old.js'
|
||||||
|
@ -12,7 +12,7 @@ describe('pyrogram/parse', () => {
|
||||||
isTest: true,
|
isTest: true,
|
||||||
userId: 5000801609,
|
userId: 5000801609,
|
||||||
dcId: 2,
|
dcId: 2,
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'1674732db80d690b4d5890d887a4bd5b0b4c810b7c331990b049158a940fdaeb'
|
'1674732db80d690b4d5890d887a4bd5b0b4c810b7c331990b049158a940fdaeb'
|
||||||
+ 'd46178f50ddcce753699f0497ad6de9655f454bc5a3030524036dee4ffe3db7c'
|
+ 'd46178f50ddcce753699f0497ad6de9655f454bc5a3030524036dee4ffe3db7c'
|
||||||
+ '73b526c378a184a0fafa14e9679c170b632e0b412c174f99e96b216214f78263'
|
+ '73b526c378a184a0fafa14e9679c170b632e0b412c174f99e96b216214f78263'
|
||||||
|
@ -32,7 +32,7 @@ describe('pyrogram/parse', () => {
|
||||||
isTest: true,
|
isTest: true,
|
||||||
userId: 5000801609,
|
userId: 5000801609,
|
||||||
dcId: 2,
|
dcId: 2,
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'4e4e8ab2caa290a3f1121c5f5c9a64a0522043fa3c5690a4ff8834b5c1ded2b5'
|
'4e4e8ab2caa290a3f1121c5f5c9a64a0522043fa3c5690a4ff8834b5c1ded2b5'
|
||||||
+ '425f1df801a0cabda34e95b909399e23037008f220d8908da8a6e89f6ccffb4b'
|
+ '425f1df801a0cabda34e95b909399e23037008f220d8908da8a6e89f6ccffb4b'
|
||||||
+ '6bbd30c767ae37e0e63a5f9177c8f7ec05f032bf5011887b5ce4fc86e7d081cb'
|
+ '6bbd30c767ae37e0e63a5f9177c8f7ec05f032bf5011887b5ce4fc86e7d081cb'
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// source: https://github.com/pyrogram/pyrogram/blob/master/pyrogram/storage/storage.py
|
// source: https://github.com/pyrogram/pyrogram/blob/master/pyrogram/storage/storage.py
|
||||||
|
|
||||||
import { Long } from '@mtcute/core'
|
import { Long } from '@mtcute/core'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
|
||||||
import { dataViewFromBuffer, longFromBuffer } from '@mtcute/core/utils.js'
|
import { dataViewFromBuffer, longFromBuffer } from '@mtcute/core/utils.js'
|
||||||
|
import { base64 } from '@fuman/utils'
|
||||||
|
|
||||||
import type { PyrogramSession } from './types.js'
|
import type { PyrogramSession } from './types.js'
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ const SESSION_STRING_SIZE = 351
|
||||||
const SESSION_STRING_SIZE_64 = 356
|
const SESSION_STRING_SIZE_64 = 356
|
||||||
|
|
||||||
export function parsePyrogramSession(session: string): PyrogramSession {
|
export function parsePyrogramSession(session: string): PyrogramSession {
|
||||||
const data = getPlatform().base64Decode(session, true)
|
const data = base64.decode(session, true)
|
||||||
const dv = dataViewFromBuffer(data)
|
const dv = dataViewFromBuffer(data)
|
||||||
|
|
||||||
if (session.length === SESSION_STRING_SIZE || session.length === SESSION_STRING_SIZE_64) {
|
if (session.length === SESSION_STRING_SIZE || session.length === SESSION_STRING_SIZE_64) {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { PYROGRAM_TEST_SESSION } from './__fixtures__/session.js'
|
import { PYROGRAM_TEST_SESSION } from './__fixtures__/session.js'
|
||||||
import { PYROGRAM_TEST_SESSION_OLD } from './__fixtures__/session_old.js'
|
import { PYROGRAM_TEST_SESSION_OLD } from './__fixtures__/session_old.js'
|
||||||
|
@ -13,7 +13,7 @@ describe('pyrogram/serialize', () => {
|
||||||
isTest: true,
|
isTest: true,
|
||||||
userId: 5000801609,
|
userId: 5000801609,
|
||||||
dcId: 2,
|
dcId: 2,
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'1674732db80d690b4d5890d887a4bd5b0b4c810b7c331990b049158a940fdaeb'
|
'1674732db80d690b4d5890d887a4bd5b0b4c810b7c331990b049158a940fdaeb'
|
||||||
+ 'd46178f50ddcce753699f0497ad6de9655f454bc5a3030524036dee4ffe3db7c'
|
+ 'd46178f50ddcce753699f0497ad6de9655f454bc5a3030524036dee4ffe3db7c'
|
||||||
+ '73b526c378a184a0fafa14e9679c170b632e0b412c174f99e96b216214f78263'
|
+ '73b526c378a184a0fafa14e9679c170b632e0b412c174f99e96b216214f78263'
|
||||||
|
@ -35,7 +35,7 @@ describe('pyrogram/serialize', () => {
|
||||||
isTest: true,
|
isTest: true,
|
||||||
userId: 5000801609,
|
userId: 5000801609,
|
||||||
dcId: 2,
|
dcId: 2,
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'4e4e8ab2caa290a3f1121c5f5c9a64a0522043fa3c5690a4ff8834b5c1ded2b5'
|
'4e4e8ab2caa290a3f1121c5f5c9a64a0522043fa3c5690a4ff8834b5c1ded2b5'
|
||||||
+ '425f1df801a0cabda34e95b909399e23037008f220d8908da8a6e89f6ccffb4b'
|
+ '425f1df801a0cabda34e95b909399e23037008f220d8908da8a6e89f6ccffb4b'
|
||||||
+ '6bbd30c767ae37e0e63a5f9177c8f7ec05f032bf5011887b5ce4fc86e7d081cb'
|
+ '6bbd30c767ae37e0e63a5f9177c8f7ec05f032bf5011887b5ce4fc86e7d081cb'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Long, MtArgumentError } from '@mtcute/core'
|
import { Long, MtArgumentError } from '@mtcute/core'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
|
||||||
import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
||||||
|
import { base64 } from '@fuman/utils'
|
||||||
|
|
||||||
import type { PyrogramSession } from './types.js'
|
import type { PyrogramSession } from './types.js'
|
||||||
|
|
||||||
|
@ -41,5 +41,5 @@ export function serializePyrogramSession(session: PyrogramSession): string {
|
||||||
dv.setUint8(270, session.isBot ? 1 : 0)
|
dv.setUint8(270, session.isBot ? 1 : 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
return getPlatform().base64Encode(u8, true)
|
return base64.encode(u8, true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { TELETHON_TEST_SESSION } from './__fixtures__/session.js'
|
import { TELETHON_TEST_SESSION } from './__fixtures__/session.js'
|
||||||
import { convertFromTelethonSession, convertToTelethonSession } from './convert.js'
|
import { convertFromTelethonSession, convertToTelethonSession } from './convert.js'
|
||||||
|
@ -7,7 +7,7 @@ import { convertFromTelethonSession, convertToTelethonSession } from './convert.
|
||||||
describe('telethon/convert', () => {
|
describe('telethon/convert', () => {
|
||||||
it('should correctly convert from telethon sessions', () => {
|
it('should correctly convert from telethon sessions', () => {
|
||||||
expect(convertFromTelethonSession(TELETHON_TEST_SESSION)).toEqual({
|
expect(convertFromTelethonSession(TELETHON_TEST_SESSION)).toEqual({
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'28494b5ff1c142b4d48b3870ebd06b524a4e7d4f39a6dd31409f2e65cd605532'
|
'28494b5ff1c142b4d48b3870ebd06b524a4e7d4f39a6dd31409f2e65cd605532'
|
||||||
+ 'bc6deff59fea6c5345a77cd83fefb7695a53608d83a41d886f8ea9fdbc120b48'
|
+ 'bc6deff59fea6c5345a77cd83fefb7695a53608d83a41d886f8ea9fdbc120b48'
|
||||||
+ 'f54048ef750c498f6e9c563f0d7ec96b0a462b755de094e85d7334aad3c929df'
|
+ 'f54048ef750c498f6e9c563f0d7ec96b0a462b755de094e85d7334aad3c929df'
|
||||||
|
@ -39,7 +39,7 @@ describe('telethon/convert', () => {
|
||||||
it('should correctly convert to telethon sessions', () => {
|
it('should correctly convert to telethon sessions', () => {
|
||||||
expect(
|
expect(
|
||||||
convertToTelethonSession({
|
convertToTelethonSession({
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'28494b5ff1c142b4d48b3870ebd06b524a4e7d4f39a6dd31409f2e65cd605532'
|
'28494b5ff1c142b4d48b3870ebd06b524a4e7d4f39a6dd31409f2e65cd605532'
|
||||||
+ 'bc6deff59fea6c5345a77cd83fefb7695a53608d83a41d886f8ea9fdbc120b48'
|
+ 'bc6deff59fea6c5345a77cd83fefb7695a53608d83a41d886f8ea9fdbc120b48'
|
||||||
+ 'f54048ef750c498f6e9c563f0d7ec96b0a462b755de094e85d7334aad3c929df'
|
+ 'f54048ef750c498f6e9c563f0d7ec96b0a462b755de094e85d7334aad3c929df'
|
||||||
|
@ -55,15 +55,16 @@ describe('telethon/convert', () => {
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
ipv6: false,
|
ipv6: false,
|
||||||
port: 80,
|
port: 80,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
media: {
|
media: {
|
||||||
id: 2,
|
id: 2,
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
ipv6: false,
|
ipv6: false,
|
||||||
port: 80,
|
port: 80,
|
||||||
|
testMode: true,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
testMode: true,
|
|
||||||
version: 3,
|
version: 3,
|
||||||
}),
|
}),
|
||||||
).toEqual(TELETHON_TEST_SESSION)
|
).toEqual(TELETHON_TEST_SESSION)
|
||||||
|
|
|
@ -17,13 +17,13 @@ export function convertFromTelethonSession(session: TelethonSession | string): S
|
||||||
ipAddress: session.ipAddress,
|
ipAddress: session.ipAddress,
|
||||||
port: session.port,
|
port: session.port,
|
||||||
ipv6: session.ipv6,
|
ipv6: session.ipv6,
|
||||||
|
// we don't exactly have that information. try to deduce it from DC_MAPPING_TEST
|
||||||
|
// todo: we should maybe check this at connect?
|
||||||
|
testMode: isTestDc(session.ipAddress),
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
version: 3,
|
version: 3,
|
||||||
// we don't exactly have that information. try to deduce it from DC_MAPPING_TEST
|
|
||||||
// todo: we should maybe check this at connect?
|
|
||||||
testMode: isTestDc(session.ipAddress),
|
|
||||||
primaryDcs: {
|
primaryDcs: {
|
||||||
main: dc,
|
main: dc,
|
||||||
media: dc,
|
media: dc,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { TELETHON_TEST_SESSION } from './__fixtures__/session.js'
|
import { TELETHON_TEST_SESSION } from './__fixtures__/session.js'
|
||||||
import { TELETHON_TEST_SESSION_V6 } from './__fixtures__/session_v6.js'
|
import { TELETHON_TEST_SESSION_V6 } from './__fixtures__/session_v6.js'
|
||||||
|
@ -12,7 +12,7 @@ describe('telethon/parse', () => {
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
port: 80,
|
port: 80,
|
||||||
ipv6: false,
|
ipv6: false,
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'28494b5ff1c142b4d48b3870ebd06b524a4e7d4f39a6dd31409f2e65cd605532'
|
'28494b5ff1c142b4d48b3870ebd06b524a4e7d4f39a6dd31409f2e65cd605532'
|
||||||
+ 'bc6deff59fea6c5345a77cd83fefb7695a53608d83a41d886f8ea9fdbc120b48'
|
+ 'bc6deff59fea6c5345a77cd83fefb7695a53608d83a41d886f8ea9fdbc120b48'
|
||||||
+ 'f54048ef750c498f6e9c563f0d7ec96b0a462b755de094e85d7334aad3c929df'
|
+ 'f54048ef750c498f6e9c563f0d7ec96b0a462b755de094e85d7334aad3c929df'
|
||||||
|
@ -31,7 +31,7 @@ describe('telethon/parse', () => {
|
||||||
ipAddress: '2001:0b28:f23d:f001:0000:0000:0000:000e',
|
ipAddress: '2001:0b28:f23d:f001:0000:0000:0000:000e',
|
||||||
port: 443,
|
port: 443,
|
||||||
ipv6: true,
|
ipv6: true,
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'8a6f780156484e75fedacab2b45078cbc65cc97c7c8e8db06696a9dad75deab2'
|
'8a6f780156484e75fedacab2b45078cbc65cc97c7c8e8db06696a9dad75deab2'
|
||||||
+ '6979def6a36d86a9eb0661f9ea41df3a115408f4a857334dac682742bebb0184'
|
+ '6979def6a36d86a9eb0661f9ea41df3a115408f4a857334dac682742bebb0184'
|
||||||
+ '1b921a4ffd89a5d840ddf1ea5d73a1b2c21e2ad8d0606325ba5414fc50a83cf7'
|
+ '1b921a4ffd89a5d840ddf1ea5d73a1b2c21e2ad8d0606325ba5414fc50a83cf7'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { MtArgumentError } from '@mtcute/core'
|
import { MtArgumentError } from '@mtcute/core'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
|
||||||
import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
||||||
|
import { base64 } from '@fuman/utils'
|
||||||
|
|
||||||
import { parseIpFromBytes } from '../utils/ip.js'
|
import { parseIpFromBytes } from '../utils/ip.js'
|
||||||
|
|
||||||
|
@ -14,7 +14,7 @@ export function parseTelethonSession(session: string): TelethonSession {
|
||||||
|
|
||||||
session = session.slice(1)
|
session = session.slice(1)
|
||||||
|
|
||||||
const data = getPlatform().base64Decode(session, true)
|
const data = base64.decode(session, true)
|
||||||
const dv = dataViewFromBuffer(data)
|
const dv = dataViewFromBuffer(data)
|
||||||
|
|
||||||
const dcId = dv.getUint8(0)
|
const dcId = dv.getUint8(0)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { TELETHON_TEST_SESSION } from './__fixtures__/session.js'
|
import { TELETHON_TEST_SESSION } from './__fixtures__/session.js'
|
||||||
import { TELETHON_TEST_SESSION_V6 } from './__fixtures__/session_v6.js'
|
import { TELETHON_TEST_SESSION_V6 } from './__fixtures__/session_v6.js'
|
||||||
|
@ -13,7 +13,7 @@ describe('telethon/serialize', () => {
|
||||||
ipAddress: '149.154.167.40',
|
ipAddress: '149.154.167.40',
|
||||||
port: 80,
|
port: 80,
|
||||||
ipv6: false,
|
ipv6: false,
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'28494b5ff1c142b4d48b3870ebd06b524a4e7d4f39a6dd31409f2e65cd605532'
|
'28494b5ff1c142b4d48b3870ebd06b524a4e7d4f39a6dd31409f2e65cd605532'
|
||||||
+ 'bc6deff59fea6c5345a77cd83fefb7695a53608d83a41d886f8ea9fdbc120b48'
|
+ 'bc6deff59fea6c5345a77cd83fefb7695a53608d83a41d886f8ea9fdbc120b48'
|
||||||
+ 'f54048ef750c498f6e9c563f0d7ec96b0a462b755de094e85d7334aad3c929df'
|
+ 'f54048ef750c498f6e9c563f0d7ec96b0a462b755de094e85d7334aad3c929df'
|
||||||
|
@ -34,7 +34,7 @@ describe('telethon/serialize', () => {
|
||||||
ipAddress: '2001:0b28:f23d:f001:0000:0000:0000:000e',
|
ipAddress: '2001:0b28:f23d:f001:0000:0000:0000:000e',
|
||||||
port: 443,
|
port: 443,
|
||||||
ipv6: true,
|
ipv6: true,
|
||||||
authKey: getPlatform().hexDecode(
|
authKey: hex.decode(
|
||||||
'8a6f780156484e75fedacab2b45078cbc65cc97c7c8e8db06696a9dad75deab2'
|
'8a6f780156484e75fedacab2b45078cbc65cc97c7c8e8db06696a9dad75deab2'
|
||||||
+ '6979def6a36d86a9eb0661f9ea41df3a115408f4a857334dac682742bebb0184'
|
+ '6979def6a36d86a9eb0661f9ea41df3a115408f4a857334dac682742bebb0184'
|
||||||
+ '1b921a4ffd89a5d840ddf1ea5d73a1b2c21e2ad8d0606325ba5414fc50a83cf7'
|
+ '1b921a4ffd89a5d840ddf1ea5d73a1b2c21e2ad8d0606325ba5414fc50a83cf7'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { MtArgumentError } from '@mtcute/core'
|
import { MtArgumentError } from '@mtcute/core'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
|
||||||
import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
||||||
|
import { base64 } from '@fuman/utils'
|
||||||
|
|
||||||
import { serializeIpv4ToBytes, serializeIpv6ToBytes } from '../utils/ip.js'
|
import { serializeIpv4ToBytes, serializeIpv6ToBytes } from '../utils/ip.js'
|
||||||
|
|
||||||
|
@ -31,7 +31,7 @@ export function serializeTelethonSession(session: TelethonSession): string {
|
||||||
pos += 2
|
pos += 2
|
||||||
u8.set(session.authKey, pos)
|
u8.set(session.authKey, pos)
|
||||||
|
|
||||||
let b64 = getPlatform().base64Encode(u8, true)
|
let b64 = base64.encode(u8, true)
|
||||||
while (b64.length % 4 !== 0) b64 += '=' // for some reason telethon uses padding
|
while (b64.length % 4 !== 0) b64 += '=' // for some reason telethon uses padding
|
||||||
|
|
||||||
return `1${b64}`
|
return `1${b64}`
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { MtArgumentError } from '@mtcute/core'
|
import { MtArgumentError } from '@mtcute/core'
|
||||||
|
|
||||||
|
// todo: use @fuman/ip
|
||||||
export function parseIpFromBytes(data: Uint8Array): string {
|
export function parseIpFromBytes(data: Uint8Array): string {
|
||||||
if (data.length === 4) {
|
if (data.length === 4) {
|
||||||
return `${data[0]}.${data[1]}.${data[2]}.${data[3]}`
|
return `${data[0]}.${data[1]}.${data[2]}.${data[3]}`
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
|
import { utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { getPlatform } from '../../../platform.js'
|
|
||||||
import type { ITelegramClient } from '../../client.types.js'
|
import type { ITelegramClient } from '../../client.types.js'
|
||||||
import type { InputMessageId } from '../../types/index.js'
|
import type { InputMessageId } from '../../types/index.js'
|
||||||
import { normalizeInputMessageId } from '../../types/index.js'
|
import { normalizeInputMessageId } from '../../types/index.js'
|
||||||
|
@ -66,7 +66,7 @@ export async function getCallbackAnswer(
|
||||||
_: 'messages.getBotCallbackAnswer',
|
_: 'messages.getBotCallbackAnswer',
|
||||||
peer: await resolvePeer(client, chatId),
|
peer: await resolvePeer(client, chatId),
|
||||||
msgId: message,
|
msgId: message,
|
||||||
data: typeof data === 'string' ? getPlatform().utf8Encode(data) : data,
|
data: typeof data === 'string' ? utf8.encoder.encode(data) : data,
|
||||||
password,
|
password,
|
||||||
game,
|
game,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
import { utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { getPlatform } from '../../../../platform.js'
|
|
||||||
import { assertNever } from '../../../../types/utils.js'
|
import { assertNever } from '../../../../types/utils.js'
|
||||||
import { toInputUser } from '../../../utils/peer-utils.js'
|
import { toInputUser } from '../../../utils/peer-utils.js'
|
||||||
|
|
||||||
|
@ -169,7 +169,7 @@ export function callback(
|
||||||
_: 'keyboardButtonCallback',
|
_: 'keyboardButtonCallback',
|
||||||
text,
|
text,
|
||||||
requiresPassword,
|
requiresPassword,
|
||||||
data: typeof data === 'string' ? getPlatform().utf8Encode(data) : data,
|
data: typeof data === 'string' ? utf8.encoder.encode(data) : data,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
|
import { utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { getPlatform } from '../../../platform.js'
|
|
||||||
import { MtArgumentError } from '../../../types/errors.js'
|
import { MtArgumentError } from '../../../types/errors.js'
|
||||||
import { makeInspectable } from '../../utils/index.js'
|
import { makeInspectable } from '../../utils/index.js'
|
||||||
import { encodeInlineMessageId } from '../../utils/inline-utils.js'
|
import { encodeInlineMessageId } from '../../utils/inline-utils.js'
|
||||||
|
@ -62,7 +62,7 @@ class BaseCallbackQuery {
|
||||||
get dataStr(): string | null {
|
get dataStr(): string | null {
|
||||||
if (!this.raw.data) return null
|
if (!this.raw.data) return null
|
||||||
|
|
||||||
return getPlatform().utf8Decode(this.raw.data)
|
return utf8.decoder.decode(this.raw.data)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,11 +1,8 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { hex } from '@fuman/utils'
|
||||||
import { getPlatform } from '../../platform.js'
|
|
||||||
|
|
||||||
import { MIME_TO_EXTENSION, guessFileMime } from './file-type.js'
|
import { MIME_TO_EXTENSION, guessFileMime } from './file-type.js'
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
describe('guessFileMime', () => {
|
describe('guessFileMime', () => {
|
||||||
it.each([
|
it.each([
|
||||||
['424d', 'image/bmp', 'bmp'],
|
['424d', 'image/bmp', 'bmp'],
|
||||||
|
@ -62,7 +59,7 @@ describe('guessFileMime', () => {
|
||||||
])('should detect %s as %s with %s extension', (header, mime, ext) => {
|
])('should detect %s as %s with %s extension', (header, mime, ext) => {
|
||||||
header += '00'.repeat(16)
|
header += '00'.repeat(16)
|
||||||
|
|
||||||
expect(guessFileMime(p.hexDecode(header))).toEqual(mime)
|
expect(guessFileMime(hex.decode(header))).toEqual(mime)
|
||||||
expect(MIME_TO_EXTENSION[mime]).toEqual(ext)
|
expect(MIME_TO_EXTENSION[mime]).toEqual(ext)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { hex, utf8 } from '@fuman/utils'
|
||||||
import { getPlatform } from '../../platform.js'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
extractFileName,
|
extractFileName,
|
||||||
|
@ -10,24 +9,22 @@ import {
|
||||||
svgPathToFile,
|
svgPathToFile,
|
||||||
} from './file-utils.js'
|
} from './file-utils.js'
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
describe('isProbablyPlainText', () => {
|
describe('isProbablyPlainText', () => {
|
||||||
it('should return true for buffers only containing printable ascii', () => {
|
it('should return true for buffers only containing printable ascii', () => {
|
||||||
expect(isProbablyPlainText(p.utf8Encode('hello this is some ascii text'))).toEqual(true)
|
expect(isProbablyPlainText(utf8.encoder.encode('hello this is some ascii text'))).toEqual(true)
|
||||||
expect(isProbablyPlainText(p.utf8Encode('hello this is some ascii text\nwith unix new lines'))).toEqual(true)
|
expect(isProbablyPlainText(utf8.encoder.encode('hello this is some ascii text\nwith unix new lines'))).toEqual(true)
|
||||||
expect(isProbablyPlainText(p.utf8Encode('hello this is some ascii text\r\nwith windows new lines'))).toEqual(true)
|
expect(isProbablyPlainText(utf8.encoder.encode('hello this is some ascii text\r\nwith windows new lines'))).toEqual(true)
|
||||||
expect(isProbablyPlainText(p.utf8Encode('hello this is some ascii text\n\twith unix new lines and tabs'))).toEqual(true)
|
expect(isProbablyPlainText(utf8.encoder.encode('hello this is some ascii text\n\twith unix new lines and tabs'))).toEqual(true)
|
||||||
expect(isProbablyPlainText(p.utf8Encode('hello this is some ascii text\r\n\twith windows new lines and tabs'))).toEqual(true)
|
expect(isProbablyPlainText(utf8.encoder.encode('hello this is some ascii text\r\n\twith windows new lines and tabs'))).toEqual(true)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return false for buffers containing some binary data', () => {
|
it('should return false for buffers containing some binary data', () => {
|
||||||
expect(isProbablyPlainText(p.utf8Encode('hello this is cedilla: ç'))).toEqual(false)
|
expect(isProbablyPlainText(utf8.encoder.encode('hello this is cedilla: ç'))).toEqual(false)
|
||||||
expect(isProbablyPlainText(p.utf8Encode('hello this is some ascii text with emojis 🌸'))).toEqual(false)
|
expect(isProbablyPlainText(utf8.encoder.encode('hello this is some ascii text with emojis 🌸'))).toEqual(false)
|
||||||
|
|
||||||
// random strings of 16 bytes
|
// random strings of 16 bytes
|
||||||
expect(isProbablyPlainText(p.hexDecode('717f80f08eb9d88c3931712c0e2be32f'))).toEqual(false)
|
expect(isProbablyPlainText(hex.decode('717f80f08eb9d88c3931712c0e2be32f'))).toEqual(false)
|
||||||
expect(isProbablyPlainText(p.hexDecode('20e8e218e54254c813b261432b0330d7'))).toEqual(false)
|
expect(isProbablyPlainText(hex.decode('20e8e218e54254c813b261432b0330d7'))).toEqual(false)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -49,14 +46,14 @@ describe('svgPathToFile', () => {
|
||||||
it('should convert SVG path to a file', () => {
|
it('should convert SVG path to a file', () => {
|
||||||
const path = 'M 0 0 L 100 0 L 100 100 L 0 100 L 0 0 Z'
|
const path = 'M 0 0 L 100 0 L 100 100 L 0 100 L 0 0 Z'
|
||||||
|
|
||||||
expect(p.utf8Decode(svgPathToFile(path))).toMatchInlineSnapshot(
|
expect(utf8.decoder.decode(svgPathToFile(path))).toMatchInlineSnapshot(
|
||||||
'"<?xml version="1.0" encoding="utf-8"?><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"viewBox="0 0 512 512" xml:space="preserve"><path d="M 0 0 L 100 0 L 100 100 L 0 100 L 0 0 Z"/></svg>"',
|
'"<?xml version="1.0" encoding="utf-8"?><svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"viewBox="0 0 512 512" xml:space="preserve"><path d="M 0 0 L 100 0 L 100 100 L 0 100 L 0 0 Z"/></svg>"',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('inflateSvgPath', () => {
|
describe('inflateSvgPath', () => {
|
||||||
const data = p.hexDecode(
|
const data = hex.decode(
|
||||||
'1a05b302dc5f4446068649064247424a6a4c704550535b5e665e5e4c044a024c'
|
'1a05b302dc5f4446068649064247424a6a4c704550535b5e665e5e4c044a024c'
|
||||||
+ '074e06414d80588863935fad74be4704854684518b528581904695498b488b56'
|
+ '074e06414d80588863935fad74be4704854684518b528581904695498b488b56'
|
||||||
+ '965c85438d8191818543894a8f4d834188818a4284498454895d9a6f86074708'
|
+ '965c85438d8191818543894a8f4d834188818a4284498454895d9a6f86074708'
|
||||||
|
@ -81,9 +78,9 @@ describe('inflateSvgPath', () => {
|
||||||
|
|
||||||
describe('strippedPhotoToJpg', () => {
|
describe('strippedPhotoToJpg', () => {
|
||||||
// strippedThumb of @Channel_Bot
|
// strippedThumb of @Channel_Bot
|
||||||
const dataPfp = p.hexDecode('010808b1f2f95fed673451457033ad1f')
|
const dataPfp = hex.decode('010808b1f2f95fed673451457033ad1f')
|
||||||
// photoStrippedSize of a random image
|
// photoStrippedSize of a random image
|
||||||
const dataPicture = p.hexDecode(
|
const dataPicture = hex.decode(
|
||||||
'012728b532aacce4b302d8c1099c74a634718675cb6381f73d3ffd557667d9b5'
|
'012728b532aacce4b302d8c1099c74a634718675cb6381f73d3ffd557667d9b5'
|
||||||
+ '816f4c28ce69aa58a863238cf62a334590f999042234cbe1986d03eefe14c68e'
|
+ '816f4c28ce69aa58a863238cf62a334590f999042234cbe1986d03eefe14c68e'
|
||||||
+ '32847cc00ce709ea7ffad577773f78fe54d6c927f78c3db14ac1ccca91a2ef4f'
|
+ '32847cc00ce709ea7ffad577773f78fe54d6c927f78c3db14ac1ccca91a2ef4f'
|
||||||
|
@ -95,7 +92,7 @@ describe('strippedPhotoToJpg', () => {
|
||||||
)
|
)
|
||||||
|
|
||||||
it('should inflate stripped jpeg (from profile picture)', () => {
|
it('should inflate stripped jpeg (from profile picture)', () => {
|
||||||
expect(p.hexEncode(strippedPhotoToJpg(dataPfp))).toMatchInlineSnapshot(
|
expect(hex.encode(strippedPhotoToJpg(dataPfp))).toMatchInlineSnapshot(
|
||||||
'"ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e192'
|
'"ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e192'
|
||||||
+ '82321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a'
|
+ '82321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a'
|
||||||
+ '0aadaad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2'
|
+ '0aadaad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2'
|
||||||
|
@ -120,7 +117,7 @@ describe('strippedPhotoToJpg', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should inflate stripped jpeg (from a picture)', () => {
|
it('should inflate stripped jpeg (from a picture)', () => {
|
||||||
expect(p.hexEncode(strippedPhotoToJpg(dataPicture))).toMatchInlineSnapshot(
|
expect(hex.encode(strippedPhotoToJpg(dataPicture))).toMatchInlineSnapshot(
|
||||||
'"ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e192'
|
'"ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e192'
|
||||||
+ '82321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a'
|
+ '82321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a'
|
||||||
+ '0aadaad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2'
|
+ '0aadaad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2'
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
|
import { hex, utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { getPlatform } from '../../platform.js'
|
|
||||||
import { MtArgumentError } from '../../types/errors.js'
|
import { MtArgumentError } from '../../types/errors.js'
|
||||||
import { concatBuffers } from '../../utils/buffer-utils.js'
|
import { concatBuffers } from '../../utils/buffer-utils.js'
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ export function isProbablyPlainText(buf: Uint8Array): boolean {
|
||||||
|
|
||||||
// from https://github.com/telegramdesktop/tdesktop/blob/bec39d89e19670eb436dc794a8f20b657cb87c71/Telegram/SourceFiles/ui/image/image.cpp#L225
|
// from https://github.com/telegramdesktop/tdesktop/blob/bec39d89e19670eb436dc794a8f20b657cb87c71/Telegram/SourceFiles/ui/image/image.cpp#L225
|
||||||
function JPEG_HEADER() {
|
function JPEG_HEADER() {
|
||||||
return getPlatform().hexDecode(
|
return hex.decode(
|
||||||
'ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e1928'
|
'ffd8ffe000104a46494600010100000100010000ffdb004300281c1e231e1928'
|
||||||
+ '2321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a0aad'
|
+ '2321232d2b28303c64413c37373c7b585d4964918099968f808c8aa0b4e6c3a0aad'
|
||||||
+ 'aad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2d2d3c35'
|
+ 'aad8a8cc8ffcbdaeef5ffffff9bc1fffffffaffe6fdfff8ffdb0043012b2d2d3c35'
|
||||||
|
@ -117,7 +117,7 @@ export function inflateSvgPath(encoded: Uint8Array): string {
|
||||||
* @param size Size attribute of the document, if available
|
* @param size Size attribute of the document, if available
|
||||||
*/
|
*/
|
||||||
export function svgPathToFile(path: string, size?: tl.RawDocumentAttributeImageSize): Uint8Array {
|
export function svgPathToFile(path: string, size?: tl.RawDocumentAttributeImageSize): Uint8Array {
|
||||||
return getPlatform().utf8Encode(
|
return utf8.encoder.encode(
|
||||||
'<?xml version="1.0" encoding="utf-8"?>'
|
'<?xml version="1.0" encoding="utf-8"?>'
|
||||||
+ '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"'
|
+ '<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"'
|
||||||
+ `viewBox="0 0 ${size?.w ?? 512} ${size?.h ?? 512}" xml:space="preserve">`
|
+ `viewBox="0 0 ${size?.w ?? 512} ${size?.h ?? 512}" xml:space="preserve">`
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { hex } from '@fuman/utils'
|
||||||
import { getPlatform } from '../../platform.js'
|
|
||||||
|
|
||||||
import { decodeWaveform, encodeWaveform } from './voice-utils.js'
|
import { decodeWaveform, encodeWaveform } from './voice-utils.js'
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
describe('decodeWaveform', () => {
|
describe('decodeWaveform', () => {
|
||||||
it('should correctly decode telegram-encoded waveform', () => {
|
it('should correctly decode telegram-encoded waveform', () => {
|
||||||
expect(
|
expect(
|
||||||
decodeWaveform(
|
decodeWaveform(
|
||||||
p.hexDecode(
|
hex.decode(
|
||||||
'0000104210428c310821a51463cc39072184524a4aa9b51663acb5e69c7bef41'
|
'0000104210428c310821a51463cc39072184524a4aa9b51663acb5e69c7bef41'
|
||||||
+ '08618c514a39e7a494d65aadb5f75e8c31ce396badf7de9cf3debbf7feff0f',
|
+ '08618c514a39e7a494d65aadb5f75e8c31ce396badf7de9cf3debbf7feff0f',
|
||||||
),
|
),
|
||||||
|
@ -123,7 +120,7 @@ describe('decodeWaveform', () => {
|
||||||
describe('encodeWaveform', () => {
|
describe('encodeWaveform', () => {
|
||||||
it('should correctly decode telegram-encoded waveform', () => {
|
it('should correctly decode telegram-encoded waveform', () => {
|
||||||
expect(
|
expect(
|
||||||
p.hexEncode(
|
hex.encode(
|
||||||
encodeWaveform([
|
encodeWaveform([
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
|
|
|
@ -1,21 +1,17 @@
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
import { describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
import { defaultTestCryptoProvider } from '@mtcute/test'
|
import { defaultTestCryptoProvider } from '@mtcute/test'
|
||||||
import type {
|
import type { TlBinaryReader, TlReaderMap } from '@mtcute/tl-runtime'
|
||||||
TlBinaryReader,
|
import { hex, utf8 } from '@fuman/utils'
|
||||||
TlReaderMap,
|
|
||||||
} from '@mtcute/tl-runtime'
|
|
||||||
|
|
||||||
import { getPlatform } from '../platform.js'
|
|
||||||
import { LogManager } from '../utils/index.js'
|
import { LogManager } from '../utils/index.js'
|
||||||
|
|
||||||
import { AuthKey } from './auth-key.js'
|
import { AuthKey } from './auth-key.js'
|
||||||
|
|
||||||
const authKey = new Uint8Array(256)
|
const authKey = new Uint8Array(256)
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
for (let i = 0; i < 256; i += 32) {
|
for (let i = 0; i < 256; i += 32) {
|
||||||
authKey.subarray(i, i + 32).set(p.hexDecode('98cb29c6ffa89e79da695a54f572e6cb101e81c688b63a4bf73c3622dec230e0'))
|
authKey.subarray(i, i + 32).set(hex.decode('98cb29c6ffa89e79da695a54f572e6cb101e81c688b63a4bf73c3622dec230e0'))
|
||||||
}
|
}
|
||||||
|
|
||||||
describe('AuthKey', () => {
|
describe('AuthKey', () => {
|
||||||
|
@ -51,19 +47,19 @@ describe('AuthKey', () => {
|
||||||
it('should calculate derivatives', async () => {
|
it('should calculate derivatives', async () => {
|
||||||
const key = await create()
|
const key = await create()
|
||||||
|
|
||||||
expect(p.hexEncode(key.key)).toEqual(p.hexEncode(authKey))
|
expect(hex.encode(key.key)).toEqual(hex.encode(authKey))
|
||||||
expect(p.hexEncode(key.clientSalt)).toEqual('f73c3622dec230e098cb29c6ffa89e79da695a54f572e6cb101e81c688b63a4b')
|
expect(hex.encode(key.clientSalt)).toEqual('f73c3622dec230e098cb29c6ffa89e79da695a54f572e6cb101e81c688b63a4b')
|
||||||
expect(p.hexEncode(key.serverSalt)).toEqual('98cb29c6ffa89e79da695a54f572e6cb101e81c688b63a4bf73c3622dec230e0')
|
expect(hex.encode(key.serverSalt)).toEqual('98cb29c6ffa89e79da695a54f572e6cb101e81c688b63a4bf73c3622dec230e0')
|
||||||
expect(p.hexEncode(key.id)).toEqual('40fa5bb7cb56a895')
|
expect(hex.encode(key.id)).toEqual('40fa5bb7cb56a895')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should encrypt a message', async () => {
|
it('should encrypt a message', async () => {
|
||||||
const message = writeMessage(p.utf8Encode('hello, world!!!!'))
|
const message = writeMessage(utf8.encoder.encode('hello, world!!!!'))
|
||||||
|
|
||||||
const key = await create()
|
const key = await create()
|
||||||
const msg = key.encryptMessage(message, serverSalt, sessionId)
|
const msg = key.encryptMessage(message, serverSalt, sessionId)
|
||||||
|
|
||||||
expect(p.hexEncode(msg)).toEqual(
|
expect(hex.encode(msg)).toEqual(
|
||||||
'40fa5bb7cb56a895f6f5a88914892aadf87c68031cc953ba29d68e118021f329'
|
'40fa5bb7cb56a895f6f5a88914892aadf87c68031cc953ba29d68e118021f329'
|
||||||
+ 'be386a620d49f3ad3a50c60dcef3733f214e8cefa3e403c11d193637d4971dc1'
|
+ 'be386a620d49f3ad3a50c60dcef3733f214e8cefa3e403c11d193637d4971dc1'
|
||||||
+ '5db7f74b26fd16cb0e8fee30bf7e3f68858fe82927e2cd06',
|
+ '5db7f74b26fd16cb0e8fee30bf7e3f68858fe82927e2cd06',
|
||||||
|
@ -86,7 +82,7 @@ describe('AuthKey', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should decrypt a message', async () => {
|
it('should decrypt a message', async () => {
|
||||||
const message = p.hexDecode(
|
const message = hex.decode(
|
||||||
'40fa5bb7cb56a8950c394b884f1529efc42fea22d972fea650a714ce6d2d1bdb'
|
'40fa5bb7cb56a8950c394b884f1529efc42fea22d972fea650a714ce6d2d1bdb'
|
||||||
+ '3d98ff5929b8768c401771a69795f36a7e720dcafac2efbccd0ba368e8a7f48b'
|
+ '3d98ff5929b8768c401771a69795f36a7e720dcafac2efbccd0ba368e8a7f48b'
|
||||||
+ '07362cac1a32ffcabe188b51a36cc4d54e1d0633cf9eaf35',
|
+ '07362cac1a32ffcabe188b51a36cc4d54e1d0633cf9eaf35',
|
||||||
|
@ -96,11 +92,11 @@ describe('AuthKey', () => {
|
||||||
|
|
||||||
expect(decMsgId).toEqual(msgId)
|
expect(decMsgId).toEqual(msgId)
|
||||||
expect(decSeqNo).toEqual(seqNo)
|
expect(decSeqNo).toEqual(seqNo)
|
||||||
expect(p.utf8Decode(data.raw(16))).toEqual('hello, world!!!!')
|
expect(utf8.decoder.decode(data.raw(16))).toEqual('hello, world!!!!')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should decrypt a message with padding', async () => {
|
it('should decrypt a message with padding', async () => {
|
||||||
const message = p.hexDecode(
|
const message = hex.decode(
|
||||||
'40fa5bb7cb56a8950c394b884f1529efc42fea22d972fea650a714ce6d2d1bdb'
|
'40fa5bb7cb56a8950c394b884f1529efc42fea22d972fea650a714ce6d2d1bdb'
|
||||||
+ '3d98ff5929b8768c401771a69795f36a7e720dcafac2efbccd0ba368e8a7f48b'
|
+ '3d98ff5929b8768c401771a69795f36a7e720dcafac2efbccd0ba368e8a7f48b'
|
||||||
+ '07362cac1a32ffcabe188b51a36cc4d54e1d0633cf9eaf35'
|
+ '07362cac1a32ffcabe188b51a36cc4d54e1d0633cf9eaf35'
|
||||||
|
@ -111,11 +107,11 @@ describe('AuthKey', () => {
|
||||||
|
|
||||||
expect(decMsgId).toEqual(msgId)
|
expect(decMsgId).toEqual(msgId)
|
||||||
expect(decSeqNo).toEqual(seqNo)
|
expect(decSeqNo).toEqual(seqNo)
|
||||||
expect(p.utf8Decode(data.raw(16))).toEqual('hello, world!!!!')
|
expect(utf8.decoder.decode(data.raw(16))).toEqual('hello, world!!!!')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should ignore messages with invalid message key', async () => {
|
it('should ignore messages with invalid message key', async () => {
|
||||||
const message = p.hexDecode(
|
const message = hex.decode(
|
||||||
'40fa5bb7cb56a8950000000000000000000000000000000050a714ce6d2d1bdb'
|
'40fa5bb7cb56a8950000000000000000000000000000000050a714ce6d2d1bdb'
|
||||||
+ '3d98ff5929b8768c401771a69795f36a7e720dcafac2efbccd0ba368e8a7f48b'
|
+ '3d98ff5929b8768c401771a69795f36a7e720dcafac2efbccd0ba368e8a7f48b'
|
||||||
+ '07362cac1a32ffcabe188b51a36cc4d54e1d0633cf9eaf35',
|
+ '07362cac1a32ffcabe188b51a36cc4d54e1d0633cf9eaf35',
|
||||||
|
@ -125,7 +121,7 @@ describe('AuthKey', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should ignore messages with invalid session_id', async () => {
|
it('should ignore messages with invalid session_id', async () => {
|
||||||
const message = p.hexDecode(
|
const message = hex.decode(
|
||||||
'40fa5bb7cb56a895a986a7e97f4e90aa2769b5e702c6e86f5e1e82c6ff0c6829'
|
'40fa5bb7cb56a895a986a7e97f4e90aa2769b5e702c6e86f5e1e82c6ff0c6829'
|
||||||
+ '2521a2ba9704fa37fb341d895cf32662c6cf47ba31cbf27c30d5c03f6c2930f4'
|
+ '2521a2ba9704fa37fb341d895cf32662c6cf47ba31cbf27c30d5c03f6c2930f4'
|
||||||
+ '30fd8858b836b73fe32d4a95b8ebcdbc9ca8908f7964c40a',
|
+ '30fd8858b836b73fe32d4a95b8ebcdbc9ca8908f7964c40a',
|
||||||
|
@ -135,12 +131,12 @@ describe('AuthKey', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should ignore messages with invalid length', async () => {
|
it('should ignore messages with invalid length', async () => {
|
||||||
const messageTooLong = p.hexDecode(
|
const messageTooLong = hex.decode(
|
||||||
'40fa5bb7cb56a8950d19412233dd5d24be697c73274e08fbe515cf65e0c5f70c'
|
'40fa5bb7cb56a8950d19412233dd5d24be697c73274e08fbe515cf65e0c5f70c'
|
||||||
+ 'ad75fd2badc18c9f999f287351144eeb1cfcaa9bea33ef5058999ad96a498306'
|
+ 'ad75fd2badc18c9f999f287351144eeb1cfcaa9bea33ef5058999ad96a498306'
|
||||||
+ '08d2859425685a55b21fab413bfabc42ec5da283853b28c0',
|
+ '08d2859425685a55b21fab413bfabc42ec5da283853b28c0',
|
||||||
)
|
)
|
||||||
const messageUnaligned = p.hexDecode(
|
const messageUnaligned = hex.decode(
|
||||||
'40fa5bb7cb56a8957b4e4bec561eee4a5a1025bc8a35d3d0c79a3685d2b90ff0'
|
'40fa5bb7cb56a8957b4e4bec561eee4a5a1025bc8a35d3d0c79a3685d2b90ff0'
|
||||||
+ '5f638e9c42c9fd9448b0ce8e7d49e7ea1ce458e47b825b5c7fd8ddf5b4fded46'
|
+ '5f638e9c42c9fd9448b0ce8e7d49e7ea1ce458e47b825b5c7fd8ddf5b4fded46'
|
||||||
+ '2a4bcc02f3ff2e89de6764d6d219f575e457fdcf8c163cdf',
|
+ '2a4bcc02f3ff2e89de6764d6d219f575e457fdcf8c163cdf',
|
||||||
|
@ -153,7 +149,7 @@ describe('AuthKey', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should ignore messages with invalid padding', async () => {
|
it('should ignore messages with invalid padding', async () => {
|
||||||
const message = p.hexDecode(
|
const message = hex.decode(
|
||||||
'40fa5bb7cb56a895133671d1c637a9836e2c64b4d1a0521d8a25a6416fd4dc9e'
|
'40fa5bb7cb56a895133671d1c637a9836e2c64b4d1a0521d8a25a6416fd4dc9e'
|
||||||
+ '79f9478fb837703cc9efa0a19d12143c2a26e57cb4bc64d7bc972dd8f19c53c590cc258162f44afc',
|
+ '79f9478fb837703cc9efa0a19d12143c2a26e57cb4bc64d7bc972dd8f19c53c590cc258162f44afc',
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { describe, expect, it, vi } from 'vitest'
|
import { describe, expect, it, vi } from 'vitest'
|
||||||
import { defaultTestCryptoProvider, u8HexDecode } from '@mtcute/test'
|
import { defaultTestCryptoProvider } from '@mtcute/test'
|
||||||
import { Bytes } from '@fuman/io'
|
import { Bytes } from '@fuman/io'
|
||||||
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { getPlatform } from '../../platform.js'
|
import { getPlatform } from '../../platform.js'
|
||||||
import { LogManager } from '../../utils/index.js'
|
import { LogManager } from '../../utils/index.js'
|
||||||
|
@ -28,7 +29,7 @@ describe('ObfuscatedPacketCodec', () => {
|
||||||
|
|
||||||
const tag = await codec.tag()
|
const tag = await codec.tag()
|
||||||
|
|
||||||
expect(p.hexEncode(tag)).toEqual(
|
expect(hex.encode(tag)).toEqual(
|
||||||
`${'ff'.repeat(56)}fce8ab2203db2bff`, // encrypted part
|
`${'ff'.repeat(56)}fce8ab2203db2bff`, // encrypted part
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -46,7 +47,7 @@ describe('ObfuscatedPacketCodec', () => {
|
||||||
|
|
||||||
const tag = await codec.tag()
|
const tag = await codec.tag()
|
||||||
|
|
||||||
expect(p.hexEncode(tag)).toEqual(
|
expect(hex.encode(tag)).toEqual(
|
||||||
`${'ff'.repeat(56)}ecec4cbda8bb188b`, // encrypted part with dcId = 1
|
`${'ff'.repeat(56)}ecec4cbda8bb188b`, // encrypted part with dcId = 1
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -63,7 +64,7 @@ describe('ObfuscatedPacketCodec', () => {
|
||||||
|
|
||||||
const tag = await codec.tag()
|
const tag = await codec.tag()
|
||||||
|
|
||||||
expect(p.hexEncode(tag)).toEqual(
|
expect(hex.encode(tag)).toEqual(
|
||||||
`${'ff'.repeat(56)}ecec4cbdb89c188b`, // encrypted part with dcId = 10001
|
`${'ff'.repeat(56)}ecec4cbdb89c188b`, // encrypted part with dcId = 10001
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -80,7 +81,7 @@ describe('ObfuscatedPacketCodec', () => {
|
||||||
|
|
||||||
const tag = await codec.tag()
|
const tag = await codec.tag()
|
||||||
|
|
||||||
expect(p.hexEncode(tag)).toEqual(
|
expect(hex.encode(tag)).toEqual(
|
||||||
`${'ff'.repeat(56)}ecec4cbd5644188b`, // encrypted part with dcId = -1
|
`${'ff'.repeat(56)}ecec4cbd5644188b`, // encrypted part with dcId = -1
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -115,14 +116,14 @@ describe('ObfuscatedPacketCodec', () => {
|
||||||
expect(spyCreateAesCtr).toHaveBeenCalledTimes(2)
|
expect(spyCreateAesCtr).toHaveBeenCalledTimes(2)
|
||||||
expect(spyCreateAesCtr).toHaveBeenNthCalledWith(
|
expect(spyCreateAesCtr).toHaveBeenNthCalledWith(
|
||||||
1,
|
1,
|
||||||
u8HexDecode('10b6b4ad6d56ef5df9453f88e6ee6adb6e0544ba635dc6a8a990c9b8b980c343'),
|
hex.decode('10b6b4ad6d56ef5df9453f88e6ee6adb6e0544ba635dc6a8a990c9b8b980c343'),
|
||||||
u8HexDecode('936b33fa7f97bae025102532233abb26'),
|
hex.decode('936b33fa7f97bae025102532233abb26'),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
expect(spyCreateAesCtr).toHaveBeenNthCalledWith(
|
expect(spyCreateAesCtr).toHaveBeenNthCalledWith(
|
||||||
2,
|
2,
|
||||||
u8HexDecode('26bb3a2332251025e0ba977ffa336b9343c380b9b8c990a9a8c65d63ba44056e'),
|
hex.decode('26bb3a2332251025e0ba977ffa336b9343c380b9b8c990a9a8c65d63ba44056e'),
|
||||||
u8HexDecode('db6aeee6883f45f95def566dadb4b610'),
|
hex.decode('db6aeee6883f45f95def566dadb4b610'),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -130,7 +131,7 @@ describe('ObfuscatedPacketCodec', () => {
|
||||||
it('should correctly create aes ctr for mtproxy', async () => {
|
it('should correctly create aes ctr for mtproxy', async () => {
|
||||||
const proxy: MtProxyInfo = {
|
const proxy: MtProxyInfo = {
|
||||||
dcId: 1,
|
dcId: 1,
|
||||||
secret: p.hexDecode('00112233445566778899aabbccddeeff'),
|
secret: hex.decode('00112233445566778899aabbccddeeff'),
|
||||||
test: true,
|
test: true,
|
||||||
media: false,
|
media: false,
|
||||||
}
|
}
|
||||||
|
@ -143,20 +144,20 @@ describe('ObfuscatedPacketCodec', () => {
|
||||||
expect(spyCreateAesCtr).toHaveBeenCalledTimes(2)
|
expect(spyCreateAesCtr).toHaveBeenCalledTimes(2)
|
||||||
expect(spyCreateAesCtr).toHaveBeenNthCalledWith(
|
expect(spyCreateAesCtr).toHaveBeenNthCalledWith(
|
||||||
1,
|
1,
|
||||||
u8HexDecode('dd03188944590983e28dad14d97d0952389d118af4ffcbdb28d56a6a612ef7a6'),
|
hex.decode('dd03188944590983e28dad14d97d0952389d118af4ffcbdb28d56a6a612ef7a6'),
|
||||||
u8HexDecode('936b33fa7f97bae025102532233abb26'),
|
hex.decode('936b33fa7f97bae025102532233abb26'),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
expect(spyCreateAesCtr).toHaveBeenNthCalledWith(
|
expect(spyCreateAesCtr).toHaveBeenNthCalledWith(
|
||||||
2,
|
2,
|
||||||
u8HexDecode('413b8e08021fbb08a2962b6d7187194fe46565c6b329d3bbdfcffd4870c16119'),
|
hex.decode('413b8e08021fbb08a2962b6d7187194fe46565c6b329d3bbdfcffd4870c16119'),
|
||||||
u8HexDecode('db6aeee6883f45f95def566dadb4b610'),
|
hex.decode('db6aeee6883f45f95def566dadb4b610'),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly encrypt the underlying codec', async () => {
|
it('should correctly encrypt the underlying codec', async () => {
|
||||||
const data = p.hexDecode('6cfeffff')
|
const data = hex.decode('6cfeffff')
|
||||||
const msg1 = 'a1020630a410e940'
|
const msg1 = 'a1020630a410e940'
|
||||||
const msg2 = 'f53ff53f371db495'
|
const msg2 = 'f53ff53f371db495'
|
||||||
|
|
||||||
|
@ -166,11 +167,11 @@ describe('ObfuscatedPacketCodec', () => {
|
||||||
|
|
||||||
const buf = Bytes.alloc()
|
const buf = Bytes.alloc()
|
||||||
await codec.encode(data, buf)
|
await codec.encode(data, buf)
|
||||||
expect(p.hexEncode(buf.result())).toEqual(msg1)
|
expect(hex.encode(buf.result())).toEqual(msg1)
|
||||||
|
|
||||||
buf.reset()
|
buf.reset()
|
||||||
await codec.encode(data, buf)
|
await codec.encode(data, buf)
|
||||||
expect(p.hexEncode(buf.result())).toEqual(msg2)
|
expect(hex.encode(buf.result())).toEqual(msg2)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly decrypt the underlying codec', async () => {
|
it('should correctly decrypt the underlying codec', async () => {
|
||||||
|
@ -181,8 +182,8 @@ describe('ObfuscatedPacketCodec', () => {
|
||||||
|
|
||||||
await codec.tag()
|
await codec.tag()
|
||||||
|
|
||||||
expect(codec.decode(Bytes.from(p.hexDecode(msg1)), false)).rejects.toThrow(TransportError)
|
expect(codec.decode(Bytes.from(hex.decode(msg1)), false)).rejects.toThrow(TransportError)
|
||||||
expect(codec.decode(Bytes.from(p.hexDecode(msg2)), false)).rejects.toThrow(TransportError)
|
expect(codec.decode(Bytes.from(hex.decode(msg2)), false)).rejects.toThrow(TransportError)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly reset', async () => {
|
it('should correctly reset', async () => {
|
||||||
|
|
|
@ -1,11 +1,10 @@
|
||||||
import type { ITlPlatform } from '@mtcute/tl-runtime'
|
|
||||||
import { TlBinaryReader, TlBinaryWriter } from '@mtcute/tl-runtime'
|
|
||||||
|
|
||||||
import type { UploadFileLike } from './highlevel/types/files/utils.js'
|
import type { UploadFileLike } from './highlevel/types/files/utils.js'
|
||||||
import { MtUnsupportedError } from './types/errors.js'
|
import { MtUnsupportedError } from './types/errors.js'
|
||||||
import type { MaybePromise } from './types/index.js'
|
import type { MaybePromise } from './types/index.js'
|
||||||
|
|
||||||
export interface ICorePlatform extends ITlPlatform {
|
// todo: can we make this non-global?
|
||||||
|
|
||||||
|
export interface ICorePlatform {
|
||||||
beforeExit: (fn: () => void) => () => void
|
beforeExit: (fn: () => void) => () => void
|
||||||
log: (color: number, level: number, tag: string, fmt: string, args: unknown[]) => void
|
log: (color: number, level: number, tag: string, fmt: string, args: unknown[]) => void
|
||||||
getDefaultLogLevel: () => number | null
|
getDefaultLogLevel: () => number | null
|
||||||
|
@ -37,8 +36,6 @@ export function setPlatform(platform: ICorePlatform): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
_platform = platform
|
_platform = platform
|
||||||
TlBinaryReader.platform = platform
|
|
||||||
TlBinaryWriter.platform = platform
|
|
||||||
|
|
||||||
;(globalThis as any)[platformKey] = platform
|
;(globalThis as any)[platformKey] = platform
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { defaultTestCryptoProvider } from '@mtcute/test'
|
import { defaultTestCryptoProvider } from '@mtcute/test'
|
||||||
|
import { hex } from '@fuman/utils'
|
||||||
import { getPlatform } from '../platform.js'
|
|
||||||
|
|
||||||
import {
|
import {
|
||||||
bigIntBitLength,
|
bigIntBitLength,
|
||||||
|
@ -16,8 +15,6 @@ import {
|
||||||
twoMultiplicity,
|
twoMultiplicity,
|
||||||
} from './index.js'
|
} from './index.js'
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
describe('bigIntBitLength', () => {
|
describe('bigIntBitLength', () => {
|
||||||
it('should correctly calculate bit length', () => {
|
it('should correctly calculate bit length', () => {
|
||||||
expect(bigIntBitLength(0n)).eq(0)
|
expect(bigIntBitLength(0n)).eq(0)
|
||||||
|
@ -35,7 +32,7 @@ describe('bigIntToBuffer', () => {
|
||||||
expect([...bigIntToBuffer(BigInt('10495708'), 8, false)]).eql([0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x26, 0xDC])
|
expect([...bigIntToBuffer(BigInt('10495708'), 8, false)]).eql([0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x26, 0xDC])
|
||||||
expect([...bigIntToBuffer(BigInt('3038102549'), 4, false)]).eql([0xB5, 0x15, 0xC4, 0x15])
|
expect([...bigIntToBuffer(BigInt('3038102549'), 4, false)]).eql([0xB5, 0x15, 0xC4, 0x15])
|
||||||
expect([...bigIntToBuffer(BigInt('9341376580368336208'), 8, false)]).eql([
|
expect([...bigIntToBuffer(BigInt('9341376580368336208'), 8, false)]).eql([
|
||||||
...p.hexDecode('81A33C81D2020550'),
|
...hex.decode('81A33C81D2020550'),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -45,12 +42,12 @@ describe('bigIntToBuffer', () => {
|
||||||
expect([...bigIntToBuffer(BigInt('10495708'), 8, true)]).eql([0xDC, 0x26, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00])
|
expect([...bigIntToBuffer(BigInt('10495708'), 8, true)]).eql([0xDC, 0x26, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00])
|
||||||
expect([...bigIntToBuffer(BigInt('3038102549'), 4, true)]).eql([0x15, 0xC4, 0x15, 0xB5])
|
expect([...bigIntToBuffer(BigInt('3038102549'), 4, true)]).eql([0x15, 0xC4, 0x15, 0xB5])
|
||||||
expect([...bigIntToBuffer(BigInt('9341376580368336208'), 8, true)]).eql([
|
expect([...bigIntToBuffer(BigInt('9341376580368336208'), 8, true)]).eql([
|
||||||
...p.hexDecode('81A33C81D2020550').reverse(),
|
...hex.decode('81A33C81D2020550').reverse(),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle large integers', () => {
|
it('should handle large integers', () => {
|
||||||
const buf = p.hexDecode(
|
const buf = hex.decode(
|
||||||
'1a981ce8bf86bf4a1bd79c2ef829914172f8d0e54cb7ad807552d56977e1c946872e2c7bd77052be30e7e9a7a35c4feff848a25759f5f2f5b0e96538',
|
'1a981ce8bf86bf4a1bd79c2ef829914172f8d0e54cb7ad807552d56977e1c946872e2c7bd77052be30e7e9a7a35c4feff848a25759f5f2f5b0e96538',
|
||||||
)
|
)
|
||||||
const num = BigInt(
|
const num = BigInt(
|
||||||
|
@ -76,7 +73,7 @@ describe('bufferToBigInt', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should handle large integers', () => {
|
it('should handle large integers', () => {
|
||||||
const buf = p.hexDecode(
|
const buf = hex.decode(
|
||||||
'1a981ce8bf86bf4a1bd79c2ef829914172f8d0e54cb7ad807552d56977e1c946872e2c7bd77052be30e7e9a7a35c4feff848a25759f5f2f5b0e96538',
|
'1a981ce8bf86bf4a1bd79c2ef829914172f8d0e54cb7ad807552d56977e1c946872e2c7bd77052be30e7e9a7a35c4feff848a25759f5f2f5b0e96538',
|
||||||
)
|
)
|
||||||
const num = BigInt(
|
const num = BigInt(
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
// all available libraries either suck or are extremely large for the use case, so i made my own~
|
// all available libraries either suck or are extremely large for the use case, so i made my own~
|
||||||
|
|
||||||
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { getPlatform } from '../../platform.js'
|
import { getPlatform } from '../../platform.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -66,7 +68,7 @@ export function parseAsn1(data: Uint8Array): Asn1Object {
|
||||||
if (0x80 & asn1.length) {
|
if (0x80 & asn1.length) {
|
||||||
asn1.lengthSize = 0x7F & asn1.length
|
asn1.lengthSize = 0x7F & asn1.length
|
||||||
// I think that buf->hex->int solves the problem of Endianness... not sure
|
// I think that buf->hex->int solves the problem of Endianness... not sure
|
||||||
asn1.length = Number.parseInt(getPlatform().hexEncode(buf.subarray(index, index + asn1.lengthSize)), 16)
|
asn1.length = Number.parseInt(hex.encode(buf.subarray(index, index + asn1.lengthSize)), 16)
|
||||||
index += asn1.lengthSize
|
index += asn1.lengthSize
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@ import type Long from 'long'
|
||||||
import type { TlPublicKey } from '@mtcute/tl/binary/rsa-keys.js'
|
import type { TlPublicKey } from '@mtcute/tl/binary/rsa-keys.js'
|
||||||
import { __publicKeyIndex as keysIndex } from '@mtcute/tl/binary/rsa-keys.js'
|
import { __publicKeyIndex as keysIndex } from '@mtcute/tl/binary/rsa-keys.js'
|
||||||
import { TlBinaryWriter } from '@mtcute/tl-runtime'
|
import { TlBinaryWriter } from '@mtcute/tl-runtime'
|
||||||
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { getPlatform } from '../../platform.js'
|
|
||||||
import { parseAsn1, parsePemContents } from '../binary/asn1-parser.js'
|
import { parseAsn1, parsePemContents } from '../binary/asn1-parser.js'
|
||||||
|
|
||||||
import type { ICryptoProvider } from './abstract.js'
|
import type { ICryptoProvider } from './abstract.js'
|
||||||
|
@ -27,15 +27,13 @@ export function parsePublicKey(crypto: ICryptoProvider, key: string, old = false
|
||||||
writer.bytes(modulus)
|
writer.bytes(modulus)
|
||||||
writer.bytes(exponent)
|
writer.bytes(exponent)
|
||||||
|
|
||||||
const platform = getPlatform()
|
|
||||||
|
|
||||||
const data = writer.result()
|
const data = writer.result()
|
||||||
const sha = crypto.sha1(data)
|
const sha = crypto.sha1(data)
|
||||||
const fp = platform.hexEncode(sha.slice(-8).reverse())
|
const fp = hex.encode(sha.slice(-8).reverse())
|
||||||
|
|
||||||
return {
|
return {
|
||||||
modulus: platform.hexEncode(modulus),
|
modulus: hex.encode(modulus),
|
||||||
exponent: platform.hexEncode(exponent),
|
exponent: hex.encode(exponent),
|
||||||
fingerprint: fp,
|
fingerprint: fp,
|
||||||
old,
|
old,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,14 @@
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
import { defaultTestCryptoProvider, u8HexDecode } from '@mtcute/test'
|
import { defaultTestCryptoProvider } from '@mtcute/test'
|
||||||
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { getPlatform } from '../../platform.js'
|
|
||||||
import { concatBuffers } from '../index.js'
|
import { concatBuffers } from '../index.js'
|
||||||
|
|
||||||
import { createAesIgeForMessage, createAesIgeForMessageOld, generateKeyAndIvFromNonce } from './mtproto.js'
|
import { createAesIgeForMessage, createAesIgeForMessageOld, generateKeyAndIvFromNonce } from './mtproto.js'
|
||||||
|
|
||||||
const p = getPlatform()
|
const authKeyChunk = hex.decode('98cb29c6ffa89e79da695a54f572e6cb101e81c688b63a4bf73c3622dec230e0')
|
||||||
|
|
||||||
const authKeyChunk = p.hexDecode('98cb29c6ffa89e79da695a54f572e6cb101e81c688b63a4bf73c3622dec230e0')
|
|
||||||
const authKey = concatBuffers(Array.from({ length: 8 }, () => authKeyChunk))
|
const authKey = concatBuffers(Array.from({ length: 8 }, () => authKeyChunk))
|
||||||
const messageKey = p.hexDecode('25d701f2a29205526757825a99eb2d32')
|
const messageKey = hex.decode('25d701f2a29205526757825a99eb2d32')
|
||||||
|
|
||||||
describe('mtproto 2.0', async () => {
|
describe('mtproto 2.0', async () => {
|
||||||
const crypto = await defaultTestCryptoProvider()
|
const crypto = await defaultTestCryptoProvider()
|
||||||
|
@ -21,10 +19,10 @@ describe('mtproto 2.0', async () => {
|
||||||
it('should correctly derive message key and iv for client', () => {
|
it('should correctly derive message key and iv for client', () => {
|
||||||
createAesIgeForMessage(crypto, authKey, messageKey, true)
|
createAesIgeForMessage(crypto, authKey, messageKey, true)
|
||||||
|
|
||||||
expect(p.hexEncode(createAesIgeSpy.mock.calls[0][0])).toEqual(
|
expect(hex.encode(createAesIgeSpy.mock.calls[0][0])).toEqual(
|
||||||
'af3f8e1ffa75f4c981eec33a3e5bbaa2ea48f9bb93e91597627eb1f67960a0c9',
|
'af3f8e1ffa75f4c981eec33a3e5bbaa2ea48f9bb93e91597627eb1f67960a0c9',
|
||||||
)
|
)
|
||||||
expect(p.hexEncode(createAesIgeSpy.mock.calls[0][1])).toEqual(
|
expect(hex.encode(createAesIgeSpy.mock.calls[0][1])).toEqual(
|
||||||
'9874d77f95155b35221bff94b7df4594c6996e2a62e44fcb7d93c8c4e41b79ee',
|
'9874d77f95155b35221bff94b7df4594c6996e2a62e44fcb7d93c8c4e41b79ee',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -32,10 +30,10 @@ describe('mtproto 2.0', async () => {
|
||||||
it('should correctly derive message key and iv for server', () => {
|
it('should correctly derive message key and iv for server', () => {
|
||||||
createAesIgeForMessage(crypto, authKey, messageKey, false)
|
createAesIgeForMessage(crypto, authKey, messageKey, false)
|
||||||
|
|
||||||
expect(p.hexEncode(createAesIgeSpy.mock.calls[0][0])).toEqual(
|
expect(hex.encode(createAesIgeSpy.mock.calls[0][0])).toEqual(
|
||||||
'd4b378e1e0525f10ff9d4c42807ccce5b30a033a8088c0b922b5259421751648',
|
'd4b378e1e0525f10ff9d4c42807ccce5b30a033a8088c0b922b5259421751648',
|
||||||
)
|
)
|
||||||
expect(p.hexEncode(createAesIgeSpy.mock.calls[0][1])).toEqual(
|
expect(hex.encode(createAesIgeSpy.mock.calls[0][1])).toEqual(
|
||||||
'4d7194f42f0135d2fd83050b403265b4c40ee3e9e9fba56f0f4d8ea6bcb121f5',
|
'4d7194f42f0135d2fd83050b403265b4c40ee3e9e9fba56f0f4d8ea6bcb121f5',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -50,10 +48,10 @@ describe('mtproto 1.0', async () => {
|
||||||
it('should correctly derive message key and iv for client', () => {
|
it('should correctly derive message key and iv for client', () => {
|
||||||
createAesIgeForMessageOld(crypto, authKey, messageKey, true)
|
createAesIgeForMessageOld(crypto, authKey, messageKey, true)
|
||||||
|
|
||||||
expect(p.hexEncode(createAesIgeSpy.mock.calls[0][0])).toEqual(
|
expect(hex.encode(createAesIgeSpy.mock.calls[0][0])).toEqual(
|
||||||
'1fc7b40b1d9ffbdaf4d652525a748864259698f89214abf27c0d36cb9d4cd5db',
|
'1fc7b40b1d9ffbdaf4d652525a748864259698f89214abf27c0d36cb9d4cd5db',
|
||||||
)
|
)
|
||||||
expect(p.hexEncode(createAesIgeSpy.mock.calls[0][1])).toEqual(
|
expect(hex.encode(createAesIgeSpy.mock.calls[0][1])).toEqual(
|
||||||
'7251fbda39ec5e6e089f15ded5963b03d6d8d0f7078898431fc7b40b1d9ffbda',
|
'7251fbda39ec5e6e089f15ded5963b03d6d8d0f7078898431fc7b40b1d9ffbda',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -61,10 +59,10 @@ describe('mtproto 1.0', async () => {
|
||||||
it('should correctly derive message key and iv for server', () => {
|
it('should correctly derive message key and iv for server', () => {
|
||||||
createAesIgeForMessageOld(crypto, authKey, messageKey, false)
|
createAesIgeForMessageOld(crypto, authKey, messageKey, false)
|
||||||
|
|
||||||
expect(p.hexEncode(createAesIgeSpy.mock.calls[0][0])).toEqual(
|
expect(hex.encode(createAesIgeSpy.mock.calls[0][0])).toEqual(
|
||||||
'af0e4e01318654be40ab42b125909d43b44bdeef571ff1a5dfb81474ae26d467',
|
'af0e4e01318654be40ab42b125909d43b44bdeef571ff1a5dfb81474ae26d467',
|
||||||
)
|
)
|
||||||
expect(p.hexEncode(createAesIgeSpy.mock.calls[0][1])).toEqual(
|
expect(hex.encode(createAesIgeSpy.mock.calls[0][1])).toEqual(
|
||||||
'15c9ba6021d2c5cf04f0842540ae216a970b4eac8f46ef01af0e4e01318654be',
|
'15c9ba6021d2c5cf04f0842540ae216a970b4eac8f46ef01af0e4e01318654be',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
@ -76,13 +74,13 @@ describe('mtproto key/iv from nonce', async () => {
|
||||||
it('should correctly derive message key and iv for given nonces', () => {
|
it('should correctly derive message key and iv for given nonces', () => {
|
||||||
const res = generateKeyAndIvFromNonce(
|
const res = generateKeyAndIvFromNonce(
|
||||||
crypto,
|
crypto,
|
||||||
u8HexDecode('8af24c551836e5ed7002f5857e6e71b2'),
|
hex.decode('8af24c551836e5ed7002f5857e6e71b2'),
|
||||||
u8HexDecode('3bf48b2d3152f383d82d1f2b32ac7fb5'),
|
hex.decode('3bf48b2d3152f383d82d1f2b32ac7fb5'),
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(res).to.eql([
|
expect(res).to.eql([
|
||||||
u8HexDecode('b0b5ffeadff0249fa6292f5ae0351556fd6619ba5dd4809601669292456d3e5a'),
|
hex.decode('b0b5ffeadff0249fa6292f5ae0351556fd6619ba5dd4809601669292456d3e5a'),
|
||||||
u8HexDecode('13fef5bfd8c46b12dfd1753013b86cc012e1ce8ed6f8ecdd7bf36f3a3bf48b2d'),
|
hex.decode('13fef5bfd8c46b12dfd1753013b86cc012e1ce8ed6f8ecdd7bf36f3a3bf48b2d'),
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,20 +2,17 @@ import Long from 'long'
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { defaultTestCryptoProvider } from '@mtcute/test'
|
import { defaultTestCryptoProvider } from '@mtcute/test'
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
|
import { hex, utf8 } from '@fuman/utils'
|
||||||
import { getPlatform } from '../../platform.js'
|
|
||||||
|
|
||||||
import { computeNewPasswordHash, computePasswordHash, computeSrpParams } from './index.js'
|
import { computeNewPasswordHash, computePasswordHash, computeSrpParams } from './index.js'
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
// a real-world request from an account with "qwe123" password
|
// a real-world request from an account with "qwe123" password
|
||||||
const fakeAlgo: tl.RawPasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow = {
|
const fakeAlgo: tl.RawPasswordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow = {
|
||||||
_: 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
|
_: 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
|
||||||
salt1: p.hexDecode('9b3accc457c0d5288e8cff31eb21094048bc11902f6614dbb9afb839ee7641c37619537d8ebe749e'),
|
salt1: hex.decode('9b3accc457c0d5288e8cff31eb21094048bc11902f6614dbb9afb839ee7641c37619537d8ebe749e'),
|
||||||
salt2: p.hexDecode('6c619bb0786dc4ed1bf211d23f6e4065'),
|
salt2: hex.decode('6c619bb0786dc4ed1bf211d23f6e4065'),
|
||||||
g: 3,
|
g: 3,
|
||||||
p: p.hexDecode(
|
p: hex.decode(
|
||||||
'c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f'
|
'c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f'
|
||||||
+ '48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c37'
|
+ '48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c37'
|
||||||
+ '20fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f64'
|
+ '20fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f64'
|
||||||
|
@ -32,7 +29,7 @@ const fakeRequest: tl.account.RawPassword = {
|
||||||
hasSecureValues: false,
|
hasSecureValues: false,
|
||||||
hasPassword: true,
|
hasPassword: true,
|
||||||
currentAlgo: fakeAlgo,
|
currentAlgo: fakeAlgo,
|
||||||
srpB: p.hexDecode(
|
srpB: hex.decode(
|
||||||
'1476a7b5991d7f028bbee33b3455cad3f2cd0eb3737409fcce92fa7d4cd5c733'
|
'1476a7b5991d7f028bbee33b3455cad3f2cd0eb3737409fcce92fa7d4cd5c733'
|
||||||
+ 'ec6d2cb3454e587d4c17eda2fd7ef9a57327215f38292cc8bd5dc77d3e1d31cd'
|
+ 'ec6d2cb3454e587d4c17eda2fd7ef9a57327215f38292cc8bd5dc77d3e1d31cd'
|
||||||
+ 'dae2652f8347c4b0093f7c78242f70e6cc13137ee7acc257a49855a63113db8f'
|
+ 'dae2652f8347c4b0093f7c78242f70e6cc13137ee7acc257a49855a63113db8f'
|
||||||
|
@ -45,10 +42,10 @@ const fakeRequest: tl.account.RawPassword = {
|
||||||
srpId: Long.fromBits(-2046015018, 875006452),
|
srpId: Long.fromBits(-2046015018, 875006452),
|
||||||
newAlgo: {
|
newAlgo: {
|
||||||
_: 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
|
_: 'passwordKdfAlgoSHA256SHA256PBKDF2HMACSHA512iter100000SHA256ModPow',
|
||||||
salt1: p.hexDecode('9b3accc457c0d528'),
|
salt1: hex.decode('9b3accc457c0d528'),
|
||||||
salt2: p.hexDecode('6c619bb0786dc4ed1bf211d23f6e4065'),
|
salt2: hex.decode('6c619bb0786dc4ed1bf211d23f6e4065'),
|
||||||
g: 3,
|
g: 3,
|
||||||
p: p.hexDecode(
|
p: hex.decode(
|
||||||
'c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f'
|
'c71caeb9c6b1c9048e6c522f70f13f73980d40238e3e21c14934d037563d930f'
|
||||||
+ '48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c37'
|
+ '48198a0aa7c14058229493d22530f4dbfa336f6e0ac925139543aed44cce7c37'
|
||||||
+ '20fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f64'
|
+ '20fd51f69458705ac68cd4fe6b6b13abdc9746512969328454f18faf8c595f64'
|
||||||
|
@ -61,7 +58,7 @@ const fakeRequest: tl.account.RawPassword = {
|
||||||
},
|
},
|
||||||
newSecureAlgo: {
|
newSecureAlgo: {
|
||||||
_: 'securePasswordKdfAlgoPBKDF2HMACSHA512iter100000',
|
_: 'securePasswordKdfAlgoPBKDF2HMACSHA512iter100000',
|
||||||
salt: p.hexDecode('fdd59abc0bffb24d'),
|
salt: hex.decode('fdd59abc0bffb24d'),
|
||||||
},
|
},
|
||||||
secureRandom: new Uint8Array(), // unused
|
secureRandom: new Uint8Array(), // unused
|
||||||
}
|
}
|
||||||
|
@ -70,16 +67,16 @@ const password = 'qwe123'
|
||||||
describe('SRP', () => {
|
describe('SRP', () => {
|
||||||
it('should correctly compute password hash as defined by MTProto', async () => {
|
it('should correctly compute password hash as defined by MTProto', async () => {
|
||||||
const crypto = await defaultTestCryptoProvider()
|
const crypto = await defaultTestCryptoProvider()
|
||||||
const hash = await computePasswordHash(crypto, p.utf8Encode(password), fakeAlgo.salt1, fakeAlgo.salt2)
|
const hash = await computePasswordHash(crypto, utf8.encoder.encode(password), fakeAlgo.salt1, fakeAlgo.salt2)
|
||||||
|
|
||||||
expect(p.hexEncode(hash)).toEqual('750f1fe282965e63ce17b98427b35549fb864465211840f6a7c1f2fb657cc33b')
|
expect(hex.encode(hash)).toEqual('750f1fe282965e63ce17b98427b35549fb864465211840f6a7c1f2fb657cc33b')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly compute new password hash as defined by MTProto', async () => {
|
it('should correctly compute new password hash as defined by MTProto', async () => {
|
||||||
const crypto = await defaultTestCryptoProvider()
|
const crypto = await defaultTestCryptoProvider()
|
||||||
const hash = await computeNewPasswordHash(crypto, fakeAlgo, '123qwe')
|
const hash = await computeNewPasswordHash(crypto, fakeAlgo, '123qwe')
|
||||||
|
|
||||||
expect(p.hexEncode(hash)).toEqual(
|
expect(hex.encode(hash)).toEqual(
|
||||||
'2540539ceeffd4543cd845bf319b8392e6b17bf7cf26bafcf6282ce9ae795368'
|
'2540539ceeffd4543cd845bf319b8392e6b17bf7cf26bafcf6282ce9ae795368'
|
||||||
+ '4ff49469c2863b17e6d65ddb16ae6f60bc07cc254c00e5ba389292f6cea0b3aa'
|
+ '4ff49469c2863b17e6d65ddb16ae6f60bc07cc254c00e5ba389292f6cea0b3aa'
|
||||||
+ 'c459d1d08984d65319df8c5d124042169bbe2ab8c0c93bc7178827f2ea84e7c3'
|
+ 'c459d1d08984d65319df8c5d124042169bbe2ab8c0c93bc7178827f2ea84e7c3'
|
||||||
|
@ -96,7 +93,7 @@ describe('SRP', () => {
|
||||||
const params = await computeSrpParams(crypto, fakeRequest, password)
|
const params = await computeSrpParams(crypto, fakeRequest, password)
|
||||||
|
|
||||||
expect(params.srpId).toEqual(fakeRequest.srpId)
|
expect(params.srpId).toEqual(fakeRequest.srpId)
|
||||||
expect(p.hexEncode(params.A)).toEqual(
|
expect(hex.encode(params.A)).toEqual(
|
||||||
'363976f55edb57cc5cc0c4aaca9b7539eff98a43a93fa84be34860d18ac3a80f'
|
'363976f55edb57cc5cc0c4aaca9b7539eff98a43a93fa84be34860d18ac3a80f'
|
||||||
+ 'ffd57c4617896ff667677d0552a079eb189d25d147ec96edd4495c946a18652d'
|
+ 'ffd57c4617896ff667677d0552a079eb189d25d147ec96edd4495c946a18652d'
|
||||||
+ '31d78eede40a8b29da340c19b32ccac78f8482406e392102c03d850d1db87223'
|
+ '31d78eede40a8b29da340c19b32ccac78f8482406e392102c03d850d1db87223'
|
||||||
|
@ -106,6 +103,6 @@ describe('SRP', () => {
|
||||||
+ '4fa454aa69d9219d9c5fa3625f5c6f1ac03892a70aa17269c76cd9bf2949a961'
|
+ '4fa454aa69d9219d9c5fa3625f5c6f1ac03892a70aa17269c76cd9bf2949a961'
|
||||||
+ 'fad2a71e5fa961824b32db037130c7e9aad4c1e9f02ebc5b832622f98b59597e',
|
+ 'fad2a71e5fa961824b32db037130c7e9aad4c1e9f02ebc5b832622f98b59597e',
|
||||||
)
|
)
|
||||||
expect(p.hexEncode(params.M1)).toEqual('25a91b21c634ad670a144165a9829192d152e131a716f676abc48cd817f508c6')
|
expect(hex.encode(params.M1)).toEqual('25a91b21c634ad670a144165a9829192d152e131a716f676abc48cd817f508c6')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import type { tl } from '@mtcute/tl'
|
import type { tl } from '@mtcute/tl'
|
||||||
|
import { utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { getPlatform } from '../../platform.js'
|
|
||||||
import { MtSecurityError, MtUnsupportedError } from '../../types/errors.js'
|
import { MtSecurityError, MtUnsupportedError } from '../../types/errors.js'
|
||||||
import { bigIntModPow, bigIntToBuffer, bufferToBigInt } from '../bigint-utils.js'
|
import { bigIntModPow, bigIntToBuffer, bufferToBigInt } from '../bigint-utils.js'
|
||||||
import { concatBuffers } from '../buffer-utils.js'
|
import { concatBuffers } from '../buffer-utils.js'
|
||||||
|
@ -50,7 +50,7 @@ export async function computeNewPasswordHash(
|
||||||
crypto.randomFill(salt1.subarray(algo.salt1.length))
|
crypto.randomFill(salt1.subarray(algo.salt1.length))
|
||||||
;(algo as tl.Mutable<typeof algo>).salt1 = salt1
|
;(algo as tl.Mutable<typeof algo>).salt1 = salt1
|
||||||
|
|
||||||
const _x = await computePasswordHash(crypto, getPlatform().utf8Encode(password), algo.salt1, algo.salt2)
|
const _x = await computePasswordHash(crypto, utf8.encoder.encode(password), algo.salt1, algo.salt2)
|
||||||
|
|
||||||
const g = BigInt(algo.g)
|
const g = BigInt(algo.g)
|
||||||
const p = bufferToBigInt(algo.p)
|
const p = bufferToBigInt(algo.p)
|
||||||
|
@ -104,7 +104,7 @@ export async function computeSrpParams(
|
||||||
|
|
||||||
const _k = crypto.sha256(concatBuffers([algo.p, _g]))
|
const _k = crypto.sha256(concatBuffers([algo.p, _g]))
|
||||||
const _u = crypto.sha256(concatBuffers([_gA, request.srpB]))
|
const _u = crypto.sha256(concatBuffers([_gA, request.srpB]))
|
||||||
const _x = await computePasswordHash(crypto, getPlatform().utf8Encode(password), algo.salt1, algo.salt2)
|
const _x = await computePasswordHash(crypto, utf8.encoder.encode(password), algo.salt1, algo.salt2)
|
||||||
const k = bufferToBigInt(_k)
|
const k = bufferToBigInt(_k)
|
||||||
const u = bufferToBigInt(_u)
|
const u = bufferToBigInt(_u)
|
||||||
const x = bufferToBigInt(_x)
|
const x = bufferToBigInt(_x)
|
||||||
|
|
|
@ -1,63 +1,60 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { hex, utf8 } from '@fuman/utils'
|
||||||
import { getPlatform } from '../../platform.js'
|
|
||||||
|
|
||||||
import { xorBuffer, xorBufferInPlace } from './utils.js'
|
import { xorBuffer, xorBufferInPlace } from './utils.js'
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
describe('xorBuffer', () => {
|
describe('xorBuffer', () => {
|
||||||
it('should xor buffers without modifying original', () => {
|
it('should xor buffers without modifying original', () => {
|
||||||
const data = p.utf8Encode('hello')
|
const data = utf8.encoder.encode('hello')
|
||||||
const key = p.utf8Encode('xor')
|
const key = utf8.encoder.encode('xor')
|
||||||
|
|
||||||
const xored = xorBuffer(data, key)
|
const xored = xorBuffer(data, key)
|
||||||
expect(p.utf8Decode(data)).eq('hello')
|
expect(utf8.decoder.decode(data)).eq('hello')
|
||||||
expect(p.utf8Decode(key)).eq('xor')
|
expect(utf8.decoder.decode(key)).eq('xor')
|
||||||
expect(p.hexEncode(xored)).eq('100a1e6c6f')
|
expect(hex.encode(xored)).eq('100a1e6c6f')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should be deterministic', () => {
|
it('should be deterministic', () => {
|
||||||
const data = p.utf8Encode('hello')
|
const data = utf8.encoder.encode('hello')
|
||||||
const key = p.utf8Encode('xor')
|
const key = utf8.encoder.encode('xor')
|
||||||
|
|
||||||
const xored1 = xorBuffer(data, key)
|
const xored1 = xorBuffer(data, key)
|
||||||
expect(p.hexEncode(xored1)).eq('100a1e6c6f')
|
expect(hex.encode(xored1)).eq('100a1e6c6f')
|
||||||
|
|
||||||
const xored2 = xorBuffer(data, key)
|
const xored2 = xorBuffer(data, key)
|
||||||
expect(p.hexEncode(xored2)).eq('100a1e6c6f')
|
expect(hex.encode(xored2)).eq('100a1e6c6f')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('second call should decode content', () => {
|
it('second call should decode content', () => {
|
||||||
const data = p.utf8Encode('hello')
|
const data = utf8.encoder.encode('hello')
|
||||||
const key = p.utf8Encode('xor')
|
const key = utf8.encoder.encode('xor')
|
||||||
|
|
||||||
const xored1 = xorBuffer(data, key)
|
const xored1 = xorBuffer(data, key)
|
||||||
expect(p.hexEncode(xored1)).eq('100a1e6c6f')
|
expect(hex.encode(xored1)).eq('100a1e6c6f')
|
||||||
|
|
||||||
const xored2 = xorBuffer(xored1, key)
|
const xored2 = xorBuffer(xored1, key)
|
||||||
expect(p.utf8Decode(xored2)).eq('hello')
|
expect(utf8.decoder.decode(xored2)).eq('hello')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('xorBufferInPlace', () => {
|
describe('xorBufferInPlace', () => {
|
||||||
it('should xor buffers by modifying original', () => {
|
it('should xor buffers by modifying original', () => {
|
||||||
const data = p.utf8Encode('hello')
|
const data = utf8.encoder.encode('hello')
|
||||||
const key = p.utf8Encode('xor')
|
const key = utf8.encoder.encode('xor')
|
||||||
|
|
||||||
xorBufferInPlace(data, key)
|
xorBufferInPlace(data, key)
|
||||||
expect(p.hexEncode(data)).eq('100a1e6c6f')
|
expect(hex.encode(data)).eq('100a1e6c6f')
|
||||||
expect(p.utf8Decode(key)).eq('xor')
|
expect(utf8.decoder.decode(key)).eq('xor')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('second call should decode content', () => {
|
it('second call should decode content', () => {
|
||||||
const data = p.utf8Encode('hello')
|
const data = utf8.encoder.encode('hello')
|
||||||
const key = p.utf8Encode('xor')
|
const key = utf8.encoder.encode('xor')
|
||||||
|
|
||||||
xorBufferInPlace(data, key)
|
xorBufferInPlace(data, key)
|
||||||
expect(p.hexEncode(data)).eq('100a1e6c6f')
|
expect(hex.encode(data)).eq('100a1e6c6f')
|
||||||
|
|
||||||
xorBufferInPlace(data, key)
|
xorBufferInPlace(data, key)
|
||||||
expect(p.utf8Decode(data)).eq('hello')
|
expect(utf8.decoder.decode(data)).eq('hello')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { tl } from '@mtcute/tl'
|
import { tl } from '@mtcute/tl'
|
||||||
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import type { ICorePlatform } from '../platform.js'
|
import type { ICorePlatform } from '../platform.js'
|
||||||
import { getPlatform } from '../platform.js'
|
import { getPlatform } from '../platform.js'
|
||||||
|
@ -67,7 +68,7 @@ export class Logger {
|
||||||
args.splice(idx, 1)
|
args.splice(idx, 1)
|
||||||
|
|
||||||
if (m === '%h') {
|
if (m === '%h') {
|
||||||
if (ArrayBuffer.isView(val)) return this.mgr.platform.hexEncode(val as Uint8Array)
|
if (ArrayBuffer.isView(val)) return hex.encode(val as Uint8Array)
|
||||||
if (typeof val === 'number' || typeof val === 'bigint') return val.toString(16)
|
if (typeof val === 'number' || typeof val === 'bigint') return val.toString(16)
|
||||||
|
|
||||||
return String(val)
|
return String(val)
|
||||||
|
@ -85,7 +86,7 @@ export class Logger {
|
||||||
|| (typeof v === 'object' && v.type === 'Buffer' && Array.isArray(v.data)) // todo: how can we do this better?
|
|| (typeof v === 'object' && v.type === 'Buffer' && Array.isArray(v.data)) // todo: how can we do this better?
|
||||||
) {
|
) {
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
let str = v.data ? Buffer.from(v.data as number[]).toString('hex') : this.mgr.platform.hexEncode(v)
|
let str = v.data ? Buffer.from(v.data as number[]).toString('hex') : hex.encode(v)
|
||||||
|
|
||||||
if (str.length > 300) {
|
if (str.length > 300) {
|
||||||
str = `${str.slice(0, 300)}...`
|
str = `${str.slice(0, 300)}...`
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
import type { ICorePlatform } from '@mtcute/core/platform.js'
|
import type { ICorePlatform } from '@mtcute/core/platform.js'
|
||||||
|
|
||||||
import { base64Decode, base64Encode } from './common-internals-web/base64.js'
|
|
||||||
import { hexDecode, hexEncode } from './common-internals-web/hex.js'
|
|
||||||
import { defaultLoggingHandler } from './common-internals-web/logging.js'
|
import { defaultLoggingHandler } from './common-internals-web/logging.js'
|
||||||
import { utf8ByteLength, utf8Decode, utf8Encode } from './common-internals-web/utf8.js'
|
|
||||||
import { beforeExit } from './utils/exit-hook.js'
|
import { beforeExit } from './utils/exit-hook.js'
|
||||||
import { normalizeFile } from './utils/normalize-file.js'
|
import { normalizeFile } from './utils/normalize-file.js'
|
||||||
|
|
||||||
|
@ -25,24 +22,8 @@ export class DenoPlatform implements ICorePlatform {
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// ITlPlatform
|
|
||||||
declare utf8ByteLength: typeof utf8ByteLength
|
|
||||||
declare utf8Encode: typeof utf8Encode
|
|
||||||
declare utf8Decode: typeof utf8Decode
|
|
||||||
declare hexEncode: typeof hexEncode
|
|
||||||
declare hexDecode: typeof hexDecode
|
|
||||||
declare base64Encode: typeof base64Encode
|
|
||||||
declare base64Decode: typeof base64Decode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DenoPlatform.prototype.utf8ByteLength = utf8ByteLength
|
|
||||||
DenoPlatform.prototype.utf8Encode = utf8Encode
|
|
||||||
DenoPlatform.prototype.utf8Decode = utf8Decode
|
|
||||||
DenoPlatform.prototype.hexEncode = hexEncode
|
|
||||||
DenoPlatform.prototype.hexDecode = hexDecode
|
|
||||||
DenoPlatform.prototype.base64Encode = base64Encode
|
|
||||||
DenoPlatform.prototype.base64Decode = base64Decode
|
|
||||||
DenoPlatform.prototype.log = defaultLoggingHandler
|
DenoPlatform.prototype.log = defaultLoggingHandler
|
||||||
DenoPlatform.prototype.beforeExit = beforeExit
|
DenoPlatform.prototype.beforeExit = beforeExit
|
||||||
DenoPlatform.prototype.normalizeFile = normalizeFile
|
DenoPlatform.prototype.normalizeFile = normalizeFile
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { CallbackQuery, MtArgumentError, PeersIndex } from '@mtcute/core'
|
import { CallbackQuery, MtArgumentError, PeersIndex } from '@mtcute/core'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
|
||||||
import { createStub } from '@mtcute/test'
|
import { createStub } from '@mtcute/test'
|
||||||
|
|
||||||
import { CallbackDataBuilder } from './callback-data-builder.js'
|
import { CallbackDataBuilder } from './callback-data-builder.js'
|
||||||
|
@ -52,7 +51,7 @@ describe('CallbackDataBuilder', () => {
|
||||||
const createCb = (data: string) =>
|
const createCb = (data: string) =>
|
||||||
new CallbackQuery(
|
new CallbackQuery(
|
||||||
createStub('updateBotCallbackQuery', {
|
createStub('updateBotCallbackQuery', {
|
||||||
data: getPlatform().utf8Encode(data),
|
data: new TextEncoder().encode(data),
|
||||||
}),
|
}),
|
||||||
new PeersIndex(),
|
new PeersIndex(),
|
||||||
)
|
)
|
||||||
|
|
|
@ -15,9 +15,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mtcute/tl-runtime": "workspace:^",
|
"@mtcute/tl-runtime": "workspace:^",
|
||||||
|
"@fuman/utils": "workspace:^",
|
||||||
"long": "5.2.3"
|
"long": "5.2.3"
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@mtcute/test": "workspace:^"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { defaultPlatform } from '@mtcute/test'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { parseFileId } from './parse.js'
|
import { parseFileId } from './parse.js'
|
||||||
import { tdFileId as td } from './types.js'
|
import { tdFileId as td } from './types.js'
|
||||||
|
@ -9,14 +9,14 @@ import { tdFileId as td } from './types.js'
|
||||||
|
|
||||||
describe('parsing file ids', () => {
|
describe('parsing file ids', () => {
|
||||||
const test = (id: string, expected: td.RawFullRemoteFileLocation) => {
|
const test = (id: string, expected: td.RawFullRemoteFileLocation) => {
|
||||||
expect(parseFileId(defaultPlatform, id)).eql(expected)
|
expect(parseFileId(id)).eql(expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
it('parses common file ids', () => {
|
it('parses common file ids', () => {
|
||||||
test('CAACAgIAAxkBAAEJny9gituz1_V_uSKBUuG_nhtzEtFOeQACXFoAAuCjggfYjw_KAAGSnkgfBA', {
|
test('CAACAgIAAxkBAAEJny9gituz1_V_uSKBUuG_nhtzEtFOeQACXFoAAuCjggfYjw_KAAGSnkgfBA', {
|
||||||
_: 'remoteFileLocation',
|
_: 'remoteFileLocation',
|
||||||
dcId: 2,
|
dcId: 2,
|
||||||
fileReference: defaultPlatform.hexDecode('0100099f2f608adbb3d7f57fb9228152e1bf9e1b7312d14e79'),
|
fileReference: hex.decode('0100099f2f608adbb3d7f57fb9228152e1bf9e1b7312d14e79'),
|
||||||
location: {
|
location: {
|
||||||
_: 'common',
|
_: 'common',
|
||||||
accessHash: Long.fromString('5232780349138767832'),
|
accessHash: Long.fromString('5232780349138767832'),
|
||||||
|
@ -27,7 +27,7 @@ describe('parsing file ids', () => {
|
||||||
test('BQACAgIAAxkBAAEJnzNgit00IDsKd07OdSeanwz8osecYAACdAwAAueoWEicaPvNdOYEwB8E', {
|
test('BQACAgIAAxkBAAEJnzNgit00IDsKd07OdSeanwz8osecYAACdAwAAueoWEicaPvNdOYEwB8E', {
|
||||||
_: 'remoteFileLocation',
|
_: 'remoteFileLocation',
|
||||||
dcId: 2,
|
dcId: 2,
|
||||||
fileReference: defaultPlatform.hexDecode('0100099f33608add34203b0a774ece75279a9f0cfca2c79c60'),
|
fileReference: hex.decode('0100099f33608add34203b0a774ece75279a9f0cfca2c79c60'),
|
||||||
location: {
|
location: {
|
||||||
_: 'common',
|
_: 'common',
|
||||||
accessHash: Long.fromString('-4610306729174144868'),
|
accessHash: Long.fromString('-4610306729174144868'),
|
||||||
|
@ -41,7 +41,7 @@ describe('parsing file ids', () => {
|
||||||
test('AAMCAgADGQEAAQmfL2CK27PX9X-5IoFS4b-eG3MS0U55AAJcWgAC4KOCB9iPD8oAAZKeSK1c8w4ABAEAB20AA1kCAAIfBA', {
|
test('AAMCAgADGQEAAQmfL2CK27PX9X-5IoFS4b-eG3MS0U55AAJcWgAC4KOCB9iPD8oAAZKeSK1c8w4ABAEAB20AA1kCAAIfBA', {
|
||||||
_: 'remoteFileLocation',
|
_: 'remoteFileLocation',
|
||||||
dcId: 2,
|
dcId: 2,
|
||||||
fileReference: defaultPlatform.hexDecode('0100099f2f608adbb3d7f57fb9228152e1bf9e1b7312d14e79'),
|
fileReference: hex.decode('0100099f2f608adbb3d7f57fb9228152e1bf9e1b7312d14e79'),
|
||||||
location: {
|
location: {
|
||||||
_: 'photo',
|
_: 'photo',
|
||||||
accessHash: Long.fromString('5232780349138767832'),
|
accessHash: Long.fromString('5232780349138767832'),
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { ITlPlatform } from '@mtcute/tl-runtime'
|
|
||||||
import { TlBinaryReader } from '@mtcute/tl-runtime'
|
import { TlBinaryReader } from '@mtcute/tl-runtime'
|
||||||
|
import { base64 } from '@fuman/utils'
|
||||||
|
|
||||||
import { tdFileId as td } from './types.js'
|
import { tdFileId as td } from './types.js'
|
||||||
import { telegramRleDecode } from './utils.js'
|
import { telegramRleDecode } from './utils.js'
|
||||||
|
@ -12,7 +12,7 @@ function parseWebFileLocation(reader: TlBinaryReader): td.RawWebRemoteFileLocati
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parsePhotoSizeSource(platform: ITlPlatform, reader: TlBinaryReader): td.TypePhotoSizeSource {
|
function parsePhotoSizeSource(reader: TlBinaryReader): td.TypePhotoSizeSource {
|
||||||
const variant = reader.int()
|
const variant = reader.int()
|
||||||
|
|
||||||
switch (variant) {
|
switch (variant) {
|
||||||
|
@ -26,7 +26,7 @@ function parsePhotoSizeSource(platform: ITlPlatform, reader: TlBinaryReader): td
|
||||||
|
|
||||||
if (fileType < 0 || fileType >= td.FileType.Size) {
|
if (fileType < 0 || fileType >= td.FileType.Size) {
|
||||||
throw new td.UnsupportedError(
|
throw new td.UnsupportedError(
|
||||||
`Unsupported file type: ${fileType} (${platform.base64Encode(reader.uint8View)})`,
|
`Unsupported file type: ${fileType} (${base64.encode(reader.uint8View)})`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ function parsePhotoSizeSource(platform: ITlPlatform, reader: TlBinaryReader): td
|
||||||
|
|
||||||
if (thumbnailType < 0 || thumbnailType > 255) {
|
if (thumbnailType < 0 || thumbnailType > 255) {
|
||||||
throw new td.InvalidFileIdError(
|
throw new td.InvalidFileIdError(
|
||||||
`Wrong thumbnail type: ${thumbnailType} (${platform.base64Encode(reader.uint8View)})`,
|
`Wrong thumbnail type: ${thumbnailType} (${base64.encode(reader.uint8View)})`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -113,13 +113,12 @@ function parsePhotoSizeSource(platform: ITlPlatform, reader: TlBinaryReader): td
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new td.UnsupportedError(
|
throw new td.UnsupportedError(
|
||||||
`Unsupported photo size source ${variant} (${platform.base64Encode(reader.uint8View)})`,
|
`Unsupported photo size source ${variant} (${base64.encode(reader.uint8View)})`,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parsePhotoFileLocation(
|
function parsePhotoFileLocation(
|
||||||
platform: ITlPlatform,
|
|
||||||
reader: TlBinaryReader,
|
reader: TlBinaryReader,
|
||||||
version: number,
|
version: number,
|
||||||
): td.RawPhotoRemoteFileLocation {
|
): td.RawPhotoRemoteFileLocation {
|
||||||
|
@ -128,13 +127,13 @@ function parsePhotoFileLocation(
|
||||||
let source: td.TypePhotoSizeSource
|
let source: td.TypePhotoSizeSource
|
||||||
|
|
||||||
if (version >= 32) {
|
if (version >= 32) {
|
||||||
source = parsePhotoSizeSource(platform, reader)
|
source = parsePhotoSizeSource(reader)
|
||||||
} else {
|
} else {
|
||||||
const volumeId = reader.long()
|
const volumeId = reader.long()
|
||||||
let localId = 0
|
let localId = 0
|
||||||
|
|
||||||
if (version >= 22) {
|
if (version >= 22) {
|
||||||
source = parsePhotoSizeSource(platform, reader)
|
source = parsePhotoSizeSource(reader)
|
||||||
localId = reader.int()
|
localId = reader.int()
|
||||||
} else {
|
} else {
|
||||||
source = {
|
source = {
|
||||||
|
@ -197,9 +196,9 @@ function parseCommonFileLocation(reader: TlBinaryReader): td.RawCommonRemoteFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromPersistentIdV23(platform: ITlPlatform, binary: Uint8Array, version: number): td.RawFullRemoteFileLocation {
|
function fromPersistentIdV23(binary: Uint8Array, version: number): td.RawFullRemoteFileLocation {
|
||||||
if (version < 0 || version > td.CURRENT_VERSION) {
|
if (version < 0 || version > td.CURRENT_VERSION) {
|
||||||
throw new td.UnsupportedError(`Unsupported file ID v3 subversion: ${version} (${platform.base64Encode(binary)})`)
|
throw new td.UnsupportedError(`Unsupported file ID v3 subversion: ${version} (${base64.encode(binary)})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
binary = telegramRleDecode(binary)
|
binary = telegramRleDecode(binary)
|
||||||
|
@ -215,7 +214,7 @@ function fromPersistentIdV23(platform: ITlPlatform, binary: Uint8Array, version:
|
||||||
fileType &= ~td.FILE_REFERENCE_FLAG
|
fileType &= ~td.FILE_REFERENCE_FLAG
|
||||||
|
|
||||||
if (fileType < 0 || fileType >= td.FileType.Size) {
|
if (fileType < 0 || fileType >= td.FileType.Size) {
|
||||||
throw new td.UnsupportedError(`Unsupported file type: ${fileType} (${platform.base64Encode(binary)})`)
|
throw new td.UnsupportedError(`Unsupported file type: ${fileType} (${base64.encode(binary)})`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const dcId = reader.int()
|
const dcId = reader.int()
|
||||||
|
@ -244,7 +243,7 @@ function fromPersistentIdV23(platform: ITlPlatform, binary: Uint8Array, version:
|
||||||
case td.FileType.EncryptedThumbnail:
|
case td.FileType.EncryptedThumbnail:
|
||||||
case td.FileType.Wallpaper: {
|
case td.FileType.Wallpaper: {
|
||||||
// location_type = photo
|
// location_type = photo
|
||||||
location = parsePhotoFileLocation(platform, reader, version)
|
location = parsePhotoFileLocation(reader, version)
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
switch (location.source._) {
|
switch (location.source._) {
|
||||||
|
@ -294,7 +293,7 @@ function fromPersistentIdV23(platform: ITlPlatform, binary: Uint8Array, version:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new td.UnsupportedError(`Invalid file type: ${fileType} (${platform.base64Encode(binary)})`)
|
throw new td.UnsupportedError(`Invalid file type: ${fileType} (${base64.encode(binary)})`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -307,14 +306,14 @@ function fromPersistentIdV23(platform: ITlPlatform, binary: Uint8Array, version:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromPersistentIdV2(platform: ITlPlatform, binary: Uint8Array) {
|
function fromPersistentIdV2(binary: Uint8Array) {
|
||||||
return fromPersistentIdV23(platform, binary.subarray(0, -1), 0)
|
return fromPersistentIdV23(binary.subarray(0, -1), 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
function fromPersistentIdV3(platform: ITlPlatform, binary: Uint8Array) {
|
function fromPersistentIdV3(binary: Uint8Array) {
|
||||||
const subversion = binary[binary.length - 2]
|
const subversion = binary[binary.length - 2]
|
||||||
|
|
||||||
return fromPersistentIdV23(platform, binary.subarray(0, -2), subversion)
|
return fromPersistentIdV23(binary.subarray(0, -2), subversion)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -322,18 +321,18 @@ function fromPersistentIdV3(platform: ITlPlatform, binary: Uint8Array) {
|
||||||
*
|
*
|
||||||
* @param fileId File ID as a base-64 encoded string or Buffer
|
* @param fileId File ID as a base-64 encoded string or Buffer
|
||||||
*/
|
*/
|
||||||
export function parseFileId(platform: ITlPlatform, fileId: string | Uint8Array): td.RawFullRemoteFileLocation {
|
export function parseFileId(fileId: string | Uint8Array): td.RawFullRemoteFileLocation {
|
||||||
if (typeof fileId === 'string') fileId = platform.base64Decode(fileId, true)
|
if (typeof fileId === 'string') fileId = base64.decode(fileId, true)
|
||||||
|
|
||||||
const version = fileId[fileId.length - 1]
|
const version = fileId[fileId.length - 1]
|
||||||
|
|
||||||
if (version === td.PERSISTENT_ID_VERSION_OLD) {
|
if (version === td.PERSISTENT_ID_VERSION_OLD) {
|
||||||
return fromPersistentIdV2(platform, fileId)
|
return fromPersistentIdV2(fileId)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version === td.PERSISTENT_ID_VERSION) {
|
if (version === td.PERSISTENT_ID_VERSION) {
|
||||||
return fromPersistentIdV3(platform, fileId)
|
return fromPersistentIdV3(fileId)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new td.UnsupportedError(`Unsupported file ID version: ${version} (${platform.base64Encode(fileId)})`)
|
throw new td.UnsupportedError(`Unsupported file ID version: ${version} (${base64.encode(fileId)})`)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { defaultPlatform } from '@mtcute/test'
|
|
||||||
|
|
||||||
import { parseFileId } from './parse.js'
|
import { parseFileId } from './parse.js'
|
||||||
import { toUniqueFileId } from './serialize-unique.js'
|
import { toUniqueFileId } from './serialize-unique.js'
|
||||||
|
@ -8,7 +7,7 @@ import { toUniqueFileId } from './serialize-unique.js'
|
||||||
|
|
||||||
describe('serializing unique file ids', () => {
|
describe('serializing unique file ids', () => {
|
||||||
const test = (id: string, expected: string) => {
|
const test = (id: string, expected: string) => {
|
||||||
expect(toUniqueFileId(defaultPlatform, parseFileId(defaultPlatform, id))).eql(expected)
|
expect(toUniqueFileId(parseFileId(id))).eql(expected)
|
||||||
}
|
}
|
||||||
|
|
||||||
it('serializes unique ids for old file ids', () => {
|
it('serializes unique ids for old file ids', () => {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { ITlPlatform } from '@mtcute/tl-runtime'
|
|
||||||
import { TlBinaryWriter } from '@mtcute/tl-runtime'
|
import { TlBinaryWriter } from '@mtcute/tl-runtime'
|
||||||
|
import { base64, utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { tdFileId as td } from './types.js'
|
import { tdFileId as td } from './types.js'
|
||||||
import { assertNever, telegramRleEncode } from './utils.js'
|
import { assertNever, telegramRleEncode } from './utils.js'
|
||||||
|
@ -21,11 +21,10 @@ export type InputUniqueLocation =
|
||||||
*
|
*
|
||||||
* @param location Information about file location
|
* @param location Information about file location
|
||||||
*/
|
*/
|
||||||
export function toUniqueFileId(platform: ITlPlatform, location: Omit<td.RawFullRemoteFileLocation, '_'>): string
|
export function toUniqueFileId(location: Omit<td.RawFullRemoteFileLocation, '_'>): string
|
||||||
export function toUniqueFileId(platform: ITlPlatform, type: td.FileType, location: InputUniqueLocation): string
|
export function toUniqueFileId(type: td.FileType, location: InputUniqueLocation): string
|
||||||
|
|
||||||
export function toUniqueFileId(
|
export function toUniqueFileId(
|
||||||
platform: ITlPlatform,
|
|
||||||
first: td.FileType | Omit<td.RawFullRemoteFileLocation, '_'>,
|
first: td.FileType | Omit<td.RawFullRemoteFileLocation, '_'>,
|
||||||
second?: InputUniqueLocation,
|
second?: InputUniqueLocation,
|
||||||
): string {
|
): string {
|
||||||
|
@ -142,7 +141,7 @@ export function toUniqueFileId(
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
case 'web':
|
case 'web':
|
||||||
writer = TlBinaryWriter.manual(platform.utf8ByteLength(inputLocation.url) + 8)
|
writer = TlBinaryWriter.manual(utf8.encodedLength(inputLocation.url) + 8)
|
||||||
writer.int(type)
|
writer.int(type)
|
||||||
writer.string(inputLocation.url)
|
writer.string(inputLocation.url)
|
||||||
break
|
break
|
||||||
|
@ -155,5 +154,5 @@ export function toUniqueFileId(
|
||||||
assertNever(inputLocation)
|
assertNever(inputLocation)
|
||||||
}
|
}
|
||||||
|
|
||||||
return platform.base64Encode(telegramRleEncode(writer.result()), true)
|
return base64.encode(telegramRleEncode(writer.result()), true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import type { ITlPlatform } from '@mtcute/tl-runtime'
|
|
||||||
import { TlBinaryWriter } from '@mtcute/tl-runtime'
|
import { TlBinaryWriter } from '@mtcute/tl-runtime'
|
||||||
|
import { base64, utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { tdFileId as td } from './types.js'
|
import { tdFileId as td } from './types.js'
|
||||||
import { assertNever, telegramRleEncode } from './utils.js'
|
import { assertNever, telegramRleEncode } from './utils.js'
|
||||||
|
@ -12,7 +12,7 @@ const SUFFIX = new Uint8Array([td.CURRENT_VERSION, td.PERSISTENT_ID_VERSION])
|
||||||
*
|
*
|
||||||
* @param location Information about file location
|
* @param location Information about file location
|
||||||
*/
|
*/
|
||||||
export function toFileId(platform: ITlPlatform, location: Omit<td.RawFullRemoteFileLocation, '_'>): string {
|
export function toFileId(location: Omit<td.RawFullRemoteFileLocation, '_'>): string {
|
||||||
const loc = location.location
|
const loc = location.location
|
||||||
|
|
||||||
let type: number = location.type
|
let type: number = location.type
|
||||||
|
@ -26,7 +26,7 @@ export function toFileId(platform: ITlPlatform, location: Omit<td.RawFullRemoteF
|
||||||
//
|
//
|
||||||
// longest file ids are around 80 bytes, so i guess
|
// longest file ids are around 80 bytes, so i guess
|
||||||
// we are safe with allocating 100 bytes
|
// we are safe with allocating 100 bytes
|
||||||
const writer = TlBinaryWriter.manual(loc._ === 'web' ? platform.utf8ByteLength(loc.url) + 32 : 100)
|
const writer = TlBinaryWriter.manual(loc._ === 'web' ? utf8.encodedLength(loc.url) + 32 : 100)
|
||||||
|
|
||||||
writer.int(type)
|
writer.int(type)
|
||||||
writer.int(location.dcId)
|
writer.int(location.dcId)
|
||||||
|
@ -109,5 +109,5 @@ export function toFileId(platform: ITlPlatform, location: Omit<td.RawFullRemoteF
|
||||||
withSuffix.set(result)
|
withSuffix.set(result)
|
||||||
withSuffix.set(SUFFIX, result.length)
|
withSuffix.set(SUFFIX, result.length)
|
||||||
|
|
||||||
return platform.base64Encode(withSuffix, true)
|
return base64.encode(withSuffix, true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +1,31 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { defaultPlatform } from '@mtcute/test'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { telegramRleDecode, telegramRleEncode } from './utils.js'
|
import { telegramRleDecode, telegramRleEncode } from './utils.js'
|
||||||
|
|
||||||
const p = defaultPlatform
|
|
||||||
|
|
||||||
describe('telegramRleEncode', () => {
|
describe('telegramRleEncode', () => {
|
||||||
it('should not modify input if there are no \\x00', () => {
|
it('should not modify input if there are no \\x00', () => {
|
||||||
expect(p.hexEncode(telegramRleEncode(p.hexDecode('aaeeff')))).eq('aaeeff')
|
expect(hex.encode(telegramRleEncode(hex.decode('aaeeff')))).eq('aaeeff')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should collapse consecutive \\x00', () => {
|
it('should collapse consecutive \\x00', () => {
|
||||||
expect(p.hexEncode(telegramRleEncode(p.hexDecode('00000000aa')))).eq('0004aa')
|
expect(hex.encode(telegramRleEncode(hex.decode('00000000aa')))).eq('0004aa')
|
||||||
expect(p.hexEncode(telegramRleEncode(p.hexDecode('00000000aa000000aa')))).eq('0004aa0003aa')
|
expect(hex.encode(telegramRleEncode(hex.decode('00000000aa000000aa')))).eq('0004aa0003aa')
|
||||||
expect(p.hexEncode(telegramRleEncode(p.hexDecode('00000000aa0000')))).eq('0004aa0002')
|
expect(hex.encode(telegramRleEncode(hex.decode('00000000aa0000')))).eq('0004aa0002')
|
||||||
expect(p.hexEncode(telegramRleEncode(p.hexDecode('00aa00')))).eq('0001aa0001')
|
expect(hex.encode(telegramRleEncode(hex.decode('00aa00')))).eq('0001aa0001')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('telegramRleDecode', () => {
|
describe('telegramRleDecode', () => {
|
||||||
it('should not mofify input if there are no \\x00', () => {
|
it('should not mofify input if there are no \\x00', () => {
|
||||||
expect(p.hexEncode(telegramRleDecode(p.hexDecode('aaeeff')))).eq('aaeeff')
|
expect(hex.encode(telegramRleDecode(hex.decode('aaeeff')))).eq('aaeeff')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should expand two-byte sequences starting with \\x00', () => {
|
it('should expand two-byte sequences starting with \\x00', () => {
|
||||||
expect(p.hexEncode(telegramRleDecode(p.hexDecode('0004aa')))).eq('00000000aa')
|
expect(hex.encode(telegramRleDecode(hex.decode('0004aa')))).eq('00000000aa')
|
||||||
expect(p.hexEncode(telegramRleDecode(p.hexDecode('0004aa0000')))).eq('00000000aa')
|
expect(hex.encode(telegramRleDecode(hex.decode('0004aa0000')))).eq('00000000aa')
|
||||||
expect(p.hexEncode(telegramRleDecode(p.hexDecode('0004aa0003aa')))).eq('00000000aa000000aa')
|
expect(hex.encode(telegramRleDecode(hex.decode('0004aa0003aa')))).eq('00000000aa000000aa')
|
||||||
expect(p.hexEncode(telegramRleDecode(p.hexDecode('0004aa0002')))).eq('00000000aa0000')
|
expect(hex.encode(telegramRleDecode(hex.decode('0004aa0002')))).eq('00000000aa0000')
|
||||||
expect(p.hexEncode(telegramRleDecode(p.hexDecode('0001aa0001')))).eq('00aa00')
|
expect(hex.encode(telegramRleDecode(hex.decode('0001aa0001')))).eq('00aa00')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -7,8 +7,6 @@ import { normalizeFile } from '../utils/normalize-file.js'
|
||||||
import { beforeExit } from './exit-hook.js'
|
import { beforeExit } from './exit-hook.js'
|
||||||
import { defaultLoggingHandler } from './logging.js'
|
import { defaultLoggingHandler } from './logging.js'
|
||||||
|
|
||||||
const BUFFER_BASE64_URL_AVAILABLE = typeof Buffer.isEncoding === 'function' && Buffer.isEncoding('base64url')
|
|
||||||
|
|
||||||
const toBuffer = (buf: Uint8Array): Buffer => Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)
|
const toBuffer = (buf: Uint8Array): Buffer => Buffer.from(buf.buffer, buf.byteOffset, buf.byteLength)
|
||||||
|
|
||||||
export class NodePlatform implements ICorePlatform {
|
export class NodePlatform implements ICorePlatform {
|
||||||
|
@ -30,51 +28,6 @@ export class NodePlatform implements ICorePlatform {
|
||||||
|
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
// ITlPlatform
|
|
||||||
utf8ByteLength(str: string): number {
|
|
||||||
return Buffer.byteLength(str, 'utf8')
|
|
||||||
}
|
|
||||||
|
|
||||||
utf8Encode(str: string): Uint8Array {
|
|
||||||
return Buffer.from(str, 'utf8')
|
|
||||||
}
|
|
||||||
|
|
||||||
utf8Decode(buf: Uint8Array): string {
|
|
||||||
return toBuffer(buf).toString('utf8')
|
|
||||||
}
|
|
||||||
|
|
||||||
hexEncode(buf: Uint8Array): string {
|
|
||||||
return toBuffer(buf).toString('hex')
|
|
||||||
}
|
|
||||||
|
|
||||||
hexDecode(str: string): Uint8Array {
|
|
||||||
return Buffer.from(str, 'hex')
|
|
||||||
}
|
|
||||||
|
|
||||||
base64Encode(buf: Uint8Array, url = false): string {
|
|
||||||
const nodeBuffer = toBuffer(buf)
|
|
||||||
|
|
||||||
if (url && BUFFER_BASE64_URL_AVAILABLE) return nodeBuffer.toString('base64url')
|
|
||||||
|
|
||||||
const str = nodeBuffer.toString('base64')
|
|
||||||
if (url) return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
|
|
||||||
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
base64Decode(string: string, url = false): Uint8Array {
|
|
||||||
if (url && BUFFER_BASE64_URL_AVAILABLE) {
|
|
||||||
return Buffer.from(string, 'base64url')
|
|
||||||
}
|
|
||||||
|
|
||||||
if (url) {
|
|
||||||
string = string.replace(/-/g, '+').replace(/_/g, '/')
|
|
||||||
while (string.length % 4) string += '='
|
|
||||||
}
|
|
||||||
|
|
||||||
return Buffer.from(string, 'base64')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NodePlatform.prototype.log = defaultLoggingHandler
|
NodePlatform.prototype.log = defaultLoggingHandler
|
||||||
|
|
|
@ -27,7 +27,8 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"long": "5.2.3"
|
"long": "5.2.3",
|
||||||
|
"@fuman/utils": "workspace:^"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/tl-utils": "workspace:^"
|
"@mtcute/tl-utils": "workspace:^"
|
||||||
|
|
|
@ -2,9 +2,9 @@ import { gzipSync, inflateSync } from 'node:zlib'
|
||||||
|
|
||||||
import type { MockInstance } from 'vitest'
|
import type { MockInstance } from 'vitest'
|
||||||
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
|
import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
|
||||||
import type { ICryptoProvider } from '@mtcute/core/utils.js'
|
import type { ICryptoProvider } from '@mtcute/core/utils.js'
|
||||||
import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
import { dataViewFromBuffer } from '@mtcute/core/utils.js'
|
||||||
|
import { hex, utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { defaultCryptoProvider } from './platform.js'
|
import { defaultCryptoProvider } from './platform.js'
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ fa3de8e50aac96c1275591a1221c32a60a1513370a33a228e00894341b10cf44a6ae6ac250d17a36
|
||||||
`.replace(/\s/g, '')
|
`.replace(/\s/g, '')
|
||||||
|
|
||||||
export function withFakeRandom(provider: ICryptoProvider, source: string = DEFAULT_ENTROPY): ICryptoProvider {
|
export function withFakeRandom(provider: ICryptoProvider, source: string = DEFAULT_ENTROPY): ICryptoProvider {
|
||||||
const sourceBytes = getPlatform().hexDecode(source)
|
const sourceBytes = hex.decode(source)
|
||||||
let offset = 0
|
let offset = 0
|
||||||
|
|
||||||
function getRandomValues(buf: Uint8Array) {
|
function getRandomValues(buf: Uint8Array) {
|
||||||
|
@ -52,7 +52,7 @@ export function withFakeRandom(provider: ICryptoProvider, source: string = DEFAU
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useFakeMathRandom(source: string = DEFAULT_ENTROPY): void {
|
export function useFakeMathRandom(source: string = DEFAULT_ENTROPY): void {
|
||||||
const sourceBytes = getPlatform().hexDecode(source)
|
const sourceBytes = hex.decode(source)
|
||||||
const dv = dataViewFromBuffer(sourceBytes)
|
const dv = dataViewFromBuffer(sourceBytes)
|
||||||
|
|
||||||
let spy: MockInstance<() => number>
|
let spy: MockInstance<() => number>
|
||||||
|
@ -82,8 +82,6 @@ export async function defaultTestCryptoProvider(source: string = DEFAULT_ENTROPY
|
||||||
export function testCryptoProvider(c: ICryptoProvider): void {
|
export function testCryptoProvider(c: ICryptoProvider): void {
|
||||||
beforeAll(() => c.initialize?.())
|
beforeAll(() => c.initialize?.())
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
function gzipSyncWrap(data: Uint8Array) {
|
function gzipSyncWrap(data: Uint8Array) {
|
||||||
if (import.meta.env.TEST_ENV === 'browser') {
|
if (import.meta.env.TEST_ENV === 'browser') {
|
||||||
// @ts-expect-error fucking crutch because @jspm/core uses Buffer.isBuffer for some reason
|
// @ts-expect-error fucking crutch because @jspm/core uses Buffer.isBuffer for some reason
|
||||||
|
@ -107,85 +105,85 @@ export function testCryptoProvider(c: ICryptoProvider): void {
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should calculate sha1', () => {
|
it('should calculate sha1', () => {
|
||||||
expect(p.hexEncode(c.sha1(p.utf8Encode('')))).to.eq('da39a3ee5e6b4b0d3255bfef95601890afd80709')
|
expect(hex.encode(c.sha1(utf8.encoder.encode('')))).to.eq('da39a3ee5e6b4b0d3255bfef95601890afd80709')
|
||||||
expect(p.hexEncode(c.sha1(p.utf8Encode('hello')))).to.eq('aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d')
|
expect(hex.encode(c.sha1(utf8.encoder.encode('hello')))).to.eq('aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d')
|
||||||
expect(p.hexEncode(c.sha1(p.hexDecode('aebb1f')))).to.eq('62849d15c5dea495916c5eea8dba5f9551288850')
|
expect(hex.encode(c.sha1(hex.decode('aebb1f')))).to.eq('62849d15c5dea495916c5eea8dba5f9551288850')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should calculate sha256', () => {
|
it('should calculate sha256', () => {
|
||||||
expect(p.hexEncode(c.sha256(p.utf8Encode('')))).to.eq(
|
expect(hex.encode(c.sha256(utf8.encoder.encode('')))).to.eq(
|
||||||
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
|
'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855',
|
||||||
)
|
)
|
||||||
expect(p.hexEncode(c.sha256(p.utf8Encode('hello')))).to.eq(
|
expect(hex.encode(c.sha256(utf8.encoder.encode('hello')))).to.eq(
|
||||||
'2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
|
'2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824',
|
||||||
)
|
)
|
||||||
expect(p.hexEncode(c.sha256(p.hexDecode('aebb1f')))).to.eq(
|
expect(hex.encode(c.sha256(hex.decode('aebb1f')))).to.eq(
|
||||||
'2d29658aba48f2b286fe8bbddb931b7ad297e5adb5b9a6fc3aab67ef7fbf4e80',
|
'2d29658aba48f2b286fe8bbddb931b7ad297e5adb5b9a6fc3aab67ef7fbf4e80',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should calculate hmac-sha256', async () => {
|
it('should calculate hmac-sha256', async () => {
|
||||||
const key = p.hexDecode('aaeeff')
|
const key = hex.decode('aaeeff')
|
||||||
|
|
||||||
expect(p.hexEncode(await c.hmacSha256(p.utf8Encode(''), key))).to.eq(
|
expect(hex.encode(await c.hmacSha256(utf8.encoder.encode(''), key))).to.eq(
|
||||||
'642711307c9e4437df09d6ebaa6bdc1b3a810c7f15c50fd1d0f8d7d5490f44dd',
|
'642711307c9e4437df09d6ebaa6bdc1b3a810c7f15c50fd1d0f8d7d5490f44dd',
|
||||||
)
|
)
|
||||||
expect(p.hexEncode(await c.hmacSha256(p.utf8Encode('hello'), key))).to.eq(
|
expect(hex.encode(await c.hmacSha256(utf8.encoder.encode('hello'), key))).to.eq(
|
||||||
'39b00bab151f9868e6501655c580b5542954711181243474d46b894703b1c1c2',
|
'39b00bab151f9868e6501655c580b5542954711181243474d46b894703b1c1c2',
|
||||||
)
|
)
|
||||||
expect(p.hexEncode(await c.hmacSha256(p.hexDecode('aebb1f'), key))).to.eq(
|
expect(hex.encode(await c.hmacSha256(hex.decode('aebb1f'), key))).to.eq(
|
||||||
'a3a7273871808711cab17aba14f58e96f63f3ccfc5097d206f0f00ead2c3dd35',
|
'a3a7273871808711cab17aba14f58e96f63f3ccfc5097d206f0f00ead2c3dd35',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should derive pbkdf2 key', async () => {
|
it('should derive pbkdf2 key', async () => {
|
||||||
expect(p.hexEncode(await c.pbkdf2(p.utf8Encode('pbkdf2 test'), p.utf8Encode('some salt'), 10))).to.eq(
|
expect(hex.encode(await c.pbkdf2(utf8.encoder.encode('pbkdf2 test'), utf8.encoder.encode('some salt'), 10))).to.eq(
|
||||||
'e43276cfa27f135f261cec8ddcf593fd74ec251038e459c165461f2308f3a7235e0744ee1aed9710b00db28d1a2112e20fea3601c60e770ac57ffe6b33ca8be1',
|
'e43276cfa27f135f261cec8ddcf593fd74ec251038e459c165461f2308f3a7235e0744ee1aed9710b00db28d1a2112e20fea3601c60e770ac57ffe6b33ca8be1',
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should encrypt and decrypt aes-ctr', () => {
|
it('should encrypt and decrypt aes-ctr', () => {
|
||||||
let aes = c.createAesCtr(
|
let aes = c.createAesCtr(
|
||||||
p.hexDecode('d450aae0bf0060a4af1044886b42a13f7c506b35255d134a7e87ab3f23a9493b'),
|
hex.decode('d450aae0bf0060a4af1044886b42a13f7c506b35255d134a7e87ab3f23a9493b'),
|
||||||
p.hexDecode('0182de2bd789c295c3c6c875c5e9e190'),
|
hex.decode('0182de2bd789c295c3c6c875c5e9e190'),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
|
||||||
const data = p.hexDecode('7baae571e4c2f4cfadb1931d5923aca7')
|
const data = hex.decode('7baae571e4c2f4cfadb1931d5923aca7')
|
||||||
expect(p.hexEncode(aes.process(data))).eq('df5647dbb70bc393f2fb05b72f42286f')
|
expect(hex.encode(aes.process(data))).eq('df5647dbb70bc393f2fb05b72f42286f')
|
||||||
expect(p.hexEncode(aes.process(data))).eq('3917147082672516b3177150129bc579')
|
expect(hex.encode(aes.process(data))).eq('3917147082672516b3177150129bc579')
|
||||||
expect(p.hexEncode(aes.process(data))).eq('2a7a9089270a5de45d5e3dd399cac725')
|
expect(hex.encode(aes.process(data))).eq('2a7a9089270a5de45d5e3dd399cac725')
|
||||||
expect(p.hexEncode(aes.process(data))).eq('56d085217771398ac13583de4d677dd8')
|
expect(hex.encode(aes.process(data))).eq('56d085217771398ac13583de4d677dd8')
|
||||||
expect(p.hexEncode(aes.process(data))).eq('cc639b488126cf36e79c4515e8012b92')
|
expect(hex.encode(aes.process(data))).eq('cc639b488126cf36e79c4515e8012b92')
|
||||||
expect(p.hexEncode(aes.process(data))).eq('01384d100646cd562cc5586ec3f8f8c4')
|
expect(hex.encode(aes.process(data))).eq('01384d100646cd562cc5586ec3f8f8c4')
|
||||||
|
|
||||||
aes.close?.()
|
aes.close?.()
|
||||||
aes = c.createAesCtr(
|
aes = c.createAesCtr(
|
||||||
p.hexDecode('d450aae0bf0060a4af1044886b42a13f7c506b35255d134a7e87ab3f23a9493b'),
|
hex.decode('d450aae0bf0060a4af1044886b42a13f7c506b35255d134a7e87ab3f23a9493b'),
|
||||||
p.hexDecode('0182de2bd789c295c3c6c875c5e9e190'),
|
hex.decode('0182de2bd789c295c3c6c875c5e9e190'),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
|
||||||
expect(p.hexEncode(aes.process(p.hexDecode('df5647dbb70bc393f2fb05b72f42286f')))).eq(p.hexEncode(data))
|
expect(hex.encode(aes.process(hex.decode('df5647dbb70bc393f2fb05b72f42286f')))).eq(hex.encode(data))
|
||||||
expect(p.hexEncode(aes.process(p.hexDecode('3917147082672516b3177150129bc579')))).eq(p.hexEncode(data))
|
expect(hex.encode(aes.process(hex.decode('3917147082672516b3177150129bc579')))).eq(hex.encode(data))
|
||||||
expect(p.hexEncode(aes.process(p.hexDecode('2a7a9089270a5de45d5e3dd399cac725')))).eq(p.hexEncode(data))
|
expect(hex.encode(aes.process(hex.decode('2a7a9089270a5de45d5e3dd399cac725')))).eq(hex.encode(data))
|
||||||
expect(p.hexEncode(aes.process(p.hexDecode('56d085217771398ac13583de4d677dd8')))).eq(p.hexEncode(data))
|
expect(hex.encode(aes.process(hex.decode('56d085217771398ac13583de4d677dd8')))).eq(hex.encode(data))
|
||||||
expect(p.hexEncode(aes.process(p.hexDecode('cc639b488126cf36e79c4515e8012b92')))).eq(p.hexEncode(data))
|
expect(hex.encode(aes.process(hex.decode('cc639b488126cf36e79c4515e8012b92')))).eq(hex.encode(data))
|
||||||
expect(p.hexEncode(aes.process(p.hexDecode('01384d100646cd562cc5586ec3f8f8c4')))).eq(p.hexEncode(data))
|
expect(hex.encode(aes.process(hex.decode('01384d100646cd562cc5586ec3f8f8c4')))).eq(hex.encode(data))
|
||||||
|
|
||||||
aes.close?.()
|
aes.close?.()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should encrypt and decrypt aes-ige', () => {
|
it('should encrypt and decrypt aes-ige', () => {
|
||||||
const aes = c.createAesIge(
|
const aes = c.createAesIge(
|
||||||
p.hexDecode('5468697320697320616E20696D706C655468697320697320616E20696D706C65'),
|
hex.decode('5468697320697320616E20696D706C655468697320697320616E20696D706C65'),
|
||||||
p.hexDecode('6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353'),
|
hex.decode('6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353'),
|
||||||
)
|
)
|
||||||
expect(
|
expect(
|
||||||
p.hexEncode(aes.encrypt(p.hexDecode('99706487a1cde613bc6de0b6f24b1c7aa448c8b9c3403e3467a8cad89340f53b'))),
|
hex.encode(aes.encrypt(hex.decode('99706487a1cde613bc6de0b6f24b1c7aa448c8b9c3403e3467a8cad89340f53b'))),
|
||||||
).to.eq('792ea8ae577b1a66cb3bd92679b8030ca54ee631976bd3a04547fdcb4639fa69')
|
).to.eq('792ea8ae577b1a66cb3bd92679b8030ca54ee631976bd3a04547fdcb4639fa69')
|
||||||
expect(
|
expect(
|
||||||
p.hexEncode(aes.decrypt(p.hexDecode('792ea8ae577b1a66cb3bd92679b8030ca54ee631976bd3a04547fdcb4639fa69'))),
|
hex.encode(aes.decrypt(hex.decode('792ea8ae577b1a66cb3bd92679b8030ca54ee631976bd3a04547fdcb4639fa69'))),
|
||||||
).to.eq('99706487a1cde613bc6de0b6f24b1c7aa448c8b9c3403e3467a8cad89340f53b')
|
).to.eq('99706487a1cde613bc6de0b6f24b1c7aa448c8b9c3403e3467a8cad89340f53b')
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -193,9 +191,9 @@ export function testCryptoProvider(c: ICryptoProvider): void {
|
||||||
'should decompose PQ to prime factors P and Q',
|
'should decompose PQ to prime factors P and Q',
|
||||||
async () => {
|
async () => {
|
||||||
const testFactorization = async (pq: string, p_: string, q: string) => {
|
const testFactorization = async (pq: string, p_: string, q: string) => {
|
||||||
const [p1, q1] = await c.factorizePQ(p.hexDecode(pq))
|
const [p1, q1] = await c.factorizePQ(hex.decode(pq))
|
||||||
expect(p.hexEncode(p1)).eq(p_.toLowerCase())
|
expect(hex.encode(p1)).eq(p_.toLowerCase())
|
||||||
expect(p.hexEncode(q1)).eq(q.toLowerCase())
|
expect(hex.encode(q1)).eq(q.toLowerCase())
|
||||||
}
|
}
|
||||||
|
|
||||||
// from samples at https://core.telegram.org/mtproto/samples-auth_key
|
// from samples at https://core.telegram.org/mtproto/samples-auth_key
|
||||||
|
@ -217,7 +215,7 @@ export function testCryptoProvider(c: ICryptoProvider): void {
|
||||||
const decompressed = inflateSyncWrap(compressed!)
|
const decompressed = inflateSyncWrap(compressed!)
|
||||||
|
|
||||||
expect(compressed!.length).toBeLessThan(data.length)
|
expect(compressed!.length).toBeLessThan(data.length)
|
||||||
expect(p.hexEncode(decompressed)).toEqual(p.hexEncode(data))
|
expect(hex.encode(decompressed)).toEqual(hex.encode(data))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly gunzip', () => {
|
it('should correctly gunzip', () => {
|
||||||
|
@ -226,7 +224,7 @@ export function testCryptoProvider(c: ICryptoProvider): void {
|
||||||
const compressed = gzipSyncWrap(data)
|
const compressed = gzipSyncWrap(data)
|
||||||
const decompressed = c.gunzip(compressed)
|
const decompressed = c.gunzip(compressed)
|
||||||
|
|
||||||
expect(p.hexEncode(decompressed)).toEqual(p.hexEncode(data))
|
expect(hex.encode(decompressed)).toEqual(hex.encode(data))
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('randomBytes', () => {
|
describe('randomBytes', () => {
|
||||||
|
@ -249,14 +247,3 @@ export function testCryptoProvider(c: ICryptoProvider): void {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function u8HexDecode(hex: string): Uint8Array {
|
|
||||||
const buf = getPlatform().hexDecode(hex)
|
|
||||||
|
|
||||||
// eslint-disable-next-line no-restricted-globals
|
|
||||||
if ((import.meta.env.TEST_ENV === 'node' || import.meta.env.TEST_ENV === 'bun') && Buffer.isBuffer(buf)) {
|
|
||||||
return new Uint8Array(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buf
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,59 +0,0 @@
|
||||||
import { describe, expect, it } from 'vitest'
|
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
describe('base64', () => {
|
|
||||||
it('should decode base64 string to new buffer', () => {
|
|
||||||
const buf = p.base64Decode('AQIDBA==')
|
|
||||||
expect(new Uint8Array(buf)).toEqual(new Uint8Array([1, 2, 3, 4]))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should encode buffer to base64 string', () => {
|
|
||||||
const buf = new Uint8Array([1, 2, 3, 4])
|
|
||||||
expect(p.base64Encode(buf)).toEqual('AQIDBA==')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should decode url-safe base64 string to new buffer', () => {
|
|
||||||
const buf = p.base64Decode('AQIDBA', true)
|
|
||||||
expect(new Uint8Array(buf)).toEqual(new Uint8Array([1, 2, 3, 4]))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should encode buffer to url-safe base64 string', () => {
|
|
||||||
const buf = new Uint8Array([1, 2, 3, 4])
|
|
||||||
expect(p.base64Encode(buf, true)).toEqual('AQIDBA')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('hex', () => {
|
|
||||||
it('should decode hex string to new buffer', () => {
|
|
||||||
const buf = p.hexDecode('01020304')
|
|
||||||
expect(new Uint8Array(buf)).toEqual(new Uint8Array([1, 2, 3, 4]))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should encode buffer to hex string', () => {
|
|
||||||
const buf = new Uint8Array([1, 2, 3, 4])
|
|
||||||
expect(p.hexEncode(buf)).toEqual('01020304')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
describe('utf8', () => {
|
|
||||||
it('should encode utf8 string into new buffer', () => {
|
|
||||||
const buf = p.utf8Encode('abcd')
|
|
||||||
expect(new Uint8Array(buf)).toEqual(new Uint8Array([97, 98, 99, 100]))
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should decode utf8 string from existing buffer', () => {
|
|
||||||
const buf = new Uint8Array([97, 98, 99, 100])
|
|
||||||
expect(p.utf8Decode(buf)).toEqual('abcd')
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should return byte length of utf8 string', () => {
|
|
||||||
expect(p.utf8ByteLength('abcd')).toEqual(4)
|
|
||||||
})
|
|
||||||
|
|
||||||
it('should properly handle utf8 string with non-ascii characters', () => {
|
|
||||||
expect(p.utf8ByteLength('абвг')).toEqual(8)
|
|
||||||
expect(p.utf8ByteLength('🌸')).toEqual(4)
|
|
||||||
})
|
|
||||||
})
|
|
|
@ -12,5 +12,4 @@ are patching the schema (which is a rare case anyways).
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- Supports all TL features used by the public schema
|
- Supports all TL features used by the public schema
|
||||||
- Uint8Array utilities like `hexDecode`
|
|
||||||
- Supports browsers out of the box
|
- Supports browsers out of the box
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
"build": "pnpm run -w build-package tl-runtime"
|
"build": "pnpm run -w build-package tl-runtime"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"long": "5.2.3"
|
"long": "5.2.3",
|
||||||
|
"@fuman/utils": "workspace:^"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,2 @@
|
||||||
export * from './platform.js'
|
|
||||||
export * from './reader.js'
|
export * from './reader.js'
|
||||||
export * from './writer.js'
|
export * from './writer.js'
|
||||||
|
|
|
@ -1,14 +0,0 @@
|
||||||
/**
|
|
||||||
* Platform-specific functions used by {@link TlBinaryReader} and {@link TlBinaryWriter}
|
|
||||||
*/
|
|
||||||
export interface ITlPlatform {
|
|
||||||
utf8Encode: (str: string) => Uint8Array
|
|
||||||
utf8Decode: (buf: Uint8Array) => string
|
|
||||||
utf8ByteLength: (str: string) => number
|
|
||||||
|
|
||||||
hexEncode: (buf: Uint8Array) => string
|
|
||||||
hexDecode: (str: string) => Uint8Array
|
|
||||||
|
|
||||||
base64Encode: (buf: Uint8Array, url?: boolean) => string
|
|
||||||
base64Decode: (str: string, url?: boolean) => Uint8Array
|
|
||||||
}
|
|
|
@ -3,14 +3,11 @@
|
||||||
// import Long from 'long'
|
// import Long from 'long'
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import type { TlReaderMap } from './reader.js'
|
import type { TlReaderMap } from './reader.js'
|
||||||
import { TlBinaryReader } from './reader.js'
|
import { TlBinaryReader } from './reader.js'
|
||||||
|
|
||||||
// todo: replace with platform-specific packages
|
|
||||||
const hexEncode = (buf: Uint8Array) => buf.reduce((acc, val) => acc + val.toString(16).padStart(2, '0'), '')
|
|
||||||
const hexDecodeToBuffer = (hex: string) => new Uint8Array(hex.match(/.{1,2}/g)!.map(byte => Number.parseInt(byte, 16)))
|
|
||||||
|
|
||||||
let randomBytes: (n: number) => Uint8Array
|
let randomBytes: (n: number) => Uint8Array
|
||||||
|
|
||||||
if (import.meta.env.TEST_ENV === 'node' || import.meta.env.TEST_ENV === 'bun') {
|
if (import.meta.env.TEST_ENV === 'node' || import.meta.env.TEST_ENV === 'bun') {
|
||||||
|
@ -287,22 +284,22 @@ describe('TlBinaryReader', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const expected = {
|
const expected = {
|
||||||
nonce: hexDecodeToBuffer('3E0549828CCA27E966B301A48FECE2FC'),
|
nonce: hex.decode('3E0549828CCA27E966B301A48FECE2FC'),
|
||||||
serverNonce: hexDecodeToBuffer('A5CF4D33F4A11EA877BA4AA573907330'),
|
serverNonce: hex.decode('A5CF4D33F4A11EA877BA4AA573907330'),
|
||||||
pq: hexDecodeToBuffer('17ED48941A08F981'),
|
pq: hex.decode('17ED48941A08F981'),
|
||||||
serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', false, 16)],
|
serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', false, 16)],
|
||||||
}
|
}
|
||||||
|
|
||||||
const r = new TlBinaryReader(map, hexDecodeToBuffer(input))
|
const r = new TlBinaryReader(map, hex.decode(input))
|
||||||
expect(r.long().toString()).toEqual('0') // authKeyId
|
expect(r.long().toString()).toEqual('0') // authKeyId
|
||||||
expect(r.long().toString(16)).toEqual('51E57AC91E83C801'.toLowerCase()) // messageId
|
expect(r.long().toString(16)).toEqual('51E57AC91E83C801'.toLowerCase()) // messageId
|
||||||
expect(r.uint()).toEqual(64) // messageLength
|
expect(r.uint()).toEqual(64) // messageLength
|
||||||
|
|
||||||
const obj = r.object() as any
|
const obj = r.object() as any
|
||||||
expect(obj._).toEqual('mt_resPQ')
|
expect(obj._).toEqual('mt_resPQ')
|
||||||
expect(hexEncode(obj.nonce)).toEqual(hexEncode(expected.nonce))
|
expect(hex.encode(obj.nonce)).toEqual(hex.encode(expected.nonce))
|
||||||
expect(hexEncode(obj.serverNonce)).toEqual(hexEncode(expected.serverNonce))
|
expect(hex.encode(obj.serverNonce)).toEqual(hex.encode(expected.serverNonce))
|
||||||
expect(hexEncode(obj.pq)).toEqual(hexEncode(expected.pq))
|
expect(hex.encode(obj.pq)).toEqual(hex.encode(expected.pq))
|
||||||
expect(obj.serverPublicKeyFingerprints.length).toEqual(1)
|
expect(obj.serverPublicKeyFingerprints.length).toEqual(1)
|
||||||
expect(obj.serverPublicKeyFingerprints[0].toString(16)).toEqual(
|
expect(obj.serverPublicKeyFingerprints[0].toString(16)).toEqual(
|
||||||
expected.serverPublicKeyFingerprints[0].toString(16),
|
expected.serverPublicKeyFingerprints[0].toString(16),
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
import { utf8 } from '@fuman/utils'
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
|
|
||||||
import type { ITlPlatform } from './platform.js'
|
|
||||||
|
|
||||||
const TWO_PWR_32_DBL = (1 << 16) * (1 << 16)
|
const TWO_PWR_32_DBL = (1 << 16) * (1 << 16)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,8 +24,6 @@ export type TlReaderMap = Record<number, (r: any) => unknown> & {
|
||||||
* Reader for TL objects.
|
* Reader for TL objects.
|
||||||
*/
|
*/
|
||||||
export class TlBinaryReader {
|
export class TlBinaryReader {
|
||||||
static platform: ITlPlatform
|
|
||||||
|
|
||||||
readonly dataView: DataView
|
readonly dataView: DataView
|
||||||
readonly uint8View: Uint8Array
|
readonly uint8View: Uint8Array
|
||||||
|
|
||||||
|
@ -173,7 +170,7 @@ export class TlBinaryReader {
|
||||||
}
|
}
|
||||||
|
|
||||||
string(): string {
|
string(): string {
|
||||||
return TlBinaryReader.platform.utf8Decode(this.bytes())
|
return utf8.decoder.decode(this.bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
object(id: number = this.uint()): unknown {
|
object(id: number = this.uint()): unknown {
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
/* eslint-disable ts/no-unsafe-call */
|
/* eslint-disable ts/no-unsafe-call */
|
||||||
import Long from 'long'
|
import Long from 'long'
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import type { TlWriterMap } from './writer.js'
|
import type { TlWriterMap } from './writer.js'
|
||||||
import { TlBinaryWriter, TlSerializationCounter } from './writer.js'
|
import { TlBinaryWriter, TlSerializationCounter } from './writer.js'
|
||||||
|
|
||||||
// todo: replace with platform-specific packages
|
|
||||||
const hexEncode = (buf: Uint8Array) => buf.reduce((acc, val) => acc + val.toString(16).padStart(2, '0'), '')
|
|
||||||
const hexDecodeToBuffer = (hex: string) => new Uint8Array(hex.match(/.{1,2}/g)!.map(byte => Number.parseInt(byte, 16)))
|
|
||||||
|
|
||||||
let randomBytes: (n: number) => Uint8Array
|
let randomBytes: (n: number) => Uint8Array
|
||||||
|
|
||||||
if (import.meta.env.TEST_ENV === 'node' || import.meta.env.TEST_ENV === 'bun') {
|
if (import.meta.env.TEST_ENV === 'node' || import.meta.env.TEST_ENV === 'bun') {
|
||||||
|
@ -28,7 +25,7 @@ describe('TlBinaryWriter', () => {
|
||||||
fn(w)
|
fn(w)
|
||||||
expect(w.pos).toEqual(size)
|
expect(w.pos).toEqual(size)
|
||||||
|
|
||||||
return hexEncode(w.uint8View)
|
return hex.encode(w.uint8View)
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should write int32', () => {
|
it('should write int32', () => {
|
||||||
|
@ -85,14 +82,14 @@ describe('TlBinaryWriter', () => {
|
||||||
expect(testSingleMethod(8, w => w.bytes(new Uint8Array([1, 2, 3, 4])))).toEqual('0401020304000000')
|
expect(testSingleMethod(8, w => w.bytes(new Uint8Array([1, 2, 3, 4])))).toEqual('0401020304000000')
|
||||||
|
|
||||||
const random250bytes = randomBytes(250)
|
const random250bytes = randomBytes(250)
|
||||||
expect(testSingleMethod(252, w => w.bytes(random250bytes))).toEqual(`fa${hexEncode(random250bytes)}00`)
|
expect(testSingleMethod(252, w => w.bytes(random250bytes))).toEqual(`fa${hex.encode(random250bytes)}00`)
|
||||||
|
|
||||||
const random1000bytes = randomBytes(1000)
|
const random1000bytes = randomBytes(1000)
|
||||||
const buffer = new Uint8Array(1004)
|
const buffer = new Uint8Array(1004)
|
||||||
buffer[0] = 254
|
buffer[0] = 254
|
||||||
new DataView(buffer.buffer).setUint32(1, 1000, true)
|
new DataView(buffer.buffer).setUint32(1, 1000, true)
|
||||||
buffer.set(random1000bytes, 4)
|
buffer.set(random1000bytes, 4)
|
||||||
expect(testSingleMethod(1004, w => w.bytes(random1000bytes))).toEqual(hexEncode(buffer))
|
expect(testSingleMethod(1004, w => w.bytes(random1000bytes))).toEqual(hex.encode(buffer))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should write tg-encoded string', () => {
|
it('should write tg-encoded string', () => {
|
||||||
|
@ -189,9 +186,9 @@ describe('TlBinaryWriter', () => {
|
||||||
|
|
||||||
const resPq = {
|
const resPq = {
|
||||||
_: 'mt_resPQ',
|
_: 'mt_resPQ',
|
||||||
nonce: hexDecodeToBuffer('3E0549828CCA27E966B301A48FECE2FC'),
|
nonce: hex.decode('3E0549828CCA27E966B301A48FECE2FC'),
|
||||||
serverNonce: hexDecodeToBuffer('A5CF4D33F4A11EA877BA4AA573907330'),
|
serverNonce: hex.decode('A5CF4D33F4A11EA877BA4AA573907330'),
|
||||||
pq: hexDecodeToBuffer('17ED48941A08F981'),
|
pq: hex.decode('17ED48941A08F981'),
|
||||||
serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', 16)],
|
serverPublicKeyFingerprints: [Long.fromString('c3b42b026ce86b21', 16)],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
|
import { utf8 } from '@fuman/utils'
|
||||||
import type Long from 'long'
|
import type Long from 'long'
|
||||||
|
|
||||||
import type { ITlPlatform } from './platform.js'
|
|
||||||
|
|
||||||
const TWO_PWR_32_DBL = (1 << 16) * (1 << 16)
|
const TWO_PWR_32_DBL = (1 << 16) * (1 << 16)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -115,7 +114,7 @@ export class TlSerializationCounter {
|
||||||
}
|
}
|
||||||
|
|
||||||
string(val: string): void {
|
string(val: string): void {
|
||||||
const length = TlBinaryWriter.platform.utf8ByteLength(val)
|
const length = utf8.encodedLength(val)
|
||||||
this.count += TlSerializationCounter.countBytesOverhead(length) + length
|
this.count += TlSerializationCounter.countBytesOverhead(length) + length
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,8 +133,6 @@ export class TlSerializationCounter {
|
||||||
* Writer for TL objects.
|
* Writer for TL objects.
|
||||||
*/
|
*/
|
||||||
export class TlBinaryWriter {
|
export class TlBinaryWriter {
|
||||||
static platform: ITlPlatform
|
|
||||||
|
|
||||||
readonly dataView: DataView
|
readonly dataView: DataView
|
||||||
readonly uint8View: Uint8Array
|
readonly uint8View: Uint8Array
|
||||||
|
|
||||||
|
@ -305,7 +302,7 @@ export class TlBinaryWriter {
|
||||||
}
|
}
|
||||||
|
|
||||||
string(val: string): void {
|
string(val: string): void {
|
||||||
this.bytes(TlBinaryWriter.platform.utf8Encode(val))
|
this.bytes(utf8.encoder.encode(val))
|
||||||
}
|
}
|
||||||
|
|
||||||
// hot path, avoid additional runtime checks
|
// hot path, avoid additional runtime checks
|
||||||
|
|
|
@ -19,7 +19,8 @@
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@mtcute/core": "workspace:^",
|
"@mtcute/core": "workspace:^",
|
||||||
"@mtcute/node": "workspace:^",
|
"@mtcute/node": "workspace:^",
|
||||||
"@mtcute/web": "workspace:^"
|
"@mtcute/web": "workspace:^",
|
||||||
|
"@fuman/utils": "workspace:^"
|
||||||
},
|
},
|
||||||
"jsrOnlyFields": {
|
"jsrOnlyFields": {
|
||||||
"exports": "./src/index.ts"
|
"exports": "./src/index.ts"
|
||||||
|
|
|
@ -1,29 +1,27 @@
|
||||||
import { beforeAll, describe, expect, it } from 'vitest'
|
import { beforeAll, describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { __getWasm, createCtr256, ctr256, freeCtr256 } from '../src/index.js'
|
import { __getWasm, createCtr256, ctr256, freeCtr256 } from '../src/index.js'
|
||||||
|
|
||||||
import { initWasm } from './init.js'
|
import { initWasm } from './init.js'
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initWasm()
|
await initWasm()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('aes-ctr', () => {
|
describe('aes-ctr', () => {
|
||||||
const key = p.hexDecode('603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4')
|
const key = hex.decode('603DEB1015CA71BE2B73AEF0857D77811F352C073B6108D72D9810A30914DFF4')
|
||||||
const iv = p.hexDecode('F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF')
|
const iv = hex.decode('F0F1F2F3F4F5F6F7F8F9FAFBFCFDFEFF')
|
||||||
|
|
||||||
describe('NIST', () => {
|
describe('NIST', () => {
|
||||||
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CTR.pdf
|
// https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/AES_CTR.pdf
|
||||||
const data = p.hexDecode(
|
const data = hex.decode(
|
||||||
`6BC1BEE2 2E409F96 E93D7E11 7393172A
|
`6BC1BEE2 2E409F96 E93D7E11 7393172A
|
||||||
AE2D8A57 1E03AC9C 9EB76FAC 45AF8E51
|
AE2D8A57 1E03AC9C 9EB76FAC 45AF8E51
|
||||||
30C81C46 A35CE411 E5FBC119 1A0A52EF
|
30C81C46 A35CE411 E5FBC119 1A0A52EF
|
||||||
F69F2445 DF4F9B17 AD2B417B E66C3710`.replace(/\s/g, ''),
|
F69F2445 DF4F9B17 AD2B417B E66C3710`.replace(/\s/g, ''),
|
||||||
)
|
)
|
||||||
const dataEnc = p.hexDecode(
|
const dataEnc = hex.decode(
|
||||||
`601EC313 775789A5 B7A7F504 BBF3D228
|
`601EC313 775789A5 B7A7F504 BBF3D228
|
||||||
F443E3CA 4D62B59A CA84E990 CACAF5C5
|
F443E3CA 4D62B59A CA84E990 CACAF5C5
|
||||||
2B0930DA A23DE94C E87017BA 2D84988D
|
2B0930DA A23DE94C E87017BA 2D84988D
|
||||||
|
@ -35,7 +33,7 @@ describe('aes-ctr', () => {
|
||||||
const res = ctr256(ctr, data)
|
const res = ctr256(ctr, data)
|
||||||
freeCtr256(ctr)
|
freeCtr256(ctr)
|
||||||
|
|
||||||
expect(p.hexEncode(res)).toEqual(p.hexEncode(dataEnc))
|
expect(hex.encode(res)).toEqual(hex.encode(dataEnc))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly decrypt', () => {
|
it('should correctly decrypt', () => {
|
||||||
|
@ -43,15 +41,15 @@ describe('aes-ctr', () => {
|
||||||
const res = ctr256(ctr, dataEnc)
|
const res = ctr256(ctr, dataEnc)
|
||||||
freeCtr256(ctr)
|
freeCtr256(ctr)
|
||||||
|
|
||||||
expect(p.hexEncode(res)).toEqual(p.hexEncode(data))
|
expect(hex.encode(res)).toEqual(hex.encode(data))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('stream', () => {
|
describe('stream', () => {
|
||||||
const data = p.hexDecode('6BC1BEE22E409F96E93D7E117393172A')
|
const data = hex.decode('6BC1BEE22E409F96E93D7E117393172A')
|
||||||
const dataEnc1 = p.hexDecode('601ec313775789a5b7a7f504bbf3d228')
|
const dataEnc1 = hex.decode('601ec313775789a5b7a7f504bbf3d228')
|
||||||
const dataEnc2 = p.hexDecode('31afd77f7d218690bd0ef82dfcf66cbe')
|
const dataEnc2 = hex.decode('31afd77f7d218690bd0ef82dfcf66cbe')
|
||||||
const dataEnc3 = p.hexDecode('7000927e2f2192cbe4b6a8b2441ddd48')
|
const dataEnc3 = hex.decode('7000927e2f2192cbe4b6a8b2441ddd48')
|
||||||
|
|
||||||
it('should correctly encrypt', () => {
|
it('should correctly encrypt', () => {
|
||||||
const ctr = createCtr256(key, iv)
|
const ctr = createCtr256(key, iv)
|
||||||
|
@ -61,9 +59,9 @@ describe('aes-ctr', () => {
|
||||||
|
|
||||||
freeCtr256(ctr)
|
freeCtr256(ctr)
|
||||||
|
|
||||||
expect(p.hexEncode(res1)).toEqual(p.hexEncode(dataEnc1))
|
expect(hex.encode(res1)).toEqual(hex.encode(dataEnc1))
|
||||||
expect(p.hexEncode(res2)).toEqual(p.hexEncode(dataEnc2))
|
expect(hex.encode(res2)).toEqual(hex.encode(dataEnc2))
|
||||||
expect(p.hexEncode(res3)).toEqual(p.hexEncode(dataEnc3))
|
expect(hex.encode(res3)).toEqual(hex.encode(dataEnc3))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly decrypt', () => {
|
it('should correctly decrypt', () => {
|
||||||
|
@ -74,20 +72,20 @@ describe('aes-ctr', () => {
|
||||||
|
|
||||||
freeCtr256(ctr)
|
freeCtr256(ctr)
|
||||||
|
|
||||||
expect(p.hexEncode(res1)).toEqual(p.hexEncode(data))
|
expect(hex.encode(res1)).toEqual(hex.encode(data))
|
||||||
expect(p.hexEncode(res2)).toEqual(p.hexEncode(data))
|
expect(hex.encode(res2)).toEqual(hex.encode(data))
|
||||||
expect(p.hexEncode(res3)).toEqual(p.hexEncode(data))
|
expect(hex.encode(res3)).toEqual(hex.encode(data))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('stream (unaligned)', () => {
|
describe('stream (unaligned)', () => {
|
||||||
const data = p.hexDecode('6BC1BEE22E40')
|
const data = hex.decode('6BC1BEE22E40')
|
||||||
const dataEnc1 = p.hexDecode('601ec3137757')
|
const dataEnc1 = hex.decode('601ec3137757')
|
||||||
const dataEnc2 = p.hexDecode('7df2e078a555')
|
const dataEnc2 = hex.decode('7df2e078a555')
|
||||||
const dataEnc3 = p.hexDecode('a3a17be0742e')
|
const dataEnc3 = hex.decode('a3a17be0742e')
|
||||||
const dataEnc4 = p.hexDecode('025ced833746')
|
const dataEnc4 = hex.decode('025ced833746')
|
||||||
const dataEnc5 = p.hexDecode('3ff238dea125')
|
const dataEnc5 = hex.decode('3ff238dea125')
|
||||||
const dataEnc6 = p.hexDecode('1055a52302dc')
|
const dataEnc6 = hex.decode('1055a52302dc')
|
||||||
|
|
||||||
it('should correctly encrypt', () => {
|
it('should correctly encrypt', () => {
|
||||||
const ctr = createCtr256(key, iv)
|
const ctr = createCtr256(key, iv)
|
||||||
|
@ -100,12 +98,12 @@ describe('aes-ctr', () => {
|
||||||
|
|
||||||
freeCtr256(ctr)
|
freeCtr256(ctr)
|
||||||
|
|
||||||
expect(p.hexEncode(res1)).toEqual(p.hexEncode(dataEnc1))
|
expect(hex.encode(res1)).toEqual(hex.encode(dataEnc1))
|
||||||
expect(p.hexEncode(res2)).toEqual(p.hexEncode(dataEnc2))
|
expect(hex.encode(res2)).toEqual(hex.encode(dataEnc2))
|
||||||
expect(p.hexEncode(res3)).toEqual(p.hexEncode(dataEnc3))
|
expect(hex.encode(res3)).toEqual(hex.encode(dataEnc3))
|
||||||
expect(p.hexEncode(res4)).toEqual(p.hexEncode(dataEnc4))
|
expect(hex.encode(res4)).toEqual(hex.encode(dataEnc4))
|
||||||
expect(p.hexEncode(res5)).toEqual(p.hexEncode(dataEnc5))
|
expect(hex.encode(res5)).toEqual(hex.encode(dataEnc5))
|
||||||
expect(p.hexEncode(res6)).toEqual(p.hexEncode(dataEnc6))
|
expect(hex.encode(res6)).toEqual(hex.encode(dataEnc6))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly decrypt', () => {
|
it('should correctly decrypt', () => {
|
||||||
|
@ -119,17 +117,17 @@ describe('aes-ctr', () => {
|
||||||
|
|
||||||
freeCtr256(ctr)
|
freeCtr256(ctr)
|
||||||
|
|
||||||
expect(p.hexEncode(res1)).toEqual(p.hexEncode(data))
|
expect(hex.encode(res1)).toEqual(hex.encode(data))
|
||||||
expect(p.hexEncode(res2)).toEqual(p.hexEncode(data))
|
expect(hex.encode(res2)).toEqual(hex.encode(data))
|
||||||
expect(p.hexEncode(res3)).toEqual(p.hexEncode(data))
|
expect(hex.encode(res3)).toEqual(hex.encode(data))
|
||||||
expect(p.hexEncode(res4)).toEqual(p.hexEncode(data))
|
expect(hex.encode(res4)).toEqual(hex.encode(data))
|
||||||
expect(p.hexEncode(res5)).toEqual(p.hexEncode(data))
|
expect(hex.encode(res5)).toEqual(hex.encode(data))
|
||||||
expect(p.hexEncode(res6)).toEqual(p.hexEncode(data))
|
expect(hex.encode(res6)).toEqual(hex.encode(data))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not leak memory', () => {
|
it('should not leak memory', () => {
|
||||||
const data = p.hexDecode('6BC1BEE22E409F96E93D7E117393172A')
|
const data = hex.decode('6BC1BEE22E409F96E93D7E117393172A')
|
||||||
const mem = __getWasm().memory.buffer
|
const mem = __getWasm().memory.buffer
|
||||||
const memSize = mem.byteLength
|
const memSize = mem.byteLength
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { gzipSync } from 'node:zlib'
|
import { gzipSync } from 'node:zlib'
|
||||||
|
|
||||||
import { beforeAll, describe, expect, it } from 'vitest'
|
import { beforeAll, describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { __getWasm, gunzip } from '../src/index.js'
|
import { __getWasm, gunzip } from '../src/index.js'
|
||||||
|
|
||||||
|
@ -11,8 +11,6 @@ beforeAll(async () => {
|
||||||
await initWasm()
|
await initWasm()
|
||||||
})
|
})
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
function gzipSyncWrap(data: Uint8Array) {
|
function gzipSyncWrap(data: Uint8Array) {
|
||||||
if (import.meta.env.TEST_ENV === 'browser' || import.meta.env.TEST_ENV === 'deno') {
|
if (import.meta.env.TEST_ENV === 'browser' || import.meta.env.TEST_ENV === 'deno') {
|
||||||
// @ts-expect-error fucking crutch because @jspm/core uses Buffer.isBuffer for some reason
|
// @ts-expect-error fucking crutch because @jspm/core uses Buffer.isBuffer for some reason
|
||||||
|
@ -27,7 +25,7 @@ function gzipSyncWrap(data: Uint8Array) {
|
||||||
describe('gunzip', () => {
|
describe('gunzip', () => {
|
||||||
it('should correctly read zlib headers', () => {
|
it('should correctly read zlib headers', () => {
|
||||||
const wasm = __getWasm()
|
const wasm = __getWasm()
|
||||||
const data = gzipSyncWrap(p.utf8Encode('hello world'))
|
const data = gzipSyncWrap(utf8.encoder.encode('hello world'))
|
||||||
|
|
||||||
const inputPtr = wasm.__malloc(data.length)
|
const inputPtr = wasm.__malloc(data.length)
|
||||||
new Uint8Array(wasm.memory.buffer).set(data, inputPtr)
|
new Uint8Array(wasm.memory.buffer).set(data, inputPtr)
|
||||||
|
@ -37,11 +35,11 @@ describe('gunzip', () => {
|
||||||
|
|
||||||
it('should correctly inflate', () => {
|
it('should correctly inflate', () => {
|
||||||
const data = Array.from({ length: 1000 }, () => 'a').join('')
|
const data = Array.from({ length: 1000 }, () => 'a').join('')
|
||||||
const res = gzipSyncWrap(p.utf8Encode(data))
|
const res = gzipSyncWrap(utf8.encoder.encode(data))
|
||||||
|
|
||||||
expect(res).not.toBeNull()
|
expect(res).not.toBeNull()
|
||||||
expect(res.length).toBeLessThan(100)
|
expect(res.length).toBeLessThan(100)
|
||||||
expect(gunzip(res)).toEqual(new Uint8Array(p.utf8Encode(data)))
|
expect(gunzip(res)).toEqual(new Uint8Array(utf8.encoder.encode(data)))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not leak memory', () => {
|
it('should not leak memory', () => {
|
||||||
|
@ -49,11 +47,11 @@ describe('gunzip', () => {
|
||||||
|
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
const data = Array.from({ length: 1000 }, () => 'a').join('')
|
const data = Array.from({ length: 1000 }, () => 'a').join('')
|
||||||
const deflated = gzipSyncWrap(p.utf8Encode(data))
|
const deflated = gzipSyncWrap(utf8.encoder.encode(data))
|
||||||
|
|
||||||
const res = gunzip(deflated)
|
const res = gunzip(deflated)
|
||||||
|
|
||||||
expect(p.utf8Decode(res)).toEqual(data)
|
expect(utf8.decoder.decode(res)).toEqual(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(__getWasm().memory.buffer.byteLength).toEqual(memSize)
|
expect(__getWasm().memory.buffer.byteLength).toEqual(memSize)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { beforeAll, describe, expect, it } from 'vitest'
|
import { beforeAll, describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { hex, utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { __getWasm, sha1, sha256 } from '../src/index.js'
|
import { __getWasm, sha1, sha256 } from '../src/index.js'
|
||||||
|
|
||||||
|
@ -9,13 +9,11 @@ beforeAll(async () => {
|
||||||
await initWasm()
|
await initWasm()
|
||||||
})
|
})
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
describe('sha256', () => {
|
describe('sha256', () => {
|
||||||
it('should correctly calculate sha-256 hash', () => {
|
it('should correctly calculate sha-256 hash', () => {
|
||||||
const hash = sha256(p.utf8Encode('abc'))
|
const hash = sha256(utf8.encoder.encode('abc'))
|
||||||
|
|
||||||
expect(p.hexEncode(hash)).toEqual('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad')
|
expect(hex.encode(hash)).toEqual('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not leak memory', () => {
|
it('should not leak memory', () => {
|
||||||
|
@ -23,7 +21,7 @@ describe('sha256', () => {
|
||||||
const memSize = mem.byteLength
|
const memSize = mem.byteLength
|
||||||
|
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
sha256(p.utf8Encode('abc'))
|
sha256(utf8.encoder.encode('abc'))
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(mem.byteLength).toEqual(memSize)
|
expect(mem.byteLength).toEqual(memSize)
|
||||||
|
@ -32,9 +30,9 @@ describe('sha256', () => {
|
||||||
|
|
||||||
describe('sha1', () => {
|
describe('sha1', () => {
|
||||||
it('should correctly calculate sha-1 hash', () => {
|
it('should correctly calculate sha-1 hash', () => {
|
||||||
const hash = sha1(p.utf8Encode('abc'))
|
const hash = sha1(utf8.encoder.encode('abc'))
|
||||||
|
|
||||||
expect(p.hexEncode(hash)).toEqual('a9993e364706816aba3e25717850c26c9cd0d89d')
|
expect(hex.encode(hash)).toEqual('a9993e364706816aba3e25717850c26c9cd0d89d')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not leak memory', () => {
|
it('should not leak memory', () => {
|
||||||
|
@ -42,7 +40,7 @@ describe('sha1', () => {
|
||||||
const memSize = mem.byteLength
|
const memSize = mem.byteLength
|
||||||
|
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
sha1(p.utf8Encode('abc'))
|
sha1(utf8.encoder.encode('abc'))
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(mem.byteLength).toEqual(memSize)
|
expect(mem.byteLength).toEqual(memSize)
|
||||||
|
|
|
@ -1,33 +1,31 @@
|
||||||
import { beforeAll, describe, expect, it } from 'vitest'
|
import { beforeAll, describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { hex } from '@fuman/utils'
|
||||||
|
|
||||||
import { __getWasm, ige256Decrypt, ige256Encrypt } from '../src/index.js'
|
import { __getWasm, ige256Decrypt, ige256Encrypt } from '../src/index.js'
|
||||||
|
|
||||||
import { initWasm } from './init.js'
|
import { initWasm } from './init.js'
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
await initWasm()
|
await initWasm()
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('aes-ige', () => {
|
describe('aes-ige', () => {
|
||||||
const key = p.hexDecode('5468697320697320616E20696D706C655468697320697320616E20696D706C65')
|
const key = hex.decode('5468697320697320616E20696D706C655468697320697320616E20696D706C65')
|
||||||
const iv = p.hexDecode('6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353')
|
const iv = hex.decode('6D656E746174696F6E206F6620494745206D6F646520666F72204F70656E5353')
|
||||||
|
|
||||||
const data = p.hexDecode('99706487a1cde613bc6de0b6f24b1c7aa448c8b9c3403e3467a8cad89340f53b')
|
const data = hex.decode('99706487a1cde613bc6de0b6f24b1c7aa448c8b9c3403e3467a8cad89340f53b')
|
||||||
const dataEnc = p.hexDecode('792ea8ae577b1a66cb3bd92679b8030ca54ee631976bd3a04547fdcb4639fa69')
|
const dataEnc = hex.decode('792ea8ae577b1a66cb3bd92679b8030ca54ee631976bd3a04547fdcb4639fa69')
|
||||||
|
|
||||||
it('should correctly encrypt', () => {
|
it('should correctly encrypt', () => {
|
||||||
const aes = ige256Encrypt(data, key, iv)
|
const aes = ige256Encrypt(data, key, iv)
|
||||||
|
|
||||||
expect(p.hexEncode(aes)).toEqual(p.hexEncode(dataEnc))
|
expect(hex.encode(aes)).toEqual(hex.encode(dataEnc))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly decrypt', () => {
|
it('should correctly decrypt', () => {
|
||||||
const aes = ige256Decrypt(dataEnc, key, iv)
|
const aes = ige256Decrypt(dataEnc, key, iv)
|
||||||
|
|
||||||
expect(p.hexEncode(aes)).toEqual(p.hexEncode(data))
|
expect(hex.encode(aes)).toEqual(hex.encode(data))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not leak memory', () => {
|
it('should not leak memory', () => {
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
{
|
{
|
||||||
"extends": "../../../tsconfig.json",
|
"extends": "../../../tsconfig.json",
|
||||||
"references": [
|
|
||||||
{ "path": "../" }
|
|
||||||
],
|
|
||||||
"include": [
|
"include": [
|
||||||
"."
|
"."
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { inflateSync } from 'node:zlib'
|
import { inflateSync } from 'node:zlib'
|
||||||
|
|
||||||
import { beforeAll, describe, expect, it } from 'vitest'
|
import { beforeAll, describe, expect, it } from 'vitest'
|
||||||
import { getPlatform } from '@mtcute/core/platform.js'
|
import { utf8 } from '@fuman/utils'
|
||||||
|
|
||||||
import { __getWasm, deflateMaxSize } from '../src/index.js'
|
import { __getWasm, deflateMaxSize } from '../src/index.js'
|
||||||
|
|
||||||
|
@ -11,8 +11,6 @@ beforeAll(async () => {
|
||||||
await initWasm()
|
await initWasm()
|
||||||
})
|
})
|
||||||
|
|
||||||
const p = getPlatform()
|
|
||||||
|
|
||||||
function inflateSyncWrap(data: Uint8Array) {
|
function inflateSyncWrap(data: Uint8Array) {
|
||||||
if (import.meta.env.TEST_ENV === 'browser' || import.meta.env.TEST_ENV === 'deno') {
|
if (import.meta.env.TEST_ENV === 'browser' || import.meta.env.TEST_ENV === 'deno') {
|
||||||
// @ts-expect-error fucking crutch because @jspm/core uses Buffer.isBuffer for some reason
|
// @ts-expect-error fucking crutch because @jspm/core uses Buffer.isBuffer for some reason
|
||||||
|
@ -26,25 +24,25 @@ function inflateSyncWrap(data: Uint8Array) {
|
||||||
|
|
||||||
describe('zlib deflate', () => {
|
describe('zlib deflate', () => {
|
||||||
it('should add zlib headers', () => {
|
it('should add zlib headers', () => {
|
||||||
const res = deflateMaxSize(p.utf8Encode('hello world'), 100)
|
const res = deflateMaxSize(utf8.encoder.encode('hello world'), 100)
|
||||||
|
|
||||||
expect(res).not.toBeNull()
|
expect(res).not.toBeNull()
|
||||||
expect(res!.slice(0, 2)).toEqual(new Uint8Array([0x78, 0x9C]))
|
expect(res!.slice(0, 2)).toEqual(new Uint8Array([0x78, 0x9C]))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should return null if compressed data is larger than size', () => {
|
it('should return null if compressed data is larger than size', () => {
|
||||||
const res = deflateMaxSize(p.utf8Encode('hello world'), 1)
|
const res = deflateMaxSize(utf8.encoder.encode('hello world'), 1)
|
||||||
|
|
||||||
expect(res).toBeNull()
|
expect(res).toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should correctly deflate', () => {
|
it('should correctly deflate', () => {
|
||||||
const data = Array.from({ length: 1000 }, () => 'a').join('')
|
const data = Array.from({ length: 1000 }, () => 'a').join('')
|
||||||
const res = deflateMaxSize(p.utf8Encode(data), 100)
|
const res = deflateMaxSize(utf8.encoder.encode(data), 100)
|
||||||
|
|
||||||
expect(res).not.toBeNull()
|
expect(res).not.toBeNull()
|
||||||
expect(res!.length).toBeLessThan(100)
|
expect(res!.length).toBeLessThan(100)
|
||||||
expect(inflateSyncWrap(res!)).toEqual(p.utf8Encode(data))
|
expect(inflateSyncWrap(res!)).toEqual(utf8.encoder.encode(data))
|
||||||
})
|
})
|
||||||
|
|
||||||
it('should not leak memory', () => {
|
it('should not leak memory', () => {
|
||||||
|
@ -52,11 +50,11 @@ describe('zlib deflate', () => {
|
||||||
|
|
||||||
for (let i = 0; i < 100; i++) {
|
for (let i = 0; i < 100; i++) {
|
||||||
const data = Array.from({ length: 1000 }, () => 'a').join('')
|
const data = Array.from({ length: 1000 }, () => 'a').join('')
|
||||||
const deflated = deflateMaxSize(p.utf8Encode(data), 100)
|
const deflated = deflateMaxSize(utf8.encoder.encode(data), 100)
|
||||||
|
|
||||||
const res = inflateSyncWrap(deflated!)
|
const res = inflateSyncWrap(deflated!)
|
||||||
|
|
||||||
expect(p.utf8Decode(res)).toEqual(data)
|
expect(utf8.decoder.decode(res)).toEqual(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(__getWasm().memory.buffer.byteLength).toEqual(memSize)
|
expect(__getWasm().memory.buffer.byteLength).toEqual(memSize)
|
||||||
|
|
|
@ -1,132 +0,0 @@
|
||||||
/// Based on https://github.com/beatgammit/base64-js, MIT license
|
|
||||||
const lookup: string[] = []
|
|
||||||
const revLookup: number[] = []
|
|
||||||
|
|
||||||
const code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
|
|
||||||
|
|
||||||
for (let i = 0, len = code.length; i < len; ++i) {
|
|
||||||
lookup[i] = code[i]
|
|
||||||
revLookup[code.charCodeAt(i)] = i
|
|
||||||
}
|
|
||||||
|
|
||||||
function getLens(b64: string): [number, number] {
|
|
||||||
const len = b64.length
|
|
||||||
|
|
||||||
if (len % 4 > 0) {
|
|
||||||
throw new Error('Invalid string. Length must be a multiple of 4')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trim off extra bytes after placeholder bytes are found
|
|
||||||
// See: https://github.com/beatgammit/base64-js/issues/42
|
|
||||||
let validLen = b64.indexOf('=')
|
|
||||||
if (validLen === -1) validLen = len
|
|
||||||
|
|
||||||
const placeHoldersLen = validLen === len ? 0 : 4 - (validLen % 4)
|
|
||||||
|
|
||||||
return [validLen, placeHoldersLen]
|
|
||||||
}
|
|
||||||
|
|
||||||
function _byteLength(b64: string, validLen: number, placeHoldersLen: number) {
|
|
||||||
return ((validLen + placeHoldersLen) * 3) / 4 - placeHoldersLen
|
|
||||||
}
|
|
||||||
|
|
||||||
function toByteArray(b64: string, arr: Uint8Array) {
|
|
||||||
let tmp
|
|
||||||
const lens = getLens(b64)
|
|
||||||
const validLen = lens[0]
|
|
||||||
const placeHoldersLen = lens[1]
|
|
||||||
|
|
||||||
let curByte = 0
|
|
||||||
|
|
||||||
// if there are placeholders, only get up to the last complete 4 chars
|
|
||||||
const len = placeHoldersLen > 0 ? validLen - 4 : validLen
|
|
||||||
|
|
||||||
let i
|
|
||||||
|
|
||||||
for (i = 0; i < len; i += 4) {
|
|
||||||
tmp
|
|
||||||
= (revLookup[b64.charCodeAt(i)] << 18)
|
|
||||||
| (revLookup[b64.charCodeAt(i + 1)] << 12)
|
|
||||||
| (revLookup[b64.charCodeAt(i + 2)] << 6)
|
|
||||||
| revLookup[b64.charCodeAt(i + 3)]
|
|
||||||
arr[curByte++] = (tmp >> 16) & 0xFF
|
|
||||||
arr[curByte++] = (tmp >> 8) & 0xFF
|
|
||||||
arr[curByte++] = tmp & 0xFF
|
|
||||||
}
|
|
||||||
|
|
||||||
if (placeHoldersLen === 2) {
|
|
||||||
tmp = (revLookup[b64.charCodeAt(i)] << 2) | (revLookup[b64.charCodeAt(i + 1)] >> 4)
|
|
||||||
arr[curByte++] = tmp & 0xFF
|
|
||||||
}
|
|
||||||
|
|
||||||
if (placeHoldersLen === 1) {
|
|
||||||
tmp
|
|
||||||
= (revLookup[b64.charCodeAt(i)] << 10)
|
|
||||||
| (revLookup[b64.charCodeAt(i + 1)] << 4)
|
|
||||||
| (revLookup[b64.charCodeAt(i + 2)] >> 2)
|
|
||||||
arr[curByte++] = (tmp >> 8) & 0xFF
|
|
||||||
arr[curByte++] = tmp & 0xFF
|
|
||||||
}
|
|
||||||
|
|
||||||
return arr
|
|
||||||
}
|
|
||||||
|
|
||||||
function tripletToBase64(num: number) {
|
|
||||||
return lookup[(num >> 18) & 0x3F] + lookup[(num >> 12) & 0x3F] + lookup[(num >> 6) & 0x3F] + lookup[num & 0x3F]
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeChunk(uint8: Uint8Array, start: number, end: number) {
|
|
||||||
let tmp
|
|
||||||
const output = []
|
|
||||||
|
|
||||||
for (let i = start; i < end; i += 3) {
|
|
||||||
tmp = ((uint8[i] << 16) & 0xFF0000) + ((uint8[i + 1] << 8) & 0xFF00) + (uint8[i + 2] & 0xFF)
|
|
||||||
output.push(tripletToBase64(tmp))
|
|
||||||
}
|
|
||||||
|
|
||||||
return output.join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromByteArray(uint8: Uint8Array) {
|
|
||||||
let tmp
|
|
||||||
const len = uint8.length
|
|
||||||
const extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
|
|
||||||
const parts = []
|
|
||||||
const maxChunkLength = 16383 // must be multiple of 3
|
|
||||||
|
|
||||||
// go through the array every three bytes, we'll deal with trailing stuff later
|
|
||||||
for (let i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
|
|
||||||
parts.push(encodeChunk(uint8, i, i + maxChunkLength > len2 ? len2 : i + maxChunkLength))
|
|
||||||
}
|
|
||||||
|
|
||||||
// pad the end with zeros, but make sure to not forget the extra bytes
|
|
||||||
if (extraBytes === 1) {
|
|
||||||
tmp = uint8[len - 1]
|
|
||||||
parts.push(`${lookup[tmp >> 2] + lookup[(tmp << 4) & 0x3F]}==`)
|
|
||||||
} else if (extraBytes === 2) {
|
|
||||||
tmp = (uint8[len - 2] << 8) + uint8[len - 1]
|
|
||||||
parts.push(`${lookup[tmp >> 10] + lookup[(tmp >> 4) & 0x3F] + lookup[(tmp << 2) & 0x3F]}=`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return parts.join('')
|
|
||||||
}
|
|
||||||
|
|
||||||
export function base64Encode(buf: Uint8Array, url: boolean = false): string {
|
|
||||||
const str = fromByteArray(buf)
|
|
||||||
if (url) return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '')
|
|
||||||
|
|
||||||
return str
|
|
||||||
}
|
|
||||||
|
|
||||||
export function base64Decode(string: string, url: boolean = false): Uint8Array {
|
|
||||||
if (url) {
|
|
||||||
string = string.replace(/-/g, '+').replace(/_/g, '/')
|
|
||||||
while (string.length % 4) string += '='
|
|
||||||
}
|
|
||||||
|
|
||||||
const buf = new Uint8Array(_byteLength(string, ...getLens(string)))
|
|
||||||
|
|
||||||
toByteArray(string, buf)
|
|
||||||
|
|
||||||
return buf
|
|
||||||
}
|
|
|
@ -1,75 +0,0 @@
|
||||||
/// Based on https://github.com/feross/buffer, MIT license
|
|
||||||
|
|
||||||
const hexSliceLookupTable = (function () {
|
|
||||||
const alphabet = '0123456789abcdef'
|
|
||||||
const table: string[] = Array.from({ length: 256 })
|
|
||||||
|
|
||||||
for (let i = 0; i < 16; ++i) {
|
|
||||||
const i16 = i * 16
|
|
||||||
|
|
||||||
for (let j = 0; j < 16; ++j) {
|
|
||||||
table[i16 + j] = alphabet[i] + alphabet[j]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return table
|
|
||||||
})()
|
|
||||||
|
|
||||||
const hexCharValueTable: Record<string, number> = {
|
|
||||||
0: 0,
|
|
||||||
1: 1,
|
|
||||||
2: 2,
|
|
||||||
3: 3,
|
|
||||||
4: 4,
|
|
||||||
5: 5,
|
|
||||||
6: 6,
|
|
||||||
7: 7,
|
|
||||||
8: 8,
|
|
||||||
9: 9,
|
|
||||||
a: 10,
|
|
||||||
b: 11,
|
|
||||||
c: 12,
|
|
||||||
d: 13,
|
|
||||||
e: 14,
|
|
||||||
f: 15,
|
|
||||||
A: 10,
|
|
||||||
B: 11,
|
|
||||||
C: 12,
|
|
||||||
D: 13,
|
|
||||||
E: 14,
|
|
||||||
F: 15,
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hexEncode(buf: Uint8Array): string {
|
|
||||||
let out = ''
|
|
||||||
|
|
||||||
for (let i = 0; i < buf.byteLength; ++i) {
|
|
||||||
out += hexSliceLookupTable[buf[i]]
|
|
||||||
}
|
|
||||||
|
|
||||||
return out
|
|
||||||
}
|
|
||||||
|
|
||||||
function hexDecodeInner(buf: Uint8Array, string: string): void {
|
|
||||||
const strLen = string.length
|
|
||||||
const length = Math.min(buf.length, strLen / 2)
|
|
||||||
|
|
||||||
let i
|
|
||||||
|
|
||||||
for (i = 0; i < length; ++i) {
|
|
||||||
const a = hexCharValueTable[string[i * 2]]
|
|
||||||
const b = hexCharValueTable[string[i * 2 + 1]]
|
|
||||||
|
|
||||||
if (a === undefined || b === undefined) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
buf[i] = (a << 4) | b
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export function hexDecode(string: string): Uint8Array {
|
|
||||||
const buf = new Uint8Array(Math.ceil(string.length / 2))
|
|
||||||
hexDecodeInner(buf, string)
|
|
||||||
|
|
||||||
return buf
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
const sharedEncoder = new TextEncoder()
|
|
||||||
const sharedDecoder = new TextDecoder('utf8')
|
|
||||||
|
|
||||||
export function utf8ByteLength(str: string): number {
|
|
||||||
// https://stackoverflow.com/a/23329386
|
|
||||||
let s = str.length
|
|
||||||
|
|
||||||
for (let i = str.length - 1; i >= 0; i--) {
|
|
||||||
const code = str.charCodeAt(i)
|
|
||||||
if (code > 0x7F && code <= 0x7FF) s++
|
|
||||||
else if (code > 0x7FF && code <= 0xFFFF) s += 2
|
|
||||||
if (code >= 0xDC00 && code <= 0xDFFF) i-- // trail surrogate
|
|
||||||
}
|
|
||||||
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
export function utf8Decode(buf: Uint8Array): string {
|
|
||||||
return sharedDecoder.decode(buf)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function utf8Encode(str: string): Uint8Array {
|
|
||||||
return sharedEncoder.encode(str)
|
|
||||||
}
|
|
|
@ -1,9 +1,6 @@
|
||||||
import type { ICorePlatform } from '@mtcute/core/platform.js'
|
import type { ICorePlatform } from '@mtcute/core/platform.js'
|
||||||
|
|
||||||
import { base64Decode, base64Encode } from './common-internals-web/base64.js'
|
|
||||||
import { hexDecode, hexEncode } from './common-internals-web/hex.js'
|
|
||||||
import { defaultLoggingHandler } from './common-internals-web/logging.js'
|
import { defaultLoggingHandler } from './common-internals-web/logging.js'
|
||||||
import { utf8ByteLength, utf8Decode, utf8Encode } from './common-internals-web/utf8.js'
|
|
||||||
import { beforeExit } from './exit-hook.js'
|
import { beforeExit } from './exit-hook.js'
|
||||||
|
|
||||||
export class WebPlatform implements ICorePlatform {
|
export class WebPlatform implements ICorePlatform {
|
||||||
|
@ -45,23 +42,7 @@ export class WebPlatform implements ICorePlatform {
|
||||||
isOnline(): boolean {
|
isOnline(): boolean {
|
||||||
return navigator.onLine
|
return navigator.onLine
|
||||||
}
|
}
|
||||||
|
|
||||||
// ITlPlatform
|
|
||||||
declare utf8ByteLength: typeof utf8ByteLength
|
|
||||||
declare utf8Encode: typeof utf8Encode
|
|
||||||
declare utf8Decode: typeof utf8Decode
|
|
||||||
declare hexEncode: typeof hexEncode
|
|
||||||
declare hexDecode: typeof hexDecode
|
|
||||||
declare base64Encode: typeof base64Encode
|
|
||||||
declare base64Decode: typeof base64Decode
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebPlatform.prototype.log = defaultLoggingHandler
|
WebPlatform.prototype.log = defaultLoggingHandler
|
||||||
WebPlatform.prototype.beforeExit = beforeExit
|
WebPlatform.prototype.beforeExit = beforeExit
|
||||||
WebPlatform.prototype.utf8ByteLength = utf8ByteLength
|
|
||||||
WebPlatform.prototype.utf8Encode = utf8Encode
|
|
||||||
WebPlatform.prototype.utf8Decode = utf8Decode
|
|
||||||
WebPlatform.prototype.hexEncode = hexEncode
|
|
||||||
WebPlatform.prototype.hexDecode = hexDecode
|
|
||||||
WebPlatform.prototype.base64Encode = base64Encode
|
|
||||||
WebPlatform.prototype.base64Decode = base64Decode
|
|
||||||
|
|
|
@ -129,6 +129,9 @@ importers:
|
||||||
|
|
||||||
packages/convert:
|
packages/convert:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@fuman/utils':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../private/fuman/packages/utils
|
||||||
'@mtcute/core':
|
'@mtcute/core':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../core
|
version: link:../core
|
||||||
|
@ -270,16 +273,15 @@ importers:
|
||||||
|
|
||||||
packages/file-id:
|
packages/file-id:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@fuman/utils':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../private/fuman/packages/utils
|
||||||
'@mtcute/tl-runtime':
|
'@mtcute/tl-runtime':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../tl-runtime
|
version: link:../tl-runtime
|
||||||
long:
|
long:
|
||||||
specifier: 5.2.3
|
specifier: 5.2.3
|
||||||
version: 5.2.3
|
version: 5.2.3
|
||||||
devDependencies:
|
|
||||||
'@mtcute/test':
|
|
||||||
specifier: workspace:^
|
|
||||||
version: link:../test
|
|
||||||
|
|
||||||
packages/html-parser:
|
packages/html-parser:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -344,6 +346,9 @@ importers:
|
||||||
|
|
||||||
packages/test:
|
packages/test:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@fuman/utils':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../private/fuman/packages/utils
|
||||||
'@mtcute/core':
|
'@mtcute/core':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../core
|
version: link:../core
|
||||||
|
@ -400,6 +405,9 @@ importers:
|
||||||
|
|
||||||
packages/tl-runtime:
|
packages/tl-runtime:
|
||||||
dependencies:
|
dependencies:
|
||||||
|
'@fuman/utils':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../private/fuman/packages/utils
|
||||||
long:
|
long:
|
||||||
specifier: 5.2.3
|
specifier: 5.2.3
|
||||||
version: 5.2.3
|
version: 5.2.3
|
||||||
|
@ -415,6 +423,9 @@ importers:
|
||||||
|
|
||||||
packages/wasm:
|
packages/wasm:
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@fuman/utils':
|
||||||
|
specifier: workspace:^
|
||||||
|
version: link:../../private/fuman/packages/utils
|
||||||
'@mtcute/core':
|
'@mtcute/core':
|
||||||
specifier: workspace:^
|
specifier: workspace:^
|
||||||
version: link:../core
|
version: link:../core
|
||||||
|
|
Loading…
Reference in a new issue