Class SubHandle

Represents a context-isolated child Handle in the Me2em hierarchy.

A SubHandle is always derived from a parent Handle and represents a leaf node in the derivation tree (MAX_DEPTH = 2). It inherits all cryptographic capabilities of Handle (signing, key derivation, etc.) but adds:

  • A derivation path ([handleName, subName]) carried in session tokens.
  • Constraint enforcement on audience, scopes, and TTL at session creation.
  • An optional expiration timestamp for temporary access grants.

SubHandle is used for polymorphism in Session: both Handle and SubHandle can create and verify sessions, but SubHandle sessions carry the additional hPath field for hierarchical auditing.

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.

  • 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
  • Returns the derivation path as a slash-separated string.

    Returns string

    The path string, e.g. "station-001/connector-1".

  • Returns the depth of this SubHandle in the derivation hierarchy.

    With MAX_DEPTH = 2, this is always 2 (Identity = 0, Handle = 1, SubHandle = 2).

    Returns number

    The depth, always 2.

  • Returns whether this SubHandle is a leaf node.

    With MAX_DEPTH = 2, a SubHandle is always a leaf and cannot derive children.

    Returns boolean

    Always true.

  • Validates session options against this SubHandle's constraints.

    Called internally by Session.create before signing. Throws if any constraint is violated:

    • audience not in allowedAudiences
    • any scope not in allowedScopes
    • ttl exceeds maxSessionTtl
    • current time is past expiresAt

    Parameters

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

      The session options to validate.

      • audience: string
      • scopes: string[]
      • ttl: number

    Returns void

    If any constraint is violated.