2

I'm writing my project in NodeJS, but I'm forced to use a C shared library. Because of this I have to use Node.js Addons. Calling the C++ code works perfectly, I was also able to load the custom library, and call two C functions successfully: one gives me the version number of the library, the other method generated a key based on the data provided form JS.

The problem that I'm having is related to the generated Key. I have to use in the encrypt and decrypt methods.

This is the variable that I'm using to store the key:

unsigned char generated_key[256];

This is the function that generates the key:

extern "C"
{
    #ifdef WIN32
        VE_EXPORT int CALLBACK generate_key
    #else
        int generate_key
    #endif
        (char *variable_msg,            /*!< variable seed */
        char *shared_seed,              /*!< shared seed */
        unsigned char *generated_key,   /*!< generated key */
        unsigned int key_size);         /*!< key size */
}

This is a function that uses the key:

extern "C"
{
    #ifdef WIN32
        VE_EXPORT int CALLBACK encrypt
    #else
        int encrypt
    #endif
        (const unsigned char * const plain_data,    /*!< plain data */
        const unsigned int plain_data_len,          /*!< number bytes to be encrypted */
        unsigned char *encrypted_data,              /*!< encrypted data */
        const unsigned int encrypted_data_size,     /*!< size encrypted_data array */
        unsigned int *encrypted_data_len,           /*!< number of bytes encrypted */
        const unsigned char * const key             /*!< symmetric encryption key */
        );
}

When I try to compile the code I get:

gyp info it worked if it ends with ok
gyp info using node-gyp@3.4.0
gyp info using node@6.2.0 | linux | x64
gyp info spawn /usr/bin/python2
gyp info spawn args [ '/usr/local/lib/node_modules/node-gyp/gyp/gyp_main.py',
gyp info spawn args   'binding.gyp',
gyp info spawn args   '-f',
gyp info spawn args   'make',
gyp info spawn args   '-I',
gyp info spawn args   '/home/dgatti/sequr/sequr-experiments/CPP/Sequr/build/config.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/usr/local/lib/node_modules/node-gyp/addon.gypi',
gyp info spawn args   '-I',
gyp info spawn args   '/home/dgatti/.node-gyp/6.2.0/include/node/common.gypi',
gyp info spawn args   '-Dlibrary=shared_library',
gyp info spawn args   '-Dvisibility=default',
gyp info spawn args   '-Dnode_root_dir=/home/dgatti/.node-gyp/6.2.0',
gyp info spawn args   '-Dnode_gyp_dir=/usr/local/lib/node_modules/node-gyp',
gyp info spawn args   '-Dnode_lib_file=node.lib',
gyp info spawn args   '-Dmodule_root_dir=/home/dgatti/sequr/sequr-experiments/CPP/Sequr',
gyp info spawn args   '--depth=.',
gyp info spawn args   '--no-parallel',
gyp info spawn args   '--generator-output',
gyp info spawn args   'build',
gyp info spawn args   '-Goutput_dir=.' ]
make: Entering directory `/home/dgatti/sequr/sequr-experiments/CPP/Sequr/build'
gyp info spawn make
gyp info spawn args [ 'BUILDTYPE=Release', '-C', 'build' ]
  CXX(target) Release/obj.target/vertx/main.o
../main.cpp: In function ‘void demo::VertxEncrypt(const v8::FunctionCallbackInfo<v8::Value>&)’:
../main.cpp:127:102: error: invalid conversion from ‘char*’ to ‘const unsigned char*’ [-fpermissive]
   vertx_encrypt(cPayload, sizeof(cPayload), encMessage, sizeof(encMessage), &encLength, generated_key);
                                                                                                      ^
In file included from ../main.cpp:3:0:
../libvertx_encryption.h:79:7: error:   initializing argument 1 of ‘int vertx_encrypt(const unsigned char*, unsigned int, unsigned char*, unsigned int, unsigned int*, const unsigned char*)’ [-fpermissive]
   int vertx_encrypt
       ^
make: *** [Release/obj.target/vertx/main.o] Error 1
make: Leaving directory `/home/dgatti/sequr/sequr-experiments/CPP/Sequr/build'
gyp ERR! build error
gyp ERR! stack Error: `make` failed with exit code: 2
gyp ERR! stack     at ChildProcess.onExit (/usr/local/lib/node_modules/node-gyp/lib/build.js:276:23)
gyp ERR! stack     at emitTwo (events.js:106:13)
gyp ERR! stack     at ChildProcess.emit (events.js:191:7)
gyp ERR! stack     at Process.ChildProcess._handle.onexit (internal/child_process.js:204:12)
gyp ERR! System Linux 3.10.0-327.13.1.el7.x86_64
gyp ERR! command "/usr/local/bin/node" "/usr/local/bin/node-gyp" "rebuild"
gyp ERR! cwd /home/dgatti/sequr/sequr-experiments/CPP/Sequr
gyp ERR! node -v v6.2.0
gyp ERR! node-gyp -v v3.4.0
gyp ERR! not ok

