@batalabs/virlow-crypto 3.11.4

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Typelets Secure Notes
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # @batalabs/virlow-crypto
2
+
3
+ Client-side encryption primitives for [Virlow](https://virlow.com): PBKDF2 key
4
+ derivation and AES-256-GCM. Framework-free; runs in browsers and Node 20+.
5
+
6
+ Virlow encrypts notes on the user's device, so the key is derived from their
7
+ master password and never sent anywhere. This package is that layer, extracted
8
+ so the web app, the local MCP connector, and the hosted connector all use one
9
+ implementation rather than three.
10
+
11
+ ```ts
12
+ import { deriveMasterKey, encryptNoteFields, decryptNoteFields } from '@batalabs/virlow-crypto';
13
+
14
+ const { key } = await deriveMasterKey(masterPassword, userId);
15
+ const { encryptedTitle, encryptedContent, iv } = await encryptNoteFields(key, title, body);
16
+ ```
17
+
18
+ Published from [batalabs/virlow-app](https://github.com/batalabs/virlow-app).
@@ -0,0 +1,4 @@
1
+ export declare function bytesToBase64(input: Uint8Array | ArrayBuffer): string;
2
+ export declare function base64ToBytes(base64: string): Uint8Array;
3
+ export declare function base64ToArrayBuffer(base64: string): ArrayBuffer;
4
+ //# sourceMappingURL=base64.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64.d.ts","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"AAEA,wBAAgB,aAAa,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,MAAM,CAQrE;AAED,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAOxD;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,WAAW,CAM/D"}
package/dist/base64.js ADDED
@@ -0,0 +1,23 @@
1
+ const CHUNK_SIZE = 0x8000;
2
+ export function bytesToBase64(input) {
3
+ const bytes = input instanceof Uint8Array ? input : new Uint8Array(input);
4
+ const chunks = [];
5
+ for (let i = 0; i < bytes.length; i += CHUNK_SIZE) {
6
+ const chunk = bytes.subarray(i, Math.min(i + CHUNK_SIZE, bytes.length));
7
+ chunks.push(String.fromCharCode.apply(null, Array.from(chunk)));
8
+ }
9
+ return btoa(chunks.join(''));
10
+ }
11
+ export function base64ToBytes(base64) {
12
+ const binary = atob(base64);
13
+ const bytes = new Uint8Array(binary.length);
14
+ for (let i = 0; i < binary.length; i++) {
15
+ bytes[i] = binary.charCodeAt(i);
16
+ }
17
+ return bytes;
18
+ }
19
+ export function base64ToArrayBuffer(base64) {
20
+ const bytes = base64ToBytes(base64);
21
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
22
+ }
23
+ //# sourceMappingURL=base64.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base64.js","sourceRoot":"","sources":["../src/base64.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,GAAG,MAAM,CAAC;AAE1B,MAAM,UAAU,aAAa,CAAC,KAA+B;IAC3D,MAAM,KAAK,GAAG,KAAK,YAAY,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAC1E,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,IAAI,UAAU,EAAE,CAAC;QAClD,MAAM,KAAK,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QACxE,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAClE,CAAC;IACD,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;AAC/B,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,MAAc;IAC1C,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,CAAC;IAC5B,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC5C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACvC,KAAK,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,KAAK,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CACvB,KAAK,CAAC,UAAU,EAChB,KAAK,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CACrB,CAAC;AACnB,CAAC"}
package/dist/blob.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ /** Encrypt bytes into a self-describing "v2:<ivB64>:<ciphertextB64>" blob. */
2
+ export declare function encryptBytesBlob(key: CryptoKey, bytes: Uint8Array): Promise<string>;
3
+ /** Decrypt a "v2:<ivB64>:<ciphertextB64>" blob back to bytes. */
4
+ export declare function decryptBytesBlob(key: CryptoKey, blob: string): Promise<Uint8Array>;
5
+ export declare function encryptStringBlob(key: CryptoKey, plaintext: string): Promise<string>;
6
+ export declare function decryptStringBlob(key: CryptoKey, blob: string): Promise<string>;
7
+ //# sourceMappingURL=blob.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob.d.ts","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"AAGA,8EAA8E;AAC9E,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,UAAU,GAChB,OAAO,CAAC,MAAM,CAAC,CAQjB;AAED,iEAAiE;AACjE,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,UAAU,CAAC,CAgBrB;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,SAAS,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAEjB;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,MAAM,CAAC,CAEjB"}
package/dist/blob.js ADDED
@@ -0,0 +1,29 @@
1
+ import { base64ToArrayBuffer, base64ToBytes, bytesToBase64 } from './base64';
2
+ import { ENCRYPTION_CONFIG } from './constants';
3
+ /** Encrypt bytes into a self-describing "v2:<ivB64>:<ciphertextB64>" blob. */
4
+ export async function encryptBytesBlob(key, bytes) {
5
+ const iv = crypto.getRandomValues(new Uint8Array(ENCRYPTION_CONFIG.IV_LENGTH));
6
+ const ciphertext = await crypto.subtle.encrypt({ name: ENCRYPTION_CONFIG.ALGORITHM, iv }, key, bytes);
7
+ return 'v2:' + bytesToBase64(iv) + ':' + bytesToBase64(ciphertext);
8
+ }
9
+ /** Decrypt a "v2:<ivB64>:<ciphertextB64>" blob back to bytes. */
10
+ export async function decryptBytesBlob(key, blob) {
11
+ if (!blob.startsWith('v2:')) {
12
+ throw new Error('Malformed encrypted blob: missing v2 prefix');
13
+ }
14
+ const rest = blob.slice(3);
15
+ const sep = rest.indexOf(':');
16
+ if (sep === -1) {
17
+ throw new Error('Malformed encrypted blob: missing IV separator');
18
+ }
19
+ const iv = base64ToBytes(rest.slice(0, sep));
20
+ const decrypted = await crypto.subtle.decrypt({ name: ENCRYPTION_CONFIG.ALGORITHM, iv: iv }, key, base64ToArrayBuffer(rest.slice(sep + 1)));
21
+ return new Uint8Array(decrypted);
22
+ }
23
+ export async function encryptStringBlob(key, plaintext) {
24
+ return encryptBytesBlob(key, new TextEncoder().encode(plaintext));
25
+ }
26
+ export async function decryptStringBlob(key, blob) {
27
+ return new TextDecoder().decode(await decryptBytesBlob(key, blob));
28
+ }
29
+ //# sourceMappingURL=blob.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"blob.js","sourceRoot":"","sources":["../src/blob.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAc,EACd,KAAiB;IAEjB,MAAM,EAAE,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/E,MAAM,UAAU,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAC5C,EAAE,IAAI,EAAE,iBAAiB,CAAC,SAAS,EAAE,EAAE,EAAE,EACzC,GAAG,EACH,KAAqB,CACtB,CAAC;IACF,OAAO,KAAK,GAAG,aAAa,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,aAAa,CAAC,UAAU,CAAC,CAAC;AACrE,CAAC;AAED,iEAAiE;AACjE,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,GAAc,EACd,IAAY;IAEZ,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;IACjE,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC3B,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QACf,MAAM,IAAI,KAAK,CAAC,gDAAgD,CAAC,CAAC;IACpE,CAAC;IACD,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC7C,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAC3C,EAAE,IAAI,EAAE,iBAAiB,CAAC,SAAS,EAAE,EAAE,EAAE,EAAkB,EAAE,EAC7D,GAAG,EACH,mBAAmB,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CACzC,CAAC;IACF,OAAO,IAAI,UAAU,CAAC,SAAS,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAAc,EACd,SAAiB;IAEjB,OAAO,gBAAgB,CAAC,GAAG,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;AACpE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAAc,EACd,IAAY;IAEZ,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,MAAM,gBAAgB,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;AACrE,CAAC"}
@@ -0,0 +1,13 @@
1
+ export declare const ENCRYPTION_CONFIG: {
2
+ readonly ALGORITHM: "AES-GCM";
3
+ readonly KEY_LENGTH: 256;
4
+ readonly IV_LENGTH: 16;
5
+ readonly ITERATIONS: 250000;
6
+ readonly SALT_LENGTH: 32;
7
+ };
8
+ /**
9
+ * Known constant encrypted under the master key to form the verifier blob.
10
+ * Verification = GCM-decrypt succeeds (auth tag) AND plaintext equals this.
11
+ */
12
+ export declare const VERIFIER_CONSTANT = "virlow-mp-verifier-v1";
13
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,iBAAiB;;;;;;CAMpB,CAAC;AAEX;;;GAGG;AACH,eAAO,MAAM,iBAAiB,0BAA0B,CAAC"}
@@ -0,0 +1,13 @@
1
+ export const ENCRYPTION_CONFIG = {
2
+ ALGORITHM: 'AES-GCM',
3
+ KEY_LENGTH: 256,
4
+ IV_LENGTH: 16,
5
+ ITERATIONS: 250000,
6
+ SALT_LENGTH: 32,
7
+ };
8
+ /**
9
+ * Known constant encrypted under the master key to form the verifier blob.
10
+ * Verification = GCM-decrypt succeeds (auth tag) AND plaintext equals this.
11
+ */
12
+ export const VERIFIER_CONSTANT = 'virlow-mp-verifier-v1';
13
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,SAAS,EAAE,SAAS;IACpB,UAAU,EAAE,GAAG;IACf,SAAS,EAAE,EAAE;IACb,UAAU,EAAE,MAAM;IAClB,WAAW,EAAE,EAAE;CACP,CAAC;AAEX;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,uBAAuB,CAAC"}
@@ -0,0 +1,8 @@
1
+ export { base64ToArrayBuffer, base64ToBytes, bytesToBase64 } from './base64';
2
+ export { decryptBytesBlob, decryptStringBlob, encryptBytesBlob, encryptStringBlob, } from './blob';
3
+ export { ENCRYPTION_CONFIG, VERIFIER_CONSTANT } from './constants';
4
+ export { deriveMasterKey, importMasterKey } from './keys';
5
+ export { decryptNoteFields, decryptTitleField, encryptNoteFields, type EncryptedNoteFields, } from './notes';
6
+ export { canDecryptWithKey, createVerifierBlob, verifyVerifierBlob, type VerifierBlob, } from './verifier';
7
+ export declare const PACKAGE_NAME = "@batalabs/virlow-crypto";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,EACjB,KAAK,mBAAmB,GACzB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,EAClB,KAAK,YAAY,GAClB,MAAM,YAAY,CAAC;AACpB,eAAO,MAAM,YAAY,4BAA4B,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,8 @@
1
+ export { base64ToArrayBuffer, base64ToBytes, bytesToBase64 } from './base64';
2
+ export { decryptBytesBlob, decryptStringBlob, encryptBytesBlob, encryptStringBlob, } from './blob';
3
+ export { ENCRYPTION_CONFIG, VERIFIER_CONSTANT } from './constants';
4
+ export { deriveMasterKey, importMasterKey } from './keys';
5
+ export { decryptNoteFields, decryptTitleField, encryptNoteFields, } from './notes';
6
+ export { canDecryptWithKey, createVerifierBlob, verifyVerifierBlob, } from './verifier';
7
+ export const PACKAGE_NAME = '@batalabs/virlow-crypto';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EACL,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,GAClB,MAAM,QAAQ,CAAC;AAChB,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,iBAAiB,GAElB,MAAM,SAAS,CAAC;AACjB,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,kBAAkB,GAEnB,MAAM,YAAY,CAAC;AACpB,MAAM,CAAC,MAAM,YAAY,GAAG,yBAAyB,CAAC"}
package/dist/keys.d.ts ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Derive the master key from (password, userId) exactly as the web app does:
3
+ * PBKDF2(password, salt = UTF8(`typelets-salt-${userId}-v1`), 250000, SHA-256)
4
+ * -> AES-GCM 256. Returned key is EXTRACTABLE so callers can obtain keyString
5
+ * for later importMasterKey; commit paths should re-import non-extractably.
6
+ */
7
+ export declare function deriveMasterKey(masterPassword: string | Uint8Array, userId: string): Promise<{
8
+ key: CryptoKey;
9
+ keyString: string;
10
+ }>;
11
+ /** Import raw AES key bytes as a NON-extractable CryptoKey. */
12
+ export declare function importMasterKey(keyString: string): Promise<CryptoKey>;
13
+ //# sourceMappingURL=keys.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.d.ts","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAGA;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,cAAc,EAAE,MAAM,GAAG,UAAU,EACnC,MAAM,EAAE,MAAM,GACb,OAAO,CAAC;IAAE,GAAG,EAAE,SAAS,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAAC,CAqChD;AAED,+DAA+D;AAC/D,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAS3E"}
package/dist/keys.js ADDED
@@ -0,0 +1,36 @@
1
+ import { base64ToBytes, bytesToBase64 } from './base64';
2
+ import { ENCRYPTION_CONFIG } from './constants';
3
+ /**
4
+ * Derive the master key from (password, userId) exactly as the web app does:
5
+ * PBKDF2(password, salt = UTF8(`typelets-salt-${userId}-v1`), 250000, SHA-256)
6
+ * -> AES-GCM 256. Returned key is EXTRACTABLE so callers can obtain keyString
7
+ * for later importMasterKey; commit paths should re-import non-extractably.
8
+ */
9
+ export async function deriveMasterKey(masterPassword, userId) {
10
+ if (!userId) {
11
+ throw new Error('deriveMasterKey requires a userId');
12
+ }
13
+ const encoder = new TextEncoder();
14
+ const passwordBytes = typeof masterPassword === 'string'
15
+ ? encoder.encode(masterPassword)
16
+ : masterPassword;
17
+ const userSalt = encoder.encode(`typelets-salt-${userId}-v1`);
18
+ const keyMaterial = await crypto.subtle.importKey('raw', passwordBytes, { name: 'PBKDF2' }, false, ['deriveKey']);
19
+ const key = await crypto.subtle.deriveKey({
20
+ name: 'PBKDF2',
21
+ salt: userSalt,
22
+ iterations: ENCRYPTION_CONFIG.ITERATIONS,
23
+ hash: 'SHA-256',
24
+ }, keyMaterial, {
25
+ name: ENCRYPTION_CONFIG.ALGORITHM,
26
+ length: ENCRYPTION_CONFIG.KEY_LENGTH,
27
+ }, true, ['encrypt', 'decrypt']);
28
+ const exportedKey = await crypto.subtle.exportKey('raw', key);
29
+ return { key, keyString: bytesToBase64(new Uint8Array(exportedKey)) };
30
+ }
31
+ /** Import raw AES key bytes as a NON-extractable CryptoKey. */
32
+ export async function importMasterKey(keyString) {
33
+ const keyData = base64ToBytes(keyString);
34
+ return crypto.subtle.importKey('raw', keyData, { name: ENCRYPTION_CONFIG.ALGORITHM }, false, ['encrypt', 'decrypt']);
35
+ }
36
+ //# sourceMappingURL=keys.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keys.js","sourceRoot":"","sources":["../src/keys.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAEhD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,cAAmC,EACnC,MAAc;IAEd,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACvD,CAAC;IACD,MAAM,OAAO,GAAG,IAAI,WAAW,EAAE,CAAC;IAClC,MAAM,aAAa,GACjB,OAAO,cAAc,KAAK,QAAQ;QAChC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC;QAChC,CAAC,CAAC,cAAc,CAAC;IACrB,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,iBAAiB,MAAM,KAAK,CAAC,CAAC;IAE9D,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAC/C,KAAK,EACL,aAA6B,EAC7B,EAAE,IAAI,EAAE,QAAQ,EAAE,EAClB,KAAK,EACL,CAAC,WAAW,CAAC,CACd,CAAC;IAEF,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CACvC;QACE,IAAI,EAAE,QAAQ;QACd,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE,iBAAiB,CAAC,UAAU;QACxC,IAAI,EAAE,SAAS;KAChB,EACD,WAAW,EACX;QACE,IAAI,EAAE,iBAAiB,CAAC,SAAS;QACjC,MAAM,EAAE,iBAAiB,CAAC,UAAU;KACrC,EACD,IAAI,EACJ,CAAC,SAAS,EAAE,SAAS,CAAC,CACvB,CAAC;IAEF,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAC9D,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,aAAa,CAAC,IAAI,UAAU,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;AACxE,CAAC;AAED,+DAA+D;AAC/D,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,SAAiB;IACrD,MAAM,OAAO,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC;IACzC,OAAO,MAAM,CAAC,MAAM,CAAC,SAAS,CAC5B,KAAK,EACL,OAAuB,EACvB,EAAE,IAAI,EAAE,iBAAiB,CAAC,SAAS,EAAE,EACrC,KAAK,EACL,CAAC,SAAS,EAAE,SAAS,CAAC,CACvB,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface EncryptedNoteFields {
2
+ encryptedTitle: string;
3
+ /** "v2:<ivB64>:<ciphertextB64>" — carries its own IV. */
4
+ encryptedContent: string;
5
+ /** Title IV (v1 column back-compat). */
6
+ iv: string;
7
+ }
8
+ export declare function encryptNoteFields(key: CryptoKey, title: string, content: string): Promise<EncryptedNoteFields>;
9
+ export declare function decryptNoteFields(key: CryptoKey, encryptedTitle: string, encryptedContent: string, ivBase64: string): Promise<{
10
+ title: string;
11
+ content: string;
12
+ }>;
13
+ export declare function decryptTitleField(key: CryptoKey, encryptedTitle: string, ivBase64: string): Promise<string>;
14
+ //# sourceMappingURL=notes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notes.d.ts","sourceRoot":"","sources":["../src/notes.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,MAAM,CAAC;IACvB,yDAAyD;IACzD,gBAAgB,EAAE,MAAM,CAAC;IACzB,wCAAwC;IACxC,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,mBAAmB,CAAC,CAgC9B;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,SAAS,EACd,cAAc,EAAE,MAAM,EACtB,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA+B7C;AAED,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,SAAS,EACd,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAQjB"}
package/dist/notes.js ADDED
@@ -0,0 +1,47 @@
1
+ import { base64ToArrayBuffer, base64ToBytes, bytesToBase64 } from './base64';
2
+ import { ENCRYPTION_CONFIG } from './constants';
3
+ export async function encryptNoteFields(key, title, content) {
4
+ // VIR-7: AES-GCM must NEVER reuse a (key, IV) pair. Distinct IV for title
5
+ // and content; the `iv` field carries the title IV (v1 back-compat) and the
6
+ // content carries its own IV inline.
7
+ const ivTitle = crypto.getRandomValues(new Uint8Array(ENCRYPTION_CONFIG.IV_LENGTH));
8
+ const ivContent = crypto.getRandomValues(new Uint8Array(ENCRYPTION_CONFIG.IV_LENGTH));
9
+ const encoder = new TextEncoder();
10
+ const encryptedTitleBuffer = await crypto.subtle.encrypt({ name: ENCRYPTION_CONFIG.ALGORITHM, iv: ivTitle }, key, encoder.encode(title || ''));
11
+ const encryptedContentBuffer = await crypto.subtle.encrypt({ name: ENCRYPTION_CONFIG.ALGORITHM, iv: ivContent }, key, encoder.encode(content || ''));
12
+ return {
13
+ encryptedTitle: bytesToBase64(encryptedTitleBuffer),
14
+ encryptedContent: 'v2:' +
15
+ bytesToBase64(ivContent) +
16
+ ':' +
17
+ bytesToBase64(encryptedContentBuffer),
18
+ iv: bytesToBase64(ivTitle),
19
+ };
20
+ }
21
+ export async function decryptNoteFields(key, encryptedTitle, encryptedContent, ivBase64) {
22
+ const ivTitle = base64ToBytes(ivBase64);
23
+ // v2 content carries its own IV ("v2:<ivB64>:<ctB64>"); v1 content is plain
24
+ // base64 and shares the column IV with the title. base64 never contains ':',
25
+ // so detection is unambiguous.
26
+ let ivContent = ivTitle;
27
+ let contentCipher = encryptedContent;
28
+ if (encryptedContent.startsWith('v2:')) {
29
+ const rest = encryptedContent.slice(3);
30
+ const sep = rest.indexOf(':');
31
+ ivContent = base64ToBytes(rest.slice(0, sep));
32
+ contentCipher = rest.slice(sep + 1);
33
+ }
34
+ const decryptedTitleBuffer = await crypto.subtle.decrypt({ name: ENCRYPTION_CONFIG.ALGORITHM, iv: ivTitle }, key, base64ToArrayBuffer(encryptedTitle));
35
+ const decryptedContentBuffer = await crypto.subtle.decrypt({ name: ENCRYPTION_CONFIG.ALGORITHM, iv: ivContent }, key, base64ToArrayBuffer(contentCipher));
36
+ const decoder = new TextDecoder();
37
+ return {
38
+ title: decoder.decode(decryptedTitleBuffer),
39
+ content: decoder.decode(decryptedContentBuffer),
40
+ };
41
+ }
42
+ export async function decryptTitleField(key, encryptedTitle, ivBase64) {
43
+ const iv = base64ToBytes(ivBase64);
44
+ const decrypted = await crypto.subtle.decrypt({ name: ENCRYPTION_CONFIG.ALGORITHM, iv: iv }, key, base64ToArrayBuffer(encryptedTitle));
45
+ return new TextDecoder().decode(decrypted);
46
+ }
47
+ //# sourceMappingURL=notes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notes.js","sourceRoot":"","sources":["../src/notes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ export type VerifierBlob = {
2
+ ciphertext: string;
3
+ iv: string;
4
+ salt: string;
5
+ };
6
+ /**
7
+ * Build a verifier blob: encrypt the known constant with the derived master
8
+ * key using the SAME AES-GCM primitive used for notes. `salt` is a fresh
9
+ * random value stored alongside the blob; it is NOT used to derive the master
10
+ * key (derivation stays password+userId).
11
+ */
12
+ export declare function createVerifierBlob(key: CryptoKey): Promise<VerifierBlob>;
13
+ /**
14
+ * Verify a verifier blob: GCM-decrypt (the auth tag guarantees integrity) and
15
+ * confirm the plaintext equals the known constant. False on any failure.
16
+ */
17
+ export declare function verifyVerifierBlob(key: CryptoKey, blob: VerifierBlob): Promise<boolean>;
18
+ /**
19
+ * True iff the key GCM-decrypts the given ciphertext (auth tag validates).
20
+ * Used to verify a migrated user's password against existing data when no
21
+ * verifier blob exists.
22
+ */
23
+ export declare function canDecryptWithKey(key: CryptoKey, ciphertextBase64: string, ivBase64: string): Promise<boolean>;
24
+ //# sourceMappingURL=verifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifier.d.ts","sourceRoot":"","sources":["../src/verifier.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;GAKG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,YAAY,CAAC,CAe9E;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,SAAS,EACd,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,OAAO,CAAC,CAYlB;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,SAAS,EACd,gBAAgB,EAAE,MAAM,EACxB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,OAAO,CAAC,CAYlB"}
@@ -0,0 +1,48 @@
1
+ import { base64ToArrayBuffer, base64ToBytes, bytesToBase64 } from './base64';
2
+ import { ENCRYPTION_CONFIG, VERIFIER_CONSTANT } from './constants';
3
+ /**
4
+ * Build a verifier blob: encrypt the known constant with the derived master
5
+ * key using the SAME AES-GCM primitive used for notes. `salt` is a fresh
6
+ * random value stored alongside the blob; it is NOT used to derive the master
7
+ * key (derivation stays password+userId).
8
+ */
9
+ export async function createVerifierBlob(key) {
10
+ const iv = crypto.getRandomValues(new Uint8Array(ENCRYPTION_CONFIG.IV_LENGTH));
11
+ const salt = crypto.getRandomValues(new Uint8Array(ENCRYPTION_CONFIG.SALT_LENGTH));
12
+ const ciphertextBuffer = await crypto.subtle.encrypt({ name: ENCRYPTION_CONFIG.ALGORITHM, iv }, key, new TextEncoder().encode(VERIFIER_CONSTANT));
13
+ return {
14
+ ciphertext: bytesToBase64(ciphertextBuffer),
15
+ iv: bytesToBase64(iv),
16
+ salt: bytesToBase64(salt),
17
+ };
18
+ }
19
+ /**
20
+ * Verify a verifier blob: GCM-decrypt (the auth tag guarantees integrity) and
21
+ * confirm the plaintext equals the known constant. False on any failure.
22
+ */
23
+ export async function verifyVerifierBlob(key, blob) {
24
+ try {
25
+ const iv = base64ToBytes(blob.iv);
26
+ const decrypted = await crypto.subtle.decrypt({ name: ENCRYPTION_CONFIG.ALGORITHM, iv: iv }, key, base64ToArrayBuffer(blob.ciphertext));
27
+ return new TextDecoder().decode(decrypted) === VERIFIER_CONSTANT;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ /**
34
+ * True iff the key GCM-decrypts the given ciphertext (auth tag validates).
35
+ * Used to verify a migrated user's password against existing data when no
36
+ * verifier blob exists.
37
+ */
38
+ export async function canDecryptWithKey(key, ciphertextBase64, ivBase64) {
39
+ try {
40
+ const iv = base64ToBytes(ivBase64);
41
+ await crypto.subtle.decrypt({ name: ENCRYPTION_CONFIG.ALGORITHM, iv: iv }, key, base64ToArrayBuffer(ciphertextBase64));
42
+ return true;
43
+ }
44
+ catch {
45
+ return false;
46
+ }
47
+ }
48
+ //# sourceMappingURL=verifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"verifier.js","sourceRoot":"","sources":["../src/verifier.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAQnE;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,GAAc;IACrD,MAAM,EAAE,GAAG,MAAM,CAAC,eAAe,CAAC,IAAI,UAAU,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC;IAC/E,MAAM,IAAI,GAAG,MAAM,CAAC,eAAe,CACjC,IAAI,UAAU,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAC9C,CAAC;IACF,MAAM,gBAAgB,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAClD,EAAE,IAAI,EAAE,iBAAiB,CAAC,SAAS,EAAE,EAAE,EAAE,EACzC,GAAG,EACH,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAC5C,CAAC;IACF,OAAO;QACL,UAAU,EAAE,aAAa,CAAC,gBAAgB,CAAC;QAC3C,EAAE,EAAE,aAAa,CAAC,EAAE,CAAC;QACrB,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC;KAC1B,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,GAAc,EACd,IAAkB;IAElB,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClC,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CAC3C,EAAE,IAAI,EAAE,iBAAiB,CAAC,SAAS,EAAE,EAAE,EAAE,EAAkB,EAAE,EAC7D,GAAG,EACH,mBAAmB,CAAC,IAAI,CAAC,UAAU,CAAC,CACrC,CAAC;QACF,OAAO,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,iBAAiB,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,GAAc,EACd,gBAAwB,EACxB,QAAgB;IAEhB,IAAI,CAAC;QACH,MAAM,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QACnC,MAAM,MAAM,CAAC,MAAM,CAAC,OAAO,CACzB,EAAE,IAAI,EAAE,iBAAiB,CAAC,SAAS,EAAE,EAAE,EAAE,EAAkB,EAAE,EAC7D,GAAG,EACH,mBAAmB,CAAC,gBAAgB,CAAC,CACtC,CAAC;QACF,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@batalabs/virlow-crypto",
3
+ "version": "3.11.4",
4
+ "type": "module",
5
+ "description": "Virlow client-side encryption primitives (PBKDF2 + AES-256-GCM). Framework-free; browser and Node 20+.",
6
+ "license": "MIT",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js"
11
+ }
12
+ },
13
+ "devDependencies": {
14
+ "@types/node": "^22.10.0",
15
+ "typescript": "^5.8.0",
16
+ "vitest": "^3.0.0"
17
+ },
18
+ "main": "./dist/index.js",
19
+ "types": "./dist/index.d.ts",
20
+ "files": [
21
+ "LICENSE",
22
+ "README.md",
23
+ "dist"
24
+ ],
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "repository": {
29
+ "type": "git",
30
+ "url": "git+https://github.com/batalabs/virlow-app.git",
31
+ "directory": "packages/crypto"
32
+ },
33
+ "scripts": {
34
+ "test": "vitest run",
35
+ "type-check": "tsc --noEmit",
36
+ "build": "tsc -p tsconfig.build.json"
37
+ }
38
+ }