@atlasauth/pca-abe 0.2.0 → 0.4.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.
@@ -1,9 +1,10 @@
1
- import { type Capability } from '@atlasauth/pca';
1
+ import { type Capability, type CapabilityChain } from '@atlasauth/pca';
2
2
  /**
3
3
  * Canonical attribute strings derived from a real PCA capability (see `@atlasauth/pca`'s `Capability`
4
4
  * and the signed policy `Envelope`). An "attribute" is a lowercase `kind:value` token; a policy
5
- * references attributes by exactly these tokens, and `keygenForCapability` issues one BF-IBE decryption
6
- * key per token the capability carries.
5
+ * references attributes by exactly these tokens, and `keygenForCapability` issues one per-attribute
6
+ * decryption key (the collusion-resistant per-holder-randomised scheme in `index.ts`, not plain
7
+ * Boneh-Franklin IBE) per token the capability carries.
7
8
  *
8
9
  * Four kinds are derived, matching the capability's authority surface (verb / resource / scopes /
9
10
  * holder):
@@ -13,18 +14,38 @@ import { type Capability } from '@atlasauth/pca';
13
14
  * - `scope:<s>` every scope carried in a `{ type:'scope', value }` or
14
15
  * `{ type:'scopes', values:[...] }` caveat.
15
16
  *
16
- * Because the envelope and caveats are SIGNED into the capability (and attenuation is append-only), the
17
- * attribute set a capability yields cannot be widened by a holder — it is exactly what the principal
18
- * authorised.
17
+ * - `act:["<v>","<r>"]` a BOUND (verb, resource) pair, one per verb x resource of each predicate.
18
+ *
19
+ * With a single predicate the flat `verb:` / `resource:` tokens are exact and are also emitted. With
20
+ * two or more predicates only the pair tokens (plus holder/scope) are emitted, so the pairing cannot
21
+ * be recombined (RT-08, breaking). Predicate `where` conditions and non-predicate caveats are NOT
22
+ * reflected in any token: a policy that depends on them must also require a `scope:` or `holder:`.
23
+ *
24
+ * `scope:` tokens, and (R3-09) the envelope predicates behind `act:`/`verb:`/`resource:`, come ONLY from the ROOT capability (R2-07). Attenuation may append ANY caveat, so a
25
+ * delegated hop's own `scope` caveats are restrictions, never grants: reading them as positive attributes would let a
26
+ * delegatee append `scope:admin` and obtain a key for it. A delegated capability (one with a `parent`) therefore yields
27
+ * `scope:`/`act:`/`verb:`/`resource:` tokens only when the verified `chain` (root first, ending in that capability) is supplied, and then from
28
+ * `chain[0]` alone. Without a chain a delegated capability gets no `scope:` tokens; a supplied chain that fails
29
+ * verification yields NO attributes (fail closed).
19
30
  */
31
+ export interface CapabilityAttributeOpts {
32
+ /** The capability chain, root first, whose last element is the capability. Required for `scope:` tokens on a delegated hop. */
33
+ chain?: CapabilityChain;
34
+ /** The principal key the root must be issued by (recommended: pin it; defaults to the chain's own root issuer). */
35
+ principal?: string;
36
+ /** verifier-side PQ pins by Ed25519 identity (R3-01). A pure-PQ / hybrid ROOT is accepted only if the root issuer has a pin equal to its `pq_pk` (a signed binding from `pqPinFor`, or your own registry); without one it is refused. */
37
+ pqPins?: Readonly<Record<string, string>>;
38
+ }
20
39
  export declare const Attr: {
21
40
  readonly holder: (key: string) => string;
22
41
  readonly verb: (v: string) => string;
23
42
  readonly resource: (r: string) => string;
24
43
  readonly scope: (s: string) => string;
44
+ /** A bound (verb, resource) pair: only a predicate that grants exactly this verb ON exactly this resource yields it. */
45
+ readonly act: (verb: string, resource: string) => string;
25
46
  };
26
47
  /**
27
48
  * Derive the canonical attribute set a capability possesses. Deterministic and total (never throws): a
28
49
  * capability with no signed envelope still yields its `holder:` attribute (plus any scope caveats).
29
50
  */
30
- export declare function capabilityAttributes(cap: Capability): Set<string>;
51
+ export declare function capabilityAttributes(cap: Capability, opts?: CapabilityAttributeOpts): Set<string>;
@@ -3,29 +3,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Attr = void 0;
4
4
  exports.capabilityAttributes = capabilityAttributes;
5
5
  const pca_1 = require("@atlasauth/pca");
6
- /**
7
- * Canonical attribute strings derived from a real PCA capability (see `@atlasauth/pca`'s `Capability`
8
- * and the signed policy `Envelope`). An "attribute" is a lowercase `kind:value` token; a policy
9
- * references attributes by exactly these tokens, and `keygenForCapability` issues one BF-IBE decryption
10
- * key per token the capability carries.
11
- *
12
- * Four kinds are derived, matching the capability's authority surface (verb / resource / scopes /
13
- * holder):
14
- * - `holder:<b64u key>` the key the capability is bound to (`capability.holder`),
15
- * - `verb:<v>` every literal verb named in the signed envelope's predicates,
16
- * - `resource:<r>` every resource matcher named in those predicates,
17
- * - `scope:<s>` every scope carried in a `{ type:'scope', value }` or
18
- * `{ type:'scopes', values:[...] }` caveat.
19
- *
20
- * Because the envelope and caveats are SIGNED into the capability (and attenuation is append-only), the
21
- * attribute set a capability yields cannot be widened by a holder — it is exactly what the principal
22
- * authorised.
23
- */
24
6
  exports.Attr = {
25
7
  holder: (key) => `holder:${key}`,
26
8
  verb: (v) => `verb:${v}`,
27
9
  resource: (r) => `resource:${r}`,
28
10
  scope: (s) => `scope:${s}`,
11
+ /** A bound (verb, resource) pair: only a predicate that grants exactly this verb ON exactly this resource yields it. */
12
+ act: (verb, resource) => `act:${JSON.stringify([verb, resource])}`,
29
13
  };
