diff --git a/examples/crypto/eea/gf_inverse.py b/examples/crypto/eea/gf_inverse.py new file mode 100644 index 0000000000..65c6392d48 --- /dev/null +++ b/examples/crypto/eea/gf_inverse.py @@ -0,0 +1,79 @@ +# gf_inverse.py +# Extended Euclidean Algorithm +# james.stine@okstate.edu 8 April 2025 + +def gf_mul(a, b, modulus): + result = 0 + while b: + if b & 1: + result ^= a + b >>= 1 + a <<= 1 + if a & (1 << 8): + a ^= modulus + return result & 0xFF + +def gf_mul_poly(a, b): + result = 0 + while b: + if b & 1: + result ^= a + a <<= 1 + b >>= 1 + return result + +def gf_divmod(a, b): + if b == 0: + raise ZeroDivisionError() + deg_a = a.bit_length() - 1 + deg_b = b.bit_length() - 1 + quotient = 0 + while deg_a >= deg_b: + shift = deg_a - deg_b + quotient ^= 1 << shift + a ^= b << shift + deg_a = a.bit_length() - 1 + return quotient, a + +def gf_inverse(a, modulus): + if a == 0: + raise ValueError("No inverse for 0 in GF(2^n)") + + r0, r1 = modulus, a + s0, s1 = 0, 1 + loop_count = 0 + + print(f"{'Loop':<5} {'r0':>6} {'r1':>6} {'q':>6} {'s0':>6} {'s1':>6}") + + while r1 != 0: + q, _ = gf_divmod(r0, r1) + new_r = r0 ^ gf_mul_poly(q, r1) + new_s = s0 ^ gf_mul_poly(q, s1) + + print(f"{loop_count:<5} {r0:06X} {r1:06X} {q:06X} {s0:06X} {s1:06X}") + + r0, r1 = r1, new_r + s0, s1 = s1, new_s + loop_count += 1 + + print(f"{loop_count:<5} {r0:06X} {' '*6} {' '*6} {s0:06X}") + + if r0 != 1: + raise ValueError(f"No inverse for {a:#02x} mod {modulus:#04x}") + + return s0 & 0xFF + +# Example usage +if __name__ == "__main__": + import sys + if len(sys.argv) != 3: + print("Usage: python gf_inverse.py ") + print("Example: python gf_inverse.py 0x53 0x11B") + sys.exit(1) + + value = int(sys.argv[1], 0) + modulus = int(sys.argv[2], 0) + + inv = gf_inverse(value, modulus) + print(f"\nMultiplicative inverse of {value:#02x} in GF(2^8) mod {modulus:#04x} is {inv:#02x}") + assert gf_mul(value, inv, modulus) == 1, "Verification failed!" diff --git a/examples/exercises/18p5/Makefile b/examples/exercises/18p5/Makefile new file mode 100644 index 0000000000..fc3c3c04ee --- /dev/null +++ b/examples/exercises/18p5/Makefile @@ -0,0 +1,21 @@ +TARGET = gf_inv + +$(TARGET).objdump: $(TARGET) + riscv64-unknown-elf-objdump -Dls $(TARGET) > $(TARGET).objdump + +$(TARGET): $(TARGET).S Makefile + riscv64-unknown-elf-gcc -g -o $(TARGET) -march=rv32gc -mabi=ilp32 -mcmodel=medany \ + -nostartfiles -nostdlib -T../../link/link.ld $(TARGET).S + +sim: + spike --isa=rv32gc_zicsr_zicntr +signature=$(TARGET).signature.output +signature-granularity=4 $(TARGET) + diff --ignore-case $(TARGET).signature.output $(TARGET).reference_output || exit + echo "Signature matches! Success!" + +clean: + rm -f $(TARGET) $(TARGET).objdump $(TARGET).signature.output + rm -f *~ + + + + diff --git a/examples/exercises/18p5/README.md b/examples/exercises/18p5/README.md new file mode 100644 index 0000000000..4d958ceb68 --- /dev/null +++ b/examples/exercises/18p5/README.md @@ -0,0 +1,83 @@ +Extended Euclidean Algorithm + +EEA Cycle Estimates + +Main loop body: 25 (N times) +swap_and_negate: 22 (S times) +gf_degree: 10 (twice per loop) +Number of Loops: N + +\ext{Total Cycles} = N \times (25 + 2 \times 10) + S \times 22 + +Pseuo-Code + +r0 := m +r1 := a +s0 := 0 +s1 := 1 + +while r1 \neq 0: + deg_r0 = degree(r0) + deg_r1 = degree(r1) + shift = deg_r0 - deg_r1 + + if shift < 0: + swap(r0, r1) + swap(s0, s1) + shift := -shift + + r0 = r0 ^ (r1 << shift) + s0 = s0 ^ (s1 << shift) + +if r0 \neq 1: + return "No inverse" +else: + return s0 + + +LaTeX +------ +\documentclass{article} +\usepackage{algorithm} +\usepackage{algpseudocode} +\usepackage{amsmath} + +\begin{document} + +\begin{algorithm} +\caption{Extended Euclidean Algorithm in GF(2\textsuperscript{8})} +\begin{algorithmic}[1] +\Require Input value $a$, modulus $m$ (usually $0x11B$) +\Ensure Return $s_0$ such that $s_0 \cdot a \equiv 1 \pmod{m}$ if inverse exists + +\State $r_0 \gets m$ +\State $r_1 \gets a$ +\State $s_0 \gets 0$ +\State $s_1 \gets 1$ + +\While{$r_1 \ne 0$} + \State $d_0 \gets \text{deg}(r_0)$ + \State $d_1 \gets \text{deg}(r_1)$ + \State $\text{shift} \gets d_0 - d_1$ + + \If{$\text{shift} < 0$} + \State Swap $r_0 \leftrightarrow r_1$ + \State Swap $s_0 \leftrightarrow s_1$ + \State $\text{shift} \gets -\text{shift}$ + \EndIf + + \State $r_0 \gets r_0 \oplus (r_1 \ll \text{shift})$ + \State $s_0 \gets s_0 \oplus (s_1 \ll \text{shift})$ + + \State Mask $r_0$ and $s_0$ to 9 bits: $r_0 \gets r_0 \mathbin{\&} 0x1FF$, $s_0 \gets s_0 \mathbin{\&} 0x1FF$ +\EndWhile + +\If{$r_0 = 1$} + \State \Return $s_0$ \Comment{Inverse found} +\Else + \State \Return \textbf{error} \Comment{No inverse exists} +\EndIf +\end{algorithmic} +\end{algorithm} + +\end{document} diff --git a/examples/exercises/18p5/gf_inv.S b/examples/exercises/18p5/gf_inv.S new file mode 100644 index 0000000000..bb8eb822d0 --- /dev/null +++ b/examples/exercises/18p5/gf_inv.S @@ -0,0 +1,182 @@ +// gf_inv.S +// james.stine@okstate.edu 9 April 2025 + +.text + +.global rvtest_entry_point +rvtest_entry_point: + + li a0, 0x32 # Input value to invert in GF(2^8) + li t0, 0x11B # Modulus: irreducible poly x^8 + x^4 + x^3 + x + 1 + + # Initialize Extended Euclidean variables: a*x+b*y=gcd(a,b) + li t1, 0 # s0 = 0 (old x) + li t2, 1 # s1 = 1 (new x) + mv t3, t0 # r0 = modulus + mv t4, a0 # r1 = input value + + csrr s8, instret # count instructions at beginning + + # --- Register usage --- + # a0: Input / Final result (return value) + # t0: Modulus (0x11B) + # t1: s0 (previous x value) + # t2: s1 (current x value) + # t3: r0 (previous remainder) + # t4: r1 (current remainder) + # t5: degree(r0) + # t6: degree(r1) + # a1: argument to gf_degree + # a2: shift amount + # a3: temporary for SLT result + # a4/a5: shift results, temporaries + # s8: initial seed of instruction count (instret) + # s9: final value of instruction count (instret) + +# -------------------------------------- +# Main loop: Extended Euclidean Division +# -------------------------------------- +inv_loop: + beqz t4, fail # If r1 == 0, input is not invertible → fail + + # Compute degree of r0 + mv a1, t3 # Set a1 = r0 = (modulus m) + call gf_degree # Compute deg(r0) + mv t5, a0 # degree(r0) + + # Compute degree of r1 + mv a1, t4 # Set a1 = r1 = (input a) + call gf_degree # Compute deg(r1) + mv t6, a0 # degree (r1) + + # Compute shift = deg(r0) - deg(r1) + sub a2, t5, t6 # alignment r1 with highest term in r0 + slt a3, a2, zero # Check if shift < 0 + bnez a3, swap_and_negate + + # Perform r0 ^= r1 << shift + # Polynomial subtraction in GF(2) + sll a4, t4, a2 + xor t3, t3, a4 + andi t3, t3, 0x1FF # Mask off 9 bits to stay in GF(2^8) + + # Update s0: s0 ^= s1 << shift - Update Bezout coefficient + sll a5, t2, a2 + xor t1, t1, a5 + andi t1, t1, 0x1FF # Mask off 9 bits to stay in GF(2^8) + + # Swap (r0, r1) + mv a4, t3 + mv t3, t4 + mv t4, a4 + + # Swap (s0, s1) + mv a4, t1 + mv t1, t2 + mv t2, a4 + + # Check if r0 == 1, then we are done + li t5, 1 + beq t3, t5, done + + j inv_loop + +# ------------------------------------------- +# Case: shift < 0 → negate, then apply shift +# ------------------------------------------- +swap_and_negate: + # Swap r0 <-> r1 + mv a4, t3 + mv t3, t4 + mv t4, a4 + + # Swap s0 <-> s1 + mv a4, t1 + mv t1, t2 + mv t2, a4 + + # shift = -shift + sub a2, zero, a2 + + # r0 ^= r1 << shift + sll a4, t4, a2 + xor t3, t3, a4 + andi t3, t3, 0x1FF # Mask off to 9 bits again + + # s0 ^= s1 << shift + sll a5, t2, a2 + xor t1, t1, a5 + andi t1, t1, 0x1FF + + # Final swap to maintain invariant + mv a4, t3 + mv t3, t4 + mv t4, a4 + + mv a4, t1 + mv t1, t2 + mv t2, a4 + + # Check if r0 == 1 + li t5, 1 + beq t3, t5, done + + j inv_loop + +# ------------------------------------- +# Helper: compute degree of a1 (MSB set) +# ------------------------------------- +gf_degree: + li t0, 8 # Start checking from MSB down +deg_loop: + srl a0, a1, t0 # Right shift a1 by t0 + andi a0, a0, 1 # Isolate LSB + bnez a0, done_deg # If bit is set, we're done + addi t0, t0, -1 + bgez t0, deg_loop # Check next bit + li a0, 0 # Nothing set + ret +done_deg: + mv a0, t0 + ret + +# --------------------- +# Finalize inverse +# --------------------- +done: + mv a0, t1 # Result is a0 + andi a0, a0, 0xFF # Mask to 8 bits + csrr s9, instret # count instructions at end + sub s9, s9, s8 # get number of #instructions executed + +write_tohost: # HTIF stuff + la t1, tohost + li t0, 1 # 1 for success, 3 for failure + sw t0, 0(t1) # send success code + + la t0, begin_signature # Address of signature + sw a0, 0(t0) # Store result (i.e., a0) + sw s9, 4(t0) # record #instructions executed + +fail: + li a0, 0xff # Signal failure: no inverse exists + +self_loop: + j self_loop + +.section .tohost +tohost: # write to HTIF + .word 0 +fromhost: + .word 0 + +.data +.EQU XLEN,32 +begin_signature: + .fill 2*(XLEN/32),4,0xdeadbeef +end_signature: + + +.bss + .space 512 + diff --git a/examples/exercises/18p5/gf_inv.reference_output b/examples/exercises/18p5/gf_inv.reference_output new file mode 100644 index 0000000000..89b5c63953 --- /dev/null +++ b/examples/exercises/18p5/gf_inv.reference_output @@ -0,0 +1,2 @@ +00000092 +00000398