diff options
author | EuAndreh <eu@euandre.org> | 2024-10-18 13:45:15 -0300 |
---|---|---|
committer | EuAndreh <eu@euandre.org> | 2024-10-18 18:04:41 -0300 |
commit | 804ca010dd354ff30e12dbf2c40dcb83e9820918 (patch) | |
tree | 434bd43a59308089c1de4e38fb6980fc61a3f9c4 | |
parent | Add baseline functional test, fuzz target and benchmark (diff) | |
download | scrypt-804ca010dd354ff30e12dbf2c40dcb83e9820918.tar.gz scrypt-804ca010dd354ff30e12dbf2c40dcb83e9820918.tar.xz |
Remove Go code in favor of upstream libscrypt-kdf
Adapt build to handle cgo shenanigans.
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | Makefile | 51 | ||||
-rw-r--r-- | deps.mk | 11 | ||||
-rwxr-xr-x | mkdeps.sh | 2 | ||||
-rw-r--r-- | src/scrypt.go | 357 | ||||
l--------- | tests/functional/version/main.go | 1 | ||||
-rw-r--r-- | tests/functional/version/scrypt.go | 13 | ||||
-rw-r--r-- | tests/fuzz/api/scrypt.go | 2 | ||||
-rw-r--r-- | tests/scrypt.go | 267 |
9 files changed, 197 insertions, 508 deletions
@@ -1,5 +1,6 @@ /src/version.go /*.bin +/src/*cgo* /src/*.a /src/*.bin /tests/*.a @@ -17,22 +17,23 @@ MANDIR = $(SHAREDIR)/man EXEC = ./ ## Where to store the installation. Empty by default. DESTDIR = -LDLIBS = +LDLIBS = --static -lscrypt-kdf GOCFLAGS = -I $(GOLIBDIR) GOLDFLAGS = -L $(GOLIBDIR) .SUFFIXES: -.SUFFIXES: .go .a .bin .bin-check +.SUFFIXES: .go .a .c .o .bin .bin-check + +.c.o: + $(CC) $(CFLAGS) -o $@ -c $< .go.a: - go tool compile $(GOCFLAGS) -I $(@D) -o $@ -p $(*F) \ - `find $< $$(if [ $(*F) != main ]; then \ - echo src/$(NAME).go src/version.go; fi) | uniq` + go tool compile $(GOCFLAGS) -I $(@D) -o $@ -p $(*F) $< .a.bin: - go tool link $(GOLDFLAGS) -L $(@D) -o $@ --extldflags '$(LDLIBS)' $< + go tool link $(GOLDFLAGS) -L $(@D) -o $@ --extldflags '$(LDLIBS)' $< @@ -47,6 +48,23 @@ functional-tests/lib.a = $(functional-tests/lib.go:.go=.a) fuzz-targets/lib.a = $(fuzz-targets/lib.go:.go=.a) benchmarks/lib.a = $(benchmarks/lib.go:.go=.a) +cgo.go = \ + src/_cgo_import.go \ + src/_cgo_gotypes.go \ + src/$(NAME).cgo1.go \ + +cgo.c = \ + src/_cgo_export.c \ + src/$(NAME).cgo2.c \ + +cgo.o = $(cgo.c:.c=.o) + +objects = \ + src/_cgo_.o \ + $(cgo.go) \ + $(cgo.c) \ + $(cgo.o) \ + sources = \ src/$(NAME).go \ src/version.go \ @@ -55,12 +73,15 @@ sources = \ derived-assets = \ src/version.go \ + $(objects) \ $(libs.a) \ $(mains.a) \ $(mains.bin) \ $(NAME).bin \ side-assets = \ + src/_cgo_export.h \ + src/_cgo_main.c \ tests/fuzz/corpus/ \ tests/benchmarks/*/main.txt \ @@ -71,13 +92,29 @@ side-assets = \ all: $(derived-assets) +$(objects): Makefile deps.mk $(libs.a): Makefile deps.mk $(libs.a): src/$(NAME).go src/version.go +$(libs.a): $(cgo.go) $(cgo.o) + +$(cgo.go) $(cgo.c) $(cgo.o): src/_cgo_.o + + +src/_cgo_.o: src/$(NAME).go + go tool cgo --objdir $(@D) src/$(NAME).go + +src/_cgo_import.go: src/_cgo_.o + go tool cgo --dynpackage $(NAME) --dynimport src/_cgo_.o --dynout $@ +src/$(NAME).a tests/$(NAME).a $(functional-tests/lib.a) $(benchmarks/lib.a): + go tool compile $(GOCFLAGS) -o $@ -p $(*F) $(cgo.go) src/version.go \ + `find $*.go | grep -Ev '^src/$(NAME)\.go$$'` + go tool pack r $@ $(cgo.o) $(fuzz-targets/lib.a): go tool compile $(GOCFLAGS) -o $@ -p $(NAME) -d=libfuzzer \ - src/version.go $*.go src/$(NAME).go + $*.go $(cgo.go) src/version.go + go tool pack r $@ $(cgo.o) src/version.go: Makefile echo 'package $(NAME); const Version = "$(VERSION)"' > $@ @@ -2,6 +2,7 @@ libs.go = \ src/scrypt.go \ tests/benchmarks/hash/scrypt.go \ tests/functional/hash-and-check/scrypt.go \ + tests/functional/version/scrypt.go \ tests/fuzz/api/scrypt.go \ tests/scrypt.go \ @@ -9,14 +10,17 @@ mains.go = \ src/main.go \ tests/benchmarks/hash/main.go \ tests/functional/hash-and-check/main.go \ + tests/functional/version/main.go \ tests/fuzz/api/main.go \ tests/main.go \ -functional-tests/libs.go = \ +functional-tests/lib.go = \ tests/functional/hash-and-check/scrypt.go \ + tests/functional/version/scrypt.go \ functional-tests/main.go = \ tests/functional/hash-and-check/main.go \ + tests/functional/version/main.go \ fuzz-targets/lib.go = \ tests/fuzz/api/scrypt.go \ @@ -36,6 +40,8 @@ tests/benchmarks/hash/main.a: tests/benchmarks/hash/main.go tests/benchmarks/hash/scrypt.a: tests/benchmarks/hash/scrypt.go tests/functional/hash-and-check/main.a: tests/functional/hash-and-check/main.go tests/functional/hash-and-check/scrypt.a: tests/functional/hash-and-check/scrypt.go +tests/functional/version/main.a: tests/functional/version/main.go +tests/functional/version/scrypt.a: tests/functional/version/scrypt.go tests/fuzz/api/main.a: tests/fuzz/api/main.go tests/fuzz/api/scrypt.a: tests/fuzz/api/scrypt.go tests/main.a: tests/main.go @@ -43,15 +49,18 @@ tests/scrypt.a: tests/scrypt.go src/main.bin: src/main.a tests/benchmarks/hash/main.bin: tests/benchmarks/hash/main.a tests/functional/hash-and-check/main.bin: tests/functional/hash-and-check/main.a +tests/functional/version/main.bin: tests/functional/version/main.a tests/fuzz/api/main.bin: tests/fuzz/api/main.a tests/main.bin: tests/main.a src/main.bin-check: src/main.bin tests/benchmarks/hash/main.bin-check: tests/benchmarks/hash/main.bin tests/functional/hash-and-check/main.bin-check: tests/functional/hash-and-check/main.bin +tests/functional/version/main.bin-check: tests/functional/version/main.bin tests/fuzz/api/main.bin-check: tests/fuzz/api/main.bin tests/main.bin-check: tests/main.bin src/main.a: src/$(NAME).a tests/benchmarks/hash/main.a: tests/benchmarks/hash/$(NAME).a tests/functional/hash-and-check/main.a: tests/functional/hash-and-check/$(NAME).a +tests/functional/version/main.a: tests/functional/version/$(NAME).a tests/fuzz/api/main.a: tests/fuzz/api/$(NAME).a tests/main.a: tests/$(NAME).a @@ -16,7 +16,7 @@ mains() { libs | varlist 'libs.go' mains | varlist 'mains.go' -find tests/functional/*/*.go -not -name main.go | varlist 'functional-tests/libs.go' +find tests/functional/*/*.go -not -name main.go | varlist 'functional-tests/lib.go' find tests/functional/*/main.go | varlist 'functional-tests/main.go' find tests/fuzz/*/*.go -not -name main.go | varlist 'fuzz-targets/lib.go' find tests/fuzz/*/main.go | varlist 'fuzz-targets/main.go' diff --git a/src/scrypt.go b/src/scrypt.go index fe6cb8d..a4f03d2 100644 --- a/src/scrypt.go +++ b/src/scrypt.go @@ -1,28 +1,30 @@ package scrypt import ( - "crypto/hmac" "crypto/rand" - "crypto/sha256" - "encoding/binary" "encoding/hex" "errors" "fmt" - "hash" "io" - "math/bits" "os" "slices" ) +/* +#define _XOPEN_SOURCE 700 +#include <stdlib.h> +#include <scrypt-kdf.h> +*/ +import "C" + + + const ( - saltMinLength = 32 - desiredLength = 32 - maxInt = int((^uint(0)) >> 1) MinimumPasswordLength = 16 - + _SALT_MIN_LENGTH = 32 + _DESIRED_LENGTH = 32 _N = 1 << 15 r = 8 p = 1 @@ -30,274 +32,12 @@ const ( var ( - ErrBadN = errors.New("scrypt: N must be > 1 and a power of 2") - ErrParamsTooLarge = errors.New("scrypt: parameters are too large") - ErrSaltTooSmall = errors.New("scrypt: salt is too small") + ErrSaltTooSmall = errors.New("scrypt: salt is too small") + ErrInternal = errors.New("scrypt: internal error") ) -// Package pbkdf2 implements the key derivation function PBKDF2 as defined in -// RFC 2898 / PKCS #5 v2.0. -// -// A key derivation function is useful when encrypting data based on a password -// or any other not-fully-random data. It uses a pseudorandom function to derive -// a secure encryption key based on the password. -// -// While v2.0 of the standard defines only one pseudorandom function to use, -// HMAC-SHA1, the drafted v2.1 specification allows use of all five FIPS -// Approved Hash Functions SHA-1, SHA-224, SHA-256, SHA-384 and SHA-512 for -// HMAC. To choose, you can pass the `New` functions from the different SHA -// packages to pbkdf2.Key. -// -// -// Key derives a key from the password, salt and iteration count, returning a -// []byte of length keylen that can be used as cryptographic key. The key is -// derived based on the method described as PBKDF2 with the HMAC variant using -// the supplied hash function. -// -// For example, to use a HMAC-SHA-1 based PBKDF2 key derivation function, you -// can get a derived key for e.g. AES-256 (which needs a 32-byte key) by -// doing: -// -// dk := pbkdf2.Key([]byte("some password"), salt, 4096, 32, sha1.New) -// -// Remember to get a good random salt. At least 8 bytes is recommended by the -// RFC. -// -// Using a higher iteration count will increase the cost of an exhaustive -// search but will also make derivation proportionally slower. -func _PBKDF2Key( - password []byte, - salt []byte, - iter int, - keyLen int, - h func() hash.Hash, -) []byte { - prf := hmac.New(h, password) - hashLen := prf.Size() - numBlocks := (keyLen + hashLen - 1) / hashLen - - var buffer [4]byte - dk := make([]byte, 0, numBlocks*hashLen) - U := make([]byte, hashLen) - for block := 1; block <= numBlocks; block++ { - // N.B.: || means concatenation, ^ means XOR - // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter - // U_1 = PRF(password, salt || uint(i)) - prf.Reset() - prf.Write(salt) - buffer[0] = byte(block >> 24) - buffer[1] = byte(block >> 16) - buffer[2] = byte(block >> 8) - buffer[3] = byte(block >> 0) - prf.Write(buffer[:4]) - dk = prf.Sum(dk) - T := dk[len(dk) - hashLen:] - copy(U, T) - - // U_n = PRF(password, U_(n - 1)) - for n := 2; n <= iter; n++ { - prf.Reset() - prf.Write(U) - U = U[:0] - U = prf.Sum(U) - for x := range U { - T[x] ^= U[x] - } - } - } - return dk[:keyLen] -} - -// blockCopy copies n numbers from src into dst. -func blockCopy(dst []uint32, src []uint32, n int) { - copy(dst, src[:n]) -} - -// blockXOR XORs numbers from dst with n numbers from src. -func blockXOR(dst []uint32, src []uint32, n int) { - for i, v := range src[:n] { - dst[i] ^= v - } -} - -// salsaXOR applies Salsa20/8 to the XOR of 16 numbers from tmp and in, -// and puts the result into both tmp and out. -func salsaXOR(tmp *[16]uint32, in []uint32, out []uint32) { - w0 := tmp[0] ^ in[0] - w1 := tmp[1] ^ in[1] - w2 := tmp[2] ^ in[2] - w3 := tmp[3] ^ in[3] - w4 := tmp[4] ^ in[4] - w5 := tmp[5] ^ in[5] - w6 := tmp[6] ^ in[6] - w7 := tmp[7] ^ in[7] - w8 := tmp[8] ^ in[8] - w9 := tmp[9] ^ in[9] - w10 := tmp[10] ^ in[10] - w11 := tmp[11] ^ in[11] - w12 := tmp[12] ^ in[12] - w13 := tmp[13] ^ in[13] - w14 := tmp[14] ^ in[14] - w15 := tmp[15] ^ in[15] - - x0 := w0 - x1 := w1 - x2 := w2 - x3 := w3 - x4 := w4 - x5 := w5 - x6 := w6 - x7 := w7 - x8 := w8 - x9 := w9 - x10 := w10 - x11 := w11 - x12 := w12 - x13 := w13 - x14 := w14 - x15 := w15 - - for i := 0; i < 8; i += 2 { - x4 ^= bits.RotateLeft32(x0 + x12, 7) - x8 ^= bits.RotateLeft32(x4 + x0, 9) - x12 ^= bits.RotateLeft32(x8 + x4, 13) - x0 ^= bits.RotateLeft32(x12 + x8, 18) - - x9 ^= bits.RotateLeft32(x5 + x1, 7) - x13 ^= bits.RotateLeft32(x9 + x5, 9) - x1 ^= bits.RotateLeft32(x13 + x9, 13) - x5 ^= bits.RotateLeft32(x1 + x13, 18) - - x14 ^= bits.RotateLeft32(x10 + x6, 7) - x2 ^= bits.RotateLeft32(x14 + x10, 9) - x6 ^= bits.RotateLeft32(x2 + x14, 13) - x10 ^= bits.RotateLeft32(x6 + x2, 18) - - x3 ^= bits.RotateLeft32(x15 + x11, 7) - x7 ^= bits.RotateLeft32(x3 + x15, 9) - x11 ^= bits.RotateLeft32(x7 + x3, 13) - x15 ^= bits.RotateLeft32(x11 + x7, 18) - - x1 ^= bits.RotateLeft32(x0 + x3, 7) - x2 ^= bits.RotateLeft32(x1 + x0, 9) - x3 ^= bits.RotateLeft32(x2 + x1, 13) - x0 ^= bits.RotateLeft32(x3 + x2, 18) - - x6 ^= bits.RotateLeft32(x5 + x4, 7) - x7 ^= bits.RotateLeft32(x6 + x5, 9) - x4 ^= bits.RotateLeft32(x7 + x6, 13) - x5 ^= bits.RotateLeft32(x4 + x7, 18) - - x11 ^= bits.RotateLeft32(x10 + x9, 7) - x8 ^= bits.RotateLeft32(x11 + x10, 9) - x9 ^= bits.RotateLeft32(x8 + x11, 13) - x10 ^= bits.RotateLeft32(x9 + x8, 18) - - x12 ^= bits.RotateLeft32(x15 + x14, 7) - x13 ^= bits.RotateLeft32(x12 + x15, 9) - x14 ^= bits.RotateLeft32(x13 + x12, 13) - x15 ^= bits.RotateLeft32(x14 + x13, 18) - } - - x0 += w0 - x1 += w1 - x2 += w2 - x3 += w3 - x4 += w4 - x5 += w5 - x6 += w6 - x7 += w7 - x8 += w8 - x9 += w9 - x10 += w10 - x11 += w11 - x12 += w12 - x13 += w13 - x14 += w14 - x15 += w15 - - out[0], tmp[0] = x0, x0 - out[1], tmp[1] = x1, x1 - out[2], tmp[2] = x2, x2 - out[3], tmp[3] = x3, x3 - out[4], tmp[4] = x4, x4 - out[5], tmp[5] = x5, x5 - out[6], tmp[6] = x6, x6 - out[7], tmp[7] = x7, x7 - out[8], tmp[8] = x8, x8 - out[9], tmp[9] = x9, x9 - out[10], tmp[10] = x10, x10 - out[11], tmp[11] = x11, x11 - out[12], tmp[12] = x12, x12 - out[13], tmp[13] = x13, x13 - out[14], tmp[14] = x14, x14 - out[15], tmp[15] = x15, x15 -} - -func blockMix(tmp *[16]uint32, in []uint32, out []uint32, r int) { - blockCopy(tmp[:], in[(2 * r - 1) * 16:], 16) - for i := 0; i < 2 * r; i += 2 { - salsaXOR(tmp, in[i * 16:], out[i * 8:]) - salsaXOR(tmp, in[i * 16 + 16:], out[i * 8 + r * 16:]) - } -} - -func integer(b []uint32, r int) uint64 { - j := (2 * r - 1) * 16 - return uint64(b[j]) | (uint64(b[j + 1]) << 32) -} - -func smix(b []byte, r int, N int, v []uint32, xy []uint32) { - var tmp [16]uint32 - R := 32 * r - x := xy - y := xy[R:] - - j := 0 - for i := 0; i < R; i++ { - x[i] = binary.LittleEndian.Uint32(b[j:]) - j += 4 - } - for i := 0; i < N; i += 2 { - blockCopy(v[i * R:], x, R) - blockMix(&tmp, x, y, r) - - blockCopy(v[(i + 1) * R:], y, R) - blockMix(&tmp, y, x, r) - } - for i := 0; i < N; i += 2 { - j := int(integer(x, r) & uint64(N - 1)) - blockXOR(x, v[j * R:], R) - blockMix(&tmp, x, y, r) - - j = int(integer(y, r) & uint64(N - 1)) - blockXOR(y, v[j * R:], R) - blockMix(&tmp, y, x, r) - } - j = 0 - for _, v := range x[:R] { - binary.LittleEndian.PutUint32(b[j:], v) - j += 4 - } -} - -func validateParams(N int, r int, p int) error { - if N <= 1 || N & (N - 1) != 0 { - return ErrBadN - } - - if ((uint64(r) * uint64(p)) >= (1 << 30)) || - r > maxInt / 128 / p || - r > maxInt / 256 || - N > maxInt / 128 / r { - return ErrParamsTooLarge - } - - return nil -} - // 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). @@ -325,39 +65,37 @@ func scrypt( N int, r int, p int, - keyLen int, + outlen int, ) ([]byte, error) { - err := validateParams(N, r, p) - if err != nil { - return nil, err - } - - xy := make([]uint32, 64 * r) - v := make([]uint32, 32 * r * N) - b := _PBKDF2Key(password, salt, 1, p * 128 * r, sha256.New) - - for i := 0; i < p; i++ { - smix(b[i * 128 * r:], r, N, v, xy) - } - - return _PBKDF2Key(password, b, 1, keyLen, sha256.New), nil -} - -func SaltFrom(r io.Reader) ([]byte, error) { - buffer := make([]byte, saltMinLength) - _, err := io.ReadFull(r, buffer) - if err != nil { - return nil, err + 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 } - return buffer, nil -} -func Salt() ([]byte, error) { - return SaltFrom(rand.Reader) + out := C.GoBytes(outbuf, C.int(outlen)) + return out, nil } func Hash(password []byte, salt []byte) ([]byte, error) { - if len(salt) < saltMinLength { + if len(salt) < _SALT_MIN_LENGTH { return nil, ErrSaltTooSmall } @@ -367,7 +105,7 @@ func Hash(password []byte, salt []byte) ([]byte, error) { _N, r, p, - desiredLength, + _DESIRED_LENGTH, ) if err != nil { return nil, err @@ -376,6 +114,19 @@ func Hash(password []byte, salt []byte) ([]byte, error) { return hash, nil } +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(password []byte, salt []byte, hash []byte) (bool, error) { candidate, err := Hash(password, salt) if err != nil { @@ -392,8 +143,10 @@ func Main() { fmt.Fprintf(os.Stderr, "Usage: scrypt PASSWORD SALT\n") os.Exit(2) } + password := os.Args[1] + salt := os.Args[2] - payload, err := Hash([]byte(os.Args[1]), []byte(os.Args[2])) + payload, err := Hash([]byte(password), []byte(salt)) if err != nil { if err == ErrSaltTooSmall { fmt.Fprintln(os.Stderr, err) diff --git a/tests/functional/version/main.go b/tests/functional/version/main.go new file mode 120000 index 0000000..f67563d --- /dev/null +++ b/tests/functional/version/main.go @@ -0,0 +1 @@ +../../main.go
\ No newline at end of file diff --git a/tests/functional/version/scrypt.go b/tests/functional/version/scrypt.go new file mode 100644 index 0000000..5133f8c --- /dev/null +++ b/tests/functional/version/scrypt.go @@ -0,0 +1,13 @@ +package scrypt + +import ( + g "gobang" +) + + + +func MainTest() { + g.Testing("we can get the lib version", func() { + g.TAssertEqual(len(Version) >= 5, true) + }) +} diff --git a/tests/fuzz/api/scrypt.go b/tests/fuzz/api/scrypt.go index eb29d2d..8e785a5 100644 --- a/tests/fuzz/api/scrypt.go +++ b/tests/fuzz/api/scrypt.go @@ -10,7 +10,7 @@ import ( func api(f *testing.F) { f.Fuzz(func(t *testing.T, password []byte, salt []byte) { - if len(salt) < saltMinLength { + if len(salt) < _SALT_MIN_LENGTH { return } diff --git a/tests/scrypt.go b/tests/scrypt.go index cb97b08..f499f84 100644 --- a/tests/scrypt.go +++ b/tests/scrypt.go @@ -1,175 +1,19 @@ package scrypt import ( - "crypto/sha1" - "crypto/sha256" "encoding/base64" - // "encoding/hex" - "hash" + "strings" g "gobang" ) -type pbkdfTestVector struct { - password string - salt string - iter int - output []byte -} - -// Test vectors from RFC 6070, http://tools.ietf.org/html/rfc6070 -var sha1TestVectors = []pbkdfTestVector { - { - "password", - "salt", - 1, - []byte { - 0x0c, 0x60, 0xc8, 0x0f, 0x96, 0x1f, 0x0e, 0x71, - 0xf3, 0xa9, 0xb5, 0x24, 0xaf, 0x60, 0x12, 0x06, - 0x2f, 0xe0, 0x37, 0xa6, - }, - }, - { - "password", - "salt", - 2, - []byte{ - 0xea, 0x6c, 0x01, 0x4d, 0xc7, 0x2d, 0x6f, 0x8c, - 0xcd, 0x1e, 0xd9, 0x2a, 0xce, 0x1d, 0x41, 0xf0, - 0xd8, 0xde, 0x89, 0x57, - }, - }, - { - "password", - "salt", - 4096, - []byte{ - 0x4b, 0x00, 0x79, 0x01, 0xb7, 0x65, 0x48, 0x9a, - 0xbe, 0xad, 0x49, 0xd9, 0x26, 0xf7, 0x21, 0xd0, - 0x65, 0xa4, 0x29, 0xc1, - }, - }, - // // This one takes too long - // { - // "password", - // "salt", - // 16777216, - // []byte { - // 0xee, 0xfe, 0x3d, 0x61, 0xcd, 0x4d, 0xa4, 0xe4, - // 0xe9, 0x94, 0x5b, 0x3d, 0x6b, 0xa2, 0x15, 0x8c, - // 0x26, 0x34, 0xe9, 0x84, - // }, - // }, - { - "passwordPASSWORDpassword", - "saltSALTsaltSALTsaltSALTsaltSALTsalt", - 4096, - []byte{ - 0x3d, 0x2e, 0xec, 0x4f, 0xe4, 0x1c, 0x84, 0x9b, - 0x80, 0xc8, 0xd8, 0x36, 0x62, 0xc0, 0xe4, 0x4a, - 0x8b, 0x29, 0x1a, 0x96, 0x4c, 0xf2, 0xf0, 0x70, - 0x38, - }, - }, - { - "pass\000word", - "sa\000lt", - 4096, - []byte{ - 0x56, 0xfa, 0x6a, 0xa7, 0x55, 0x48, 0x09, 0x9d, - 0xcc, 0x37, 0xd7, 0xf0, 0x34, 0x25, 0xe0, 0xc3, - }, - }, -} - -// Test vectors from -// http://stackoverflow.com/questions/5130513/pbkdf2-hmac-sha2-test-vectors -var sha256TestVectors = []pbkdfTestVector { - { - "password", - "salt", - 1, - []byte { - 0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, - 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, - 0xa8, 0x65, 0x48, 0xc9, - }, - }, - { - "password", - "salt", - 2, - []byte { - 0xae, 0x4d, 0x0c, 0x95, 0xaf, 0x6b, 0x46, 0xd3, - 0x2d, 0x0a, 0xdf, 0xf9, 0x28, 0xf0, 0x6d, 0xd0, - 0x2a, 0x30, 0x3f, 0x8e, - }, - }, - { - "password", - "salt", - 4096, - []byte { - 0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, - 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, - 0x96, 0x28, 0x93, 0xa0, - }, - }, - { - "passwordPASSWORDpassword", - "saltSALTsaltSALTsaltSALTsaltSALTsalt", - 4096, - []byte { - 0x34, 0x8c, 0x89, 0xdb, 0xcb, 0xd3, 0x2b, 0x2f, - 0x32, 0xd8, 0x14, 0xb8, 0x11, 0x6e, 0x84, 0xcf, - 0x2b, 0x17, 0x34, 0x7e, 0xbc, 0x18, 0x00, 0x18, - 0x1c, - }, - }, - { - "pass\000word", - "sa\000lt", - 4096, - []byte { - 0x89, 0xb6, 0x9d, 0x05, 0x16, 0xf8, 0x29, 0x89, - 0x3c, 0x69, 0x62, 0x26, 0x65, 0x0a, 0x86, 0x87, - }, - }, -} - -func testHash(h func() hash.Hash, hashName string, vectors []pbkdfTestVector) { - for _, v := range vectors { - out := _PBKDF2Key( - []byte(v.password), - []byte(v.salt), - v.iter, - len(v.output), - h, - ) - g.TAssertEqual(out, v.output) - } -} - -func test__PBKDF() { - g.TestStart("_PBKDF()") - - g.Testing("HMAC with SHA1", func() { - testHash(sha1.New, "SHA1", sha1TestVectors) - }) - - g.Testing("HMAC with SHA256", func() { - testHash(sha256.New, "SHA256", sha256TestVectors) - }) -} - type scryptTestVector struct { password string salt string N, r, p int output []byte - err error } var good = []scryptTestVector { @@ -183,7 +27,6 @@ var good = []scryptTestVector { 0x87, 0x25, 0x1a, 0x53, 0x4f, 0x75, 0xac, 0xd9, 0x5a, 0xc5, 0xe5, 0xa, 0xa1, 0x5f, }, - nil, }, { "password", @@ -195,7 +38,6 @@ var good = []scryptTestVector { 0x74, 0x82, 0x95, 0x25, 0xb1, 0x8d, 0x73, 0x23, 0xa5, 0x7f, 0x91, 0x96, 0x3c, 0x37, }, - nil, }, { "this is a long \000 password", @@ -212,7 +54,6 @@ var good = []scryptTestVector { 0x14, 0x32, 0xbb, 0x3b, 0x6f, 0x7e, 0x12, 0x64, 0x40, 0x18, 0x79, 0xe6, 0x41, 0xae, }, - nil, }, { "p", @@ -222,9 +63,7 @@ var good = []scryptTestVector { 0x48, 0xb0, 0xd2, 0xa8, 0xa3, 0x27, 0x26, 0x11, 0x98, 0x4c, 0x50, 0xeb, 0xd6, 0x30, 0xaf, 0x52, }, - nil, }, - { "", "", @@ -239,7 +78,6 @@ var good = []scryptTestVector { 0x36, 0x28, 0xcf, 0x35, 0xe2, 0x0c, 0x38, 0xd1, 0x89, 0x06, }, - nil, }, { "password", @@ -255,7 +93,6 @@ var good = []scryptTestVector { 0xee, 0x6d, 0x83, 0x60, 0xcb, 0xdf, 0xa2, 0xcc, 0x06, 0x40, }, - nil, }, { "pleaseletmein", "SodiumChloride", @@ -270,7 +107,6 @@ var good = []scryptTestVector { 0x1e, 0x40, 0xdf, 0xcf, 0x01, 0x7b, 0x45, 0x57, 0x58, 0x87, }, - nil, }, // // Disabled: needs 1 GiB RAM and takes too long for a simple test. // { @@ -286,16 +122,15 @@ var good = []scryptTestVector { // 0x52, 0xfb, 0xcb, 0xf4, 0x5c, 0x6f, 0xa7, 0x7a, 0x41, // 0xa4, // }, - // nil, // }, } -const halfMax = maxInt / 2 +const halfMax = (int((^uint(0)) >> 1)) / 2 var bad = []scryptTestVector { - {"p", "s", 0, 1, 1, nil, ErrBadN}, // N == 0 - {"p", "s", 1, 1, 1, nil, ErrBadN}, // N == 1 - {"p", "s", 7, 8, 1, nil, ErrBadN}, // N is not power of 2 - {"p", "s", 16, halfMax, halfMax, nil, ErrParamsTooLarge}, // p * r too large + {"p", "s", 0, 1, 1, nil}, // N == 0 + {"p", "s", 1, 1, 1, nil}, // N == 1 + {"p", "s", 7, 8, 1, nil}, // N is not power of 2 + {"p", "s", 16, halfMax, halfMax, nil}, // p * r too large } func test_scrypt() { @@ -326,7 +161,7 @@ func test_scrypt() { v.p, 32, ) - g.TAssertEqual(err, v.err) + g.TErrorNil(err) } }) @@ -354,34 +189,22 @@ func test_scrypt() { func test_SaltFrom() { g.TestStart("SaltFrom()") - g.Testing("error when reader errors", func() { + g.Testing("reader error is propagated", func() { + r := strings.NewReader("input") + payload, err := SaltFrom(r) + g.TAssertEqual(err.Error(), "unexpected EOF") + g.TAssertEqual(payload, []byte(nil)) }) - // FIXME -} - -func test_HashFrom() { - g.TestStart("HashFrom()") - // FIXME -} - -func test_CheckFrom() { - g.TestStart("CheckFrom()") - // FIXME } func test_Salt() { g.TestStart("Salt()") - /* g.Testing("we generate a random salt of a fixed size", func() { - salt := Salt() - g.TAssertEqual(len(salt), saltMinLength) - - var buffer [saltMinLength * 2]byte - hex.Encode(buffer[:], salt) - // FIXME + salt, err := Salt() + g.TErrorIf(err) + g.TAssertEqual(len(salt), _SALT_MIN_LENGTH) }) - */ } func test_Hash() { @@ -406,17 +229,69 @@ func test_Hash() { func test_Check() { g.TestStart("Check()") - // FIXME + + h := func(password []byte, salt []byte) []byte { + hash, err := Hash(password, salt) + g.TErrorIf(err) + return hash + } + + chk := func(password []byte, salt []byte, hash []byte) bool { + ok, err := Check(password, salt, hash) + g.TErrorIf(err) + return ok + } + + + g.Testing("true for equal inputs", func() { + password1 := []byte("password") + password2 := []byte("passw0rd") + salt1 := []byte("salt0000111111112222222233333333") + salt2 := []byte("salt0000l11111112222222233333333") + + hash1 := h(password1, salt1) + hash2 := h(password1, salt2) + hash3 := h(password2, salt1) + hash4 := h(password2, salt2) + + given1 := []bool{ + chk(password1, salt1, hash1), + chk(password1, salt2, hash2), + chk(password2, salt1, hash3), + chk(password2, salt2, hash4), + } + + expected1 := []bool{ + true, + true, + true, + true, + } + + given2 := []bool{ + chk(password1, salt1, hash4), + chk(password1, salt2, hash3), + chk(password2, salt1, hash2), + chk(password2, salt2, hash1), + } + + expected2 := []bool{ + false, + false, + false, + false, + } + + g.TAssertEqual(given1, expected1) + g.TAssertEqual(given2, expected2) + }) } func MainTest() { - test__PBKDF() test_scrypt() test_SaltFrom() - test_HashFrom() - test_CheckFrom() test_Salt() test_Hash() test_Check() |