ABEnumeric - Numeric Attribute Encoding

This module implements the “bag of bits” technique from the Bethencourt-Sahai-Waters CP-ABE paper (IEEE S&P 2007) for representing numeric attributes and comparisons.

Overview

Traditional ABE policies use string attributes like ADMIN or DEPARTMENT_HR. This module extends ABE to support numeric comparisons like age >= 21 or level > 5.

The technique converts numeric comparisons into boolean attribute expressions that can be evaluated using standard ABE schemes.

Quick Start

from charm.toolbox.ABEnumeric import NumericAttributeHelper
from charm.schemes.abenc.abenc_bsw07 import CPabe_BSW07
from charm.toolbox.pairinggroup import PairingGroup

# Setup
group = PairingGroup('SS512')
cpabe = CPabe_BSW07(group)
helper = NumericAttributeHelper(num_bits=8)

# Encryption with numeric policy
policy = helper.expand_policy("age >= 21 and department == 5")
# ... use expanded policy with cpabe.encrypt()

# Key generation with numeric attributes
user_attrs = helper.user_attributes({'age': 25, 'department': 5})
# ... use user_attrs with cpabe.keygen()

Supported Operators

Operator

Example

Description

>=

age >= 21

Greater than or equal

>

level > 5

Greater than

<=

priority <= 3

Less than or equal

<

score < 100

Less than

==

department == 7

Equality

NumericAttributeHelper Class

class charm.toolbox.ABEnumeric.NumericAttributeHelper(num_bits=32, strict=False)[source]

Bases: object

Helper class for working with numeric attributes in CP-ABE.

This class provides a high-level interface for: - Expanding policies with numeric comparisons - Converting numeric attribute values to bit representations

Usage:

helper = NumericAttributeHelper(num_bits=16) # 16-bit integers

# For encryption: expand the policy policy = helper.expand_policy(“age >= 21 and level > 5”)

# For key generation: get user attributes user_attrs = helper.user_attributes({‘age’: 25, ‘level’: 7, ‘role’: ‘manager’}) # Returns: [‘AGE#B0#1’, ‘AGE#B1#0’, …, ‘LEVEL#B0#1’, …, ‘MANAGER’]

Attributes:

num_bits: Number of bits for numeric representation max_value: Maximum representable value for the configured bit width

check_satisfaction(user_attrs, required_comparison, attr_name, operator, value)[source]

Check if a user’s numeric attribute satisfies a comparison.

This is a utility for testing/debugging.

Args:

user_attrs: Dict with user’s attribute values attr_name: Name of the numeric attribute operator: Comparison operator value: Comparison value

Returns:

True if the comparison is satisfied

expand_negated_policy(attr_name, operator, value)[source]

Expand a negated numeric comparison into a bit-level policy expression.

This method first negates the comparison, then expands it to bit-level attributes.

Args:

attr_name: The attribute name (e.g., ‘age’, ‘level’) operator: The original operator to negate (‘==’, ‘>’, ‘>=’, ‘<’, ‘<=’) value: The numeric value in the comparison

Returns:

A policy string with bit-level attributes representing NOT (attr op value)

Example:
>>> helper = NumericAttributeHelper(num_bits=8)
>>> # NOT (age >= 21) becomes age < 21
>>> policy = helper.expand_negated_policy('age', '>=', 21)
>>> # Returns the bit-level encoding of age < 21
expand_policy(policy_str)[source]

Expand numeric comparisons in a policy string.

Args:

policy_str: Policy with numeric comparisons like “age >= 21”

Returns:

Expanded policy with bit-level attributes

Raises:

ValueError: If policy_str is None NumericAttributeError: In strict mode, if expansion fails

negate_comparison(attr_name, operator, value)[source]

Convert a negated numeric comparison to its equivalent positive form.

This is a convenience wrapper around the module-level negate_comparison() function.

Args:

attr_name: The attribute name (e.g., ‘age’, ‘level’) operator: The original operator to negate (‘==’, ‘>’, ‘>=’, ‘<’, ‘<=’) value: The numeric value in the comparison

Returns:

For simple negations: (attr_name, negated_operator, value) For equality negation: ((attr_name, ‘<’, value), (attr_name, ‘>’, value))

Example:
>>> helper = NumericAttributeHelper(num_bits=8)
>>> helper.negate_comparison('age', '>=', 21)
('age', '<', 21)
user_attributes(attr_dict)[source]

Convert a dictionary of user attributes to a list suitable for ABE.

Numeric values are converted to bit representations. String values are uppercased as per standard attribute handling.

Args:
attr_dict: Dictionary mapping attribute names to values

e.g., {‘age’: 25, ‘role’: ‘admin’, ‘level’: 5}

Returns:

List of attribute strings for key generation

Raises:

ValueError: If a numeric value is negative BitOverflowError: If a numeric value exceeds the bit width AttributeNameConflictError: If an attribute name conflicts with encoding

Negation Support

Important: The underlying Monotone Span Program (MSP) used in ABE schemes does NOT support logical negation. This is a fundamental cryptographic limitation.

The PolicyParser’s ! prefix creates an attribute with ! in its name, but this is NOT logical negation.

Workaround: Use equivalent expressions:

Negated Expression

Equivalent Positive Form

NOT (age >= 21)

age < 21

NOT (age > 21)

age <= 21

NOT (age <= 21)

age > 21

NOT (age < 21)

age >= 21

NOT (age == 21)

(age < 21) or (age > 21)

Helper Functions

