Skip to content

Commit 00acb17

Browse files
committed
Merge branch '19-add-tiny-hash-function-for-small-prime-fields'
2 parents 8955f81 + 115f1b4 commit 00acb17

4 files changed

Lines changed: 130 additions & 0 deletions

File tree

src/TinyCrypto.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,20 @@ module TinyCrypto
33
include("macroes.jl")
44
include("utils.jl")
55
include("field.jl")
6+
include("core/hash.jl")
67

78
using .Macroes
89
using .Field
910
using .Utils
11+
using .Hash
1012

1113
include("core/ecpoint.jl")
1214
include("curves/weierstrass.jl")
1315
include("curves/montgomery.jl")
1416
include("curves/edwards.jl")
1517
# Re-export core symbols for public API
1618
export @define, @attach
19+
export H, H₈, H₁₆, H8, H16
1720
export Fp, 𝔽ₚ
1821
export is_prime, primes, mod_inverse
1922
export Point, ∞, Curve, ECPoint, Infinity

src/core/hash.jl

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
module Hash
2+
export H, H₈,H₁₆, H8, H16
3+
4+
const INIT8 = UInt8[0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0]
5+
const K8 = UInt8[0x1f, 0x2b, 0x3c, 0x4d, 0x5e, 0x6a, 0x7d, 0x8e]
6+
7+
rotate(x::UInt8, n::Int) = ((x >> n) | (x << (8 - n))) % UInt8
8+
rotate(x::UInt16, n::Int) = ((x >> n) | (x << (16 - n))) % UInt16
9+
10+
function pad8(msg::Vector{UInt8})
11+
len = length(msg)
12+
padded = copy(msg)
13+
push!(padded, 0x80)
14+
while (length(padded) % 8) != 7
15+
push!(padded, 0x00)
16+
end
17+
push!(padded, UInt8(len % 256))
18+
return padded
19+
end
20+
21+
function compress_block8(block::Vector{UInt8}, state::Vector{UInt8})
22+
W = copy(block)
23+
for i in 1:8
24+
W[i] ⊻= K8[i]
25+
W[i] = rotate(W[i], i % 8 + 1)
26+
end
27+
for i in 1:8
28+
state[i] = rotate(state[i] W[i], i)
29+
end
30+
end
31+
32+
function tiny_hash8(msg::Vector{UInt8})
33+
padded = pad8(msg)
34+
state = copy(INIT8)
35+
for i in 1:8:length(padded)
36+
block = padded[i:i+7]
37+
compress_block8(block, state)
38+
end
39+
result = state[1]
40+
for i in 2:8
41+
result ⊻= rotate(state[i], i)
42+
end
43+
return result
44+
end
45+
tiny_hash8(msg::String) = tiny_hash8(Vector{UInt8}(codeunits(msg)))
46+
47+
function tiny_hash16(msg::Vector{UInt8})
48+
acc = UInt16(0xabcd)
49+
for (i, byte) in enumerate(msg)
50+
acc ⊻= UInt16(byte) << (i % 8)
51+
acc = (acc << 5) (acc >> 3)
52+
end
53+
return acc
54+
end
55+
tiny_hash16(msg::String) = tiny_hash16(Vector{UInt8}(codeunits(msg)))
56+
57+
function H8(msg::Union{String, Vector{UInt8}})
58+
bytes = msg isa String ? Vector{UInt8}(codeunits(msg)) : msg
59+
return tiny_hash8(bytes)
60+
end
61+
62+
function H16(msg::Union{String, Vector{UInt8}})
63+
bytes = msg isa String ? Vector{UInt8}(codeunits(msg)) : msg
64+
return tiny_hash16(bytes)
65+
end
66+
67+
H(msg::Union{String, Vector{UInt8}}) = H8(msg)
68+
H₈(msg::Union{String, Vector{UInt8}}) = H8(msg)
69+
H₁₆(msg::Union{String, Vector{UInt8}}) = H16(msg)
70+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ using Test
22
using TinyCrypto
33

44
@testset "Curve tests" begin
5+
include("test_hash.jl")
56
include("test_field.jl")
67
include("test_montgomery.jl")
78
include("test_edwards.jl")

test/test_hash.jl

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
using Test
2+
using TinyCrypto: H, H8, H16, H₈, H₁₆
3+
4+
@testset "Hash - TinyHash API" begin
5+
6+
@testset "Determinism" begin
7+
@test H("msg") == H("msg")
8+
@test H8("abc") == H8("abc")
9+
@test H16("abc") == H16("abc")
10+
end
11+
12+
@testset "Types & Output Range" begin
13+
@test H8("") isa UInt8
14+
@test H16("") isa UInt16
15+
@test 0x00 <= H8("abc") <= 0xff
16+
@test 0x0000 <= H16("abc") <= 0xffff
17+
end
18+
19+
@testset "Unicode and Multibyte Strings" begin
20+
@test H("𝒞rypto") isa UInt8
21+
@test H16("éçüΩ") != H16("ecuo")
22+
end
23+
24+
@testset "Empty input" begin
25+
@test H8("") == H(collect(UInt8, ""))
26+
@test H16("") == H16(UInt8[])
27+
end
28+
29+
@testset "Single and Repeated Characters" begin
30+
@test H8("a") == H("a")
31+
@test H8("aaaaaaaa") != H8("aaaaaaaaa") # should trigger padding into another block
32+
@test H16("z"^1000) isa UInt16
33+
end
34+
35+
@testset "Vectors of UInt8" begin
36+
@test H8(UInt8[0x61, 0x62, 0x63]) == H8("abc")
37+
@test H16(UInt8[0x61, 0x62, 0x63]) == H16("abc")
38+
end
39+
40+
@testset "Aliases" begin
41+
str = "hello world"
42+
@test H(str) == H8(str)
43+
@test H₈(str) == H8(str)
44+
@test H₁₆(str) == H16(str)
45+
end
46+
47+
@testset "Corner padding boundaries" begin
48+
# Pad to block sizes: exact 7, 8, 14, 15, etc.
49+
for n in (0, 1, 6, 7, 8, 13, 14, 15, 16, 100)
50+
msg = "a"^n
51+
@test H8(msg) isa UInt8
52+
@test H16(msg) isa UInt16
53+
end
54+
end
55+
56+
end

0 commit comments

Comments
 (0)