Class Handle

Represents a context-specific cryptographic handle derived from an Identity.

A Handle encapsulates a private key tied to a specific name (context). It provides methods for signing data, verifying signatures, and deriving secondary secrets (passwords, channel keys, shared secrets) without ever exposing the underlying private key.

A Handle can also derive SubHandles for hierarchical access control (e.g., an IoT device deriving keys for its components). The private key never leaves the Handle — derivation is performed internally using HKDF.

Hierarchy (view full)

Methods

  • Returns a URL-safe Base64-encoded identifier for this Handle, derived from its public key.

    Returns string

    A unique, URL-safe string identifier.

  • Cryptographically signs arbitrary data using the Handle's private key.

    Parameters

    • data: Uint8Array

      The data to be signed, as a Uint8Array.

    Returns Promise<Uint8Array>

    A Promise resolving to the Ed25519 signature as a Uint8Array.

  • Verifies an Ed25519 signature against the provided data and public key.

    Parameters

    • signature: Uint8Array

      The signature to verify (Uint8Array).

    • data: Uint8Array

      The original data that was signed (Uint8Array).

    • publicKey: Uint8Array

      The public key to verify against (Uint8Array).

    Returns Promise<boolean>

    A Promise resolving to true if the signature is valid, false otherwise.

  • Returns undefined | string[]

    The derivation path, or undefined for Handle (only SubHandle has a path).

  • Validates session options against this Handle's constraints. Handle has no constraints.

    Parameters

    • _options: {
          audience: string;
          scopes: string[];
          ttl: number;
      }
      • audience: string
      • scopes: string[]
      • ttl: number

    Returns void

  • Derives a SubHandle from this Handle.

    This is the autonomous entry point for IoT devices and other contexts where the Identity is not available locally. The Handle uses its own private key internally (it never leaves the class) to derive a child key via HKDF-SHA256.

    The resulting SubHandle is cryptographically identical to the one produced by Identity.deriveSubHandle(this.name, subName).

    Parameters

    • name: string

      The SubHandle name (will be normalized).

    • Optionalmetadata: SubHandleMetadata

      Optional SubHandle metadata with constraints.

    Returns Promise<SubHandle>

    A Promise resolving to the derived SubHandle with path [this.name, name].

    // On an IoT device (no Identity available)
    const connector = await stationHandle.deriveSubHandle('connector-1', {
    allowedScopes: ['charge:start'],
    maxSessionTtl: 3600
    });
  • Issues an attestation for a derived SubHandle, binding its derived key to the name and grant. Autonomous: requires only this Handle's own key, no Identity and no network.

    The child key is derived internally, so the attestation's subjectId always equals the key produced by identity.deriveSubHandle(this.name, subName).

    Note: this method does not check this Handle's own grant — it may not have one (Mode 1). Nesting is enforced by verifiers in Session.verifyAttested (a child grant exceeding the parent's is rejected with SCOPE_EXCEEDED/TTL_EXCEEDED at level ROOT).

    Parameters

    • subName: string
    • grant: AttestationGrant
    • Optionalopts: {
          ttlSeconds?: number;
          expiresAt?: number;
          jti?: string;
          now?: number;
      }
      • OptionalttlSeconds?: number
      • OptionalexpiresAt?: number
      • Optionaljti?: string
      • Optionalnow?: number

    Returns Promise<Attestation>

    const B = await station.attestSubHandle('connector-ccs', {
    audiences: ['ev-app.com'],
    scopes: ['charge:start', 'charge:stop'],
    maxSessionTtl: 7200,
    }, { ttlSeconds: 1800 });
    // Ship [A.token, B.token] together with the session token.
  • Deterministically derives a secret (e.g., a password or API key) for a specific service context.

    The private key never leaves this class, ensuring maximum security.

    Parameters

    • context: string

      A unique identifier for the service (e.g., 'google', 'github').

    • length: number = 16

      Length of the derived raw bytes (default: 16 bytes ≈ 22 chars base64url).

    Returns string

    A URL-safe base64 string suitable for use as a strong password.

    const githubPassword = handle.derivePassword('github', 20);
    
  • Derives a symmetric 256-bit channel key for secure communication between the Identity (controller) and this Handle (device/context).

    Both parties can independently compute this key because:

    • The Handle possesses its own private key directly.
    • The Identity can derive the same private key via identity.deriveHandle(name).

    This enables zero-knowledge encrypted channels without key exchange protocols.

    Parameters

    • context: string

      Channel identifier for domain separation (e.g., 'drone-001'). Both parties MUST use the same context.

    Returns Uint8Array

    A 32-byte Uint8Array suitable for AES-256-GCM encryption.

    // AES-GCM helpers are application-side; the protocol provides
    // only the key:
    const channelKey = droneHandle.deriveChannelKey('telemetry-v1');
  • Derives a shared secret using ECDH (X25519) for P2P key exchange.

    Converts the Ed25519 keypair to X25519 for Diffie-Hellman key agreement, then applies HKDF-SHA256 to produce a clean 32-byte channel key.

    Parameters

    • otherPublicKey: Uint8Array

      The Ed25519 public key of the other party (32 bytes).

    Returns Promise<Uint8Array>

    A Promise resolving to a 32-byte shared secret suitable for AES-256-GCM.

    const aliceShared = await aliceHandle.deriveSharedSecret(bobHandle.getPublicKey());
    const bobShared = await bobHandle.deriveSharedSecret(aliceHandle.getPublicKey());
    // aliceShared === bobShared