Skip to content

Commit 96cd3cc

Browse files
author
Janis Erdmanis
committed
optimizing and refactoring parser and generator basis
1 parent 8a81924 commit 96cd3cc

File tree

9 files changed

+290
-109
lines changed

9 files changed

+290
-109
lines changed

Project.toml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
name = "SigmaProofs"
22
uuid = "f8559b4c-f045-44a2-8db2-503e40bb7416"
33
authors = ["Janis Erdmanis <[email protected]>"]
4-
version = "0.1.2"
4+
version = "0.2.0"
55

66
[deps]
7+
BitIntegers = "c3b6d118-76ef-56ca-8cc7-ebb389d030a1"
78
CryptoGroups = "bc997328-bedd-407e-bcd3-5758e064a52d"
89
CryptoPRG = "d846c407-34c1-46cb-aa27-d51818cc05e2"
910
CryptoUtils = "04afed74-ac16-11e9-37b6-1352e3e05830"
1011
InteractiveUtils = "b77e0a4c-d291-57a0-90e8-8db25a27a240"
1112
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1213

1314
[compat]
14-
CryptoGroups = "0.5.0"
15-
CryptoPRG = "0.1.1"
15+
BitIntegers = "0.3.2"
16+
CryptoGroups = "0.6"
17+
CryptoPRG = "0.2"
1618
CryptoUtils = "0.1.1"
1719
InteractiveUtils = "1.10"
1820
Random = "1.10"

src/Parser.jl

Lines changed: 159 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ module Parser
22

33
using ...ElGamal: ElGamalRow
44
using CryptoGroups.Curves: a, b, field, gx, gy
5-
using CryptoGroups: PGroup, ECGroup, Group, value, concretize_type, spec, generator, name, ECPoint, modulus, order
5+
using CryptoGroups: PGroup, ECGroup, Group, value, concretize_type, spec, generator, name, ECPoint, modulus, order, octet
66
using CryptoPRG.Verificatum: HashSpec
77
import CryptoGroups.Fields: bitlength # TODO: import -> using
88
using CryptoGroups.Utils: int2octet, @check
@@ -13,16 +13,16 @@ import Base.==
1313
tobig(x) = parse(BigInt, bytes2hex(reverse(x)), base=16)
1414
interpret(::Type{BigInt}, x::Vector{UInt8}) = tobig(reverse(x))
1515

16-
function interpret(::Type{T}, x::Vector{UInt8}) where T <: Integer
17-
16+
function interpret(::Type{T}, x::AbstractVector{UInt8}) where T <: Integer
1817
L = bitlength(T) ÷ 8
19-
y = UInt8[zeros(UInt8, L - length(x))..., x...]
20-
21-
r = reinterpret(T, reverse(y))[1]
22-
return r
18+
result = zero(T)
19+
for i in 1:min(length(x), L)
20+
result |= T(x[length(x)-i+1]) << (8(i-1)) # @inbounds
21+
end
22+
return result
2323
end
2424

25-
function int2bytes(x::Integer)
25+
function interpret(::Type{Vector{UInt8}}, x::BigInt)
2626

2727
@check x > 0
2828

@@ -31,10 +31,9 @@ function int2bytes(x::Integer)
3131
hex = string("0", hex)
3232
end
3333

34-
return reverse(hex2bytes(hex))
34+
return hex2bytes(hex)
3535
end
3636

37-
interpret(::Type{Vector{UInt8}}, x::BigInt) = reverse(int2bytes(x))
3837
interpret(::Type{Vector{UInt8}}, x::Integer) = reverse(reinterpret(UInt8, [x])) # Number of bytes are useful for construction for bytes.
3938

4039
function interpret(::Type{Vector{T}}, 𝐫::Vector{UInt8}, N::Int) where T <: Integer
@@ -72,75 +71,104 @@ Base.push!(n::Node, y) = push!(n.x, y)
7271

7372
toint(x) = reinterpret(UInt32, x[4:-1:1])[1] ### TOREMOVE
7473

