/* * steghide 0.5.1 - a steganography program * Copyright (C) 1999-2003 Stefan Hetzl * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ #include #include #include "DFSAPHeuristic.h" #include "BitString.h" #include "CvrStgFile.h" #include "DMDConstructionHeuristic.h" #include "EmbData.h" #include "Embedder.h" #include "Edge.h" #include "Graph.h" #include "Matching.h" #include "ProgressOutput.h" #include "Selector.h" #include "Vertex.h" #include "WKSConstructionHeuristic.h" #include "common.h" #include "error.h" #include "msg.h" Globals Globs ; Embedder::Embedder () { // read embfile VerboseMessage vrs ; if (Args.EmbFn.getValue() == "") { vrs.setMessage (_("reading secret data from standard input...")) ; } else { vrs.setMessage (_("reading secret file \"%s\"..."), Args.EmbFn.getValue().c_str()) ; vrs.setNewline (false) ; } vrs.printMessage() ; std::vector emb ; BinaryIO embio (Args.EmbFn.getValue(), BinaryIO::READ) ; while (!embio.eof()) { emb.push_back (embio.read8()) ; } embio.close() ; VerboseMessage vdone (_(" done")) ; if (Args.EmbFn.getValue() != "") { vdone.printMessage() ; } // create bitstring to be embedded std::string fn = "" ; if (Args.EmbedEmbFn.getValue()) { fn = Args.EmbFn.getValue() ; } EmbData embdata (EmbData::EMBED, Args.Passphrase.getValue(), fn) ; embdata.setEncAlgo (Args.EncAlgo.getValue()) ; embdata.setEncMode (Args.EncMode.getValue()) ; embdata.setCompression (Args.Compression.getValue()) ; embdata.setChecksum (Args.Checksum.getValue()) ; embdata.setData (emb) ; ToEmbed = embdata.getBitString() ; // read cover-/stego-file VerboseMessage vrc ; if (Args.CvrFn.getValue() == "") { vrc.setMessage (_("reading cover file from standard input...")) ; } else { vrc.setMessage (_("reading cover file \"%s\"..."), Args.CvrFn.getValue().c_str()) ; } vrc.setNewline (false) ; vrc.printMessage() ; CvrStgFile::readFile (Args.CvrFn.getValue()) ; vdone.printMessage() ; ToEmbed.setArity (Globs.TheCvrStgFile->getEmbValueModulus()) ; if ((ToEmbed.getNAryLength() * Globs.TheCvrStgFile->getSamplesPerVertex()) > Globs.TheCvrStgFile->getNumSamples()) { throw SteghideError (_("the cover file is too short to embed the data.")) ; } // create graph Selector sel (Globs.TheCvrStgFile->getNumSamples(), Args.Passphrase.getValue()) ; VerboseMessage v (_("creating the graph...")) ; v.setNewline (false) ; v.printMessage() ; new Graph (Globs.TheCvrStgFile, ToEmbed, sel) ; Globs.TheGraph->printVerboseInfo() ; if (Args.Check.getValue()) { if (!Globs.TheGraph->check()) { CriticalWarning w ("integrity checking of graph data structures failed!") ; // TODO: internationalize this w.printMessage() ; } } #ifdef DEBUG if (Args.DebugCommand.getValue() == PRINTGRAPH) { Globs.TheGraph->print() ; exit (EXIT_SUCCESS) ; } else if (Args.DebugCommand.getValue() == PRINTGMLGRAPH) { Globs.TheGraph->print_gml (std::cout) ; exit (EXIT_SUCCESS) ; } else if (Args.DebugCommand.getValue() == PRINTGMLVERTEX) { std::vector nodeprinted (Globs.TheGraph->getNumVertices()) ; std::vector edgesprinted (Globs.TheGraph->getNumVertices()) ; Globs.TheGraph->printPrologue_gml(std::cout) ; Globs.TheGraph->printVertex_gml (std::cout, Globs.TheGraph->getVertex(Args.GmlStartVertex.getValue()), Args.GmlGraphRecDepth.getValue(), nodeprinted, edgesprinted) ; Globs.TheGraph->printEpilogue_gml(std::cout) ; exit (EXIT_SUCCESS) ; } #endif } Embedder::~Embedder () { delete Globs.TheGraph ; delete Globs.TheCvrStgFile ; } void Embedder::embed () { ProgressOutput* prout = NULL ; if (Args.Verbosity.getValue() == NORMAL) { std::string embstring, cvrstring ; if (Args.EmbFn.getValue() == "") { embstring = _("standard input") ; } else { embstring = "\"" + Args.EmbFn.getValue() + "\"" ; } if (Args.CvrFn.getValue() == "") { cvrstring = _("standard input") ; } else { cvrstring = "\"" + Args.CvrFn.getValue() + "\"" ; } char buf[200] ; sprintf (buf, _("embedding %s in %s..."), embstring.c_str(), cvrstring.c_str()) ; prout = new ProgressOutput (std::string(buf)) ; } else if (Args.Verbosity.getValue() == VERBOSE) { prout = new ProgressOutput () ; } const Matching* M = calculateMatching (prout) ; // embed matched edges const std::list medges = M->getEdges() ; for (std::list::const_iterator it = medges.begin() ; it != medges.end() ; it++) { embedEdge (*it) ; } // embed exposed vertices const std::list *expvertices = M->getExposedVerticesLink() ; for (std::list::const_iterator it = expvertices->begin() ; it != expvertices->end() ; it++) { embedExposedVertex (*it) ; } delete M ; // write stego file Globs.TheCvrStgFile->transform (Args.StgFn.getValue()) ; bool displaydone = false ; if (Globs.TheCvrStgFile->is_std()) { Message ws (_("writing stego file to standard output... ")) ; ws.printMessage() ; } else { if (Args.StgFn.getValue() != Args.CvrFn.getValue()) { Message ws (_("writing stego file \"%s\"... "), Globs.TheCvrStgFile->getName().c_str()) ; ws.setNewline (false) ; ws.printMessage() ; displaydone = true ; } } Globs.TheCvrStgFile->write() ; if (displaydone) { Message wsd (_("done")) ; wsd.printMessage() ; } } const Matching* Embedder::calculateMatching (ProgressOutput* prout) { Matching* matching = new Matching (Globs.TheGraph, prout) ; std::vector MatchingAlgos = Globs.TheCvrStgFile->getMatchingAlgorithms (Globs.TheGraph, matching) ; for (std::vector::const_iterator ait = MatchingAlgos.begin() ; ait != MatchingAlgos.end() ; ait++) { if (Args.Verbosity.getValue() == VERBOSE) { prout->setMessage (_("executing %s..."), (*ait)->getName()) ; } (*ait)->setGoal (Args.Goal.getValue()) ; (*ait)->run() ; delete *ait ; if (Args.Verbosity.getValue() == VERBOSE) { prout->done (matching->getMatchedRate(), matching->getAvgEdgeWeight()) ; } } if (Args.Check.getValue()) { if (!matching->check()) { CriticalWarning w ("integrity checking of matching data structures failed!") ; // TODO: internationalize this w.printMessage() ; } } matching->printVerboseInfo() ; // only print info for best matching if (Args.Verbosity.getValue() == NORMAL) { prout->done() ; } if (prout) { delete prout ; } return matching ; } void Embedder::embedEdge (Edge *e) { Vertex* v1 = e->getVertex1() ; Vertex* v2 = e->getVertex2() ; Globs.TheCvrStgFile->replaceSample (e->getSamplePos(v1), e->getReplacingSampleValue (v1)) ; Globs.TheCvrStgFile->replaceSample (e->getSamplePos(v2), e->getReplacingSampleValue (v2)) ; } void Embedder::embedExposedVertex (Vertex *v) { SamplePos samplepos = 0 ; SampleValue *newsample = NULL ; float mindistance = FLT_MAX ; for (unsigned short i = 0 ; i < Globs.TheCvrStgFile->getSamplesPerVertex() ; i++) { SampleValue *curold = v->getSampleValue(i) ; SampleValue *curnew = v->getSampleValue(i)->getNearestTargetSampleValue(v->getTargetValue(i)) ; if (curold->calcDistance (curnew) < mindistance) { samplepos = v->getSamplePos(i) ; newsample = curnew ; mindistance = curold->calcDistance (curnew) ; } else { delete curnew ; } } #ifdef DEBUG printDebug (1, "embedding vertex with label %lu by changing sample position %lu.", v->getLabel(), samplepos) ; #endif EmbValue oldev = Globs.TheCvrStgFile->getEmbeddedValue (samplepos) ; Globs.TheCvrStgFile->replaceSample (samplepos, newsample) ; myassert (oldev != Globs.TheCvrStgFile->getEmbeddedValue (samplepos)) ; delete newsample ; }