arkimg 0.0.1

Encrypted image library


To use this package, run the following command in your project's root directory:

Manual usage
Put the following dependency into your project's dependences section:

ArkImg

GitHub tag CI Status downloads BSL-1.0 codecov Document

ArkImg is a library that allows you to embed encrypted data into image files (PNG, JPEG, BMP, WebP). It can be used to hide and securely store or transfer confidential information or files within images.

Features

  • Embed and extract data in PNG/JPEG/BMP/WebP images
  • Data encryption using AES
  • Signing and verification using Ed25519
  • Support for embedding multiple files and metadata

How to Use as a Library

Usage

dub add arkimg

Sample Code

import arkimg;
import std.file;
auto commonKey = createCommonKey();
auto prvKey    = createPrivateKey();
auto pubKey    = createPublicKey(prvKey);

auto img = new ArkPng;
// Set the AES common key. 128bits/192bits/256bits
img.setKey(commonKey);
// Load and set the base image to be used as a thumbnail
img.baseImage = cast(immutable(ubyte)[])std.file.read("input.bmp");
// Insert a hidden file
img.addSecretItem(cast(immutable(ubyte)[])std.file.read("secret.png"));
// Optionally, you can sign the image
// The key used for image signing must be a 32-byte Ed25519 private key.
img.sign(prvKey);
assert(img.hasSign);
assert(img.verify(pubKey));

std.file.write("encrypted.png", img.save());

API

  • ArkImg: interface
  • void load(in ubyte[] binary): Load image file data
  • immutable(ubyte)[] save() const: Save data to image file
  • void setKey(in ubyte[] commonKey): Set the common key for encryption/decryption
  • void sign(in ubyte[] prvKey): Sign all data
  • bool verify(in ubyte[] pubKey) const: Verify the signature of all data
  • bool hasSign() const: Check if the image has a signature
  • void metadata(in JSONValue metadata): Set metadata
  • JSONValue metadata() const: Get metadata
  • void baseImage(in ubyte[] binary, string mimeType = "image/bmp"): Set base image
  • immutable(ubyte)[] baseImage(string mimeType = "image/bmp"): Get base image
  • void addSecretItem(in ubyte[] binary, string name = null, string mimeType = null, in ubyte[] prvKey = null): Add attached data (in plaintext)
  • void clearSecretItems(): Remove all attached data
  • size_t getSecretItemCount() const: Get the number of attached encrypted data
  • immutable(ubyte)[] getDecryptedItem(size_t idx) const: Get decrypted attached data
  • immutable(ubyte)[] getEncryptedItem(size_t idx) const: Get encrypted attached data
  • ArkBmp: ArkImg
  • ArkPng: ArkImg
  • ArkJpg: ArkImg
  • ArkWebp: ArkImg
  • Helper functions
  • string mimeType(string filename): Get MIME type from filename
  • immutable(ubyte)[] createCommonKey(size_t keySize = 32): Generate a common key
  • immutable(ubyte)[] createRandomIV(): Generate an initialization vector (usually not used)
  • immutable(ubyte)[] createPrivateKey(): Generate a private key for signing
  • immutable(ubyte)[] createPublicKey(in ubyte[] prvKey): Generate a public key for verification
  • ArkImg loadImage(immutable(ubyte)[] binary, string mimeType = "image/png", in ubyte[] commonKey, in ubyte[] iv = null): Load image from byte array
  • ArkImg loadImage(string filename, in ubyte[] commonKey = null, in ubyte[] iv = null): Load image from filename
  • immutable(ubyte)[] saveImage(ArkImg img, string mimeType = "image/png", in ubyte[] commonKey, in ubyte[] iv = null): Save image to byte array (save to file separately)

File Structure Specifications

PNG

Data is included at any position, but usually after the IDAT chunk (image data):

  • eDAt chunk: Encrypted data body. There may be multiple per file.
  • eMDt chunk: Metadata. 0 or 1 per file.

Note: Some browsers or viewers may not be able to handle PNG images with chunks after the IDAT chunk.

JPG

Data is included after the EOI (end marker) in the following structure:

[SOI] | [Segment 1] | [Segment 2] | ... | [EOI] | [Encrypted Chunk 1] | [Encrypted Chunk 2] ...
[Encrypted Chunk]: [Type: 4 bytes] | [Length: 4 bytes] | [Data: N bytes]

  • Encrypted chunk: multiple
  • Signature: 4 bytes
  • Data length: 4 bytes LittleEndian unsigned 64-bit integer
  • Data: N bytes

There may be multiple encrypted chunks. Each encrypted chunk contains a signature, a data length, and variable-length data.

  • Signature ['E', 'D', 'A', 'T']: Data is encrypted data body. There may be multiple per file. 4 bytes: [0x45, 0x44, 0x41, 0x54] as "EDAT" in ASCII codes.
  • Signature ['E', 'M', 'D', 'T']: Data is metadata. 0 or 1 per file. 4 bytes: [0x45, 0x4d, 0x44, 0x54] as "EMDT" in ASCII codes.

Note: Some browsers or viewers may not be able to handle JPG images with data after the EOI marker.

BMP

Data is included after the end of the pixel data specified by the image size in the BMP info header, in the following structure:

[Bitmap File Header] | [Bitmap Info Header] | [Color Pallete] | [Pixel Data] | [Encrypted Chunk 1] | [Encrypted Chunk 2] ...
[Encrypted Chunk]: [Signature: 4 bytes] | [Length: 4 bytes] | [Data: N bytes]

  • Encrypted chunk: multiple
  • Signature: 4 bytes
  • Data length: 4 bytes LittleEndian unsigned 64-bit integer
  • Data: N bytes

