@atlasauth/pca-abe 0.0.0-stage → 0.2.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Atlas Authorization
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,63 @@
1
- # Temporary Holding Version
1
+ # @atlasauth/pca-abe
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
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.
4
+
5
+ ## Install
6
+
7
+ ```sh
8
+ npm i @atlasauth/pca-abe
9
+ ```
10
+
11
+ Depends on `@atlasauth/pca`, `@noble/curves` and `@noble/hashes` (installed transitively).
12
+
13
+ ## Usage
14
+
15
+ ```ts
16
+ import { generateKeyPair, mintGrant, b64u, DEFAULT_RISK_POLICY } from '@atlasauth/pca';
17
+ import {
18
+ setup, keygenForCapability, encryptToolPayload, decryptWithCapability, Attr,
19
+ } from '@atlasauth/pca-abe';
20
+
21
+ // A capability bound to an agent, authorising `read` on /tickets/*.
22
+ const principal = generateKeyPair();
23
+ const agentPub = b64u(generateKeyPair().publicKey);
24
+ const { grant } = mintGrant({
25
+ principalSecret: principal.secretKey,
26
+ principalPublic: b64u(principal.publicKey),
27
+ holder: agentPub,
28
+ goal: 'read tickets',
29
+ envelope: {
30
+ predicates: [{ verb: 'read', resource: '/tickets/*' }],
31
+ caveats: [], agent_binding: {}, risk_policy: DEFAULT_RISK_POLICY,
32
+ },
33
+ });
34
+
35
+ // The issuer holds the master key; the encryptor only needs the public half.
36
+ const { mpk, msk } = setup();
37
+ const policy = { allOf: [{ attr: Attr.holder(agentPub) }, { attr: Attr.verb('read') }] };
38
+ const { ciphertext } = encryptToolPayload(mpk, policy, { ticket: 'T-42' });
39
+
40
+ // The issuer derives a key from the capability and hands it to the agent.
41
+ const key = keygenForCapability(msk, grant);
42
+ decryptWithCapability(key, ciphertext); // { ok: true, payload: { ticket: 'T-42' } }
43
+ ```
44
+
45
+ A key derived from a capability that does not satisfy the policy gets `{ ok: false, reason }`.
46
+
47
+ ## API
48
+
49
+ - Keys: `setup`, `keygenForAttributes`, `keygenForCapability`.
50
+ - Raw bytes/text: `encryptForPolicy`, `decrypt`, `decryptText`, `parseCiphertext`.
51
+ - Tool payloads: `encryptToolPayload`, `decryptWithCapability`.
52
+ - Policies: `Policy` (`{ attr }`, `{ allOf }`, `{ anyOf }`, `{ threshold, of }`), `normalizePolicy`, `satisfies`, `policyAttributes`.
53
+ - Attributes: `Attr`, `capabilityAttributes`.
54
+
55
+ ## Status
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.
58
+
59
+ Source and issues: https://github.com/Atlas-Authorization/pca
60
+
61
+ ## License
62
+
63
+ MIT - see LICENSE
package/dist/abe.d.ts ADDED
@@ -0,0 +1,117 @@
1
+ import { type Capability } from '@atlasauth/pca';
2
+ import { type Policy } from './policy';
3
+ /**
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.
7
+ *
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).
19
+ *
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.
26
+ *
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".
34
+ */
35
+ export declare const ABE_ALG = "bf-ibe-bls12381/lsss/aes-256-gcm";
36
+ export interface MasterPublicKey {
37
+ v: 1;
38
+ curve: 'bls12-381';
39
+ /** b64u compressed G2 point `P_pub = s * g2`. */
40
+ pPub: string;
41
+ }
42
+ export interface MasterSecretKey {
43
+ v: 1;
44
+ curve: 'bls12-381';
45
+ /** b64u 32-byte master secret scalar `s`. */
46
+ s: string;
47
+ }
48
+ export interface DecryptionKey {
49
+ v: 1;
50
+ curve: 'bls12-381';
51
+ /** The capability's bound holder key, when issued from a capability (informational). */
52
+ holder?: string;
53
+ /** attribute token -> b64u compressed G1 decryption key `d_A = s * H1(A)`. */
54
+ attrs: Record<string, string>;
55
+ }
56
+ /** The KEM header: the access structure realised as a tree of per-leaf encapsulations. */
57
+ export type KemTree = {
58
+ kind: 'leaf';
59
+ attr: string;
60
+ U: string;
61
+ wrap: string;
62
+ } | {
63
+ kind: 'gate';
64
+ t: number;
65
+ of: {
66
+ i: number;
67
+ sub: KemTree;
68
+ }[];
69
+ };
70
+ export interface AbeCiphertext {
71
+ v: 1;
72
+ alg: typeof ABE_ALG;
73
+ /** b64u HKDF salt (16 bytes) bound into every derived key and the CEK. */
74
+ salt: string;
75
+ /** The share-distribution tree (also the authenticated access structure). */
76
+ kem: KemTree;
77
+ /** b64u AES-256-GCM IV (12 bytes). */
78
+ iv: string;
79
+ /** b64u AES-256-GCM ciphertext. */
80
+ ct: string;
81
+ /** b64u AES-256-GCM tag (16 bytes). */
82
+ tag: string;
83
+ }
84
+ export declare class PcaEncryptionError extends Error {
85
+ constructor(message: string);
86
+ }
87
+ /** Generate a fresh master key pair. */
88
+ export declare function setup(): {
89
+ mpk: MasterPublicKey;
90
+ msk: MasterSecretKey;
91
+ };
92
+ /** Issue a decryption key for an explicit set of attribute tokens. */
93
+ export declare function keygenForAttributes(msk: MasterSecretKey, attrs: Iterable<string>): DecryptionKey;
94
+ /**
95
+ * Issue a decryption key BOUND to a PCA capability: its attributes are exactly those the (signed)
96
+ * capability carries — holder, and the verbs/resources/scopes of its policy envelope — optionally
97
+ * extended with explicit extra tokens. The key can decrypt only ciphertexts whose policy is satisfied
98
+ * by this capability's authority.
99
+ */
100
+ export declare function keygenForCapability(msk: MasterSecretKey, capability: Capability, extraAttributes?: Iterable<string>): DecryptionKey;
101
+ /**
102
+ * Encrypt `plaintext` so that it decrypts only for a holder whose attribute set satisfies `policy`.
103
+ * Throws {@link PcaEncryptionError} on an invalid policy or master public key.
104
+ */
105
+ export declare function encryptForPolicy(mpk: MasterPublicKey, policy: Policy, plaintext: Uint8Array | string): {
106
+ ciphertext: AbeCiphertext;
107
+ };
108
+ /** Validate an untrusted value as an {@link AbeCiphertext}; null (fail-closed) on any deviation. */
109
+ export declare function parseCiphertext(x: unknown): AbeCiphertext | null;
110
+ /**
111
+ * 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.
114
+ */
115
+ export declare function decrypt(key: DecryptionKey, ciphertext: unknown): Uint8Array | null;
116
+ /** Like {@link decrypt} but returns the plaintext decoded as UTF-8 text, or null. */
117
+ export declare function decryptText(key: DecryptionKey, ciphertext: unknown): string | null;
package/dist/abe.js ADDED
@@ -0,0 +1,262 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PcaEncryptionError = exports.ABE_ALG = void 0;
4
+ exports.setup = setup;
5
+ exports.keygenForAttributes = keygenForAttributes;
6
+ exports.keygenForCapability = keygenForCapability;
7
+ exports.encryptForPolicy = encryptForPolicy;
8
+ exports.parseCiphertext = parseCiphertext;
9
+ exports.decrypt = decrypt;
10
+ exports.decryptText = decryptText;
11
+ const pca_1 = require("@atlasauth/pca");
12
+ const bls_1 = require("./bls");
13
+ const ibe_1 = require("./ibe");
14
+ const aead_1 = require("./aead");
15
+ const sss_1 = require("./sss");
16
+ const attributes_1 = require("./attributes");
17
+ const policy_1 = require("./policy");
18
+ const wire_1 = require("./wire");
19
+ const utils_1 = require("@noble/hashes/utils");
20
+ /**
21
+ * Proof-carrying encryption: Ciphertext-Policy attribute/policy-based encryption built from a
22
+ * Boneh-Franklin IBE KEM on BLS12-381, composed over a monotone access structure by Shamir
23
+ * secret-sharing (an LSSS), sealing the payload with AES-256-GCM.
24
+ *
25
+ * CONSTRUCTION
26
+ * setup() -> master key pair (s, P_pub = s*g2).
27
+ * keygen(attrs) -> one BF decryption key d_A = s*H1(A) per attribute A the holder owns.
28
+ * encrypt(policy, m) -> pick a random root secret S; share it down the policy's threshold tree with
29
+ * Shamir (AND = n-of-n, OR = 1-of-n, threshold = k-of-n); at each leaf attribute
30
+ * A, BF-encapsulate a fresh KEM key and XOR-mask that leaf's assigned share.
31
+ * The content-encryption key is HKDF(S); the payload is AES-256-GCM sealed with
32
+ * the whole KEM header as AAD.
33
+ * decrypt(key, ct) -> at every leaf the key owns, decapsulate -> unmask that share; interpolate each
34
+ * gate once >= t shares are recovered; recover S iff the key's attribute set
35
+ * satisfies the policy; derive the CEK and open the AEAD. Else fail (null).
36
+ *
37
+ * SECURITY PROPERTY (honest)
38
+ * Confidentiality holds under the Bilinear Diffie-Hellman assumption on BLS12-381 in the random-oracle
39
+ * model (standard BF-IBE), and the Shamir sharing is information-theoretic (fewer than `t` shares of a
40
+ * gate reveal nothing about its secret). A holder whose attribute set does NOT satisfy the policy
41
+ * cannot recover enough shares to reconstruct S and therefore cannot derive the CEK. Payload and header
42
+ * integrity are guaranteed by AES-256-GCM.
43
+ *
44
+ * This is NOT a proof-of-knowledge / NIZK: it binds confidentiality to POSSESSION of capability-derived
45
+ * attribute keys, not to a verified proof. Like any IBE-KEM-composed access structure (as opposed to a
46
+ * single monolithic CP-ABE with per-key randomisation), it is collusion-resistant only within a single
47
+ * issued key: two SEPARATELY issued keys under the same master, each holding part of the required
48
+ * attributes, could in principle pool their attribute keys. Atlas issues exactly one key per capability
49
+ * (`keygenForCapability`), and decryption is evaluated against that one key's attribute set — so the
50
+ * enforced statement is precisely "this capability's authority satisfies the policy".
51
+ */
52
+ exports.ABE_ALG = 'bf-ibe-bls12381/lsss/aes-256-gcm';
53
+ const CEK_INFO = 'atlas-pca-abe/cek/v1';
54
+ const SALT_LEN = 16;
55
+ class PcaEncryptionError extends Error {
56
+ constructor(message) {
57
+ super(message);
58
+ this.name = 'PcaEncryptionError';
59
+ }
60
+ }
61
+ exports.PcaEncryptionError = PcaEncryptionError;
62
+ // ---- setup / keygen ---------------------------------------------------------------------------
63
+ /** Generate a fresh master key pair. */
64
+ function setup() {
65
+ const { scalar, bytes } = (0, sss_1.randomSecret)();
66
+ return {
67
+ msk: { v: 1, curve: 'bls12-381', s: (0, wire_1.b64u)(bytes) },
68
+ mpk: { v: 1, curve: 'bls12-381', pPub: (0, wire_1.b64u)((0, ibe_1.masterPublic)(scalar)) },
69
+ };
70
+ }
71
+ function mskScalar(msk) {
72
+ const bytes = (0, wire_1.unb64u)(msk?.s);
73
+ const s = bytes === null ? null : (0, bls_1.bytesToScalar)(bytes);
74
+ if (s === null || s === 0n)
75
+ throw new PcaEncryptionError('invalid master secret key');
76
+ return s;
77
+ }
78
+ /** Issue a decryption key for an explicit set of attribute tokens. */
79
+ function keygenForAttributes(msk, attrs) {
80
+ const s = mskScalar(msk);
81
+ const out = {};
82
+ for (const a of attrs) {
83
+ if (typeof a === 'string' && a.length > 0)
84
+ out[a] = (0, wire_1.b64u)((0, ibe_1.keygenAttr)(s, a));
85
+ }
86
+ return { v: 1, curve: 'bls12-381', attrs: out };
87
+ }
88
+ /**
89
+ * Issue a decryption key BOUND to a PCA capability: its attributes are exactly those the (signed)
90
+ * capability carries — holder, and the verbs/resources/scopes of its policy envelope — optionally
91
+ * extended with explicit extra tokens. The key can decrypt only ciphertexts whose policy is satisfied
92
+ * by this capability's authority.
93
+ */
94
+ function keygenForCapability(msk, capability, extraAttributes = []) {
95
+ const attrs = (0, attributes_1.capabilityAttributes)(capability);
96
+ for (const a of extraAttributes)
97
+ if (typeof a === 'string' && a.length > 0)
98
+ attrs.add(a);
99
+ const key = keygenForAttributes(msk, attrs);
100
+ if (typeof capability.holder === 'string')
101
+ key.holder = capability.holder;
102
+ return key;
103
+ }
104
+ // ---- encrypt ----------------------------------------------------------------------------------
105
+ function distribute(mpkPub, node, nodeSecret, salt) {
106
+ if (node.kind === 'leaf') {
107
+ const enc = (0, ibe_1.encapsulate)(mpkPub, node.attr, salt);
108
+ if (enc === null)
109
+ throw new PcaEncryptionError('invalid master public key');
110
+ const wrap = (0, wire_1.xorBytes)((0, bls_1.scalarToBytes)(nodeSecret), enc.key);
111
+ if (wrap === null)
112
+ throw new PcaEncryptionError('internal: wrap length mismatch');
113
+ return { kind: 'leaf', attr: node.attr, U: (0, wire_1.b64u)(enc.U), wrap: (0, wire_1.b64u)(wrap) };
114
+ }
115
+ const shares = (0, sss_1.splitSecret)(nodeSecret, node.t, node.of.length);
116
+ const of = [];
117
+ for (let idx = 0; idx < node.of.length; idx++) {
118
+ const child = node.of[idx];
119
+ const share = shares[idx];
120
+ if (child === undefined || share === undefined)
121
+ throw new PcaEncryptionError('internal: share/child mismatch');
122
+ of.push({ i: Number(share.x), sub: distribute(mpkPub, child, share.y, salt) });
123
+ }
124
+ return { kind: 'gate', t: node.t, of };
125
+ }
126
+ function aad(c) {
127
+ return (0, pca_1.canonicalBytes)({ v: c.v, alg: c.alg, salt: c.salt, kem: c.kem });
128
+ }
129
+ /**
130
+ * Encrypt `plaintext` so that it decrypts only for a holder whose attribute set satisfies `policy`.
131
+ * Throws {@link PcaEncryptionError} on an invalid policy or master public key.
132
+ */
133
+ function encryptForPolicy(mpk, policy, plaintext) {
134
+ const norm = (0, policy_1.normalizePolicy)(policy);
135
+ if (!norm.ok)
136
+ throw new PcaEncryptionError(`invalid policy: ${norm.reason}`);
137
+ const mpkPub = (0, wire_1.unb64u)(mpk?.pPub);
138
+ if (mpkPub === null)
139
+ throw new PcaEncryptionError('invalid master public key');
140
+ const salt = new Uint8Array((0, utils_1.randomBytes)(SALT_LEN));
141
+ const { scalar: rootSecret, bytes: rootBytes } = (0, sss_1.randomSecret)();
142
+ const kem = distribute(mpkPub, norm.tree, rootSecret, salt);
143
+ const header = { v: 1, alg: exports.ABE_ALG, salt: (0, wire_1.b64u)(salt), kem };
144
+ const cek = (0, bls_1.kdf32)(rootBytes, salt, CEK_INFO);
145
+ const pt = typeof plaintext === 'string' ? new TextEncoder().encode(plaintext) : plaintext;
146
+ const sealed = (0, aead_1.seal)(cek, pt, aad(header));
147
+ return {
148
+ ciphertext: { ...header, iv: (0, wire_1.b64u)(sealed.iv), ct: (0, wire_1.b64u)(sealed.ct), tag: (0, wire_1.b64u)(sealed.tag) },
149
+ };
150
+ }
151
+ // ---- decrypt ----------------------------------------------------------------------------------
152
+ function parseKemTree(x, depth) {
153
+ if (depth > 64 || !(0, wire_1.isObject)(x))
154
+ return null;
155
+ if (x.kind === 'leaf') {
156
+ if (!(0, wire_1.isString)(x.attr) || !(0, wire_1.isString)(x.U) || !(0, wire_1.isString)(x.wrap))
157
+ return null;
158
+ if ((0, wire_1.unb64u)(x.U) === null || (0, wire_1.unb64u)(x.wrap) === null)
159
+ return null;
160
+ return { kind: 'leaf', attr: x.attr, U: x.U, wrap: x.wrap };
161
+ }
162
+ if (x.kind === 'gate') {
163
+ if (!(0, wire_1.isPosInt)(x.t) || !Array.isArray(x.of) || x.of.length === 0)
164
+ return null;
165
+ if (x.t > x.of.length)
166
+ return null;
167
+ const of = [];
168
+ for (const branch of x.of) {
169
+ if (!(0, wire_1.isObject)(branch) || !(0, wire_1.isPosInt)(branch.i))
170
+ return null;
171
+ const sub = parseKemTree(branch.sub, depth + 1);
172
+ if (sub === null)
173
+ return null;
174
+ of.push({ i: branch.i, sub });
175
+ }
176
+ return { kind: 'gate', t: x.t, of };
177
+ }
178
+ return null;
179
+ }
180
+ /** Validate an untrusted value as an {@link AbeCiphertext}; null (fail-closed) on any deviation. */
181
+ function parseCiphertext(x) {
182
+ if (!(0, wire_1.isObject)(x) || x.v !== 1 || x.alg !== exports.ABE_ALG)
183
+ return null;
184
+ if (!(0, wire_1.isString)(x.salt) || !(0, wire_1.isString)(x.iv) || !(0, wire_1.isString)(x.ct) || !(0, wire_1.isString)(x.tag))
185
+ return null;
186
+ if ((0, wire_1.unb64u)(x.salt) === null || (0, wire_1.unb64u)(x.iv) === null || (0, wire_1.unb64u)(x.ct) === null || (0, wire_1.unb64u)(x.tag) === null)
187
+ return null;
188
+ const kem = parseKemTree(x.kem, 0);
189
+ if (kem === null)
190
+ return null;
191
+ return { v: 1, alg: exports.ABE_ALG, salt: x.salt, kem, iv: x.iv, ct: x.ct, tag: x.tag };
192
+ }
193
+ function recover(node, keys, salt) {
194
+ if (node.kind === 'leaf') {
195
+ const dA = keys.get(node.attr);
196
+ if (dA === undefined)
197
+ return null; // the key does not own this attribute: nothing to recover here.
198
+ const uBytes = (0, wire_1.unb64u)(node.U);
199
+ const wrapBytes = (0, wire_1.unb64u)(node.wrap);
200
+ if (uBytes === null || wrapBytes === null)
201
+ return null;
202
+ const kemKey = (0, ibe_1.decapsulate)(dA, node.attr, uBytes, salt);
203
+ if (kemKey === null)
204
+ return null;
205
+ const shareBytes = (0, wire_1.xorBytes)(wrapBytes, kemKey);
206
+ if (shareBytes === null)
207
+ return null;
208
+ return (0, bls_1.bytesToScalar)(shareBytes);
209
+ }
210
+ const shares = [];
211
+ for (const branch of node.of) {
212
+ const y = recover(branch.sub, keys, salt);
213
+ if (y !== null)
214
+ shares.push({ x: BigInt(branch.i), y });
215
+ if (shares.length >= node.t)
216
+ break; // enough branches recovered; no need to decrypt more.
217
+ }
218
+ if (shares.length < node.t)
219
+ return null;
220
+ return (0, sss_1.recoverSecret)(shares);
221
+ }
222
+ /**
223
+ * Decrypt. Returns the plaintext iff the key's attribute set satisfies the ciphertext's policy AND the
224
+ * AEAD authenticates; otherwise returns null (never throws). A key missing a required attribute cannot
225
+ * recover the root secret and therefore cannot derive the content-encryption key.
226
+ */
227
+ function decrypt(key, ciphertext) {
228
+ const ct = parseCiphertext(ciphertext);
229
+ if (ct === null)
230
+ return null;
231
+ if (!(0, wire_1.isObject)(key) || !(0, wire_1.isObject)(key.attrs))
232
+ return null;
233
+ const salt = (0, wire_1.unb64u)(ct.salt);
234
+ const iv = (0, wire_1.unb64u)(ct.iv);
235
+ const body = (0, wire_1.unb64u)(ct.ct);
236
+ const tag = (0, wire_1.unb64u)(ct.tag);
237
+ if (salt === null || iv === null || body === null || tag === null)
238
+ return null;
239
+ // Decode the attribute decryption keys we hold (skip any malformed point: fail-closed per attribute).
240
+ const keys = new Map();
241
+ for (const [attr, enc] of Object.entries(key.attrs)) {
242
+ const bytes = (0, wire_1.unb64u)(enc);
243
+ if (bytes === null)
244
+ continue;
245
+ const pt = (0, bls_1.g1FromBytes)(bytes);
246
+ if (pt !== null)
247
+ keys.set(attr, pt);
248
+ }
249
+ const rootSecret = recover(ct.kem, keys, salt);
250
+ if (rootSecret === null)
251
+ return null; // attribute set does not satisfy the policy.
252
+ const cek = (0, bls_1.kdf32)((0, bls_1.scalarToBytes)(rootSecret), salt, CEK_INFO);
253
+ // The AEAD is the final integrity gate: a wrong reconstructed secret (cross-master key / tamper) or a
254
+ // tampered payload/header yields a null here.
255
+ return (0, aead_1.open)(cek, { iv, ct: body, tag }, aad(ct));
256
+ }
257
+ /** Like {@link decrypt} but returns the plaintext decoded as UTF-8 text, or null. */
258
+ function decryptText(key, ciphertext) {
259
+ const pt = decrypt(key, ciphertext);
260
+ return pt === null ? null : new TextDecoder().decode(pt);
261
+ }
262
+ //# sourceMappingURL=abe.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"abe.js","sourceRoot":"","sources":["../src/abe.ts"],"names":[],"mappings":"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"}
package/dist/aead.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ export interface Sealed {
2
+ iv: Uint8Array;
3
+ ct: Uint8Array;
4
+ tag: Uint8Array;
5
+ }
6
+ /** AES-256-GCM seal. `key` must be 32 bytes. A fresh random 96-bit IV is used per call. */
7
+ export declare function seal(key: Uint8Array, plaintext: Uint8Array, aad: Uint8Array): Sealed;
8
+ /**
9
+ * AES-256-GCM open. Returns the plaintext, or null (never throws) if the key is wrong, the tag/AAD does
10
+ * not authenticate, or any field is malformed.
11
+ */
12
+ export declare function open(key: Uint8Array, sealed: Sealed, aad: Uint8Array): Uint8Array | null;
package/dist/aead.js ADDED
@@ -0,0 +1,42 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.seal = seal;
4
+ exports.open = open;
5
+ const node_crypto_1 = require("node:crypto");
6
+ /**
7
+ * Authenticated symmetric encryption: AES-256-GCM (via `node:crypto`). The content-encryption key is
8
+ * derived from the ABE/KEM layer; this module only turns a 32-byte key into a sealed payload and back.
9
+ * The ABE header is passed as Additional Authenticated Data so any tampering with the policy or the KEM
10
+ * ciphertext — not just the payload — fails the GCM tag.
11
+ */
12
+ const IV_LEN = 12;
13
+ const TAG_LEN = 16;
14
+ /** AES-256-GCM seal. `key` must be 32 bytes. A fresh random 96-bit IV is used per call. */
15
+ function seal(key, plaintext, aad) {
16
+ if (key.length !== 32)
17
+ throw new RangeError('aead.seal: key must be 32 bytes');
18
+ const iv = new Uint8Array((0, node_crypto_1.randomBytes)(IV_LEN));
19
+ const cipher = (0, node_crypto_1.createCipheriv)('aes-256-gcm', key, iv);
20
+ cipher.setAAD(aad);
21
+ const ct = new Uint8Array(Buffer.concat([cipher.update(plaintext), cipher.final()]));
22
+ const tag = new Uint8Array(cipher.getAuthTag());
23
+ return { iv, ct, tag };
24
+ }
25
+ /**
26
+ * AES-256-GCM open. Returns the plaintext, or null (never throws) if the key is wrong, the tag/AAD does
27
+ * not authenticate, or any field is malformed.
28
+ */
29
+ function open(key, sealed, aad) {
30
+ if (key.length !== 32 || sealed.iv.length !== IV_LEN || sealed.tag.length !== TAG_LEN)
31
+ return null;
32
+ try {
33
+ const decipher = (0, node_crypto_1.createDecipheriv)('aes-256-gcm', key, sealed.iv);
34
+ decipher.setAAD(aad);
35
+ decipher.setAuthTag(sealed.tag);
36
+ return new Uint8Array(Buffer.concat([decipher.update(sealed.ct), decipher.final()]));
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
42
+ //# sourceMappingURL=aead.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aead.js","sourceRoot":"","sources":["../src/aead.ts"],"names":[],"mappings":";;AAmBA,oBAQC;AAMD,oBAUC;AA3CD,6CAA4E;AAE5E;;;;;GAKG;AAEH,MAAM,MAAM,GAAG,EAAE,CAAC;AAClB,MAAM,OAAO,GAAG,EAAE,CAAC;AAQnB,2FAA2F;AAC3F,SAAgB,IAAI,CAAC,GAAe,EAAE,SAAqB,EAAE,GAAe;IAC1E,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE;QAAE,MAAM,IAAI,UAAU,CAAC,iCAAiC,CAAC,CAAC;IAC/E,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,IAAA,yBAAW,EAAC,MAAM,CAAC,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAG,IAAA,4BAAc,EAAC,aAAa,EAAE,GAAG,EAAE,EAAE,CAAC,CAAC;IACtD,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACnB,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IACrF,MAAM,GAAG,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,CAAC;IAChD,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,SAAgB,IAAI,CAAC,GAAe,EAAE,MAAc,EAAE,GAAe;IACnE,IAAI,GAAG,CAAC,MAAM,KAAK,EAAE,IAAI,MAAM,CAAC,EAAE,CAAC,MAAM,KAAK,MAAM,IAAI,MAAM,CAAC,GAAG,CAAC,MAAM,KAAK,OAAO;QAAE,OAAO,IAAI,CAAC;IACnG,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAA,8BAAgB,EAAC,aAAa,EAAE,GAAG,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;QACjE,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACrB,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAChC,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,QAAQ,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;IACvF,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,30 @@
1
+ import { type Capability } from '@atlasauth/pca';
2
+ /**
3
+ * Canonical attribute strings derived from a real PCA capability (see `@atlasauth/pca`'s `Capability`
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.
7
+ *
8
+ * Four kinds are derived, matching the capability's authority surface (verb / resource / scopes /
9
+ * holder):
10
+ * - `holder:<b64u key>` the key the capability is bound to (`capability.holder`),
11
+ * - `verb:<v>` every literal verb named in the signed envelope's predicates,
12
+ * - `resource:<r>` every resource matcher named in those predicates,
13
+ * - `scope:<s>` every scope carried in a `{ type:'scope', value }` or
14
+ * `{ type:'scopes', values:[...] }` caveat.
15
+ *
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.
19
+ */
20
+ export declare const Attr: {
21
+ readonly holder: (key: string) => string;
22
+ readonly verb: (v: string) => string;
23
+ readonly resource: (r: string) => string;
24
+ readonly scope: (s: string) => string;
25
+ };
26
+ /**
27
+ * Derive the canonical attribute set a capability possesses. Deterministic and total (never throws): a
28
+ * capability with no signed envelope still yields its `holder:` attribute (plus any scope caveats).
29
+ */
30
+ export declare function capabilityAttributes(cap: Capability): Set<string>;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Attr = void 0;
4
+ exports.capabilityAttributes = capabilityAttributes;
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
+ exports.Attr = {
25
+ holder: (key) => `holder:${key}`,
26
+ verb: (v) => `verb:${v}`,
27
+ resource: (r) => `resource:${r}`,
28
+ scope: (s) => `scope:${s}`,
29
+ };
30
+ function addVerb(out, verb) {
31
+ if (typeof verb === 'string')
32
+ out.add(exports.Attr.verb(verb));
33
+ else if (Array.isArray(verb))
34
+ for (const v of verb)
35
+ if (typeof v === 'string')
36
+ out.add(exports.Attr.verb(v));
37
+ }
38
+ function addScopes(out, caveats) {
39
+ for (const cv of caveats) {
40
+ if (cv.type === 'scope' && typeof cv.value === 'string')
41
+ out.add(exports.Attr.scope(cv.value));
42
+ if (cv.type === 'scopes' && Array.isArray(cv.values)) {
43
+ for (const s of cv.values)
44
+ if (typeof s === 'string')
45
+ out.add(exports.Attr.scope(s));
46
+ }
47
+ }
48
+ }
49
+ /**
50
+ * Derive the canonical attribute set a capability possesses. Deterministic and total (never throws): a
51
+ * capability with no signed envelope still yields its `holder:` attribute (plus any scope caveats).
52
+ */
53
+ function capabilityAttributes(cap) {
54
+ const out = new Set();
55
+ if (typeof cap.holder === 'string' && cap.holder.length > 0)
56
+ 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);
60
+ 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);
65
+ const r = p.resource;
66
+ if (typeof r === 'string')
67
+ out.add(exports.Attr.resource(r));
68
+ }
69
+ }
70
+ return out;
71
+ }
72
+ //# sourceMappingURL=attributes.js.map