simple encryption/decryption method

Hi Adam, I am interested in speed gains! This one won’t make much difference for this script but I need to learn what you’ve done here. I’ve only been doing this for a month or so, so I have lots to learn. I haven’t even gotten into using the script calls as you’ve done here, so I need time to figure out why it’s faster, what the advatages of using them are etc. I see your comment about it loading stuff into ram, so it looks interesting. If you know of any place where I can learn about the script call let me know. I read a little last night but just the basics. You can be sure I’ll be practicing with them.

Anyway, I ran your speed test and here’s my results from a dual 2 GHz G5 with 2 GB ram… {10.79, 6.756, 1.597098875074}.

It’s no where near the speed increase you’re seeing. I’m not sure why since the specs of our machines should be close. I even rebooted into a freash user account to make sure I didn’t have any addons or mods that I’ve made affecting the results. They were the same in both accounts.

Hi, Regulus;

Speed comparisons of scripts for doing something at random are spotty at best because, as Nigel said in this Code Exchange thread, it depends on how many “some items” are required to find a unique addition to the list and “does not contain” involves a lot of testing as the list grows.

Having said that, the use of a script entity within a script is a well-known speeder-upper in list manipulations because of differences in the way in which AppleScript stores the list(s) involved. When you run a script object you are not running the script per se, you are running an instance of it. Each time the script object is invoked (in this case just once), AppleScript creates a new copy of it in memory and sets up the structure to connect it to the script that called it. In our case here, all we did with our script object was store some properties; but as our main script runs, the data in those properties are available “live” from memory. If we had simply declared the properties without the script object, then AppleScript (at least as I understand it) stores a reference to the array in memory that contains the list and has to grab the data by following the references each time its variable is encountered.

As you can see, my understanding of this distinction is somewhat “fuzzy” because I don’t know the details of how Script objects are stored and treated, but it is generally accepted practice that when you must visit a long list frequently as we do in this case, then it’s worth the overhead of setting up the script object in the first place, but probably not otherwise. If you read the thread on QuickSort here in Code Exchange you’ll see more examples.

With respect to your speed tests, I reran the test on my machine (a dual-core G5/2.3 with 2GB memory, Tiger’s latest) and got the same results for 500 repeats - 42.8 seconds for yours, 6.193 for mine, ratio 6.9:1. I don’t know why yours are different unless you ran a much smaller value of “Lotsa” - then the extreme variation would produce spotty results. One thing I would point out - considering case is not speedy - and my version doesn’t need it.

You got a speed of 42.8 secs for 500 iterations of my randomize code
I got a speed of 10.79 secs for 500 iterations of my randomize code

I ran the speed test at least 20 times and none of the times for my code were over 12 secs. I’m really surprised at your results considering your machine is faster than mine. When you said your code was 7.5 times faster than mine, I assumed you meant that you were getting faster results on your code than I was, not that you were getting slower results on my code than I was… that’s why I rebooted my machine into a fresh user account… I was thinking I needed to do something to speed up my machine.

This doesn’t explain why my code runs so much slower on your machine because my code doesn’t do any of this. I reduce the length of my list as I get the random item from it, so no extra iterations of these are necessary. This would affect your code, but we’re not seeing a huge speed difference on your code.

I’m stumped. In any case though your randomize code is faster than mine, so I’ve ditched my code and am using yours now. Plus your code will work on mixed lists where mine didn’t.

And thanks for the tips about the script entities. I’ve got some work to do to understand them better. While I was reading through the Qsort thread, I picked up another tip i.e. where using > is faster than using >= … so I’ve got a lot to learn. I guess that’s half the fun of all this! :rolleyes:

Wow. Your machine can run your code 4 times faster than mine does. Somethng wrong with that picture - I’ll look into it further and let you know. I was running mine from Script Debugger 4 while you were probably using the Script Editor - I’ll look into that too. Thanks for letting me know.

Hi Regulus;

Turns out that the differences are due to running the test in Script Debugger 4. When I run it in the Script Editor I get 1.49 as the ratio.

Also, in this thread, Nigel Garvey has posted a killer randomizer that he called Rand2 - substantially faster than mine because it avoids the “some” repetitions for uniqueness. :wink:

If you’re looking for real encryption, you can easily access many standard encryption methods via openssl. While it’s a nice exercise in scripting to create ascii shifting encryptors (I’ve spent a bit of time messing with them them myself), it’s ultimately easier and more secure to just use real encryption technology.

