fix(core): continuous aes ctr
This commit is contained in:
parent
afa679cef4
commit
9e681cb13f
8 changed files with 128 additions and 129 deletions
|
@ -13,5 +13,9 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@mtcute/tl": "^0.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/ws": "^7.4.1",
|
||||
"ws": "^7.4.4"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,17 +16,6 @@ export function typedArrayToBuffer(arr: NodeJS.TypedArray): Buffer {
|
|||
Buffer.from(arr)
|
||||
}
|
||||
|
||||
export function reverseBuffer(buffer: Buffer): Buffer {
|
||||
for (let i = 0, j = buffer.length - 1; i < j; ++i, --j) {
|
||||
const t = buffer[j]
|
||||
|
||||
buffer[j] = buffer[i]
|
||||
buffer[i] = t
|
||||
}
|
||||
|
||||
return buffer
|
||||
}
|
||||
|
||||
export function buffersEqual(a: Buffer, b: Buffer): boolean {
|
||||
if (a.length !== b.length) return false
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ export interface ICryptoProvider {
|
|||
rsaEncrypt(data: Buffer, key: TlPublicKey): MaybeAsync<Buffer>
|
||||
|
||||
// in telegram, iv is always either used only once, or is the same for all calls for the key
|
||||
createAesCtr(key: Buffer, iv: Buffer): IEncryptionScheme
|
||||
createAesCtr(key: Buffer, iv: Buffer, encrypt: boolean): IEncryptionScheme
|
||||
createAesIge(key: Buffer, iv: Buffer): IEncryptionScheme
|
||||
createAesEcb(key: Buffer): IEncryptionScheme
|
||||
|
||||
|
@ -65,7 +65,7 @@ export abstract class BaseCryptoProvider implements ICryptoProvider {
|
|||
return bigIntToBuffer(encryptedBigInt)
|
||||
}
|
||||
|
||||
abstract createAesCtr(key: Buffer, iv: Buffer): IEncryptionScheme
|
||||
abstract createAesCtr(key: Buffer, iv: Buffer, encrypt: boolean): IEncryptionScheme
|
||||
|
||||
abstract createAesEcb(key: Buffer): IEncryptionScheme
|
||||
|
||||
|
|
|
@ -15,41 +15,41 @@ export class ForgeCryptoProvider extends BaseCryptoProvider {
|
|||
)
|
||||
}
|
||||
|
||||
createAesCtr(key: Buffer, iv: Buffer): IEncryptionScheme {
|
||||
return this._createAes(key, iv, 'CTR')
|
||||
createAesCtr(key: Buffer, iv: Buffer, encrypt: boolean): IEncryptionScheme {
|
||||
const cipher = forge.cipher[encrypt ? 'createCipher' : 'createDecipher']('AES-CTR', key.toString('binary'))
|
||||
cipher.start({ iv: iv.toString('binary') })
|
||||
|
||||
const update = (data: Buffer): Buffer => {
|
||||
cipher.output.data = ''
|
||||
cipher.update(forge.util.createBuffer(data.toString('binary')))
|
||||
return Buffer.from(cipher.output.data, 'binary')
|
||||
}
|
||||
|
||||
return {
|
||||
encrypt: update,
|
||||
decrypt: update
|
||||
}
|
||||
}
|
||||
|
||||
createAesEcb(key: Buffer): IEncryptionScheme {
|
||||
return this._createAes(key, null, 'ECB')
|
||||
}
|
||||
|
||||
private _createAes(
|
||||
key: Buffer,
|
||||
iv: Buffer | null,
|
||||
method: string
|
||||
): IEncryptionScheme {
|
||||
const methodName = `AES-${method}`
|
||||
const keyBuffer = key.toString('binary')
|
||||
const ivBuffer = iv ? iv.toString('binary') : undefined
|
||||
|
||||
return {
|
||||
encrypt(data: Buffer) {
|
||||
const cipher = forge.cipher.createCipher(methodName, keyBuffer)
|
||||
if (method === 'ECB')
|
||||
const cipher = forge.cipher.createCipher('AES-ECB', keyBuffer)
|
||||
cipher.start({})
|
||||
cipher.mode.pad = cipher.mode.unpad = false
|
||||
cipher.start(method === 'ECB' ? {} : { iv: ivBuffer })
|
||||
cipher.update(forge.util.createBuffer(data.toString('binary')))
|
||||
cipher.finish()
|
||||
return Buffer.from(cipher.output.data, 'binary')
|
||||
},
|
||||
decrypt(data: Buffer) {
|
||||
const cipher = forge.cipher.createDecipher(
|
||||
methodName,
|
||||
'AES-ECB',
|
||||
keyBuffer
|
||||
)
|
||||
if (method === 'ECB')
|
||||
cipher.start({})
|
||||
cipher.mode.pad = cipher.mode.unpad = false
|
||||
cipher.start(method === 'ECB' ? {} : { iv: ivBuffer })
|
||||
cipher.update(forge.util.createBuffer(data.toString('binary')))
|
||||
cipher.finish()
|
||||
return Buffer.from(cipher.output.data, 'binary')
|
||||
|
|
|
@ -9,29 +9,29 @@ export class NodeCryptoProvider extends BaseCryptoProvider {
|
|||
throw new Error('Cannot use Node crypto functions outside NodeJS!')
|
||||
}
|
||||
|
||||
createAesCtr(key: Buffer, iv: Buffer): IEncryptionScheme {
|
||||
return this._createAes(key, iv, 'ctr')
|
||||
createAesCtr(key: Buffer, iv: Buffer, encrypt: boolean): IEncryptionScheme {
|
||||
const cipher = nodeCrypto[encrypt ? 'createCipheriv' : 'createDecipheriv'](`aes-${key.length * 8}-ctr`, key, iv)
|
||||
|
||||
const update = (data: Buffer) => cipher.update(data)
|
||||
|
||||
return {
|
||||
encrypt: update,
|
||||
decrypt: update,
|
||||
}
|
||||
}
|
||||
|
||||
createAesEcb(key: Buffer): IEncryptionScheme {
|
||||
return this._createAes(key, null, 'ecb')
|
||||
}
|
||||
const methodName = `aes-${key.length * 8}-ecb`
|
||||
|
||||
private _createAes(
|
||||
key: Buffer,
|
||||
iv: Buffer | null,
|
||||
method: string
|
||||
): IEncryptionScheme {
|
||||
const methodName = `aes-${key.length * 8}-${method}`
|
||||
return {
|
||||
encrypt(data: Buffer) {
|
||||
const cipher = nodeCrypto.createCipheriv(methodName, key, iv)
|
||||
if (method === 'ecb') cipher.setAutoPadding(false)
|
||||
const cipher = nodeCrypto.createCipheriv(methodName, key, null)
|
||||
cipher.setAutoPadding(false)
|
||||
return Buffer.concat([cipher.update(data), cipher.final()])
|
||||
},
|
||||
decrypt(data: Buffer) {
|
||||
const cipher = nodeCrypto.createDecipheriv(methodName, key, iv)
|
||||
if (method === 'ecb') cipher.setAutoPadding(false)
|
||||
const cipher = nodeCrypto.createDecipheriv(methodName, key, null)
|
||||
cipher.setAutoPadding(false)
|
||||
return Buffer.concat([cipher.update(data), cipher.final()])
|
||||
},
|
||||
}
|
||||
|
|
|
@ -4,30 +4,14 @@ import {
|
|||
buffersEqual,
|
||||
cloneBuffer,
|
||||
encodeUrlSafeBase64,
|
||||
isProbablyPlainText,
|
||||
parseUrlSafeBase64,
|
||||
randomBytes,
|
||||
reverseBuffer,
|
||||
telegramRleDecode,
|
||||
telegramRleEncode,
|
||||
xorBuffer,
|
||||
xorBufferInPlace,
|
||||
} from '../src/utils/buffer-utils'
|
||||
|
||||
describe('reverseBuffer', () => {
|
||||
it('should reverse even-sized buffers', () => {
|
||||
const buf = Buffer.from([1, 2, 3, 4])
|
||||
reverseBuffer(buf)
|
||||
expect([...buf]).to.eql([4, 3, 2, 1])
|
||||
})
|
||||
|
||||
it('should reverse odd-sized buffers', () => {
|
||||
const buf = Buffer.from([1, 2, 3])
|
||||
reverseBuffer(buf)
|
||||
expect([...buf]).to.eql([3, 2, 1])
|
||||
})
|
||||
})
|
||||
|
||||
describe('buffersEqual', () => {
|
||||
it('should return true for equal buffers', () => {
|
||||
expect(buffersEqual(Buffer.from([]), Buffer.from([]))).is.true
|
||||
|
@ -207,60 +191,60 @@ describe('telegramRleDecode', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('isProbablyPlainText', () => {
|
||||
it('should return true for buffers only containing printable ascii', () => {
|
||||
expect(
|
||||
isProbablyPlainText(Buffer.from('hello this is some ascii text'))
|
||||
).to.be.true
|
||||
expect(
|
||||
isProbablyPlainText(
|
||||
Buffer.from(
|
||||
'hello this is some ascii text\nwith unix new lines'
|
||||
)
|
||||
)
|
||||
).to.be.true
|
||||
expect(
|
||||
isProbablyPlainText(
|
||||
Buffer.from(
|
||||
'hello this is some ascii text\r\nwith windows new lines'
|
||||
)
|
||||
)
|
||||
).to.be.true
|
||||
expect(
|
||||
isProbablyPlainText(
|
||||
Buffer.from(
|
||||
'hello this is some ascii text\n\twith unix new lines and tabs'
|
||||
)
|
||||
)
|
||||
).to.be.true
|
||||
expect(
|
||||
isProbablyPlainText(
|
||||
Buffer.from(
|
||||
'hello this is some ascii text\r\n\twith windows new lines and tabs'
|
||||
)
|
||||
)
|
||||
).to.be.true
|
||||
})
|
||||
|
||||
it('should return false for buffers containing some binary data', () => {
|
||||
expect(isProbablyPlainText(Buffer.from('hello this is cedilla: ç'))).to
|
||||
.be.false
|
||||
expect(
|
||||
isProbablyPlainText(
|
||||
Buffer.from('hello this is some ascii text with emojis 🌸')
|
||||
)
|
||||
).to.be.false
|
||||
|
||||
// random strings of 16 bytes
|
||||
expect(
|
||||
isProbablyPlainText(
|
||||
Buffer.from('717f80f08eb9d88c3931712c0e2be32f', 'hex')
|
||||
)
|
||||
).to.be.false
|
||||
expect(
|
||||
isProbablyPlainText(
|
||||
Buffer.from('20e8e218e54254c813b261432b0330d7', 'hex')
|
||||
)
|
||||
).to.be.false
|
||||
})
|
||||
})
|
||||
// describe('isProbablyPlainText', () => {
|
||||
// it('should return true for buffers only containing printable ascii', () => {
|
||||
// expect(
|
||||
// isProbablyPlainText(Buffer.from('hello this is some ascii text'))
|
||||
// ).to.be.true
|
||||
// expect(
|
||||
// isProbablyPlainText(
|
||||
// Buffer.from(
|
||||
// 'hello this is some ascii text\nwith unix new lines'
|
||||
// )
|
||||
// )
|
||||
// ).to.be.true
|
||||
// expect(
|
||||
// isProbablyPlainText(
|
||||
// Buffer.from(
|
||||
// 'hello this is some ascii text\r\nwith windows new lines'
|
||||
// )
|
||||
// )
|
||||
// ).to.be.true
|
||||
// expect(
|
||||
// isProbablyPlainText(
|
||||
// Buffer.from(
|
||||
// 'hello this is some ascii text\n\twith unix new lines and tabs'
|
||||
// )
|
||||
// )
|
||||
// ).to.be.true
|
||||
// expect(
|
||||
// isProbablyPlainText(
|
||||
// Buffer.from(
|
||||
// 'hello this is some ascii text\r\n\twith windows new lines and tabs'
|
||||
// )
|
||||
// )
|
||||
// ).to.be.true
|
||||
// })
|
||||
//
|
||||
// it('should return false for buffers containing some binary data', () => {
|
||||
// expect(isProbablyPlainText(Buffer.from('hello this is cedilla: ç'))).to
|
||||
// .be.false
|
||||
// expect(
|
||||
// isProbablyPlainText(
|
||||
// Buffer.from('hello this is some ascii text with emojis 🌸')
|
||||
// )
|
||||
// ).to.be.false
|
||||
//
|
||||
// // random strings of 16 bytes
|
||||
// expect(
|
||||
// isProbablyPlainText(
|
||||
// Buffer.from('717f80f08eb9d88c3931712c0e2be32f', 'hex')
|
||||
// )
|
||||
// ).to.be.false
|
||||
// expect(
|
||||
// isProbablyPlainText(
|
||||
// Buffer.from('20e8e218e54254c813b261432b0330d7', 'hex')
|
||||
// )
|
||||
// ).to.be.false
|
||||
// })
|
||||
// })
|
||||
|
|
|
@ -46,16 +46,31 @@ export function testCryptoProvider(c: ICryptoProvider): void {
|
|||
})
|
||||
|
||||
it('should encrypt and decrypt aes-ctr', async () => {
|
||||
const aes = c.createAesCtr(
|
||||
Buffer.from('8ddcf593fd74ec251038e459c165461f', 'hex'),
|
||||
Buffer.from('0fea3601c60e770ac57ffe6b33ca8be1', 'hex')
|
||||
let aes = c.createAesCtr(
|
||||
Buffer.from('d450aae0bf0060a4af1044886b42a13f7c506b35255d134a7e87ab3f23a9493b', 'hex'),
|
||||
Buffer.from('0182de2bd789c295c3c6c875c5e9e190', 'hex'),
|
||||
true
|
||||
)
|
||||
expect((await aes.encrypt(Buffer.from('hello'))).toString('hex')).to.eq(
|
||||
'4f6d702526'
|
||||
|
||||
expect((await aes.encrypt(Buffer.from([1, 2, 3]))).toString('hex')).eq('a5fea1')
|
||||
expect((await aes.encrypt(Buffer.from([1, 2, 3]))).toString('hex')).eq('ab51ca')
|
||||
expect((await aes.encrypt(Buffer.from([1, 2, 3]))).toString('hex')).eq('365e5c')
|
||||
expect((await aes.encrypt(Buffer.from([1, 2, 3]))).toString('hex')).eq('4b94a9')
|
||||
expect((await aes.encrypt(Buffer.from([1, 2, 3]))).toString('hex')).eq('776387')
|
||||
expect((await aes.encrypt(Buffer.from([1, 2, 3]))).toString('hex')).eq('c940be')
|
||||
|
||||
aes = c.createAesCtr(
|
||||
Buffer.from('d450aae0bf0060a4af1044886b42a13f7c506b35255d134a7e87ab3f23a9493b', 'hex'),
|
||||
Buffer.from('0182de2bd789c295c3c6c875c5e9e190', 'hex'),
|
||||
false
|
||||
)
|
||||
expect(
|
||||
(await aes.decrypt(Buffer.from('4f6d702526', 'hex'))).toString()
|
||||
).to.eq('hello')
|
||||
|
||||
expect((await aes.decrypt(Buffer.from('a5fea1', 'hex'))).toString('hex')).eq('010203')
|
||||
expect((await aes.decrypt(Buffer.from('ab51ca', 'hex'))).toString('hex')).eq('010203')
|
||||
expect((await aes.decrypt(Buffer.from('365e5c', 'hex'))).toString('hex')).eq('010203')
|
||||
expect((await aes.decrypt(Buffer.from('4b94a9', 'hex'))).toString('hex')).eq('010203')
|
||||
expect((await aes.decrypt(Buffer.from('776387', 'hex'))).toString('hex')).eq('010203')
|
||||
expect((await aes.decrypt(Buffer.from('c940be', 'hex'))).toString('hex')).eq('010203')
|
||||
})
|
||||
|
||||
it('should encrypt and decrypt aes-ige', async () => {
|
||||
|
|
|
@ -2722,6 +2722,13 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/ws@^7.4.1":
|
||||
version "7.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.1.tgz#49eacb15a0534663d53a36fbf5b4d98f5ae9a73a"
|
||||
integrity sha512-ISCK1iFnR+jYv7+jLNX0wDqesZ/5RAeY3wUx6QaphmocphU61h+b+PHjS18TF4WIPTu/MMzxIq2PHr32o2TS5Q==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/yargs-parser@*":
|
||||
version "20.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-20.2.0.tgz#dd3e6699ba3237f0348cd085e4698780204842f9"
|
||||
|
@ -15016,7 +15023,7 @@ write-pkg@^4.0.0:
|
|||
type-fest "^0.4.1"
|
||||
write-json-file "^3.2.0"
|
||||
|
||||
ws@7.4.4, ws@^7.3.0, ws@~7.4.2:
|
||||
ws@7.4.4, ws@^7.3.0, ws@^7.4.4, ws@~7.4.2:
|
||||
version "7.4.4"
|
||||
resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.4.tgz#383bc9742cb202292c9077ceab6f6047b17f2d59"
|
||||
integrity sha512-Qm8k8ojNQIMx7S+Zp8u/uHOx7Qazv3Yv4q68MiWWWOJhiwG5W3x7iqmRtJo8xxrciZUY4vRxUTJCKuRnF28ZZw==
|
||||
|
|
Loading…
Reference in a new issue