@atlasauth/pca-abe 0.3.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @atlasauth/pca-abe
2
2
 
3
- Proof-carrying encryption for Proof-Carrying Authority (PCA). Tool payloads or results are sealed under an access policy over capability attributes (`holder:`, `verb:`, `resource:`, `scope:`), so they decrypt only for an agent whose PCA capability satisfies the policy. The construction is a Boneh-Franklin identity-based KEM on BLS12-381 composed over a monotone AND/OR/threshold tree by Shamir secret sharing, with the payload sealed by AES-256-GCM.
3
+ Proof-carrying encryption for Proof-Carrying Authority (PCA). Tool payloads or results are sealed under an access policy over capability attributes (`holder:`, `verb:`, `resource:`, `scope:`, and bound `act:` verb-on-resource pairs), so they decrypt only for an agent whose PCA capability satisfies the policy. The construction is Bethencourt-Sahai-Waters ciphertext-policy ABE (IEEE S&P 2007, ePrint 2006/309) with a per-key randomiser, adapted to the asymmetric (type-3) pairing on BLS12-381, over a monotone AND/OR/threshold tree shared by Shamir secret sharing, with the payload sealed by AES-256-GCM. Keys issued to different holders cannot be pooled to satisfy a policy neither satisfies.
4
4
 
5
5
  ## Install
6
6
 
