The demo below simulates the heart of ABE: a policy tree with AND and OR gates. The encrypted file is associated with the policy at the top. You choose which attributes the user holds, and the tree lights up as each gate is satisfied.
<!-- ABE policy-tree demo: a policy tree of AND/OR gates over attribute leaves -->
<div class="abe-wrap">
<div class="policy-panel">
<div class="panel-label">Access Policy (encrypted with)</div>
<div id="tree" class="tree"></div>
<div id="verdict" class="verdict"></div>
</div>
<div class="attr-panel">
<div class="panel-label">User Attributes</div>
<div id="attr-list" class="attr-list"></div>
<button id="btn-reset" type="button" class="ghost-btn">Clear all</button>
</div>
</div>
/* Styles for the ABE policy tree and attribute selector */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; color: #222; font-size: .92rem; }
.abe-wrap { display: flex; gap: 1rem; flex-wrap: wrap; padding: .4rem 0; }
.policy-panel { flex: 1 1 260px; }
.attr-panel { flex: 0 0 180px; }
.panel-label { font-weight: 700; font-size: .78rem; text-transform: uppercase;
letter-spacing: .06em; color: #5a7088; margin-bottom: .5rem; }
/* Tree node styles: gate (AND/OR) and leaf (attribute) */
.tree { display: flex; flex-direction: column; align-items: flex-start; gap: 0; }
.node { display: flex; align-items: center; gap: .35rem; padding: .28rem .55rem;
border-radius: 7px; font-weight: 600; font-size: .88rem;
border: 1.5px solid #cdd9e3; background: #f2f5f8;
transition: background .18s, border-color .18s; }
.node.gate { background: #e4ecf4; border-color: #a8c0d6; }
.node.leaf { background: #f8f9fa; border-color: #cdd9e3; }
.node.sat { background: #d4edda; border-color: #28a745; color: #155724; }
.node.unsat { background: #f8d7da; border-color: #dc3545; color: #721c24; }
.node .icon { font-size: 1rem; width: 1.1rem; text-align: center; }
/* Connector lines that show the tree hierarchy */
.row { display: flex; align-items: flex-start; }
.indent { width: 1.4rem; flex-shrink: 0; display: flex; flex-direction: column; align-items: center; }
.vline { width: 1.5px; background: #cdd9e3; flex: 1; min-height: .55rem; }
.hline { width: .7rem; height: 1.5px; background: #cdd9e3; align-self: center; }
.subtree { flex: 1; display: flex; flex-direction: column; gap: 0; }
/* Verdict banner below the tree */
.verdict { margin-top: .7rem; font-weight: 700; font-size: 1rem; min-height: 1.4em; }
.verdict.ok { color: #155724; }
.verdict.bad { color: #721c24; }
/* Toggle list of user attributes on the right */
.attr-list { display: flex; flex-direction: column; gap: .4rem; margin-bottom: .6rem; }
.attr-row { display: flex; align-items: center; gap: .45rem; cursor: pointer;
padding: .3rem .5rem; border-radius: 6px; border: 1.5px solid #cdd9e3;
background: #f2f5f8; transition: all .15s; user-select: none; }
.attr-row.on { background: #cce5ff; border-color: #3a86e8; }
.attr-row .dot { width: 10px; height: 10px; border-radius: 50%; background: #cdd9e3;
flex-shrink: 0; transition: background .15s; }
.attr-row.on .dot { background: #3a86e8; }
.ghost-btn { font: 600 13px system-ui; padding: .35rem .7rem; border: 1.5px solid #5a7088;
background: #fff; color: #5a7088; border-radius: 7px; cursor: pointer; }
// ABE policy-tree demo: evaluate a boolean access policy against a set of user attributes.
// No crypto here — just the satisfaction logic that mirrors what ABE does mathematically.
// Access policy: (role=doctor OR role=nurse) AND (dept=cardiology OR clearance>=3)
const POLICY = {
type: 'AND',
children: [
{
type: 'OR',
children: [
{ type: 'leaf', attr: 'role=doctor' },
{ type: 'leaf', attr: 'role=nurse' }
]
},
{
type: 'OR',
children: [
{ type: 'leaf', attr: 'dept=cardiology' },
{ type: 'leaf', attr: 'clearance>=3' }
]
}
]
};
// All possible attributes a user can hold in this scenario
const ALL_ATTRS = [
'role=doctor',
'role=nurse',
'dept=cardiology',
'dept=oncology',
'clearance>=3'
];
const userAttrs = new Set(); // set of attributes the current user holds — toggled via the UI
// Recursively evaluate whether a policy node is satisfied by the given attribute set
function evaluate(node, attrs) {
if (node.type === 'leaf') return attrs.has(node.attr);
if (node.type === 'AND') return node.children.every(c => evaluate(c, attrs));
if (node.type === 'OR') return node.children.some(c => evaluate(c, attrs));
return false;
}
// Render a policy node and its children as nested DOM rows with connector lines
function renderTree(node, attrs, container, depth) {
const row = document.createElement('div');
row.className = 'row';
if (depth > 0) {
const indent = document.createElement('div');
indent.className = 'indent';
const vl = document.createElement('div'); vl.className = 'vline';
const hl = document.createElement('div'); hl.className = 'hline';
indent.append(vl, hl);
row.appendChild(indent);
}
const subtree = document.createElement('div');
subtree.className = 'subtree';
const sat = evaluate(node, attrs);
const nodeEl = document.createElement('div');
if (node.type === 'leaf') {
nodeEl.className = 'node leaf ' + (sat ? 'sat' : 'unsat');
const icon = document.createElement('span'); icon.className = 'icon';
icon.textContent = sat ? '✓' : '✗';
const label = document.createElement('span');
label.textContent = node.attr;
nodeEl.append(icon, label);
subtree.appendChild(nodeEl);
} else {
nodeEl.className = 'node gate ' + (sat ? 'sat' : 'unsat');
const icon = document.createElement('span'); icon.className = 'icon';
icon.textContent = sat ? '✓' : '✗';
const label = document.createElement('span');
label.textContent = node.type; // AND or OR — not translated; used as a logic keyword
nodeEl.append(icon, label);
subtree.appendChild(nodeEl);
node.children.forEach(child => renderTree(child, attrs, subtree, depth + 1));
}
row.appendChild(subtree);
container.appendChild(row);
}
function render() {
const treeEl = document.getElementById('tree');
treeEl.innerHTML = '';
renderTree(POLICY, userAttrs, treeEl, 0);
const ok = evaluate(POLICY, userAttrs);
const v = document.getElementById('verdict');
if (ok) {
v.textContent = '✓ Policy satisfied — decryption succeeds';
v.className = 'verdict ok';
} else {
v.textContent = '✗ Policy not satisfied — decryption fails';
v.className = 'verdict bad';
}
}
function buildAttrList() {
const list = document.getElementById('attr-list');
list.innerHTML = '';
ALL_ATTRS.forEach(attr => {
const row = document.createElement('div');
row.className = 'attr-row' + (userAttrs.has(attr) ? ' on' : '');
const dot = document.createElement('span'); dot.className = 'dot';
const lbl = document.createElement('span'); lbl.textContent = attr;
row.append(dot, lbl);
row.addEventListener('click', () => {
if (userAttrs.has(attr)) userAttrs.delete(attr);
else userAttrs.add(attr);
row.classList.toggle('on', userAttrs.has(attr));
row.querySelector('.dot').style.background = userAttrs.has(attr) ? '#3a86e8' : '';
render();
});
list.appendChild(row);
});
}
document.getElementById('btn-reset').addEventListener('click', () => {
userAttrs.clear();
buildAttrList();
render();
});
buildAttrList();
render();
<!-- ABE policy-tree demo: a policy tree of AND/OR gates over attribute leaves -->
<div class="abe-wrap">
<div class="policy-panel">
<div class="panel-label">Access Policy (encrypted with)</div>
<div id="tree" class="tree"></div>
<div id="verdict" class="verdict"></div>
</div>
<div class="attr-panel">
<div class="panel-label">User Attributes</div>
<div id="attr-list" class="attr-list"></div>
<button id="btn-reset" type="button" class="ghost-btn">Clear all</button>
</div>
</div>
/* Styles for the ABE policy tree and attribute selector */
* { box-sizing: border-box; }
body { font-family: system-ui, sans-serif; margin: 0; color: #222; font-size: .92rem; }
.abe-wrap { display: flex; gap: 1rem; flex-wrap: wrap; padding: .4rem 0; }
.policy-panel { flex: 1 1 260px; }
.attr-panel { flex: 0 0 180px; }
.panel-label { font-weight: 700; font-size: .78rem; text-transform: uppercase;
letter-spacing: .06em; color: #5a7088; margin-bottom: .5rem; }
/* Tree node styles: gate (AND/OR) and leaf (attribute) */
.tree { display: flex; flex-direction: column; align-items: flex-start; gap: 0; }
.node { display: flex; align-items: center; gap: .35rem; padding: .28rem .55rem;
border-radius: 7px; font-weight: 600; font-size: .88rem;
border: 1.5px solid #cdd9e3; background: #f2f5f8;
transition: background .18s, border-color .18s; }
.node.gate { background: #e4ecf4; border-color: #a8c0d6; }
.node.leaf { background: #f8f9fa; border-color: #cdd9e3; }
.node.sat { background: #d4edda; border-color: #28a745; color: #155724; }
.node.unsat { background: #f8d7da; border-color: #dc3545; color: #721c24; }
.node .icon { font-size: 1rem; width: 1.1rem; text-align: center; }
/* Connector lines that show the tree hierarchy */
.row { display: flex; align-items: flex-start; }
.indent { width: 1.4rem; flex-shrink: 0; display: flex; flex-direction: column; align-items: center; }
.vline { width: 1.5px; background: #cdd9e3; flex: 1; min-height: .55rem; }
.hline { width: .7rem; height: 1.5px; background: #cdd9e3; align-self: center; }
.subtree { flex: 1; display: flex; flex-direction: column; gap: 0; }
/* Verdict banner below the tree */
.verdict { margin-top: .7rem; font-weight: 700; font-size: 1rem; min-height: 1.4em; }
.verdict.ok { color: #155724; }
.verdict.bad { color: #721c24; }
/* Toggle list of user attributes on the right */
.attr-list { display: flex; flex-direction: column; gap: .4rem; margin-bottom: .6rem; }
.attr-row { display: flex; align-items: center; gap: .45rem; cursor: pointer;
padding: .3rem .5rem; border-radius: 6px; border: 1.5px solid #cdd9e3;
background: #f2f5f8; transition: all .15s; user-select: none; }
.attr-row.on { background: #cce5ff; border-color: #3a86e8; }
.attr-row .dot { width: 10px; height: 10px; border-radius: 50%; background: #cdd9e3;
flex-shrink: 0; transition: background .15s; }
.attr-row.on .dot { background: #3a86e8; }
.ghost-btn { font: 600 13px system-ui; padding: .35rem .7rem; border: 1.5px solid #5a7088;
background: #fff; color: #5a7088; border-radius: 7px; cursor: pointer; }
// ABE policy-tree demo: evaluate a boolean access policy against a set of user attributes.
// No crypto here — just the satisfaction logic that mirrors what ABE does mathematically.
// Access policy: (role=doctor OR role=nurse) AND (dept=cardiology OR clearance>=3)
const POLICY = {
type: 'AND',
children: [
{
type: 'OR',
children: [
{ type: 'leaf', attr: 'role=doctor' },
{ type: 'leaf', attr: 'role=nurse' }
]
},
{
type: 'OR',
children: [
{ type: 'leaf', attr: 'dept=cardiology' },
{ type: 'leaf', attr: 'clearance>=3' }
]
}
]
};
// All possible attributes a user can hold in this scenario
const ALL_ATTRS = [
'role=doctor',
'role=nurse',
'dept=cardiology',
'dept=oncology',
'clearance>=3'
];
const userAttrs = new Set(); // set of attributes the current user holds — toggled via the UI
// Recursively evaluate whether a policy node is satisfied by the given attribute set
function evaluate(node, attrs) {
if (node.type === 'leaf') return attrs.has(node.attr);
if (node.type === 'AND') return node.children.every(c => evaluate(c, attrs));
if (node.type === 'OR') return node.children.some(c => evaluate(c, attrs));
return false;
}
// Render a policy node and its children as nested DOM rows with connector lines
function renderTree(node, attrs, container, depth) {
const row = document.createElement('div');
row.className = 'row';
if (depth > 0) {
const indent = document.createElement('div');
indent.className = 'indent';
const vl = document.createElement('div'); vl.className = 'vline';
const hl = document.createElement('div'); hl.className = 'hline';
indent.append(vl, hl);
row.appendChild(indent);
}
const subtree = document.createElement('div');
subtree.className = 'subtree';
const sat = evaluate(node, attrs);
const nodeEl = document.createElement('div');
if (node.type === 'leaf') {
nodeEl.className = 'node leaf ' + (sat ? 'sat' : 'unsat');
const icon = document.createElement('span'); icon.className = 'icon';
icon.textContent = sat ? '✓' : '✗';
const label = document.createElement('span');
label.textContent = node.attr;
nodeEl.append(icon, label);
subtree.appendChild(nodeEl);
} else {
nodeEl.className = 'node gate ' + (sat ? 'sat' : 'unsat');
const icon = document.createElement('span'); icon.className = 'icon';
icon.textContent = sat ? '✓' : '✗';
const label = document.createElement('span');
label.textContent = node.type; // AND or OR — not translated; used as a logic keyword
nodeEl.append(icon, label);
subtree.appendChild(nodeEl);
node.children.forEach(child => renderTree(child, attrs, subtree, depth + 1));
}
row.appendChild(subtree);
container.appendChild(row);
}
function render() {
const treeEl = document.getElementById('tree');
treeEl.innerHTML = '';
renderTree(POLICY, userAttrs, treeEl, 0);
const ok = evaluate(POLICY, userAttrs);
const v = document.getElementById('verdict');
if (ok) {
v.textContent = '✓ Policy satisfied — decryption succeeds';
v.className = 'verdict ok';
} else {
v.textContent = '✗ Policy not satisfied — decryption fails';
v.className = 'verdict bad';
}
}
function buildAttrList() {
const list = document.getElementById('attr-list');
list.innerHTML = '';
ALL_ATTRS.forEach(attr => {
const row = document.createElement('div');
row.className = 'attr-row' + (userAttrs.has(attr) ? ' on' : '');
const dot = document.createElement('span'); dot.className = 'dot';
const lbl = document.createElement('span'); lbl.textContent = attr;
row.append(dot, lbl);
row.addEventListener('click', () => {
if (userAttrs.has(attr)) userAttrs.delete(attr);
else userAttrs.add(attr);
row.classList.toggle('on', userAttrs.has(attr));
row.querySelector('.dot').style.background = userAttrs.has(attr) ? '#3a86e8' : '';
render();
});
list.appendChild(row);
});
}
document.getElementById('btn-reset').addEventListener('click', () => {
userAttrs.clear();
buildAttrList();
render();
});
buildAttrList();
render();
Notice that the user only decrypts when every gate on the path from root to leaves is satisfied. Flip any required attribute off and the root turns red — decryption fails. This is exactly what happens inside ABE: the authority issues a key that encodes attributes, and the ciphertext encodes the policy; the two must match or the math produces nothing useful.
Comments
Loading comments...