-
Notifications
You must be signed in to change notification settings - Fork 35
/
Copy pathshake.jl
133 lines (127 loc) · 4.24 KB
/
shake.jl
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
131
132
133
abstract type SHAKE <: SHA3_CTX end
# note, that field property used has differend uses, depending on T<:SHAKE or T<:SHA3_CTX
mutable struct SHAKE_128_CTX <: SHAKE
state::Vector{UInt64}
bytecount::UInt128
buffer::Vector{UInt8}
bc::Vector{UInt64}
used::Bool
end
mutable struct SHAKE_256_CTX <: SHAKE
state::Vector{UInt64}
bytecount::UInt128
buffer::Vector{UInt8}
bc::Vector{UInt64}
used::Bool
end
digestlen(::Type{SHAKE_128_CTX}) = 16
digestlen(::Type{SHAKE_256_CTX}) = 32
blocklen(::Type{SHAKE_128_CTX}) = UInt64(25*8 - 2*digestlen(SHAKE_128_CTX))
blocklen(::Type{SHAKE_256_CTX}) = UInt64(25*8 - 2*digestlen(SHAKE_256_CTX))
buffer_pointer(ctx::T) where {T<:SHAKE} = Ptr{state_type(T)}(pointer(ctx.buffer))
# construct an empty SHA context
SHAKE_128_CTX() = SHAKE_128_CTX(zeros(UInt64, 25), 0, zeros(UInt8, blocklen(SHAKE_128_CTX)), Vector{UInt64}(undef, 5), false)
SHAKE_256_CTX() = SHAKE_256_CTX(zeros(UInt64, 25), 0, zeros(UInt8, blocklen(SHAKE_256_CTX)), Vector{UInt64}(undef, 5), false)
function transform!(context::T) where {T<:SHAKE}
# First, update state with buffer
pbuf = Ptr{eltype(context.state)}(pointer(context.buffer))
# after SHAKE_256_MAX_READ (digestlen) is reached, simply work with context.state[idx]
if !context.used
for idx in 1:div(blocklen(T),8)
context.state[idx] = context.state[idx] ⊻ unsafe_load(pbuf, idx)
end
end
bc = context.bc
state = context.state
# We always assume 24 rounds
@inbounds for round in 0:23
# Theta function
for i in 1:5
bc[i] = state[i] ⊻ state[i + 5] ⊻ state[i + 10] ⊻ state[i + 15] ⊻ state[i + 20]
end
for i in 0:4
temp = bc[rem(i + 4, 5) + 1] ⊻ L64(1, bc[rem(i + 1, 5) + 1])
for j in 0:5:20
state[Int(i + j + 1)] = state[i + j + 1] ⊻ temp
end
end
# Rho Pi
temp = state[2]
for i in 1:24
j = SHA3_PILN[i]
bc[1] = state[j]
state[j] = L64(SHA3_ROTC[i], temp)
temp = bc[1]
end
# Chi
for j in 0:5:20
for i in 1:5
bc[i] = state[i + j]
end
for i in 0:4
state[j + i + 1] = state[j + i + 1] ⊻ (~bc[rem(i + 1, 5) + 1] & bc[rem(i + 2, 5) + 1])
end
end
# Iota
state[1] = state[1] ⊻ SHA3_ROUND_CONSTS[round+1]
end
return context.state
end
function digest!(context::T,d::UInt,p::Ptr{UInt8}) where {T<:SHAKE}
usedspace = context.bytecount % blocklen(T)
# If we have anything in the buffer still, pad and transform that data
if usedspace < blocklen(T) - 1
# Begin padding with a 0x1f
context.buffer[usedspace+1] = 0x1f
# Fill with zeros up until the last byte
context.buffer[usedspace+2:end-1] .= 0x00
# Finish it off with a 0x80
context.buffer[end] = 0x80
else
# Otherwise, we have to add on a whole new buffer
context.buffer[end] = 0x9f
end
# Final transform:
transform!(context)
# Return the digest:
# fill the given memory via pointer, if d>blocklen, update pointer and digest again.
if d <= blocklen(T)
for i = 1:d
unsafe_store!(p,reinterpret(UInt8, context.state)[i],i)
end
return
else
for i = 1:blocklen(T)
unsafe_store!(p,reinterpret(UInt8, context.state)[i],i)
end
context.used = true
p+=blocklen(T)
next_d_len = UInt(d - blocklen(T))
digest!(context, next_d_len, p)
return
end
end
"""
shake128(data::AbstractBytes,d::UInt)
Hash data using the `shake128` algorithm and return the first d resulting bytes.
"""
function shake128(data::AbstractBytes,d::UInt)
ctx = SHAKE_128_CTX()
update!(ctx, data)
M = Vector{UInt8}(undef,d) # prealloc
p = pointer(M)
digest!(ctx,d,p)
return M
end
"""
shake256(data::AbstractBytes,d::UInt)
Hash data using the `shake258` algorithm and return the first d resulting bytes.
"""
function shake256(data::AbstractBytes,d::UInt)
ctx = SHAKE_256_CTX()
update!(ctx, data)
M = Vector{UInt8}(undef,d) # prealloc
p = pointer(M)
digest!(ctx,d,p)
return M
end