@@ -46,15 +46,30 @@ A key derived from a capability that does not satisfy the policy gets `{ ok: fal
46
46
 
47
47
  ## API
48
48
 
49
- - Keys: `setup`, `keygenForAttributes`, `keygenForCapability`.
49
+ - Keys: `setup`, `keygenForAttributes`, `keygenForCapability`, `rerandomizeKey` (re-randomise a key, optionally to a subset of its attributes; needs only the public key).
50
50
  - Raw bytes/text: `encryptForPolicy`, `decrypt`, `decryptText`, `parseCiphertext`.
51
51
  - Tool payloads: `encryptToolPayload`, `decryptWithCapability`.
52
52
  - Policies: `Policy` (`{ attr }`, `{ allOf }`, `{ anyOf }`, `{ threshold, of }`), `normalizePolicy`, `satisfies`, `policyAttributes`.
53
- - Attributes: `Attr`, `capabilityAttributes`.
53
+ - Attributes: `Attr`, `capabilityAttributes`. A capability with ONE predicate yields flat `verb:`/`resource:` tokens plus `act:` pair tokens; a capability with TWO OR MORE predicates yields only `act:["verb","resource"]` pairs (plus `holder:`/`scope:`), so `read /public/*` + `write /acct/42` can never open a `read /acct/42` ciphertext (red-team RT-08; breaking: policies over flat tokens for multi-predicate capabilities must move to `Attr.act`). Predicate `where` conditions and other caveats are not encoded in any token. `scope:` tokens come only from the ROOT capability's caveats: a delegated hop's own `scope` caveats are restrictions, not grants (R2-07, breaking). For a delegated capability pass the chain, `capabilityAttributes(cap, { chain, principal })` / `keygenForCapability(msk, cap, [], { chain, principal })`; without a verified chain it gets no `scope:` tokens, and (R3-09, breaking) no `act:`/`verb:`/`resource:` tokens either, because the envelope is also read from the ROOT only (a delegatee-appended `envelope` caveat is a restriction and can no longer self-grant attributes); a chain that fails verification yields no attributes.
54
54
 
55
55
  ## Status
56
56
 
57
- Experimental and unaudited. Confidentiality rests on the Bilinear Diffie-Hellman assumption (random-oracle model); integrity on AES-256-GCM. Policies are monotone (no negation). This binds confidentiality to possession of capability-derived keys, not to a zero-knowledge proof, and collusion resistance holds only within a single issued key: two separately issued keys could pool attributes. Issue one key per capability. The scheme is not post-quantum.
57
+ Research-grade, unaudited. Do not make it the only control for high-value data.
58
+
59
+ What holds, and under what assumptions:
60
+
61
+ - Collusion resistance. Every key carries a fresh randomiser `r`, so components of different keys do not combine; pooling two holders' keys (whole, or component by component) fails. This is tested, including against a py_ecc oracle for the key and ciphertext components and the pairing equations.
62
+ - The security notion of BSW07 is selective chosen-plaintext security (the adversary names the challenge policy before seeing the public key) in the generic bilinear group model, with the attribute hash as a random oracle. That proof is for the paper's symmetric pairing.
63
+
64
+ What is not established:
65
+
66
+ - The type-3 adaptation used here (which elements live in G1 and which in G2) is this package's own; it has no proof. No reduction to a standard assumption, no standard-model security.
67
+ - No adaptive security, and no CCA security of the ABE layer. AES-256-GCM over the whole header makes tampering detectable (integrity); that is not a CCA proof.
68
+ - The policy is public in the ciphertext (no attribute hiding). No key revocation. Whoever holds the master secret can decrypt everything. Policies are monotone (no negation).
69
+ - Not a zero-knowledge proof: confidentiality is bound to possession of capability-derived keys. Not post-quantum (pairing based). Not constant-time (JavaScript).
70
+ - Decryption costs one multi-pairing over the satisfied leaves; it is slower than the earlier per-attribute scheme.
71
+
72
+ Breaking change from earlier 0.x: key, public key, master key and ciphertext formats changed; old material is not readable.
58
73
 
59
74
  Source and issues: https://github.com/Atlas-Authorization/pca
60
75
 
package/dist/abe.d.ts CHANGED
@@ -1,64 +1,96 @@
1
1
  import { type Capability } from '@atlasauth/pca';
2
+ import { type CapabilityAttributeOpts } from './attributes';
2
3
  import { type Policy } from './policy';
3
4
  /**
4
- * Proof-carrying encryption: Ciphertext-Policy attribute/policy-based encryption built from a
5
- * Boneh-Franklin IBE KEM on BLS12-381, composed over a monotone access structure by Shamir
6
- * secret-sharing (an LSSS), sealing the payload with AES-256-GCM.
5
+ * Proof-carrying encryption: Ciphertext-Policy Attribute-Based Encryption after Bethencourt, Sahai and
6
+ * Waters, "Ciphertext-Policy Attribute-Based Encryption" (IEEE S&P 2007, ePrint 2006/309; "BSW07"),
7
+ * Section 4, adapted to the asymmetric (type-3) pairing e: G1 x G2 -> GT of BLS12-381, with the payload
8
+ * sealed under AES-256-GCM. The access structure is the package's AND / OR / k-of-n tree, realised as a
9
+ * Shamir sharing down the tree exactly as BSW's access tree.
7
10
  *
8
- * CONSTRUCTION
9
- * setup() -> master key pair (s, P_pub = s*g2).
10
- * keygen(attrs) -> one BF decryption key d_A = s*H1(A) per attribute A the holder owns.
11
- * encrypt(policy, m) -> pick a random root secret S; share it down the policy's threshold tree with
12
- * Shamir (AND = n-of-n, OR = 1-of-n, threshold = k-of-n); at each leaf attribute
13
- * A, BF-encapsulate a fresh KEM key and XOR-mask that leaf's assigned share.
14
- * The content-encryption key is HKDF(S); the payload is AES-256-GCM sealed with
15
- * the whole KEM header as AAD.
16
- * decrypt(key, ct) -> at every leaf the key owns, decapsulate -> unmask that share; interpolate each
17
- * gate once >= t shares are recovered; recover S iff the key's attribute set
18
- * satisfies the policy; derive the CEK and open the AEAD. Else fail (null).
11
+ * CONSTRUCTION (g1, g2 the generators of G1, G2; H: {0,1}* -> G1 hash-to-curve, a random oracle)
12
+ * setup() alpha, beta <- Z_r*. MSK = (alpha, beta).
13
+ * MPK = ( h = beta*g2 in G2, f = beta^-1 * g1 in G1, Y = e(g1,g2)^alpha in GT ).
14
+ * keygen(S) r <- Z_r*; for each attribute j in S, r_j <- Z_r*.
15
+ * D = ((alpha + r)/beta) * g1 (G1)
16
+ * D_j = r*g1 + r_j*H(j) (G1), D'_j = r_j*g2 (G2) for j in S
17
+ * The per-USER randomiser r is shared by every component of one key and is what makes
18
+ * keys of different users incompatible (collusion resistance).
19
+ * encrypt(T, m) s <- Z_r*; C_0 = s*h (G2). Share s down the tree T (q_root(0) = s, Shamir per gate);
20
+ * for each leaf x with attribute j: C_x = q_x(0)*g2 (G2), C'_x = q_x(0)*H(j) (G1).
21
+ * The blinding value is K = Y^s = e(g1,g2)^(alpha*s); CEK = HKDF(K, salt).
22
+ * decrypt(SK, CT) DecryptNode at a leaf x (j in S):
23
+ * F_x = e(D_j, C_x) / e(C'_x, D'_j) = e(g1,g2)^(r*q_x(0)).
24
+ * At a gate, F_gate = prod_i F_i^{Delta_i(0)} over any t recovered children (Lagrange
25
+ * interpolation in the exponent) = e(g1,g2)^(r*q_gate(0)). At the root
26
+ * A = e(g1,g2)^(r*s), K = e(D, C_0) / A = e(g1,g2)^((alpha+r)*s - r*s) = Y^s.
19
27
  *
20
- * SECURITY PROPERTY (honest)
21
- * Confidentiality holds under the Bilinear Diffie-Hellman assumption on BLS12-381 in the random-oracle
22
- * model (standard BF-IBE), and the Shamir sharing is information-theoretic (fewer than `t` shares of a
23
- * gate reveal nothing about its secret). A holder whose attribute set does NOT satisfy the policy
24
- * cannot recover enough shares to reconstruct S and therefore cannot derive the CEK. Payload and header
25
- * integrity are guaranteed by AES-256-GCM.
28
+ * ADAPTATION TO TYPE-3 PAIRINGS (this is the package's adaptation, not a result of the BSW paper)
29
+ * BSW07 is stated in a symmetric bilinear group of composite-free prime order p with a single generator g.
30
+ * Here every element is assigned a fixed side so that each pairing in the scheme has one G1 and one G2
31
+ * argument: D, D_j, C'_x, f, H(.) in G1; h, D'_j, C_x, C_0 in G2. The one extra public element f = g^(1/beta)
32
+ * (present in BSW07's public key for Delegate) is used here for key re-randomisation and delegation
33
+ * (`rerandomizeKey`). The equations above are exactly BSW07's with g replaced by g1 or g2 according to the side.
26
34
  *
27
- * This is NOT a proof-of-knowledge / NIZK: it binds confidentiality to POSSESSION of capability-derived
28
- * attribute keys, not to a verified proof. Like any IBE-KEM-composed access structure (as opposed to a
29
- * single monolithic CP-ABE with per-key randomisation), it is collusion-resistant only within a single
30
- * issued key: two SEPARATELY issued keys under the same master, each holding part of the required
31
- * attributes, could in principle pool their attribute keys. Atlas issues exactly one key per capability
32
- * (`keygenForCapability`), and decryption is evaluated against that one key's attribute set — so the
33
- * enforced statement is precisely "this capability's authority satisfies the policy".
35
+ * SECURITY NOTION (honest)
36
+ * BSW07 proves the scheme secure against collusion and chosen-plaintext attack in the SELECTIVE model
37
+ * (the adversary commits to the challenge access structure before seeing the public key), in the GENERIC
38
+ * BILINEAR GROUP model with H a random oracle (the paper's Theorem 1 is for the symmetric setting). This
39
+ * package does NOT carry that proof over: the type-3 adaptation above has no proof of its own here, the
40
+ * generic-group argument is not a reduction to a standard assumption, and no standard-model security is
41
+ * claimed. No adaptive (full) security, no CCA security of the ABE layer (the AES-GCM tag over the whole
42
+ * header makes tampering detectable, which is integrity, not a CCA proof), no key-revocation, no
43
+ * attribute hiding (the policy is public in the ciphertext), and the MSK holder can decrypt everything.
44
+ * Not a NIZK: confidentiality is bound to POSSESSION of capability-derived keys, not to a verified proof.
45
+ * Research-grade, not independently audited.
46
+ *
47
+ * What the construction does give, in the paper's model: two users whose attribute sets individually do
48
+ * not satisfy a policy cannot combine their keys (or components of them) to decrypt it, because every
49
+ * leaf recovery yields e(g1,g2)^(r*q) with the randomiser r of the key it was computed from, and the root
50
+ * needs a single consistent r.
34
51
  */
