kxco-post-quantum 1.4.0 → 1.4.1
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/BENCHMARKS.md +124 -0
- package/CHANGELOG.md +39 -0
- package/CONFORMANCE.md +49 -18
- package/README.md +5 -4
- package/SECURITY.md +22 -0
- package/package.json +4 -2
package/BENCHMARKS.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Performance
|
|
2
|
+
|
|
3
|
+
Per-algorithm latency and memory, reported as tail latency rather than a mean.
|
|
4
|
+
|
|
5
|
+
A mean hides the tail, and the tail is what a request budget has to absorb. For
|
|
6
|
+
ML-DSA that distinction is not cosmetic: signing uses rejection sampling, so it
|
|
7
|
+
loops until the candidate signature falls in range, and the slow tail is inherent
|
|
8
|
+
to the algorithm rather than measurement noise. Anyone sizing a timeout from a
|
|
9
|
+
mean will size it wrong.
|
|
10
|
+
|
|
11
|
+
Reproduce with:
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
node --expose-gc bench/primitives.mjs --iterations 100 --json bench/results/primitives.json
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Every parameter set the package can reach is measured, not only the five it wraps
|
|
18
|
+
in its own helpers.
|
|
19
|
+
|
|
20
|
+
## Signatures
|
|
21
|
+
|
|
22
|
+
Milliseconds. 100 iterations except where the `n` column says otherwise.
|
|
23
|
+
|
|
24
|
+
| Algorithm | Operation | n | median | p95 | p99 | ops/s |
|
|
25
|
+
|---|---|---:|---:|---:|---:|---:|
|
|
26
|
+
| ML-DSA-44 | keygen | 100 | 1.695 | 2.847 | 3.317 | 545 |
|
|
27
|
+
| ML-DSA-44 | sign | 100 | 8.880 | 24.104 | 30.193 | 91 |
|
|
28
|
+
| ML-DSA-44 | verify | 100 | 1.554 | 3.000 | 3.650 | 552 |
|
|
29
|
+
| ML-DSA-65 | keygen | 100 | 3.394 | 9.058 | 11.699 | 238 |
|
|
30
|
+
| ML-DSA-65 | sign | 100 | 11.308 | 36.752 | 51.084 | 64 |
|
|
31
|
+
| ML-DSA-65 | verify | 100 | 3.456 | 5.032 | 5.553 | 288 |
|
|
32
|
+
| ML-DSA-87 | keygen | 100 | 5.226 | 8.270 | 9.635 | 194 |
|
|
33
|
+
| ML-DSA-87 | sign | 100 | 13.148 | 33.932 | 42.381 | 62 |
|
|
34
|
+
| ML-DSA-87 | verify | 100 | 4.877 | 11.119 | 21.760 | 167 |
|
|
35
|
+
| SLH-DSA-SHA2-128f | sign | 20 | 151.319 | 194.915 | 205.881 | 7 |
|
|
36
|
+
| SLH-DSA-SHA2-128f | verify | 20 | 6.412 | 10.672 | 30.298 | 121 |
|
|
37
|
+
| SLH-DSA-SHA2-192s | sign | 3 | 6788.860 | 7229.209 | 7229.209 | 0 |
|
|
38
|
+
| SLH-DSA-SHA2-192s | verify | 10 | 4.997 | 62.287 | 62.287 | 89 |
|
|
39
|
+
| SLH-DSA-SHAKE-256f | sign | 5 | 3333.195 | 3786.271 | 3786.271 | 0 |
|
|
40
|
+
| SLH-DSA-SHAKE-256f | verify | 10 | 72.219 | 86.779 | 86.779 | 14 |
|
|
41
|
+
|
|
42
|
+
**Two numbers to design around.**
|
|
43
|
+
|
|
44
|
+
**ML-DSA signing has a long tail.** ML-DSA-65 signs in 11 ms at the median and
|
|
45
|
+
51 ms at p99, a factor of 4.5. Rejection sampling means an unlucky signature does
|
|
46
|
+
several more rounds. Size request budgets from p99, not the median.
|
|
47
|
+
|
|
48
|
+
**SLH-DSA-SHA2-192s signs in about 6.8 seconds.** That is the set `slhDsa` wraps,
|
|
49
|
+
and it is not a per-request operation. It suits infrequent, high-value signatures
|
|
50
|
+
such as firmware or root attestations. Verification is cheap, around 5 ms, so an
|
|
51
|
+
SLH-DSA signature is expensive to make and cheap to check. The `f` variants trade
|
|
52
|
+
signature size for signing speed: SHA2-128f signs in 151 ms.
|
|
53
|
+
|
|
54
|
+
## Key encapsulation
|
|
55
|
+
|
|
56
|
+
| Algorithm | Operation | n | median | p95 | p99 | ops/s |
|
|
57
|
+
|---|---|---:|---:|---:|---:|---:|
|
|
58
|
+
| ML-KEM-512 | keygen | 100 | 0.338 | 0.969 | 1.277 | 2222 |
|
|
59
|
+
| ML-KEM-512 | encapsulate | 100 | 0.512 | 1.043 | 1.617 | 1720 |
|
|
60
|
+
| ML-KEM-512 | decapsulate | 100 | 0.845 | 2.133 | 2.561 | 965 |
|
|
61
|
+
| ML-KEM-768 | keygen | 100 | 0.645 | 1.357 | 1.757 | 1344 |
|
|
62
|
+
| ML-KEM-768 | encapsulate | 100 | 0.822 | 2.050 | 5.067 | 971 |
|
|
63
|
+
| ML-KEM-768 | decapsulate | 100 | 1.292 | 2.750 | 4.048 | 674 |
|
|
64
|
+
| ML-KEM-1024 | keygen | 100 | 1.062 | 2.043 | 2.776 | 860 |
|
|
65
|
+
| ML-KEM-1024 | encapsulate | 100 | 1.066 | 1.902 | 10.844 | 641 |
|
|
66
|
+
| ML-KEM-1024 | decapsulate | 100 | 0.917 | 1.813 | 2.169 | 918 |
|
|
67
|
+
|
|
68
|
+
ML-KEM is sub-millisecond at the median across all three sets. Moving from
|
|
69
|
+
Category 3 to Category 5 costs well under a millisecond per operation, so the
|
|
70
|
+
migration cost of ML-KEM-1024 is its 1568-byte keys and ciphertexts, not its
|
|
71
|
+
speed. See [MIGRATION.md](MIGRATION.md).
|
|
72
|
+
|
|
73
|
+
## Wrapper overhead
|
|
74
|
+
|
|
75
|
+
The package helpers derive keys from a master secret through HKDF, which the raw
|
|
76
|
+
primitives do not. That is the only overhead they add:
|
|
77
|
+
|
|
78
|
+
| Helper | median | raw keygen median | HKDF cost |
|
|
79
|
+
|---|---:|---:|---:|
|
|
80
|
+
| `mlDsa.keypairFromMaster` | 3.945 | 3.394 | ~0.55 ms |
|
|
81
|
+
| `mlKem.keypairFromMaster` | 1.129 | 0.645 | ~0.48 ms |
|
|
82
|
+
|
|
83
|
+
Signing and verification go straight through, so they carry no wrapper cost
|
|
84
|
+
beyond hex encoding.
|
|
85
|
+
|
|
86
|
+
## Memory
|
|
87
|
+
|
|
88
|
+
Heap growth per operation, in bytes, from `process.memoryUsage().heapUsed` across
|
|
89
|
+
a batch:
|
|
90
|
+
|
|
91
|
+
| Algorithm | keygen | sign or encapsulate |
|
|
92
|
+
|---|---:|---:|
|
|
93
|
+
| ML-KEM-768 | ~11.7 kB | ~13.0 kB |
|
|
94
|
+
| ML-KEM-1024 | ~20.6 kB | ~6.1 kB |
|
|
95
|
+
| ML-DSA-65 | ~12.9 kB | ~21.0 kB |
|
|
96
|
+
| ML-DSA-87 | ~3.3 kB | ~11.4 kB |
|
|
97
|
+
| SLH-DSA-SHA2-128f | ~143 kB | |
|
|
98
|
+
| SLH-DSA-SHA2-192s | ~192 kB | |
|
|
99
|
+
| SLH-DSA-SHAKE-256f | ~864 kB | |
|
|
100
|
+
|
|
101
|
+
SLH-DSA allocates one to three orders of magnitude more than the lattice schemes,
|
|
102
|
+
which is consistent with building a hypertree per key. The lattice figures are
|
|
103
|
+
tens of kilobytes and are not a constraint at these rates.
|
|
104
|
+
|
|
105
|
+
**Read these as orders of magnitude, not exact allocations.** Garbage collection
|
|
106
|
+
can run mid-batch, so a figure smaller than a sibling's does not reliably mean
|
|
107
|
+
less allocation. ML-DSA-87 keygen reading lower than ML-DSA-65 is an artefact of
|
|
108
|
+
collection timing, not evidence that the larger parameter set allocates less.
|
|
109
|
+
|
|
110
|
+
## What these figures are not
|
|
111
|
+
|
|
112
|
+
- **One machine, one run.** Node v26.1.0 on win32-x64. Absolute numbers move with
|
|
113
|
+
hardware and runtime; the ratios between operations are the portable part.
|
|
114
|
+
- **Not a cross-vendor comparison.** Nothing here was measured against another
|
|
115
|
+
implementation, so it says nothing about how this compares to a native or
|
|
116
|
+
hardware-backed stack. It will be slower than both.
|
|
117
|
+
- **Reduced samples where marked.** SLH-DSA slow variants take 3 to 20 samples
|
|
118
|
+
rather than 100, because 100 signatures at 6.8 seconds each is not a benchmark,
|
|
119
|
+
it is an afternoon. Where n is small, p95 and p99 collapse onto the maximum and
|
|
120
|
+
are reported that way rather than dressed up.
|
|
121
|
+
- **Not a side-channel measurement.** Timing here is throughput, gathered without
|
|
122
|
+
any attempt to detect secret-dependent variation, and it must not be read as
|
|
123
|
+
evidence about constant-time behaviour. See [THREAT-MODEL.md](THREAT-MODEL.md),
|
|
124
|
+
which states plainly that no such property is claimed.
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,44 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.4.1
|
|
4
|
+
|
|
5
|
+
Evidence and documentation only. **No `src/` module changed**, no export was
|
|
6
|
+
added or removed, and the dependency set is unchanged, so no call site can
|
|
7
|
+
behave differently than it did on 1.4.0.
|
|
8
|
+
|
|
9
|
+
**liboqs is now a third interop implementation.** The cross-implementation
|
|
10
|
+
matrix ran against two peers and now runs against three: liboqs 0.16.0 (C),
|
|
11
|
+
Bouncy Castle 1.85.2 (Java) and dilithium-py 1.4.0 / kyber-py 1.2.0 (Python).
|
|
12
|
+
**225 checks passed, 0 failed, 42 not applicable, across 38 rows**, up from
|
|
13
|
+
156/0/10 across 24. The previous figures are reproduced exactly when the new
|
|
14
|
+
peer is excluded, so nothing about the existing evidence moved.
|
|
15
|
+
|
|
16
|
+
SLH-DSA had one peer and now has two. liboqs runs from a container built from
|
|
17
|
+
source at a tag pinned in `peers-lock.json`, and CI builds it, so the version
|
|
18
|
+
tested is the version named.
|
|
19
|
+
|
|
20
|
+
A peer that cannot do something now records as not-applicable rather than as a
|
|
21
|
+
disagreement. The 42 not-applicable are itemised in CONFORMANCE.md: ten from our
|
|
22
|
+
own hedged signing, thirty-two from two liboqs API limits.
|
|
23
|
+
|
|
24
|
+
**AUDIT.md corrections.** The reviewer checklist told anyone doing due diligence
|
|
25
|
+
to fetch an endpoint that returns 500. Verification now goes to Armature L1 over
|
|
26
|
+
public JSON-RPC, with the calldata layout documented and a named article page
|
|
27
|
+
for the other half of the join. Both documented commands were run verbatim
|
|
28
|
+
against production before publishing.
|
|
29
|
+
|
|
30
|
+
Two false claims in the same file are withdrawn. It said we pin
|
|
31
|
+
`@noble/post-quantum@0.6.1`, "the exact version covered by the maintainer's own
|
|
32
|
+
self-audit"; we ship `0.7.0`, the self-audit covers `0.6.1`, and **the version we
|
|
33
|
+
ship is covered by no audit at all**. It also described the dependency as
|
|
34
|
+
"itself audited", contradicting its own section 1.
|
|
35
|
+
|
|
36
|
+
**Supply chain.** Each release now publishes its CycloneDX SBOM as a GitHub
|
|
37
|
+
Release asset at `releases/download/<tag>/sbom.cyclonedx.json`, a permanent
|
|
38
|
+
unauthenticated URL. It was previously generated but retained only as an
|
|
39
|
+
expiring Actions artifact, which is not a published SBOM. The dependency policy
|
|
40
|
+
is now stated in SECURITY.md rather than living only in `dependabot.yml`.
|
|
41
|
+
|
|
3
42
|
## 1.4.0
|
|
4
43
|
|
|
5
44
|
Adds the Security Category 5 parameter sets, and publishes conformance and
|
package/CONFORMANCE.md
CHANGED
|
@@ -5,7 +5,7 @@ Two claims, each with a harness in this repository that anyone can run:
|
|
|
5
5
|
1. **This package computes what FIPS 203, 204 and 205 say it should.** Evidenced
|
|
6
6
|
against NIST's own ACVP test vectors.
|
|
7
7
|
2. **Independent implementations can consume what it produces, and it can
|
|
8
|
-
consume theirs.** Evidenced against Bouncy Castle (Java) and
|
|
8
|
+
consume theirs.** Evidenced against liboqs (C), Bouncy Castle (Java) and
|
|
9
9
|
dilithium-py / kyber-py (Python), in both directions.
|
|
10
10
|
|
|
11
11
|
The second claim is the one that matters in deployment and the one that vector
|
|
@@ -93,18 +93,26 @@ Peers, both pinned in
|
|
|
93
93
|
|
|
94
94
|
| Peer | Implementation | Language | Covers |
|
|
95
95
|
|---|---|---|---|
|
|
96
|
+
| `liboqs` | `liboqs` 0.16.0 with binding 0.16.0, built from source | C | ML-DSA, ML-KEM, SLH-DSA |
|
|
96
97
|
| `bouncycastle` | `org.bouncycastle:bcprov-jdk18on:1.85.2`, SHA-256 pinned | Java | ML-DSA, ML-KEM, SLH-DSA |
|
|
97
98
|
| `python` | `dilithium-py==1.4.0`, `kyber-py==1.2.0` | Python | ML-DSA, ML-KEM |
|
|
98
99
|
|
|
99
|
-
SLH-DSA is covered by Bouncy Castle
|
|
100
|
-
implementation was available to pin. That is a
|
|
101
|
-
families and is stated rather than averaged
|
|
100
|
+
SLH-DSA is covered by liboqs and Bouncy Castle, not by the Python pair, because
|
|
101
|
+
no maintained pure-Python implementation was available to pin. That is a
|
|
102
|
+
narrower base than the other two families and is stated rather than averaged
|
|
103
|
+
away.
|
|
102
104
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
+
None of the three shares code with this package's backend. liboqs is the
|
|
106
|
+
reference C implementation the wider ecosystem tests against; Bouncy Castle is a
|
|
107
|
+
widely deployed independent implementation; the Python pair are independent
|
|
105
108
|
spec-derived implementations.
|
|
106
109
|
|
|
107
|
-
|
|
110
|
+
liboqs needs a C toolchain, so its peer runs in a container built from
|
|
111
|
+
[conformance/interop/peers/liboqs.Dockerfile](conformance/interop/peers/liboqs.Dockerfile)
|
|
112
|
+
rather than requiring every contributor to install one. If the image is absent
|
|
113
|
+
the peer reports unavailable, which is not a failure but is also not evidence.
|
|
114
|
+
|
|
115
|
+
**Result: 225 checks passed, 0 failed, 42 not applicable, across 38 rows.**
|
|
108
116
|
|
|
109
117
|
Per row, in both directions:
|
|
110
118
|
|
|
@@ -130,17 +138,40 @@ Each parameter set this package publishes a helper for is run twice: once
|
|
|
130
138
|
through that helper (`wrapper`) and once through the primitive (`backend`), so
|
|
131
139
|
the evidence covers the published API and not only its dependency.
|
|
132
140
|
|
|
133
|
-
### The
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
and
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
for
|
|
141
|
+
### The forty-two not-applicable checks
|
|
142
|
+
|
|
143
|
+
A check is recorded as not applicable when a peer says it cannot do something,
|
|
144
|
+
never when a peer disagrees with us. A peer that cannot honour a request answers
|
|
145
|
+
`unsupported` and the matrix records N/A; any other error is a failure and is
|
|
146
|
+
counted as one.
|
|
147
|
+
|
|
148
|
+
**Ten from hedged signing.** This package's `sign` is hedged: it draws fresh
|
|
149
|
+
randomness per signature, which FIPS 204 permits and recommends, so its output is
|
|
150
|
+
deliberately not reproducible. Byte equality is asserted on the `backend` rows
|
|
151
|
+
for the same parameter sets, so determinism is still evidenced everywhere it is
|
|
152
|
+
meaningful. Reasoning is in [THREAT-MODEL.md](THREAT-MODEL.md).
|
|
153
|
+
|
|
154
|
+
They are the `bytes` check on each signature `wrapper` row, in each of two
|
|
155
|
+
context modes: three such rows against Bouncy Castle (ML-DSA-65, ML-DSA-87,
|
|
156
|
+
SLH-DSA-SHA2-192s) and two against the Python pair, which has no SLH-DSA. The
|
|
157
|
+
KEM wrapper rows carry no `bytes` check, because a KEM produces a fresh
|
|
158
|
+
ciphertext by design and byte equality is not defined for it.
|
|
159
|
+
|
|
160
|
+
**Thirty-two from two liboqs API limits**, both properties of that library
|
|
161
|
+
rather than of this one:
|
|
162
|
+
|
|
163
|
+
- `keys` on all fourteen liboqs rows. liboqs exposes no seed-derived keygen, so
|
|
164
|
+
it cannot rebuild a key from a FIPS 203 / FIPS 204 seed the way the other
|
|
165
|
+
peers do. It is addressed by encoded secret key instead, which still tests
|
|
166
|
+
something real: the private key encodings are themselves standardised, so a
|
|
167
|
+
key of ours that failed to load into liboqs and produce interoperable output
|
|
168
|
+
would be a genuine defect.
|
|
169
|
+
- `bytes` on all eighteen liboqs signature checks. liboqs signs hedged and
|
|
170
|
+
exposes no deterministic mode through its Python binding, so byte equality
|
|
171
|
+
against it is not defined. Bouncy Castle and the Python pair both cover it.
|
|
172
|
+
|
|
173
|
+
Everything else is exercised against liboqs, in both directions, including FIPS
|
|
174
|
+
204 context strings and both negative controls.
|
|
144
175
|
|
|
145
176
|
---
|
|
146
177
|
|
package/README.md
CHANGED
|
@@ -11,10 +11,11 @@ ML-DSA-65 (FIPS 204) and SLH-DSA-SHA2-192s (FIPS 205) signatures, ML-KEM-768 (FI
|
|
|
11
11
|
|
|
12
12
|
**Evidence, not adjectives:**
|
|
13
13
|
|
|
14
|
-
- [CONFORMANCE.md](./CONFORMANCE.md)
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
17
|
-
- [
|
|
14
|
+
- [CONFORMANCE.md](./CONFORMANCE.md): NIST ACVP vectors for FIPS 203/204/205 (2,103 tests, 0 failed), and a cross-implementation interop matrix against liboqs, Bouncy Castle and two pure-Python implementations (225 checks, 0 failed, both directions, with negative controls). Reproducible: `npm run conformance:acvp`, `npm run conformance:interop`.
|
|
15
|
+
- [BENCHMARKS.md](./BENCHMARKS.md): per-algorithm latency at p95/p99, plus memory. Two figures worth designing around: ML-DSA signing has a 4.5x tail between median and p99, and SLH-DSA-SHA2-192s signs in about 6.8 seconds.
|
|
16
|
+
- [THREAT-MODEL.md](./THREAT-MODEL.md): what this defends against and what it does not. Read the side-channel section before deciding where a signing key lives.
|
|
17
|
+
- [MIGRATION.md](./MIGRATION.md): moving an RSA or ECDSA system across, and moving between versions of this package.
|
|
18
|
+
- [SECURITY.md](./SECURITY.md): reporting, and release integrity.
|
|
18
19
|
|
|
19
20
|
---
|
|
20
21
|
|
package/SECURITY.md
CHANGED
|
@@ -36,6 +36,28 @@ Out of scope (report upstream to https://github.com/paulmillr/noble-post-quantum
|
|
|
36
36
|
- HMAC-SHA-256
|
|
37
37
|
- HKDF-SHA-512 (RFC 5869)
|
|
38
38
|
|
|
39
|
+
## Dependency policy
|
|
40
|
+
Both runtime dependencies are pinned to an exact version, never a range:
|
|
41
|
+
`@noble/post-quantum` and `@noble/hashes`. A range would let the code that runs
|
|
42
|
+
the cryptography change without a release of this package, which is not a
|
|
43
|
+
property we are willing to give up for convenience. Every GitHub Action in our
|
|
44
|
+
workflows is pinned by 40-character commit SHA for the same reason.
|
|
45
|
+
|
|
46
|
+
Updates are proposed, never automatic. Dependabot opens pull requests weekly for
|
|
47
|
+
both npm and GitHub Actions, and Dependabot security updates are enabled at the
|
|
48
|
+
repository level so an advisory does not wait for the Monday run. The
|
|
49
|
+
configuration is in [.github/dependabot.yml](.github/dependabot.yml).
|
|
50
|
+
|
|
51
|
+
A dependency bump is not merged on the strength of a green test run alone. A
|
|
52
|
+
change to `@noble/post-quantum` is a change to the primitives themselves, so it
|
|
53
|
+
is gated on the full conformance evidence regenerating clean: the NIST ACVP
|
|
54
|
+
vectors and the cross-implementation interoperability matrix, both described in
|
|
55
|
+
[CONFORMANCE.md](CONFORMANCE.md).
|
|
56
|
+
|
|
57
|
+
Each release publishes a CycloneDX SBOM as a GitHub Release asset, at
|
|
58
|
+
`releases/download/<tag>/sbom.cyclonedx.json`, generated from the tree that was
|
|
59
|
+
actually installed for that build.
|
|
60
|
+
|
|
39
61
|
## Disclosure
|
|
40
62
|
We follow coordinated disclosure with a 90-day default window.
|
|
41
63
|
For actively-exploited issues we ship a patch release within 48 hours.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "kxco-post-quantum",
|
|
3
|
-
"version": "1.4.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "ML-DSA-65, ML-KEM-768 and SLH-DSA-SHA2-192s primitives with key fingerprinting. The base layer for all kxco-pq-* packages.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"post-quantum",
|
|
@@ -95,6 +95,7 @@
|
|
|
95
95
|
"README.md",
|
|
96
96
|
"LICENSE",
|
|
97
97
|
"CONFORMANCE.md",
|
|
98
|
+
"BENCHMARKS.md",
|
|
98
99
|
"THREAT-MODEL.md",
|
|
99
100
|
"MIGRATION.md",
|
|
100
101
|
"SECURITY.md",
|
|
@@ -115,7 +116,8 @@
|
|
|
115
116
|
"conformance:fetch": "node conformance/fetch-vectors.mjs",
|
|
116
117
|
"conformance:acvp": "node conformance/run-acvp.mjs --json conformance/results/acvp.json",
|
|
117
118
|
"conformance:interop": "node conformance/interop/run-interop.mjs --json conformance/results/interop.json",
|
|
118
|
-
"sbom": "npm sbom --sbom-format cyclonedx --sbom-type library"
|
|
119
|
+
"sbom": "npm sbom --sbom-format cyclonedx --sbom-type library",
|
|
120
|
+
"bench:primitives": "node --expose-gc bench/primitives.mjs --iterations 100 --json bench/results/primitives.json"
|
|
119
121
|
},
|
|
120
122
|
"publishConfig": {
|
|
121
123
|
"provenance": true,
|