Contact Class

Overview

The Contact class represents external cryptographic identities in the Keychain system. Contacts are associated with personas and are used as recipients for encryption operations and as trusted signers for verification operations.

Package: keychain.identity.contact

from keychain.identity.contact import Contact

Class Definition

class Contact(KeychainObject):
    """Represents an external cryptographic identity."""

Constructor

def __init__(self, c_pointer) -> None

Initialize a Contact object with a C library pointer. Contacts are typically created through Gateway.add_contact() or Persona.add_contact() methods rather than direct construction.

Parameters:

  • c_pointer - C library pointer to the contact object

Properties

Basic Properties

name

@property
def name(self) -> str

Get the primary name of the contact.

Returns: Primary name string

subname

@property
def subname(self) -> str

Get the secondary name of the contact.

Returns: Secondary name string

did

@property
def did(self) -> PersonaDID

Get the contact’s decentralized identifier.

Returns: PersonaDID object

Status Properties

is_known

@property
def is_known(self) -> bool

Check if the contact is known (has verified cryptographic information).

Returns: True if contact is known

is_mature

@property
def is_mature(self) -> bool

Check if the contact is mature (ready for cryptographic operations).

Returns: True if contact is mature

Cryptographic Properties

encryption_algorithm

@property
def encryption_algorithm(self) -> EncryptionScheme

Get the encryption algorithm used by this contact.

Returns: EncryptionScheme enumeration value

encryption_key_size

@property
def encryption_key_size(self) -> int

Get the encryption key size in bits.

Returns: Key size in bits

signature_algorithm

@property
def signature_algorithm(self) -> SignatureScheme

Get the signature algorithm used by this contact.

Returns: SignatureScheme enumeration value

signature_key_size

@property
def signature_key_size(self) -> int

Get the signature key size in bits.

Returns: Key size in bits

cipher

@property
def cipher(self) -> Cipher

Get the symmetric cipher algorithm used by this contact.

Returns: Cipher enumeration value

Status Methods

is_null()

def is_null(self) -> bool

Check if the contact is null (uninitialized).

Returns: True if contact is null

Example:

if not contact.is_null():
    print(f"Contact {contact.name}.{contact.subname} is valid")
else:
    print("Contact is null")

Keychain Access Methods

keychain(keychain_type)

def keychain(self, keychain_type: KeychainType) -> Keychain

Get a specific keychain from the contact.

Parameters:

  • keychain_type (KeychainType) - Type of keychain (ENCRYPT or SIGN)

Returns: Keychain object

encryption_keychain()

def encryption_keychain(self) -> Keychain

Get the encryption keychain for this contact.

Returns: Encryption Keychain object

signature_keychain()

def signature_keychain(self) -> Keychain

Get the signature keychain for this contact.

Returns: Signature Keychain object

Lifecycle Operations

refresh()

def refresh(self) -> None

Refresh contact data from the PKI network.

Example:

# Refresh contact information from network
contact.refresh()
print(f"Contact {contact.name}.{contact.subname} refreshed")

await_maturity()

def await_maturity(self) -> None

Block until this contact becomes mature.

Example:

# Wait for newly added contact to become ready
if not contact.is_mature():
    print("Waiting for contact to mature...")
    contact.await_maturity()
    print("Contact is now ready!")

Comparison Methods

eq(other)

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

Test equality with another contact.

Parameters:

  • other (Contact) - Contact to compare with

Returns: True if contacts are equal

Example:

# Check if two contact references are the same
if contact1 == contact2:
    print("Same contact")
else:
    print("Different contacts")

Example: Working with Contacts

from keychain.gateway import Gateway
from keychain.constants import SecurityLevel, KeychainType
from keychain.exceptions import KeychainNotFoundError

# Initialize gateway
settings = Gateway.init("keychain.config")
gateway = Gateway(settings)

try:
    # Get or create personas
    alice = gateway.find_persona("alice", "company.com")
    bob = gateway.find_persona("bob", "company.com")

    # Add Bob as a contact for Alice
    bob_contact = gateway.add_contact(
        persona=alice,
        name="bob",
        subname="company.com",
        did=bob.did
    )

    print(f"Added contact: {bob_contact.name}.{bob_contact.subname}")
    print(f"Contact DID: {bob_contact.did}")

    # Wait for contact to become mature if needed
    if not bob_contact.is_mature():
        print("Waiting for contact to mature...")
        bob_contact.await_maturity()

    # Check contact status
    print(f"Contact status:")
    print(f"  Is known: {bob_contact.is_known}")
    print(f"  Is mature: {bob_contact.is_mature}")
    print(f"  Is null: {bob_contact.is_null()}")

    # Display cryptographic information
    print(f"Cryptographic details:")
    print(f"  Encryption: {bob_contact.encryption_algorithm} ({bob_contact.encryption_key_size} bits)")
    print(f"  Signature: {bob_contact.signature_algorithm} ({bob_contact.signature_key_size} bits)")
    print(f"  Cipher: {bob_contact.cipher}")

    # Access keychains
    enc_keychain = bob_contact.encryption_keychain()
    sig_keychain = bob_contact.signature_keychain()
    print(f"Contact has encryption and signature keychains")

    # Use contact for encryption
    message = "Confidential message for Bob"
    encrypted = alice.encrypt(message, [bob_contact])
    print("Message encrypted for contact")

    # Refresh contact information from network
    bob_contact.refresh()
    print("Contact information refreshed from PKI network")

    # Find contact in persona's contact list
    try:
        found_contact = alice.find_contact("bob", "company.com")
        if found_contact == bob_contact:
            print("✓ Contact found in persona's contact list")
    except KeychainNotFoundError:
        print("✗ Contact not found in persona's contact list")

    # List all contacts for Alice
    print(f"\nAlice's contacts ({len(alice.contacts)}):")
    for i, contact in enumerate(alice.contacts):
        print(f"  {i}: {contact.name}.{contact.subname}")
        print(f"      Known: {contact.is_known}, Mature: {contact.is_mature}")

