2023-11-04 06:44:18 +03:00
|
|
|
// eslint-disable-next-line no-restricted-imports
|
2023-11-12 00:36:00 +03:00
|
|
|
import { createCipheriv, createHash, createHmac, pbkdf2, randomFillSync } from 'crypto'
|
2024-02-28 00:33:23 +03:00
|
|
|
import { readFile } from 'fs/promises'
|
|
|
|
import { createRequire } from 'module'
|
2023-11-04 06:44:18 +03:00
|
|
|
import { deflateSync, gunzipSync } from 'zlib'
|
|
|
|
|
2024-02-28 00:33:23 +03:00
|
|
|
import { MaybePromise } from '@mtcute/core'
|
|
|
|
import { BaseCryptoProvider, IAesCtr, ICryptoProvider, IEncryptionScheme } from '@mtcute/core/utils.js'
|
|
|
|
import { ige256Decrypt, ige256Encrypt, initSync } from '@mtcute/wasm'
|
2023-11-04 06:44:18 +03:00
|
|
|
|
|
|
|
export abstract class BaseNodeCryptoProvider extends BaseCryptoProvider {
|
|
|
|
createAesCtr(key: Uint8Array, iv: Uint8Array): IAesCtr {
|
|
|
|
const cipher = createCipheriv(`aes-${key.length * 8}-ctr`, key, iv)
|
|
|
|
|
|
|
|
const update = (data: Uint8Array) => cipher.update(data)
|
|
|
|
|
|
|
|
return {
|
|
|
|
process: update,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pbkdf2(
|
|
|
|
password: Uint8Array,
|
|
|
|
salt: Uint8Array,
|
|
|
|
iterations: number,
|
|
|
|
keylen = 64,
|
|
|
|
algo = 'sha512',
|
2024-02-03 20:14:03 +03:00
|
|
|
): MaybePromise<Uint8Array> {
|
2023-11-04 06:44:18 +03:00
|
|
|
return new Promise((resolve, reject) =>
|
|
|
|
pbkdf2(password, salt, iterations, keylen, algo, (err: Error | null, buf: Uint8Array) =>
|
|
|
|
err !== null ? reject(err) : resolve(buf),
|
|
|
|
),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
sha1(data: Uint8Array): Uint8Array {
|
|
|
|
return createHash('sha1').update(data).digest()
|
|
|
|
}
|
|
|
|
|
|
|
|
sha256(data: Uint8Array): Uint8Array {
|
|
|
|
return createHash('sha256').update(data).digest()
|
|
|
|
}
|
|
|
|
|
|
|
|
hmacSha256(data: Uint8Array, key: Uint8Array): Uint8Array {
|
|
|
|
return createHmac('sha256', key).update(data).digest()
|
|
|
|
}
|
|
|
|
|
|
|
|
gzip(data: Uint8Array, maxSize: number): Uint8Array | null {
|
|
|
|
try {
|
|
|
|
// telegram accepts both zlib and gzip, but zlib is faster and has less overhead, so we use it here
|
|
|
|
return deflateSync(data, {
|
|
|
|
maxOutputLength: maxSize,
|
|
|
|
})
|
|
|
|
// hot path, avoid additional runtime checks
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
|
|
} catch (e: any) {
|
|
|
|
if (e.code === 'ERR_BUFFER_TOO_LARGE') {
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
throw e
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gunzip(data: Uint8Array): Uint8Array {
|
|
|
|
return gunzipSync(data)
|
|
|
|
}
|
2023-11-12 00:36:00 +03:00
|
|
|
|
|
|
|
randomFill(buf: Uint8Array) {
|
|
|
|
randomFillSync(buf)
|
|
|
|
}
|
2023-11-04 06:44:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
export class NodeCryptoProvider extends BaseNodeCryptoProvider implements ICryptoProvider {
|
2024-02-28 00:33:23 +03:00
|
|
|
async initialize(): Promise<void> {
|
|
|
|
// @only-if-esm
|
|
|
|
const require = createRequire(import.meta.url)
|
|
|
|
// @/only-if-esm
|
|
|
|
const wasmFile = require.resolve('@mtcute/wasm/mtcute.wasm')
|
|
|
|
const wasm = await readFile(wasmFile)
|
|
|
|
initSync(wasm)
|
2023-11-04 06:44:18 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
createAesIge(key: Uint8Array, iv: Uint8Array): IEncryptionScheme {
|
|
|
|
return {
|
|
|
|
encrypt(data: Uint8Array): Uint8Array {
|
|
|
|
return ige256Encrypt(data, key, iv)
|
|
|
|
},
|
|
|
|
decrypt(data: Uint8Array): Uint8Array {
|
|
|
|
return ige256Decrypt(data, key, iv)
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|