-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathargon2.nim
130 lines (114 loc) · 3.18 KB
/
argon2.nim
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
from os import DirSep
from strutils import rsplit
# Build Argon2 library
#
# Header files
{.passC: "-I" & currentSourcePath().rsplit(DirSep, 1)[0] & "/phc-winner-argon2/include".}
# Source files
{.compile: "phc-winner-argon2/src/argon2.c".}
{.compile: "phc-winner-argon2/src/core.c".}
{.compile: "phc-winner-argon2/src/encoding.c".}
{.compile: "phc-winner-argon2/src/ref.c".}
{.compile: "phc-winner-argon2/src/thread.c".}
{.compile: "phc-winner-argon2/src/blake2/blake2b.c".}
# C functions
func c_argon2_encodedlen(
iterations: uint32,
memory: uint32,
threads: uint32,
saltlen: uint32,
hashlen: uint32,
argon2type: uint8
): cint {.header: "argon2.h", importc: "argon2_encodedlen".}
func c_argon2_hash(
iterations: uint32,
memory: uint32,
threads: uint32,
pwd: ptr uint8,
pwdlen: uint32,
salt: ptr uint8,
saltlen: uint32,
hash: ptr uint8,
hashlen: uint32,
encoded: ptr char,
encodedlen: uint32,
argon2type: uint8,
version: uint32
): cint {.header: "argon2.h", importc: "argon2_hash".}
# nim functions
#
# return object
type hashes* = object
enc*: string
raw*: seq[byte]
# base function
func argon2*(
argon2type: string,
pwd: string,
salt: string,
iterations: uint32,
memory: uint32,
threads: uint32,
hashlen: uint32
): hashes =
var
localpwd = pwd
localsalt = salt
encstr: string
enclen: cint
a2type: uint8
rawseq = newSeq[byte](hashlen)
case argon2type:
of "d":
a2type = 0
of "i":
a2type = 1
of "id":
a2type = 2
else:
raise newException(Exception, "Invalid Argon2 Type. Valid Types: \"i\", \"d\", \"id\"")
if pwd.len < 1:
raise newException(Exception, "Provided password/data is empty")
if salt.len < 8:
raise newException(Exception, "Provided salt must be at least eight characters")
if iterations < 1:
raise newException(Exception, "Provided iterations must be greater than zero")
if memory < 8:
raise newException(Exception, "Provided memory cost must be at least 8 KiB")
if threads < 1:
raise newException(Exception, "Provided thread count must be at least one")
if hashlen < 4:
raise newException(Exception, "Provided hash length must be at least four")
enclen = c_argon2_encodedlen(iterations, memory, threads, uint32(salt.len), hashlen, a2type)
# for "safety" give capacity reported from encodedlen
encstr = newStringOfCap(enclen)
# discount NULL byte accounted for in encodedlen
encstr.setLen(enclen - 1)
# pass everything off to the library
let ret = c_argon2_hash(
iterations,
memory,
threads,
cast[ptr uint8](addr localpwd[0]),
uint32(pwd.len),
cast[ptr uint8](addr localsalt[0]),
uint32(salt.len),
cast[ptr uint8](addr rawseq[0]),
hashlen,
cast[ptr char](addr encstr[0]),
uint32(enclen),
a2type,
0x13 # Argon2 version
)
if ret == 0:
result.enc = encstr
result.raw = rawseq
else:
raise newException(Exception, "Argon2 library error " & $ret)
# Simplified function
# defaults to using Argon2id, 1 iteration, 4096 KiB memory, 1 thread, 32 byte hash length
func argon2*(
pwd: string,
salt: string,
): hashes =
result = argon2("id", pwd, salt, 1, 4096, 1, 32)