finally:
    Gateway.close()

Example: Contact Management Workflow

from keychain.gateway import Gateway
from keychain.constants import SecurityLevel
from keychain.exceptions import KeychainNotFoundError

def manage_contacts_workflow():
    """Demonstrate comprehensive contact management."""

    settings = Gateway.init("keychain.config")
    gateway = Gateway(settings)

    try:
        # Create or get main persona
        try:
            alice = gateway.find_persona("alice", "company.com")
        except KeychainNotFoundError:
            alice = gateway.create_persona(
                name="alice",
                subname="company.com",
                security_level=SecurityLevel.HIGH,
                auto_renew=True
            )
            alice.await_maturity()

        # Create multiple contacts
        contact_info = [
            ("bob", "company.com"),
            ("charlie", "partner.org"),
            ("diana", "client.net"),
            ("eve", "vendor.biz")
        ]

        contacts = []

        for name, subname in contact_info:
            # Create persona for contact (in real scenario, these would be external)
            try:
                contact_persona = gateway.find_persona(name, subname)
            except KeychainNotFoundError:
                contact_persona = gateway.create_persona(
                    name=name,
                    subname=subname,
                    security_level=SecurityLevel.MEDIUM,
                    auto_renew=True
                )
                contact_persona.await_maturity()

            # Add as contact
            contact = alice.add_contact(
                name=name,
                subname=subname,
                did=contact_persona.did
            )

            contacts.append(contact)
            print(f"Added contact: {contact.name}.{contact.subname}")

        # Wait for all contacts to mature
        print("\nWaiting for contacts to mature...")
        for contact in contacts:
            if not contact.is_mature():
                contact.await_maturity()

        # Display contact information
        print(f"\nContact Information:")
        print(f"Alice has {len(alice.contacts)} contacts:")

        for i, contact in enumerate(alice.contacts):
            print(f"\n  Contact {i+1}: {contact.name}.{contact.subname}")
            print(f"    DID: {contact.did}")
            print(f"    Status: Known={contact.is_known}, Mature={contact.is_mature}")
            print(f"    Encryption: {contact.encryption_algorithm} ({contact.encryption_key_size} bits)")
            print(f"    Signature: {contact.signature_algorithm} ({contact.signature_key_size} bits)")

        # Test encryption to multiple contacts
        print(f"\nTesting encryption to multiple contacts...")
        message = "Important announcement to all team members"

        encrypted = alice.encrypt(message, contacts[:3])  # Send to first 3 contacts
        print(f"Message encrypted for {len(contacts[:3])} recipients")

        # Test contact lookup
        print(f"\nTesting contact lookup...")
        try:
            bob_contact = alice.find_contact("bob", "company.com")
            print(f"✓ Found Bob: {bob_contact.name}.{bob_contact.subname}")

            charlie_contact = alice.find_contact("charlie", "partner.org")
            print(f"✓ Found Charlie: {charlie_contact.name}.{charlie_contact.subname}")

        except KeychainNotFoundError as e:
            print(f"✗ Contact not found: {e}")

        # Test contact refresh
        print(f"\nRefreshing contact information...")
        for contact in alice.contacts:
            contact.refresh()
            print(f"  Refreshed: {contact.name}.{contact.subname}")

        # Test contact equality
        print(f"\nTesting contact equality...")
        contact1 = alice.get_contact(0)
        contact2 = alice.find_contact(contact1.name, contact1.subname)

        if contact1 == contact2:
            print("✓ Contact equality test passed")
        else:
            print("✗ Contact equality test failed")

        return contacts

    finally:
        Gateway.close()

if __name__ == "__main__":
    contacts = manage_contacts_workflow()

Error Handling

Contact methods can raise various exceptions:

  • KeychainNotFoundError - Contact not found in searches

  • KeychainSecurityError - Cryptographic operation failures

  • KeychainValidationError - Input validation errors

  • IndexError - Contact index out of range

Example:

from keychain.exceptions import KeychainNotFoundError, KeychainSecurityError

try:
    # Safe contact operations
    contact = persona.find_contact("unknown", "domain.com")
    contact.refresh()

    if contact.is_mature():
        keychain = contact.encryption_keychain()

except KeychainNotFoundError:
    print("Contact not found")
except KeychainSecurityError:
    print("Cryptographic operation failed")
except Exception as e:
    print(f"Unexpected error: {e}")

Best Practices

  1. Always check maturity: Verify is_mature() before using contacts for cryptographic operations

  2. Handle contact discovery: Use try-catch blocks when finding contacts

  3. Refresh when needed: Call refresh() to get latest PKI information

  4. Check status: Verify is_known before trusting contact for sensitive operations

  5. Await maturity: Use await_maturity() for newly added contacts

Example:

def safe_contact_usage(persona, contact_name, contact_subname):
    """Demonstrate safe contact usage patterns."""

    try:
        # Find contact
        contact = persona.find_contact(contact_name, contact_subname)

        # Check basic status
        if contact.is_null():
            print("Contact is null - cannot use")
            return None

        # Ensure contact is known
        if not contact.is_known:
            print("Contact is not known - refreshing...")
            contact.refresh()

        # Wait for maturity if needed
        if not contact.is_mature():
            print("Contact is not mature - waiting...")
            contact.await_maturity()

        # Now safe to use
        print(f"Contact {contact.name}.{contact.subname} is ready for use")
        return contact

    except KeychainNotFoundError:
        print(f"Contact {contact_name}.{contact_subname} not found")
        return None

See Also