quadqr-js 0.7.0 → 1.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/README.md +42 -26
- package/bin/quadqr.js +102 -102
- package/dist/esm/node.js +275 -275
- package/dist/esm/quadqr.js +1962 -1962
- package/dist/esm/wasm.js +98 -98
- package/dist/quadqr.js +1921 -1921
- package/docs/API.md +160 -39
- package/docs/BROWSER_CDN.md +83 -29
- package/docs/CLI.md +76 -0
- package/docs/GETTING_STARTED.md +104 -22
- package/docs/NODE.md +51 -20
- package/docs/PUBLISHING.md +12 -18
- package/docs/README.md +25 -10
- package/docs/SECURITY.md +72 -21
- package/docs/WASM.md +42 -11
- package/package.json +2 -2
- package/types/benchmark.d.ts +7 -7
- package/types/index.d.ts +139 -139
- package/types/node.d.ts +9 -9
package/docs/GETTING_STARTED.md
CHANGED
|
@@ -1,28 +1,87 @@
|
|
|
1
1
|
# Getting Started
|
|
2
2
|
|
|
3
|
-
## Install
|
|
3
|
+
## Install
|
|
4
4
|
|
|
5
5
|
```bash
|
|
6
|
-
npm install quadqr
|
|
6
|
+
npm install quadqr-js
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Node.js usage targets Node.js 20.19 or newer. Browser use works through a bundler, direct ESM, or the prebuilt CDN/global bundle.
|
|
10
10
|
|
|
11
|
-
##
|
|
11
|
+
## Encode and decode text
|
|
12
12
|
|
|
13
13
|
```js
|
|
14
|
-
import { encodeText, decodeMatrix } from "quadqr";
|
|
14
|
+
import { encodeText, decodeMatrix } from "quadqr-js";
|
|
15
15
|
|
|
16
|
-
const code = encodeText("Hello from QuadQR", {
|
|
17
|
-
|
|
16
|
+
const code = encodeText("Hello from QuadQR", {
|
|
17
|
+
ecc: "M"
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const result = decodeMatrix(code.matrix);
|
|
21
|
+
console.log(result.text);
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
If no version is supplied, QuadQR automatically chooses the smallest version that fits the payload.
|
|
25
|
+
|
|
26
|
+
## Render in the browser
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
import {
|
|
30
|
+
encodeText,
|
|
31
|
+
renderToCanvas
|
|
32
|
+
} from "quadqr-js/browser";
|
|
33
|
+
|
|
34
|
+
const code = encodeText("Rendered in the browser");
|
|
35
|
+
|
|
36
|
+
renderToCanvas(code, document.querySelector("#qr"), {
|
|
37
|
+
moduleSize: 12,
|
|
38
|
+
quietZone: 4,
|
|
39
|
+
style: "classic"
|
|
40
|
+
});
|
|
41
|
+
```
|
|
18
42
|
|
|
19
|
-
|
|
43
|
+
Available presentation styles are `classic`, `depth`, `soft`, and `inset`. Styling does not change the encoded matrix.
|
|
44
|
+
|
|
45
|
+
## Scan an uploaded image
|
|
46
|
+
|
|
47
|
+
```js
|
|
48
|
+
import { scanFile } from "quadqr-js/browser";
|
|
49
|
+
|
|
50
|
+
const file = document.querySelector("#image").files[0];
|
|
51
|
+
const result = await scanFile(file);
|
|
52
|
+
|
|
53
|
+
console.log(result.text);
|
|
20
54
|
```
|
|
21
55
|
|
|
56
|
+
## Scan from the camera
|
|
57
|
+
|
|
58
|
+
```js
|
|
59
|
+
import { startCameraScanner } from "quadqr-js/browser";
|
|
60
|
+
|
|
61
|
+
let scanner;
|
|
62
|
+
scanner = await startCameraScanner(
|
|
63
|
+
document.querySelector("#video"),
|
|
64
|
+
{
|
|
65
|
+
onDecode(result) {
|
|
66
|
+
console.log(result);
|
|
67
|
+
scanner.stop();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Camera access requires HTTPS or localhost.
|
|
74
|
+
|
|
22
75
|
## Secure password mode
|
|
23
76
|
|
|
77
|
+
Secure payloads are optional. Password mode is the simplest choice when a person will provide the secret during decryption.
|
|
78
|
+
|
|
24
79
|
```js
|
|
25
|
-
import {
|
|
80
|
+
import {
|
|
81
|
+
encodeSecureText,
|
|
82
|
+
decodeMatrix,
|
|
83
|
+
decryptDecoded
|
|
84
|
+
} from "quadqr-js";
|
|
26
85
|
|
|
27
86
|
const code = await encodeSecureText("Private payload", {
|
|
28
87
|
ecc: "M",
|
|
@@ -33,6 +92,7 @@ const code = await encodeSecureText("Private payload", {
|
|
|
33
92
|
});
|
|
34
93
|
|
|
35
94
|
const locked = decodeMatrix(code.matrix);
|
|
95
|
+
|
|
36
96
|
const unlocked = await decryptDecoded(locked, {
|
|
37
97
|
password: "correct horse battery staple"
|
|
38
98
|
});
|
|
@@ -40,19 +100,21 @@ const unlocked = await decryptDecoded(locked, {
|
|
|
40
100
|
console.log(unlocked.text);
|
|
41
101
|
```
|
|
42
102
|
|
|
43
|
-
|
|
103
|
+
A secure image or camera scan follows the same pattern: scanning returns a locked result, then `decryptDecoded()` reveals plaintext only after successful authentication.
|
|
104
|
+
|
|
105
|
+
## Secure raw-key mode
|
|
106
|
+
|
|
107
|
+
Use raw-key mode when an application already manages its own cryptographic keys.
|
|
44
108
|
|
|
45
109
|
```js
|
|
46
110
|
import {
|
|
47
|
-
bytesToHex,
|
|
48
111
|
encodeSecureText,
|
|
49
112
|
generateRaw256Key
|
|
50
|
-
} from "quadqr";
|
|
113
|
+
} from "quadqr-js";
|
|
51
114
|
|
|
52
115
|
const key = generateRaw256Key();
|
|
53
|
-
console.log(bytesToHex(key));
|
|
54
116
|
|
|
55
|
-
const code = await encodeSecureText("
|
|
117
|
+
const code = await encodeSecureText("Application-managed secret", {
|
|
56
118
|
security: {
|
|
57
119
|
mode: "raw-key",
|
|
58
120
|
key
|
|
@@ -60,15 +122,16 @@ const code = await encodeSecureText("Machine-readable secret", {
|
|
|
60
122
|
});
|
|
61
123
|
```
|
|
62
124
|
|
|
63
|
-
Never
|
|
125
|
+
The key must be exactly 32 bytes. Never embed the secret key inside the same QuadQR payload.
|
|
64
126
|
|
|
65
|
-
## Node.js
|
|
127
|
+
## Generate and scan PNG files in Node.js
|
|
66
128
|
|
|
67
129
|
```js
|
|
68
|
-
import { encodeText } from "quadqr";
|
|
69
|
-
import { savePNG, scanFile } from "quadqr/node";
|
|
130
|
+
import { encodeText } from "quadqr-js";
|
|
131
|
+
import { savePNG, scanFile } from "quadqr-js/node";
|
|
70
132
|
|
|
71
133
|
const code = encodeText("Generated on Node.js");
|
|
134
|
+
|
|
72
135
|
await savePNG(code, "quadqr.png", {
|
|
73
136
|
moduleSize: 12,
|
|
74
137
|
quietZone: 4
|
|
@@ -78,14 +141,33 @@ const result = await scanFile("quadqr.png");
|
|
|
78
141
|
console.log(result.text);
|
|
79
142
|
```
|
|
80
143
|
|
|
81
|
-
|
|
144
|
+
PNG generation and decoding are built into the Node.js adapter.
|
|
145
|
+
|
|
146
|
+
## Use a script tag
|
|
82
147
|
|
|
83
148
|
```html
|
|
84
|
-
<
|
|
149
|
+
<canvas id="qr"></canvas>
|
|
150
|
+
<script src="https://cdn.jsdelivr.net/npm/quadqr-js@1.0.0/dist/quadqr.min.js"></script>
|
|
85
151
|
<script>
|
|
86
152
|
const code = QuadQR.encodeText("No build step");
|
|
87
|
-
|
|
153
|
+
QuadQR.renderToCanvas(code, document.querySelector("#qr"));
|
|
88
154
|
</script>
|
|
89
155
|
```
|
|
90
156
|
|
|
91
|
-
|
|
157
|
+
Pin an exact package version in production.
|
|
158
|
+
|
|
159
|
+
## Use the CLI
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
npx quadqr-js encode "Hello QuadQR" -o hello.png
|
|
163
|
+
npx quadqr-js decode hello.png
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Password-protected CLI workflow:
|
|
167
|
+
|
|
168
|
+
```bash
|
|
169
|
+
npx quadqr-js encode "Private data" --password "my-password" -o secure.png
|
|
170
|
+
npx quadqr-js decode secure.png --password "my-password"
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
See [CLI.md](./CLI.md) for the full command reference.
|
package/docs/NODE.md
CHANGED
|
@@ -1,78 +1,80 @@
|
|
|
1
1
|
# Node.js
|
|
2
2
|
|
|
3
|
-
QuadQR uses the same matrix codec and
|
|
3
|
+
QuadQR uses the same matrix codec and scanner core in Node.js and the browser. The `quadqr-js/node` entry adds Node-specific PNG, file, and buffer helpers.
|
|
4
4
|
|
|
5
5
|
## Requirements
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Node.js 20.19 or newer.
|
|
8
8
|
|
|
9
9
|
## ESM
|
|
10
10
|
|
|
11
11
|
```js
|
|
12
|
-
import { encodeText } from "quadqr";
|
|
13
|
-
import { savePNG, scanFile } from "quadqr/node";
|
|
12
|
+
import { encodeText } from "quadqr-js";
|
|
13
|
+
import { savePNG, scanFile } from "quadqr-js/node";
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
## CommonJS
|
|
17
17
|
|
|
18
|
-
On supported modern Node versions:
|
|
19
|
-
|
|
20
18
|
```js
|
|
21
|
-
const QuadQR = require("quadqr");
|
|
22
|
-
const QuadQRNode = require("quadqr/node");
|
|
19
|
+
const QuadQR = require("quadqr-js");
|
|
20
|
+
const QuadQRNode = require("quadqr-js/node");
|
|
23
21
|
```
|
|
24
22
|
|
|
25
|
-
## Generate PNG
|
|
23
|
+
## Generate a PNG
|
|
26
24
|
|
|
27
25
|
```js
|
|
28
26
|
const code = QuadQR.encodeText("Server generated");
|
|
27
|
+
|
|
29
28
|
await QuadQRNode.savePNG(code, "output.png", {
|
|
30
29
|
moduleSize: 12,
|
|
31
30
|
quietZone: 4
|
|
32
31
|
});
|
|
33
32
|
```
|
|
34
33
|
|
|
35
|
-
`toPNG()`
|
|
34
|
+
Use `toPNG()` when you need an in-memory `Buffer`:
|
|
36
35
|
|
|
37
36
|
```js
|
|
38
37
|
const png = QuadQRNode.toPNG(code);
|
|
39
38
|
```
|
|
40
39
|
|
|
41
|
-
This
|
|
40
|
+
This works well for HTTP responses, object storage, attachments, and other buffer-based workflows.
|
|
42
41
|
|
|
43
|
-
## Scan PNG
|
|
42
|
+
## Scan a PNG file
|
|
44
43
|
|
|
45
44
|
```js
|
|
46
45
|
const result = await QuadQRNode.scanFile("output.png");
|
|
47
46
|
console.log(result.text);
|
|
48
47
|
```
|
|
49
48
|
|
|
50
|
-
Or scan
|
|
49
|
+
Or scan a buffer:
|
|
51
50
|
|
|
52
51
|
```js
|
|
53
52
|
const result = await QuadQRNode.scanBuffer(pngBuffer);
|
|
54
53
|
```
|
|
55
54
|
|
|
56
|
-
PNG
|
|
55
|
+
PNG generation and decoding do not require external native dependencies.
|
|
57
56
|
|
|
58
|
-
##
|
|
57
|
+
## Other image formats
|
|
59
58
|
|
|
60
|
-
|
|
59
|
+
For JPEG, WebP, or AVIF input, the Node adapter can use `sharp` when the consuming application has it installed:
|
|
61
60
|
|
|
62
61
|
```bash
|
|
63
62
|
npm install sharp
|
|
64
63
|
```
|
|
65
64
|
|
|
66
|
-
If `sharp` is not installed,
|
|
65
|
+
If `sharp` is not installed, decode the image to RGBA in your own image pipeline and pass the pixels to `scanImageData()`.
|
|
67
66
|
|
|
68
|
-
## Secure
|
|
67
|
+
## Secure password workflow
|
|
69
68
|
|
|
70
69
|
```js
|
|
71
70
|
import {
|
|
72
71
|
decryptDecoded,
|
|
73
72
|
encodeSecureText
|
|
74
|
-
} from "quadqr";
|
|
75
|
-
import {
|
|
73
|
+
} from "quadqr-js";
|
|
74
|
+
import {
|
|
75
|
+
savePNG,
|
|
76
|
+
scanFile
|
|
77
|
+
} from "quadqr-js/node";
|
|
76
78
|
|
|
77
79
|
const code = await encodeSecureText("private", {
|
|
78
80
|
security: {
|
|
@@ -84,9 +86,38 @@ const code = await encodeSecureText("private", {
|
|
|
84
86
|
await savePNG(code, "secure.png");
|
|
85
87
|
|
|
86
88
|
const locked = await scanFile("secure.png");
|
|
89
|
+
|
|
87
90
|
const unlocked = await decryptDecoded(locked, {
|
|
88
91
|
password: process.env.QUADQR_PASSWORD
|
|
89
92
|
});
|
|
90
93
|
|
|
91
94
|
console.log(unlocked.text);
|
|
92
95
|
```
|
|
96
|
+
|
|
97
|
+
## Secure raw-key workflow
|
|
98
|
+
|
|
99
|
+
```js
|
|
100
|
+
import {
|
|
101
|
+
decryptDecoded,
|
|
102
|
+
encodeSecureText,
|
|
103
|
+
generateRaw256Key
|
|
104
|
+
} from "quadqr-js";
|
|
105
|
+
import { toPNG, scanBuffer } from "quadqr-js/node";
|
|
106
|
+
|
|
107
|
+
const key = generateRaw256Key();
|
|
108
|
+
|
|
109
|
+
const code = await encodeSecureText("machine secret", {
|
|
110
|
+
security: {
|
|
111
|
+
mode: "raw-key",
|
|
112
|
+
key
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const png = toPNG(code);
|
|
117
|
+
const locked = await scanBuffer(png);
|
|
118
|
+
const unlocked = await decryptDecoded(locked, { key });
|
|
119
|
+
|
|
120
|
+
console.log(unlocked.text);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Keep long-lived application keys in an appropriate secret-management or platform key-storage system.
|
package/docs/PUBLISHING.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# Publishing QuadQR to npm
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
QuadQR is published on npm as [`quadqr-js`](https://www.npmjs.com/package/quadqr-js). The repository package name is `quadqr-js`.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
The QuadQR project/brand, browser global (`QuadQR`), CLI executable (`quadqr`), and generated filenames remain unchanged. Only the npm package identifier is `quadqr-js`.
|
|
6
6
|
|
|
7
7
|
## 1. Create/sign in to npm
|
|
8
8
|
|
|
@@ -30,13 +30,13 @@ npm install
|
|
|
30
30
|
|
|
31
31
|
so your local package lock and npm metadata are synchronized.
|
|
32
32
|
|
|
33
|
-
## 3. Verify the package
|
|
33
|
+
## 3. Verify the package metadata
|
|
34
34
|
|
|
35
35
|
```bash
|
|
36
|
-
npm view quadqr
|
|
36
|
+
npm view quadqr-js
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
Confirm the published package and current version before releasing an update.
|
|
40
40
|
|
|
41
41
|
## 4. Run the full release checks
|
|
42
42
|
|
|
@@ -56,18 +56,12 @@ npm pack
|
|
|
56
56
|
|
|
57
57
|
## 5. Publish
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
The package is an unscoped public package and `publishConfig.access` is already set to `public`, so publish with:
|
|
60
60
|
|
|
61
61
|
```bash
|
|
62
62
|
npm publish
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
For a scoped public package:
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
npm publish --access public
|
|
69
|
-
```
|
|
70
|
-
|
|
71
65
|
npm publishing requires an account that satisfies npm's current 2FA/authentication requirements.
|
|
72
66
|
|
|
73
67
|
## 6. Verify installation from a clean directory
|
|
@@ -76,15 +70,15 @@ npm publishing requires an account that satisfies npm's current 2FA/authenticati
|
|
|
76
70
|
mkdir quadqr-consumer-test
|
|
77
71
|
cd quadqr-consumer-test
|
|
78
72
|
npm init -y
|
|
79
|
-
npm install quadqr
|
|
73
|
+
npm install quadqr-js
|
|
80
74
|
```
|
|
81
75
|
|
|
82
76
|
Then test ESM, Node PNG, and the CLI.
|
|
83
77
|
|
|
84
78
|
```bash
|
|
85
|
-
npx quadqr keygen
|
|
86
|
-
npx quadqr encode "Hello from npm" -o hello.png
|
|
87
|
-
npx quadqr decode hello.png
|
|
79
|
+
npx quadqr-js keygen
|
|
80
|
+
npx quadqr-js encode "Hello from npm" -o hello.png
|
|
81
|
+
npx quadqr-js decode hello.png
|
|
88
82
|
```
|
|
89
83
|
|
|
90
84
|
## 7. Verify CDN availability
|
|
@@ -92,8 +86,8 @@ npx quadqr decode hello.png
|
|
|
92
86
|
After npm and CDN caches update, test:
|
|
93
87
|
|
|
94
88
|
```text
|
|
95
|
-
https://cdn.jsdelivr.net/npm/quadqr@0.
|
|
96
|
-
https://unpkg.com/quadqr@0.
|
|
89
|
+
https://cdn.jsdelivr.net/npm/quadqr-js@1.0.0/dist/quadqr.min.js
|
|
90
|
+
https://unpkg.com/quadqr-js@1.0.0/dist/quadqr.min.js
|
|
97
91
|
```
|
|
98
92
|
|
|
99
93
|
Then call `QuadQR.encodeText(...)` from a plain HTML page.
|
package/docs/README.md
CHANGED
|
@@ -1,26 +1,41 @@
|
|
|
1
1
|
# QuadQR Documentation
|
|
2
2
|
|
|
3
|
-
QuadQR is an experimental four-state RGBW matrix symbology. Each data cell carries exactly two bits using red, green, blue, or white. The library
|
|
3
|
+
QuadQR is an experimental four-state RGBW matrix symbology. Each data cell carries exactly two bits using red, green, blue, or white. The JavaScript library supports encoding, rendering, matrix decoding, image and camera scanning, Spectrum ECC, optional authenticated encryption, Node.js PNG workflows, CDN usage, a CLI, TypeScript declarations, and optional prebuilt WebAssembly acceleration.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Live links
|
|
6
|
+
|
|
7
|
+
- [Documentation Site](https://akanshsirohi.github.io/QuadQR/docs-site/)
|
|
8
|
+
- [Interactive Demo](https://akanshsirohi.github.io/QuadQR/demo/)
|
|
9
|
+
- [quadqr-js on npm](https://www.npmjs.com/package/quadqr-js)
|
|
10
|
+
- [GitHub Repository](https://github.com/akanshsirohi/QuadQR)
|
|
11
|
+
|
|
12
|
+
## Documentation
|
|
6
13
|
|
|
7
14
|
- [Getting Started](./GETTING_STARTED.md)
|
|
8
15
|
- [API Reference](./API.md)
|
|
9
16
|
- [Browser and CDN](./BROWSER_CDN.md)
|
|
10
17
|
- [Node.js](./NODE.md)
|
|
18
|
+
- [CLI](./CLI.md)
|
|
11
19
|
- [Secure Payloads](./SECURITY.md)
|
|
12
|
-
- [
|
|
13
|
-
- [Publishing to npm](./PUBLISHING.md)
|
|
20
|
+
- [WebAssembly](./WASM.md)
|
|
14
21
|
- [Wire Format](../FORMAT.md)
|
|
15
22
|
|
|
16
23
|
## Package entry points
|
|
17
24
|
|
|
18
25
|
| Entry | Purpose |
|
|
19
26
|
| --- | --- |
|
|
20
|
-
| `quadqr` | Runtime-neutral core API
|
|
21
|
-
| `quadqr/browser` | Browser ESM entry |
|
|
22
|
-
| `quadqr/node` | Node.js core plus PNG
|
|
23
|
-
| `quadqr/benchmark` | Capacity and codec benchmark helpers |
|
|
24
|
-
| `quadqr/quadqr.min.js` | Classic browser global bundle for
|
|
27
|
+
| `quadqr-js` | Runtime-neutral core API, rendering, scanning, secure payloads, utilities, and optional WASM |
|
|
28
|
+
| `quadqr-js/browser` | Browser ESM entry for canvas, files, video, and camera workflows |
|
|
29
|
+
| `quadqr-js/node` | Node.js core plus PNG, file, and buffer helpers |
|
|
30
|
+
| `quadqr-js/benchmark` | Capacity and codec benchmark helpers |
|
|
31
|
+
| `quadqr-js/quadqr.min.js` | Classic browser global bundle for CDN/script-tag usage |
|
|
32
|
+
|
|
33
|
+
The matrix codec is shared across runtimes. Browser and Node.js adapters only handle environment-specific input and output.
|
|
34
|
+
|
|
35
|
+
## Compatibility note
|
|
36
|
+
|
|
37
|
+
QuadQR is a custom experimental format, not ISO QR Code. Standard QR scanner applications cannot decode QuadQR symbols.
|
|
38
|
+
|
|
39
|
+
## License
|
|
25
40
|
|
|
26
|
-
|
|
41
|
+
QuadQR is licensed under AGPL-3.0. See [`LICENSE`](../LICENSE).
|
package/docs/SECURITY.md
CHANGED
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
# Secure Payloads
|
|
2
2
|
|
|
3
|
-
QuadQR Secure Payload v1 is an optional layer above the matrix codec and Spectrum ECC.
|
|
3
|
+
QuadQR Secure Payload v1 is an optional authenticated-encryption layer above the matrix codec and Spectrum ECC.
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
6
|
plaintext
|
|
7
|
-
-> encryption
|
|
7
|
+
-> encryption + authentication
|
|
8
8
|
-> secure envelope
|
|
9
9
|
-> QuadQR framing
|
|
10
10
|
-> Spectrum ECC
|
|
11
11
|
-> RGBW matrix
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
Normal QuadQR symbols are unencrypted by default. Secure Payload is used only when explicitly requested.
|
|
15
|
+
|
|
16
|
+
## Which mode should I use?
|
|
17
|
+
|
|
18
|
+
| Mode | Use it when |
|
|
19
|
+
| --- | --- |
|
|
20
|
+
| Password | A person will enter or share a secret password to unlock the payload |
|
|
21
|
+
| Raw 256-bit key | Your application, service, device, or scanner already manages cryptographic keys |
|
|
22
|
+
|
|
23
|
+
Both modes use AES-256-GCM for confidentiality and authenticated integrity.
|
|
15
24
|
|
|
16
25
|
## Password mode
|
|
17
26
|
|
|
@@ -22,9 +31,11 @@ Password mode uses:
|
|
|
22
31
|
- configurable iteration count, default 600,000
|
|
23
32
|
- AES-256-GCM
|
|
24
33
|
- random 12-byte nonce
|
|
25
|
-
- authenticated metadata
|
|
34
|
+
- authenticated envelope metadata
|
|
26
35
|
|
|
27
36
|
```js
|
|
37
|
+
import { encodeSecureText } from "quadqr-js";
|
|
38
|
+
|
|
28
39
|
const code = await encodeSecureText("private", {
|
|
29
40
|
security: {
|
|
30
41
|
mode: "password",
|
|
@@ -37,9 +48,14 @@ The password is never stored in the QuadQR symbol.
|
|
|
37
48
|
|
|
38
49
|
## Raw 256-bit key mode
|
|
39
50
|
|
|
40
|
-
Raw-key mode accepts exactly 32
|
|
51
|
+
Raw-key mode accepts exactly 32 secret bytes, either as bytes or a 64-character hexadecimal string.
|
|
41
52
|
|
|
42
53
|
```js
|
|
54
|
+
import {
|
|
55
|
+
encodeSecureText,
|
|
56
|
+
generateRaw256Key
|
|
57
|
+
} from "quadqr-js";
|
|
58
|
+
|
|
43
59
|
const key = generateRaw256Key();
|
|
44
60
|
|
|
45
61
|
const code = await encodeSecureText("machine secret", {
|
|
@@ -50,29 +66,64 @@ const code = await encodeSecureText("machine secret", {
|
|
|
50
66
|
});
|
|
51
67
|
```
|
|
52
68
|
|
|
53
|
-
By default
|
|
69
|
+
By default, the secure envelope can include a short SHA-256 key fingerprint/key ID. This is a non-secret routing hint that can help an application choose the correct key. The actual secret key is never embedded in the symbol.
|
|
54
70
|
|
|
55
|
-
##
|
|
71
|
+
## Scanning secure QuadQR
|
|
56
72
|
|
|
57
|
-
|
|
73
|
+
Scanning does not automatically reveal protected plaintext. The scanner first returns a locked result:
|
|
58
74
|
|
|
59
75
|
```js
|
|
60
|
-
const locked =
|
|
76
|
+
const locked = await scanFile(file);
|
|
61
77
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
78
|
+
console.log(locked.secure); // true
|
|
79
|
+
console.log(locked.requiresDecryption); // true
|
|
80
|
+
console.log(locked.security.mode); // password | raw-key
|
|
81
|
+
console.log(locked.text); // null
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Then unlock it with the correct credential:
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
const unlocked = await decryptDecoded(locked, {
|
|
88
|
+
password: "long unique password"
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
console.log(unlocked.text);
|
|
67
92
|
```
|
|
68
93
|
|
|
69
|
-
|
|
94
|
+
For raw-key mode:
|
|
95
|
+
|
|
96
|
+
```js
|
|
97
|
+
const unlocked = await decryptDecoded(locked, { key });
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Image upload, camera, matrix, RGBA, and Node.js scan results all use the same secure-result model.
|
|
101
|
+
|
|
102
|
+
## Authentication behavior
|
|
103
|
+
|
|
104
|
+
AES-GCM authentication rejects:
|
|
105
|
+
|
|
106
|
+
- an incorrect password;
|
|
107
|
+
- an incorrect raw key;
|
|
108
|
+
- modified ciphertext;
|
|
109
|
+
- modified authenticated secure-envelope metadata.
|
|
110
|
+
|
|
111
|
+
QuadQR's CRC and Spectrum ECC still handle matrix/image corruption before decryption is attempted. Cryptographic authentication is a separate final security check.
|
|
112
|
+
|
|
113
|
+
## Key management guidance
|
|
114
|
+
|
|
115
|
+
- Use long, unique passwords for password mode.
|
|
116
|
+
- Generate raw keys with `generateRaw256Key()` or another cryptographically secure random source.
|
|
117
|
+
- Store long-lived raw keys in an appropriate secret manager, operating-system key store, HSM, or equivalent secure storage for your application.
|
|
118
|
+
- Do not put the secret password or raw key inside the same QuadQR symbol.
|
|
119
|
+
- Do not hard-code a long-lived raw key in public client-side JavaScript if it is intended to remain secret.
|
|
120
|
+
|
|
121
|
+
## Security boundaries
|
|
70
122
|
|
|
71
|
-
|
|
123
|
+
Secure Payload protects payload confidentiality and integrity, but it does not solve every application-security problem:
|
|
72
124
|
|
|
73
|
-
- Secure Payload protects payload confidentiality and integrity.
|
|
74
125
|
- It does not hide the fact that a QuadQR symbol exists.
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
78
|
-
-
|
|
126
|
+
- A photographed symbol can still be copied.
|
|
127
|
+
- Encryption by itself is not replay protection.
|
|
128
|
+
- Ticket/access systems should use server-side state, nonces, expiry, signatures, or another replay-control mechanism appropriate to the application.
|
|
129
|
+
- Decoded plaintext is still untrusted application input and should be validated before use.
|
package/docs/WASM.md
CHANGED
|
@@ -1,36 +1,67 @@
|
|
|
1
1
|
# WebAssembly
|
|
2
2
|
|
|
3
|
-
QuadQR
|
|
3
|
+
QuadQR includes an optional prebuilt WebAssembly module at:
|
|
4
4
|
|
|
5
5
|
```text
|
|
6
6
|
dist/wasm/quadqr-core.wasm
|
|
7
7
|
```
|
|
8
8
|
|
|
9
|
-
The normal JavaScript
|
|
9
|
+
The normal JavaScript implementation works without WASM. Applications can opt in when they want to use the bundled accelerator.
|
|
10
10
|
|
|
11
|
-
## Enable
|
|
11
|
+
## Enable WASM
|
|
12
12
|
|
|
13
13
|
```js
|
|
14
|
-
import { initWasm } from "quadqr";
|
|
14
|
+
import { initWasm } from "quadqr-js";
|
|
15
15
|
|
|
16
16
|
const state = await initWasm();
|
|
17
17
|
console.log(state);
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
In QuadQR 0.7.x, the WASM module accelerates CRC-32. Once initialized, normal encode/decode operations automatically use the installed accelerator.
|
|
21
21
|
|
|
22
|
-
##
|
|
22
|
+
## Check the current state
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
```js
|
|
25
|
+
import { getWasmState } from "quadqr-js";
|
|
26
|
+
|
|
27
|
+
console.log(getWasmState());
|
|
28
|
+
```
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
Before initialization, or after disabling WASM, the result is `null`.
|
|
27
31
|
|
|
28
|
-
## Disable
|
|
32
|
+
## Disable WASM
|
|
29
33
|
|
|
30
34
|
```js
|
|
31
|
-
import { disableWasm } from "quadqr";
|
|
35
|
+
import { disableWasm } from "quadqr-js";
|
|
32
36
|
|
|
33
37
|
disableWasm();
|
|
34
38
|
```
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
QuadQR immediately returns to the JavaScript implementation.
|
|
41
|
+
|
|
42
|
+
## Custom WASM URL
|
|
43
|
+
|
|
44
|
+
Browser and CDN applications can override the asset location:
|
|
45
|
+
|
|
46
|
+
```js
|
|
47
|
+
await initWasm({
|
|
48
|
+
url: "https://example.com/assets/quadqr-core.wasm"
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
You can also provide WASM bytes directly through the `bytes` option.
|
|
53
|
+
|
|
54
|
+
## CDN behavior
|
|
55
|
+
|
|
56
|
+
When the classic `quadqr.min.js` global build is loaded from a CDN, `QuadQR.initWasm()` resolves the bundled sibling WASM asset from the same package/version location.
|
|
57
|
+
|
|
58
|
+
```html
|
|
59
|
+
<script src="https://cdn.jsdelivr.net/npm/quadqr-js@1.0.0/dist/quadqr.min.js"></script>
|
|
60
|
+
<script>
|
|
61
|
+
await QuadQR.initWasm();
|
|
62
|
+
</script>
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Fallback behavior
|
|
66
|
+
|
|
67
|
+
WASM is never required for the QuadQR wire format. If your environment cannot load WebAssembly, simply do not call `initWasm()` and continue using the JavaScript implementation.
|