XOR messages

I had a cryptography problem to solve a long time ago where I needed to XOR two messages together to find something. I found that someone had posted hex, decimal, binary, and ASCII conversion handlers, so I added a user interface and some handlers for XORing the items. The code here is pure ugliness but it could be useful (especially if you edit it and add it as part of a library for cryptography or something).

Here’s a wiki for what XOR is: https://en.wikipedia.org/wiki/Xor

set type_list to {"ascii", "decimal", "binary", "hex"}
set fromType1 to (choose from list type_list with prompt "Convert from what?" without multiple selections allowed) as string
set input_query1 to display dialog "Please enter your first input text" & return & "(hit delete first, then tab and enter)" default answer "



" --lots of return spaces to make it easier to read
set nums1 to text returned of input_query1
set fromType2 to {choose from list type_list with prompt "Convert second string from what?" without multiple selections allowed} as string
set input_query2 to display dialog "Please enter your second cipher text string" & return & "(hit delete first, then tab and enter)" default answer "



" --lots of return spaces to make it easier to read
set nums2 to text returned of input_query2
set toType to (choose from list type_list with prompt "Select your final format:" without multiple selections allowed) as string
set the_result1 to my substitute(nums1, fromType1, "binary") --convert to binary
set the_result2 to my substitute(nums2, fromType2, "binary") --convert to binary
set nums3 to my bin_xor(the_result1, the_result2) --xor binary numbers
set nums3 to my replace_chars(nums3, "2", "0")
set nums3 to my replace_chars(nums3, ",", "")
if toType is not "binary" then
	set nums3 to my substitute(nums3, "binary", toType)
end if
display dialog "XOR of text 1 and 2:" & return default answer nums3


--------- binary handler
----------------------------------
on bin_xor(nums1, nums2) --handler to xor binary sets!
	set num_list to {}
	set count1 to number of characters in nums1 --check for string sizes (must match)
	set count2 to number of characters in nums2
	if count1 < count2 then --adjusts for relative lenghts
		set count1 to number of characters in nums2
		set count2 to number of characters in nums1
		set num_2 to nums2 --number switching for zero addition later
		set nums2 to nums1
		set nums1 to num_2
		set themissing to count1 - count2
	end if
	set the_missing to count1 - count2
	if the_missing ≠ 0 then --sets second string to equal length by adding zeros
		set zero_list to {}
		repeat with i from 1 to (the_missing)
			set zero to "0"
			copy zero to end of zero_list
		end repeat
		set zero_list to my joinList(zero_list, ",")
		set nums2 to nums2 & zero_list
		set nums2 to my replace_chars(nums2, ",", "") --drops the commas delimiters
	end if
	set theCount to 0
	repeat with i from 1 to count1
		set theCount to theCount + 1
		set x to item (theCount) of nums1
		set y to item (theCount) of nums2 --follows second string
		set nums3 to x + y
		copy nums3 to end of num_list
	end repeat
	set num_list to my joinList(num_list, ",")
	return num_list
end bin_xor

to joinList(aList, delimiter)
	set retVal to ""
	set oldTID to AppleScript's text item delimiters
	set AppleScript's text item delimiters to delimiter
	set retVal to aList as string
	set AppleScript's text item delimiters to oldTID
	return retVal
end joinList

on replace_chars(this_text, search_string, replacement_string)
	set oldTID to AppleScript's text item delimiters
	set AppleScript's text item delimiters to the search_string
	set the item_list to every text item of this_text
	set AppleScript's text item delimiters to the replacement_string
	set this_text to the item_list as string
	set AppleScript's text item delimiters to oldTID
	return this_text
end replace_chars

