93 lines
2.8 KiB
TypeScript
93 lines
2.8 KiB
TypeScript
|
// eslint-disable-next-line no-restricted-imports
|
||
|
import { createCipheriv, createHash, createHmac, pbkdf2 } from 'crypto'
|
||
|
import { deflateSync, gunzipSync } from 'zlib'
|
||
|
|
||
|
import { ige256Decrypt, ige256Encrypt, initAsync, InitInput } from '@mtcute/wasm'
|
||
|
|
||
|
import { MaybeAsync } from '../../types/index.js'
|
||
|
import { BaseCryptoProvider, IAesCtr, ICryptoProvider, IEncryptionScheme } from './abstract.js'
|
||
|
|
||
|
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',
|
||
|
): MaybeAsync<Uint8Array> {
|
||
|
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 {
|
||
|
// todo: test if wasm impl is better fit here
|
||
|
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)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
export class NodeCryptoProvider extends BaseNodeCryptoProvider implements ICryptoProvider {
|
||
|
private wasmInput?: InitInput
|
||
|
|
||
|
constructor(params?: { wasmInput?: InitInput }) {
|
||
|
super()
|
||
|
this.wasmInput = params?.wasmInput
|
||
|
}
|
||
|
|
||
|
initialize(): Promise<void> {
|
||
|
return initAsync(this.wasmInput)
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
},
|
||
|
}
|
||
|
}
|
||
|
}
|