75-
function parseb(x)
76-
77-
if x[1] == LEAF
78-
79-
L = interpret(UInt32, x[2:5])
80-
81-
bytes = x[6:5+L]
82-
leaf = Leaf(bytes)
83-
84-
if length(x) == L + 5
85-
rest = []
86-
else
87-
rest = x[L+6:end]
88-
end
89-
90-
return leaf, rest
9174

92-
elseif x[2] == NODE
75+
# Add position tracking to avoid array slicing
76+
mutable struct BinaryParser
77+
data::Vector{UInt8}
78+
pos::Int
79+
end
9380

94-
N = interpret(UInt32, x[2:5])
81+
function read_uint32!(parser::BinaryParser)
82+
val = interpret(UInt32, @view parser.data[parser.pos:parser.pos+3])
83+
parser.pos += 4
84+
return val
85+
end
9586

96-
rest = x[6:end]
87+
function read_bytes!(parser::BinaryParser, len::Integer)
88+
bytes = @view parser.data[parser.pos:parser.pos+len-1]
89+
parser.pos += len
90+
return bytes
91+
end
9792

93+
function parseb!(parser::BinaryParser)
94+
marker = parser.data[parser.pos]
95+
parser.pos += 1
96+
97+
if marker == LEAF
98+
L = read_uint32!(parser)
99+
bytes = read_bytes!(parser, L)
100+
return Leaf(bytes)
101+
elseif marker == NODE
102+
N = read_uint32!(parser)
98103
node = Node()
99-
100-
for i in 1:N
101-
head, tail = parseb(rest)
102-
push!(node, head)
103-
rest = tail
104+
sizehint!(node.x, N) # Pre-allocate space for children
105+
for _ in 1:N
106+
child = parseb!(parser)
107+
push!(node, child)
104108
end
105-
106-
return node, rest
109+
return node
110+
else
111+
throw(ArgumentError("Invalid marker byte: $marker"))
107112
end
108113
end
109114

115+
# Main entry point that maintains the same interface
116+
function parseb(x::Vector{UInt8})
117+
parser = BinaryParser(x, 1)
118+
tree = parseb!(parser)
119+
remaining = if parser.pos <= length(x)
120+
@view x[parser.pos:end]
121+
else
122+
UInt8[]
123+
end
124+
return tree, remaining
125+
end
110126

111127
decode(x::Vector{UInt8}) = parseb(x)[1]
112128
decode(x::AbstractString) = decode(hex2bytes(replace(x, " "=>""))) # I could have optional arguments here as well
113129

114-
115-
function tobin(leaf::Leaf)
116-
117-
N = UInt32(length(leaf.x))
118-
119-
Nbin = interpret(Vector{UInt8}, N)
120-
bin = UInt8[LEAF, Nbin..., leaf.x...]
121-
122-
return bin
130+
# Helper function to write UInt32 in correct byte order
131+
function write_uint32!(io::IO, n::UInt32)
132+
# Write length in little-endian format
133+
bytes = reinterpret(UInt8, [n])
134+
# Reverse bytes to match expected format
135+
write(io, reverse(bytes))
123136
end
124137

125-
function tobin(node::Node)
138+
function encode!(io::IO, leaf::Leaf)
139+
# Write type marker for leaf (01)
140+
write(io, UInt8(0x01))
126141

127-
N = UInt32(length(node.x))
128-
Nbin = interpret(Vector{UInt8}, N)
142+
# Write length of data
143+
write_uint32!(io, UInt32(length(leaf.x)))
129144

130-
data = UInt8[]
145+
# Write data directly
146+
write(io, leaf.x)
147+
end
131148

132-
for n in node.x
133-
b = tobin(n)
134-
append!(data, b)
149+
function encode!(io::IO, node::Node)
150+
# Write type marker for node (00)
151+
write(io, UInt8(0x00))
152+
153+
# Write number of children
154+
write_uint32!(io, UInt32(length(node.x)))
155+
156+
# Write all child nodes directly
157+
for child in node.x
158+
encode!(io, child)
135159
end
160+
end
136161

