telegram-mtproto
Version:
Telegram MTProto library
174 lines (151 loc) • 4.89 kB
JavaScript
//@flow
import { identity, has, both } from 'ramda'
import { isNode, isWebpack } from 'Runtime'
import blueDefer from 'Util/defer'
import type { Defer } from 'Util/defer'
import { immediate } from 'mtproto-shared'
import { convertToUint8Array, sha1HashSync, sha256HashSync,
aesEncryptSync, aesDecryptSync, convertToByteArray, convertToArrayBuffer,
pqPrimeFactorization, bytesModPow } from './bin'
const convertIfArray = (val) => Array.isArray(val)
? convertToUint8Array(val)
: val
const hasWindow = typeof window !== 'undefined'
let webWorker = !isNode
let taskID = 0
const awaiting: { [task: number]: Defer } = {}
const webCrypto = isWebCrypto()
const useWebCrypto = webCrypto && !!webCrypto.digest
let useSha1Crypto = useWebCrypto
let useSha256Crypto = useWebCrypto
const finalizeTask = (taskID: number, result) => {
const deferred = awaiting[taskID]
if (deferred) {
deferred.resolve(result)
delete awaiting[taskID]
}
}
function isWebCrypto() {
if (isNode || !hasWindow) return false
//eslint-disable-next-line
if (window.crypto === void 0 && window.msCrypto === void 0)
return false
//eslint-disable-next-line
return (window.crypto && (window.crypto.subtle || window.crypto.webkitSubtle))
//eslint-disable-next-line
|| window.msCrypto && window.msCrypto.subtle
}
const isCryptoTask = both(has('taskID'), has('result'))
//eslint-disable-next-line
const workerEnable = !isNode && hasWindow && window.Worker && isWebpack
function initWorker() {
let TmpWorker, tmpWorker
try {
//$FlowIssue
TmpWorker = require('worker-loader?inline&fallback=false!./worker.js')
} catch (err) {
console.error('webWorker disabled', err)
webWorker = false
return
}
try {
tmpWorker = new TmpWorker()
} catch (err) {
console.error('webWorker disabled', err)
webWorker = false
return
}
// tmpWorker.onmessage = function(event) {
// console.info('CW tmpWorker.onmessage', event && event.data)
// }
tmpWorker.onmessage = e => {
if (e.data === 'ready') {
console.info('CW ready')
} else if (!isCryptoTask(e.data)) {
console.info('Not crypto task', e, e.data)
return e
} else
return webWorker
? finalizeTask(e.data.taskID, e.data.result)
: webWorker = tmpWorker
}
tmpWorker.onerror = function(error) {
console.error('CW error', error, error.stack)
webWorker = false
}
tmpWorker.postMessage('b')
webWorker = tmpWorker
}
if (workerEnable)
initWorker()
function performTaskWorker(task, params, embed) {
// console.log(rework_d_T(), 'CW start', task)
const deferred = blueDefer()
awaiting[taskID] = deferred
params.task = task
params.taskID = taskID
;(embed || webWorker).postMessage(params)
taskID++
return deferred.promise
}
const sha1Hash = (bytes: *) => {
if (useSha1Crypto) {
// We don't use buffer since typedArray.subarray(...).buffer gives the whole buffer and not sliced one.
// webCrypto.digest supports typed array
const bytesTyped = convertIfArray(bytes)
// console.log(rework_d_T(), 'Native sha1 start')
return webCrypto.digest({ name: 'SHA-1' }, bytesTyped).then(digest =>
// console.log(rework_d_T(), 'Native sha1 done')
digest, e => {
console.error('Crypto digest error', e)
useSha1Crypto = false
return sha1HashSync(bytes)
})
}
return immediate(sha1HashSync, bytes)
}
const sha256Hash = (bytes: *) => {
if (useSha256Crypto) {
const bytesTyped = convertIfArray(bytes)
// console.log(rework_d_T(), 'Native sha1 start')
return webCrypto.digest({ name: 'SHA-256' }, bytesTyped)
.then(identity
// console.log(rework_d_T(), 'Native sha1 done')
, e => {
console.error('Crypto digest error', e)
useSha256Crypto = false
return sha256HashSync(bytes)
})
}
return immediate(sha256HashSync, bytes)
}
const aesEncrypt = (bytes: *, keyBytes: *, ivBytes: *): Promise<ArrayBuffer> =>
immediate(() => convertToArrayBuffer(aesEncryptSync(bytes, keyBytes, ivBytes)))
const aesDecrypt = (encryptedBytes: *, keyBytes: *, ivBytes: *): Promise<ArrayBuffer> =>
immediate(() => convertToArrayBuffer(
aesDecryptSync(encryptedBytes, keyBytes, ivBytes)))
function factorize(bytesSrc: number[] | Uint8Array) {
const bytes = convertToByteArray(bytesSrc)
return webWorker
? performTaskWorker('factorize', { bytes })
: immediate(pqPrimeFactorization, bytes)
}
const modPow = (x: number[] | Uint8Array,
y: number[] | Uint8Array,
m: number[] | Uint8Array): Promise<number[]> =>
webWorker
? performTaskWorker('mod-pow', {
x,
y,
m
})
: immediate(bytesModPow, x, y, m)
export const CryptoWorker = {
sha1Hash,
sha256Hash,
aesEncrypt,
aesDecrypt,
factorize,
modPow
}
export default CryptoWorker