Steganographic Squares

C, 201 (Encoding) + 175 (Decoding) = 376 bytes

To Encode:

E(char*J){size_t L=ceil(sqrt(strlen(J)));int U;srand(time(NULL));for(int i=0;i<L;i++){for(int f=0;f<L;f++){printf("#%02X%02X%02X ",rand()%256,(U<strlen(J))?(int)J[U]:32,rand()%256);U+=1;}printf("\n");}}

Encodes each character of the input string in the green channel of the RGB spectrum while setting the two other channels as random hex values. Takes input through STDIN as a string and outputs to STDOUT a multiline string of hex color code in the shape of a square. Assuming you have Python 3 and ImageMagick installed, and the above file is compiled into a file named a.out in the current working directory (CWD), you can directly get the resulting image, named Output.png, to the CWD from the textual output using the following command:

./a.out "<Multiline Input>"|python3 -c "import sys,subprocess;Input=sys.stdin.read();print('# ImageMagick pixel enumeration: {0},{0},255,rgb\n'.format(len(Input.split('\n')[1].split()))+'\n'.join(['%d,%d:(%d,%d,%d)'%(g,i,int(j[1:][:2],16),int(j[1:][2:4],16),int(j[1:][4:6],16))for g,h in enumerate(Input.split('\n'))for i,j in enumerate(h.split())]))"|convert - -scale 1000% Output.png

Here is a sample output image created by the above commamd using Programming Puzzles and Code Golf as the input string:

Sample Output

To Decode:

D(int c,char**U){char T[c];for(int Y=1;Y<c;Y++){char G[2]={U[Y][3],U[Y][4]};T[Y-1]=(char)strtol(G,NULL,16);}int C=c-1;T[C]='\0';while(T[C]==' '){T[C]='\0';C-=1;}printf("%s\n",T);}

Takes input through STDIN a sequence of space-separated hex color code strings with each one enclosed in double quotes (") (char** argv in main) and also, when called in main, int argc for the integer input. Outputs to STDOUT a single/multi-line string representing the decoded message.

I will try to golf these more over time whenever and wherever I can.


Also, if you same both of the methods into the same file, you can use the following main method to put it all together with each function getting the correct inputs:

int main(int argc,char**argv){if(strcmp(argv[1],"E")==0){Encode(argv[2]);}else{Decode(argc,argv);}}

and using this, for encoding you must provide E as the first argument to call the encoding method followed by the single string argument, whereas for decoding, all you need to provide is the sequence of space-separated hex color code strings with each one enclosed in double-quotes (").


Finally, if you want, you can get the fully prepared, ready-to-use version here, although it is not golfed, but also does not output any warnings or errors upon compilation.


Python 2, 164 160 + 94 93 = 253 bytes

Saved 1+1 byte thanks to Wheat Wizard.

-5 bytes thanks to Kade

Encoder PictureEncoder: string must be enclosed in quotes, e.g. "CodeGolf", output is a color ascii PPM image.

from random import*
s=input()
n=int((len(s)-1)**0.5)+1
s=s.ljust(n*n)
r=randint
print"P3 %d %d 255 "%(n,n)+''.join("%d "*3%(r(0,255),r(0,255),ord(c))for c in s)

Decoder PictureDecoder: Takes input filename as command line argument

from sys import*
print''.join(chr(int(c))for c in open(argv[1]).read().split()[6::3]).strip()

Usage:

 python golf_stegansquare_enc.py > stega.ppm

 python golf_stegansquare_dec.py stega.ppm

Example:

Programming Puzzles and Code GolfProgramming Puzzles and Code Golf

Lorem IpsumLorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.


Scala, 97 + 68 = 165 bytes

Encryption (97 bytes):

s=>s.map(_+((math.random*65535).toInt<<8)).iterator.grouped(math.sqrt(s.size)toInt)withPadding 32

Takes a String and retuns an Iterator of Sequences of Integers.

Decryption (68 bytes):

a=>" +$".r.replaceAllIn(a.flatten.map(h=>(h&0xFF)toChar)mkString,"")

Takes an Iterator of Sequences of Integers and returns a string.

Explanation:

s=>                         //define an anonymous function
  s.map(                      //map each char of the string
    _+(                         //to the ascii value plus
      (math.random*65535).toInt)  //a random integer between 0 and 65535
      <<8                         //shifted 8 bits to the left
    )
  )
  .iterator                     //create an iterator
  .grouped(                     //group them in groups of size...
    math.sqrt(s.size)toInt        //sqrt of the size of the input, rounded up
  )withPadding 32               //pad with spaces to make a square

.

a=>
  " +$"              //take this string
  .r                 //parse it as a regex
  .replaceAllIn(     //replace every occurence of the regex in...
    a.flatten          //a flattened
    .map(h=>           //each element mapped
      (h&0xFF)toChar)    //to the character of the lower 8 bits
    mkString,          //joined to a string
    ""               //with an empty string
  )