├── README.md ├── doc ├── after.jpg └── before.jpg ├── examples ├── hjbj2vqp.jpg ├── hjbj32hb.jpg ├── hjbj3ea7.jpg ├── hjbj69nd.jpg ├── image.jpg └── test.js ├── node_modules └── package.json │ ├── bin │ └── main.js │ ├── lib │ ├── advanced_math.js │ ├── call_counter.js │ └── simple_math.js │ └── package.json ├── package.json └── stegger.js /README.md: -------------------------------------------------------------------------------- 1 | Stegger 2 | ========= 3 | 4 | **Steganography made simple for Node.JS** 5 | 6 | 7 | ##About# 8 | Stegger is a steganographic utility for Node.JS, to conceal encrypted text 9 | within JPEG images (yes, JPEG!). It wraps around the UNIX utility, `outguess`. 10 | 11 | I couldn't find a working existing module on `npm`, so I made my own. = 12 | 13 | 14 | ##About Steganography# 15 | Stegaography is the art of concealing data within ordinary looking objects, 16 | such that the original file does not appear to be tampered with. 17 | See http://en.wikipedia.org/wiki/Steganography for more 18 | information. 19 | 20 | 21 | ##Example## 22 | **Before:** 23 | 24 | ![before](https://github.com/toiletfreak/stegger/blob/master/doc/before.jpg) 25 | 26 | **After:** 27 | 28 | ![After](https://github.com/toiletfreak/stegger/blob/master/doc/after.jpg) 29 | 30 | Encrypted message: 'hello world (안녕하세요! 잘지네세요?)' 31 | 32 | 33 | 34 | ##Installation# 35 | 36 | 1. Install `outguess` with your relevant package manager. This can be done in Ubuntu via: 37 | 38 | sudo apt-get install outguess 39 | 40 | 2. Install from `npm`: 41 | 42 | npm install stegger 43 | 44 | 3. You're all set! Load a picture, and get swagging! 45 | 46 | 47 | ##Documentation# 48 | 49 | Check out the examples directory. The example provided, tests the `encrypt()` 50 | and `decrypt()` function sequentially. This is done via asynchronous sequences. 51 | 52 | Easy to use - just call either `encrypt()` or `decrypt()`. Pass in the 53 | relevant argument object. Listen for the `promise` callback. 54 | 55 | 56 | ##Issues# 57 | 58 | None at the moment. Post any via the bug tracker. 59 | 60 | 61 | ##Security## 62 | How secure is it? Well, pretty secure. 63 | 64 | 1. Text is encrypted with AES-256 cipher and salted. 65 | 2. Text is concealed within least-probable detected areas and salted with a second 66 | salt. 67 | 3. Image would have to be earmarked as having embedded information (hard), 68 | before being cracked. 69 | 70 | ##Credit# 71 | 72 | Joel Haowen TONG (me [at] joeltong [dot] org) 73 | -------------------------------------------------------------------------------- /doc/after.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myrtleTree33/stegger/15456323d8085d5cd768e0db7b69e4cf660b498e/doc/after.jpg -------------------------------------------------------------------------------- /doc/before.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myrtleTree33/stegger/15456323d8085d5cd768e0db7b69e4cf660b498e/doc/before.jpg -------------------------------------------------------------------------------- /examples/hjbj2vqp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myrtleTree33/stegger/15456323d8085d5cd768e0db7b69e4cf660b498e/examples/hjbj2vqp.jpg -------------------------------------------------------------------------------- /examples/hjbj32hb.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myrtleTree33/stegger/15456323d8085d5cd768e0db7b69e4cf660b498e/examples/hjbj32hb.jpg -------------------------------------------------------------------------------- /examples/hjbj3ea7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myrtleTree33/stegger/15456323d8085d5cd768e0db7b69e4cf660b498e/examples/hjbj3ea7.jpg -------------------------------------------------------------------------------- /examples/hjbj69nd.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myrtleTree33/stegger/15456323d8085d5cd768e0db7b69e4cf660b498e/examples/hjbj69nd.jpg -------------------------------------------------------------------------------- /examples/image.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/myrtleTree33/stegger/15456323d8085d5cd768e0db7b69e4cf660b498e/examples/image.jpg -------------------------------------------------------------------------------- /examples/test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @file test.js 3 | * @brief Test driver for Stegger.js 4 | * @author Joel Haowen TONG 5 | * @version 0.1.1 6 | * @date 2013-07-19 7 | */ 8 | 9 | 10 | /** This test module embeds the image with a UTF-8 string (Korean and English), 11 | * using 2-layer encryption. AES-256 cipher is first used, with a secret key. 12 | * Thereafter, the resultant crypted string is salted with the passphrase. 13 | * 14 | * Note: stegger.encrypt() and decrypt() return promises, which must then be listened for a result. 15 | * 16 | */ 17 | 18 | var stegger = require('../stegger') 19 | , Sequence = require('futures').sequence; 20 | 21 | 22 | Sequence() 23 | .then(function(next) { 24 | console.log(''); 25 | console.log('--Testing ENCRYPT--') 26 | stegger.encrypt({ 27 | inFile : 'image.jpg', 28 | outFile : 'output.jpg', 29 | msg : 'hello world (안녕하세요! 잘지네세요?)', 30 | key : 'bla bla', 31 | method : 'aes-256-cbc', 32 | passphrase : 'blue dog' 33 | }) 34 | 35 | .when(function(err, data) { 36 | console.log('[OUTPUT] image path: ' + data); 37 | next(data); 38 | 39 | }); 40 | 41 | }) 42 | 43 | 44 | .then(function(next, data) { 45 | console.log('--Testing DECRYPT--') 46 | stegger.decrypt({ 47 | inFile : data, 48 | outFile : 'decrypted.txt', 49 | key : 'bla bla', 50 | method : 'aes-256-cbc', 51 | passphrase : 'blue dog' 52 | 53 | }) 54 | 55 | .when(function(err, data) { 56 | if (err) { 57 | console.log(err); 58 | 59 | } 60 | console.log('[OUTPUT] Decrypted message: ' + data + '\n'); 61 | 62 | }); 63 | 64 | }); 65 | 66 | -------------------------------------------------------------------------------- /node_modules/package.json/bin/main.js: -------------------------------------------------------------------------------- 1 | var path = require("path"); 2 | var fs = require("fs"); 3 | var lib = path.join(path.dirname(fs.realpathSync(__filename)), "../lib"); 4 | var simple = require(lib + "/simple_math.js"); 5 | var advanced = require(lib + "/advanced_math.js"); 6 | 7 | module.exports = { 8 | addition: simple.addition, 9 | subtraction: simple.subtraction, 10 | multiplication: advanced.multiplication, 11 | division: advanced.division, 12 | fibonacci: advanced.fibonacci 13 | } -------------------------------------------------------------------------------- /node_modules/package.json/lib/advanced_math.js: -------------------------------------------------------------------------------- 1 | var call_counter = require("./call_counter"); 2 | 3 | function mulitply(x, y) { 4 | call_counter(); 5 | return x * y; 6 | } 7 | 8 | function divide(x, y) { 9 | call_counter(); 10 | return x / y; 11 | } 12 | 13 | function fibo(count) { 14 | call_counter(); 15 | return private_fibo(count); 16 | } 17 | 18 | function private_fibo(count, counter, first, second) { 19 | if(count == 0) 20 | return 0; 21 | 22 | if(counter == undefined) { 23 | counter = 1; 24 | first = 1; 25 | second = 2; 26 | } 27 | 28 | result = first + second; 29 | if(counter == count) 30 | return result; 31 | 32 | private_fibo(count, ++counter, second, result); 33 | 34 | return result; 35 | } 36 | 37 | module.exports = { 38 | multiplication: multiply, 39 | division: divide, 40 | fibonacci: fibo 41 | } -------------------------------------------------------------------------------- /node_modules/package.json/lib/call_counter.js: -------------------------------------------------------------------------------- 1 | var internal_call_counter = 0; 2 | 3 | function count_call() { 4 | ++internal_call_counter; 5 | console.log("You have made " + internal_call_counter + " calls!"); 6 | } 7 | 8 | module.exports = count_call; -------------------------------------------------------------------------------- /node_modules/package.json/lib/simple_math.js: -------------------------------------------------------------------------------- 1 | var call_counter = require("./call_counter"); 2 | 3 | function add(x, y) { 4 | call_counter(); 5 | return x + y; 6 | } 7 | 8 | function subtract(x, y) { 9 | call_counter(); 10 | return x - y; 11 | } 12 | 13 | module.exports = { 14 | addition: add, 15 | subtraction: subtract 16 | } -------------------------------------------------------------------------------- /node_modules/package.json/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "package.json", 3 | "version": "0.0.0", 4 | "description": "ERROR: No README.md file found!", 5 | "main": "bin/main.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": "", 10 | "author": "", 11 | "license": "BSD", 12 | "readme": "ERROR: No README data found!", 13 | "_id": "package.json@0.0.0", 14 | "_from": "package.json@" 15 | } 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "moment", 3 | "version" : "0.1.1", 4 | "description" : "2-layer encryption of text, embedded within images (Steganography). Wrapper for the *NIX utility, outguess.", 5 | "homepage" : "http://blog.joeltong.org", 6 | "author" : "Joel H Tong (jhtong) (http://blog.joeltong.org)", 7 | "keywords" : [ 8 | "steganography", 9 | "stegger", 10 | "outguess wrapper", 11 | "embedded text", 12 | "cryptography", 13 | "hide text within images" 14 | 15 | ], 16 | "main" : "./stegger.js", 17 | "engines" : { 18 | "node" : "*" 19 | }, 20 | "repository" : { 21 | "type" : "git", 22 | "url" : "https://github.com/toiletfreak/stegger.git" 23 | }, 24 | "bugs" : { 25 | "url" : "https://github.com/toiletfreak/stegger/issues" 26 | }, 27 | "licenses" : [{ 28 | "type" : "MIT" 29 | }], 30 | "devDependencies" : { 31 | "crypto" : "latest", 32 | "moment" : "latest", 33 | "fs" : "latest", 34 | "futures" : "latest", 35 | "future" : "latest", 36 | "util" : "latest", 37 | "child_process" : "latest" 38 | }, 39 | "scripts" : { 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /stegger.js: -------------------------------------------------------------------------------- 1 | var http = require('http') 2 | , fs = require('fs') 3 | , crypto = require('crypto') 4 | , moment = require('moment') 5 | , shasum = crypto.createHash('sha1') 6 | , Sequence = require('futures').sequence 7 | , Promise = require('future') 8 | , util = require("util") 9 | , exec = require("child_process").exec; 10 | 11 | 12 | var stegger = (function() { 13 | return { 14 | /* ----------------------------------------------------------------*/ 15 | /** 16 | * @brief 17 | * 18 | * @param arg { 19 | * inFile -- the file to read in 20 | * msg -- a message to encode 21 | * key -- a key to encode string 22 | * method -- the encryption method used to encode 23 | * passphrase -- a passphrase used by outguess 24 | * } 25 | * 26 | * @return 27 | */ 28 | /* ------------------------------------------------------------------*/ 29 | encrypt : function(arg) { 30 | 31 | var promise = Promise(); 32 | 33 | new Sequence() 34 | 35 | .then(function(next) { 36 | var cipher = crypto.createCipher(arg.method, arg.key); 37 | var crypted = cipher.update(arg.msg,'utf8','hex') + 38 | cipher.final('hex'); 39 | 40 | next(crypted); 41 | }) 42 | 43 | 44 | .then(function(next, crypted) { 45 | var filename = moment().valueOf().toString(36).toString('utf8') + '.txt'; 46 | fs.writeFile(filename, crypted, function(err, data) { 47 | if (err) { 48 | console.err('There was an error'); 49 | } 50 | next(filename); 51 | 52 | }); 53 | }) 54 | 55 | 56 | .then(function(next, filename) { 57 | var outFilename = moment().valueOf().toString(36).toString('utf8') + '.jpg'; 58 | next(filename, outFilename); 59 | 60 | }) 61 | 62 | 63 | .then(function(next, filename, outFilename) { 64 | // ensure .jpg extension, else won't work 65 | if (arg.inFile.slice(-3) !== 'jpg' && 66 | arg.inFile.slice(-4) !== 'jpeg') { 67 | console.log(arg.inFile); 68 | 69 | var newName = arg.inFile + '.jpg'; 70 | fs.rename(arg.inFile, newName, function(err) { 71 | if (err) { 72 | throw err; 73 | 74 | } 75 | 76 | arg.inFile = newName; 77 | 78 | next(filename, outFilename); 79 | 80 | }); 81 | 82 | } else { 83 | next(filename, outFilename); 84 | } 85 | }) 86 | 87 | 88 | .then(function(next, filename, outFilename) { 89 | var cmd = "outguess -k \'" + arg.passphrase + "\' -d " + filename + " " + arg.inFile + " " + outFilename; 90 | exec(cmd , function(err, stdout, stderr) { 91 | next(filename, outFilename); 92 | 93 | }); 94 | 95 | }) 96 | 97 | 98 | .then(function(next, filename, outFilename) { 99 | fs.unlink(filename, function() { 100 | promise.fulfill(undefined, outFilename); 101 | next(); 102 | 103 | }); 104 | }); 105 | 106 | 107 | return promise; 108 | 109 | }, 110 | 111 | 112 | /* ----------------------------------------------------------------*/ 113 | /** 114 | * @brief 115 | * 116 | * @param arg { 117 | * inFile -- the file to read in (an image) 118 | * outFile -- the file to write out the message 119 | * key -- a key to encode string 120 | * method -- the encryption method used to encode 121 | * passphrase -- a passphrase used by outguess 122 | * } 123 | * 124 | * @return a promise, with a callback signature (err, message) 125 | */ 126 | /* ------------------------------------------------------------------*/ 127 | decrypt : function(arg) { 128 | 129 | var promise = Promise(); 130 | 131 | new Sequence() 132 | .then(function(next) { 133 | // ensure .jpg extension, else won't work 134 | if (arg.inFile.slice(-3) !== 'jpg' && 135 | arg.inFile.slice(-4) !== 'jpeg') { 136 | console.log(arg.inFile); 137 | 138 | var newName = arg.inFile + '.jpg'; 139 | fs.rename(arg.inFile, newName, function(err) { 140 | if (err) { 141 | throw err; 142 | 143 | } 144 | 145 | arg.inFile = newName; 146 | 147 | next(); 148 | 149 | }); 150 | 151 | } else { 152 | next(); 153 | } 154 | }) 155 | 156 | 157 | .then(function(next) { 158 | var filename = moment().valueOf().toString(36).toString('utf8') + '.txt'; 159 | var cmd = "outguess -k \'" + arg.passphrase + "\' -r " + arg.inFile + " " + filename; 160 | exec(cmd , function(err, stdout, stderr) { 161 | next(filename); 162 | 163 | }); 164 | 165 | }) 166 | 167 | 168 | .then(function(next, filename) { 169 | fs.readFile(filename, function(err, data) { 170 | next(data, filename); 171 | 172 | }); 173 | }) 174 | 175 | 176 | .then(function(next, data, filename) { 177 | try { 178 | //if (data.isString('utf8')) { 179 | //console.log('is string!'); 180 | 181 | 182 | //} else { 183 | //console.log('not string!'); 184 | 185 | //} 186 | var decipher = crypto.createDecipher(arg.method, arg.key); 187 | var dec = decipher.update(data.toString('utf8'), 'hex', 'utf8'); 188 | dec += decipher.final('utf8'); 189 | if (dec === '') { 190 | promise.fulfill(true, dec); 191 | 192 | } else { 193 | promise.fulfill(undefined, dec); 194 | 195 | } 196 | 197 | next(filename); 198 | 199 | /* Invalid cipher */ 200 | } catch (ex) { 201 | promise.fulfill(true, dec); 202 | next(filename); 203 | 204 | } 205 | 206 | 207 | }) 208 | 209 | 210 | .then(function(next, filename) { 211 | fs.unlink(filename, function() { 212 | next(); 213 | 214 | }); 215 | }); 216 | 217 | 218 | return promise; 219 | 220 | } 221 | } 222 | 223 | })(); 224 | 225 | 226 | module.exports = stegger; 227 | --------------------------------------------------------------------------------