KeychainElement Class

Overview

The KeychainElement class represents an element of a keychain, comprised of the public key and metadata about the public key. Each element represents a specific key pair within a keychain’s lifecycle and includes associated DIDs, cryptographic proofs, and status information.

Unlike other keychain classes, KeychainElement is not serializable and is typically obtained through keychain operations rather than constructed directly.

Package: keychain.identity.keychain_element

from keychain.identity.keychain_element import KeychainElement

Class Definition

class KeychainElement(KeychainObject):
    """Represents an element of a keychain with public key and metadata."""

Constructor

def __init__(self, c_pointer: Optional[ctypes.c_void_p] = None) -> None

Initialize a KeychainElement object.

Parameters:

  • c_pointer (ctypes.c_void_p, optional) - Existing C pointer to wrap

Note: KeychainElement objects are typically created through keychain operations, not constructed directly by users.

Example:

# Usually obtained from keychain operations
keychain = get_keychain()
elements = keychain.get_elements()
element = elements[0]  # First element

# Or from existing C pointer (internal use)
element = KeychainElement(c_pointer=existing_pointer)

Instance Methods

copy()

def copy(self) -> "KeychainElement"

Create a deep copy of this keychain element.

Returns: A new KeychainElement object that is a deep copy

Example:

original_element = get_keychain_element()
copied_element = original_element.copy()

did_status()

def did_status(self, did: KeychainDID) -> DidStatus

Get the status of a DID in the keychain element’s DID list.

Parameters:

  • did (KeychainDID) - The keychain DID to check status for

Returns: The DidStatus of the DID

Raises: ValueError if the DID is not found in this element

Example:

from keychain.constants import DidStatus

element = get_keychain_element()
dids = element.dids

for did in dids:
    try:
        status = element.did_status(did)
        print(f"DID {did} status: {status.name}")
    except ValueError as e:
        print(f"Error: {e}")

Properties

Key Properties

public_key

@property
def public_key(self) -> str

Get the public key.

Returns: The public key in hex-encoded DER format

Example:

element = get_keychain_element()
public_key = element.public_key
print(f"Public key: {public_key[:32]}...")  # First 32 characters
print(f"Key length: {len(public_key)} characters")

proof

@property
def proof(self) -> str

Get the proof.

Returns: The proof text that provides evidence of the validity of the element

Example:

element = get_keychain_element()
proof = element.proof
print(f"Proof: {proof}")

Identity Properties

dids

@property
def dids(self) -> List[KeychainDID]

Get the DIDs for this element.

Returns: List of KeychainDID objects associated with this element

Example:

element = get_keychain_element()
dids = element.dids
print(f"Element has {len(dids)} associated DIDs:")

for i, did in enumerate(dids):
    print(f"  {i}: {did}")
    print(f"     Type: {did.keychain_type_str}")
    print(f"     Network: {did.network}")

Sequence Properties

index

@property
def index(self) -> int

Get the index of the keychain element.

Returns: The zero-indexed position of this element in the keychain

Example:

element = get_keychain_element()
index = element.index
print(f"Element index in keychain: {index}")

Algorithm Properties

algorithm

@property
def algorithm(self) -> int

Get the cryptographic algorithm.

Returns: The algorithm as an integer (cast to signature_scheme or encryption_scheme depending on keychain type)

Example:

from keychain.constants import SignatureScheme, EncryptionScheme, KeychainType

element = get_keychain_element()
algorithm_value = element.algorithm

# Determine if this is encryption or signature keychain
dids = element.dids
if dids:
    first_did = dids[0]
    if first_did.keychain_type == KeychainType.ENCRYPT:
        algorithm = EncryptionScheme(algorithm_value)
        print(f"Encryption algorithm: {algorithm.name}")
    elif first_did.keychain_type == KeychainType.SIGN:
        algorithm = SignatureScheme(algorithm_value)
        print(f"Signature algorithm: {algorithm.name}")
else:
    print(f"Algorithm value: {algorithm_value}")

Comparison Methods

eq()

def __eq__(self, other: object) -> bool

Test equality of two keychain elements.

Parameters:

  • other (KeychainElement) - The other keychain element to compare

Returns: True if element fields test equal, False otherwise

Example:

element1 = get_keychain_element()
element2 = element1.copy()

if element1 == element2:
    print("Elements are identical")

String Representation

str()

def __str__(self) -> str

Return string representation of the keychain element.

Returns: String representation showing element index and algorithm

repr()