This is how I call the function, right now I'm just passing the variable as is, but I tried many different way of converting, casting and changing.

encrypt(cPayload, sizeof(cPayload), encMessage, sizeof(encMessage), &encLength, generated_key);

I wrote an example in C and everything works fine, but when I try to use this C functions in C++, things brakes. I have experience in C but defiantly not in C++. I looked for days to find a solution but uncle Google and buddy Stack weren't able to com up with a solution.

What I'm looking for

  • a keyword that could help me in my search for an answer,
  • or the solution to my problem.

Tech spec

  • gcc version 4.8.5 20150623 (Red Hat 4.8.5-4) (GCC)

Complete code

This is the complete code in C++ that I'm calling from NodeJS to access C functions:

#include <node.h>
#include <iostream>
#include "lib_encryption.h"

using namespace std;

namespace demo {

    using v8::Exception;
    using v8::FunctionCallbackInfo;
    using v8::Isolate;
    using v8::Local;
    using v8::Number;
    using v8::Object;
    using v8::String;
    using v8::Value;

    unsigned char generated_key[256];
    unsigned int key_size = 32;
    unsigned int encLength;
    unsigned char encMessage[256] = {0};

    //
    //  This is the implementation of the "add" method
    //  Input arguments are passed using the
    //  const FunctionCallbackInfo<Value>& args struct
    //
    void GenerateKey(const FunctionCallbackInfo<Value>& args)
    {
        v8::String::Utf8Value param1(args[0]->ToString());
        std::string strMessage = std::string(*param1);

        v8::String::Utf8Value param2(args[1]->ToString());
        std::string strSecret = std::string(*param2);

        char cMessage[4];
        strcpy(cMessage, strMessage.c_str());

        char cSecret[11];
        strcpy(cSecret, strSecret.c_str());

        //
        //  Creating the key
        //
        _generate_key(cMessage, cSecret, generated_key, key_size);

        //
        //  1.  Create isolation storage
        //
        Isolate* isolate = args.GetIsolate();

        //
        //  2.  Save the value in to a isolate storage
        //
        Local<Value> str = String::NewFromUtf8(isolate, "Key done");

        //
        //  3.  Set the return value (using the passed in
        //      FunctionCallbackInfo<Value>&)
        //
        args.GetReturnValue().Set(str);
    }

    void Encrypt(const FunctionCallbackInfo<Value>& args)
    {
        Isolate* isolate = args.GetIsolate();

        v8::String::Utf8Value param1(args[0]->ToString());
        std::string payload = std::string(*param1);

        char cPayload[4];
        strcpy(cPayload, payload.c_str());

        for (int i=0; i<256; i++)
        {
            printf("%.2X ", generated_key[i]);
        }

        printf("\n");

        encrypt(cPayload, sizeof(cPayload), encMessage, sizeof(encMessage), &encLength, generated_key);

        //
        //  1.  Create isolation storage
        //

        //
        //  2.  Save the value in to a isolate storage
        //
        Local<Value> str = String::NewFromUtf8(isolate, "Encrypt");

        //
        //  3.  Set the return value (using the passed in
        //      FunctionCallbackInfo<Value>&)
        //
        args.GetReturnValue().Set(str);
    }

    //
    //  *   Initialize the object and expose the functions that need exposure.
    //
    void Init(Local<Object> exports)
    {
        NODE_SET_METHOD(exports, "GenerateKey", GenerateKey);
        NODE_SET_METHOD(exports, "Encrypt", Encrypt);

    }

    //
    //  There is no semi-colon after NODE_MODULE as it's not a function
    //
    NODE_MODULE(, Init)
}
ZachB
  • 13,051
  • 4
  • 61
  • 89
