# How to Manage E2EE Keys and Key Rotation in LINEJS: A Complete Guide

> Master E2EE key management and rotation in LINEJS. Learn to securely generate, store, and negotiate keys with Curve25519 and AES using the E2EE class.

- Repository: [Evex  Developers/linejs](https://github.com/evex-dev/linejs)
- Tags: how-to-guide
- Published: 2026-03-01

---

**LINEJS provides a complete end-to-end encryption (E2EE) implementation through the `E2EE` class in [`packages/linejs/base/e2ee/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/e2ee/mod.ts), enabling secure key generation, storage, negotiation, and rotation using Curve25519 and AES-CBC/GCM cryptography.**

Managing cryptographic keys securely is essential for building privacy-focused LINE bots and clients. The LINEJS library implements the official LINE client's full E2EE workflow, allowing developers to manage E2EE keys and perform key rotation programmatically. This guide walks through the architecture, lifecycle management, and practical implementation details based on the actual source code in the `evex-dev/linejs` repository.

## Understanding the E2EE Architecture in LINEJS

The E2EE system in LINEJS consists of four primary components working together to handle cryptographic operations:

| Component | Responsibility | Key Source |
|-----------|---------------|------------|
| **`E2EE` class** | Manages local key pairs, fetches remote public keys, generates shared secrets using Curve25519 from `curve25519-js`, and handles AES-CBC/GCM encryption/decryption. | [[`packages/linejs/base/e2ee/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/e2ee/mod.ts)](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/e2ee/mod.ts) |
| **Talk Service** | Provides backend API wrappers for public-key negotiation, group-key registration, and key lookup operations. | [[`packages/linejs/base/service/talk/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/service/talk/mod.ts)](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/service/talk/mod.ts) |
| **Storage Layer** | Persists key material locally using keys like `e2eeKeys:<id>`, `e2eePublicKeys:<id>`, and `e2eeGroupKeys:<chatMid>`. | `packages/linejs/base/storage/*` |
| **Type Definitions** | Defines Thrift structs and error codes such as `E2EE_UPDATE_PRIMARY_DEVICE` for protocol compliance. | [[`packages/types/line_types.ts`](https://github.com/evex-dev/linejs/blob/main/packages/types/line_types.ts)](https://github.com/evex-dev/linejs/blob/main/packages/types/line_types.ts) |

## Key Lifecycle Management in LINEJS

LINEJS handles the complete lifecycle of E2EE keys through specific methods in the `E2EE` class. Understanding these stages ensures proper implementation of secure messaging.

### Self-Key Retrieval and Creation

The `E2EE.getE2EESelfKeyData(mid)` method (lines 23-46 in [`mod.ts`](https://github.com/evex-dev/linejs/blob/main/mod.ts)) manages your local identity key pair. It first attempts to read existing keys from storage using the prefix `e2eeKeys:<mid>`. If no local key exists, it enumerates all registered public keys via `client.talk.getE2EEPublicKeys()` and attempts to load the matching private key through `getE2EESelfKeyDataByKeyId`. Once located, the key is cached locally using `saveE2EESelfKeyData` for subsequent operations.

### Remote Public Key Negotiation

For one-to-one encrypted chats, `E2EE.getE2EELocalPublicKey(mid)` (lines 73-110) handles peer key acquisition. This method calls `talk.negotiateE2EEPublicKey` to obtain the recipient's public key and associated `keyId`, then caches the result under `e2eePublicKeys:<keyId>` to avoid repeated network requests.

### Group Key Acquisition and Registration

Group chats utilize shared secrets rather than pairwise keys. The system first checks `e2eeGroupKeys:<mid>` for cached entries (lines 112-124). If missing, it retrieves the current group key via `talk.getLastE2EEGroupSharedKey` (lines 130-147). For new groups without existing keys, `tryRegisterE2EEGroupKey` automatically generates and registers a fresh group key.

## How to Rotate E2EE Keys in LINEJS

Key rotation is critical for maintaining security when group membership changes or after defined time intervals. LINEJS implements atomic key rotation through the `tryRegisterE2EEGroupKey` method.

When you call `tryRegisterE2EEGroupKey(chatMid)` (lines 207-260), the following occurs:

1. **Participant Enumeration**: Retrieves current member public keys via `talk.getLastE2EEPublicKeys`.
2. **Secret Generation**: Creates a new random 32-byte group secret using `private_key` generation.
3. **Per-User Encryption**: For each member, derives a shared secret using Curve25519 (`generateSharedSecret`) and encrypts the group secret with AES-CBC (lines 41-49).
4. **Atomic Registration**: Transmits all encrypted secrets to LINE servers via `talk.registerE2EEGroupKey`, returning a `Pb1_U3` struct containing the new `groupKeyId`, `creator`, and `receiverKeyId`.

To rotate keys after a member leaves, simply re-invoke `tryRegisterE2EEGroupKey` with the same `chatMid`. The server handles distribution atomically, ensuring all remaining participants can decrypt the new secret with their private keys.

## Practical Implementation Examples

The following examples demonstrate how to manage E2EE keys and perform rotation using LINEJS.

### Initializing the Client and E2EE Helper

```typescript
import { LineClient } from "@evex/linejs";

const client = await LineClient.loginWithQrCode();
const e2ee = client.base.e2ee;  // E2EE class instance

```

### Retrieving Your Private Key

```typescript
// mid is your user ID (e.g., "Uxxxxxxxxxxxxxxxx")
const myKey = await e2ee.getE2EESelfKeyData(client.profile!.mid);
console.log("Key ID:", myKey.keyId);

```

*Reference: [[`mod.ts`](https://github.com/evex-dev/linejs/blob/main/mod.ts) lines 23-46](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/e2ee/mod.ts#L23-L46)*

### Negotiating Peer Public Keys

```typescript
const peerMid = "Uabcdef1234567890";
const peerKey = await e2ee.getE2EELocalPublicKey(peerMid);
console.log("Peer key (base64):", peerKey.toString("base64"));

```

*Reference: [[`mod.ts`](https://github.com/evex-dev/linejs/blob/main/mod.ts) lines 73-110](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/e2ee/mod.ts#L73-L110)*

### Registering and Rotating Group Keys

```typescript
const groupMid = "C1234567890123456";

// Initial registration or rotation
const result = await e2ee.tryRegisterE2EEGroupKey(groupMid);
console.log("Group Key ID:", result.groupKeyId);

// Post-rotation cleanup (optional)
await client.storage.delete("e2eeGroupKeys:" + groupMid);

```

*Reference: [[`mod.ts`](https://github.com/evex-dev/linejs/blob/main/mod.ts) lines 207-260](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/e2ee/mod.ts#L207-L260)*

### Sending Encrypted Messages

```typescript
const encryptedParts = await e2ee.encryptE2EEMessage(
  groupMid,
  "Secure message content",
  undefined  // default content type
);

await client.base.talk.sendMessage({
  to: groupMid,
  contentType: 0,  // TEXT
  content: encryptedParts,  // Buffer array
});

```

*Reference: [[`mod.ts`](https://github.com/evex-dev/linejs/blob/main/mod.ts) lines 382-421](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/e2ee/mod.ts#L382-L421)*

### Registering New User Public Keys

To publish a new public key to the LINE backend (typically during initial setup), use the service method directly:

```typescript
// Register a new public key (lines 629-639 in talk service)
await client.base.talk.registerE2EEPublicKey({
  keyData: publicKeyBuffer,
  keyId: newKeyId,
});

```

*Reference: [[`talk/mod.ts`](https://github.com/evex-dev/linejs/blob/main/talk/mod.ts) lines 629-639](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/service/talk/mod.ts#L629-L639)*

## Summary

- **The `E2EE` class** in [`packages/linejs/base/e2ee/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/e2ee/mod.ts) provides the primary interface for all cryptographic operations, including key storage, negotiation, and encryption.
- **Self-key management** uses `getE2EESelfKeyData` to retrieve or create your Curve25519 key pair, cached under `e2eeKeys:<mid>`.
- **Peer key negotiation** occurs through `getE2EELocalPublicKey`, which calls `talk.negotiateE2EEPublicKey` and caches results as `e2eePublicKeys:<keyId>`.
- **Group key rotation** is handled by `tryRegisterE2EEGroupKey`, which generates fresh 32-byte secrets, encrypts them per-participant using AES-CBC, and registers them via `talk.registerE2EEGroupKey`.
- **Storage prefixes** follow the pattern `e2eeKeys:*` for private keys, `e2eePublicKeys:*` for cached public keys, and `e2eeGroupKeys:*` for shared group secrets.

## Frequently Asked Questions

### How does LINEJS store E2EE keys locally?

LINEJS persists cryptographic material through a pluggable storage layer located in `packages/linejs/base/storage/*`. Private keys are stored under `e2eeKeys:<mid>`, cached public keys under `e2eePublicKeys:<keyId>`, and group shared secrets under `e2eeGroupKeys:<chatMid>`. The storage backend can be memory-based, file-based, or use IndexedDB depending on your configuration.

### What cryptographic algorithms does LINEJS use for E2EE?

According to the source code in [`packages/linejs/base/e2ee/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/e2ee/mod.ts), LINEJS uses **Curve25519** for elliptic-curve Diffie-Hellman key exchange (via `sharedKey` from `curve25519-js`) and **AES-CBC/GCM** for symmetric encryption of messages and group secrets. This matches the cryptographic suite used by the official LINE client.

### When should I rotate group keys in LINEJS?

You should invoke `tryRegisterE2EEGroupKey` to rotate keys whenever group membership changes (such as when a member leaves or joins) or according to your application's security policy time interval. The method automatically regenerates secrets and re-encrypts them for all current participants, ensuring forward secrecy.

### Can I register new public keys for my LINEJS client?

Yes. Use the `talk.registerE2EEPublicKey` method (defined in [`packages/linejs/base/service/talk/mod.ts`](https://github.com/evex-dev/linejs/blob/main/packages/linejs/base/service/talk/mod.ts) lines 629-639) to publish new public keys to the LINE backend. This is typically handled automatically during initial setup but can be invoked manually if you need to update your client's cryptographic identity.