A Rust implementation of the OpenTDF (Trusted Data Format) specification, providing data-centric security that travels with the data.
OpenTDF-RS enables cryptographic binding of access policies directly to data objects, supporting a Zero Trust security model with continuous verification. This library allows secure data sharing across organizations and industries.
- TDF Archive Creation and Reading
- Cryptographic Operations (AES-256-GCM encryption)
- Policy Binding through HMAC-SHA256
- Streaming Operations for Efficient Data Handling
- Attribute-Based Access Control (ABAC): Fine-grained access control using attributes
- Hierarchical Attributes: Support for attributes with inheritance relationships
- Time-Based Constraints: Policies with validity periods
- Logical Operators: AND, OR, NOT combinations for complex policies
- Comprehensive Audit Logging: Detailed records of access attempts and attribute evaluation
OpenTDF-RS implements ABAC to provide fine-grained access control that is cryptographically bound to protected data.
Represents a uniquely identifiable attribute with namespace and name:
// Create from string in "namespace:name" format
let attr_id = AttributeIdentifier::from_string("gov.example:clearance")?;
// Or create directly
let attr_id = AttributeIdentifier {
namespace: "gov.example".to_string(),
name: "clearance".to_string(),
};
Supports multiple data types for flexible attribute representation:
// String value
let value = AttributeValue::String("TOP_SECRET".to_string());
// Numeric value
let value = AttributeValue::Number(42.0);
// Boolean value
let value = AttributeValue::Boolean(true);
// Date/time value
let value = AttributeValue::DateTime(chrono::Utc::now());
// Array values
let value = AttributeValue::StringArray(vec!["ENGINEERING".to_string(), "EXECUTIVE".to_string()]);
let value = AttributeValue::NumberArray(vec![1.0, 2.0, 3.0]);
Rich set of comparison operators for attribute evaluation:
Operator | Description | Example |
---|---|---|
Equals | Exact match | department == "ENGINEERING" |
NotEquals | Negated match | department != "FINANCE" |
GreaterThan | Numeric comparison | age > 21 |
GreaterThanOrEqual | Numeric comparison | priority >= 5 |
LessThan | Numeric comparison | risk < 3 |
LessThanOrEqual | Numeric comparison | level <= 4 |
Contains | String contains | name contains "Admin" |
In | Value in array | department in ["HR", "LEGAL"] |
AllOf | All array values present | certifications allof ["ISO", "SOC2"] |
AnyOf | Any array values present | skills anyof ["Rust", "C++"] |
NotIn | Value not in array | restricted_country notin ["US", "EU"] |
MinimumOf | Hierarchical minimum | clearance minimumof "SECRET" |
MaximumOf | Hierarchical maximum | clearance maximumof "TOP_SECRET" |
Present | Attribute exists | employee_id present |
NotPresent | Attribute doesn't exist | terminated notpresent |
Build complex policy expressions with logical operators:
// Simple condition
let is_executive = AttributePolicy::condition(
AttributeIdentifier::from_string("gov.example:role")?,
Operator::Equals,
Some("EXECUTIVE".into())
);
// Logical AND
let and_policy = AttributePolicy::and(vec![condition1, condition2, condition3]);
// Logical OR
let or_policy = AttributePolicy::or(vec![condition1, condition2, condition3]);
// Logical NOT (using operator overloading)
let not_policy = !condition1;
// Complex nested policy
let complex_policy = AttributePolicy::or(vec![
AttributePolicy::and(vec![condition1, condition2]),
AttributePolicy::and(vec![condition3, !condition4]),
]);
Complete policy with time constraints and dissemination:
// Create policy with attribute conditions and recipients
let policy = Policy {
uuid: uuid::Uuid::new_v4().to_string(),
valid_from: Some(chrono::Utc::now()),
valid_to: Some(chrono::Utc::now() + chrono::Duration::days(30)),
body: PolicyBody {
attributes: vec![attribute_policy],
dissem: vec!["[email protected]".to_string()],
},
};
use opentdf::{
AttributePolicy, AttributeIdentifier, AttributeValue, Operator,
Policy, PolicyBody, TdfArchive, TdfArchiveBuilder, TdfEncryption, TdfManifest
};
use std::collections::HashMap;
// 1. Create policy with attribute conditions
let clearance = AttributePolicy::condition(
AttributeIdentifier::from_string("gov.example:clearance")?,
Operator::MinimumOf,
Some("SECRET".into())
);
let department = AttributePolicy::condition(
AttributeIdentifier::from_string("gov.example:department")?,
Operator::In,
Some(AttributeValue::StringArray(vec![
"ENGINEERING".to_string(),
"EXECUTIVE".to_string()
]))
);
// Combine with logical AND
let combined_policy = AttributePolicy::and(vec![clearance, department]);
// Create full policy with time constraints
let policy = Policy {
uuid: uuid::Uuid::new_v4().to_string(),
valid_from: Some(chrono::Utc::now()),
valid_to: Some(chrono::Utc::now() + chrono::Duration::days(30)),
body: PolicyBody {
attributes: vec![combined_policy],
dissem: vec!["[email protected]".to_string()],
},
};
// 2. Encrypt data using TDF
let data = b"Sensitive information".to_vec();
let tdf_encryption = TdfEncryption::new()?;
let encrypted_payload = tdf_encryption.encrypt(&data)?;
// 3. Create manifest with KAS information
let mut manifest = TdfManifest::new(
"0.payload".to_string(),
"https://kas.example.com".to_string(),
);
manifest.encryption_information.method.algorithm = "AES-256-GCM".to_string();
manifest.encryption_information.method.iv = encrypted_payload.iv.clone();
manifest.encryption_information.key_access[0].wrapped_key =
encrypted_payload.encrypted_key.clone();
// 4. Bind policy to manifest and generate cryptographic binding
manifest.set_policy(&policy)?;
manifest.encryption_information.key_access[0].generate_policy_binding(
&policy,
tdf_encryption.policy_key()
)?;
// 5. Create TDF archive with encrypted payload
let mut builder = TdfArchiveBuilder::new("example.tdf")?;
builder.add_entry(&manifest, &encrypted_payload.ciphertext.as_bytes(), 0)?;
builder.finish()?;
// 6. Later: Evaluate access based on user attributes
let user_attrs = HashMap::from([
(
AttributeIdentifier::from_string("gov.example:clearance")?,
AttributeValue::String("TOP_SECRET".to_string())
),
(
AttributeIdentifier::from_string("gov.example:department")?,
AttributeValue::String("ENGINEERING".to_string())
),
]);
let access_granted = combined_policy.evaluate(&user_attrs)?;
assert!(access_granted, "User should have access based on attributes");
// Require specific role
let role_policy = AttributePolicy::condition(
AttributeIdentifier::from_string("org:role")?,
Operator::Equals,
Some("ADMIN".into())
);
// Allow access for multiple departments
let dept_policy = AttributePolicy::condition(
AttributeIdentifier::from_string("org:department")?,
Operator::In,
Some(AttributeValue::StringArray(vec![
"FINANCE".to_string(),
"LEGAL".to_string(),
"EXECUTIVE".to_string()
]))
);
// Require minimum clearance and time-bound access
let clearance_policy = AttributePolicy::condition(
AttributeIdentifier::from_string("gov:clearance")?,
Operator::MinimumOf,
Some("SECRET".into())
);
let policy = Policy {
uuid: uuid::Uuid::new_v4().to_string(),
// Valid for next 24 hours only
valid_from: Some(chrono::Utc::now()),
valid_to: Some(chrono::Utc::now() + chrono::Duration::hours(24)),
body: PolicyBody {
attributes: vec![clearance_policy],
dissem: vec!["[email protected]".to_string()],
},
};
// Require specific location AND secure network
let location_policy = AttributePolicy::condition(
AttributeIdentifier::from_string("env:location")?,
Operator::Equals,
Some("HEADQUARTERS".into())
);
let network_policy = AttributePolicy::condition(
AttributeIdentifier::from_string("env:network_type")?,
Operator::Equals,
Some("SECURE".into())
);
let security_policy = AttributePolicy::and(vec![location_policy, network_policy]);
// Employees only (NOT contractors)
let contractor_check = AttributePolicy::condition(
AttributeIdentifier::from_string("org:employment_type")?,
Operator::Equals,
Some("CONTRACTOR".into())
);
let employees_only = !contractor_check;
OpenTDF-RS includes an implementation of the Model Context Protocol (MCP) server, allowing AI assistants and other tools to interact with TDF capabilities via a standardized API.
The MCP server provides the following tools:
Tool Name | Description |
---|---|
tdf_create |
Creates a new TDF archive with encrypted data |
tdf_read |
Reads contents from a TDF archive |
encrypt |
Encrypts data using TDF encryption methods |
decrypt |
Decrypts TDF-encrypted data |
policy_create |
Creates a new policy for TDF encryption |
policy_validate |
Validates a policy against a TDF archive |
attribute_define |
Defines attribute namespaces with optional hierarchies |
user_attributes |
Sets user attributes for testing access control |
access_evaluate |
Evaluates whether a user with attributes can access protected content with detailed audit records |
policy_binding_verify |
Verifies the cryptographic binding of a policy to a TDF |
All access attempts and attribute evaluations are comprehensively logged for compliance and auditing purposes. The audit logging system captures detailed information about each operation, including:
- The requesting entity identifiers
- Complete sets of attributes presented
- Attribute sources and verification status
- Detailed evaluation results for each attribute in the policy
- Final access decisions with timestamps
- Policy version information
To run the MCP server and interact with it via Claude or other MCP-compatible clients:
cargo run -p opentdf-mcp-server
The server listens on stdio for JSON-RPC messages, making it compatible with tools like Claude Code that use the MCP protocol for communication.
Claude Code can connect to the MCP server to perform TDF operations:
claude --mcp="cargo run -p opentdf-mcp-server"
This starts Claude with the MCP server, allowing you to use TDF capabilities directly within the chat interface.
Example commands:
/mcp opentdf tdf_create {"data": "SGVsbG8gV29ybGQh", "kas_url": "https://kas.example.com", "policy": {"uuid": "sample-uuid", "body": {"attributes": [{"attribute": "gov.example:clearance", "operator": "MinimumOf", "value": "secret"}], "dissem": ["[email protected]"]}}}
/mcp opentdf tdf_read {"tdf_data": "<base64-encoded-tdf-data>"}
The MCP server supports comprehensive ABAC functionality testing:
# Basic ABAC testing
node tools/test-mcp.js
# Comprehensive attribute access logging test
node tools/audit-logging-test.js
These scripts demonstrate:
- Attribute namespace definition with hierarchies
- User attribute assignment
- Policy creation with attribute conditions
- TDF creation with policy binding
- Access evaluation based on attributes
- Policy binding verification
- Comprehensive audit logging and compliance reporting
The audit logging test generates detailed compliance reports in the tools/reports
directory. See tools/audit-guide.md
for more information on the audit logging system.
The MCP server implements the JSON-RPC 2.0 protocol over stdio to provide TDF capabilities to clients. When developing or extending the MCP server:
- Tool Definitions: Define tools with both
schema
andinputSchema
fields for compatibility - Protocol Version: Use the latest MCP protocol version (currently "2024-11-05")
- Response Format: Ensure all responses follow the JSON-RPC 2.0 specification
- Error Handling: Use standard JSON-RPC error codes (-32xxx)
- Testing: Use
tools/test-mcp.js
to verify functionality
If adding new tools, remember to:
- Add the tool to both the initialize response and listTools response
- Implement proper parameter validation
- Follow the JSON-RPC request/response flow
- Document the tool in this README
Add to your Cargo.toml:
[dependencies]
opentdf = "0.3.0"
use opentdf::{TdfArchive, TdfArchiveBuilder, TdfEncryption, TdfManifest};
// Create a new TDF encryption
let tdf_encryption = TdfEncryption::new()?;
// Encrypt data
let data = b"Sensitive data".to_vec();
let encrypted_payload = tdf_encryption.encrypt(&data)?;
// Create manifest
let mut manifest = TdfManifest::new(
"0.payload".to_string(),
"http://kas.example.com".to_string(),
);
// Update manifest with encryption details
manifest.encryption_information.method.algorithm = "AES-256-GCM".to_string();
manifest.encryption_information.method.iv = encrypted_payload.iv.clone();
manifest.encryption_information.key_access[0].wrapped_key =
encrypted_payload.encrypted_key.clone();
// Set ABAC policy
let policy = Policy::new(
AttributePolicy::condition(
AttributeIdentifier::from_string("gov.example:clearance")?,
Operator::MinimumOf,
Some("SECRET".into())
),
vec!["[email protected]".to_string()]
);
manifest.set_policy(&policy)?;
manifest.encryption_information.key_access[0].generate_policy_binding(
&policy,
tdf_encryption.policy_key()
)?;
// Create TDF archive
let mut builder = TdfArchiveBuilder::new("example.tdf")?;
builder.add_entry(&manifest, &encrypted_payload.ciphertext.as_bytes(), 0)?;
builder.finish()?;
This project is licensed under [LICENSE].