12
$\begingroup$

Let's save a definition. Encode it with password/key and Get it again.

We will not use Get directly on directory but with combination of Get+StringToStream+Import.

Get works with streams since V9.0 so I see no reason not to go this way.

file = FileNameJoin[{$TemporaryDirectory, "def.m"}]; fileEnc = FileNameJoin[{$TemporaryDirectory, "def.enc"}]; DeleteFile /@ {file, fileEnc} //Quiet; (*just in case*) ClearAll @ f; f[x_] := x^2; Save[file, f]; password = "key"; Encode[file, fileEnc, password]; ClearAll @ f; stream = StringToStream @ Import[fileEnc, "Text"]; Get[stream , password]; Close[stream]; f[2] 

4

Yeah, great... fortunately I've tested this with different password:

password = "kuba"; Encode[file, fileEnc, password]; ClearAll @ f; stream = StringToStream @ Import[fileEnc, "Text"]; Get[stream , password]; Close[stream]; f[2] 
Syntax::sntx: Invalid syntax in or before "f[x_] :eeev" (line 1 of "String["(*!1N!*)4mx. ^ w24yf0¡'h;1;U.#+"]") f[2] 

From my observations it seems to be quite random.

p.s. using Get directly will work. but this is not what I'm after.

Reproduced on V9 V10 Win7

$\endgroup$
10
  • 2
    $\begingroup$ Hmm - odd, but as Get's documentation says nothing about Get[stream,password], this comprises the use of an undocumented feature and therefore may or may not do what one thinks it might. $\endgroup$ Commented Feb 28, 2015 at 14:27
  • 1
    $\begingroup$ @Jinxed I agree. On the other hand documentation is not always accurate so I assumed it should work. $\endgroup$ Commented Feb 28, 2015 at 14:41
  • $\begingroup$ On the one hand, the feature you use is undocumented, on the other, it is behaving oddly nonetheless. Maybe you could add a hint, that this actually is about an undocumented feature? $\endgroup$ Commented Feb 28, 2015 at 22:45
  • $\begingroup$ It would be interesting to analyze, exactly which conditions make a "good" or "bad" password in your specific context. Maybe I can take a look later on. $\endgroup$ Commented Mar 1, 2015 at 0:23
  • $\begingroup$ @Jinxed Thanks for the edit. $\endgroup$ Commented Mar 1, 2015 at 8:34

2 Answers 2

8
$\begingroup$

Being able to use this provides us with a way to produce quite secure CDFs designed for FreePlayer.


Maybe it is a problem with encoding somewhere or with overloaded definitions of Get.

Nevermind, as pointed out by Rolf Mertig if one uses OpenRead with DefineInputStreamMethod to convert a binary file to a stream, everything seems to work.


Steps:

  • DefineInputStreamMethod["ByteList", ...
  • binarydata = Import[encodedFile, "Binary"]
  • stream = OpenRead["whateverName", Method -> {"ByteList", "Bytes" -> binarydata}]
  • Get[stream, password]

Execution:

(* This part is taken bit by bit from the documentation of DefineInputStreamMethod *) DefineInputStreamMethod["ByteList", { "ConstructorFunction" -> Function[{name, caller, opts}, If[MatchQ[opts, {___, "Bytes" -> {_Integer ...}, ___}], {True, {0, "Bytes" /. opts}}, {False, $Failed} ]], "ReadFunction" -> Function[{state, n}, Module[{pos = state[[1]], bytes = state[[2]], bytesRead}, If[pos >= Length[bytes], {{}, state}, bytesRead = Part[bytes, pos + 1 ;; Min[Length[bytes], pos + n]]; {bytesRead, {pos + Length[bytesRead], bytes}} ] ]], "EndOfFileQFunction" -> ({#[[1]] >= Length[#[[2]]], #} &) }] 

file = FileNameJoin[{$TemporaryDirectory, "def.m"}]; fileEnc = FileNameJoin[{$TemporaryDirectory, "def.enc"}]; DeleteFile /@ {file, fileEnc} // Quiet;(*just in case*) ClearAll@f; f[x_] := x^2; Save[file, f]; password = "kuba"; Encode[file, fileEnc, password]; ClearAll@f; bin = Import[fileEnc, "Binary"]; stream = OpenRead["dumpsave", Method -> {"ByteList", "Bytes" -> bin}]; Get[stream, password]; Close[stream]; f[2] 
4 
$\endgroup$
1
4
$\begingroup$

No "real" answer, but a deeper analysis into the behavior you experienced:

file = "original.m"; fileEnc = "enc.m"; (* alphabet for password: [0-9a-zA-Z] *) alphabet=Sort[CharacterRange @@@ {{"a", "z"}, {"A", "Z"}, {"0", "9"}}//Flatten, ToCharacterCode@#1 < ToCharacterCode@#2&]; (* checking and recording success of decryption together with password *) res=Join@@Quiet@Table[Block[{keys=Tuples[alphabet,len],stream}, ClearAll@f; (Encode[file,fileEnc,StringJoin@#]; {StringJoin@#,ToCharacterCode/@#, stream = StringToStream@Import[fileEnc,{"Text","Plaintext"}]; Check[Get[stream, StringJoin@#];Quiet@Close@stream;True, Quiet@Close@stream; False]})&/@keys],{len,2}]; 

I did this for passwords of length 1 and 2 only, with disturbing results:

(* useable passwords of length==1 (black) *) ArrayPlot@Boole@{Cases[res,{_,b_,c_}:>{c}/;Length@b==1]} 

usable passwords of length==1

(* usable passwords of length==2 (black), 62x62 array *) ArrayPlot[Partition[Boole@Cases[res,{_, b_, c_}:>{c}/;Length@b==2],62],ImageSize->Large] 

usable passwords of length==2

Even after checking the character codes (and did multiple operations on them), I fail to find a pattern in what passwords are valid in this context, and which are not. :(

Conclusion

Using Get on encoded streams is undocumented and obviously not meant to be called by end users, as usable passwords seem to have to be picked by trial-and-error.

$\endgroup$
1
  • 1
    $\begingroup$ Or I can just give the user a message: "please choose different password, this on is not safe" :D $\endgroup$ Commented Mar 2, 2015 at 7:48

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.