quantumsure-qstore 0.0.0-stage → 0.1.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/README.md CHANGED
@@ -1,3 +1,14 @@
1
- # Temporary Holding Version
1
+ # quantumsure-qstore
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Quantum-resistant field-level encryption for client data.
4
+
5
+ Encrypt sensitive fields **before** they are written to your database.
6
+ Decrypt them only when you need the plaintext.
7
+ The plaintext never leaves your environment.
8
+
9
+ Powered by **ML-KEM-1024** (Kyber) + **ChaCha20-Poly1305**.
10
+
11
+ ## Installation
12
+
13
+ ```bash
14
+ npm install quantumsure-qstore
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Encrypt plaintext with a recipient's ML-KEM-1024 public key.
3
+ *
4
+ * Format of the returned blob:
5
+ * kemCiphertext : nonce : ciphertext : authTag
6
+ * (all parts base64, separated by `:`)
7
+ *
8
+ * @param plaintext - The field value to protect
9
+ * @param publicKeyB64 - Recipient's public key (base64)
10
+ * @returns Encrypted blob that is safe to store in any database
11
+ */
12
+ declare function qstoreEncrypt(plaintext: string, publicKeyB64: string): Promise<string>;
13
+
14
+ /**
15
+ * Decrypt a blob previously produced by qstoreEncrypt.
16
+ *
17
+ * @param encryptedBlob - The string returned by qstoreEncrypt
18
+ * @param privateKeyB64 - The corresponding ML-KEM-1024 private key (base64)
19
+ * @returns Original plaintext
20
+ */
21
+ declare function qstoreDecrypt(encryptedBlob: string, privateKeyB64: string): Promise<string>;
22
+
23
+ export { qstoreDecrypt, qstoreEncrypt };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Encrypt plaintext with a recipient's ML-KEM-1024 public key.
3
+ *
4
+ * Format of the returned blob:
5
+ * kemCiphertext : nonce : ciphertext : authTag
6
+ * (all parts base64, separated by `:`)
7
+ *
8
+ * @param plaintext - The field value to protect
9
+ * @param publicKeyB64 - Recipient's public key (base64)
10
+ * @returns Encrypted blob that is safe to store in any database
11
+ */
12
+ declare function qstoreEncrypt(plaintext: string, publicKeyB64: string): Promise<string>;
13
+
14
+ /**
15
+ * Decrypt a blob previously produced by qstoreEncrypt.
16
+ *
17
+ * @param encryptedBlob - The string returned by qstoreEncrypt
18
+ * @param privateKeyB64 - The corresponding ML-KEM-1024 private key (base64)
19
+ * @returns Original plaintext
20
+ */
21
+ declare function qstoreDecrypt(encryptedBlob: string, privateKeyB64: string): Promise<string>;
22
+
23
+ export { qstoreDecrypt, qstoreEncrypt };
package/dist/index.js ADDED
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ qstoreDecrypt: () => qstoreDecrypt,
24
+ qstoreEncrypt: () => qstoreEncrypt
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/encrypt.ts
29
+ var import_mlkem = require("mlkem");
30
+ var import_chacha = require("@noble/ciphers/chacha.js");
31
+
32
+ // src/utils.ts
33
+ var import_buffer = require("buffer");
34
+ function randomBytes(length) {
35
+ const array = new Uint8Array(length);
36
+ crypto.getRandomValues(array);
37
+ return import_buffer.Buffer.from(array);
38
+ }
39
+ async function computeMac(data, key) {
40
+ const dataBytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
41
+ const keyBytes = typeof key === "string" ? new TextEncoder().encode(key) : key;
42
+ const combined = import_buffer.Buffer.concat([
43
+ import_buffer.Buffer.from(dataBytes),
44
+ import_buffer.Buffer.from(keyBytes)
45
+ ]);
46
+ const hash = await crypto.subtle.digest("SHA-256", combined);
47
+ return import_buffer.Buffer.from(hash);
48
+ }
49
+ function toBase64(data) {
50
+ return import_buffer.Buffer.from(data).toString("base64");
51
+ }
52
+ function fromBase64(b64) {
53
+ return import_buffer.Buffer.from(b64, "base64");
54
+ }
55
+
56
+ // src/encrypt.ts
57
+ async function qstoreEncrypt(plaintext, publicKeyB64) {
58
+ if (!plaintext && plaintext !== "") {
59
+ throw new Error("qstoreEncrypt: plaintext is required");
60
+ }
61
+ if (!publicKeyB64) {
62
+ throw new Error("qstoreEncrypt: publicKeyB64 is required");
63
+ }
64
+ const publicKey = fromBase64(publicKeyB64);
65
+ const kem = new import_mlkem.MlKem1024();
66
+ const [kemCiphertext, sharedSecret] = await kem.encap(publicKey);
67
+ const nonce = randomBytes(12);
68
+ const dataBytes = new TextEncoder().encode(plaintext);
69
+ const chacha = (0, import_chacha.chacha20poly1305)(sharedSecret, nonce);
70
+ const encrypted = chacha.encrypt(dataBytes);
71
+ const combinedForMac = new TextEncoder().encode(
72
+ `${toBase64(nonce)}${toBase64(encrypted)}`
73
+ );
74
+ const authTag = await computeMac(combinedForMac, sharedSecret);
75
+ return [
76
+ toBase64(kemCiphertext),
77
+ toBase64(nonce),
78
+ toBase64(encrypted),
79
+ toBase64(authTag)
80
+ ].join(":");
81
+ }
82
+
83
+ // src/decrypt.ts
84
+ var import_mlkem2 = require("mlkem");
85
+ var import_chacha2 = require("@noble/ciphers/chacha.js");
86
+ async function qstoreDecrypt(encryptedBlob, privateKeyB64) {
87
+ if (!encryptedBlob) {
88
+ throw new Error("qstoreDecrypt: encryptedBlob is required");
89
+ }
90
+ if (!privateKeyB64) {
91
+ throw new Error("qstoreDecrypt: privateKeyB64 is required");
92
+ }
93
+ const parts = encryptedBlob.split(":");
94
+ if (parts.length !== 4) {
95
+ throw new Error(
96
+ "qstoreDecrypt: invalid encrypted blob format (expected 4 parts)"
97
+ );
98
+ }
99
+ const [kemCiphertextB64, nonceB64, encryptedB64, authTagB64] = parts;
100
+ const privateKey = fromBase64(privateKeyB64);
101
+ const kemCiphertext = fromBase64(kemCiphertextB64);
102
+ const nonce = fromBase64(nonceB64);
103
+ const encrypted = fromBase64(encryptedB64);
104
+ const authTag = fromBase64(authTagB64);
105
+ const kem = new import_mlkem2.MlKem1024();
106
+ const sharedSecret = await kem.decap(kemCiphertext, privateKey);
107
+ const combinedForMac = new TextEncoder().encode(
108
+ `${nonceB64}${encryptedB64}`
109
+ );
110
+ const computedMac = await computeMac(combinedForMac, sharedSecret);
111
+ if (!computedMac.equals(authTag)) {
112
+ throw new Error(
113
+ "qstoreDecrypt: invalid MAC \u2013 data may have been tampered with"
114
+ );
115
+ }
116
+ const chacha = (0, import_chacha2.chacha20poly1305)(sharedSecret, nonce);
117
+ const decrypted = chacha.decrypt(encrypted);
118
+ return new TextDecoder().decode(decrypted);
119
+ }
120
+ // Annotate the CommonJS export names for ESM import in node:
121
+ 0 && (module.exports = {
122
+ qstoreDecrypt,
123
+ qstoreEncrypt
124
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,96 @@
1
+ // src/encrypt.ts
2
+ import { MlKem1024 } from "mlkem";
3
+ import { chacha20poly1305 } from "@noble/ciphers/chacha.js";
4
+
5
+ // src/utils.ts
6
+ import { Buffer } from "buffer";
7
+ function randomBytes(length) {
8
+ const array = new Uint8Array(length);
9
+ crypto.getRandomValues(array);
10
+ return Buffer.from(array);
11
+ }
12
+ async function computeMac(data, key) {
13
+ const dataBytes = typeof data === "string" ? new TextEncoder().encode(data) : data;
14
+ const keyBytes = typeof key === "string" ? new TextEncoder().encode(key) : key;
15
+ const combined = Buffer.concat([
16
+ Buffer.from(dataBytes),
17
+ Buffer.from(keyBytes)
18
+ ]);
19
+ const hash = await crypto.subtle.digest("SHA-256", combined);
20
+ return Buffer.from(hash);
21
+ }
22
+ function toBase64(data) {
23
+ return Buffer.from(data).toString("base64");
24
+ }
25
+ function fromBase64(b64) {
26
+ return Buffer.from(b64, "base64");
27
+ }
28
+
29
+ // src/encrypt.ts
30
+ async function qstoreEncrypt(plaintext, publicKeyB64) {
31
+ if (!plaintext && plaintext !== "") {
32
+ throw new Error("qstoreEncrypt: plaintext is required");
33
+ }
34
+ if (!publicKeyB64) {
35
+ throw new Error("qstoreEncrypt: publicKeyB64 is required");
36
+ }
37
+ const publicKey = fromBase64(publicKeyB64);
38
+ const kem = new MlKem1024();
39
+ const [kemCiphertext, sharedSecret] = await kem.encap(publicKey);
40
+ const nonce = randomBytes(12);
41
+ const dataBytes = new TextEncoder().encode(plaintext);
42
+ const chacha = chacha20poly1305(sharedSecret, nonce);
43
+ const encrypted = chacha.encrypt(dataBytes);
44
+ const combinedForMac = new TextEncoder().encode(
45
+ `${toBase64(nonce)}${toBase64(encrypted)}`
46
+ );
47
+ const authTag = await computeMac(combinedForMac, sharedSecret);
48
+ return [
49
+ toBase64(kemCiphertext),
50
+ toBase64(nonce),
51
+ toBase64(encrypted),
52
+ toBase64(authTag)
53
+ ].join(":");
54
+ }
55
+
56
+ // src/decrypt.ts
57
+ import { MlKem1024 as MlKem10242 } from "mlkem";
58
+ import { chacha20poly1305 as chacha20poly13052 } from "@noble/ciphers/chacha.js";
59
+ async function qstoreDecrypt(encryptedBlob, privateKeyB64) {
60
+ if (!encryptedBlob) {
61
+ throw new Error("qstoreDecrypt: encryptedBlob is required");
62
+ }
63
+ if (!privateKeyB64) {
64
+ throw new Error("qstoreDecrypt: privateKeyB64 is required");
65
+ }
66
+ const parts = encryptedBlob.split(":");
67
+ if (parts.length !== 4) {
68
+ throw new Error(
69
+ "qstoreDecrypt: invalid encrypted blob format (expected 4 parts)"
70
+ );
71
+ }
72
+ const [kemCiphertextB64, nonceB64, encryptedB64, authTagB64] = parts;
73
+ const privateKey = fromBase64(privateKeyB64);
74
+ const kemCiphertext = fromBase64(kemCiphertextB64);
75
+ const nonce = fromBase64(nonceB64);
76
+ const encrypted = fromBase64(encryptedB64);
77
+ const authTag = fromBase64(authTagB64);
78
+ const kem = new MlKem10242();
79
+ const sharedSecret = await kem.decap(kemCiphertext, privateKey);
80
+ const combinedForMac = new TextEncoder().encode(
81
+ `${nonceB64}${encryptedB64}`
82
+ );
83
+ const computedMac = await computeMac(combinedForMac, sharedSecret);
84
+ if (!computedMac.equals(authTag)) {
85
+ throw new Error(
86
+ "qstoreDecrypt: invalid MAC \u2013 data may have been tampered with"
87
+ );
88
+ }
89
+ const chacha = chacha20poly13052(sharedSecret, nonce);
90
+ const decrypted = chacha.decrypt(encrypted);
91
+ return new TextDecoder().decode(decrypted);
92
+ }
93
+ export {
94
+ qstoreDecrypt,
95
+ qstoreEncrypt
96
+ };
package/package.json CHANGED
@@ -1,6 +1,48 @@
1
1
  {
2
2
  "name": "quantumsure-qstore",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.0",
4
+ "description": "Quantum-resistant field-level encryption for client data. Encrypt before storage, decrypt on read — plaintext never leaves your environment.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "module": "dist/index.mjs",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+
14
+ }
15
+ },
16
+ "files": ["dist"],
17
+ "scripts": {
18
+ "build": "tsup src/index.ts --format cjs,esm --dts --clean",
19
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
20
+ "prepublishOnly": "npm run build"
21
+ },
22
+ "keywords": [
23
+ "quantum",
24
+ "post-quantum",
25
+ "encryption",
26
+ "field-level",
27
+ "ml-kem",
28
+ "kyber",
29
+ "zero-knowledge",
30
+ "quantumsure"
31
+ ],
32
+ "author": "QuantumSure",
33
+ "license": "MIT",
34
+ "dependencies": {
35
+ "mlkem": "^2.3.1",
36
+ "@noble/ciphers": "^1.0.0",
37
+ "buffer": "^6.0.3"
38
+ },
39
+ "devDependencies": {
40
+ "typescript": "^5.4.0",
41
+ "tsup": "^8.0.0",
42
+ "@types/node": "^20.0.0"
43
+ },
44
+ "engines": {
45
+ "node": ">=18"
46
+ },
47
+ "sideEffects": false
48
+ }