mtcute/packages/node/src/utils/crypto.ts

94 lines
2.9 KiB
TypeScript
Raw Normal View History

2023-11-04 06:44:18 +03:00
// eslint-disable-next-line no-restricted-imports
import { createCipheriv, createHash, createHmac, pbkdf2, randomFillSync } from 'crypto'
import { readFile } from 'fs/promises'
import { createRequire } from 'module'
2023-11-04 06:44:18 +03:00
import { deflateSync, gunzipSync } from 'zlib'
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-04-28 22:41:28 +03:00
): Promise<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)
}
randomFill(buf: Uint8Array) {
randomFillSync(buf)
}
2023-11-04 06:44:18 +03:00
}
export class NodeCryptoProvider extends BaseNodeCryptoProvider implements ICryptoProvider {
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)
},
}
}
}