10
$\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$
  • 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$ – Jinxed Feb 28 '15 at 14:27
  • 1
    $\begingroup$ @Jinxed I agree. On the other hand documentation is not always accurate so I assumed it should work. $\endgroup$ – Kuba Feb 28 '15 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$ – Jinxed Feb 28 '15 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$ – Jinxed Mar 1 '15 at 0:23
  • $\begingroup$ @Jinxed Thanks for the edit. $\endgroup$ – Kuba Mar 1 '15 at 8:34
7
$\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$
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
    $\begingroup$ Or I can just give the user a message: "please choose different password, this on is not safe" :D $\endgroup$ – Kuba Mar 2 '15 at 7:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.