diff --git a/CMakeLists.txt b/CMakeLists.txt index f741f91..473099d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 3.10) -project(fun VERSION 0.37.21 LANGUAGES C) +project(fun VERSION 0.37.22 LANGUAGES C) set(CMAKE_C_STANDARD 99) set(CMAKE_C_STANDARD_REQUIRED ON) diff --git a/examples/ripemd160_debug.fun b/examples/ripemd160_debug.fun new file mode 100644 index 0000000..0c40593 --- /dev/null +++ b/examples/ripemd160_debug.fun @@ -0,0 +1,154 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-12-27 + */ + +#include + +fun to_hex_32(v) + d = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"] + out = [] + i = 0 + while i < 4 + b = band(shr(v, (3 - i) * 8), 0xFF) + push(out, d[shr(b, 4)]) + push(out, d[band(b, 0xF)]) + i = i + 1 + return join(out, "") + +rmd = RIPEMD160() +state = [1732584193, 4023233417, 2562383102, 271733878, 3285377520] +block = [] +push(block, 128) // 0x80 +i = 1 +while i < 64 + push(block, 0) + i = i + 1 + +print("Initial state:") +for v in state + print(" " + to_hex_32(v)) + +// Manual process_block logic with prints +M = [] +i = 0 +while i < 16 + j = i * 4 + push(M, rmd.word32_le(block[j], block[j+1], block[j+2], block[j+3])) + i = i + 1 + +al = state[0] +bl = state[1] +cl = state[2] +dl = state[3] +el = state[4] +ar = state[0] +br = state[1] +cr = state[2] +dr = state[3] +er = state[4] + +j = 0 +while j < 80 + if j < 16 + stage = 0 + stage_r = 4 + else if j < 32 + stage = 1 + stage_r = 3 + else if j < 48 + stage = 2 + stage_r = 2 + else if j < 64 + stage = 3 + stage_r = 1 + else + stage = 4 + stage_r = 0 + + f = rmd.F(stage, bl, cl, dl) + TL1 = rmd.add32(al, f) + TL2 = rmd.add32(M[rmd.RL[j]], rmd.KL[stage]) + TL3 = rmd.add32(TL1, TL2) + TL4 = rmd.rol32(TL3, rmd.SL[j]) + TL = rmd.add32(TL4, el) + if j < 2 + print("j=" + to_string(j) + " L: al=" + to_hex_32(al) + " f=" + to_hex_32(f) + " TL3=" + to_hex_32(TL3) + " TL4=" + to_hex_32(TL4) + " TL=" + to_hex_32(TL)) + al = el + el = dl + dl = rmd.rol32(cl, 10) + cl = bl + bl = TL + + fr = rmd.F(stage_r, br, cr, dr) + TR1 = rmd.add32(ar, fr) + TR2 = rmd.add32(M[rmd.RR[j]], rmd.KR[stage]) + TR3 = rmd.add32(TR1, TR2) + TR4 = rmd.rol32(TR3, rmd.SR[j]) + TR = rmd.add32(TR4, er) + if j < 2 + print("j=" + to_string(j) + " R: ar=" + to_hex_32(ar) + " fr=" + to_hex_32(fr) + " TR3=" + to_hex_32(TR3) + " TR4=" + to_hex_32(TR4) + " TR=" + to_hex_32(TR)) + ar = er + er = dr + dr = rmd.rol32(cr, 10) + cr = br + br = TR + + if j == 0 + print("After j=0:") + print(" L: al=" + to_hex_32(al) + " bl=" + to_hex_32(bl) + " cl=" + to_hex_32(cl) + " dl=" + to_hex_32(dl) + " el=" + to_hex_32(el)) + print(" R: ar=" + to_hex_32(ar) + " br=" + to_hex_32(br) + " cr=" + to_hex_32(cr) + " dr=" + to_hex_32(dr) + " er=" + to_hex_32(er)) + + j = j + 1 + +print("Registers after 80 steps:") +print(" L: A=" + to_hex_32(al) + " B=" + to_hex_32(bl) + " C=" + to_hex_32(cl) + " D=" + to_hex_32(dl) + " E=" + to_hex_32(el)) +print(" R: Ar=" + to_hex_32(ar) + " Br=" + to_hex_32(br) + " Cr=" + to_hex_32(cr) + " Dr=" + to_hex_32(dr) + " Er=" + to_hex_32(er)) + +h0 = rmd.add32(state[1], rmd.add32(cl, dr)) +h1 = rmd.add32(state[2], rmd.add32(dl, er)) +h2 = rmd.add32(state[3], rmd.add32(el, ar)) +h3 = rmd.add32(state[4], rmd.add32(al, br)) +h4 = rmd.add32(state[0], rmd.add32(bl, cr)) +state[0] = h0 +state[1] = h1 +state[2] = h2 +state[3] = h3 +state[4] = h4 + +print("Final state:") +for v in state + print(" " + to_hex_32(v)) + +/* Possible output: +Initial state: + 67452301 + efcdab89 + 98badcfe + 10325476 + c3d2e1f0 +j=0 L: al=67452301 f=67452301 TL3=ce8a4682 TL4=52341674 TL=1606f864 +j=0 R: ar=67452301 fr=10325476 TR3=c81a035d TR4=1a035dc8 TR=ddd63fb8 +After j=0: + L: al=c3d2e1f0 bl=1606f864 cl=efcdab89 dl=eb73fa62 el=10325476 + R: ar=c3d2e1f0 br=ddd63fb8 cr=efcdab89 dr=eb73fa62 er=10325476 +j=1 L: al=c3d2e1f0 f=12b8a98f TL3=d68b8b7f TL4=e2dff5a2 TL=f3124a18 +j=1 R: ar=c3d2e1f0 fr=221b9025 TR3=3690fdfb TR4=21fbf66d TR=322e4ae3 +Registers after 80 steps: + L: A=594637a7 B=215d97b3 C=eba027eb D=b99cdc7a E=ec4dcdff + R: Ar=e7eb2e49 Br=85bff1f4 Cr=1d9bc99d Dr=a7ffa90b Er=ffcec761 +Final state: + 836d7c7f + 522680d9 + e46b50be + a2d90b8b + a63e8451 +*/ diff --git a/examples/ripemd160_demo.fun b/examples/ripemd160_demo.fun new file mode 100644 index 0000000..9f7d06c --- /dev/null +++ b/examples/ripemd160_demo.fun @@ -0,0 +1,58 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-12-27 + */ + +// THIS IS BROKEN ACTUALLY! IT DOES NOT CALCULATE THE EXPECTED HASHES! + +// Example usage of the RIPEMD-160 hash algorithm. + +#include + +rmd = RIPEMD160() + +// Test case 1: Empty string +s1 = "" +h1 = rmd.ripemd160_str(s1) +print("RIPEMD-160(\"" + s1 + "\") = " + h1) +// Expected: 9c1185a5c5e9fc54612808977ee8f548b2258d31 + +// Test case 2: "abc" +s2 = "abc" +h2 = rmd.ripemd160_str(s2) +print("RIPEMD-160(\"" + s2 + "\") = " + h2) +// Expected: 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc + +// Test case 3: "message digest" +s3 = "message digest" +h3 = rmd.ripemd160_str(s3) +print("RIPEMD-160(\"" + s3 + "\") = " + h3) +// Expected: 5d0689ef49d2fae572b881b123a85ffa21595f36 + +// Example of hashing from hex string +hex_input = "616263" // "abc" in hex +h4 = rmd.ripemd160_hex(hex_input) +print("RIPEMD-160(hex: " + hex_input + ") = " + h4) +// Expected: 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc + +/* Expected output: +RIPEMD-160("") = 9c1185a5c5e9fc54612808977ee8f548b2258d31 +RIPEMD-160("abc") = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc +RIPEMD-160("message digest") = 5d0689ef49d2fae572b881b123a85ffa21595f36 +RIPEMD-160(hex: 616263) = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc +*/ + +/* Actual output: +RIPEMD-160("") = d90cc72ef4ad5a2c51f89dc938de0b989c4c4b46 +RIPEMD-160("abc") = 7f339b642aaa074a849c6c1cd860cb049cc792b3 +RIPEMD-160("message digest") = a4f9cd0270a341d05a713df4bf49a3ce8aabde64 +RIPEMD-160(hex: 616263) = 7f339b642aaa074a849c6c1cd860cb049cc792b3 +*/ diff --git a/examples/ripemd160_test.fun b/examples/ripemd160_test.fun new file mode 100644 index 0000000..8c2dc27 --- /dev/null +++ b/examples/ripemd160_test.fun @@ -0,0 +1,19 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-12-27 + */ + +#include +print("Include OK") + +/* Expected output: +Include OK +*/ diff --git a/examples/test_bits.fun b/examples/test_bits.fun new file mode 100644 index 0000000..ab810b2 --- /dev/null +++ b/examples/test_bits.fun @@ -0,0 +1,26 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-12-27 + */ + +print(bxor(0x80000000, 0x00000001)) +print(bor(0x80000000, 0x00000001)) +print(band(0x80000000, 0x80000000)) +print(bnot(0x80000000)) +print(bnot(0x7FFFFFFF)) + +/* Expected output: +2147483649 +2147483649 +2147483648 +2147483647 +2147483648 +*/ diff --git a/examples/test_rol.fun b/examples/test_rol.fun new file mode 100644 index 0000000..46c87f1 --- /dev/null +++ b/examples/test_rol.fun @@ -0,0 +1,22 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-12-27 + */ + +print(rol(0x12345678, 4)) // Should be 0x23456781 +print(rol(0x80000000, 1)) // Should be 0x00000001 +print(rol(0x00000001, 31)) // Should be 0x80000000 + +/* Expected output: +591751041 +1 +2147483648 +*/ diff --git a/examples/test_shl.fun b/examples/test_shl.fun new file mode 100644 index 0000000..1024225 --- /dev/null +++ b/examples/test_shl.fun @@ -0,0 +1,20 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-12-27 + */ + +print(shl(0x80, 24)) +print(128 * 16777216) + +/* Expected output: +2147483648 +2147483648 +*/ diff --git a/examples/test_types.fun b/examples/test_types.fun new file mode 100644 index 0000000..120774d --- /dev/null +++ b/examples/test_types.fun @@ -0,0 +1,24 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-12-27 + */ + +print(typeof(128 * 16777216)) +print(128 * 16777216) +print(typeof(0x80000000)) +print(0x80000000) + +/* Expected output: +Number +2147483648 +Number +2147483648 +*/ diff --git a/examples/test_xor.fun b/examples/test_xor.fun new file mode 100644 index 0000000..d1caccf --- /dev/null +++ b/examples/test_xor.fun @@ -0,0 +1,22 @@ +#!/usr/bin/env fun + +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-12-27 + */ + +print(bxor(0xefcdab89, 0x98badcfe)) +print(bxor(0x77767677, 0x10325476)) +print(bxor(bxor(0xefcdab89, 0x98badcfe), 0x10325476)) + +/* Expected output: +2004318071 +1732518401 +1732584193 +*/ diff --git a/lib/crypt/ripemd160.fun b/lib/crypt/ripemd160.fun new file mode 100644 index 0000000..bb9abd6 --- /dev/null +++ b/lib/crypt/ripemd160.fun @@ -0,0 +1,272 @@ +/* + * This file is part of the Fun programming language. + * https://fun-lang.xyz/ + * + * Copyright 2025 Johannes Findeisen + * Licensed under the terms of the Apache-2.0 license. + * https://opensource.org/license/apache-2-0 + * + * Added: 2025-12-27 + */ + +// THIS IS BROKEN ACTUALLY! IT DOES NOT CALCULATE THE EXPECTED HASHES! + +#include + +class RIPEMD160() + KL = [0, 1518500249, 1859775393, 2400959708, 2840853838] + KR = [1352829926, 1548603684, 1836062451, 2054012649, 0] + + RL = [ + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 7, 4, 13, 1, 10, 6, 15, 3, 12, 0, 9, 5, 2, 14, 11, 8, + 3, 10, 14, 4, 9, 15, 8, 1, 2, 7, 0, 6, 13, 11, 5, 12, + 1, 9, 11, 10, 0, 8, 12, 4, 13, 3, 7, 15, 14, 5, 6, 2, + 4, 0, 5, 9, 7, 12, 2, 10, 14, 1, 3, 8, 11, 6, 15, 13 + ] + + RR = [ + 5, 14, 7, 0, 9, 2, 11, 4, 13, 6, 15, 8, 1, 10, 3, 12, + 6, 11, 3, 7, 0, 13, 5, 10, 14, 15, 8, 12, 4, 9, 1, 2, + 15, 5, 1, 3, 7, 14, 6, 9, 11, 8, 12, 2, 10, 0, 4, 13, + 8, 6, 4, 1, 3, 11, 15, 0, 5, 12, 2, 13, 9, 7, 10, 14, + 12, 15, 10, 4, 1, 5, 8, 7, 6, 2, 13, 14, 0, 3, 9, 11 + ] + + SL = [ + 11, 14, 15, 12, 5, 8, 7, 9, 11, 13, 14, 15, 6, 7, 9, 8, + 7, 6, 8, 13, 11, 9, 7, 15, 7, 12, 15, 9, 11, 7, 13, 12, + 11, 13, 6, 7, 14, 9, 13, 15, 14, 8, 13, 6, 5, 12, 7, 5, + 11, 12, 14, 15, 14, 15, 9, 8, 9, 14, 5, 6, 8, 6, 5, 12, + 9, 15, 5, 11, 6, 8, 13, 12, 5, 12, 13, 14, 11, 8, 5, 6 + ] + + SR = [ + 8, 9, 9, 11, 13, 15, 15, 5, 7, 7, 8, 11, 14, 14, 12, 6, + 9, 13, 15, 7, 12, 8, 9, 11, 7, 7, 12, 7, 6, 15, 13, 11, + 9, 7, 15, 11, 8, 6, 6, 14, 12, 13, 5, 14, 13, 13, 7, 5, + 15, 5, 8, 11, 14, 14, 6, 14, 6, 9, 12, 9, 12, 5, 15, 8, + 8, 5, 12, 9, 12, 5, 14, 6, 8, 13, 6, 5, 15, 13, 11, 11 + ] + + fun u32(this, x) + m = 4294967296 + while x < 0 + x = x + m + while x >= m + x = x - m + return x + + fun rol32(this, x, s) + return rol(this.u32(x), s) + + fun add32(this, a, b) + return this.u32(a + b) + + fun F(this, stage, x, y, z) + if stage == 0 + return bxor(bxor(x, y), z) + else if stage == 1 + return bor(band(x, y), band(bnot(x), z)) + else if stage == 2 + return bxor(bor(x, bnot(y)), z) + else if stage == 3 + return bor(band(x, z), band(y, bnot(z))) + else + return bxor(x, bor(y, bnot(z))) + + fun hex_val(this, ch) + if ch == "0" + return 0 + else if ch == "1" + return 1 + else if ch == "2" + return 2 + else if ch == "3" + return 3 + else if ch == "4" + return 4 + else if ch == "5" + return 5 + else if ch == "6" + return 6 + else if ch == "7" + return 7 + else if ch == "8" + return 8 + else if ch == "9" + return 9 + else if ch == "a" || ch == "A" + return 10 + else if ch == "b" || ch == "B" + return 11 + else if ch == "c" || ch == "C" + return 12 + else if ch == "d" || ch == "D" + return 13 + else if ch == "e" || ch == "E" + return 14 + else if ch == "f" || ch == "F" + return 15 + else + return 0 + + fun byte_from_hex_pair(this, hh) + hi = this.hex_val(substr(hh, 0, 1)) + lo = this.hex_val(substr(hh, 1, 1)) + return hi * 16 + lo + + fun from_hex(this, hex) + arr = [] + i = 0 + n = len(hex) + while i + 1 < n + push(arr, this.byte_from_hex_pair(substr(hex, i, 2))) + i = i + 2 + return arr + + fun two_hex(this, n) + n = band(n, 0xFF) + d = ["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"] + return join([d[shr(n, 4)], d[band(n, 0xF)]], "") + + fun bytes_to_hex(this, arr) + i = 0 + out = [] + while i < len(arr) + push(out, this.two_hex(arr[i])) + i = i + 1 + return join(out, "") + + fun pad_bytes(this, bytes) + L = len(bytes) + out = [] + i = 0 + while i < L + push(out, bytes[i]) + i = i + 1 + push(out, 128) + while (len(out) % 64) != 56 + push(out, 0) + len_bits = L * 8 + j = 0 + while j < 8 + push(out, band(shr(len_bits, 8 * j), 0xFF)) + j = j + 1 + return out + + fun word32_le(this, b0, b1, b2, b3) + return bor(bor(bor(b0, shl(b1, 8)), shl(b2, 16)), shl(b3, 24)) + + fun process_block(this, state, block) + M = [] + i = 0 + while i < 16 + j = i * 4 + push(M, this.word32_le(block[j], block[j+1], block[j+2], block[j+3])) + i = i + 1 + + A = state[0] + B = state[1] + C = state[2] + D = state[3] + E = state[4] + Ar = state[0] + Br = state[1] + Cr = state[2] + Dr = state[3] + Er = state[4] + + j = 0 + while j < 80 + if j < 16 + stage = 0 + stage_r = 4 + else if j < 32 + stage = 1 + stage_r = 3 + else if j < 48 + stage = 2 + stage_r = 2 + else if j < 64 + stage = 3 + stage_r = 1 + else + stage = 4 + stage_r = 0 + + // Left side step + T = this.add32(this.rol32(this.add32(this.add32(A, this.F(stage, B, C, D)), this.add32(M[this.RL[j]], this.KL[stage])), this.SL[j]), E) + A = E + E = D + D = this.rol32(C, 10) + C = B + B = T + + // Right side step + Tr = this.add32(this.rol32(this.add32(this.add32(Ar, this.F(stage_r, Br, Cr, Dr)), this.add32(M[this.RR[j]], this.KR[stage])), this.SR[j]), Er) + Ar = Er + Er = Dr + Dr = this.rol32(Cr, 10) + Cr = Br + Br = Tr + + j = j + 1 + + al = A + bl = B + cl = C + dl = D + el = E + ar = Ar + br = Br + cr = Cr + dr = Dr + er = Er + + h0 = state[0] + h1 = state[1] + h2 = state[2] + h3 = state[3] + h4 = state[4] + + state[0] = this.add32(h1, this.add32(B, Cr)) + state[1] = this.add32(h2, this.add32(C, Dr)) + state[2] = this.add32(h3, this.add32(D, Er)) + state[3] = this.add32(h4, this.add32(E, Ar)) + state[4] = this.add32(h0, this.add32(A, Br)) + return state + + fun ripemd160_bytes(this, bytes) + state = [1732584193, 4023233417, 2562383102, 271733878, 3285377520] + data = this.pad_bytes(bytes) + N = len(data) + off = 0 + while off < N + block = [] + i = 0 + while i < 64 + push(block, data[off + i]) + i = i + 1 + state = this.process_block(state, block) + off = off + 64 + out = [] + j = 0 + while j < 5 + v = state[j] + push(out, band(v, 0xFF)) + push(out, band(shr(v, 8), 0xFF)) + push(out, band(shr(v, 16), 0xFF)) + push(out, band(shr(v, 24), 0xFF)) + j = j + 1 + return out + + fun ripemd160_hex(this, hexStr) + bytes = this.from_hex(hexStr) + digest = this.ripemd160_bytes(bytes) + return this.bytes_to_hex(digest) + + fun ripemd160_str(this, str) + bytes = string_to_bytes_ascii(str) + digest = this.ripemd160_bytes(bytes) + return this.bytes_to_hex(digest)