David Gatti
  • 3,576
  • 3
  • 33
  • 64
  • This should help: http://stackoverflow.com/questions/332030/when-should-static-cast-dynamic-cast-const-cast-and-reinterpret-cast-be-used – Nazar Sakharenko Aug 10 '16 at 13:20
  • The generated key is stored in a unsigned char buffer, and the function expects the key be passed to encrypt as an unsigned char*. If it is complaining about a char* being converted to an unsigned char*, I'm guessing the generated_key variable isn't the variable being passed to the function at that position. Please edit your question to include the code that causes the error. – ktb Aug 10 '16 at 13:29
  • Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and the shortest code necessary to reproduce it in the question itself. Questions without a clear problem statement are not useful to other readers. See: How to create a Minimal, Complete, and Verifiable example. – too honest for this site Aug 10 '16 at 13:32
  • True, I forgot to add how I call the function. Updated. – David Gatti Aug 10 '16 at 13:38
  • 1
    `When I try to compile the code I get: error [...]` You should include the full file/line/column reference that you've omitted from that error. – underscore_d Aug 10 '16 at 14:39
  • You are right, just updated. – David Gatti Aug 10 '16 at 14:42
  • 2
    @DavidGatti There should be more to the error message, indicating which parameter is wrong (hint: it's `cPayload`). – molbdnilo Aug 10 '16 at 14:45
  • the underscore is a typo while pasting the code. Removed. – David Gatti Aug 10 '16 at 14:47
  • What about this isn't clear? `cPayload` is a `char[]`. Its name when passed decays to a `char *`. You are passing it to a function that expects an `unsigned char *` in that position. These are two distinct types. – underscore_d Aug 10 '16 at 14:57
  • OK, full error mode ON! :) – David Gatti Aug 10 '16 at 14:58

3 Answers3

3

The other answers mention the background but not the specific problem, or they cast doubt on generated_key instead, but that one is OK. [edit] I see molbdnilo's comment now; I thought someone had mentioned it, but I couldn't seem to find it! [/edit] So here is the real reason:

The object cPayload is a char[4]. Like any array, its name implicitly converts (often colloquially phrased as 'decays') to a char * in several contexts, including when passing it as a function argument. You passed it to a function, encrypt(), which expects an unsigned char * in that place. But char * and unsigned char * are two distinct types. Hence the error.

Back to the background, since it's worth specifying. char must have the same range of values as either signed char or unsigned char, can be static_cast with possible truncation, and has the same aliasing powers - etc. - but, in any case, it is considered a distinct type. You can verify this without any compile error by checking typeid results for the 3 types.

underscore_d
  • 6,309
  • 3
  • 38
  • 64
  • 1
    OMG, you are right, the error throw me towards the Key, but the problem was the actual data to encrypt. Thank you, and also thank you to all of you for helping out! – David Gatti Aug 10 '16 at 15:09
1

I don't guess I understood in which part of code there is problem. But I think you are trying to cast from char* to const unsigned char* that are two different types. Try to change char* type to unsigned char* or maybe use everywhere in your code unsigned char*. Here is the one answer, that may help you where said that Plain char, signed char, and unsigned char are three distinct types.

Conversion from char* to signed char*

Community
  • 1
  • 1
Turkhan Badalov
  • 845
  • 1
  • 11
  • 17
  • I know the compiler is saying such thing, but check my post again, the first example that I give, is the variable where I'm storing the key, and it is unsigned. Try the compiler is saying otherwise, but unless i'm blind, when I look in the window of my IDE, I see `unsigned char generated_key[256];` – David Gatti Aug 10 '16 at 14:00
  • Are you passing your array here? const unsigned char * const key ? If yes, what about explicit casting, like (const char*) generated_key, when passing to function? And try your code with removing const keyword in function declaration, let me know the result – Turkhan Badalov Aug 10 '16 at 14:18
  • And are you sure const unsigned char * const key is what you want? Maybe you should delete CONST keyword before key? Like that: const unsigned char * key Also, try this way of casting when you pass generated_key into function: const_cast (generated_key) – Turkhan Badalov Aug 10 '16 at 14:28
0

The default char type has implementation-defined signedness and could either be signed or unsigned, depending on compiler. Therefore, it should never be used to store anything but strings. Never use it to store other forms of data. It is not necessarily compatible with unsigned char.

