fix(core): continuous aes ctr

This commit is contained in:
teidesu 2021-04-15 19:39:41 +03:00
parent afa679cef4
commit 9e681cb13f
8 changed files with 128 additions and 129 deletions

View file

@ -13,5 +13,9 @@
},
"dependencies": {
"@mtcute/tl": "^0.0.0"
},
"devDependencies": {
"@types/ws": "^7.4.1",
"ws": "^7.4.4"
}
}

View file

@ -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

View file

@ -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

View file

@ -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')
cipher.mode.pad = cipher.mode.unpad = false
cipher.start(method === 'ECB' ? {} : { iv: ivBuffer })
const cipher = forge.cipher.createCipher('AES-ECB', keyBuffer)
cipher.start({})
cipher.mode.pad = cipher.mode.unpad = false
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.mode.pad = cipher.mode.unpad = false
cipher.start(method === 'ECB' ? {} : { iv: ivBuffer })
cipher.start({})
cipher.mode.pad = cipher.mode.unpad = false
cipher.update(forge.util.createBuffer(data.toString('binary')))
cipher.finish()
return Buffer.from(cipher.output.data, 'binary')

View file

@ -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()])
},
}

View file

@ -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
// })
// })

View file

@ -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 () => {

View file

@ -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==