35
- export declare const ABE_ALG = "bf-ibe-bls12381/lsss/aes-256-gcm";
52
+ export declare const ABE_ALG = "bsw07-cpabe-bls12381-type3/lsss/aes-256-gcm";
36
53
  export interface MasterPublicKey {
37
54
  v: 1;
38
55
  curve: 'bls12-381';
39
- /** b64u compressed G2 point `P_pub = s * g2`. */
40
- pPub: string;
56
+ /** b64u compressed G2 point `h = beta * g2`. */
57
+ h: string;
58
+ /** b64u compressed G1 point `f = beta^-1 * g1` (used to re-randomise / delegate keys). */
59
+ f: string;
60
+ /** b64u 576-byte GT element `Y = e(g1, g2)^alpha`. */
61
+ egga: string;
41
62
  }
42
63
  export interface MasterSecretKey {
43
64
  v: 1;
44
65
  curve: 'bls12-381';
45
- /** b64u 32-byte master secret scalar `s`. */
46
- s: string;
66
+ /** b64u 32-byte master scalar `alpha`. */
67
+ alpha: string;
68
+ /** b64u 32-byte master scalar `beta`. */
69
+ beta: string;
70
+ }
71
+ /** Per-attribute key component: `D_j = r*g1 + r_j*H(j)` (G1) and `D'_j = r_j*g2` (G2). */
72
+ export interface AttributeKey {
73
+ /** b64u compressed G1 point `D_j`. */
74
+ dj: string;
75
+ /** b64u compressed G2 point `D'_j`. */
76
+ dpj: string;
47
77
  }