--------------------- conversion handlers below for hex, bin, ascii, decimal
-------------------------------------------------------------------------------------
on substitute(input, fromType, toType)
	set input_list to {} as list
	set output_list to {} as list
	if fromType is "ascii" as string then
		repeat with x from 1 to the (length of input)
			set theCharacter to the character x of input
			set theCharacter to character_number(theCharacter)
			set input_list to input_list & theCharacter as list
		end repeat
	else if fromType is "decimal" as string then
		set input to input as number
		if input is less than 256 then
			set input_list to input_list & (input) as list
		else
			repeat while input as integer is greater than 1
				set input_list to input_list & (input mod 256) as list
				set input to ((input / 256) - 0.5) as integer
			end repeat
		end if
	else if fromType is "binary" as string then
		set padding to 8 - ((length of input) mod 8)
		if padding is equal to 8 then set padding to 0
		repeat with x from 1 to padding
			set input to "0" & input as string
		end repeat
		repeat with x from 1 to ((length of input) / 8)
			set current_eight to characters ((x - 1) * 8 + 1) thru (x * 8) of input as string
			set input_list to input_list & bin2dec(current_eight)
		end repeat
	else if fromType is "hex" as string then
		set padding to 2 - ((length of input) mod 2)
		if padding is equal to 2 then set padding to 0
		repeat with x from 1 to padding
			set input to "0" & input as string
		end repeat
		repeat with x from 1 to ((length of input) / 2)
			set current_pair to characters ((x - 1) * 2 + 1) thru (x * 2) of input as string
			set input_list to input_list & hex2dec(current_pair)
		end repeat
	end if
	
	if toType is "binary" as string then
		repeat with x from 1 to (length of input_list)
			set output_list to output_list & dec2bin(item x of input_list)
		end repeat
		
	else if toType is "ascii" as string then
		repeat with x from 1 to (length of input_list)
			set output_list to output_list & dec2asc(item x of input_list)
		end repeat
	else if toType is "hex" as string then
		repeat with x from 1 to (length of input_list)
			set output_list to output_list & dec2hex(item x of input_list)
		end repeat
	else if toType is "decimal" as string then
		set output_list to input_list
	end if
	
	return output_list as string
end substitute

on bin2dec(binary_eight)
	set value to 128 * (character 1 of binary_eight) + 64 * (character 2 of binary_eight) + 32 * (character 3 of binary_eight) + 16 * (character 4 of binary_eight) + 8 * (character 5 of binary_eight) + 4 * (character 6 of binary_eight) + 2 * (character 7 of binary_eight) + 1 * (character 8 of binary_eight)
	return value
end bin2dec

