mindoodb 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -24
- package/dist/__mocks__/expo-standard-web-crypto.d.ts +10 -0
- package/dist/__mocks__/expo-standard-web-crypto.d.ts.map +1 -0
- package/dist/__mocks__/expo-standard-web-crypto.js +22 -0
- package/dist/__mocks__/expo-standard-web-crypto.js.map +1 -0
- package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.d.ts +8 -0
- package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.d.ts.map +1 -0
- package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.js +16 -0
- package/dist/appendonlystores/BasicOnDiskContentAddressedStoreFactory.js.map +1 -0
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.d.ts +1 -1
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.d.ts.map +1 -1
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.js +2 -2
- package/dist/appendonlystores/InMemoryContentAddressedStoreFactory.js.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts +19 -2
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js +133 -22
- package/dist/appendonlystores/network/ClientNetworkContentAddressedStore.js.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.d.ts +20 -3
- package/dist/appendonlystores/network/HttpTransport.d.ts.map +1 -1
- package/dist/appendonlystores/network/HttpTransport.js +136 -21
- package/dist/appendonlystores/network/HttpTransport.js.map +1 -1
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts +33 -3
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.d.ts.map +1 -1
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js +148 -27
- package/dist/appendonlystores/network/ServerNetworkContentAddressedStore.js.map +1 -1
- package/dist/appendonlystores/network/index.d.ts +1 -1
- package/dist/appendonlystores/network/index.d.ts.map +1 -1
- package/dist/appendonlystores/network/index.js.map +1 -1
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts +61 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.d.ts.map +1 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js +685 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStore.js.map +1 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.d.ts +25 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.d.ts.map +1 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.js +41 -0
- package/dist/browser/appendonlystores/IndexedDBContentAddressedStoreFactory.js.map +1 -0
- package/dist/browser/index.d.ts +5 -0
- package/dist/browser/index.d.ts.map +1 -1
- package/dist/browser/index.js +13 -1
- package/dist/browser/index.js.map +1 -1
- package/dist/core/BaseMindooDB.d.ts +38 -4
- package/dist/core/BaseMindooDB.d.ts.map +1 -1
- package/dist/core/BaseMindooDB.js +810 -329
- package/dist/core/BaseMindooDB.js.map +1 -1
- package/dist/core/BaseMindooTenant.d.ts +42 -6
- package/dist/core/BaseMindooTenant.d.ts.map +1 -1
- package/dist/core/BaseMindooTenant.js +259 -93
- package/dist/core/BaseMindooTenant.js.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.d.ts +3 -1
- package/dist/core/BaseMindooTenantDirectory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantDirectory.js +50 -40
- package/dist/core/BaseMindooTenantDirectory.js.map +1 -1
- package/dist/core/BaseMindooTenantFactory.d.ts +25 -29
- package/dist/core/BaseMindooTenantFactory.d.ts.map +1 -1
- package/dist/core/BaseMindooTenantFactory.js +175 -73
- package/dist/core/BaseMindooTenantFactory.js.map +1 -1
- package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.d.ts +68 -0
- package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -0
- package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.js +659 -0
- package/dist/core/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -0
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts +50 -4
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.d.ts.map +1 -1
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.js +204 -39
- package/dist/core/appendonlystores/InMemoryContentAddressedStore.js.map +1 -1
- package/dist/core/appendonlystores/bloom.d.ts +85 -0
- package/dist/core/appendonlystores/bloom.d.ts.map +1 -0
- package/dist/core/appendonlystores/bloom.js +228 -0
- package/dist/core/appendonlystores/bloom.js.map +1 -0
- package/dist/core/appendonlystores/network/AuthenticationService.d.ts +3 -0
- package/dist/core/appendonlystores/network/AuthenticationService.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/AuthenticationService.js +21 -17
- package/dist/core/appendonlystores/network/AuthenticationService.js.map +1 -1
- package/dist/core/appendonlystores/network/NetworkTransport.d.ts +36 -2
- package/dist/core/appendonlystores/network/NetworkTransport.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/types.d.ts +10 -0
- package/dist/core/appendonlystores/network/types.d.ts.map +1 -1
- package/dist/core/appendonlystores/network/types.js.map +1 -1
- package/dist/core/appendonlystores/types.d.ts +137 -1
- package/dist/core/appendonlystores/types.d.ts.map +1 -1
- package/dist/core/automerge-adapter.d.ts +15 -0
- package/dist/core/automerge-adapter.d.ts.map +1 -0
- package/dist/core/automerge-adapter.js +73 -0
- package/dist/core/automerge-adapter.js.map +1 -0
- package/dist/core/crypto/MindooDocSigner.d.ts +3 -1
- package/dist/core/crypto/MindooDocSigner.d.ts.map +1 -1
- package/dist/core/crypto/MindooDocSigner.js +9 -5
- package/dist/core/crypto/MindooDocSigner.js.map +1 -1
- package/dist/core/crypto/RSAEncryption.d.ts +3 -1
- package/dist/core/crypto/RSAEncryption.d.ts.map +1 -1
- package/dist/core/crypto/RSAEncryption.js +13 -9
- package/dist/core/crypto/RSAEncryption.js.map +1 -1
- package/dist/core/crypto/pbkdf2Iterations.d.ts +12 -0
- package/dist/core/crypto/pbkdf2Iterations.d.ts.map +1 -0
- package/dist/core/crypto/pbkdf2Iterations.js +55 -0
- package/dist/core/crypto/pbkdf2Iterations.js.map +1 -0
- package/dist/core/index.d.ts +7 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +30 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.d.ts +1 -1
- package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.js +2 -2
- package/dist/core/indexing/virtualviews/MindooDBVirtualViewDataProvider.js.map +1 -1
- package/dist/core/indexing/virtualviews/index.d.ts +1 -1
- package/dist/core/indexing/virtualviews/index.js +1 -1
- package/dist/core/keys/KeyBag.d.ts +36 -13
- package/dist/core/keys/KeyBag.d.ts.map +1 -1
- package/dist/core/keys/KeyBag.js +96 -31
- package/dist/core/keys/KeyBag.js.map +1 -1
- package/dist/core/keys/KeyContext.d.ts +4 -0
- package/dist/core/keys/KeyContext.d.ts.map +1 -0
- package/dist/core/keys/KeyContext.js +11 -0
- package/dist/core/keys/KeyContext.js.map +1 -0
- package/dist/core/logging/LogLevel.d.ts +21 -0
- package/dist/core/logging/LogLevel.d.ts.map +1 -0
- package/dist/core/logging/LogLevel.js +56 -0
- package/dist/core/logging/LogLevel.js.map +1 -0
- package/dist/core/logging/Logger.d.ts +37 -0
- package/dist/core/logging/Logger.d.ts.map +1 -0
- package/dist/core/logging/Logger.js +3 -0
- package/dist/core/logging/Logger.js.map +1 -0
- package/dist/core/logging/MindooLogger.d.ts +28 -0
- package/dist/core/logging/MindooLogger.d.ts.map +1 -0
- package/dist/core/logging/MindooLogger.js +217 -0
- package/dist/core/logging/MindooLogger.js.map +1 -0
- package/dist/core/logging/index.d.ts +4 -0
- package/dist/core/logging/index.d.ts.map +1 -0
- package/dist/core/logging/index.js +10 -0
- package/dist/core/logging/index.js.map +1 -0
- package/dist/core/types.d.ts +313 -77
- package/dist/core/types.d.ts.map +1 -1
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -1
- package/dist/core/uri/MindooURI.d.ts +58 -0
- package/dist/core/uri/MindooURI.d.ts.map +1 -0
- package/dist/core/uri/MindooURI.js +169 -0
- package/dist/core/uri/MindooURI.js.map +1 -0
- package/dist/core/userid/index.d.ts +0 -10
- package/dist/core/userid/index.d.ts.map +1 -1
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts +541 -0
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.d.ts.map +1 -0
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js +1334 -0
- package/dist/node/appendonlystores/BasicOnDiskContentAddressedStore.js.map +1 -0
- package/dist/node/index.d.ts +1 -0
- package/dist/node/index.d.ts.map +1 -1
- package/dist/node/index.js +5 -1
- package/dist/node/index.js.map +1 -1
- package/dist/reactnative/crypto/AESAdapter.d.ts +18 -0
- package/dist/reactnative/crypto/AESAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/AESAdapter.js +112 -0
- package/dist/reactnative/crypto/AESAdapter.js.map +1 -0
- package/dist/reactnative/crypto/Ed25519Adapter.d.ts +34 -0
- package/dist/reactnative/crypto/Ed25519Adapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/Ed25519Adapter.js +386 -0
- package/dist/reactnative/crypto/Ed25519Adapter.js.map +1 -0
- package/dist/reactnative/crypto/ExpoGoCryptoAdapter.d.ts +23 -0
- package/dist/reactnative/crypto/ExpoGoCryptoAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/ExpoGoCryptoAdapter.js +51 -0
- package/dist/reactnative/crypto/ExpoGoCryptoAdapter.js.map +1 -0
- package/dist/reactnative/crypto/HMACAdapter.d.ts +13 -0
- package/dist/reactnative/crypto/HMACAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/HMACAdapter.js +50 -0
- package/dist/reactnative/crypto/HMACAdapter.js.map +1 -0
- package/dist/reactnative/crypto/KeyFormatConverter.d.ts +21 -0
- package/dist/reactnative/crypto/KeyFormatConverter.d.ts.map +1 -0
- package/dist/reactnative/crypto/KeyFormatConverter.js +72 -0
- package/dist/reactnative/crypto/KeyFormatConverter.js.map +1 -0
- package/dist/reactnative/crypto/PBKDF2Adapter.d.ts +19 -0
- package/dist/reactnative/crypto/PBKDF2Adapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/PBKDF2Adapter.js +67 -0
- package/dist/reactnative/crypto/PBKDF2Adapter.js.map +1 -0
- package/dist/reactnative/crypto/QuickCryptoAdapter.d.ts +37 -0
- package/dist/reactnative/crypto/QuickCryptoAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/QuickCryptoAdapter.js +72 -0
- package/dist/reactnative/crypto/QuickCryptoAdapter.js.map +1 -0
- package/dist/reactnative/crypto/RSAAdapter.d.ts +38 -0
- package/dist/reactnative/crypto/RSAAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/RSAAdapter.js +184 -0
- package/dist/reactnative/crypto/RSAAdapter.js.map +1 -0
- package/dist/reactnative/crypto/ReactNativeCryptoAdapter.d.ts +33 -0
- package/dist/reactnative/crypto/ReactNativeCryptoAdapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/ReactNativeCryptoAdapter.js +61 -0
- package/dist/reactnative/crypto/ReactNativeCryptoAdapter.js.map +1 -0
- package/dist/reactnative/crypto/SHA256Adapter.d.ts +9 -0
- package/dist/reactnative/crypto/SHA256Adapter.d.ts.map +1 -0
- package/dist/reactnative/crypto/SHA256Adapter.js +29 -0
- package/dist/reactnative/crypto/SHA256Adapter.js.map +1 -0
- package/dist/reactnative/crypto/SubtleCryptoPolyfill.d.ts +63 -0
- package/dist/reactnative/crypto/SubtleCryptoPolyfill.d.ts.map +1 -0
- package/dist/reactnative/crypto/SubtleCryptoPolyfill.js +350 -0
- package/dist/reactnative/crypto/SubtleCryptoPolyfill.js.map +1 -0
- package/dist/reactnative/index.d.ts +8 -0
- package/dist/reactnative/index.d.ts.map +1 -0
- package/dist/reactnative/index.js +38 -0
- package/dist/reactnative/index.js.map +1 -0
- package/package.json +51 -6
- package/patches/PATCHES.md +111 -0
- package/patches/react-native+0.76.9.patch +34 -0
- package/patches/react-native-quick-crypto+1.0.7.patch +42 -0
- package/scripts/cli.js +21 -0
- package/scripts/setup-react-native.js +228 -0
- package/templates/reactnative/App.js +252 -0
- package/templates/reactnative/app.json +29 -0
- package/templates/reactnative/babel.config.js +6 -0
- package/templates/reactnative/index.js +26 -0
- package/templates/reactnative/metro.config.snippet.js +39 -0
- package/templates/reactnative/mindoodb-polyfills.js +116 -0
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BaseMindooTenant = void 0;
|
|
4
|
+
const types_1 = require("./types");
|
|
4
5
|
const BaseMindooDB_1 = require("./BaseMindooDB");
|
|
5
6
|
const BaseMindooTenantDirectory_1 = require("./BaseMindooTenantDirectory");
|
|
6
7
|
const MindooDocSigner_1 = require("./crypto/MindooDocSigner");
|
|
7
8
|
const errors_1 = require("./errors");
|
|
9
|
+
const logging_1 = require("./logging");
|
|
10
|
+
const MindooURI_1 = require("./uri/MindooURI");
|
|
8
11
|
/**
|
|
9
12
|
* BaseMindooTenant is a platform-agnostic implementation of MindooTenant
|
|
10
13
|
* that works in both browser and server environments.
|
|
@@ -20,19 +23,18 @@ const errors_1 = require("./errors");
|
|
|
20
23
|
* - PBKDF2 key derivation (for password-based key derivation)
|
|
21
24
|
*/
|
|
22
25
|
class BaseMindooTenant {
|
|
23
|
-
constructor(factory, tenantId,
|
|
26
|
+
constructor(factory, tenantId, administrationPublicKey, administrationEncryptionPublicKey, currentUser, currentUserPassword, keyBag, storeFactory, cryptoAdapter, logger, additionalTrustedKeys) {
|
|
24
27
|
this.databaseCache = new Map();
|
|
25
28
|
this.directoryCache = null;
|
|
26
29
|
this.factory = factory;
|
|
27
30
|
this.tenantId = tenantId;
|
|
28
|
-
this.tenantEncryptionKey = tenantEncryptionKey;
|
|
29
|
-
this.tenantEncryptionKeyPassword = tenantEncryptionKeyPassword;
|
|
30
31
|
this.administrationPublicKey = administrationPublicKey;
|
|
31
32
|
this.administrationEncryptionPublicKey = administrationEncryptionPublicKey;
|
|
32
33
|
this.currentUser = currentUser;
|
|
33
34
|
this.currentUserPassword = currentUserPassword;
|
|
34
35
|
this.keyBag = keyBag;
|
|
35
36
|
this.storeFactory = storeFactory;
|
|
37
|
+
this.additionalTrustedKeys = additionalTrustedKeys;
|
|
36
38
|
// Import createCryptoAdapter dynamically to avoid issues in browser environments
|
|
37
39
|
if (!cryptoAdapter) {
|
|
38
40
|
const { createCryptoAdapter } = require("./crypto/CryptoAdapter");
|
|
@@ -41,15 +43,19 @@ class BaseMindooTenant {
|
|
|
41
43
|
else {
|
|
42
44
|
this.cryptoAdapter = cryptoAdapter;
|
|
43
45
|
}
|
|
46
|
+
// Create logger if not provided (for backward compatibility)
|
|
47
|
+
this.logger =
|
|
48
|
+
logger ||
|
|
49
|
+
new logging_1.MindooLogger((0, logging_1.getDefaultLogLevel)(), `Tenant:${tenantId}`, true);
|
|
44
50
|
}
|
|
45
51
|
/**
|
|
46
52
|
* Initialize the tenant.
|
|
47
53
|
*/
|
|
48
54
|
async initialize() {
|
|
49
|
-
|
|
55
|
+
this.logger.info(`Initializing tenant ${this.tenantId}`);
|
|
50
56
|
// KeyBag is already loaded by the caller before passing it to the constructor
|
|
51
57
|
const keyCount = (await this.keyBag.listKeys()).length;
|
|
52
|
-
|
|
58
|
+
this.logger.info(`Tenant initialized with ${keyCount} keys in KeyBag`);
|
|
53
59
|
}
|
|
54
60
|
getCryptoAdapter() {
|
|
55
61
|
return this.cryptoAdapter;
|
|
@@ -79,98 +85,97 @@ class BaseMindooTenant {
|
|
|
79
85
|
getAdministrationEncryptionPublicKey() {
|
|
80
86
|
return this.administrationEncryptionPublicKey;
|
|
81
87
|
}
|
|
82
|
-
getTenantEncryptionKey() {
|
|
83
|
-
return this.tenantEncryptionKey;
|
|
84
|
-
}
|
|
85
88
|
async encryptPayload(payload, decryptionKeyId) {
|
|
86
|
-
|
|
87
|
-
|
|
89
|
+
this.logger.debug(`Encrypting payload with key: ${decryptionKeyId}`);
|
|
90
|
+
this.logger.debug(`Payload size: ${payload.length} bytes`);
|
|
88
91
|
// Get the symmetric key for this key ID
|
|
89
92
|
let symmetricKey;
|
|
90
93
|
try {
|
|
91
94
|
if (decryptionKeyId === "default") {
|
|
92
|
-
|
|
95
|
+
this.logger.debug(`Using default key (tenant encryption key)`);
|
|
93
96
|
// Use cached tenant encryption key if available
|
|
94
97
|
if (this.decryptedTenantKeyCache) {
|
|
95
98
|
symmetricKey = this.decryptedTenantKeyCache;
|
|
96
|
-
|
|
99
|
+
this.logger.debug(`Using cached tenant key, length: ${symmetricKey.length} bytes`);
|
|
97
100
|
}
|
|
98
101
|
else {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
this.logger.debug(`Resolving tenant encryption key from KeyBag`);
|
|
103
|
+
const tenantKey = await this.keyBag.get("tenant", this.tenantId);
|
|
104
|
+
if (!tenantKey) {
|
|
105
|
+
throw new errors_1.SymmetricKeyNotFoundError(`tenant:${this.tenantId}`);
|
|
106
|
+
}
|
|
107
|
+
symmetricKey = tenantKey;
|
|
103
108
|
this.decryptedTenantKeyCache = symmetricKey;
|
|
104
|
-
|
|
109
|
+
this.logger.debug(`Loaded tenant key from KeyBag, length: ${symmetricKey.length} bytes`);
|
|
105
110
|
}
|
|
106
111
|
}
|
|
107
112
|
else {
|
|
108
|
-
|
|
113
|
+
this.logger.debug(`Getting named key from KeyBag: ${decryptionKeyId}`);
|
|
109
114
|
// Get the decrypted key from KeyBag
|
|
110
|
-
const decryptedKey = await this.keyBag.get(decryptionKeyId);
|
|
115
|
+
const decryptedKey = await this.keyBag.get("doc", decryptionKeyId);
|
|
111
116
|
if (!decryptedKey) {
|
|
112
117
|
throw new errors_1.SymmetricKeyNotFoundError(decryptionKeyId);
|
|
113
118
|
}
|
|
114
119
|
symmetricKey = decryptedKey;
|
|
115
|
-
|
|
120
|
+
this.logger.debug(`Got named key, length: ${symmetricKey.length} bytes`);
|
|
116
121
|
}
|
|
117
122
|
}
|
|
118
123
|
catch (error) {
|
|
119
|
-
|
|
124
|
+
this.logger.error(`Error getting symmetric key:`, error);
|
|
120
125
|
throw error;
|
|
121
126
|
}
|
|
122
|
-
|
|
127
|
+
this.logger.debug(`Got symmetric key, length: ${symmetricKey.length} bytes`);
|
|
123
128
|
const subtle = this.cryptoAdapter.getSubtle();
|
|
124
129
|
// Bind getRandomValues to maintain 'this' context
|
|
125
130
|
const randomValues = this.cryptoAdapter.getRandomValues.bind(this.cryptoAdapter);
|
|
126
131
|
// Import the symmetric key
|
|
127
|
-
|
|
132
|
+
this.logger.debug(`Importing symmetric key for AES-GCM`);
|
|
128
133
|
let cryptoKey;
|
|
129
134
|
try {
|
|
130
135
|
// Create a new Uint8Array to ensure we have a proper ArrayBuffer
|
|
131
136
|
const keyArray = new Uint8Array(symmetricKey);
|
|
132
|
-
|
|
137
|
+
this.logger.debug(`Key buffer size: ${keyArray.buffer.byteLength} bytes`);
|
|
133
138
|
cryptoKey = await subtle.importKey("raw", keyArray.buffer, { name: "AES-GCM" }, false, // not extractable
|
|
134
139
|
["encrypt"]);
|
|
135
|
-
|
|
140
|
+
this.logger.debug(`Successfully imported key`);
|
|
136
141
|
}
|
|
137
142
|
catch (error) {
|
|
138
|
-
|
|
139
|
-
|
|
143
|
+
this.logger.error(`Error importing key:`, error);
|
|
144
|
+
this.logger.error(`Key length: ${symmetricKey.length}, expected: 32 bytes for AES-256`);
|
|
140
145
|
throw error;
|
|
141
146
|
}
|
|
142
147
|
// Generate IV (12 bytes for AES-GCM)
|
|
143
|
-
|
|
148
|
+
this.logger.debug(`Generating IV`);
|
|
144
149
|
let iv;
|
|
145
150
|
try {
|
|
146
151
|
const ivArray = new Uint8Array(12);
|
|
147
152
|
randomValues(ivArray);
|
|
148
153
|
iv = new Uint8Array(ivArray.buffer, ivArray.byteOffset, ivArray.byteLength);
|
|
149
|
-
|
|
154
|
+
this.logger.debug(`Generated IV: ${iv.length} bytes`);
|
|
150
155
|
}
|
|
151
156
|
catch (error) {
|
|
152
|
-
|
|
157
|
+
this.logger.error(`Error generating IV:`, error);
|
|
153
158
|
throw error;
|
|
154
159
|
}
|
|
155
160
|
// Encrypt the payload
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
161
|
+
this.logger.debug(`Encrypting payload with AES-GCM`);
|
|
162
|
+
this.logger.debug(`Payload buffer size: ${payload.buffer.byteLength} bytes`);
|
|
163
|
+
this.logger.debug(`Payload byteOffset: ${payload.byteOffset}, byteLength: ${payload.byteLength}`);
|
|
159
164
|
let encrypted;
|
|
160
165
|
try {
|
|
161
166
|
// Create a new Uint8Array to ensure we have a proper ArrayBuffer
|
|
162
167
|
const payloadArray = new Uint8Array(payload);
|
|
163
|
-
|
|
168
|
+
this.logger.debug(`Using payload array: ${payloadArray.length} bytes`);
|
|
164
169
|
encrypted = await subtle.encrypt({
|
|
165
170
|
name: "AES-GCM",
|
|
166
171
|
iv: iv,
|
|
167
172
|
tagLength: 128, // 128-bit authentication tag
|
|
168
173
|
}, cryptoKey, payloadArray);
|
|
169
|
-
|
|
174
|
+
this.logger.debug(`Successfully encrypted payload, encrypted size: ${encrypted.byteLength} bytes`);
|
|
170
175
|
}
|
|
171
176
|
catch (error) {
|
|
172
|
-
|
|
173
|
-
|
|
177
|
+
this.logger.error(`Error encrypting payload:`, error);
|
|
178
|
+
this.logger.error(`Error details:`, {
|
|
174
179
|
errorName: error instanceof Error ? error.name : typeof error,
|
|
175
180
|
errorMessage: error instanceof Error ? error.message : String(error),
|
|
176
181
|
payloadLength: payload.length,
|
|
@@ -181,16 +186,16 @@ class BaseMindooTenant {
|
|
|
181
186
|
}
|
|
182
187
|
// Combine IV and encrypted data
|
|
183
188
|
// Format: IV (12 bytes) + encrypted data (includes authentication tag)
|
|
184
|
-
|
|
189
|
+
this.logger.debug(`Combining IV and encrypted data`);
|
|
185
190
|
const encryptedArray = new Uint8Array(encrypted);
|
|
186
191
|
const result = new Uint8Array(12 + encryptedArray.length);
|
|
187
192
|
result.set(iv, 0);
|
|
188
193
|
result.set(encryptedArray, 12);
|
|
189
|
-
|
|
194
|
+
this.logger.debug(`Encrypted payload (${payload.length} -> ${result.length} bytes)`);
|
|
190
195
|
return result;
|
|
191
196
|
}
|
|
192
197
|
async decryptPayload(encryptedPayload, decryptionKeyId) {
|
|
193
|
-
|
|
198
|
+
this.logger.debug(`Decrypting payload with key: ${decryptionKeyId}`);
|
|
194
199
|
if (encryptedPayload.length < 12) {
|
|
195
200
|
throw new Error("Encrypted payload too short (missing IV)");
|
|
196
201
|
}
|
|
@@ -205,15 +210,17 @@ class BaseMindooTenant {
|
|
|
205
210
|
symmetricKey = this.decryptedTenantKeyCache;
|
|
206
211
|
}
|
|
207
212
|
else {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
213
|
+
const tenantKey = await this.keyBag.get("tenant", this.tenantId);
|
|
214
|
+
if (!tenantKey) {
|
|
215
|
+
throw new errors_1.SymmetricKeyNotFoundError(`tenant:${this.tenantId}`);
|
|
216
|
+
}
|
|
217
|
+
symmetricKey = tenantKey;
|
|
211
218
|
this.decryptedTenantKeyCache = symmetricKey;
|
|
212
219
|
}
|
|
213
220
|
}
|
|
214
221
|
else {
|
|
215
222
|
// Get the decrypted key from KeyBag
|
|
216
|
-
const decryptedKey = await this.keyBag.get(decryptionKeyId);
|
|
223
|
+
const decryptedKey = await this.keyBag.get("doc", decryptionKeyId);
|
|
217
224
|
if (!decryptedKey) {
|
|
218
225
|
throw new errors_1.SymmetricKeyNotFoundError(decryptionKeyId);
|
|
219
226
|
}
|
|
@@ -229,11 +236,11 @@ class BaseMindooTenant {
|
|
|
229
236
|
iv: iv,
|
|
230
237
|
tagLength: 128, // 128-bit authentication tag
|
|
231
238
|
}, cryptoKey, encryptedData.buffer);
|
|
232
|
-
|
|
239
|
+
this.logger.debug(`Decrypted payload (${encryptedPayload.length} -> ${decrypted.byteLength} bytes)`);
|
|
233
240
|
return new Uint8Array(decrypted);
|
|
234
241
|
}
|
|
235
242
|
async signPayload(payload) {
|
|
236
|
-
|
|
243
|
+
this.logger.debug(`Signing payload`);
|
|
237
244
|
// Get the current user's signing private key (decrypted)
|
|
238
245
|
const signingKey = await this.getDecryptedSigningKey();
|
|
239
246
|
const subtle = this.cryptoAdapter.getSubtle();
|
|
@@ -241,11 +248,11 @@ class BaseMindooTenant {
|
|
|
241
248
|
const signature = await subtle.sign({
|
|
242
249
|
name: "Ed25519",
|
|
243
250
|
}, signingKey, payload.buffer);
|
|
244
|
-
|
|
251
|
+
this.logger.debug(`Signed payload (signature: ${signature.byteLength} bytes)`);
|
|
245
252
|
return new Uint8Array(signature);
|
|
246
253
|
}
|
|
247
254
|
async signPayloadWithKey(payload, signingKeyPair, password) {
|
|
248
|
-
|
|
255
|
+
this.logger.debug(`Signing payload with provided key`);
|
|
249
256
|
const subtle = this.cryptoAdapter.getSubtle();
|
|
250
257
|
// Decrypt the provided signing private key
|
|
251
258
|
const decryptedKeyBuffer = await this.decryptPrivateKey(signingKeyPair.privateKey, password, "signing");
|
|
@@ -258,15 +265,15 @@ class BaseMindooTenant {
|
|
|
258
265
|
const signature = await subtle.sign({
|
|
259
266
|
name: "Ed25519",
|
|
260
267
|
}, signingKey, payload.buffer);
|
|
261
|
-
|
|
268
|
+
this.logger.debug(`Signed payload with provided key (signature: ${signature.byteLength} bytes)`);
|
|
262
269
|
return new Uint8Array(signature);
|
|
263
270
|
}
|
|
264
271
|
async verifySignature(payload, signature, publicKey) {
|
|
265
|
-
|
|
272
|
+
this.logger.debug(`Verifying signature`);
|
|
266
273
|
const directory = await this.openDirectory();
|
|
267
274
|
const isTrusted = await directory.validatePublicSigningKey(publicKey);
|
|
268
275
|
if (!isTrusted) {
|
|
269
|
-
|
|
276
|
+
this.logger.warn(`Public key not trusted: ${publicKey}`);
|
|
270
277
|
return false;
|
|
271
278
|
}
|
|
272
279
|
const subtle = this.cryptoAdapter.getSubtle();
|
|
@@ -281,11 +288,11 @@ class BaseMindooTenant {
|
|
|
281
288
|
const isValid = await subtle.verify({
|
|
282
289
|
name: "Ed25519",
|
|
283
290
|
}, cryptoKey, signature.buffer, payload.buffer);
|
|
284
|
-
|
|
291
|
+
this.logger.info(`Signature verification result: ${isValid}`);
|
|
285
292
|
return isValid;
|
|
286
293
|
}
|
|
287
294
|
async validatePublicSigningKey(publicKey) {
|
|
288
|
-
|
|
295
|
+
this.logger.info(`Validating public signing key`);
|
|
289
296
|
// Load the directory database to check if this key belongs to a trusted user
|
|
290
297
|
const directoryDB = await this.openDB("directory");
|
|
291
298
|
// TODO: Query the directory database to check if this public key belongs to a registered,
|
|
@@ -301,14 +308,21 @@ class BaseMindooTenant {
|
|
|
301
308
|
// Convert PrivateUserId to PublicUserId
|
|
302
309
|
return {
|
|
303
310
|
username: this.currentUser.username,
|
|
304
|
-
administrationSignature: this.currentUser.administrationSignature,
|
|
305
311
|
userSigningPublicKey: this.currentUser.userSigningKeyPair.publicKey,
|
|
306
312
|
userEncryptionPublicKey: this.currentUser.userEncryptionKeyPair.publicKey,
|
|
307
313
|
};
|
|
308
314
|
}
|
|
315
|
+
/**
|
|
316
|
+
* Get the additional trusted signing keys map, if configured.
|
|
317
|
+
* Used by BaseMindooTenantDirectory to check keys before the directory DB.
|
|
318
|
+
*/
|
|
319
|
+
getAdditionalTrustedKeys() {
|
|
320
|
+
return this.additionalTrustedKeys;
|
|
321
|
+
}
|
|
309
322
|
async openDirectory() {
|
|
310
323
|
if (!this.directoryCache) {
|
|
311
|
-
|
|
324
|
+
const directoryLogger = this.logger.createChild("Directory");
|
|
325
|
+
this.directoryCache = new BaseMindooTenantDirectory_1.BaseMindooTenantDirectory(this, directoryLogger);
|
|
312
326
|
}
|
|
313
327
|
return this.directoryCache;
|
|
314
328
|
}
|
|
@@ -331,18 +345,151 @@ class BaseMindooTenant {
|
|
|
331
345
|
const { adminOnlyDb, attachmentConfig, ...storeOptions } = effectiveOptions;
|
|
332
346
|
// Create the database stores using the factory
|
|
333
347
|
const { docStore, attachmentStore } = this.storeFactory.createStore(id, storeOptions);
|
|
334
|
-
const
|
|
348
|
+
const dbLogger = this.logger.createChild("BaseMindooDB");
|
|
349
|
+
const db = new BaseMindooDB_1.BaseMindooDB(this, docStore, attachmentStore, attachmentConfig, adminOnlyDb ?? false, dbLogger);
|
|
335
350
|
await db.initialize();
|
|
336
351
|
// Cache the database for future use
|
|
337
352
|
this.databaseCache.set(id, db);
|
|
338
353
|
return db;
|
|
339
354
|
}
|
|
340
355
|
createDocSignerFor(signKey) {
|
|
341
|
-
|
|
342
|
-
|
|
356
|
+
this.logger.debug(`Creating MindooDocSigner for signing key pair`);
|
|
357
|
+
const signerLogger = this.logger.createChild("MindooDocSigner");
|
|
358
|
+
return new MindooDocSigner_1.MindooDocSigner(this, signKey, signerLogger);
|
|
359
|
+
}
|
|
360
|
+
async approveJoinRequest(joinRequest, options) {
|
|
361
|
+
// Parse the join request if it's a URI string
|
|
362
|
+
let request;
|
|
363
|
+
if (typeof joinRequest === "string") {
|
|
364
|
+
if (!(0, MindooURI_1.isMindooURI)(joinRequest)) {
|
|
365
|
+
throw new Error("Invalid join request: expected a JoinRequest object or a mdb://join-request/... URI string");
|
|
366
|
+
}
|
|
367
|
+
const decoded = (0, MindooURI_1.decodeMindooURI)(joinRequest);
|
|
368
|
+
if (decoded.type !== "join-request") {
|
|
369
|
+
throw new Error(`Invalid URI type: expected "join-request", got "${decoded.type}"`);
|
|
370
|
+
}
|
|
371
|
+
request = decoded.payload;
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
request = joinRequest;
|
|
375
|
+
}
|
|
376
|
+
console.log(`[approveJoinRequest] Approving join request for user "${request.username}"`);
|
|
377
|
+
this.logger.info(`Approving join request for user: ${request.username}`);
|
|
378
|
+
// 1. Register the user in the directory
|
|
379
|
+
const directory = await this.openDirectory();
|
|
380
|
+
const publicUserId = {
|
|
381
|
+
username: request.username,
|
|
382
|
+
userSigningPublicKey: request.signingPublicKey,
|
|
383
|
+
userEncryptionPublicKey: request.encryptionPublicKey,
|
|
384
|
+
};
|
|
385
|
+
await directory.registerUser(publicUserId, options.adminSigningKey, options.adminPassword);
|
|
386
|
+
// 2. Export the tenant key encrypted with the share password
|
|
387
|
+
const encryptedTenantKey = await this.keyBag.encryptAndExportKey("tenant", this.tenantId, options.sharePassword);
|
|
388
|
+
if (!encryptedTenantKey) {
|
|
389
|
+
throw new Error(`Failed to export tenant key for tenant "${this.tenantId}"`);
|
|
390
|
+
}
|
|
391
|
+
// 3. Export the $publicinfos key encrypted with the share password
|
|
392
|
+
const encryptedPublicInfosKey = await this.keyBag.encryptAndExportKey("doc", types_1.PUBLIC_INFOS_KEY_ID, options.sharePassword);
|
|
393
|
+
if (!encryptedPublicInfosKey) {
|
|
394
|
+
throw new Error(`Failed to export $publicinfos key`);
|
|
395
|
+
}
|
|
396
|
+
// 4. Build the join response
|
|
397
|
+
const joinResponse = {
|
|
398
|
+
v: 1,
|
|
399
|
+
tenantId: this.tenantId,
|
|
400
|
+
adminSigningPublicKey: this.administrationPublicKey,
|
|
401
|
+
adminEncryptionPublicKey: this.administrationEncryptionPublicKey,
|
|
402
|
+
encryptedTenantKey,
|
|
403
|
+
encryptedPublicInfosKey,
|
|
404
|
+
};
|
|
405
|
+
if (options.serverUrl) {
|
|
406
|
+
joinResponse.serverUrl = options.serverUrl;
|
|
407
|
+
}
|
|
408
|
+
console.log(`[approveJoinRequest] ✓ Join request approved for user "${request.username}"`);
|
|
409
|
+
this.logger.info(`Join request approved for user: ${request.username}`);
|
|
410
|
+
// Return as URI string or object depending on format option
|
|
411
|
+
if (options.format === "uri") {
|
|
412
|
+
return (0, MindooURI_1.encodeMindooURI)("join-response", joinResponse);
|
|
413
|
+
}
|
|
414
|
+
return joinResponse;
|
|
415
|
+
}
|
|
416
|
+
/**
|
|
417
|
+
* Publish (register) this tenant on a MindooDB server.
|
|
418
|
+
*/
|
|
419
|
+
async publishToServer(serverUrl, options) {
|
|
420
|
+
console.log(`[publishToServer] Publishing tenant "${this.tenantId}" to server: ${serverUrl}`);
|
|
421
|
+
this.logger.info(`Publishing tenant "${this.tenantId}" to server: ${serverUrl}`);
|
|
422
|
+
// Export the $publicinfos key so the server can read the directory DB
|
|
423
|
+
const publicInfosKeyBytes = await this.keyBag.get("doc", types_1.PUBLIC_INFOS_KEY_ID);
|
|
424
|
+
if (!publicInfosKeyBytes) {
|
|
425
|
+
throw new Error(`Cannot publish to server: $publicinfos key not found in KeyBag`);
|
|
426
|
+
}
|
|
427
|
+
const publicInfosKeyBase64 = this.uint8ArrayToBase64(publicInfosKeyBytes);
|
|
428
|
+
// Build the registration request
|
|
429
|
+
const requestBody = {
|
|
430
|
+
tenantId: this.tenantId,
|
|
431
|
+
adminSigningPublicKey: this.administrationPublicKey,
|
|
432
|
+
adminEncryptionPublicKey: this.administrationEncryptionPublicKey,
|
|
433
|
+
publicInfosKey: publicInfosKeyBase64,
|
|
434
|
+
};
|
|
435
|
+
// Add users if provided
|
|
436
|
+
if (options?.registerUsers && options.registerUsers.length > 0) {
|
|
437
|
+
requestBody.users = options.registerUsers.map((u) => ({
|
|
438
|
+
username: u.username,
|
|
439
|
+
signingPublicKey: u.userSigningPublicKey,
|
|
440
|
+
encryptionPublicKey: u.userEncryptionPublicKey,
|
|
441
|
+
}));
|
|
442
|
+
}
|
|
443
|
+
// Build headers
|
|
444
|
+
const headers = {
|
|
445
|
+
"Content-Type": "application/json",
|
|
446
|
+
};
|
|
447
|
+
if (options?.adminApiKey) {
|
|
448
|
+
headers["x-api-key"] = options.adminApiKey;
|
|
449
|
+
}
|
|
450
|
+
// POST to the server's admin register-tenant endpoint
|
|
451
|
+
const url = `${serverUrl.replace(/\/$/, "")}/admin/register-tenant`;
|
|
452
|
+
const response = await fetch(url, {
|
|
453
|
+
method: "POST",
|
|
454
|
+
headers,
|
|
455
|
+
body: JSON.stringify(requestBody),
|
|
456
|
+
});
|
|
457
|
+
if (!response.ok) {
|
|
458
|
+
const errorBody = await response.text();
|
|
459
|
+
throw new Error(`Failed to publish tenant to server (HTTP ${response.status}): ${errorBody}`);
|
|
460
|
+
}
|
|
461
|
+
console.log(`[publishToServer] ✓ Tenant "${this.tenantId}" published to server successfully`);
|
|
462
|
+
this.logger.info(`Tenant "${this.tenantId}" published to server successfully`);
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Create a remote store connected to a MindooDB server, ready for sync.
|
|
466
|
+
*/
|
|
467
|
+
async connectToServer(serverUrl, dbId) {
|
|
468
|
+
console.log(`[connectToServer] Connecting to server: ${serverUrl}, db: ${dbId}`);
|
|
469
|
+
this.logger.info(`Connecting to server: ${serverUrl}, db: ${dbId}`);
|
|
470
|
+
// Lazy-import network modules to avoid circular dependencies and keep core lightweight
|
|
471
|
+
const { HttpTransport } = await import("../appendonlystores/network/HttpTransport.js");
|
|
472
|
+
const { ClientNetworkContentAddressedStore } = await import("../appendonlystores/network/ClientNetworkContentAddressedStore.js");
|
|
473
|
+
// Create the HTTP transport
|
|
474
|
+
const baseUrl = `${serverUrl.replace(/\/$/, "")}/${this.tenantId}`;
|
|
475
|
+
const transport = new HttpTransport({
|
|
476
|
+
baseUrl,
|
|
477
|
+
tenantId: this.tenantId,
|
|
478
|
+
dbId,
|
|
479
|
+
}, this.logger.createChild("HttpTransport"));
|
|
480
|
+
// Get the current user's decrypted signing key
|
|
481
|
+
const signingKey = await this.getDecryptedSigningKey();
|
|
482
|
+
// Get the current user's decrypted RSA encryption private key (for decrypting entries)
|
|
483
|
+
const decryptedEncryptionKey = await this.getDecryptedEncryptionKey();
|
|
484
|
+
// Create the client network store
|
|
485
|
+
const store = new ClientNetworkContentAddressedStore(dbId, transport, this.cryptoAdapter, this.currentUser.username, signingKey, decryptedEncryptionKey, this.logger.createChild(`ClientNetworkStore:${dbId}`));
|
|
486
|
+
console.log(`[connectToServer] ✓ Connected to server for db "${dbId}"`);
|
|
487
|
+
this.logger.info(`Connected to server for db "${dbId}"`);
|
|
488
|
+
return store;
|
|
343
489
|
}
|
|
344
490
|
/**
|
|
345
491
|
* Internal method to get the decrypted signing key for the current user.
|
|
492
|
+
* Protected so that connectToServer() helper and subclasses can access it.
|
|
346
493
|
*/
|
|
347
494
|
async getDecryptedSigningKey() {
|
|
348
495
|
// Use cached signing key if available
|
|
@@ -360,6 +507,23 @@ class BaseMindooTenant {
|
|
|
360
507
|
this.decryptedUserSigningKeyCache = cryptoKey;
|
|
361
508
|
return cryptoKey;
|
|
362
509
|
}
|
|
510
|
+
/**
|
|
511
|
+
* Internal method to get the decrypted RSA encryption private key for the current user.
|
|
512
|
+
* Used by connectToServer() to create the network store.
|
|
513
|
+
*/
|
|
514
|
+
async getDecryptedEncryptionKey() {
|
|
515
|
+
if (this.decryptedUserEncryptionKeyCache) {
|
|
516
|
+
return this.decryptedUserEncryptionKeyCache;
|
|
517
|
+
}
|
|
518
|
+
const decryptedKeyBuffer = await this.decryptPrivateKey(this.currentUser.userEncryptionKeyPair.privateKey, this.currentUserPassword, "encryption");
|
|
519
|
+
const subtle = this.cryptoAdapter.getSubtle();
|
|
520
|
+
const cryptoKey = await subtle.importKey("pkcs8", decryptedKeyBuffer, {
|
|
521
|
+
name: "RSA-OAEP",
|
|
522
|
+
hash: "SHA-256",
|
|
523
|
+
}, false, ["decrypt"]);
|
|
524
|
+
this.decryptedUserEncryptionKeyCache = cryptoKey;
|
|
525
|
+
return cryptoKey;
|
|
526
|
+
}
|
|
363
527
|
/**
|
|
364
528
|
* Internal method to decrypt a private key using password-based key derivation.
|
|
365
529
|
* Protected so that BaseMindooTenantDirectory can access it.
|
|
@@ -371,12 +535,12 @@ class BaseMindooTenant {
|
|
|
371
535
|
* @returns The decrypted private key as ArrayBuffer
|
|
372
536
|
*/
|
|
373
537
|
async decryptPrivateKey(encryptedKey, password, saltString) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
538
|
+
this.logger.debug(`decryptPrivateKey: Starting decryption with saltString: ${saltString}`);
|
|
539
|
+
this.logger.debug(`decryptPrivateKey: Password length: ${password.length}`);
|
|
540
|
+
this.logger.debug(`decryptPrivateKey: EncryptedKey iterations: ${encryptedKey.iterations}`);
|
|
377
541
|
const subtle = this.cryptoAdapter.getSubtle();
|
|
378
542
|
// Decode base64 strings
|
|
379
|
-
|
|
543
|
+
this.logger.debug(`decryptPrivateKey: Decoding base64 strings`);
|
|
380
544
|
let ciphertext;
|
|
381
545
|
let iv;
|
|
382
546
|
let tag;
|
|
@@ -386,33 +550,33 @@ class BaseMindooTenant {
|
|
|
386
550
|
iv = this.base64ToUint8Array(encryptedKey.iv);
|
|
387
551
|
tag = this.base64ToUint8Array(encryptedKey.tag);
|
|
388
552
|
saltBytes = this.base64ToUint8Array(encryptedKey.salt);
|
|
389
|
-
|
|
553
|
+
this.logger.debug(`decryptPrivateKey: Decoded - ciphertext: ${ciphertext.length} bytes, iv: ${iv.length} bytes, tag: ${tag.length} bytes, salt: ${saltBytes.length} bytes`);
|
|
390
554
|
}
|
|
391
555
|
catch (error) {
|
|
392
|
-
|
|
556
|
+
this.logger.error(`decryptPrivateKey: Error decoding base64:`, error);
|
|
393
557
|
throw error;
|
|
394
558
|
}
|
|
395
559
|
// Derive key from password using PBKDF2
|
|
396
560
|
// Combine the stored salt bytes with the salt string for additional security
|
|
397
561
|
// This ensures different key types (signing, encryption, etc.) use different derived keys
|
|
398
562
|
// even if they share the same password
|
|
399
|
-
|
|
563
|
+
this.logger.debug(`decryptPrivateKey: Combining salt with saltString`);
|
|
400
564
|
const saltStringBytes = new TextEncoder().encode(saltString);
|
|
401
565
|
const combinedSalt = new Uint8Array(saltBytes.length + saltStringBytes.length);
|
|
402
566
|
combinedSalt.set(saltBytes);
|
|
403
567
|
combinedSalt.set(saltStringBytes, saltBytes.length);
|
|
404
|
-
|
|
405
|
-
|
|
568
|
+
this.logger.debug(`decryptPrivateKey: Combined salt length: ${combinedSalt.length} bytes`);
|
|
569
|
+
this.logger.debug(`decryptPrivateKey: Importing password key for PBKDF2`);
|
|
406
570
|
let passwordKey;
|
|
407
571
|
try {
|
|
408
572
|
passwordKey = await subtle.importKey("raw", new TextEncoder().encode(password), "PBKDF2", false, ["deriveBits", "deriveKey"]);
|
|
409
|
-
|
|
573
|
+
this.logger.debug(`decryptPrivateKey: Successfully imported password key`);
|
|
410
574
|
}
|
|
411
575
|
catch (error) {
|
|
412
|
-
|
|
576
|
+
this.logger.error(`decryptPrivateKey: Error importing password key:`, error);
|
|
413
577
|
throw error;
|
|
414
578
|
}
|
|
415
|
-
|
|
579
|
+
this.logger.debug(`decryptPrivateKey: Deriving key with PBKDF2 (iterations: ${encryptedKey.iterations})`);
|
|
416
580
|
let derivedKey;
|
|
417
581
|
try {
|
|
418
582
|
derivedKey = await subtle.deriveKey({
|
|
@@ -424,39 +588,39 @@ class BaseMindooTenant {
|
|
|
424
588
|
name: "AES-GCM",
|
|
425
589
|
length: 256,
|
|
426
590
|
}, false, ["decrypt"]);
|
|
427
|
-
|
|
591
|
+
this.logger.debug(`decryptPrivateKey: Successfully derived key`);
|
|
428
592
|
}
|
|
429
593
|
catch (error) {
|
|
430
|
-
|
|
594
|
+
this.logger.error(`decryptPrivateKey: Error deriving key:`, error);
|
|
431
595
|
throw error;
|
|
432
596
|
}
|
|
433
597
|
// Combine ciphertext and tag (GCM authentication tag is separate)
|
|
434
598
|
// AES-GCM expects the tag to be appended to the ciphertext
|
|
435
|
-
|
|
599
|
+
this.logger.debug(`decryptPrivateKey: Combining ciphertext and tag`);
|
|
436
600
|
const encryptedData = new Uint8Array(ciphertext.length + tag.length);
|
|
437
601
|
encryptedData.set(ciphertext);
|
|
438
602
|
encryptedData.set(tag, ciphertext.length);
|
|
439
|
-
|
|
603
|
+
this.logger.debug(`decryptPrivateKey: Combined encrypted data length: ${encryptedData.length} bytes`);
|
|
440
604
|
// Decrypt the private key
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
605
|
+
this.logger.debug(`decryptPrivateKey: Decrypting with AES-GCM`);
|
|
606
|
+
this.logger.debug(`decryptPrivateKey: IV length: ${iv.length} bytes (expected: 12 for AES-GCM)`);
|
|
607
|
+
this.logger.debug(`decryptPrivateKey: Tag length: ${tag.length} bytes (expected: 16 for 128-bit tag)`);
|
|
608
|
+
this.logger.debug(`decryptPrivateKey: Ciphertext length: ${ciphertext.length} bytes`);
|
|
445
609
|
let decrypted;
|
|
446
610
|
try {
|
|
447
611
|
// Create a new ArrayBuffer to ensure we have the correct buffer without any offset issues
|
|
448
612
|
const encryptedBuffer = new Uint8Array(encryptedData).buffer;
|
|
449
|
-
|
|
613
|
+
this.logger.debug(`decryptPrivateKey: Encrypted buffer size: ${encryptedBuffer.byteLength} bytes`);
|
|
450
614
|
decrypted = await subtle.decrypt({
|
|
451
615
|
name: "AES-GCM",
|
|
452
616
|
iv: new Uint8Array(iv), // Create a new Uint8Array to ensure proper buffer
|
|
453
617
|
tagLength: 128,
|
|
454
618
|
}, derivedKey, encryptedBuffer);
|
|
455
|
-
|
|
619
|
+
this.logger.debug(`decryptPrivateKey: Successfully decrypted, result length: ${decrypted.byteLength} bytes`);
|
|
456
620
|
}
|
|
457
621
|
catch (error) {
|
|
458
|
-
|
|
459
|
-
|
|
622
|
+
this.logger.error(`decryptPrivateKey: Error during decryption:`, error);
|
|
623
|
+
this.logger.error(`decryptPrivateKey: Error details:`, {
|
|
460
624
|
errorName: error instanceof Error ? error.name : typeof error,
|
|
461
625
|
errorMessage: error instanceof Error ? error.message : String(error),
|
|
462
626
|
ivLength: iv.length,
|
|
@@ -531,15 +695,17 @@ class BaseMindooTenant {
|
|
|
531
695
|
if (this.decryptedTenantKeyCache) {
|
|
532
696
|
return this.decryptedTenantKeyCache;
|
|
533
697
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
698
|
+
const tenantKey = await this.keyBag.get("tenant", this.tenantId);
|
|
699
|
+
if (!tenantKey) {
|
|
700
|
+
throw new errors_1.SymmetricKeyNotFoundError(`tenant:${this.tenantId}`);
|
|
701
|
+
}
|
|
702
|
+
const symmetricKey = tenantKey;
|
|
537
703
|
this.decryptedTenantKeyCache = symmetricKey;
|
|
538
704
|
return symmetricKey;
|
|
539
705
|
}
|
|
540
706
|
else {
|
|
541
707
|
// Get the decrypted key from KeyBag
|
|
542
|
-
const decryptedKey = await this.keyBag.get(decryptionKeyId);
|
|
708
|
+
const decryptedKey = await this.keyBag.get("doc", decryptionKeyId);
|
|
543
709
|
if (!decryptedKey) {
|
|
544
710
|
throw new errors_1.SymmetricKeyNotFoundError(decryptionKeyId);
|
|
545
711
|
}
|
|
@@ -576,7 +742,7 @@ class BaseMindooTenant {
|
|
|
576
742
|
* @returns The encrypted payload with mode prefix
|
|
577
743
|
*/
|
|
578
744
|
async encryptAttachmentPayload(payload, decryptionKeyId) {
|
|
579
|
-
|
|
745
|
+
this.logger.debug(`Encrypting attachment payload with key: ${decryptionKeyId}`);
|
|
580
746
|
const symmetricKey = await this.getSymmetricKey(decryptionKeyId);
|
|
581
747
|
const subtle = this.cryptoAdapter.getSubtle();
|
|
582
748
|
const randomValues = this.cryptoAdapter.getRandomValues.bind(this.cryptoAdapter);
|
|
@@ -595,13 +761,13 @@ class BaseMindooTenant {
|
|
|
595
761
|
const payloadBuffer = payload.buffer.slice(payload.byteOffset, payload.byteOffset + payload.byteLength);
|
|
596
762
|
const hashBuffer = await subtle.digest("SHA-256", payloadBuffer);
|
|
597
763
|
iv = new Uint8Array(hashBuffer).slice(0, 12);
|
|
598
|
-
|
|
764
|
+
this.logger.debug(`Using deterministic IV derived from content hash`);
|
|
599
765
|
}
|
|
600
766
|
else {
|
|
601
767
|
// Random IV
|
|
602
768
|
iv = new Uint8Array(12);
|
|
603
769
|
randomValues(iv);
|
|
604
|
-
|
|
770
|
+
this.logger.debug(`Using random IV`);
|
|
605
771
|
}
|
|
606
772
|
// Encrypt the payload
|
|
607
773
|
const payloadArray = new Uint8Array(payload);
|
|
@@ -616,7 +782,7 @@ class BaseMindooTenant {
|
|
|
616
782
|
result[0] = mode;
|
|
617
783
|
result.set(iv, 1);
|
|
618
784
|
result.set(encryptedArray, 13);
|
|
619
|
-
|
|
785
|
+
this.logger.debug(`Encrypted attachment (mode=${mode}, ${payload.length} -> ${result.length} bytes)`);
|
|
620
786
|
return result;
|
|
621
787
|
}
|
|
622
788
|
/**
|
|
@@ -628,7 +794,7 @@ class BaseMindooTenant {
|
|
|
628
794
|
* @returns The decrypted plaintext
|
|
629
795
|
*/
|
|
630
796
|
async decryptAttachmentPayload(encryptedPayload, decryptionKeyId) {
|
|
631
|
-
|
|
797
|
+
this.logger.debug(`Decrypting attachment payload with key: ${decryptionKeyId}`);
|
|
632
798
|
if (encryptedPayload.length < 13) {
|
|
633
799
|
throw new Error("Invalid encrypted attachment payload: too short");
|
|
634
800
|
}
|
|
@@ -638,7 +804,7 @@ class BaseMindooTenant {
|
|
|
638
804
|
mode !== BaseMindooTenant.ENCRYPTION_MODE_DETERMINISTIC) {
|
|
639
805
|
throw new Error(`Invalid encryption mode: ${mode}`);
|
|
640
806
|
}
|
|
641
|
-
|
|
807
|
+
this.logger.debug(`Decrypting attachment with mode=${mode}`);
|
|
642
808
|
// Extract IV (bytes 1-12)
|
|
643
809
|
const iv = encryptedPayload.slice(1, 13);
|
|
644
810
|
// Extract ciphertext (bytes 13 onwards)
|
|
@@ -655,7 +821,7 @@ class BaseMindooTenant {
|
|
|
655
821
|
tagLength: 128,
|
|
656
822
|
}, cryptoKey, ciphertext);
|
|
657
823
|
const result = new Uint8Array(decrypted);
|
|
658
|
-
|
|
824
|
+
this.logger.debug(`Decrypted attachment (${encryptedPayload.length} -> ${result.length} bytes)`);
|
|
659
825
|
return result;
|
|
660
826
|
}
|
|
661
827
|
}
|