feat: build crypto-node against node crypto

This commit is contained in:
alina 🌸 2023-11-02 19:11:26 +03:00
parent 59c6ae4cbf
commit 5e79dc32c5
Signed by: teidesu
SSH key fingerprint: SHA256:uNeCpw6aTSU4aIObXLvHfLkDa82HWH9EiOj9AXOIRpI
7 changed files with 117 additions and 28 deletions

View file

@ -3,10 +3,9 @@
📖 [API Reference](https://ref.mtcute.dev/modules/_mtcute_crypto_node.html)
Native extension for NodeJS that improves performance of the most used
cryptographic mode in Telegram (IGE), which is not implemented by OpenSSL.
cryptographic mode in Telegram (IGE), which is not implemented directly by OpenSSL.
Other modes used (i.e. CBC, CTR) and hashes are supported natively by OpenSSL,
and they *are* faster than the custom implementation, so OpenSSL will be used for them.
Uses OpenSSL under the hood to provide maximum performance
## Installation
You will need all the pre-requisites for [node-gyp](https://github.com/nodejs/node-gyp#installation).
@ -28,5 +27,6 @@ const tg = new TelegramClient({
> **Tip**: When using `@mtcute/node`, this will be done automatically for you.
## Acknowledgments
Based on [pyrogram/tgcrypto](https://github.com/pyrogram/tgcrypto)
## Benchmarks
See https://github.com/mtcute/benchmarks

View file

@ -3,11 +3,8 @@
{
"target_name": "crypto",
"sources": [
"../crypto/aes256.c",
"../crypto/aes256.h",
"../crypto/ige256.c",
"../crypto/ige256.h",
"lib/entry.cpp",
"lib/ige256.cpp",
],
"cflags_cc": [
"-std=c++17"

View file

@ -1,6 +1,6 @@
#include <assert.h>
#include <node_api.h>
#include "../../crypto/ige256.h"
#include "ige256.h"
#ifndef NODE_GYP_MODULE_NAME
#define NODE_GYP_MODULE_NAME crypto
@ -74,7 +74,7 @@
napi_value ret; \
CALL_ASSERT_OK(napi_create_buffer(env, size, (void**) &output_buf, &ret)); \
\
name(input_buf, size, key_buf, iv_buf, output_buf); \
name(env, input_buf, size, key_buf, iv_buf, output_buf); \
\
return ret; \
}

View file

@ -0,0 +1,92 @@
#include <node_api.h>
#include <openssl/evp.h>
#include <openssl/err.h>
#include "ige256.h"
#define THROW_OPENSSL_ERROR \
napi_throw_error(env, NULL, ERR_error_string(ERR_get_error(), NULL)); \
return;
void ige256_encrypt(napi_env env, uint8_t* in, uint32_t length, uint8_t* key, uint8_t* iv, uint8_t* out) {
EVP_CIPHER_CTX *ctx;
uint32_t i, j;
uint8_t* iv1;
uint8_t* iv2;
uint8_t* block;
int written;
iv1 = &iv[0];
iv2 = &iv[16];
if (!(ctx = EVP_CIPHER_CTX_new())) {
THROW_OPENSSL_ERROR
}
if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_ecb(), NULL, key, NULL)) {
THROW_OPENSSL_ERROR
}
for (i = 0; i < length; i += AES_BLOCK_SIZE) {
block = &out[i];
for (j = 0; j < AES_BLOCK_SIZE; ++j)
block[j] = in[i + j] ^ iv1[j];
if (1 != EVP_EncryptUpdate(ctx, block, &written, block, AES_BLOCK_SIZE)) {
THROW_OPENSSL_ERROR
}
for (j = 0; j < AES_BLOCK_SIZE; ++j)
block[j] ^= iv2[j];
iv1 = block;
iv2 = &in[i];
}
EVP_CIPHER_CTX_free(ctx);
}
void ige256_decrypt(napi_env env, uint8_t* in, uint32_t length, uint8_t* key, uint8_t* iv, uint8_t* out) {
EVP_CIPHER_CTX *ctx;
uint32_t i, j;
uint8_t* iv1;
uint8_t* iv2;
uint8_t* block;
int written;
iv1 = &iv[16];
iv2 = &iv[0];
if (!(ctx = EVP_CIPHER_CTX_new())) {
THROW_OPENSSL_ERROR
}
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_ecb(), NULL, key, NULL)) {
THROW_OPENSSL_ERROR
}
if (1 != EVP_CIPHER_CTX_set_padding(ctx, 0)) {
THROW_OPENSSL_ERROR
}
for (i = 0; i < length; i += AES_BLOCK_SIZE) {
block = &out[i];
for (j = 0; j < AES_BLOCK_SIZE; ++j)
block[j] = in[i + j] ^ iv1[j];
if (1 != EVP_DecryptUpdate(ctx, block, &written, block, AES_BLOCK_SIZE)) {
THROW_OPENSSL_ERROR
}
for (j = 0; j < AES_BLOCK_SIZE; ++j)
block[j] ^= iv2[j];
iv1 = block;
iv2 = &in[i];
}
}

View file

@ -0,0 +1,12 @@
#include <stdint.h>
#include <node_api.h>
#ifndef IGE256_H
#define IGE256_H
#define AES_BLOCK_SIZE 16
void ige256_encrypt(napi_env env, uint8_t* in, uint32_t length, uint8_t* key, uint8_t* iv, uint8_t* out);
void ige256_decrypt(napi_env env, uint8_t* in, uint32_t length, uint8_t* key, uint8_t* iv, uint8_t* out);
#endif // IGE256_H

View file

@ -26,6 +26,10 @@
".": {
"import": "./esm/index.js",
"require": "./cjs/index.js"
},
"./native": {
"import": "./esm/native.cjs",
"require": "./cjs/native.cjs"
}
}
},

View file

@ -67,8 +67,7 @@ const BUILD_CONFIGS = {
customScript(packageDir, outDir) {
// copy native sources and binding.gyp file
fs.mkdirSync(path.join(outDir, 'lib'), { recursive: true })
fs.mkdirSync(path.join(outDir, 'crypto'), { recursive: true })
fs.cpSync(path.join(packageDir, 'lib'), path.join(outDir, 'lib'), { recursive: true })
const bindingGyp = fs.readFileSync(path.join(packageDir, 'binding.gyp'), 'utf8')
fs.writeFileSync(
@ -78,21 +77,6 @@ const BUILD_CONFIGS = {
.replace(/"\.\.\/crypto/g, '"crypto'),
)
for (const f of fs.readdirSync(path.join(packageDir, 'lib'))) {
const content = fs.readFileSync(path.join(packageDir, 'lib', f), 'utf8')
fs.writeFileSync(
path.join(outDir, 'lib', f),
content
// replace paths to crypto
.replace(/#include "\.\.\/\.\.\/crypto/g, '#include "../crypto'),
)
}
for (const f of fs.readdirSync(path.join(packageDir, '../crypto'))) {
fs.copyFileSync(path.join(packageDir, '../crypto', f), path.join(outDir, 'crypto', f))
}
// for some unknown fucking reason ts doesn't do this
fs.copyFileSync(path.join(packageDir, 'src/native.cjs'), path.join(outDir, 'cjs/native.cjs'))
fs.copyFileSync(path.join(packageDir, 'src/native.cjs'), path.join(outDir, 'esm/native.cjs'))