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

shelpngcrypt

v0.1.0

Published

Encrypt UTF-8 text into PNG metadata with optional LSB redundancy.

Readme

ShelPngCrypt

Encrypt UTF-8 text into PNG files with a shared passphrase. The library stores the encrypted payload in PNG metadata and can optionally duplicate the same payload into pixel LSBs for redundancy.

Features

  • Encrypt plain text into an existing PNG or a generated blank PNG
  • Shared-passphrase encryption with PBKDF2-SHA256 + AES-256-GCM
  • Dual-channel embedding:
    • PNG tEXt metadata
    • RGBA pixel LSB redundancy
  • Recover text from PNG using the same passphrase
  • Works with Uint8Array for both Node.js and browser usage
  • Includes a CLI

Security Model

This package is a text-encryption carrier, not an advanced anti-forensics steganography system.

  • Confidentiality depends on the passphrase strength
  • Metadata may be stripped by platforms that rewrite PNG files
  • LSB data may be destroyed by recompression, redraw, screenshotting, or format conversion
  • The package does not try to hide the fact that data may exist in the image

Install

npm install shelpngcrypt

For local development:

npm install
npm run build
npm test

Library Usage

Encrypt text into a PNG

import { encryptTextToPng } from "shelpngcrypt";
import { readFile, writeFile } from "node:fs/promises";

const cover = new Uint8Array(await readFile("cover.png"));

const result = await encryptTextToPng("hello from ShelPngCrypt", {
  passphrase: "shared-secret",
  inputPng: cover
});

await writeFile("secret.png", result.png);
console.log(result.channel);
console.log(result.capacity);

Auto-generate a carrier PNG

import { encryptTextToPng } from "shelpngcrypt";
import { writeFile } from "node:fs/promises";

const result = await encryptTextToPng("text without an input PNG", {
  passphrase: "shared-secret"
});

await writeFile("generated-secret.png", result.png);

Decrypt text from a PNG

import { decryptTextFromPng } from "shelpngcrypt";
import { readFile } from "node:fs/promises";

const png = new Uint8Array(await readFile("secret.png"));
const plaintext = await decryptTextFromPng(png, "shared-secret");

console.log(plaintext);

Inspect whether a PNG contains data

import { inspectPng } from "shelpngcrypt";
import { readFile } from "node:fs/promises";

const png = new Uint8Array(await readFile("secret.png"));
const result = await inspectPng(png);

console.log(result);

Browser Usage

The public API uses Uint8Array, so browser usage can stay close to the Node version:

import { encryptTextToPng, decryptTextFromPng } from "shelpngcrypt";

const file = input.files?.[0];
const inputBytes = file ? new Uint8Array(await file.arrayBuffer()) : undefined;

const encrypted = await encryptTextToPng("browser secret", {
  passphrase: "shared-secret",
  inputPng: inputBytes
});

const blob = new Blob([encrypted.png], { type: "image/png" });
const url = URL.createObjectURL(blob);

image.src = url;

const plaintext = await decryptTextFromPng(encrypted.png, "shared-secret");
console.log(plaintext);

Example Demo

A browser demo is included in example/index.html.

Run it locally:

npm install
npm run example:dev

Build the static demo:

npm run example:build

What it does:

  • Upload an optional carrier PNG
  • Enter text and a shared passphrase
  • Generate and download an encrypted PNG
  • Inspect a PNG for embedded payloads
  • Decrypt text from a PNG in the browser

API

encryptTextToPng(plaintext, options)

type EncryptOptions = {
  passphrase: string;
  inputPng?: Uint8Array;
  width?: number;
  height?: number;
  redundant?: boolean;
};

type EncryptResult = {
  png: Uint8Array;
  channel: "metadata" | "metadata+lsb";
  capacity?: { bytesTotal: number; bytesUsed: number };
};

Behavior:

  • If inputPng is provided, data is embedded into that PNG
  • If inputPng is omitted:
    • width and height are used if both exist
    • otherwise the library generates the smallest square PNG it can
  • redundant defaults to true
  • When redundant is true, insufficient LSB capacity throws an error

decryptTextFromPng(png, passphrase)

  • Reads metadata first
  • Falls back to LSB payload if metadata is missing or unreadable
  • Throws 口令错误或图片已损坏 when decryption or recovery fails

inspectPng(png)

Returns:

{
  hasMetadataPayload: boolean;
  hasStegoPayload: boolean;
  recoverable: boolean;
  version?: number;
}

CLI

After building:

npm run build
node dist/cli.js --help

Encrypt using an existing PNG

node dist/cli.js encrypt --in cover.png --out secret.png --text "hello" --passphrase "shared-secret"

Encrypt without an existing PNG

node dist/cli.js encrypt --out secret.png --text "hello" --passphrase "shared-secret" --width 512 --height 512

Metadata only

node dist/cli.js encrypt --out secret.png --text "hello" --passphrase "shared-secret" --metadata-only

Decrypt

node dist/cli.js decrypt --in secret.png --passphrase "shared-secret"

Inspect

node dist/cli.js inspect --in secret.png

Project Layout

src/
  cli.ts
  crypto.ts
  index.ts
  payload.ts
  png-metadata.ts
  png-stego.ts
example/
  index.html
  main.js
  styles.css
test/
  index.test.ts

Development

npm install
npm run check
npm run build
npm test

Limitations

  • PNG only
  • UTF-8 text only
  • No streaming mode for large payloads
  • No resistance guarantee against image transformation pipelines
  • Current metadata storage uses tEXt, not encrypted PNG-standard ancillary compression chunks

Next Recommended Work

  • Add browser demo page
  • Add README badges and package publishing metadata
  • Add payload size guidance and better CLI output
  • Add E2E tests against real-world PNG files
  • Add optional metadata-only vs redundancy mode examples in docs

See the full implementation guide in docs/IMPLEMENTATION_GUIDE.md.