Need help with CryptoStego?
Click the “chat” button below for chat support from the developer who created it, or find similar developers for support.

About the developer

zeruniverse
245 Stars 28 Forks Other 55 Commits 0 Opened issues

Description

JS library for steganography with encryption - Hide text in an image with encryption and obfuscation.

Services available

!
?

Need anything else?

Contributors list

CryptoStego

JS library for steganography with encryption - Hide text in an image with encryption and obfuscation. Support least significant bit mode and DCT mode.

Version

v1.8

DEMO

http://stego.js.org Note: Library needs HTML5 support!

Download

Download cryptostego.min.js Note: This JS library needs HTML5 support!

Features

  • Obfuscation - Random initialization of invalid bits
  • Non-linear bit-by-bit message storage without any header. Built for absolute security (No signal for password error. Wrong password results in wrong message).
  • New in 1.8: Valid bits and their order decided by Mersenne Twister PRNG, and seed generated using
    SHA512(password)
    . This is faster compared to old method and leads to less JS errors.
  • LSB (Least Significant Bit) mode
    • Use least significant bits of RGB channels of each pixel to store message
    • Resulting image is visually identical to original one
    • Can only be stored in non-compressed format such as PNG
  • DCT (Discrete cosine transform) mode
    • Store information by slightly changing lowest frequency component of each block in frequency domain
    • Robust to image compression but stores less data compared to LSB mode
    • Resulting image looks different from original one
    • New in 1.8: Add support CbCr downsampling. Hence more robust on compression at level 5
    • New in 1.8: Add support to JPEG style quantization and the bucket range is thus more reasonable on secrecy and robustness trade-off
    • New in 1.8: Use DCT difference instead of absolute value. Thus basic robustness against filtering (e.g. exposure, brilliance adjustment)
    • New in 1.8: Add support to write data into any frequency band on DCT space. Before I only wrote to lowest frequency band.
    • New in 1.8: More detailed comments in base class code. You can adjust all parameters for your use case

Usage

This library provides 3 wrapper functions. Use

 in your HTML to include this library.

loadIMGtoCanvas(inputid, canvasid, callback, maxsize)

This function loads an image from file input to a dynamically generated canvas. After that, it will call

callback()
function to do some stuff, and then delete the generated canvas. +
inputid
is the id of the html5 file input. + You need to put a file input element like
 in your HTML and ask user to select an image here.
  + 
canvasid
is the id of the canvas that this function will generate.
canvasid
will be generated by this function dynamically when called.
You will use this
canvasid
in
callback()
to locate the canvas. + You must use an id that not currently used in your HTML, When this function is called, a canvas with id to be
canvasid
will be created. Then, the image selected by user will be loaded to this canvas. + Canvas created by this function (
canvasid
) will be deleted after finishing calling callback function.
So you might want to copy image in this canvas to another canvas in callback function or trig a download in callback function. +
callback
is the callback function that will be called after image successfully loaded to canvas. + Use
canvasid
to locate the canvas in
callback()
function. +
canvasid
will be deleted after calling
callback()
. Make sure you store all data needed in
callback
function. + An example for callback -> download the result image after steganography:
JavaScript
    //callback function is writefunc()
    function writefunc(){
        if(writeMsgToCanvas('canvas',$("#msg").val(),$("#pass").val(),3)!=null){
        var myCanvas = document.getElementById("canvas"); //canvasid='canvas'
        var image = myCanvas.toDataURL("image/jpeg",1.0);
        var element = document.createElement('a');
        element.setAttribute('href', image);
        element.setAttribute('download', 'result.jpg');
        element.style.display = 'none';
        document.body.appendChild(element);
        element.click();
        document.body.removeChild(element);
        }
    }
    //For convenience, jQuery is used here. But that's not necessary.
    loadIMGtoCanvas('file','canvas',writefunc,500);
