├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE.md ├── README.md ├── binding.gyp ├── config ├── index.js ├── installrng.sh ├── package.json ├── postinstall.sh ├── src ├── addon.cc ├── aesxor.cc ├── aesxor.h ├── rng.cc ├── rng.h ├── seifecc.cc ├── seifecc.h ├── seifsha3.cc ├── seifsha3.h ├── util.h └── xorShift128.hpp └── test ├── aestest.js ├── ecctest.js ├── rngtest.js └── shatest.js /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | 19 | # Compiled Static libraries 20 | *.lai 21 | *.la 22 | *.a 23 | *.lib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | 30 | # Build Directory 31 | build 32 | 33 | #Hidden test files 34 | examples/.ecies* 35 | examples/rng1 36 | test/.ecies* 37 | test/rng1 38 | 39 | # modules 40 | node_modules 41 | deps 42 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | # [1.0.3] - 2017-04-17 5 | ### Added 6 | - Functionality to assess entropy mining strength. 7 | - Contributing guidelines in CONTRIBUTING.md 8 | - CHANGELOG. 9 | 10 | ### Changed 11 | - XorShift+ as RNG for AES modulation. 12 | - ECC encrypts and decrypts buffers. 13 | - Changed SHA-3 to FIPS 202 (F1600, XOF d=0x06). 14 | - Dependency management during pre-install. 15 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing To seifnode 2 | 3 | We are always looking for ways to make our modules better. Adding features and fixing bugs allows everyone who depends 4 | on this code to create better, more stable applications. 5 | Feel free to raise a pull request to us. Our team would review your proposed modifications and, if appropriate, merge 6 | your changes into our code. Ideas and other comments are also welcome. 7 | 8 | ## Getting Started 9 | 1. Create your own [fork](https://help.github.com/articles/fork-a-repo) of this [repository](../../fork). 10 | 11 | ```bash 12 | # Clone it 13 | git clone git@github.com:me/seifnode.git 14 | 15 | # Change directory 16 | cd seifnode 17 | 18 | # Add the upstream repo 19 | git remote add upstream git://github.com/paypal/seifnode.git 20 | 21 | # Get the latest upstream changes 22 | git pull upstream 23 | 24 | # Configure project and install dependencies 25 | npm install 26 | 27 | # Run tests 28 | npm test 29 | ``` 30 | 31 | ## Making Changes 32 | 1. Make sure that your changes adhere to the current coding conventions used throughout the project, indentation, accurate comments, etc. 33 | 2. Ensure that all the tests pass. 34 | 35 | ## Submitting Changes 36 | 1. Commit your changes in logical chunks, i.e. keep your changes small per single commit. 37 | 2. Locally merge (or rebase) the upstream branch into your topic branch: `$ git pull upstream && git merge`. 38 | 3. Push your topic branch up to your fork: `$ git push origin `. 39 | 4. Open a [Pull Request](https://help.github.com/articles/using-pull-requests) with a clear title and description. 40 | 41 | If you have any questions about contributing, please feel free to contact us by posting your questions on GitHub. 42 | 43 | Copyright 2015, 2016, 2017 PayPal under [The MIT License](LICENSE.md). 44 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # This file describes how to build seifnode into a runnable linux container 2 | # with all dependencies installed. 3 | # 4 | # To build: 5 | # 6 | # 1) Install docker (http://docker.io) 7 | # 2) Build: copy Dockerfile to an empty dir & run docker build -t $USER/seifnode . 8 | # 3) Test: docker run $USER/seifnode node -e "console.log(require('seifnode'))" 9 | # 10 | # Note: Container does not automatically access hardware components for entropy 11 | # mining. 12 | 13 | from ubuntu:16.04 14 | 15 | MAINTAINER Aashish Sheshadri 16 | MAINTAINER Rohit Harchandani 17 | 18 | run apt-get update 19 | 20 | RUN useradd -ms /bin/bash seif 21 | 22 | # Installing OpenCV 23 | 24 | run apt-get install -y cmake git subversion libopencv-dev libhighgui-dev 25 | 26 | 27 | # Installing Portaudio 28 | 29 | run apt-get install -y libasound-dev portaudio19-dev 30 | 31 | 32 | # Installing crypto++ 33 | 34 | run svn checkout https://svn.code.sf.net/p/cryptopp/code/trunk/c5 cryptopp \ 35 | && /bin/bash -c 'cd cryptopp; make static; make install PREFIX=/usr/local' 36 | 37 | 38 | # Installing Node 39 | # Note: Adds fs-extra to npm and replaces the fs.rename method with the fs.extra 40 | # move method that now automatically chooses what to do (rename/move). 41 | # https://github.com/npm/npm/issues/9863 42 | 43 | run apt-get install -y nodejs npm && ln -s /usr/bin/nodejs /usr/local/bin/node 44 | run cd usr/lib/nodejs/npm \ 45 | && npm install fs-extra \ 46 | && sed -i -e s/graceful-fs/fs-extra/ -e s/fs.rename/fs.move/ ./lib/utils/rename.js 47 | 48 | 49 | # Installing patchelf 50 | 51 | run echo "deb http://us.archive.ubuntu.com/ubuntu vivid main universe" >> /etc/apt/sources.list 52 | run apt-get update 53 | run apt-get install -y patchelf 54 | 55 | 56 | USER seif 57 | WORKDIR /home/seif 58 | 59 | 60 | # Installing seifnode 61 | 62 | run npm install nan 63 | run npm install seifnode 64 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015, 2016, 2017 PayPal 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | seifnode 2 | ======== 3 | 4 | Node.js Implementation of Seif Crypto Modules 5 | 6 | Getting Started 7 | =============== 8 | 9 | From your project directory, run (see below for requirements): 10 | 11 | ``` 12 | $ npm install seifnode 13 | ``` 14 | 15 | Seifnode depends on the c++ library [Seifrng](https://github.com/paypal/seifrng) which will be installed locally during pre-install; Seifrng uses the [CMake](https://cmake.org) build system which too will be installed locally if not found. The node module also depends of Crypto++ which will be locally installed by [Seifrng](https://github.com/paypal/seifrng) if not found during pre-install. 16 | 17 | On Linux systems the node module requires [PatchELF](https://nixos.org/patchelf.html) utility which will be installed locally if not found during pre-install. 18 | 19 | 20 | 21 | Alternatively, to use the latest development version from Github: 22 | 23 | ``` 24 | $ npm install https://github.com/paypal/seifnode.git 25 | ``` 26 | 27 | Test 28 | ==== 29 | 30 | Please refer to the "test" directory to view the "mocha" unit tests. To run the tests, please run the following command from the top-level directory. 31 | 32 | ``` 33 | $ npm test 34 | ``` 35 | 36 | Examples 37 | ======== 38 | 39 | Please refer to the "examples" directory to see examples of how to use the various modules. 40 | 41 | Interface 42 | ========= 43 | 44 | The module exposes four different interfaces useful for different purposes. 45 | 46 | ### 1. RNG 47 | 48 | This module exposes the ISAAC random number generator to node.js from the c++ library [seifrng](https://github.com/paypal/seifrng). We haven't made any changes to the random number generation process as such. The only enhancement is that we are accessing the random number generator state and encrypting it before persisting it to the disk. 49 | 50 | **Initialization:** 51 | 52 | ```javascript 53 | let seifnode = require("seifnode"); 54 | let seifrng = seifnode.RNG(); 55 | ``` 56 | 57 | **Usage:** 58 | 59 | The functions exposed are as follows: 60 | 61 | **function isInitialized(key, filename, function callback(result){...})** 62 | 63 | Creates an async worker to check if the RNG has been initialized by checking if the state file exists and can be decrypted using the given key. Once the async work is complete, the RNG is initialized with the state on the disk if present or an appropriate error is given to the callback. 64 | 65 | ```javascript 66 | seifrng.isInitialized(key, filename, function(result) { 67 | 68 | console.log(result.code); 69 | console.log(result.message); 70 | 71 | }); 72 | // 'key' is a buffer containing the disk encryption/decryption key 73 | // 'filename' is the name of the RNG saved state file on disk 74 | // 'result' is an object containing the code('code') and message('message') 75 | ``` 76 | 77 | **function initialize(key, filename)** 78 | 79 | Initilizes the RNG by gathering entropy from the available sources (Please look at the "rng" repo for more details at <>). Once the entropy generation is complete, the RNG is initialized using the generated seed and it is ready to be used. 80 | 81 | ```javascript 82 | seifrng.initialize(key, filename); 83 | // 'key' is a buffer containing the disk encryption/decryption key 84 | // 'filename' is the name of the RNG saved state file on disk 85 | ``` 86 | 87 | **function getBytes(n)** 88 | 89 | Gets the number of random bytes required and returns a buffer with the random output. If the RNG has not been initialized an error will be thrown. 90 | 91 | ```javascript 92 | let numbytes = 32; 93 | let buffer = seifrng.getBytes(numBytes); 94 | // 'numBytes' is the number of required random bytes 95 | // 'buffer' is a node.js buffer 96 | ``` 97 | 98 | **function saveState()** 99 | 100 | Encrypts and saves the RNG state to disk. 101 | 102 | ```javascript 103 | seifrng.saveState(function(result) { 104 | 105 | console.log(result.code); 106 | console.log(result.message); 107 | 108 | }); 109 | ``` 110 | 111 | **function destroy()** 112 | 113 | Destroys the underlying RNG object thus saving the state to disk. 114 | 115 | ```javascript 116 | seifrng.destroy(); 117 | ``` 118 | 119 | 120 | ### 2. ECC 121 | 122 | This module is responsible for exposing Crypto++ ECC functions using our implementation of isaac random number generator. 123 | 124 | **Initialization:** 125 | 126 | ```javascript 127 | let seifnode = require("seifnode"); 128 | let seifecc = seifnode.ECC(diskKey, folder); 129 | // 'diskKey' is the key used to encrypt the keys and rng state 130 | // 'folder' is the folder where the keys and rng state are saved on disk 131 | ``` 132 | 133 | **Usage:** 134 | 135 | The functions exposed are as follows: 136 | 137 | **function loadKeys()** 138 | 139 | Creates an async worker to load keys from the disk (encrypted using the key provided during initialization) and invokes the callback function with the error object (if applicable) and/or the object containing the keys. 140 | 141 | ```javascript 142 | seifecc.loadKeys(function(status, keys) { 143 | 144 | // 'status' (if applicable) is of the form: {code: [statusCode], message: [statusMessage]} 145 | console.log(status); 146 | // 'keys' (if available) is of the form: {enc: [publicKey], dec: [privateKey]} 147 | console.log(keys); 148 | 149 | }); 150 | ``` 151 | 152 | **function generateKeys()** 153 | 154 | Initializes the isaac RNG and uses it to generate the public/private keys and return them to the caller. These keys are also encrypted and saved to the disk. 155 | 156 | ```javascript 157 | let keys = seifecc.generateKeys(); 158 | // 'keys' (if available) is of the form: {enc: [publicKey], dec: [privateKey]} 159 | ``` 160 | 161 | **function encrypt(publicKey, message)** 162 | 163 | Encrypts the message buffer using the public key to return the cipher string (We are using Cryptopp ECIES for this purpose and the curve used is the NIST approved SECP521r1). 164 | 165 | ```javascript 166 | seifecc.loadKeys(function(status, keys) { 167 | 168 | if (status === undefined && keys !== undefined) { 169 | 170 | let cipher = obj.encrypt(keys.enc, message); 171 | // 'keys.enc' is the string containing the hex encoded ECC public key 172 | // 'message' is the buffer containing the message to be encrypted 173 | // 'cipher' is a buffer containing the encrypted cipher 174 | 175 | } 176 | 177 | }); 178 | ``` 179 | 180 | **function decrypt(privateKey, cipher)** 181 | 182 | Decrypts the cipher buffer using the private key to return the original message buffer (We are using Cryptopp ECIES for this purpose and the curve used is the NIST approved SECP521r1). 183 | 184 | ```javascript 185 | seifecc.loadKeys(function(status, keys) { 186 | 187 | if (status === undefined && keys !== undefined) { 188 | 189 | let message = seifecc.decrypt(keys.dec, cipher); 190 | // 'keys.dec' is the string containing the hex encoded ECC private key 191 | // 'cipher' is the buffer containing the cipher to be decrypted 192 | // 'message' is the buffer containing the decrypted message 193 | 194 | } 195 | 196 | }); 197 | ``` 198 | 199 | 200 | ### 3. AESXOR 201 | 202 | This module is responsible for exposing our implementation of link encryption. We are exposing the Cryptopp AES implementation in the GCM mode with slight modifications to enhance security as explained below. Similary, after the cipher bytes have been decrypted they are XOR'd with XORShift+ random bytes to get the original message. 203 | 204 | **Initialization:** 205 | 206 | ```javascript 207 | let seifnode = require("seifnode"); 208 | let seifaes = seifnode.AESXOR(seed); 209 | // 'seed' is a buffer containing bytes representing the uint64 pcg seed 210 | ``` 211 | 212 | **Usage:** 213 | 214 | The functions exposed are as follows: 215 | 216 | **function encrypt(key, message)** 217 | 218 | Encrypts the message using the given key to return the cipher. As part of this process, the message bytes are first XOR'd with equal number of random bytes generated using XORShift+ and then encrypted using the AES-GCM mode. 219 | 220 | ```javascript 221 | let cipher = seifaes.encrypt(key, message); 222 | // 'key' is the buffer containing the AES key 223 | // 'message' is the buffer containing the message to be encrypted 224 | // 'cipher' is the buffer containing the encrypted cipher 225 | ``` 226 | 227 | **function decrypt(key, cipher)** 228 | 229 | Decrypts the cipher to return the original message. As part of this process, after the cipher bytes have been decrypted using the AES-GCM mode, the decrypted buffer is XOR'd with as many XORShift+ random bytes to get the original message. 230 | 231 | ```javascript 232 | let message = seifaes.decrypt(key, cipher); 233 | // 'key' is the buffer containing the AES key 234 | // 'cipher' is the buffer containing the cipher to be decrypted 235 | // 'message' is the buffer containing the decrypted message 236 | ``` 237 | 238 | 239 | ### 4. SEIFSHA3 240 | 241 | This module is responsible for exposing Crypto++ SHA3 function 242 | 243 | **Initialization:** 244 | 245 | ```javascript 246 | let seifnode = require("seifnode"); 247 | let seifsha3 = seifnode.SEIFSHA3(); 248 | ``` 249 | 250 | **Usage:** 251 | 252 | The functions exposed are as follows: 253 | 254 | **function hash(data)** 255 | 256 | Gets the string data and returns the hash (using Cryptopp implementation of SHA3-256) of the given input as a buffer object. 257 | 258 | ```javascript 259 | let hash = seifsha3.hash(stringData); 260 | // 'stringData' is the string data to be hashed 261 | // 'hash' is the output buffer containing the SHA3-256 hash 262 | ``` 263 | 264 | 265 | 266 | 267 | Dependencies 268 | ============ 269 | 270 | ### 1. [Seifrng](https://github.com/paypal/seifrng) 271 | 272 | For generating cryptographically secure random numbers. 273 | 274 | **License:** 275 | https://github.com/paypal/seifrng/blob/master/LICENSE.md 276 | 277 | ### 2. [CryptoPP/Crypto++](https://www.cryptopp.com/) 278 | 279 | Used for all cryptographic functions. Library installed version 5.6.5 280 | 281 | **License:** 282 | Crypto++ Library is copyrighted as a compilation and (as of version 5.6.5) licensed under the Boost Software License 1.0, while the individual files in the compilation are all public domain. 283 | https://www.cryptopp.com/License.txt 284 | 285 | ### 3. Node modules: [nan](https://github.com/nodejs/nan) 286 | 287 | This is basically a header file containing macros and utilities to store all logic necessary to develop native Node.js addons without having to inspect NODE_MODULE_VERSION 288 | 289 | **License & copyright:** 290 | https://github.com/nodejs/nan/blob/master/LICENSE.md 291 | 292 | License 293 | ======= 294 | 295 | The MIT License (MIT) 296 | 297 | Copyright (c) 2015, 2016, 2017 PayPal 298 | 299 | Permission is hereby granted, free of charge, to any person obtaining a copy 300 | of this software and associated documentation files (the "Software"), to deal 301 | in the Software without restriction, including without limitation the rights 302 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 303 | copies of the Software, and to permit persons to whom the Software is 304 | furnished to do so, subject to the following conditions: 305 | 306 | The above copyright notice and this permission notice shall be included in 307 | all copies or substantial portions of the Software. 308 | 309 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 310 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 311 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 312 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 313 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 314 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 315 | THE SOFTWARE. 316 | -------------------------------------------------------------------------------- /binding.gyp: -------------------------------------------------------------------------------- 1 | { 2 | "targets": [ 3 | { 4 | "target_name": "seifnode", 5 | "sources": [ 6 | "src/addon.cc", 7 | "src/seifecc.cc", 8 | "src/aesxor.cc", 9 | "src/rng.cc", 10 | "src/seifsha3.cc" 11 | ], 12 | "cflags_cc!": [ 13 | "-fno-rtti", 14 | "-fno-exceptions" 15 | ], 16 | "conditions": [ 17 | [ 'OS=="mac"', { 18 | "xcode_settings": { 19 | 'OTHER_CPLUSPLUSFLAGS' : [ 20 | '-std=c++11', 21 | '-stdlib=libc++', 22 | '-v' 23 | ], 24 | 'OTHER_LDFLAGS': ['-stdlib=libc++'], 25 | 'MACOSX_DEPLOYMENT_TARGET': '10.10', 26 | 'GCC_ENABLE_CPP_RTTI': 'YES', 27 | 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES' 28 | }, 29 | "include_dirs": [ 30 | " /dev/null; then 21 | # attempt to install cmake 22 | echo "Attempting to install cmake locally....." 23 | echo "This is slow. It is recommended that cmake be installed globally." 24 | 25 | if [ ! -d "./3rdParty" ]; then 26 | mkdir 3rdParty 27 | fi 28 | 29 | cd 3rdParty 30 | git clone https://gitlab.kitware.com/cmake/cmake.git 31 | 32 | cd cmake 33 | ./bootstrap && make -j4 34 | 35 | rngCmakeCmd="../3rdParty/cmake/bin/cmake ../" 36 | cd ../../ 37 | fi 38 | 39 | 40 | mkdir build 41 | cd build 42 | 43 | rngBuildCmd="make" 44 | rngBuildInstallCmd="make install" 45 | 46 | echo "Runnning $rngCmakeCmd" 47 | eval $rngCmakeCmd 48 | 49 | echo "Runnning $rngBuildInstallCmd" 50 | eval $rngBuildInstallCmd 51 | 52 | if [[ $? != 0 ]]; then 53 | exit 1 54 | fi 55 | 56 | echo "Running $rngBuildCmd" 57 | echo `$rngBuildCmd` 58 | 59 | 60 | 61 | if [ -d "../3rdParty/cryptopp" ]; then 62 | echo "CryptoPP Installed Locally." 63 | else 64 | 65 | if [ ! -d "../3rdParty" ]; then 66 | mkdir ../3rdParty 67 | fi 68 | 69 | echo "Moving CryptoPP." 70 | cp -r /usr/local/include/cryptopp ../3rdParty 71 | cp /usr/local/lib/libcryptopp.a ../3rdParty/cryptopp/libcryptopp.a 72 | fi 73 | 74 | cd ../../../ 75 | node-gyp rebuild 76 | else 77 | echo "[Error] OS not supported." 78 | exit 1 79 | fi 80 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "seifnode", 3 | "version": "1.0.3", 4 | "description": "Node.js Implementation of Seif random and crypto services", 5 | "main": "index.js", 6 | "scripts": { 7 | "preinstall": "bash installrng.sh", 8 | "test": "mocha", 9 | "postinstall": "bash postinstall.sh" 10 | }, 11 | "dependencies": { 12 | "nan": "*" 13 | }, 14 | "devDependencies": { 15 | "glob": "*", 16 | "mocha": "*" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "https://github.com/paypal/seifnode.git" 21 | }, 22 | "gypfile": true, 23 | "contributors": [{ 24 | "name": "Aashish Sheshadri", 25 | "email": "aashish.sheshadri@gmail.com", 26 | "url": "http://www.aashish.me" 27 | }, { 28 | "name": "Rohit Harchandani", 29 | "email": "harchu@gmail.com" 30 | }], 31 | "engines": { 32 | "node": ">=6.9.2" 33 | }, 34 | "license": "MIT", 35 | "homepage": "http://www.seif.place", 36 | "keywords": [ 37 | "security", 38 | "crypto", 39 | "encryption", 40 | "random" 41 | ] 42 | } 43 | -------------------------------------------------------------------------------- /postinstall.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | echo "Adding rpath after build is complete" 3 | temp=`pwd` 4 | unamestr=`node -e "console.log(process.platform);"` 5 | if [[ "$unamestr" == "darwin" ]]; then 6 | install_name_tool -add_rpath $temp/deps/seifrng/lib $temp/build/Release/seifnode.node 7 | fi 8 | 9 | if [[ "$unamestr" == "linux" ]]; then 10 | 11 | patchelfCmd="patchelf" 12 | 13 | if ! type patchelf > /dev/null; then 14 | echo "patchelf not found; attempting to install locally." 15 | cd deps/3rdParty 16 | wget http://nixos.org/releases/patchelf/patchelf-0.9/patchelf-0.9.tar.bz2 17 | tar xf patchelf-0.9.tar.bz2 18 | rm patchelf-0.9.tar.bz2 19 | cd patchelf-0.9/ 20 | ./configure 21 | make 22 | patchelfCmd=./src/patchelf 23 | fi 24 | 25 | 26 | eval $patchelfCmd --set-rpath $temp/deps/seifrng/lib $temp/build/Release/seifnode.node 27 | cd $temp 28 | fi 29 | -------------------------------------------------------------------------------- /src/addon.cc: -------------------------------------------------------------------------------- 1 | /** @file addon.cc 2 | * @brief Main file containing the module initialization function 3 | * 4 | * @author Aashish Sheshadri 5 | * @author Rohit Harchandani 6 | * 7 | * The MIT License (MIT) 8 | * 9 | * Copyright (c) 2015, 2016, 2017 PayPal 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a copy 12 | * of this software and associated documentation files (the "Software"), to 13 | * deal in the Software without restriction, including without limitation the 14 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 15 | * sell copies of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included in 19 | * all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | * DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | // ---------------------- 31 | // node.js addon includes 32 | // ---------------------- 33 | #include 34 | 35 | // ---------------- 36 | // library includes 37 | // ---------------- 38 | #include "seifecc.h" 39 | #include "aesxor.h" 40 | #include "rng.h" 41 | #include "seifsha3.h" 42 | 43 | 44 | // ---------- 45 | // Initialize 46 | // ---------- 47 | /** 48 | * @brief Executed when the module is required. It initialized the different 49 | * native classes being wrapped by this addon. 50 | * The below function and macro are equivalent to: 51 | * 'module.exports = Initialize()' 52 | * @param target refers to the node.js module exports object 53 | * @return void 54 | */ 55 | void Initialize(v8::Handle target) { 56 | SEIFECC::Init(target); 57 | AESXOR256::Init(target); 58 | RNG::Init(target); 59 | SEIFSHA3::Init(target); 60 | } 61 | 62 | 63 | NODE_MODULE(seifnode, Initialize) 64 | -------------------------------------------------------------------------------- /src/aesxor.cc: -------------------------------------------------------------------------------- 1 | /** @file aesxor.cc 2 | * @brief Definition of the class functions provided in aesxor.h and helper 3 | * functions to convert bytes container to a number and vice versa 4 | * 5 | * @author Aashish Sheshadri 6 | * @author Rohit Harchandani 7 | * 8 | * The MIT License (MIT) 9 | * 10 | * Copyright (c) 2015, 2016, 2017 PayPal 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to 14 | * deal in the Software without restriction, including without limitation the 15 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16 | * sell copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in 20 | * all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | * DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | // ----------------- 32 | // standard includes 33 | // ----------------- 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | // ---------------------- 41 | // node.js addon includes 42 | // ---------------------- 43 | #include 44 | 45 | 46 | // ----------------- 47 | // cryptopp includes 48 | // ----------------- 49 | #include "filters.h" 50 | using CryptoPP::StringSink; 51 | using CryptoPP::StringSource; 52 | using CryptoPP::ArraySink; 53 | using CryptoPP::ArraySource; 54 | #include "modes.h" 55 | #include "aes.h" 56 | #include "gcm.h" 57 | 58 | // ---------------- 59 | // library includes 60 | // ---------------- 61 | #include "aesxor.h" 62 | 63 | 64 | // javascript object constructor 65 | Nan::Persistent AESXOR256::constructor; 66 | // AES key length 67 | const int AESXOR256::AESNODE_DEFAULT_KEY_LENGTH_BYTES = 32; 68 | 69 | 70 | // ------------- 71 | // uInt64toBytes 72 | // ------------- 73 | /** 74 | * @brief Convert uint64 array to an array of bytes. 75 | * 76 | * We traverse the input container from 'begin' to 'end' and store the 77 | * extracted bytes into 'out' 78 | * 79 | * @param begin beginning of container of uint64 values 80 | * @param end end of container of uint64 values 81 | * @param out beginning of container of resulting byte values 82 | * 83 | * @return void 84 | */ 85 | template 86 | void uInt64toBytes(II begin, II end, OI out) { 87 | // traverse bytes in uint64 values and store in 'out' 88 | while (begin != end) { 89 | for (int i = 0; i < 8; ++i) { 90 | *out = static_cast ( 91 | (*begin & (uint64_t(0x00000000000000FF) << (8 * i))) >> (8 * i) 92 | ); 93 | ++out; 94 | } 95 | ++begin; 96 | } 97 | } 98 | 99 | 100 | // ------------- 101 | // bytesToUInt64 102 | // ------------- 103 | /** 104 | * @brief Convert array of bytes to a uint64 value 105 | * 106 | * @param bufferData container of byte values 107 | * @param bufferLength size of container of byte values 108 | * 109 | * @return uint64 value containing the result of the conversion 110 | */ 111 | uint64_t bytesToUInt64(uint8_t* bufferData, size_t bufferLength) { 112 | uint64_t returnVal = 0; 113 | int idx = 0; 114 | 115 | while (bufferLength > 0 && idx < 8) { 116 | 117 | returnVal = (returnVal << 8) + (uint8_t)bufferData[idx]; 118 | idx = idx + 1; 119 | bufferLength = bufferLength - 1; 120 | 121 | } 122 | 123 | return returnVal; 124 | } 125 | 126 | 127 | // ----------- 128 | // Constructor 129 | // ----------- 130 | /** 131 | * Constructor 132 | * @brief Initializes the object including the PCG random number 133 | * generator using the provided seed and cipher block size. 134 | * 135 | * @param seed seed for pcg rng 136 | * @param blockSize aes encryption block size 137 | * 138 | * @return 139 | */ 140 | AESXOR256::AESXOR256(std::vector seed): 141 | _rng(seed) { 142 | 143 | } 144 | 145 | 146 | // --------- 147 | // getRandom 148 | // --------- 149 | /** 150 | * @brief Gets random uint64 values from pcg and stores them in a byte 151 | * array. 152 | * 153 | * @param random container for resulting random bytes 154 | * @param len number of random bytes required 155 | * 156 | * @return void 157 | */ 158 | void AESXOR256::getRandom(uint8_t* random, int len) { 159 | /* Getting the number of random uint64 values required based on 160 | * the number of bytes asked for. 161 | */ 162 | int numRand = len % 8 == 0 ? len / 8 : len / 8 + 1; 163 | 164 | std::vector randomVector(numRand); 165 | 166 | for (int i = 0; i < numRand; ++i) { 167 | /* Getting random uint64 value from pcg random number generator 168 | * using operator(). 169 | */ 170 | randomVector[i] = _rng(); 171 | } 172 | 173 | // Convert uint64 container to a byte array containing random values. 174 | uInt64toBytes(randomVector.begin(), randomVector.end(), random); 175 | } 176 | 177 | 178 | 179 | // ------------ 180 | // encryptBlock 181 | // ------------ 182 | /** 183 | * @brief Encrypts the given message using AES in GCM mode to provide 184 | * confidentiality and authenticity using the given key resulting 185 | * in the cipher block. 186 | * 187 | * @param cipher byte container for the resulting cipher 188 | * @param key byte container for the AES key 189 | * @param message byte container for the message 190 | * 191 | * @throw Cryptopp:Exception in case of encryption errors 192 | * 193 | * @return void 194 | */ 195 | void AESXOR256::encryptBlock(std::vector& cipher, 196 | const std::vector& key, const std::vector& message) { 197 | 198 | // initial vector (IV) for AES to XOR 199 | std::vector iv(CryptoPP::AES::BLOCKSIZE); 200 | 201 | std::string ciphertext; //store encrypted message 202 | 203 | 204 | // Initialize AES with GCM mode 205 | CryptoPP::GCM::Encryption e; 206 | 207 | // Set AES Key and load IV. 208 | e.SetKeyWithIV(key.data(), key.size(), iv.data()); 209 | 210 | /* Apply the Cryptopp AuthenticatedEncryptionFilter to the message buffer 211 | * using ArraySource, to transform it into an encrypted string 'ciphertext'. 212 | */ 213 | ArraySource ss1( 214 | message.data(), 215 | message.size(), 216 | true, 217 | new CryptoPP::AuthenticatedEncryptionFilter(e, 218 | new StringSink( ciphertext ) 219 | ) // AuthenticatedEncryptionFilter 220 | ); // ArraySource 221 | 222 | // Store the string cipher into the cipher byte vector. 223 | cipher.resize(ciphertext.size()); 224 | StringSource ss2( 225 | ciphertext, 226 | true, 227 | new ArraySink(cipher.data(), cipher.size()) 228 | ); 229 | return; 230 | } 231 | 232 | 233 | // ------------ 234 | // decryptBlock 235 | // ------------ 236 | /** 237 | * @brief Decrypts the given cipher using AES in GCM mode to provide 238 | * confidentiality and authenticity using the given key resulting 239 | * in the message block. 240 | * 241 | * @param message byte container for the resulting decrypted message 242 | * @param cipher byte container for the input cipher 243 | * @param key byte container for the AES key 244 | * 245 | * @throw Cryptopp:Exception in case of decryption errors 246 | * 247 | * @return void 248 | */ 249 | void AESXOR256::decryptBlock(std::vector& message, 250 | const std::vector& key, const std::vector& cipher) { 251 | 252 | // initial vector (IV) for AES to XOR 253 | std::vector iv(CryptoPP::AES::BLOCKSIZE); 254 | 255 | std::string decryptedtext; //store decrypted message 256 | 257 | // initialize AES 258 | CryptoPP::GCM< AES >::Decryption e; 259 | 260 | // Set AES Key and load IV. 261 | e.SetKeyWithIV(key.data(), key.size(), iv.data()); 262 | 263 | /* Apply the Cryptopp AuthenticatedDecryptionFilter to the cipher buffer 264 | * using ArraySource, to transform it into the decrypted string. 265 | */ 266 | ArraySource ss1( 267 | cipher.data(), 268 | cipher.size(), 269 | true, 270 | new CryptoPP::AuthenticatedDecryptionFilter(e, 271 | new StringSink( decryptedtext ) 272 | ) // AuthenticatedDecryptionFilter 273 | ); // ArraySource 274 | 275 | // Store the decrypted string into the message byte vector. 276 | message.resize(decryptedtext.size()); 277 | StringSource ss2( 278 | decryptedtext, 279 | true, 280 | new ArraySink(message.data(), message.size()) 281 | ); 282 | 283 | return; 284 | } 285 | 286 | 287 | 288 | // ------------- 289 | // xorRandomData 290 | // ------------- 291 | /** 292 | * @brief XORs the given input container with requal number of random 293 | * bytes obtained using the PCG random number generator. 294 | * 295 | * @param output byte container for the resulting XOR'd output 296 | * @param input byte container for the input data to be XOR'd with 297 | * random bytes 298 | * 299 | * @return void 300 | */ 301 | void AESXOR256::xorRandomData(std::vector& output, 302 | const std::vector& input) { 303 | 304 | // Get PCG random bytes based on the size of the input vector. 305 | std::vector random(input.size()); 306 | getRandom(random.data(), random.size()); 307 | 308 | /* XOR the random bytes with the input vector and store the result in the 309 | * output vector 310 | */ 311 | std::transform(input.begin(), input.begin() + input.size(), 312 | random.data(), output.begin(), std::bit_xor()); 313 | } 314 | 315 | 316 | // --- 317 | // New 318 | // --- 319 | /** 320 | * @brief Creates the wrapped object and corresponding underlying 321 | * object with provided arguments - seed buffer and block size. 322 | * 323 | * Invoked as: 324 | * 'let obj = new AESXOR256(seed)' or 325 | * 'let obj = AESXOR256(seed)' 326 | * 'seed' is a buffer containing bytes representing the uint64 pcg seed 327 | * 328 | * @param info node.js arguments wrapper containing seed buffer and 329 | * block size 330 | * 331 | * @return void 332 | */ 333 | NAN_METHOD(AESXOR256::New) { 334 | 335 | if (info.IsConstructCall()) { 336 | // Invoked as constructor: `new AESXOR256(...)`. 337 | 338 | // Checking if first argument (seed) is a valid node.js buffer. 339 | if (!node::Buffer::HasInstance(info[0])) { 340 | 341 | Nan::ThrowError("Incorrect Arguments. Seed buffer not provided"); 342 | return; 343 | } 344 | 345 | // Generating the uin64 seed from the node.js seed buffer. 346 | v8::Local bufferObj = 347 | Nan::To(info[0]).ToLocalChecked(); 348 | 349 | uint8_t* bufferData = (uint8_t*)node::Buffer::Data(bufferObj); 350 | size_t bufferLength = node::Buffer::Length(bufferObj); 351 | 352 | std::vector seed(2); 353 | seed[0] = bytesToUInt64(bufferData, bufferLength); 354 | seed[1] = bytesToUInt64(bufferData + 8, bufferLength); 355 | 356 | // Initializing the wrapped object with given seed. 357 | AESXOR256* obj = new AESXOR256(seed); 358 | 359 | obj->Wrap(info.This()); 360 | 361 | info.GetReturnValue().Set(info.This()); 362 | 363 | } else { 364 | // Invoked as plain function `AESXOR256(...)`, turn into construct call. 365 | const int argc = info.Length(); 366 | 367 | // Creating argument array for construct call. 368 | std::vector > argv; 369 | argv.reserve(argc); 370 | for (int i = 0; i < argc; ++i) { 371 | argv.push_back(info[i]); 372 | } 373 | 374 | v8::Local cons = Nan::New(constructor); 375 | info.GetReturnValue().Set(cons->NewInstance(argc, argv.data())); 376 | 377 | } 378 | } 379 | 380 | 381 | 382 | 383 | // ------- 384 | // encrypt 385 | // ------- 386 | /** 387 | * @brief Unwraps the arguments to get the AES encryption key and 388 | * message, and encrypts the message block by block to return 389 | * the cipher. 390 | * 391 | * Invoked as: 392 | * 'let cipher = obj.encrypt(key, message)' 393 | * 'key' is the buffer containing the AES key 394 | * 'message' is the buffer containing the message to be encrypted 395 | * 'cipher' is the buffer containing the encrypted cipher 396 | * PreCondition: key buffer size == AESNODE_DEFAULT_KEY_LENGTH_BYTES 397 | * 398 | * @param info node.js arguments wrapper for AES key and message to be 399 | * encrypted 400 | * 401 | * @return void 402 | */ 403 | NAN_METHOD(AESXOR256::encrypt) { 404 | 405 | AESXOR256* obj = ObjectWrap::Unwrap(info.Holder()); 406 | 407 | // Checking arguments. 408 | if (info.Length() < 2 409 | || !node::Buffer::HasInstance(info[0]) 410 | || !node::Buffer::HasInstance(info[1])) { 411 | 412 | Nan::ThrowError("Incorrect Arguments. Please provide buffers for 'key' " 413 | "and 'message' function encrypt(key, message)'"); 414 | return; 415 | } 416 | 417 | /* Unwrap the first argument to get the AES key buffer and validate 418 | * that the key length = AESNODE_DEFAULT_KEY_LENGTH_BYTES. 419 | */ 420 | v8::Local bufferObj0 = 421 | Nan::To(info[0]).ToLocalChecked(); 422 | uint8_t* keyData = (uint8_t *)node::Buffer::Data(bufferObj0); 423 | size_t keyLength = node::Buffer::Length(bufferObj0); 424 | 425 | if (keyLength != AESNODE_DEFAULT_KEY_LENGTH_BYTES) { 426 | Nan::ThrowError("Incorrect Arguments. Please provide a key of size " 427 | "32 bytes"); 428 | return; 429 | } 430 | 431 | // Unwrap the second argument to get the message buffer. 432 | v8::Local bufferObj1 = 433 | Nan::To(info[1]).ToLocalChecked(); 434 | uint8_t* messageData = (uint8_t *)node::Buffer::Data(bufferObj1); 435 | size_t messageLength = node::Buffer::Length(bufferObj1); 436 | 437 | // XOR random bytes with the given message buffer before encrypting it. 438 | std::vector temp(messageLength); 439 | obj->xorRandomData(temp, 440 | std::vector(messageData, messageData + messageLength) 441 | ); 442 | 443 | 444 | // Encrypt the XOR'd bytes using the given key and store in ciper data. 445 | std::vector cipherData; 446 | try { 447 | 448 | obj->encryptBlock(cipherData, 449 | std::vector(keyData, keyData + keyLength), temp 450 | ); 451 | 452 | } catch (const CryptoPP::Exception& e) { 453 | 454 | // Throw an error to node.js in case of encryption errors. 455 | Nan::ThrowError(e.what()); 456 | return; 457 | } 458 | 459 | // Copy cipherData vector into a node.js buffer. 460 | auto slowBuffer = Nan::CopyBuffer((const char*)cipherData.data(), 461 | cipherData.size()).ToLocalChecked(); 462 | 463 | // Set node.js buffer as return value of the function 464 | info.GetReturnValue().Set(slowBuffer); 465 | } 466 | 467 | 468 | 469 | // ------- 470 | // decrypt 471 | // ------- 472 | /** 473 | * @brief Unwraps the arguments to get the AES decryption key and 474 | * cipher, and decrypts the cipher block by block to return the 475 | * original message. 476 | * 477 | * Invoked as: 478 | * 'let message = obj.decrypt(key, cipher)' 479 | * 'key' is the buffer containing the AES key 480 | * 'cipher' is the buffer containing the cipher to be decrypted 481 | * 'message' is the buffer containing the original decypted message 482 | * PreCondition: key buffer size == AESNODE_DEFAULT_KEY_LENGTH_BYTES 483 | * 484 | * @param info node.js arguments wrapper for AES key and cipher to be 485 | * decrypted 486 | * 487 | * @return void 488 | */ 489 | NAN_METHOD(AESXOR256::decrypt) { 490 | 491 | AESXOR256* obj = ObjectWrap::Unwrap(info.Holder()); 492 | 493 | // Checking arguments. 494 | if (info.Length() < 2 495 | || !node::Buffer::HasInstance(info[0]) 496 | || !node::Buffer::HasInstance(info[1])) { 497 | 498 | Nan::ThrowError("Incorrect Arguments. Please provide buffers for 'key' " 499 | "and 'message' -> 'function encrypt(key, message)'"); 500 | return; 501 | } 502 | 503 | /* Unwrap the first argument to get the AES key buffer and validate 504 | * that the key length = AESNODE_DEFAULT_KEY_LENGTH_BYTES. 505 | */ 506 | v8::Local bufferObj0 = 507 | Nan::To(info[0]).ToLocalChecked(); 508 | uint8_t* keyData = (uint8_t *)node::Buffer::Data(bufferObj0); 509 | size_t keyLength = node::Buffer::Length(bufferObj0); 510 | 511 | if (keyLength != AESNODE_DEFAULT_KEY_LENGTH_BYTES) { 512 | Nan::ThrowError("Incorrect Arguments. Please provide a key of size " 513 | "32 bytes"); 514 | return; 515 | } 516 | 517 | // Unwrap the second argument to get the message buffer. 518 | v8::Local bufferObj1 = 519 | Nan::To(info[1]).ToLocalChecked(); 520 | uint8_t* cipherData = (uint8_t *)node::Buffer::Data(bufferObj1); 521 | size_t cipherLength = node::Buffer::Length(bufferObj1); 522 | 523 | 524 | // Decrypt the given cipher buffer using the given key. 525 | std::vector temp; 526 | try { 527 | 528 | obj->decryptBlock(temp, 529 | std::vector(keyData, keyData + keyLength), 530 | std::vector(cipherData, cipherData + cipherLength) 531 | ); 532 | 533 | } catch (const CryptoPP::Exception& e) { 534 | 535 | // throw an error to node.js in case of decryption errors 536 | Nan::ThrowError(e.what()); 537 | return; 538 | } 539 | 540 | // XOR random bytes with the decrypted message buffer. 541 | std::vector messageData(temp.size()); 542 | obj->xorRandomData(messageData, temp); 543 | 544 | // Copy messageData vector into a node.js buffer. 545 | auto slowBuffer = Nan::CopyBuffer((const char*)messageData.data(), 546 | messageData.size()).ToLocalChecked();; 547 | 548 | // Set node.js buffer as return value of the function. 549 | info.GetReturnValue().Set(slowBuffer); 550 | } 551 | 552 | 553 | 554 | // ---- 555 | // Init 556 | // ---- 557 | /** 558 | * @brief Initialization function for node.js object wrapper exported 559 | * by the addon. 560 | * 561 | * @param exports node.js module exports 562 | * 563 | * @return void 564 | */ 565 | void AESXOR256::Init(v8::Handle exports) { 566 | 567 | Nan::HandleScope scope; 568 | 569 | // Prepare constructor template. 570 | v8::Local tpl = Nan::New(New); 571 | tpl->SetClassName(Nan::New("AESXOR256").ToLocalChecked()); 572 | tpl->InstanceTemplate()->SetInternalFieldCount(2); 573 | 574 | // Prototype 575 | Nan::SetPrototypeMethod(tpl, "encrypt", encrypt); 576 | Nan::SetPrototypeMethod(tpl, "decrypt", decrypt); 577 | 578 | constructor.Reset(tpl->GetFunction()); 579 | 580 | // Setting node.js module.exports. 581 | exports->Set(Nan::New("AESXOR256").ToLocalChecked(), tpl->GetFunction()); 582 | } 583 | -------------------------------------------------------------------------------- /src/aesxor.h: -------------------------------------------------------------------------------- 1 | /** @file aesxor.h 2 | * @brief Class header for native object wrapped in javascript object 3 | * responsible for performing Crypto++ AES cryptography functions 4 | * 5 | * @author Aashish Sheshadri 6 | * @author Rohit Harchandani 7 | * 8 | * The MIT License (MIT) 9 | * 10 | * Copyright (c) 2015, 2016, 2017 PayPal 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to 14 | * deal in the Software without restriction, including without limitation the 15 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16 | * sell copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in 20 | * all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | * DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | #ifndef AESXOR_H 32 | #define AESXOR_H 33 | 34 | // ---------------------- 35 | // node.js addon includes 36 | // ---------------------- 37 | #include 38 | #include 39 | #include 40 | 41 | // ----------------- 42 | // cryptopp includes 43 | // ----------------- 44 | #include "aes.h" 45 | using CryptoPP::AES; 46 | 47 | // ---------------- 48 | // xor shift 128 includes 49 | // ---------------- 50 | #include "xorShift128.hpp" 51 | 52 | // --------- 53 | // AESXOR256 54 | // --------- 55 | 56 | /* 57 | * @class This class represents a C++ object exposing Crypto++ AES functions, 58 | * wrapped in a javascript object. 59 | * 60 | * The functions exposed to node.js are: 61 | * function encrypt(key, message) -> returns cipher 62 | * function decrypt(key, cipher) -> returns message 63 | */ 64 | class AESXOR256 : public Nan::ObjectWrap { 65 | 66 | private: 67 | 68 | // javascript object constructor 69 | static Nan::Persistent constructor; 70 | 71 | 72 | // ---- 73 | // data 74 | // ---- 75 | 76 | // xorShift128 77 | XORShift128 _rng; 78 | 79 | // AES key length 80 | static const int AESNODE_DEFAULT_KEY_LENGTH_BYTES; 81 | 82 | 83 | // ----------- 84 | // Constructor 85 | // ----------- 86 | /** 87 | * Constructor 88 | * @brief Initializes the object including the PCG random number 89 | * generator using the provided seed and cipher block size. 90 | * 91 | * @param seed seed for pcg rng 92 | * @param blockSize aes encryption block size 93 | * 94 | * @return 95 | */ 96 | explicit AESXOR256(std::vector seed); 97 | 98 | 99 | // --------- 100 | // getRandom 101 | // --------- 102 | /** 103 | * @brief Gets random uint64 values from pcg and stores them in a byte 104 | * array. 105 | * 106 | * @param random container for resulting random bytes 107 | * @param len number of random bytes required 108 | * 109 | * @return void 110 | */ 111 | void getRandom(uint8_t* random, int len); 112 | 113 | 114 | // ------------ 115 | // encryptBlock 116 | // ------------ 117 | /** 118 | * @brief Encrypts the given message using AES in GCM mode to provide 119 | * confidentiality and authenticity using the given key resulting 120 | * in the cipher block. 121 | * 122 | * @param cipher byte container for the resulting cipher 123 | * @param key byte container for the AES key 124 | * @param message byte container for the message 125 | * 126 | * @throw Cryptopp:Exception in case of encryption errors 127 | * 128 | * @return void 129 | */ 130 | void encryptBlock(std::vector& cipher, 131 | const std::vector& key, 132 | const std::vector& message); 133 | 134 | 135 | // ------------ 136 | // decryptBlock 137 | // ------------ 138 | /** 139 | * @brief Decrypts the given cipher using AES in GCM mode to provide 140 | * confidentiality and authenticity using the given key resulting 141 | * in the message block. 142 | * 143 | * @param message byte container for the resulting decrypted message 144 | * @param cipher byte container for the input cipher 145 | * @param key byte container for the AES key 146 | * 147 | * @throw Cryptopp:Exception in case of decryption errors 148 | * 149 | * @return void 150 | */ 151 | void decryptBlock(std::vector& message, 152 | const std::vector& key, 153 | const std::vector& cipher); 154 | 155 | 156 | // ------------- 157 | // xorRandomData 158 | // ------------- 159 | /** 160 | * @brief XORs the given input container with requal number of random 161 | * bytes obtained using the PCG random number generator. 162 | * 163 | * @param output byte container for the resulting XOR'd output 164 | * @param input byte container for the input data to be XOR'd with 165 | * random bytes 166 | * 167 | * @return void 168 | */ 169 | void xorRandomData( 170 | std::vector& output, 171 | const std::vector& input 172 | ); 173 | 174 | 175 | // --- 176 | // New 177 | // --- 178 | /** 179 | * @brief Creates the wrapped object and corresponding underlying 180 | * object with provided arguments - seed buffer 181 | * 182 | * Invoked as: 183 | * 'let obj = new AESXOR256(seed)' or 184 | * 'let obj = AESXOR256(seed)' 185 | * 'seed' is a buffer containing bytes representing the uint64 pcg seed 186 | * 187 | * @param info node.js arguments wrapper containing seed buffer and 188 | * block size 189 | * 190 | * @return void 191 | */ 192 | static NAN_METHOD(New); 193 | 194 | 195 | // ------- 196 | // encrypt 197 | // ------- 198 | /** 199 | * @brief Unwraps the arguments to get the AES encryption key and 200 | * message, and encrypts the message to return 201 | * the cipher. 202 | * 203 | * Invoked as: 204 | * 'let cipher = obj.encrypt(key, message)' 205 | * 'key' is the buffer containing the AES key 206 | * 'message' is the buffer containing the message to be encrypted 207 | * 'cipher' is the buffer containing the encrypted cipher 208 | * PreCondition: key buffer size == AESNODE_DEFAULT_KEY_LENGTH_BYTES 209 | * 210 | * @param info node.js arguments wrapper for AES key and message to be 211 | * encrypted 212 | * 213 | * @return void 214 | */ 215 | static NAN_METHOD(encrypt); 216 | 217 | 218 | // ------- 219 | // decrypt 220 | // ------- 221 | /** 222 | * @brief Unwraps the arguments to get the AES decryption key and 223 | * cipher, and decrypts the cipher to return the 224 | * original message. 225 | * 226 | * Invoked as: 227 | * 'let message = obj.decrypt(key, cipher)' 228 | * 'key' is the buffer containing the AES key 229 | * 'cipher' is the buffer containing the cipher to be decrypted 230 | * 'message' is the buffer containing the original decypted message 231 | * PreCondition: key buffer size == AESNODE_DEFAULT_KEY_LENGTH_BYTES 232 | * 233 | * @param info node.js arguments wrapper for AES key and cipher to be 234 | * decrypted 235 | * 236 | * @return void 237 | */ 238 | static NAN_METHOD(decrypt); 239 | 240 | public: 241 | 242 | // ---- 243 | // Init 244 | // ---- 245 | /** 246 | * @brief Initialization function for node.js object wrapper exported 247 | * by the addon. 248 | * 249 | * @param exports node.js module exports 250 | * 251 | * @return void 252 | */ 253 | static void Init(v8::Handle exports); 254 | 255 | 256 | }; 257 | 258 | #endif 259 | -------------------------------------------------------------------------------- /src/rng.cc: -------------------------------------------------------------------------------- 1 | /** @file rng.cc 2 | * @brief Definition of the class functions provided in rng.h including the 3 | * Worker class responsible for running async operations to check if 4 | * the RNG has state saved on the disk 5 | * 6 | * @author Aashish Sheshadri 7 | * @author Rohit Harchandani 8 | * 9 | * The MIT License (MIT) 10 | * 11 | * Copyright (c) 2015, 2016, 2017 PayPal 12 | * 13 | * Permission is hereby granted, free of charge, to any person obtaining a copy 14 | * of this software and associated documentation files (the "Software"), to 15 | * deal in the Software without restriction, including without limitation the 16 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 17 | * sell copies of the Software, and to permit persons to whom the Software is 18 | * furnished to do so, subject to the following conditions: 19 | * 20 | * The above copyright notice and this permission notice shall be included in 21 | * all copies or substantial portions of the Software. 22 | * 23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 28 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 29 | * DEALINGS IN THE SOFTWARE. 30 | */ 31 | 32 | // ----------------- 33 | // standard includes 34 | // ----------------- 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | // ---------------------- 42 | // node.js addon includes 43 | // ---------------------- 44 | #include 45 | 46 | // ----------------- 47 | // cryptopp includes 48 | // ----------------- 49 | #include 50 | using CryptoPP::SHA3_256; 51 | 52 | // ---------------- 53 | // library includes 54 | // ---------------- 55 | #include 56 | 57 | #include "rng.h" 58 | #include "util.h" 59 | 60 | #define MAX_ENTROPY_GEN_MULTIPLIER 6 61 | 62 | // javascript object constructor 63 | Nan::Persistent RNG::constructor; 64 | 65 | // ----------- 66 | // Constructor 67 | // ----------- 68 | /** 69 | * Constructor 70 | * @brief Initilizes and constructs internal data. 71 | * 72 | * @param initCallback callback to be invoked after async 73 | * operation 74 | * @param prng isaac RNG object pointer 75 | * @param fileId file identifier of RNG state on disk 76 | * @param digest key used to encrypt/decrypt RNG state on disk 77 | */ 78 | RNG::Worker::Worker(Nan::Callback* initCallback, 79 | IsaacRandomPool* prng, 80 | const std::string& fileId, 81 | const std::vector& digest 82 | ): Nan::AsyncWorker(initCallback), 83 | _prng(prng), 84 | _fileId(fileId), 85 | _digest(digest), 86 | _isLoaded(false) { 87 | 88 | } 89 | 90 | RNG::Worker::Worker(Nan::Callback* initCallback, 91 | IsaacRandomPool* prng, 92 | bool isLoaded 93 | ): Nan::AsyncWorker(initCallback), 94 | _prng(prng), 95 | _isLoaded(isLoaded) { 96 | 97 | } 98 | 99 | 100 | // ---------------- 101 | // HandleOKCallback 102 | // ---------------- 103 | /** 104 | * @brief Executed when the async work is complete without 105 | * error, this function will be run inside the main event 106 | * loop, invoking the given callback with result of the 107 | * async operation of checking saved RNG state. 108 | * 109 | * The keys are returned as the second argument to the callback 110 | * {enc: [publicKey], dec: [privateKey]} 111 | * 112 | * @return void 113 | */ 114 | void RNG::Worker::HandleOKCallback () { 115 | Nan::HandleScope scope; 116 | v8::Local status = Nan::New(); 117 | Nan::Set(status, 118 | Nan::New("code").ToLocalChecked(), 119 | Nan::New((int)_result)); 120 | Nan::Set(status, 121 | Nan::New("message").ToLocalChecked(), 122 | Nan::New("Success").ToLocalChecked()); 123 | 124 | v8::Local argv[] = {status}; 125 | if (callback->IsEmpty() == false) { 126 | callback->Call(1, argv); 127 | } 128 | } 129 | 130 | 131 | 132 | // ------------------- 133 | // HandleErrorCallback 134 | // ------------------- 135 | /** 136 | * @brief Executed when the async work is complete with 137 | * error, this function will be run inside the main event 138 | * loop, invoking the given callback with the 139 | * corresponding error. 140 | * 141 | * The error is returned as the first argument to the callback 142 | * {code: [statusCode], message: [errorMessage]} 143 | * 144 | * @return void 145 | */ 146 | void RNG::Worker::HandleErrorCallback () { 147 | Nan::HandleScope scope; 148 | 149 | v8::Local error = Nan::New(); 150 | Nan::Set(error, 151 | Nan::New("code").ToLocalChecked(), 152 | Nan::New((int)_result)); 153 | Nan::Set(error, 154 | Nan::New("message").ToLocalChecked(), 155 | Nan::New(ErrorMessage()).ToLocalChecked()); 156 | 157 | v8::Local argv[] = {error}; 158 | if (callback->IsEmpty() == false) { 159 | callback->Call(1, argv); 160 | } 161 | } 162 | 163 | 164 | 165 | // ------- 166 | // Execute 167 | // ------- 168 | /** 169 | * @brief Executed in a separate thread, asynchronously 170 | * checking if RNG has saved state on the disk and 171 | * communicating the status of the operation via the 172 | * Worker class. 173 | * 174 | * @return void 175 | */ 176 | void RNG::Worker::Execute() { 177 | // Check if the RNG has state on disk and is initialized in memory. 178 | 179 | if (_isLoaded == false) { 180 | _result = _prng->IsInitialized(_fileId, _digest); 181 | } else { 182 | _result = _prng->SaveState(); 183 | } 184 | 185 | if (_result == IsaacRandomPool::STATUS::SUCCESS) { 186 | return; 187 | } 188 | 189 | /* Set error message if the RNG is not initialized thus invoking the 190 | * error callback. 191 | */ 192 | if (_result == IsaacRandomPool::STATUS::FILE_NOT_FOUND) { 193 | SetErrorMessage("File Not Found"); 194 | } else if (_result == IsaacRandomPool::STATUS::DECRYPTION_ERROR) { 195 | SetErrorMessage("Decryption Error"); 196 | } else{ 197 | SetErrorMessage("Unknown Error"); 198 | } 199 | } 200 | 201 | 202 | 203 | // --- 204 | // New 205 | // --- 206 | /** 207 | * @brief Creates the node object and corresponding underlying object. 208 | * 209 | * Invoked as: 210 | * 'let obj = new RNG()' or 211 | * 'let obj = RNG()' 212 | * 213 | * @param info node.js arguments wrapper 214 | * @return void 215 | */ 216 | NAN_METHOD(RNG::New) { 217 | 218 | if (info.IsConstructCall()) { 219 | 220 | // Invoked as constructor: 'let obj = new RNG()'. 221 | RNG* obj = new RNG(); 222 | 223 | obj->Wrap(info.This()); 224 | info.GetReturnValue().Set(info.This()); 225 | 226 | } else { 227 | 228 | // Invoked as plain function `RNG()`, turn into construct call. 229 | const int argc = info.Length(); 230 | 231 | // Creating argument array for construct call. 232 | std::vector > argv; 233 | argv.reserve(argc); 234 | for (int i = 0; i < argc; ++i) { 235 | argv.push_back(info[i]); 236 | } 237 | 238 | v8::Local cons = Nan::New(constructor); 239 | info.GetReturnValue().Set(cons->NewInstance(argc, argv.data())); 240 | } 241 | } 242 | 243 | 244 | 245 | // ------------- 246 | // isInitialized 247 | // ------------- 248 | /** 249 | * @brief Unwraps the arguments to get the key and the file name 250 | * for saving the RNG state and creates an async worker to check 251 | * if the RNG has been initialized by checking if the state file 252 | * exists. Once the async work is complete the given callback is 253 | * invoked with the result of the operation. 254 | * 255 | * Invoked as: 256 | * 'obj.isInitialized(key, filename, function(result){})' 257 | * 'key' is a buffer containing the disk encryption/decryption key 258 | * 'filename' is the name of the RNG saved state file on disk 259 | * 'result' is a js object containing the code('code') and 260 | * message('message') 261 | * 262 | * @param info node.js arguments wrapper containing file id, folder 263 | * path for rng state and the callback function 264 | * 265 | * @return void 266 | */ 267 | NAN_METHOD(RNG::isInitialized) { 268 | 269 | RNG* obj = ObjectWrap::Unwrap(info.Holder()); 270 | 271 | // Check arguments. 272 | if (!node::Buffer::HasInstance(info[0])) { 273 | 274 | Nan::ThrowError("Incorrect Arguments. Key buffer not " 275 | "provided"); 276 | return; 277 | } 278 | 279 | /* Unwrap the first argument to get the buffer containing file 280 | * encryption/decryption key. 281 | */ 282 | v8::Local bufferObj = 283 | Nan::To(info[0]).ToLocalChecked(); 284 | 285 | uint8_t* bufferData = (uint8_t*)node::Buffer::Data(bufferObj); 286 | size_t bufferLength = node::Buffer::Length(bufferObj); 287 | 288 | /* Unwrap the second argument to get the file identifier of the saved 289 | * state on disk. 290 | */ 291 | std::string fileId = "./"; 292 | if (!info[1]->IsUndefined()) { 293 | 294 | v8::String::Utf8Value str(info[1]->ToString()); 295 | fileId = *str; 296 | 297 | } 298 | 299 | /* If the size of key buffer is less than AES key size then hash the given 300 | * data to get key of the required size. 301 | */ 302 | std::vector digest; 303 | if (bufferLength < 32) { 304 | 305 | digest.resize(CryptoPP::SHA3_256::DIGESTSIZE); 306 | std::string bufferString(reinterpret_cast(bufferData), 307 | reinterpret_cast(bufferData) + bufferLength); 308 | hashString(digest, bufferString); 309 | 310 | } else { 311 | 312 | digest.reserve(CryptoPP::SHA3_256::DIGESTSIZE); 313 | std::copy(bufferData, bufferData + bufferLength, 314 | std::back_inserter(digest)); 315 | } 316 | 317 | // Unwrap the third argument to get given callback function. 318 | Nan::Callback *callback = new Nan::Callback(info[2].As()); 319 | 320 | // Initialize the async worker and queue it. 321 | Worker* worker = new Worker(callback, &obj->prng, fileId, digest); 322 | 323 | Nan::AsyncQueueWorker(worker); 324 | 325 | } 326 | 327 | // --------------- 328 | // entropyStrength 329 | // --------------- 330 | 331 | /** 332 | * @brief Returns the possible strength of entropy avaible for mining. 333 | * 334 | * @return A string with values "WEAK", "MEDIUM" or "STRONG". If the only 335 | * source of entropy is the OS this makes the module's strength 336 | * WEAK w.r.t entropy, access to either the microphone or camera 337 | * results in Medium strength and finally access to the OS, camera, 338 | * microphone and more enables STRONG strength. 339 | */ 340 | NAN_METHOD(RNG::entropyStrength) { 341 | // Get a reference to the wrapped object from the argument. 342 | RNG* obj = ObjectWrap::Unwrap(info.Holder()); 343 | 344 | std::string strength = obj->prng.EntropyStrength(); 345 | // Return strength of underlying RNG used for key generation. 346 | info.GetReturnValue().Set( 347 | v8::String::NewFromUtf8(Nan::GetCurrentContext()->GetIsolate(), 348 | strength.c_str()) 349 | ); 350 | } 351 | 352 | // ---------- 353 | // initialize 354 | // ---------- 355 | /** 356 | * @brief Unwraps the arguments to get the key and the file name 357 | * for rng state and initilizes the RNG by gathering entropy. 358 | * 359 | * Invoked as: 360 | * 'obj.initialize(key, folder)' where 361 | * 'key' is a buffer containing the disk encryption/decryption key 362 | * 'filename' is the name of the RNG saved state file on disk 363 | * @param info node.js arguments wrapper containing key and filename 364 | * for saving the rng state 365 | * @return void 366 | */ 367 | NAN_METHOD(RNG::initialize) { 368 | 369 | RNG* obj = ObjectWrap::Unwrap(info.Holder()); 370 | 371 | // Check arguments 372 | if (!node::Buffer::HasInstance(info[0])) { 373 | 374 | Nan::ThrowError("Incorrect Arguments. File Identifier buffer not " 375 | "provided"); 376 | return; 377 | } 378 | 379 | /* Unwrap the first argument to get the buffer containing file 380 | * encryption/decryption key. 381 | */ 382 | v8::Local bufferObj = 383 | Nan::To(info[0]).ToLocalChecked(); 384 | 385 | uint8_t* bufferData = (uint8_t*)node::Buffer::Data(bufferObj); 386 | size_t bufferLength = node::Buffer::Length(bufferObj); 387 | 388 | /* Unwrap the second argument to get the file identifier of the saved 389 | * state on disk. 390 | */ 391 | std::string fileId = "./"; 392 | if (!info[1]->IsUndefined()) { 393 | 394 | v8::String::Utf8Value str(info[1]->ToString()); 395 | fileId = *str; 396 | 397 | } 398 | 399 | /* If the size of key buffer is less than AES key size then hash the given 400 | * data to get key of the required size. 401 | */ 402 | std::vector digest; 403 | if (bufferLength < 32) { 404 | 405 | digest.resize(CryptoPP::SHA3_256::DIGESTSIZE); 406 | std::string bufferString(reinterpret_cast(bufferData), 407 | reinterpret_cast(bufferData) + bufferLength); 408 | hashString(digest, bufferString); 409 | 410 | } else { 411 | 412 | digest.reserve(CryptoPP::SHA3_256::DIGESTSIZE); 413 | std::copy(bufferData, bufferData + bufferLength, 414 | std::back_inserter(digest)); 415 | } 416 | 417 | /* Initialize the global Isaac rng object and check if 418 | * initialization succeeded. if it fails, increase the multiplier argument 419 | * which causes more data to be collected to get higher entropy. 420 | */ 421 | int multiplier = 0; 422 | 423 | try { 424 | for (; multiplier < MAX_ENTROPY_GEN_MULTIPLIER; ++multiplier) { 425 | 426 | if (obj->prng.Initialize(fileId, multiplier, digest)) { 427 | break; 428 | } 429 | } 430 | } catch (const std::exception& ex) { 431 | 432 | // If there is any hardware error, catch and throw the error to node.js 433 | Nan::ThrowError(ex.what()); 434 | return; 435 | } 436 | 437 | // If initialization fails after max retries, throw an error to node.js. 438 | if (multiplier == MAX_ENTROPY_GEN_MULTIPLIER) { 439 | Nan::ThrowError("Not enough entropy!"); 440 | return; 441 | } 442 | 443 | info.GetReturnValue().Set(Nan::True()); 444 | 445 | } 446 | 447 | 448 | 449 | // -------- 450 | // getBytes 451 | // -------- 452 | /** 453 | * @brief Unwraps the arguments to get the number of random bytes 454 | * required and returns a buffer with the random output. 455 | * 456 | * Invoked as: 457 | * 'let buffer = obj.getBytes(numBytes)' where 458 | * 'numBytes' is the number of required random bytes 459 | * 'buffer' is a node.js buffer 460 | * 461 | * @param info node.js arguments wrapper containing number of random 462 | * bytes required 463 | * 464 | * @return void 465 | */ 466 | NAN_METHOD(RNG::getBytes) { 467 | 468 | RNG* obj = ObjectWrap::Unwrap(info.Holder()); 469 | 470 | // Unwrap the first argument to get the number of required random bytes. 471 | uint32_t val = 0; 472 | if (!info[0]->IsUndefined()) { 473 | val = info[0]->NumberValue(); 474 | } 475 | 476 | // Initialize 'output' vector containing the random bytes. 477 | std::vector output(val); 478 | 479 | // Invoke 'GenerateBlock' on the isaac RNG to get the required random bytes. 480 | try { 481 | 482 | obj->prng.GenerateBlock(output.data(), val); 483 | 484 | } catch (const std::exception& ex) { 485 | 486 | // Error thrown when getBytes invoked before RNG has been initialized. 487 | Nan::ThrowError(ex.what()); 488 | return; 489 | } 490 | 491 | // Copy the random bytes into node.js buffer. 492 | auto slowBuffer = Nan::CopyBuffer((const char*)output.data(), 493 | val).ToLocalChecked(); 494 | 495 | // Set node.js buffer as return value of the function. 496 | info.GetReturnValue().Set(slowBuffer); 497 | } 498 | 499 | 500 | 501 | // --------- 502 | // saveState 503 | // --------- 504 | /** 505 | * @brief Encryptes and saves the state of the RNG to disk 506 | * 507 | * Invoked as: 508 | * 'obj.saveState(function (result) {})' 509 | * 'result' is a js object containing the code('code') and 510 | * message('message') 511 | * 512 | * @return void 513 | */ 514 | NAN_METHOD(RNG::saveState) { 515 | RNG* obj = ObjectWrap::Unwrap(info.Holder()); 516 | 517 | // Unwrap the first argument to get given callback function. 518 | Nan::Callback *callback = new Nan::Callback(info[0].As()); 519 | 520 | // Initialize the async worker and queue it. 521 | Worker* worker = new Worker(callback, &obj->prng, true); 522 | 523 | Nan::AsyncQueueWorker(worker); 524 | } 525 | 526 | 527 | 528 | // ------- 529 | // destroy 530 | // ------- 531 | /** 532 | * @brief Destroys the underlying RNG object thus saving the state 533 | * to disk. 534 | * 535 | * Invoked as: 536 | * 'obj.destroy()' 537 | * 538 | * @return void 539 | */ 540 | NAN_METHOD(RNG::destroy) { 541 | RNG* obj = ObjectWrap::Unwrap(info.Holder()); 542 | 543 | obj->prng.Destroy(); 544 | } 545 | 546 | 547 | 548 | // ---- 549 | // Init 550 | // ---- 551 | /** 552 | * @brief Initialization function for node.js object wrapper exported 553 | * by the addon. 554 | * 555 | * @param exports node.js module exports 556 | * 557 | * @return void 558 | */ 559 | void RNG::Init(v8::Handle exports) { 560 | 561 | Nan::HandleScope scope; 562 | 563 | // Prepare constructor template. 564 | v8::Local tpl = Nan::New(New); 565 | tpl->SetClassName(Nan::New("RNG").ToLocalChecked()); 566 | tpl->InstanceTemplate()->SetInternalFieldCount(6); 567 | 568 | // Prototype 569 | Nan::SetPrototypeMethod(tpl, "getBytes", getBytes); 570 | Nan::SetPrototypeMethod(tpl, "isInitialized", isInitialized); 571 | Nan::SetPrototypeMethod(tpl, "entropyStrength", entropyStrength); 572 | Nan::SetPrototypeMethod(tpl, "initialize", initialize); 573 | Nan::SetPrototypeMethod(tpl, "saveState", saveState); 574 | Nan::SetPrototypeMethod(tpl, "destroy", destroy); 575 | 576 | constructor.Reset(tpl->GetFunction()); 577 | 578 | // Setting node.js module.exports. 579 | exports->Set(Nan::New("RNG").ToLocalChecked(), tpl->GetFunction()); 580 | } 581 | -------------------------------------------------------------------------------- /src/rng.h: -------------------------------------------------------------------------------- 1 | /** @file rng.h 2 | * @brief Class header for native object wrapped in javascript object 3 | * responsible for generating random numbers (isaac) based on a given 4 | * seed and saving/restoring the RNG state to the disk. The class also 5 | * provides the ability to accept a secret key to encrypt the state 6 | * saved to the disk 7 | * 8 | * @author Aashish Sheshadri 9 | * @author Rohit Harchandani 10 | * 11 | * The MIT License (MIT) 12 | * 13 | * Copyright (c) 2015, 2016, 2017 PayPal 14 | * 15 | * Permission is hereby granted, free of charge, to any person obtaining a copy 16 | * of this software and associated documentation files (the "Software"), to 17 | * deal in the Software without restriction, including without limitation the 18 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 19 | * sell copies of the Software, and to permit persons to whom the Software is 20 | * furnished to do so, subject to the following conditions: 21 | * 22 | * The above copyright notice and this permission notice shall be included in 23 | * all copies or substantial portions of the Software. 24 | * 25 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 30 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 31 | * DEALINGS IN THE SOFTWARE. 32 | */ 33 | 34 | #ifndef RNG_H 35 | #define RNG_H 36 | 37 | #include 38 | #include 39 | 40 | // ---------------------- 41 | // node.js addon includes 42 | // ---------------------- 43 | #include 44 | #include 45 | #include 46 | 47 | // ---------------- 48 | // library includes 49 | // ---------------- 50 | #include 51 | 52 | 53 | // --- 54 | // RNG 55 | // --- 56 | 57 | /* 58 | * @class This class represents the C++ object exposing our implementation of 59 | * isaac random number generator, wrapped inside the javascript object. 60 | * 61 | * The functions exposed to node.js are: 62 | * function isInitialized(key, filename, callback) 63 | * function initialize(key, filename) 64 | * function getBytes(n) -> returns node.js buffer with 'n' random bytes 65 | * function destroy() -> save RNG state to disk and destroy the object 66 | */ 67 | class RNG : public Nan::ObjectWrap { 68 | 69 | private: 70 | 71 | // javascript object constructor 72 | static Nan::Persistent constructor; 73 | 74 | // isaac RNG object 75 | IsaacRandomPool prng; 76 | 77 | // ------ 78 | // Worker 79 | // ------ 80 | /* 81 | * @class This class represents the node.js async worker responsible for 82 | * checking if the RNG has saved state on the disk and invoke the 83 | * given callback function with the status of the operation and 84 | */ 85 | class Worker: public Nan::AsyncWorker { 86 | 87 | private: 88 | // ---- 89 | // data 90 | // ---- 91 | // pointer to isaac RNG object 92 | IsaacRandomPool* _prng; 93 | // file identifier of RNG state on disk 94 | std::string _fileId; 95 | // key used to encrypt/decrypt RNG state on disk 96 | std::vector _digest; 97 | // status of operation of checking if RNG state is saved on disk 98 | IsaacRandomPool::STATUS _result; 99 | /* boolean indicating whether the Worker is going to save or 100 | * load the RNG state 101 | */ 102 | bool _isLoaded; 103 | 104 | public: 105 | // ----------- 106 | // Constructor 107 | // ----------- 108 | /** 109 | * Constructor 110 | * @brief Initilizes and constructs internal data. 111 | * 112 | * @param initCallback callback to be invoked after async 113 | * operation 114 | * @param prng isaac RNG object pointer 115 | * @param fileId file identifier of RNG state on disk 116 | * @param digest key used to encrypt/decrypt RNG state on disk 117 | */ 118 | Worker(Nan::Callback* initCallback, 119 | IsaacRandomPool* prng, 120 | const std::string& fileId, 121 | const std::vector& digest 122 | ); 123 | 124 | Worker(Nan::Callback* initCallback, 125 | IsaacRandomPool* prng, 126 | bool isLoaded 127 | ); 128 | 129 | 130 | // ---------------- 131 | // HandleOKCallback 132 | // ---------------- 133 | /** 134 | * @brief Executed when the async work is complete without 135 | * error, this function will be run inside the main event 136 | * loop, invoking the given callback with result of the 137 | * async operation of checking saved RNG state. 138 | * 139 | * The keys are returned as the second argument to the callback 140 | * {enc: [publicKey], dec: [privateKey]} 141 | * 142 | * @return void 143 | */ 144 | void HandleOKCallback(); 145 | 146 | 147 | // ------------------- 148 | // HandleErrorCallback 149 | // ------------------- 150 | /** 151 | * @brief Executed when the async work is complete with 152 | * error, this function will be run inside the main event 153 | * loop, invoking the given callback with the 154 | * corresponding error. 155 | * 156 | * The error is returned as the first argument to the callback 157 | * {code: [statusCode], message: [errorMessage]} 158 | * 159 | * @return void 160 | */ 161 | void HandleErrorCallback(); 162 | 163 | 164 | // ------- 165 | // Execute 166 | // ------- 167 | /** 168 | * @brief Executed in a separate thread, asynchronously 169 | * checking if RNG has saved state on the disk and 170 | * communicating the status of the operation via the 171 | * Worker class. 172 | * 173 | * @return void 174 | */ 175 | void Execute(); 176 | }; 177 | 178 | 179 | // --- 180 | // New 181 | // --- 182 | /** 183 | * @brief Creates the node object and corresponding underlying object. 184 | * 185 | * Invoked as: 186 | * 'let obj = new RNG()' or 187 | * 'let obj = RNG()' 188 | * 189 | * @param info node.js arguments wrapper 190 | * @return void 191 | */ 192 | static NAN_METHOD(New); 193 | 194 | 195 | // ------------- 196 | // isInitialized 197 | // ------------- 198 | /** 199 | * @brief Unwraps the arguments to get the key and the file name 200 | * for saving the RNG state and creates an async worker to check 201 | * if the RNG has been initialized by checking if the state file 202 | * exists. Once the async work is complete the given callback is 203 | * invoked with the result of the operation. 204 | * 205 | * Invoked as: 206 | * 'obj.isInitialized(key, filename, function(result){})' 207 | * 'key' is a buffer containing the disk encryption/decryption key 208 | * 'filename' is the name of the RNG saved state file on disk 209 | * 'result' is a js object containing the code('code') and 210 | * message('message') 211 | * 212 | * @param info node.js arguments wrapper containing file id, folder 213 | * path for rng state and the callback function 214 | * 215 | * @return void 216 | */ 217 | static NAN_METHOD(isInitialized); 218 | 219 | // --------------- 220 | // entropyStrength 221 | // --------------- 222 | 223 | /** 224 | * @brief Returns the possible strength of entropy avaible for mining. 225 | * 226 | * @return A string with values "WEAK", "MEDIUM" or "STRONG". If the only 227 | * source of entropy is the OS this makes the module's strength 228 | * WEAK w.r.t entropy, access to either the microphone or camera 229 | * results in Medium strength and finally access to the OS, camera, 230 | * microphone and more enables STRONG strength. 231 | */ 232 | static NAN_METHOD(entropyStrength); 233 | 234 | // ---------- 235 | // initialize 236 | // ---------- 237 | /** 238 | * @brief Unwraps the arguments to get the file id and the folder path 239 | * for rng state and initilizes the RNG by gathering entropy. 240 | * 241 | * Invoked as: 242 | * 'obj.initialize(key, filename)' where 243 | * 'key' is a buffer containing the disk encryption/decryption key 244 | * 'filename' is the name of the RNG saved state file on disk 245 | * @param info node.js arguments wrapper containing key and filename 246 | * for saving the rng state 247 | * @return void 248 | */ 249 | static NAN_METHOD(initialize); 250 | 251 | 252 | // -------- 253 | // getBytes 254 | // -------- 255 | /** 256 | * @brief Unwraps the arguments to get the number of random bytes 257 | * required and returns a buffer with the random output. 258 | * 259 | * Invoked as: 260 | * 'let buffer = obj.getBytes(numBytes)' where 261 | * 'numBytes' is the number of required random bytes 262 | * 'buffer' is a node.js buffer 263 | * 264 | * @param info node.js arguments wrapper containing number of random 265 | * bytes required 266 | * 267 | * @return void 268 | */ 269 | static NAN_METHOD(getBytes); 270 | 271 | 272 | // --------- 273 | // saveState 274 | // --------- 275 | /** 276 | * @brief Encryptes and saves the state of the RNG to disk 277 | * 278 | * Invoked as: 279 | * 'obj.saveState(function (result) {})' 280 | * 'result' is a js object containing the code('code') and 281 | * message('message') 282 | * 283 | * @return void 284 | */ 285 | static NAN_METHOD(saveState); 286 | 287 | 288 | // ------- 289 | // destroy 290 | // ------- 291 | /** 292 | * @brief Destroys the underlying RNG object thus saving the state 293 | * to disk. 294 | * 295 | * Invoked as: 296 | * 'obj.destroy()' 297 | * 298 | * @return void 299 | */ 300 | static NAN_METHOD(destroy); 301 | 302 | public: 303 | 304 | // ---- 305 | // Init 306 | // ---- 307 | /** 308 | * @brief Initialization function for node.js object wrapper exported 309 | * by the addon. 310 | * 311 | * @param exports node.js module exports 312 | * 313 | * @return void 314 | */ 315 | static void Init(v8::Handle exports); 316 | 317 | }; 318 | 319 | #endif 320 | -------------------------------------------------------------------------------- /src/seifecc.cc: -------------------------------------------------------------------------------- 1 | /** @file eccisaac.cc 2 | * @brief Definition of the class functions provided in eccisaac.h and helper 3 | * functions to print the public/private ECC keys 4 | * 5 | * @author Aashish Sheshadri 6 | * @author Rohit Harchandani 7 | * 8 | * The MIT License (MIT) 9 | * 10 | * Copyright (c) 2015, 2016, 2017 PayPal 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to 14 | * deal in the Software without restriction, including without limitation the 15 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16 | * sell copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in 20 | * all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | * DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | // ----------------- 32 | // standard includes 33 | // ----------------- 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | // ---------------------- 40 | // node.js addon includes 41 | // ---------------------- 42 | #include 43 | 44 | // ----------------- 45 | // cryptopp includes 46 | // ----------------- 47 | #include "hex.h" 48 | 49 | #include "filters.h" 50 | using CryptoPP::StringSink; 51 | using CryptoPP::StringSource; 52 | using CryptoPP::ArraySink; 53 | using CryptoPP::ArraySource; 54 | using CryptoPP::PK_EncryptorFilter; 55 | using CryptoPP::PK_DecryptorFilter; 56 | 57 | #include "eccrypto.h" 58 | using CryptoPP::ECP; 59 | using CryptoPP::ECIES; 60 | using CryptoPP::ECPPoint; 61 | using CryptoPP::DL_GroupParameters_EC; 62 | using CryptoPP::DL_FixedBasePrecomputation; 63 | 64 | #include "pubkey.h" 65 | using CryptoPP::DL_PrivateKey_EC; 66 | using CryptoPP::DL_PublicKey_EC; 67 | 68 | #include "asn.h" 69 | #include "oids.h" 70 | namespace ASN1 = CryptoPP::ASN1; 71 | 72 | #include "cryptlib.h" 73 | 74 | #include "sha3.h" 75 | using CryptoPP::SHA3_256; 76 | 77 | #include "osrng.h" 78 | using CryptoPP::AutoSeededRandomPool; 79 | 80 | 81 | // ---------------- 82 | // library includes 83 | // ---------------- 84 | #include "seifecc.h" 85 | #include "util.h" 86 | 87 | namespace { 88 | // Strings represnting names of files to be stored to the disk. 89 | // rng state file name 90 | const std::string RNG_STATE_FILE_NAME = ".ecies.rng"; 91 | // private key file name 92 | const std::string PRIV_KEY_FILE_NAME = "ecies.private.key"; 93 | // public key file name 94 | const std::string PUB_KEY_FILE_NAME = "ecies.public.key"; 95 | } 96 | 97 | // javascript object constructor 98 | Nan::Persistent SEIFECC::constructor; 99 | 100 | // Helper functions for printing the public and private keys. 101 | void PrintPrivateKey(const DL_PrivateKey_EC& key, 102 | std::ostream& out = std::cout); 103 | void PrintPublicKey(const DL_PublicKey_EC& key, 104 | std::ostream& out = std::cout); 105 | 106 | 107 | // --------------- 108 | // PrintPrivateKey 109 | // --------------- 110 | /** 111 | * @brief Prints the private key components 112 | * 113 | * @param key private key object 114 | * @param out output stram 115 | * @params flags output stream flags 116 | * 117 | * @return void 118 | */ 119 | void PrintPrivateKey(const DL_PrivateKey_EC& key, std::ostream& out) { 120 | 121 | const std::ios_base::fmtflags flags = out.flags(); 122 | 123 | // Group parameters 124 | const DL_GroupParameters_EC& params = key.GetGroupParameters(); 125 | // Base precomputation 126 | const DL_FixedBasePrecomputation& bpc = 127 | params.GetBasePrecomputation(); 128 | // Public Key (just do the exponentiation) 129 | const ECPPoint point = bpc.Exponentiate(params.GetGroupPrecomputation(), 130 | key.GetPrivateExponent()); 131 | 132 | out << "Modulus: " << std::hex << 133 | params.GetCurve().GetField().GetModulus() << std::endl; 134 | out << "Cofactor: " << std::hex << params.GetCofactor() << std::endl; 135 | 136 | out << "Coefficients" << std::endl; 137 | out << " A: " << std::hex << params.GetCurve().GetA() << std::endl; 138 | out << " B: " << std::hex << params.GetCurve().GetB() << std::endl; 139 | 140 | out << "Base Point" << std::endl; 141 | out << " x: " << std::hex << params.GetSubgroupGenerator().x << std::endl; 142 | out << " y: " << std::hex << params.GetSubgroupGenerator().y << std::endl; 143 | 144 | out << "Public Point" << std::endl; 145 | out << " x: " << std::hex << point.x << std::endl; 146 | out << " y: " << std::hex << point.y << std::endl; 147 | 148 | out << "Private Exponent (multiplicand): " << std::endl; 149 | out << " " << std::hex << key.GetPrivateExponent() << std::endl; 150 | 151 | out << std::endl; 152 | out.flags(flags); 153 | } 154 | 155 | 156 | // -------------- 157 | // PrintPublicKey 158 | // -------------- 159 | /** 160 | * @brief Prints the public key components. 161 | * 162 | * @param key public key object 163 | * @param out output stram 164 | * @params flags output stream flags 165 | * 166 | * @return void 167 | */ 168 | void PrintPublicKey(const DL_PublicKey_EC& key, std::ostream& out) 169 | { 170 | const std::ios_base::fmtflags flags = out.flags(); 171 | 172 | // Group parameters 173 | const DL_GroupParameters_EC& params = key.GetGroupParameters(); 174 | // Public key 175 | const ECPPoint& point = key.GetPublicElement(); 176 | 177 | out << "Modulus: " << std::hex << 178 | params.GetCurve().GetField().GetModulus() << std::endl; 179 | out << "Cofactor: " << std::hex << params.GetCofactor() << std::endl; 180 | 181 | out << "Coefficients" << std::endl; 182 | out << " A: " << std::hex << params.GetCurve().GetA() << std::endl; 183 | out << " B: " << std::hex << params.GetCurve().GetB() << std::endl; 184 | 185 | out << "Base Point" << std::endl; 186 | out << " x: " << std::hex << params.GetSubgroupGenerator().x << std::endl; 187 | out << " y: " << std::hex << params.GetSubgroupGenerator().y << std::endl; 188 | 189 | out << "Public Point" << std::endl; 190 | out << " x: " << std::hex << point.x << std::endl; 191 | out << " y: " << std::hex << point.y << std::endl; 192 | 193 | out << std::endl; 194 | out.flags(flags); 195 | } 196 | 197 | 198 | 199 | // ----------- 200 | // Constructor 201 | // ----------- 202 | /** 203 | * Constructor 204 | * @brief Initilizes and constructs internal data. 205 | * 206 | * @param initCallback callback to be invoked after async 207 | * operation 208 | * @param key disk access key for public/private keys and 209 | * rng state 210 | * @param folderPath folder containing keys and rng state files 211 | */ 212 | SEIFECC::Worker::Worker( 213 | Nan::Callback* initCallback, 214 | const std::vector& key, 215 | const std::string& folderPath 216 | ): Nan::AsyncWorker(initCallback), 217 | _wfolderPath(folderPath), 218 | _wkey(key) { 219 | 220 | } 221 | 222 | 223 | 224 | // ---------------- 225 | // HandleOKCallback 226 | // ---------------- 227 | /** 228 | * @brief Executed when the async work is complete without 229 | * error, this function will be run inside the main event 230 | * loop, invoking the given callback with the loaded 231 | * keys as an argument. 232 | * 233 | * The keys are returned as the second argument to the callback 234 | * {enc: [publicKey], dec: [privateKey]} 235 | * 236 | * @return void 237 | */ 238 | void SEIFECC::Worker::HandleOKCallback () { 239 | Nan::HandleScope scope; 240 | 241 | /* Creating js status object with 'code' set as the status code(0) and 242 | * 'message' as "Success". 243 | */ 244 | v8::Local status = Nan::New(); 245 | Nan::Set(status, 246 | Nan::New("code").ToLocalChecked(), 247 | Nan::New(0) 248 | ); 249 | Nan::Set(status, 250 | Nan::New("message").ToLocalChecked(), 251 | Nan::New("Success").ToLocalChecked() 252 | ); 253 | 254 | /* Creating js object with 'enc' set as the public key and 255 | * 'dec' as the private key. 256 | */ 257 | v8::Local ret = Nan::New(); 258 | Nan::Set(ret, 259 | Nan::New("enc").ToLocalChecked(), 260 | Nan::New(_encodedPub).ToLocalChecked() 261 | ); 262 | Nan::Set(ret, 263 | Nan::New("dec").ToLocalChecked(), 264 | Nan::New(_encodedPriv).ToLocalChecked() 265 | ); 266 | 267 | // Invoking given callback with an undefined error and keys object. 268 | v8::Local argv[] = {status, ret}; 269 | 270 | callback->Call(2, argv); 271 | } 272 | 273 | 274 | 275 | // ------------------- 276 | // HandleErrorCallback 277 | // ------------------- 278 | /** 279 | * @brief Executed when the async work is complete with 280 | * error, this function will be run inside the main event 281 | * loop, invoking the given callback with the 282 | * corresponding error. 283 | * 284 | * The error is returned as the first argument to the callback 285 | * {code: [statusCode], message: [errorMessage]} 286 | * 287 | * @return void 288 | */ 289 | void SEIFECC::Worker::HandleErrorCallback () { 290 | Nan::HandleScope scope; 291 | 292 | /* Creating js error object with 'code' set as the status code and 293 | * 'message' as the error message. 294 | */ 295 | v8::Local error = Nan::New(); 296 | Nan::Set(error, 297 | Nan::New("code").ToLocalChecked(), 298 | Nan::New((int)_status) 299 | ); 300 | Nan::Set(error, 301 | Nan::New("message").ToLocalChecked(), 302 | Nan::New(ErrorMessage()).ToLocalChecked() 303 | ); 304 | 305 | // Invoke callback with the above error object and undefined keys. 306 | v8::Local argv[] = {error, Nan::Undefined()}; 307 | 308 | callback->Call(2, argv); 309 | } 310 | 311 | 312 | 313 | // ------- 314 | // Execute 315 | // ------- 316 | /** 317 | * @brief Executed in a separate thread, asynchronously loading 318 | * the public/private keys from the disk and 319 | * communicating the status of the operation via the 320 | * Worker class. 321 | * 322 | * @return void 323 | */ 324 | void SEIFECC::Worker::Execute() { 325 | 326 | try { 327 | // Try to load keys from the disk into the string arguments. 328 | _status = SEIFECC::loadKeys( 329 | _encodedPub, 330 | _encodedPriv, 331 | _wkey, 332 | _wfolderPath 333 | ); 334 | 335 | if (_status == STATUS::SUCCESS) { 336 | return; 337 | } 338 | 339 | // In case of error set the error message with appropriate message. 340 | if (_status == STATUS::FILE_NOT_FOUND) { 341 | SetErrorMessage("File Not Found"); 342 | } else if (_status == STATUS::DECRYPTION_ERROR) { 343 | SetErrorMessage("Decryption Error"); 344 | } else { 345 | SetErrorMessage("Unknown Error"); 346 | } 347 | return; 348 | 349 | } catch (...) { 350 | // Exception thrown while loading keys. 351 | SetErrorMessage("Unknown Error"); 352 | return; 353 | } 354 | 355 | } 356 | 357 | 358 | // ----------- 359 | // Constructor 360 | // ----------- 361 | /** 362 | * Constructor 363 | * @brief Initilizes and constructs internal data. 364 | * 365 | * @param keyData byte vector corresponding to disk access key 366 | * @param folderPath folder containing the encrypted keys 367 | */ 368 | SEIFECC::SEIFECC( 369 | const std::vector& keyData, 370 | const std::string& folderPath 371 | ): _key(keyData), _folderPath(folderPath) { 372 | 373 | } 374 | 375 | 376 | 377 | // -------------- 378 | // SavePrivateKey 379 | // -------------- 380 | /** 381 | * @brief Encrypt and save the private key to the disk with the given 382 | * file name. 383 | * 384 | * @param privateKey private key object 385 | * @param file file name of encrypted private key 386 | * @param key disk access key for public/private keys and 387 | * rng state 388 | * @param folderPath folder containing keys and rng state files 389 | * 390 | * @return void 391 | */ 392 | void SEIFECC::SavePrivateKey( 393 | const PrivateKey& privateKey, 394 | const std::string& file, 395 | const std::vector& key, 396 | const std::string& folderPath 397 | ) 398 | { 399 | // Create file encryptor object. 400 | FileCryptopp fileEncryptor(folderPath + file); 401 | 402 | // Save the private key into a string using CryptoPP StringSink. 403 | std::string keyStr; 404 | StringSink keySink(keyStr); 405 | 406 | privateKey.Save(keySink); 407 | 408 | // Write the string form of the private key to the file. 409 | std::stringstream fss(keyStr); 410 | fileEncryptor.writeFile(fss, key); 411 | } 412 | 413 | 414 | // ------------- 415 | // SavePublicKey 416 | // ------------- 417 | /** 418 | * @brief Encrypt and save the public key to the disk with the given 419 | * file name. 420 | * 421 | * @param publicKey public key object 422 | * @param file file name of encrypted public key 423 | * @param key disk access key for public/private keys and 424 | * rng state 425 | * @param folderPath folder containing keys and rng state files 426 | * 427 | * @return void 428 | */ 429 | void SEIFECC::SavePublicKey( 430 | const PublicKey& publicKey, 431 | const std::string& file, 432 | const std::vector& key, 433 | const std::string& folderPath 434 | ) 435 | { 436 | // Create file encryptor object. 437 | FileCryptopp fileEncryptor(folderPath + file); 438 | 439 | // Save the public key into a string using CryptoPP StringSink. 440 | std::string keyStr; 441 | StringSink keySink(keyStr); 442 | 443 | publicKey.Save(keySink); 444 | 445 | // Write the string form of the public key to the file. 446 | std::stringstream fss(keyStr); 447 | fileEncryptor.writeFile(fss, key); 448 | } 449 | 450 | 451 | 452 | // -------------- 453 | // LoadPrivateKey 454 | // -------------- 455 | /** 456 | * @brief Decrypt the file with the given name and load it as the 457 | * private key. 458 | * 459 | * @param privateKey private key object 460 | * @param file file name of encrypted private key 461 | * @param key disk access key for public/private keys and 462 | * rng state 463 | * @param folderPath folder containing keys and rng state files 464 | * 465 | * @return status code indicating success or cause of error 466 | */ 467 | SEIFECC::STATUS SEIFECC::LoadPrivateKey( 468 | PrivateKey& privateKey, 469 | const std::string& file, 470 | const std::vector& key, 471 | const std::string& folderPath 472 | ) 473 | { 474 | // Create file decryptor object. 475 | FileCryptopp fileDecryptor(folderPath + file); 476 | 477 | // If private key file does not exist then return appropriate error. 478 | if (!fileDecryptor.fileExists()) { 479 | return SEIFECC::STATUS::FILE_NOT_FOUND; 480 | } 481 | 482 | // Read the encrypted file to get the private key string. 483 | std::stringstream fss; 484 | if (!fileDecryptor.readFile(fss, key)) { 485 | // If decryption fails return an error. 486 | return SEIFECC::STATUS::DECRYPTION_ERROR; 487 | } 488 | 489 | std::string keyStr = fss.str(); 490 | 491 | // Use CryptoPP StringSource to get the private key object from the string. 492 | StringSource keySource(keyStr, true); 493 | 494 | privateKey.Load(keySource); 495 | 496 | return SEIFECC::STATUS::SUCCESS; 497 | } 498 | 499 | 500 | // ------------- 501 | // LoadPublicKey 502 | // ------------- 503 | /** 504 | * @brief Decrypt the file with the given name and load it as the 505 | * public key. 506 | * 507 | * @param publicKey public key object 508 | * @param file file name of encrypted public key 509 | * @param key disk access key for public/private keys and 510 | * rng state 511 | * @param folderPath folder containing keys and rng state files 512 | * 513 | * @return status code indicating success or cause of error 514 | */ 515 | SEIFECC::STATUS SEIFECC::LoadPublicKey( 516 | PublicKey& publicKey, 517 | const std::string& file, 518 | const std::vector& key, 519 | const std::string& folderPath 520 | ) 521 | { 522 | // Create file decryptor object. 523 | FileCryptopp fileDecryptor(folderPath + file); 524 | 525 | // If public key file does not exist then return appropriate error. 526 | if (!fileDecryptor.fileExists()) { 527 | return SEIFECC::STATUS::FILE_NOT_FOUND; 528 | } 529 | 530 | // Read the encrypted file to get the public key string. 531 | std::stringstream fss; 532 | if (!fileDecryptor.readFile(fss, key)) { 533 | // If decryption fails return an error. 534 | return SEIFECC::STATUS::DECRYPTION_ERROR; 535 | } 536 | 537 | std::string keyStr = fss.str(); 538 | 539 | // Use CryptoPP StringSource to get the public key object from the string. 540 | StringSource keySource(keyStr, true); 541 | 542 | publicKey.Load(keySource); 543 | 544 | return SEIFECC::STATUS::SUCCESS; 545 | } 546 | 547 | 548 | 549 | // -------- 550 | // loadKeys 551 | // -------- 552 | /** 553 | * @brief Loads the keys by decrypting existing files and initializes 554 | * the random number generator. 555 | * 556 | * @param encodedPub public key to be loaded from encrypted file 557 | * @param encodedPriv private key to be loaded from encrypted file 558 | * @param key disk access key for public/private keys and 559 | * rng state 560 | * @param folderPath folder containing keys and rng state files 561 | * 562 | * @return status code indicating success or cause of error 563 | */ 564 | SEIFECC::STATUS SEIFECC::loadKeys( 565 | std::string& encodedPub, 566 | std::string& encodedPriv, 567 | const std::vector& key, 568 | const std::string& folderPath 569 | ) 570 | { 571 | // ECC Decryption object containing the private key. 572 | ECIES::Decryptor d0; 573 | 574 | // Load the private key from encrypted file on disk. 575 | SEIFECC::STATUS rc; 576 | if ((rc = LoadPrivateKey(d0.AccessPrivateKey(), PRIV_KEY_FILE_NAME, 577 | key, folderPath)) 578 | != SEIFECC::STATUS::SUCCESS) { 579 | /* If loading the key fails return the error which could be due to 580 | * the file not being present on the disk or due to a decryption error. 581 | */ 582 | return rc; 583 | } 584 | 585 | // ECC Decryption object containing the public key 586 | ECIES::Encryptor e0; 587 | 588 | // Load the public key from encrypted file on disk. 589 | if ((rc = LoadPublicKey(e0.AccessPublicKey(), PUB_KEY_FILE_NAME, 590 | key, folderPath)) 591 | != SEIFECC::STATUS::SUCCESS) { 592 | /* If loading the key fails return the error which could be due to 593 | * the file not being present on the disk or due to a decryption error. 594 | */ 595 | return rc; 596 | } 597 | 598 | /* Get the string versions of the keys from the encryption and decryption 599 | * objects using CryptoPP StringSink. 600 | */ 601 | std::string pubStr, privStr; 602 | StringSink pubSs(pubStr), privSs(privStr); 603 | e0.GetPublicKey().Save(pubSs); 604 | d0.GetPrivateKey().Save(privSs); 605 | 606 | // Hex encode the string keys using CryptoPP StringSource and HexEncoder. 607 | StringSource ss1(pubStr, true, 608 | new CryptoPP::HexEncoder(new StringSink(encodedPub))); 609 | 610 | StringSource ss2(privStr, true, 611 | new CryptoPP::HexEncoder(new StringSink(encodedPriv))); 612 | 613 | // Hash the hex encoded private key string using CryptoPP SHA3_256. 614 | std::vector digest(CryptoPP::SHA3_256::DIGESTSIZE); 615 | hashString(digest, encodedPriv); 616 | 617 | // Using the default file name for the RNG saved state. 618 | std::string fileName = RNG_STATE_FILE_NAME; 619 | 620 | std::string fileId = folderPath + fileName; 621 | 622 | return SEIFECC::STATUS::SUCCESS; 623 | } 624 | 625 | 626 | // ------------ 627 | // generateKeys 628 | // ------------ 629 | /** 630 | * @brief Initialize the RNG and use it to generate the public and 631 | * private keys, encrypt them and save to disk. 632 | * 633 | * @param encodedPub public key to be generated 634 | * @param encodedPriv private key to be generated 635 | * @param key disk access key for public/private keys and 636 | * rng state 637 | * @param folderPath folder containing keys and rng state files 638 | * 639 | * @return boolean indicating success/failure 640 | */ 641 | bool SEIFECC::generateKeys( 642 | std::string& encodedPub, 643 | std::string& encodedPriv, 644 | const std::vector& key, 645 | const std::string& folderPath 646 | ) 647 | { 648 | // Using the default file name for the RNG saved state. 649 | std::string fileName = RNG_STATE_FILE_NAME; 650 | 651 | std::string fileId = folderPath + fileName; 652 | 653 | IsaacRandomPool prng; 654 | 655 | /* Initialize the global Isaac rng object and check if 656 | * initialization succeeded. if it fails, increase the multiplier argument 657 | * which causes more data to be collected to get higher entropy. 658 | */ 659 | int multiplier = 0; 660 | 661 | try { 662 | for (; multiplier < 6; ++multiplier) { 663 | if (prng.Initialize(fileId, multiplier)) { 664 | break; 665 | } 666 | } 667 | } catch (const std::exception& ex) { 668 | 669 | // If there is any hardware error, catch and throw the error to node.js 670 | Nan::ThrowError(ex.what()); 671 | return false; 672 | } 673 | 674 | // If initialization fails after max retries, throw an error to node.js. 675 | if (multiplier == 6) { 676 | Nan::ThrowError("Not enough entropy!"); 677 | return false; 678 | } 679 | 680 | // ECC Decryption object created using our Isaac RNG and secp521r1 curve. 681 | ECIES::Decryptor d0(prng, CryptoPP::ASN1::secp521r1()); 682 | 683 | // ECC Encryption object corresponding to the above decryptor object. 684 | ECIES::Encryptor e0(d0); 685 | 686 | /* Generate the private and public keys using our Isaac RNG and 687 | * save them to encrypted files on the disk in the given folder. 688 | */ 689 | try { 690 | d0.GetPrivateKey().ThrowIfInvalid(prng, 3); 691 | e0.GetPublicKey().ThrowIfInvalid(prng, 3); 692 | SavePrivateKey(d0.GetPrivateKey(), PRIV_KEY_FILE_NAME, key, folderPath); 693 | SavePublicKey(e0.GetPublicKey(), PUB_KEY_FILE_NAME, key, folderPath); 694 | } catch (...) { 695 | return false; 696 | } 697 | 698 | // Get the string versions of the public and private keys using StringSink. 699 | std::string pubStr, privStr; 700 | StringSink pubSs(pubStr), privSs(privStr); 701 | e0.GetPublicKey().Save(pubSs); 702 | d0.GetPrivateKey().Save(privSs); 703 | 704 | // Hex encode the string keys using CryptoPP StringSource and HexEncoder. 705 | StringSource ss1(pubStr, true, 706 | new CryptoPP::HexEncoder(new StringSink(encodedPub))); 707 | 708 | StringSource ss2(privStr, true, 709 | new CryptoPP::HexEncoder(new StringSink(encodedPriv))); 710 | 711 | return true; 712 | } 713 | 714 | 715 | 716 | 717 | 718 | // --- 719 | // New 720 | // --- 721 | /** 722 | * @brief Creates the node object and corresponding underlying object 723 | * with provided arguments - disk access key and folder 724 | * containing the encrypted keys. 725 | * 726 | * Invoked as: 727 | * 'let obj = new SEIFECC(diskKey, folder)' or 728 | * 'let obj = SEIFECC(diskKey, folder)' where 729 | * 'diskKey' is the key used to encrypt the keys and rng state 730 | * 'folder' is the folder where the keys and rng state are saved on disk 731 | * 732 | * @param info node.js arguments wrapper containing the disk access key 733 | * and folder path 734 | * 735 | * @return 736 | */ 737 | NAN_METHOD(SEIFECC::New) { 738 | 739 | if (info.IsConstructCall()) { 740 | 741 | // Invoked as constructor: 'let obj = new SEIFECC()'. 742 | 743 | // Check arguments. 744 | if (!node::Buffer::HasInstance(info[0])) { 745 | 746 | Nan::ThrowError("Incorrect Arguments. Disk access key buffer " 747 | "not provided"); 748 | return; 749 | } 750 | 751 | /* Unwrap the first argument to get the key buffer used to store 752 | * RNG state to the disk. 753 | */ 754 | v8::Local bufferObj = 755 | Nan::To(info[0]).ToLocalChecked(); 756 | 757 | uint8_t* bufferData = (uint8_t*)node::Buffer::Data(bufferObj); 758 | size_t bufferLength = node::Buffer::Length(bufferObj); 759 | 760 | /* If the size of key buffer is less than AES key size then hash the 761 | * given data to get key of the required size. 762 | */ 763 | std::vector digest; 764 | if (bufferLength < 32) { 765 | digest.resize(CryptoPP::SHA3_256::DIGESTSIZE); 766 | std::string bufferString(reinterpret_cast(bufferData), 767 | reinterpret_cast(bufferData) + bufferLength); 768 | hashString(digest, bufferString); 769 | 770 | } else { 771 | digest.reserve(CryptoPP::SHA3_256::DIGESTSIZE); 772 | std::copy(bufferData, bufferData + bufferLength, 773 | std::back_inserter(digest)); 774 | } 775 | 776 | /* Unwrap the second argument to get the folder on disk where the RNG 777 | * state and keys will be encrypted and stored. 778 | */ 779 | std::string folder = "./"; 780 | if (!info[1]->IsUndefined()) { 781 | v8::String::Utf8Value str(info[1]->ToString()); 782 | folder = *str; 783 | if (folder.back() != '/') { 784 | folder = folder + "/"; 785 | } 786 | } 787 | 788 | // Create the wrapped object using the disk access key and given folder. 789 | SEIFECC* obj = new SEIFECC(digest, folder); 790 | 791 | obj->Wrap(info.This()); 792 | info.GetReturnValue().Set(info.This()); 793 | 794 | } else { 795 | 796 | // Invoked as plain function `SEIFECC(...)`, turn into construct call. 797 | const int argc = info.Length(); 798 | 799 | // Creating argument array for construct call. 800 | std::vector > argv; 801 | argv.reserve(argc); 802 | for (int i = 0; i < argc; ++i) { 803 | argv.push_back(info[i]); 804 | } 805 | 806 | v8::Local cons = Nan::New(constructor); 807 | info.GetReturnValue().Set(cons->NewInstance(argc, argv.data())); 808 | 809 | } 810 | } 811 | 812 | 813 | 814 | 815 | // -------- 816 | // loadKeys 817 | // -------- 818 | /** 819 | * @brief Creates an async worker to load keys from the disk and 820 | * invokes the callback function with the error object (if 821 | * applicable) and/or the object containing the keys. 822 | * 823 | * Invoked as: 824 | * 'obj.loadKeys(function(status, keys){})' where 825 | * 'status' (if applicable) is of the form: 826 | * {code: [statusCode], message: [statusMessage]} 827 | * 'keys' (if available) is of the form: 828 | * {enc: [publicKey], dec: [privateKey]} 829 | * 830 | * @param info node.js arguments wrapper 831 | * 832 | * @return void 833 | */ 834 | NAN_METHOD(SEIFECC::loadKeys) { 835 | 836 | // Get a reference to the wrapped object from the argument. 837 | SEIFECC* obj = ObjectWrap::Unwrap(info.Holder()); 838 | 839 | // Unwrap the first argument to get given callback function. 840 | Nan::Callback *callback = new Nan::Callback(info[0].As()); 841 | 842 | // Initialize the async worker and queue it. 843 | Worker* worker = new Worker( 844 | callback, 845 | obj->_key, 846 | obj->_folderPath 847 | ); 848 | 849 | Nan::AsyncQueueWorker(worker); 850 | } 851 | 852 | 853 | // --------------- 854 | // entropyStrength 855 | // --------------- 856 | 857 | /** 858 | * @brief Returns the possible strength of entropy avaible for mining. 859 | * 860 | * @return A string with values "WEAK", "MEDIUM" or "STRONG". If the only 861 | * source of entropy is the OS this makes the module's strength 862 | * WEAK w.r.t entropy, access to either the microphone or camera 863 | * results in Medium strength and finally access to the OS, camera, 864 | * microphone and more enables STRONG strength. 865 | */ 866 | NAN_METHOD(SEIFECC::entropyStrength) { 867 | // Get a reference to the wrapped object from the argument. 868 | SEIFECC* obj = ObjectWrap::Unwrap(info.Holder()); 869 | 870 | std::string strength = obj->prng.EntropyStrength(); 871 | // Return strength of underlying RNG used for key generation. 872 | info.GetReturnValue().Set( 873 | v8::String::NewFromUtf8(Nan::GetCurrentContext()->GetIsolate(), 874 | strength.c_str()) 875 | ); 876 | } 877 | 878 | 879 | // ------------ 880 | // generateKeys 881 | // ------------ 882 | /** 883 | * @brief Initializes the isaac RNG and uses it to generate the 884 | * public/private keys and return them to the caller. 885 | * 886 | * Invoked as: 887 | * 'let keys = obj.generateKeys()' where 888 | * 'keys' (if available) is of the form: 889 | * {enc: [publicKey], dec: [privateKey]} 890 | * 891 | * @param info node.js arguments wrapper 892 | * 893 | * @return void 894 | */ 895 | NAN_METHOD(SEIFECC::generateKeys) { 896 | 897 | // Get a reference to the wrapped object from the argument. 898 | SEIFECC* obj = ObjectWrap::Unwrap(info.Holder()); 899 | 900 | // Generate the public and private keys as strings and save them to disk. 901 | std::string encodedPub, encodedPriv; 902 | if (!obj->generateKeys( 903 | encodedPub, 904 | encodedPriv, 905 | obj->_key, 906 | obj->_folderPath 907 | ) 908 | ) { 909 | 910 | return; 911 | } 912 | 913 | /* Creating js object with 'enc' set as the public key and 914 | * 'dec' as the private key. 915 | */ 916 | v8::Local ret = Nan::New(); 917 | Nan::Set(ret, 918 | Nan::New("enc").ToLocalChecked(), 919 | Nan::New(encodedPub).ToLocalChecked()); 920 | Nan::Set(ret, 921 | Nan::New("dec").ToLocalChecked(), 922 | Nan::New(encodedPriv).ToLocalChecked()); 923 | 924 | // Set the above object as the value to be returned to node.js. 925 | info.GetReturnValue().Set(ret); 926 | } 927 | 928 | 929 | 930 | // ------- 931 | // encrypt 932 | // ------- 933 | /** 934 | * @brief Unwraps the arguments to get the public key and message and 935 | * encrypt the message using the public key to return the cipher. 936 | * 937 | * Invoked as: 938 | * 'let cipher = obj.encrypt(key, message)' 939 | * 'key' is the string containing the hex encoded ECC public key 940 | * 'message' is the buffer containing the message to be encrypted 941 | * 'cipher' is the string containing the encrypted cipher 942 | * 943 | * @param info node.js arguments wrapper containing public key string 944 | * and message 945 | * 946 | * @return void 947 | */ 948 | NAN_METHOD(SEIFECC::encrypt) { 949 | 950 | // Check arguments. 951 | if (info[0]->IsUndefined()) { 952 | Nan::ThrowError("Incorrect Arguments. Missing Public key string"); 953 | return; 954 | } 955 | 956 | if (!node::Buffer::HasInstance(info[1])) { 957 | 958 | Nan::ThrowError("Incorrect Arguments. Message buffer not provided"); 959 | return; 960 | } 961 | 962 | SEIFECC* obj = ObjectWrap::Unwrap(info.Holder()); 963 | 964 | // Unwrap the first argument to get the hex encoded public key string. 965 | v8::String::Utf8Value str(info[0]->ToString()); 966 | std::string pubStr(*str); 967 | 968 | // Unwrap the second argument to get the message buffer to be encrypted. 969 | v8::Local bufferObj = 970 | Nan::To(info[1]).ToLocalChecked(); 971 | 972 | uint8_t* messageData = (uint8_t*)node::Buffer::Data(bufferObj); 973 | size_t messageLength = node::Buffer::Length(bufferObj); 974 | 975 | // Vector containing hex encoded encrypted cipher. 976 | std::vector enc; 977 | try { 978 | /* Hex decode the string to get the public key string using 979 | * CryptoPP StringSource and HexDecoder and store in 'em'. 980 | */ 981 | std::string em; 982 | StringSource ss0(pubStr, true, 983 | new CryptoPP::HexDecoder(new StringSink(em))); 984 | 985 | /* This decoded string can now be converted to public key object wrapped 986 | * in the ECC encryption object using StringSource. 987 | */ 988 | ECIES::Encryptor e1; 989 | StringSource ss(em, true); 990 | e1.AccessPublicKey().Load(ss); 991 | 992 | AutoSeededRandomPool prng; 993 | 994 | /* Apply the CryptoPP PK_EncryptorFilter transformer to encrypt the 995 | * message buffer; store the result in a vector 'em0' using ArraySource. 996 | */ 997 | std::string em0; 998 | ArraySource ss1 ( 999 | messageData, 1000 | messageLength, 1001 | true, 1002 | new PK_EncryptorFilter( 1003 | prng, 1004 | e1, 1005 | new StringSink(em0) 1006 | ) 1007 | ); 1008 | 1009 | enc.resize(em0.size()); 1010 | // Hex encode the string cipher using CryptoPP StringSource & HexEncoder 1011 | StringSource ss6( 1012 | em0, 1013 | true, 1014 | new ArraySink(enc.data(), enc.size()) 1015 | ); 1016 | 1017 | } catch (const std::exception& ex) { 1018 | Nan::ThrowError(ex.what()); 1019 | return; 1020 | } catch (...) { 1021 | Nan::ThrowError("Unknown error while encrypting message"); 1022 | return; 1023 | } 1024 | 1025 | // Set the encoded cipher output as the value to be returned to node.js. 1026 | // info.GetReturnValue().Set( 1027 | // Nan::New(encoded.c_str()).ToLocalChecked()); 1028 | 1029 | // Copy cipherData vector into a node.js buffer. 1030 | auto slowBuffer = Nan::CopyBuffer((const char*)enc.data(), 1031 | enc.size()).ToLocalChecked(); 1032 | 1033 | // Set node.js buffer as return value of the function 1034 | info.GetReturnValue().Set(slowBuffer); 1035 | } 1036 | 1037 | 1038 | // ------- 1039 | // decrypt 1040 | // ------- 1041 | /** 1042 | * @brief Unwraps the arguments to get private key and cipher and 1043 | * decrypt the cipher using the private key to return the 1044 | * original message. 1045 | * 1046 | * Invoked as: 1047 | * 'let message = obj.decrypt(key, cipher)' 1048 | * 'key' is the string containing the hex encoded ECC private key 1049 | * 'cipher' is the string containing the cipher to be decrypted 1050 | * 'message' is the buffer containing the decrypted message 1051 | * 1052 | * @param info node.js arguments wrapper containing private key string 1053 | * and cipher. 1054 | * 1055 | * @return void 1056 | */ 1057 | NAN_METHOD(SEIFECC::decrypt) { 1058 | 1059 | // Check arguments. 1060 | if (info[0]->IsUndefined()) { 1061 | Nan::ThrowError("Incorrect Arguments. Missing Public key string"); 1062 | return; 1063 | } 1064 | 1065 | if (info[1]->IsUndefined() || !node::Buffer::HasInstance(info[1])) { 1066 | Nan::ThrowError("Incorrect Arguments. Missing encrypted cipher buffer"); 1067 | return; 1068 | } 1069 | 1070 | SEIFECC* obj = ObjectWrap::Unwrap(info.Holder()); 1071 | 1072 | // Unwrap the first argument to get the hex encoded private key string. 1073 | v8::String::Utf8Value str(info[0]->ToString()); 1074 | std::string privStr(*str); 1075 | 1076 | // Unwrap the second argument to get the cipher buffer. 1077 | v8::Local bufferObj1 = 1078 | Nan::To(info[1]).ToLocalChecked(); 1079 | uint8_t* cipherData = (uint8_t *)node::Buffer::Data(bufferObj1); 1080 | size_t cipherLength = node::Buffer::Length(bufferObj1); 1081 | 1082 | // string containing decrypted string message 1083 | std::string dm0; 1084 | 1085 | try { 1086 | 1087 | /* Hex decode the string to get the private key string using 1088 | * CryptoPP StringSource and HexDecoder and store it in string 'em'. 1089 | */ 1090 | std::string em; 1091 | StringSource ss0(privStr, true, 1092 | new CryptoPP::HexDecoder(new StringSink(em))); 1093 | 1094 | /* This decoded string can now be converted to private key object 1095 | * wrapped in the ECC decryption object using StringSource. 1096 | */ 1097 | ECIES::Decryptor d1; 1098 | StringSource ss(em, true); 1099 | d1.AccessPrivateKey().Load(ss); 1100 | 1101 | AutoSeededRandomPool prng; 1102 | 1103 | /* Apply the CryptoPP PK_DecryptorFilter transformer to decrypt the 1104 | * cipher buffer and store the result in a string using StringSink. 1105 | */ 1106 | ArraySource ss6( 1107 | cipherData, 1108 | cipherLength, 1109 | true, 1110 | new PK_DecryptorFilter(prng, d1, new StringSink(dm0)) 1111 | ); 1112 | 1113 | } catch (const std::exception& ex) { 1114 | 1115 | Nan::ThrowError(ex.what()); 1116 | return; 1117 | 1118 | } catch (...) { 1119 | 1120 | Nan::ThrowError("Unknown error while encrypting message"); 1121 | return; 1122 | } 1123 | 1124 | // Copy the decrypted string message into a node.js buffer. 1125 | auto slowBuffer = Nan::CopyBuffer((const char*)dm0.data(), 1126 | dm0.size()).ToLocalChecked(); 1127 | 1128 | // Set the buffer as the value to returned to node.js. 1129 | info.GetReturnValue().Set(slowBuffer); 1130 | } 1131 | 1132 | 1133 | // ---- 1134 | // Init 1135 | // ---- 1136 | /** 1137 | * @brief Initialization function for node.js object wrapper exported 1138 | * by the addon. 1139 | * 1140 | * @param exports node.js module exports 1141 | * 1142 | * @return void 1143 | */ 1144 | void SEIFECC::Init(v8::Handle exports) { 1145 | 1146 | Nan::HandleScope scope; 1147 | 1148 | // Prepare constructor template. 1149 | v8::Local tpl = Nan::New(New); 1150 | tpl->SetClassName(Nan::New("SEIFECC").ToLocalChecked()); 1151 | tpl->InstanceTemplate()->SetInternalFieldCount(5); 1152 | 1153 | // Prototype 1154 | Nan::SetPrototypeMethod(tpl, "loadKeys", loadKeys); 1155 | Nan::SetPrototypeMethod(tpl, "entropyStrength", entropyStrength); 1156 | Nan::SetPrototypeMethod(tpl, "generateKeys", generateKeys); 1157 | Nan::SetPrototypeMethod(tpl, "encrypt", encrypt); 1158 | Nan::SetPrototypeMethod(tpl, "decrypt", decrypt); 1159 | 1160 | constructor.Reset(tpl->GetFunction()); 1161 | 1162 | // Setting node.js module.exports. 1163 | exports->Set(Nan::New("SEIFECC").ToLocalChecked(), tpl->GetFunction()); 1164 | } 1165 | -------------------------------------------------------------------------------- /src/seifecc.h: -------------------------------------------------------------------------------- 1 | /** @file eccisaac.h 2 | * @brief Class header for native object wrapped in javascript object 3 | * responsible for performing Crypto++ ECC cryptography functions 4 | * 5 | * @author Aashish Sheshadri 6 | * @author Rohit Harchandani 7 | * 8 | * The MIT License (MIT) 9 | * 10 | * Copyright (c) 2015, 2016, 2017 PayPal 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to 14 | * deal in the Software without restriction, including without limitation the 15 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16 | * sell copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in 20 | * all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | * DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | #ifndef SEIFECC_H 32 | #define SEIFECC_H 33 | 34 | // ----------------- 35 | // standard includes 36 | // ----------------- 37 | #include 38 | #include 39 | 40 | // ---------------------- 41 | // node.js addon includes 42 | // ---------------------- 43 | #include 44 | #include 45 | #include 46 | 47 | // ----------------- 48 | // cryptopp includes 49 | // ----------------- 50 | #include "pubkey.h" 51 | using CryptoPP::PublicKey; 52 | using CryptoPP::PrivateKey; 53 | 54 | // ---------------- 55 | // library includes 56 | // ---------------- 57 | #include 58 | 59 | 60 | // -------- 61 | // SEIFECC 62 | // -------- 63 | 64 | /* 65 | * @class This class represents a C++ object wrapped inside a javascript object, 66 | * exposing Crypto++ ECC functions using our implementation of 67 | * isaac random number generator. 68 | * 69 | * The functions exposed to node.js are: 70 | * function loadKeys() -> returns public/private key object 71 | * function generateKeys() -> returns public/private key object 72 | * function encrypt(publicKey, message) -> returns cipher 73 | * function decrypt(privateKey, cipher) -> returns message 74 | */ 75 | class SEIFECC : public Nan::ObjectWrap { 76 | 77 | private: 78 | 79 | // javascript object constructor 80 | static Nan::Persistent constructor; 81 | 82 | // Status enum for different types of errors 83 | enum class STATUS:int { 84 | SUCCESS = 0, // Success 85 | FILE_NOT_FOUND = -1, // RNG state file not found 86 | DECRYPTION_ERROR = -2, // Error Decrypting RNG state file 87 | ENTROPY_ERROR = -3, // Error gathering entropy 88 | RNG_INIT_ERROR = -4 // RNG not initialized 89 | }; 90 | 91 | // ---- 92 | // data 93 | // ---- 94 | // Disk encrypt/decrypt key 95 | std::vector _key; 96 | // Folder containing keys + RNG state 97 | std::string _folderPath; 98 | 99 | // isaac RNG object 100 | IsaacRandomPool prng; 101 | 102 | // ------ 103 | // Worker 104 | // ------ 105 | /* 106 | * @class This class represents the node.js async worker responsible for 107 | * loading public/private keys from the disk and invoke the 108 | * given callback function with the status of the operation and 109 | * keys if available 110 | */ 111 | class Worker: public Nan::AsyncWorker { 112 | 113 | private: 114 | // ---- 115 | // data 116 | // ---- 117 | 118 | // folder containing keys and rng state files 119 | std::string _wfolderPath; 120 | // disk access key for public/private keys and rng state 121 | std::vector _wkey; 122 | // status of loading keys 123 | SEIFECC::STATUS _status; 124 | // encoded public key 125 | std::string _encodedPub; 126 | // encoded private key 127 | std::string _encodedPriv; 128 | 129 | public: 130 | // ----------- 131 | // Constructor 132 | // ----------- 133 | /** 134 | * Constructor 135 | * @brief Initilizes and constructs internal data. 136 | * 137 | * @param initCallback callback to be invoked after async 138 | * operation 139 | * @param key disk access key for public/private keys and 140 | * rng state 141 | * @param folderPath folder containing keys and rng state files 142 | */ 143 | // Worker(Nan::Callback* initCallback, SEIFECC* obj); 144 | Worker( 145 | Nan::Callback* initCallback, 146 | const std::vector& key, 147 | const std::string& folderPath 148 | ); 149 | 150 | 151 | // ---------------- 152 | // HandleOKCallback 153 | // ---------------- 154 | /** 155 | * @brief Executed when the async work is complete without 156 | * error, this function will be run inside the main event 157 | * loop, invoking the given callback with the loaded 158 | * keys as an argument. 159 | * 160 | * The keys are returned as the second argument to the callback 161 | * {enc: [publicKey], dec: [privateKey]} 162 | * 163 | * @return void 164 | */ 165 | void HandleOKCallback (); 166 | 167 | 168 | // ------------------- 169 | // HandleErrorCallback 170 | // ------------------- 171 | /** 172 | * @brief Executed when the async work is complete with 173 | * error, this function will be run inside the main event 174 | * loop, invoking the given callback with the 175 | * corresponding error. 176 | * 177 | * The error is returned as the first argument to the callback 178 | * {code: [statusCode], message: [errorMessage]} 179 | * 180 | * @return void 181 | */ 182 | void HandleErrorCallback (); 183 | 184 | 185 | // ------- 186 | // Execute 187 | // ------- 188 | /** 189 | * @brief Executed in a separate thread, asynchronously loading 190 | * the public/private keys from the disk and 191 | * communicating the status of the operation via the 192 | * Worker class. 193 | * 194 | * @return void 195 | */ 196 | void Execute(); 197 | }; 198 | 199 | 200 | 201 | // ----------- 202 | // Constructor 203 | // ----------- 204 | /** 205 | * Constructor 206 | * @brief Initilizes and constructs internal data. 207 | * 208 | * @param keyData byte vector corresponding to disk access key 209 | * @param folderPath folder containing the encrypted keys 210 | */ 211 | explicit SEIFECC(const std::vector& keyData, 212 | const std::string& folderPath); 213 | 214 | 215 | // -------------- 216 | // SavePrivateKey 217 | // -------------- 218 | /** 219 | * @brief Encrypt and save the private key to the disk with the given 220 | * file name. 221 | * 222 | * @param privateKey private key object 223 | * @param file file name of encrypted private key 224 | * @param key disk access key for public/private keys and 225 | * rng state 226 | * @param folderPath folder containing keys and rng state files 227 | * 228 | * @return void 229 | */ 230 | static void SavePrivateKey( 231 | const PrivateKey& privateKey, 232 | const std::string& file, 233 | const std::vector& key, 234 | const std::string& folderPath 235 | ); 236 | 237 | 238 | // ------------- 239 | // SavePublicKey 240 | // ------------- 241 | /** 242 | * @brief Encrypt and save the public key to the disk with the given 243 | * file name. 244 | * 245 | * @param publicKey public key object 246 | * @param file file name of encrypted public key 247 | * @param key disk access key for public/private keys and 248 | * rng state 249 | * @param folderPath folder containing keys and rng state files 250 | * 251 | * @return void 252 | */ 253 | static void SavePublicKey( 254 | const PublicKey& publicKey, 255 | const std::string& file, 256 | const std::vector& key, 257 | const std::string& folderPath 258 | ); 259 | 260 | 261 | // -------------- 262 | // LoadPrivateKey 263 | // -------------- 264 | /** 265 | * @brief Decrypt the file with the given name and load it as the 266 | * private key. 267 | * 268 | * @param key private key object 269 | * @param file file name of encrypted private key 270 | * @param key disk access key for public/private keys and 271 | * rng state 272 | * @param folderPath folder containing keys and rng state files 273 | * 274 | * @return status code indicating success or cause of error 275 | */ 276 | static STATUS LoadPrivateKey( 277 | PrivateKey& privateKey, 278 | const std::string& file, 279 | const std::vector& key, 280 | const std::string& folderPath 281 | ); 282 | 283 | 284 | // ------------- 285 | // LoadPublicKey 286 | // ------------- 287 | /** 288 | * @brief Decrypt the file with the given name and load it as the 289 | * public key. 290 | * 291 | * @param key public key object 292 | * @param file file name of encrypted public key 293 | * @param key disk access key for public/private keys and 294 | * rng state 295 | * @param folderPath folder containing keys and rng state files 296 | * 297 | * @return status code indicating success or cause of error 298 | */ 299 | static STATUS LoadPublicKey( 300 | PublicKey& publicKey, 301 | const std::string& file, 302 | const std::vector& key, 303 | const std::string& folderPath 304 | ); 305 | 306 | 307 | // -------- 308 | // loadKeys 309 | // -------- 310 | /** 311 | * @brief Loads the keys by decrypting existing files and initializes 312 | * the random number generator. 313 | * 314 | * @param encodedPub public key to be loaded from encrypted file 315 | * @param encodedPriv private key to be loaded from encrypted file 316 | * @param prng isaac rng object reference 317 | * @param key disk access key for public/private keys and 318 | * rng state 319 | * @param folderPath folder containing keys and rng state files 320 | * 321 | * @return status code indicating success or cause of error 322 | */ 323 | static STATUS loadKeys( 324 | std::string& encodedPub, 325 | std::string& encodedPriv, 326 | const std::vector& key, 327 | const std::string& folderPath 328 | ); 329 | 330 | 331 | // ------------ 332 | // generateKeys 333 | // ------------ 334 | /** 335 | * @brief Initialize the RNG and use it to generate the public and 336 | * private keys, encrypt them and save to disk. 337 | * 338 | * @param encodedPub public key to be generated 339 | * @param encodedPriv private key to be generated 340 | * @param prng isaac rng object reference 341 | * @param key disk access key for public/private keys and 342 | * rng state 343 | * @param folderPath folder containing keys and rng state files 344 | * 345 | * @return boolean indicating success/failure 346 | */ 347 | static bool generateKeys( 348 | std::string& encodedPub, 349 | std::string& encodedPriv, 350 | const std::vector& key, 351 | const std::string& folderPath 352 | ); 353 | 354 | 355 | 356 | // --- 357 | // New 358 | // --- 359 | /** 360 | * @brief Creates the node object and corresponding underlying object 361 | * with provided arguments - disk access key and folder 362 | * containing the encrypted keys. 363 | * 364 | * Invoked as: 365 | * 'let obj = new SEIFECC(diskKey, folder)' or 366 | * 'let obj = SEIFECC(diskKey, folder)' where 367 | * 'diskKey' is the key used to encrypt the keys and rng state 368 | * 'folder' is the folder where the keys and rng state are saved on disk 369 | * 370 | * @param info node.js arguments wrapper containing the disk access key 371 | * and folder path 372 | * 373 | * @return 374 | */ 375 | static NAN_METHOD(New); 376 | 377 | 378 | // -------- 379 | // loadKeys 380 | // -------- 381 | /** 382 | * @brief Creates an async worker to load keys from the disk and 383 | * invokes the callback function with the error object (if 384 | * applicable) and/or the object containing the keys. 385 | * 386 | * Invoked as: 387 | * 'obj.loadKeys(function(status, keys){})' where 388 | * 'status' (if applicable) is of the form: 389 | * {code: [statusCode], message: [statusMessage]} 390 | * 'keys' (if available) is of the form: 391 | * {enc: [publicKey], dec: [privateKey]} 392 | * 393 | * @param info node.js arguments wrapper 394 | * 395 | * @return void 396 | */ 397 | static NAN_METHOD(loadKeys); 398 | 399 | // --------------- 400 | // entropyStrength 401 | // --------------- 402 | 403 | /** 404 | * @brief Returns the possible strength of entropy avaible for mining. 405 | * 406 | * @return A string with values "WEAK", "MEDIUM" or "STRONG". If the only 407 | * source of entropy is the OS this makes the module's strength 408 | * WEAK w.r.t entropy, access to either the microphone or camera 409 | * results in Medium strength and finally access to the OS, camera, 410 | * microphone and more enables STRONG strength. 411 | */ 412 | static NAN_METHOD(entropyStrength); 413 | 414 | 415 | // ------------ 416 | // generateKeys 417 | // ------------ 418 | /** 419 | * @brief Initializes the isaac RNG and uses it to generate the 420 | * public/private keys and return them to the caller. 421 | * 422 | * Invoked as: 423 | * 'let keys = obj.generateKeys()' where 424 | * 'keys' (if available) is of the form: 425 | * {enc: [publicKey], dec: [privateKey]} 426 | * 427 | * @param info node.js arguments wrapper 428 | * 429 | * @return void 430 | */ 431 | static NAN_METHOD(generateKeys); 432 | 433 | 434 | // ------- 435 | // encrypt 436 | // ------- 437 | /** 438 | * @brief Unwraps the arguments to get the public key and message and 439 | * encrypt the message using the public key to return the cipher. 440 | * 441 | * Invoked as: 442 | * 'let cipher = obj.encrypt(key, message)' 443 | * 'key' is the string containing the hex encoded ECC public key 444 | * 'message' is the buffer containing the message to be encrypted 445 | * 'cipher' is the string containing the encrypted cipher 446 | * 447 | * @param info node.js arguments wrapper containing public key string 448 | * and message 449 | * 450 | * @return void 451 | */ 452 | static NAN_METHOD(encrypt); 453 | 454 | 455 | // ------- 456 | // decrypt 457 | // ------- 458 | /** 459 | * @brief Unwraps the arguments to get private key and cipher and 460 | * decrypt the cipher using the private key to return the 461 | * original message. 462 | * 463 | * Invoked as: 464 | * 'let message = obj.decrypt(key, cipher)' 465 | * 'key' is the string containing the hex encoded ECC private key 466 | * 'cipher' is the string containing the cipher to be decrypted 467 | * 'message' is the buffer containing the decrypted message 468 | * 469 | * @param info node.js arguments wrapper containing private key string 470 | * and cipher. 471 | * 472 | * @return void 473 | */ 474 | static NAN_METHOD(decrypt); 475 | 476 | public: 477 | 478 | // ---- 479 | // Init 480 | // ---- 481 | /** 482 | * @brief Initialization function for node.js object wrapper exported 483 | * by the addon. 484 | * 485 | * @param exports node.js module exports 486 | * 487 | * @return void 488 | */ 489 | static void Init(v8::Handle exports); 490 | 491 | }; 492 | 493 | #endif 494 | -------------------------------------------------------------------------------- /src/seifsha3.cc: -------------------------------------------------------------------------------- 1 | /** @file seifsha3.cc 2 | * @brief Definition of the class functions provided in seifsha3.h 3 | * 4 | * @author Aashish Sheshadri 5 | * @author Rohit Harchandani 6 | * 7 | * The MIT License (MIT) 8 | * 9 | * Copyright (c) 2015, 2016, 2017 PayPal 10 | * 11 | * Permission is hereby granted, free of charge, to any person obtaining a copy 12 | * of this software and associated documentation files (the "Software"), to 13 | * deal in the Software without restriction, including without limitation the 14 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 15 | * sell copies of the Software, and to permit persons to whom the Software is 16 | * furnished to do so, subject to the following conditions: 17 | * 18 | * The above copyright notice and this permission notice shall be included in 19 | * all copies or substantial portions of the Software. 20 | * 21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 26 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 27 | * DEALINGS IN THE SOFTWARE. 28 | */ 29 | 30 | // ----------------- 31 | // standard includes 32 | // ----------------- 33 | #include 34 | #include 35 | #include 36 | 37 | // ---------------------- 38 | // node.js addon includes 39 | // ---------------------- 40 | #include 41 | 42 | // ----------------- 43 | // cryptopp includes 44 | // ----------------- 45 | #include "sha3.h" 46 | using CryptoPP::SHA3_256; 47 | 48 | // ---------------- 49 | // library includes 50 | // ---------------- 51 | #include 52 | 53 | #include "seifsha3.h" 54 | #include "util.h" 55 | 56 | 57 | // javascript object constructor 58 | Nan::Persistent SEIFSHA3::constructor; 59 | 60 | 61 | 62 | // --- 63 | // New 64 | // --- 65 | /** 66 | * @brief Creates the node object and corresponding underlying object. 67 | * 68 | * Invoked as: 69 | * 'let obj = new SEIFSHA3()' or 70 | * 'let obj = SEIFSHA3()' 71 | * 72 | * @param info node.js arguments wrapper 73 | * 74 | * @return void 75 | */ 76 | NAN_METHOD(SEIFSHA3::New) { 77 | 78 | if (info.IsConstructCall()) { 79 | 80 | // Invoked as constructor: 'let obj = new SEIFSHA3()'. 81 | SEIFSHA3* obj = new SEIFSHA3(); 82 | 83 | obj->Wrap(info.This()); 84 | info.GetReturnValue().Set(info.This()); 85 | 86 | } else { 87 | 88 | // Invoked as plain function `SEIFSHA3()`, turn into construct call. 89 | const int argc = info.Length(); 90 | 91 | // Creating argument array for construct call. 92 | std::vector > argv; 93 | argv.reserve(argc); 94 | for (int i = 0; i < argc; ++i) { 95 | argv.push_back(info[i]); 96 | } 97 | 98 | v8::Local cons = Nan::New(constructor); 99 | info.GetReturnValue().Set(cons->NewInstance(argc, argv.data())); 100 | 101 | } 102 | } 103 | 104 | 105 | 106 | // ---- 107 | // hash 108 | // ---- 109 | /** 110 | * @brief Unwraps the arguments to get the string data and returns 111 | * the hash of the given input as a buffer object. 112 | * 113 | * Invoked as: 114 | * 'let hash = obj.hash(stringData)' where 115 | * 'stringData' is the string data to be hashed 116 | * 'hash' is the output buffer containing the SHA3-256 hash 117 | * To generate the output we are using Crypto++ SHA3-256 hash functions 118 | * 119 | * @param info node.js arguments wrapper containing string value to be 120 | * hashed 121 | * 122 | * @return void 123 | */ 124 | NAN_METHOD(SEIFSHA3::hash) { 125 | 126 | // Checking arguments and unwrapping them to get the string data. 127 | if (info[0]->IsUndefined()) { 128 | Nan::ThrowError("Incorrect Arguments. Value to be hashed not " 129 | "provided"); 130 | return; 131 | } 132 | 133 | // Output buffer containing the hash 134 | std::vector digest(CryptoPP::SHA3_256::DIGESTSIZE); 135 | 136 | // Check if first argument is a buffer or a string and hash accordingly. 137 | if (!node::Buffer::HasInstance(info[0])) { 138 | v8::String::Utf8Value str(info[0]->ToString()); 139 | 140 | /* Using crypto++ sha3-256 hash function to hash data and store in 141 | * 'digest' array. Definition of 'hashString' can be found in 'util.h'. 142 | */ 143 | hashString(digest, *str); 144 | } else { 145 | // Unwrap the first argument to get the input buffer to be hashed 146 | v8::Local bufferObj = 147 | Nan::To(info[0]).ToLocalChecked(); 148 | 149 | uint8_t* bufferData = (uint8_t*)node::Buffer::Data(bufferObj); 150 | size_t bufferLength = node::Buffer::Length(bufferObj); 151 | 152 | /* Using crypto++ sha3-256 hash function to hash data and store in 153 | * 'digest' array. Definition of 'hashBuffer' can be found in 'util.h'. 154 | */ 155 | hashBuffer(digest, bufferData, bufferLength); 156 | } 157 | 158 | // Copying digest hash value to node.js buffer. 159 | auto slowBuffer = Nan::CopyBuffer((const char*)digest.data(), 160 | digest.size()).ToLocalChecked(); 161 | 162 | // Set node.js buffer as return value of the function. 163 | info.GetReturnValue().Set(slowBuffer); 164 | } 165 | 166 | 167 | 168 | // ---- 169 | // Init 170 | // ---- 171 | /** 172 | * @brief Initialization function for node.js object wrapper exported 173 | * by the addon. 174 | * 175 | * @param exports node.js module exports 176 | * 177 | * @return void 178 | */ 179 | void SEIFSHA3::Init(v8::Handle exports) { 180 | 181 | Nan::HandleScope scope; 182 | 183 | // Prepare constructor template. 184 | v8::Local tpl = Nan::New(New); 185 | tpl->SetClassName(Nan::New("SEIFSHA3").ToLocalChecked()); 186 | tpl->InstanceTemplate()->SetInternalFieldCount(1); 187 | 188 | // Prototype 189 | Nan::SetPrototypeMethod(tpl, "hash", hash); 190 | 191 | constructor.Reset(tpl->GetFunction()); 192 | 193 | // Setting node.js module.exports. 194 | exports->Set(Nan::New("SEIFSHA3").ToLocalChecked(), tpl->GetFunction()); 195 | } 196 | -------------------------------------------------------------------------------- /src/seifsha3.h: -------------------------------------------------------------------------------- 1 | /** @file seifsha3.h 2 | * @brief Class header for native object wrapped in javascript object 3 | * responsible for performing Crypto++ SHA3 hash function 4 | * 5 | * @author Aashish Sheshadri 6 | * @author Rohit Harchandani 7 | * 8 | * The MIT License (MIT) 9 | * 10 | * Copyright (c) 2015, 2016, 2017 PayPal 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to 14 | * deal in the Software without restriction, including without limitation the 15 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16 | * sell copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in 20 | * all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | * DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | #ifndef SEIFSHA3_H 32 | #define SEIFSHA3_H 33 | 34 | // ---------------------- 35 | // node.js addon includes 36 | // ---------------------- 37 | #include 38 | #include 39 | #include 40 | 41 | 42 | // -------- 43 | // SEIFSHA3 44 | // -------- 45 | 46 | /* 47 | * @class This class represents native object wrapped inside a javascript 48 | * object, exposing Crypto++ SHA3 function. 49 | * 50 | * The functions exposed to node.js are: 51 | * function hash(data) -> returns SHA3-256 hash of the data 52 | */ 53 | class SEIFSHA3 : public Nan::ObjectWrap { 54 | 55 | private: 56 | 57 | // javascript object constructor 58 | static Nan::Persistent constructor; 59 | 60 | // --- 61 | // New 62 | // --- 63 | /** 64 | * @brief Creates the node object and corresponding underlying object. 65 | * 66 | * Invoked as: 67 | * 'let obj = new SEIFSHA3()' or 68 | * 'let obj = SEIFSHA3()' 69 | * 70 | * @param info node.js arguments wrapper 71 | * 72 | * @return void 73 | */ 74 | static NAN_METHOD(New); 75 | 76 | 77 | // ---- 78 | // hash 79 | // ---- 80 | /** 81 | * @brief Unwraps the arguments to get the string data and returns 82 | * the hash of the given input as a buffer object. 83 | * 84 | * Invoked as: 85 | * 'let hash = obj.hash(stringData)' where 86 | * 'stringData' is the string data to be hashed 87 | * 'hash' is the output buffer containing the SHA3-256 hash 88 | * To generate the output we are using Crypto++ SHA3-256 hash functions 89 | * 90 | * @param info node.js arguments wrapper containing string value to be 91 | * hashed 92 | * 93 | * @return void 94 | */ 95 | static NAN_METHOD(hash); 96 | 97 | public: 98 | 99 | // ---- 100 | // Init 101 | // ---- 102 | /** 103 | * @brief Initialization function for node.js object wrapper exported 104 | * by the addon. 105 | * 106 | * @param exports node.js module exports 107 | * 108 | * @return void 109 | */ 110 | static void Init(v8::Handle exports); 111 | 112 | }; 113 | 114 | #endif 115 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | /** @file util.h 2 | * @brief header/implementation file for utility functions used by the node 3 | * module 4 | * 5 | * @author Aashish Sheshadri 6 | * @author Rohit Harchandani 7 | * 8 | * The MIT License (MIT) 9 | * 10 | * Copyright (c) 2015, 2016, 2017 PayPal 11 | * 12 | * Permission is hereby granted, free of charge, to any person obtaining a copy 13 | * of this software and associated documentation files (the "Software"), to 14 | * deal in the Software without restriction, including without limitation the 15 | * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 16 | * sell copies of the Software, and to permit persons to whom the Software is 17 | * furnished to do so, subject to the following conditions: 18 | * 19 | * The above copyright notice and this permission notice shall be included in 20 | * all copies or substantial portions of the Software. 21 | * 22 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 23 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 24 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 25 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 26 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 27 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 28 | * DEALINGS IN THE SOFTWARE. 29 | */ 30 | 31 | #ifndef SEIFNODE_UTIL_H 32 | #define SEIFNODE_UTIL_H 33 | 34 | // ----------------- 35 | // standard includes 36 | // ----------------- 37 | #include 38 | #include 39 | 40 | // ----------------- 41 | // cryptopp includes 42 | // ----------------- 43 | #include "sha3.h" 44 | using CryptoPP::SHA3_256; 45 | 46 | 47 | // ---------- 48 | // hashString 49 | // ---------- 50 | /** 51 | * @brief creates a SHA3-256 hash of the given string using CryptoPP 52 | * @param digest output vector in which the hash to be stored 53 | * @param str input string to be hashed 54 | * PreCondition: 'digest' should be of appropriate size 55 | * (CryptoPP::SHA3_256::DIGESTSIZE) 56 | * @return void 57 | */ 58 | static void hashString(std::vector& digest, const std::string& str) { 59 | CryptoPP::SHA3_256 hash; 60 | hash.Update(reinterpret_cast(str.c_str()), str.size()); 61 | hash.Final(digest.data()); 62 | } 63 | 64 | 65 | // ---------- 66 | // hashBuffer 67 | // ---------- 68 | /** 69 | * @brief creates a SHA3-256 hash of the given buffer using CryptoPP 70 | * @param digest output vector in which the hash to be stored 71 | * @param input input buffer to be hashed 72 | * @param inputLen length of input buffer to be hashed 73 | * PreCondition: 'digest' should be of appropriate size 74 | * (CryptoPP::SHA3_256::DIGESTSIZE) 75 | * @return void 76 | */ 77 | static void hashBuffer( 78 | std::vector& digest, 79 | const uint8_t* input, 80 | int inputLen 81 | ) { 82 | CryptoPP::SHA3_256 hash; 83 | hash.Update(input, inputLen); 84 | hash.Final(digest.data()); 85 | } 86 | 87 | 88 | #endif 89 | -------------------------------------------------------------------------------- /src/xorShift128.hpp: -------------------------------------------------------------------------------- 1 | /* Written in 2016 by David Blackman and Sebastiano Vigna (vigna@acm.org) 2 | 3 | To the extent possible under law, the author has dedicated all copyright 4 | and related and neighboring rights to this software to the public domain 5 | worldwide. This software is distributed without any warranty. 6 | 7 | See . */ 8 | 9 | #include 10 | #include 11 | 12 | /* This is the successor to xorshift128+. It is the fastest full-period 13 | generator passing BigCrush without systematic failures, but due to the 14 | relatively short period it is acceptable only for applications with a 15 | mild amount of parallelism; otherwise, use a xorshift1024* generator. 16 | 17 | Beside passing BigCrush, this generator passes the PractRand test suite 18 | up to (and included) 16TB, with the exception of binary rank tests, 19 | which fail due to the lowest bit being an LFSR; all other bits pass all 20 | tests. We suggest to use a sign test to extract a random Boolean value. 21 | 22 | Note that the generator uses a simulated rotate operation, which most C 23 | compilers will turn into a single instruction. In Java, you can use 24 | Long.rotateLeft(). In languages that do not make low-level rotation 25 | instructions accessible xorshift128+ could be faster. 26 | 27 | The state must be seeded so that it is not everywhere zero. If you have 28 | a 64-bit seed, we suggest to seed a splitmix64 generator and use its 29 | output to fill s. */ 30 | 31 | static inline uint64_t rotl(const uint64_t x, int k) { 32 | return (x << k) | (x >> (64 - k)); 33 | } 34 | 35 | class XORShift128 { 36 | std::vector _state; 37 | public: 38 | XORShift128(std::vector seed):_state(seed) { 39 | for (int i = 0; i < 100; i++) { 40 | operator()(); 41 | } 42 | } 43 | 44 | uint64_t operator() () { 45 | const uint64_t s0 = _state[0]; 46 | uint64_t s1 = _state[1]; 47 | const uint64_t result = s0 + s1; 48 | 49 | s1 ^= s0; 50 | _state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); // a, b 51 | _state[1] = rotl(s1, 36); // c 52 | 53 | return result; 54 | } 55 | 56 | }; 57 | -------------------------------------------------------------------------------- /test/aestest.js: -------------------------------------------------------------------------------- 1 | let addon = require('seifnode'); 2 | let assert = require("assert"); 3 | 4 | // buffer containing seed for ocg random number generator used by AESXOR 5 | let seedBuffer = new Buffer([0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 6 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff]); 7 | 8 | // 16 byte message block to be encrypted 9 | let msg = new Buffer([0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0,0,0,0,0,0,0,0]); 10 | 11 | // 32 byte key to be used to encrypt message 12 | let key = new Buffer([0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 13 | 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, 14 | 0xff,0xff,0xff,0xff,0xff,0xff]); 15 | 16 | // letiable representing buffer containing encrypted message 17 | let cipher; 18 | 19 | // Mocha tests for AESXOR object. 20 | describe("seifnode AESXOR object", function() { 21 | 22 | // Testing 'encrypt' functionality. 23 | describe("#encrypt()", function() { 24 | 25 | /* Test should encrypt message and return cipher buffer of same length 26 | * as original message block and should contain different data. 27 | */ 28 | it("should encrypt msg and return a buffer with the cipher", 29 | function() { 30 | 31 | let test = addon.AESXOR256(seedBuffer); 32 | 33 | cipher = test.encrypt(key, msg); 34 | 35 | // checking if the cipher is differs from the message 36 | assert.notEqual(true, cipher.equals(msg)); 37 | }); 38 | 39 | /* Test should take a shorter key and throw an exception when trying to 40 | * encrypt a message with it. 41 | */ 42 | it("should give an error when encrypting a message with key of wrong " + 43 | "length", function() { 44 | 45 | let test = addon.AESXOR256(seedBuffer); 46 | 47 | let wrongKey = new Buffer([0xff,0xff,0xff,0xff,0xff,0,0,0,0,0,0]); 48 | 49 | // Checking if 'encrypt' throws an exception. 50 | assert.throws(function() { 51 | let wrongCipher = test.encrypt(wrongKey, msg); 52 | }, 53 | function(err) { 54 | if ((err instanceof Error) && /Incorrect Arguments/.test(err)) { 55 | console.log(err); 56 | return true; 57 | } 58 | }, 59 | "Error thrown"); 60 | }); 61 | }); 62 | 63 | // Testing 'decrypt' functionality. 64 | describe("#decrypt()", function() { 65 | 66 | /* Test should decrypt cipher and return message buffer which should 67 | * be exactly same as the original buffer. 68 | */ 69 | it("should decrypt cipher and return the same message", function() { 70 | 71 | let test = addon.AESXOR256(seedBuffer); 72 | 73 | let decryptedMsg = test.decrypt(key, cipher); 74 | 75 | // Checking that the decrypted buffer is same as original message. 76 | assert.equal(true, decryptedMsg.equals(msg)); 77 | }); 78 | 79 | /* Test should take a shorter key and throw an exception when trying to 80 | * decrypt a cipher with it. 81 | */ 82 | it("should give an error when decrypting a message with key of wrong " + 83 | "length", function() { 84 | 85 | let test = addon.AESXOR256(seedBuffer); 86 | 87 | let wrongKey = new Buffer([0xff,0xff,0xff,0xff,0xff,0,0,0,0,0,0]); 88 | 89 | // Checking if 'decrypt' throws an exception. 90 | assert.throws(function() { 91 | let wrongCipher = test.decrypt(wrongKey, cipher); 92 | }, 93 | function(err) { 94 | if ((err instanceof Error) && /Incorrect Arguments/.test(err)) { 95 | console.log(err); 96 | return true; 97 | } 98 | }, 99 | "Error thrown"); 100 | }); 101 | 102 | /* Test should take a different key from the one used to encrypt the 103 | * message and throw an exception when trying to decrypt the 104 | * corresponding cipher with it. 105 | */ 106 | it("should give an error when decrypting a message with wrong key", 107 | function() { 108 | 109 | let test = addon.AESXOR256(seedBuffer); 110 | 111 | // Modifying the first byte of the key. 112 | let wrongKey = key; 113 | wrongKey[0] = 0; 114 | 115 | // Checking if a decryption error is thrown. 116 | assert.throws(function() { 117 | let wrongCipher = test.decrypt(wrongKey, cipher); 118 | }, 119 | function(err) { 120 | if ((err instanceof Error)) { 121 | console.log(err); 122 | return true; 123 | } 124 | }, 125 | "Error thrown"); 126 | }); 127 | }); 128 | }); 129 | -------------------------------------------------------------------------------- /test/ecctest.js: -------------------------------------------------------------------------------- 1 | var addon = require('seifnode'); 2 | var fs = require("fs"); 3 | var glob = require("glob") 4 | var assert = require("assert"); 5 | 6 | var hash = new Buffer([0xB6,0x8F,0xE4,0x3F,0x0D,0x1A,0x0D,0x7A,0xEF,0x12,0x37, 7 | 0x22,0x67,0x0B,0xE5,0x02,0x68,0xE1,0x53,0x65,0x40,0x1C,0x44,0x2F,0x88,0x06, 8 | 0xEF,0x83,0xB6,0x12,0x97,0x6B]); 9 | 10 | var eccFolder = __dirname; 11 | 12 | var msg = new Buffer([0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41,0x41, 13 | 0x41,0x41,0x41,0x41,0x41]); 14 | 15 | // variable representing the keys loaded from disk 16 | var loadedKeys; 17 | 18 | // variable representing buffer containing encrypted message 19 | var cipher; 20 | 21 | // Mocha tests for SEIFECC object. 22 | describe("seifnode SEIFECC object", function() { 23 | 24 | // Deleting all files stored during prior tests. 25 | before(function() { 26 | var filenames = glob.sync(eccFolder + "/ecies*"); 27 | filenames.forEach(function(val, index, arr) { 28 | fs.unlinkSync(val); 29 | }); 30 | }); 31 | 32 | // Testing 'loadKeys' functionality before generating the keys. 33 | describe("#loadKeys() before", function() { 34 | 35 | /* Test should set the status object with an error when trying to load 36 | * keys from the disk when no files are present. 37 | */ 38 | it("should return a file not found error when trying to load keys", 39 | function(done) { 40 | 41 | var test = new addon.SEIFECC(hash, eccFolder); 42 | 43 | test.loadKeys(function(status, keys) { 44 | // Checking if status code indicates file not found error. 45 | assert.equal(-1, status.code); 46 | done(); 47 | }); 48 | }); 49 | 50 | }); 51 | 52 | // Testing 'entropyStrength' functionality. 53 | describe("#entropyStrength()", function() { 54 | 55 | it("should describe strenght of entropy mining as WEAK, MEDIUM or " + 56 | "STRONG", function(done) { 57 | let test = new addon.RNG(); 58 | 59 | let strength = test.entropyStrength(); 60 | let result = 0; 61 | if (strength === "WEAK" || 62 | strength === "MEDIUM" || 63 | strength === "STRONG" 64 | ) { 65 | result = 1; 66 | } 67 | assert.equal(1, result); 68 | done(); 69 | }); 70 | }); 71 | 72 | // Testing 'generateKeys' functionality. 73 | describe("#generateKeys()", function() { 74 | 75 | /* Test should generate the keys and set the status object indicating 76 | * success. 77 | */ 78 | it("should generate keys and store them on the disk so that they can " + 79 | "be loaded", function(done) { 80 | 81 | var test = new addon.SEIFECC(hash, eccFolder); 82 | 83 | this.timeout(150000); 84 | 85 | 86 | assert.doesNotThrow(function() { 87 | var generatedKeys = test.generateKeys(); 88 | // test.destroy(); 89 | }, 90 | function(err) { 91 | if ((err instanceof Error)) { 92 | console.log(err); 93 | return true; 94 | } 95 | }, 96 | "Error thrown" 97 | ); 98 | console.log(eccFolder); 99 | test.loadKeys(function(status, keys) { 100 | // Checking if status code indicates success. 101 | assert.equal(0, status.code); 102 | done(); 103 | }); 104 | 105 | }); 106 | 107 | }); 108 | 109 | // Testing 'loadKeys' functionality after keys have been generated. 110 | describe("#loadKeys() after", function() { 111 | 112 | /* Test should set the status object with a decryption error when 113 | * trying to load ECC keys from the disk when an incorrect key is given. 114 | */ 115 | it("should return an error when trying to load keys with wrong key", 116 | function(done) { 117 | 118 | var testhash = new Buffer([0xB2,0x8F,0xE4,0x3F,0x0D]); 119 | var test = new addon.SEIFECC(testhash, eccFolder); 120 | 121 | test.loadKeys(function(status, keys) { 122 | assert.equal(-2, status.code); 123 | done(); 124 | }); 125 | }); 126 | 127 | }); 128 | 129 | // Testing 'encrypt' functionality. 130 | describe("#encrypt()", function() { 131 | 132 | /* Test should return cipher string without throwing any exception when 133 | * encrypting message. 134 | */ 135 | it("should encrypt msg and return the cipher without any errors", 136 | function(done) { 137 | 138 | var test = new addon.SEIFECC(hash, eccFolder); 139 | 140 | test.loadKeys(function(status, keys) { 141 | 142 | // Checking that no exception is thrown when encrypting message. 143 | assert.doesNotThrow(function() { 144 | cipher = test.encrypt(keys.enc, msg); 145 | 146 | }, 147 | function(err) { 148 | if ((err instanceof Error)) { 149 | console.log(err); 150 | return true; 151 | } 152 | }, 153 | "Error thrown"); 154 | 155 | done(); 156 | }); 157 | 158 | }); 159 | }); 160 | 161 | // Testing 'decrypt' functionality. 162 | describe("#decrypt()", function() { 163 | 164 | /* Test should return message buffer without throwing any exception when 165 | * decrypting cipher and this message should be same as the original. 166 | */ 167 | it("should decrypt cipher and return the original message without any" + 168 | " errors", 169 | function(done) { 170 | 171 | var test = new addon.SEIFECC(hash, eccFolder); 172 | 173 | test.loadKeys(function(status, keys) { 174 | 175 | var decryptedMsg; 176 | 177 | // Checking that no exception is thrown when decrypting. 178 | assert.doesNotThrow(function() { 179 | decryptedMsg = test.decrypt(keys.dec, cipher); 180 | 181 | }, 182 | function(err) { 183 | if ((err instanceof Error)) { 184 | console.log(err); 185 | return true; 186 | } 187 | }, 188 | "Error thrown"); 189 | 190 | // Checking that decrypted message is same as the original. 191 | assert.equal(true, decryptedMsg.equals(msg)); 192 | done(); 193 | }); 194 | 195 | }); 196 | 197 | /* Test should take a different key from the one used to encrypt the 198 | * message and throw an exception when trying to decrypt the corresponding 199 | * cipher with it 200 | */ 201 | it("should give an error when decrypting with wrong key", 202 | function(done) { 203 | 204 | var test = new addon.SEIFECC(hash, eccFolder); 205 | 206 | test.loadKeys(function(status, keys) { 207 | // Modifying the loaded private key. 208 | keys.dec = keys.dec.substr(0, keys.dec.length - 1); 209 | keys.dec = keys.dec + "A"; 210 | 211 | /* Checking that an exception is thrown when decrypting with 212 | * the wrong private key. 213 | */ 214 | assert.throws(function() { 215 | var decryptedMsg = test.decrypt(keys.dec, cipher); 216 | console.log(decryptedMsg); 217 | }, 218 | function(err) { 219 | if ((err instanceof Error)) { 220 | console.log(err); 221 | return true; 222 | } 223 | }, 224 | "Error thrown"); 225 | 226 | done(); 227 | }); 228 | 229 | }); 230 | }); 231 | 232 | after(function() { 233 | var filenames = glob.sync(eccFolder + "/ecies*"); 234 | filenames.forEach(function(val, index, arr) { 235 | fs.unlinkSync(val); 236 | }); 237 | }); 238 | }); 239 | -------------------------------------------------------------------------------- /test/rngtest.js: -------------------------------------------------------------------------------- 1 | let addon = require('seifnode'); 2 | let fs = require("fs"); 3 | let assert = require("assert"); 4 | 5 | let hash = new Buffer([0xB6,0x8F,0xE4,0x3F,0x0D,0x1A]); 6 | let stateFile = __dirname + "/rng1"; 7 | let numBytes = 32; 8 | 9 | // Mocha tests for RNG object. 10 | describe("seifnode RNG object", function() { 11 | 12 | before(function() { 13 | if (fs.existsSync(stateFile)) { 14 | fs.unlinkSync(stateFile); 15 | } 16 | }); 17 | 18 | // Testing 'isInitialized' functionality before initializing the RNG. 19 | describe("#isInitialized() before", function() { 20 | 21 | /* Since RNG is not initialized, no state will be found on the disk and 22 | * the corresponding error should be returned. 23 | */ 24 | it("should return file not found error when checking if the rng is " + 25 | "initialized", function(done) { 26 | let test = new addon.RNG(); 27 | 28 | test.isInitialized(hash, stateFile, function(result) { 29 | // Checking that the result code indicates file not found error. 30 | assert.equal(-1, result.code); 31 | done(); 32 | }); 33 | }); 34 | }); 35 | 36 | // Testing 'entropyStrength' functionality. 37 | describe("#entropyStrength()", function() { 38 | 39 | it("should describe strenght of entropy mining as WEAK, MEDIUM or " + 40 | "STRONG", function(done) { 41 | let test = new addon.RNG(); 42 | 43 | let strength = test.entropyStrength(); 44 | let result = 0; 45 | if (strength === "WEAK" || 46 | strength === "MEDIUM" || 47 | strength === "STRONG" 48 | ) { 49 | result = 1; 50 | } 51 | assert.equal(1, result); 52 | done(); 53 | }); 54 | }); 55 | 56 | // Testing 'initialize' functionality. 57 | describe("#initialize()", function() { 58 | 59 | // RNG should be initialized successfully without any errors. 60 | it("should initialize rng object successfully", function(done) { 61 | let test = new addon.RNG(); 62 | let hash = new Buffer([0xB6,0x8F,0xE4,0x3F,0x0D,0x1A]); 63 | this.timeout(150000); 64 | 65 | assert.doesNotThrow(function() { 66 | test.initialize(hash, stateFile); 67 | }, 68 | function(err) { 69 | if ((err instanceof Error)) { 70 | console.log(err); 71 | return true; 72 | } 73 | }, 74 | "Error thrown" 75 | ); 76 | 77 | test.isInitialized(hash, stateFile, function(result) { 78 | // Checking that the result code indicates Success. 79 | assert.equal(0, result.code); 80 | // Destroy RNG to ensure state file is saved to disk. 81 | test.destroy(); 82 | done(); 83 | }); 84 | }); 85 | }); 86 | 87 | // Testing 'isInitialized' functionality after initializing the RNG. 88 | describe("#isInitialized() after", function() { 89 | 90 | /* When using a wrong key a decryption error should be returned when 91 | * trying to initialize the RNG from state stored on the disk. 92 | */ 93 | it("should return decryption error due to incorrect argument", 94 | function(done) { 95 | 96 | let test = new addon.RNG(); 97 | /* Using a different key instead of the one used before to save 98 | * state. 99 | */ 100 | let testhash = new Buffer([0xB2,0x8F,0xE4,0x3F,0x0D]); 101 | 102 | test.isInitialized(testhash, stateFile, function(result) { 103 | // Checking that the result code indicated decryption error. 104 | assert.equal(-2, result.code); 105 | done(); 106 | }); 107 | }); 108 | }); 109 | 110 | // Testing 'getBytes' functionality. 111 | describe("#getBytes()", function() { 112 | 113 | // A non-zero buffer of given length should be returned. 114 | it("should return buffer with given number of random bytes", 115 | function(done) { 116 | 117 | let test = new addon.RNG(); 118 | 119 | test.isInitialized(hash, stateFile, function(result) { 120 | assert.equal(0, result.code); 121 | 122 | let testBuffer; 123 | assert.doesNotThrow(function() { 124 | testBuffer = test.getBytes(numBytes); 125 | }, 126 | function(err) { 127 | if ((err instanceof Error)) { 128 | console.log(err); 129 | return true; 130 | } 131 | }, 132 | "Error thrown" 133 | ); 134 | 135 | // Checking the length of the random bytes buffer. 136 | assert.equal(numBytes, testBuffer.length); 137 | let i; 138 | for (i = 0; i < testBuffer.length; ++i) { 139 | if (testBuffer[i] !== 0) { 140 | break; 141 | } 142 | } 143 | // Checking that all bytes in output buffer are not 0. 144 | assert.notEqual(numBytes, i); 145 | 146 | done(); 147 | }); 148 | }); 149 | }); 150 | 151 | after(function() { 152 | if (fs.existsSync(stateFile)) { 153 | fs.unlinkSync(stateFile); 154 | } 155 | }); 156 | }); 157 | -------------------------------------------------------------------------------- /test/shatest.js: -------------------------------------------------------------------------------- 1 | let addon = require('seifnode'); 2 | let assert = require("assert"); 3 | 4 | // Mocha tests for SEIFSHA3 object. 5 | describe("seifnode SEIFSHA3 hash object", function() { 6 | 7 | // Test should return correct hash value of known input. 8 | it("should compute correct SHA3-256 hash value", function() { 9 | let test = new addon.SEIFSHA3(); 10 | let hash = test.hash("abc"); 11 | 12 | let testarr = 13 | [0x3a, 0x98, 0x5d, 0xa7, 0x4f, 0xe2, 0x25, 0xb2, 0x04, 0x5c, 14 | 0x17, 0x2d, 0x6b, 0xd3, 0x90, 0xbd, 0x85, 0x5f, 0x08, 0x6e, 15 | 0x3e, 0x9d, 0x52, 0x5b, 0x46, 0xbf, 0xe2, 0x45, 0x11, 0x43, 0x15, 0x32]; 16 | 17 | let testhash = new Buffer(testarr); 18 | 19 | // Comparing returned hash buffer with the known hash value buffer. 20 | assert.equal(true, hash.equals(testhash)); 21 | }); 22 | }); 23 | --------------------------------------------------------------------------------