npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@zigbang-smarthome/seal

v26.5.0

Published

Wire-compatible TypeScript port of Samsung SDS SEAL 2.0.1. See ./docs/protocol.md.

Readme

@zigbang-smarthome/seal

Wire-compatible TypeScript port of Samsung SDS SEAL 2.0.1 (com.sds.seal) — a proprietary IoT device authentication / encryption protocol used by Samsung-branded smart doorlocks, wallpads, and lobby phones manufactured 2014~. Server-side only. Runs on Bun, Node 18+, Deno, and Cloudflare Workers (workerd).

🪦 DEPRECATED — Legacy compatibility only

SEAL has not received an updated release since the 2016-09-23 build of v2.0.1, and no public security review has been published. This package exists solely so server infrastructure operating SEAL-speaking devices doesn't have to remain pinned to the original Java runtime. Do not adopt SEAL for new projects. Modern alternatives:

  • Matter PASE (SPAKE2+ + cert chain) — IoT industry standard since 2022
  • TLS 1.3 + mTLS — generic client–server
  • OPAQUE — formally-proven password-authenticated key exchange

Install

bun add @zigbang-smarthome/seal
# or: npm i @zigbang-smarthome/seal / pnpm add @zigbang-smarthome/seal

Zero runtime dependencies on Node-specific APIs. Uses crypto.subtle (WebCrypto), TextEncoder/TextDecoder, btoa/atob, and native bigint. Pulls in three audited libraries:

| Package | Why | |---|---| | @noble/hashes | byte utilities (concatBytes, bytesToHex, hexToBytes); audited by Trail of Bits | | bigint-mod-arith | modular exponentiation | | fast-xml-parser | optional — only loaded by the XML config helper |


What's in the box

import {
  // wire codec
  decodeHandshakeRequest, encodeHandshakeRequest,
  decodeHandshakeResponse, encodeHandshakeResponse,
  decodeEncryptedMessage, encodeEncryptedMessage,
  decodeOnePass, encodeOnePass,

  // server side
  processHandshake, decryptOnePass,
  encryptMessage, decryptMessage,

  // client side
  SealClient, encryptOnePass,

  // public-key derivation (id → publicKey)
  AlphabetPublicKeyGenerator, PublicParamPublicKeyGenerator, isValidKeyPair,

  // ClientLongTermKey
  deriveCltk, checkCltk,

  // server config XML loader
  parseServerSettingsXml,

  // primitives (re-exported for convenience)
  modPow, sha256, aesCbcEncrypt, aesCbcDecrypt,
  bigintToBytesSigned, bytesToBigintUnsigned,
  bytesToHex, hexToBytes,
  utf8, fromUtf8, b64encode, b64decode,
  randomBigint,

  // typed errors (all extend SealError)
  SealError, InvalidMessageFormatError, VersionIncompatibleError,
  DecryptFailedError, UserAuthFailedError, IdRangeViolationError, InvalidSettingsError,
} from "@zigbang-smarthome/seal";

Full protocol specification (wire format, handshake math, OnePass derivation, security analysis): docs/protocol.md.


Quick start

Server — handshake

import {
  processHandshake, encodeHandshakeResponse, decodeHandshakeRequest,
  encryptMessage, decryptMessage, encodeEncryptedMessage, decodeEncryptedMessage,
  randomBigint, type SealServerConfig,
} from "@zigbang-smarthome/seal";

const cfg: SealServerConfig = {
  serverId: "MYSERVER",
  versions: {
    1: { n: 0x...n, g: 0x...n, serverPrivateKey: 0x...n },
    2: { n: 0x...n, g: 0x...n, serverPrivateKey: 0x...n },
  },
};

// Inbound HsReq from device
const req = decodeHandshakeRequest(b64FromDevice);

// Process — `r` is the server's 160-bit ephemeral.
const r = randomBigint(160);
const { response, sessionKey } = await processHandshake(cfg, req, longTermKey, r);

// Send wire-encoded response back
const respB64 = encodeHandshakeResponse(response);

if (sessionKey === null) {
  // auth failure — `response` is the header-only blob
  return;
}

// Subsequent traffic
const em = await encryptMessage(sessionKey, "hello device");
const wire = encodeEncryptedMessage(em);

const inbound = decodeEncryptedMessage(b64FromDevice2);
const plaintext = await decryptMessage(sessionKey, inbound);

Client — handshake

import { SealClient, encodeHandshakeRequest, decodeHandshakeResponse, randomBigint, modPow } from "@zigbang-smarthome/seal";

const client = await SealClient.connect({
  n, g, serverId: "MYSERVER",
  serverPublicKey: modPow(g, sk, n), // out-of-band knowledge of g^sk
  x: randomBigint(160),
});
const req = await client.makeHandshakeRequest({
  clientId: "DEVICE001",
  clientPw: "longTermKey",
  extraParams: ["serial", "model"],
});
const reqB64 = encodeHandshakeRequest(req);

