PersonaDID Class
Overview
The PersonaDID
class combines two keychain DIDs since personas track two keychains - one for encryption and one for signature operations. This provides a unified identifier that encompasses both cryptographic capabilities of a persona.
Package: keychain.identity.persona_did
from keychain.identity.persona_did import PersonaDID
Constructor
def __init__(
self,
did_string: Optional[str] = None,
encr_keychain_did: Optional[KeychainDID] = None,
sign_keychain_did: Optional[KeychainDID] = None,
c_pointer: Optional[ctypes.c_void_p] = None,
) -> None
Initialize a PersonaDID object from a DID string, keychain DIDs, or C pointer.
Parameters:
-
did_string
(str
, optional) - PersonaDID string to parse (W3C DID format) -
encr_keychain_did
(KeychainDID
, optional) - The encryption keychain DID -
sign_keychain_did
(KeychainDID
, optional) - The signature keychain DID -
c_pointer
(ctypes.c_void_p
, optional) - Existing C pointer to wrap
Example:
from keychain.identity.keychain_did import KeychainDID
from keychain.constants import KeychainType
# Create from PersonaDID string
persona_did = PersonaDID("did:kcn-persona:alice.company.com")
# Create from keychain DIDs
encrypt_did = KeychainDID.from_keychain_components(
KeychainType.ENCRYPT, "btc_regtest1", "encrypt_abc123"
)
sign_did = KeychainDID.from_keychain_components(
KeychainType.SIGN, "btc_regtest1", "sign_xyz789"
)
persona_did = PersonaDID(
encr_keychain_did=encrypt_did,
sign_keychain_did=sign_did
)
# Create empty PersonaDID
empty_did = PersonaDID()
Properties
Keychain DID Properties
Methods
copy()
def copy(self) -> "PersonaDID"
Create a deep copy of this PersonaDID.
Returns: New PersonaDID
object that is a deep copy
serialize()
def serialize(self) -> bytes
Serialize the PersonaDID to bytes as defined in the W3C specification.
Returns: The PersonaDID serialized as UTF-8 encoded bytes
Example:
persona_did = PersonaDID("did:kcn-persona:alice.company.com")
serialized = persona_did.serialize()
# Returns: b"did:kcn-persona:alice.company.com"
data_type()
def data_type(self) -> DataType
Get the data type.
Returns: DataType.DID
(persona DIDs use the same data type as regular DIDs)
is_null()
def is_null(self) -> bool
Check if this is a null PersonaDID.
Returns: True
if this is a null/empty PersonaDID, False
otherwise
parse_keychain_dids()
def parse_keychain_dids(self) -> Tuple[KeychainDID, KeychainDID]
Parse the PersonaDID into its keychain DID components.
Returns: A tuple of (encryption_keychain_did, signature_keychain_did)
Example:
persona_did = PersonaDID(encr_keychain_did=encrypt_did, sign_keychain_did=sign_did)
encr_did, sign_did = persona_did.parse_keychain_dids()
print(f"Encryption DID: {encr_did}")
print(f"Signature DID: {sign_did}")
Class Methods
from_keychain_dids(encr_keychain_did, sign_keychain_did)
@classmethod
def from_keychain_dids(
cls, encr_keychain_did: KeychainDID, sign_keychain_did: KeychainDID
) -> "PersonaDID"
Create a PersonaDID from two keychain DIDs.
Parameters:
-
encr_keychain_did
(KeychainDID
) - The encryption keychain DID -
sign_keychain_did
(KeychainDID
) - The signature keychain DID
Returns: New PersonaDID
object constructed from the keychain DIDs
Example:
from keychain.identity.keychain_did import KeychainDID
from keychain.constants import KeychainType
encrypt_did = KeychainDID.from_keychain_components(
KeychainType.ENCRYPT, "btc_regtest1", "encrypt_abc123"
)
sign_did = KeychainDID.from_keychain_components(
KeychainType.SIGN, "btc_regtest1", "sign_xyz789"
)
persona_did = PersonaDID.from_keychain_dids(encrypt_did, sign_did)
print(str(persona_did))
Comparison Methods
Example: Working with PersonaDIDs
from keychain.identity.persona_did import PersonaDID
from keychain.identity.keychain_did import KeychainDID
from keychain.constants import KeychainType
# Create keychain DIDs for encryption and signing
encrypt_did = KeychainDID.from_keychain_components(
KeychainType.ENCRYPT,
"btc_regtest1",
"alice_encrypt_key"
)
sign_did = KeychainDID.from_keychain_components(
KeychainType.SIGN,
"btc_regtest1",
"alice_sign_key"
)
# Create PersonaDID from keychain DIDs
persona_did = PersonaDID.from_keychain_dids(encrypt_did, sign_did)
print(f"Persona DID: {persona_did}")
# Parse PersonaDID components
print("\n=== Persona DID Analysis ===")
print(f"Scheme: {persona_did.scheme}")
print(f"Method: {persona_did.method}")
print(f"Method-specific ID: {persona_did.method_specific_id}")
# Access keychain DIDs
encr_did = persona_did.encr_keychain_did
sign_did = persona_did.sign_keychain_did
print(f"\nEncryption DID: {encr_did}")
print(f" Type: {encr_did.keychain_type_str}")
print(f" Network: {encr_did.network}")
print(f" Network ID: {encr_did.network_specific_id}")
print(f"\nSignature DID: {sign_did}")
print(f" Type: {sign_did.keychain_type_str}")
print(f" Network: {sign_did.network}")
print(f" Network ID: {sign_did.network_specific_id}")
# Parse keychain DIDs
encr_did_parsed, sign_did_parsed = persona_did.parse_keychain_dids()
print(f"\nParsed encryption DID: {encr_did_parsed}")
print(f"Parsed signature DID: {sign_did_parsed}")
# Verify equality
if encr_did == encr_did_parsed and sign_did == sign_did_parsed:
print("✓ Keychain DID parsing successful")
# Create from string and verify round-trip
did_string = str(persona_did)
reconstructed = PersonaDID(did_string)
if persona_did == reconstructed:
print("✓ String serialization/deserialization successful")
# Copy PersonaDID
copied_did = persona_did.copy()
if persona_did == copied_did:
print("✓ Copy operation successful")
Example: Persona Identity Management
from keychain.identity.persona_did import PersonaDID
from keychain.identity.keychain_did import KeychainDID
from keychain.constants import KeychainType
from typing import Dict, List, Optional
class PersonaIdentityManager:
"""Manage persona identities and their associated DIDs."""
def __init__(self):
self._personas: Dict[str, PersonaDID] = {}
self._by_network: Dict[str, List[str]] = {}
def register_persona(self, name: str, persona_did: PersonaDID) -> None:
"""Register a persona identity."""
self._personas[name] = persona_did.copy()
# Index by network
encr_did = persona_did.encr_keychain_did
network = encr_did.network
if network not in self._by_network:
self._by_network[network] = []
if name not in self._by_network[network]:
self._by_network[network].append(name)
def get_persona_did(self, name: str) -> Optional[PersonaDID]:
"""Get a persona DID by name."""
if name in self._personas:
return self._personas[name].copy()
return None
def get_personas_for_network(self, network: str) -> List[str]:
"""Get all persona names for a specific network."""
return self._by_network.get(network, []).copy()
def create_persona_from_components(
self,
name: str,
network: str,
base_id: str
) -> PersonaDID:
"""Create a new persona DID from components."""
# Create keychain DIDs
encrypt_did = KeychainDID.from_keychain_components(
KeychainType.ENCRYPT,
network,
f"{base_id}_encrypt"
)
sign_did = KeychainDID.from_keychain_components(
KeychainType.SIGN,
network,
f"{base_id}_sign"
)
# Create persona DID
persona_did = PersonaDID.from_keychain_dids(encrypt_did, sign_did)
# Register it
self.register_persona(name, persona_did)
return persona_did
def get_persona_network_info(self, name: str) -> Optional[Dict]:
"""Get network information for a persona."""
persona_did = self.get_persona_did(name)
if not persona_did:
return None
encr_did = persona_did.encr_keychain_did
sign_did = persona_did.sign_keychain_did
return {
"name": name,
"persona_did": str(persona_did),
"network": encr_did.network,
"encryption_did": str(encr_did),
"signature_did": str(sign_did),
"encryption_network_id": encr_did.network_specific_id,
"signature_network_id": sign_did.network_specific_id
}
def list_personas(self) -> List[str]:
"""List all registered persona names."""
return list(self._personas.keys())
def get_networks(self) -> List[str]:
"""Get all networks with registered personas."""
return list(self._by_network.keys())
def export_persona_info(self) -> Dict:
"""Export information about all personas."""
export_data = {
"personas": {},
"by_network": self._by_network.copy(),
"total_personas": len(self._personas)
}
for name, persona_did in self._personas.items():
info = self.get_persona_network_info(name)
export_data["personas"][name] = info
return export_data
# Usage example
def persona_identity_manager_example():
"""Demonstrate persona identity management."""
manager = PersonaIdentityManager()
print("=== Persona Identity Manager Demo ===")
# Create personas for different networks
personas = [
("alice", "btc_regtest1", "alice_keys"),
("bob", "btc_regtest1", "bob_keys"),
("charlie", "btc_testnet", "charlie_keys"),
("diana", "btc_mainnet", "diana_keys")
]
for name, network, base_id in personas:
persona_did = manager.create_persona_from_components(name, network, base_id)
print(f"Created persona '{name}': {persona_did}")
# List personas by network
print(f"\n=== Personas by Network ===")
for network in manager.get_networks():
network_personas = manager.get_personas_for_network(network)
print(f"{network}: {', '.join(network_personas)}")
# Get detailed information for each persona
print(f"\n=== Persona Details ===")
for name in manager.list_personas():
info = manager.get_persona_network_info(name)
if info:
print(f"\n{info['name']}:")
print(f" Persona DID: {info['persona_did']}")
print(f" Network: {info['network']}")
print(f" Encryption DID: {info['encryption_did']}")
print(f" Signature DID: {info['signature_did']}")
# Export all persona information
export_data = manager.export_persona_info()
print(f"\n=== Export Summary ===")
print(f"Total personas: {export_data['total_personas']}")
print(f"Networks: {list(export_data['by_network'].keys())}")
if __name__ == "__main__":
persona_identity_manager_example()
Example: PersonaDID Validation and Analysis
from keychain.identity.persona_did import PersonaDID
from keychain.identity.keychain_did import KeychainDID
from keychain.constants import KeychainType
class PersonaDIDAnalyzer:
"""Utility class for PersonaDID analysis and validation."""
@staticmethod
def validate_persona_did(did_string: str) -> dict:
"""Validate a PersonaDID string."""
try:
persona_did = PersonaDID(did_string)
if persona_did.is_null():
return {
"valid": False,
"error": "PersonaDID is null or empty"
}
# Parse components
scheme, method, method_id = persona_did.parse_components()
# Basic validation
if scheme != "did":
return {
"valid": False,
"error": f"Invalid scheme: {scheme} (expected 'did')"
}
# Get keychain DIDs
try:
encr_did = persona_did.encr_keychain_did
sign_did = persona_did.sign_keychain_did
# Validate keychain DIDs
if encr_did.keychain_type != KeychainType.ENCRYPT:
return {
"valid": False,
"error": f"Invalid encryption keychain type: {encr_did.keychain_type}"
}
if sign_did.keychain_type != KeychainType.SIGN:
return {
"valid": False,
"error": f"Invalid signature keychain type: {sign_did.keychain_type}"
}
# Check if both DIDs are on the same network
if encr_did.network != sign_did.network:
return {
"valid": False,
"error": f"Network mismatch: encrypt={encr_did.network}, sign={sign_did.network}"
}
return {
"valid": True,
"scheme": scheme,
"method": method,
"method_specific_id": method_id,
"network": encr_did.network,
"encryption_did": str(encr_did),
"signature_did": str(sign_did),
"serialized": persona_did.serialize().decode('utf-8')
}
except Exception as e:
return {
"valid": False,
"error": f"Failed to access keychain DIDs: {str(e)}"
}
except Exception as e:
return {
"valid": False,
"error": f"Failed to parse PersonaDID: {str(e)}"
}
@staticmethod
def compare_persona_dids(did1: PersonaDID, did2: PersonaDID) -> dict:
"""Compare two PersonaDIDs and provide detailed analysis."""
analysis = {
"equal": did1 == did2,
"same_network": False,
"same_method": False,
"differences": []
}
# Compare basic DID properties
if did1.scheme != did2.scheme:
analysis["differences"].append(f"Different schemes: {did1.scheme} vs {did2.scheme}")
if did1.method != did2.method:
analysis["differences"].append(f"Different methods: {did1.method} vs {did2.method}")
else:
analysis["same_method"] = True
if did1.method_specific_id != did2.method_specific_id:
analysis["differences"].append(f"Different method-specific IDs: {did1.method_specific_id} vs {did2.method_specific_id}")
# Compare keychain DIDs
try:
encr1, sign1 = did1.parse_keychain_dids()
encr2, sign2 = did2.parse_keychain_dids()
if encr1.network == encr2.network and sign1.network == sign2.network:
analysis["same_network"] = True
else:
analysis["differences"].append(f"Different networks: {encr1.network} vs {encr2.network}")
if encr1 != encr2:
analysis["differences"].append("Different encryption keychain DIDs")
if sign1 != sign2:
analysis["differences"].append("Different signature keychain DIDs")
except Exception as e:
analysis["differences"].append(f"Error comparing keychain DIDs: {str(e)}")
return analysis
@staticmethod
def extract_persona_info(persona_did: PersonaDID) -> dict:
"""Extract comprehensive information from a PersonaDID."""
try:
encr_did, sign_did = persona_did.parse_keychain_dids()
return {
"persona_did": str(persona_did),
"scheme": persona_did.scheme,
"method": persona_did.method,
"method_specific_id": persona_did.method_specific_id,
"network": encr_did.network,
"encryption": {
"did": str(encr_did),
"network_id": encr_did.network_specific_id,
"type": encr_did.keychain_type_str
},
"signature": {
"did": str(sign_did),
"network_id": sign_did.network_specific_id,
"type": sign_did.keychain_type_str
}
}
except Exception as e:
return {
"error": f"Failed to extract persona info: {str(e)}"
}
# Usage example
def persona_did_analyzer_example():
"""Demonstrate PersonaDID analysis utilities."""
print("=== PersonaDID Analyzer Demo ===")
# Create test PersonaDIDs
encrypt_did1 = KeychainDID.from_keychain_components(
KeychainType.ENCRYPT, "btc_regtest1", "alice_encrypt"
)
sign_did1 = KeychainDID.from_keychain_components(
KeychainType.SIGN, "btc_regtest1", "alice_sign"
)
persona_did1 = PersonaDID.from_keychain_dids(encrypt_did1, sign_did1)
encrypt_did2 = KeychainDID.from_keychain_components(
KeychainType.ENCRYPT, "btc_testnet", "bob_encrypt"
)
sign_did2 = KeychainDID.from_keychain_components(
KeychainType.SIGN, "btc_testnet", "bob_sign"
)
persona_did2 = PersonaDID.from_keychain_dids(encrypt_did2, sign_did2)
# Validate PersonaDIDs
print("=== Validation Tests ===")
for i, persona_did in enumerate([persona_did1, persona_did2], 1):
result = PersonaDIDAnalyzer.validate_persona_did(str(persona_did))
print(f"\nPersonaDID {i}: {result['valid']}")
if result['valid']:
print(f" Network: {result['network']}")
print(f" Encryption DID: {result['encryption_did']}")
print(f" Signature DID: {result['signature_did']}")
else:
print(f" Error: {result['error']}")
# Compare PersonaDIDs
print("\n=== Comparison Analysis ===")
comparison = PersonaDIDAnalyzer.compare_persona_dids(persona_did1, persona_did2)
print(f"Equal: {comparison['equal']}")
print(f"Same network: {comparison['same_network']}")
print(f"Same method: {comparison['same_method']}")
if comparison['differences']:
print("Differences:")
for diff in comparison['differences']:
print(f" - {diff}")
# Extract detailed information
print("\n=== Detailed Information ===")
for i, persona_did in enumerate([persona_did1, persona_did2], 1):
info = PersonaDIDAnalyzer.extract_persona_info(persona_did)
print(f"\nPersona {i}:")
if 'error' not in info:
print(f" DID: {info['persona_did']}")
print(f" Network: {info['network']}")
print(f" Encryption: {info['encryption']['did']}")
print(f" Signature: {info['signature']['did']}")
else:
print(f" Error: {info['error']}")
if __name__ == "__main__":
persona_did_analyzer_example()
Error Handling
PersonaDID operations can raise various exceptions:
Example:
from keychain.exceptions import KeychainValidationError
from keychain.identity.persona_did import PersonaDID
from keychain.identity.keychain_did import KeychainDID
from keychain.constants import KeychainType
try:
# Create PersonaDID from string
persona_did = PersonaDID("did:kcn-persona:alice.company.com")
# Create from keychain DIDs
encrypt_did = KeychainDID.from_keychain_components(
KeychainType.ENCRYPT, "btc_regtest1", "encrypt_id"
)
sign_did = KeychainDID.from_keychain_components(
KeychainType.SIGN, "btc_regtest1", "sign_id"
)
persona_did2 = PersonaDID.from_keychain_dids(encrypt_did, sign_did)
# Access properties
encr_did = persona_did2.encr_keychain_did
sign_did = persona_did2.sign_keychain_did
# Parse components
encr_parsed, sign_parsed = persona_did2.parse_keychain_dids()
# Serialize
serialized = persona_did.serialize()
except KeychainValidationError as e:
print(f"Validation error: {e}")
except Exception as e:
print(f"Unexpected error: {e}")
See Also
-
DID Class - Base DID functionality
-
KeychainDID Class - Keychain-specific DIDs
-
Persona Class - PersonaDID usage in personas
-
Contact Class - PersonaDID usage in contacts
-
Constants and Enums - KeychainType enumeration
-
Exception Classes - Error handling