30
14
  function addVerb(out, verb) {
31
15
  if (typeof verb === 'string')
@@ -35,6 +19,13 @@ function addVerb(out, verb) {
35
19
  if (typeof v === 'string')
36
20
  out.add(exports.Attr.verb(v));
37
21
  }
22
+ function verbsOf(verb) {
23
+ if (typeof verb === 'string')
24
+ return [verb];
25
+ if (Array.isArray(verb))
26
+ return verb.filter((v) => typeof v === 'string');
27
+ return [];
28
+ }
38
29
  function addScopes(out, caveats) {
39
30
  for (const cv of caveats) {
40
31
  if (cv.type === 'scope' && typeof cv.value === 'string')
@@ -50,21 +41,51 @@ function addScopes(out, caveats) {
50
41
  * Derive the canonical attribute set a capability possesses. Deterministic and total (never throws): a
51
42
  * capability with no signed envelope still yields its `holder:` attribute (plus any scope caveats).
52
43
  */
53
- function capabilityAttributes(cap) {
44
+ function capabilityAttributes(cap, opts = {}) {
54
45
  const out = new Set();
46
+ let scopeSource;
47
+ // R3-09: the envelope (act/verb/resource tokens) is read from the ROOT only, like scopes: a delegatee may append
48
+ // its own envelope caveat, which is a restriction, never a grant.
49
+ let envSource;
50
+ if (opts.chain !== undefined) {
51
+ const chain = opts.chain;
52
+ const root = chain[0];
53
+ if (!Array.isArray(chain) || chain.length === 0 || chain[chain.length - 1]?.id !== cap.id || root === undefined)
54
+ return out;
55
+ const v = (0, pca_1.verifyChain)(chain, opts.principal ?? root.issuer, { pqPins: opts.pqPins });
56
+ if (!v.ok)
57
+ return out;
58
+ scopeSource = root.caveats;
59
+ envSource = root;
60
+ }
61
+ else if (cap.parent === undefined) {
62
+ scopeSource = cap.caveats; // a root capability
63
+ envSource = cap;
64
+ }
55
65
  if (typeof cap.holder === 'string' && cap.holder.length > 0)
56
66
  out.add(exports.Attr.holder(cap.holder));
57
- if (Array.isArray(cap.caveats))
58
- addScopes(out, cap.caveats);
59
- const env = (0, pca_1.readEnvelope)(cap);
67
+ if (Array.isArray(scopeSource))
68
+ addScopes(out, scopeSource);
69
+ const env = envSource === undefined ? null : (0, pca_1.readEnvelope)(envSource);
60
70
  if (env !== null && Array.isArray(env.predicates)) {
61
- for (const p of env.predicates) {
62
- if (p === null || typeof p !== 'object')
63
- continue;
64
- addVerb(out, p.verb);
71
+ const preds = env.predicates.filter((p) => p !== null && typeof p === 'object');
72
+ // Paired tokens: one per (verb, resource) of each predicate. These are the ONLY tokens that
73
+ // carry authority when the envelope holds more than one predicate (RT-08).
74
+ for (const p of preds) {
75
+ const verbs = verbsOf(p.verb);
65
76
  const r = p.resource;
66
77
  if (typeof r === 'string')
67
- out.add(exports.Attr.resource(r));
78
+ for (const v of verbs)
79
+ out.add(exports.Attr.act(v, r));
80
+ }
81
+ // Flat verb:/resource: tokens are exact only for a single predicate (verbs x ONE resource), so
82
+ // they are emitted only then; with several predicates they would cross-pair (read of A + write
83
+ // of B would also open "read B").
84
+ if (preds.length === 1) {
85
+ const p = preds[0];
86
+ addVerb(out, p.verb);
87
+ if (typeof p.resource === 'string')
88
+ out.add(exports.Attr.resource(p.resource));
68
89
  }
69
90
  }
70
91
  return out;
@@ -1 +1 @@
1
- {"version":3,"file":"attributes.js","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":";;;AA8CA,oDAcC;AA5DD,wCAA+D;AAE/D;;;;;;;;;;;;;;;;;GAiBG;AAEU,QAAA,IAAI,GAAG;IAClB,MAAM,EAAE,CAAC,GAAW,EAAU,EAAE,CAAC,UAAU,GAAG,EAAE;IAChD,IAAI,EAAE,CAAC,CAAS,EAAU,EAAE,CAAC,QAAQ,CAAC,EAAE;IACxC,QAAQ,EAAE,CAAC,CAAS,EAAU,EAAE,CAAC,YAAY,CAAC,EAAE;IAChD,KAAK,EAAE,CAAC,CAAS,EAAU,EAAE,CAAC,SAAS,CAAC,EAAE;CAClC,CAAC;AAEX,SAAS,OAAO,CAAC,GAAgB,EAAE,IAAa;IAC9C,IAAI,OAAO,IAAI,KAAK,QAAQ;QAAE,GAAG,CAAC,GAAG,CAAC,YAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;SAClD,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC;QAAE,KAAK,MAAM,CAAC,IAAI,IAAI;YAAE,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,GAAG,CAAC,GAAG,CAAC,YAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;AACvG,CAAC;AAED,SAAS,SAAS,CAAC,GAAgB,EAAE,OAA0D;IAC7F,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;QACzB,IAAI,EAAE,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,EAAE,CAAC,KAAK,KAAK,QAAQ;YAAE,GAAG,CAAC,GAAG,CAAC,YAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;QACvF,IAAI,EAAE,CAAC,IAAI,KAAK,QAAQ,IAAI,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;YACrD,KAAK,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM;gBAAE,IAAI,OAAO,CAAC,KAAK,QAAQ;oBAAE,GAAG,CAAC,GAAG,CAAC,YAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,SAAgB,oBAAoB,CAAC,GAAe;IAClD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,IAAI,OAAO,GAAG,CAAC,MAAM,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC;QAAE,GAAG,CAAC,GAAG,CAAC,YAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9F,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;QAAE,SAAS,CAAC,GAAG,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5D,MAAM,GAAG,GAAG,IAAA,kBAAY,EAAC,GAAG,CAAC,CAAC;IAC9B,IAAI,GAAG,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,CAAC;QAClD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;YAC/B,IAAI,CAAC,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,SAAS;YAClD,OAAO,CAAC,GAAG,EAAG,CAAwB,CAAC,IAAI,CAAC,CAAC;YAC7C,MAAM,CAAC,GAAI,CAA4B,CAAC,QAAQ,CAAC;YACjD,IAAI,OAAO,CAAC,KAAK,QAAQ;gBAAE,GAAG,CAAC,GAAG,CAAC,YAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
1
+ {"version":3,"file":"attributes.js","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":"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"}
package/dist/bls.d.ts CHANGED
@@ -4,17 +4,20 @@ import { bls12_381 } from '@noble/curves/bls12-381';
4
4
  * audited `bls12_381` implementation (G1/G2/GT, the ate pairing, RFC 9380 hash-to-curve); nothing here
5
5
  * re-implements field or point arithmetic.
6
6
  *
7
- * Group placement (type-3 / asymmetric pairing `e: G1 x G2 -> GT`):
8
- * - attribute identity points `Q_attr = H1(attr)` live in G1 (hash-to-curve),
9
- * - the master public `P_pub = s * g2` and ephemerals `U = x * g2` live in G2,
10
- * - decryption keys `d_attr = s * Q_attr` live in G1,
11
- * - KEM secrets live in GT.
7
+ * Group placement (type-3 / asymmetric pairing `e: G1 x G2 -> GT`), see `abe.ts` for the scheme:
8
+ * - the attribute hash `H(attr)` lives in G1 (hash-to-curve),
9
+ * - user-key components D, D_j live in G1 and D'_j in G2,
10
+ * - ciphertext components C_0 and C_x live in G2 and C'_x in G1,
11
+ * - the blinding value e(g1,g2)^alpha and every recovered quantity live in GT.
12
12
  */
13
13
  /** Curve subgroup (scalar field) order r — a 255-bit prime, so every scalar fits in 32 bytes. */
14
14
  export declare const R: bigint;
15
+ /** The fixed G1 generator the whole system is parameterised over. */
16
+ export declare const G1_BASE: import("@noble/curves/abstract/weierstrass").ProjPointType<bigint>;
15
17
  /** The fixed G2 generator the whole system is parameterised over. */
16
18
  export declare const G2_BASE: import("@noble/curves/abstract/weierstrass").ProjPointType<import("@noble/curves/abstract/tower").Fp2>;
17
19
  export type G1Point = ReturnType<typeof bls12_381.G1.ProjectivePoint.fromAffine>;
20
+ export type GtElement = ReturnType<typeof bls12_381.pairing>;
18
21
  export type G2Point = ReturnType<typeof bls12_381.G2.ProjectivePoint.fromAffine>;
19
22
  /** Reduce a bigint into the canonical scalar field Z_r. */
20
23
  export declare function frCreate(n: bigint): bigint;
@@ -28,7 +31,13 @@ export declare function randomScalar(): bigint;
28
31
  export declare function scalarToBytes(v: bigint): Uint8Array;
29
32
  /** Decode a 32-byte big-endian scalar back into Z_r. Returns null if not exactly 32 bytes. */
30
33
  export declare function bytesToScalar(bytes: Uint8Array): bigint | null;
31
- /** Hash an attribute string to its identity point `Q_attr` in G1 (prime-order subgroup, RO model). */
34
+ /** True iff `s` contains an unpaired UTF-16 surrogate (such strings alias each other once UTF-8 encoded). */
35
+ export declare function isIllFormedUtf16(s: string): boolean;
36
+ /**
37
+ * Hash an attribute string to its identity point `Q_attr` in G1 (prime-order subgroup, RO model).
38
+ * Throws on ill-formed UTF-16: `TextEncoder` maps every lone surrogate to U+FFFD, so two different JS strings
39
+ * would otherwise hash to the SAME identity and a key issued for one would open ciphertexts for the other.
40
+ */
32
41
  export declare function hashAttrToG1(attr: string): G1Point;
33
42
  /** Scalar-multiply a G1 point. */
34
43
  export declare function g1Mul(p: G1Point, k: bigint): G1Point;
@@ -38,15 +47,29 @@ export declare function g2Mul(p: G2Point, k: bigint): G2Point;
38
47
  export declare function g1ToBytes(p: G1Point): Uint8Array;
39
48
  /** Serialise a G2 point (compressed, 96 bytes). */
40
49
  export declare function g2ToBytes(p: G2Point): Uint8Array;
41
- /** Parse a compressed G1 point; null (never throws) on any malformed / off-curve / wrong-subgroup input. */
50
+ /**
51
+ * Parse a G1 point; null (never throws) on any malformed / off-curve / wrong-subgroup input AND on the point at
52
+ * infinity (no honest key, ephemeral or public element is ever the identity, and pairing with it is undefined).
53
+ */
42
54
  export declare function g1FromBytes(bytes: Uint8Array): G1Point | null;
43
- /** Parse a compressed G2 point; null (never throws) on any malformed / off-curve / wrong-subgroup input. */
55
+ /** Parse a G2 point; null (never throws) on any malformed / off-curve / wrong-subgroup / identity input. */
44
56
  export declare function g2FromBytes(bytes: Uint8Array): G2Point | null;
45
57
  /** The (final-exponentiated) ate pairing `e(P, Q)` in GT. */
46
- export declare function pairing(p: G1Point, q: G2Point): ReturnType<typeof bls12_381.pairing>;
58
+ export declare function pairing(p: G1Point, q: G2Point): GtElement;
47
59
  /** Raise a GT element to a scalar power. */
48
- export declare function gtPow(gt: ReturnType<typeof bls12_381.pairing>, k: bigint): ReturnType<typeof bls12_381.pairing>;
49
- /** Canonical byte serialisation of a GT element (576 bytes) — the KEM secret material fed into the KDF. */
50
- export declare function gtToBytes(gt: ReturnType<typeof bls12_381.pairing>): Uint8Array;
60
+ export declare function gtPow(gt: GtElement, k: bigint): GtElement;
61
+ /** Product of pairings `prod e(P_i, Q_i)` with ONE shared final exponentiation (a multi-Miller loop). Throws on an identity point. */
62
+ export declare function pairingProduct(pairs: readonly {
63
+ g1: G1Point;
64
+ g2: G2Point;
65
+ }[]): GtElement;
66
+ export declare function gtEql(a: GtElement, b: GtElement): boolean;
67
+ /**
68
+ * Parse a GT element (576 bytes). Null unless the bytes are the canonical encoding of an element of the
69
+ * prime-order subgroup GT (x^r = 1) other than the identity.
70
+ */
71
+ export declare function gtFromBytes(bytes: Uint8Array): GtElement | null;
72
+ /** Canonical byte serialisation of a GT element (576 bytes) — the secret material fed into the KDF. */
73
+ export declare function gtToBytes(gt: GtElement): Uint8Array;
51
74
  /** HKDF-SHA256 to exactly 32 bytes, with explicit salt + info domain separation. */
52
75
  export declare function kdf32(ikm: Uint8Array, salt: Uint8Array, info: string): Uint8Array;
package/dist/bls.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.G2_BASE = exports.R = void 0;
3
+ exports.G2_BASE = exports.G1_BASE = exports.R = void 0;
4
4
  exports.frCreate = frCreate;
5
5
  exports.frAdd = frAdd;
6
6
  exports.frSub = frSub;
@@ -9,6 +9,7 @@ exports.frInv = frInv;
9
9
  exports.randomScalar = randomScalar;
10
10
  exports.scalarToBytes = scalarToBytes;
11
11
  exports.bytesToScalar = bytesToScalar;
12
+ exports.isIllFormedUtf16 = isIllFormedUtf16;
12
13
  exports.hashAttrToG1 = hashAttrToG1;
13
14
  exports.g1Mul = g1Mul;
14
15
  exports.g2Mul = g2Mul;
@@ -18,6 +19,9 @@ exports.g1FromBytes = g1FromBytes;
18
19
  exports.g2FromBytes = g2FromBytes;
19
20
  exports.pairing = pairing;
20
21
  exports.gtPow = gtPow;
22
+ exports.pairingProduct = pairingProduct;
23
+ exports.gtEql = gtEql;
24
+ exports.gtFromBytes = gtFromBytes;
21
25
  exports.gtToBytes = gtToBytes;
22
26
  exports.kdf32 = kdf32;
23
27
  const bls12_381_1 = require("@noble/curves/bls12-381");
@@ -30,18 +34,21 @@ const utils_2 = require("@noble/hashes/utils");
30
34
  * audited `bls12_381` implementation (G1/G2/GT, the ate pairing, RFC 9380 hash-to-curve); nothing here
31
35
  * re-implements field or point arithmetic.
32
36
  *
33
- * Group placement (type-3 / asymmetric pairing `e: G1 x G2 -> GT`):
34
- * - attribute identity points `Q_attr = H1(attr)` live in G1 (hash-to-curve),
35
- * - the master public `P_pub = s * g2` and ephemerals `U = x * g2` live in G2,
36
- * - decryption keys `d_attr = s * Q_attr` live in G1,
37
- * - KEM secrets live in GT.
37
+ * Group placement (type-3 / asymmetric pairing `e: G1 x G2 -> GT`), see `abe.ts` for the scheme:
38
+ * - the attribute hash `H(attr)` lives in G1 (hash-to-curve),
39
+ * - user-key components D, D_j live in G1 and D'_j in G2,
40
+ * - ciphertext components C_0 and C_x live in G2 and C'_x in G1,
41
+ * - the blinding value e(g1,g2)^alpha and every recovered quantity live in GT.
38
42
  */
39
43
  /** Curve subgroup (scalar field) order r — a 255-bit prime, so every scalar fits in 32 bytes. */
40
44
  exports.R = bls12_381_1.bls12_381.params.r;
41
45
  const Fr = bls12_381_1.bls12_381.fields.Fr;
46
+ const Fp = bls12_381_1.bls12_381.fields.Fp;
42
47
  const Fp12 = bls12_381_1.bls12_381.fields.Fp12;
43
48
  /** Domain-separation tag for hashing an attribute string to a point in G1 (RFC 9380 SSWU_RO). */
44
- const ATTR_DST = 'ATLAS-PCA-ABE:BF-IBE:G1:SHA256-SSWU-RO:v1';
49
+ const ATTR_DST = 'ATLAS-PCA-ABE:BSW07:H-G1:SHA256-SSWU-RO:v2';
50
+ /** The fixed G1 generator the whole system is parameterised over. */
51
+ exports.G1_BASE = bls12_381_1.bls12_381.G1.ProjectivePoint.BASE;
45
52
  /** The fixed G2 generator the whole system is parameterised over. */
46
53
  exports.G2_BASE = bls12_381_1.bls12_381.G2.ProjectivePoint.BASE;
47
54
  // ---- field scalars (Z_r) --------------------------------------------------------------------
@@ -80,8 +87,18 @@ function bytesToScalar(bytes) {
80
87
  return Fr.create((0, utils_1.bytesToNumberBE)(bytes));
81
88
  }
82
89
  // ---- points ---------------------------------------------------------------------------------
83
- /** Hash an attribute string to its identity point `Q_attr` in G1 (prime-order subgroup, RO model). */
90
+ /** True iff `s` contains an unpaired UTF-16 surrogate (such strings alias each other once UTF-8 encoded). */
91
+ function isIllFormedUtf16(s) {
92
+ return /[\ud800-\udbff](?![\udc00-\udfff])|(?<![\ud800-\udbff])[\udc00-\udfff]/.test(s);
93
+ }
94
+ /**
95
+ * Hash an attribute string to its identity point `Q_attr` in G1 (prime-order subgroup, RO model).
96
+ * Throws on ill-formed UTF-16: `TextEncoder` maps every lone surrogate to U+FFFD, so two different JS strings
97
+ * would otherwise hash to the SAME identity and a key issued for one would open ciphertexts for the other.
98
+ */
84
99
  function hashAttrToG1(attr) {
100
+ if (isIllFormedUtf16(attr))
101
+ throw new RangeError('attribute is not well-formed UTF-16');
85
102
  const msg = new TextEncoder().encode(attr);
86
103
  const h = bls12_381_1.bls12_381.G1.hashToCurve(msg, { DST: ATTR_DST });
87
104
  // Normalise the hash-to-curve result into a ProjectivePoint so the full (de)serialisation API is available.
@@ -103,22 +120,41 @@ function g1ToBytes(p) {
103
120
  function g2ToBytes(p) {
104
121
  return p.toRawBytes(true);
105
122
  }
106
- /** Parse a compressed G1 point; null (never throws) on any malformed / off-curve / wrong-subgroup input. */
123
+ function bytesEqual(a, b) {
124
+ if (a.length !== b.length)
125
+ return false;
126
+ let d = 0;
127
+ for (let i = 0; i < a.length; i++)
128
+ d |= (a[i] ?? 0) ^ (b[i] ?? 0);
129
+ return d === 0;
130
+ }
131
+ /**
132
+ * Parse a G1 point; null (never throws) on any malformed / off-curve / wrong-subgroup input AND on the point at
133
+ * infinity (no honest key, ephemeral or public element is ever the identity, and pairing with it is undefined).
134
+ */
107
135
  function g1FromBytes(bytes) {
108
136
  try {
109
137
  const p = bls12_381_1.bls12_381.G1.ProjectivePoint.fromHex(bytes);
110
138
  p.assertValidity();
139
+ if (p.equals(bls12_381_1.bls12_381.G1.ProjectivePoint.ZERO))
140
+ return null;
141
+ if (!bytesEqual(p.toRawBytes(true), bytes))
142
+ return null; // strict: only the canonical compressed encoding
111
143
  return p;
112
144
  }
113
145
  catch {
114
146
  return null;
115
147
  }
116
148
  }
117
- /** Parse a compressed G2 point; null (never throws) on any malformed / off-curve / wrong-subgroup input. */
149
+ /** Parse a G2 point; null (never throws) on any malformed / off-curve / wrong-subgroup / identity input. */
118
150
  function g2FromBytes(bytes) {
119
151
  try {
120
152
  const p = bls12_381_1.bls12_381.G2.ProjectivePoint.fromHex(bytes);
121
153
  p.assertValidity();
154
+ if (p.equals(bls12_381_1.bls12_381.G2.ProjectivePoint.ZERO))
155
+ return null;
156
+ if (!bytesEqual(p.toRawBytes(true), bytes))
157
+ return null; // strict: only the canonical compressed encoding
122
158
  return p;
123
159
  }
124
160
  catch {
@@ -134,7 +170,41 @@ function pairing(p, q) {
134
170
  function gtPow(gt, k) {
135
171
  return Fp12.pow(gt, Fr.create(k));
136
172
  }
137
- /** Canonical byte serialisation of a GT element (576 bytes) — the KEM secret material fed into the KDF. */
173
+ /** Product of pairings `prod e(P_i, Q_i)` with ONE shared final exponentiation (a multi-Miller loop). Throws on an identity point. */
174
+ function pairingProduct(pairs) {
175
+ return bls12_381_1.bls12_381.pairingBatch([...pairs], true);
176
+ }
177
+ function gtEql(a, b) {
178
+ return Fp12.eql(a, b);
179
+ }
180
+ /**
181
+ * Parse a GT element (576 bytes). Null unless the bytes are the canonical encoding of an element of the
182
+ * prime-order subgroup GT (x^r = 1) other than the identity.
183
+ */
184
+ function gtFromBytes(bytes) {
185
+ if (bytes.length !== 576)
186
+ return null;
187
+ // @noble's Fp12.fromBytes does NOT reduce coordinates: a coordinate c + p parses as a distinct, structurally
188
+ // unequal value that still behaves as c in arithmetic (so 1 + p would slip past the identity test below) and
189
+ // re-serialises to the same unreduced bytes (so the round-trip check cannot see it). Require every Fp coordinate < p.
190
+ for (let off = 0; off < 576; off += 48)
191
+ if ((0, utils_1.bytesToNumberBE)(bytes.subarray(off, off + 48)) >= Fp.ORDER)
192
+ return null;
193
+ try {
194
+ const x = Fp12.fromBytes(bytes);
195
+ if (Fp12.eql(x, Fp12.ONE))
196
+ return null;
197
+ if (!Fp12.eql(Fp12.pow(x, exports.R), Fp12.ONE))
198
+ return null;
199
+ if (!bytesEqual(Fp12.toBytes(x), bytes))
200
+ return null;
201
+ return x;
202
+ }
203
+ catch {
204
+ return null;
205
+ }
206
+ }
207
+ /** Canonical byte serialisation of a GT element (576 bytes) — the secret material fed into the KDF. */
138
208
  function gtToBytes(gt) {
139
209
  return Fp12.toBytes(gt);
140
210
  }
package/dist/bls.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bls.js","sourceRoot":"","sources":["../src/bls.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bls.js","sourceRoot":"","sources":["../src/bls.ts"],"names":[],"mappings":";;;AAyCA,4BAEC;AAED,sBAEC;AAED,sBAEC;AAED,sBAEC;AAED,sBAEC;AAGD,oCAKC;AAGD,sCAEC;AAGD,sCAGC;AAKD,4CAEC;AAOD,oCAMC;AAGD,sBAEC;AAGD,sBAEC;AAGD,8BAEC;AAGD,8BAEC;AAaD,kCAUC;AAGD,kCAUC;AAKD,0BAEC;AAGD,sBAEC;AAGD,wCAEC;AAED,sBAEC;AAMD,kCAeC;AAGD,8BAEC;AAKD,sBAEC;AAhND,uDAAoD;AACpD,wDAAgF;AAChF,6CAA0C;AAC1C,iDAA8C;AAC9C,+CAAkD;AAElD;;;;;;;;;;GAUG;AAEH,iGAAiG;AACpF,QAAA,CAAC,GAAW,qBAAS,CAAC,MAAM,CAAC,CAAC,CAAC;AAE5C,MAAM,EAAE,GAAG,qBAAS,CAAC,MAAM,CAAC,EAAE,CAAC;AAC/B,MAAM,EAAE,GAAG,qBAAS,CAAC,MAAM,CAAC,EAAE,CAAC;AAC/B,MAAM,IAAI,GAAG,qBAAS,CAAC,MAAM,CAAC,IAAI,CAAC;AAEnC,iGAAiG;AACjG,MAAM,QAAQ,GAAG,4CAA4C,CAAC;AAE9D,qEAAqE;AACxD,QAAA,OAAO,GAAG,qBAAS,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;AAEzD,qEAAqE;AACxD,QAAA,OAAO,GAAG,qBAAS,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;AAMzD,gGAAgG;AAEhG,2DAA2D;AAC3D,SAAgB,QAAQ,CAAC,CAAS;IAChC,OAAO,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;AACtB,CAAC;AAED,SAAgB,KAAK,CAAC,CAAS,EAAE,CAAS;IACxC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACtB,CAAC;AAED,SAAgB,KAAK,CAAC,CAAS,EAAE,CAAS;IACxC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACtB,CAAC;AAED,SAAgB,KAAK,CAAC,CAAS,EAAE,CAAS;IACxC,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACtB,CAAC;AAED,SAAgB,KAAK,CAAC,CAAS;IAC7B,OAAO,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AACnB,CAAC;AAED,uGAAuG;AACvG,SAAgB,YAAY;IAC1B,SAAS,CAAC;QACR,MAAM,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAA,uBAAe,EAAC,IAAA,mBAAW,EAAC,EAAE,CAAC,CAAC,CAAC,CAAC;QACtD,IAAI,CAAC,KAAK,EAAE;YAAE,OAAO,CAAC,CAAC;IACzB,CAAC;AACH,CAAC;AAED,6FAA6F;AAC7F,SAAgB,aAAa,CAAC,CAAS;IACrC,OAAO,IAAA,uBAAe,EAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC3C,CAAC;AAED,8FAA8F;AAC9F,SAAgB,aAAa,CAAC,KAAiB;IAC7C,IAAI,KAAK,CAAC,MAAM,KAAK,EAAE;QAAE,OAAO,IAAI,CAAC;IACrC,OAAO,EAAE,CAAC,MAAM,CAAC,IAAA,uBAAe,EAAC,KAAK,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED,gGAAgG;AAEhG,6GAA6G;AAC7G,SAAgB,gBAAgB,CAAC,CAAS;IACxC,OAAO,wEAAwE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAC1F,CAAC;AAED;;;;GAIG;AACH,SAAgB,YAAY,CAAC,IAAY;IACvC,IAAI,gBAAgB,CAAC,IAAI,CAAC;QAAE,MAAM,IAAI,UAAU,CAAC,qCAAqC,CAAC,CAAC;IACxF,MAAM,GAAG,GAAG,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC3C,MAAM,CAAC,GAAG,qBAAS,CAAC,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;IAC3D,4GAA4G;IAC5G,OAAO,qBAAS,CAAC,EAAE,CAAC,eAAe,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC;AAC/D,CAAC;AAED,kCAAkC;AAClC,SAAgB,KAAK,CAAC,CAAU,EAAE,CAAS;IACzC,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,kCAAkC;AAClC,SAAgB,KAAK,CAAC,CAAU,EAAE,CAAS;IACzC,OAAO,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,mDAAmD;AACnD,SAAgB,SAAS,CAAC,CAAU;IAClC,OAAO,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,mDAAmD;AACnD,SAAgB,SAAS,CAAC,CAAU;IAClC,OAAO,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AAC5B,CAAC;AAED,SAAS,UAAU,CAAC,CAAa,EAAE,CAAa;IAC9C,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACxC,IAAI,CAAC,GAAG,CAAC,CAAC;IACV,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE;QAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;IAClE,OAAO,CAAC,KAAK,CAAC,CAAC;AACjB,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,KAAiB;IAC3C,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,qBAAS,CAAC,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACtD,CAAC,CAAC,cAAc,EAAE,CAAC;QACnB,IAAI,CAAC,CAAC,MAAM,CAAC,qBAAS,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7D,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,iDAAiD;QAC1G,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,4GAA4G;AAC5G,SAAgB,WAAW,CAAC,KAAiB;IAC3C,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,qBAAS,CAAC,EAAE,CAAC,eAAe,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QACtD,CAAC,CAAC,cAAc,EAAE,CAAC;QACnB,IAAI,CAAC,CAAC,MAAM,CAAC,qBAAS,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;YAAE,OAAO,IAAI,CAAC;QAC7D,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,CAAC,iDAAiD;QAC1G,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,gGAAgG;AAEhG,6DAA6D;AAC7D,SAAgB,OAAO,CAAC,CAAU,EAAE,CAAU;IAC5C,OAAO,qBAAS,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;AACvC,CAAC;AAED,4CAA4C;AAC5C,SAAgB,KAAK,CAAC,EAAa,EAAE,CAAS;IAC5C,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,sIAAsI;AACtI,SAAgB,cAAc,CAAC,KAA8C;IAC3E,OAAO,qBAAS,CAAC,YAAY,CAAC,CAAC,GAAG,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;AAClD,CAAC;AAED,SAAgB,KAAK,CAAC,CAAY,EAAE,CAAY;IAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AACxB,CAAC;AAED;;;GAGG;AACH,SAAgB,WAAW,CAAC,KAAiB;IAC3C,IAAI,KAAK,CAAC,MAAM,KAAK,GAAG;QAAE,OAAO,IAAI,CAAC;IACtC,6GAA6G;IAC7G,6GAA6G;IAC7G,sHAAsH;IACtH,KAAK,IAAI,GAAG,GAAG,CAAC,EAAE,GAAG,GAAG,GAAG,EAAE,GAAG,IAAI,EAAE;QAAE,IAAI,IAAA,uBAAe,EAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;IACpH,IAAI,CAAC;QACH,MAAM,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACvC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,SAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACrD,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC;QACrD,OAAO,CAAC,CAAC;IACX,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,uGAAuG;AACvG,SAAgB,SAAS,CAAC,EAAa;IACrC,OAAO,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED,gGAAgG;AAEhG,oFAAoF;AACpF,SAAgB,KAAK,CAAC,GAAe,EAAE,IAAgB,EAAE,IAAY;IACnE,OAAO,IAAA,WAAI,EAAC,eAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC;AACrE,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  /**
2
2
  * @atlasauth/pca-abe — proof-carrying encryption.
3
3
  *
4
- * Ciphertext-policy attribute/policy-based encryption on BLS12-381 (a Boneh-Franklin IBE KEM composed
5
- * over a monotone access structure by Shamir secret sharing, sealed with AES-256-GCM) so a tool payload
6
- * or result decrypts ONLY for a holder whose PCA capability satisfies the policy. See `abe.ts` for the
4
+ * Collusion-resistant ciphertext-policy attribute-based encryption on BLS12-381 (Bethencourt-Sahai-Waters
5
+ * CP-ABE with a per-user key randomiser, type-3 pairing adaptation, Shamir access tree, AES-256-GCM) so a
6
+ * tool payload or result decrypts ONLY for a holder whose PCA capability satisfies the policy. See `abe.ts` for the
7
7
  * full construction and security property.
8
8
  */
9
- export { ABE_ALG, PcaEncryptionError, setup, keygenForAttributes, keygenForCapability, encryptForPolicy, decrypt, decryptText, parseCiphertext, type MasterPublicKey, type MasterSecretKey, type DecryptionKey, type AbeCiphertext, type KemTree, } from './abe';
9
+ export { ABE_ALG, PcaEncryptionError, setup, keygenForAttributes, keygenForCapability, rerandomizeKey, encryptForPolicy, decrypt, decryptText, parseCiphertext, type MasterPublicKey, type MasterSecretKey, type DecryptionKey, type AbeCiphertext, type AttributeKey, type KemTree, } from './abe';
10
10
  export { encryptToolPayload, decryptWithCapability, type DecryptedPayload } from './pca';
11
- export { normalizePolicy, satisfies, policyAttributes, MAX_POLICY_NODES, type Policy, type ThresholdTree, type PolicyResult, } from './policy';
12
- export { Attr, capabilityAttributes } from './attributes';
11
+ export { normalizePolicy, satisfies, policyAttributes, MAX_POLICY_NODES, MAX_POLICY_DEPTH, type Policy, type ThresholdTree, type PolicyResult, } from './policy';
12
+ export { Attr, capabilityAttributes, type CapabilityAttributeOpts } from './attributes';
package/dist/index.js CHANGED
@@ -2,19 +2,20 @@
2
2
  /**
3
3
  * @atlasauth/pca-abe — proof-carrying encryption.
4
4
  *
5
- * Ciphertext-policy attribute/policy-based encryption on BLS12-381 (a Boneh-Franklin IBE KEM composed
6
- * over a monotone access structure by Shamir secret sharing, sealed with AES-256-GCM) so a tool payload
7
- * or result decrypts ONLY for a holder whose PCA capability satisfies the policy. See `abe.ts` for the
5
+ * Collusion-resistant ciphertext-policy attribute-based encryption on BLS12-381 (Bethencourt-Sahai-Waters
6
+ * CP-ABE with a per-user key randomiser, type-3 pairing adaptation, Shamir access tree, AES-256-GCM) so a
7
+ * tool payload or result decrypts ONLY for a holder whose PCA capability satisfies the policy. See `abe.ts` for the
8
8
  * full construction and security property.
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.capabilityAttributes = exports.Attr = exports.MAX_POLICY_NODES = exports.policyAttributes = exports.satisfies = exports.normalizePolicy = exports.decryptWithCapability = exports.encryptToolPayload = exports.parseCiphertext = exports.decryptText = exports.decrypt = exports.encryptForPolicy = exports.keygenForCapability = exports.keygenForAttributes = exports.setup = exports.PcaEncryptionError = exports.ABE_ALG = void 0;
11
+ exports.capabilityAttributes = exports.Attr = exports.MAX_POLICY_DEPTH = exports.MAX_POLICY_NODES = exports.policyAttributes = exports.satisfies = exports.normalizePolicy = exports.decryptWithCapability = exports.encryptToolPayload = exports.parseCiphertext = exports.decryptText = exports.decrypt = exports.encryptForPolicy = exports.rerandomizeKey = exports.keygenForCapability = exports.keygenForAttributes = exports.setup = exports.PcaEncryptionError = exports.ABE_ALG = void 0;
12
12
  var abe_1 = require("./abe");
13
13
  Object.defineProperty(exports, "ABE_ALG", { enumerable: true, get: function () { return abe_1.ABE_ALG; } });
14
14
  Object.defineProperty(exports, "PcaEncryptionError", { enumerable: true, get: function () { return abe_1.PcaEncryptionError; } });
15
15
  Object.defineProperty(exports, "setup", { enumerable: true, get: function () { return abe_1.setup; } });
16
16
  Object.defineProperty(exports, "keygenForAttributes", { enumerable: true, get: function () { return abe_1.keygenForAttributes; } });
17
17
  Object.defineProperty(exports, "keygenForCapability", { enumerable: true, get: function () { return abe_1.keygenForCapability; } });
18
+ Object.defineProperty(exports, "rerandomizeKey", { enumerable: true, get: function () { return abe_1.rerandomizeKey; } });
18
19
  Object.defineProperty(exports, "encryptForPolicy", { enumerable: true, get: function () { return abe_1.encryptForPolicy; } });
19
20
  Object.defineProperty(exports, "decrypt", { enumerable: true, get: function () { return abe_1.decrypt; } });
20
21
  Object.defineProperty(exports, "decryptText", { enumerable: true, get: function () { return abe_1.decryptText; } });
@@ -27,6 +28,7 @@ Object.defineProperty(exports, "normalizePolicy", { enumerable: true, get: funct
27
28
  Object.defineProperty(exports, "satisfies", { enumerable: true, get: function () { return policy_1.satisfies; } });
28
29
  Object.defineProperty(exports, "policyAttributes", { enumerable: true, get: function () { return policy_1.policyAttributes; } });
29
30
  Object.defineProperty(exports, "MAX_POLICY_NODES", { enumerable: true, get: function () { return policy_1.MAX_POLICY_NODES; } });
31
+ Object.defineProperty(exports, "MAX_POLICY_DEPTH", { enumerable: true, get: function () { return policy_1.MAX_POLICY_DEPTH; } });
30
32
  var attributes_1 = require("./attributes");
31
33
  Object.defineProperty(exports, "Attr", { enumerable: true, get: function () { return attributes_1.Attr; } });
32
34
  Object.defineProperty(exports, "capabilityAttributes", { enumerable: true, get: function () { return attributes_1.capabilityAttributes; } });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,6BAee;AAdb,8FAAA,OAAO,OAAA;AACP,yGAAA,kBAAkB,OAAA;AAClB,4FAAA,KAAK,OAAA;AACL,0GAAA,mBAAmB,OAAA;AACnB,0GAAA,mBAAmB,OAAA;AACnB,uGAAA,gBAAgB,OAAA;AAChB,8FAAA,OAAO,OAAA;AACP,kGAAA,WAAW,OAAA;AACX,sGAAA,eAAe,OAAA;AAQjB,6BAAyF;AAAhF,yGAAA,kBAAkB,OAAA;AAAE,4GAAA,qBAAqB,OAAA;AAElD,mCAQkB;AAPhB,yGAAA,eAAe,OAAA;AACf,mGAAA,SAAS,OAAA;AACT,0GAAA,gBAAgB,OAAA;AAChB,0GAAA,gBAAgB,OAAA;AAMlB,2CAA0D;AAAjD,kGAAA,IAAI,OAAA;AAAE,kHAAA,oBAAoB,OAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,6BAiBe;AAhBb,8FAAA,OAAO,OAAA;AACP,yGAAA,kBAAkB,OAAA;AAClB,4FAAA,KAAK,OAAA;AACL,0GAAA,mBAAmB,OAAA;AACnB,0GAAA,mBAAmB,OAAA;AACnB,qGAAA,cAAc,OAAA;AACd,uGAAA,gBAAgB,OAAA;AAChB,8FAAA,OAAO,OAAA;AACP,kGAAA,WAAW,OAAA;AACX,sGAAA,eAAe,OAAA;AASjB,6BAAyF;AAAhF,yGAAA,kBAAkB,OAAA;AAAE,4GAAA,qBAAqB,OAAA;AAElD,mCASkB;AARhB,yGAAA,eAAe,OAAA;AACf,mGAAA,SAAS,OAAA;AACT,0GAAA,gBAAgB,OAAA;AAChB,0GAAA,gBAAgB,OAAA;AAChB,0GAAA,gBAAgB,OAAA;AAMlB,2CAAwF;AAA/E,kGAAA,IAAI,OAAA;AAAE,kHAAA,oBAAoB,OAAA"}
package/dist/pca.d.ts CHANGED
@@ -25,8 +25,9 @@ import type { Policy } from './policy';
25
25
  * derive from the same signed capability. Encryption adds CONFIDENTIALITY (a passive observer, or a
26
26
  * holder of a different/weaker capability, learns nothing) on top of the PCActn's AUTHORISATION.
27
27
  *
28
- * SECURITY: as in `abe.ts` — confidentiality under Bilinear Diffie-Hellman (ROM), integrity under
29
- * AES-256-GCM. Not a NIZK; collusion resistance is within a single issued key (one key per capability).
28
+ * SECURITY: as in `abe.ts` — BSW07 CP-ABE (selective security in the paper's generic-group model, not
29
+ * re-proved for this type-3 adaptation), integrity under AES-256-GCM. Not a NIZK. Keys carry a per-key
30
+ * randomiser, so keys issued to different agents cannot be pooled to satisfy a policy neither satisfies.
30
31
  */
31
32
  /** Result of a payload decryption attempt. `ok:false` means the key's authority did not satisfy the policy (or the ciphertext was tampered/malformed). */
32
33
  export type DecryptedPayload = {
package/dist/pca.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"pca.js","sourceRoot":"","sources":["../src/pca.ts"],"names":[],"mappings":";;AA6CA,gDAOC;AAMD,sDAQC;AAlED,+BAMe;AAmCf;;;GAGG;AACH,SAAgB,kBAAkB,CAChC,GAAoB,EACpB,wBAAgC,EAChC,OAAgB;IAEhB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;IAC7C,OAAO,IAAA,sBAAgB,EAAC,GAAG,EAAE,wBAAwB,EAAE,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,SAAgB,qBAAqB,CAAC,GAAkB,EAAE,UAAmB;IAC3E,MAAM,EAAE,GAAG,IAAA,aAAO,EAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACpC,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kEAAkE,EAAE,CAAC;IAClH,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAY,EAAE,CAAC;IACpF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,qCAAqC,EAAE,CAAC;IACtE,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"pca.js","sourceRoot":"","sources":["../src/pca.ts"],"names":[],"mappings":";;AA8CA,gDAOC;AAMD,sDAQC;AAnED,+BAMe;AAoCf;;;GAGG;AACH,SAAgB,kBAAkB,CAChC,GAAoB,EACpB,wBAAgC,EAChC,OAAgB;IAEhB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,IAAI,IAAI,CAAC,CAAC;IAC7C,OAAO,IAAA,sBAAgB,EAAC,GAAG,EAAE,wBAAwB,EAAE,IAAI,CAAC,CAAC;AAC/D,CAAC;AAED;;;GAGG;AACH,SAAgB,qBAAqB,CAAC,GAAkB,EAAE,UAAmB;IAC3E,MAAM,EAAE,GAAG,IAAA,aAAO,EAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IACpC,IAAI,EAAE,KAAK,IAAI;QAAE,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,kEAAkE,EAAE,CAAC;IAClH,IAAI,CAAC;QACH,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,CAAY,EAAE,CAAC;IACpF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,qCAAqC,EAAE,CAAC;IACtE,CAAC;AACH,CAAC"}
package/dist/policy.d.ts CHANGED
@@ -40,6 +40,13 @@ export interface PolicyOk {
40
40
  export type PolicyResult = PolicyOk | PolicyError;
41
41
  /** Upper bound on total nodes in a policy tree (bounds work on untrusted input). */
42
42
  export declare const MAX_POLICY_NODES = 1024;
43
+ /**
44
+ * Upper bound on gate nesting along any root-to-leaf path. The ciphertext header is authenticated as canonical
45
+ * JSON, whose nesting is capped at 32 by the PCA canonicalizer; every gate level costs three JSON levels
46
+ * (gate object, `of` array, branch object) and the root sits at level 2, so 10 gate levels is the most that can
47
+ * ever be authenticated. Enforcing it here makes "encryptable" imply "decryptable".
48
+ */
49
+ export declare const MAX_POLICY_DEPTH = 10;
43
50
  /**
44
51
  * Validate and normalise an access structure into a threshold tree. Fails closed (returns a reason)
45
52
  * on any malformed node, empty gate, out-of-range threshold, or oversized tree.
package/dist/policy.js CHANGED
@@ -1,19 +1,27 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MAX_POLICY_NODES = void 0;
3
+ exports.MAX_POLICY_DEPTH = exports.MAX_POLICY_NODES = void 0;
4
4
  exports.normalizePolicy = normalizePolicy;
5
5
  exports.satisfies = satisfies;
6
6
  exports.policyAttributes = policyAttributes;
7
+ const bls_1 = require("./bls");
7
8
  const wire_1 = require("./wire");
8
9
  /** Upper bound on total nodes in a policy tree (bounds work on untrusted input). */
9
10
  exports.MAX_POLICY_NODES = 1024;
11
+ /**
12
+ * Upper bound on gate nesting along any root-to-leaf path. The ciphertext header is authenticated as canonical
13
+ * JSON, whose nesting is capped at 32 by the PCA canonicalizer; every gate level costs three JSON levels
14
+ * (gate object, `of` array, branch object) and the root sits at level 2, so 10 gate levels is the most that can
15
+ * ever be authenticated. Enforcing it here makes "encryptable" imply "decryptable".
16
+ */
17
+ exports.MAX_POLICY_DEPTH = 10;
10
18
  /**
11
19
  * Validate and normalise an access structure into a threshold tree. Fails closed (returns a reason)
12
20
  * on any malformed node, empty gate, out-of-range threshold, or oversized tree.
13
21
  */
14
22
  function normalizePolicy(policy) {
15
23
  let nodes = 0;
16
- function walk(p, path) {
24
+ function walk(p, path, gates) {
17
25
  if (++nodes > exports.MAX_POLICY_NODES)
18
26
  return { ok: false, reason: `policy too large (> ${exports.MAX_POLICY_NODES} nodes)` };
19
27
  if (!(0, wire_1.isObject)(p))
@@ -21,17 +29,26 @@ function normalizePolicy(policy) {
21
29
  if ('attr' in p) {
22
30
  if (!(0, wire_1.isString)(p.attr) || p.attr.length === 0)
23
31
  return { ok: false, reason: `${path}: 'attr' must be a non-empty string` };
32
+ if ((0, bls_1.isIllFormedUtf16)(p.attr))
33
+ return { ok: false, reason: `${path}: 'attr' is not well-formed UTF-16` };
24
34
  if ('allOf' in p || 'anyOf' in p || 'threshold' in p || 'of' in p) {
25
35
  return { ok: false, reason: `${path}: a leaf must not mix 'attr' with a gate` };
26
36
  }
27
37
  return { kind: 'leaf', attr: p.attr };
28
38
  }
39
+ const gateKeys = ['allOf', 'anyOf', 'threshold', 'of'].filter((k) => k in p).filter((k) => k !== 'of' || !('threshold' in p));
40
+ if (gateKeys.length > 1) {
41
+ return { ok: false, reason: `${path}: ambiguous node (${gateKeys.join(', ')}); a gate must use exactly one of allOf/anyOf/threshold` };
42
+ }
43
+ if (gateKeys.length === 1 && gates + 1 > exports.MAX_POLICY_DEPTH) {
44
+ return { ok: false, reason: `${path}: policy nested deeper than ${exports.MAX_POLICY_DEPTH} gates` };
45
+ }
29
46
  const parseChildren = (raw, key) => {
30
47
  if (!Array.isArray(raw) || raw.length === 0)
31
48
  return { ok: false, reason: `${path}: '${key}' must be a non-empty array` };
32
49
  const out = [];
33
50
  for (let i = 0; i < raw.length; i++) {
34
- const child = walk(raw[i], `${path}.${key}[${i}]`);
51
+ const child = walk(raw[i], `${path}.${key}[${i}]`, gates + 1);
35
52
  if ('ok' in child)
36
53
  return child;
37
54
  out.push(child);
@@ -63,7 +80,7 @@ function normalizePolicy(policy) {
63
80
  }
64
81
  return { ok: false, reason: `${path}: unknown policy node (expected attr/allOf/anyOf/threshold)` };
65
82
  }
66
- const tree = walk(policy, 'policy');
83
+ const tree = walk(policy, 'policy', 0);
67
84
  if ('ok' in tree)
68
85
  return tree;
69
86
  return { ok: true, tree };