4

I am currently playing around with an example from the book Violent Python. You can see my implementation here

I am now trying to implement the same script in Go to compare performance, note I am completely new to Go. Opening the file and iterating over the lines is fine, however I cannot figure out how to use the "crypto" library to hash the string in the same way as Python's crypt.crypt(str_to_hash, salt). I thought it maybe something like

import "crypto/des"
des.NewCipher([]byte("abcdefgh"))

However, no cigar. Any help would be much appreciated as it'd be really interesting to compare Go's parallel performance to Python's multithreaded.

Edit: Python docs for crypt.crypt

igniteflow
  • 8,404
  • 10
  • 38
  • 46
  • "Python's multithreaded" 'Nuff said. – Ignacio Vazquez-Abrams Jan 01 '13 at 11:15
  • I'm aware it is not testing like for like, I'm just interested to see how much faster Go will be. Likely at least an order of magnitude I'm guessing – igniteflow Jan 01 '13 at 13:51
  • 1
    Be careful comparing Go's *concurrent* performance with Python's multithreaded. Potentially, you'd see Go running more slowly because the Go runtime may actually use fewer (or only one) OS threads, so the Go routines are being timesliced onto CPU core(s). The difference between concurrency and parallelism is a good feature of Go but you could get caught out. More: http://golang.org/doc/effective_go.html#parallel – Rick-777 Jan 02 '13 at 12:53

4 Answers4

3

I believe there isn't currently any publicly available package for Go which implements the old-fashioned Unix "salted" DES based crypt() functionality. This is different from the normal symmetrical DES encryption/decryption which is implemented in the "crypto/des" package (as you have discovered).

You would have to implement it on your own. There are plenty of existing implementations in different languages (mostly C), for example in FreeBSD sources or in glibc. If you implement it in Go, please publish it. :)

For new projects it is much better to use some stronger password hashing algorithm, such as bcrypt. A good implementation is available in the go.crypto repository. The documentation is available here. Unfortunately this does not help if you need to work with pre-existing legacy password hashes.

Edited to add: I had a look at Python's crypt.crypt() implementation and found out that it is just a wrapper around the libc implementation. It would be simple to implement the same wrapper for Go. However your idea of comparing a Python implementation to a Go implementation is already ruined: you would have to implement both of them yourself to make any meaningful comparisons.

snap
  • 2,751
  • 22
  • 33
3

crypt is very easy to wrap with cgo, eg

package main

import (
    "fmt"
    "unsafe"
)

// #cgo LDFLAGS: -lcrypt
// #define _GNU_SOURCE
// #include <crypt.h>
// #include <stdlib.h>
import "C"

// crypt wraps C library crypt_r
func crypt(key, salt string) string {
    data := C.struct_crypt_data{}
    ckey := C.CString(key)
    csalt := C.CString(salt)
    out := C.GoString(C.crypt_r(ckey, csalt, &data))
    C.free(unsafe.Pointer(ckey))
    C.free(unsafe.Pointer(csalt))
    return out
}

func main() {
    fmt.Println(crypt("abcdefg", "aa"))
}

Which produces this when run

aaTcvO819w3js

Which is identical to python crypt.crypt

>>> from crypt import crypt
>>> crypt("abcdefg","aa")
'aaTcvO819w3js'
>>> 

(Updated to free the CStrings - thanks @james-henstridge)

Nick Craig-Wood
  • 52,955
  • 12
  • 126
  • 132
  • 2
    The OP wanted to compare the speed of a Python implementation to a Go implementation. With this approach he would be benchmarking the C implementation against the same C implementation. He would be effectively just benchmarking Python's C wrapper against Go's C wrapper. – snap Jan 01 '13 at 19:11
  • 2
    The OP specifically wanted to compare the parallel performance of crypt in python and crypt in go - this will give a good comparison of the differing concurrency capabilities since they will be using the same C library underneath. – Nick Craig-Wood Jan 01 '13 at 23:11
  • IMHO it would be more fair to benchmark parallel performance with native code. The concurrency capabilities of a language may be limited when using an external non-native library (for example with Go the Go scheduler can not do anything while the program or an OS thread is running external library code). OTOH if the algorithm is implemented in native code, it will be difficult to limit the benchmark to compare only the concurrency capabilities as the resulting code will be different due to language/compiler/interpreter differences. This is a harder problem than I initially thought. – snap Jan 02 '13 at 01:06
  • This looks like you're leaking the `C.CString` versions of `key` and `salt`. You will need to explicitly free those values. – James Henstridge Jan 02 '13 at 03:21
2

E.g.

package main

import (
        "crypto/des"
        "fmt"
        "log"
)

func main() {
        b, err := des.NewCipher([]byte("abcdefgh"))
        if err != nil {
                log.Fatal(err)
        }

        msg := []byte("Hello!?!")
        fmt.Printf("% 02x: %q\n", msg, msg)
        b.Encrypt(msg, msg)
        fmt.Printf("% 02x: %q\n", msg, msg)
        b.Decrypt(msg, msg)
        fmt.Printf("% 02x: %q\n", msg, msg)
}

(Also: http://play.golang.org/p/czYDRjtWNR)


Output:

48 65 6c 6c 6f 21 3f 21: "Hello!?!"
3e 41 67 99 2d 9a 72 b9: ">Ag\x99-\x9ar\xb9"
48 65 6c 6c 6f 21 3f 21: "Hello!?!"
zzzz
  • 87,403
  • 16
  • 175
  • 139
  • Thanks for this, it's really useful to have a fully working example. In Python one would pass the string to encrypt and an optional two char salt eg. crypt.crypt('abc', '12') -> '12BWKETBcM70Q', however the above example does not return the same. Does the returned cipher.Block need to be cast to crypto.Hash to achieve the same? – igniteflow Jan 01 '13 at 14:00
  • This is not relevant to the question. Symmetrical DES encryption is somewhat different from the traditional Unix `crypt()` function (which is DES based with some salt mixed in, but certainly not the same). – snap Jan 01 '13 at 18:02
  • My bad, I should have been more explicit in my question. Python's crypt.crypt() runs the crypt(3) password, standard in Unix password hashing. crypt(3) is a variant on DES. – igniteflow Jan 02 '13 at 00:07
2

Good news! There's actually an open source implementation of what you're looking for. Osutil has a crypt package that reimplements crypt in pure Go.

https://github.com/kless/osutil/tree/master/user/crypt

Zach Brock
  • 149
  • 1
  • 6