mirror of
https://github.com/openhwgroup/cvw.git
synced 2025-04-19 03:24:50 -04:00
Merge pull request #1353 from stineje/main
Exercise 18.5 + EEA Python program
This commit is contained in:
commit
a3de429fc9
5 changed files with 367 additions and 0 deletions
79
examples/crypto/eea/gf_inverse.py
Normal file
79
examples/crypto/eea/gf_inverse.py
Normal file
|
@ -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
examples/exercises/18p5/Makefile
Normal file
21
examples/exercises/18p5/Makefile
Normal file
|
@ -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
examples/exercises/18p5/README.md
Normal file
83
examples/exercises/18p5/README.md
Normal file
|
@ -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
examples/exercises/18p5/gf_inv.S
Normal file
182
examples/exercises/18p5/gf_inv.S
Normal file
|
@ -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
examples/exercises/18p5/gf_inv.reference_output
Normal file
2
examples/exercises/18p5/gf_inv.reference_output
Normal file
|
@ -0,0 +1,2 @@
|
|||
00000092
|
||||
00000398
|
Loading…
Add table
Reference in a new issue