48
78
  export interface DecryptionKey {
49
79
  v: 1;
50
80
  curve: 'bls12-381';
51
81
  /** The capability's bound holder key, when issued from a capability (informational). */
52
82
  holder?: string;
53
- /** attribute token -> b64u compressed G1 decryption key `d_A = s * H1(A)`. */
54
- attrs: Record<string, string>;
83
+ /** b64u compressed G1 point `D = ((alpha + r)/beta) * g1`, bound to this key's randomiser `r`. */
84
+ d: string;
85
+ /** attribute token -> its `(D_j, D'_j)` components. */
86
+ attrs: Record<string, AttributeKey>;
55
87
  }
56
- /** The KEM header: the access structure realised as a tree of per-leaf encapsulations. */
88
+ /** The access tree: gates carry the Shamir threshold; leaves carry `C_x = q_x(0)*g2` and `C'_x = q_x(0)*H(attr)`. */
57
89
  export type KemTree = {
58
90
  kind: 'leaf';
59
91
  attr: string;
60
- U: string;
61
- wrap: string;
92
+ c: string;
93
+ cp: string;
62
94
  } | {
63
95
  kind: 'gate';
64
96
  t: number;
@@ -70,9 +102,11 @@ export type KemTree = {
70
102
  export interface AbeCiphertext {
71
103
  v: 1;
72
104
  alg: typeof ABE_ALG;
73
- /** b64u HKDF salt (16 bytes) bound into every derived key and the CEK. */
105
+ /** b64u HKDF salt (16 bytes) bound into the CEK. */
74
106
  salt: string;
75
- /** The share-distribution tree (also the authenticated access structure). */
107
+ /** b64u compressed G2 point `C_0 = s * h`. */
108
+ c0: string;
109
+ /** The access tree (also the authenticated access structure). */
76
110
  kem: KemTree;
77
111
  /** b64u AES-256-GCM IV (12 bytes). */
78
112
  iv: string;
@@ -97,7 +131,14 @@ export declare function keygenForAttributes(msk: MasterSecretKey, attrs: Iterabl
97
131
  * extended with explicit extra tokens. The key can decrypt only ciphertexts whose policy is satisfied
98
132
  * by this capability's authority.
99
133
  */
100
- export declare function keygenForCapability(msk: MasterSecretKey, capability: Capability, extraAttributes?: Iterable<string>): DecryptionKey;
134
+ export declare function keygenForCapability(msk: MasterSecretKey, capability: Capability, extraAttributes?: Iterable<string>, opts?: CapabilityAttributeOpts): DecryptionKey;
135
+ /**
136
+ * Re-randomise a key with fresh randomness (BSW07 Delegate): the result decrypts exactly what `key` decrypts
137
+ * (restricted to `subset` when given, which must be attributes the key holds) but shares no component with it
138
+ * and carries a new randomiser `r + r~`, so it is as incompatible with the original as with any other user's key.
139
+ * Needs only the public key. Throws {@link PcaEncryptionError} on a malformed key / mpk or a subset attribute not held.
140
+ */
141
+ export declare function rerandomizeKey(mpk: MasterPublicKey, key: DecryptionKey, subset?: Iterable<string>): DecryptionKey;
101
142
  /**
102
143
  * Encrypt `plaintext` so that it decrypts only for a holder whose attribute set satisfies `policy`.
103
144
  * Throws {@link PcaEncryptionError} on an invalid policy or master public key.
@@ -109,8 +150,8 @@ export declare function encryptForPolicy(mpk: MasterPublicKey, policy: Policy, p
109
150
  export declare function parseCiphertext(x: unknown): AbeCiphertext | null;
110
151
  /**
111
152
  * Decrypt. Returns the plaintext iff the key's attribute set satisfies the ciphertext's policy AND the
112
- * AEAD authenticates; otherwise returns null (never throws). A key missing a required attribute cannot
113
- * recover the root secret and therefore cannot derive the content-encryption key.
153
+ * AEAD authenticates; otherwise returns null (never throws). Components of keys issued to different users
154
+ * carry different randomisers and cannot be combined.
114
155
  */
115
156
  export declare function decrypt(key: DecryptionKey, ciphertext: unknown): Uint8Array | null;
116
157
  /** Like {@link decrypt} but returns the plaintext decoded as UTF-8 text, or null. */