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:
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: 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: 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 Golf
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. 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
)