Reason why it doesn't compile could be because the signedness if char varies between your C and C++ compilers, but it could also be because C++ has a stricter and better type safety system than C. While a C compiler is more lax and might let the code slip through without raising a diagnostic message, even though it is always bad practice to mix these types in C and C++ both.

Simply change to unsigned char or preferably uint8_t and the problems will go away.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • I strongly doubt `char` signedness is different between C++ and C on any platform (did you ever encounter such a system?). I miss a [mcve] in the question. – too honest for this site Aug 10 '16 at 13:31
  • @Olaf Yes of course - if the C and C++ compilers are made by different vendors. Signedness of char has little to do with the target system and everything to do with compiler whims. That being said, most likely the error comes from C++ being stricter than C regarding which types that are considered compatible. – Lundin Aug 10 '16 at 13:37
  • But I'm already doing that as stated in the first code example, this is how I declare my variable `unsigned char generated_key[256];`. I know the compiler is saying `... from ‘char*’ to ...`, but it is not true :) I definitely have `unsigned`. – David Gatti Aug 10 '16 at 13:55
  • @DavidGatti Somewhere you don't use unsigned char. Another variable? Function parameters? You don't post any variable/function declarations, so there's no telling which one. – Lundin Aug 10 '16 at 14:03
  • Hmm, at least on modern ARM it is defined by the ABI, thus not left to the implementation. But agreed, if the compiler vendors try to lock the user to their product, they can ignore the recommended ABI. I might have used too often gcc... – too honest for this site Aug 10 '16 at 14:08
  • @Olaf IIRC then gcc always uses signed `char`, whereas it makes most sense for `char` to be unsigned on pretty much any (embedded) system. – Lundin Aug 10 '16 at 14:12
  • @Lundin No. gcc uses the appropriate ABI. On ARM (now) AAPCS (which has unsigned `char`, on MSP430 also `unsigned char` (the MSP430 is 16 bits with unsigned load/extend-only). (and IMO signed `char` is bullocks in general) – too honest for this site Aug 10 '16 at 14:17
  • I wanted to be precise with the post, but it seams it created more confusion then good ;) updated with the whole code, hope now my issue will be more clear. – David Gatti Aug 10 '16 at 14:30
  • `uint8_t` is **worse** if the code needs the guaranteed special semantics of `char` types, as it is not guaranteed to be a _typedef_ to any of those. – underscore_d Aug 10 '16 at 14:56
  • @underscore_d Any system that doesn't treat `uint8_t` as a character type is completely useless in practice. Such systems deserve all the bugs they can get, as a punishment to the people who designed and use the system. Programmers need not consider such systems, it is just a good thing if their programs crash when ported to one (if such systems still exist... I only think there were some oddball DSPs in the 1990s where this would cause problems). – Lundin Aug 11 '16 at 06:17
  • @Lundin ...Wow. I'll let the people who design and use such systems respond to the idea that because their requirements dare to differ from your more conservative ones, they deserve to be "punished" and have their efforts fail. Wow. Anyway, my point wasn't to elicit a rant based on your own lack of experience in less-mainstream platforms: it was only that there are Standardised reasons that the exact-width typedefs, particularly the `[u]int8_t` variants, need not have the types or semantics people might expect. – underscore_d Aug 11 '16 at 07:49
  • @underscore_d My whole point is: just because there once existed an oddball DSP somewhere during the 1980s, all future programs should not need to take that old crap in account. It's a huge waste of time for mankind. If I was a silicon manufacturer today and decide to release a CPU core where every byte is 15 bits and signedness is one's complement, then programmers shouldn't think "ooh I need to adapt all my programs to that CPU" but rather they should think "what is this crap, these guys aren't sane, their company needs to go broke asap". – Lundin Aug 11 '16 at 07:57
  • @Lundin I disagree that such processors are all in the past as you think and that even if they were, that would mean they should be deliberately and derisively ignored... and _separate from that_, that it's such a burden for us 'norms' just to type `unsigned char` when we _need_ an `unsigned char`, or make our own guaranteed _typedef_. It doesn't hurt me to work this way, even though I'll probably never port my code to an 'oddball DSP', but it _does_ mean people _still_ writing code for such ICs can use C++. And to me, that means (even a few) more folk to keep the language alive and improve it – underscore_d Aug 11 '16 at 08:49