Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions examples/crypto/eea/gf_inverse.py
Original file line number Diff line number Diff line change
@@ -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 <value> <modulus>")
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!"
21 changes: 21 additions & 0 deletions examples/exercises/18p5/Makefile
Original file line number Diff line number Diff line change
@@ -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 *~




83 changes: 83 additions & 0 deletions examples/exercises/18p5/README.md
Original file line number Diff line number Diff line change
@@ -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}
182 changes: 182 additions & 0 deletions examples/exercises/18p5/gf_inv.S
Original file line number Diff line number Diff line change
@@ -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

2 changes: 2 additions & 0 deletions examples/exercises/18p5/gf_inv.reference_output
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
00000092
00000398