# Hex2bytes: length of iterable must be even

Hi there, it seems hex2bytes is really not safe, but depending on the concrete number, it will throw an error. Workaround: using `"0" * string(65537, base=16)`, but this looks really weird.

Can someone explain why such a basic function does not work as described in its own documentation?

``````julia> hex2bytes(string(65537, base=16))
ERROR: ArgumentError: length of iterable must be even
Stacktrace:
[1] hex2bytes!(dest::Vector{UInt8}, itr::Base.CodeUnits{UInt8, String})
@ Base ./strings/util.jl:847
[2] hex2bytes!
@ ./strings/util.jl:831 [inlined]
[3] hex2bytes(s::String)
@ Base ./strings/util.jl:825
[4] top-level scope
@ REPL[57]:1
``````

I guess at least, the documentation should be updated, it currently reads like above

``````julia> s = string(12345, base = 16)
"3039"

julia> hex2bytes(s)
2-element Vector{UInt8}:
0x30
0x39
``````

what do you think?

``````string(n,base=b, pad=Int(ceil(log(b,n)))+(1-iseven(Int(ceil(log(b,n)))))) # :innocent:
``````
1 Like

This behavior corresponds precisely to its documentation, which states:

each successive pair of hexadecimal digits in `itr` gives the value of one byte in the return vector.

(emphasis added). i.e. it is documented on working on pairs of hexadecimal digits, since this is the canonical way to represent bytes as hex.

If you are trying to parse arbitrary hexadecimal numbers that arenâ€™t representations of byte sequences (i.e. which donâ€™t consist of paired hex digits), then youâ€™re doing something different from what `hex2bytes` was intended for. What is your application?

I agree that the example in the docs with `string(n, base=16)` is misleading, however.

3 Likes

Thank you for the help!

I am trying to get a number to hex bytes and run into this error because I followed the example in the documentation.

this is very impressive thank you so much

given that cryptic code, I only feel slightly safer, but I feel at least a little safer

Use `digits`:

``````julia> digits(65537, base=256)
3-element Vector{Int64}:
1
0
1

julia> digits(12345, base=256)
2-element Vector{Int64}:
57
48
``````

Youâ€™ll have to manually convert to bytes though.

EDIT: 256 instead of 16, of course

1 Like

Do you just want something like `digits(UInt8, n, base=256)` or perhaps `num2bytes(n) = reinterpret(UInt8, [n])`? Or if you just want particular bytes you can use shift and mod operations on `n`.

In general, I would avoid the intermediate step of converting the number to a string and then parsing the string.

1 Like
``````julia> hex2bytes(lpad((s=string(65537, base=16);s),(length(s)+1)Ă·2*2,'0'))
3-element Vector{UInt8}:
0x01
0x00
0x01
``````

But it is still awkward.

thank you both,
everything is indeed clarified already - just the example in the documentation should get an update

my background: I am using JWTs to create authentication pipeline and there the exponent of the private key needs to be given in `hex2bytes(int2hexstr(my_exponent))`, because JWTs expects this

I am now running with

``````function int2hexstr(n)
str = string(n, base=16)
iseven(length(str)) ? str : "0" * str
end
``````

Just donâ€™t create unnecessary intermediate strings:

``````julia> UInt8.(digits(65537, base=256))
3-element Vector{UInt8}:
0x01
0x00
0x01
``````

But in general I agree with @stevengj here - shifting and building it that way is likely better, depending on the ultimate usecase of this.

1 Like

Have you considered using JWT.jl?

Padding unknown (or heck, even known) incoming data in a cryptographic context â€śto make the algorithm workâ€ť is NOT a good idea. Generally, neither is implementing a seemingly simple padding operation - you can introduce subtle bugs that leave your code vulnerable.

2 Likes

just now I understood that this actually does the same, so yeah, no need to go via a hexstr

yes I am using that package.

There is no API for creating the modulus and exponent, so I use openssl and need to do the conversions myself. I guess that is life

If you donâ€™t mind me asking, but how are you generating the input in the first place? Going via regular `Int64` or similar things seems much more of a hassle instead of directly generating an array of bytes to pass to e.g. openssl.

for JWTs keys need to be given under yourdomain/jwks in a json format which includes modulus and exponend base64 encoded with some extra url handling.

While there is an easy way to extract the modulus from a private key using openssl, I couldnâ€™t find anything like that for the exponent, hence I am just assuming the default exponent will be used.

So here the answer: I am indeed using plain Int64 value as the source for my exponent because this is the best readable so that I or others can later check that this is really the default exponent.

final comment from my side

(EDIT: for further tips and tricks please ping me per private message or open a new discourse thread, so that this one is not further stacked up)