1package argon2
2
3// #cgo pkg-config: libargon2
4// #include <argon2.h>
5// #include "wrapper.h"
6import "C"
7
8// Context represents a structure that holds all static configuration values,
9// used to parameterize an Argon2 hash function.
10type 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
24func 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
40func (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