The Node.js extension provides access to the features and functions of the Themis cryptographic library:
There are also example console utils available for the NodeJS wrapper for Themis (as well as for some other wrappers — see the full list here). They help understand the specific mechanics of encryption/decryption processes of this specific wrapper. You can find the example console utils for the NodeJS wrapper here.
jsthemis v0.10.1
is tested and supported on Node.js v8-v10. It depends on nan > 2.8.0
package.
If you are looking for compatibility with older Node.js or nan, please see older jsthemis versions.
In addition to the common set of build tools, Themis currently requires either the OpenSSL or LibreSSL package with the developer version of the package (as it provides header files). In either case, we strongly recommend you using the most recent version of these packages.
Install OpenSSL, LibreSSL or BoringSSL as system libraries.
Install Themis as system library using your system's package manager.
Install jsthemis wrapper from npm.
npm install jsthemis
git clone https://github.com/cossacklabs/themis.git
Note that the default installation process assumes the use of the standard LibreSSL/OpenSSL library and will install Themis to the standard /usr/lib and /usr/include locations.
In a typical case, all you need to do is (depending on your rights, sudo might be necessary):
make install
If your path is different, please see Building and installing for more details on how to change it.
To build and run tests, you can use:
make test
To install, build and run jsthemis test suit, you can use:
make jsthemis_install && make prepare_tests_all test_js
All the tests need to be passed with no errors.
Themis supports both Elliptic Curve and RSA algorithms for asymmetric cryptography. Algorithm type is chosen according to the generated key type. Asymmetric keys are needed for Secure Message and Secure Session objects.
WARNING: When you distribute private keys to your users, make sure the keys are sufficiently protected. You can find the guidelines here.
NOTE: When using public keys of other peers, make sure they come from trusted sources.
function KeyPair(alg);
Description:
KeyPair()
- return private key and public key strings for Elliptic Curve algorithm. Raises ThemisError
on failure.
KeyPair("rsa")
- return private key and public key strings for RSA algorithm. Raises ThemisError
on failure.
// OR themis.KeyPair("rsa") key_pair = new themis.KeyPair(); private_key = key_pair.private().toString("base64"); public_key = key_pair.public().toString("base64");
The Secure Message functions provide a sequence-independent, stateless, contextless messaging system. This may be preferred in cases that don't require frequent sequential message exchange and/or in low-bandwidth contexts. This is secure enough to exchange messages from time to time, but if you'd like to have Perfect Forward Secrecy and higher security guarantees, please consider using Secure Session instead.
The Secure Message functions offer two modes of operation:
In Sign/Verify mode, the message is signed using the sender's private key and is verified by the receiver using the sender's public key. The message is packed in a suitable container and ECDSA is used by default to sign the message (when RSA key is used, RSA+PSS+PKCS#7 digital signature is used).
In Encrypt/Decrypt mode, the message will be encrypted with a randomly generated key (in RSA) or a key derived by ECDH (in ECDSA), via symmetric algorithm with Secure Cell in seal mode (keys are 256 bits long).
The mode is selected by the sender supplying a valid public key of the receiver (encrypt/decrypt) or setting this parameter to NULL, or an empty string to use sign/verify.
Read more about Secure Message's cryptographic internals here.
function SecureMessage(private_key, peer_public_key) { function encrypt(message); function decrypt(message); function sign(message); function verify(message); }
Description:
SecureMessage(private_key, peer_public_key)
- initialise Secure Message object with private_key and peer_public_key. Raises Error on failure.
encrypt(message)
- encrypt message. Return encrypted secure message container as Buffer. Raises Error on failure.
decrypt(message)
- decrypt message. Return decrypted message as Buffer. Raises Error on failure.
sign(message)
- Sign message. Return signed Secure Message container as Buffer. Raises Error on failure.
verify(message)
- Verify Buffer contained signed Secure Message container. Return Buffer with plain message. Raises Error on failure.
For detailed explanation of Secure Message, see Secure Message description.
Initialise encrypter:
smessage = themis.SecureMessage(private_key, peer_public_key) // or smessage = themis.SecureMessage(new Buffer(private_key, "base64"), new Buffer(peer_public_key, "base64"));
Encrypt message:
try { encrypted_message = smessage.encrypt(message) } catch(error_var) { // error occurred }
Decrypt message:
try { decrypted_message = smessage.decrypt(encrypted_message) } catch(error_var) { // error occurred }
Sign message:
try { signed_message = smessage.sign(message) } catch(error_var) { // error occurred }
Verify message:
try { message = smessage.verify(signed_message) } catch(error_var) { // error occurred }
The Secure Сell functions provide the means of protection for arbitrary data contained in stores, such as database records or filesystem files. These functions provide both strong symmetric encryption and data authentication mechanisms.
The general approach is that given:
Secure Cell functions will produce:
The purpose of the optional "context information" (i.e. a database row number or file name) is to establish a secure association between this context and the protected data. In short, even when the password is known, if the context is incorrect, then decryption will fail.
The purpose of the authentication data is to validate that given a correct password (and context), the decrypted data is indeed the same as the original source data.
The authentication data must be stored somewhere. The most convenient way is to simply be appended it to the encrypted data, but this is not always possible due to the storage architecture of your application. The Secure Cell functions offer variants that address this issue in different ways.
The encryption algorithm used by Secure Cell (by default) is AES-256. The generated authentication data is 16 bytes long.
Secure Cell is available in 3 modes:
You can learn more about the underlying considerations, limitations, and features here.
function SecureCellSeal(key) { function encrypt(message, context); function decrypt(message, context); } function SecureCellTokenProtect(key) { function encrypt(message, token, context); function decrypt(message, token, context); } function SecureCellContextImprint(key) { function encrypt(message, context); function decrypt(message, context); }
Description:
SecureCellSeal(key)
- initialise Secure Cell Seal mode object with key.encrypt(message, context)
- encrypt message with optional context. Return encrypted Buffer. Raises Error on failure. decrypt(message, context)
- decrypt message with optional context. Return decrypted Buffer. Raises Error on failure. SecureCellTokenProtect(key)
- initialise Secure Cell Token Protect mode object with key.encrypt(message, context)
- encrypt message with optional context. Return array of two Buffers: encrypted message and token. Raises Error on failure. decrypt(message, token, context)
- decrypt message with token and optional context. Return decrypted Buffer. Raises Error on failure. SecureCellContextImprint(key)
- initialise Secure Cell Context Imprint mode object with key.encrypt(message, context)
- encrypt message with context. Return encrypted Buffer. Raises Error on failure. decrypt(message, context)
- decrypt message with context. Return decrypted Buffer. Raises Error on failure. Initialise encrypter/decrypter
scell_seal = new themis.SecureCellSeal(key);
Encrypt
// context is optional encrypted_message = scell_seal.encrypt(message, context)
Decrypt
The context should be same as in the encrypt function call for successful decryption.
decrypted_message = scell_seal.decrypt(encrypted_message, context)
Initialise encrypter/decrypter
scell_token_protect = new SecureCellTokenProtect(key);
Encrypt
// context is optional encrypted_array = scell_token_protect.encrypt(message, context);
Decrypt
The context should be same as in the encrypt function call for successful decryption.
decrypted_message = scell_token_protect.decrypt(encrypted_array.data, encrypted_array.token, context);
Initialise encrypter/decrypter
scell_context_imprint = new themis.SecureCellContextImprint(key);
Encrypt
encrypted_message = scell_context_imprint.encrypt(message, context);
Decrypt
decrypted_message = scell_context_imprint.decrypt(encrypted_message, context)
Secure Session is a sequence- and session- dependent, stateful messaging system. It is suitable for protecting long-lived peer-to-peer message exchanges where the secure data exchange is bound to a specific session context.
Secure Session operates in two stages: session negotiation where the keys are established and cryptographic material is exchanged to generate ephemeral keys and data exchange the where exchanging of messages can be carried out between peers.
You can read a more detailed description of the process here.
Put simply, Secure Session takes the following form:
function SecureSession(id, private_key, get_pub_key_by_id function(peer_id) { return pub_key_by_id; }) { function connectRequest(); function wrap(message); function unwrap(message); function isEstablished(); }
Description:
SecureSession(id, private_key, get_pub_key_by_id function(peer_id){})
- initialise Secure Session object with id, private_key and a get_pub_key_by_id function(peer_id) - function that gets Buffer with peer identifier and returns public key for it. Raises Error on failure.
connect_request()
- Return connection initialisation message; Raises Error on failure.
wrap(message)
- return ready for sending encrypted message. Raises Error on failure.unwrap(message)
- return unwrapped message. Raises Error on failure. Secure Session has two parties that are called client and server for the sake of simplicity, but they could be more precisely called initiator and acceptor - the only difference between them is in who starts the communication.
Secure Session relies on the user's passing a number of callback functions to send/receive messages - and the keys are retrieved from local storage (see more in Secure Session cryptosystem description).
First, initialisation:
client_session = new addon.SecureSession(client_id, client_keypair.private(), function(id) { // get public key for specified id from file, database, etc. return public_key; }); data = client_session.connectRequest() do { // send data to peer // receive response from peer data = client_session.unwrap(response); } while(!client_session.isEstablished());
After the loop finishes, Secure Session is established and is ready to be used.
To encrypt the outgoing message, use:
encrypted_message = client_session.wrap(message); // send encrypted_message by any preferred method
To decrypt the received message, use:
// receive encrypted_message from peer message = client_session.unwrap(encrypted_message)
First, initialise everything.
server_session = new addon.SecureSession(server_id, server_keypair.private(), function(id) { // get public key for specified id from file, database, etc. return public_key; }); while(!client_session.isEstablished()) { // receive response from peer data = client_session.unwrap(response); // send data to peer }
Secure Session is ready. See the full example available in docs/examples/js
Secure Comparator is an interactive protocol for two parties that compares whether they share the same secret or not. It is built around a Zero Knowledge Proof-based protocol (Socialist Millionaire's Protocol), with a number of security enhancements.
Secure Comparator is transport-agnostic and only requires the user(s) to pass messages in a certain sequence. The protocol itself is ingrained into the functions and requires minimal integration efforts from the developer.
Secure Comparator has two parties — called client and server — the only difference between them is in who starts the comparison.
var themis = require('jsthemis'); var client = new themis.SecureComparator(new Buffer("shared secret")); var data = client.beginCompare() while (!client.isCompareComplete()) { // send data on server and receive response sendDataOnServer(data); data = receiveFromServer(); // proceed and send again data = client.proceedCompare(data); }
After the loop finishes, the comparison is over and its result can be checked calling isMatch()
:
if (client.isMatch()) { // secrets match } else { // secrets don't match }
Server part can be described in any language, let's pretend that both client and server are using Swift.
var themis = require('jsthemis'); var server = new themis.SecureComparator(new Buffer("shared secret")); var data; while (!server.isCompareComplete()) { // receive from client data = receiveFromClient(); // proceed and send again data = server.proceedCompare(data); }
After the loop finishes, the comparison is over and its result can be checked calling status
:
if (server.isMatch()) { // secrets match } else { // secrets don't match }
This is it. See the full examples available in docs/examples/js.