The following uses the blowfish algorithm in only a few lines of code. I added a simple handler that creates a 16 character nonsensicle password (encryption key) from various character ranges of a list of jumbled text objects. This makes the password you use technically hardcoded, but not readily visible in the script… even when it’s run-only and viewed in a text editor. You could use any number of methods of deriving this key, from a simple key stored in the script to a complex function that magically derives a string from some other source, as I’ve done… depending on the level of security desired. I also put in a simple handler (‘processInput’) that allows you to test the handlers by just running the script.

processInput("", "Encrypt")

(* * * * * * Handlers for encrypting the data * * * * * * * * * * *)
to eS(inS, inK, inM)
	(* 'Encrypt String':
		This handler uses the blowfish encryption available in openssl.  
			The handler is configured to accept the string to be encrypted, the key to use 
			(generated by the 'gK()' handler below), and the mode to pass to the openssl
			call... either encrypt("e") or decrypt("d"). *)
	
	return do shell script ("echo " & (quoted form of inS) & " | openssl enc -bf -" & inM & " -pass pass:" & (quoted form of inK) & " -salt -a")
end eS

to gK()
	(* 'Get Key':
		This handler assembles a password(encryption key) to use with the blowfish script
			This is done to better hide the key in the script, so people reading through a
			compiled, run-only script will have a difficult time determining what the key is. *)
	
	set kL to {"jTiiGHa67567U2FsdGVkX184g", "C3w2235689mNVxw35467JnJMH", "JGdD34hn7n7N6bdFg67H6d54o"}
	return ((characters 8 through 13 of (item 2 of kL)) & (characters 2 through 6 of (item 3 of kL)) & (characters 19 through 23 of (item 1 of kL))) as string
end gK

(* * * * * * Sample handler to show usage * * * * * * * * * * *)
to processInput(inString, inAction)
	set outString to ""
	
	if (inString is not "") then --> Process any incoming string
		if (inAction is "Encrypt") then
			set {encMode, inAction} to {"e", "Decrypt"}
		else if (inAction is "Decrypt") then
			set {encMode, inAction} to {"d", "Encrypt"}
		end if
		set outString to (eS(inString, gK(), encMode))
	end if
	
	set userInput to (display dialog "Enter a string:" default answer outString buttons {"Decrypt", "Cancel", "Encrypt"} default button inAction)
	set {inString, inAction} to {(text returned of userInput), (button returned of userInput)}
	processInput(inString, inAction)
end processInput

Cheers,
j

jobu, that’s awesome. I was waiting for someone to chime in with some real encryption method.

Now I just need about a week to understand your code. I never saw applescript like that before! I’m looking at it now and I’m having trouble following it. But I’ll get it, just give me a little time!

Hi Jobu, I think I’m understanding it now after a little concentration. Here’s what I got… please tell me if this is right… and answer a couple questions at the bottom!

inS is the string to encrypt or decrypt
inK is the key, gotten from gk() subroutine but could be anything I choose
inM is the mode, e or d for encrypt/decrypt

–breaking down the shell command
echo inS | → pipes your string to be encoded/decoded to openssl command

–openssl arguments
enc -bf → encoding with cyphers using the blowfish cypher, but there are other cyphers that could be used
-inM → e or d for encrypt/decrypt
-pass pass:" & (quoted form of inK) → the_pass_key

  1. what is -salt and -a
  2. When I encrypt the same word over and over I get a different encrypted string returned to me each time??? I can then input any of the encrypted strings and get back my original word!!! How does that happen considering I’m using the same password?

Hi Adam, wow that’s amazing!

I saw it already! Pretty cool. Considering what jobu posted though I don’t think I’ll need it.

‘-salt’ is a function used by the enc function to encode the input. I’m no expert, but I believe that it’s the ‘seed’ used to create a more random password. You can leave it out if you wish, but it offers free added security if I’m not mistaken, and makes the code standards compliant. Not using it is only an option to allow for backwards compatibility according to the docs.

‘-a’ is also used by the enc function, and it encodes the string as base64 after encrypting it and before decoding it. This ensures compatibility with acsii encoding, and is probably best left alone.

That’s the magic of true encryption. The output string you get back when you run the function contains the original string, your password, and a magic number (that is random every time)… all jumbled up and held together by the algorithm that that you’ve used to encrypt it. So, yes, you can put in any of the output strings you receive and have them be decrypted successfully. They are not just evaluated as valid based on the password and the string, but also on their compliance with the encryption method… the blowfish algorithm, in this case. This certainly adds to not just the randomness, but also the level of security. By successfully hiding your key in your script, there really is no way for anyone to break the encryption, as far as I know.

I’m no master of encryption, though, I just know what I’ve found on the interweb and from trial and error… so this is just my take on things. :cool:

j

Regulus;

You might not need Nigel’s script now, but it’s the fastest one I’ve seen – save it in your collection.

