@ziffer-io/verify 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/LICENSE +120 -0
- package/README.md +121 -0
- package/THIRD-PARTY-NOTICES +40 -0
- package/dist/canon.d.ts +61 -0
- package/dist/canon.d.ts.map +1 -0
- package/dist/canon.js +147 -0
- package/dist/canon.js.map +1 -0
- package/dist/clauses.d.ts +53 -0
- package/dist/clauses.d.ts.map +1 -0
- package/dist/clauses.js +53 -0
- package/dist/clauses.js.map +1 -0
- package/dist/ed25519.d.ts +51 -0
- package/dist/ed25519.d.ts.map +1 -0
- package/dist/ed25519.js +81 -0
- package/dist/ed25519.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/instant.d.ts +25 -0
- package/dist/instant.d.ts.map +1 -0
- package/dist/instant.js +104 -0
- package/dist/instant.js.map +1 -0
- package/dist/refusal.d.ts +16 -0
- package/dist/refusal.d.ts.map +1 -0
- package/dist/refusal.js +20 -0
- package/dist/refusal.js.map +1 -0
- package/dist/suite.d.ts +56 -0
- package/dist/suite.d.ts.map +1 -0
- package/dist/suite.js +85 -0
- package/dist/suite.js.map +1 -0
- package/dist/testkeys.d.ts +20 -0
- package/dist/testkeys.d.ts.map +1 -0
- package/dist/testkeys.js +28 -0
- package/dist/testkeys.js.map +1 -0
- package/dist/verify.d.ts +123 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +338 -0
- package/dist/verify.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
ZIFFER SDK LICENSE
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 code75 SASU, Paris, France. All rights reserved.
|
|
4
|
+
ZIFFER is a registered trademark of code75 SASU ("code75") — code75.io · ziffer.io
|
|
5
|
+
|
|
6
|
+
This licence governs the software package it accompanies (the "SDK"): the ZIFFER client
|
|
7
|
+
libraries, receipt verifier, wire types and MCP server published by code75. The SDK is
|
|
8
|
+
proprietary software. It is not open source.
|
|
9
|
+
|
|
10
|
+
1. DEFINITIONS
|
|
11
|
+
"Agreement" means the written contract under which code75 provides the ZIFFER service to
|
|
12
|
+
you or the organisation you act for. code75's standard form of that contract is the ZIFFER
|
|
13
|
+
Customer Agreement, docs/legal/ziffer-customer-agreement.md, v0.1, provided on request.
|
|
14
|
+
"Service" means the ZIFFER service so provided. "You" means the person or organisation
|
|
15
|
+
using the SDK under an Agreement.
|
|
16
|
+
|
|
17
|
+
2. LICENCE
|
|
18
|
+
Subject to the Agreement and to this licence, code75 grants you a limited, non-exclusive,
|
|
19
|
+
non-transferable, non-sublicensable, revocable licence to install and run the SDK, in
|
|
20
|
+
unmodified form, solely to interact with the Service and to verify the receipts it issues,
|
|
21
|
+
for the term of the Agreement.
|
|
22
|
+
|
|
23
|
+
3. RESTRICTIONS
|
|
24
|
+
Except as expressly permitted by section 2 or by a law that cannot be excluded by
|
|
25
|
+
contract, you shall not, and shall not permit anyone else to:
|
|
26
|
+
(a) copy, distribute, publish, sublicense, sell, rent, lease or lend the SDK, or make it
|
|
27
|
+
available to any third party;
|
|
28
|
+
(b) modify, adapt, translate or create derivative works of the SDK;
|
|
29
|
+
(c) reverse engineer, decompile, disassemble or otherwise attempt to derive the source
|
|
30
|
+
code, algorithms or protocols of the SDK or the Service;
|
|
31
|
+
(d) use the SDK to build, train or benchmark a product or service that competes with the
|
|
32
|
+
Service, or to access the Service other than through the interfaces code75 documents;
|
|
33
|
+
(e) circumvent, disable or interfere with any security, verification or usage-control
|
|
34
|
+
mechanism of the SDK or the Service;
|
|
35
|
+
(f) remove, obscure or alter any copyright, trademark or other proprietary notice on or
|
|
36
|
+
in the SDK.
|
|
37
|
+
|
|
38
|
+
4. OWNERSHIP AND INTELLECTUAL PROPERTY
|
|
39
|
+
The SDK is licensed, not sold. code75 and its licensors own and retain all right, title
|
|
40
|
+
and interest in and to the SDK and the Service, including all copyright, patent, trade
|
|
41
|
+
secret, trademark and other intellectual property rights, and all improvements and
|
|
42
|
+
derivative works, by whomever made. Nothing in this licence transfers any such right to
|
|
43
|
+
you. All rights not expressly granted are reserved. Any suggestion, idea or feedback you
|
|
44
|
+
provide about the SDK or the Service may be used by code75 without restriction or
|
|
45
|
+
compensation. "ZIFFER" and "code75" and the associated logos are brands and marks of
|
|
46
|
+
code75; this licence grants no right to use them.
|
|
47
|
+
|
|
48
|
+
5. THIRD-PARTY COMPONENTS
|
|
49
|
+
The SDK includes third-party open-source components listed in the accompanying file
|
|
50
|
+
THIRD-PARTY-NOTICES. Those components are licensed under their own terms, which govern
|
|
51
|
+
them and prevail over this licence for those components only. This licence does not
|
|
52
|
+
apply to them.
|
|
53
|
+
|
|
54
|
+
6. UPDATES
|
|
55
|
+
code75 may release updated versions of the SDK. This licence applies to each version you
|
|
56
|
+
install unless a later version is accompanied by a different licence. code75 has no
|
|
57
|
+
obligation to provide updates or support except as stated in the Agreement.
|
|
58
|
+
|
|
59
|
+
7. YOUR RESPONSIBILITY FOR YOUR ACTIONS AND YOUR DATA
|
|
60
|
+
The Service issues decisions and signed receipts; it does not perform any action. You
|
|
61
|
+
alone decide what your systems do with a decision, you alone hold the credentials with
|
|
62
|
+
which any action is performed, and you alone are responsible for every action your
|
|
63
|
+
systems take or fail to take, for the policy you write and sign, for the data you submit
|
|
64
|
+
to the Service, and for your compliance with applicable law, including data protection,
|
|
65
|
+
export control and sanctions laws. A decision or receipt is not legal, regulatory,
|
|
66
|
+
financial or professional advice and is not a guarantee that any action is lawful,
|
|
67
|
+
appropriate or safe.
|
|
68
|
+
|
|
69
|
+
7A. INDEMNITY
|
|
70
|
+
You shall defend, indemnify and hold harmless code75, its officers, employees and
|
|
71
|
+
contractors from and against any claim, loss, liability, damage, cost or expense
|
|
72
|
+
(including reasonable legal fees) arising out of or relating to: (a) your use of the
|
|
73
|
+
SDK or the Service; (b) any action taken or not taken by your systems; (c) the policy,
|
|
74
|
+
data or content you provide; or (d) your breach of this licence, the Agreement or
|
|
75
|
+
applicable law.
|
|
76
|
+
|
|
77
|
+
7B. PRE-RELEASE SOFTWARE
|
|
78
|
+
The SDK and the Service may be provided in a pre-release, evaluation or limited form.
|
|
79
|
+
You accept that such software may contain errors and may change or be withdrawn, and
|
|
80
|
+
you use it at your own risk.
|
|
81
|
+
|
|
82
|
+
8. NO WARRANTY
|
|
83
|
+
THE SDK IS PROVIDED "AS IS" AND "AS AVAILABLE", WITHOUT WARRANTY OF ANY KIND, EXPRESS,
|
|
84
|
+
IMPLIED OR STATUTORY, INCLUDING WITHOUT LIMITATION ANY WARRANTY OF MERCHANTABILITY,
|
|
85
|
+
FITNESS FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, TO THE MAXIMUM EXTENT
|
|
86
|
+
PERMITTED BY LAW.
|
|
87
|
+
|
|
88
|
+
9. LIMITATION OF LIABILITY
|
|
89
|
+
TO THE MAXIMUM EXTENT PERMITTED BY LAW, CODE75 SHALL NOT BE LIABLE FOR ANY INDIRECT,
|
|
90
|
+
INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES, OR FOR ANY LOSS OF PROFITS,
|
|
91
|
+
REVENUE, DATA OR GOODWILL, ARISING OUT OF OR RELATING TO THE SDK, HOWEVER CAUSED AND
|
|
92
|
+
UNDER ANY THEORY OF LIABILITY, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
|
|
93
|
+
CODE75'S TOTAL LIABILITY UNDER THIS LICENCE SHALL NOT EXCEED THE AMOUNT STATED IN THE
|
|
94
|
+
AGREEMENT OR, IF NONE IS STATED, ONE HUNDRED EUROS (EUR 100). Nothing in this licence
|
|
95
|
+
excludes or limits liability that cannot be excluded or limited under applicable law.
|
|
96
|
+
|
|
97
|
+
10. TERMINATION
|
|
98
|
+
This licence terminates automatically, without notice, if you breach it or when the
|
|
99
|
+
Agreement ends. On termination you shall stop all use of the SDK and destroy every copy
|
|
100
|
+
in your possession or control. Sections 3, 4, 7, 7A, 8, 9, 10, 11 and 12 survive
|
|
101
|
+
termination.
|
|
102
|
+
|
|
103
|
+
11. GOVERNING LAW AND JURISDICTION
|
|
104
|
+
This licence is governed by the laws of France, without regard to conflict-of-law rules.
|
|
105
|
+
Any dispute arising out of or relating to this licence is submitted to the exclusive
|
|
106
|
+
jurisdiction of the competent courts of Paris, France, unless the Agreement states
|
|
107
|
+
otherwise.
|
|
108
|
+
|
|
109
|
+
12. GENERAL
|
|
110
|
+
This licence, the Agreement and THIRD-PARTY-NOTICES are the entire terms for the SDK.
|
|
111
|
+
Where this licence and the Agreement conflict, the Agreement prevails. If any provision
|
|
112
|
+
of this licence is held unenforceable, it is enforced to the maximum extent permitted
|
|
113
|
+
and the remainder stays in effect. No failure or delay by code75 in exercising a right
|
|
114
|
+
is a waiver of it. You may not assign this licence without code75's written consent;
|
|
115
|
+
code75 may assign it to a successor of the ZIFFER business. Any claim by you relating to
|
|
116
|
+
the SDK must be brought within one (1) year after it arises, to the extent the law
|
|
117
|
+
allows. This licence is written in English; a translation is for convenience only and
|
|
118
|
+
the English text prevails.
|
|
119
|
+
|
|
120
|
+
Contact: code75 SASU, Paris — code75.io · ziffer.io
|
package/README.md
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# @ziffer-io/verify
|
|
2
|
+
|
|
3
|
+
The **third implementation** of ACP receipt verification. The first two live in the
|
|
4
|
+
engine repository at the pin this workspace's `Cargo.toml` names: the Python reference
|
|
5
|
+
(`reference/src/acp_executor.py`, the readable §9.3 checklist) and Rust
|
|
6
|
+
(`crates/acp-decision`, the production decision path). This package lives in ziffer
|
|
7
|
+
**only because the engine repository is publicly frozen and ziffer CI cannot reach new
|
|
8
|
+
engine commits**; the mirrored corpus in `fixtures/` plus `tools/check-verify-mirror.py`
|
|
9
|
+
is what keeps three copies of one rule honest. Engine-home for this package is revisited
|
|
10
|
+
if the freeze lifts.
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @ziffer-io/verify
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
Most integrations do not install this directly. `@ziffer-io/client` depends on it and re-exports
|
|
17
|
+
`verifyReceipt`, so there is one verifier in your dependency tree rather than two that can
|
|
18
|
+
disagree. Install it on its own when you verify receipts and never call the API.
|
|
19
|
+
|
|
20
|
+
## What it does
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { verifyReceipt, Refusal } from '@ziffer-io/verify';
|
|
24
|
+
|
|
25
|
+
const verified = verifyReceipt(receiptJson, proposalBytes, {
|
|
26
|
+
classical, // Ed25519 verification key, 32 bytes, from the signed bundle (PB-12)
|
|
27
|
+
pq, // ML-DSA-65 verification key, 1,952 bytes, same home
|
|
28
|
+
minSuite: 'hybrid-ed25519-mldsa65', // CR-4 floor, from the signed manifest
|
|
29
|
+
});
|
|
30
|
+
// throws Refusal on any defect; verified.proposalHash is the verifier's OWN hash
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
`verifyReceipt` runs exactly the **stateless** portion of the §9.3 checklist — the part
|
|
34
|
+
answerable with only receipt + proposal + identity: AB-0 (receipt version 3 only),
|
|
35
|
+
AB-5/AB-6 (signed-body exclusion and byte cap), CR-1/CR-4/CR-3 (suite known, floor by
|
|
36
|
+
containment, hybrid **conjunctive** — every primitive must verify), 9.3-2 (decision
|
|
37
|
+
domain), 9.3-3 (proposal hash **recomputed** from the caller's own bytes, never read
|
|
38
|
+
from the message — RES-8), 9.3-5/L-14 (temporal position and window ceiling, over
|
|
39
|
+
strictly parsed RFC 3339 UTC instants), WE-4/L-17 (nonce type and size).
|
|
40
|
+
|
|
41
|
+
A pass means "the stateless set found nothing", never "the receipt may be consumed":
|
|
42
|
+
steps 4 (policy basis), 6's CL-2 claim, 7 (grading recomputation), 7b (the quorum),
|
|
43
|
+
8 (tenant scoping) and 9–10 need the signed bundle, the attester registry, a ledger and
|
|
44
|
+
a context store, and are deliberately absent rather than approximated. The module doc in
|
|
45
|
+
`src/verify.ts` carries the full table and the R/B/T classification.
|
|
46
|
+
|
|
47
|
+
Every refusal is a thrown `Refusal` whose `clause` is spelled **exactly as the engine's
|
|
48
|
+
Rust clause constants** spell it (`AB-0`, `AB-6`, `CR-1`, `CR-4`, `AT-8a`, `9.3-1`,
|
|
49
|
+
`9.3-2`, `9.3-3`, `9.3-5`, `L-14`, `WE-4`, `L-17`) — the clause is what the three
|
|
50
|
+
implementations are compared on.
|
|
51
|
+
|
|
52
|
+
## What it does not do
|
|
53
|
+
|
|
54
|
+
- **It is not a receipt gate.** A pass is "the stateless set found nothing", not "this receipt may
|
|
55
|
+
be consumed" — the paragraph above lists the six §9.3 steps that need a signed bundle, an
|
|
56
|
+
attester registry, a ledger and a context store, and are absent rather than approximated.
|
|
57
|
+
- **It fetches nothing and trusts nothing you did not hand it.** The identity keys and the CR-4
|
|
58
|
+
suite floor are arguments. It will not read them from the receipt, because a verifier that takes
|
|
59
|
+
a derived security value from the party it is verifying is the defect class this specification
|
|
60
|
+
has published five corrections for (RES-8).
|
|
61
|
+
- **It does not hash your proposal for you.** You pass the bytes your own code passes to the API,
|
|
62
|
+
and the verifier recomputes the hash from them. Handing it the hash would verify nothing.
|
|
63
|
+
- **It has no `approve`, and no test mode that mints a receipt.** Signing lives behind keys this
|
|
64
|
+
package never holds.
|
|
65
|
+
|
|
66
|
+
## Strict Ed25519, written out
|
|
67
|
+
|
|
68
|
+
`@noble/curves`' `verify` with `{ zip215: false }` enforces canonical encodings but —
|
|
69
|
+
like OpenSSL — does **not** refuse small-order points. The ACP-106 rule (a small-order
|
|
70
|
+
public key or a small-order `R` makes the verification equation stop mentioning the
|
|
71
|
+
message) is therefore written out in `src/ed25519.ts`, exactly as the Python reference
|
|
72
|
+
writes it in `verify_prim` and Rust gets from `verify_strict`. The mirrored
|
|
73
|
+
`fixtures/refused_ed25519.json` is the executable statement: every vector is accepted by
|
|
74
|
+
RFC 8032 as OpenSSL implements it and refused by all three implementations. Deleting the
|
|
75
|
+
guard here was observed to accept the corpus's `r_is_the_identity_point` vector.
|
|
76
|
+
|
|
77
|
+
## Canonicalization, proven not transcribed
|
|
78
|
+
|
|
79
|
+
`src/canon.ts` reproduces the engine canon (sorted keys at every depth by **code
|
|
80
|
+
point** — not JS's UTF-16 unit order — no whitespace, raw UTF-8). The proof is a
|
|
81
|
+
triangle: `fixtures/canon_vectors.json` was generated by importing and executing the
|
|
82
|
+
engine's own `acp_executor.canon` (`fixtures/gen_canon_vectors.py`), the TS tests assert
|
|
83
|
+
byte-identity with the fixture on every case, and `tools/check-verify-mirror.py`
|
|
84
|
+
re-derives every case against the engine at the pin.
|
|
85
|
+
|
|
86
|
+
**Disclosed divergence (fail-closed):** the engine accepts a float *nested* in a signed
|
|
87
|
+
structure (the ACP-75 bug-for-bug agreement between Python and Rust). This
|
|
88
|
+
implementation refuses any non-integer number at any depth under `AT-8a`, because
|
|
89
|
+
`JSON.parse` collapses `1000.0` and `1000` into one value and reproducing the engine's
|
|
90
|
+
bytes would be a guess about bytes it never saw. Integers beyond `2^53−1` are refused
|
|
91
|
+
for the same reason (the ACP-54 integer-domain family reached from the JS side). No
|
|
92
|
+
conforming wire receipt carries either — every temporal field is an RFC 3339 string
|
|
93
|
+
since v1.3.28 (ACP-167). Both refusals are pinned by tests so the divergence cannot
|
|
94
|
+
vanish silently.
|
|
95
|
+
|
|
96
|
+
## Fixtures
|
|
97
|
+
|
|
98
|
+
Byte-identical mirrors of engine files, each with a `.provenance.json` sidecar (source
|
|
99
|
+
path, rev, sha256 — a header *inside* the JSON would change the bytes the identity claim
|
|
100
|
+
is about): `refused_ed25519.json`, `python_signatures.json` (the reference's signatures,
|
|
101
|
+
which this side must accept — the TS leg of `tests/python_interop.rs`' claim; its seeds
|
|
102
|
+
also pin the test-key derivation mirror in `src/testkeys.ts`), and
|
|
103
|
+
`instant-type-vectors.json` (this package is the shared instant corpus's fourth
|
|
104
|
+
consumer). Plus the derived `canon_vectors.json` above. Re-mirror at a pin bump;
|
|
105
|
+
`tools/check-verify-mirror.py` refuses a mirror whose provenance rev is not the
|
|
106
|
+
workspace pin.
|
|
107
|
+
|
|
108
|
+
## Running
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
pnpm --filter @ziffer-io/verify test # tsc -b && node --test (27 tests)
|
|
112
|
+
python3 tools/check-verify-mirror.py # from the repo root; needs ACP_REPO_PATH at the pin
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Licence
|
|
116
|
+
|
|
117
|
+
Proprietary. Copyright (c) 2026 code75 SASU, Paris, France. ZIFFER is a registered trademark of code75
|
|
118
|
+
SASU. This package is **not open source**: it is licensed for use with the ZIFFER service under
|
|
119
|
+
your agreement with code75, on the terms in `LICENSE` beside this file. The third-party
|
|
120
|
+
open-source components it redistributes are listed in `THIRD-PARTY-NOTICES` and are governed by
|
|
121
|
+
their own licences.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
THIRD-PARTY NOTICES for @ziffer-io/verify
|
|
2
|
+
|
|
3
|
+
GENERATED FILE -- do not edit by hand.
|
|
4
|
+
Regenerate with: node tools/third-party-notices.mjs
|
|
5
|
+
tools/release-npm.sh refuses to publish when this file is out of date.
|
|
6
|
+
|
|
7
|
+
The LICENSE beside this file governs the ZIFFER SDK itself, which is
|
|
8
|
+
proprietary software of code75 SASU. The components below are third-party
|
|
9
|
+
open-source, are redistributed under their own licences, and those licences
|
|
10
|
+
govern them and prevail for those components only (SDK licence, section 5).
|
|
11
|
+
Each entry is read from the component as it is installed, at the version
|
|
12
|
+
this release resolves.
|
|
13
|
+
|
|
14
|
+
4 component(s):
|
|
15
|
+
|
|
16
|
+
----------------------------------------------------------------------------
|
|
17
|
+
@noble/ciphers 2.3.0
|
|
18
|
+
licence: MIT
|
|
19
|
+
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
|
|
20
|
+
full text: LICENSE, as distributed in the package
|
|
21
|
+
|
|
22
|
+
----------------------------------------------------------------------------
|
|
23
|
+
@noble/curves 2.3.0
|
|
24
|
+
licence: MIT
|
|
25
|
+
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
|
|
26
|
+
full text: LICENSE, as distributed in the package
|
|
27
|
+
|
|
28
|
+
----------------------------------------------------------------------------
|
|
29
|
+
@noble/hashes 2.3.0
|
|
30
|
+
licence: MIT
|
|
31
|
+
Copyright (c) 2022 Paul Miller (https://paulmillr.com)
|
|
32
|
+
full text: LICENSE, as distributed in the package
|
|
33
|
+
|
|
34
|
+
----------------------------------------------------------------------------
|
|
35
|
+
@noble/post-quantum 0.7.0
|
|
36
|
+
licence: MIT
|
|
37
|
+
Copyright (c) 2024 Paul Miller (https://paulmillr.com)
|
|
38
|
+
full text: LICENSE, as distributed in the package
|
|
39
|
+
|
|
40
|
+
----------------------------------------------------------------------------
|
package/dist/canon.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AT-8a -- the canonical bytes, the third writing of one encoding.
|
|
3
|
+
*
|
|
4
|
+
* The engine's two: `reference/src/acp_executor.py::canon` (json.dumps,
|
|
5
|
+
* sort_keys, separators (",", ":"), ensure_ascii=False) and
|
|
6
|
+
* `crates/acp-decision/src/receipt.rs::canon` (sorted keys at every depth,
|
|
7
|
+
* scalars through serde's writer). `fixtures/canon_vectors.json` holds bytes
|
|
8
|
+
* the ENGINE's own canon produced, and tools/check-verify-mirror.py re-derives
|
|
9
|
+
* them against the engine at the pin -- so this file agreeing with the fixture
|
|
10
|
+
* is this file agreeing with the engine, not with a transcription of it.
|
|
11
|
+
*
|
|
12
|
+
* # Key order is CODE-POINT order, sorted here, at every depth
|
|
13
|
+
*
|
|
14
|
+
* Python's `sort_keys=True` sorts str by code point; Rust sorts `&str` by
|
|
15
|
+
* UTF-8 bytes, which is the same order. JavaScript's default string comparison
|
|
16
|
+
* is by UTF-16 CODE UNIT, which disagrees for exactly one region: an astral
|
|
17
|
+
* character (>= U+10000) encodes as a surrogate pair starting at 0xD800, so it
|
|
18
|
+
* sorts BEFORE U+E000..U+FFFF instead of after. `compareCodePoints` below is
|
|
19
|
+
* written out for that reason, and the fixture's
|
|
20
|
+
* `astral_keys_sort_by_code_point_not_utf16_unit` case is red under a naive
|
|
21
|
+
* `keys.sort()`. Sorting is done HERE, never by trusting object insertion
|
|
22
|
+
* order -- the ACP-126 lesson (a map that is sorted "by construction" is
|
|
23
|
+
* sorted in exactly one build configuration) applies to JS objects verbatim:
|
|
24
|
+
* they preserve insertion order, and integer-like keys don't even do that.
|
|
25
|
+
*
|
|
26
|
+
* # Numbers: safe integers ONLY, and this is a disclosed divergence
|
|
27
|
+
*
|
|
28
|
+
* The engine accepts a float NESTED in a structure (top-level only is refused)
|
|
29
|
+
* -- the ACP-75 bug-for-bug agreement between Python and Rust. This
|
|
30
|
+
* implementation cannot join that agreement honestly: JSON.parse collapses
|
|
31
|
+
* `1000.0` and `1000` into one value, and Python/serde render the first as
|
|
32
|
+
* "1000.0" while JS would render "1000" -- silently different signed bytes for
|
|
33
|
+
* bytes the engine accepted. Guessing would be a forgery of the preimage, so
|
|
34
|
+
* a non-integer number is REFUSED here at any depth (AT-8a, fail closed).
|
|
35
|
+
* No conforming wire receipt carries one: every temporal field is an RFC 3339
|
|
36
|
+
* string since v1.3.28 (ACP-167). The same rule refuses integers outside
|
|
37
|
+
* Number.MAX_SAFE_INTEGER, where JSON.parse has already lost the digits the
|
|
38
|
+
* engine would have signed (the ACP-54 integer-domain family, reached from the
|
|
39
|
+
* JS side). Both refusals are pinned by tests so the divergence cannot vanish
|
|
40
|
+
* silently.
|
|
41
|
+
*/
|
|
42
|
+
/** What a parsed JSON document can be. `unknown` is narrowed at the walk. */
|
|
43
|
+
export type Json = null | boolean | number | string | Json[] | {
|
|
44
|
+
[key: string]: Json;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Code-point comparison -- Python's `sorted()` over str, Rust's `&str` `Ord`.
|
|
48
|
+
*
|
|
49
|
+
* Iterating a string with `for..of` yields whole code points (surrogate pairs
|
|
50
|
+
* arrive as one unit), so comparing `codePointAt(0)` per step IS code-point
|
|
51
|
+
* order; the loop exists because `<` on the full strings would be UTF-16 order.
|
|
52
|
+
*/
|
|
53
|
+
export declare function compareCodePoints(a: string, b: string): number;
|
|
54
|
+
/**
|
|
55
|
+
* The canonical UTF-8 bytes of a parsed JSON value.
|
|
56
|
+
*
|
|
57
|
+
* One encoding for BOTH signing preimages and id derivation, exactly as the
|
|
58
|
+
* reference states it: sorted keys, no whitespace, no floats, raw UTF-8.
|
|
59
|
+
*/
|
|
60
|
+
export declare function canon(value: unknown): Uint8Array;
|
|
61
|
+
//# sourceMappingURL=canon.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canon.d.ts","sourceRoot":"","sources":["../src/canon.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AAKH,6EAA6E;AAC7E,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,EAAE,GAAG;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC;AAEvF;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAkB9D;AAkED;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,OAAO,GAAG,UAAU,CAIhD"}
|
package/dist/canon.js
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AT-8a -- the canonical bytes, the third writing of one encoding.
|
|
3
|
+
*
|
|
4
|
+
* The engine's two: `reference/src/acp_executor.py::canon` (json.dumps,
|
|
5
|
+
* sort_keys, separators (",", ":"), ensure_ascii=False) and
|
|
6
|
+
* `crates/acp-decision/src/receipt.rs::canon` (sorted keys at every depth,
|
|
7
|
+
* scalars through serde's writer). `fixtures/canon_vectors.json` holds bytes
|
|
8
|
+
* the ENGINE's own canon produced, and tools/check-verify-mirror.py re-derives
|
|
9
|
+
* them against the engine at the pin -- so this file agreeing with the fixture
|
|
10
|
+
* is this file agreeing with the engine, not with a transcription of it.
|
|
11
|
+
*
|
|
12
|
+
* # Key order is CODE-POINT order, sorted here, at every depth
|
|
13
|
+
*
|
|
14
|
+
* Python's `sort_keys=True` sorts str by code point; Rust sorts `&str` by
|
|
15
|
+
* UTF-8 bytes, which is the same order. JavaScript's default string comparison
|
|
16
|
+
* is by UTF-16 CODE UNIT, which disagrees for exactly one region: an astral
|
|
17
|
+
* character (>= U+10000) encodes as a surrogate pair starting at 0xD800, so it
|
|
18
|
+
* sorts BEFORE U+E000..U+FFFF instead of after. `compareCodePoints` below is
|
|
19
|
+
* written out for that reason, and the fixture's
|
|
20
|
+
* `astral_keys_sort_by_code_point_not_utf16_unit` case is red under a naive
|
|
21
|
+
* `keys.sort()`. Sorting is done HERE, never by trusting object insertion
|
|
22
|
+
* order -- the ACP-126 lesson (a map that is sorted "by construction" is
|
|
23
|
+
* sorted in exactly one build configuration) applies to JS objects verbatim:
|
|
24
|
+
* they preserve insertion order, and integer-like keys don't even do that.
|
|
25
|
+
*
|
|
26
|
+
* # Numbers: safe integers ONLY, and this is a disclosed divergence
|
|
27
|
+
*
|
|
28
|
+
* The engine accepts a float NESTED in a structure (top-level only is refused)
|
|
29
|
+
* -- the ACP-75 bug-for-bug agreement between Python and Rust. This
|
|
30
|
+
* implementation cannot join that agreement honestly: JSON.parse collapses
|
|
31
|
+
* `1000.0` and `1000` into one value, and Python/serde render the first as
|
|
32
|
+
* "1000.0" while JS would render "1000" -- silently different signed bytes for
|
|
33
|
+
* bytes the engine accepted. Guessing would be a forgery of the preimage, so
|
|
34
|
+
* a non-integer number is REFUSED here at any depth (AT-8a, fail closed).
|
|
35
|
+
* No conforming wire receipt carries one: every temporal field is an RFC 3339
|
|
36
|
+
* string since v1.3.28 (ACP-167). The same rule refuses integers outside
|
|
37
|
+
* Number.MAX_SAFE_INTEGER, where JSON.parse has already lost the digits the
|
|
38
|
+
* engine would have signed (the ACP-54 integer-domain family, reached from the
|
|
39
|
+
* JS side). Both refusals are pinned by tests so the divergence cannot vanish
|
|
40
|
+
* silently.
|
|
41
|
+
*/
|
|
42
|
+
import { CLAUSE_CANONICAL } from './clauses.js';
|
|
43
|
+
import { Refusal } from './refusal.js';
|
|
44
|
+
/**
|
|
45
|
+
* Code-point comparison -- Python's `sorted()` over str, Rust's `&str` `Ord`.
|
|
46
|
+
*
|
|
47
|
+
* Iterating a string with `for..of` yields whole code points (surrogate pairs
|
|
48
|
+
* arrive as one unit), so comparing `codePointAt(0)` per step IS code-point
|
|
49
|
+
* order; the loop exists because `<` on the full strings would be UTF-16 order.
|
|
50
|
+
*/
|
|
51
|
+
export function compareCodePoints(a, b) {
|
|
52
|
+
const ia = a[Symbol.iterator]();
|
|
53
|
+
const ib = b[Symbol.iterator]();
|
|
54
|
+
for (;;) {
|
|
55
|
+
const na = ia.next();
|
|
56
|
+
const nb = ib.next();
|
|
57
|
+
if (na.done && nb.done)
|
|
58
|
+
return 0;
|
|
59
|
+
if (na.done)
|
|
60
|
+
return -1;
|
|
61
|
+
if (nb.done)
|
|
62
|
+
return 1;
|
|
63
|
+
const ca = na.value.codePointAt(0);
|
|
64
|
+
const cb = nb.value.codePointAt(0);
|
|
65
|
+
// codePointAt(0) on a non-empty string cannot be undefined; the guard is
|
|
66
|
+
// for the type system, and refusing loudly beats a silent NaN comparison.
|
|
67
|
+
if (ca === undefined || cb === undefined) {
|
|
68
|
+
throw new Refusal(CLAUSE_CANONICAL, 'key is not a comparable string');
|
|
69
|
+
}
|
|
70
|
+
if (ca !== cb)
|
|
71
|
+
return ca - cb;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// A type predicate, not an `as` cast (the project rules forbid the cast, and
|
|
75
|
+
// here it would be load-bearing): null and arrays are handled before this is
|
|
76
|
+
// consulted, so what remains of `object` is a plain key-value record.
|
|
77
|
+
function isPlainObject(v) {
|
|
78
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
79
|
+
}
|
|
80
|
+
function writeCanon(value, out) {
|
|
81
|
+
if (value === null || value === true || value === false) {
|
|
82
|
+
out.push(String(value));
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
if (typeof value === 'number') {
|
|
86
|
+
if (!Number.isSafeInteger(value)) {
|
|
87
|
+
// Both halves of the disclosed divergence in the module doc: a float at
|
|
88
|
+
// ANY depth (the engine refuses top-level only), and an integer beyond
|
|
89
|
+
// 2^53-1 (JSON.parse already lost digits the engine would have signed).
|
|
90
|
+
throw new Refusal(CLAUSE_CANONICAL, 'number is not a safe integer: JS cannot reproduce the engine canon bytes for it');
|
|
91
|
+
}
|
|
92
|
+
// A JSON `-0` parses to negative zero here and to integer zero in Python
|
|
93
|
+
// and serde; String(-0) is "0", so all three emit the same byte. Noted so
|
|
94
|
+
// nobody "fixes" this to preserve the sign JS kept and JSON did not.
|
|
95
|
+
out.push(String(value));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
if (typeof value === 'string') {
|
|
99
|
+
// JSON.stringify escapes exactly what Python's ensure_ascii=False and
|
|
100
|
+
// serde_json escape: the quote, the backslash, \b \t \n \f \r, and other
|
|
101
|
+
// control chars as \u00xx -- everything else travels as raw UTF-8. The
|
|
102
|
+
// fixture's control-char and astral cases hold all three to it.
|
|
103
|
+
out.push(JSON.stringify(value));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (Array.isArray(value)) {
|
|
107
|
+
out.push('[');
|
|
108
|
+
for (let i = 0; i < value.length; i += 1) {
|
|
109
|
+
if (i > 0)
|
|
110
|
+
out.push(',');
|
|
111
|
+
writeCanon(value[i], out);
|
|
112
|
+
}
|
|
113
|
+
out.push(']');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (isPlainObject(value)) {
|
|
117
|
+
const obj = value;
|
|
118
|
+
const keys = Object.keys(obj).sort(compareCodePoints);
|
|
119
|
+
out.push('{');
|
|
120
|
+
for (let i = 0; i < keys.length; i += 1) {
|
|
121
|
+
const key = keys[i];
|
|
122
|
+
if (key === undefined)
|
|
123
|
+
continue; // unreachable; index is in range
|
|
124
|
+
if (i > 0)
|
|
125
|
+
out.push(',');
|
|
126
|
+
out.push(JSON.stringify(key));
|
|
127
|
+
out.push(':');
|
|
128
|
+
writeCanon(obj[key], out);
|
|
129
|
+
}
|
|
130
|
+
out.push('}');
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// undefined, functions, bigints, symbols: not JSON, not encodable.
|
|
134
|
+
throw new Refusal(CLAUSE_CANONICAL, `type ${typeof value} is not canonically encodable`);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The canonical UTF-8 bytes of a parsed JSON value.
|
|
138
|
+
*
|
|
139
|
+
* One encoding for BOTH signing preimages and id derivation, exactly as the
|
|
140
|
+
* reference states it: sorted keys, no whitespace, no floats, raw UTF-8.
|
|
141
|
+
*/
|
|
142
|
+
export function canon(value) {
|
|
143
|
+
const out = [];
|
|
144
|
+
writeCanon(value, out);
|
|
145
|
+
return new TextEncoder().encode(out.join(''));
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=canon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canon.js","sourceRoot":"","sources":["../src/canon.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The refusal names, spelled EXACTLY as the engine's Rust clause constants
|
|
3
|
+
* spell them (crates/acp-decision/src/{receipt,decide,quorum}.rs at the pin).
|
|
4
|
+
*
|
|
5
|
+
* This is the third implementation of receipt verification, and the clause a
|
|
6
|
+
* refusal carries is what the three are compared ON: two verifiers that both
|
|
7
|
+
* refuse a forged, expired receipt -- one saying 9.3-1, the other 9.3-5 --
|
|
8
|
+
* have not been shown to agree on anything an operator could act on
|
|
9
|
+
* (decide.rs module doc). So every name here is a wire contract, not a label,
|
|
10
|
+
* and a constant may change only when the engine's does.
|
|
11
|
+
*
|
|
12
|
+
* The Rust constant NAME is kept beside each value so a reader diffing the two
|
|
13
|
+
* files sees one table, not two vocabularies.
|
|
14
|
+
*/
|
|
15
|
+
/** Rust `receipt::CLAUSE_VERSION` -- AB-0, the receipt does not declare version 3. */
|
|
16
|
+
export declare const CLAUSE_VERSION = "AB-0";
|
|
17
|
+
/** Rust `receipt::CLAUSE_BODY_SIZE` -- AB-6, the signed body exceeds the custody byte cap. */
|
|
18
|
+
export declare const CLAUSE_BODY_SIZE = "AB-6";
|
|
19
|
+
/** Rust `receipt::CLAUSE_UNKNOWN_SUITE` -- CR-1, the suite name is not one this build knows. */
|
|
20
|
+
export declare const CLAUSE_UNKNOWN_SUITE = "CR-1";
|
|
21
|
+
/** Rust `receipt::CLAUSE_SUITE_FLOOR` -- CR-4, the suite does not contain every floor primitive. */
|
|
22
|
+
export declare const CLAUSE_SUITE_FLOOR = "CR-4";
|
|
23
|
+
/** Rust `receipt::CLAUSE_CANONICAL` -- AT-8a, the structure is not canonically encodable. */
|
|
24
|
+
export declare const CLAUSE_CANONICAL = "AT-8a";
|
|
25
|
+
/** Rust `receipt::CLAUSE_SIGNATURE` -- 9.3-1, the signature did not verify under the bundle key. */
|
|
26
|
+
export declare const CLAUSE_SIGNATURE = "9.3-1";
|
|
27
|
+
/** Rust `receipt::CLAUSE_DECISION` -- 9.3-2, the decision was not ALLOW. */
|
|
28
|
+
export declare const CLAUSE_DECISION = "9.3-2";
|
|
29
|
+
/**
|
|
30
|
+
* Rust `decide.rs` spells 9.3-3 as a literal at the refusal site, not a
|
|
31
|
+
* constant; the value is the contract either way. Receipt not bound to this
|
|
32
|
+
* proposal (B-1a).
|
|
33
|
+
*/
|
|
34
|
+
export declare const CLAUSE_PROPOSAL_BINDING = "9.3-3";
|
|
35
|
+
/** Rust literal in `decide.rs` step 5 -- temporal position (missing, malformed, expired, future). */
|
|
36
|
+
export declare const CLAUSE_TEMPORAL = "9.3-5";
|
|
37
|
+
/**
|
|
38
|
+
* Rust literal in `decide.rs` step 5 -- L-14, the validity-window ceiling.
|
|
39
|
+
* A SEPARATE clause from 9.3-5: a receipt whose window is 10x too long is an
|
|
40
|
+
* attacker widening the interval a stolen receipt is usable in (Y2), and
|
|
41
|
+
* calling that "expired" would tell an operator the wrong thing.
|
|
42
|
+
*/
|
|
43
|
+
export declare const CLAUSE_VALIDITY_WINDOW = "L-14";
|
|
44
|
+
/** Rust `quorum::CLAUSE_WIRE_TYPE` -- WE-4, the nonce is not `b64:` + RFC 4648 s4 base64. */
|
|
45
|
+
export declare const CLAUSE_WIRE_TYPE = "WE-4";
|
|
46
|
+
/**
|
|
47
|
+
* Rust `decide::CLAUSE_RECEIPT_NONCE_SIZE` -- L-17, the receipt nonce is
|
|
48
|
+
* well-formed but not 128-bit. The receipt nonce's OWN size clause, never
|
|
49
|
+
* AT-1 (which sizes the attestation nonce): one number, two clauses, and the
|
|
50
|
+
* refusal carries the field's (ACP-88/ACP-89).
|
|
51
|
+
*/
|
|
52
|
+
export declare const CLAUSE_RECEIPT_NONCE_SIZE = "L-17";
|
|
53
|
+
//# sourceMappingURL=clauses.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clauses.d.ts","sourceRoot":"","sources":["../src/clauses.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,sFAAsF;AACtF,eAAO,MAAM,cAAc,SAAS,CAAC;AACrC,8FAA8F;AAC9F,eAAO,MAAM,gBAAgB,SAAS,CAAC;AACvC,gGAAgG;AAChG,eAAO,MAAM,oBAAoB,SAAS,CAAC;AAC3C,oGAAoG;AACpG,eAAO,MAAM,kBAAkB,SAAS,CAAC;AACzC,6FAA6F;AAC7F,eAAO,MAAM,gBAAgB,UAAU,CAAC;AACxC,oGAAoG;AACpG,eAAO,MAAM,gBAAgB,UAAU,CAAC;AACxC,4EAA4E;AAC5E,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,UAAU,CAAC;AAC/C,qGAAqG;AACrG,eAAO,MAAM,eAAe,UAAU,CAAC;AACvC;;;;;GAKG;AACH,eAAO,MAAM,sBAAsB,SAAS,CAAC;AAC7C,6FAA6F;AAC7F,eAAO,MAAM,gBAAgB,SAAS,CAAC;AACvC;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,SAAS,CAAC"}
|
package/dist/clauses.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The refusal names, spelled EXACTLY as the engine's Rust clause constants
|
|
3
|
+
* spell them (crates/acp-decision/src/{receipt,decide,quorum}.rs at the pin).
|
|
4
|
+
*
|
|
5
|
+
* This is the third implementation of receipt verification, and the clause a
|
|
6
|
+
* refusal carries is what the three are compared ON: two verifiers that both
|
|
7
|
+
* refuse a forged, expired receipt -- one saying 9.3-1, the other 9.3-5 --
|
|
8
|
+
* have not been shown to agree on anything an operator could act on
|
|
9
|
+
* (decide.rs module doc). So every name here is a wire contract, not a label,
|
|
10
|
+
* and a constant may change only when the engine's does.
|
|
11
|
+
*
|
|
12
|
+
* The Rust constant NAME is kept beside each value so a reader diffing the two
|
|
13
|
+
* files sees one table, not two vocabularies.
|
|
14
|
+
*/
|
|
15
|
+
/** Rust `receipt::CLAUSE_VERSION` -- AB-0, the receipt does not declare version 3. */
|
|
16
|
+
export const CLAUSE_VERSION = 'AB-0';
|
|
17
|
+
/** Rust `receipt::CLAUSE_BODY_SIZE` -- AB-6, the signed body exceeds the custody byte cap. */
|
|
18
|
+
export const CLAUSE_BODY_SIZE = 'AB-6';
|
|
19
|
+
/** Rust `receipt::CLAUSE_UNKNOWN_SUITE` -- CR-1, the suite name is not one this build knows. */
|
|
20
|
+
export const CLAUSE_UNKNOWN_SUITE = 'CR-1';
|
|
21
|
+
/** Rust `receipt::CLAUSE_SUITE_FLOOR` -- CR-4, the suite does not contain every floor primitive. */
|
|
22
|
+
export const CLAUSE_SUITE_FLOOR = 'CR-4';
|
|
23
|
+
/** Rust `receipt::CLAUSE_CANONICAL` -- AT-8a, the structure is not canonically encodable. */
|
|
24
|
+
export const CLAUSE_CANONICAL = 'AT-8a';
|
|
25
|
+
/** Rust `receipt::CLAUSE_SIGNATURE` -- 9.3-1, the signature did not verify under the bundle key. */
|
|
26
|
+
export const CLAUSE_SIGNATURE = '9.3-1';
|
|
27
|
+
/** Rust `receipt::CLAUSE_DECISION` -- 9.3-2, the decision was not ALLOW. */
|
|
28
|
+
export const CLAUSE_DECISION = '9.3-2';
|
|
29
|
+
/**
|
|
30
|
+
* Rust `decide.rs` spells 9.3-3 as a literal at the refusal site, not a
|
|
31
|
+
* constant; the value is the contract either way. Receipt not bound to this
|
|
32
|
+
* proposal (B-1a).
|
|
33
|
+
*/
|
|
34
|
+
export const CLAUSE_PROPOSAL_BINDING = '9.3-3';
|
|
35
|
+
/** Rust literal in `decide.rs` step 5 -- temporal position (missing, malformed, expired, future). */
|
|
36
|
+
export const CLAUSE_TEMPORAL = '9.3-5';
|
|
37
|
+
/**
|
|
38
|
+
* Rust literal in `decide.rs` step 5 -- L-14, the validity-window ceiling.
|
|
39
|
+
* A SEPARATE clause from 9.3-5: a receipt whose window is 10x too long is an
|
|
40
|
+
* attacker widening the interval a stolen receipt is usable in (Y2), and
|
|
41
|
+
* calling that "expired" would tell an operator the wrong thing.
|
|
42
|
+
*/
|
|
43
|
+
export const CLAUSE_VALIDITY_WINDOW = 'L-14';
|
|
44
|
+
/** Rust `quorum::CLAUSE_WIRE_TYPE` -- WE-4, the nonce is not `b64:` + RFC 4648 s4 base64. */
|
|
45
|
+
export const CLAUSE_WIRE_TYPE = 'WE-4';
|
|
46
|
+
/**
|
|
47
|
+
* Rust `decide::CLAUSE_RECEIPT_NONCE_SIZE` -- L-17, the receipt nonce is
|
|
48
|
+
* well-formed but not 128-bit. The receipt nonce's OWN size clause, never
|
|
49
|
+
* AT-1 (which sizes the attestation nonce): one number, two clauses, and the
|
|
50
|
+
* refusal carries the field's (ACP-88/ACP-89).
|
|
51
|
+
*/
|
|
52
|
+
export const CLAUSE_RECEIPT_NONCE_SIZE = 'L-17';
|
|
53
|
+
//# sourceMappingURL=clauses.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clauses.js","sourceRoot":"","sources":["../src/clauses.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,sFAAsF;AACtF,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC;AACrC,8FAA8F;AAC9F,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AACvC,gGAAgG;AAChG,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAC3C,oGAAoG;AACpG,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC;AACzC,6FAA6F;AAC7F,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC;AACxC,oGAAoG;AACpG,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC;AACxC,4EAA4E;AAC5E,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AACvC;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,OAAO,CAAC;AAC/C,qGAAqG;AACrG,MAAM,CAAC,MAAM,eAAe,GAAG,OAAO,CAAC;AACvC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,MAAM,CAAC;AAC7C,6FAA6F;AAC7F,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC;AACvC;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,MAAM,CAAC"}
|