├── .gitignore ├── LICENSE ├── example_test.go ├── wrapper.h ├── README.md ├── error.go ├── context.go ├── bench_test.go ├── argon2.go └── argon2_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | 26 | *.swp 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Tijmen van der Burgt 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package argon2_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/tvdburgt/go-argon2" 6 | "log" 7 | ) 8 | 9 | func ExampleHash() { 10 | password := []byte("password") 11 | salt := make([]byte, 16) // pad salt to 16 bytes 12 | copy(salt, []byte("somesalt")) 13 | 14 | ctx := &argon2.Context{ 15 | Iterations: 2, 16 | Memory: 1 << 16, 17 | Parallelism: 4, 18 | HashLen: 32, 19 | Mode: argon2.ModeArgon2i, 20 | Version: argon2.Version10, 21 | } 22 | 23 | hash, err := argon2.Hash(ctx, password, salt) 24 | if err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | fmt.Printf("%x\n", hash) 29 | 30 | // Output: 31 | // 4162f32384d8f4790bd994cb73c83a4a29f076165ec18af3cfdcf10a8d1b9066 32 | } 33 | 34 | func ExampleHashEncoded() { 35 | password := []byte("password") 36 | salt := make([]byte, 16) // pad salt to 16 bytes 37 | copy(salt, []byte("somesalt")) 38 | 39 | ctx := &argon2.Context{ 40 | Iterations: 2, 41 | Memory: 1 << 16, 42 | Parallelism: 4, 43 | HashLen: 32, 44 | Mode: argon2.ModeArgon2i, 45 | Version: argon2.Version10, 46 | } 47 | 48 | s, err := argon2.HashEncoded(ctx, password, salt) 49 | if err != nil { 50 | log.Fatal(err) 51 | } 52 | 53 | fmt.Println(s) 54 | 55 | // Output: 56 | // $argon2i$v=16$m=65536,t=2,p=4$c29tZXNhbHQAAAAAAAAAAA$QWLzI4TY9HkL2ZTLc8g6SinwdhZewYrzz9zxCo0bkGY 57 | } 58 | -------------------------------------------------------------------------------- /wrapper.h: -------------------------------------------------------------------------------- 1 | // Wrapper function to pass pointers as individual parameters. 2 | // 3 | // Go does not allow passing memory containing Go pointers as this 4 | // would fail when the garbage collector moves things around. Passing 5 | // pointers as parameters to a single C call is allowed, so the struct 6 | // must be instantiated, used and freed all within a single C 7 | // function. 8 | int argon2_wrapper(uint8_t *out, uint32_t outlen, 9 | uint8_t *pwd, uint32_t pwdlen, 10 | uint8_t *salt, uint32_t saltlen, 11 | uint8_t *secret, uint32_t secretlen, 12 | uint8_t *ad, uint32_t adlen, 13 | 14 | uint32_t t_cost, 15 | uint32_t m_cost, 16 | uint32_t lanes, 17 | uint32_t threads, 18 | 19 | uint32_t version, 20 | 21 | allocate_fptr allocate_cbk, 22 | deallocate_fptr free_cbk, 23 | 24 | uint32_t flags, 25 | argon2_type type) { 26 | 27 | argon2_context context = { 28 | .out = out, 29 | .outlen = outlen, 30 | 31 | .pwd = pwd, 32 | .pwdlen = pwdlen, 33 | 34 | .salt = salt, 35 | .saltlen = saltlen, 36 | 37 | .secret = secret, 38 | .secretlen = secretlen, 39 | 40 | .ad = ad, 41 | .adlen = adlen, 42 | 43 | .t_cost = t_cost, 44 | .m_cost = m_cost, 45 | .lanes = lanes, 46 | .threads = threads, 47 | 48 | .version = version, 49 | 50 | .allocate_cbk = allocate_cbk, 51 | .free_cbk = free_cbk, 52 | 53 | .flags = flags 54 | }; 55 | 56 | return argon2_ctx(&context, type); 57 | } 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-argon2 2 | 3 | [![GoDoc](https://godoc.org/github.com/tvdburgt/go-argon2?status.svg)](https://godoc.org/github.com/tvdburgt/go-argon2) 4 | 5 | Go bindings for the reference C implementation of 6 | [Argon2](https://github.com/P-H-C/phc-winner-argon2), the winner of the 7 | [Password Hash Competition](https://password-hashing.net). 8 | 9 | ## Installation 10 | 11 | ``` 12 | $ go get -d github.com/tvdburgt/go-argon2 13 | ``` 14 | 15 | This package depends on `libargon2`, specifically `libargon2.so` and `argon2.h`. 16 | Make sure the library files are available in `/usr`: 17 | 18 | 19 | ``` 20 | $ git clone https://github.com/P-H-C/phc-winner-argon2.git argon2 21 | $ cd argon2 22 | $ git checkout tags/20171227 # switch to latest release 23 | $ sudo make install 24 | ``` 25 | 26 | Test everything is installed correctly: 27 | 28 | ``` 29 | $ cd $GOPATH/src/github.com/tvdburgt/go-argon2/ 30 | $ go test 31 | ``` 32 | 33 | ## Usage 34 | ### Raw hash with default configuration 35 | 36 | ```go 37 | hash, err := argon2.Hash(argon2.NewContext(), []byte("password"), []byte("somesalt")) 38 | if err != nil { 39 | log.Fatal(err) 40 | } 41 | 42 | fmt.Printf("%x\n", hash) 43 | ``` 44 | 45 | ### Encoded hash with custom configuration 46 | 47 | ```go 48 | ctx := &argon2.Context{ 49 | Iterations: 5, 50 | Memory: 1 << 16, 51 | Parallelism: 2, 52 | HashLen: 32, 53 | Mode: argon2.ModeArgon2i, 54 | Version: argon2.Version13, 55 | } 56 | 57 | s, err := argon2.HashEncoded(ctx, []byte("password"), []byte("somesalt")) 58 | if err != nil { 59 | log.Fatal(err) 60 | } 61 | 62 | fmt.Println(s) 63 | ``` 64 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package argon2 2 | 3 | // #cgo CFLAGS: -I/usr/include 4 | // #include 5 | import "C" 6 | 7 | import ( 8 | "errors" 9 | "fmt" 10 | ) 11 | 12 | // Error represents the internal error code propagated from libargon2. 13 | type Error int 14 | 15 | func (e Error) Error() string { 16 | msg := C.argon2_error_message(C.int(e)) 17 | return fmt.Sprintf("argon2: %s", C.GoString(msg)) 18 | } 19 | 20 | var ( 21 | ErrContext = errors.New("argon2: context is nil") 22 | ErrPassword = errors.New("argon2: password is nil or empty") 23 | ErrSalt = errors.New("argon2: salt is nil or empty") 24 | ErrHash = errors.New("argon2: hash is nil or empty") 25 | ) 26 | 27 | var ( 28 | ErrOutputPtrNull Error = C.ARGON2_OUTPUT_PTR_NULL 29 | ErrOutputTooShort Error = C.ARGON2_OUTPUT_TOO_SHORT 30 | ErrOutputTooLong Error = C.ARGON2_OUTPUT_TOO_LONG 31 | ErrPwdTooShort Error = C.ARGON2_PWD_TOO_SHORT 32 | ErrPwdTooLong Error = C.ARGON2_PWD_TOO_LONG 33 | ErrSaltTooShort Error = C.ARGON2_SALT_TOO_SHORT 34 | ErrSaltTooLong Error = C.ARGON2_SALT_TOO_LONG 35 | ErrAdTooShort Error = C.ARGON2_AD_TOO_SHORT 36 | ErrAdTooLong Error = C.ARGON2_AD_TOO_LONG 37 | ErrSecretTooShort Error = C.ARGON2_SECRET_TOO_SHORT 38 | ErrSecretTooLong Error = C.ARGON2_SECRET_TOO_LONG 39 | ErrTimeTooSmall Error = C.ARGON2_TIME_TOO_SMALL 40 | ErrTimeTooLarge Error = C.ARGON2_TIME_TOO_LARGE 41 | ErrMemoryTooLittle Error = C.ARGON2_MEMORY_TOO_LITTLE 42 | ErrMemoryTooMuch Error = C.ARGON2_MEMORY_TOO_MUCH 43 | ErrLanesTooFew Error = C.ARGON2_LANES_TOO_FEW 44 | ErrLanesTooMany Error = C.ARGON2_LANES_TOO_MANY 45 | ErrPwdPtrMismatch Error = C.ARGON2_PWD_PTR_MISMATCH 46 | ErrSaltPtrMismatch Error = C.ARGON2_SALT_PTR_MISMATCH 47 | ErrSecretPtrMismatch Error = C.ARGON2_SECRET_PTR_MISMATCH 48 | ErrAdPtrMismatch Error = C.ARGON2_AD_PTR_MISMATCH 49 | ErrMemoryAllocationError Error = C.ARGON2_MEMORY_ALLOCATION_ERROR 50 | ErrFreeMemoryCbkNull Error = C.ARGON2_FREE_MEMORY_CBK_NULL 51 | ErrAllocateMemoryCbkNull Error = C.ARGON2_ALLOCATE_MEMORY_CBK_NULL 52 | ErrIncorrectParameter Error = C.ARGON2_INCORRECT_PARAMETER 53 | ErrIncorrectType Error = C.ARGON2_INCORRECT_TYPE 54 | ErrOutPtrMismatch Error = C.ARGON2_OUT_PTR_MISMATCH 55 | ErrThreadsTooFew Error = C.ARGON2_THREADS_TOO_FEW 56 | ErrThreadsTooMany Error = C.ARGON2_THREADS_TOO_MANY 57 | ErrMissingArgs Error = C.ARGON2_MISSING_ARGS 58 | ErrEncodingFail Error = C.ARGON2_ENCODING_FAIL 59 | ErrDecodingFail Error = C.ARGON2_DECODING_FAIL 60 | ErrThreadFail Error = C.ARGON2_THREAD_FAIL 61 | ErrDecodingLengthFail Error = C.ARGON2_DECODING_LENGTH_FAIL 62 | ErrVerifyMismatch Error = C.ARGON2_VERIFY_MISMATCH 63 | ) 64 | -------------------------------------------------------------------------------- /context.go: -------------------------------------------------------------------------------- 1 | package argon2 2 | 3 | // #cgo CFLAGS: -I/usr/include 4 | // #include 5 | // #include "wrapper.h" 6 | import "C" 7 | 8 | // Context represents a structure that holds all static configuration values, 9 | // used to parameterize an Argon2 hash function. 10 | type Context struct { 11 | Iterations int // number of iterations (t_cost) 12 | Memory int // memory usage in KiB (m_cost) 13 | Parallelism int // number of parallel threads 14 | HashLen int // desired hash output length 15 | Mode int // ModeArgon2d, ModeArgon2i, or ModeArgon2id 16 | Version int // Version10 or Version13 (aka VersionDefault) 17 | Secret []byte // optional (not used by default) 18 | AssociatedData []byte // optional (not used by default) 19 | Flags int // optional (default is FlagDefault) 20 | } 21 | 22 | // NewContext initializes a new Argon2 context with reasonable defaults. 23 | // allows the mode to be set as an optional paramter 24 | func NewContext(mode ...int) *Context { 25 | context := &Context{ 26 | Iterations: 3, 27 | Memory: 1 << 12, // 4 MiB 28 | Parallelism: 1, 29 | HashLen: 32, 30 | Mode: ModeArgon2i, 31 | Version: VersionDefault, 32 | } 33 | if len(mode) >= 1 { 34 | context.Mode = mode[0] 35 | } 36 | return context 37 | } 38 | 39 | // hash password and salt 40 | func (ctx *Context) hash(password []byte, salt []byte) ([]byte, error) { 41 | 42 | if len(password) == 0 { 43 | return nil, ErrPassword 44 | } 45 | if len(salt) == 0 { 46 | return nil, ErrSalt 47 | } 48 | 49 | hash := make([]byte, ctx.HashLen) 50 | 51 | // optional secret 52 | secret := (*C.uint8_t)(nil) 53 | if len(ctx.Secret) > 0 { 54 | secret = (*C.uint8_t)(&ctx.Secret[0]) 55 | } 56 | 57 | // optional associated data 58 | associatedData := (*C.uint8_t)(nil) 59 | if len(ctx.AssociatedData) > 0 { 60 | associatedData = (*C.uint8_t)(&ctx.AssociatedData[0]) 61 | } 62 | 63 | // optional flags 64 | flags := FlagDefault 65 | if ctx.Flags != 0 { 66 | flags = ctx.Flags 67 | } 68 | 69 | // ensure version has a default 70 | version := VersionDefault 71 | if ctx.Version != 0 { 72 | version = ctx.Version 73 | } 74 | 75 | // wrapper to overcome go pointer passing limitations 76 | result := C.argon2_wrapper( 77 | (*C.uint8_t)(&hash[0]), C.uint32_t(ctx.HashLen), 78 | (*C.uint8_t)(&password[0]), C.uint32_t(len(password)), 79 | (*C.uint8_t)(&salt[0]), C.uint32_t(len(salt)), 80 | secret, C.uint32_t(len(ctx.Secret)), 81 | associatedData, C.uint32_t(len(ctx.AssociatedData)), 82 | C.uint32_t(ctx.Iterations), 83 | C.uint32_t(ctx.Memory), 84 | C.uint32_t(ctx.Parallelism), 85 | C.uint32_t(ctx.Parallelism), 86 | C.uint32_t(version), 87 | nil, nil, 88 | C.uint32_t(flags), 89 | C.argon2_type(ctx.Mode)) 90 | 91 | if result != C.ARGON2_OK { 92 | return nil, Error(result) 93 | } 94 | 95 | return hash, nil 96 | } 97 | -------------------------------------------------------------------------------- /bench_test.go: -------------------------------------------------------------------------------- 1 | package argon2 2 | 3 | import ( 4 | "crypto/rand" 5 | "testing" 6 | ) 7 | 8 | var password = make([]byte, 32) 9 | var salt = make([]byte, 32) 10 | 11 | func init() { 12 | _, err := rand.Read(password) 13 | if err != nil { 14 | panic(err) 15 | } 16 | _, err = rand.Read(salt) 17 | if err != nil { 18 | panic(err) 19 | } 20 | } 21 | 22 | func BenchmarkHash_i_m12_p1(b *testing.B) { benchmarkHash(b, ModeArgon2i, 12, 1) } // 4 MiB 23 | func BenchmarkHash_i_m12_p2(b *testing.B) { benchmarkHash(b, ModeArgon2i, 12, 2) } 24 | func BenchmarkHash_i_m12_p4(b *testing.B) { benchmarkHash(b, ModeArgon2i, 12, 4) } 25 | func BenchmarkHash_i_m15_p1(b *testing.B) { benchmarkHash(b, ModeArgon2i, 15, 1) } // 32 MiB 26 | func BenchmarkHash_i_m15_p2(b *testing.B) { benchmarkHash(b, ModeArgon2i, 15, 2) } 27 | func BenchmarkHash_i_m15_p4(b *testing.B) { benchmarkHash(b, ModeArgon2i, 15, 4) } 28 | func BenchmarkHash_i_m18_p1(b *testing.B) { benchmarkHash(b, ModeArgon2i, 18, 1) } // 256 MiB 29 | func BenchmarkHash_i_m18_p2(b *testing.B) { benchmarkHash(b, ModeArgon2i, 18, 2) } 30 | func BenchmarkHash_i_m18_p4(b *testing.B) { benchmarkHash(b, ModeArgon2i, 18, 4) } 31 | func BenchmarkHash_i_m20_p1(b *testing.B) { benchmarkHash(b, ModeArgon2i, 20, 1) } // 1024 MiB 32 | func BenchmarkHash_i_m20_p2(b *testing.B) { benchmarkHash(b, ModeArgon2i, 20, 2) } 33 | func BenchmarkHash_i_m20_p4(b *testing.B) { benchmarkHash(b, ModeArgon2i, 20, 4) } 34 | 35 | func BenchmarkHash_d_m12_p1(b *testing.B) { benchmarkHash(b, ModeArgon2d, 12, 1) } // 4 MiB 36 | func BenchmarkHash_d_m12_p2(b *testing.B) { benchmarkHash(b, ModeArgon2d, 12, 2) } 37 | func BenchmarkHash_d_m12_p4(b *testing.B) { benchmarkHash(b, ModeArgon2d, 12, 4) } 38 | func BenchmarkHash_d_m15_p1(b *testing.B) { benchmarkHash(b, ModeArgon2d, 15, 1) } // 32 MiB 39 | func BenchmarkHash_d_m15_p2(b *testing.B) { benchmarkHash(b, ModeArgon2d, 15, 2) } 40 | func BenchmarkHash_d_m15_p4(b *testing.B) { benchmarkHash(b, ModeArgon2d, 15, 4) } 41 | func BenchmarkHash_d_m18_p1(b *testing.B) { benchmarkHash(b, ModeArgon2d, 18, 1) } // 256 MiB 42 | func BenchmarkHash_d_m18_p2(b *testing.B) { benchmarkHash(b, ModeArgon2d, 18, 2) } 43 | func BenchmarkHash_d_m18_p4(b *testing.B) { benchmarkHash(b, ModeArgon2d, 18, 4) } 44 | func BenchmarkHash_d_m20_p1(b *testing.B) { benchmarkHash(b, ModeArgon2d, 20, 1) } // 1024 MiB 45 | func BenchmarkHash_d_m20_p2(b *testing.B) { benchmarkHash(b, ModeArgon2d, 20, 2) } 46 | func BenchmarkHash_d_m20_p4(b *testing.B) { benchmarkHash(b, ModeArgon2d, 20, 4) } 47 | 48 | func BenchmarkHash_id_m12_p1(b *testing.B) { benchmarkHash(b, ModeArgon2id, 12, 1) } // 4 MiB 49 | func BenchmarkHash_id_m12_p2(b *testing.B) { benchmarkHash(b, ModeArgon2id, 12, 2) } 50 | func BenchmarkHash_id_m12_p4(b *testing.B) { benchmarkHash(b, ModeArgon2id, 12, 4) } 51 | func BenchmarkHash_id_m15_p1(b *testing.B) { benchmarkHash(b, ModeArgon2id, 15, 1) } // 32 MiB 52 | func BenchmarkHash_id_m15_p2(b *testing.B) { benchmarkHash(b, ModeArgon2id, 15, 2) } 53 | func BenchmarkHash_id_m15_p4(b *testing.B) { benchmarkHash(b, ModeArgon2id, 15, 4) } 54 | func BenchmarkHash_id_m18_p1(b *testing.B) { benchmarkHash(b, ModeArgon2id, 18, 1) } // 256 MiB 55 | func BenchmarkHash_id_m18_p2(b *testing.B) { benchmarkHash(b, ModeArgon2id, 18, 2) } 56 | func BenchmarkHash_id_m18_p4(b *testing.B) { benchmarkHash(b, ModeArgon2id, 18, 4) } 57 | func BenchmarkHash_id_m20_p1(b *testing.B) { benchmarkHash(b, ModeArgon2id, 20, 1) } // 1024 MiB 58 | func BenchmarkHash_id_m20_p2(b *testing.B) { benchmarkHash(b, ModeArgon2id, 20, 2) } 59 | func BenchmarkHash_id_m20_p4(b *testing.B) { benchmarkHash(b, ModeArgon2id, 20, 4) } 60 | 61 | func benchmarkHash(b *testing.B, mode, memory, parallelism int) { 62 | ctx := &Context{ 63 | Iterations: 1, 64 | Memory: 1 << uint(memory), 65 | Parallelism: parallelism, 66 | HashLen: 32, 67 | Mode: mode, 68 | } 69 | 70 | b.SetBytes(int64(ctx.Memory) << 10) 71 | 72 | for n := 0; n < b.N; n++ { 73 | if _, err := Hash(ctx, password, salt); err != nil { 74 | b.Error(err) 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /argon2.go: -------------------------------------------------------------------------------- 1 | // Package argon2 provides low-level bindings for the Argon2 hashing library: 2 | // libargon2. Argon2 specifies three versions: Argon2i, Argon2d, and Argon2id. 3 | // Argon2i is useful for protection against side-channel attacks (key 4 | // derivation), while Argon2d provides the highest resistance against GPU 5 | // cracking attacks (proof-of-work). Argon2id provides good protection against 6 | // both side-channel and GPU cracking attacks. 7 | package argon2 8 | 9 | // #cgo CFLAGS: -I/usr/include 10 | // #cgo LDFLAGS: -L/usr/lib -largon2 11 | // #include 12 | // #include 13 | import "C" 14 | 15 | import ( 16 | "bytes" 17 | "crypto/subtle" 18 | "strings" 19 | "unsafe" 20 | ) 21 | 22 | const ( 23 | ModeArgon2d int = C.Argon2_d 24 | ModeArgon2i int = C.Argon2_i 25 | ModeArgon2id int = C.Argon2_id 26 | ) 27 | 28 | const ( 29 | Version10 int = C.ARGON2_VERSION_10 30 | Version13 int = C.ARGON2_VERSION_13 31 | VersionDefault int = C.ARGON2_VERSION_NUMBER 32 | ) 33 | 34 | const ( 35 | FlagDefault int = C.ARGON2_DEFAULT_FLAGS 36 | FlagClearPassword int = C.ARGON2_FLAG_CLEAR_PASSWORD 37 | FlagClearSecret int = C.ARGON2_FLAG_CLEAR_SECRET 38 | ) 39 | 40 | // Set C.FLAG_clear_internal_memory = 0 to disable internal memory clearing 41 | 42 | // Hash hashes a password given a salt and an initialized Argon2 context. It 43 | // returns the calculated hash as an output of raw bytes. 44 | func Hash(ctx *Context, password, salt []byte) ([]byte, error) { 45 | if ctx == nil { 46 | return nil, ErrContext 47 | } 48 | 49 | return ctx.hash(password, salt) 50 | } 51 | 52 | // HashEncoded hashes a password and produces a crypt-like encoded string. 53 | func HashEncoded(ctx *Context, password []byte, salt []byte) (string, error) { 54 | if ctx == nil { 55 | return "", ErrContext 56 | } 57 | 58 | if len(password) == 0 { 59 | return "", ErrPassword 60 | } 61 | if len(salt) == 0 { 62 | return "", ErrSalt 63 | } 64 | 65 | encodedlen := C.argon2_encodedlen( 66 | C.uint32_t(ctx.Iterations), 67 | C.uint32_t(ctx.Memory), 68 | C.uint32_t(ctx.Parallelism), 69 | C.uint32_t(len(salt)), 70 | C.uint32_t(ctx.HashLen), 71 | C.argon2_type(ctx.Mode)) 72 | 73 | s := make([]byte, encodedlen) 74 | 75 | result := C.argon2_hash( 76 | C.uint32_t(ctx.Iterations), 77 | C.uint32_t(ctx.Memory), 78 | C.uint32_t(ctx.Parallelism), 79 | unsafe.Pointer(&password[0]), C.size_t(len(password)), 80 | unsafe.Pointer(&salt[0]), C.size_t(len(salt)), 81 | nil, C.size_t(ctx.HashLen), 82 | (*C.char)(unsafe.Pointer(&s[0])), C.size_t(encodedlen), 83 | C.argon2_type(ctx.Mode), 84 | C.uint32_t(ctx.Version)) 85 | 86 | if result != C.ARGON2_OK { 87 | return "", Error(result) 88 | } 89 | 90 | // Strip trailing null byte(s) 91 | s = bytes.TrimRight(s, "\x00") 92 | return string(s), nil 93 | } 94 | 95 | // Verify verifies an Argon2 hash against a plaintext password. 96 | func Verify(ctx *Context, hash, password, salt []byte) (bool, error) { 97 | if ctx == nil { 98 | return false, ErrContext 99 | } 100 | if len(hash) == 0 { 101 | return false, ErrHash 102 | } 103 | 104 | hash2, err := ctx.hash(password, salt) 105 | if err != nil { 106 | return false, err 107 | } 108 | 109 | return subtle.ConstantTimeCompare(hash, hash2) == 1, nil 110 | } 111 | 112 | // VerifyEncoded verifies an encoded Argon2 hash s against a plaintext password. 113 | func VerifyEncoded(s string, password []byte) (bool, error) { 114 | mode, err := getMode(s) 115 | 116 | if err != nil { 117 | return false, err 118 | } 119 | 120 | cs := C.CString(s) 121 | defer C.free(unsafe.Pointer(cs)) 122 | 123 | result := C.argon2_verify( 124 | cs, 125 | unsafe.Pointer(&password[0]), 126 | C.size_t(len(password)), 127 | C.argon2_type(mode)) 128 | 129 | if result == C.ARGON2_OK { 130 | return true, nil 131 | } else if result == C.ARGON2_VERIFY_MISMATCH { 132 | return false, nil 133 | } 134 | 135 | // argon2_verify always seems to return an error in this case... 136 | return false, Error(result) 137 | } 138 | 139 | // getMode tries to extract the mode from an Argon2 encoded string. 140 | func getMode(s string) (int, error) { 141 | switch { 142 | case strings.HasPrefix(s, "$argon2d"): 143 | return ModeArgon2d, nil 144 | case strings.HasPrefix(s, "$argon2id"): 145 | return ModeArgon2id, nil 146 | case strings.HasPrefix(s, "$argon2i"): 147 | return ModeArgon2i, nil 148 | default: 149 | return -1, ErrDecodingFail 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /argon2_test.go: -------------------------------------------------------------------------------- 1 | package argon2 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "testing" 7 | ) 8 | 9 | func TestHash(t *testing.T) { 10 | vectors := []struct { 11 | ctx *Context 12 | password []byte 13 | salt []byte 14 | hash string 15 | }{ 16 | 17 | { 18 | &Context{ 19 | Iterations: 3, 20 | Memory: 1 << 5, 21 | Parallelism: 4, 22 | Secret: bytes.Repeat([]byte{3}, 8), 23 | AssociatedData: bytes.Repeat([]byte{4}, 12), 24 | HashLen: 32, 25 | Mode: ModeArgon2i, 26 | Version: Version10, 27 | }, 28 | bytes.Repeat([]byte{1}, 32), 29 | bytes.Repeat([]byte{2}, 16), 30 | "87aeedd6517ab830cd9765cd8231abb2e647a5dee08f7c05e02fcb763335d0fd", 31 | }, 32 | { 33 | &Context{ 34 | Iterations: 3, 35 | Memory: 1 << 5, 36 | Parallelism: 4, 37 | Secret: bytes.Repeat([]byte{3}, 8), 38 | AssociatedData: bytes.Repeat([]byte{4}, 12), 39 | HashLen: 32, 40 | Mode: ModeArgon2i, 41 | Version: Version13, 42 | }, 43 | bytes.Repeat([]byte{1}, 32), 44 | bytes.Repeat([]byte{2}, 16), 45 | "c814d9d1dc7f37aa13f0d77f2494bda1c8de6b016dd388d29952a4c4672b6ce8", 46 | }, 47 | { 48 | &Context{ 49 | Iterations: 3, 50 | Memory: 1 << 5, 51 | Parallelism: 4, 52 | Secret: bytes.Repeat([]byte{3}, 8), 53 | AssociatedData: bytes.Repeat([]byte{4}, 12), 54 | HashLen: 32, 55 | Mode: ModeArgon2d, 56 | Version: Version10, 57 | }, 58 | bytes.Repeat([]byte{1}, 32), 59 | bytes.Repeat([]byte{2}, 16), 60 | "96a9d4e5a1734092c85e29f410a45914a5dd1f5cbf08b2670da68a0285abf32b", 61 | }, 62 | { 63 | &Context{ 64 | Iterations: 3, 65 | Memory: 1 << 5, 66 | Parallelism: 4, 67 | Secret: bytes.Repeat([]byte{3}, 8), 68 | AssociatedData: bytes.Repeat([]byte{4}, 12), 69 | HashLen: 32, 70 | Mode: ModeArgon2d, 71 | Version: Version13, 72 | }, 73 | bytes.Repeat([]byte{1}, 32), 74 | bytes.Repeat([]byte{2}, 16), 75 | "512b391b6f1162975371d30919734294f868e3be3984f3c1a13a4db9fabe4acb", 76 | }, 77 | { 78 | &Context{ 79 | Iterations: 3, 80 | Memory: 1 << 5, 81 | Parallelism: 4, 82 | Secret: bytes.Repeat([]byte{3}, 8), 83 | AssociatedData: bytes.Repeat([]byte{4}, 12), 84 | HashLen: 32, 85 | Mode: ModeArgon2id, 86 | Version: Version10, 87 | }, 88 | bytes.Repeat([]byte{1}, 32), 89 | bytes.Repeat([]byte{2}, 16), 90 | "b64615f07789b66b645b67ee9ed3b377ae350b6bfcbb0fc95141ea8f322613c0", 91 | }, 92 | { 93 | &Context{ 94 | Iterations: 3, 95 | Memory: 1 << 5, 96 | Parallelism: 4, 97 | Secret: bytes.Repeat([]byte{3}, 8), 98 | AssociatedData: bytes.Repeat([]byte{4}, 12), 99 | HashLen: 32, 100 | Mode: ModeArgon2id, 101 | Version: Version13, 102 | }, 103 | bytes.Repeat([]byte{1}, 32), 104 | bytes.Repeat([]byte{2}, 16), 105 | "0d640df58d78766c08c037a34a8b53c9d01ef0452d75b65eb52520e96b01e659", 106 | }, 107 | } 108 | 109 | for i, v := range vectors { 110 | expected, _ := hex.DecodeString(v.hash) 111 | hash, err := Hash(v.ctx, v.password, v.salt) 112 | if err != nil { 113 | t.Errorf("received error: %s (%d)", err, i) 114 | } 115 | if !bytes.Equal(hash, expected) { 116 | t.Errorf("%d: got: %x", i, hash) 117 | t.Errorf("%d: expected: %x", i, expected) 118 | } 119 | } 120 | 121 | } 122 | 123 | func TestHashEncoded(t *testing.T) { 124 | ctx := NewContext(ModeArgon2d) 125 | 126 | password = []byte("somepassword") 127 | salt := []byte("somesalt") 128 | 129 | expected := "$argon2d$v=19$m=4096,t=3,p=1$c29tZXNhbHQ$THaZx86KeqT+xuygENqvxaYIk3zu4wH0UmqzBL/wrdQ" 130 | 131 | s, err := HashEncoded(ctx, password, salt) 132 | if err != nil { 133 | t.Fatal(err) 134 | } 135 | if s != expected { 136 | t.Fatalf("HashEncoded: got %q want %q", s, expected) 137 | } 138 | 139 | ctx.Version = Version10 140 | expected = "$argon2d$v=16$m=4096,t=3,p=1$c29tZXNhbHQ$9zHzndOtdbtKI3zBlrpnnpjNj9FnrkeiK43kb8NuuMc" 141 | 142 | s, err = HashEncoded(ctx, password, salt) 143 | if err != nil { 144 | t.Fatal(err) 145 | } 146 | if s != expected { 147 | t.Fatalf("HashEncoded: got %q want %q", s, expected) 148 | } 149 | } 150 | 151 | func TestHash_Error(t *testing.T) { 152 | ctx := NewContext() 153 | _, err := Hash(ctx, []byte("password"), []byte("s")) 154 | if err != ErrSaltTooShort { 155 | t.Errorf("got %q want %q", err, ErrSaltTooShort) 156 | } 157 | 158 | ctx = NewContext() 159 | ctx.Mode = 99 160 | _, err = Hash(ctx, []byte("password"), []byte("somesalt")) 161 | if err != ErrIncorrectType { 162 | t.Errorf("got %q want %q", err, ErrIncorrectType) 163 | } 164 | 165 | ctx = NewContext() 166 | ctx.Memory = 4 167 | _, err = Hash(ctx, []byte("password"), []byte("somesalt")) 168 | if err != ErrMemoryTooLittle { 169 | t.Errorf("got %q want %q", err, ErrMemoryTooLittle) 170 | } 171 | } 172 | 173 | func TestVerify(t *testing.T) { 174 | ctx := NewContext(ModeArgon2d) 175 | testVerify(t, ctx) 176 | 177 | ctx.Mode = ModeArgon2i 178 | testVerify(t, ctx) 179 | } 180 | 181 | func TestVerifyEncoded(t *testing.T) { 182 | ctx := NewContext(ModeArgon2d) 183 | testVerifyEncoded(t, ctx) 184 | 185 | ctx.Mode = ModeArgon2i 186 | testVerifyEncoded(t, ctx) 187 | } 188 | 189 | func TestFlagClearPassword(t *testing.T) { 190 | ctx := NewContext() 191 | ctx.Flags = FlagDefault 192 | password := []byte("somepassword") 193 | salt := []byte("somesalt") 194 | 195 | Hash(ctx, password, salt) 196 | if !bytes.Equal([]byte("somepassword"), password) { 197 | t.Fatalf("password slice is modified") 198 | } 199 | 200 | ctx.Flags = FlagClearPassword 201 | Hash(ctx, password, salt) 202 | if !bytes.Equal(make([]byte, len(password)), password) { 203 | t.Fatalf("password slice is not cleared") 204 | } 205 | } 206 | 207 | func TestFlagClearSecret(t *testing.T) { 208 | ctx := NewContext() 209 | ctx.Flags = FlagDefault 210 | ctx.Secret = []byte("somesecret") 211 | password := []byte("somepassword") 212 | salt := []byte("somesalt") 213 | 214 | Hash(ctx, password, salt) 215 | if !bytes.Equal([]byte("somesecret"), ctx.Secret) { 216 | t.Fatalf("secret slice is modified") 217 | } 218 | 219 | ctx.Flags = FlagClearSecret 220 | Hash(ctx, password, salt) 221 | if !bytes.Equal(make([]byte, len(ctx.Secret)), ctx.Secret) { 222 | t.Fatalf("secret slice is not cleared") 223 | } 224 | } 225 | 226 | func testVerifyEncoded(t *testing.T, ctx *Context) { 227 | s, err := HashEncoded(ctx, []byte("somepassword"), []byte("somesalt")) 228 | if err != nil { 229 | t.Fatal(err) 230 | } 231 | 232 | pw := []byte("somepassword") 233 | ok, err := VerifyEncoded(s, pw) 234 | if err != nil { 235 | t.Fatal(err) 236 | } 237 | if !ok { 238 | t.Errorf("VerifyEncoded(s, []byte(%q)) = false want true", pw) 239 | } 240 | 241 | pw = []byte("someotherpassword") 242 | ok, err = VerifyEncoded(s, pw) 243 | if err != nil { 244 | t.Fatal(err) 245 | } 246 | if ok { 247 | t.Errorf("VerifyEncoded(s, []byte(%q)) = true want false", pw) 248 | } 249 | } 250 | 251 | func testVerify(t *testing.T, ctx *Context) { 252 | password := []byte("hunter2") 253 | salt := []byte("somesalt") 254 | hash, err := Hash(ctx, password, salt) 255 | if err != nil { 256 | t.Fatal(err) 257 | } 258 | 259 | // Test correct password 260 | ok, err := Verify(ctx, hash, password, salt) 261 | if err != nil { 262 | t.Fatal(err) 263 | } 264 | if !ok { 265 | t.Errorf("Verify(..) = false want true (%v)", ctx) 266 | } 267 | 268 | // Test incorrect password 269 | ok, err = Verify(ctx, hash, []byte("hunter3"), salt) 270 | if err != nil { 271 | t.Fatal(err) 272 | } 273 | if ok { 274 | t.Errorf("Verify(badpw) = true want false (%v)", ctx) 275 | } 276 | 277 | // Test incorrect salt 278 | ok, err = Verify(ctx, hash, password, []byte("somepepper")) 279 | if err != nil { 280 | t.Fatal(err) 281 | } 282 | if ok { 283 | t.Errorf("Verify(badsalt) = true want false (%v)", ctx) 284 | } 285 | } 286 | --------------------------------------------------------------------------------