137-
bin = UInt8[NODE, Nbin..., data...]
162+
# Generic dispatch for Tree type
163+
encode!(io::IO, tree::Tree) = encode!(io, tree)
138164

139-
return bin
165+
# Optional convenience method that returns the encoded bytes
166+
function encode(tree::Tree)
167+
io = IOBuffer()
168+
encode!(io, tree)
169+
take!(io)
140170
end
141171

142-
encode(x::Tree) = tobin(x)
143-
144172
convert(::Type{T}, x::Leaf) where T <: Integer = interpret(T, x.x)
145173

146174
function convert(::Type{String}, x::Leaf)
@@ -168,25 +196,36 @@ function Leaf(x::Signed)
168196
end
169197

170198
Leaf(x::Unsigned) = Leaf(interpret(Vector{UInt8}, x))
171-
172199

173-
function Leaf(x::Integer, k::Integer)
174-
200+
function interpret!(result::Vector{UInt8}, k::Integer, x::Integer)
175201
if x == 0
176-
177-
return Leaf(zeros(UInt8, k))
178-
179-
else
180-
leaf = Leaf(x)
181-
182-
N = findfirst(x -> x != UInt8(0), leaf.x)
183-
bytes = leaf.x[N:end]
184-
pad = k - length(bytes)
185-
186-
return newleaf = Leaf(UInt8[zeros(UInt8, pad)...,bytes...])
202+
fill!(result, 0x00)
203+
return 0
204+
end
205+
206+
hex = string(x, base=16)
207+
bytes_needed = (length(hex) + 1) ÷ 2
208+
209+
# Fill padding zeros if needed
210+
if k > bytes_needed
211+
fill!(view(result, 1:k-bytes_needed), 0x00)
212+
end
213+
214+
# Ensure even length for hex string
215+
if mod(length(hex), 2) != 0
216+
hex = string("0", hex)
187217
end
218+
219+
hex2bytes!(view(result, k-bytes_needed+1:k), hex)
220+
221+
return bytes_needed
188222
end
189223

224+
function Leaf(x::Integer, k::Integer)
225+
result = Vector{UInt8}(undef, k)
226+
interpret!(result, k, x)
227+
return Leaf(result)
228+
end
190229

191230
function Leaf(x::AbstractString)
192231
bytes = Vector{UInt8}(x)
@@ -213,10 +252,8 @@ function Node(x::Tuple; L=nothing)
213252
return node
214253
end
215254

216-
217255
############################ COMPOSITE TYPE PARSING ############################
218256

219-
220257
function convert(::Type{Vector{G}}, x::Node; allow_one=false) where G <: Group
221258
return G[convert(G, i; allow_one) for i in x.x]
222259
end
@@ -236,19 +273,62 @@ Tree(x::PGroup; L = bitlength(x)) = Leaf(value(x), div(L + 1, 8, RoundUp))
236273
# Probably I will need to replace
237274
convert(::Type{G}, x::Leaf; allow_one=false) where G <: PGroup = convert(G, convert(BigInt, x); allow_one)
238275