Jobu

Fantastic stuff. I haven’t a clue how it works, but it not only does it, it does it quickly. :slight_smile:

Hi. I finally got down to writing my Christmas cards this evening, so this reply is to the state of this thread when I downloaded it several hours ago.

I hope you’ll excuse a couple of nitpickings about the opening encryption and decryption scripts. Both points concern the first and last lines inside the ‘considering case’ blocks.

set p_letter_list to text items of words_to_encrypt

That should be ‘characters of words_to_encrypt’. A string’s ‘text items’ depend on the current value of AppleScript’s text item delimiters. The default value of these delimiters is {“”} and, when they’re set to this, the string’s ‘text items’ are the same as its ‘characters’. But if, for some reason, the delimiters had been set to some other value elsewhere in the script “ say, for argument, to “encrypt” “ the text items of words_to_encrypt would be {"I want to “, " sentences. Not just words!”}. If you’re going to use ‘text items’, it’s good practise to set the text item delimiters explicitly beforehand “ though in this case it’s better to use the term ‘characters’.

set c_string to c_letter_list as string

Similarly, when a list is coerced to string, the result is affected by current setting of the text item delimiters, which are inserted between each item in the list. So it’s a good idea to be explicit about the delimiter setting here too:

set astid to AppleScript's text item delimiters -- Note the current delimiter setting.
set AppleScript's text item delimiters to {""} -- or "" -- Set the delimiters to an empty string.
set c_string to c_letter_list as string -- Do the list coercion in that state.
set AppleScript's text item delimiters to astid -- Restore the previous delimiter setting.
c_string

More accurately, it’s to do with differences in the way AppleScript handles lists when they’re referred to by variables and when they’re referred to by references to variables. In the encryption script above, there’s this line:

set this_char to item j of charList

Here, the list is referred to by the variable charList. When handling the expression ‘item j of charList’, AppleScript carries out certain safety checks which include, I believe, a check to make sure that the list doesn’t contain itself.

Since charList is a top-level variable (ie. not in a handler) it’s technically a global “ though it may need to be explicitly declared as such to be accessed from inside a handler. Globals and top-level properties “belong” to the script in which they occur, so it’s possible to refer to them in relation to the script itself, which is called ‘me’ in AppleScript.

set this_char to item j of my charList -- NB. 'my'.

A possessive expression like ‘my charList’ or ‘item j of charList’ is called a “reference”. ‘item j of charList’ is a reference to an item in the list held by charList; ‘my charList’ is a reference to the variable charList itself. It’s when we combine these “ including a reference to the list variable in the reference to the list item, as in the example immediately above “ that we see the increase in speed. For some reason, AppleScript doesn’t carry out the safety checks in this case, and the time saved by not doing them makes an enormous difference to overall time.

Inside a handler, variables are, by default, local. Locals are just temporary and don’t “belong” to anything, so it’s not possible to set up references to them. However, it’s possible, while they exist, to give them custody of some data called a “script object”, which is a sort of script-within-a-script. A script object can have its own properties and it’s possible to have references to these. So by assigning a list to a property of a script object, we’re able to use the list-variable reference syntax locally within a handler. The script object doesn’t physically contain the list or affect it in any way. It merely provides a referenceable variable whereby we can refer to it.

The speed gain, by the way, only occurs when the reference to the list variable is included in a reference to one or more of the list’s elements or properties. There’s no gain with processes that act on the list itself, such as ‘count’, ‘contains’, ‘is in’. In fact, referencing the list variable may even slow them down v-e-r-y slightly.

-- In this illustration:
-- 'myList' is a top-level variable;
-- 'theList' is a local variable within the handler;
-- 'l' is a property of the script object held in the handler's local variable 'o'.
-- The list is the same physical entity in each case, only the variables are local or global.

set myList to {1, 2, 3, 4, 5} -- Imagine this list as being much longer.
myHandler(myList)

on myHandler(theList)
	script o
		property l : theList
	end script

	set c to (count theList) -- marginally faster than '(count o's l)'.
	repeat with i from 1 to c
		get item i of o's l -- very much faster than 'item i of theList'.
	end repeat
end myHandler

Nigel, that’s an awesome explanation… very clear and understandable. All of your points are well taken. That answers my questions on script entities and their advantages. I’ll certainly try to incorporate these lessons into my code writing. I know I’ll be a better programmer for it. Thank you!

Jobu, when I try to run the script below with text created and written from a script it works. When I try to read a text file that was created, for example, in bbedit, it encrypts, but fails on decryption with the message “Bad Magic Number” and the shell script highlighted. Any clues?


