package scrypt import ( "crypto/rand" "encoding/hex" "flag" "fmt" "io" "os" "slices" gt "gotext" g "gobang" ) /* #define _XOPEN_SOURCE 700 #include #include */ import "C" const ( MinimumPasswordLength = 16 _SALT_MIN_LENGTH = 32 _DESIRED_LENGTH = 32 _N = 1 << 15 r = 8 p = 1 ) var ( ErrSaltTooSmall = gt.Error(gt.Gettext("scrypt: salt is too small")) ErrInternal = gt.Error(gt.Gettext("scrypt: internal error")) ) type HashInputT struct{ Password []byte Salt []byte } type CheckInputT struct{ Password []byte Salt []byte Hash []byte } type argsT struct{ allArgs []string input HashInputT } type envT struct{ args argsT in io.Reader out io.Writer err io.Writer } // Package scrypt implements the scrypt key derivation function as defined in // Colin Percival's paper "Stronger Key Derivation via Sequential Memory-Hard // Functions" (https://www.tarsnap.com/scrypt/scrypt.pdf). // // // Key derives a key from the password, salt, and cost parameters, returning // a byte slice of length keyLen that can be used as cryptographic key. // // N is a CPU/memory cost parameter, which must be a power of 2 greater than 1. // r and p must satisfy r * p < 2³⁰. If the parameters do not satisfy the // limits, the function returns a nil byte slice and an error. // // For example, you can get a derived key for e.g. AES-256 (which needs a // 32-byte key) by doing: // // dk, err := scrypt.Key([]byte("some password"), salt, 32768, 8, 1, 32) // // The recommended parameters for interactive logins as of 2017 are N=32768, r=8 // and p=1. The parameters N, r, and p should be increased as memory latency and // CPU parallelism increases; consider setting N to the highest power of 2 you // can derive within 100 milliseconds. Remember to get a good random salt. func scrypt( password []byte, salt []byte, N int, r int, p int, outlen int, ) ([]byte, error) { passwordbuf := C.CBytes(password) saltbuf := C.CBytes(salt) defer C.free(passwordbuf) defer C.free(saltbuf) outbuf := C.malloc(C.size_t(outlen)) defer C.free(outbuf) rv := C.scrypt_kdf( (*C.uint8_t)(passwordbuf), C.size_t(len(password)), (*C.uint8_t)(saltbuf), C.size_t(len(salt)), C.uint64_t(N), C.uint32_t(r), C.uint32_t(p), (*C.uint8_t)(outbuf), C.size_t(outlen), ) if rv != 0 { return nil, ErrInternal } out := C.GoBytes(outbuf, C.int(outlen)) return out, nil } func Hash(input HashInputT) ([]byte, error) { if len(input.Salt) < _SALT_MIN_LENGTH { return nil, ErrSaltTooSmall } hash, err := scrypt( input.Password, input.Salt, _N, r, p, _DESIRED_LENGTH, ) return hash, err } func SaltFrom(r io.Reader) ([]byte, error) { buffer := make([]byte, _SALT_MIN_LENGTH) _, err := io.ReadFull(r, buffer) if err != nil { return nil, err } return buffer, nil } func Salt() ([]byte, error) { return SaltFrom(rand.Reader) } func Check(input CheckInputT) (bool, error) { hashInput := HashInputT{ Password: input.Password, Salt: input.Salt, } candidate, err := Hash(hashInput) if err != nil { return false, err } return slices.Equal(candidate, input.Hash), nil } func usage(argv0 string, w io.Writer) { fmt.Fprintf( w, gt.Gettext("Usage: %s PASSWORD SALT\n"), argv0, ) } func getopt(allArgs []string, w io.Writer) (argsT, int) { argv0 := allArgs[0] argv := allArgs[1:] fs := flag.NewFlagSet("", flag.ContinueOnError) fs.Usage = func() {} fs.SetOutput(w) if fs.Parse(argv) != nil { usage(argv0, w) return argsT{}, 2 } subArgs := fs.Args() if len(subArgs) != 2 { usage(argv0, w) return argsT{}, 2 } password := []byte(subArgs[0]) salt := []byte(subArgs[1]) input := HashInputT{ Password: password, Salt: salt, } return argsT{ allArgs: allArgs, input: input, }, 0 } func run(env envT) int { payload, err := Hash(env.args.input) if err != nil { fmt.Fprintln(env.err, err) return 1 } fmt.Fprintln(env.out, hex.EncodeToString(payload)) return 0 } func Main() { g.Init() gt.Init(Name, LOCALEDIR) args, rc := getopt(os.Args, os.Stderr) g.ExitIf(rc) os.Exit(run(envT{ args: args, in: os.Stdin, out: os.Stdout, err: os.Stderr, })) }