239-
### Note that only PrimeCurves are supported.
240-
convert(::Type{G}, x::Node; allow_one=false) where G <: ECGroup = convert(G, convert(Tuple{BigInt, BigInt}, x); allow_one)
241-
convert(::Type{ECGroup{P}}, x::Node; allow_one=false) where P <: ECPoint = convert(ECGroup{P}, convert(Tuple{BigInt, BigInt}, x); allow_one)
276+
# Perhaps I could also use a let here
277+
# It could be made threaad local
278+
const EC_BUFFER = Vector{UInt8}(undef, 1000) # We can make it also thread local
279+
function convert(::Type{ECGroup{P}}, x::Node; allow_one=false) where P <: ECPoint
280+
281+
L = bitlength(field(P))
282+
nbytes_field = cld(L, 8)
283+
284+
x_bytes = @view x[1].x[end-(nbytes_field-1):end]
285+
y_bytes = @view x[2].x[end-(nbytes_field-1):end]
286+
287+
if all(iszero, x_bytes) && all(iszero, y_bytes)
288+
point_bytes = UInt8[0x00]
289+
else
290+
#buffer = Vector{UInt8}(undef, 1 + 2*nbytes_field)
291+
EC_BUFFER[1] = 0x04
292+
@inbounds copyto!(@view(EC_BUFFER[2:nbytes_field+1]), x_bytes)
293+
@inbounds copyto!(@view(EC_BUFFER[nbytes_field+2:2nbytes_field+1]), y_bytes)
294+
295+
point_bytes = @view EC_BUFFER[1:2nbytes_field+1]
296+
#point_bytes = UInt8[0x04; x_bytes; y_bytes]
297+
end
242298

299+
return convert(ECGroup{P}, point_bytes; allow_one)
300+
end
243301

244302
function Tree(g::G; L = bitlength(G)) where G <: ECGroup
303+
304+
nbytes_field = cld(L, 8) # Using cld instead of div(x, y, RoundUp)
305+
nbytes_spec = cld(L + 1, 8)
245306

246-
gxleaf = Leaf(value(gx(g)), div(L + 1, 8, RoundUp))
247-
gyleaf = Leaf(value(gy(g)), div(L + 1, 8, RoundUp))
307+
bytes = octet(g)
308+
309+
if iszero(bytes[1])
310+
311+
leaf = Leaf(zeros(UInt8, nbytes_spec))
312+
return Tree((leaf, leaf))
313+
314+
end
315+
316+
@views x_bytes = bytes[2:nbytes_field + 1]
317+
@views y_bytes = bytes[nbytes_field + 2:end]
248318

249-
gtree = Tree((gxleaf, gyleaf))
319+
if nbytes_spec == nbytes_field
250320

251-
return gtree
321+
gxleaf = Leaf(x_bytes)
322+
gyleaf = Leaf(y_bytes)
323+
324+
elseif nbytes_spec == nbytes_field + 1 # This is such an unfortunate oversight in the specification
325+
326+
gxleaf = Leaf(UInt8[0x00; x_bytes])
327+
gyleaf = Leaf(UInt8[0x00; y_bytes])
328+
329+
end
330+
331+
return Tree((gxleaf, gyleaf))
252332
end
253333

254334
function Tree(x::Vector{<:Group})
@@ -363,9 +443,6 @@ end
363443

364444
function convert(::Type{Vector{ElGamalRow{G, N}}}, tree::Node; allow_one=false) where {G <: Group, N}
365445

366-
#𝐚 = convert(NTuple{N, Vector{G}}, tree[1]; allow_one)
367-
#𝐛 = convert(NTuple{N, Vector{G}}, tree[2]; allow_one)
368-
369446
𝐚 = ntuple(n -> convert(Vector{G}, tree[1][n]; allow_one), N)
370447
𝐛 = ntuple(n -> convert(Vector{G}, tree[2][n]; allow_one), N)
371448

@@ -384,14 +461,11 @@ function convert(::Type{ElGamalRow{G, 1}}, tree::Node; allow_one=false) where G
384461
return ElGamalRow(a, b)
385462
end
386463

387-
388464
convert(::Type{NTuple{N, G}}, tree::Node; allow_one=false) where {G <: Group, N} = ntuple(n -> convert(G, tree[n]; allow_one), N)
389465

390-
391466
convert(::Type{NTuple{N, BigInt}}, tree::Node) where N = ntuple(n -> convert(BigInt, tree[n]), N)
392467
convert(::Type{NTuple{1, BigInt}}, tree::Leaf) = tuple(convert(BigInt, tree))
393468

394-
395469
function convert(::Type{ElGamalRow{G, N}}, tree::Node; allow_one=false) where {G <: Group, N}
396470

397471
a_tree, b_tree = tree.x

0 commit comments

Comments
 (0)