Compare commits
44 Commits
v9.3.0.68
...
feature/ex
| Author | SHA1 | Date | |
|---|---|---|---|
| 6f8ec5dee5 | |||
| 79f14203dd | |||
| e4800b8c54 | |||
| b8fda7871c | |||
| 9a2ad514db | |||
| ce3fdfdf8a | |||
| 672c629fdf | |||
| 68447041f3 | |||
| 43b0ecfa19 | |||
| ff6a201f78 | |||
| e10c0b73ca | |||
| 4a51b18279 | |||
| ee56819e63 | |||
| d6490a1671 | |||
| 2138e38d00 | |||
| 9f6e3be634 | |||
| 71f648c694 | |||
| 3f2ad2dfd7 | |||
| 4e28069ec9 | |||
| 615a73e7eb | |||
| 01786f307c | |||
| 8d19851865 | |||
| d98d3a211f | |||
| 9f5b821c83 | |||
| dbf46c8692 | |||
| d3e27eda98 | |||
| e5953066a1 | |||
| c98942b871 | |||
| e5caeed16c | |||
| 790c92c30b | |||
| 879007c270 | |||
| 24de9afbfb | |||
| 8c4f2468a3 | |||
| 49ca47d777 | |||
| 54a4c2b6fd | |||
| 5f95ca7bfc | |||
| e66ecb52ad | |||
| 45e7b323ad | |||
| b58ac033cf | |||
| 3ceff2460c | |||
| c0c045f2f1 | |||
| 5953eb8f08 | |||
| 6a6cfd7b5b | |||
| 1efde9d7e2 |
@ -133,7 +133,7 @@ namespace NSOpenSSL
|
||||
}
|
||||
|
||||
// rsa
|
||||
bool RSA_GenerateKeys(unsigned char*& publicKey, unsigned char*& privateKey)
|
||||
bool RSA_GenerateKeys(unsigned char*& publicKey, unsigned char*& privateKey, const int keyLen)
|
||||
{
|
||||
publicKey = NULL;
|
||||
privateKey = NULL;
|
||||
@ -142,7 +142,8 @@ namespace NSOpenSSL
|
||||
BIGNUM *exponent = BN_new();
|
||||
|
||||
BN_set_word(exponent, RSA_F4);
|
||||
int result = RSA_generate_multi_prime_key(rsa, 2048, 2, exponent, NULL);
|
||||
int primes = (keyLen < 4096) ? 2 : 4;
|
||||
int result = RSA_generate_multi_prime_key(rsa, keyLen, primes, exponent, NULL);
|
||||
if (0 == result)
|
||||
return false;
|
||||
|
||||
@ -370,6 +371,27 @@ namespace NSOpenSSL
|
||||
// new algs
|
||||
bool GenerateKeysByAlgs(const std::string& alg, std::string& publicKey, std::string& privateKey)
|
||||
{
|
||||
int nRsaKeyLen = 0;
|
||||
if ("rsa2048" == alg)
|
||||
nRsaKeyLen = 2048;
|
||||
else if ("rsa4096" == alg)
|
||||
nRsaKeyLen = 4096;
|
||||
|
||||
if (nRsaKeyLen > 0)
|
||||
{
|
||||
unsigned char* publicKeyPtr = NULL;
|
||||
unsigned char* privateKeyPtr = NULL;
|
||||
if (!RSA_GenerateKeys(publicKeyPtr, privateKeyPtr))
|
||||
return false;
|
||||
|
||||
publicKey = std::string((char*)publicKeyPtr);
|
||||
privateKey = std::string((char*)privateKeyPtr);
|
||||
|
||||
openssl_free(publicKeyPtr);
|
||||
openssl_free(privateKeyPtr);
|
||||
return true;
|
||||
}
|
||||
|
||||
EVP_PKEY* pkey = NULL;
|
||||
EVP_PKEY_CTX* pctx = NULL;
|
||||
|
||||
@ -453,7 +475,7 @@ namespace NSOpenSSL
|
||||
return (1 == nResult) ? true : false;
|
||||
}
|
||||
|
||||
CMemoryData Enrypt(const unsigned char* data, const int& data_len, const std::string& publicKey)
|
||||
CMemoryData Encrypt(const unsigned char* data, const int& data_len, const std::string& publicKey, const bool& isLenToBuffer)
|
||||
{
|
||||
CMemoryData returnData;
|
||||
|
||||
@ -477,8 +499,19 @@ namespace NSOpenSSL
|
||||
|
||||
size_t ciphertextLen = 0;
|
||||
EVP_PKEY_encrypt(ctx, NULL, &ciphertextLen, data, (size_t)data_len);
|
||||
returnData.Alloc(ciphertextLen);
|
||||
EVP_PKEY_encrypt(ctx, returnData.Data, &returnData.Size, data, (size_t)data_len);
|
||||
|
||||
if (isLenToBuffer)
|
||||
{
|
||||
returnData.Alloc(ciphertextLen + 4);
|
||||
EVP_PKEY_encrypt(ctx, returnData.Data + 4, &returnData.Size, data, (size_t)data_len);
|
||||
int nLen = (int)returnData.Size;
|
||||
memcpy(returnData.Data, &nLen, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
returnData.Alloc(ciphertextLen);
|
||||
EVP_PKEY_encrypt(ctx, returnData.Data, &returnData.Size, data, (size_t)data_len);
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
@ -486,7 +519,7 @@ namespace NSOpenSSL
|
||||
return returnData;
|
||||
}
|
||||
|
||||
CMemoryData Decrypt(const unsigned char* data, const int& data_len, const std::string& privateKey)
|
||||
CMemoryData Decrypt(const unsigned char* data, const int& data_len, const std::string& privateKey, const bool& isLenToBuffer)
|
||||
{
|
||||
CMemoryData returnData;
|
||||
|
||||
@ -510,8 +543,19 @@ namespace NSOpenSSL
|
||||
|
||||
size_t ciphertextLen = 0;
|
||||
EVP_PKEY_decrypt(ctx, NULL, &ciphertextLen, data, (size_t)data_len);
|
||||
returnData.Alloc(ciphertextLen);
|
||||
EVP_PKEY_decrypt(ctx, returnData.Data, &returnData.Size, data, (size_t)data_len);
|
||||
|
||||
if (isLenToBuffer)
|
||||
{
|
||||
returnData.Alloc(ciphertextLen + 4);
|
||||
EVP_PKEY_decrypt(ctx, returnData.Data + 4, &returnData.Size, data, (size_t)data_len);
|
||||
int nLen = (int)returnData.Size;
|
||||
memcpy(returnData.Data, &nLen, 4);
|
||||
}
|
||||
else
|
||||
{
|
||||
returnData.Alloc(ciphertextLen);
|
||||
EVP_PKEY_decrypt(ctx, returnData.Data, &returnData.Size, data, (size_t)data_len);
|
||||
}
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
EVP_PKEY_CTX_free(ctx);
|
||||
|
||||
@ -75,7 +75,7 @@ namespace NSOpenSSL
|
||||
OPENSSL_DECL unsigned char* GetHash(const unsigned char* data, const unsigned int& size, const int& alg, unsigned int& len);
|
||||
|
||||
// rsa
|
||||
OPENSSL_DECL bool RSA_GenerateKeys(unsigned char*& publicKey, unsigned char*& privateKey);
|
||||
OPENSSL_DECL bool RSA_GenerateKeys(unsigned char*& publicKey, unsigned char*& privateKey, const int keyLen = 2048);
|
||||
OPENSSL_DECL bool RSA_EncryptPublic(const unsigned char* publicKey, const unsigned char* data, const unsigned int& size, unsigned char*& data_crypt, unsigned int& data_crypt_len);
|
||||
OPENSSL_DECL bool RSA_DecryptPrivate(const unsigned char* privateKey, const unsigned char* data, const unsigned int& size, unsigned char*& data_decrypt, unsigned int& data_decrypt_len);
|
||||
|
||||
@ -91,8 +91,8 @@ namespace NSOpenSSL
|
||||
OPENSSL_DECL CMemoryData Sign(const unsigned char* data, const int& len, const std::string& privateKey);
|
||||
OPENSSL_DECL bool Verify(const unsigned char* data, const int& data_len, const std::string& publicKey,
|
||||
const unsigned char* signature, const int& signature_len);
|
||||
OPENSSL_DECL CMemoryData Enrypt(const unsigned char* data, const int& data_len, const std::string& publicKey);
|
||||
OPENSSL_DECL CMemoryData Decrypt(const unsigned char* data, const int& data_len, const std::string& privateKey);
|
||||
OPENSSL_DECL CMemoryData Encrypt(const unsigned char* data, const int& data_len, const std::string& publicKey, const bool& isLenToBuffer = false);
|
||||
OPENSSL_DECL CMemoryData Decrypt(const unsigned char* data, const int& data_len, const std::string& privateKey, const bool& isLenToBuffer = false);
|
||||
|
||||
// aes
|
||||
OPENSSL_DECL int AES_GetKeySize(int type);
|
||||
|
||||
@ -10,6 +10,8 @@ import common
|
||||
|
||||
base.configure_common_apps()
|
||||
|
||||
python_binary = sys.executable
|
||||
|
||||
# fetch emsdk
|
||||
command_prefix = "" if ("windows" == base.host_platform()) else "./"
|
||||
if not base.is_dir("emsdk"):
|
||||
@ -141,9 +143,9 @@ for param in argv:
|
||||
if json_data["run_before"]:
|
||||
base.print_info("before")
|
||||
if base.is_file(work_dir + json_data["run_before"]):
|
||||
base.cmd_in_dir(work_dir, "python", [json_data["run_before"]])
|
||||
base.cmd_in_dir(work_dir, python_binary, [json_data["run_before"]])
|
||||
else:
|
||||
base.cmd_in_dir(work_dir, "python", ["-c", json_data["run_before"]])
|
||||
base.cmd_in_dir(work_dir, python_binary, ["-c", json_data["run_before"]])
|
||||
|
||||
# remove previous version
|
||||
common.clear_dir(work_dir + "/o")
|
||||
@ -169,6 +171,6 @@ for param in argv:
|
||||
if json_data["run_after"]:
|
||||
base.print_info("after")
|
||||
if base.is_file(work_dir + json_data["run_after"]):
|
||||
base.cmd_in_dir(work_dir, "python", [json_data["run_after"]])
|
||||
base.cmd_in_dir(work_dir, python_binary, [json_data["run_after"]])
|
||||
else:
|
||||
base.cmd_in_dir(work_dir, "python", ["-c", json_data["run_after"]])
|
||||
base.cmd_in_dir(work_dir, python_binary, ["-c", json_data["run_after"]])
|
||||
|
||||
@ -72,6 +72,11 @@ public:
|
||||
}
|
||||
|
||||
public:
|
||||
bool Sign(unsigned char* pData, unsigned int nSize, unsigned char*& pDataDst, unsigned int& nSizeDst)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string Sign(unsigned char* pData, unsigned int nSize)
|
||||
{
|
||||
NSOpenSSL::CMemoryData data = NSOpenSSL::Sign(pData, (int)nSize, m_pem_key);
|
||||
|
||||
13
DesktopEditor/xmlsec/src/wasm/extension/after.py
Normal file
@ -0,0 +1,13 @@
|
||||
import sys;
|
||||
sys.path.append("../../../../../../build_tools/scripts");
|
||||
import base;
|
||||
|
||||
base.replaceInFile("./deploy/engine.js", "__ATPOSTRUN__=[];", "__ATPOSTRUN__=[function(){window.cryptoJS.onLoad();}];");
|
||||
base.replaceInFile("./deploy/engine.js", "__ATPOSTRUN__ = [];", "__ATPOSTRUN__=[function(){window.cryptoJS.onLoad();}];");
|
||||
base.replaceInFile("./deploy/engine.js", "function getBinaryPromise()", "function getBinaryPromise2()");
|
||||
|
||||
base.replaceInFile("./deploy/engine_ie.js", "__ATPOSTRUN__=[];", "__ATPOSTRUN__=[function(){window.cryptoJS.onLoad();}];");
|
||||
base.replaceInFile("./deploy/engine_ie.js", "__ATPOSTRUN__ = [];", "__ATPOSTRUN__=[function(){window.cryptoJS.onLoad();}];");
|
||||
base.replaceInFile("./deploy/engine_ie.js", "function getBinaryPromise()", "function getBinaryPromise2()");
|
||||
|
||||
base.delete_file("./engine.wasm.js")
|
||||
7
DesktopEditor/xmlsec/src/wasm/extension/before.py
Normal file
@ -0,0 +1,7 @@
|
||||
import sys
|
||||
sys.path.append("../../../../../../build_tools/scripts")
|
||||
import base
|
||||
|
||||
base.cmd_in_dir("./../3rdParty", sys.executable, ["openssl.py"])
|
||||
|
||||
base.copy_file("./extension/engine.wasm.js", "./engine.wasm.js")
|
||||
224
DesktopEditor/xmlsec/src/wasm/extension/deploy/engine.js
Normal file
BIN
DesktopEditor/xmlsec/src/wasm/extension/deploy/engine.wasm
Executable file
238
DesktopEditor/xmlsec/src/wasm/extension/deploy/engine_ie.js
Normal file
56
DesktopEditor/xmlsec/src/wasm/extension/engine.json
Normal file
@ -0,0 +1,56 @@
|
||||
{
|
||||
"name": "engine",
|
||||
"res_folder": "./deploy",
|
||||
"wasm": true,
|
||||
"asm": true,
|
||||
"embed_mem_file": true,
|
||||
"run_before": "before.py",
|
||||
"run_after": "after.py",
|
||||
"base_js_content": "./engine.wasm.js",
|
||||
|
||||
"compiler_flags": [
|
||||
"-O3",
|
||||
"-Wno-unused-command-line-argument",
|
||||
"-s ALLOW_MEMORY_GROWTH=1",
|
||||
"-s FILESYSTEM=0",
|
||||
"-s ENVIRONMENT='web'",
|
||||
"-s ASSERTIONS",
|
||||
"-s LLD_REPORT_UNDEFINED",
|
||||
"-s TOTAL_MEMORY=4MB"
|
||||
],
|
||||
"exported_functions": [
|
||||
"_malloc",
|
||||
"_free",
|
||||
"_Crypto_Malloc",
|
||||
"_Crypto_Free",
|
||||
"_Crypto_CreateKeys",
|
||||
"_Crypto_Sign",
|
||||
"_Crypto_ChangePassword",
|
||||
"_Crypto_Decrypt",
|
||||
"_Crypto_Encrypt"
|
||||
],
|
||||
"include_path": [
|
||||
"./../3rdParty/openssl/include", "./../3rdParty/openssl"
|
||||
],
|
||||
"define": [
|
||||
"__linux__", "_LINUX"
|
||||
],
|
||||
"compile_files_array": [
|
||||
{
|
||||
"name": "a",
|
||||
"folder": "../../../../common/",
|
||||
"files": ["Base64.cpp", "File.cpp"]
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"folder": "../../../../../Common/3dParty/openssl/common/",
|
||||
"files": ["common_openssl.cpp"]
|
||||
},
|
||||
{
|
||||
"name": "c",
|
||||
"folder": "./",
|
||||
"files": ["main.cpp"]
|
||||
}
|
||||
],
|
||||
"sources": ["./../3rdParty/openssl/libcrypto.a"]
|
||||
}
|
||||
48
DesktopEditor/xmlsec/src/wasm/extension/extension.pro
Normal file
@ -0,0 +1,48 @@
|
||||
QT -= core gui
|
||||
|
||||
TARGET = wasm
|
||||
TEMPLATE = app
|
||||
CONFIG += console
|
||||
CONFIG -= app_bundle
|
||||
|
||||
DEFINES += TEST_AS_EXECUTABLE
|
||||
|
||||
CORE_ROOT_DIR = $$PWD/../../../../..
|
||||
PWD_ROOT_DIR = $$PWD
|
||||
include($$CORE_ROOT_DIR/Common/base.pri)
|
||||
|
||||
DEFINES += KERNEL_NO_USE_DYNAMIC_LIBRARY
|
||||
DEFINES += COMMON_OPENSSL_BUILDING_INTERNAL
|
||||
|
||||
SOURCES += \
|
||||
$$CORE_ROOT_DIR/DesktopEditor/common/File.cpp \
|
||||
$$CORE_ROOT_DIR/DesktopEditor/common/Base64.cpp
|
||||
|
||||
HEADERS += \
|
||||
$$CORE_ROOT_DIR/DesktopEditor/xmlsec/src/include/Certificate.h \
|
||||
$$CORE_ROOT_DIR/DesktopEditor/xmlsec/src/include/CertificateCommon.h
|
||||
|
||||
SOURCES += \
|
||||
$$CORE_ROOT_DIR/DesktopEditor/xmlsec/src/src/CertificateCommon.cpp
|
||||
|
||||
DEFINES += SUPPORT_OFORM
|
||||
HEADERS += $$CORE_ROOT_DIR/DesktopEditor/xmlsec/src/src/Certificate_oform.h
|
||||
|
||||
# OPENSSL
|
||||
CONFIG += open_ssl_common
|
||||
include($$CORE_ROOT_DIR/Common/3dParty/openssl/openssl.pri)
|
||||
|
||||
core_windows {
|
||||
LIBS += -lcrypt32
|
||||
LIBS += -lcryptui
|
||||
LIBS += -lAdvapi32
|
||||
LIBS += -lws2_32
|
||||
LIBS += -lUser32
|
||||
}
|
||||
|
||||
core_linux {
|
||||
LIBS += -ldl
|
||||
}
|
||||
|
||||
# WASM EXPORT
|
||||
SOURCES += main.cpp
|
||||
587
DesktopEditor/xmlsec/src/wasm/extension/extension/background.js
Normal file
@ -0,0 +1,24 @@
|
||||
function KeyStorage()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
console.log("reseived in background:", message);
|
||||
|
||||
// Отображение всплывающего окна
|
||||
chrome.action.openPopup();
|
||||
|
||||
// Посылаем событие обратно на страницу
|
||||
if (sender.tab) {
|
||||
chrome.scripting.executeScript({
|
||||
target: { tabId: sender.tab.id },
|
||||
func: (msg) => {
|
||||
document.dispatchEvent(new CustomEvent("customEventFromExtension", { detail: msg }));
|
||||
},
|
||||
args: [message]
|
||||
});
|
||||
}
|
||||
|
||||
sendResponse({ status: "received" });
|
||||
});
|
||||
37
DesktopEditor/xmlsec/src/wasm/extension/extension/content.js
Normal file
@ -0,0 +1,37 @@
|
||||
function sendEventToPage(id, data) {
|
||||
document.dispatchEvent(new CustomEvent("olala", { detail: data }));
|
||||
}
|
||||
|
||||
var ENGINE_VERSION = 1;
|
||||
var ENGINE_MESSAGE_CHECK = "onlyoffice-engine-check";
|
||||
var ENGINE_MESSAGE_DATA = "onlyoffice-engine-data";
|
||||
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.action === "popupMessage") {
|
||||
console.log("Сообщение из popup:", message.text);
|
||||
|
||||
if (window.pluginEngine && window.pluginEngine.onMessageFromPlugin)
|
||||
window.pluginEngine.onMessageFromPlugin(message);
|
||||
|
||||
sendResponse({ status: "Сообщение получено!" });
|
||||
|
||||
sendEventToPage({ message: "Привет от content.js" });
|
||||
}
|
||||
});
|
||||
|
||||
// event from page with info about engine (is exist, version...)
|
||||
document.addEventListener(ENGINE_MESSAGE_CHECK + "-page", (event) => {
|
||||
document.dispatchEvent(new CustomEvent(ENGINE_MESSAGE_CHECK + "-content", { detail: { version : ENGINE_VERSION } }));
|
||||
});
|
||||
|
||||
// event from page with action (proxy to background)
|
||||
document.addEventListener(ENGINE_MESSAGE_DATA + "-page", (event) => {
|
||||
chrome.runtime.sendMessage({
|
||||
id : ENGINE_MESSAGE_DATA + "-engine",
|
||||
data : event.detail
|
||||
});
|
||||
});
|
||||
|
||||
document.addEventListener(ENGINE_MESSAGE_DATA + "-engine", (event) => {
|
||||
document.dispatchEvent(new CustomEvent(ENGINE_MESSAGE_DATA + "-content", event.detail));
|
||||
});
|
||||
31
DesktopEditor/xmlsec/src/wasm/extension/extension/engine.js
Normal file
@ -0,0 +1,31 @@
|
||||
(function(window, undefined){
|
||||
|
||||
function Engine()
|
||||
{
|
||||
}
|
||||
|
||||
Engine.prototype.generateKeys = async function(alg, password, salt)
|
||||
{
|
||||
};
|
||||
|
||||
Engine.prototype.changePassword = async function(privateKey, passwordOld, passwordNew, salt)
|
||||
{
|
||||
};
|
||||
|
||||
Engine.prototype.sign = async function(privateKey, password, salt, xml)
|
||||
{
|
||||
};
|
||||
|
||||
// ENCRYPT
|
||||
Engine.prototype.decrypt = async function(privateKeyEnc, password, salt, data)
|
||||
{
|
||||
};
|
||||
|
||||
Engine.prototype.encrypt = async function(publicKey, data)
|
||||
{
|
||||
};
|
||||
|
||||
window.cryptoJS = new Engine();
|
||||
|
||||
})(window, undefined);
|
||||
|
||||
BIN
DesktopEditor/xmlsec/src/wasm/extension/extension/engine.wasm
Normal file
223
DesktopEditor/xmlsec/src/wasm/extension/extension/engine.wasm.js
Normal file
@ -0,0 +1,223 @@
|
||||
(function(window, undefined){
|
||||
WebAssembly.instantiateStreaming = undefined;
|
||||
|
||||
function MemoryData(ptr) {
|
||||
this.ptr = ptr;
|
||||
}
|
||||
MemoryData.prototype.isValid = function() {
|
||||
return (this.ptr === 0) ? false : true;
|
||||
};
|
||||
MemoryData.prototype.free = function() {
|
||||
if (0 != this.ptr)
|
||||
Module["_Crypto_Free"](this.ptr);
|
||||
};
|
||||
MemoryData.prototype.getData = function() {
|
||||
let lenArray = new Int32Array(Module["HEAP8"].buffer, this.ptr, 4);
|
||||
let len = lenArray[0];
|
||||
return new Uint8Array(Module["HEAP8"].buffer, this.ptr + 4, len);
|
||||
};
|
||||
|
||||
function StringPointer(pointer, len) {
|
||||
this.ptr = pointer;
|
||||
this.length = len;
|
||||
}
|
||||
StringPointer.prototype.free = function() {
|
||||
if (0 !== this.ptr)
|
||||
Module["_free"](this.ptr);
|
||||
};
|
||||
|
||||
String.prototype.toUtf8Pointer = function(isNoEndNull) {
|
||||
var tmp = this.toUtf8(isNoEndNull, true);
|
||||
var pointer = Module["_malloc"](tmp.length);
|
||||
if (0 == pointer)
|
||||
return null;
|
||||
|
||||
Module["HEAP8"].set(tmp, pointer);
|
||||
return new StringPointer(pointer,tmp.length);
|
||||
};
|
||||
|
||||
function typedArrayToMemory(data)
|
||||
{
|
||||
var pointer = Module["_malloc"](data.length);
|
||||
Module["HEAP8"].set(data, langBuffer);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
function Engine()
|
||||
{
|
||||
this.isInit = false;
|
||||
this.waitResolvers = [];
|
||||
}
|
||||
|
||||
Engine.prototype.onLoad = function()
|
||||
{
|
||||
this.isInit = true;
|
||||
|
||||
for (let i = 0, len = this.waitResolvers.length; i < len; i++)
|
||||
this.waitResolvers[i]();
|
||||
|
||||
this.waitResolvers = [];
|
||||
};
|
||||
|
||||
Engine.prototype.init = async function()
|
||||
{
|
||||
if (this.isInit)
|
||||
return;
|
||||
|
||||
return new Promise(resolve => (function(){
|
||||
window.CryptoJS.waitResolvers.push(resolve);
|
||||
})());
|
||||
};
|
||||
|
||||
// SIGN
|
||||
Engine.prototype.generateKeys = async function(alg, password, salt)
|
||||
{
|
||||
await this.init();
|
||||
|
||||
if (!salt)
|
||||
salt = window.UtilsJS.toBase64(window.UtilsJS.random(32));
|
||||
|
||||
let algPtr = "ed25519".toUtf8Pointer();
|
||||
let passwordPtr = password.toUtf8Pointer();
|
||||
let saltPtr = salt.toUtf8Pointer();
|
||||
|
||||
let keys = Module["_Crypto_CreateKeys"](algPtr.ptr, passwordPtr.ptr, saltPtr.ptr);
|
||||
|
||||
algPtr.free();
|
||||
passwordPtr.free();
|
||||
saltPtr.free();
|
||||
|
||||
if (keys === 0)
|
||||
return null;
|
||||
|
||||
let heap = Module["HEAP8"];
|
||||
|
||||
let currentStart = keys;
|
||||
let currentEnd = currentStart;
|
||||
while (heap[currentEnd] != 0)
|
||||
currentEnd++;
|
||||
let publicKey = "".fromUtf8(heap, currentStart, currentEnd - currentStart);
|
||||
|
||||
currentStart = currentEnd + 1;
|
||||
currentEnd = currentStart;
|
||||
while (heap[currentEnd] != 0)
|
||||
currentEnd++;
|
||||
let privateKey = "".fromUtf8(heap, currentStart, currentEnd - currentStart);
|
||||
|
||||
Module["_Crypto_Free"](keys);
|
||||
|
||||
return {
|
||||
"salt": salt,
|
||||
"privateKey": privateKey,
|
||||
"publicKey": publicKey
|
||||
};
|
||||
};
|
||||
|
||||
Engine.prototype.changePassword = async function(privateKey, passwordOld, passwordNew, salt)
|
||||
{
|
||||
await this.init();
|
||||
|
||||
let privateKeyPtr = privateKey.toUtf8Pointer();
|
||||
let passwordOldPtr = passwordOld.toUtf8Pointer();
|
||||
let passwordNewPtr = passwordNew.toUtf8Pointer();
|
||||
let saltPtr = salt.toUtf8Pointer();
|
||||
|
||||
let privateKeyEnc = Module["_Crypto_ChangePassword"](privateKeyPtr.ptr, passwordOldPtr.ptr, passwordNewPtr.ptr, saltPtr.ptr);
|
||||
|
||||
privateKeyPtr.free();
|
||||
passwordOldPtr.free();
|
||||
passwordNewPtr.free();
|
||||
saltPtr.free();
|
||||
|
||||
if (privateKeyEnc === 0)
|
||||
return null;
|
||||
|
||||
let heap = Module["HEAP8"];
|
||||
|
||||
let currentStart = privateKeyEnc;
|
||||
let currentEnd = currentStart;
|
||||
while (heap[currentEnd] != 0)
|
||||
currentEnd++;
|
||||
|
||||
let privateKeyString = "".fromUtf8(heap, currentStart, currentEnd - currentStart);
|
||||
|
||||
Module["_Crypto_Free"](privateKeyEnc);
|
||||
return privateKeyString;
|
||||
};
|
||||
|
||||
Engine.prototype.sign = async function(privateKey, password, salt, xml)
|
||||
{
|
||||
await this.init();
|
||||
|
||||
let privateKeyPtr = privateKey.toUtf8Pointer();
|
||||
let passwordPtr = password.toUtf8Pointer();
|
||||
let saltPtr = salt.toUtf8Pointer();
|
||||
let xmlPtr = xml.toUtf8Pointer();
|
||||
|
||||
let signData = Module["_Crypto_Sign"](privateKeyPtr.ptr, passwordPtr.ptr, saltPtr.ptr, xmlPtr.ptr, xmlPtr.length);
|
||||
|
||||
privateKeyPtr.free();
|
||||
passwordPtr.free();
|
||||
saltPtr.free();
|
||||
xmlPtr.free();
|
||||
|
||||
if (signData === 0)
|
||||
return null;
|
||||
|
||||
let heap = Module["HEAP8"];
|
||||
|
||||
let currentStart = signData;
|
||||
let currentEnd = currentStart;
|
||||
while (heap[currentEnd] != 0)
|
||||
currentEnd++;
|
||||
|
||||
let signString = "".fromUtf8(heap, currentStart, currentEnd - currentStart);
|
||||
|
||||
Module["_Crypto_Free"](signData);
|
||||
return signString;
|
||||
};
|
||||
|
||||
// ENCRYPT
|
||||
Engine.prototype.decrypt = async function(privateKeyEnc, password, salt, data)
|
||||
{
|
||||
await this.init();
|
||||
|
||||
let privateKeyEncPtr = privateKeyEnc.toUtf8Pointer();
|
||||
let passwordPtr = password.toUtf8Pointer();
|
||||
let saltPtr = salt.toUtf8Pointer();
|
||||
|
||||
let dataPtr = typedArrayToMemory(data);
|
||||
|
||||
let decryptData = Module["_Crypto_Decrypt"](privateKeyEncPtr, passwordPtr, saltPtr.ptr, dataPtr, data.length);
|
||||
let memoryData = new CMemoryData(decryptData);
|
||||
|
||||
privateKeyEncPtr.free();
|
||||
passwordPtr.free();
|
||||
saltPtr.free();
|
||||
|
||||
Module["_free"](dataPtr);
|
||||
|
||||
return memoryData;
|
||||
};
|
||||
|
||||
Engine.prototype.encrypt = async function(publicKey, data)
|
||||
{
|
||||
await this.init();
|
||||
|
||||
let publicKeyEncPtr = publicKey.toUtf8Pointer();
|
||||
let dataPtr = typedArrayToMemory(data);
|
||||
|
||||
let encryptData = Module["_Crypto_Encrypt"](publicKeyEncPtr, dataPtr, data.length);
|
||||
let memoryData = new CMemoryData(decryptData);
|
||||
|
||||
publicKeyEncPtr.free();
|
||||
Module["_free"](dataPtr);
|
||||
|
||||
return memoryData;
|
||||
};
|
||||
|
||||
window.cryptoJS = new Engine();
|
||||
|
||||
//module
|
||||
|
||||
});
|
||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 999 B |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
@ -0,0 +1,43 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "ONLYOFFICE Keychain",
|
||||
"version": "1.0",
|
||||
"permissions": ["tabs", "scripting"],
|
||||
"icons": {
|
||||
"16": "icons/icon16.png",
|
||||
"32": "icons/icon32.png",
|
||||
"48": "icons/icon48.png",
|
||||
"64": "icons/icon64.png",
|
||||
"128": "icons/icon128.png"
|
||||
},
|
||||
"background": {
|
||||
"service_worker": "background.js"
|
||||
},
|
||||
"host_permissions": ["<all_urls>"],
|
||||
"action": {
|
||||
"default_icon": {
|
||||
"16": "icons/icon16.png",
|
||||
"32": "icons/icon32.png",
|
||||
"48": "icons/icon48.png",
|
||||
"64": "icons/icon64.png",
|
||||
"128": "icons/icon128.png"
|
||||
},
|
||||
"default_popup": "popup.html"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["content.js"]
|
||||
}
|
||||
],
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": ["engine.wasm"],
|
||||
"matches": ["<all_urls>"]
|
||||
}
|
||||
],
|
||||
"content_security_policy": {
|
||||
"extension_pages" : "script-src 'self' 'wasm-unsafe-eval'; object-src 'self'"
|
||||
}
|
||||
}
|
||||
|
||||
14
DesktopEditor/xmlsec/src/wasm/extension/extension/popup.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Popup</title>
|
||||
</head>
|
||||
<body>
|
||||
<h2>Сообщение получено!</h2>
|
||||
<div id="message"></div>
|
||||
<button id="testButton">buttton</button>
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
17
DesktopEditor/xmlsec/src/wasm/extension/extension/popup.js
Normal file
@ -0,0 +1,17 @@
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
document.getElementById("message").innerText = JSON.stringify(message);
|
||||
});
|
||||
|
||||
document.addEventListener("DOMContentLoaded", (event) => {
|
||||
|
||||
document.getElementById("testButton").onclick = function(e) {
|
||||
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
|
||||
if (tabs.length === 0) return;
|
||||
|
||||
chrome.tabs.sendMessage(tabs[0].id, { action: "popupMessage", text: "Привет от Popup!" }, (response) => {
|
||||
console.log("Ответ от контентного скрипта:", response);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
@ -0,0 +1,22 @@
|
||||
import sys
|
||||
sys.path.append("../../../../../../../build_tools/scripts")
|
||||
import base
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Generate background script for debug')
|
||||
|
||||
parser.add_argument("--wasm", action="store_true")
|
||||
args = parser.parse_args()
|
||||
|
||||
content = base.readFile("./utils.js") + "\n\n"
|
||||
|
||||
if args.wasm:
|
||||
content = content + "\n\n" + base.readFile("./../deploy/engine.js")
|
||||
base.copy_file("./../deploy/engine.wasm", "./engine.wasm")
|
||||
else:
|
||||
content = content + "\n\n" + base.readFile("./engine.js")
|
||||
|
||||
content = content + "\n\n" + base.readFile("./background_base.js")
|
||||
|
||||
base.delete_file("./background.js")
|
||||
base.writeFile("./background.js", content)
|
||||
334
DesktopEditor/xmlsec/src/wasm/extension/extension/utils.js
Normal file
@ -0,0 +1,334 @@
|
||||
(function(window, undefined) {
|
||||
if (undefined !== String.prototype.fromUtf8 && undefined !== String.prototype.toUtf8)
|
||||
return;
|
||||
|
||||
var STRING_UTF8_BUFFER_LENGTH = 1024;
|
||||
var STRING_UTF8_BUFFER = new ArrayBuffer(STRING_UTF8_BUFFER_LENGTH);
|
||||
|
||||
/**
|
||||
* Read string from utf8
|
||||
* @param {Uint8Array} buffer
|
||||
* @param {number} [start=0]
|
||||
* @param {number} [len]
|
||||
* @returns {string}
|
||||
*/
|
||||
String.prototype.fromUtf8 = function(buffer, start, len) {
|
||||
if (undefined === start)
|
||||
start = 0;
|
||||
if (undefined === len)
|
||||
len = buffer.length - start;
|
||||
|
||||
var result = "";
|
||||
var index = start;
|
||||
var end = start + len;
|
||||
while (index < end) {
|
||||
var u0 = buffer[index++];
|
||||
if (!(u0 & 128)) {
|
||||
result += String.fromCharCode(u0);
|
||||
continue;
|
||||
}
|
||||
var u1 = buffer[index++] & 63;
|
||||
if ((u0 & 224) == 192) {
|
||||
result += String.fromCharCode((u0 & 31) << 6 | u1);
|
||||
continue;
|
||||
}
|
||||
var u2 = buffer[index++] & 63;
|
||||
if ((u0 & 240) == 224)
|
||||
u0 = (u0 & 15) << 12 | u1 << 6 | u2;
|
||||
else
|
||||
u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | buffer[index++] & 63;
|
||||
if (u0 < 65536)
|
||||
result += String.fromCharCode(u0);
|
||||
else {
|
||||
var ch = u0 - 65536;
|
||||
result += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert string to utf8 array
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
String.prototype.toUtf8 = function(isNoEndNull, isUseBuffer) {
|
||||
var inputLen = this.length;
|
||||
var testLen = 6 * inputLen + 1;
|
||||
var tmpStrings = (isUseBuffer && testLen < STRING_UTF8_BUFFER_LENGTH) ? STRING_UTF8_BUFFER : new ArrayBuffer(testLen);
|
||||
|
||||
var code = 0;
|
||||
var index = 0;
|
||||
|
||||
var outputIndex = 0;
|
||||
var outputDataTmp = new Uint8Array(tmpStrings);
|
||||
var outputData = outputDataTmp;
|
||||
|
||||
while (index < inputLen) {
|
||||
code = this.charCodeAt(index++);
|
||||
if (code >= 0xD800 && code <= 0xDFFF && index < inputLen)
|
||||
code = 0x10000 + (((code & 0x3FF) << 10) | (0x03FF & this.charCodeAt(index++)));
|
||||
|
||||
if (code < 0x80)
|
||||
outputData[outputIndex++] = code;
|
||||
else if (code < 0x0800) {
|
||||
outputData[outputIndex++] = 0xC0 | (code >> 6);
|
||||
outputData[outputIndex++] = 0x80 | (code & 0x3F);
|
||||
} else if (code < 0x10000) {
|
||||
outputData[outputIndex++] = 0xE0 | (code >> 12);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 6) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | (code & 0x3F);
|
||||
} else if (code < 0x1FFFFF) {
|
||||
outputData[outputIndex++] = 0xF0 | (code >> 18);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 12) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 6) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | (code & 0x3F);
|
||||
} else if (code < 0x3FFFFFF) {
|
||||
outputData[outputIndex++] = 0xF8 | (code >> 24);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 18) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 12) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 6) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | (code & 0x3F);
|
||||
} else if (code < 0x7FFFFFFF) {
|
||||
outputData[outputIndex++] = 0xFC | (code >> 30);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 24) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 18) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 12) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 6) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | (code & 0x3F);
|
||||
}
|
||||
}
|
||||
|
||||
if (isNoEndNull !== true)
|
||||
outputData[outputIndex++] = 0;
|
||||
|
||||
return new Uint8Array(tmpStrings,0,outputIndex);
|
||||
};
|
||||
|
||||
window.UtilsJS = {};
|
||||
|
||||
var charA = "A".charCodeAt(0);
|
||||
var charZ = "Z".charCodeAt(0);
|
||||
var chara = "a".charCodeAt(0);
|
||||
var charz = "z".charCodeAt(0);
|
||||
var char0 = "0".charCodeAt(0);
|
||||
var char9 = "9".charCodeAt(0);
|
||||
var charp = "+".charCodeAt(0);
|
||||
var chars = "/".charCodeAt(0);
|
||||
var char_break = ";".charCodeAt(0);
|
||||
|
||||
function decodeBase64Char(ch)
|
||||
{
|
||||
if (ch >= charA && ch <= charZ)
|
||||
return ch - charA + 0;
|
||||
if (ch >= chara && ch <= charz)
|
||||
return ch - chara + 26;
|
||||
if (ch >= char0 && ch <= char9)
|
||||
return ch - char0 + 52;
|
||||
if (ch == charp)
|
||||
return 62;
|
||||
if (ch == chars)
|
||||
return 63;
|
||||
return -1;
|
||||
}
|
||||
|
||||
var stringBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var arrayBase64 = [];
|
||||
for (var index64 = 0; index64 < stringBase64.length; index64++)
|
||||
{
|
||||
arrayBase64.push(stringBase64.charAt(index64));
|
||||
}
|
||||
|
||||
window.UtilsJS.Base64 = {};
|
||||
|
||||
window.UtilsJS.Base64.decodeData = function(input, input_offset, input_len, output, output_offset)
|
||||
{
|
||||
var isBase64 = typeof input === "string";
|
||||
if (undefined === input_len) input_len = input.length;
|
||||
var writeIndex = (undefined === output_offset) ? 0 : output_offset;
|
||||
var index = (undefined === input_offset) ? 0 : input_offset;
|
||||
|
||||
while (index < input_len)
|
||||
{
|
||||
var dwCurr = 0;
|
||||
var i;
|
||||
var nBits = 0;
|
||||
for (i=0; i<4; i++)
|
||||
{
|
||||
if (index >= input_len)
|
||||
break;
|
||||
var nCh = decodeBase64Char(isBase64 ? input.charCodeAt(index) : input[index]);
|
||||
index++;
|
||||
if (nCh == -1)
|
||||
{
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
dwCurr <<= 6;
|
||||
dwCurr |= nCh;
|
||||
nBits += 6;
|
||||
}
|
||||
|
||||
dwCurr <<= 24-nBits;
|
||||
for (i=0; i<(nBits>>3); i++)
|
||||
{
|
||||
output[writeIndex++] = ((dwCurr & 0x00ff0000) >>> 16);
|
||||
dwCurr <<= 8;
|
||||
}
|
||||
}
|
||||
return writeIndex;
|
||||
};
|
||||
|
||||
window.UtilsJS.Base64.decode = function(input, isUsePrefix, dstlen, offset)
|
||||
{
|
||||
var srcLen = input.length;
|
||||
var index = (undefined === offset) ? 0 : offset;
|
||||
var dstLen = (undefined === dstlen) ? srcLen : dstlen;
|
||||
|
||||
var isBase64 = typeof input === "string";
|
||||
|
||||
if (isUsePrefix && isBase64)
|
||||
{
|
||||
dstLen = 0;
|
||||
var maxLen = Math.max(11, srcLen); // > 4 Gb
|
||||
while (index < maxLen)
|
||||
{
|
||||
var c = input.charCodeAt(index++);
|
||||
if (c == char_break)
|
||||
break;
|
||||
|
||||
dstLen *= 10;
|
||||
dstLen += (c - char0);
|
||||
}
|
||||
|
||||
if (index == maxLen)
|
||||
{
|
||||
index = 0;
|
||||
dstLen = srcLen;
|
||||
}
|
||||
}
|
||||
|
||||
var dst = new Uint8Array(dstLen);
|
||||
var writeIndex = window.AscCommon.Base64.decodeData(input, index, srcLen, dst, 0);
|
||||
|
||||
if (writeIndex == dstLen)
|
||||
return dst;
|
||||
|
||||
return new Uint8Array(dst.buffer, 0, writeIndex);
|
||||
};
|
||||
|
||||
window.UtilsJS.Base64.encode = function(input, offset, length, isUsePrefix)
|
||||
{
|
||||
var srcLen = (undefined === length) ? input.length : length;
|
||||
var index = (undefined === offset) ? 0 : offset;
|
||||
|
||||
var len1 = (((srcLen / 3) >> 0) * 4);
|
||||
var len2 = (len1 / 76) >> 0;
|
||||
var len3 = 19;
|
||||
var dstArray = [];
|
||||
|
||||
var sTemp = "";
|
||||
var dwCurr = 0;
|
||||
for (var i = 0; i <= len2; i++)
|
||||
{
|
||||
if (i == len2)
|
||||
len3 = ((len1 % 76) / 4) >> 0;
|
||||
|
||||
for (var j = 0; j < len3; j++)
|
||||
{
|
||||
dwCurr = 0;
|
||||
for (var n = 0; n < 3; n++)
|
||||
{
|
||||
dwCurr |= input[index++];
|
||||
dwCurr <<= 8;
|
||||
}
|
||||
|
||||
sTemp = "";
|
||||
for (var k = 0; k < 4; k++)
|
||||
{
|
||||
var b = (dwCurr >>> 26) & 0xFF;
|
||||
sTemp += arrayBase64[b];
|
||||
dwCurr <<= 6;
|
||||
dwCurr &= 0xFFFFFFFF;
|
||||
}
|
||||
dstArray.push(sTemp);
|
||||
}
|
||||
}
|
||||
len2 = (srcLen % 3 != 0) ? (srcLen % 3 + 1) : 0;
|
||||
if (len2)
|
||||
{
|
||||
dwCurr = 0;
|
||||
for (var n = 0; n < 3; n++)
|
||||
{
|
||||
if (n < (srcLen % 3))
|
||||
dwCurr |= input[index++];
|
||||
dwCurr <<= 8;
|
||||
}
|
||||
|
||||
sTemp = "";
|
||||
for (var k = 0; k < len2; k++)
|
||||
{
|
||||
var b = (dwCurr >>> 26) & 0xFF;
|
||||
sTemp += arrayBase64[b];
|
||||
dwCurr <<= 6;
|
||||
}
|
||||
|
||||
len3 = (len2 != 0) ? 4 - len2 : 0;
|
||||
for (var j = 0; j < len3; j++)
|
||||
{
|
||||
sTemp += '=';
|
||||
}
|
||||
dstArray.push(sTemp);
|
||||
}
|
||||
|
||||
return isUsePrefix ? (("" + srcLen + ";") + dstArray.join("")) : dstArray.join("");
|
||||
};
|
||||
|
||||
window.UtilsJS.Hex = {};
|
||||
|
||||
window.UtilsJS.Hex.decode = function(input)
|
||||
{
|
||||
var hexToByte = function(c) {
|
||||
if (c >= 48 && c <= 57) return c - 48; // 0..9
|
||||
if (c >= 97 && c <= 102) return c - 87;
|
||||
if (c >= 65 && c <= 70) return c - 55;
|
||||
return 0;
|
||||
};
|
||||
|
||||
var len = input.length;
|
||||
if (len & 0x01) len -= 1;
|
||||
var result = new Uint8Array(len >> 1);
|
||||
var resIndex = 0;
|
||||
for (var i = 0; i < len; i += 2)
|
||||
{
|
||||
result[resIndex++] = hexToByte(input.charCodeAt(i)) << 4 | hexToByte(input.charCodeAt(i + 1));
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
window.UtilsJS.Hex.encode = function(input, isUpperCase)
|
||||
{
|
||||
var byteToHex = new Array(256);
|
||||
for (var i = 0; i < 16; i++)
|
||||
byteToHex[i] = "0" + (isUpperCase ? i.toString(16).toUpperCase() : i.toString(16));
|
||||
for (var i = 16; i < 256; i++)
|
||||
byteToHex[i] = isUpperCase ? i.toString(16).toUpperCase() : i.toString(16);
|
||||
|
||||
var result = "";
|
||||
for (var i = 0, len = input.length; i < len; i++)
|
||||
result += byteToHex[input[i]];
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
window.UtilsJS.random = function(length) {
|
||||
let byteArray = new Uint8Array(length);
|
||||
let engine = window.crypto || window.msCrypto;
|
||||
if (engine)
|
||||
engine.getRandomValues(byteArray);
|
||||
else {
|
||||
for (let i = 0; i < length; i++)
|
||||
byteArray[i] = (Math.random() * 256) >> 0;
|
||||
}
|
||||
return byteArray;
|
||||
};
|
||||
})(self);
|
||||
24
DesktopEditor/xmlsec/src/wasm/extension/extension2/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@ -0,0 +1,6 @@
|
||||
# Onlyoffice keychain extension
|
||||
|
||||
To run the dev build:
|
||||
1. Go to the extension2 folder
|
||||
2. Run ```npm run dev```
|
||||
3. Upload the dist folder as an unpacked extension to the browser
|
||||
@ -0,0 +1,26 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
import tseslint from 'typescript-eslint'
|
||||
import { globalIgnores } from 'eslint/config'
|
||||
|
||||
export default tseslint.config([
|
||||
globalIgnores(['dist']),
|
||||
{
|
||||
files: ['**/*.{ts,tsx}'],
|
||||
extends: [
|
||||
js.configs.recommended,
|
||||
tseslint.configs.recommended,
|
||||
reactHooks.configs['recommended-latest'],
|
||||
reactRefresh.configs.vite,
|
||||
],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
},
|
||||
rules: {
|
||||
'@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
||||
}
|
||||
},
|
||||
])
|
||||
@ -0,0 +1,34 @@
|
||||
import { defineManifest } from '@crxjs/vite-plugin';
|
||||
import pkg from "./package.json";
|
||||
|
||||
export default defineManifest({
|
||||
manifest_version: 3,
|
||||
name: pkg.name,
|
||||
version: pkg.version,
|
||||
icons: {
|
||||
16: "public/icons/icon16.png",
|
||||
32: "public/icons/icon32.png",
|
||||
48: "public/icons/icon48.png",
|
||||
64: "public/icons/icon64.png",
|
||||
128: "public/icons/icon128.png",
|
||||
},
|
||||
action: {
|
||||
default_icon: {
|
||||
16: "public/icons/icon16.png",
|
||||
32: "public/icons/icon32.png",
|
||||
48: "public/icons/icon48.png",
|
||||
64: "public/icons/icon64.png",
|
||||
128: "public/icons/icon128.png",
|
||||
},
|
||||
default_popup: 'src/popup/index.html',
|
||||
},
|
||||
content_scripts: [{
|
||||
js: ['src/content/content.ts'],
|
||||
matches: ['<all_urls>'],
|
||||
run_at: "document_end"
|
||||
}],
|
||||
background: {
|
||||
service_worker: "src/background/background.ts"
|
||||
},
|
||||
permissions: ["storage"],
|
||||
});
|
||||
2995
DesktopEditor/xmlsec/src/wasm/extension/extension2/package-lock.json
generated
Normal file
@ -0,0 +1,33 @@
|
||||
{
|
||||
"name": "ONLYOFFICE Keychain",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"react": "^19.1.1",
|
||||
"react-dom": "^19.1.1",
|
||||
"webextension-polyfill": "^0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@crxjs/vite-plugin": "^2.2.0",
|
||||
"@eslint/js": "^9.33.0",
|
||||
"@types/react": "^19.1.10",
|
||||
"@types/react-dom": "^19.1.7",
|
||||
"@types/webextension-polyfill": "^0.12.3",
|
||||
"@vitejs/plugin-react": "^5.0.0",
|
||||
"chrome-types": "^0.1.375",
|
||||
"eslint": "^9.33.0",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.20",
|
||||
"globals": "^16.3.0",
|
||||
"typescript": "~5.8.3",
|
||||
"typescript-eslint": "^8.39.1",
|
||||
"vite": "^7.1.5"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 29 KiB |
|
After Width: | Height: | Size: 999 B |
|
After Width: | Height: | Size: 2.9 KiB |
|
After Width: | Height: | Size: 5.1 KiB |
|
After Width: | Height: | Size: 8.4 KiB |
@ -0,0 +1,5 @@
|
||||
import {initCheckOpenedPopup} from "./utils.ts";
|
||||
import initTaskManager from "./task-manager/task-manager.ts";
|
||||
|
||||
initTaskManager();
|
||||
initCheckOpenedPopup();
|
||||
@ -0,0 +1,31 @@
|
||||
import browser from "webextension-polyfill";
|
||||
import {messageTypes} from "../../common/message-const.ts";
|
||||
import {generatePopupKeys, selectSignKeys, signData, verifyData} from "./tasks.ts";
|
||||
import {isBackgroundMessageType} from "../../common/message-types.ts";
|
||||
|
||||
const initTaskManager = () => {
|
||||
browser.runtime.onMessage.addListener((message: unknown) => {
|
||||
if (!isBackgroundMessageType(message)) {
|
||||
return false;
|
||||
}
|
||||
const data = message.data;
|
||||
switch (data.type) {
|
||||
case messageTypes.GENERATE_KEYS: {
|
||||
return generatePopupKeys();
|
||||
}
|
||||
case messageTypes.SELECT_SIGN_KEYS: {
|
||||
return selectSignKeys();
|
||||
}
|
||||
case messageTypes.SIGN_DATA: {
|
||||
return signData(data.base64Data, data.guid);
|
||||
}
|
||||
case messageTypes.VERIFY_DATA: {
|
||||
return verifyData(data.base64Data, data.base64Signature, data.guid);
|
||||
}
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default initTaskManager;
|
||||
@ -0,0 +1,43 @@
|
||||
import {openPopup} from "../utils.ts";
|
||||
import {messageTypes} from "../../common/message-const.ts";
|
||||
import {sendToPopup} from "../../content/messenger.ts";
|
||||
// @ts-ignore
|
||||
import {StorageManager} from "../../common/storage.ts";
|
||||
import {ab2base64, base642ui} from "../../common/utils.ts";
|
||||
import getCrypto from "../../common/crypto.ts";
|
||||
|
||||
export const generatePopupKeys = async () => {
|
||||
await openPopup();
|
||||
await sendToPopup({type: messageTypes.WAIT_ENTER_PASSWORD});
|
||||
return true;
|
||||
}
|
||||
|
||||
export const selectSignKeys = async () => {
|
||||
await openPopup();
|
||||
await sendToPopup({type: messageTypes.WAIT_ENTER_PASSWORD});
|
||||
return await sendToPopup({type: messageTypes.SELECT_SIGN_KEYS});
|
||||
};
|
||||
|
||||
export const signData = async (base64Data: string , guid: string) => {
|
||||
const keyStorage = new StorageManager();
|
||||
await keyStorage.loadKeysFromStorage();
|
||||
const keyPair = keyStorage.getKeyByGuid(guid);
|
||||
if (!keyPair) {
|
||||
throw new Error("Key pair is not found");
|
||||
}
|
||||
const data = base642ui(base64Data);
|
||||
const signData = await keyPair.sign(data);
|
||||
return ab2base64(signData);
|
||||
}
|
||||
|
||||
export const verifyData = async (base64Data: string, base64Signature: string, guid: string) => {
|
||||
const keyStorage = new StorageManager();
|
||||
await keyStorage.loadKeysFromStorage();
|
||||
const keyPair = keyStorage.getKeyByGuid(guid);
|
||||
if (!keyPair) {
|
||||
throw new Error("Key pair is not found");
|
||||
}
|
||||
const data = base642ui(base64Data);
|
||||
const signature = base642ui(base64Signature);
|
||||
return await keyPair.verify(signature, data);
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
import browser from "webextension-polyfill";
|
||||
import getCrypto from "../common/crypto.ts";
|
||||
|
||||
export const initCheckOpenedPopup = () => {
|
||||
browser.runtime.onConnect.addListener((port) => {
|
||||
if (port.name === "popup") {
|
||||
browser.storage.session.set({isOpenPopup: true});
|
||||
|
||||
port.onDisconnect.addListener(() => {
|
||||
browser.storage.session.set({isOpenPopup: false});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const isOpenedPopup = async () => {
|
||||
const isOpenPopupItem = await browser.storage.session.get("isOpenPopup");
|
||||
return !!(isOpenPopupItem && isOpenPopupItem.isOpenPopup);
|
||||
};
|
||||
|
||||
const waitClosingPopup = async () => {
|
||||
const isOpenPopup = await isOpenedPopup();
|
||||
if (!isOpenPopup) {
|
||||
return true;
|
||||
}
|
||||
return new Promise(resolve => {
|
||||
browser.storage.session.onChanged.addListener(function handler(change) {
|
||||
if (change.isOpenPopup && !change.isOpenPopup.newValue) {
|
||||
browser.storage.session.onChanged.removeListener(handler);
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const openPopup = async () => {
|
||||
await waitClosingPopup();
|
||||
await browser.action.openPopup();
|
||||
return new Promise(resolve => {
|
||||
browser.storage.session.onChanged.addListener(function handler(change) {
|
||||
if (change.isOpenPopup && change.isOpenPopup.newValue) {
|
||||
browser.storage.session.onChanged.removeListener(handler);
|
||||
resolve(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
type TGuid = `{${string}-${string}-${string}-${string}-${string}}`
|
||||
export const getGUID = (): TGuid => {
|
||||
const crypto = getCrypto();
|
||||
return `{${crypto.randomUUID()}}`;
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
import {Key, KeyPair, KeyUsages, PrivateKey, PublicKey, SymmetricKey} from "./keys/keys.ts";
|
||||
import {
|
||||
type DigestType,
|
||||
digestTypes,
|
||||
type ExportKeyFormat,
|
||||
exportKeyFormats,
|
||||
type KeyGenParams,
|
||||
type KeyParams
|
||||
} from "./keys/key-types.ts";
|
||||
import {AesGcmGenParams, type AesGcmParams} from "./keys/params.ts";
|
||||
|
||||
const pbkdf2Parameters = {
|
||||
iterations: 150000,
|
||||
hash: digestTypes.SHA256,
|
||||
saltLength: 16
|
||||
};
|
||||
// type DecryptKey = PrivateKey | SymmetricKey;
|
||||
// type EncryptKey = PublicKey | SymmetricKey;
|
||||
abstract class CCryptoBase {
|
||||
abstract sign(key: PrivateKey, data: ArrayBuffer): Promise<ArrayBuffer>;
|
||||
abstract digest(algorithm: DigestType, data: ArrayBuffer): Promise<ArrayBuffer>;
|
||||
abstract verify(key: PublicKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean>;
|
||||
// abstract decrypt(key: DecryptKey, data: ArrayBuffer): Promise<ArrayBuffer>;
|
||||
// abstract encrypt(key: EncryptKey, data: ArrayBuffer): Promise<ArrayBuffer>;
|
||||
abstract generateKey(params: KeyGenParams, keyUsage: KeyUsages): Promise<SymmetricKey | KeyPair>;
|
||||
abstract wrapKey(format: ExportKeyFormat, key: Key, masterPassword: ArrayBuffer, salt: ArrayBuffer, aesParams: AesGcmParams, keyUsage: KeyUsages): Promise<ArrayBuffer>
|
||||
abstract unwrapKey(format: ExportKeyFormat, key: ArrayBuffer, masterPassword: ArrayBuffer, salt: ArrayBuffer, aesParams: AesGcmParams, keyParams: KeyParams, keyUsage: KeyUsages): Promise<ArrayBuffer>
|
||||
abstract getRandomValues(length: number): ArrayBuffer;
|
||||
abstract randomUUID(): string;
|
||||
}
|
||||
class CWebCrypto extends CCryptoBase {
|
||||
crypto = globalThis.crypto;
|
||||
subtle = this.crypto.subtle;
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
getRandomValues(length: number) {
|
||||
const ui = new Uint8Array(length);
|
||||
return this.crypto.getRandomValues(ui);
|
||||
}
|
||||
async getAesCryptoKey(masterPassword: ArrayBuffer, salt: ArrayBuffer) {
|
||||
const pwKey = await this.subtle.importKey(
|
||||
'raw',
|
||||
masterPassword,
|
||||
{ name: 'PBKDF2' },
|
||||
false,
|
||||
['deriveKey']
|
||||
);
|
||||
return this.subtle.deriveKey(
|
||||
{
|
||||
name: 'PBKDF2',
|
||||
salt: salt,
|
||||
iterations: pbkdf2Parameters.iterations,
|
||||
hash: pbkdf2Parameters.hash
|
||||
},
|
||||
pwKey,
|
||||
new AesGcmGenParams(),
|
||||
false,
|
||||
['wrapKey', 'unwrapKey']
|
||||
);
|
||||
};
|
||||
async wrapKey(format: ExportKeyFormat, key: Key, masterPassword: ArrayBuffer, salt: ArrayBuffer, aesParams: AesGcmParams, keyUsage: KeyUsages) {
|
||||
const cryptoAesKey = await this.getAesCryptoKey(masterPassword, salt);
|
||||
const importKey = await this.getCryptoKeyFromWrapper(key, keyUsage);
|
||||
return this.subtle.wrapKey(format, importKey, cryptoAesKey, aesParams);
|
||||
}
|
||||
async unwrapKey(format: ExportKeyFormat, key: ArrayBuffer, masterPassword: ArrayBuffer, salt: ArrayBuffer, aesParams: AesGcmParams, keyParams: KeyParams, keyUsages: KeyUsages) {
|
||||
const cryptoAesKey = await this.getAesCryptoKey(masterPassword, salt);
|
||||
const cryptoKey = await this.subtle.unwrapKey(format, key, cryptoAesKey, aesParams, keyParams, true, /*this.getKeyUsages(keyUsages)*/["sign"]);
|
||||
return this.subtle.exportKey(format, cryptoKey);
|
||||
}
|
||||
async getCryptoKeyFromWrapper(key: Key, keyUsage: KeyUsages) {
|
||||
return this.subtle.importKey(key.exportFormat, key.key, key.params, true, this.getKeyUsages(keyUsage, key));
|
||||
}
|
||||
getKeyUsages({isEncrypt, isSign}: KeyUsages, key?: Key) {
|
||||
const keyUsages: KeyUsage[] = [];
|
||||
if (isEncrypt) {
|
||||
if (key instanceof PrivateKey) {
|
||||
keyUsages.push("decrypt");
|
||||
} else if (key instanceof PublicKey) {
|
||||
keyUsages.push("encrypt");
|
||||
} else {
|
||||
keyUsages.push("encrypt", "decrypt");
|
||||
}
|
||||
}
|
||||
if (isSign) {
|
||||
if (key instanceof PrivateKey) {
|
||||
keyUsages.push("sign");
|
||||
} else if (key instanceof PublicKey) {
|
||||
keyUsages.push("verify");
|
||||
} else {
|
||||
keyUsages.push("sign", "verify");
|
||||
}
|
||||
}
|
||||
return keyUsages;
|
||||
}
|
||||
async sign(key: PrivateKey, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||
const cryptoKey = await this.getCryptoKeyFromWrapper(key, new KeyUsages(false, true));
|
||||
return this.subtle.sign(key.params, cryptoKey, data);
|
||||
}
|
||||
async digest(algorithm: DigestType, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||
return this.subtle.digest(algorithm, data);
|
||||
}
|
||||
async verify(key: PublicKey, signature: ArrayBuffer, data: ArrayBuffer): Promise<boolean> {
|
||||
const cryptoKey = await this.getCryptoKeyFromWrapper(key, new KeyUsages(false, true));
|
||||
return this.subtle.verify(key.params, cryptoKey, signature, data);
|
||||
}
|
||||
// async decrypt(key: DecryptKey, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||
// const cryptoKey = await this.getCryptoKeyFromWrapper(key);
|
||||
// return this.subtle.decrypt(cryptoKey);
|
||||
// }
|
||||
// async encrypt(key: EncryptKey, data: ArrayBuffer): Promise<ArrayBuffer> {
|
||||
// throw new Error("Method not implemented.");
|
||||
// }
|
||||
async generateKey(params: KeyGenParams, keyUsage: KeyUsages) {
|
||||
const cryptoKey = await this.subtle.generateKey(params, true, this.getKeyUsages(keyUsage));
|
||||
const importParams = params.getImportParams();
|
||||
if (("privateKey" in cryptoKey) && ("publicKey" in cryptoKey)) {
|
||||
const publicKeyBuffer = await this.subtle.exportKey(exportKeyFormats.spki, cryptoKey.publicKey);
|
||||
const publicKey = new PublicKey(publicKeyBuffer, importParams);
|
||||
const privateKeyBuffer = await this.subtle.exportKey(exportKeyFormats.pkcs8, cryptoKey.privateKey);
|
||||
const privateKey = new PrivateKey(privateKeyBuffer, importParams, this.getRandomValues(pbkdf2Parameters.saltLength));
|
||||
return new KeyPair(publicKey, privateKey, keyUsage);
|
||||
}
|
||||
const keyBuffer = await this.subtle.exportKey(exportKeyFormats.raw, cryptoKey);
|
||||
return new SymmetricKey(keyBuffer, importParams, keyUsage);
|
||||
};
|
||||
|
||||
randomUUID() {
|
||||
return this.crypto.randomUUID();
|
||||
}
|
||||
}
|
||||
|
||||
const getCrypto = () => {
|
||||
return new CWebCrypto();
|
||||
}
|
||||
|
||||
export default getCrypto;
|
||||
@ -0,0 +1,73 @@
|
||||
import {KeyStorage} from "key-storage";
|
||||
import {downloadBinary, selectBinary} from "./utils.ts";
|
||||
|
||||
export function StorageManager() {
|
||||
this.keyStorage = new KeyStorage();
|
||||
}
|
||||
StorageManager.prototype.getBinaryKeys = function () {
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
StorageManager.prototype.loadKeysFromStorage = function() {
|
||||
const oThis = this;
|
||||
return Promise.all([this.getMasterPassword(), this.getBinaryKeys()]).then(function ([masterPassword, binaryData]) {
|
||||
return oThis.keyStorage.import(binaryData, masterPassword);
|
||||
});
|
||||
}
|
||||
StorageManager.prototype.changeMasterPassword = function(newMasterPassword) {
|
||||
const oThis = this;
|
||||
return this.getMasterPassword().then(function (oldMasterPassword) {
|
||||
return oThis.keyStorage.changeMasterPassword(oldMasterPassword, newMasterPassword);
|
||||
});
|
||||
};
|
||||
StorageManager.prototype.getMasterPassword = function() {
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
StorageManager.prototype.writeKeys = function() {
|
||||
const oThis = this;
|
||||
return this.keyStorage.export().then(function (exportedKeys) {
|
||||
return oThis.setStorageKeys(exportedKeys);
|
||||
});
|
||||
}
|
||||
StorageManager.prototype.setStorageKeys = function (exportedKeys) {
|
||||
return Promise.resolve();
|
||||
};
|
||||
StorageManager.prototype.addNewKeys = function (keys) {
|
||||
this.keyStorage.addKeys(keys);
|
||||
return this.writeKeys();
|
||||
};
|
||||
StorageManager.prototype.deprecateKey = function (key) {
|
||||
key.setIsValid(false);
|
||||
return this.writeKeys();
|
||||
};
|
||||
|
||||
StorageManager.prototype.exportKeys = function () {
|
||||
return this.keyStorage.export().then(downloadBinary);
|
||||
};
|
||||
|
||||
StorageManager.prototype.importKeys = function (callback) {
|
||||
const oThis = this;
|
||||
return this.getMasterPassword().then(function (masterPassword) {
|
||||
selectBinary(function (file) {
|
||||
try {
|
||||
file.arrayBuffer().then(function (binaryData) {
|
||||
return oThis.keyStorage.import(binaryData, masterPassword);
|
||||
}).then(function (keyObjects) {
|
||||
return oThis.addNewKeys(keyObjects);
|
||||
}).then(function () {
|
||||
callback();
|
||||
});
|
||||
} catch (e) {
|
||||
}
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
StorageManager.prototype.generateKeys = function (params) {
|
||||
return this.keyStorage.generateKey(params);
|
||||
};
|
||||
StorageManager.prototype.getValidKeys = function () {
|
||||
return this.keyStorage.getValidKeys();
|
||||
};
|
||||
StorageManager.prototype.getKeyByGuid = function (guid) {
|
||||
return this.keyStorage.getKeyByGuid(guid);
|
||||
};
|
||||
@ -0,0 +1,108 @@
|
||||
import {
|
||||
AesGcmParams,
|
||||
AesImportParams,
|
||||
AesKeyGenParams, Ed25519ImportParams,
|
||||
Ed25519KeyGenParams,
|
||||
type RsaImportParams,
|
||||
RSAKeyGenParams
|
||||
} from "./params.ts";
|
||||
import {Key, KeyPair, PrivateKey, PublicKey} from "./keys.ts";
|
||||
export const exportKeyFormats = {
|
||||
pkcs8: "pkcs8",
|
||||
spki: "spki",
|
||||
raw: "raw"
|
||||
} as const;
|
||||
|
||||
export const algorithmTypes = {
|
||||
AES_GCM: "AES-GCM",
|
||||
AES_CTR: "AES-CTR",
|
||||
AES_CBC: "AES-CBC",
|
||||
AES_KW: "AES-KW",
|
||||
ED25519: "Ed25519",
|
||||
RSASSA_PKCS1_v1_5: "RSASSA-PKCS1-v1_5",
|
||||
RSA_PSS: "RSA-PSS",
|
||||
RSA_OAEP: "RSA-OAEP"
|
||||
} as const;
|
||||
|
||||
export const rsaTypes = {
|
||||
RSASSA_PKCS1_v1_5: algorithmTypes.RSASSA_PKCS1_v1_5,
|
||||
RSA_PSS: algorithmTypes.RSA_PSS,
|
||||
RSA_OAEP: algorithmTypes.RSA_OAEP,
|
||||
} as const;
|
||||
|
||||
export const aesTypes = {
|
||||
AES_GCM: algorithmTypes.AES_GCM,
|
||||
AES_CTR: algorithmTypes.AES_CTR,
|
||||
AES_CBC: algorithmTypes.AES_CBC,
|
||||
AES_KW: algorithmTypes.AES_KW,
|
||||
} as const;
|
||||
|
||||
export const digestTypes = {
|
||||
SHA1: "SHA-1",
|
||||
SHA256: "SHA-256",
|
||||
SHA384: "SHA-384",
|
||||
SHA512: "SHA-512",
|
||||
} as const;
|
||||
|
||||
export const keyTypes = {
|
||||
symmetric: "symmetric",
|
||||
pair: "pair"
|
||||
} as const;
|
||||
|
||||
export const pairKeyTypes = {
|
||||
private: "private",
|
||||
public: "public",
|
||||
} as const;
|
||||
|
||||
export const signAlgorithms = {
|
||||
ED25519: algorithmTypes.ED25519
|
||||
}
|
||||
|
||||
export const cryptAlgorithms = {
|
||||
...aesTypes,
|
||||
RSA_OAEP: algorithmTypes.RSA_OAEP
|
||||
}
|
||||
|
||||
export const isRSAJson = (obj: JSONKeyParams): obj is RsaJSONType => {
|
||||
const name = obj.name;
|
||||
return Object.values(rsaTypes).includes(name as RsaType);
|
||||
}
|
||||
export const isEd25519Json = (obj: JSONKeyParams): obj is Ed25519JSONParams => {
|
||||
const name = obj.name;
|
||||
return name === algorithmTypes.ED25519;
|
||||
};
|
||||
export const isAesJson = (obj: JSONKeyParams): obj is AesJSONType => {
|
||||
const name = obj.name;
|
||||
return Object.values(aesTypes).includes(name as AesType);
|
||||
}
|
||||
|
||||
export type RsaJSONType = ReturnType<RsaImportParams["toJSON"]>;
|
||||
export type AesJSONType = ReturnType<AesImportParams["toJSON"]>;
|
||||
export type Ed25519JSONParams = ReturnType<Ed25519ImportParams["toJSON"]>;
|
||||
export type JSONAesGcmParams = ReturnType<AesGcmParams["toJSON"]>;
|
||||
export type AesKeyGenLength = 128 | 192 | 256;
|
||||
export type KeyParams = RsaImportParams | AesImportParams | Ed25519ImportParams;
|
||||
export type JSONKeyParams = RsaJSONType | AesJSONType | Ed25519JSONParams;
|
||||
export type KeyGenParams = RSAKeyGenParams | Ed25519KeyGenParams | AesKeyGenParams;
|
||||
export type DigestType = typeof digestTypes[keyof typeof digestTypes];
|
||||
export type AesType = typeof aesTypes[keyof typeof aesTypes];
|
||||
export type RsaType = typeof rsaTypes[keyof typeof rsaTypes];
|
||||
export type AlgorithmType = typeof algorithmTypes[keyof typeof algorithmTypes];
|
||||
export type ExportKeyFormat = typeof exportKeyFormats[keyof typeof exportKeyFormats];
|
||||
export type SignAlgorithm = typeof signAlgorithms[keyof typeof signAlgorithms];
|
||||
export type CryptAlgorithm = typeof cryptAlgorithms[keyof typeof cryptAlgorithms];
|
||||
export type JSONKey = Awaited<ReturnType<Key["toJSON"]>>;
|
||||
export type JSONPublicKey = Awaited<ReturnType<PublicKey["toJSON"]>>;
|
||||
export type JSONPrivateKey = Awaited<ReturnType<PrivateKey["toJSON"]>>;
|
||||
export type JSONKeyPair = Awaited<ReturnType<KeyPair["toJSON"]>>;
|
||||
export type PairKey = PrivateKey | PublicKey;
|
||||
type JSONEncryptExportKeyFormat = {
|
||||
encrypt: true;
|
||||
salt: string;
|
||||
data: string;
|
||||
};
|
||||
type JSONDecryptExportKeyFormat = {
|
||||
encrypt: false;
|
||||
data: JSONKeyPair[];
|
||||
};
|
||||
export type JSONExportKeyFormat = JSONEncryptExportKeyFormat | JSONDecryptExportKeyFormat;
|
||||
@ -0,0 +1,156 @@
|
||||
import {getGUID} from "../../background/utils.ts";
|
||||
import {ab2base64, base642ui, str2ui} from "../utils.ts";
|
||||
import {
|
||||
type ExportKeyFormat,
|
||||
exportKeyFormats, type JSONKey, type JSONKeyPair, type JSONPrivateKey, type JSONPublicKey,
|
||||
type KeyParams,
|
||||
keyTypes,
|
||||
pairKeyTypes
|
||||
} from "./key-types.ts";
|
||||
import getCrypto from "../crypto.ts";
|
||||
import {AesGcmParams, getKeyParamsFromJson} from "./params.ts";
|
||||
|
||||
export class Key {
|
||||
params: KeyParams;
|
||||
key;
|
||||
exportFormat;
|
||||
constructor(key: ArrayBuffer, params: KeyParams, exportFormat: ExportKeyFormat) {
|
||||
this.key = key;
|
||||
this.params = params;
|
||||
this.exportFormat = exportFormat;
|
||||
}
|
||||
static async fromJSON(json: JSONKey, _masterPassword?: string, _keyUsage?: KeyUsages) {
|
||||
const params = getKeyParamsFromJson(json.params);
|
||||
const key = base642ui(json.key);
|
||||
return new this(key, params, exportKeyFormats.raw);
|
||||
}
|
||||
async toJSON(_masterPassword?: string, _keyUsage?: KeyUsages) {
|
||||
const key = ab2base64(this.key);
|
||||
return {
|
||||
params: this.params.toJSON(),
|
||||
key: key
|
||||
};
|
||||
}
|
||||
}
|
||||
export class SymmetricKey extends Key {
|
||||
type = keyTypes.symmetric;
|
||||
keyUsages;
|
||||
constructor(key: ArrayBuffer, params: KeyParams, keyUsage = new KeyUsages(true)) {
|
||||
super(key, params, exportKeyFormats.raw);
|
||||
this.keyUsages = keyUsage;
|
||||
}
|
||||
}
|
||||
export class PublicKey extends Key {
|
||||
type = pairKeyTypes.public;
|
||||
constructor(key: ArrayBuffer, params: KeyParams) {
|
||||
super(key, params, exportKeyFormats.spki);
|
||||
}
|
||||
static override async fromJSON(json: JSONPublicKey) {
|
||||
const params = getKeyParamsFromJson(json.params);
|
||||
const key = base642ui(json.key);
|
||||
return new PublicKey(key, params);
|
||||
}
|
||||
override async toJSON() {
|
||||
const params = this.params.toJSON();
|
||||
const base64Key = ab2base64(this.key);
|
||||
return {
|
||||
format: exportKeyFormats.spki,
|
||||
key: base64Key,
|
||||
params
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class PrivateKey extends Key {
|
||||
type = pairKeyTypes.private;
|
||||
salt;
|
||||
constructor(key: ArrayBuffer, params: KeyParams, salt: ArrayBuffer) {
|
||||
super(key, params, exportKeyFormats.pkcs8);
|
||||
this.salt = salt;
|
||||
}
|
||||
static override async fromJSON(json: JSONPrivateKey, masterPassword: string, keyUsage: KeyUsages) {
|
||||
const salt = base642ui(json.salt);
|
||||
const params = getKeyParamsFromJson(json.params);
|
||||
const crypto = getCrypto();
|
||||
const strWrapKey = json.key;
|
||||
const wrapKey = base642ui(strWrapKey);
|
||||
const wrapParams = new AesGcmParams();
|
||||
wrapParams.fromJSON(json.wrapParams);
|
||||
const key = await crypto.unwrapKey(exportKeyFormats.pkcs8, wrapKey, str2ui(masterPassword), salt, wrapParams, params, keyUsage);
|
||||
|
||||
return new PrivateKey(key, params, salt);
|
||||
}
|
||||
override async toJSON(masterPassword: string, keyUsage: KeyUsages) {
|
||||
const crypto = getCrypto();
|
||||
const iv = crypto.getRandomValues(12);
|
||||
const aesParams = new AesGcmParams(iv);
|
||||
const wrapKey = await crypto.wrapKey(this.exportFormat, this, str2ui(masterPassword), this.salt, aesParams, keyUsage);
|
||||
const base64WrapKey = ab2base64(wrapKey);
|
||||
const params = this.params.toJSON();
|
||||
const wrapParams = aesParams.toJSON();
|
||||
return {
|
||||
format: this.exportFormat,
|
||||
key: base64WrapKey,
|
||||
salt: ab2base64(this.salt),
|
||||
params,
|
||||
wrapParams
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class KeyPair {
|
||||
privateKey;
|
||||
publicKey;
|
||||
date;
|
||||
type = keyTypes.pair;
|
||||
keyUsage;
|
||||
guid;
|
||||
isValid;
|
||||
static async fromJSON(json: JSONKeyPair, masterPassword: string) {
|
||||
const keyUsage = KeyUsages.fromJSON(json.keyUsage);
|
||||
const publicKey = await PublicKey.fromJSON(json.publicKey);
|
||||
const privateKey = await PrivateKey.fromJSON(json.privateKey, masterPassword, keyUsage);
|
||||
const date = new Date(json.date);
|
||||
const guid = json.guid;
|
||||
const isValid = json.isValid;
|
||||
return new KeyPair(publicKey, privateKey, keyUsage, date, guid, isValid);
|
||||
}
|
||||
constructor(publicKey: PublicKey, privateKey: PrivateKey, keyUsage = new KeyUsages(true), date = new Date(), guid: string = getGUID(), isValid: boolean = true) {
|
||||
this.privateKey = privateKey;
|
||||
this.publicKey = publicKey;
|
||||
this.date = date;
|
||||
this.keyUsage = keyUsage;
|
||||
this.guid = guid;
|
||||
this.isValid = isValid;
|
||||
}
|
||||
async toJSON(masterPassword: string) {
|
||||
return {
|
||||
publicKey: await this.publicKey.toJSON(),
|
||||
privateKey: await this.privateKey.toJSON(masterPassword, this.keyUsage),
|
||||
date: this.date.toISOString(),
|
||||
keyUsage: this.keyUsage.toJSON(),
|
||||
guid: this.guid,
|
||||
isValid: this.isValid
|
||||
}
|
||||
}
|
||||
setIsValid(isValid: boolean) {
|
||||
this.isValid = isValid;
|
||||
};
|
||||
}
|
||||
export class KeyUsages {
|
||||
isEncrypt;
|
||||
isSign;
|
||||
constructor(isEncrypt?: boolean, isSign?: boolean) {
|
||||
this.isEncrypt = !!isEncrypt;
|
||||
this.isSign = !!isSign;
|
||||
}
|
||||
static fromJSON(json: ReturnType<KeyUsages["toJSON"]>) {
|
||||
return new KeyUsages(json.isEncrypt, json.isSign);
|
||||
}
|
||||
toJSON() {
|
||||
return {
|
||||
isEncrypt: this.isEncrypt,
|
||||
isSign: this.isSign
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
import {
|
||||
type AesKeyGenLength,
|
||||
type AesType,
|
||||
aesTypes,
|
||||
type AlgorithmType,
|
||||
algorithmTypes,
|
||||
type DigestType,
|
||||
digestTypes, isAesJson, isEd25519Json,
|
||||
isRSAJson,
|
||||
type JSONAesGcmParams,
|
||||
type JSONKeyParams,
|
||||
type RsaJSONType,
|
||||
type RsaType
|
||||
} from "./key-types.ts";
|
||||
import {ab2base64, base642ui} from "../utils.ts";
|
||||
|
||||
export const getKeyParamsFromJson = (keyParamsJson: JSONKeyParams) => {
|
||||
if (isRSAJson(keyParamsJson)) {
|
||||
return new RsaImportParams(keyParamsJson.name, keyParamsJson.hash);
|
||||
}
|
||||
if (isEd25519Json(keyParamsJson)) {
|
||||
return new Ed25519ImportParams();
|
||||
}
|
||||
if (isAesJson(keyParamsJson)) {
|
||||
return new AesImportParams(keyParamsJson.name);
|
||||
}
|
||||
throw new Error("Unknown param type");
|
||||
};
|
||||
|
||||
|
||||
export class AlgorithmParams<TName extends AlgorithmType = AlgorithmType> {
|
||||
name: TName;
|
||||
constructor(name: TName) {
|
||||
this.name = name;
|
||||
}
|
||||
toJSON() {
|
||||
return {
|
||||
name: this.name
|
||||
};
|
||||
};
|
||||
fromJSON(json: {name: TName}) {
|
||||
this.name = json.name;
|
||||
}
|
||||
getImportParams() {
|
||||
return new AlgorithmParams(this.name);
|
||||
}
|
||||
}
|
||||
|
||||
export class RsaImportParams extends AlgorithmParams<RsaType> {
|
||||
hash;
|
||||
constructor(name: RsaType, hash: DigestType = digestTypes.SHA256) {
|
||||
super(name);
|
||||
this.hash = hash;
|
||||
}
|
||||
override toJSON() {
|
||||
return {
|
||||
name: this.name,
|
||||
hash: this.hash,
|
||||
}
|
||||
}
|
||||
override fromJSON(json: RsaJSONType) {
|
||||
this.name = json.name;
|
||||
this.hash = json.hash;
|
||||
}
|
||||
}
|
||||
|
||||
export class RSAKeyGenParams extends RsaImportParams {
|
||||
modulusLength;
|
||||
publicExponent;
|
||||
constructor(name: RsaType, hash: DigestType = digestTypes.SHA256, modulusLength = 2048, publicExponent = new Uint8Array([0x01, 0x00, 0x01])) {
|
||||
super(name, hash);
|
||||
this.modulusLength = modulusLength;
|
||||
this.publicExponent = publicExponent;
|
||||
}
|
||||
override getImportParams() {
|
||||
return new RsaImportParams(this.name, this.hash);
|
||||
}
|
||||
}
|
||||
export class Ed25519ImportParams extends AlgorithmParams<typeof algorithmTypes.ED25519> {
|
||||
constructor() {
|
||||
super(algorithmTypes.ED25519);
|
||||
}
|
||||
}
|
||||
|
||||
export class AesImportParams extends AlgorithmParams<AesType> {
|
||||
constructor(name: AesType) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
|
||||
export class AesGcmParams {
|
||||
name = algorithmTypes.AES_GCM;
|
||||
iv: ArrayBuffer;
|
||||
constructor(iv: ArrayBuffer = new Uint8Array(12)) {
|
||||
this.iv = iv;
|
||||
}
|
||||
toJSON() {
|
||||
return {
|
||||
name: this.name,
|
||||
iv: ab2base64(this.iv)
|
||||
}
|
||||
};
|
||||
fromJSON(json: JSONAesGcmParams) {
|
||||
this.iv = base642ui(json.iv);
|
||||
};
|
||||
}
|
||||
|
||||
export class AesKeyGenParams extends AesImportParams {
|
||||
length: AesKeyGenLength;
|
||||
constructor(name: AesType, length: AesKeyGenLength) {
|
||||
super(name);
|
||||
this.length = length;
|
||||
}
|
||||
override getImportParams() {
|
||||
return new AesImportParams(this.name);
|
||||
}
|
||||
}
|
||||
export class AesGcmGenParams extends AesKeyGenParams {
|
||||
constructor() {
|
||||
super(aesTypes.AES_GCM, 256);
|
||||
}
|
||||
}
|
||||
export class Ed25519KeyGenParams extends Ed25519ImportParams {
|
||||
override getImportParams() {
|
||||
return new Ed25519ImportParams();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,20 @@
|
||||
export const messageTypes = {
|
||||
GENERATE_KEYS: "GENERATE_KEYS",
|
||||
POPUP_IS_OPENED: "POPUP_IS_OPENED",
|
||||
WAIT_ENTER_PASSWORD: "WAIT_ENTER_PASSWORD",
|
||||
SELECT_SIGN_KEYS: "SELECT_SIGN_KEYS",
|
||||
SIGN_DATA: "SIGN_DATA",
|
||||
VERIFY_DATA: "VERIFY_DATA",
|
||||
ENCRYPT: "ENCRYPT",
|
||||
ENGINE_IS_EXIST: "ENGINE_IS_EXIST"
|
||||
} as const;
|
||||
|
||||
export const messageListeners = {
|
||||
background: "background",
|
||||
popup: "popup",
|
||||
} as const;
|
||||
|
||||
export const onlyofficeChannels = {
|
||||
onlyofficeExtensionChannel: "onlyoffice-sign-extension-channel",
|
||||
onlyofficeClientChannel: "onlyoffice-sign-client-channel",
|
||||
} as const;
|
||||
@ -0,0 +1,35 @@
|
||||
import {messageListeners, messageTypes} from "./message-const.ts";
|
||||
|
||||
|
||||
export type MessagesType = {type: typeof messageTypes[keyof typeof messageTypes]};
|
||||
|
||||
export const isMessages = (arg: unknown): arg is MessagesType => {
|
||||
return !!(arg && typeof arg === "object" && "type" in arg && typeof arg.type === "string" && arg.type in messageTypes);
|
||||
};
|
||||
|
||||
export type DispatchEventMessageType = {
|
||||
id?: number;
|
||||
data: MessagesType;
|
||||
}
|
||||
export type AnswerMainPageEventType = {
|
||||
id?: number;
|
||||
data: unknown;
|
||||
};
|
||||
type Listeners = typeof messageListeners[keyof typeof messageListeners];
|
||||
type ExtensionMessage<T extends Listeners = Listeners> = {
|
||||
data: MessagesType;
|
||||
listener: T;
|
||||
};
|
||||
export type BackgroundMessage = ExtensionMessage<typeof messageListeners.background>;
|
||||
export type PopupMessage = ExtensionMessage<typeof messageListeners.popup>;
|
||||
|
||||
const isExtensionMessageType = (arg: unknown): arg is ExtensionMessage => {
|
||||
return !!(arg && typeof arg === "object" && "data" in arg && isMessages(arg.data) && "listener" in arg && typeof arg.listener === "string");
|
||||
};
|
||||
export const isBackgroundMessageType = (arg: unknown): arg is BackgroundMessage => {
|
||||
return isExtensionMessageType(arg) && arg.listener === messageListeners.background;
|
||||
};
|
||||
|
||||
export const isPopupMessageType = (arg: unknown): arg is PopupMessage => {
|
||||
return isExtensionMessageType(arg) && arg.listener === messageListeners.popup;
|
||||
};
|
||||
@ -0,0 +1,29 @@
|
||||
// @ts-ignore
|
||||
import {StorageManager} from "../../../key-storage/key-storage.js";
|
||||
import browser from "webextension-polyfill";
|
||||
import type {JSONKeyPair} from "./keys/key-types.ts";
|
||||
|
||||
function ExtensionStorageManager() {
|
||||
StorageManager.call(this);
|
||||
}
|
||||
ExtensionStorageManager.prototype = Object.create(StorageManager);
|
||||
ExtensionStorageManager.prototype.constructor = ExtensionStorageManager;
|
||||
ExtensionStorageManager.prototype.getStorageKeys = function() {
|
||||
return browser.storage.local.get("keys").then(function(item) {
|
||||
if (item && Array.isArray(item.keys)) {
|
||||
return item.keys;
|
||||
}
|
||||
return [];
|
||||
});
|
||||
};
|
||||
ExtensionStorageManager.prototype.getMasterPassword = function() {
|
||||
return browser.storage.local.get("masterPassword").then(function(item) {
|
||||
return item.masterPassword ? item.masterPassword : null;
|
||||
});
|
||||
};
|
||||
ExtensionStorageManager.prototype.setStorageKeys = function(exportedKeys: JSONKeyPair[]) {
|
||||
return browser.storage.local.set({keys: exportedKeys});
|
||||
}
|
||||
ExtensionStorageManager.prototype.setMasterPasswordWithKeys = function(exportedKeys: JSONKeyPair[]) {
|
||||
return browser.storage.local.set({keys: exportedKeys});
|
||||
}
|
||||
@ -0,0 +1,42 @@
|
||||
export function ab2str(buf: ArrayBuffer) {
|
||||
return String.fromCharCode.apply(null, buf);
|
||||
}
|
||||
export function ab2base64(buf: ArrayBuffer) {
|
||||
const str = ab2str(buf);
|
||||
return btoa(str);
|
||||
}
|
||||
export function base642ui(base64: string) {
|
||||
const str = atob(base64);
|
||||
return str2ui(str);
|
||||
}
|
||||
export function str2ui(str: string) {
|
||||
const ui = new Uint8Array(str.length);
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
ui[i] = str.charCodeAt(i);
|
||||
}
|
||||
return ui;
|
||||
}
|
||||
|
||||
export const selectBinary = (callback: (file: File) => void) => {
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = "application/octet-stream";
|
||||
input.addEventListener("change", (e) => {
|
||||
const target = e.target as HTMLInputElement;
|
||||
const file = target.files?.[0];
|
||||
if (file) {
|
||||
callback(file);
|
||||
}
|
||||
});
|
||||
input.click();
|
||||
};
|
||||
|
||||
export const downloadBinary = (data: Uint8Array) => {
|
||||
const blob = new Blob([data], {type: "application/octet-stream"});
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = `onlyoffice_keychain_${(new Date()).toISOString()}.bin`;
|
||||
link.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
import {sendToBackground, sendToPage} from "./messenger.ts";
|
||||
import {messageTypes, onlyofficeChannels} from "../common/message-const.ts";
|
||||
import {
|
||||
type DispatchEventMessageType,
|
||||
} from "../common/message-types.ts";
|
||||
|
||||
window.addEventListener(onlyofficeChannels.onlyofficeExtensionChannel, (event: CustomEvent<DispatchEventMessageType>) => {
|
||||
sendToBackground(event.detail.data).then((response: unknown) => {
|
||||
sendToPage({id: event.detail.id, data: response});
|
||||
});
|
||||
});
|
||||
window.dispatchEvent(new CustomEvent<DispatchEventMessageType>(onlyofficeChannels.onlyofficeClientChannel, {detail: {data: {type: messageTypes.ENGINE_IS_EXIST}}}));
|
||||
@ -0,0 +1,22 @@
|
||||
import type {
|
||||
AnswerMainPageEventType,
|
||||
BackgroundMessage,
|
||||
MessagesType,
|
||||
PopupMessage
|
||||
} from "../common/message-types.ts";
|
||||
import browser from "webextension-polyfill";
|
||||
import {messageListeners, onlyofficeChannels} from "../common/message-const.ts";
|
||||
|
||||
export const sendToBackground = async (data: MessagesType) => {
|
||||
const backgroundData: BackgroundMessage = {data, listener: messageListeners.background};
|
||||
return browser.runtime.sendMessage(backgroundData);
|
||||
};
|
||||
|
||||
export const sendToPopup = async (data: MessagesType) => {
|
||||
const sendData: PopupMessage = {listener: messageListeners.popup, data};
|
||||
return browser.runtime.sendMessage(sendData);
|
||||
};
|
||||
|
||||
export const sendToPage = (data: AnswerMainPageEventType) => {
|
||||
window.dispatchEvent(new CustomEvent<AnswerMainPageEventType>(onlyofficeChannels.onlyofficeClientChannel, {detail: data}));
|
||||
};
|
||||
@ -0,0 +1,3 @@
|
||||
export function Loader() {
|
||||
return <div>Loading...</div>;
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
type PropsType = {
|
||||
name: string;
|
||||
labelText: string;
|
||||
onChange: (e: string) => void;
|
||||
}
|
||||
|
||||
export default function PasswordInput({onChange, name, labelText}: PropsType) {
|
||||
return <>
|
||||
<label htmlFor={name}>{labelText}</label>
|
||||
<input required={true} minLength={8} onChange={(e) => onChange(e.target.value)} type="password" name={name}/>
|
||||
</>;
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
import { StrictMode } from 'react'
|
||||
import { createRoot } from 'react-dom/client'
|
||||
import App from './pages/app/app.tsx'
|
||||
|
||||
createRoot(document.getElementById('root')!).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>,
|
||||
)
|
||||
@ -0,0 +1,91 @@
|
||||
import {useState, useEffect} from 'react'
|
||||
import Login from "../login/login.tsx";
|
||||
import {getStorageMasterPassword, initCheckOpenedPopup, setStorageMasterPassword} from "../../../utils/utils.ts";
|
||||
import {useTaskManager} from "../../../task-manager/task-manager.ts";
|
||||
import {Dashboard} from "../dashboard/dashboard.tsx";
|
||||
// @ts-ignore
|
||||
import {StorageManager} from "../storage-manager/storage-manager.ts";
|
||||
import {KeyPair} from "../../../../common/keys/keys.ts";
|
||||
import {Ed25519KeyGenParams} from "../../../../common/keys/params.ts";
|
||||
import {ChangePasswordPage} from "../change-password/change-password.tsx";
|
||||
import {locations} from "../../../utils/locations.ts";
|
||||
import SelectKeysPage from "../select-keys/select-keys.tsx";
|
||||
import {messageTypes} from "../../../../common/message-const.ts";
|
||||
const storageManager = new StorageManager();
|
||||
const generateKeys = async () => {
|
||||
const key = await storageManager.generateKeys(new Ed25519KeyGenParams());
|
||||
if (key) {
|
||||
await storageManager.addNewKeys([key]);
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
export default function App() {
|
||||
const [localMasterPassword, setLocalMasterPassword] = useState<string | null>(null);
|
||||
const [keys, setKeys] = useState<KeyPair[]>([]);
|
||||
const {location, setLocation, promiseRef} = useTaskManager();
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const storageMasterPassword = await getStorageMasterPassword();
|
||||
setLocalMasterPassword(storageMasterPassword);
|
||||
await storageManager.loadKeysFromStorage();
|
||||
setKeys(storageManager.getValidKeys());
|
||||
})();
|
||||
initCheckOpenedPopup();
|
||||
}, []);
|
||||
|
||||
const handleSelectKey = (e: React.MouseEvent<HTMLLIElement>) => {
|
||||
if (promiseRef.current) {
|
||||
const guid = e.currentTarget.dataset.guid;
|
||||
if (promiseRef.current.messageId === messageTypes.SELECT_SIGN_KEYS && guid) {
|
||||
promiseRef.current.resolve(guid);
|
||||
} else {
|
||||
promiseRef.current.reject("Another task was expected to resolve");
|
||||
}
|
||||
}
|
||||
promiseRef.current = null;
|
||||
window.close();
|
||||
};
|
||||
|
||||
const handleSubmitMasterPassword = (masterPassword: string) => {
|
||||
setStorageMasterPassword(masterPassword);
|
||||
setLocalMasterPassword(masterPassword);
|
||||
};
|
||||
|
||||
const handleSubmitNewMasterPassword = async (newMasterPassword: string) => {
|
||||
await storageManager.changeMasterPassword(newMasterPassword);
|
||||
setLocalMasterPassword(newMasterPassword);
|
||||
setLocation("");
|
||||
};
|
||||
|
||||
const handleGenerateKeys = async () => {
|
||||
const keyPair = await generateKeys();
|
||||
if (keyPair) {
|
||||
setKeys(storageManager.getValidKeys());
|
||||
}
|
||||
};
|
||||
const handleExportKeys = () => {
|
||||
storageManager.exportKeys();
|
||||
}
|
||||
const handleImportKeys = async () => {
|
||||
storageManager.importKeys(() => {setKeys(storageManager.getValidKeys())});
|
||||
}
|
||||
|
||||
const handleDeprecateKey = async (key: KeyPair) => {
|
||||
await storageManager.deprecateKey(key);
|
||||
setKeys(storageManager.getValidKeys());
|
||||
};
|
||||
const isLoggedOut = localMasterPassword === null;
|
||||
return (
|
||||
<>
|
||||
{
|
||||
isLoggedOut ?
|
||||
<Login handleSubmitMasterPassword={handleSubmitMasterPassword} /> :
|
||||
location === locations.changeMasterPassword ? <ChangePasswordPage handleSubmitNewMasterPassword={handleSubmitNewMasterPassword} /> :
|
||||
location === locations.selectKeys ? <SelectKeysPage keys={keys} handleKey={handleSelectKey}/> :
|
||||
<Dashboard handleDeprecateKey={handleDeprecateKey} changeLocation={setLocation} handleImportKeys={handleImportKeys} handleExportKeys={handleExportKeys} handleGenerateSignKeys={handleGenerateKeys} keys={keys} masterPassword={localMasterPassword}/>
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
import PasswordInput from "../../components/password-input/password-input.tsx";
|
||||
import {type FormEvent, useState} from "react";
|
||||
import {compareWithOldMasterPassword} from "../../../utils/utils.ts";
|
||||
|
||||
type ChangePasswordPageProps = {
|
||||
handleSubmitNewMasterPassword: (newMasterPassword: string) => void;
|
||||
};
|
||||
export function ChangePasswordPage({handleSubmitNewMasterPassword}: ChangePasswordPageProps) {
|
||||
const [oldMasterPassword, setOldMasterPassword] = useState("");
|
||||
const [newMasterPassword, setNewMasterPassword] = useState("");
|
||||
const [confirmMasterPassword, setConfirmMasterPassword] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const isEqualsOldPassword = await compareWithOldMasterPassword(oldMasterPassword);
|
||||
if (!isEqualsOldPassword) {
|
||||
setError("Check if you entered your old password correctly.");
|
||||
} else if (newMasterPassword !== confirmMasterPassword) {
|
||||
setError("The new passwords do not match.");
|
||||
} else {
|
||||
handleSubmitNewMasterPassword(newMasterPassword);
|
||||
}
|
||||
};
|
||||
return <form onSubmit={onSubmit}>
|
||||
<PasswordInput name={"old-password"} labelText={"Enter old master password"} onChange={setOldMasterPassword}/>
|
||||
<PasswordInput name={"new-password"} labelText={"Enter new master password"} onChange={setNewMasterPassword}/>
|
||||
<PasswordInput name={"confirm-new-password"} labelText={"Confirm new master password"} onChange={setConfirmMasterPassword}/>
|
||||
{error && <div>{error}</div>}
|
||||
<button type={"submit"}>Confirm</button>
|
||||
</form>
|
||||
}
|
||||
@ -0,0 +1,8 @@
|
||||
|
||||
.wrapper {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
:hover {
|
||||
background-color: rgba(240 , 240, 240, 255);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
import type {KeyPair} from "../../../../common/keys/keys.ts";
|
||||
import {locations} from "../../../utils/locations.ts";
|
||||
import css from "./dashboard.module.css";
|
||||
|
||||
type DashboardProps = {
|
||||
masterPassword: string;
|
||||
keys: KeyPair[];
|
||||
handleGenerateSignKeys: () => Promise<void>
|
||||
handleImportKeys: () => void;
|
||||
handleExportKeys: () => void;
|
||||
handleDeprecateKey: (key: KeyPair) => void;
|
||||
changeLocation: (location: string) => void
|
||||
};
|
||||
|
||||
export function Dashboard({keys, handleDeprecateKey, masterPassword, handleGenerateSignKeys, handleImportKeys, handleExportKeys, changeLocation}: DashboardProps) {
|
||||
return <div>
|
||||
<div>Hello, your master password: {masterPassword}</div>
|
||||
<button onClick={() => {changeLocation(locations.changeMasterPassword)}}>Change password</button>
|
||||
<button onClick={handleExportKeys}>Export keys</button>
|
||||
<button onClick={handleImportKeys}>Import keys</button>
|
||||
<button onClick={handleGenerateSignKeys}>Generate sign keys</button>
|
||||
<div>Generated sign keys</div>
|
||||
{keys.map((key, idx) =>
|
||||
<div key={idx} className={css.wrapper}>
|
||||
<div>{key.guid}</div>
|
||||
<div onClick={() => handleDeprecateKey(key)}>×</div>
|
||||
</div>)}
|
||||
</div>
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
import {type FormEvent, useState} from "react";
|
||||
import PasswordInput from "../../components/password-input/password-input.tsx";
|
||||
|
||||
type LoginProps = {
|
||||
handleSubmitMasterPassword: (password: string) => void;
|
||||
};
|
||||
export default function Login({handleSubmitMasterPassword}: LoginProps) {
|
||||
const [masterPassword, setMasterPassword] = useState("");
|
||||
const [confirmMasterPassword, setConfirmMasterPassword] = useState("");
|
||||
const [error, setError] = useState("");
|
||||
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (masterPassword === confirmMasterPassword) {
|
||||
handleSubmitMasterPassword(masterPassword);
|
||||
} else {
|
||||
setError("The passwords don't match");
|
||||
}
|
||||
};
|
||||
return <form onSubmit={handleSubmit}>
|
||||
<PasswordInput name={"login-password"} labelText={"Enter new master password"} onChange={setMasterPassword}/>
|
||||
<PasswordInput name={"login-confirm-password"} labelText={"Confirm new master password"} onChange={setConfirmMasterPassword}/>
|
||||
{error && <div>{error}</div>}
|
||||
<button type={"submit"}>Confirm</button>
|
||||
</form>
|
||||
};
|
||||
@ -0,0 +1,6 @@
|
||||
.key {
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background-color: rgba(0, 0, 0, 10%);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
import type {KeyPair} from "../../../../common/keys/keys.ts";
|
||||
import type {MouseEventHandler} from "react";
|
||||
import css from "./select-keys.module.css";
|
||||
|
||||
type TSelectKeysProps = {
|
||||
keys: KeyPair[];
|
||||
handleKey: MouseEventHandler<HTMLLIElement>;
|
||||
};
|
||||
|
||||
export default function SelectKeysPage({keys, handleKey}: TSelectKeysProps) {
|
||||
return <ul>
|
||||
{keys.map((key) => <li className={css.key} key={key.guid} data-guid={key.guid} onClick={handleKey}>
|
||||
{key.guid}
|
||||
</li>)}
|
||||
</ul>
|
||||
};
|
||||
@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/popup/App/main.tsx"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,46 @@
|
||||
import browser from "webextension-polyfill";
|
||||
import {isPopupMessageType} from "../../common/message-types.ts";
|
||||
import {messageTypes} from "../../common/message-const.ts";
|
||||
import {isStorageLogged, selectSignKeys} from "./tasks.ts";
|
||||
import {useEffect, useRef, useState} from "react";
|
||||
|
||||
type TSelectKeysPromise = {
|
||||
resolve: (guid: string) => void;
|
||||
messageId: typeof messageTypes.SELECT_SIGN_KEYS
|
||||
};
|
||||
type TPromiseArgs = {
|
||||
reject: (error: string) => void;
|
||||
} & (TSelectKeysPromise);
|
||||
export type TPromiseRef = (newResolve: TPromiseArgs["resolve"], newReject: TPromiseArgs["reject"], id: TPromiseArgs["messageId"]) => void;
|
||||
export const useTaskManager = () => {
|
||||
const [location, setLocation] = useState("");
|
||||
const promiseRef = useRef<TPromiseArgs | null>(null);
|
||||
const setPromiseRef: TPromiseRef = (newResolve, newReject, id) => {
|
||||
if (promiseRef.current) {
|
||||
promiseRef.current.reject("Another task has been selected");
|
||||
}
|
||||
promiseRef.current = {resolve: newResolve, reject: newReject, messageId: id};
|
||||
}
|
||||
useEffect(() => {
|
||||
const listener = (message: unknown) => {
|
||||
if (!isPopupMessageType(message)) {
|
||||
return false;
|
||||
}
|
||||
const data = message.data;
|
||||
switch (data.type) {
|
||||
case messageTypes.WAIT_ENTER_PASSWORD: {
|
||||
return isStorageLogged();
|
||||
}
|
||||
case messageTypes.SELECT_SIGN_KEYS: {
|
||||
return selectSignKeys(setLocation, setPromiseRef);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
browser.runtime.onMessage.addListener(listener);
|
||||
return () => {
|
||||
browser.runtime.onMessage.removeListener(listener);
|
||||
}
|
||||
}, []);
|
||||
return {location, setLocation, promiseRef};
|
||||
};
|
||||
@ -0,0 +1,16 @@
|
||||
import {checkIsStorageLogged} from "../utils/utils.ts";
|
||||
import {locations} from "../utils/locations.ts";
|
||||
import {messageTypes} from "../../common/message-const.ts";
|
||||
import type {TPromiseRef} from "./task-manager.ts";
|
||||
|
||||
export const isStorageLogged = async () => {
|
||||
await checkIsStorageLogged();
|
||||
return true;
|
||||
};
|
||||
|
||||
export const selectSignKeys = (setNavigation: (location: string) => void, setPromiseRef: TPromiseRef): Promise<string> => {
|
||||
setNavigation(locations.selectKeys);
|
||||
return new Promise((resolve, reject) => {
|
||||
setPromiseRef(resolve, reject, messageTypes.SELECT_SIGN_KEYS);
|
||||
});
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
export const locations = {
|
||||
changeMasterPassword: "changeMasterPassword",
|
||||
selectKeys: "selectKeys",
|
||||
} as const;
|
||||
@ -0,0 +1,38 @@
|
||||
import browser from "webextension-polyfill";
|
||||
export const getStorageMasterPassword = async () => {
|
||||
const masterPassword = await browser.storage.local.get('masterPassword');
|
||||
if (masterPassword && typeof masterPassword.masterPassword === 'string') {
|
||||
return masterPassword.masterPassword;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
export const compareWithOldMasterPassword = async (checkPassword: string) => {
|
||||
const masterPassword = await getStorageMasterPassword();
|
||||
return masterPassword === checkPassword;
|
||||
}
|
||||
export const setStorageMasterPassword = (masterPassword: string) => {
|
||||
browser.storage.local.set({masterPassword});
|
||||
}
|
||||
|
||||
export const checkIsStorageLogged = async () => {
|
||||
const masterPassword = await getStorageMasterPassword();
|
||||
if (masterPassword) {
|
||||
return true;
|
||||
}
|
||||
return getChangedProperty("masterPassword");
|
||||
};
|
||||
const getChangedProperty = (key: string) => {
|
||||
return new Promise((resolve) => {
|
||||
browser.storage.local.onChanged.addListener(function handler(change) {
|
||||
if (change[key]) {
|
||||
browser.storage.local.onChanged.removeListener(handler);
|
||||
resolve(change[key].newValue);
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const initCheckOpenedPopup = () => {
|
||||
const port = browser.runtime.connect({ name: "popup" });
|
||||
port.postMessage({ opened: true });
|
||||
};
|
||||
7
DesktopEditor/xmlsec/src/wasm/extension/extension2/src/types.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
export {};
|
||||
|
||||
declare global {
|
||||
interface WindowEventMap {
|
||||
"onlyoffice-sign-extension-channel": CustomEvent;
|
||||
}
|
||||
}
|
||||
1
DesktopEditor/xmlsec/src/wasm/extension/extension2/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
/// <reference types="vite/client" />
|
||||
@ -0,0 +1,29 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
||||
"target": "ES2022",
|
||||
"useDefineForClassFields": true,
|
||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
"jsx": "react-jsx",
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true,
|
||||
"noImplicitOverride": true,
|
||||
"resolveJsonModule": true
|
||||
},
|
||||
"include": ["src"]
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"files": [],
|
||||
"references": [
|
||||
{ "path": "./tsconfig.app.json" },
|
||||
{ "path": "./tsconfig.node.json" }
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
||||
"target": "ES2023",
|
||||
"lib": ["ES2023"],
|
||||
"module": "ESNext",
|
||||
"skipLibCheck": true,
|
||||
|
||||
/* Bundler mode */
|
||||
"moduleResolution": "bundler",
|
||||
"allowImportingTsExtensions": true,
|
||||
"verbatimModuleSyntax": true,
|
||||
"moduleDetection": "force",
|
||||
"noEmit": true,
|
||||
|
||||
/* Linting */
|
||||
"strict": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"erasableSyntaxOnly": true,
|
||||
"noFallthroughCasesInSwitch": true,
|
||||
"noUncheckedSideEffectImports": true
|
||||
},
|
||||
"include": ["vite.config.ts"]
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import {crx} from "@crxjs/vite-plugin";
|
||||
import manifest from "./manifest.config.ts";
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [crx({ manifest }), react()],
|
||||
build: {
|
||||
sourcemap: 'inline'
|
||||
}
|
||||
})
|
||||
@ -0,0 +1,11 @@
|
||||
# Crypto library
|
||||
|
||||
## Information for development
|
||||
|
||||
1. Run ```npm run dev``` in the library directory to develop the library in watch mode.
|
||||
2. Run ```npm link``` in the library directory.
|
||||
3. Run ```npm link crypto-library``` in the directory where the library is required as a npm module.
|
||||
|
||||
## Information for build
|
||||
|
||||
Run ```npm run build``` in the library directory to build the library as ```es``` and ```iife``` modules.
|
||||
1056
DesktopEditor/xmlsec/src/wasm/extension/key-storage/package-lock.json
generated
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "key-storage",
|
||||
"version": "1.0.0",
|
||||
"main": "dist/key-storage.es.js",
|
||||
"files": ["dist"],
|
||||
"devDependencies": {
|
||||
"vite": "^7.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"dev": "vite build --watch",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,142 @@
|
||||
import {EncryptData, WebEncryptKeyPair, WebSignKeyPair, WebSymmetricKey} from "./keys";
|
||||
import {AesGcmKeyGenParams, PBKDF2Params} from "./params";
|
||||
import {c_oAscExportKeyFormat} from "./defines";
|
||||
import {BinaryWriter} from "./serialize/writer";
|
||||
import {initClass} from "./utils";
|
||||
|
||||
function CCryptoBase() {
|
||||
|
||||
}
|
||||
CCryptoBase.prototype.sign = function(key, data) {};
|
||||
CCryptoBase.prototype.digest = function(algorithm, data) {};
|
||||
CCryptoBase.prototype.verify = function(key, signature, data) {};
|
||||
CCryptoBase.prototype.decrypt = function(key, data) {};
|
||||
CCryptoBase.prototype.encrypt = function(key, data) {};
|
||||
CCryptoBase.prototype.generateKey = function(params) {};
|
||||
CCryptoBase.prototype.getRandomValues = function(length) {};
|
||||
CCryptoBase.prototype.randomUUID = function() {};
|
||||
CCryptoBase.prototype.initKey = function(key, masterPassword) {return Promise.resolve()};
|
||||
CCryptoBase.prototype.getAesKey = function(key, pbkdfParams) {return Promise.resolve()};
|
||||
|
||||
function CWebCrypto() {
|
||||
CCryptoBase.call(this);
|
||||
this.crypto = self.crypto;
|
||||
this.subtle = this.crypto.subtle;
|
||||
}
|
||||
initClass(CWebCrypto, CCryptoBase);
|
||||
CWebCrypto.prototype.getRandomValues = function(length) {
|
||||
const ui = new Uint8Array(length);
|
||||
return this.crypto.getRandomValues(ui);
|
||||
}
|
||||
CWebCrypto.prototype.getAesKey = function(masterPassword, pbkdfParams) {
|
||||
const oThis = this;
|
||||
const aesKeyGenParams = new AesGcmKeyGenParams();
|
||||
return this.subtle.importKey(
|
||||
'raw',
|
||||
masterPassword,
|
||||
{ name: 'PBKDF2' },
|
||||
false,
|
||||
['deriveKey']
|
||||
).then(function(pwKey) {
|
||||
return oThis.subtle.deriveKey(
|
||||
pbkdfParams.getCryptoParams(),
|
||||
pwKey,
|
||||
aesKeyGenParams.getKeyGenCryptoParams(),
|
||||
false,
|
||||
['encrypt', 'decrypt']
|
||||
);
|
||||
}).then(function(aesKey) {
|
||||
return WebSymmetricKey.fromCryptoKey(aesKey, aesKeyGenParams.getImportParams());
|
||||
});
|
||||
};
|
||||
CWebCrypto.prototype.sign = function(key, data) {
|
||||
const oThis = this;
|
||||
const cryptoKey = key.getCryptoKey();
|
||||
const params = key.getCryptoParams();
|
||||
return oThis.subtle.sign(cryptoKey, cryptoKey, data);
|
||||
}
|
||||
CWebCrypto.prototype.digest = function(algorithm, data) {
|
||||
return this.subtle.digest(algorithm, data);
|
||||
}
|
||||
CWebCrypto.prototype.verify = function(key, signature, data) {
|
||||
const oThis = this;
|
||||
const cryptoKey = key.getCryptoKey();
|
||||
return oThis.subtle.verify(key.params, cryptoKey, signature, data);
|
||||
}
|
||||
CWebCrypto.prototype.decrypt = function(key, data) {
|
||||
const oThis = this;
|
||||
const cryptoKey = key.getCryptoKey();
|
||||
const encryptParams = data.getEncryptParams();
|
||||
const algorithm = encryptParams.getCryptoParams();
|
||||
return oThis.subtle.decrypt(algorithm, cryptoKey, data.getEncryptData()).then(function(data) {
|
||||
return new Uint8Array(data);
|
||||
});
|
||||
}
|
||||
CWebCrypto.prototype.encrypt = function(key, data) {
|
||||
const cryptoKey = key.getCryptoKey();
|
||||
const encryptParams = key.getEncryptParams();
|
||||
const algorithm = encryptParams.getCryptoParams();
|
||||
return this.subtle.encrypt(algorithm, cryptoKey, data).then(function (encryptedData) {
|
||||
const encryptData = new EncryptData(encryptedData, encryptParams);
|
||||
return encryptData.export();
|
||||
});
|
||||
}
|
||||
CWebCrypto.prototype.exportKey = function(key) {
|
||||
const cryptoKey = key.getCryptoKey();
|
||||
const format = key.getCryptoFormat();
|
||||
return this.subtle.exportKey(format, cryptoKey);
|
||||
}
|
||||
CWebCrypto.prototype.generateKey = function(params, aesKey) {
|
||||
const oThis = this;
|
||||
const cryptoParams = params.getKeyGenCryptoParams();
|
||||
const cryptoUsages = params.getCryptoUsages();
|
||||
let saveCryptoKey;
|
||||
return this.subtle.generateKey(cryptoParams, true, cryptoUsages).then(function(cryptoKey) {
|
||||
saveCryptoKey = cryptoKey;
|
||||
if (cryptoKey.privateKey && cryptoKey.publicKey) {
|
||||
const publicKey = oThis.subtle.exportKey(c_oAscExportKeyFormat.spki, cryptoKey.publicKey);
|
||||
const privateKey = oThis.subtle.exportKey(c_oAscExportKeyFormat.pkcs8, cryptoKey.privateKey).then(function(data) {
|
||||
return aesKey.encrypt(data);
|
||||
});
|
||||
return Promise.all([publicKey, privateKey]);
|
||||
}
|
||||
return oThis.subtle.exportKey(c_oAscExportKeyFormat.raw, cryptoKey).then(function (data) {
|
||||
return aesKey.encrypt(data);
|
||||
});
|
||||
}).then(function(exportedKeys) {
|
||||
const importParams = params.getImportParams();
|
||||
if (Array.isArray(exportedKeys)) {
|
||||
const publicKeyBuffer = exportedKeys[0];
|
||||
const privateKeyBuffer = exportedKeys[1];
|
||||
if (params.isSign()) {
|
||||
return WebSignKeyPair.fromWebCrypto(publicKeyBuffer, saveCryptoKey.publicKey, privateKeyBuffer, saveCryptoKey.privateKey, importParams);
|
||||
}
|
||||
return WebEncryptKeyPair.fromWebCrypto(publicKeyBuffer, saveCryptoKey.publicKey, privateKeyBuffer, saveCryptoKey.privateKey, importParams);
|
||||
} else {
|
||||
return WebSymmetricKey.fromWebCrypto(exportedKeys, saveCryptoKey, importParams);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
CWebCrypto.prototype.randomUUID = function() {
|
||||
return this.crypto.randomUUID();
|
||||
}
|
||||
CWebCrypto.prototype.initKey = function (key, aesKey) {
|
||||
const binaryKey = key.getBinaryKey();
|
||||
const oThis = this;
|
||||
let binaryKeyPromise;
|
||||
if (aesKey) {
|
||||
binaryKeyPromise = aesKey.decrypt(binaryKey);
|
||||
} else {
|
||||
binaryKeyPromise = Promise.resolve(binaryKey);
|
||||
}
|
||||
return binaryKeyPromise.then(function(binaryCryptoData) {
|
||||
return oThis.subtle.importKey(key.getImportFormat(), binaryCryptoData, key.getImportCryptoParams(), true, key.getCryptoUsages());
|
||||
}).then(function (cryptoKey) {
|
||||
key.setCryptoKey(cryptoKey);
|
||||
});
|
||||
};
|
||||
|
||||
export function getCrypto() {
|
||||
return new CWebCrypto();
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
export const c_oAscDigestType = {
|
||||
SHA1: 1,
|
||||
SHA256: 2,
|
||||
SHA384: 3,
|
||||
SHA512: 4
|
||||
};
|
||||
|
||||
export const c_oAscCryptoDigestType = {};
|
||||
c_oAscCryptoDigestType[c_oAscDigestType.SHA1] = "SHA-1";
|
||||
c_oAscCryptoDigestType[c_oAscDigestType.SHA256] = "SHA-256";
|
||||
c_oAscCryptoDigestType[c_oAscDigestType.SHA384] = "SHA-384";
|
||||
c_oAscCryptoDigestType[c_oAscDigestType.SHA512] = "SHA-512";
|
||||
|
||||
export const c_oAscExportKeyFormat = {
|
||||
pkcs8: "pkcs8",
|
||||
spki: "spki",
|
||||
raw: "raw"
|
||||
};
|
||||
|
||||
export const c_oAscKeyStorageType = {
|
||||
NoType: 0,
|
||||
WebSymmetricKey: 1,
|
||||
WebSignKeyPair: 2,
|
||||
WebEncryptKeyPair: 3,
|
||||
Ed25519ImportParams: 6,
|
||||
EncryptData: 7,
|
||||
RSAOAEPImportParams: 8,
|
||||
RSAOAEPKeyGenParams: 9,
|
||||
Ed25519KeyGenParams: 10,
|
||||
AesGCMCryptoParams: 11,
|
||||
AesGCMKeyGenParams: 12,
|
||||
PBKDF2Params: 14,
|
||||
WebPrivateSignKey: 15,
|
||||
WebPrivateEncryptKey: 16,
|
||||
WebPublicSignKey: 17,
|
||||
WebPublicEncryptKey: 18,
|
||||
RsaOAEPCryptoParams: 19
|
||||
};
|
||||
|
||||
export const c_oAscCryptoRsaType = {};
|
||||
c_oAscCryptoRsaType[c_oAscKeyStorageType.RSAOAEPKeyGenParams] = "RSA-OAEP";
|
||||
c_oAscCryptoRsaType[c_oAscKeyStorageType.RSAOAEPImportParams] = "RSA-OAEP";
|
||||
|
||||
export const c_oAscCryptoAesType = {};
|
||||
c_oAscCryptoAesType[c_oAscKeyStorageType.AesGCMKeyGenParams] = "AES-GCM";
|
||||
@ -0,0 +1,34 @@
|
||||
import {c_oAscKeyStorageType} from "./defines";
|
||||
import {
|
||||
EncryptData,
|
||||
WebEncryptKeyPair, WebPrivateEncryptKey,
|
||||
WebPrivateSignKey, WebPublicEncryptKey,
|
||||
WebPublicSignKey,
|
||||
WebSignKeyPair,
|
||||
WebSymmetricKey
|
||||
} from "./keys";
|
||||
import {
|
||||
AesGcmCryptoParams, AesGcmKeyGenParams,
|
||||
Ed25519ImportParams,
|
||||
Ed25519KeyGenParams, PBKDF2Params, RsaOAEPCryptoParams,
|
||||
RsaOAEPImportParams,
|
||||
RsaOAEPKeyGenParams
|
||||
} from "./params";
|
||||
|
||||
export const c_oAscObjectFactory = {};
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.WebSymmetricKey] = WebSymmetricKey;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.WebSignKeyPair] = WebSignKeyPair;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.WebEncryptKeyPair] = WebEncryptKeyPair;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.Ed25519ImportParams] = Ed25519ImportParams;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.EncryptData] = EncryptData;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.RSAOAEPImportParams] = RsaOAEPImportParams;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.RSAOAEPKeyGenParams] = RsaOAEPKeyGenParams;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.Ed25519KeyGenParams] = Ed25519KeyGenParams;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.AesGCMCryptoParams] = AesGcmCryptoParams;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.AesGCMKeyGenParams] = AesGcmKeyGenParams;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.PBKDF2Params] = PBKDF2Params;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.WebPrivateSignKey] = WebPrivateSignKey;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.WebPrivateEncryptKey] = WebPrivateEncryptKey;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.WebPublicSignKey] = WebPublicSignKey;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.WebPublicEncryptKey] = WebPublicEncryptKey;
|
||||
c_oAscObjectFactory[c_oAscKeyStorageType.RsaOAEPCryptoParams] = RsaOAEPCryptoParams;
|
||||
@ -0,0 +1,3 @@
|
||||
export {KeyStorage} from './key-storage';
|
||||
export {c_oAscDigestType} from './defines';
|
||||
export {RsaOAEPKeyGenParams} from './params';
|
||||
@ -0,0 +1,121 @@
|
||||
import {getCrypto} from "./crypto";
|
||||
import {BinaryReader, readObject} from "./serialize/reader";
|
||||
import {BinaryWriter, writeObject} from "./serialize/writer";
|
||||
import {PBKDF2Params, PBKDFSaltLength} from "./params";
|
||||
|
||||
export function KeyStorage() {
|
||||
this.keys = [];
|
||||
this.version = 1;
|
||||
this.pbkdfParams = null;
|
||||
this.isInit = false;
|
||||
}
|
||||
KeyStorage.prototype.init = function() {
|
||||
this.pbkdfParams = new PBKDF2Params(true);
|
||||
this.isInit = true;
|
||||
};
|
||||
KeyStorage.prototype.export = function () {
|
||||
const writer = new BinaryWriter();
|
||||
writer.WriteLong(this.version);
|
||||
switch (this.version) {
|
||||
case 1: {
|
||||
writeObject(writer, this.pbkdfParams);
|
||||
writer.WriteLong(this.keys.length);
|
||||
for (let i = 0; i < this.keys.length; i++) {
|
||||
const key = this.keys[i];
|
||||
writeObject(writer, key);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return writer.GetData();
|
||||
};
|
||||
KeyStorage.prototype.import = function (binaryData, masterPassword) {
|
||||
const oThis = this;
|
||||
const reader = new BinaryReader(binaryData, binaryData.length);
|
||||
this.version = reader.GetLong();
|
||||
switch (this.version) {
|
||||
case 1: {
|
||||
this.setPBKDFParams(readObject(reader));
|
||||
const length = reader.GetLong();
|
||||
const keys = [];
|
||||
for (let i = 0; i < length; i++) {
|
||||
const key = readObject(reader);
|
||||
if (key) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
const crypto = getCrypto();
|
||||
return crypto.getAesKey(masterPassword, this.pbkdfParams).then(function(aesKey) {
|
||||
const initKeys = keys.map(function (key) {
|
||||
return key.initKey(aesKey);
|
||||
});
|
||||
return Promise.all(initKeys).then(function() {
|
||||
return oThis.addKeys(keys);
|
||||
});
|
||||
});
|
||||
}
|
||||
default: {
|
||||
return Promise.resolve();
|
||||
}
|
||||
}
|
||||
};
|
||||
KeyStorage.prototype.changeMasterPassword = function (oldMasterPassword, newMasterPassword) {
|
||||
const oThis = this;
|
||||
const oldPBKDFParams = this.pbkdfParams;
|
||||
const newPBKDFParams = new PBKDF2Params(true);
|
||||
const crypto = getCrypto();
|
||||
return Promise.all([crypto.getAesKey(oldMasterPassword, oldPBKDFParams), crypto.getAesKey(newMasterPassword, newPBKDFParams)]).then(function(aesKeys) {
|
||||
const keys = oThis.keys.map(function(key) {
|
||||
return key.changeMasterPassword(aesKeys[0], aesKeys[1]);
|
||||
});
|
||||
return Promise.all(keys);
|
||||
}).then(function() {
|
||||
oThis.pbkdfParams = newPBKDFParams;
|
||||
});
|
||||
};
|
||||
KeyStorage.prototype.getKeyByGuid = function (guid) {
|
||||
for (let i = 0; i < this.keys.length; i++) {
|
||||
const key = this.keys[i];
|
||||
if (guid === key.guid) {
|
||||
return key;
|
||||
}
|
||||
}
|
||||
};
|
||||
KeyStorage.prototype.getValidKeys = function () {
|
||||
const keys = [];
|
||||
for (let i = 0; i < this.keys.length; i++) {
|
||||
const key = this.keys[i];
|
||||
if (key.isValid) {
|
||||
keys.push(key);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
KeyStorage.prototype.generateKey = function (keyParams, masterPassword) {
|
||||
const crypto = getCrypto();
|
||||
return crypto.getAesKey(masterPassword, this.pbkdfParams).then(function(aesKey) {
|
||||
return crypto.generateKey(keyParams, aesKey);
|
||||
});
|
||||
};
|
||||
KeyStorage.prototype.addKeys = function (keys) {
|
||||
this.keys.push.apply(this.keys, keys);
|
||||
};
|
||||
KeyStorage.prototype.setKeys = function (keys) {
|
||||
this.keys = keys;
|
||||
};
|
||||
KeyStorage.prototype.getKeyByPublicKey = function(publicKeyData) {
|
||||
const reader = new BinaryReader(publicKeyData, publicKeyData.length);
|
||||
const publicKey = readObject(reader);
|
||||
for (let i = 0; i < this.keys.length; i++) {
|
||||
if (this.keys[i].isHavePublicKey(publicKey)) {
|
||||
return this.keys[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
KeyStorage.prototype.setPBKDFParams = function(params) {
|
||||
this.pbkdfParams = params;
|
||||
};
|
||||
565
DesktopEditor/xmlsec/src/wasm/extension/key-storage/src/keys.js
Normal file
@ -0,0 +1,565 @@
|
||||
import {CryptoBase, initClass, isEqualArrays} from "./utils";
|
||||
import {c_oAscExportKeyFormat, c_oAscKeyStorageType} from "./defines";
|
||||
import {BinaryWriter, writeBool, writeBuffer, writeLong, writeObject, writeString} from "./serialize/writer";
|
||||
import {getCrypto} from "./crypto";
|
||||
import {BinaryReader, readBool, readBuffer, readLong, readObject, readString} from "./serialize/reader";
|
||||
|
||||
function CryptoKeyBase() {
|
||||
CryptoBase.call(this);
|
||||
this.date = null;
|
||||
this.isValid = false;
|
||||
this.uid = null;
|
||||
this.version = 1;
|
||||
this.params = null;
|
||||
this.type = c_oAscKeyStorageType.NoType;
|
||||
}
|
||||
initClass(CryptoKeyBase, CryptoBase);
|
||||
CryptoKeyBase.import = function(reader, version, symmetricKey) {};
|
||||
CryptoKeyBase.prototype.init = function() {
|
||||
const crypto = getCrypto();
|
||||
this.setDate(new Date());
|
||||
this.setIsValid(true);
|
||||
this.setUID(crypto.randomUUID());
|
||||
}
|
||||
CryptoKeyBase.prototype.export = function(writer) {
|
||||
|
||||
};
|
||||
CryptoKeyBase.prototype.encrypt = function() {
|
||||
|
||||
};
|
||||
CryptoKeyBase.prototype.decrypt = function() {
|
||||
|
||||
};
|
||||
CryptoKeyBase.prototype.setDate = function(date) {
|
||||
this.date = new Date(date);
|
||||
};
|
||||
CryptoKeyBase.prototype.setIsValid = function(isValid) {
|
||||
this.isValid = isValid;
|
||||
};
|
||||
CryptoKeyBase.prototype.setUID = function(uid) {
|
||||
this.uid = uid;
|
||||
};
|
||||
CryptoKeyBase.prototype.setVersion = function(version) {
|
||||
this.version = version;
|
||||
};
|
||||
CryptoKeyBase.prototype.setType = function(type) {
|
||||
this.type = type;
|
||||
};
|
||||
CryptoKeyBase.prototype.setParams = function(params) {
|
||||
this.params = params;
|
||||
};
|
||||
|
||||
|
||||
function WebKeyPair() {
|
||||
CryptoKeyBase.call(this);
|
||||
this.privateKey = null;
|
||||
this.publicKey = null;
|
||||
this.version = 1;
|
||||
}
|
||||
initClass(WebKeyPair, CryptoKeyBase);
|
||||
WebKeyPair.import = function(reader) {
|
||||
const keyPair = new this();
|
||||
keyPair.setVersion(readLong(reader));
|
||||
switch (keyPair.version) {
|
||||
case 1: {
|
||||
keyPair.setDate(readString(reader));
|
||||
keyPair.setUID(readString(reader));
|
||||
keyPair.setIsValid(readBool(reader));
|
||||
keyPair.setPublicKey(readObject(reader));
|
||||
keyPair.setPrivateKey(readObject(reader));
|
||||
keyPair.setParams(readObject(reader));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return keyPair;
|
||||
};
|
||||
|
||||
WebKeyPair.prototype.export = function(writer) {
|
||||
writeLong(writer, this.version);
|
||||
switch (this.version) {
|
||||
case 1: {
|
||||
writeString(writer, this.date.toISOString());
|
||||
writeString(writer, this.uid);
|
||||
writeBool(writer, this.isValid);
|
||||
writeObject(writer, this.publicKey);
|
||||
writeObject(writer, this.privateKey);
|
||||
writeObject(writer, this.params);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
WebKeyPair.prototype.setParams = function(params) {
|
||||
this.params = params;
|
||||
if (this.publicKey) {
|
||||
this.publicKey.setParams(params);
|
||||
}
|
||||
if (this.privateKey) {
|
||||
this.privateKey.setParams(params);
|
||||
}
|
||||
};
|
||||
WebKeyPair.prototype.setPublicKey = function(publicKey) {
|
||||
this.publicKey = publicKey;
|
||||
};
|
||||
WebKeyPair.prototype.setPrivateKey = function(privateKey) {
|
||||
this.privateKey = privateKey;
|
||||
};
|
||||
WebKeyPair.fromWebCrypto = function(publicKeyBuffer, publicCryptoKey, privateKeyBuffer, privateCryptoKey, importParams) {
|
||||
const keyPair = new this();
|
||||
keyPair.init();
|
||||
const publicKey = new (this.getPublicKeyConstructor())();
|
||||
publicKey.setBinaryKey(publicKeyBuffer);
|
||||
publicKey.setCryptoKey(publicCryptoKey);
|
||||
const privateKey = new (this.getPrivateKeyConstructor())();
|
||||
privateKey.setBinaryKey(privateKeyBuffer);
|
||||
privateKey.setCryptoKey(privateCryptoKey);
|
||||
keyPair.setPublicKey(publicKey);
|
||||
keyPair.setPrivateKey(privateKey);
|
||||
keyPair.setParams(importParams);
|
||||
return keyPair;
|
||||
};
|
||||
WebKeyPair.getPublicKeyConstructor = function() {
|
||||
return WebPublicKey;
|
||||
};
|
||||
WebKeyPair.getPrivateKeyConstructor = function() {
|
||||
return WebPrivateKey;
|
||||
};
|
||||
WebKeyPair.prototype.initKey = function (aesKey) {
|
||||
const crypto = getCrypto();
|
||||
return Promise.all([crypto.initKey(this.publicKey), crypto.initKey(this.privateKey, aesKey)]);
|
||||
};
|
||||
WebKeyPair.prototype.isHavePublicKey = function(publicKey) {
|
||||
return this.publicKey.isEqual(publicKey);
|
||||
};
|
||||
WebKeyPair.prototype.getExportPublicKey =function() {
|
||||
const writer = new BinaryWriter();
|
||||
writeObject(writer, this.publicKey);
|
||||
return writer.GetData();
|
||||
};
|
||||
WebKeyPair.prototype.changeMasterPassword = function(oldAesKey, newAesKey) {
|
||||
return this.privateKey.changeMasterPassword(oldAesKey, newAesKey);
|
||||
};
|
||||
export function WebSignKeyPair() {
|
||||
WebKeyPair.call(this);
|
||||
}
|
||||
initClass(WebSignKeyPair, WebKeyPair, c_oAscKeyStorageType.WebSignKeyPair);
|
||||
WebSignKeyPair.import = WebKeyPair.import;
|
||||
WebSignKeyPair.fromWebCrypto = WebKeyPair.fromWebCrypto;
|
||||
WebSignKeyPair.getPublicKeyConstructor = function() {
|
||||
return WebPublicSignKey;
|
||||
};
|
||||
WebSignKeyPair.getPrivateKeyConstructor = function() {
|
||||
return WebPrivateSignKey;
|
||||
};
|
||||
WebEncryptKeyPair.prototype.verify = function (data) {
|
||||
return this.publicKey.verify(data);
|
||||
};
|
||||
WebEncryptKeyPair.prototype.sign = function (data) {
|
||||
return this.privateKey.sign(data);
|
||||
};
|
||||
|
||||
export function WebEncryptKeyPair() {
|
||||
WebKeyPair.call(this);
|
||||
this.privateKey = null;
|
||||
this.publicKey = null;
|
||||
}
|
||||
initClass(WebEncryptKeyPair, WebKeyPair, c_oAscKeyStorageType.WebEncryptKeyPair);
|
||||
WebEncryptKeyPair.import = WebKeyPair.import;
|
||||
WebEncryptKeyPair.fromWebCrypto = WebKeyPair.fromWebCrypto;
|
||||
WebEncryptKeyPair.getPublicKeyConstructor = function() {
|
||||
return WebPublicEncryptKey;
|
||||
};
|
||||
WebEncryptKeyPair.getPrivateKeyConstructor = function() {
|
||||
return WebPrivateEncryptKey;
|
||||
};
|
||||
WebEncryptKeyPair.prototype.encrypt = function (data) {
|
||||
return this.publicKey.encrypt(data);
|
||||
};
|
||||
WebEncryptKeyPair.prototype.decrypt = function (data) {
|
||||
return this.privateKey.decrypt(data);
|
||||
};
|
||||
|
||||
function AsymmetricKey() {
|
||||
CryptoBase.call(this);
|
||||
this.binaryKey = null;
|
||||
this.cryptoKey = null;
|
||||
this.version = 1;
|
||||
this.params = null;
|
||||
}
|
||||
initClass(AsymmetricKey, CryptoBase);
|
||||
AsymmetricKey.prototype.setBinaryKey = function(binaryKey) {
|
||||
this.binaryKey = new Uint8Array(binaryKey);
|
||||
};
|
||||
AsymmetricKey.prototype.setCryptoKey = function(cryptoKey) {
|
||||
this.cryptoKey = cryptoKey;
|
||||
};
|
||||
AsymmetricKey.prototype.setVersion = function(version) {
|
||||
this.version = version;
|
||||
};
|
||||
AsymmetricKey.prototype.changeMasterPassword = function(oldAesKey, newAesKey) {
|
||||
return Promise.resolve();
|
||||
};
|
||||
AsymmetricKey.prototype.getCryptoKey = function() {
|
||||
return this.cryptoKey;
|
||||
};
|
||||
AsymmetricKey.prototype.setParams = function(params) {
|
||||
this.params = params;
|
||||
}
|
||||
AsymmetricKey.prototype.getImportCryptoParams = function() {
|
||||
return this.params.getCryptoParams();
|
||||
};
|
||||
AsymmetricKey.prototype.getBinaryKey = function() {
|
||||
return this.binaryKey;
|
||||
};
|
||||
AsymmetricKey.prototype.getCryptoKey = function() {
|
||||
return this.cryptoKey;
|
||||
};
|
||||
AsymmetricKey.prototype.getEncryptParams = function() {
|
||||
return this.params.getEncryptParams();
|
||||
}
|
||||
|
||||
function WebPrivateKey() {
|
||||
AsymmetricKey.call(this);
|
||||
}
|
||||
initClass(WebPrivateKey, AsymmetricKey);
|
||||
|
||||
WebPrivateKey.import = function(reader) {
|
||||
const key = new this();
|
||||
key.setVersion(readLong(reader));
|
||||
switch (key.version) {
|
||||
case 1: {
|
||||
key.setBinaryKey(readBuffer(reader));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return key;
|
||||
};
|
||||
WebPrivateKey.prototype.export = function(writer) {
|
||||
writeLong(writer, this.version);
|
||||
switch (this.version) {
|
||||
case 1: {
|
||||
writeBuffer(writer, this.binaryKey);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
WebPrivateKey.prototype.decrypt = function(data) {
|
||||
const crypto = getCrypto();
|
||||
return crypto.decrypt(this, EncryptData.import(data));
|
||||
};
|
||||
WebPrivateKey.prototype.sign = function(data) {
|
||||
const crypto = getCrypto();
|
||||
return crypto.sign(this, data);
|
||||
};
|
||||
WebPrivateKey.prototype.getImportFormat = function () {
|
||||
return c_oAscExportKeyFormat.pkcs8;
|
||||
}
|
||||
WebPrivateKey.prototype.changeMasterPassword = function(oldAesKey, newAesKey) {
|
||||
const oThis = this;
|
||||
return oldAesKey.decrypt(this.binaryKey).then(function(decryptedKey) {
|
||||
return newAesKey.encrypt(decryptedKey);
|
||||
}).then(function(encryptedKey) {
|
||||
oThis.binaryKey = encryptedKey;
|
||||
});
|
||||
};
|
||||
|
||||
export function WebPrivateSignKey() {
|
||||
WebPrivateKey.call(this);
|
||||
}
|
||||
initClass(WebPrivateSignKey, WebPrivateKey, c_oAscKeyStorageType.WebPrivateSignKey);
|
||||
WebPrivateSignKey.import = WebPrivateKey.import;
|
||||
WebPrivateSignKey.prototype.getCryptoUsages = function () {
|
||||
return ["sign"];
|
||||
};
|
||||
|
||||
export function WebPrivateEncryptKey() {
|
||||
WebPrivateKey.call(this);
|
||||
}
|
||||
initClass(WebPrivateEncryptKey, WebPrivateKey, c_oAscKeyStorageType.WebPrivateEncryptKey);
|
||||
WebPrivateEncryptKey.import = WebPrivateKey.import;
|
||||
WebPrivateEncryptKey.prototype.getCryptoUsages = function () {
|
||||
return ["decrypt"];
|
||||
};
|
||||
|
||||
function WebPublicKey() {
|
||||
AsymmetricKey.call(this);
|
||||
}
|
||||
initClass(WebPublicKey, AsymmetricKey);
|
||||
|
||||
WebPublicKey.import = function(reader) {
|
||||
const key = new this();
|
||||
key.setVersion(readLong(reader));
|
||||
switch (key.version) {
|
||||
case 1: {
|
||||
key.setBinaryKey(readBuffer(reader));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return key;
|
||||
};
|
||||
WebPublicKey.prototype.export = function(writer) {
|
||||
writeLong(writer, this.version);
|
||||
switch (this.version) {
|
||||
case 1: {
|
||||
writeBuffer(writer, this.binaryKey);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
WebPublicKey.prototype.encrypt = function(data) {
|
||||
const crypto = getCrypto();
|
||||
return crypto.encrypt(this, data);
|
||||
};
|
||||
WebPublicKey.prototype.verify = function(data) {
|
||||
const crypto = getCrypto();
|
||||
return crypto.verify(this, data);
|
||||
};
|
||||
WebPublicKey.prototype.getImportFormat = function () {
|
||||
return c_oAscExportKeyFormat.spki;
|
||||
}
|
||||
WebPublicKey.prototype.isEqual = function (publicKey) {
|
||||
return isEqualArrays(this.binaryKey, publicKey.binaryKey);
|
||||
};
|
||||
|
||||
export function WebPublicSignKey() {
|
||||
WebPublicKey.call(this);
|
||||
}
|
||||
initClass(WebPublicSignKey, WebPublicKey, c_oAscKeyStorageType.WebPublicSignKey);
|
||||
WebPublicSignKey.import = WebPublicKey.import;
|
||||
WebPublicSignKey.prototype.getCryptoUsages = function () {
|
||||
return ["verify"];
|
||||
};
|
||||
|
||||
export function WebPublicEncryptKey() {
|
||||
WebPublicKey.call(this);
|
||||
}
|
||||
initClass(WebPublicEncryptKey, WebPublicKey, c_oAscKeyStorageType.WebPublicEncryptKey);
|
||||
WebPublicEncryptKey.import = WebPublicKey.import;
|
||||
WebPublicEncryptKey.prototype.getCryptoUsages = function () {
|
||||
return ["encrypt"];
|
||||
};
|
||||
|
||||
export function WebSymmetricKey() {
|
||||
CryptoKeyBase.call(this);
|
||||
this.version = 1;
|
||||
this.cryptoKey = null;
|
||||
this.binaryKey = null;
|
||||
}
|
||||
initClass(WebSymmetricKey, CryptoKeyBase, c_oAscKeyStorageType.WebSymmetricKey);
|
||||
|
||||
WebSymmetricKey.import = function(reader) {
|
||||
const symmetricKey = new WebSymmetricKey();
|
||||
symmetricKey.setVersion(readLong(reader));
|
||||
switch (symmetricKey.version) {
|
||||
case 1: {
|
||||
symmetricKey.setDate(readString(reader));
|
||||
symmetricKey.setUID(readString(reader));
|
||||
symmetricKey.setIsValid(readBool(reader));
|
||||
symmetricKey.setBinaryKey(readBuffer(reader));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return symmetricKey;
|
||||
};
|
||||
WebSymmetricKey.fromWebCrypto = function(symmetricKeyBuffer, symmetricCryptoKey, importParams) {
|
||||
const key = new WebSymmetricKey();
|
||||
key.init();
|
||||
key.setBinaryKey(symmetricKeyBuffer);
|
||||
key.setParams(importParams);
|
||||
return key;
|
||||
};
|
||||
WebSymmetricKey.fromCryptoKey = function(cryptoKey, pbkdfParams) {
|
||||
const key = new WebSymmetricKey();
|
||||
key.init();
|
||||
key.setCryptoKey(cryptoKey);
|
||||
key.setParams(pbkdfParams);
|
||||
return key;
|
||||
};
|
||||
WebSymmetricKey.prototype.setBinaryKey = function(buffer) {
|
||||
this.binaryKey = new Uint8Array(buffer);
|
||||
};
|
||||
WebSymmetricKey.prototype.getBinaryKey = function() {
|
||||
return this.binaryKey;
|
||||
};
|
||||
WebSymmetricKey.prototype.setCryptoKey = function(cryptoKey) {
|
||||
this.cryptoKey = cryptoKey;
|
||||
};
|
||||
WebSymmetricKey.prototype.getCryptoKey = function() {
|
||||
return this.cryptoKey;
|
||||
};
|
||||
WebSymmetricKey.prototype.export = function(writer) {
|
||||
writeLong(writer, this.version);
|
||||
switch (this.version) {
|
||||
case 1: {
|
||||
writeString(writer, this.date.toISOString());
|
||||
writeString(writer, this.uid);
|
||||
writeBool(writer, this.isValid);
|
||||
writeBuffer(writer, this.binaryKey);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
WebSymmetricKey.prototype.encrypt = function(data) {
|
||||
const crypto = getCrypto();
|
||||
return crypto.encrypt(this, data);
|
||||
};
|
||||
WebSymmetricKey.prototype.decrypt = function(data) {
|
||||
const crypto = getCrypto();
|
||||
return crypto.decrypt(this, EncryptData.import(data));
|
||||
};
|
||||
WebSymmetricKey.prototype.initKey = function(aesKey) {
|
||||
const crypto = getCrypto();
|
||||
return crypto.initKey(this, aesKey);
|
||||
};
|
||||
WebSymmetricKey.prototype.getImportFormat = function() {
|
||||
return c_oAscExportKeyFormat.raw;
|
||||
};
|
||||
WebSymmetricKey.prototype.getImportCryptoParams = function() {
|
||||
|
||||
};
|
||||
WebSymmetricKey.prototype.getCryptoUsages = function() {
|
||||
return ["encrypt", "decrypt"];
|
||||
};
|
||||
WebSymmetricKey.prototype.isHavePublicKey = function(publicKey) {
|
||||
return false;
|
||||
};
|
||||
WebSymmetricKey.prototype.getExportPublicKey = function() {
|
||||
return null;
|
||||
};
|
||||
WebSymmetricKey.prototype.changeMasterPassword = function(oldAesKey, newAesKey) {
|
||||
const oThis= this;
|
||||
return oldAesKey.decrypt(this.binaryKey).then(function(decryptedKey) {
|
||||
return newAesKey.encrypt(decryptedKey);
|
||||
}).then(function(encryptedKey) {
|
||||
oThis.binaryKey = encryptedKey;
|
||||
});
|
||||
};
|
||||
WebSymmetricKey.prototype.getEncryptParams = function() {
|
||||
return this.params.getEncryptParams();
|
||||
};
|
||||
|
||||
export function EncryptData(encryptData, params) {
|
||||
CryptoBase.call(this);
|
||||
this.version = 1;
|
||||
this.encryptData = encryptData ? new Uint8Array(encryptData) : null;
|
||||
this.params = params || null;
|
||||
}
|
||||
initClass(EncryptData, CryptoBase, c_oAscKeyStorageType.EncryptData);
|
||||
EncryptData.import = function(binaryData) {
|
||||
const data = new EncryptData();
|
||||
const reader = new BinaryReader(binaryData);
|
||||
data.setVersion(readLong(reader));
|
||||
switch (data.version) {
|
||||
case 1: {
|
||||
data.setEncryptParams(readObject(reader));
|
||||
data.setEncryptData(readBuffer(reader));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
};
|
||||
EncryptData.prototype.export = function () {
|
||||
const writer = new BinaryWriter();
|
||||
writeLong(writer, this.version);
|
||||
switch (this.version) {
|
||||
case 1: {
|
||||
writeObject(writer, this.params);
|
||||
writeBuffer(writer, this.encryptData);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return writer.GetData();
|
||||
};
|
||||
EncryptData.prototype.setEncryptData = function (encryptData) {
|
||||
this.encryptData = encryptData;
|
||||
};
|
||||
EncryptData.prototype.setEncryptParams = function (params) {
|
||||
this.params = params;
|
||||
};
|
||||
EncryptData.prototype.getEncryptParams = function () {
|
||||
return this.params;
|
||||
};
|
||||
EncryptData.prototype.getEncryptData = function () {
|
||||
return this.encryptData;
|
||||
};
|
||||
EncryptData.prototype.setVersion = function(version) {
|
||||
this.version = version;
|
||||
};
|
||||
|
||||
|
||||
function ExternalMasterPassword() {
|
||||
this.version = 1;
|
||||
this.pbkdfParams = null;
|
||||
this.masterPassword = null;
|
||||
}
|
||||
ExternalMasterPassword.import = function(binaryData) {
|
||||
const reader = new BinaryReader(binaryData, binaryData.length);
|
||||
const masterPasswordInfo = new ExternalMasterPassword(true);
|
||||
masterPasswordInfo.setVersion(readLong(reader));
|
||||
switch (masterPasswordInfo.version) {
|
||||
case 1: {
|
||||
masterPasswordInfo.setMasterPassword(readBuffer(reader));
|
||||
masterPasswordInfo.setPBKDFParams(readObject(reader));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return masterPasswordInfo;
|
||||
};
|
||||
ExternalMasterPassword.prototype.export = function() {
|
||||
const writer = new BinaryWriter();
|
||||
writeLong(writer, this.version);
|
||||
switch (this.version) {
|
||||
case 1: {
|
||||
writeBuffer(writer, this.masterPassword);
|
||||
writeObject(writer, this.pbkdfParams);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
ExternalMasterPassword.prototype.init = function() {
|
||||
this.setPBKDFParams(new PBKDF2Params(true));
|
||||
};
|
||||
ExternalMasterPassword.prototype.setVersion = function(version) {
|
||||
this.version = version;
|
||||
};
|
||||
ExternalMasterPassword.prototype.setMasterPassword = function(masterPassword) {
|
||||
this.masterPassword = masterPassword;
|
||||
};
|
||||
ExternalMasterPassword.prototype.getMasterPassword = function(masterPassword) {
|
||||
return masterPassword;
|
||||
};
|
||||
ExternalMasterPassword.prototype.setPBKDFParams = function(params) {
|
||||
this.pbkdfParams = params;
|
||||
};
|
||||
@ -0,0 +1,314 @@
|
||||
import {
|
||||
c_oAscCryptoAesType,
|
||||
c_oAscCryptoDigestType,
|
||||
c_oAscCryptoRsaType,
|
||||
c_oAscDigestType,
|
||||
c_oAscKeyStorageType
|
||||
} from "./defines";
|
||||
import {readLong, readBuffer} from "./serialize/reader";
|
||||
import {writeBuffer, writeLong} from "./serialize/writer";
|
||||
import {CryptoBase, getRandomValues, initClass} from "./utils";
|
||||
|
||||
function AlgorithmParams() {
|
||||
CryptoBase.call(this);
|
||||
this.version = 1;
|
||||
}
|
||||
initClass(AlgorithmParams, CryptoBase);
|
||||
AlgorithmParams.import = function(reader) {
|
||||
const params = new this();
|
||||
params.setVersion(readLong(reader));
|
||||
switch (params.version) {
|
||||
case 1: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return params;
|
||||
};
|
||||
AlgorithmParams.prototype.setVersion = function (version) {
|
||||
this.version = version;
|
||||
};
|
||||
AlgorithmParams.prototype.export = function(writer) {
|
||||
writeLong(writer, this.version);
|
||||
switch (this.version) {
|
||||
case 1: {
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
AlgorithmParams.prototype.isSign = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
function RsaImportParams(hash) {
|
||||
AlgorithmParams.call(this);
|
||||
this.hash = hash;
|
||||
}
|
||||
initClass(RsaImportParams, AlgorithmParams);
|
||||
RsaImportParams.import = function(reader) {
|
||||
const params = new this();
|
||||
params.setVersion(readLong(reader));
|
||||
switch (params.version) {
|
||||
case 1: {
|
||||
params.setHash(readLong(reader));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return params;
|
||||
};
|
||||
RsaImportParams.prototype.export = function(writer) {
|
||||
writeLong(writer, this.version);
|
||||
switch (this.version) {
|
||||
case 1: {
|
||||
writeLong(writer, this.hash);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
RsaImportParams.prototype.setHash = function (hash) {
|
||||
this.hash = hash;
|
||||
};
|
||||
RsaImportParams.prototype.getCryptoParams = function() {
|
||||
return {
|
||||
name: c_oAscCryptoRsaType[this.objectType],
|
||||
hash: c_oAscCryptoDigestType[this.hash]
|
||||
};
|
||||
};
|
||||
export function RsaOAEPImportParams(hash) {
|
||||
RsaImportParams.call(this, hash);
|
||||
}
|
||||
initClass(RsaOAEPImportParams, RsaImportParams, c_oAscKeyStorageType.RSAOAEPImportParams);
|
||||
RsaOAEPImportParams.import = RsaImportParams.import;
|
||||
RsaOAEPImportParams.prototype.getEncryptParams = function() {
|
||||
return new RsaOAEPCryptoParams();
|
||||
};
|
||||
export function RsaOAEPCryptoParams() {
|
||||
AlgorithmParams.call(this);
|
||||
}
|
||||
initClass(RsaOAEPCryptoParams, AlgorithmParams, c_oAscKeyStorageType.RsaOAEPCryptoParams);
|
||||
RsaOAEPCryptoParams.import = function(reader) {
|
||||
const params = new RsaOAEPCryptoParams();
|
||||
params.setVersion(readLong(reader));
|
||||
return params;
|
||||
};
|
||||
RsaOAEPCryptoParams.prototype.export = function(writer) {
|
||||
writeLong(writer, this.version);
|
||||
};
|
||||
RsaOAEPCryptoParams.prototype.getCryptoParams = function() {
|
||||
return {
|
||||
name: "RSA-OAEP"
|
||||
}
|
||||
};
|
||||
function RsaKeyGenParams(hash, modulusLength, publicExponent) {
|
||||
RsaImportParams.call(this, hash);
|
||||
this.modulusLength = typeof modulusLength === "number" ? modulusLength : 2048;
|
||||
this.publicExponent = publicExponent || new Uint8Array([0x01, 0x00, 0x01]);
|
||||
}
|
||||
initClass(RsaKeyGenParams, RsaImportParams);
|
||||
RsaKeyGenParams.prototype.getImportParams = function() {
|
||||
return new RsaImportParams(this.hash);
|
||||
};
|
||||
RsaKeyGenParams.prototype.getKeyGenCryptoParams = function() {
|
||||
return {
|
||||
name: c_oAscCryptoRsaType[this.objectType],
|
||||
modulusLength: this.modulusLength,
|
||||
publicExponent: this.publicExponent,
|
||||
hash: c_oAscCryptoDigestType[this.hash]
|
||||
};
|
||||
};
|
||||
RsaKeyGenParams.prototype.getCryptoUsages = function() {
|
||||
return ['sign', 'verify'];
|
||||
};
|
||||
|
||||
export function RsaOAEPKeyGenParams(hash, modulusLength, publicExponent) {
|
||||
RsaKeyGenParams.call(this, hash, modulusLength, publicExponent);
|
||||
}
|
||||
initClass(RsaOAEPKeyGenParams, RsaKeyGenParams, c_oAscKeyStorageType.RSAOAEPKeyGenParams);
|
||||
RsaOAEPKeyGenParams.prototype.getCryptoUsages = function() {
|
||||
return ['encrypt', 'decrypt'];
|
||||
};
|
||||
RsaOAEPKeyGenParams.prototype.getImportParams = function() {
|
||||
return new RsaOAEPImportParams(this.hash);
|
||||
};
|
||||
export function Ed25519ImportParams() {
|
||||
AlgorithmParams.call(this);
|
||||
}
|
||||
initClass(Ed25519ImportParams, AlgorithmParams, c_oAscKeyStorageType.Ed25519ImportParams);
|
||||
Ed25519ImportParams.import = AlgorithmParams.import;
|
||||
|
||||
export function Ed25519KeyGenParams() {
|
||||
Ed25519ImportParams.call(this);
|
||||
}
|
||||
initClass(Ed25519KeyGenParams, Ed25519ImportParams, c_oAscKeyStorageType.Ed25519KeyGenParams);
|
||||
Ed25519KeyGenParams.prototype.getImportParams = function() {
|
||||
return new Ed25519ImportParams();
|
||||
};
|
||||
Ed25519KeyGenParams.prototype.getKeyGenCryptoParams = function() {
|
||||
return {
|
||||
name: "Ed25519"
|
||||
};
|
||||
};
|
||||
Ed25519KeyGenParams.prototype.getCryptoUsages = function() {
|
||||
return ['sign', 'verify'];
|
||||
};
|
||||
|
||||
export function AesGcmCryptoParams(iv, tagLength) {
|
||||
AlgorithmParams.call(this);
|
||||
this.iv = iv || null;
|
||||
this.tagLength = typeof tagLength === "number" ? tagLength : null;
|
||||
}
|
||||
initClass(AesGcmCryptoParams, AlgorithmParams, c_oAscKeyStorageType.AesGCMCryptoParams);
|
||||
AesGcmCryptoParams.import = function (reader) {
|
||||
const params = new AesGcmCryptoParams();
|
||||
params.setVersion(readLong(reader));
|
||||
switch (params.version) {
|
||||
case 1: {
|
||||
params.setIV(readBuffer(reader));
|
||||
params.setTagLength(readLong(reader));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return params;
|
||||
};
|
||||
AesGcmCryptoParams.prototype.setIV = function (iv) {
|
||||
this.iv = iv;
|
||||
};
|
||||
AesGcmCryptoParams.prototype.setTagLength = function (tagLength) {
|
||||
this.tagLength = tagLength;
|
||||
};
|
||||
AesGcmCryptoParams.prototype.export = function(writer) {
|
||||
writeLong(writer, this.version);
|
||||
switch (this.version) {
|
||||
case 1: {
|
||||
writeBuffer(writer, this.iv);
|
||||
writeLong(writer, this.tagLength);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
AesGcmCryptoParams.prototype.init = function () {
|
||||
this.iv = getRandomValues(12);
|
||||
this.tagLength = 128;
|
||||
};
|
||||
AesGcmCryptoParams.prototype.getCryptoParams = function() {
|
||||
return {
|
||||
name: "AES-GCM",
|
||||
iv: this.iv,
|
||||
tagLength: this.tagLength
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function AesImportParams() {
|
||||
AlgorithmParams.call(this);
|
||||
}
|
||||
initClass(AesImportParams, AlgorithmParams);
|
||||
|
||||
function AesKeyGenParams(length) {
|
||||
AesImportParams.call(this);
|
||||
this.length = length;
|
||||
}
|
||||
initClass(AesKeyGenParams, AesImportParams);
|
||||
AesKeyGenParams.prototype.getImportParams = function() {
|
||||
return new AesImportParams();
|
||||
};
|
||||
AesKeyGenParams.prototype.getKeyGenCryptoParams = function() {
|
||||
return {
|
||||
name: c_oAscCryptoAesType[this.objectType],
|
||||
length: this.length
|
||||
}
|
||||
};
|
||||
AesKeyGenParams.prototype.getCryptoUsages = function() {
|
||||
return ['encrypt', 'decrypt'];
|
||||
};
|
||||
|
||||
function AesGcmImportParams() {
|
||||
AesImportParams.call(this);
|
||||
}
|
||||
initClass(AesGcmImportParams, AesImportParams);
|
||||
AesGcmImportParams.prototype.getEncryptParams = function() {
|
||||
const cryptoParams = new AesGcmCryptoParams();
|
||||
cryptoParams.init();
|
||||
return cryptoParams;
|
||||
};
|
||||
|
||||
export function AesGcmKeyGenParams() {
|
||||
AesKeyGenParams.call(this, 256);
|
||||
}
|
||||
initClass(AesGcmKeyGenParams, AesKeyGenParams, c_oAscKeyStorageType.AesGCMKeyGenParams);
|
||||
AesGcmKeyGenParams.prototype.getImportParams = function() {
|
||||
return new AesGcmImportParams();
|
||||
};
|
||||
|
||||
export const PBKDFSaltLength = 16;
|
||||
export function PBKDF2Params(isInit) {
|
||||
AlgorithmParams.call(this);
|
||||
this.iterations = isInit ? 600000 : null;
|
||||
this.hash = isInit ? c_oAscDigestType.SHA256 : null;
|
||||
this.salt = isInit ? getRandomValues(PBKDFSaltLength) : null;
|
||||
}
|
||||
initClass(PBKDF2Params, AlgorithmParams, c_oAscKeyStorageType.PBKDF2Params);
|
||||
PBKDF2Params.import = function(reader) {
|
||||
const params = new PBKDF2Params();
|
||||
params.setVersion(readLong(reader));
|
||||
switch (params.version) {
|
||||
case 1: {
|
||||
params.setIterations(readLong(reader));
|
||||
params.setHash(readLong(reader));
|
||||
params.setSalt(readBuffer(reader));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
PBKDF2Params.prototype.export = function(writer) {
|
||||
writeLong(writer, this.version);
|
||||
switch (this.version) {
|
||||
case 1: {
|
||||
writeLong(writer, this.iterations);
|
||||
writeLong(writer, this.hash);
|
||||
writeBuffer(writer, this.salt);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
PBKDF2Params.prototype.getCryptoParams = function() {
|
||||
return {
|
||||
name: 'PBKDF2',
|
||||
salt: this.salt,
|
||||
iterations: this.iterations,
|
||||
hash: c_oAscCryptoDigestType[this.hash]
|
||||
};
|
||||
};
|
||||
PBKDF2Params.prototype.setIterations = function(iterations) {
|
||||
this.iterations = iterations;
|
||||
};
|
||||
PBKDF2Params.prototype.setSalt = function(salt) {
|
||||
this.salt = salt;
|
||||
};
|
||||
PBKDF2Params.prototype.setHash = function(hash) {
|
||||
this.hash = hash;
|
||||
};
|
||||
@ -0,0 +1,141 @@
|
||||
import {c_oAscObjectFactory} from "../factory";
|
||||
|
||||
var c_oSerConstants = {
|
||||
ErrorFormat: -2,
|
||||
ErrorUnknown: -1,
|
||||
ReadOk:0,
|
||||
ReadUnknown:1,
|
||||
ErrorStream:0x55
|
||||
};
|
||||
|
||||
export function BinaryReader(data, size) {
|
||||
this.data = data;
|
||||
this.size = size;
|
||||
this.pos = 0;
|
||||
this.cur = 0;
|
||||
}
|
||||
|
||||
BinaryReader.prototype.Seek = function(_pos) {
|
||||
if (_pos > this.size)
|
||||
return c_oSerConstants.ErrorStream;
|
||||
this.pos = _pos;
|
||||
return c_oSerConstants.ReadOk;
|
||||
};
|
||||
BinaryReader.prototype.Seek2 = function(_cur) {
|
||||
if (_cur > this.size)
|
||||
return c_oSerConstants.ErrorStream;
|
||||
this.cur = _cur;
|
||||
return c_oSerConstants.ReadOk;
|
||||
};
|
||||
BinaryReader.prototype.Skip = function(_skip) {
|
||||
if (_skip < 0)
|
||||
return c_oSerConstants.ErrorStream;
|
||||
return this.Seek(this.pos + _skip);
|
||||
};
|
||||
BinaryReader.prototype.Skip2 = function(_skip) {
|
||||
if (_skip < 0)
|
||||
return c_oSerConstants.ErrorStream;
|
||||
return this.Seek2(this.cur + _skip);
|
||||
};
|
||||
BinaryReader.prototype.SkipRecord = function() {
|
||||
var _len = this.GetLong();
|
||||
this.Skip2(_len);
|
||||
};
|
||||
|
||||
BinaryReader.prototype.GetUChar = function() {
|
||||
if (this.cur >= this.size)
|
||||
return 0;
|
||||
return this.data[this.cur++];
|
||||
};
|
||||
|
||||
BinaryReader.prototype.GetBool = function() {
|
||||
var Value = this.GetUChar();
|
||||
return ( Value == 0 ? false : true );
|
||||
};
|
||||
|
||||
BinaryReader.prototype.GetLong = function() {
|
||||
if (this.cur + 3 >= this.size)
|
||||
return 0;
|
||||
return (this.data[this.cur++] | this.data[this.cur++] << 8 | this.data[this.cur++] << 16 | this.data[this.cur++] << 24);
|
||||
};
|
||||
BinaryReader.prototype.GetString = function() {
|
||||
const Len = this.GetLong();
|
||||
return this.GetStringLE(Len);
|
||||
};
|
||||
//String
|
||||
const global_string_decoder_le = (typeof TextDecoder !== "undefined") ? new TextDecoder('utf-16le', { ignoreBOM: true }) : null;
|
||||
BinaryReader.prototype.GetStringLE = function(len)
|
||||
{
|
||||
if (this.cur + len > this.size)
|
||||
return "";
|
||||
|
||||
if (global_string_decoder_le && undefined !== this.data.buffer) {
|
||||
const subdata = new Uint8Array(this.data.buffer, this.data.byteOffset + this.cur, len);
|
||||
this.cur += len;
|
||||
return global_string_decoder_le.decode(subdata);
|
||||
}
|
||||
|
||||
var a = [];
|
||||
for (var i = 0; i + 1 < len; i += 2)
|
||||
a.push(String.fromCharCode(this.data[this.cur + i] | this.data[this.cur + i + 1] << 8));
|
||||
this.cur += len;
|
||||
return a.join("");
|
||||
};
|
||||
BinaryReader.prototype.GetCurPos = function() {
|
||||
return this.cur;
|
||||
};
|
||||
BinaryReader.prototype.GetSize = function() {
|
||||
return this.size;
|
||||
};
|
||||
|
||||
BinaryReader.prototype.GetDouble = function() {
|
||||
var dRes = 0.0;
|
||||
dRes |= this.GetUChar();
|
||||
dRes |= this.GetUChar() << 8;
|
||||
dRes |= this.GetUChar() << 16;
|
||||
dRes |= this.GetUChar() << 24;
|
||||
dRes /= 100000;
|
||||
return dRes;
|
||||
};
|
||||
BinaryReader.prototype.GetBuffer = function(length) {
|
||||
var res = new Uint8Array(length);
|
||||
for(var i = 0 ; i < length ;++i){
|
||||
res[i] = this.data[this.cur++]
|
||||
}
|
||||
return res;
|
||||
};
|
||||
|
||||
export function readString(reader) {
|
||||
const isWrite = reader.GetBool();
|
||||
return isWrite ? reader.GetString() : null;
|
||||
}
|
||||
|
||||
export function readBool(reader) {
|
||||
const isWrite = reader.GetBool();
|
||||
return isWrite ? reader.GetBool() : null;
|
||||
}
|
||||
|
||||
export function readLong(reader) {
|
||||
const isWrite = reader.GetBool();
|
||||
return isWrite ? reader.GetLong() : null;
|
||||
}
|
||||
|
||||
export function readBuffer(reader) {
|
||||
const isWrite = reader.GetBool();
|
||||
return isWrite ? reader.GetBuffer(reader.GetLong()) : null;
|
||||
}
|
||||
|
||||
export function readObject(reader) {
|
||||
const isWrite = reader.GetBool();
|
||||
let object = null;
|
||||
if (isWrite) {
|
||||
const type = reader.GetLong();
|
||||
var nStart = reader.cur;
|
||||
var nEnd = nStart + reader.GetLong() + 4;
|
||||
if (c_oAscObjectFactory[type]) {
|
||||
object = c_oAscObjectFactory[type].import(reader);
|
||||
}
|
||||
reader.Seek2(nEnd);
|
||||
}
|
||||
return object;
|
||||
}
|
||||
@ -0,0 +1,173 @@
|
||||
import {c_oAscObjectFactory} from "../factory";
|
||||
|
||||
export function BinaryWriter(bIsNoInit)
|
||||
{
|
||||
this.data = null;
|
||||
this.len = 0;
|
||||
this.pos = 0;
|
||||
|
||||
if (true !== bIsNoInit)
|
||||
this.Init();
|
||||
}
|
||||
BinaryWriter.prototype.Init = function(len)
|
||||
{
|
||||
this.len = (len === undefined) ? 1024 * 1024 * 5 : len;
|
||||
this.data = new Uint8Array(this.len);
|
||||
this.pos = 0;
|
||||
}
|
||||
|
||||
BinaryWriter.prototype.CheckSize = function(count)
|
||||
{
|
||||
if (this.pos + count >= this.len)
|
||||
{
|
||||
var oldData = this.data;
|
||||
|
||||
this.len = Math.max(this.len * 2, this.pos + ((3 * count / 2) >> 0));
|
||||
this.data = new Uint8Array(this.len);
|
||||
|
||||
for (var i = 0; i < this.pos; i++)
|
||||
this.data[i] = oldData[i];
|
||||
}
|
||||
}
|
||||
BinaryWriter.prototype.GetData = function()
|
||||
{
|
||||
var len = this.GetCurPosition();
|
||||
var res = new Uint8Array(len);
|
||||
|
||||
for (var i = 0; i < len; i++)
|
||||
res[i] = this.data[i];
|
||||
return res;
|
||||
}
|
||||
BinaryWriter.prototype.GetCurPosition = function()
|
||||
{
|
||||
return this.pos;
|
||||
}
|
||||
BinaryWriter.prototype.Seek = function(nPos)
|
||||
{
|
||||
this.pos = nPos;
|
||||
}
|
||||
BinaryWriter.prototype.Skip = function(nDif)
|
||||
{
|
||||
this.pos += nDif;
|
||||
}
|
||||
BinaryWriter.prototype.WriteBool = function(val)
|
||||
{
|
||||
this.CheckSize(1);
|
||||
if (false == val)
|
||||
this.data[this.pos++] = 0;
|
||||
else
|
||||
this.data[this.pos++] = 1;
|
||||
}
|
||||
BinaryWriter.prototype.WriteByte = function(val)
|
||||
{
|
||||
this.CheckSize(1);
|
||||
this.data[this.pos++] = val;
|
||||
}
|
||||
BinaryWriter.prototype.WriteLong = function(val)
|
||||
{
|
||||
this.CheckSize(4);
|
||||
this.data[this.pos++] = (val) & 0xFF;
|
||||
this.data[this.pos++] = (val >>> 8) & 0xFF;
|
||||
this.data[this.pos++] = (val >>> 16) & 0xFF;
|
||||
this.data[this.pos++] = (val >>> 24) & 0xFF;
|
||||
}
|
||||
BinaryWriter.prototype.WriteDouble = function(val)
|
||||
{
|
||||
this.CheckSize(4);
|
||||
var lval = ((val * 100000) >> 0) & 0xFFFFFFFF;
|
||||
this.data[this.pos++] = (lval) & 0xFF;
|
||||
this.data[this.pos++] = (lval >>> 8) & 0xFF;
|
||||
this.data[this.pos++] = (lval >>> 16) & 0xFF;
|
||||
this.data[this.pos++] = (lval >>> 24) & 0xFF;
|
||||
}
|
||||
BinaryWriter.prototype.WriteString = function(text)
|
||||
{
|
||||
if ("string" != typeof text)
|
||||
text = text + "";
|
||||
|
||||
var count = text.length & 0x7FFFFFFF;
|
||||
var countWrite = 2 * count;
|
||||
this.WriteLong(countWrite);
|
||||
this.CheckSize(countWrite);
|
||||
for (let i = 0; i < count; i++)
|
||||
{
|
||||
const c = text.charCodeAt(i) & 0xFFFF;
|
||||
this.data[this.pos++] = c & 0xFF;
|
||||
this.data[this.pos++] = (c >>> 8) & 0xFF;
|
||||
}
|
||||
}
|
||||
BinaryWriter.prototype.WriteBuffer = function(data, _pos, count)
|
||||
{
|
||||
this.CheckSize(count);
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
this.data[this.pos++] = data[_pos + i];
|
||||
}
|
||||
}
|
||||
BinaryWriter.prototype.WriteItem = function(type, fWrite)
|
||||
{
|
||||
this.WriteLong(type);
|
||||
this.WriteItemWithLength(fWrite);
|
||||
};
|
||||
BinaryWriter.prototype.WriteItemWithLength = function(fWrite)
|
||||
{
|
||||
var nStart = this.WriteItemWithLengthStart();
|
||||
fWrite();
|
||||
this.WriteItemWithLengthEnd(nStart);
|
||||
};
|
||||
BinaryWriter.prototype.WriteItemWithLengthStart = function()
|
||||
{
|
||||
var nStart = this.GetCurPosition();
|
||||
this.Skip(4);
|
||||
return nStart;
|
||||
};
|
||||
BinaryWriter.prototype.WriteItemWithLengthEnd = function(nStart)
|
||||
{
|
||||
var nEnd = this.GetCurPosition();
|
||||
this.Seek(nStart);
|
||||
this.WriteLong(nEnd - nStart - 4);
|
||||
this.Seek(nEnd);
|
||||
};
|
||||
|
||||
export function writeString(writer, str) {
|
||||
const isWrite = typeof str === "string";
|
||||
writer.WriteBool(isWrite);
|
||||
if (isWrite) {
|
||||
writer.WriteString(str);
|
||||
}
|
||||
}
|
||||
export function writeBool(writer, bool) {
|
||||
const isWrite = typeof bool === "boolean";
|
||||
writer.WriteBool(isWrite);
|
||||
if (isWrite) {
|
||||
writer.WriteBool(bool);
|
||||
}
|
||||
}
|
||||
export function writeLong(writer, int) {
|
||||
const isWrite = typeof int === "number";
|
||||
writer.WriteBool(isWrite);
|
||||
if (isWrite) {
|
||||
writer.WriteLong(int);
|
||||
}
|
||||
}
|
||||
export function writeBuffer(writer, buffer) {
|
||||
const isWrite = buffer instanceof Uint8Array;
|
||||
writer.WriteBool(isWrite);
|
||||
if (isWrite) {
|
||||
writer.WriteLong(buffer.length);
|
||||
writer.WriteBuffer(buffer, 0, buffer.length);
|
||||
}
|
||||
}
|
||||
export function writeObject(writer, object) {
|
||||
let isWrite = false;
|
||||
const type = object && object.getObjectType();
|
||||
if (c_oAscObjectFactory[type]) {
|
||||
isWrite = true;
|
||||
}
|
||||
writer.WriteBool(isWrite);
|
||||
if (isWrite) {
|
||||
writer.WriteItem(type, function () {
|
||||
object.export(writer);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
import {c_oAscKeyStorageType} from "./defines";
|
||||
import {getCrypto} from "./crypto";
|
||||
|
||||
export function CryptoBase() {
|
||||
}
|
||||
CryptoBase.prototype.getObjectType = function () {
|
||||
return this.objectType;
|
||||
};
|
||||
|
||||
export function initClass(fClass, fBase, type) {
|
||||
fClass.prototype = Object.create(fBase.prototype);
|
||||
fClass.prototype.constructor = fClass;
|
||||
fClass.prototype.objectType = type || c_oAscKeyStorageType.NoType;
|
||||
}
|
||||
|
||||
export function getRandomValues(length) {
|
||||
const crypto = getCrypto();
|
||||
return crypto.getRandomValues(length);
|
||||
}
|
||||
|
||||
export function isEqualArrays(array1, array2) {
|
||||
if (!array1 && array2 || array1 && !array2) {
|
||||
return false;
|
||||
}
|
||||
if (array1.length !== array2.length) {
|
||||
return false;
|
||||
}
|
||||
for (let i = 0; i < array1.length; i += 1) {
|
||||
if (array1[i] !== array2[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Test variant</title>
|
||||
</head>
|
||||
<body>
|
||||
<button id="generate-key-button">Generate key</button>
|
||||
<div></div>
|
||||
<label for="public-key-input">Input data for public key</label>
|
||||
<input type="text" id="public-key-input" />
|
||||
<button id="get-key-button">Get key by public key</button>
|
||||
<div>Selected key:</div>
|
||||
<div id="selected-key"></div>
|
||||
<label for="data-input">Input data for encrypt</label>
|
||||
<input type="text" id="data-input" />
|
||||
<button id="encrypt-data-button">Encrypt</button>
|
||||
<div>Encrypted data: </div>
|
||||
<div id="encrypted-data"></div>
|
||||
<label for="encrypt-data-input">Input data for decrypt</label>
|
||||
<input type="text" id="encrypt-data-input" />
|
||||
<button id="decrypt-data-button">Decrypt</button>
|
||||
<div>Decrypted data: </div>
|
||||
<div id="decrypted-data"></div>
|
||||
<button id="save-storage-button">Save storage</button>
|
||||
<button id="load-storage-button">Load storage</button>
|
||||
<button id="change-password-button">Change password</button>
|
||||
|
||||
<script src="../dist/key-storage.iife.js"></script>
|
||||
<script src="./string-polyfill.js"></script>
|
||||
<script src="./storage-manager.js"></script>
|
||||
<script src="./index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@ -0,0 +1,68 @@
|
||||
const storageManager = new StorageManager();
|
||||
|
||||
const generateKeyButton = document.getElementById('generate-key-button');
|
||||
const publicKeyInput = document.getElementById('public-key-input');
|
||||
const getKeyButton = document.getElementById('get-key-button');
|
||||
const encryptKeyButton = document.getElementById('encrypt-data-button');
|
||||
const decryptKeyButton = document.getElementById('decrypt-data-button');
|
||||
const dataInput = document.getElementById('data-input');
|
||||
const encryptDataInput = document.getElementById('encrypt-data-input');
|
||||
const selectedKeyDiv = document.getElementById('selected-key');
|
||||
const encryptedDataDiv = document.getElementById('encrypted-data');
|
||||
const decryptedDataDiv = document.getElementById('decrypted-data');
|
||||
const saveStorageButton = document.getElementById('save-storage-button');
|
||||
const loadStorageButton = document.getElementById('load-storage-button');
|
||||
const changePasswordButton = document.getElementById('change-password-button');
|
||||
|
||||
let selectedKey = null;
|
||||
generateKeyButton.addEventListener('click', (e) => {
|
||||
storageManager.generateAsymmetricKey().then(function (key) {
|
||||
selectedKeyDiv.textContent = key.getExportPublicKey().toBase64();
|
||||
selectedKey = key;
|
||||
});
|
||||
});
|
||||
|
||||
getKeyButton.addEventListener('click', (e) => {
|
||||
const key = storageManager.getKeyByPublicKey(Uint8Array.fromBase64(publicKeyInput.value))
|
||||
selectedKeyDiv.textContent = key.getExportPublicKey().toBase64();
|
||||
selectedKey = key;
|
||||
});
|
||||
|
||||
encryptKeyButton.addEventListener('click', (e) => {
|
||||
selectedKey.encrypt(dataInput.value.toUtf8(true)).then(function (binaryData) {
|
||||
encryptedDataDiv.textContent = binaryData.toBase64();
|
||||
});
|
||||
});
|
||||
|
||||
decryptKeyButton.addEventListener('click', (e) => {
|
||||
selectedKey.decrypt(Uint8Array.fromBase64(encryptDataInput.value)).then(function (binaryData) {
|
||||
decryptedDataDiv.textContent = String.fromUtf8(binaryData, 0, binaryData.length);
|
||||
});
|
||||
});
|
||||
|
||||
saveStorageButton.addEventListener('click', (e) => {
|
||||
const exportKeys = storageManager.exportKeys();
|
||||
localStorage.setItem("tempKeyStorage", exportKeys.toBase64());
|
||||
alert("Key storage is exported");
|
||||
});
|
||||
|
||||
loadStorageButton.addEventListener('click', (e) => {
|
||||
const item = localStorage.getItem("tempKeyStorage");
|
||||
if (item === null) {
|
||||
alert("localStorage is empty");
|
||||
} else {
|
||||
storageManager.importKeys(item).then(function () {
|
||||
alert("Key storage is loaded");
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
changePasswordButton.addEventListener('click', (e) => {
|
||||
storageManager.changeMasterPassword().then(function() {
|
||||
alert("Master password is changed");
|
||||
}).catch(function(e) {
|
||||
if (e.name === "OperationError") {
|
||||
alert("Master password is invalid");
|
||||
}
|
||||
});
|
||||
});
|
||||
@ -0,0 +1,70 @@
|
||||
const KeyStorage = KeyStorageLibrary.KeyStorage;
|
||||
const RsaOAEPKeyGenParams = KeyStorageLibrary.RsaOAEPKeyGenParams;
|
||||
const c_oAscDigestType = KeyStorageLibrary.c_oAscDigestType;
|
||||
|
||||
function StorageManager() {
|
||||
this.keyStorage = new KeyStorage();
|
||||
}
|
||||
StorageManager.prototype.generateAsymmetricKey = function () {
|
||||
const oThis = this;
|
||||
return this.askMasterPassword().then(function (masterPassword) {
|
||||
return oThis.keyStorage.generateKey(new RsaOAEPKeyGenParams(c_oAscDigestType.SHA256), masterPassword.toUtf8(true));
|
||||
}).then(function (key) {
|
||||
oThis.keyStorage.addKeys([key]);
|
||||
return key;
|
||||
});
|
||||
};
|
||||
StorageManager.prototype.getMasterPassword = function () {
|
||||
let item = window.localStorage.getItem("masterPassword");
|
||||
if (item !== undefined) {
|
||||
return item;
|
||||
}
|
||||
item = window.sessionStorage.getItem("masterPassword");
|
||||
if (item !== undefined) {
|
||||
return item;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
StorageManager.prototype.changeMasterPassword = function () {
|
||||
const oldMasterPassword = prompt("Enter Old Master Password");
|
||||
const newMasterPassword = prompt("Enter New Master Password");
|
||||
const oThis = this;
|
||||
return this.keyStorage.changeMasterPassword(oldMasterPassword.toUtf8(true), newMasterPassword.toUtf8(true)).then(function() {
|
||||
oThis.setMasterPassword(newMasterPassword);
|
||||
});
|
||||
};
|
||||
StorageManager.prototype.askMasterPassword = function () {
|
||||
let masterPassword = this.getMasterPassword();
|
||||
if (masterPassword === null) {
|
||||
masterPassword = prompt("Enter Master Password");
|
||||
this.setMasterPassword(masterPassword);
|
||||
}
|
||||
if (!this.keyStorage.isInit) {
|
||||
this.keyStorage.init();
|
||||
}
|
||||
return Promise.resolve(masterPassword);
|
||||
};
|
||||
StorageManager.prototype.clearMasterPassword = function() {
|
||||
localStorage.removeItem("masterPassword");
|
||||
sessionStorage.removeItem("masterPassword");
|
||||
};
|
||||
StorageManager.prototype.setMasterPassword = function(masterPassword) {
|
||||
this.clearMasterPassword();
|
||||
if (confirm("Save your master password in localStorage?")) {
|
||||
localStorage.setItem("masterPassword", masterPassword);
|
||||
} else {
|
||||
sessionStorage.setItem("masterPassword", masterPassword);
|
||||
}
|
||||
};
|
||||
StorageManager.prototype.getKeyByPublicKey = function (binaryData) {
|
||||
return this.keyStorage.getKeyByPublicKey(binaryData);
|
||||
};
|
||||
StorageManager.prototype.exportKeys = function () {
|
||||
return this.keyStorage.export();
|
||||
};
|
||||
StorageManager.prototype.importKeys = function (binaryData) {
|
||||
const oThis = this;
|
||||
return this.askMasterPassword().then(function (masterPassword) {
|
||||
return oThis.keyStorage.import(Uint8Array.fromBase64(binaryData), masterPassword.toUtf8(true));
|
||||
});
|
||||
};
|
||||
@ -0,0 +1,101 @@
|
||||
var STRING_UTF8_BUFFER_LENGTH = 1024;
|
||||
var STRING_UTF8_BUFFER = new ArrayBuffer(STRING_UTF8_BUFFER_LENGTH);
|
||||
|
||||
/**
|
||||
* Read string from utf8
|
||||
* @param {Uint8Array} buffer
|
||||
* @param {number} [start=0]
|
||||
* @param {number} [len]
|
||||
* @returns {string}
|
||||
*/
|
||||
String.fromUtf8 = function(buffer, start, len) {
|
||||
if (undefined === start)
|
||||
start = 0;
|
||||
if (undefined === len)
|
||||
len = buffer.length - start;
|
||||
|
||||
var result = "";
|
||||
var index = start;
|
||||
var end = start + len;
|
||||
while (index < end) {
|
||||
var u0 = buffer[index++];
|
||||
if (!(u0 & 128)) {
|
||||
result += String.fromCharCode(u0);
|
||||
continue;
|
||||
}
|
||||
var u1 = buffer[index++] & 63;
|
||||
if ((u0 & 224) == 192) {
|
||||
result += String.fromCharCode((u0 & 31) << 6 | u1);
|
||||
continue;
|
||||
}
|
||||
var u2 = buffer[index++] & 63;
|
||||
if ((u0 & 240) == 224)
|
||||
u0 = (u0 & 15) << 12 | u1 << 6 | u2;
|
||||
else
|
||||
u0 = (u0 & 7) << 18 | u1 << 12 | u2 << 6 | buffer[index++] & 63;
|
||||
if (u0 < 65536)
|
||||
result += String.fromCharCode(u0);
|
||||
else {
|
||||
var ch = u0 - 65536;
|
||||
result += String.fromCharCode(55296 | ch >> 10, 56320 | ch & 1023);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert string to utf8 array
|
||||
* @returns {Uint8Array}
|
||||
*/
|
||||
String.prototype.toUtf8 = function(isNoEndNull, isUseBuffer) {
|
||||
var inputLen = this.length;
|
||||
var testLen = 6 * inputLen + 1;
|
||||
var tmpStrings = (isUseBuffer && testLen < STRING_UTF8_BUFFER_LENGTH) ? STRING_UTF8_BUFFER : new ArrayBuffer(testLen);
|
||||
|
||||
var code = 0;
|
||||
var index = 0;
|
||||
|
||||
var outputIndex = 0;
|
||||
var outputDataTmp = new Uint8Array(tmpStrings);
|
||||
var outputData = outputDataTmp;
|
||||
|
||||
while (index < inputLen) {
|
||||
code = this.charCodeAt(index++);
|
||||
if (code >= 0xD800 && code <= 0xDFFF && index < inputLen)
|
||||
code = 0x10000 + (((code & 0x3FF) << 10) | (0x03FF & this.charCodeAt(index++)));
|
||||
|
||||
if (code < 0x80)
|
||||
outputData[outputIndex++] = code;
|
||||
else if (code < 0x0800) {
|
||||
outputData[outputIndex++] = 0xC0 | (code >> 6);
|
||||
outputData[outputIndex++] = 0x80 | (code & 0x3F);
|
||||
} else if (code < 0x10000) {
|
||||
outputData[outputIndex++] = 0xE0 | (code >> 12);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 6) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | (code & 0x3F);
|
||||
} else if (code < 0x1FFFFF) {
|
||||
outputData[outputIndex++] = 0xF0 | (code >> 18);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 12) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 6) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | (code & 0x3F);
|
||||
} else if (code < 0x3FFFFFF) {
|
||||
outputData[outputIndex++] = 0xF8 | (code >> 24);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 18) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 12) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 6) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | (code & 0x3F);
|
||||
} else if (code < 0x7FFFFFFF) {
|
||||
outputData[outputIndex++] = 0xFC | (code >> 30);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 24) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 18) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 12) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | ((code >> 6) & 0x3F);
|
||||
outputData[outputIndex++] = 0x80 | (code & 0x3F);
|
||||
}
|
||||
}
|
||||
|
||||
if (isNoEndNull !== true)
|
||||
outputData[outputIndex++] = 0;
|
||||
|
||||
return new Uint8Array(tmpStrings,0,outputIndex);
|
||||
};
|
||||
@ -0,0 +1,19 @@
|
||||
import { defineConfig } from 'vite'
|
||||
import { resolve } from 'path'
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
lib: {
|
||||
formats: ['iife', 'es'],
|
||||
entry: resolve(__dirname, 'src/index.js'),
|
||||
name: 'KeyStorageLibrary',
|
||||
fileName: (format) => `key-storage.${format}.js`
|
||||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
generatedCode: "es5"
|
||||
}
|
||||
},
|
||||
minify: false
|
||||
},
|
||||
})
|
||||
91
DesktopEditor/xmlsec/src/wasm/extension/lib/keychain.js
Normal file
@ -0,0 +1,91 @@
|
||||
(function (window) {
|
||||
// 1) Ввод мастер пароля в popup
|
||||
// 2) Генерация ключей - генерируем, генерируем соль и храним публичный, шифрованный AES-GCM приватный, соль, дата.
|
||||
// 3) Смена мастер пароля.
|
||||
// 4) Подпись, проверка подписи
|
||||
// 5) Шифрование, проверка шифрования.
|
||||
// 6) Отзыв ключа.
|
||||
// 7) импорт/экспорт всех данных
|
||||
|
||||
// Написать тестовый пример. Ставим расширение. Можем вызвать попап и сгенерировать и отозвать ключ,
|
||||
// поменять пароль, ввести пароль (первый раз), импорт, экспорт.
|
||||
// Связка странички с расширением.
|
||||
|
||||
const pluginMessenger = window.Asc.pluginMessenger;
|
||||
const messageTypes = {
|
||||
CHECK_ENGINE: "CHECK_ENGINE",
|
||||
GENERATE_KEYS: "GENERATE_KEYS",
|
||||
SELECT_SIGN_KEYS: "SELECT_SIGN_KEYS",
|
||||
SIGN_DATA: "SIGN_DATA",
|
||||
VERIFY_DATA: "VERIFY_DATA",
|
||||
};
|
||||
var KeychainVersion = 1;
|
||||
var KeychainKeyVersion = 2;
|
||||
|
||||
var Purpose = {
|
||||
Encode : 0,
|
||||
Sign : 1
|
||||
};
|
||||
|
||||
var Algorithm = {
|
||||
ed25519 : 0,
|
||||
x25519 : 1
|
||||
};
|
||||
|
||||
function Key()
|
||||
{
|
||||
this.version = KeychainKeyVersion;
|
||||
|
||||
this.privateKey = {
|
||||
data : "",
|
||||
salt : ""
|
||||
};
|
||||
this.publicKey = "";
|
||||
|
||||
this.date;
|
||||
this.valid = true;
|
||||
}
|
||||
|
||||
function Keychain()
|
||||
{
|
||||
this.version = KeychainVersion;
|
||||
this.keys = [];
|
||||
}
|
||||
|
||||
Keychain.prototype.checkExistEngine = async function()
|
||||
{
|
||||
let result = await pluginMessenger.isInitExtension();
|
||||
return !!result;
|
||||
};
|
||||
|
||||
Keychain.prototype.generateEncryptKeys = async function()
|
||||
{
|
||||
let key = await pluginMessenger.postMessage({
|
||||
type : messageTypes.GENERATE_KEYS,
|
||||
});
|
||||
return key;
|
||||
};
|
||||
|
||||
Keychain.prototype.selectSignKeys = async function() {
|
||||
return pluginMessenger.postMessage({
|
||||
type : messageTypes.SELECT_SIGN_KEYS
|
||||
});
|
||||
};
|
||||
Keychain.prototype.signData = async function(base64, guid) {
|
||||
return pluginMessenger.postMessage({
|
||||
type : messageTypes.SIGN_DATA,
|
||||
base64Data: base64,
|
||||
guid: guid
|
||||
});
|
||||
};
|
||||
Keychain.prototype.verifyData = async function(base64Data, base64Signature, guid) {
|
||||
return pluginMessenger.postMessage({
|
||||
type : messageTypes.VERIFY_DATA,
|
||||
base64Data: base64Data,
|
||||
base64Signature: base64Signature,
|
||||
guid: guid
|
||||
});
|
||||
};
|
||||
window.Asc = window.Asc || {};
|
||||
window.Asc.Keychain = new Keychain();
|
||||
})(window);
|
||||
58
DesktopEditor/xmlsec/src/wasm/extension/lib/messenger.js
Normal file
@ -0,0 +1,58 @@
|
||||
(function (window) {
|
||||
const onlyofficeClientChannel = "onlyoffice-sign-client-channel";
|
||||
const onlyofficeExtensionChannel = "onlyoffice-sign-extension-channel";
|
||||
function PluginMessenger() {
|
||||
this.id = 0;
|
||||
this.resolvers = {};
|
||||
this.isInitEngine = null;
|
||||
}
|
||||
PluginMessenger.prototype.isInitExtension = async function () {
|
||||
if (typeof this.isInitEngine !== "boolean") {
|
||||
this.isInitEngine = await new Promise((resolve) => {
|
||||
const handler = () => {
|
||||
this.isInitEngine = true;
|
||||
resolve(true);
|
||||
window.removeEventListener(onlyofficeClientChannel, handler);
|
||||
}
|
||||
window.addEventListener(onlyofficeClientChannel, handler);
|
||||
setTimeout(() => {
|
||||
this.isInitEngine = false;
|
||||
window.removeEventListener(onlyofficeClientChannel, handler);
|
||||
resolve(false);
|
||||
}, 1000);
|
||||
});
|
||||
}
|
||||
return this.isInitEngine;
|
||||
};
|
||||
PluginMessenger.prototype.init = function () {
|
||||
window.addEventListener(onlyofficeClientChannel, (event) => {
|
||||
if (event.detail && typeof event.detail.id === 'number' && this.resolvers[event.detail.id]) {
|
||||
const resolve = this.resolvers[event.detail.id];
|
||||
delete this.resolvers[event.detail.id];
|
||||
resolve(event.detail.data);
|
||||
}
|
||||
});
|
||||
};
|
||||
PluginMessenger.prototype.postMessage = async function (data, timeout) {
|
||||
try {
|
||||
const id = this.id++;
|
||||
return new Promise((resolve, reject) => {
|
||||
window.dispatchEvent(new CustomEvent(onlyofficeExtensionChannel, { detail: {data: data, id: id} }));
|
||||
this.resolvers[id] = resolve;
|
||||
if (typeof timeout === "number") {
|
||||
setTimeout(() => {
|
||||
delete this.resolvers[id];
|
||||
reject(`Long waiting time: ${id}`);
|
||||
}, timeout);
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
const pluginMessenger = new PluginMessenger();
|
||||
pluginMessenger.init();
|
||||
window.Asc = window.Asc || {};
|
||||
window.Asc.pluginMessenger = pluginMessenger;
|
||||
})(window);
|
||||
269
DesktopEditor/xmlsec/src/wasm/extension/main.cpp
Normal file
@ -0,0 +1,269 @@
|
||||
#include "../../../../../common/File.h"
|
||||
#include "../../../../../../Common/3dParty/openssl/common/common_openssl.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#define WASM_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define WASM_EXPORT __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
namespace NSInternal
|
||||
{
|
||||
int CheckEncryptedVersion(const std::string& input, int& offset)
|
||||
{
|
||||
offset = 0;
|
||||
// VER%NUMBER%; version from 100 to 999
|
||||
if (input.length() > 7)
|
||||
{
|
||||
const char* input_ptr = input.c_str();
|
||||
if (input_ptr[0] == 'V' && input_ptr[1] == 'E' && input_ptr[2] == 'R')
|
||||
{
|
||||
input_ptr += 3;
|
||||
int nVersion = 0;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (*input_ptr == ';')
|
||||
{
|
||||
offset = 3 + i + 1;
|
||||
return nVersion;
|
||||
}
|
||||
|
||||
nVersion *= 10;
|
||||
nVersion += (*input_ptr - '0');
|
||||
++input_ptr;
|
||||
}
|
||||
return nVersion;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::string DecodePrivateKey(const std::string& sPrivateKeyEnc, const std::string& sPassword, const std::string& sSalt)
|
||||
{
|
||||
int nHeaderLen = 0;
|
||||
int nVersion = CheckEncryptedVersion(sPrivateKeyEnc, nHeaderLen);
|
||||
|
||||
std::string sPrivateKey;
|
||||
bool bIsSuccess = false;
|
||||
if (nVersion == 2)
|
||||
bIsSuccess = NSOpenSSL::AES_Decrypt_desktop_GCM(sPassword, sPrivateKeyEnc, sPrivateKey, sSalt, nHeaderLen);
|
||||
else
|
||||
bIsSuccess = NSOpenSSL::AES_Decrypt_desktop(sPassword, sPrivateKeyEnc, sPrivateKey, sSalt);
|
||||
|
||||
if (!bIsSuccess)
|
||||
return "";
|
||||
|
||||
return sPrivateKey;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
// Memory
|
||||
WASM_EXPORT void* Crypto_Malloc(unsigned int size)
|
||||
{
|
||||
NSOpenSSL::CMemoryData oData;
|
||||
oData.Alloc(size);
|
||||
return oData.Data;
|
||||
}
|
||||
WASM_EXPORT void Crypto_Free(void* p)
|
||||
{
|
||||
if (p)
|
||||
{
|
||||
NSOpenSSL::CMemoryData oData;
|
||||
oData.Data = (unsigned char*)p;
|
||||
oData.Free();
|
||||
}
|
||||
}
|
||||
|
||||
// создаем ключи, приватный шифруем паролем, результат - две base64 строки
|
||||
WASM_EXPORT char* Crypto_CreateKeys(const char* alg, const char* password, const char* salt)
|
||||
{
|
||||
std::string publicKey;
|
||||
std::string privateKey;
|
||||
|
||||
if (!NSOpenSSL::GenerateKeysByAlgs(alg, publicKey, privateKey))
|
||||
return NULL;
|
||||
|
||||
std::string privateKeyEnc;
|
||||
NSOpenSSL::AES_Encrypt_desktop_GCM(password, privateKey, privateKeyEnc, salt);
|
||||
|
||||
char* pDataPublicBase64 = NULL;
|
||||
int nDataPublicBase64Len = 0;
|
||||
NSFile::CBase64Converter::Encode((BYTE*)publicKey.c_str(), (int)publicKey.length(), pDataPublicBase64, nDataPublicBase64Len, NSBase64::B64_BASE64_FLAG_NOCRLF);
|
||||
publicKey = std::string(pDataPublicBase64, (size_t)nDataPublicBase64Len);
|
||||
RELEASEARRAYOBJECTS(pDataPublicBase64);
|
||||
|
||||
size_t public_len = publicKey.length();
|
||||
size_t private_len = privateKeyEnc.length();
|
||||
char* result = (char*)Crypto_Malloc((unsigned int)(public_len + private_len + 2));
|
||||
memcpy(result, publicKey.c_str(), public_len);
|
||||
result[public_len] = '\0';
|
||||
memcpy(result + public_len + 1, privateKeyEnc.c_str(), private_len);
|
||||
result[public_len + private_len + 1] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
WASM_EXPORT char* Crypto_Sign(const char* privateKeyEnc, const char* password, const char* salt, const char* data, int dataLen)
|
||||
{
|
||||
std::string sPrivateKey = NSInternal::DecodePrivateKey(std::string(privateKeyEnc), std::string(password), std::string(salt));
|
||||
if (sPrivateKey.empty())
|
||||
return NULL;
|
||||
|
||||
if (0 == dataLen)
|
||||
dataLen = (int)strlen(data);
|
||||
|
||||
NSOpenSSL::CMemoryData dataSign = NSOpenSSL::Sign((const unsigned char*)data, dataLen, sPrivateKey);
|
||||
|
||||
int nDataSignBase64Len = NSBase64::Base64EncodeGetRequiredLength((int)dataSign.Size, NSBase64::B64_BASE64_FLAG_NOCRLF);
|
||||
char* pDataSignBase64 = (char*)Crypto_Malloc(nDataSignBase64Len + 1);
|
||||
memset(pDataSignBase64, 0, nDataSignBase64Len + 1);
|
||||
|
||||
if (FALSE == NSBase64::Base64Encode(dataSign.Data, (int)dataSign.Size, (BYTE*)pDataSignBase64, &nDataSignBase64Len, NSBase64::B64_BASE64_FLAG_NOCRLF))
|
||||
{
|
||||
dataSign.Free();
|
||||
Crypto_Free((void*)pDataSignBase64);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dataSign.Free();
|
||||
return pDataSignBase64;
|
||||
}
|
||||
|
||||
WASM_EXPORT unsigned char* Crypto_Decrypt(const char* privateKeyEnc, const char* password, const char* salt, const unsigned char* data, const int& data_len)
|
||||
{
|
||||
std::string sPrivateKey = NSInternal::DecodePrivateKey(std::string(privateKeyEnc), std::string(password), std::string(salt));
|
||||
if (sPrivateKey.empty())
|
||||
return NULL;
|
||||
|
||||
NSOpenSSL::CMemoryData decryptData = NSOpenSSL::Decrypt(data, data_len, sPrivateKey, true);
|
||||
return decryptData.Data;
|
||||
}
|
||||
|
||||
WASM_EXPORT unsigned char* Crypto_Encrypt(const char* publicKey, const unsigned char* data, const int& data_len)
|
||||
{
|
||||
NSOpenSSL::CMemoryData encryptData = NSOpenSSL::Encrypt(data, data_len, std::string(publicKey), true);
|
||||
return encryptData.Data;
|
||||
}
|
||||
|
||||
WASM_EXPORT char* Crypto_ChangePassword(const char* privateKeyEnc, const char* passwordOld, const char* passwordNew, const char* salt)
|
||||
{
|
||||
std::string sPrivateKey = NSInternal::DecodePrivateKey(std::string(privateKeyEnc), std::string(passwordOld), std::string(salt));
|
||||
if (sPrivateKey.empty())
|
||||
return NULL;
|
||||
|
||||
std::string sPrivateKeyEnc = "";
|
||||
NSOpenSSL::AES_Encrypt_desktop_GCM(std::string(passwordNew), sPrivateKey, sPrivateKeyEnc, std::string(salt));
|
||||
|
||||
size_t nEncLen = sPrivateKeyEnc.length();
|
||||
char* result = (char*)Crypto_Malloc((unsigned int)(nEncLen + 1));
|
||||
memcpy(result, sPrivateKeyEnc.c_str(), nEncLen);
|
||||
result[nEncLen] = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef TEST_AS_EXECUTABLE
|
||||
#include <iostream>
|
||||
int main()
|
||||
{
|
||||
if (false)
|
||||
{
|
||||
std::string sSalt = "123546789";
|
||||
std::string sPassword = "qwerty";
|
||||
std::string sPasswordNew = "qwerty2";
|
||||
|
||||
char* pGeneratedKeys = Crypto_CreateKeys("ed25519", sPassword.c_str(), sSalt.c_str());
|
||||
if (NULL == pGeneratedKeys)
|
||||
return 1;
|
||||
|
||||
size_t nLen = 0;
|
||||
while (pGeneratedKeys[nLen] != 0)
|
||||
nLen++;
|
||||
|
||||
std::string sPublicKey = std::string(pGeneratedKeys, nLen);
|
||||
|
||||
size_t nLen2 = nLen + 1;
|
||||
while (pGeneratedKeys[nLen2] != 0)
|
||||
nLen2++;
|
||||
|
||||
std::string sPrivateKeyEnc = std::string(pGeneratedKeys + nLen + 1, nLen2 - nLen - 1);
|
||||
|
||||
Crypto_Free(pGeneratedKeys);
|
||||
|
||||
std::string sSignData = "hello world!";
|
||||
|
||||
char* pSignData = Crypto_Sign(sPrivateKeyEnc.c_str(), sPassword.c_str(), sSalt.c_str(), sSignData.c_str(), (int)sSignData.length());
|
||||
std::string sSignature(pSignData);
|
||||
|
||||
Crypto_Free(pSignData);
|
||||
|
||||
char* pNewPrivateKey = Crypto_ChangePassword(sPrivateKeyEnc.c_str(), sPassword.c_str(), sPasswordNew.c_str(), sSalt.c_str());
|
||||
|
||||
std::string sPrivateKeyEncNew(pNewPrivateKey);
|
||||
|
||||
Crypto_Free(pNewPrivateKey);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------
|
||||
|
||||
|
||||
if (false)
|
||||
{
|
||||
std::string publicKey;
|
||||
std::string privateKey;
|
||||
|
||||
NSOpenSSL::GenerateKeysByAlgs("ed25519", publicKey, privateKey);
|
||||
|
||||
if (true)
|
||||
{
|
||||
std::string sTestData = "hello";
|
||||
std::string sTestData2 = "Hello";
|
||||
NSOpenSSL::CMemoryData data = NSOpenSSL::Sign((const unsigned char*)sTestData.c_str(), (int)sTestData.length(), privateKey);
|
||||
|
||||
bool isVerify = NSOpenSSL::Verify((const unsigned char*)sTestData.c_str(), (int)sTestData.length(), publicKey, data.Data, (int)data.Size);
|
||||
bool isVerify2 = NSOpenSSL::Verify((const unsigned char*)sTestData2.c_str(), (int)sTestData2.length(), publicKey, data.Data, (int)data.Size);
|
||||
|
||||
data.Free();
|
||||
}
|
||||
}
|
||||
|
||||
if (true)
|
||||
{
|
||||
std::string publicKey;
|
||||
std::string privateKey;
|
||||
//NSOpenSSL::GenerateKeysByAlgs("x25519", publicKey, privateKey);
|
||||
NSOpenSSL::GenerateKeysByAlgs("rsa4096", publicKey, privateKey);
|
||||
if (true)
|
||||
{
|
||||
std::string sTestData = "hello";
|
||||
NSOpenSSL::CMemoryData data = NSOpenSSL::Encrypt((const unsigned char*)sTestData.c_str(), (int)sTestData.length(), publicKey);
|
||||
|
||||
if (data.Data)
|
||||
{
|
||||
NSOpenSSL::CMemoryData data2 = NSOpenSSL::Decrypt(data.Data, (int)data.Size, privateKey);
|
||||
if (data2.Data)
|
||||
{
|
||||
std::string sTestDataDecrypt = data2.Serialize(OPENSSL_SERIALIZE_TYPE_ASCII);
|
||||
std::cout << sTestDataDecrypt << std::endl;
|
||||
}
|
||||
|
||||
data2.Free();
|
||||
}
|
||||
|
||||
data.Free();
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
20
DesktopEditor/xmlsec/src/wasm/extension/test/index.html
Normal file
@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Test</title>
|
||||
</head>
|
||||
<body>
|
||||
<input type="text" id="inputTest1">
|
||||
<button id="selectSign">Select sign keys</button>
|
||||
<div id="guid">Selected key's guid: </div>
|
||||
<button id="signText">Sign text</button>
|
||||
<button id="verifyText">Verify</button>
|
||||
<div id="signValue">base64 sign: </div>
|
||||
<div id="verifyValue">Signature verification result: </div>
|
||||
<script src="../lib/messenger.js"></script>
|
||||
<script src="../lib/keychain.js"></script>
|
||||
<script src="page.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
46
DesktopEditor/xmlsec/src/wasm/extension/test/page.js
Normal file
@ -0,0 +1,46 @@
|
||||
const keyChain = window.Asc.Keychain;
|
||||
const input = document.getElementById("inputTest1");
|
||||
const selectSign = document.getElementById("selectSign");
|
||||
const selectedSignGuid = document.getElementById("guid");
|
||||
const signText = document.getElementById("signText");
|
||||
const verifyText = document.getElementById("verifyText");
|
||||
const signValue = document.getElementById("signValue");
|
||||
const verifyValue = document.getElementById("verifyValue");
|
||||
|
||||
let guid = null;
|
||||
selectSign.addEventListener("click", async function(e) {
|
||||
guid = await keyChain.selectSignKeys();
|
||||
selectedSignGuid.innerText = `Selected key's guid: ${guid}`;
|
||||
});
|
||||
|
||||
let base64Signature;
|
||||
signText.addEventListener("click", (e) => {
|
||||
if (guid === null) {
|
||||
throw new Error("Please select guid");
|
||||
}
|
||||
const textForSign = input.value;
|
||||
const base64 = btoa(textForSign);
|
||||
(async () => {
|
||||
base64Signature = await keyChain.signData(base64, guid);
|
||||
signValue.innerText = `base64 sign: ${base64Signature}`;
|
||||
})();
|
||||
});
|
||||
|
||||
verifyText.addEventListener("click", (e) => {
|
||||
if (guid === null) {
|
||||
throw new Error("Please select guid");
|
||||
}
|
||||
const textForVerify = input.value;
|
||||
const base64 = btoa(textForVerify);
|
||||
(async () => {
|
||||
const isVerify = await keyChain.verifyData(base64, base64Signature, guid);
|
||||
verifyValue.innerText = `Signature verification result: ${isVerify ? "the signature is valid" : "the signature is invalid"}`;
|
||||
})();
|
||||
});
|
||||
|
||||
(async () => {
|
||||
let isInstalled = await keyChain.checkExistEngine();
|
||||
console.log("Check installed: " + isInstalled);
|
||||
})();
|
||||
|
||||
|
||||