@abraca/dabra 1.8.2 → 2.0.0
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/dist/abracadabra-provider.cjs +12722 -9050
- package/dist/abracadabra-provider.cjs.map +1 -1
- package/dist/abracadabra-provider.esm.js +12683 -9061
- package/dist/abracadabra-provider.esm.js.map +1 -1
- package/dist/index.d.ts +1485 -118
- package/package.json +1 -1
- package/src/AbracadabraBaseProvider.ts +51 -2
- package/src/AbracadabraClient.ts +516 -66
- package/src/AbracadabraProvider.ts +22 -7
- package/src/AbracadabraWS.ts +1 -1
- package/src/ChatClient.ts +193 -113
- package/src/ContentManager.ts +228 -0
- package/src/CryptoIdentityKeystore.ts +3 -3
- package/src/DocConverters.ts +1862 -0
- package/src/DocKeyManager.ts +60 -12
- package/src/DocTypes.ts +628 -0
- package/src/DocUtils.ts +89 -0
- package/src/DocumentManager.ts +319 -0
- package/src/E2EAbracadabraProvider.ts +189 -0
- package/src/EncryptedChatClient.ts +173 -0
- package/src/EncryptedY.ts +2 -2
- package/src/FileBlobStore.ts +10 -0
- package/src/IdentityDoc.ts +25 -0
- package/src/MetaManager.ts +100 -0
- package/src/MnemonicKeyDerivation.ts +4 -4
- package/src/NotificationsClient.ts +120 -98
- package/src/OutgoingMessages/SubdocMessage.ts +2 -2
- package/src/RpcClient.ts +659 -0
- package/src/TreeManager.ts +473 -0
- package/src/TreeTimestamps.ts +28 -25
- package/src/index.ts +71 -1
- package/src/messageRecord.ts +121 -0
- package/src/types.ts +174 -16
- package/src/webrtc/AbracadabraWebRTC.ts +2 -2
- package/src/webrtc/DataChannelRouter.ts +2 -2
- package/src/webrtc/E2EEChannel.ts +3 -3
- package/src/webrtc/FileTransferChannel.ts +9 -2
package/src/DocKeyManager.ts
CHANGED
|
@@ -45,16 +45,64 @@ export class DocKeyManager {
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const envelope = await client.getMyKeyEnvelope(docId);
|
|
48
|
-
if (
|
|
48
|
+
if (envelope) {
|
|
49
|
+
const x25519PrivKey = await keystore.getX25519PrivateKey();
|
|
50
|
+
try {
|
|
51
|
+
const wrapped = fromBase64(envelope.encrypted_key);
|
|
52
|
+
const docKey = await this._unwrapKey(wrapped, x25519PrivKey, docId);
|
|
53
|
+
this.cache.set(docId, { key: docKey, epoch: envelope.key_epoch, fetchedAt: Date.now() });
|
|
54
|
+
return docKey;
|
|
55
|
+
} finally {
|
|
56
|
+
x25519PrivKey.fill(0);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
49
59
|
|
|
50
|
-
|
|
60
|
+
// Inheritance fallback. The doc has no envelope of our own — but it
|
|
61
|
+
// might inherit its encryption mode from an ancestor (e.g. a child
|
|
62
|
+
// page under an E2E space). The ancestor IS provisioned for us
|
|
63
|
+
// (otherwise we couldn't open the parent either). Resolve the
|
|
64
|
+
// encryption-source via /docs/:id/encryption.inherited_from, fetch
|
|
65
|
+
// OUR envelope on that source, unwrap with the source's salt,
|
|
66
|
+
// re-wrap for the current docId with its own salt, upload the new
|
|
67
|
+
// envelope so subsequent calls hit the direct-envelope path.
|
|
51
68
|
try {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
69
|
+
const enc = await client.getDocEncryption(docId);
|
|
70
|
+
if (enc.effective_mode !== "e2e" || !enc.inherited_from || enc.inherited_from === docId) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
const sourceEnv = await client.getMyKeyEnvelope(enc.inherited_from);
|
|
74
|
+
if (!sourceEnv) return null;
|
|
75
|
+
const x25519PrivKey = await keystore.getX25519PrivateKey();
|
|
76
|
+
try {
|
|
77
|
+
const wrappedSrc = fromBase64(sourceEnv.encrypted_key);
|
|
78
|
+
const docKey = await this._unwrapKey(wrappedSrc, x25519PrivKey, enc.inherited_from);
|
|
79
|
+
this.cache.set(docId, { key: docKey, epoch: sourceEnv.key_epoch, fetchedAt: Date.now() });
|
|
80
|
+
// Re-wrap for the current doc + upload so future opens take
|
|
81
|
+
// the direct path. Best-effort — caller already has the key.
|
|
82
|
+
const me = await client.getMe();
|
|
83
|
+
if (me.publicKey) {
|
|
84
|
+
const myKeys = await client.listUserKeys(me.id);
|
|
85
|
+
const primaryKey = myKeys[0];
|
|
86
|
+
if (primaryKey?.x25519Key) {
|
|
87
|
+
const x25519Pub = fromBase64(primaryKey.x25519Key.replace(/-/g, "+").replace(/_/g, "/"));
|
|
88
|
+
const rewrapped = await this.wrapKeyForRecipient(docKey, x25519Pub, docId);
|
|
89
|
+
const b64 = (() => {
|
|
90
|
+
let s = "";
|
|
91
|
+
for (let i = 0; i < rewrapped.length; i++) s += String.fromCharCode(rewrapped[i]!);
|
|
92
|
+
return btoa(s);
|
|
93
|
+
})();
|
|
94
|
+
await client.uploadKeyEnvelopes(docId, {
|
|
95
|
+
key_epoch: sourceEnv.key_epoch,
|
|
96
|
+
envelopes: [{ recipient_key_id: primaryKey.id, encrypted_key: b64 }],
|
|
97
|
+
}).catch(() => null);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return docKey;
|
|
101
|
+
} finally {
|
|
102
|
+
x25519PrivKey.fill(0);
|
|
103
|
+
}
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
58
106
|
}
|
|
59
107
|
}
|
|
60
108
|
|
|
@@ -73,11 +121,11 @@ export class DocKeyManager {
|
|
|
73
121
|
|
|
74
122
|
const salt = new TextEncoder().encode(docId);
|
|
75
123
|
const keyBytes = hkdf(sha256, sharedSecret, salt, HKDF_INFO, 32);
|
|
76
|
-
const wrapKey = await crypto.subtle.importKey("raw", keyBytes, { name: "AES-GCM" }, false, ["encrypt"]);
|
|
124
|
+
const wrapKey = await crypto.subtle.importKey("raw", keyBytes as BufferSource, { name: "AES-GCM" }, false, ["encrypt"]);
|
|
77
125
|
|
|
78
126
|
const rawDocKey = await crypto.subtle.exportKey("raw", docKey);
|
|
79
127
|
const nonce = crypto.getRandomValues(new Uint8Array(12));
|
|
80
|
-
const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce }, wrapKey, rawDocKey));
|
|
128
|
+
const ciphertext = new Uint8Array(await crypto.subtle.encrypt({ name: "AES-GCM", iv: nonce as BufferSource }, wrapKey, rawDocKey));
|
|
81
129
|
|
|
82
130
|
const result = new Uint8Array(32 + 12 + ciphertext.length);
|
|
83
131
|
result.set(ephemeralPub, 0);
|
|
@@ -94,8 +142,8 @@ export class DocKeyManager {
|
|
|
94
142
|
const sharedSecret = x25519.getSharedSecret(recipientX25519PrivKey, ephemeralPub);
|
|
95
143
|
const salt = new TextEncoder().encode(docId);
|
|
96
144
|
const keyBytes = hkdf(sha256, sharedSecret, salt, HKDF_INFO, 32);
|
|
97
|
-
const wrapKey = await crypto.subtle.importKey("raw", keyBytes, { name: "AES-GCM" }, false, ["decrypt"]);
|
|
98
|
-
const rawDocKey = await crypto.subtle.decrypt({ name: "AES-GCM", iv: nonce }, wrapKey, ciphertext);
|
|
145
|
+
const wrapKey = await crypto.subtle.importKey("raw", keyBytes as BufferSource, { name: "AES-GCM" }, false, ["decrypt"]);
|
|
146
|
+
const rawDocKey = await crypto.subtle.decrypt({ name: "AES-GCM", iv: nonce as BufferSource }, wrapKey, ciphertext as BufferSource);
|
|
99
147
|
return crypto.subtle.importKey("raw", rawDocKey, { name: "AES-GCM" }, true, ["encrypt", "decrypt"]);
|
|
100
148
|
}
|
|
101
149
|
|