on hex2dec(hex_pair)
	set hex_lookup to {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"} as list
	repeat with x from 1 to 16
		if character 1 of hex_pair is equal to item x in hex_lookup then set MSD to (x - 1)
		if character 2 of hex_pair is equal to item x in hex_lookup then set LSD to (x - 1)
	end repeat
	set value to 16 * MSD + 1 * LSD
end hex2dec

on dec2bin(value)
	return number_in_binary(value) as string
end dec2bin

on dec2hex(value)
	set hex_lookup to {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"} as list
	set LSD to value mod 16
	set MSD to ((value / 16) - 0.5) as integer -- the -0.5 ensures the cast to int is a floor
	set LSD to item (LSD + 1) of hex_lookup
	set MSD to item (MSD + 1) of hex_lookup
	return (MSD & LSD) as string
end dec2hex

on dec2asc(value)
	return number_to_character(value)
end dec2asc

on number_to_character(theNumber)
	if theNumber is less than 32 then
		return " "
	else
		return the (ASCII character of theNumber) as string
	end if
end number_to_character

on character_number(theCharacter)
	return the (ASCII number of theCharacter) as string
end character_number

on number_in_binary(theNumber)
	set theString to ""
	set currnumber to 128
	repeat while currnumber ≥ 1
		if theNumber - currnumber ≥ 0 then
			set theNumber to theNumber - currnumber
			set theString to theString & 1
		else
			set theString to theString & 0
		end if
		set currnumber to currnumber / 2
	end repeat
	return theString
end number_in_binary

Oh, no I think there is a bug in this code! I’m sorry, I should have checked it better before posting. It used to work fine so I’m not sure what happened to it. It should be very close to XORing correctly.

Sorry again

Hello.

Never mind, it takes attempts :slight_smile: If you never try you never accomplish!

Here is a way, I cheat and use bash by this cheat sheet! It uses hex values.


set a to "0x35"
set b to "0x1b"
set res to (do shell script "echo $(( " & a & " ^ " & b & ")) ")
res
”46

say if you wanted to do this with binary, then you’d just and bit by bit, and and the negated values bit by bit, or those intermediate results together, before you invert again.

Here is a cheap way to make a hex value:

set a to 53
set res to (do shell script "printf \"0x%x\" " & a)

And here is from hex to integer

set a to "0x35"
set res to (do shell script "printf \"%d\" " & a)

Here is a way to convert between different bases:

do shell script "echo 'obase=2;ibase=16;35' | bc"
”>110101

Oh wow, ^ in bash equals like 50 lines of AppleScript, lol. You’re right, using bash looks like a way better solution.
The script could probably be written in very few lines with those bash conversions and the ^ for XOR :stuck_out_tongue:

For what it’s worth the code is finally debugged so it should work now.

set type_list to {"ascii", "decimal", "binary", "hex"}
set fromType1 to (choose from list type_list with prompt "Convert from what?" without multiple selections allowed) as string
set input_query1 to display dialog "Please enter your first input text" & return & "(hit delete first, then tab and enter)" default answer "



" --lots of return spaces to make it easier to read
set nums1 to text returned of input_query1
set fromType2 to {choose from list type_list with prompt "Convert second string from what?" without multiple selections allowed} as string
set input_query2 to display dialog "Please enter your second cipher text string" & return & "(hit delete first, then tab and enter)" default answer "



" --lots of return spaces to make it easier to read
set nums2 to text returned of input_query2
set toType to (choose from list type_list with prompt "Select your final format:" without multiple selections allowed) as string
set the_result1 to my substitute(nums1, fromType1, "binary") --convert to binary
set the_result2 to my substitute(nums2, fromType2, "binary") --convert to binary
set nums3 to my bin_xor(the_result1, the_result2) --xor binary numbers
set nums3 to my replace_chars(nums3, "2", "0")
set nums3 to my replace_chars(nums3, ",", "")
if toType is not "binary" then
	set nums3 to my substitute(nums3, "binary", toType)
end if
display dialog "XOR of text 1 and 2:" & return default answer nums3


--------- binary handler
----------------------------------
on bin_xor(nums1, nums2) --handler to xor binary sets!
	set num_list to {}
	set count1 to number of characters in nums1 --check for string sizes (must match)
	set count2 to number of characters in nums2
	if count1 < count2 then --adjusts for relative lenghts
		set count1 to number of characters in nums2
		set count2 to number of characters in nums1
		set num_2 to nums2 --number switching for zero addition later
		set nums2 to nums1
		set nums1 to num_2
		set themissing to count1 - count2
	end if
	set the_missing to count1 - count2
	if the_missing ≠ 0 then --sets second string to equal length by adding zeros
		set zero_list to {}
		repeat with i from 1 to (the_missing)
			set zero to "0"
			copy zero to end of zero_list
		end repeat
		set zero_list to my joinList(zero_list, ",")
		set nums2 to nums2 & zero_list
		set nums2 to my replace_chars(nums2, ",", "") --drops the commas delimiters
	end if
	set theCount to 0
	repeat with i from 1 to count1
		set theCount to theCount + 1
		set x to item (theCount) of nums1
		set y to item (theCount) of nums2 --follows second string
		set nums3 to x + y
		copy nums3 to end of num_list
	end repeat
	set num_list to my joinList(num_list, ",")
	return num_list
end bin_xor

to joinList(aList, delimiter)
	set retVal to ""
	set oldTID to AppleScript's text item delimiters
	set AppleScript's text item delimiters to delimiter
	set retVal to aList as string
	set AppleScript's text item delimiters to oldTID
	return retVal
end joinList

on replace_chars(this_text, search_string, replacement_string)
	set oldTID to AppleScript's text item delimiters
	set AppleScript's text item delimiters to the search_string
	set the item_list to every text item of this_text
	set AppleScript's text item delimiters to the replacement_string
	set this_text to the item_list as string
	set AppleScript's text item delimiters to oldTID
	return this_text
end replace_chars

--------------------- conversion handlers below for hex, bin, ascii, decimal
-------------------------------------------------------------------------------------
on substitute(input, fromType, toType)
	set input_list to {} as list
	set output_list to {} as list
	if fromType is "ascii" as string then
		repeat with x from 1 to the (length of input)
			set theCharacter to the character x of input
			set theCharacter to character_number(theCharacter)
			set input_list to input_list & theCharacter as list
		end repeat
	else if fromType is "decimal" as string then
		set input to input as number
		if input is less than 256 then
			set input_list to input_list & (input) as list
		else
			repeat while input as integer is greater than 1
				set input_list to input_list & (input mod 256) as list
				set input to ((input / 256) - 0.5) as integer
			end repeat
		end if
	else if fromType is "binary" as string then
		set input_length to (length of input)
		set padding to 8 - ((length of input) mod 8)
		if padding is equal to 8 then set padding to 0
		repeat with x from 1 to padding
			set input to "0" & input as string
		end repeat
		repeat with x from 1 to ((length of input) / 8)
			set current_eight to characters ((x - 1) * 8 + 1) thru (x * 8) of input as string
			set input_list to input_list & bin2dec(current_eight)
		end repeat
	else if fromType is "hex" as string then
		set padding to 2 - ((length of input) mod 2)
		if padding is equal to 2 then set padding to 0
		repeat with x from 1 to padding
			set input to "0" & input as string
		end repeat
		repeat with x from 1 to ((length of input) / 2)
			set current_pair to characters ((x - 1) * 2 + 1) thru (x * 2) of input as string
			set input_list to input_list & hex2dec(current_pair)
		end repeat
	end if
	
	if toType is "binary" as string then
		repeat with x from 1 to (length of input_list)
			set output_list to output_list & dec2bin(item x of input_list)
		end repeat
		
	else if toType is "ascii" as string then
		repeat with x from 1 to (length of input_list)
			set output_list to output_list & dec2asc(item x of input_list)
		end repeat
	else if toType is "hex" as string then
		repeat with x from 1 to (length of input_list)
			set output_list to output_list & dec2hex(item x of input_list)
		end repeat
	else if toType is "decimal" as string then
		set output_list to input_list
	end if
	
	--this conditional is my debugging solution, yay!
	if fromType is "binary" as string and input_length < 8 and input_length ≠ 0 then
		set output_list to output_list as text
		set output_list to text -1 thru ((8 - input_length) + 1) of output_list
		set output_list to output_list as integer
	end if
	return output_list as string
end substitute

on bin2dec(binary_eight)
	set value to 128 * (character 1 of binary_eight) + 64 * (character 2 of binary_eight) + 32 * (character 3 of binary_eight) + 16 * (character 4 of binary_eight) + 8 * (character 5 of binary_eight) + 4 * (character 6 of binary_eight) + 2 * (character 7 of binary_eight) + 1 * (character 8 of binary_eight)
	return value
end bin2dec

on hex2dec(hex_pair)
	set hex_lookup to {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"} as list
	repeat with x from 1 to 16
		if character 1 of hex_pair is equal to item x in hex_lookup then set MSD to (x - 1)
		if character 2 of hex_pair is equal to item x in hex_lookup then set LSD to (x - 1)
	end repeat
	set value to 16 * MSD + 1 * LSD
end hex2dec

on dec2bin(value)
	return number_in_binary(value) as string
end dec2bin

on dec2hex(value)
	set hex_lookup to {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"} as list
	set LSD to value mod 16
	set MSD to ((value / 16) - 0.5) as integer -- the -0.5 ensures the cast to int is a floor
	set LSD to item (LSD + 1) of hex_lookup
	set MSD to item (MSD + 1) of hex_lookup
	return (MSD & LSD) as string
end dec2hex

on dec2asc(value)
	return number_to_character(value)
end dec2asc

on number_to_character(theNumber)
	if theNumber is less than 32 then
		return " "
	else
		return the (ASCII character of theNumber) as string
	end if
end number_to_character

on character_number(theCharacter)
	return the (ASCII number of theCharacter) as string
end character_number

on number_in_binary(theNumber)
	set theString to ""
	set currnumber to 128
	repeat while currnumber ≥ 1
		if theNumber - currnumber ≥ 0 then
			set theNumber to theNumber - currnumber
			set theString to theString & 1
		else
			set theString to theString & 0
		end if
		set currnumber to currnumber / 2
	end repeat
	return theString
end number_in_binary

Your code may be like 100 times longer, but executes like 1000 times faster. :wink:

So … Thank you very much!

By the way, I think there may be other implementations here as well in applescript. Scriptbuilders.net is down now, so you can’t find anything there, but you can google for Julio, Juliosoft, and Pescados software together with binary as well. Auhtors may be Nigel Garvey and DJ Bazzie Wazzie here.

For logic, it is of course just to implement XOR and its evil twin brother DCMP (data comparative) as handlers

There are some 16-bit binary masking handlers here, including an XOR. They take integer inputs or inputs which can be so coerced.

Thanks Nigel. :smiley:

By the way.

It has come to my notice, that on IBM’s AIX platform, there is a program called “Nigels Monitor”.

You wouldn’t know anything about it, would you? :slight_smile:

No I wouldn’t. And for anyone else keen to associate me with names they find on the Internet, I’m not a dodgy businessman/mystic from Yorkshire, an Italian nudist, an overseas aid worker, or someone’s cousin in “Edinbourgh”. And I’m not on Facebook and you can’t get me cheaper on eBay. :wink:

Now that’s a shame! (Escpecially about the “cheaper on ebay”.) :smiley:

I guess it would always have been cheap to outsource stuff to you though, thinking price/ performance.

It is not a harass Nigel day for his abilities, I just found a reference to that program over at IBM’s site, and seriously wondered, likelihood and all. :slight_smile:

Hello.

Here is two convenience handlers for making if tests more readable, when you are familiar with datacomparative and exclusive or.

set a to DCMP(true, false)
set b to XOR(true, false)
# Data comparative, true if both states are alike
on DCMP(stateA, stateB)
	
	if stateA and stateB then return true
	if (not stateA) and (not stateB) then return true
	return false
end DCMP
# xclusive or, true if one of the states is true
# Other must then be false for xor to be true
on XOR(stateA, stateB)
	if stateA and (not stateB) then return true
	if (not stateA) and stateB then return true
	return false
end XOR

Hi McUsrII.

More simply, of course:

set a to DCMP(true, false)
set b to XOR(true, false)

# Data comparative, true if both states are alike
on DCMP(stateA, stateB)
	return (stateA = stateB)
end DCMP
# xclusive or, true if one of the states is true
# Other must then be false for xor to be true
on XOR(stateA, stateB)
	return (stateA ≠ stateB)
end XOR

Better. :slight_smile:

I didn’t think that far, but those handlers are just for making complicated if statments readable when you know of the convention, and should as such be as simple as possible. Thanks.

I want to add that just one if test are needed inside such an if test, to “pinpoint” the whole situation.

Edit
On one line you don’t even need the handlers. When I wrote the post with the handlers above, I thought it went without saying that you’d take two maybe complex compound statements, and assign them to two variables, each holding the boolean result.

= and ≠obivates the need for the handlers of course, since it now is all on one line.

I think it is a useful convention, for building complex logic as it breaks down the logic into “layers of states”.