+
maxsize
is the max width or height for created canvas (default value is 0) + If either image width or image height larger than
maxsize
, This function scale the image so that it fits the generated canvas with width and height not larger than
maxsize
+ If
maxsize
<=0, image will not be scaled (use original size). This is not recommended if your callback function is steganography function (write info to image). As super large image will make browser dead. A recommended value is 500. + Make sure when callback function is reading info from image, your
maxsize
is at least the
maxsize
you use for steganography. As steganography algorithm is not robust to scale.

writeMsgToCanvas(canvasid,msg,pass,level)

This function writes your message into image in canvas

canvasid
. Before calling this function, make sure some image is loaded in
canvasid
. Usually, this function will be called in callback function of
loadIMGtoCanvas
+
canvasid
specifies the id of the canvas to whose image the message will be written to. +
msg
specifies the message. +
pass
specifies the password for retrieving the message. (default value is '') +
level
an integer specifies the steganography level. [0-5] +
0
-> LSB mode (default), result image looks identical to original image. +
1
-
5
-> DCT mode, higher value means better robustness to compression but the image looks more different from the original one. + Generally, if you don't need image compression robustness, use level
0
, otherwise, level
2
and
3
are recommended.
  • Return value is either
    true
    or a string,
    ===true
    stands for
    success
    and a string stands for
    failure
    with string being error message.

readMsgFromCanvas(canvasid,pass,level)

This function reads your message from image in canvas

canvasid
. Before calling this function, make sure some image is loaded in
canvasid
. Usually, this function will be called in callback function of
loadIMGtoCanvas
+ To successfully read message,
pass
and
level
should be same as what you use in
writeMsgToCanvas
. And the image in
canvasid
should be the result image of
writeMsgToCanvas
.
+ All parameters have same meaning as in
writeMsgToCanvas
. + Return value is a string + the string is either retrieved message or error message.

Advance Usage

writeMsgToCanvas
and
readMsgFromCanvas
functions are wrappers with 5 sets of pre-defined parameters. If none of them work for you, you can finetune those parameters yourself using
writeMsgToCanvas_base(canvasid, msg, pass, use_dct, num_copy, multiply, loc, use_y, use_downsampling)
and
readMsgFromCanvas_base(canvasid, pass, use_dct, num_copy, multiply, loc, use_y, use_downsampling)
. Configurable parameters are
use_dct, num_copy, multiply, loc, use_y, use_downsampling
. Make sure they are same on write and read otherwise read will fail.

writeMsgToCanvas_base
returns
true
on success (check with
result === true
). Otherwise, a string with error message.

readMsgFromCanvas_base
returns a size 2 array
[status, message]
:
status
is a boolean:
true
means success and decrypted message is in
message
.
false
means failure and error message is in
message
. Note,
status===true
does not imply real success, the
message
might be random characters if password is wrong. By design, there's no way to check if this function call is really successful.

Configurable parameters

  • use_dct (bool): true for DCT, false for LSB
  • numcopy (positive integer): how many copies of each bit to write into image. Larger value is more robust but reduces data capacity (how many data you can write). For LSB, you should just use `numcopy=1` as LSB is not robust to compression anyway

below only valid for

use_dct=true

  • multiply (positive real number):
    Q' = multiply * Q
    will be used to quantize DCT matrix.
    Q
    is JPEG 50% quantization matrix. Larger value is more robust but image is more distorted
  • loc (1D array of int 0-63): which frequency band locations on each block to write data. For example
    [1,8]
    means to use frequency matrix location
    [(0, 1), (1, 0)]
    and
    [0]
    means only using lowest frequency band
  • use_y (bool): whether to manipulate Y channel. If
    false
    , data will only be written to CbCr channels
  • use_downsampling(bool): whether to downsample on CrCb, if
    true
    , CbCr DCT will be performed on
    16*16
    blocks

Build Your Project with CryptoStego

Generally speaking, you don't need to touch any algorithm details as they are well encapsulated. I think for most (99%) use cases (for example, you don't want to use

