4

I want to make an AppleScript that generates a salted hash with Terminal. Is there a specific Terminal command that can generate a salted hash, preferably a secure one like SHA-512? If possible, I would like one that's a one-liner so I can use it with the do shell script command. I searched the web but didn't find a way to generate a salted hash in Terminal, just a regular one.

I'm running OS X Mavericks 10.9.5.

  • @GeorgeNetu: To get SHA-512 encoding on OSX, as of openssl v0.9.8, you must use `openssl dgst -sha512` (`openssl sha512` only works in later versions, such as v1.0.1f on Ubuntu 14.04). That is, you must use the `dgst` command with the SHA algorithm as an _option_. `openssl` won't help with generating salts, however. – mklement0 May 20 '15 at 03:14

1 Answers1

1

From what I understand, at least conceptually, what you're asking for requires 2 steps:

  • Obtain a random salt value.
  • Concatenate the salt value with the input text (password) and compute the hash for the combined value.

For later verification, you'll have to store the salt along with the resulting hash.

The following AppleScript handlers wrap shell functions that provide the requisite functionality - they're preceded by sample invocations.

Disclaimer: my understanding of this field is limited, so take these functions with a grain of salt (ha!).

The salt-generating function was gratefully adapted from this post.

# Sample text to hash.
set passwd to "somePassword"

# Generate salt value with 10 chars, amounting to about a 64-bit value.
set salt to generateSalt(10)

# Compute hash from combined salt and input value.
set hash to getSha512(salt & passwd)


# SYNOPSIS
#   getSha512(text)
# DESCRIPTION
#   Calculates and outputs TEXT's hash value using the SHA-512 (SHA-2) algorithm.
#   Output is a 128-characters string composed of lowercase hexadecimal digits.
#   To create a salted hash, obtain a salt with generateSalt() first and
#   prepend it to the text to hash.
# PREREQUISITES
#   Requires either the sha512sum or the shasum utility. One or the other should be
#   available on BSD/OSX and Linux systems.
# EXAMPLE
#   set salt to generateSalt(20)
#   set hash to getSha512(salt & passwd)
on getSha512(txt)
    do shell script "
getSha512() {
  local -a shaCmd
  if command -v sha512sum &>/dev/null; then
    shaCmd=( sha512sum )
  elif command -v shasum  &>/dev/null; then
    shaCmd=( shasum -a 512 )
  else
    { echo 'ERROR: Cannot locate SHA-512-generating utility.' >&2; return 1; }
  fi
  # Invoke the SHA-generating command and output the first space-separated field.
  # (The subsequent fields indicate the mode and input filename.)
  \"${shaCmd[@]}\" <<<\"$1\" | cut -d' ' -f1
  return \"${PIPESTATUS[0]}\"
}
getSha512 " & quoted form of txt
end getSha512

# SYNOPSIS
#   generateSalt(numChars)
# DESCRIPTION
#   Generates NUMCHARS random *printable* ASCII characters that can serve as 
#   cryptographic salt. Due to the range of printable characters, each character
#   returned contains ca. 6.55 bits of information.
#   Thus, for instance, to get a 64-bit salt value, specify 10 for NUMCHARS.
#   For a 128-bit value, specify 20.
#   Use /dev/urandom as the source of random data.
# PREREQUISITES
#   File /dev/urandom as a source of random bytes.
#   The `head` utility must support the -c option to extract a number of *bytes*.
#   Both BSD/OSX and Linux systems fulfill these requirements.
# EXAMPLE
#   set salt to generateSalt(20) # get a ca. 128-bit salt value as 20 printable ASCII chars.
on generateSalt(numChars)
    do shell script "
generateSalt() {
  [[ -c /dev/urandom ]] || { echo 'ERROR: Random source /dev/urandom not available.' >&2; return 1; }
  LC_ALL=C tr -cd '!\"#$%&'\\''()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~' < /dev/urandom | head -c $1
}
generateSalt " & numChars
end generateSalt
Community
  • 1
  • 1
mklement0
  • 382,024
  • 64
  • 607
  • 775
  • Would this work too? `set passCode to "123456"`, `set salt to random number from 10000000 to 99999999` (this is the salt) `set passAndSalt to passCode & salt`, `do shell script "echo " & passAndSalt & " | shasum -a 512 | 'awk {print $1}'` –  May 17 '15 at 16:42
  • It would work (except for a typo: the last command should be `do shell script "echo " & passAndSalt & " | shasum -a 512 | awk '{print $1}'"`), but note that your salt is limited to 90 million possibilities, which amounts to less than 27 bits of randomness, whereas the recommended minimum is 64 bits. My function gives you more randomness per byte and is not limited by the range that AppleScript real numbers can accommodate. Thus, with the same number of bytes (8), my function gives you roughly *twice* the number of bits, and using 10 bytes will give you more than 64 bits, as in my example. – mklement0 May 17 '15 at 18:14
  • Tested it now, your script works great! It's really complicated though. I was just asking if my idea would work too. –  May 17 '15 at 18:17
  • Glad to hear it; thanks for accepting. The `getSha512()` handler doesn't offer much over directly passing `... | shasum -a 512 | awk '{print $1}'` to `do shell script` (the handler wraps a _cross-platform_ shell function, but if you're calling it from _AppleScript_, you're by definition on OSX), except that it triggers an error if the `shasum` command fails, whereas the `do shell script` command would mask that (you'd have to check the return value). – mklement0 May 17 '15 at 18:22
  • You'll need to store the _original_ salt alongside the _combined hash_. If you store both in a _single_ database field, e.g., as ``, you'll need to record the length of the salt somewhere, so you can split that single value value back into salt and hash later - one option is to make the 1st byte of the database field contain the length of the salt. Is that what you're asking? – mklement0 May 17 '15 at 18:38
  • I mean how can you see what is the salt that was generated, because the output only shows the final hash. –  May 17 '15 at 18:41
  • 1
    You mean for debugging? The simplest, but disruptive approach is to do `display alert salt`; in Script Editor, if you make the log visible (`View > Show Log`), you can use `log salt` and see the result in the lower pane; for more, see [this answer](http://stackoverflow.com/a/21341372/45375). – mklement0 May 17 '15 at 18:48
  • Sounds like you're missing `try ....` before the `on error` - the code that might fail goes inside the `try` block, and the error-recovery code goes into the `on error` block; the whole construct is terminated with `end try`. – mklement0 May 17 '15 at 19:28