Skip to content

arkavo-org/opentdf-rs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

9 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OpenTDF-RS

A Rust implementation of the OpenTDF (Trusted Data Format) specification, providing data-centric security that travels with the data.

Overview

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.

Features

  • 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

Attribute-Based Access Control (ABAC)

OpenTDF-RS implements ABAC to provide fine-grained access control that is cryptographically bound to protected data.

ABAC Flow Diagram

Core Components

AttributeIdentifier

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(),
};

AttributeValue

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]);

Operators

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

AttributePolicy

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]),
]);

Policy Structure

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()],
    },
};

ABAC Integration with TDF

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");

Common Policy Patterns

Role-Based Restrictions

// Require specific role
let role_policy = AttributePolicy::condition(
    AttributeIdentifier::from_string("org:role")?,
    Operator::Equals,
    Some("ADMIN".into())
);

Multi-Department Access

// 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()
    ]))
);

Clearance Level with Time Restriction

// 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()],
    },
};

Location and Network Restrictions

// 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]);

Exclude Temporary Contractors

// 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;

MCP Server

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.

MCP Server Tools

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

Running the MCP Server

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.

Using with Claude Code

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>"}

ABAC Testing with MCP

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:

  1. Attribute namespace definition with hierarchies
  2. User attribute assignment
  3. Policy creation with attribute conditions
  4. TDF creation with policy binding
  5. Access evaluation based on attributes
  6. Policy binding verification
  7. 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.

Development

MCP Server Development

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:

  1. Tool Definitions: Define tools with both schema and inputSchema fields for compatibility
  2. Protocol Version: Use the latest MCP protocol version (currently "2024-11-05")
  3. Response Format: Ensure all responses follow the JSON-RPC 2.0 specification
  4. Error Handling: Use standard JSON-RPC error codes (-32xxx)
  5. 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

Getting Started

Installation

Add to your Cargo.toml:

[dependencies]
opentdf = "0.3.0"

Basic Usage

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()?;

License

This project is licensed under [LICENSE].

About

OpenTDF in Rust community-driven

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published