KeychainDID Class
Overview
The KeychainDID
class extends the basic DID functionality with keychain-specific metadata such as the public-key infrastructure network ID and the keychain type. This provides network-aware identification for keychain entities.
Package: keychain.identity.keychain_did
from keychain.identity.keychain_did import KeychainDID
Class Definition
class KeychainDID(DID):
"""Keychain-specific Decentralized Identifier implementation."""
Constructor
def __init__(
self,
did_string: Optional[str] = None,
keychain_type: Optional[KeychainType] = None,
network: Optional[str] = None,
network_specific_id: Optional[str] = None,
c_pointer: Optional[ctypes.c_void_p] = None,
) -> None
Initialize a KeychainDID object from a DID string, components, or C pointer.
Parameters:
-
did_string
(str
, optional) - KeychainDID string to parse (e.g., "did:kcn-keychain:encrypt:btc_regtest1:id") -
keychain_type
(KeychainType
, optional) - The keychain type (encrypt or sign) -
network
(str
, optional) - The network identifier (e.g., "btc_regtest1") -
network_specific_id
(str
, optional) - The network-specific identifier -
c_pointer
(ctypes.c_void_p
, optional) - Existing C pointer to wrap
Example:
from keychain.constants import KeychainType
# Create from KeychainDID string
keychain_did = KeychainDID("did:kcn-keychain:encrypt:btc_regtest1:abc123")
# Create from components
keychain_did = KeychainDID(
keychain_type=KeychainType.ENCRYPT,
network="btc_regtest1",
network_specific_id="abc123"
)
# Create empty KeychainDID
empty_did = KeychainDID()
Properties
Keychain-Specific Properties
keychain_type
@property
def keychain_type(self) -> KeychainType
Get the keychain type.
Returns: KeychainType
enumeration value (ENCRYPT or SIGN)
keychain_type_str
@property
def keychain_type_str(self) -> str
Get the keychain type as a string.
Returns: The keychain type as a string ("encrypt" or "sign")
Methods
copy()
def copy(self) -> "KeychainDID"
Create a deep copy of this KeychainDID.
Returns: New KeychainDID
object that is a deep copy
serialize()
def serialize(self) -> bytes
Serialize the KeychainDID to bytes as defined in the W3C specification.
Returns: The KeychainDID serialized as UTF-8 encoded bytes
Example:
keychain_did = KeychainDID(
keychain_type=KeychainType.ENCRYPT,
network="btc_regtest1",
network_specific_id="abc123"
)
serialized = keychain_did.serialize()
# Returns: b"did:kcn-keychain:encrypt:btc_regtest1:abc123"
data_type()
def data_type(self) -> DataType
Get the data type.
Returns: DataType.DID
(keychain DIDs use the same data type as regular DIDs)
is_null()
def is_null(self) -> bool
Check if this is a null KeychainDID.
Returns: True
if this is a null/empty KeychainDID, False
otherwise
parse_keychain_components()
def parse_keychain_components(self) -> Tuple[KeychainType, str, str]
Parse the KeychainDID into its keychain-specific component parts.
Returns: A tuple of (keychain_type, network, network_specific_id)
Example:
keychain_did = KeychainDID("did:kcn-keychain:sign:btc_regtest1:xyz789")
kc_type, network, net_id = keychain_did.parse_keychain_components()
print(f"Type: {kc_type}, Network: {network}, ID: {net_id}")
Class Methods
from_keychain_components(keychain_type, network, network_specific_id)
@classmethod
def from_keychain_components(
cls, keychain_type: KeychainType, network: str, network_specific_id: str
) -> "KeychainDID"
Create a KeychainDID from its component parts.
Parameters:
-
keychain_type
(KeychainType
) - The keychain type (encrypt or sign) -
network
(str
) - The network identifier -
network_specific_id
(str
) - The network-specific identifier
Returns: New KeychainDID
object constructed from the components
Example:
from keychain.constants import KeychainType
keychain_did = KeychainDID.from_keychain_components(
KeychainType.SIGN,
"btc_regtest1",
"def456"
)
print(str(keychain_did))
# Output: did:kcn-keychain:sign:btc_regtest1:def456
Comparison Methods
String Representation
Example: Working with KeychainDIDs
from keychain.identity.keychain_did import KeychainDID
from keychain.constants import KeychainType
# Create KeychainDIDs for different purposes
encrypt_did = KeychainDID.from_keychain_components(
KeychainType.ENCRYPT,
"btc_regtest1",
"encrypt_abc123"
)
sign_did = KeychainDID.from_keychain_components(
KeychainType.SIGN,
"btc_regtest1",
"sign_xyz789"
)
print(f"Encryption DID: {encrypt_did}")
print(f"Signature DID: {sign_did}")
# Parse KeychainDID components
print("\n=== Encryption DID Analysis ===")
print(f"Scheme: {encrypt_did.scheme}")
print(f"Method: {encrypt_did.method}")
print(f"Method-specific ID: {encrypt_did.method_specific_id}")
print(f"Keychain Type: {encrypt_did.keychain_type_str}")
print(f"Network: {encrypt_did.network}")
print(f"Network-specific ID: {encrypt_did.network_specific_id}")
# Parse using dedicated methods
kc_type, network, net_id = encrypt_did.parse_keychain_components()
print(f"\nParsed components:")
print(f" Keychain Type: {kc_type}")
print(f" Network: {network}")
print(f" Network ID: {net_id}")
# Create from string and verify round-trip
did_string = str(encrypt_did)
reconstructed = KeychainDID(did_string)
if encrypt_did == reconstructed:
print("\nā String serialization/deserialization successful")
# Compare different KeychainDIDs
if encrypt_did != sign_did:
print("ā Different KeychainDIDs are not equal")
# Copy KeychainDID
copied_did = encrypt_did.copy()
if encrypt_did == copied_did:
print("ā Copy operation successful")
Example: Network-Aware DID Management
from keychain.identity.keychain_did import KeychainDID
from keychain.constants import KeychainType
from typing import Dict, List, Tuple
class NetworkDIDManager:
"""Manage KeychainDIDs across different networks."""
def __init__(self):
self._dids_by_network: Dict[str, List[KeychainDID]] = {}
self._dids_by_type: Dict[KeychainType, List[KeychainDID]] = {}
def register_keychain_did(self, keychain_did: KeychainDID) -> None:
"""Register a KeychainDID in the manager."""
# Group by network
network = keychain_did.network
if network not in self._dids_by_network:
self._dids_by_network[network] = []
self._dids_by_network[network].append(keychain_did.copy())
# Group by keychain type
kc_type = keychain_did.keychain_type
if kc_type not in self._dids_by_type:
self._dids_by_type[kc_type] = []
self._dids_by_type[kc_type].append(keychain_did.copy())
def get_dids_for_network(self, network: str) -> List[KeychainDID]:
"""Get all KeychainDIDs for a specific network."""
return self._dids_by_network.get(network, []).copy()
def get_dids_for_type(self, keychain_type: KeychainType) -> List[KeychainDID]:
"""Get all KeychainDIDs for a specific keychain type."""
return self._dids_by_type.get(keychain_type, []).copy()
def get_networks(self) -> List[str]:
"""Get all registered networks."""
return list(self._dids_by_network.keys())
def get_keychain_types(self) -> List[KeychainType]:
"""Get all registered keychain types."""
return list(self._dids_by_type.keys())
def find_matching_pair(self, network: str) -> Tuple[KeychainDID, KeychainDID]:
"""Find a matching encrypt/sign pair for a network."""
network_dids = self.get_dids_for_network(network)
encrypt_did = None
sign_did = None
for did in network_dids:
if did.keychain_type == KeychainType.ENCRYPT:
encrypt_did = did
elif did.keychain_type == KeychainType.SIGN:
sign_did = did
if encrypt_did and sign_did:
return (encrypt_did, sign_did)
else:
raise ValueError(f"No matching encrypt/sign pair found for network {network}")
def get_statistics(self) -> Dict:
"""Get statistics about registered DIDs."""
total_dids = sum(len(dids) for dids in self._dids_by_network.values())
stats = {
"total_dids": total_dids,
"networks": len(self._dids_by_network),
"keychain_types": len(self._dids_by_type),
"by_network": {},
"by_type": {}
}
for network, dids in self._dids_by_network.items():
stats["by_network"][network] = len(dids)
for kc_type, dids in self._dids_by_type.items():
stats["by_type"][kc_type.name] = len(dids)
return stats
# Usage example
def network_did_manager_example():
"""Demonstrate network-aware DID management."""
manager = NetworkDIDManager()
# Create KeychainDIDs for different networks
networks = ["btc_regtest1", "btc_testnet", "btc_mainnet"]
for i, network in enumerate(networks):
# Create encrypt and sign DIDs for each network
encrypt_did = KeychainDID.from_keychain_components(
KeychainType.ENCRYPT,
network,
f"encrypt_id_{i}"
)
sign_did = KeychainDID.from_keychain_components(
KeychainType.SIGN,
network,
f"sign_id_{i}"
)
manager.register_keychain_did(encrypt_did)
manager.register_keychain_did(sign_did)
print("=== Network DID Manager Demo ===")
# Get statistics
stats = manager.get_statistics()
print(f"Total DIDs: {stats['total_dids']}")
print(f"Networks: {stats['networks']}")
print(f"Keychain Types: {stats['keychain_types']}")
print("\nDIDs by network:")
for network, count in stats["by_network"].items():
print(f" {network}: {count} DIDs")
print("\nDIDs by type:")
for kc_type, count in stats["by_type"].items():
print(f" {kc_type}: {count} DIDs")
# Find matching pairs
for network in networks:
try:
encrypt_did, sign_did = manager.find_matching_pair(network)
print(f"\n{network} pair:")
print(f" Encrypt: {encrypt_did}")
print(f" Sign: {sign_did}")
except ValueError as e:
print(f"\n{network}: {e}")
# Get DIDs for specific network
regtest_dids = manager.get_dids_for_network("btc_regtest1")
print(f"\nRegtest DIDs: {len(regtest_dids)}")
for did in regtest_dids:
print(f" {did.keychain_type_str}: {did}")
if __name__ == "__main__":
network_did_manager_example()
Example: KeychainDID Utilities
from keychain.identity.keychain_did import KeychainDID
from keychain.constants import KeychainType
class KeychainDIDUtils:
"""Utility functions for KeychainDID operations."""
@staticmethod
def validate_keychain_did(did_string: str) -> dict:
"""Validate a KeychainDID string."""
try:
keychain_did = KeychainDID(did_string)
if keychain_did.is_null():
return {
"valid": False,
"error": "KeychainDID is null or empty"
}
# Validate expected method
if keychain_did.method != "kcn-keychain":
return {
"valid": False,
"error": f"Invalid method: {keychain_did.method} (expected 'kcn-keychain')"
}
# Parse components
kc_type, network, net_id = keychain_did.parse_keychain_components()
# Validate components
if not network:
return {
"valid": False,
"error": "Missing network identifier"
}
if not net_id:
return {
"valid": False,
"error": "Missing network-specific identifier"
}
return {
"valid": True,
"keychain_type": kc_type,
"keychain_type_str": keychain_did.keychain_type_str,
"network": network,
"network_specific_id": net_id,
"scheme": keychain_did.scheme,
"method": keychain_did.method,
"serialized": keychain_did.serialize().decode('utf-8')
}
except Exception as e:
return {
"valid": False,
"error": f"Failed to parse KeychainDID: {str(e)}"
}
@staticmethod
def create_keychain_did_pair(network: str, base_id: str) -> Tuple[KeychainDID, KeychainDID]:
"""Create a matching encrypt/sign KeychainDID pair."""
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"
)
return (encrypt_did, sign_did)
@staticmethod
def extract_network_info(keychain_did: KeychainDID) -> dict:
"""Extract network information from a KeychainDID."""
return {
"network": keychain_did.network,
"network_specific_id": keychain_did.network_specific_id,
"keychain_type": keychain_did.keychain_type,
"keychain_type_str": keychain_did.keychain_type_str,
"full_did": str(keychain_did)
}
# Usage example
def keychain_did_utils_example():
"""Demonstrate KeychainDID utility functions."""
print("=== KeychainDID Utilities Demo ===")
# Test validation
test_dids = [
"did:kcn-keychain:encrypt:btc_regtest1:abc123",
"did:kcn-keychain:sign:btc_testnet:xyz789",
"did:example:invalid",
"did:kcn-keychain:encrypt:",
""
]
print("DID Validation Tests:")
for did_string in test_dids:
result = KeychainDIDUtils.validate_keychain_did(did_string)
print(f"\nDID: {did_string}")
print(f"Valid: {result['valid']}")
if result['valid']:
print(f" Type: {result['keychain_type_str']}")
print(f" Network: {result['network']}")
print(f" Network ID: {result['network_specific_id']}")
else:
print(f" Error: {result['error']}")
# Create DID pairs
print("\n=== DID Pair Creation ===")
encrypt_did, sign_did = KeychainDIDUtils.create_keychain_did_pair("btc_mainnet", "user123")
print(f"Encrypt DID: {encrypt_did}")
print(f"Sign DID: {sign_did}")
# Extract network info
print("\n=== Network Information ===")
for did in [encrypt_did, sign_did]:
info = KeychainDIDUtils.extract_network_info(did)
print(f"\n{info['keychain_type_str'].title()} DID:")
print(f" Network: {info['network']}")
print(f" Network ID: {info['network_specific_id']}")
print(f" Full DID: {info['full_did']}")
if __name__ == "__main__":
keychain_did_utils_example()
Error Handling
KeychainDID operations can raise various exceptions:
Example:
from keychain.exceptions import KeychainValidationError
from keychain.constants import KeychainType
try:
# Create KeychainDID from string
keychain_did = KeychainDID("did:kcn-keychain:encrypt:btc_regtest1:abc123")
# Access properties
kc_type = keychain_did.keychain_type
network = keychain_did.network
net_id = keychain_did.network_specific_id
# Create from components
new_did = KeychainDID.from_keychain_components(
KeychainType.SIGN,
"btc_testnet",
"xyz789"
)
# Serialize
serialized = keychain_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
-
PersonaDID Class - Persona DIDs
-
Persona Class - KeychainDID usage in personas
-
Contact Class - KeychainDID usage in contacts
-
Constants and Enums - KeychainType enumeration
-
Exception Classes - Error handling