logo hsb.horse
← Back to blog index

Blog

TypeScript implementation of steganography using variant selectors

A TypeScript implementation of a steganography method that leverages Unicode's variant selector to embed hidden, invisible strings into text.

Published:

I wanted to actually try out Steganography using Unicode variant selectors: How to secretly hide secret strings in text #JavaScript - Qiita, but I wanted to implement it in TypeScript and make it work properly.

So I implemented it.

Code

class Steganography {
static #components = {
VS_BEGIN: 0xfe00,
STR_EMPTY: "",
REPLACER: /\uFEFF[\uFE00-\uFE0F]+/u,
toHex(value: number) {
return value.toString(16);
},
toHexDecimal(str: string): number {
return Number.parseInt(str, 16);
},
encode: {
PREFIX: "\u{FEFF}"
}
}
static encode(body: string, hidden: string): string {
const { VS_BEGIN, STR_EMPTY, encode, toHex, toHexDecimal } = this.#components;
return encode.PREFIX + [...hidden].flatMap(c => [...toHex(c.charCodeAt(0))])
.map((s) => String.fromCodePoint(VS_BEGIN + toHexDecimal(s)))
.join(STR_EMPTY) + body
}
static decode(value: string): [body: string, hidden: string] {
const { VS_BEGIN, STR_EMPTY, REPLACER, toHex, toHexDecimal } = this.#components;
let hidden = STR_EMPTY;
const body = value.replace(REPLACER, ([_, ...variationSelectors]) => {
hidden += variationSelectors
.map((c) => toHex(c.codePointAt(0)! - VS_BEGIN))
.reduce<string[][]>((acc, _, index, arr) => {
return index % 2 === 0 ? [...acc, arr.slice(index, index + 2)] : acc;
}, [])
.map(([a, b]) => String.fromCharCode(toHexDecimal(a + b)))
.join(STR_EMPTY);
return STR_EMPTY;
});
return [body, hidden]
}
}

Points

  1. Variation Selector: Use the range U+FE00-U+FE0F
  2. ZERO WIDTH NO-BREAK SPACE(U+FEFF): Hidden string start marker
  3. Encoding: Convert characters to HEX and express with variant selector
  4. Decode: Extract with regular expression and restore original string

Usage example

const body = "公開されるテキスト";
const hidden = "秘密のメッセージ";
const encoded = Steganography.encode(body, hidden);
console.log(encoded); // 見た目は「公開されるテキスト」
const [decodedBody, decodedHidden] = Steganography.decode(encoded);
console.log(decodedBody); // "公開されるテキスト"
console.log(decodedHidden); // "秘密のメッセージ"

summary

By leveraging Unicode’s glyph selector, you can embed secret strings into text in a way that is visually undetectable.

Regardless of its practicality, it’s a good subject to learn how Unicode works.