def __repr__(self) -> str

Return detailed string representation.

Returns: Detailed representation showing class and key fields

Example: Working with Keychain Elements

from keychain.identity.keychain_element import KeychainElement
from keychain.constants import KeychainType, SignatureScheme, EncryptionScheme

def analyze_keychain_element(element: KeychainElement):
    """Analyze a keychain element and display its properties."""

    print("=== Keychain Element Analysis ===")

    # Basic properties
    print(f"Index: {element.index}")
    print(f"Algorithm value: {element.algorithm}")

    # Public key information
    public_key = element.public_key
    print(f"Public key length: {len(public_key)} characters")
    print(f"Public key (first 32 chars): {public_key[:32]}...")

    # Proof information
    proof = element.proof
    print(f"Proof: {proof}")

    # Associated DIDs
    dids = element.dids
    print(f"\\nAssociated DIDs: {len(dids)}")

    for i, did in enumerate(dids):
        print(f"  DID {i}:")
        print(f"    String: {did}")
        print(f"    Type: {did.keychain_type_str}")
        print(f"    Network: {did.network}")
        print(f"    Network ID: {did.network_specific_id}")

        # Check DID status
        try:
            status = element.did_status(did)
            print(f"    Status: {status.name}")
        except ValueError as e:
            print(f"    Status: Error - {e}")

    # Determine algorithm type based on DIDs
    if dids:
        first_did = dids[0]
        algorithm_value = element.algorithm

        if first_did.keychain_type == KeychainType.ENCRYPT:
            try:
                algorithm = EncryptionScheme(algorithm_value)
                print(f"\\nEncryption Algorithm: {algorithm.name}")
            except ValueError:
                print(f"\\nUnknown encryption algorithm: {algorithm_value}")
        elif first_did.keychain_type == KeychainType.SIGN:
            try:
                algorithm = SignatureScheme(algorithm_value)
                print(f"\\nSignature Algorithm: {algorithm.name}")
            except ValueError:
                print(f"\\nUnknown signature algorithm: {algorithm_value}")

def compare_keychain_elements(elements: list[KeychainElement]):
    """Compare multiple keychain elements for analysis."""

    if not elements:
        print("No elements to compare")
        return

    print("=== Keychain Elements Comparison ===")

    # Basic statistics
    print(f"Total elements: {len(elements)}")

    # Algorithm distribution
    algorithms = {}
    keychain_types = {}

    for i, element in enumerate(elements):
        algorithm_value = element.algorithm
        algorithms[algorithm_value] = algorithms.get(algorithm_value, 0) + 1

        # Determine keychain type from DIDs
        dids = element.dids
        if dids:
            kc_type = dids[0].keychain_type.name
            keychain_types[kc_type] = keychain_types.get(kc_type, 0) + 1

    print("\\nAlgorithm distribution:")
    for alg, count in sorted(algorithms.items()):
        print(f"  Algorithm {alg}: {count} elements")

    print("\\nKeychain type distribution:")
    for kc_type, count in keychain_types.items():
        print(f"  {kc_type}: {count} elements")

    # Index sequence validation
    indices = [element.index for element in elements]
    expected_indices = list(range(len(elements)))

    if sorted(indices) == expected_indices:
        print("\\nāœ“ Element indices form a valid sequence (0 to n-1)")
    else:
        missing = set(expected_indices) - set(indices)
        duplicates = len(indices) - len(set(indices))
        print(f"\\nāœ— Invalid index sequence:")
        if missing:
            print(f"  Missing indices: {sorted(missing)}")
        if duplicates > 0:
            print(f"  Duplicate indices: {duplicates}")

