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)
|
📖 [API Reference](https://ref.mtcute.dev/modules/_mtcute_crypto_node.html)
|
||||||
|
|
||||||
Native extension for NodeJS that improves performance of the most used
|
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,
|
Uses OpenSSL under the hood to provide maximum performance
|
||||||
and they *are* faster than the custom implementation, so OpenSSL will be used for them.
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
You will need all the pre-requisites for [node-gyp](https://github.com/nodejs/node-gyp#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.
|
> **Tip**: When using `@mtcute/node`, this will be done automatically for you.
|
||||||
|
|
||||||
## Acknowledgments
|
## Benchmarks
|
||||||
Based on [pyrogram/tgcrypto](https://github.com/pyrogram/tgcrypto)
|
|
||||||
|
See https://github.com/mtcute/benchmarks
|
|
@ -3,11 +3,8 @@
|
||||||
{
|
{
|
||||||
"target_name": "crypto",
|
"target_name": "crypto",
|
||||||
"sources": [
|
"sources": [
|
||||||
"../crypto/aes256.c",
|
|
||||||
"../crypto/aes256.h",
|
|
||||||
"../crypto/ige256.c",
|
|
||||||
"../crypto/ige256.h",
|
|
||||||
"lib/entry.cpp",
|
"lib/entry.cpp",
|
||||||
|
"lib/ige256.cpp",
|
||||||
],
|
],
|
||||||
"cflags_cc": [
|
"cflags_cc": [
|
||||||
"-std=c++17"
|
"-std=c++17"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <node_api.h>
|
#include <node_api.h>
|
||||||
#include "../../crypto/ige256.h"
|
#include "ige256.h"
|
||||||
|
|
||||||
#ifndef NODE_GYP_MODULE_NAME
|
#ifndef NODE_GYP_MODULE_NAME
|
||||||
#define NODE_GYP_MODULE_NAME crypto
|
#define NODE_GYP_MODULE_NAME crypto
|
||||||
|
@ -74,7 +74,7 @@
|
||||||
napi_value ret; \
|
napi_value ret; \
|
||||||
CALL_ASSERT_OK(napi_create_buffer(env, size, (void**) &output_buf, &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; \
|
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",
|
"import": "./esm/index.js",
|
||||||
"require": "./cjs/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) {
|
customScript(packageDir, outDir) {
|
||||||
// copy native sources and binding.gyp file
|
// copy native sources and binding.gyp file
|
||||||
|
|
||||||
fs.mkdirSync(path.join(outDir, 'lib'), { recursive: true })
|
fs.cpSync(path.join(packageDir, 'lib'), path.join(outDir, 'lib'), { recursive: true })
|
||||||
fs.mkdirSync(path.join(outDir, 'crypto'), { recursive: true })
|
|
||||||
|
|
||||||
const bindingGyp = fs.readFileSync(path.join(packageDir, 'binding.gyp'), 'utf8')
|
const bindingGyp = fs.readFileSync(path.join(packageDir, 'binding.gyp'), 'utf8')
|
||||||
fs.writeFileSync(
|
fs.writeFileSync(
|
||||||
|
@ -78,21 +77,6 @@ const BUILD_CONFIGS = {
|
||||||
.replace(/"\.\.\/crypto/g, '"crypto'),
|
.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
|
// 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, 'cjs/native.cjs'))
|
||||||
fs.copyFileSync(path.join(packageDir, 'src/native.cjs'), path.join(outDir, 'esm/native.cjs'))
|
fs.copyFileSync(path.join(packageDir, 'src/native.cjs'), path.join(outDir, 'esm/native.cjs'))
|
||||||
|
|
Loading…
Reference in a new issue