There's a rather unusual encryption algorithm I'm trying to port to Nim from C, but it uses pointer arithmetic I'm unsure if Nim supports or not (yet?):
C implementation: https://github.com/mach-kernel/KDX-Maya/blob/master/DecKDX/kdxalgo.h
My erroneous Nim rewrite:
proc kdxCrypt(key: var uint32, data: var string): uint32 =
let data32 = cast[ptr uint32](addr data[0])
let dlen = uint32(data.len() shr 2)
for i in countup(0, dlen-1):
key = (key shl 1'u32) + 0x4878'u32
data32[i] = data32[i] xor uint32(int(key).ntohl())
return key
when isMainModule:
var s = "hello people of the soil"
var k = uint32(cast[ptr int](addr s[0])[].ntohl())
var s2: s.substr(4)
echo $k
k = kdxCrypt(k, s2)
echo $k
echo s2
I would have used signed int32 as recommended, but that "+ 0x4878" part looks like an overflow might occur if so. From what I see, the variable 'data32' reinterprets a byte sequence as a sequence of uint32s and adjusts the length accordingly by bitshifting it by 2. That is where Nim chokes. I've tried castuint32 but apparently Nim pointers lack the array functionality.
An idea I had would be to make a seq[uint32] and cast every 4 bytes from 'data' into each entry. However, that involves a deep copy and I don't believe it would be efficient for large buffers.
Untested, just simplified a bit and made it compile:
proc ntohl*(a1: uint32): uint32 {.importc, header: "<arpa/inet.h>".}
proc kdxCrypt(key: uint32, data: var string): uint32 =
let data32 = cast[ptr array[0, uint32]](addr data[0])
let dlen = data.len shr 2
result = key
for i in 0..<dlen:
result = (result shl 1) + 0x4878
data32[i] = data32[i] xor result.ntohl
when isMainModule:
var s = "hello people of the soil"
var k = 12345'u32
var s2 = s.substr(4)
echo k
k = kdxCrypt(k, s2)
echo k
echo s2
It would be cleaner to make a copy of data into an actual seq[uint32]let data32 = cast[ptr array[0, uint32]](addr data[0])
data32[i]
I don't understand why this works, shouldn't there be boundary checks for the empty array at runtime?