• Stars
    star
    306
  • Rank 136,456 (Top 3 %)
  • Language
    JavaScript
  • License
    Other
  • Created over 8 years ago
  • Updated over 4 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

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

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 <script src="cryptostego.min.js"></script> 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 <input type="file" id="file" accept="image/*" /> 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:
    //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
  • num_copy (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 num_copy=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.