charm.toolbox.ABEnumeric.negate_comparison(attr_name, operator, value)[source]

Convert a negated numeric comparison to its equivalent positive form.

Since Monotone Span Programs (MSP) used in ABE do not support logical negation, this function converts negated comparisons to equivalent positive expressions.

Args:

attr_name: The attribute name (e.g., ‘age’, ‘level’) operator: The original operator to negate (‘==’, ‘>’, ‘>=’, ‘<’, ‘<=’) value: The numeric value in the comparison

Returns:
For simple negations (>=, >, <=, <):

A tuple (attr_name, negated_operator, value)

For equality negation (==):

A tuple of two comparisons: ((attr_name, ‘<’, value), (attr_name, ‘>’, value)) These should be combined with OR in the policy.

Raises:

InvalidOperatorError: If operator is not supported

Examples:
>>> negate_comparison('age', '>=', 21)
('age', '<', 21)
>>> negate_comparison('age', '>', 21)
('age', '<=', 21)
>>> negate_comparison('age', '==', 21)
(('age', '<', 21), ('age', '>', 21))
Usage in policies:

# Instead of: NOT (age >= 21) negated = negate_comparison(‘age’, ‘>=’, 21) policy = f”{negated[0]} {negated[1]} {negated[2]}” # “age < 21”

# For equality negation: negated = negate_comparison(‘age’, ‘==’, 21) # Results in: (age < 21) or (age > 21) policy = f”({negated[0][0]} {negated[0][1]} {negated[0][2]}) or ({negated[1][0]} {negated[1][1]} {negated[1][2]})”

charm.toolbox.ABEnumeric.negate_comparison_to_policy(attr_name, operator, value)[source]

Convert a negated numeric comparison directly to a policy string.

This is a convenience function that calls negate_comparison() and formats the result as a policy string ready for use.

Args:

attr_name: The attribute name (e.g., ‘age’, ‘level’) operator: The original operator to negate (‘==’, ‘>’, ‘>=’, ‘<’, ‘<=’) value: The numeric value in the comparison

Returns:

A policy string representing the negated comparison.

Examples:
>>> negate_comparison_to_policy('age', '>=', 21)
'age < 21'
>>> negate_comparison_to_policy('age', '==', 21)
'(age < 21) or (age > 21)'

Example:

from charm.toolbox.ABEnumeric import negate_comparison, negate_comparison_to_policy

# Convert NOT (age >= 21) to equivalent
result = negate_comparison('age', '>=', 21)
# Returns: ('age', '<', 21)

# Get as policy string
policy = negate_comparison_to_policy('age', '>=', 21)
# Returns: 'age < 21'

# Equality negation returns OR expression
result = negate_comparison('age', '==', 21)
# Returns: (('age', '<', 21), ('age', '>', 21))

Exception Classes

exception charm.toolbox.ABEnumeric.NumericAttributeError[source]

Base exception for numeric attribute encoding errors.

exception charm.toolbox.ABEnumeric.BitOverflowError[source]

Raised when a value exceeds the representable range for the given bit width.

exception charm.toolbox.ABEnumeric.InvalidBitWidthError[source]

Raised when an invalid bit width is specified.

exception charm.toolbox.ABEnumeric.InvalidOperatorError[source]

Raised when an unsupported comparison operator is used.

exception charm.toolbox.ABEnumeric.AttributeNameConflictError[source]

Raised when an attribute name conflicts with the bit encoding format.

Low-Level Functions

These functions are used internally but can be called directly for advanced use cases.

charm.toolbox.ABEnumeric.int_to_bits(value, num_bits=32)[source]

Convert an integer to a list of bits (LSB first).

Args:

value: Non-negative integer to convert num_bits: Number of bits in the representation

Returns:

List of bits (0 or 1), LSB first

Raises:

ValueError: If value is negative BitOverflowError: If value exceeds bit width InvalidBitWidthError: If num_bits is invalid

charm.toolbox.ABEnumeric.expand_numeric_comparison(attr_name, operator, value, num_bits=32)[source]

Expand a numeric comparison into a boolean policy expression.

Args:

attr_name: The attribute name (e.g., ‘age’, ‘level’) operator: One of ‘==’, ‘>’, ‘>=’, ‘<’, ‘<=’ value: The numeric value to compare against num_bits: Number of bits for the representation (default 32)

Returns:

A string policy expression using bit-level attributes

Raises:

InvalidOperatorError: If operator is not supported AttributeNameConflictError: If attr_name conflicts with encoding format ValueError: If value is negative BitOverflowError: If value exceeds bit width InvalidBitWidthError: If num_bits is invalid

charm.toolbox.ABEnumeric.preprocess_numeric_policy(policy_str, num_bits=32, strict=False)[source]

Preprocess a policy string to expand numeric comparisons.

Takes a policy like:

‘(age >= 21 and clearance > 3) or admin’

And expands numeric comparisons into bit-level attributes:

‘((age#b4#1 or …) and (clearance#b…)) or admin’

Args:

policy_str: Original policy string with numeric comparisons num_bits: Number of bits for numeric representation strict: If True, raise exceptions on errors; if False, return original expression on error (default False)

Returns:

Expanded policy string with bit-level attributes

Raises:

ValueError: If policy_str is None InvalidBitWidthError: If num_bits is invalid

Notes:
  • Empty strings or whitespace-only strings return empty string

  • Malformed expressions that don’t match the pattern are left unchanged

  • In non-strict mode, errors during expansion leave the original expression