implement xchacha20 and x25519

This commit is contained in:
2026-01-03 13:49:15 +01:00
parent 9a60518e0e
commit abbe43a42c
7 changed files with 365 additions and 11 deletions

View File

@@ -13,7 +13,7 @@ A very cool language
## Syntax
```rust
func main[] : i64
let answer: i64 = math.abs(os.urandom()) % 100
let answer: i64 = math.abs(os.urandom_i64()) % 100
while true
io.println("Guess a number: ")

View File

@@ -1,5 +1,5 @@
func main[] : i64
let answer: i64 = math.abs(os.urandom()) % 100
let answer: i64 = math.abs(os.urandom_i64()) % 100
while true
io.println("Guess a number: ")

View File

@@ -1,7 +1,7 @@
func main[] : i64
let arr: array = []
for i in 0..10
array.push(arr, math.abs(os.urandom() % 1000))
array.push(arr, math.abs(os.urandom_i64()) % 1000)
for i in 0..array.size(arr)
io.println_i64(array.nth(arr, i))

205
examples/x25519.zr Normal file
View File

@@ -0,0 +1,205 @@
func unpack25519[out: ptr, input: ptr] : void
for i in 0..16
mem.write64(out + i * 8, input[i * 2] + (input[i * 2 + 1] << 8))
mem.write64(out + 8 * 15, mem.read64(out + 8 * 15) & 0x7fff)
func carry25519[elem: ptr] : void
for i in 0..16
let carry: i64 = mem.read64(elem + i * 8) >> 16
mem.write64(elem + i * 8, mem.read64(elem + i * 8) - (carry << 16))
if i < 15
mem.write64(elem + (i + 1) * 8, mem.read64(elem + (i + 1) * 8) + carry)
else
mem.write64(elem, mem.read64(elem) + 38 * carry)
func fadd[out: ptr, a: ptr, b: ptr] : void
for i in 0..16
mem.write64(out + i * 8, mem.read64(a + i * 8) + mem.read64(b + i * 8))
func fsub[out: ptr, a: ptr, b: ptr] : void
for i in 0..16
mem.write64(out + i * 8, mem.read64(a + i * 8) - mem.read64(b + i * 8))
func fmul[out: ptr, a: ptr, b: ptr] : void
let product: ptr = mem.alloc(31 * 8)
for i in 0..31
mem.write64(product + i * 8, 0)
for i in 0..16
for j in 0..16
mem.write64(product + (i + j) * 8, mem.read64(product + (i + j) * 8) + (mem.read64(a + i * 8) * mem.read64(b + j * 8)))
for i in 0..15
mem.write64(product + i * 8, mem.read64(product + i * 8) + 38 * mem.read64(product + (i + 16) * 8))
for i in 0..16
mem.write64(out + i * 8, mem.read64(product + i * 8))
carry25519(out)
carry25519(out)
mem.free(product)
func finverse[out: ptr, input: ptr] : void
let c: ptr = mem.alloc(16 * 8)
for i in 0..16
mem.write64(c + i * 8, mem.read64(input + i * 8))
let i = 253
while i >= 0
fmul(c, c, c)
if i != 2 && i != 4
fmul(c, c, input)
i = i - 1
for i in 0..16
mem.write64(out + i * 8, mem.read64(c + i * 8))
mem.free(c)
func swap25519[p: ptr, q: ptr, bit: i64] : void
for i in 0..16
let t: i64 = (-bit) & (mem.read64(p + i * 8) ^ mem.read64(q + i * 8))
mem.write64(p + i * 8, mem.read64(p + i * 8) ^ t)
mem.write64(q + i * 8, mem.read64(q + i * 8) ^ t)
func pack25519[out: ptr, input: ptr] : void
let t: ptr = mem.alloc(16 * 8)
for i in 0..16
mem.write64(t + i * 8, mem.read64(input + i * 8))
let m: ptr = mem.alloc(16 * 8)
carry25519(t)
carry25519(t)
carry25519(t)
for j in 0..2
mem.write64(m, mem.read64(t) - 0xffed)
for i in 1..15
mem.write64(m + i * 8, mem.read64(t + i * 8) - 0xffff - ((mem.read64(m + (i - 1) * 8) >> 16) & 1))
mem.write64(m + (i - 1) * 8, mem.read64(m + (i - 1) * 8) & 0xffff)
mem.write64(m + 15 * 8, mem.read64(t + 15 * 8) - 0x7fff - ((mem.read64(m + 14 * 8) >> 16) & 1))
let carry: i64 = (mem.read64(m + 15 * 8) >> 16) & 1
mem.write64(m + 14 * 8, mem.read64(m + 14 * 8) & 0xffff)
swap25519(t, m, 1 - carry)
for i in 0..16
let v: i64 = mem.read64(t + i * 8)
mem.write8(out + i * 2, v & 0xff)
mem.write8(out + i * 2 + 1, (v >> 8) & 0xff)
mem.free(t)
mem.free(m)
func scalarmult[out: ptr, scalar: ptr, point: ptr] : void
let clamped: ptr = mem.alloc(32)
let a: ptr = mem.alloc(16 * 8)
let b: ptr = mem.alloc(16 * 8)
let c: ptr = mem.alloc(16 * 8)
let d: ptr = mem.alloc(16 * 8)
let e: ptr = mem.alloc(16 * 8)
let f: ptr = mem.alloc(16 * 8)
let x: ptr = mem.alloc(16 * 8)
let magic: ptr = mem.alloc(16 * 8)
mem.zero(magic, 16 * 8)
mem.write64(magic, 0xdb41) // 121665
mem.write64(magic + 8, 1)
// copy and clamp scalar
for i in 0..32
mem.write8(clamped + i, scalar[i])
mem.write8(clamped, clamped[0] & 0xf8)
mem.write8(clamped + 31, (clamped[31] & 0x7f) | 0x40)
// load point
unpack25519(x, point)
// initialize ladder state
for i in 0..16
mem.write64(a + i * 8, 0)
mem.write64(b + i * 8, mem.read64(x + i * 8))
mem.write64(c + i * 8, 0)
mem.write64(d + i * 8, 0)
mem.write64(a, 1)
mem.write64(d, 1)
let i = 254
while i >= 0
let bit: i64 = (clamped[i >> 3] >> (i & 7)) & 1
swap25519(a, b, bit)
swap25519(c, d, bit)
fadd(e, a, c)
fsub(a, a, c)
fadd(c, b, d)
fsub(b, b, d)
fmul(d, e, e)
fmul(f, a, a)
fmul(a, c, a)
fmul(c, b, e)
fadd(e, a, c)
fsub(a, a, c)
fmul(b, a, a)
fsub(c, d, f)
fmul(a, c, magic)
fadd(a, a, d)
fmul(c, c, a)
fmul(a, d, f)
fmul(d, b, x)
fmul(b, e, e)
swap25519(a, b, bit)
swap25519(c, d, bit)
i = i - 1
finverse(c, c)
fmul(a, a, c)
pack25519(out, a)
mem.free(clamped)
mem.free(a)
mem.free(b)
mem.free(c)
mem.free(d)
mem.free(e)
mem.free(f)
mem.free(x)
mem.free(magic)
func main[] : i64
let scalar: ptr = str.hex_decode("a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4")
let point: ptr = str.hex_decode("e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c")
let expected: ptr = str.hex_decode("c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552")
let out: ptr = mem.alloc(32)
scalarmult(out, scalar, point)
io.print("Computed: ")
io.println(str.hex_encode(out, 32))
io.print("Expected: ")
io.println(str.hex_encode(expected, 32))
let base_point: ptr = mem.alloc(32)
mem.zero(base_point, 32)
mem.write8(base_point, 9)
let alice_private: ptr = str.hex_decode("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a")
io.print("A_priv: ")
io.println(str.hex_encode(alice_private, 32))
let alice_public: ptr = mem.alloc(32)
scalarmult(alice_public, alice_private, base_point)
io.print("A_pub: ")
io.println(str.hex_encode(alice_public, 32))
let bob_private: ptr = str.hex_decode("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6dbddb79b1732920165")
io.print("B_priv: ")
io.println(str.hex_encode(bob_private, 32))
let bob_public: ptr = mem.alloc(32)
scalarmult(bob_public, bob_private, base_point)
io.print("B_pub: ")
io.println(str.hex_encode(bob_public, 32))
let alice_shared: ptr = mem.alloc(32)
scalarmult(alice_shared, alice_private, bob_public)
io.print("A_shared: ")
io.println(str.hex_encode(alice_shared, 32))
let bob_shared: ptr = mem.alloc(32)
scalarmult(bob_shared, bob_private, alice_public)
io.print("B_shared: ")
io.println(str.hex_encode(bob_shared, 32))

144
examples/xchacha20.zr Normal file
View File

@@ -0,0 +1,144 @@
func rotl32[x: i64, r: i64] : i64
return ((x << r) | (x >> (32 - r))) & 0xffffffff
func load32_le[p: ptr] : i64
return p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24)
func store32_le[p: ptr, v: i64] : void
mem.write8(p, v & 0xff)
mem.write8(p + 1, (v >> 8) & 0xff)
mem.write8(p + 2, (v >> 16) & 0xff)
mem.write8(p + 3, (v >> 24) & 0xff)
func quarter_round[state: ptr, a: i64, b: i64, c: i64, d: i64] : void
let va: i64 = load32_le(state + a * 4)
let vb: i64 = load32_le(state + b * 4)
let vc: i64 = load32_le(state + c * 4)
let vd: i64 = load32_le(state + d * 4)
va = (va + vb) & 0xffffffff
vd = vd ^ va
vd = rotl32(vd, 16)
vc = (vc + vd) & 0xffffffff
vb = vb ^ vc
vb = rotl32(vb, 12)
va = (va + vb) & 0xffffffff
vd = vd ^ va
vd = rotl32(vd, 8)
vc = (vc + vd) & 0xffffffff
vb = vb ^ vc
vb = rotl32(vb, 7)
store32_le(state + a * 4, va)
store32_le(state + b * 4, vb)
store32_le(state + c * 4, vc)
store32_le(state + d * 4, vd)
func chacha20_permute[state: ptr] : void
for i in 0..10
quarter_round(state, 0, 4, 8, 12)
quarter_round(state, 1, 5, 9, 13)
quarter_round(state, 2, 6, 10, 14)
quarter_round(state, 3, 7, 11, 15)
quarter_round(state, 0, 5, 10, 15)
quarter_round(state, 1, 6, 11, 12)
quarter_round(state, 2, 7, 8, 13)
quarter_round(state, 3, 4, 9, 14)
func chacha20_block[key: ptr, nonce: ptr, blocknum: i64, out: ptr] : void
let sigma: str = "expand 32-byte k"
let state: ptr = mem.alloc(16 * 4)
store32_le(state + 0, load32_le(sigma + 0))
store32_le(state + 4, load32_le(sigma + 4))
store32_le(state + 8, load32_le(sigma + 8))
store32_le(state + 12, load32_le(sigma + 12))
for i in 0..8
store32_le(state + (4 + i) * 4, load32_le(key + i * 4))
store32_le(state + 12 * 4, blocknum)
store32_le(state + 13 * 4, load32_le(nonce + 0))
store32_le(state + 14 * 4, load32_le(nonce + 4))
store32_le(state + 15 * 4, load32_le(nonce + 8))
let working: ptr = mem.alloc(16 * 4)
for i in 0..16
store32_le(working + i * 4, load32_le(state + i * 4))
chacha20_permute(working)
for i in 0..16
let v: i64 = (load32_le(working + i * 4) + load32_le(state + i * 4)) & 0xffffffff
store32_le(out + i * 4, v)
mem.free(working)
mem.free(state)
func hchacha20[key: ptr, input: ptr, out32: ptr] : void
let sigma: str = "expand 32-byte k"
let state: ptr = mem.alloc(16 * 4)
store32_le(state + 0, load32_le(sigma + 0))
store32_le(state + 4, load32_le(sigma + 4))
store32_le(state + 8, load32_le(sigma + 8))
store32_le(state + 12, load32_le(sigma + 12))
for i in 0..8
store32_le(state + (4 + i) * 4, load32_le(key + i * 4))
for i in 0..4
store32_le(state + (12 + i) * 4, load32_le(input + i * 4))
chacha20_permute(state)
for i in 0..4
store32_le(out32 + i * 4, load32_le(state + i * 4))
for i in 0..4
store32_le(out32 + 16 + i * 4, load32_le(state + (12 + i) * 4))
mem.free(state)
func xchacha20_stream[key: ptr, nonce: ptr, out: ptr, len: i64] : void
let subkey: ptr = mem.alloc(32)
hchacha20(key, nonce, subkey)
let nonce12: ptr = mem.alloc(12)
for i in 0..12
mem.write8(nonce12 + i, 0)
for i in 0..8
mem.write8(nonce12 + 4 + i, nonce[16 + i])
let blocknum: i64 = 0
let remaining: i64 = len
let block: ptr = mem.alloc(64)
while remaining > 0
chacha20_block(subkey, nonce12, blocknum, block)
let take: i64 = 64
if remaining < 64
take = remaining
for i in 0..take
mem.write8(out + (len - remaining) + i, block[i])
remaining = remaining - take
blocknum = blocknum + 1
mem.free(block)
mem.free(nonce12)
mem.free(subkey)
func xchacha20_xor[key: ptr, nonce: ptr, input: ptr, out: ptr, len: i64] : void
if len <= 0
return 0
let ks: ptr = mem.alloc(len)
xchacha20_stream(key, nonce, ks, len)
for i in 0..len
mem.write8(out + i, input[i] ^ ks[i])
mem.free(ks)
func main[] : i64
let key: ptr = str.hex_decode("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f")
let nonce: ptr = str.hex_decode("000102030405060708090a0b0c0d0e0f1011121314151617")
let input: str = "Hello, World!"
let input_len: i64 = str.len(input)
let out: ptr = mem.alloc(input_len)
xchacha20_xor(key, nonce, input, out, input_len)
io.println(str.hex_encode(out, input_len))

View File

@@ -72,7 +72,7 @@ fn compile_file(args: Args) -> Result<(), ZernError> {
run_command(format!("nasm -f elf64 -o {}.o {}.s", args.out, args.out));
if args.no_musl {
if args.use_glibc {
run_command(format!(
"gcc -no-pie -o {} {}.o -flto -Wl,--gc-sections {}",
args.out, args.out, args.cflags
@@ -113,8 +113,8 @@ struct Args {
#[arg(short = 'r', help = "Run the compiled executable")]
run_exe: bool,
#[arg(short = 'm', help = "Don't use musl")]
no_musl: bool,
#[arg(short = 'm', help = "Use glibc instead of musl")]
use_glibc: bool,
#[arg(short = 'C', default_value = "", help = "Extra flags to pass to gcc")]
cflags: String,

View File

@@ -282,9 +282,8 @@ func str.parse_i64[s: str] : i64
i = i + 1
return num * sign
func str.hex_encode[s: str] : str
func str.hex_encode[s: str, s_len: i64] : str
let hex_chars: str = "0123456789abcdef"
let s_len: i64 = str.len(s)
let j = 0
let out: str = mem.alloc(s_len * 2 + 1)
@@ -509,11 +508,17 @@ func alg.reduce[arr: array, fn: ptr, acc: i64] : i64
func os.exit[code: i64] : void
_builtin_syscall(60, code)
func os.urandom[]: i64
let n = 0
func os.urandom[n: i64]: ptr
let buffer: ptr = mem.alloc(n)
let fd: i64 = _builtin_syscall(257, -100, "/dev/urandom", 0, 0) // openat
_builtin_syscall(0, fd, ^n, 8) // read
_builtin_syscall(0, fd, buffer, n) // read
_builtin_syscall(3, fd) // close
return buffer
func os.urandom_i64[]: i64
let buffer: ptr = os.urandom(8)
let n: i64 = mem.read64(buffer)
mem.free(buffer)
return n
func os.time[] : i64