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 |
|---|---|---|
|
|
Greater than or equal |
|
|
Greater than |
|
|
Less than or equal |
|
|
Less than |
|
|
Equality |
NumericAttributeHelper Class¶
- class charm.toolbox.ABEnumeric.NumericAttributeHelper(num_bits=32, strict=False)[source]¶
Bases:
objectHelper 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 |
|---|---|
|
|
|
|
|
|
|
|
|
|
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.
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