def extract_keychain_element_info(element: KeychainElement) -> dict:
    """Extract comprehensive information from a keychain element."""

    try:
        # Basic properties
        info = {
            "index": element.index,
            "algorithm": element.algorithm,
            "public_key": {
                "length": len(element.public_key),
                "preview": element.public_key[:32] + "..."
            },
            "proof": element.proof,
            "dids": []
        }

        # DID information
        dids = element.dids
        for did in dids:
            did_info = {
                "did_string": str(did),
                "keychain_type": did.keychain_type_str,
                "network": did.network,
                "network_id": did.network_specific_id
            }

            # Try to get status
            try:
                status = element.did_status(did)
                did_info["status"] = status.name
            except ValueError:
                did_info["status"] = "unknown"

            info["dids"].append(did_info)

        # Algorithm interpretation
        if dids:
            first_did = dids[0]
            algorithm_value = element.algorithm

            if first_did.keychain_type == KeychainType.ENCRYPT:
                try:
                    algorithm = EncryptionScheme(algorithm_value)
                    info["algorithm_name"] = algorithm.name
                    info["algorithm_type"] = "encryption"
                except ValueError:
                    info["algorithm_name"] = f"unknown_encryption_{algorithm_value}"
                    info["algorithm_type"] = "encryption"
            elif first_did.keychain_type == KeychainType.SIGN:
                try:
                    algorithm = SignatureScheme(algorithm_value)
                    info["algorithm_name"] = algorithm.name
                    info["algorithm_type"] = "signature"
                except ValueError:
                    info["algorithm_name"] = f"unknown_signature_{algorithm_value}"
                    info["algorithm_type"] = "signature"

        return info

    except Exception as e:
        return {"error": str(e)}

def validate_keychain_elements(elements: list[KeychainElement]) -> dict:
    """Validate a list of keychain elements."""

    validation_result = {
        "valid": True,
        "errors": [],
        "warnings": [],
        "element_count": len(elements),
        "statistics": {}
    }

    if not elements:
        validation_result["warnings"].append("No elements to validate")
        return validation_result

    # Check index sequence
    indices = [element.index for element in elements]
    expected_indices = list(range(len(elements)))

    if sorted(indices) != expected_indices:
        validation_result["valid"] = False
        validation_result["errors"].append("Invalid index sequence")

    # Check for duplicate elements
    seen_public_keys = set()
    duplicates = []

    for i, element in enumerate(elements):
        try:
            public_key = element.public_key
            if public_key in seen_public_keys:
                duplicates.append(i)
            else:
                seen_public_keys.add(public_key)
        except Exception as e:
            validation_result["errors"].append(f"Element {i}: Cannot access public key - {e}")
            validation_result["valid"] = False

    if duplicates:
        validation_result["warnings"].append(f"Duplicate public keys at indices: {duplicates}")

    # Check DID consistency
    networks = set()
    keychain_types = set()

    for i, element in enumerate(elements):
        try:
            dids = element.dids
            if not dids:
                validation_result["warnings"].append(f"Element {i}: No associated DIDs")
                continue

            for did in dids:
                networks.add(did.network)
                keychain_types.add(did.keychain_type.name)
        except Exception as e:
            validation_result["errors"].append(f"Element {i}: Cannot access DIDs - {e}")
            validation_result["valid"] = False

    # Statistics
    validation_result["statistics"] = {
        "unique_networks": len(networks),
        "networks": list(networks),
        "keychain_types": list(keychain_types),
        "duplicate_elements": len(duplicates)
    }

    return validation_result

# Usage examples
def keychain_element_examples():
    """Demonstrate keychain element usage."""

    # Get elements from some source (e.g., keychain)
    elements = get_keychain_elements()

    if elements:
        # Analyze first element
        analyze_keychain_element(elements[0])

        # Compare multiple elements
        if len(elements) > 1:
            compare_keychain_elements(elements)

        # Extract information
        info = extract_keychain_element_info(elements[0])
        print(f"\\n=== Element Information ===")
        for key, value in info.items():
            if isinstance(value, dict):
                print(f"{key}:")
                for subkey, subvalue in value.items():
                    print(f"  {subkey}: {subvalue}")
            elif isinstance(value, list):
                print(f"{key}: {len(value)} items")
                for i, item in enumerate(value):
                    print(f"  {i}: {item}")
            else:
                print(f"{key}: {value}")

        # Validate elements
        validation = validate_keychain_elements(elements)
        print(f"\\n=== Validation Results ===")
        print(f"Valid: {validation['valid']}")
        if validation['errors']:
            print("Errors:")
            for error in validation['errors']:
                print(f"  - {error}")
        if validation['warnings']:
            print("Warnings:")
            for warning in validation['warnings']:
                print(f"  - {warning}")

if __name__ == "__main__":
    keychain_element_examples()

Example: Keychain Element Management

from keychain.identity.keychain_element import KeychainElement
from keychain.constants import DidStatus, KeychainType

