Skip to content

Commit

Permalink
add HMAC functionalities (JuliaLang#45)
Browse files Browse the repository at this point in the history
  • Loading branch information
ylxdzsw authored and staticfloat committed Nov 8, 2017
1 parent 79bcc3f commit 43b2bad
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/SHA.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ export sha3_224, sha3_256, sha3_384, sha3_512
export SHA224_CTX, SHA256_CTX, SHA384_CTX, SHA512_CTX
export SHA2_224_CTX, SHA2_256_CTX, SHA2_384_CTX, SHA2_512_CTX
export SHA3_224_CTX, SHA3_256_CTX, SHA3_384_CTX, SHA3_512_CTX
export HMAC_CTX, hmac_sha1
export hmac_sha224, hmac_sha256, hmac_sha384, hmac_sha512
export hmac_sha2_224, hmac_sha2_256, hmac_sha2_384, hmac_sha2_512
export hmac_sha3_224, hmac_sha3_256, hmac_sha3_384, hmac_sha3_512


include("constants.jl")
Expand All @@ -19,6 +23,7 @@ include("sha1.jl")
include("sha2.jl")
include("sha3.jl")
include("common.jl")
include("hmac.jl")

# Create data types and convenience functions for each hash implemented
for (f, ctx) in [(:sha1, :SHA1_CTX),
Expand All @@ -34,16 +39,24 @@ for (f, ctx) in [(:sha1, :SHA1_CTX),
(:sha3_256, :SHA3_256_CTX),
(:sha3_384, :SHA3_384_CTX),
(:sha3_512, :SHA3_512_CTX),]
g = Symbol(:hmac_, f)

@eval begin
# Our basic function is to process arrays of bytes
function $f(data::T) where T<:Union{Array{UInt8,1},NTuple{N,UInt8} where N}
ctx = $ctx()
update!(ctx, data)
return digest!(ctx)
end
function $g(key::Vector{UInt8}, data::T) where T<:Union{Array{UInt8,1},NTuple{N,UInt8} where N}
ctx = HMAC_CTX($ctx(), key)
update!(ctx, data)
return digest!(ctx)
end

# AbstractStrings are a pretty handy thing to be able to crunch through
$f(str::AbstractString) = $f(Vector{UInt8}(str))
$g(key::Vector{UInt8}, str::AbstractString) = $g(key, Vector{UInt8}(str))

# Convenience function for IO devices, allows for things like:
# open("test.txt") do f
Expand All @@ -58,6 +71,15 @@ for (f, ctx) in [(:sha1, :SHA1_CTX),
end
return digest!(ctx)
end
function $g(key::Vector{UInt8}, io::IO, chunk_size=4*1024)
ctx = HMAC_CTX($ctx(), key)
buff = Vector{UInt8}(chunk_size)
while !eof(io)
num_read = readbytes!(io, buff)
update!(ctx, buff[1:num_read])
end
return digest!(ctx)
end
end
end

Expand Down
33 changes: 33 additions & 0 deletions src/hmac.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
struct HMAC_CTX{CTX<:SHA_CTX}
context::CTX
outer::Vector{UInt8}

function HMAC_CTX(ctx::CTX, key::Vector{UInt8}, blocksize::Integer=blocklen(CTX)) where CTX
if length(key) > blocksize
_ctx = CTX()
update!(_ctx, key)
key = digest!(_ctx)
end

pad = blocksize - length(key)

if pad > 0
key = [key; fill(0x00, pad)]
end

update!(ctx, key .⊻ 0x36)
new{CTX}(ctx, key .⊻ 0x5c)
end
end

function update!(ctx::HMAC_CTX, data)
update!(ctx.context, data)
end

function digest!(ctx::HMAC_CTX{CTX}) where CTX
digest = digest!(ctx.context)
_ctx = CTX()
update!(_ctx, ctx.outer)
update!(_ctx, digest)
digest!(_ctx)
end
26 changes: 26 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,32 @@ for sha_idx in 1:length(sha_funcs)
end
println("Done! [$(nerrors - nerrors_old) errors]")

# test hmac correctness using the examples on [wiki](https://en.wikipedia.org/wiki/Hash-based_message_authentication_code#Examples)
print("Testing on the hmac functions")
nerrors_old = nerrors
for (key, msg, fun, hash) in (
(b"", b"", hmac_sha1, "fbdb1d1b18aa6c08324b7d64b71fb76370690e1d"),
(b"", b"", hmac_sha256, "b613679a0814d9ec772f95d778c35fc5ff1697c493715653c6c712144292c5ad"),
(b"key", b"The quick brown fox jumps over the lazy dog", hmac_sha1, "de7c9b85b8b78aa6bc8a7a36f70a90701c9db4d9"),
(b"key", b"The quick brown fox jumps over the lazy dog", hmac_sha256, "f7bc83f430538424b13298e6aa6fb143ef4d59a14946175997479dbc2d1a3cd8"),
)
digest = bytes2hex(fun(key, msg))
if digest != hash
print("\n")
warn(
"""
For $fun($(String(key)), $(String(msg))) expected:
$hash
Calculated:
$digest
""")
nerrors += 1
else
print(".")
end
end
println("Done! [$(nerrors - nerrors_old) errors]")

if VERSION >= v"0.7.0-DEV.1472"
replstr(x) = sprint((io, x) -> show(IOContext(io, :limit => true), MIME("text/plain"), x), x)
else
Expand Down

0 comments on commit 43b2bad

Please sign in to comment.