python crypt in OSX

This is because Linux's glibc handles passwords differently - the salt of the password on Linux corresponds to the type of hash that it generates. OSX crypt() is plain-old DES encryption, (which is horrible).

glibc supports a variety of hash algorithms (MD5, Blowfish, SHA-256, etc).

If we take a look at the crypt.3 manpage, we can see:

   If salt is a character string starting with the characters "$id$" followed by
   a string terminated by "$":

          $id$salt$encrypted

   then instead of using the DES machine, id identifies the encryption method
   used and this then determines how the rest of the password string is
   interpreted.  The following values of id are supported:

          ID  | Method
          ---------------------------------------------------------
          1   | MD5
          2a  | Blowfish (not in mainline glibc; added in some
              | Linux distributions)
          5   | SHA-256 (since glibc 2.7)
          6   | SHA-512 (since glibc 2.7)

So, given that information.. lets take your password from the second example using Linux's crypt

$1$VFvON1xK$SboCDZGBieKF1ns2GBfY50' ('test', encrypted with salt=VFvON1xK)


1                       == MD5
VFvON1xK                == Salt
SboCDZGBieKF1ns2GBfY50  == Hashed password

Luckily for you, there is a cross-platform solution for this, passlib.hash.md5_crypt.

Here's how you'd use it:

from passlib.hash import md5_crypt
hash = md5_crypt.encrypt("test",salt="VFvON1xK")
print hash

When run on Linux or OSX, produces the glibc friendly password hash of:

$1$VFvON1xK$SboCDZGBieKF1ns2GBfY50

Identical to the original produced on the Linux machine.


You're passing specialized salt strings to the function which invoke glibc-specific crypt behaviors that aren't available on Mac OS X. From the crypt(3) man page on Debian 6:

If salt is a character string starting with the characters "$id$" followed by a string terminated by "$"...then instead of using the DES machine, id identifies the encryption method used and this then determines how the rest of the password string is interpreted.

In your python examples, you're telling crypt to use an id of 1, which causes MD5 to be used instead of DES-based hashing. There's no such extension on Mac OS X, where crypt is strictly DES-based. (Mac OS X's crypt has its own extension--the salt can be a 9-character array, beginning with an underscore, followed by 4 bytes of iteration count and 4 bytes of salt--that has no analog in glibc's implementation.)

If you avoid the crypt extensions on both platforms and use traditional crypt, in which salt can only be two bytes, you'll get the same results from the function on both platforms, e.g.:

>>> crypt.crypt( "test", "S/" )
'S/AOO.b04HTR6'

That's obviously terrible from a security perspective. Consider using something like passlib or py-bcrypt instead. Either one will get you vastly better hashing and cross-platform reliability at the same time.