Hex string to hex data to text

Hi,

Does anyone know how to coerce hex data to string? The following script gives the hex string as hex data. I think there was some trick for the coercion to text, but not sure.

set hex_string to "4865726520697320736F6D6520746578742E0A48657265206973206D6F726520746578742E"
set data_string to "«data rdat" & hex_string & "»"
set hex_data to run script data_string
--> «data rdat4865726520697320736F6D6520746578742E0A48657265206973206D6F726520746578742E»

I know that you can convert the hex string by taking pairs, doing some math, and get the text.

Thanks,
kel

Hi kel.

If your hex string definitely represents a ‘string’ (not ‘«class utf8»’ or ‘Unicode text’), it’s probably best to do the conversion the long way, since there’s a limit to the length of data you can create using ‘run script’:

set hex_string to "4865726520697320736F6D6520746578742E0A48657265206973206D6F726520746578742E"
hex2str(hex_string)

on hex2str(h)
	set hex_digits to "0123456789ABCDEF"
	
	set str to ""
	repeat with i from 1 to (count h) by 2
		set n to (offset of (character i of h) in hex_digits) * 16 + (offset of (character (i + 1) of h) in hex_digits) - 17
		set str to str & character id n
	end repeat
	
	return str
end hex2str

If you already have the data object, and again you know it represents ‘string’ text, you can write it to file and read it back ‘as string’:

set hex_data to «data rdat4865726520697320736F6D6520746578742E0A48657265206973206D6F726520746578742E»
data2str(hex_data)

on data2str(dat)
	set accessRef to (open for access file ((path to temporary items as text) & "str.dat") with write permission)
	try
		set eof accessRef to 0
		write dat to accessRef
		set str to (read accessRef from 1 as string)
	end try
	close access accessRef
	
	return str
end data2str

Edit: Actually, the data2str() handler may be OK for both ‘string’ and '«class utf8» if the ‘read’ is done ‘as «class utf8»’ instead:

set str to (read accessRef from 1 as «class utf8»)

Hi Nigel,

Nice little scripts.

The hex string is returned from the ‘security’ secure note (more than one line) using the -w flag which is utf8, I think. There might be a lot of text, so I might need to go with the Hex to String conversion. I read your other post on parsing the xml and come to think of it, the returned hex string is actually the hex form of the xml. So, I might end up using your xml parsing script for the secure note.

I’ll have to take another look at it when I get back.

Thanks a lot,
kel

Hi Nigel,

I was trying to pipe the hex string to Awk, but the quoting is too hard for me to do right now. :slight_smile: For fun, tried something with python and found that the hex string is the same as the xml (string), I think:

do shell script "security find-generic-password -l kel.note -w | python -c '
import sys, binascii
t = sys.stdin.readline().strip(\"\\n\")
print binascii.unhexlify(t)
'"

I’ll add on your script for getting the value from the note and do some timing.

Thanks a lot,
kel

Hi Nigel,

It works!

set stuff to do shell script "security find-generic-password -l kel.note -w | python -c 'import sys, binascii; t = sys.stdin.readline().strip(\"\\n\"); print binascii.unhexlify(t)'"
tell application "System Events" to get value of (make new property list item with data stuff)
set stuff to |note| of result

Crunched up the script a little. Still need to time it. I wonder if python can beat vanilla AppleScript? Your hexadecimal string to text was quick though.

Edited: I think the Python version might be quicker, because less text is returned. Just a guess.

Edited: same script:

set stuff to do shell script "security find-generic-password -l kel.note -w | python -c 'import sys, binascii; print binascii.unhexlify(sys.stdin.readline().strip(\"\\n\"))'"
tell application "System Events"
	set v to (value of (make new property list item with data stuff))
end tell
set n to |note| of v

Edited: Nigel, can you imagine doing this with Sed? You would need two counters.

Edited: Nigel, Now I know why you wrote this:

If the note is not found, then you get empty string.

Edited: darn, there’s a bug. If the label matches a secure password, then it might not find all hex digits as in a secure note (all hex) with more than one line.

Edited: to fix the bug, need to look at the ‘type’ attribute of generic password. Type should be ‘note’ if it is a note. Password types will be Null I think. Need to check on this.

Later,
kel

Didn’t fix the bug yet. Here’s what I ended up with:

-- need to be more specific, or error: not a hex string.
set the_xml to do shell script "security find-generic-password -l kel.note -w | python -c 'import sys, binascii; print binascii.unhexlify(sys.stdin.readline().strip(\"\\n\"))'"
if (the_xml begins with "<?xml version=") then
	tell application "System Events"
		set v to (value of (make new property list item with data the_xml))
	end tell
	set n to |note| of v
else
	display dialog "Note not found."
end if

If the user changes the label kel.note to a one line note or a password It will probably error because of non-hex characters, in the shell script.

Later,
kel

Hi kel.

I’ve been trying to catch up with this this morning.

I see that your version of the “security find-generic-password” command returns only a hex string, whereas the one in the script of mine to which I think you’re referring (second script in this post) returns both hex and quoted renditions of the same text.

A brief retest of my script suggests that in Mountain Lion, a plist is now always returned (in whatever form), which was not the case with one- or two-line notes in Snow Leopard. (That’s the reason for the “<?xml version=” test in my script.)

I didn’t know much sed when I wrote the script, which is why I used text item delimiters and System Events. Sed appears to be able to parse your python result in three lines, so that your script becomes:

set stuff to do shell script "security find-generic-password -l 'kel.note' -w | python -c 'import sys, binascii; print binascii.unhexlify(sys.stdin.readline().strip(\"\\n\"))' | sed -E '
/<\\?xml version=/,/<key>NOTE<\\/key>/ d
/<string>/,/<\\/string>/ s/^[[:blank:]]+<string>|<\\/string>$//g
/</,$ d'"

This seems to work for both plists with NOTE keys and text not in plists, but the logic’s a bit tight so you’d better test it for yourself!

The logic is:

"sed -E '
# If this line comes between the beginning of a plist and a NOTE key (inclusively), zap it.
/<\\?xml version=/,/<key>NOTE<\\/key>/ d

# If it's the next <string> and/or </string> tag line after a NOTE key, zap the tag(s).
/<string>/,/<\\/string>/ s/^[[:blank:]]+<string>|<\\/string>$//g

# If it comes after the first </string> tag after a NOTE key, zap it.
/</,$ d

# Print what's left.'"

Hi Nigel,

I never thought of piping it to Sed after converting the hex string to xml. Nice and compact scripts. I think that the -w option is a new addition to ‘security’'s find-generic-password.

Had a good sleep, so will be checking it out.

Thanks for doing the footwork,
kel