diff --git a/README.md b/README.md index 341c69d..797dabe 100644 --- a/README.md +++ b/README.md @@ -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: ") diff --git a/examples/guess_number.zr b/examples/guess_number.zr index a02e5b7..54db061 100644 --- a/examples/guess_number.zr +++ b/examples/guess_number.zr @@ -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: ") diff --git a/examples/quicksort.zr b/examples/quicksort.zr index 17b65ec..dca50f3 100644 --- a/examples/quicksort.zr +++ b/examples/quicksort.zr @@ -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)) diff --git a/examples/x25519.zr b/examples/x25519.zr new file mode 100644 index 0000000..9402ff3 --- /dev/null +++ b/examples/x25519.zr @@ -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)) \ No newline at end of file diff --git a/examples/xchacha20.zr b/examples/xchacha20.zr new file mode 100644 index 0000000..07dfd4b --- /dev/null +++ b/examples/xchacha20.zr @@ -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)) \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 9eb8bda..db6ea66 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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, diff --git a/src/std.zr b/src/std.zr index 82b0ce0..1c6f508 100644 --- a/src/std.zr +++ b/src/std.zr @@ -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