feat: build crypto-node against node crypto
This commit is contained in:
parent
59c6ae4cbf
commit
5e79dc32c5
7 changed files with 117 additions and 28 deletions
|
@ -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
|
|
@ -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"
|
||||
|
|
|
@ -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; \
|
||||
}
|
||||
|
|
92
packages/crypto-node/lib/ige256.cpp
Normal file
92
packages/crypto-node/lib/ige256.cpp
Normal 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];
|
||||
}
|
||||
}
|
12
packages/crypto-node/lib/ige256.h
Normal file
12
packages/crypto-node/lib/ige256.h
Normal 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
|
|
@ -26,6 +26,10 @@
|
|||
".": {
|
||||
"import": "./esm/index.js",
|
||||
"require": "./cjs/index.js"
|
||||
},
|
||||
"./native": {
|
||||
"import": "./esm/native.cjs",
|
||||
"require": "./cjs/native.cjs"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -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'))
|
||||
|
|
Loading…
Reference in a new issue