diff --git a/js/core/libsodium.js b/js/core/libsodium.js new file mode 100644 index 000000000..75d32ada3 --- /dev/null +++ b/js/core/libsodium.js @@ -0,0 +1,248 @@ +// Copyright 2014-present runtime.js project authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +'use strict'; + +var lib = require('./resources').libsodium; +var constants = lib.crypto_constants(); + +// Helper function to convert hex results into Uint8Arrays. +function hexToU8(hexStr) { + var u8 = new Uint8Array(hexStr.length/2); + for (var i = 0; i < (hexStr.length/2); i++) { + u8[i] = parseInt(hexStr.substr(i*2, 2), 16); + } + return u8; +} + +function throwErr(funcName, argName, i, msg) { + throw new Error(funcName + ': ' + argName + ' (argument ' + i + ') ' + msg + '.'); +} + +function stringOrU8(arg, funcName, argName, i, msg) { + var ret = arg; + if (!(arg instanceof Uint8Array)) { + if (typeof arg === 'string' || (arg instanceof String)) { + ret = new Uint8Array(arg.length); + for (var i = 0; i < arg.length; i++) { + ret[i] = arg.charCodeAt(i); + } + } else { + throwErr(funcName, argName, i, (msg ? msg : 'must be a string or Uint8Array')); + } + } + return ret; +} + +function isNumber(arg, funcName, argName, i, msg) { + if (typeof arg !== 'number' && !(arg instanceof Number)) { + throwErr(funcName, argName, i, (msg ? msg : 'must be a number')); + } + return arg; +} + +module.exports = { + crypto_secretbox_easy: function(data, key) { + var dataArr = stringOrU8(data, 'crypto_secretbox_easy', 'data', 0); + var keyArr = stringOrU8(key, 'crypto_secretbox_easy', 'key', 1); + + var nonceArr = runtime.random.getRandomValues(constants.crypto_secretbox_NONCEBYTES); + + var cipher = lib.crypto_secretbox_easy(dataArr, keyArr, nonceArr); + if (!cipher) { + throw new Error('crypto_secretbox_easy: error creating box.'); + } + return { + ciphertext: hexToU8(cipher), + nonce: nonceArr + }; + }, + crypto_secretbox_open_easy: function(cipher, key, nonce) { + var cipherArr = stringOrU8(cipher, 'crypto_secretbox_open_easy', 'cipher', 0); + var keyArr = stringOrU8(key, 'crypto_secretbox_open_easy', 'key', 1); + var nonceArr = stringOrU8(nonce, 'crypto_secretbox_open_easy', 'nonce', 2); + + var decipher = lib.crypto_secretbox_open_easy(cipherArr, keyArr, nonceArr); + if (!decipher) { + throw new Error('crypto_secretbox_open_easy: error decrypting box.'); + } + return hexToU8(decipher); + }, + crypto_auth: function(data, key) { + var dataArr = stringOrU8(data, 'crypto_auth', 'data', 0); + var keyArr; + if (key === null || key === undefined) { + keyArr = runtime.random.getRandomValues(constants.crypto_auth_KEYBYTES); + } else { + keyArr = stringOrU8(key, 'crypto_auth', 'key', 1, 'must be unspecified, a string, or Uint8Array'); + } + + var mac = lib.crypto_auth(dataArr, keyArr); + if (!mac) { + throw new Error('crypto_auth: error creating tag.'); + } + return { + mac: hexToU8(mac), + key: keyArr + }; + }, + crypto_auth_verify: function(mac, key, data) { + var macArr = stringOrU8(mac, 'crypto_auth_verify', 'MAC', 0); + var keyArr = stringOrU8(key, 'crypto_auth_verify', 'key', 1); + var dataArr = stringOrU8(data, 'crypto_auth_verify', 'data', 2); + + return lib.crypto_auth_verify(macArr, keyArr, dataArr); + }, + crypto_aead_chacha20poly1305_encrypt: function(data, key, addData) { + var dataArr = stringOrU8(data, 'crypto_aead_chacha20poly1305_encrypt', 'data', 0); + var keyArr = stringOrU8(key, 'crypto_aead_chacha20poly1305_encrypt', 'key', 1); + var addDataArr = stringOrU8(key, 'crypto_aead_chacha20poly1305_encrypt', 'additional data', 2); + + var nonceArr = runtime.random.getRandomValues(constants.crypto_aead_chacha20poly1305_NPUBBYTES); + + var cipher = lib.crypto_aead_chacha20poly1305_encrypt(dataArr, keyArr, nonceArr, addDataArr); + if (!cipher) { + throw new Error('crypto_aead_chacha20poly1305_encrypt: error creating box.'); + } + return { + ciphertext: hexToU8(cipher.ciphertext), + nonce: nonceArr, + length: cipher.ciphertext_len + }; + }, + crypto_aead_chacha20poly1305_decrypt: function(cipher, cipherLength, key, nonce, addData) { + var cipherArr = stringOrU8(cipher, 'crypto_aead_chacha20poly1305_decrypt', 'cipher', 0); + var cipherLen = isNumber(cipherLength, 'crypto_aead_chacha20poly1305_decrypt', 'cipher length', 1); + var keyArr = stringOrU8(key, 'crypto_aead_chacha20poly1305_decrypt', 'key', 2); + var nonceArr = stringOrU8(nonce, 'crypto_aead_chacha20poly1305_decrypt', 'nonce', 3); + var addDataArr = stringOrU8(key, 'crypto_aead_chacha20poly1305_decrypt', 'additional data', 4); + + var decipher = lib.crypto_aead_chacha20poly1305_decrypt(cipherArr, keyArr, nonceArr, addDataArr, cipherLen); + if (!decipher) { + throw new Error('crypto_aead_chacha20poly1305_decrypt: error decrypting box.'); + } + return hexToU8(decipher.deciphertext); + }, + crypto_aead_aes256gcm_is_available: function() { + // AES-256 GCM isn't supported on QEMU's system-x86_64 + return false; + }, + crypto_aead_aes256gcm_encrypt: function(data, key, addData) { + // AES-256 GCM isn't supported on QEMU's system-x86_64 + throw new Error('CPU not supported'); + /*var dataArr = stringOrU8(data, 'crypto_aead_aes256gcm_encrypt', 'data', 0); + var keyArr = stringOrU8(key, 'crypto_aead_aes256gcm_encrypt', 'key', 1); + var addDataArr = stringOrU8(key, 'crypto_aead_aes256gcm_encrypt', 'additional data', 2); + var nonceArr = runtime.random.getRandomValues(constants.crypto_aead_aes256gcm_NPUBBYTES); + var cipher = lib.crypto_aead_aes256gcm_encrypt(dataArr, keyArr, nonceArr, addDataArr); + if (!cipher) { + throw new Error('crypto_aead_aes256gcm_encrypt: error creating box.'); + } + if (cipher instanceof Error) throw cipher; + return { + ciphertext: hexToU8(cipher.ciphertext), + nonce: nonceArr, + length: cipher.ciphertext_len + };*/ + }, + crypto_aead_aes256gcm_decrypt: function(cipher, cipherLength, key, nonce, addData) { + // AES-256 GCM isn't supported on QEMU's system-x86_64 + throw new Error('CPU not supported'); + /*var cipherArr = stringOrU8(cipher, 'crypto_aead_aes256gcm_decrypt', 'cipher', 0); + var cipherLen = isNumber(cipherLength, 'crypto_aead_aes256gcm_decrypt', 'cipher length', 1); + var keyArr = stringOrU8(key, 'crypto_aead_aes256gcm_decrypt', 'key', 2); + var nonceArr = stringOrU8(nonce, 'crypto_aead_aes256gcm_decrypt', 'nonce', 3); + var addDataArr = stringOrU8(key, 'crypto_aead_aes256gcm_decrypt', 'additional data', 4); + var decipher = lib.crypto_aead_aes256gcm_decrypt(cipherArr, keyArr, nonceArr, addDataArr, cipherLen); + if (!decipher) { + throw new Error('crypto_aead_aes256gcm_decrypt: error decrypting box.'); + } + if (decipher instanceof Error) throw decipher; + return hexToU8(decipher.deciphertext);*/ + }, + crypto_box_easy: function(data, pk, sk) { + var dataArr = stringOrU8(data, 'crypto_box_easy', 'data', 0); + var pkArr = stringOrU8(pk, 'crypto_box_easy', 'public key', 1); + var skArr = stringOrU8(sk, 'crypto_box_easy', 'secret key', 2); + + var nonceArr = runtime.random.getRandomValues(constants.crypto_box_NONCEBYTES); + + var cipher = lib.crypto_box_easy(dataArr, pkArr, skArr, nonceArr); + if (!cipher) { + throw new Error('crypto_box_easy: error creating box.'); + } + return { + ciphertext: hexToU8(cipher), + nonce: nonceArr + }; + }, + crypto_box_open_easy: function(cipher, pk, sk, nonce) { + var cipherArr = stringOrU8(cipher, 'crypto_box_open_easy', 'cipher', 0); + var pkArr = stringOrU8(pk, 'crypto_box_open_easy', 'public key', 1); + var skArr = stringOrU8(sk, 'crypto_box_open_easy', 'secret key', 1); + var nonceArr = stringOrU8(nonce, 'crypto_box_open_easy', 'nonce', 2); + + var decipher = lib.crypto_box_open_easy(cipherArr, pkArr, skArr, nonceArr); + if (!decipher) { + throw new Error('crypto_box_open_easy: error decrypting box.'); + } + return hexToU8(decipher); + }, + crypto_box_keypair: function() { + var keypair = lib.crypto_box_keypair(); + if (typeof keypair === 'string') throw new Error(keypair); + return keypair; + }, + crypto_box_seed_keypair: function(seed) { + var seedArr = stringOrU8(seed, 'crypto_box_seed_keypair', 'seed', 0); + + var keypair = lib.crypto_box_seed_keypair(seedArr); + if (typeof keypair === 'string') throw new Error(keypair); + return keypair; + } +}; + +var justConvertHex = [ + { + funcName: 'randombytes_buf', + errorInfo: 'error generating randomness.', + dataVerifier: isNumber, + argName: 'buffer length' + }, + { + funcName: 'crypto_generichash', + errorInfo: 'error calculating hash.' + }, + { + funcName: 'crypto_hash_sha256', + errorInfo: 'error calculating hash.' + }, + { + funcName: 'crypto_hash_sha512', + errorInfo: 'error calculating hash.' + } +]; + +for (var i = 0; i < justConvertHex.length; i++) { + (function(i) { + var f = justConvertHex[i]; + module.exports[f.funcName] = function(data) { + var result = lib[f.funcName]((f.dataVerifier ? f.dataVerifier : stringOrU8)(data, f.funcName, (f.argName ? f.argName : 'data'), 0)); + if (!result) { + throw new Error(f.funcName + ': ' + (f.errorInfo ? f.errorInfo : 'unknown error.')); + } + return hexToU8(result); + } + })(i); +} diff --git a/js/index.js b/js/index.js index ed6ad614b..ebccf0ba7 100644 --- a/js/index.js +++ b/js/index.js @@ -59,6 +59,8 @@ runtime.shell.setCommand('reboot', function(args, env, cb) { cb(0); }); +runtime.libsodium = require('./core/libsodium'); + // Start device drivers require('./driver/ps2'); diff --git a/src/kernel/kernel-main.cc b/src/kernel/kernel-main.cc index 0bfc36a85..b40356f80 100644 --- a/src/kernel/kernel-main.cc +++ b/src/kernel/kernel-main.cc @@ -145,14 +145,56 @@ const char* runtime_rng_name() { } uint32_t runtime_rng_random() { + // prevent abort() + if (GLOBAL_engines()) { + v8::Isolate* iv8 = GLOBAL_engines()->cpu_engine()->thread_manager()->current_thread()->IsolateV8(); + v8::Local context = iv8->GetCurrentContext(); + + v8::Local global = context->Global(); + v8::Local runtimeObj = global->Get(context, v8::String::NewFromUtf8(iv8, "runtime", v8::NewStringType::kNormal).ToLocalChecked()).ToLocalChecked()->ToObject(); + v8::Local runtimeRandomObj = runtimeObj->Get(context, v8::String::NewFromUtf8(iv8, "random", v8::NewStringType::kNormal).ToLocalChecked()).ToLocalChecked()->ToObject(); + v8::Local runtimeRandomGetRandomValuesFunc = v8::Local::Cast(runtimeRandomObj->Get(context, v8::String::NewFromUtf8(iv8, "getRandomValues", v8::NewStringType::kNormal).ToLocalChecked()).ToLocalChecked()); + + v8::Local args[1] = { v8::Number::New(iv8, 1) }; + + // js equivalent: runtime.random.getRandomValues.apply(undefined, [1]) + v8::Local u8Array = runtimeRandomGetRandomValuesFunc->Call(context, v8::Undefined(iv8), 1, args).ToLocalChecked()->ToObject(context).ToLocalChecked(); + return (uint8_t)(u8Array->Get(0)->ToNumber()->Value()); + } else { + // mainly when libsodium is intialized, which is before V8 is available + printf("[randombytes] fallback used\n"); return 42; + } } void runtime_rng_buf(void* const buf, const size_t size) { - uint8_t* b = reinterpret_cast(buf); + uint8_t* b = reinterpret_cast(buf); + + // prevent abort() + if (GLOBAL_engines()) { + v8::Isolate* iv8 = GLOBAL_engines()->cpu_engine()->thread_manager()->current_thread()->IsolateV8(); + v8::Local context = iv8->GetCurrentContext(); + + v8::Local global = context->Global(); + v8::Local runtimeObj = global->Get(context, v8::String::NewFromUtf8(iv8, "runtime", v8::NewStringType::kNormal).ToLocalChecked()).ToLocalChecked()->ToObject(); + v8::Local runtimeRandomObj = runtimeObj->Get(context, v8::String::NewFromUtf8(iv8, "random", v8::NewStringType::kNormal).ToLocalChecked()).ToLocalChecked()->ToObject(); + v8::Local runtimeRandomGetRandomValuesFunc = v8::Local::Cast(runtimeRandomObj->Get(context, v8::String::NewFromUtf8(iv8, "getRandomValues", v8::NewStringType::kNormal).ToLocalChecked()).ToLocalChecked()); + + v8::Local args[1] = { v8::Number::New(iv8, size) }; + + // js equivalent: runtime.random.getRandomValues.apply(undefined, [size]) + v8::Local u8Array = runtimeRandomGetRandomValuesFunc->Call(context, v8::Undefined(iv8), 1, args).ToLocalChecked()->ToObject(context).ToLocalChecked(); + for (size_t i = 0; i < size; ++i) { - b[i] = 42; + b[i] = (uint8_t)u8Array->Get(i)->ToNumber()->Value(); } + } else { + // mainly when libsodium is intialized, which is before V8 is available + printf("[randombytes] fallback used\n"); + for (size_t i = 0; i < size; ++i) { + b[i] = 42; + } + } } void KernelMain::InitSystemBSP(void* mbt) { diff --git a/src/kernel/native-object.cc b/src/kernel/native-object.cc index 6d856f10b..e48863162 100644 --- a/src/kernel/native-object.cc +++ b/src/kernel/native-object.cc @@ -25,6 +25,7 @@ #include #include #include +#include namespace rt { @@ -444,6 +445,7 @@ NATIVE_FUNCTION(NativesObject, GetSystemResources) { LOCAL_V8STRING(s_memory_range, "memoryRange"); LOCAL_V8STRING(s_io_range, "ioRange"); LOCAL_V8STRING(s_irq_range, "irqRange"); + LOCAL_V8STRING(s_libsodium, "libsodium"); v8::Local obj = v8::Object::New(iv8); @@ -459,6 +461,10 @@ NATIVE_FUNCTION(NativesObject, GetSystemResources) { ->BindToTemplateCache(th->template_cache()) ->GetInstance()); + obj->Set(context, s_libsodium, (new LibsodiumObject()) + ->BindToTemplateCache(th->template_cache()) + ->GetInstance()); + args.GetReturnValue().Set(obj); } @@ -1125,4 +1131,804 @@ NATIVE_FUNCTION(NativesObject, AllocDMA) { args.GetReturnValue().Set(ret); } +NATIVE_FUNCTION(LibsodiumObject, RandombytesBuf) { + PROLOGUE; + USEARG(0); + VALIDATEARG(0, NUMBER, "randombytes_buf: argument 0 is not a number."); + + v8::Local bufNum = arg0->ToNumber(context).ToLocalChecked(); + unsigned long long bufLen = bufNum->Value(); + + unsigned char buf[bufLen]; + randombytes_buf(buf, bufLen); + + char returnString[(sizeof(buf)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(buf)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", buf[i]); + } + + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + + args.GetReturnValue().Set(ret); +} + +NATIVE_FUNCTION(LibsodiumObject, BlakeHash) { + PROLOGUE; + USEARG(0); + VALIDATEARG(0, OBJECT, "blake_hash: argument 0 is not a array/object"); + + v8::Local dataArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char dataBytes[(int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + dataBytes[i] = (unsigned char)dataArray->Get(i)->ToNumber()->Value(); + } + + unsigned char hash[crypto_generichash_BYTES]; + + crypto_generichash(hash, sizeof hash, dataBytes, (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), nullptr, 0); + + char returnString[(sizeof(hash)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(hash)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", hash[i]); + } + + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + + args.GetReturnValue().Set(ret); +} + +NATIVE_FUNCTION(LibsodiumObject, Sha256Hash) { + PROLOGUE; + USEARG(0); + VALIDATEARG(0, OBJECT, "sha256_hash: argument 0 is not a object/array"); + + v8::Local dataArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char dataBytes[(int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + dataBytes[i] = (unsigned char)dataArray->Get(i)->ToNumber()->Value(); + } + + unsigned char hash[crypto_hash_sha256_BYTES]; + + crypto_hash_sha256(hash, dataBytes, (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()); + + char returnString[(sizeof(hash)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(hash)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", hash[i]); + } + + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + + args.GetReturnValue().Set(ret); +} + +NATIVE_FUNCTION(LibsodiumObject, Sha512Hash) { + PROLOGUE; + USEARG(0); + VALIDATEARG(0, OBJECT, "crypto_hash_sha512: argument 0 is not a array/object."); + + v8::Local dataArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char dataBytes[(int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + dataBytes[i] = (unsigned char)dataArray->Get(i)->ToNumber()->Value(); + } + + unsigned char hash[crypto_hash_sha512_BYTES]; + + crypto_hash_sha512(hash, dataBytes, (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()); + + char returnString[(sizeof(hash)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(hash)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", hash[i]); + } + + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + + args.GetReturnValue().Set(ret); +} + +NATIVE_FUNCTION(LibsodiumObject, Constants) { + PROLOGUE; + + v8::Local ret = v8::Object::New(iv8); + LOCAL_V8STRING(s_crypto_secretbox_NONCEBYTES, "crypto_secretbox_NONCEBYTES"); + LOCAL_V8STRING(s_crypto_auth_BYTES, "crypto_auth_BYTES"); + LOCAL_V8STRING(s_crypto_auth_KEYBYTES, "crypto_auth_KEYBYTES"); + LOCAL_V8STRING(s_crypto_aead_chacha20poly1305_NPUBBYTES, "crypto_aead_chacha20poly1305_NPUBBYTES"); + //LOCAL_V8STRING(s_crypto_aead_aes256gcm_NPUBBYTES, "crypto_aead_aes256gcm_NPUBBYTES"); + LOCAL_V8STRING(s_crypto_box_NONCEBYTES, "crypto_box_NONCEBYTES"); + ret->Set(context, s_crypto_secretbox_NONCEBYTES, v8::Number::New(iv8, crypto_secretbox_NONCEBYTES)); + ret->Set(context, s_crypto_auth_BYTES, v8::Number::New(iv8, crypto_auth_BYTES)); + ret->Set(context, s_crypto_auth_KEYBYTES, v8::Number::New(iv8, crypto_auth_KEYBYTES)); + ret->Set(context, s_crypto_aead_chacha20poly1305_NPUBBYTES, v8::Number::New(iv8, crypto_aead_chacha20poly1305_NPUBBYTES)); + // "error: 'crypto_aead_aes256gcm_NPUBBYTES' was not declared in this scope"... why? + //ret->Set(context, s_crypto_aead_aes256gcm_NPUBBYTES, v8::Number::New(iv8, crypto_aead_aes256gcm_NPUBBYTES)); + ret->Set(context, s_crypto_box_NONCEBYTES, v8::Number::New(iv8, crypto_box_NONCEBYTES)); + + args.GetReturnValue().Set(ret); +} + +NATIVE_FUNCTION(LibsodiumObject, SecretboxEasy) { + PROLOGUE; + USEARG(0); + USEARG(1); + USEARG(2); + VALIDATEARG(0, OBJECT, "crypto_secretbox_easy: argument 0 is not an array/object."); + VALIDATEARG(1, OBJECT, "crypto_secretbox_easy: argument 1 is not an array/object."); + VALIDATEARG(2, OBJECT, "crypto_secretbox_easy: argument 2 is not an array/object."); + + v8::Local dataArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char dataBytes[(int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + dataBytes[i] = (unsigned char)dataArray->Get(i)->ToNumber()->Value(); + } + + v8::Local keyArray = arg1->ToObject(context).ToLocalChecked(); + unsigned char keyBytes[(int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + keyBytes[i] = (unsigned char)keyArray->Get(i)->ToNumber()->Value(); + } + + v8::Local nonceArray = arg2->ToObject(context).ToLocalChecked(); + unsigned char nonceBytes[crypto_secretbox_NONCEBYTES]; + + for (int i = 0; i < (int)nonceArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + nonceBytes[i] = (unsigned char)nonceArray->Get(i)->ToNumber()->Value(); + } + + unsigned char ciphertext[crypto_secretbox_MACBYTES + (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + crypto_secretbox_easy(ciphertext, dataBytes, (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), nonceBytes, keyBytes); + + char returnString[(sizeof(ciphertext)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(ciphertext)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", ciphertext[i]); + } + + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + + args.GetReturnValue().Set(ret); +} + +NATIVE_FUNCTION(LibsodiumObject, SecretboxEasyOpen) { + PROLOGUE; + USEARG(0); + USEARG(1); + USEARG(2); + VALIDATEARG(0, OBJECT, "crypto_secretbox_open_easy: argument 0 is not an array/object."); + VALIDATEARG(1, OBJECT, "crypto_secretbox_open_easy: argument 1 is not an array/object."); + VALIDATEARG(2, OBJECT, "crypto_secretbox_open_easy: argument 2 is not an array/object."); + + v8::Local cipherArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char cipherBytes[(int)cipherArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)cipherArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + cipherBytes[i] = (unsigned char)cipherArray->Get(i)->ToNumber()->Value(); + } + + v8::Local keyArray = arg1->ToObject(context).ToLocalChecked(); + unsigned char keyBytes[(int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + keyBytes[i] = (unsigned char)keyArray->Get(i)->ToNumber()->Value(); + } + + v8::Local nonceArray = arg2->ToObject(context).ToLocalChecked(); + unsigned char nonceBytes[crypto_secretbox_NONCEBYTES]; + + for (int i = 0; i < (int)nonceArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + nonceBytes[i] = (unsigned char)nonceArray->Get(i)->ToNumber()->Value(); + } + + unsigned char deciphertext[(int)cipherArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value() - crypto_secretbox_MACBYTES]; + + if (crypto_secretbox_open_easy(deciphertext, cipherBytes, (int)cipherArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), nonceBytes, keyBytes) != 0) { + args.GetReturnValue().SetUndefined(); + return; + } + + char returnString[(sizeof(deciphertext)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(deciphertext)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", deciphertext[i]); + } + + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + + args.GetReturnValue().Set(ret); +} + +NATIVE_FUNCTION(LibsodiumObject, Auth) { + PROLOGUE; + USEARG(0); + USEARG(1); + VALIDATEARG(0, OBJECT, "crypto_auth: argument 0 is not an array/object."); + VALIDATEARG(1, OBJECT, "crypto_auth: argument 1 is not an array/object."); + + v8::Local dataArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char dataBytes[(int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + dataBytes[i] = (unsigned char)dataArray->Get(i)->ToNumber()->Value(); + } + + v8::Local keyArray = arg1->ToObject(context).ToLocalChecked(); + unsigned char keyBytes[(int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + keyBytes[i] = (unsigned char)keyArray->Get(i)->ToNumber()->Value(); + } + + unsigned char mac[crypto_auth_BYTES]; + + crypto_auth(mac, dataBytes, (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), keyBytes); + + char returnString[(sizeof(mac)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(mac)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", mac[i]); + } + + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + + args.GetReturnValue().Set(ret); +} + +NATIVE_FUNCTION(LibsodiumObject, AuthVerify) { + PROLOGUE; + USEARG(0); + USEARG(1); + USEARG(2); + VALIDATEARG(0, OBJECT, "crypto_auth_verify: argument 0 is not an array/object."); + VALIDATEARG(1, OBJECT, "crypto_auth_verify: argument 1 is not an array/object."); + VALIDATEARG(2, OBJECT, "crypto_auth_verify: argument 2 is not an array/object."); + + v8::Local macArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char macBytes[(int)macArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)macArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + macBytes[i] = (unsigned char)macArray->Get(i)->ToNumber()->Value(); + } + + v8::Local keyArray = arg1->ToObject(context).ToLocalChecked(); + unsigned char keyBytes[(int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + keyBytes[i] = (unsigned char)keyArray->Get(i)->ToNumber()->Value(); + } + + v8::Local dataArray = arg2->ToObject(context).ToLocalChecked(); + unsigned char dataBytes[(int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + dataBytes[i] = (unsigned char)dataArray->Get(i)->ToNumber()->Value(); + } + + if (crypto_auth_verify(macBytes, dataBytes, (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), keyBytes) != 0) { + args.GetReturnValue().Set(v8::Boolean::New(iv8, false)); + return; + } + + args.GetReturnValue().Set(v8::Boolean::New(iv8, true)); +} + +NATIVE_FUNCTION(LibsodiumObject, AEADChaCha20Poly135Encrypt) { + PROLOGUE; + USEARG(0); + USEARG(1); + USEARG(2); + USEARG(3); + VALIDATEARG(0, OBJECT, "crypto_aead_chacha20poly1305_encrypt: argument 0 is not an array/object."); + VALIDATEARG(1, OBJECT, "crypto_aead_chacha20poly1305_encrypt: argument 1 is not an array/object."); + VALIDATEARG(2, OBJECT, "crypto_aead_chacha20poly1305_encrypt: argument 2 is not an array/object."); + VALIDATEARG(3, OBJECT, "crypto_aead_chacha20poly1305_encrypt: argument 3 is not an array/object."); + + v8::Local dataArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char dataBytes[(int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + dataBytes[i] = (unsigned char)dataArray->Get(i)->ToNumber()->Value(); + } + + v8::Local keyArray = arg1->ToObject(context).ToLocalChecked(); + unsigned char keyBytes[(int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + keyBytes[i] = (unsigned char)keyArray->Get(i)->ToNumber()->Value(); + } + + v8::Local nonceArray = arg2->ToObject(context).ToLocalChecked(); + unsigned char nonceBytes[crypto_secretbox_NONCEBYTES]; + + for (int i = 0; i < (int)nonceArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + nonceBytes[i] = (unsigned char)nonceArray->Get(i)->ToNumber()->Value(); + } + + v8::Local addArray = arg3->ToObject(context).ToLocalChecked(); + unsigned char addBytes[(int)addArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)addArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + addBytes[i] = (unsigned char)addArray->Get(i)->ToNumber()->Value(); + } + + unsigned char ciphertext[crypto_aead_chacha20poly1305_ABYTES + (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + unsigned long long ciphertext_len; + + crypto_aead_chacha20poly1305_encrypt(ciphertext, &ciphertext_len, dataBytes, (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), addBytes, (int)addArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), NULL, nonceBytes, keyBytes); + + char returnString[(sizeof(ciphertext)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(ciphertext)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", ciphertext[i]); + } + + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + + v8::Local retObj = v8::Object::New(iv8); + retObj->Set(context, v8::String::NewFromUtf8(iv8, "ciphertext", v8::NewStringType::kNormal).ToLocalChecked(), ret); + retObj->Set(context, v8::String::NewFromUtf8(iv8, "ciphertext_len", v8::NewStringType::kNormal).ToLocalChecked(), v8::Number::New(iv8, ciphertext_len)); + + args.GetReturnValue().Set(retObj); +} + +NATIVE_FUNCTION(LibsodiumObject, AEADChaCha20Poly135Decrypt) { + PROLOGUE; + USEARG(0); + USEARG(1); + USEARG(2); + USEARG(3); + USEARG(4); + VALIDATEARG(0, OBJECT, "crypto_aead_chacha20poly1305_decrypt: argument 0 is not an array/object."); + VALIDATEARG(1, OBJECT, "crypto_aead_chacha20poly1305_decrypt: argument 1 is not an array/object."); + VALIDATEARG(2, OBJECT, "crypto_aead_chacha20poly1305_decrypt: argument 2 is not an array/object."); + VALIDATEARG(3, OBJECT, "crypto_aead_chacha20poly1305_decrypt: argument 3 is not an array/object."); + VALIDATEARG(4, NUMBER, "crypto_aead_chacha20poly1305_decrypt: argument 4 is not a number."); + + v8::Local cipherArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char cipherBytes[(int)cipherArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)cipherArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + cipherBytes[i] = (unsigned char)cipherArray->Get(i)->ToNumber()->Value(); + } + + v8::Local keyArray = arg1->ToObject(context).ToLocalChecked(); + unsigned char keyBytes[(int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + keyBytes[i] = (unsigned char)keyArray->Get(i)->ToNumber()->Value(); + } + + v8::Local nonceArray = arg2->ToObject(context).ToLocalChecked(); + unsigned char nonceBytes[crypto_secretbox_NONCEBYTES]; + + for (int i = 0; i < (int)nonceArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + nonceBytes[i] = (unsigned char)nonceArray->Get(i)->ToNumber()->Value(); + } + + v8::Local addArray = arg3->ToObject(context).ToLocalChecked(); + unsigned char addBytes[(int)addArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)addArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + addBytes[i] = (unsigned char)addArray->Get(i)->ToNumber()->Value(); + } + + v8::Local ciphertextNum = arg4->ToNumber(context).ToLocalChecked(); + unsigned long long ciphertextLen = ciphertextNum->Value(); + + unsigned char deciphertext[ciphertextLen - crypto_aead_chacha20poly1305_ABYTES]; + unsigned long long deciphertext_len; + + if (crypto_aead_chacha20poly1305_decrypt(deciphertext, &deciphertext_len, NULL, cipherBytes, ciphertextLen, addBytes, (int)addArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), nonceBytes, keyBytes) != 0) { + args.GetReturnValue().SetUndefined(); + return; + } + + char returnString[(sizeof(deciphertext)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(deciphertext)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", deciphertext[i]); + } + + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + + v8::Local retObj = v8::Object::New(iv8); + retObj->Set(context, v8::String::NewFromUtf8(iv8, "deciphertext", v8::NewStringType::kNormal).ToLocalChecked(), ret); + retObj->Set(context, v8::String::NewFromUtf8(iv8, "deciphertext_len", v8::NewStringType::kNormal).ToLocalChecked(), v8::Number::New(iv8, deciphertext_len)); + + args.GetReturnValue().Set(retObj); +} + +// AES-256 GCM isn't supported on QEMU's system-x86_64 +// The JavaScript will automatically throw 'CPU not supported' +// plus, "crypto_aead_aes256gcm_* was not declared in this scope"... why? +/*NATIVE_FUNCTION(LibsodiumObject, AEADAES256GCMEncrypt) { + PROLOGUE; + if (crypto_aead_aes256gcm_is_available() == 0) { + args.GetReturnValue().Set(v8::Exception::Error(v8::String::NewFromUtf8(iv8, "crypto_aead_aes256gcm_encrypt: CPU not supported", v8::NewStringType::kNormal).ToLocalChecked())); + return; + } + USEARG(0); + USEARG(1); + USEARG(2); + USEARG(3); + VALIDATEARG(0, OBJECT, "crypto_aead_aes256gcm_encrypt: argument 0 is not an array/object."); + VALIDATEARG(1, OBJECT, "crypto_aead_aes256gcm_encrypt: argument 1 is not an array/object."); + VALIDATEARG(2, OBJECT, "crypto_aead_aes256gcm_encrypt: argument 2 is not an array/object."); + VALIDATEARG(3, OBJECT, "crypto_aead_aes256gcm_encrypt: argument 3 is not an array/object."); + v8::Local dataArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char dataBytes[(int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + for (int i = 0; i < (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + dataBytes[i] = (unsigned char)dataArray->Get(i)->ToNumber()->Value(); + } + v8::Local keyArray = arg1->ToObject(context).ToLocalChecked(); + unsigned char keyBytes[(int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + for (int i = 0; i < (int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + keyBytes[i] = (unsigned char)keyArray->Get(i)->ToNumber()->Value(); + } + v8::Local nonceArray = arg2->ToObject(context).ToLocalChecked(); + unsigned char nonceBytes[crypto_secretbox_NONCEBYTES]; + for (int i = 0; i < (int)nonceArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + nonceBytes[i] = (unsigned char)nonceArray->Get(i)->ToNumber()->Value(); + } + v8::Local addArray = arg3->ToObject(context).ToLocalChecked(); + unsigned char addBytes[(int)addArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + for (int i = 0; i < (int)addArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + addBytes[i] = (unsigned char)addArray->Get(i)->ToNumber()->Value(); + } + unsigned char ciphertext[crypto_aead_aes256gcm_ABYTES + (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + unsigned long long ciphertext_len; + crypto_aead_aes256gcm_encrypt(ciphertext, &ciphertext_len, dataBytes, (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), addBytes, (int)addArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), NULL, nonceBytes, keyBytes); + char returnString[(sizeof(ciphertext)/sizeof(unsigned char))*2 + 1]; + for (int i = 0; i < (sizeof(ciphertext)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", ciphertext[i]); + } + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + v8::Local retObj = v8::Object::New(iv8); + retObj->Set(context, v8::String::NewFromUtf8(iv8, "ciphertext", v8::NewStringType::kNormal).ToLocalChecked(), ret); + retObj->Set(context, v8::String::NewFromUtf8(iv8, "ciphertext_len", v8::NewStringType::kNormal).ToLocalChecked(), v8::Number::New(iv8, ciphertext_len)); + args.GetReturnValue().Set(retObj); +} +NATIVE_FUNCTION(LibsodiumObject, AEADAES256GCMDecrypt) { + PROLOGUE; + if (crypto_aead_aes256gcm_is_available() == 0) { + args.GetReturnValue().Set(v8::Exception::Error(v8::String::NewFromUtf8(iv8, "crypto_aead_aes256gcm_decrypt: CPU not supported", v8::NewStringType::kNormal).ToLocalChecked())); + return; + } + USEARG(0); + USEARG(1); + USEARG(2); + USEARG(3); + USEARG(4); + VALIDATEARG(0, OBJECT, "crypto_aead_aes256gcm_decrypt: argument 0 is not an array/object."); + VALIDATEARG(1, OBJECT, "crypto_aead_aes256gcm_decrypt: argument 1 is not an array/object."); + VALIDATEARG(2, OBJECT, "crypto_aead_aes256gcm_decrypt: argument 2 is not an array/object."); + VALIDATEARG(3, OBJECT, "crypto_aead_aes256gcm_decrypt: argument 3 is not an array/object."); + VALIDATEARG(4, NUMBER, "crypto_aead_aes256gcm_decrypt: argument 4 is not a number."); + v8::Local cipherArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char cipherBytes[(int)cipherArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + for (int i = 0; i < (int)cipherArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + cipherBytes[i] = (unsigned char)cipherArray->Get(i)->ToNumber()->Value(); + } + v8::Local keyArray = arg1->ToObject(context).ToLocalChecked(); + unsigned char keyBytes[(int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + for (int i = 0; i < (int)keyArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + keyBytes[i] = (unsigned char)keyArray->Get(i)->ToNumber()->Value(); + } + v8::Local nonceArray = arg2->ToObject(context).ToLocalChecked(); + unsigned char nonceBytes[crypto_secretbox_NONCEBYTES]; + for (int i = 0; i < (int)nonceArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + nonceBytes[i] = (unsigned char)nonceArray->Get(i)->ToNumber()->Value(); + } + v8::Local addArray = arg3->ToObject(context).ToLocalChecked(); + unsigned char addBytes[(int)addArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + for (int i = 0; i < (int)addArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + addBytes[i] = (unsigned char)addArray->Get(i)->ToNumber()->Value(); + } + v8::Local ciphertextNum = arg4->ToNumber(context).ToLocalChecked(); + unsigned long long ciphertextLen = ciphertextNum->Value(); + unsigned char deciphertext[ciphertextLen - crypto_aead_aes256gcm_ABYTES]; + unsigned long long deciphertext_len; + if (crypto_aead_aes256gcm_decrypt(deciphertext, &deciphertext_len, NULL, cipherBytes, ciphertextLen, addBytes, (int)addArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), nonceBytes, keyBytes) != 0) { + args.GetReturnValue().SetUndefined(); + return; + } + char returnString[(sizeof(deciphertext)/sizeof(unsigned char))*2 + 1]; + for (int i = 0; i < (sizeof(deciphertext)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", deciphertext[i]); + } + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + v8::Local retObj = v8::Object::New(iv8); + retObj->Set(context, v8::String::NewFromUtf8(iv8, "deciphertext", v8::NewStringType::kNormal).ToLocalChecked(), ret); + retObj->Set(context, v8::String::NewFromUtf8(iv8, "deciphertext_len", v8::NewStringType::kNormal).ToLocalChecked(), v8::Number::New(iv8, deciphertext_len)); + args.GetReturnValue().Set(retObj); +}*/ + +NATIVE_FUNCTION(LibsodiumObject, BoxEasy) { + PROLOGUE; + USEARG(0); + USEARG(1); + USEARG(2); + USEARG(3); + VALIDATEARG(0, OBJECT, "crypto_box_easy: argument 0 is not an array/object."); + VALIDATEARG(1, OBJECT, "crypto_box_easy: argument 1 is not an array/object."); + VALIDATEARG(2, OBJECT, "crypto_box_easy: argument 2 is not an array/object."); + VALIDATEARG(3, OBJECT, "crypto_box_easy: argument 3 is not an array/object."); + + v8::Local dataArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char dataBytes[(int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + dataBytes[i] = (unsigned char)dataArray->Get(i)->ToNumber()->Value(); + } + + v8::Local pkArray = arg1->ToObject(context).ToLocalChecked(); + unsigned char pkBytes[(int)pkArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)pkArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + pkBytes[i] = (unsigned char)pkArray->Get(i)->ToNumber()->Value(); + } + + v8::Local skArray = arg2->ToObject(context).ToLocalChecked(); + unsigned char skBytes[(int)skArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)skArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + skBytes[i] = (unsigned char)skArray->Get(i)->ToNumber()->Value(); + } + + v8::Local nonceArray = arg3->ToObject(context).ToLocalChecked(); + unsigned char nonceBytes[crypto_box_NONCEBYTES]; + + for (int i = 0; i < (int)nonceArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + nonceBytes[i] = (unsigned char)nonceArray->Get(i)->ToNumber()->Value(); + } + + unsigned char ciphertext[crypto_box_MACBYTES + (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + crypto_box_easy(ciphertext, dataBytes, (int)dataArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), nonceBytes, pkBytes, skBytes); + + char returnString[(sizeof(ciphertext)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(ciphertext)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", ciphertext[i]); + } + + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + + args.GetReturnValue().Set(ret); +} + +NATIVE_FUNCTION(LibsodiumObject, BoxEasyOpen) { + PROLOGUE; + USEARG(0); + USEARG(1); + USEARG(2); + USEARG(3); + VALIDATEARG(0, OBJECT, "crypto_box_open_easy: argument 0 is not an array/object."); + VALIDATEARG(1, OBJECT, "crypto_box_open_easy: argument 1 is not an array/object."); + VALIDATEARG(2, OBJECT, "crypto_box_open_easy: argument 2 is not an array/object."); + VALIDATEARG(3, OBJECT, "crypto_box_open_easy: argument 3 is not an array/object."); + + v8::Local cipherArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char cipherBytes[(int)cipherArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)cipherArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + cipherBytes[i] = (unsigned char)cipherArray->Get(i)->ToNumber()->Value(); + } + + v8::Local pkArray = arg1->ToObject(context).ToLocalChecked(); + unsigned char pkBytes[(int)pkArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)pkArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + pkBytes[i] = (unsigned char)pkArray->Get(i)->ToNumber()->Value(); + } + + v8::Local skArray = arg2->ToObject(context).ToLocalChecked(); + unsigned char skBytes[(int)skArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)skArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + skBytes[i] = (unsigned char)skArray->Get(i)->ToNumber()->Value(); + } + + v8::Local nonceArray = arg3->ToObject(context).ToLocalChecked(); + unsigned char nonceBytes[crypto_box_NONCEBYTES]; + + for (int i = 0; i < (int)nonceArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + nonceBytes[i] = (unsigned char)nonceArray->Get(i)->ToNumber()->Value(); + } + + unsigned char deciphertext[(int)cipherArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value() - crypto_box_MACBYTES]; + + if (crypto_box_open_easy(deciphertext, cipherBytes, (int)cipherArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(), nonceBytes, pkBytes, skBytes) != 0) { + args.GetReturnValue().SetUndefined(); + return; + } + + char returnString[(sizeof(deciphertext)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(deciphertext)/sizeof(unsigned char)); i++) { + sprintf(&returnString[i*2], "%02X", deciphertext[i]); + } + + v8::MaybeLocal maybe_ret = v8::String::NewFromUtf8(iv8, returnString, v8::NewStringType::kNormal); + + v8::Local ret; + if (!maybe_ret.ToLocal(&ret)) { + args.GetReturnValue().SetUndefined(); + return; + } + + args.GetReturnValue().Set(ret); +} + +NATIVE_FUNCTION(LibsodiumObject, BoxKeypair) { + PROLOGUE; + + unsigned char pk[crypto_box_PUBLICKEYBYTES]; + unsigned char sk[crypto_box_SECRETKEYBYTES]; + + crypto_box_keypair(pk, sk); + + char returnString1[(sizeof(pk)/sizeof(unsigned char))*2 + 1]; + char returnString2[(sizeof(sk)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(pk)/sizeof(unsigned char)); i++) { + sprintf(&returnString1[i*2], "%02X", pk[i]); + } + + for (int i = 0; i < (sizeof(sk)/sizeof(unsigned char)); i++) { + sprintf(&returnString2[i*2], "%02X", sk[i]); + } + + v8::MaybeLocal maybe_ret1 = v8::String::NewFromUtf8(iv8, returnString1, v8::NewStringType::kNormal); + v8::MaybeLocal maybe_ret2 = v8::String::NewFromUtf8(iv8, returnString2, v8::NewStringType::kNormal); + + v8::Local ret1; + v8::Local ret2; + + if (!maybe_ret1.ToLocal(&ret1)) { + args.GetReturnValue().Set(v8::String::NewFromUtf8(iv8, "crypto_box_keypair: error creating public key", v8::NewStringType::kNormal).ToLocalChecked()); + return; + } + if (!maybe_ret2.ToLocal(&ret2)) { + args.GetReturnValue().Set(v8::String::NewFromUtf8(iv8, "crypto_box_keypair: error creating secret key", v8::NewStringType::kNormal).ToLocalChecked()); + return; + } + + v8::Local retObj = v8::Object::New(iv8); + retObj->Set(context, v8::String::NewFromUtf8(iv8, "public", v8::NewStringType::kNormal).ToLocalChecked(), ret1); + retObj->Set(context, v8::String::NewFromUtf8(iv8, "secret", v8::NewStringType::kNormal).ToLocalChecked(), ret2); + + args.GetReturnValue().Set(retObj); +} + +NATIVE_FUNCTION(LibsodiumObject, BoxSeedKeypair) { + PROLOGUE; + USEARG(0); + VALIDATEARG(0, OBJECT, "crypto_box_seed_keypair: argument 0 is not an array/object."); + + v8::Local seedArray = arg0->ToObject(context).ToLocalChecked(); + unsigned char seedBytes[(int)seedArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value()]; + + for (int i = 0; i < (int)seedArray->Get(v8::String::NewFromUtf8(iv8, "length", v8::NewStringType::kNormal).ToLocalChecked())->ToNumber()->Value(); i++) { + seedBytes[i] = (unsigned char)seedArray->Get(i)->ToNumber()->Value(); + } + + unsigned char pk[crypto_box_PUBLICKEYBYTES]; + unsigned char sk[crypto_box_SECRETKEYBYTES]; + + crypto_box_seed_keypair(pk, sk, seedBytes); + + char returnString1[(sizeof(pk)/sizeof(unsigned char))*2 + 1]; + char returnString2[(sizeof(sk)/sizeof(unsigned char))*2 + 1]; + + for (int i = 0; i < (sizeof(pk)/sizeof(unsigned char)); i++) { + sprintf(&returnString1[i*2], "%02X", pk[i]); + } + + for (int i = 0; i < (sizeof(sk)/sizeof(unsigned char)); i++) { + sprintf(&returnString2[i*2], "%02X", sk[i]); + } + + v8::MaybeLocal maybe_ret1 = v8::String::NewFromUtf8(iv8, returnString1, v8::NewStringType::kNormal); + v8::MaybeLocal maybe_ret2 = v8::String::NewFromUtf8(iv8, returnString2, v8::NewStringType::kNormal); + + v8::Local ret1; + v8::Local ret2; + + if (!maybe_ret1.ToLocal(&ret1)) { + args.GetReturnValue().Set(v8::String::NewFromUtf8(iv8, "crypto_box_keypair: error creating public key", v8::NewStringType::kNormal).ToLocalChecked()); + return; + } + if (!maybe_ret2.ToLocal(&ret2)) { + args.GetReturnValue().Set(v8::String::NewFromUtf8(iv8, "crypto_box_keypair: error creating secret key", v8::NewStringType::kNormal).ToLocalChecked()); + return; + } + + v8::Local retObj = v8::Object::New(iv8); + retObj->Set(context, v8::String::NewFromUtf8(iv8, "public", v8::NewStringType::kNormal).ToLocalChecked(), ret1); + retObj->Set(context, v8::String::NewFromUtf8(iv8, "secret", v8::NewStringType::kNormal).ToLocalChecked(), ret2); + + args.GetReturnValue().Set(retObj); +} + } // namespace rt diff --git a/src/kernel/native-object.h b/src/kernel/native-object.h index 3b2308d24..d48782aca 100644 --- a/src/kernel/native-object.h +++ b/src/kernel/native-object.h @@ -282,4 +282,54 @@ class ResourceMemoryBlockObject : public JsObjectWrapper memory_block_; }; +class LibsodiumObject : public JsObjectWrapper { +public: + LibsodiumObject() : JsObjectWrapper() {} + + DECLARE_NATIVE(BlakeHash); + DECLARE_NATIVE(Sha256Hash); + DECLARE_NATIVE(Sha512Hash); + DECLARE_NATIVE(Constants); + DECLARE_NATIVE(SecretboxEasy); + DECLARE_NATIVE(SecretboxEasyOpen); + DECLARE_NATIVE(Auth); + DECLARE_NATIVE(AuthVerify); + DECLARE_NATIVE(AEADChaCha20Poly135Encrypt); + DECLARE_NATIVE(AEADChaCha20Poly135Decrypt); + DECLARE_NATIVE(RandombytesBuf); + // AES-256 GCM isn't supported on QEMU's system-x86_64 + // The JavaScript will automatically throw 'CPU not supported' + // plus, "crypto_aead_aes256gcm_* was not declared in this scope"... why? + /*DECLARE_NATIVE(AEADAES256GCMEncrypt); + DECLARE_NATIVE(AEADAES256GCMDecrypt);*/ + DECLARE_NATIVE(BoxEasy); + DECLARE_NATIVE(BoxEasyOpen); + DECLARE_NATIVE(BoxKeypair); + DECLARE_NATIVE(BoxSeedKeypair); + + void ObjectInit(ExportBuilder obj) { + obj.SetCallback("randombytes_buf", RandombytesBuf); + obj.SetCallback("crypto_generichash", BlakeHash); + obj.SetCallback("crypto_hash_sha256", Sha256Hash); + obj.SetCallback("crypto_hash_sha512", Sha512Hash); + obj.SetCallback("crypto_constants", Constants); + obj.SetCallback("crypto_secretbox_easy", SecretboxEasy); + obj.SetCallback("crypto_secretbox_open_easy", SecretboxEasyOpen); + obj.SetCallback("crypto_auth", Auth); + obj.SetCallback("crypto_auth_verify", AuthVerify); + obj.SetCallback("crypto_aead_chacha20poly1305_encrypt", AEADChaCha20Poly135Encrypt); + obj.SetCallback("crypto_aead_chacha20poly1305_decrypt", AEADChaCha20Poly135Decrypt); + /*obj.SetCallback("crypto_aead_aes256gcm_encrypt", AEADAES256GCMEncrypt); + obj.SetCallback("crypto_aead_aes256gcm_decrypt", AEADAES256GCMDecrypt);*/ + obj.SetCallback("crypto_box_easy", BoxEasy); + obj.SetCallback("crypto_box_open_easy", BoxEasyOpen); + obj.SetCallback("crypto_box_keypair", BoxKeypair); + obj.SetCallback("crypto_box_seed_keypair", BoxSeedKeypair); + } + + JsObjectWrapperBase* Clone() const { + return nullptr; + } +}; + } // namespace rt diff --git a/src/kernel/template-cache.h b/src/kernel/template-cache.h index ded0c90a4..18802550f 100644 --- a/src/kernel/template-cache.h +++ b/src/kernel/template-cache.h @@ -41,6 +41,7 @@ enum class NativeTypeId : uint32_t { TYPEID_RESOURCE_IO_RANGE, TYPEID_ISOLATES_MANAGER, TYPEID_ALLOCATOR, + TYPEID_LIBSODIUM, TYPEID_FUNCTION, LAST // Keep it as the last element