canvas
), you can just adapt
writeMsgToCanvas_base
and
readMsgFromCanvas_base
to fit your needs. Any function called by those two
_base
functions is pure algorithm.

Compression Robustness for DCT

Raw image and data

You can download those images and try to decrypt them on http://stego.js.org. Leave the password cell empty.

Image before steganography / post-stego (level 0) are same to human eyes (650.1KB in PNG format):

maple

Data:

你好,世界!
HELLO WORLD!
¡HOLA MUNDO!
مرحبا بالعالم!
BONJOUR LE MONDE!
こんにちは世界!
ПРИВЕТ МИР!

The decryption results should be correct for all levels without compression. Above image is generated using level 0.

Limit of Level 1 (Compression Ratio 15.8% - 102.6KB)

maple

Level 1-5 should all work at or above this compression ratio. Above image is generated using level 1 (very similar to original)

Limit of Level 2 (Compression Ratio 11% - 71.6KB)

maple

Level 2-5 should all work at or above this compression ratio. Above image is generated using level 2 (very similar to original with noticeable distortion)

Limit of Level 3 (Compression Ratio 8.8% - 57.3KB)

maple

Level 3-5 should all work at or above this compression ratio. Above image is generated using level 3

Level 3 should be safe for most compressions by social apps (reduced size image), including Messengers, WeChat etc.

Limit of Level 4 (Compression Ratio 5.4% - 35.0KB)

maple

Level 4-5 should all work at or above this compression ratio. Above image is generated using level 4

Limit of Level 5 (Compression Ratio 2.7% - 17.7KB)

maple

Level 5 should work at or above this compression ratio. Above image is generated using level 5.

Partial Decryption of level 5 (Compression Ratio 1.4% - 9.4KB)

maple

At compression ratio 1.4% (< 10KB), the level 5 steganography (above image) still recovers most of the data

你好,世界A
HELLO WOZLD!
¡HOLA MUNDO!
مرحبا بالعدلم!
BONJOUR LE OONDE!
こんにちね世畍!
ПРP鐒ѕЦ ИИР!

Other Robustness for DCT

Robust to Photo Editing

Simple photo editing (brilliance, exposure etc.) applies most changes to lowest frequency band. Level 1-3 DCT does not apply to lowest frequency band so they are pretty robust. Level 4-5 is not robust to photo editing!

I used the same maple image, wrote same data with level 3 and let iPhone photo app auto enhance the result image (basically adjusts on brilliance, exposure and highlights). Then sent to my computer via WeChat reduced size image. I can successfully read data in my computer. Below is the image I received at my computer (73.6KB) (you can download and decrypt it on http://stego.js.org. Choose level 3 and leave password cell blank):

maple

Robust to Photo Stylizing

Stylizing changes images much more compared to simple editing. But luckily, level 3 is robust to most iPhone stylization (e.g. vivid, dramatic etc.). However, to fully recover message, I can't compress much after stylization.

Below is the same maple image first stego data using level 3, no password and then stylized by iPhone photo app with vivid warm theme.

maple

Robust to Resizing

To achieve robustness on resizing, you must know at which size the message is written in because the data order is related to image size. That said, you should resize the image so that it matches the size when the data is written in before decryption.

After data is written in, scaling up will never cause issue because all information is preserved. Level 1-3 should be robust if new image is more than 0.25X of original image. Level 4-5 should be robust if new image is more than 0.0625X of original image. If compression is involved during resizing, the robustness is weakened. (Level 5 is more robust than level 4 to resizing as downsampling is used)

Below is the maple image first stego data using level 5, no password, then scale down to 0.0625X and then scale up 16X for decryption. The decrypt results are correct and complete.

maple

Coding Example

Refer to

example/
folder.

Copyright

Jeffery Zhao

License: GNU AGPL v3.0 or later (GNU GPL v3.0 license allowed for non-commercial purposes but derived / re-distributed work must apply same license as this project or pure AGPL v3.0)

The copyright for Crypto-JS is reserved by its authors.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.