steggy-noencrypt
Version:
a basic steganography library for png files (encryption support removed)
77 lines (59 loc) • 2.09 kB
JavaScript
const { PNG } = require('pngjs')
const { getShasumData } = require('./encryption')
const { isRgbByte } = require('./png')
const {
BYTE_SIZE,
LENGTH_BYTES,
SHASUM_BYTES,
} = require('./defaults')
const extractBinary = b => b % 2
const splitBitsAsBytes = bitsAsBytes => (_, i) => {
const start = i * BYTE_SIZE
return bitsAsBytes.slice(start, start + BYTE_SIZE)
}
const combineByteIntoBit = (accumulator, currentByte, i) => {
const shiftDistance = (BYTE_SIZE - 1) - i
return (currentByte << shiftDistance) | accumulator
}
const combineBufferIntoByte = buffer =>
buffer.reduce(combineByteIntoBit, 0)
const combineBits = bitsAsBytes => {
const n = Math.ceil(bitsAsBytes.length / BYTE_SIZE)
return Array(n)
.fill(null)
.map(splitBitsAsBytes(bitsAsBytes))
.map(combineBufferIntoByte)
}
const decode = data => {
const bitsAsBytes = data.map(extractBinary)
const combined = combineBits(bitsAsBytes)
return Buffer.from(combined)
}
const messageMatchesShasum = (message, shasum) =>
getShasumData(message).equals(shasum)
const extractData = imageData => {
const rgb = imageData.filter(isRgbByte)
const lengthDataSize = LENGTH_BYTES * BYTE_SIZE
const shasumDataSize = SHASUM_BYTES * BYTE_SIZE
const lengthAndShasumSize = lengthDataSize + shasumDataSize
const lengthData = rgb.slice(0, lengthDataSize)
const decodedLengthData = decode(lengthData)
const length = parseInt(decodedLengthData.toString('hex'), 16) * BYTE_SIZE
const shasumData = rgb.slice(lengthDataSize, lengthAndShasumSize)
const decodedShasumData = decode(shasumData)
const messageData = rgb.slice(lengthAndShasumSize, lengthAndShasumSize + length)
const decodedMessageData = decode(messageData)
if (!messageMatchesShasum(decodedMessageData, decodedShasumData))
throw new Error('Shasum did not match decoded message')
return decodedMessageData
}
const reveal = (image, encoding) => {
const png = PNG.sync.read(image)
const data = extractData(png.data)
return encoding
? data.toString(encoding)
: data
}
module.exports = {
reveal,
}