There may be multiple encrypted chunks. Each encrypted chunk contains a signature, a data length, and variable-length data.

  • Signature ['E', 'D', 'A', 'T']: Data is encrypted data body. There may be multiple per file. 4 bytes: [0x45, 0x44, 0x41, 0x54] as "EDAT" in ASCII codes.
  • Signature ['E', 'M', 'D', 'T']: Data is metadata. 0 or 1 per file. 4 bytes: [0x45, 0x4d, 0x44, 0x54] as "EMDT" in ASCII codes.

WebP

Data is included after the end of the pixel data specified by the image size in the BMP info header, in the following structure:

  • Encrypted chunk: multiple
  • Chunk FourCC: 4 bytes
  • Chunk size: 4 bytes LittleEndian unsigned 32-bit integer
  • Payload: N bytes

[Webp File Header] | [Chunks] | ... | [Encrypted Chunk 1] | [Encrypted Chunk 2] ...
[Encrypted Chunk]: [FourCC: 4 bytes] | [Chunk Size: 4 bytes] | [Data: N bytes]

There may be multiple encrypted chunks. Each encrypted chunk contains a FourCC, a chunk size, and variable-length payload.

  • FourCC ['E', 'D', 'A', 'T']: Data is encrypted data body. There may be multiple per file. 4 bytes: [0x45, 0x44, 0x41, 0x54] as "EDAT".
  • FourCC ['E', 'M', 'D', 'T']: Data is metadata. 0 or 1 per file. 4 bytes: [0x45, 0x4d, 0x44, 0x54] as "EMDT".

Encrypted Data Body

Encrypted byte array in one of the following formats:

  • GCM encryption mode
  • Start: IV (12 bytes)
  • Data: AES-encrypted data
  • End: Authentication data (16 bytes)
  • CBC encryption mode (no IV specified/IV included in data)
  • Start: IV (16 bytes)
  • Data: AES-encrypted data
  • CBC encryption mode (IV specified/IV not included in data)
  • Data: AES-encrypted data

Decrypting the encrypted data body yields the confidential information as a byte array.

Metadata

Encrypted byte array in one of the following formats:

  • GCM encryption mode
  • Start: IV (12 bytes)
  • Data: AES-encrypted data
  • End: Authentication data (16 bytes)
  • CBC encryption mode (no IV specified/IV included in data)
  • Start: IV (16 bytes)
  • Data: AES-encrypted data
  • CBC encryption mode (IV specified/IV not included in data)
  • Data: AES-encrypted data

Decrypting the metadata yields the following JSON data:

  • (root): Object
  • (root).items: Array, optional
  • (root).items[*]: Object / Metadata for the Nth encrypted data
  • (root).items[*].name: String, optional / Filename of the encrypted data
  • (root).items[*].mime: String, optional / File type of the encrypted data
  • (root).items[*].sign: String, optional / 32-byte Ed25519 signature data encoded in Base64URLNoPadding
  • (root).items[*].modified: String, optional / File modification date (ISO8601/UTC) format YYYY-MM-DDTHH:mm:SS.SSSZ
  • (root).items[*].comment: String, optional / Any comment
  • (root).items[*].(any): Any, optional / Any additional data per encrypted data
  • (root).(any): Any, optional / Any additional data for the entire file

Example JSON for metadata:

{
  "items": [
    {
      "comment": "Test encrypted image",
      "mime": "image/png",
      "modified": "2025-05-02T10:10:02.1455478Z",
      "name": "secret.png",
      "sign": "C5S8mzGFto9X8aUStlkvue06cGKYA6G7bi4alClNQAveq1GfZKxNnhlUsBBWqxzjm-umSIUSuPvR5m0gb9e_Bw"
    }
  ]
}

How to Use the Command Line Tool

Main Features

  • Embed secret data into images (archive)
  • Extract embedded data (extract)
  • Encrypt and decrypt data
  • Edit, delete, and list embedded data
  • Key generation, signing, and verification

Build

cd examples/arkimg_cli
dub build

Usage

Command Line Examples

# Generate a key
# CommonKey:  F25B09DF39C113BD5F81871ED12221C2
# The generated key will be output. Use this CommonKey for <key> below.
$ arkimg keyutil --genkey

# Embed a file into an image (file specified)
# Encrypt and embed secret.png into input.png, save as encrypted.png
arkimg encrypt -i input.png -s secret.png -o encrypted.png -k <key>

# Extract an embedded file (file specified)
# Decrypt and extract secret.png from encrypted.png, save as decrypted.png
arkimg encrypt -i encrypted.png -s secret.png -o decrypted.png -k <key>

# Embed all files in a directory into an image
# Encrypt and embed all files in secretdir into input.png, save as encrypted.png
arkimg archive -i input.png -s secretdir -o encrypted.png -k <key>

# Extract all embedded files to a directory
# Decrypt and extract all files from encrypted.png, save to outdir
arkimg extract -i encrypted.png -o outdir -k <key>

License

BSL-1.0

This program is provided under the BSL-1.0 license, but depends on the following libraries, each provided under their respective licenses.

This project depends on the following libraries:

Optionally, the following libraries can be enabled:

Authors:
  • SHOO
Dependencies:
libwebp, jpeg-turbo, libpng, openssl-static
Versions:
0.0.1 2025-Jul-30
~main 2025-Jul-30
Show all 2 versions
Download Stats:
  • 0 downloads today

  • 1 downloads this week

  • 2 downloads this month

  • 2 downloads total

Score:
0.0
Short URL:
arkimg.dub.pm