set inString to (read (choose file))
set O to (eS(inString, gK(), "e"))
set D to (eS(O, gK(), "d"))

to eS(inS, inK, inM)
	return do shell script ("echo " & (quoted form of inS) & " | openssl enc -bf -" & inM & " -pass pass:" & (quoted form of inK) & " -salt -a")
end eS

to gK()
	(* 'Get Key':
		This handler assembles a password(encryption key) to use with the blowfish script
			This is done to better hide the key in the script, so people reading through a
			compiled, run-only script will have a difficult time determining what the key is. *)
	
	set kL to {"jTiiGHa67567U2FsdGVkX184g", "C3w2235689mNVxw35467JnJMH", "JGdD34hn7n7N6bdFg67H6d54o"}
	return ((characters 8 through 13 of (item 2 of kL)) & (characters 2 through 6 of (item 3 of kL)) & (characters 19 through 23 of (item 1 of kL))) as string
end gK

It appears that there’s a character limit… of 734 characters, by my tests. Hmm, who’d have though? :confused: I’ve never tried to pass anything but short strings like passwords to it, so I never got to that point. I’m not sure if that’s a limitation of applescript’s do shell script command, if it’s a limit of the enc function, or if I’m off with one of the flags or parameters. Unlike you, I get the error “error reading input file” when I run it with more than 734 characters. I think that the function is really meant to encrypt files, rather than strings, so if you’re looking to encrypt larger blocks of data you might want to write to a temporary file, then encrypt that. The only other option I can think of with my limited knowledge of using openssl and enc, is to break up any input that is larger than 734 into chunks and encrypt/decrypt them individually. I know, this is ugly, but I don’t know of any other way, sorry. Like I said, if you’re handling that much data that needs to be encrypted, you’re probably handling data in separate files anyways, so it may be simple to just encrypt the whole file and read the resulting encrypted file. Google has lots of miscellaneous examples out there if you search around a bit. Good luck.

j

Thanks, J, I’ll pursue the file idea.

Not sure if it helps, but it’s worth mentioning: do shell script Tech Note

Seems to say there’s lots of space for passing the argument, so it must be the encoding/decoding funtion that has a limit:

Here’s a script that breaks up a document, encodes it, then decodes it. Seems to work on the text docs I’ve tested and it’s fast too (using Jobu’s handlers, but breaking up the doc into 500 byte portions)

set f to choose file
set Blks to (get eof of f) div 500 + 1
set CrypDoc to {}
set Doc to ""
-- encode the Original
repeat with j from 1 to 500 * Blks by 500
	set end of CrypDoc to (eS(quoted form of (read f from j for 500), gK(), "e"))
end repeat
-- decode the Encrypted doc
repeat with P in CrypDoc
	set Doc to Doc & (eS(contents of P, gK(), "d"))
end repeat

-- Jobu's Handlers
to eS(inS, inK, inM)
	return do shell script ("echo " & inS & " | openssl enc -bf -" & inM & " -pass pass:" & (quoted form of inK) & " -salt -a")
end eS

to gK()
	set kL to {"jTiiGHa67567U2FsdGVkX184g", "C3w2235689mNVxw35467JnJMH", "JGdD34hn7n7N6bdFg67H6d54o"}
	return ((characters 8 through 13 of (item 2 of kL)) & (characters 2 through 6 of (item 3 of kL)) & (characters 19 through 23 of (item 1 of kL))) as string
end gK

To use it with UTF-16 you’d have to read the file that way - but I didn’t try it (don’t have a native UTF16 doc). Also given the public airing in this thread, anyone thinking of using this for themselves should think about choosing a different set of characters in the gK() handler than Jobu has - he has 16 characters, but I don’t know if that’s a requirement. [689mNVGdD34VkX18] is the output of gK() as is.

Absolutely. The characters I used are totally random, and I encourage people to come up with their own “kL” list of strings, and it would probably be a good idea to change the ranges of the substrings and the order in which they are extracted and appended to the final key, too. This is meant as a basis for coming up with your own scheme, and it would be absurd to cut and paste this into an application and NOT doing any customization. Granted, you’ll find too many discussions about application security floating around on the web, and all of them arrive at the same point… you can try your best but there will always be someone trying to crack your security. By changing all of the factors in how your key is derived and your data stored, you can effectively modify this example to provide a totally custom, quite secure data storage method.

No, not a requirement, although the longer the key, the better the encryption, if I understand correctly. 16 was a nice round number, but you could use any length of key.

j

And if you really want to obfuscate, try some of the techniques described in this article by jj in unScripted

EDIT: Jan 25, 2008. The link above no longer works – the characters in it give the server fits.