class KeychainElementManager:
    """Manager for working with keychain elements."""

    def __init__(self):
        self.elements = []

    def add_element(self, element: KeychainElement) -> None:
        """Add an element to the manager."""
        self.elements.append(element.copy())

    def get_element_by_index(self, index: int) -> KeychainElement:
        """Get an element by its keychain index."""
        for element in self.elements:
            if element.index == index:
                return element
        raise ValueError(f"No element found with index {index}")

    def get_elements_by_type(self, keychain_type: KeychainType) -> list[KeychainElement]:
        """Get all elements of a specific keychain type."""
        matching_elements = []

        for element in self.elements:
            dids = element.dids
            if dids and dids[0].keychain_type == keychain_type:
                matching_elements.append(element)

        return matching_elements

    def get_elements_by_network(self, network: str) -> list[KeychainElement]:
        """Get all elements associated with a specific network."""
        matching_elements = []

        for element in self.elements:
            dids = element.dids
            for did in dids:
                if did.network == network:
                    matching_elements.append(element)
                    break  # Found at least one DID for this network

        return matching_elements

    def find_element_by_public_key(self, public_key: str) -> KeychainElement:
        """Find an element by its public key."""
        for element in self.elements:
            if element.public_key == public_key:
                return element
        raise ValueError(f"No element found with public key {public_key[:16]}...")

    def get_element_summary(self) -> dict:
        """Get a summary of all managed elements."""
        summary = {
            "total_elements": len(self.elements),
            "by_type": {},
            "by_network": {},
            "algorithms": {},
            "index_range": None
        }

        if not self.elements:
            return summary

        indices = []

        for element in self.elements:
            indices.append(element.index)

            # Count by type
            dids = element.dids
            if dids:
                kc_type = dids[0].keychain_type.name
                summary["by_type"][kc_type] = summary["by_type"].get(kc_type, 0) + 1

                # Count by network
                for did in dids:
                    network = did.network
                    summary["by_network"][network] = summary["by_network"].get(network, 0) + 1

            # Count algorithms
            algorithm = element.algorithm
            summary["algorithms"][algorithm] = summary["algorithms"].get(algorithm, 0) + 1

        if indices:
            summary["index_range"] = (min(indices), max(indices))

        return summary

    def validate_elements(self) -> dict:
        """Validate all managed elements."""
        return validate_keychain_elements(self.elements)

    def export_element_data(self) -> list[dict]:
        """Export data for all elements."""
        return [extract_keychain_element_info(element) for element in self.elements]

def keychain_element_manager_example():
    """Demonstrate keychain element management."""

    manager = KeychainElementManager()

    # Add elements from keychain
    keychain_elements = get_keychain_elements()
    for element in keychain_elements:
        manager.add_element(element)

    print("=== Keychain Element Manager Demo ===")

    # Get summary
    summary = manager.get_element_summary()
    print(f"Total elements: {summary['total_elements']}")

    if summary['index_range']:
        start, end = summary['index_range']
        print(f"Index range: {start} to {end}")

    print("\\nBy type:")
    for kc_type, count in summary['by_type'].items():
        print(f"  {kc_type}: {count}")

    print("\\nBy network:")
    for network, count in summary['by_network'].items():
        print(f"  {network}: {count}")

    # Find specific elements
    try:
        encrypt_elements = manager.get_elements_by_type(KeychainType.ENCRYPT)
        print(f"\\nEncryption elements: {len(encrypt_elements)}")

        if encrypt_elements:
            first_encrypt = encrypt_elements[0]
            print(f"First encryption element index: {first_encrypt.index}")
    except Exception as e:
        print(f"Error finding encryption elements: {e}")

    # Validate elements
    validation = manager.validate_elements()
    print(f"\\nValidation: {'āœ“ Valid' if validation['valid'] else 'āœ— Invalid'}")

    if validation['errors']:
        print("Errors:")
        for error in validation['errors']:
            print(f"  - {error}")

if __name__ == "__main__":
    keychain_element_manager_example()

Error Handling

KeychainElement operations can raise various exceptions:

Example:

from keychain.exceptions import KeychainValidationError

try:
    # Access element properties
    element = get_keychain_element()

    public_key = element.public_key
    index = element.index
    dids = element.dids

    # Check DID status
    if dids:
        status = element.did_status(dids[0])
        print(f"DID status: {status.name}")

    # Work with algorithm
    algorithm_value = element.algorithm
    if dids and dids[0].keychain_type == KeychainType.SIGN:
        algorithm = SignatureScheme(algorithm_value)
        print(f"Signature algorithm: {algorithm.name}")

except ValueError as e:
    print(f"Value error: {e}")
except KeychainValidationError as e:
    print(f"Validation error: {e}")
except Exception as e:
    print(f"Unexpected error: {e}")

See Also