@peac/protocol 0.15.3 → 0.16.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/dist/index.cjs +86 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +86 -3
- package/dist/index.mjs.map +1 -1
- package/dist/replay-guard.d.ts +55 -0
- package/dist/replay-guard.d.ts.map +1 -0
- package/dist/verify-local-wire01.d.ts.map +1 -1
- package/dist/verify-local.cjs +11 -2
- package/dist/verify-local.cjs.map +1 -1
- package/dist/verify-local.d.ts +1 -1
- package/dist/verify-local.d.ts.map +1 -1
- package/dist/verify-local.mjs +11 -2
- package/dist/verify-local.mjs.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional, composable replay defense for online consumers of PEAC records.
|
|
3
|
+
*
|
|
4
|
+
* createReplayGuard() returns a stateful guard that classifies an already-verified
|
|
5
|
+
* record as 'fresh', 'replayed', or 'outside-window'. The store is bounded by both
|
|
6
|
+
* count (maxEntries) and time (a TTL purge plus an iat acceptance window), so
|
|
7
|
+
* it is finite and bounded against unbounded state growth. The dedup key is
|
|
8
|
+
* (iss, jti); iat gates the window. All timestamp/window values are safe integers
|
|
9
|
+
* (epoch seconds); unsafe-integer input fails closed.
|
|
10
|
+
*
|
|
11
|
+
* This is an online acceptance policy, not a record property: PEAC adds no exp to
|
|
12
|
+
* records, changes no wire bytes, and enforces nothing. The same record stays valid
|
|
13
|
+
* and offline-verifiable indefinitely outside any window. The guard returns a
|
|
14
|
+
* verdict; the deployer chooses the response. It is intentionally not wired into the
|
|
15
|
+
* stateless verifyLocal path.
|
|
16
|
+
*
|
|
17
|
+
* The clock (now) returns epoch seconds; backward movement (e.g. an NTP or manual
|
|
18
|
+
* wall-clock regression) is clamped internally to the last observed second, so the
|
|
19
|
+
* guard stays correct even if the clock goes backward. TTL entries expire at
|
|
20
|
+
* insertion-time + windowSeconds + maxClockSkewSeconds; with the clamped (monotonic)
|
|
21
|
+
* clock, expiry order equals insertion order and purge is O(number expired).
|
|
22
|
+
*/
|
|
23
|
+
export interface ReplayGuardOptions {
|
|
24
|
+
/**
|
|
25
|
+
* Accept only records whose iat is within [now - windowSeconds, now + maxClockSkewSeconds].
|
|
26
|
+
* Positive integer (seconds).
|
|
27
|
+
*/
|
|
28
|
+
windowSeconds: number;
|
|
29
|
+
/** Allowed future skew for iat (seconds). Default 300. Non-negative integer. */
|
|
30
|
+
maxClockSkewSeconds?: number;
|
|
31
|
+
/** Upper bound on retained entries. Default 100_000. Positive integer. */
|
|
32
|
+
maxEntries?: number;
|
|
33
|
+
/**
|
|
34
|
+
* Injectable clock returning epoch seconds (tests/determinism). Backward movement
|
|
35
|
+
* is clamped internally to the last observed second. Default Math.floor(Date.now() / 1000).
|
|
36
|
+
*/
|
|
37
|
+
now?: () => number;
|
|
38
|
+
}
|
|
39
|
+
export type ReplayGuardVerdict = 'fresh' | 'replayed' | 'outside-window';
|
|
40
|
+
export interface ReplayGuard {
|
|
41
|
+
/** Classify a record that has already been verified (e.g. by verifyLocal). No I/O. */
|
|
42
|
+
check(record: {
|
|
43
|
+
iss: string;
|
|
44
|
+
jti: string;
|
|
45
|
+
iat: number;
|
|
46
|
+
}): ReplayGuardVerdict;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Create a bounded replay guard for already-verified records.
|
|
50
|
+
*
|
|
51
|
+
* @param options - window, optional max clock skew, optional entry cap, optional clock.
|
|
52
|
+
* @returns a guard whose `check(record)` returns 'fresh' | 'replayed' | 'outside-window'.
|
|
53
|
+
*/
|
|
54
|
+
export declare function createReplayGuard(options: ReplayGuardOptions): ReplayGuard;
|
|
55
|
+
//# sourceMappingURL=replay-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replay-guard.d.ts","sourceRoot":"","sources":["../src/replay-guard.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAOH,MAAM,WAAW,kBAAkB;IACjC;;;OAGG;IACH,aAAa,EAAE,MAAM,CAAC;IACtB,gFAAgF;IAChF,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAEzE,MAAM,WAAW,WAAW;IAC1B,sFAAsF;IACtF,KAAK,CAAC,MAAM,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,kBAAkB,CAAC;CAC9E;AAwCD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAiF1E"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verify-local-wire01.d.ts","sourceRoot":"","sources":["../src/verify-local-wire01.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAA0B,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC9B,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAwB,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEnG;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAChC;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,KAAK,CAAC;IACnB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,cAAc,EAAE,mBAAmB,CAAC;CACrC,GACD;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,wBAAwB,CAAC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,KAAK,CAAC;IACnB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,cAAc,EAAE,mBAAmB,CAAC;CACrC,CAAC;AAEN;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,wBAAwB,GAAG,kBAAkB,CAAC;AAoDpF;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,UAAU,EACrB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,uBAAuB,CAAC,
|
|
1
|
+
{"version":3,"file":"verify-local-wire01.d.ts","sourceRoot":"","sources":["../src/verify-local-wire01.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAA0B,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAChF,OAAO,EAGL,KAAK,iBAAiB,EACtB,KAAK,wBAAwB,EAC9B,MAAM,cAAc,CAAC;AACtB,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,KAAK,EAAwB,kBAAkB,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEnG;;GAEG;AACH,MAAM,MAAM,wBAAwB,GAChC;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,OAAO,EAAE,UAAU,CAAC;IACpB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,KAAK,CAAC;IACnB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,cAAc,EAAE,mBAAmB,CAAC;CACrC,GACD;IACE,KAAK,EAAE,IAAI,CAAC;IACZ,OAAO,EAAE,aAAa,CAAC;IACvB,MAAM,EAAE,wBAAwB,CAAC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,EAAE,KAAK,CAAC;IACnB,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,cAAc,EAAE,mBAAmB,CAAC;CACrC,CAAC;AAEN;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG,wBAAwB,GAAG,kBAAkB,CAAC;AAoDpF;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,UAAU,EACrB,OAAO,GAAE,kBAAuB,GAC/B,OAAO,CAAC,uBAAuB,CAAC,CA8OlC"}
|
package/dist/verify-local.cjs
CHANGED
|
@@ -918,7 +918,14 @@ var JOSE_CODE_MAP = {
|
|
|
918
918
|
CRYPTO_JWS_CRIT_REJECTED: "E_JWS_CRIT_REJECTED",
|
|
919
919
|
CRYPTO_JWS_MISSING_KID: "E_JWS_MISSING_KID",
|
|
920
920
|
CRYPTO_JWS_B64_REJECTED: "E_JWS_B64_REJECTED",
|
|
921
|
-
CRYPTO_JWS_ZIP_REJECTED: "E_JWS_ZIP_REJECTED"
|
|
921
|
+
CRYPTO_JWS_ZIP_REJECTED: "E_JWS_ZIP_REJECTED",
|
|
922
|
+
// Raw-input I-JSON gate (RFC 7493): specific E_IJSON_* codes so callers can
|
|
923
|
+
// distinguish duplicate member names, out-of-range numbers, and invalid
|
|
924
|
+
// strings from ordinary format errors. (Generic JSON syntax errors from the
|
|
925
|
+
// gate use CRYPTO_INVALID_JWS_FORMAT -> E_INVALID_FORMAT via FORMAT_ERROR_CODES.)
|
|
926
|
+
CRYPTO_IJSON_DUPLICATE_MEMBER_NAME: "E_IJSON_DUPLICATE_MEMBER_NAME",
|
|
927
|
+
CRYPTO_IJSON_NUMBER_OUT_OF_RANGE: "E_IJSON_NUMBER_OUT_OF_RANGE",
|
|
928
|
+
CRYPTO_IJSON_INVALID_STRING: "E_IJSON_INVALID_STRING"
|
|
922
929
|
};
|
|
923
930
|
var MAX_PARSE_ISSUES = 25;
|
|
924
931
|
function sanitizeParseIssues(issues) {
|
|
@@ -942,10 +949,12 @@ async function verifyLocal(jws, publicKey, options = {}) {
|
|
|
942
949
|
try {
|
|
943
950
|
const result = await defaultCodec.decode(jws, publicKey);
|
|
944
951
|
if (!result.valid) {
|
|
952
|
+
const signatureBytes = ed25519SignatureByteLength(jws);
|
|
953
|
+
const invalidLength = signatureBytes !== null && signatureBytes !== ED25519_SIGNATURE_BYTES;
|
|
945
954
|
return {
|
|
946
955
|
valid: false,
|
|
947
956
|
code: "E_INVALID_SIGNATURE",
|
|
948
|
-
message: "Ed25519 signature verification failed"
|
|
957
|
+
message: invalidLength ? "Ed25519 signature has invalid length" : "Ed25519 signature verification failed"
|
|
949
958
|
};
|
|
950
959
|
}
|
|
951
960
|
const accumulatedWarnings = [];
|