// ...send to server, receive respB64...
const ok = await client.finishHandshake(decodeHandshakeResponse(respB64));
if (ok) {
  // `client.sessionKey` is now usable
}

OnePass — anonymous one-shot encryption

import { encryptOnePass, decryptOnePass, encodeOnePass, decodeOnePass, randomBigint } from "@zigbang-smarthome/seal";

// Client
const opMsg = await encryptOnePass({
  n, g, serverId: "MYSERVER",
  serverPublicKey: modPow(g, sk, n),
  plaintext: "diagnostic-payload",
  x1: randomBigint(160),
  x2: randomBigint(160),
});
const wire = encodeOnePass(opMsg);

// Server
const recovered = await decryptOnePass(cfg, decodeOnePass(wire));

Public-key generators

makePublicKey(id) deterministically derives the public key for an entity (server or client) from its id string. Used at config-load time to verify g^sk == publicKey(serverId).

import { AlphabetPublicKeyGenerator, PublicParamPublicKeyGenerator, isValidKeyPair } from "@zigbang-smarthome/seal";

// v1: in-algorithm derivation (4-char chunking + sort permutation + SHA-256)
const v1 = new AlphabetPublicKeyGenerator(n1, g1);
const pk1 = await v1.makePublicKey("MYSERVER");

// v2: precomputed binary lookup table file
const v2 = new PublicParamPublicKeyGenerator(binFileBytes);
const pk2 = await v2.makePublicKey("MYSERVER");

// validate (g, sk) pair against the generator
const ok = await isValidKeyPair(v1, sk1, "MYSERVER");

XML config loader

Parses the server-settings format used by Samsung SDS SEAL deployments (XXE-protected — DOCTYPE rejected before parse).

import { parseServerSettingsXml } from "@zigbang-smarthome/seal";

const xml = await readFile("/etc/seal/server-settings.xml", "utf-8");
const cfg = parseServerSettingsXml(xml, "/etc/seal/server-settings.xml");
// cfg.versions[1] = { publicParameterN, publicParameterG, serverPrivateKey } (hex strings)
// cfg.versions[2] = { binFilePath, serverPrivateKey } (hex string for sk)

Runtime support

| Runtime | Status | |---|---| | Bun 1.3+ | ✅ | | Node 18+ | ✅ | | Deno 2+ | ✅ | | Cloudflare Workers (workerd) | ✅ |

The library only assumes Web-standard globals (crypto, TextEncoder, btoa/atob, bigint). On Node 16/17, polyfill globalThis.crypto from node:crypto's webcrypto.


⚠️ Known security issues

These are properties of the SEAL protocol itself. Wire compatibility requires preserving them, but every consumer should be aware:

  1. Predictable server ephemeral randomness. The Java reference implementation uses new java.util.Random(System.currentTimeMillis()) for its 160-bit ephemeral, which is not a CSPRNG. This port intentionally diverges on this pointrandomBigint(160) uses crypto.getRandomValues. Callers must pass a CSPRNG-derived value as r.
  2. No authenticated cipher. EncryptedMessage is AES-128-CBC-PKCS5 with no MAC; bit-flip attacks on the ciphertext propagate to plaintext (CBC malleability).
  3. No forward secrecy. The server's long-term private key is static; compromise replays all past sessions.
  4. OnePass has no replay protection. No nonce or timestamp.
  5. 1024-bit modulus. NIST SP 800-131A deprecates 1024-bit DH-style operations by 2030.
  6. 1-byte length prefix in wire format. Caps each BigInteger-bearing field at 255 bytes, indirectly enforcing the small modulus.
  7. No public cryptographic review. Not a peer-reviewed protocol; 0 third-party analysis published.

Full analysis: docs/protocol.md §8.


How it's verified

The actual seal-server-2.0.1.jar runs in a deterministic harness (Java) that emits byte-level test vectors. The TS implementation is asserted against those vectors at every release across all four runtimes.

seal-server-2.0.1.jar (real Java JAR)
       ↓ harness/SealVectorGen.java with reflection-injected randomness
test-vectors/vectors.json (Java-produced bytes)
       ↓ test/vectors.test.ts replays the same inputs
       ↓
TS output == Java output, byte-for-byte (37 tests × 4 runtimes)

Coverage: handshake (cipher3 / sessionKey / confirmMessage), AES-128-CBC-PKCS5, OnePass derivation, ClientLongTermKey, v1/v2 public-key generators, all four message-type wire codecs, BigInteger signed-byte semantics, auth-failure paths.


License

The licensing scope of the underlying SEAL protocol is currently under review. Until that completes, this package is published as private and should be treated as internal-use-only — do not redistribute or fork outside the publishing organization. The npm-distributable form will be tagged once review concludes.

For internal status and contract verification details, contributors should consult the project tracker rather than this README.


Layout

@zigbang-smarthome/seal/
├── src/index.ts             — implementation
├── docs/protocol.md         — full protocol spec
├── test/vectors.test.ts     — cross-runtime test suite
├── test-vectors/            — JSON vectors emitted by harness
└── harness/                 — Java vector generator (Maven)