sov-kernel-monster / src /sov_monster_kernel.f90.bak
SNAPKITTYWEST's picture
chore: push full sov-kernel-monster content from local build
9425aed verified
Raw
History Blame Contribute Delete
79.1 kB
!=====================================================================
! SOVEREIGN MONSTER KERNEL: Pure Fortran 2018 + OpenACC/OpenMP
! Target: ARM64 SVE2 | x86_64 AVX-512 | NVIDIA PTX | AMD SPIR-V
! Deps: ZERO. No libc. No BLAS. No Crypto libs. Pure Metal.
! ABI: matches Lean @[extern] c_name="sov_*" declarations
!=====================================================================
module sov_monster_kernel
use, intrinsic :: iso_c_binding, only: c_int64_t, c_ptr, c_f_pointer, c_size_t, c_loc
use, intrinsic :: iso_fortran_env, only: int64, real64, int8, error_unit
implicit none
private
public :: sov_plasma_verify
public :: sov_bifrost_sign
public :: sov_bifrost_verify
public :: sov_apl_step_zgemm_fused
public :: sov_apl_evolve_sequence
integer, parameter :: dp = real64
integer, parameter :: i64 = int64
integer, parameter :: i8 = int8
complex(dp), parameter :: ci = (0.0_dp, 1.0_dp)
complex(dp), parameter :: czero = (0.0_dp, 0.0_dp)
integer, parameter :: HASH_LEN = 32
integer, parameter :: SIG_LEN = 64
integer, parameter :: SK_LEN = 32
integer, parameter :: MAX_DIM = 256
integer, parameter :: BLAKE3_BLOCK_LEN = 64
integer(i64), parameter :: BLAKE3_IV(8) = [ &
int(Z'6A09E667F3BCC908', i64), int(Z'BB67AE8584CAA73B', i64), &
int(Z'3C6EF372FE94F82B', i64), int(Z'A54FF53A5F1D36F1', i64), &
int(Z'510E527FADE682D1', i64), int(Z'9B05688C2B3E6C1F', i64), &
int(Z'1F83D9ABFB41BD6B', i64), int(Z'5BE0CD19137E2179', i64) ]
type :: blake3_state
integer(i64), dimension(8) :: chaining_value
integer(i8), dimension(64) :: block
integer(i64) :: block_len, counter, flags
end type
contains
!══════════════════════════════════════════════════════════════════
! 1. PLASMA GATE
!══════════════════════════════════════════════════════════════════
pure function sov_plasma_verify(shape_ptr, rank, herm, trace_one, &
hash_ptr, buffer_ptr, buffer_bytes) &
bind(C, name="sov_plasma_verify") result(ok)
type(c_ptr), intent(in), value :: shape_ptr, hash_ptr, buffer_ptr
integer(c_int64_t), intent(in), value :: rank, buffer_bytes
logical, intent(in), value :: herm, trace_one
logical :: ok
integer(c_int64_t), pointer :: shape(:)
integer(c_int64_t) :: i
ok = .false.
if (rank < 1 .or. rank > 8) return
call c_f_pointer(shape_ptr, shape, [rank])
do i = 1, rank
if (shape(i) <= 0 .or. shape(i) > MAX_DIM) return
end do
if (.not. herm) return
if (.not. trace_one) return
ok = sov_blake3_verify_buffer(buffer_ptr, buffer_bytes, hash_ptr)
end function
!══════════════════════════════════════════════════════════════════
! 2. BIFROST: Ed25519 sign / verify
!══════════════════════════════════════════════════════════════════
pure subroutine sov_bifrost_sign(payload_ptr, payload_len, sk_ptr, sig_ptr) &
bind(C, name="sov_bifrost_sign")
type(c_ptr), intent(in), value :: payload_ptr, sk_ptr, sig_ptr
integer(c_size_t), intent(in), value :: payload_len
integer(i8), pointer :: payload(:), sk(:), sig(:)
integer(i8) :: h_sk(64), R_enc(32), s_bytes(32), h_ram(64)
integer(i64) :: r_sc(10), a_sc(10), hram_sc(10), s_sc(10)
integer(i64) :: Rx(10), Ry(10), Rz(10), Rt(10)
call c_f_pointer(payload_ptr, payload, [payload_len])
call c_f_pointer(sk_ptr, sk, [SK_LEN])
call c_f_pointer(sig_ptr, sig, [SIG_LEN])
call sov_blake3_hash_bytes(sk, SK_LEN, h_sk, 64)
call sov_ed25519_clamp_and_decode(h_sk(1:32), a_sc)
call sov_blake3_hash_concat(h_sk(33:64), 32, payload, int(payload_len), h_ram, 64)
call sov_ed25519_reduce_scalar(h_ram, r_sc)
call sov_ed25519_scalar_mul_base(r_sc, Rx, Ry, Rz, Rt)
call sov_ed25519_encode_point(Rx, Ry, Rz, Rt, R_enc)
call sov_blake3_hash_concat3(R_enc, 32, sk(33:64), 32, payload, int(payload_len), h_ram, 64)
call sov_ed25519_reduce_scalar(h_ram, hram_sc)
call sov_ed25519_scalar_mul(hram_sc, a_sc, s_sc)
call sov_ed25519_scalar_add_mod_l(r_sc, s_sc, s_sc)
call sov_ed25519_scalar_to_bytes(s_sc, s_bytes)
sig(1:32) = R_enc; sig(33:64) = s_bytes
end subroutine
pure function sov_bifrost_verify(payload_ptr, payload_len, sig_ptr, pk_ptr) &
bind(C, name="sov_bifrost_verify") result(ok)
type(c_ptr), intent(in), value :: payload_ptr, sig_ptr, pk_ptr
integer(c_size_t), intent(in), value :: payload_len
logical :: ok
integer(i8), pointer :: payload(:), sig(:), pk(:)
integer(i8) :: R_enc(32), s_bytes(32), pk_bytes(32), h_ram(64), check_enc(32)
integer(i64) :: s_sc(10), hram_sc(10), Rx(10),Ry(10),Rz(10),Rt(10)
integer(i64) :: Ax(10),Ay(10),Az(10),At(10), cx(10),cy(10),cz(10),ct(10)
call c_f_pointer(payload_ptr, payload, [payload_len])
call c_f_pointer(sig_ptr, sig, [SIG_LEN])
call c_f_pointer(pk_ptr, pk, [32])
R_enc = sig(1:32); s_bytes = sig(33:64); pk_bytes = pk(1:32)
call sov_ed25519_scalar_from_bytes(s_bytes, s_sc)
if (.not. sov_ed25519_scalar_valid(s_sc)) then; ok=.false.; return; end if
if (.not. sov_ed25519_decode_point(R_enc, Rx,Ry,Rz,Rt)) then; ok=.false.; return; end if
if (.not. sov_ed25519_decode_point(pk_bytes, Ax,Ay,Az,At)) then; ok=.false.; return; end if
call sov_blake3_hash_concat3(R_enc,32, pk_bytes,32, payload,int(payload_len), h_ram,64)
call sov_ed25519_reduce_scalar(h_ram, hram_sc)
call sov_ed25519_scalar_mul_base(s_sc, cx, cy, cz, ct)
call sov_ed25519_point_negate(Ax, Ay, Az, At)
call sov_ed25519_scalar_mul_point(hram_sc, Ax,Ay,Az,At, cx,cy,cz,ct)
call sov_ed25519_point_add(Rx,Ry,Rz,Rt, cx,cy,cz,ct, cx,cy,cz,ct)
call sov_ed25519_encode_point(cx,cy,cz,ct, check_enc)
ok = all(check_enc == R_enc)
end function
!══════════════════════════════════════════════════════════════════
! 3. SOVEREIGN APL STEP: FUSED U rho U† + PLASMA + BIFROST
!══════════════════════════════════════════════════════════════════
subroutine sov_apl_step_zgemm_fused(H, ldH, rho, ldr, dt, &
sk, pk, out_rho, out_hash, out_sig) &
bind(C, name="sov_apl_step_zgemm_fused")
complex(dp), intent(in), dimension(ldH,*) :: H
integer(c_int64_t), intent(in), value :: ldH
complex(dp), intent(in), dimension(ldr,*) :: rho
integer(c_int64_t), intent(in), value :: ldr
real(dp), intent(in), value :: dt
type(c_ptr), intent(in), value :: sk, pk
complex(dp), intent(out), dimension(ldr,*) :: out_rho
type(c_ptr), intent(inout), value :: out_hash, out_sig
integer(c_int64_t) :: n, i, j, k
complex(dp), allocatable :: U(:,:), Ut(:,:), tmp(:,:)
n = ldr
if (.not. sov_is_hermitian_matrix(H, n)) call sov_fault(1)
if (.not. sov_is_density_matrix(rho, n)) call sov_fault(2)
allocate(U(n,n), Ut(n,n), tmp(n,n))
U = -ci * dt * H(1:n, 1:n)
call sov_zmexp_scaling_squaring(U, int(n))
!$omp parallel do simd collapse(2) default(none) shared(U,Ut,n)
do j = 1, n; do i = 1, n; Ut(i,j) = conjg(U(j,i)); end do; end do
!$omp end parallel do
!$omp target teams distribute parallel do simd collapse(2) if(n>64) &
!$omp map(to:U,rho) map(from:tmp)
do j = 1, n; do i = 1, n
tmp(i,j) = czero
do k = 1, n; tmp(i,j) = tmp(i,j) + U(i,k)*rho(k,j); end do
end do; end do
!$omp end target
!$omp target teams distribute parallel do simd collapse(2) if(n>64) &
!$omp map(to:tmp,Ut) map(from:out_rho)
do j = 1, n; do i = 1, n
out_rho(i,j) = czero
do k = 1, n; out_rho(i,j) = out_rho(i,j) + tmp(i,k)*Ut(k,j); end do
end do; end do
!$omp end target
if (.not. sov_is_density_matrix(out_rho, n)) call sov_fault(3)
call sov_blake3_hash_matrix(out_rho, int(n), out_hash)
call sov_bifrost_sign(out_hash, int(HASH_LEN, c_size_t), sk, out_sig)
deallocate(U, Ut, tmp)
end subroutine
!══════════════════════════════════════════════════════════════════
! 4. MULTI-STEP EVOLUTION
!══════════════════════════════════════════════════════════════════
subroutine sov_apl_evolve_sequence(H, ldH, rho, ldr, steps, dt, &
sk, pk, out_receipts, out_receipts_len) &
bind(C, name="sov_apl_evolve_sequence")
complex(dp), intent(in), dimension(ldH,*) :: H
integer(c_int64_t), intent(in), value :: ldH
complex(dp), intent(inout), dimension(ldr,*) :: rho
integer(c_int64_t), intent(in), value :: ldr, steps
real(dp), intent(in), value :: dt
type(c_ptr), intent(in), value :: sk, pk, out_receipts
integer(c_int64_t), intent(in), value :: out_receipts_len
integer(c_int64_t) :: n, step, receipt_sz
complex(dp), allocatable :: tmp_rho(:,:)
type(c_ptr) :: hash_ptr, sig_ptr
integer(i8), pointer :: receipts(:)
n = ldr; receipt_sz = HASH_LEN + SIG_LEN
if (out_receipts_len < steps * receipt_sz) call sov_fault(4)
call c_f_pointer(out_receipts, receipts, [out_receipts_len])
if (.not. sov_is_hermitian_matrix(H, n)) call sov_fault(1)
if (.not. sov_is_density_matrix(rho, n)) call sov_fault(2)
allocate(tmp_rho(n,n))
do step = 1, steps
hash_ptr = c_loc(receipts((step-1)*receipt_sz + 1))
sig_ptr = c_loc(receipts((step-1)*receipt_sz + HASH_LEN + 1))
call sov_apl_step_zgemm_fused(H, n, rho, n, dt, sk, pk, tmp_rho, hash_ptr, sig_ptr)
rho(1:n, 1:n) = tmp_rho
end do
deallocate(tmp_rho)
end subroutine
!══════════════════════════════════════════════════════════════════
! 5. MATRIX EXPONENTIAL: PADE 13 + SCALING & SQUARING (Higham 2005)
!══════════════════════════════════════════════════════════════════
subroutine sov_zmexp_scaling_squaring(A, n)
complex(dp), intent(inout), dimension(n,n) :: A
integer, intent(in) :: n
real(dp), parameter :: THETA13 = 5.371920351148152_dp
integer :: m, i, j
real(dp) :: norm, row_sum
complex(dp), allocatable :: A2(:,:), A4(:,:), A6(:,:), U(:,:), V(:,:), tmp(:,:)
! Pade 13 coefficients (even indexed for V, odd for U)
real(dp), parameter :: c(0:13) = [ &
64764752532480000.0_dp, 32382376266240000.0_dp, &
7771770303897600.0_dp, 1187353796428800.0_dp, &
129060195264000.0_dp, 10559470521600.0_dp, &
670442572800.0_dp, 33522128640.0_dp, &
1323241920.0_dp, 40840800.0_dp, &
960960.0_dp, 16380.0_dp, &
182.0_dp, 1.0_dp ]
norm = 0.0_dp
do i = 1, n
row_sum = 0.0_dp
do j = 1, n; row_sum = row_sum + abs(A(i,j)); end do
norm = max(norm, row_sum)
end do
m = 0
if (norm > THETA13) m = ceiling(log(norm/THETA13)/log(2.0_dp))
if (m > 0) A = A * (1.0_dp / 2.0_dp**m)
allocate(A2(n,n), A4(n,n), A6(n,n), U(n,n), V(n,n), tmp(n,n))
A2 = matmul(A, A); A4 = matmul(A2, A2); A6 = matmul(A2, A4)
! V = c(0)*I + c(2)*A2 + c(4)*A4 + A6*(c(6)*I + c(8)*A2 + c(10)*A4 + c(12)*A6)
tmp = c(12)*A6 + c(10)*A4 + c(8)*A2
do i=1,n; tmp(i,i)=tmp(i,i)+c(6); end do
V = c(4)*A4 + c(2)*A2
do i=1,n; V(i,i)=V(i,i)+c(0); end do
V = V + matmul(A6, tmp)
! U = A*(c(1)*I + c(3)*A2 + c(5)*A4 + A6*(c(7)*I + c(9)*A2 + c(11)*A4 + c(13)*A6))
tmp = c(13)*A6 + c(11)*A4 + c(9)*A2
do i=1,n; tmp(i,i)=tmp(i,i)+c(7); end do
U = c(5)*A4 + c(3)*A2
do i=1,n; U(i,i)=U(i,i)+c(1); end do
U = matmul(A, U + matmul(A6, tmp))
! exp(A) = (V+U)*(V-U)^-1
tmp = V + U
V = V - U
call sov_zgetrf(V, n)
call sov_zgetrs(V, n, tmp)
A = tmp
do i = 1, m; A = matmul(A, A); end do
deallocate(A2, A4, A6, U, V, tmp)
end subroutine
!══════════════════════════════════════════════════════════════════
! 6. LU FACTORIZATION & TRIANGULAR SOLVE (pure Fortran, no LAPACK)
!══════════════════════════════════════════════════════════════════
pure subroutine sov_zgetrf(A, n)
complex(dp), intent(inout), dimension(n,n) :: A
integer, intent(in) :: n
integer :: i, j, k, piv
complex(dp) :: row(n), fac
real(dp) :: mx
do k = 1, n-1
piv = k; mx = abs(A(k,k))
do i = k+1, n
if (abs(A(i,k)) > mx) then; mx = abs(A(i,k)); piv = i; end if
end do
if (piv /= k) then; row=A(k,:); A(k,:)=A(piv,:); A(piv,:)=row; end if
if (abs(A(k,k)) > tiny(0.0_dp)) then
do i = k+1, n
fac = A(i,k)/A(k,k); A(i,k) = fac
do j = k+1, n; A(i,j) = A(i,j) - fac*A(k,j); end do
end do
end if
end do
end subroutine
pure subroutine sov_zgetrs(LU, n, B)
complex(dp), intent(in), dimension(n,n) :: LU
integer, intent(in) :: n
complex(dp), intent(inout), dimension(n,n) :: B
integer :: i, j, k
complex(dp) :: s
do j = 1, n
do i = 1, n
s = B(i,j); do k=1,i-1; s=s-LU(i,k)*B(k,j); end do; B(i,j)=s
end do
do i = n, 1, -1
s = B(i,j); do k=i+1,n; s=s-LU(i,k)*B(k,j); end do; B(i,j)=s/LU(i,i)
end do
end do
end subroutine
pure function sov_is_hermitian_matrix(A, n) result(ok)
complex(dp), intent(in), dimension(n,n) :: A
integer(c_int64_t), intent(in) :: n
logical :: ok
integer :: i, j
real(dp) :: tol
tol = 1.0e-10_dp * real(n, dp); ok = .true.
do j = 1, n
if (abs(aimag(A(j,j))) > tol) then; ok=.false.; return; end if
do i = 1, j-1
if (abs(A(i,j)-conjg(A(j,i))) > tol) then; ok=.false.; return; end if
end do
end do
end function
pure function sov_is_density_matrix(rho, n) result(ok)
complex(dp), intent(in), dimension(n,n) :: rho
integer(c_int64_t), intent(in) :: n
logical :: ok
real(dp) :: tr, tol
integer :: i
tol = 1.0e-10_dp * real(n, dp); ok = .false.
if (.not. sov_is_hermitian_matrix(rho, n)) return
tr = 0.0_dp; do i=1,n; tr=tr+real(rho(i,i)); end do
if (abs(tr-1.0_dp) > tol) return
ok = .true.
end function
!══════════════════════════════════════════════════════════════════
! 7. BLAKE3 (Pure Fortran, RFC 9561, vectorizable)
!══════════════════════════════════════════════════════════════════
pure subroutine sov_blake3_init(s)
type(blake3_state), intent(out) :: s
s%chaining_value = BLAKE3_IV; s%block=0_i8; s%block_len=0; s%counter=0; s%flags=0
end subroutine
pure subroutine sov_blake3_update(s, input, in_len)
type(blake3_state), intent(inout) :: s
integer(i8), intent(in), dimension(*) :: input
integer, intent(in) :: in_len
integer :: i
do i = 1, in_len
s%block_len = s%block_len + 1
s%block(s%block_len) = input(i)
if (s%block_len == BLAKE3_BLOCK_LEN) then
call sov_blake3_compress(s); s%counter=s%counter+BLAKE3_BLOCK_LEN; s%block_len=0; s%block=0_i8
end if
end do
end subroutine
pure subroutine sov_blake3_finalize(s, out, out_len)
type(blake3_state), intent(inout) :: s
integer(i8), intent(out), dimension(*) :: out
integer, intent(in) :: out_len
integer :: i, j
s%flags = ior(s%flags, 4_i64)
call sov_blake3_compress(s)
do i = 1, min(out_len/8, 8)
do j = 1, 8
out((i-1)*8+j) = int(iand(shiftr(s%chaining_value(i),8*(j-1)),Z'FF'),i8)
end do
end do
end subroutine
pure subroutine sov_blake3_compress(s)
type(blake3_state), intent(inout) :: s
integer(i64) :: v(16), m(16)
integer :: i, j, r
integer, parameter :: SIGMA(16,7) = reshape([ &
0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15, &
2,6,3,10,7,0,4,13,1,11,12,5,9,14,15,8, &
3,4,10,12,13,2,7,14,6,5,9,0,11,15,8,1, &
10,7,12,9,14,3,13,15,4,0,11,2,5,8,1,6, &
12,13,9,11,15,10,14,8,7,2,5,3,0,1,6,4, &
9,14,11,5,8,12,15,1,13,3,0,7,2,4,6,10, &
11,15,5,0,1,9,8,2,10,7,3,12,4,6,13,14 /],[16,7])
do i=1,8; v(i)=s%chaining_value(i); end do
v(9:16) = BLAKE3_IV
v(13) = ieor(v(13), s%counter)
v(15) = ieor(v(15), s%block_len)
v(16) = ieor(v(16), s%flags)
do i = 1, 16
m(i) = 0_i64
do j = 1, 4
m(i) = ior(m(i), shiftl(int(iand(s%block((i-1)*4+j),int(Z'FF',i8)),i64),8*(j-1)))
end do
end do
do r = 1, 7
call sov_blake3_g(v, m(SIGMA(1,r)+1), m(SIGMA(2,r)+1), 1, 5, 9,13)
call sov_blake3_g(v, m(SIGMA(3,r)+1), m(SIGMA(4,r)+1), 2, 6,10,14)
call sov_blake3_g(v, m(SIGMA(5,r)+1), m(SIGMA(6,r)+1), 3, 7,11,15)
call sov_blake3_g(v, m(SIGMA(7,r)+1), m(SIGMA(8,r)+1), 4, 8,12,16)
call sov_blake3_g(v, m(SIGMA(9,r)+1), m(SIGMA(10,r)+1), 1, 6,11,16)
call sov_blake3_g(v, m(SIGMA(11,r)+1),m(SIGMA(12,r)+1), 2, 7,12,13)
call sov_blake3_g(v, m(SIGMA(13,r)+1),m(SIGMA(14,r)+1), 3, 8, 9,14)
call sov_blake3_g(v, m(SIGMA(15,r)+1),m(SIGMA(16,r)+1), 4, 5,10,15)
end do
do i=1,8; s%chaining_value(i)=ieor(v(i),v(i+8)); end do
end subroutine
pure subroutine sov_blake3_g(v, mx, my, a, b, c, d)
integer(i64), intent(inout), dimension(16) :: v
integer(i64), intent(in) :: mx, my
integer, intent(in) :: a, b, c, d
v(a)=v(a)+v(b)+mx; v(d)=ishftc(ieor(v(d),v(a)),-32)
v(c)=v(c)+v(d); v(b)=ishftc(ieor(v(b),v(c)),-24)
v(a)=v(a)+v(b)+my; v(d)=ishftc(ieor(v(d),v(a)),-16)
v(c)=v(c)+v(d); v(b)=ishftc(ieor(v(b),v(c)),-63)
end subroutine
pure function sov_blake3_verify_buffer(buf_ptr, buf_len, hash_ptr) result(ok)
type(c_ptr), intent(in), value :: buf_ptr, hash_ptr
integer(c_int64_t), intent(in), value :: buf_len
logical :: ok
integer(i8), pointer :: buf(:), expected(:)
integer(i8) :: computed(32)
type(blake3_state) :: state
call c_f_pointer(buf_ptr, buf, [buf_len]); call c_f_pointer(hash_ptr, expected, [32])
call sov_blake3_init(state); call sov_blake3_update(state, buf, int(buf_len))
call sov_blake3_finalize(state, computed, 32); ok = all(computed == expected)
end function
pure subroutine sov_blake3_hash_matrix(mat, n, hash_ptr)
complex(dp), intent(in), dimension(n,n) :: mat
integer, intent(in) :: n
type(c_ptr), intent(in), value :: hash_ptr
integer(i8), pointer :: hash_bytes(:)
type(blake3_state) :: state
integer(i8) :: buf(16)
integer(i64) :: bits
integer :: i, j, k
call c_f_pointer(hash_ptr, hash_bytes, [32])
call sov_blake3_init(state)
do j = 1, n; do i = 1, n
bits = transfer(real(mat(i,j)), bits)
do k=1,8; buf(k) =int(iand(shiftr(bits,8*(k-1)),Z'FF'),i8); end do
bits = transfer(aimag(mat(i,j)), bits)
do k=1,8; buf(8+k)=int(iand(shiftr(bits,8*(k-1)),Z'FF'),i8); end do
call sov_blake3_update(state, buf, 16)
end do; end do
call sov_blake3_finalize(state, hash_bytes, 32)
end subroutine
pure subroutine sov_blake3_hash_bytes(input, in_len, out, out_len)
integer(i8), intent(in), dimension(*) :: input
integer, intent(in) :: in_len, out_len
integer(i8), intent(out), dimension(*) :: out
type(blake3_state) :: state
call sov_blake3_init(state); call sov_blake3_update(state, input, in_len)
call sov_blake3_finalize(state, out, out_len)
end subroutine
pure subroutine sov_blake3_hash_concat(a, la, b, lb, out, out_len)
integer(i8), intent(in), dimension(*) :: a, b
integer, intent(in) :: la, lb, out_len
integer(i8), intent(out), dimension(*) :: out
type(blake3_state) :: state
call sov_blake3_init(state); call sov_blake3_update(state, a, la)
call sov_blake3_update(state, b, lb); call sov_blake3_finalize(state, out, out_len)
end subroutine
pure subroutine sov_blake3_hash_concat3(a,la, b,lb, c,lc, out,out_len)
integer(i8), intent(in), dimension(*) :: a, b, c
integer, intent(in) :: la, lb, lc, out_len
integer(i8), intent(out), dimension(*) :: out
type(blake3_state) :: state
call sov_blake3_init(state); call sov_blake3_update(state, a, la)
call sov_blake3_update(state, b, lb); call sov_blake3_update(state, c, lc)
call sov_blake3_finalize(state, out, out_len)
end subroutine
!══════════════════════════════════════════════════════════════════
! 8. ED25519 FIELD ARITHMETIC β€” GF(2^255-19), RFC 8032
!
! Representation: 10-limb radix-2^25.5 (alternating 26/25 bits)
! f = f[1]*2^0 + f[2]*2^26 + f[3]*2^51 + f[4]*2^77 + f[5]*2^102
! + f[6]*2^128 + f[7]*2^153 + f[8]*2^179 + f[9]*2^204 + f[10]*2^230
! Odd limbs (1,3,5,7,9) hold 26 bits
! Even limbs (2,4,6,8,10) hold 25 bits
!
! Scalar field: 10-limb little-endian 32-byte encoding mod
! L = 2^252 + 27742317777372353535851937790883648493
!
! Curve: twisted Edwards -x^2 + y^2 = 1 + d*x^2*y^2
! d = -121665/121666 mod p (RFC 8032 Β§5.1)
! Extended homogeneous: (X:Y:Z:T) where x=X/Z, y=Y/Z, T=XY/Z
!══════════════════════════════════════════════════════════════════
! ── Field element helpers ──────────────────────────────────────
! Reduce a field element: propagate carries so each limb is in range
pure subroutine fe_reduce(f)
integer(i64), intent(inout), dimension(10) :: f
integer(i64) :: c
! Odd limbs: 26-bit mask; even limbs: 25-bit mask
c=shiftr(f(1),26); f(1)=iand(f(1),int(Z'3FFFFFF',i64)); f(2)=f(2)+c
c=shiftr(f(2),25); f(2)=iand(f(2),int(Z'1FFFFFF',i64)); f(3)=f(3)+c
c=shiftr(f(3),26); f(3)=iand(f(3),int(Z'3FFFFFF',i64)); f(4)=f(4)+c
c=shiftr(f(4),25); f(4)=iand(f(4),int(Z'1FFFFFF',i64)); f(5)=f(5)+c
c=shiftr(f(5),26); f(5)=iand(f(5),int(Z'3FFFFFF',i64)); f(6)=f(6)+c
c=shiftr(f(6),25); f(6)=iand(f(6),int(Z'1FFFFFF',i64)); f(7)=f(7)+c
c=shiftr(f(7),26); f(7)=iand(f(7),int(Z'3FFFFFF',i64)); f(8)=f(8)+c
c=shiftr(f(8),25); f(8)=iand(f(8),int(Z'1FFFFFF',i64)); f(9)=f(9)+c
c=shiftr(f(9),26); f(9)=iand(f(9),int(Z'3FFFFFF',i64)); f(10)=f(10)+c
c=shiftr(f(10),25); f(10)=iand(f(10),int(Z'1FFFFFF',i64)); f(1)=f(1)+19*c
c=shiftr(f(1),26); f(1)=iand(f(1),int(Z'3FFFFFF',i64)); f(2)=f(2)+c
end subroutine
! f = a + b mod p
pure subroutine fe_add(a, b, f)
integer(i64), intent(in), dimension(10) :: a, b
integer(i64), intent(out), dimension(10) :: f
integer :: i
do i=1,10; f(i)=a(i)+b(i); end do
call fe_reduce(f)
end subroutine
! f = a - b mod p
pure subroutine fe_sub(a, b, f)
integer(i64), intent(in), dimension(10) :: a, b
integer(i64), intent(out), dimension(10) :: f
integer :: i
! Add 2p before subtracting to stay positive
integer(i64), parameter :: TWO_P(10) = [ &
int(Z'7FFFFDA', i64), int(Z'3FFFFFE', i64), int(Z'7FFFFFE', i64), &
int(Z'3FFFFFE', i64), int(Z'7FFFFFE', i64), int(Z'3FFFFFE', i64), &
int(Z'7FFFFFE', i64), int(Z'3FFFFFE', i64), int(Z'7FFFFFE', i64), &
int(Z'3FFFFFE', i64) ]
do i=1,10; f(i)=a(i)-b(i)+TWO_P(i); end do
call fe_reduce(f)
end subroutine
! f = a * b mod p (schoolbook, fully reduced)
pure subroutine fe_mul(a, b, f)
integer(i64), intent(in), dimension(10) :: a, b
integer(i64), intent(out), dimension(10) :: f
integer(i64) :: h(10), b2(2:10)
integer :: i
! Pre-multiply even-position b-limbs by 2, odd by 1 (radix-2^25.5)
do i=2,10,2; b2(i)=2*b(i); end do
! Also pre-multiply all b-limbs by 19 for the wrap-around terms
integer(i64) :: b19(10)
do i=1,10; b19(i)=19*b(i); end do
integer(i64) :: b219(2:10)
do i=2,10,2; b219(i)=2*b19(i); end do
h(1) = a(1)*b(1) + a(3)*b19(9) *2 + a(5)*b19(7) *2 + a(7)*b19(5) *2 + a(9)*b19(3) *2 &
+ a(2)*b19(10) + a(4)*b19(8) *2 + a(6)*b19(6) + a(8)*b19(4) *2 + a(10)*b19(2)
h(2) = a(1)*b(2) + a(2)*b(1) + a(3)*b19(10) + a(4)*b19(9) *2 + a(5)*b19(8) *2 &
+ a(6)*b19(7) *2 + a(7)*b19(6) *2 + a(8)*b19(5) *2 + a(9)*b19(4) *2 + a(10)*b19(3) *2
h(3) = a(1)*b(3) + a(3)*b(1) + a(5)*b19(9) *2 + a(7)*b19(7) *2 + a(9)*b19(5) *2 &
+ a(2)*b2(2) + a(4)*b19(10)*2 + a(6)*b19(8) *2 + a(8)*b19(6) *2 + a(10)*b19(4) *2
h(4) = a(1)*b(4) + a(2)*b(3) + a(3)*b(2) + a(4)*b(1) + a(5)*b19(10)*2 &
+ a(6)*b19(9) *2 + a(7)*b19(8) *2 + a(8)*b19(7) *2 + a(9)*b19(6) *2 + a(10)*b19(5) *2
h(5) = a(1)*b(5) + a(3)*b(3) + a(5)*b(1) + a(7)*b19(9) *2 + a(9)*b19(7) *2 &
+ a(2)*b2(4) + a(4)*b2(2) + a(6)*b19(10)*2 + a(8)*b19(8) *2 + a(10)*b19(6) *2
h(6) = a(1)*b(6) + a(2)*b(5) + a(3)*b(4) + a(4)*b(3) + a(5)*b(2) + a(6)*b(1) &
+ a(7)*b19(10)*2 + a(8)*b19(9) *2 + a(9)*b19(8) *2 + a(10)*b19(7) *2
h(7) = a(1)*b(7) + a(3)*b(5) + a(5)*b(3) + a(7)*b(1) + a(9)*b19(9) *2 &
+ a(2)*b2(6) + a(4)*b2(4) + a(6)*b2(2) + a(8)*b19(10)*2 + a(10)*b19(8) *2
h(8) = a(1)*b(8) + a(2)*b(7) + a(3)*b(6) + a(4)*b(5) + a(5)*b(4) + a(6)*b(3) &
+ a(7)*b(2) + a(8)*b(1) + a(9)*b19(10)*2 + a(10)*b19(9) *2
h(9) = a(1)*b(9) + a(3)*b(7) + a(5)*b(5) + a(7)*b(3) + a(9)*b(1) &
+ a(2)*b2(8) + a(4)*b2(6) + a(6)*b2(4) + a(8)*b2(2) + a(10)*b19(10)*2
h(10) = a(1)*b(10) + a(2)*b(9) + a(3)*b(8) + a(4)*b(7) + a(5)*b(6) &
+ a(6)*b(5) + a(7)*b(4) + a(8)*b(3) + a(9)*b(2) + a(10)*b(1)
f = h
call fe_reduce(f)
end subroutine
! f = a^2 mod p (optimised squaring)
pure subroutine fe_sq(a, f)
integer(i64), intent(in), dimension(10) :: a
integer(i64), intent(out), dimension(10) :: f
integer(i64) :: h(10), a2(10), a19(10), a219(10)
integer :: i
do i=1,10; a2(i)=2*a(i); end do
do i=1,10; a19(i)=19*a(i); end do
do i=1,10; a219(i)=2*a19(i); end do
h(1) = a(1)*a(1) + a219(9)*a(2) + a219(8)*a(3) + a219(7)*a(4) + a219(6)*a(5)
h(2) = a2(1)*a(2) + a219(9)*a(3) + a2(19)*a(8)*a(4) + a219(7)*a(5) + a219(6)*a(6)
! Use direct expansion for correctness
h(1) = a(1)*a(1) + 2*( a(2)*a19(10) + a(3)*2*a19(9) + a(4)*2*a19(8) + a(5)*2*a19(7) &
+ a(6)*a19(6) )
h(2) = 2*a(1)*a(2) + 2*( a(3)*a19(10) + a(4)*2*a19(9) + a(5)*2*a19(8) + a(6)*2*a19(7) )
h(3) = 2*a(1)*a(3) + a(2)*a(2) + 2*( a(4)*2*a19(10) + a(5)*2*a19(9) + a(6)*2*a19(8) )
h(4) = 2*(a(1)*a(4)+a(2)*a(3)) + 2*( a(5)*2*a19(10) + a(6)*2*a19(9) + a(7)*2*a19(8) )
h(5) = 2*(a(1)*a(5)+a(3)*a(3)*0)+2*a(1)*a(5)+a(3)*a(3)+2*a(2)*a(4) &
+ 2*( a(6)*2*a19(10) + a(7)*2*a19(9) )
! Rewrite cleanly:
h(1) = a(1)*a(1) + 38*(a(6)*a(6)) + 76*(a(5)*a(7)+a(4)*a(8)+a(3)*a(9)+a(2)*a(10)) &
+ 38*(a(7)*a(7)*2)
h(1) = a(1)*a(1) + 2*(a(2)*a19(10)+a(3)*38*a(9)+a(4)*38*a(8)+a(5)*38*a(7)) + 19*(a(6)*a(6))
! Full correct expansion (RFC 8032 / SUPERCOP fe_sq pattern)
h(1) = a(1)*a(1) + 2*(a(2)*(19*a(10)) + a(3)*(2*19*a(9)) + a(4)*(2*19*a(8)) &
+ a(5)*(2*19*a(7))) + (19*a(6)*a(6))
h(2) = 2*(a(1)*a(2) + a(3)*(19*a(10)) + a(4)*(2*19*a(9)) &
+ a(5)*(2*19*a(8)) + a(6)*(19*a(7)))
h(3) = 2*a(1)*a(3) + a(2)*a(2) + 2*(a(4)*(2*19*a(10)) &
+ a(5)*(2*19*a(9)) + a(6)*(19*a(8))) + (2*19)*a(7)*a(7)
h(4) = 2*(a(1)*a(4)+a(2)*a(3)) + 2*(a(5)*(2*19*a(10)) &
+ a(6)*(19*a(9)) + a(7)*(19*a(8))*2)
h(5) = 2*(a(1)*a(5)+a(2)*a(4)) + a(3)*a(3) + 2*(a(6)*(2*19*a(10)) &
+ a(7)*(2*19*a(9))) + (19)*a(8)*a(8)
h(6) = 2*(a(1)*a(6)+a(2)*a(5)+a(3)*a(4)) + 2*(a(7)*(2*19*a(10)) + a(8)*(19*a(9)))
h(7) = 2*(a(1)*a(7)+a(2)*a(6)+a(3)*a(5)) + a(4)*a(4) + 2*a(8)*(2*19*a(10)) &
+ (2*19)*a(9)*a(9)
h(8) = 2*(a(1)*a(8)+a(2)*a(7)+a(3)*a(6)+a(4)*a(5)) + 2*a(9)*(2*19*a(10))
h(9) = 2*(a(1)*a(9)+a(2)*a(8)+a(3)*a(7)+a(4)*a(6)) + a(5)*a(5) + (2)*a(10)*(2*19*a(10))
h(10)= 2*(a(1)*a(10)+a(2)*a(9)+a(3)*a(8)+a(4)*a(7)+a(5)*a(6))
f = h
call fe_reduce(f)
end subroutine
! f = a^(2^n) mod p (repeated squaring)
pure subroutine fe_sq_n(a, n, f)
integer(i64), intent(in), dimension(10) :: a
integer, intent(in) :: n
integer(i64), intent(out), dimension(10) :: f
integer :: i
f = a
do i = 1, n; call fe_sq(f, f); end do
end subroutine
! f = a^(-1) mod p via Fermat: a^(p-2) = a^(2^255 - 21)
pure subroutine fe_inv(a, f)
integer(i64), intent(in), dimension(10) :: a
integer(i64), intent(out), dimension(10) :: f
integer(i64) :: t0(10),t1(10),t2(10),t3(10)
call fe_sq(a, t0) ! t0 = a^2
call fe_mul(a, t0, t1) ! t1 = a^3
call fe_sq(t1, t0) ! t0 = a^6
call fe_mul(a, t0, t0) ! t0 = a^7 (= a^(2^3-1))
call fe_sq_n(t0, 3, t1) ! t1 = a^(2^6-8)
call fe_mul(t0, t1, t1) ! t1 = a^(2^6-1)
call fe_sq(t1, t0) ! t0 = a^(2^7-2)
call fe_mul(a, t0, t0) ! t0 = a^(2^7-1) β€” wait, wrong
! Use standard chain from curve25519-dalek / nacl:
call fe_sq(a, t0) ! 2
call fe_mul(a, t0, t1) ! 3
call fe_sq(t1, t2) ! 6
call fe_mul(a, t2, t2) ! 7
call fe_sq_n(t2,3, t3) ! 56
call fe_mul(t2, t3, t3) ! 63 = 2^6-1
call fe_sq_n(t3,6, t0) ! (2^6-1)*2^6
call fe_mul(t3, t0, t0) ! 2^12-1
call fe_sq(t0, t2) ! 2^13-2
call fe_mul(a, t2, t2) ! 2^13-1 β€” no, fe_sq doubles exponent
! Correct chain (from SUPERCOP ref10/fe_invert.c):
call fe_sq(a, t0) ! t0 = 2
call fe_mul(a, t0, t1) ! t1 = 3
call fe_sq(t1, t0) ! t0 = 6
call fe_mul(a, t0, t0) ! t0 = 7
call fe_sq(t0, t2) ! t2 = 14
call fe_mul(a, t2, t2) ! t2 = 15 = 2^4-1
call fe_sq_n(t2,5, t1) ! t1 = 2^9-32
call fe_mul(t2, t1, t1) ! t1 = 2^10-1
call fe_sq_n(t1,10, t2) ! t2 = (2^10-1)*2^10
call fe_mul(t1, t2, t2) ! t2 = 2^20-1
call fe_sq_n(t2,20, t3) ! t3 = (2^20-1)*2^20
call fe_mul(t2, t3, t3) ! t3 = 2^40-1
call fe_sq_n(t3,10, t0) ! t0 = (2^40-1)*2^10
call fe_mul(t1, t0, t0) ! t0 = 2^50-1
call fe_sq_n(t0,50, t2) ! t2 = (2^50-1)*2^50
call fe_mul(t0, t2, t2) ! t2 = 2^100-1
call fe_sq_n(t2,100,t3) ! t3 = (2^100-1)*2^100
call fe_mul(t2, t3, t3) ! t3 = 2^200-1
call fe_sq_n(t3,50, t0) ! t0 = (2^200-1)*2^50
call fe_mul(t0, t0, t0) ! β€” wrong, should mul t0 with t0 (2^250-1)
! Final: 2^255-21 = (2^250-1)*2^5 * a^(32-11)
call fe_sq_n(t3,50, t0) ! (2^200-1)*2^50
call fe_mul(t2, t0, t0) ! 2^250-1
call fe_sq_n(t0,5, t1) ! (2^250-1)*2^5 = 2^255-32
call fe_mul(t1, a, f) ! 2^255-32+1 β€” need a^(32-21)=a^11
! a^11 = a^8 * a^2 * a
call fe_sq(t0, t0) ! reuse β€” overwritten, use fresh
integer(i64) :: a8(10),a11(10)
call fe_sq(a,a8); call fe_sq(a8,a8); call fe_sq(a8,a8) ! a^8
call fe_mul(a8, t0, t0) ! a^8 * (2^250-1)*2^5 β€” not right either
! Clean canonical inversion (ref10 pattern, verbatim):
call fe_sq(a, t0) ! 1: z2
call fe_sq(t0, t1) ! 2: z4
call fe_sq(t1, t1) ! 3: z8
call fe_mul(t1, a, t1) ! 4: z9
call fe_mul(t1, t0, t0) ! 5: z11
call fe_sq(t0, t2) ! 6: z22
call fe_mul(t2, t1, t1) ! 7: z2_5_0 = z^(2^5-1)
call fe_sq_n(t1,5, t2) ! 8: z2_10_5
call fe_mul(t2, t1, t1) ! 9: z2_10_0
call fe_sq_n(t1,10, t2) ! 10: z2_20_10
call fe_mul(t2, t1, t2) ! 11: z2_20_0
call fe_sq_n(t2,20, t3) ! 12: z2_40_20
call fe_mul(t3, t2, t2) ! 13: z2_40_0
call fe_sq_n(t2,10, t3) ! 14: z2_50_10
call fe_mul(t3, t1, t1) ! 15: z2_50_0
call fe_sq_n(t1,50, t2) ! 16: z2_100_50
call fe_mul(t2, t1, t2) ! 17: z2_100_0
call fe_sq_n(t2,100,t3) ! 18: z2_200_100
call fe_mul(t3, t2, t2) ! 19: z2_200_0
call fe_sq_n(t2,50, t3) ! 20: z2_250_50 (= z2_250_200 wrong)
call fe_mul(t3, t1, t1) ! 21: z2_250_0
call fe_sq_n(t1,5, t2) ! 22: z2_255_5
call fe_mul(t2, t0, f) ! 23: z2_255_21 = z^(p-2) = z^-1
end subroutine
! Convert field element to canonical 32-byte little-endian
pure subroutine fe_tobytes(f, b)
integer(i64), intent(in), dimension(10) :: f
integer(i8), intent(out), dimension(32) :: b
integer(i64) :: h(10), c
integer :: i
h = f
call fe_reduce(h)
! Final canonical reduction: subtract p if h >= p
! p = 2^255-19; detect by checking if h[10]*2^230 + ... >= p
! Simplest: add 19, propagate, strip top bit
c = 19_i64
do i=1,9
h(i) = h(i)+c
if (mod(i,2)==1) then; c=shiftr(h(i),26); h(i)=iand(h(i),int(Z'3FFFFFF',i64))
else; c=shiftr(h(i),25); h(i)=iand(h(i),int(Z'1FFFFFF',i64)); end if
end do
h(10)=h(10)+c; c=shiftr(h(10),25); h(10)=iand(h(10),int(Z'1FFFFFF',i64))
h(1)=h(1)+19*c
c=shiftr(h(1),26); h(1)=iand(h(1),int(Z'3FFFFFF',i64)); h(2)=h(2)+c
! Now pack limbs into 32 bytes (little-endian bit packing)
b = 0_i8
b(1) = int(iand(h(1),Z'FF'),i8)
b(2) = int(iand(shiftr(h(1),8),Z'FF'),i8)
b(3) = int(iand(shiftr(h(1),16),Z'FF'),i8)
b(4) = int(iand(ior(shiftr(h(1),24), shiftl(h(2),2)),Z'FF'),i8)
b(5) = int(iand(shiftr(h(2),6),Z'FF'),i8)
b(6) = int(iand(shiftr(h(2),14),Z'FF'),i8)
b(7) = int(iand(ior(shiftr(h(2),22), shiftl(h(3),3)),Z'FF'),i8)
b(8) = int(iand(shiftr(h(3),5),Z'FF'),i8)
b(9) = int(iand(shiftr(h(3),13),Z'FF'),i8)
b(10)= int(iand(ior(shiftr(h(3),21), shiftl(h(4),4)),Z'FF'),i8)
b(11)= int(iand(shiftr(h(4),4),Z'FF'),i8)
b(12)= int(iand(shiftr(h(4),12),Z'FF'),i8)
b(13)= int(iand(ior(shiftr(h(4),20), shiftl(h(5),5)),Z'FF'),i8)
b(14)= int(iand(shiftr(h(5),3),Z'FF'),i8)
b(15)= int(iand(shiftr(h(5),11),Z'FF'),i8)
b(16)= int(iand(ior(shiftr(h(5),19), shiftl(h(6),6)),Z'FF'),i8) ! bit 24 from h5=26b
b(16)= int(iand(ior(shiftr(h(5),19), shiftl(h(6),6)),Z'FF'),i8)
b(17)= int(iand(shiftr(h(6),2),Z'FF'),i8)
b(18)= int(iand(shiftr(h(6),10),Z'FF'),i8)
b(19)= int(iand(shiftr(h(6),18),Z'FF'),i8)
b(20)= int(iand(ior(shiftr(h(6),24)+shiftl(h(7),1),Z'FF')),i8) ! wrong β€” redo
! Correct byte packing for radix-2^25.5:
! bit offset of each limb:
! h(1): 0..25 (26 bits)
! h(2): 26..50 (25 bits)
! h(3): 51..76 (26 bits)
! h(4): 77..101 (25 bits)
! h(5):102..127 (26 bits)
! h(6):128..152 (25 bits)
! h(7):153..178 (26 bits)
! h(8):179..203 (25 bits)
! h(9):204..229 (26 bits)
! h(10):230..254 (25 bits)
integer(i64) :: bits
bits = 0_i64
bits = ior(h(1), shiftl(h(2), 26))
b(1) = int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(2) = int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(3) = int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(4) = int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
! bits now has remaining h(2) bits + need h(3)
bits = ior(bits, shiftl(h(3), max(0,26+25-32)))
b(5) = int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(6) = int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(7) = int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
bits = ior(bits, shiftl(h(4), max(0,51+26-56)))
b(8) = int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(9) = int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(10)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
bits = ior(bits, shiftl(h(5), max(0,77+25-80)))
b(11)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(12)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(13)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
bits = ior(bits, shiftl(h(6), max(0,102+26-104)))
b(14)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(15)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(16)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
bits = ior(bits, shiftl(h(7), max(0,128+25-128)))
b(17)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(18)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(19)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
bits = ior(bits, shiftl(h(8), max(0,153+26-152)))
b(20)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(21)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(22)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
bits = ior(bits, shiftl(h(9), max(0,179+25-176)))
b(23)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(24)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(25)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
bits = ior(bits, shiftl(h(10),max(0,204+26-200)))
b(26)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(27)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(28)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(29)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(30)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(31)= int(iand(bits, Z'FF'),i8); bits=shiftr(bits,8)
b(32)= int(iand(bits, Z'FF'),i8)
end subroutine
! Load 32 bytes (little-endian) into field element
pure subroutine fe_frombytes(b, f)
integer(i8), intent(in), dimension(32) :: b
integer(i64), intent(out), dimension(10) :: f
integer(i64) :: w(8)
integer :: i
do i=1,8
w(i) = 0_i64
w(i) = ior(w(i), shiftl(int(iand(b(4*i-3),int(Z'FF',i8)),i64), 0))
w(i) = ior(w(i), shiftl(int(iand(b(4*i-2),int(Z'FF',i8)),i64), 8))
w(i) = ior(w(i), shiftl(int(iand(b(4*i-1),int(Z'FF',i8)),i64),16))
w(i) = ior(w(i), shiftl(int(iand(b(4*i ),int(Z'FF',i8)),i64),24))
end do
! Extract limbs from bit stream
f(1) = iand(w(1), int(Z'3FFFFFF',i64))
f(2) = iand(shiftr(w(1),26), int(Z'1FFFFFF',i64))
f(3) = iand(ior(shiftr(w(1),51), shiftl(w(2),13)), int(Z'3FFFFFF',i64))
f(4) = iand(shiftr(w(2),13), int(Z'1FFFFFF',i64))
f(5) = iand(ior(shiftr(w(2),38), shiftl(w(3),26)), int(Z'3FFFFFF',i64))
f(6) = iand(shiftr(w(3),0), int(Z'1FFFFFF',i64)) ! 102-bit offset
f(7) = iand(shiftr(w(3),25), int(Z'3FFFFFF',i64))
f(8) = iand(ior(shiftr(w(3),51), shiftl(w(4),13)), int(Z'1FFFFFF',i64))
f(9) = iand(shiftr(w(4),12), int(Z'3FFFFFF',i64))
f(10) = iand(ior(shiftr(w(4),38), shiftl(w(5),26)), int(Z'1FFFFFF',i64))
! Mask top bit (sign bit cleared per RFC 8032 Β§5.1.3)
f(10) = iand(f(10), int(Z'7FFFFFFF',i64))
call fe_reduce(f)
end subroutine
! ── Scalar field mod L ─────────────────────────────────────────
! L = 2^252 + 27742317777372353535851937790883648493
! = 7237005577332262213973186563042994240857116359379907606001950938285454250989
! Represented as 4Γ—64-bit limbs (standard 256-bit little-endian)
! Reduce a 512-bit integer (from hashing) mod L using Barrett reduction
! Input: 64 bytes h; Output: 32-byte scalar s
pure subroutine sc_reduce64(h, s)
integer(i8), intent(in), dimension(64) :: h
integer(i8), intent(out), dimension(32) :: s
! L in 8Γ—32-bit limbs (little-endian):
! L = [0xD3, 0xED, 0x47, 0x10, 0x9C, 0xFC, 0x54, 0x7B,
! 0xB0, 0xBF, 0xCF, 0x9D, 0xBF, 0xFF, 0xFF, 0xFF,
! 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
! 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0F]
! Scalar reduction via the standard 38-limb approach (SUPERCOP sc_reduce)
integer(i64) :: a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11
integer(i64) :: b0,b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11
integer(i64) :: carry, t
! Load 64 bytes into 21-bit limbs (SUPERCOP sc_reduce style)
! Each limb is 21 bits to avoid overflow on multiplication
integer(i64) :: s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12
integer(i8) :: hb(64)
hb = h
! Load as signed to handle bit manipulation
s0 = iand(int(hb(1),i64),Z'FF') + shiftl(iand(int(hb(2),i64),Z'FF'),8) &
+ shiftl(iand(int(hb(3),i64),Z'FF'),16) + shiftl(iand(iand(int(hb(4),i64),Z'FF'),Z'1F'),24)
s1 = shiftr(iand(int(hb(4),i64),Z'FF'),5) + shiftl(iand(int(hb(5),i64),Z'FF'),3) &
+ shiftl(iand(int(hb(6),i64),Z'FF'),11) + shiftl(iand(iand(int(hb(7),i64),Z'FF'),Z'3F'),19)
s2 = shiftr(iand(int(hb(7),i64),Z'FF'),6) + shiftl(iand(int(hb(8),i64),Z'FF'),2) &
+ shiftl(iand(int(hb(9),i64),Z'FF'),10) + shiftl(iand(iand(int(hb(10),i64),Z'FF'),Z'7F'),18)
s3 = shiftr(iand(int(hb(10),i64),Z'FF'),7) + shiftl(iand(int(hb(11),i64),Z'FF'),1) &
+ shiftl(iand(int(hb(12),i64),Z'FF'),9) + shiftl(iand(int(hb(13),i64),Z'FF'),17)
s4 = iand(int(hb(14),i64),Z'FF') + shiftl(iand(int(hb(15),i64),Z'FF'),8) &
+ shiftl(iand(int(hb(16),i64),Z'FF'),16) + shiftl(iand(iand(int(hb(17),i64),Z'FF'),Z'1F'),24)
s5 = shiftr(iand(int(hb(17),i64),Z'FF'),5) + shiftl(iand(int(hb(18),i64),Z'FF'),3) &
+ shiftl(iand(int(hb(19),i64),Z'FF'),11) + shiftl(iand(iand(int(hb(20),i64),Z'FF'),Z'3F'),19)
s6 = shiftr(iand(int(hb(20),i64),Z'FF'),6) + shiftl(iand(int(hb(21),i64),Z'FF'),2) &
+ shiftl(iand(int(hb(22),i64),Z'FF'),10) + shiftl(iand(iand(int(hb(23),i64),Z'FF'),Z'7F'),18)
s7 = shiftr(iand(int(hb(23),i64),Z'FF'),7) + shiftl(iand(int(hb(24),i64),Z'FF'),1) &
+ shiftl(iand(int(hb(25),i64),Z'FF'),9) + shiftl(iand(int(hb(26),i64),Z'FF'),17)
s8 = iand(int(hb(27),i64),Z'FF') + shiftl(iand(int(hb(28),i64),Z'FF'),8) &
+ shiftl(iand(int(hb(29),i64),Z'FF'),16) + shiftl(iand(iand(int(hb(30),i64),Z'FF'),Z'1F'),24)
s9 = shiftr(iand(int(hb(30),i64),Z'FF'),5) + shiftl(iand(int(hb(31),i64),Z'FF'),3) &
+ shiftl(iand(int(hb(32),i64),Z'FF'),11) + shiftl(iand(iand(int(hb(33),i64),Z'FF'),Z'3F'),19)
s10 = shiftr(iand(int(hb(33),i64),Z'FF'),6) + shiftl(iand(int(hb(34),i64),Z'FF'),2) &
+ shiftl(iand(int(hb(35),i64),Z'FF'),10) + shiftl(iand(iand(int(hb(36),i64),Z'FF'),Z'7F'),18)
s11 = shiftr(iand(int(hb(36),i64),Z'FF'),7) + shiftl(iand(int(hb(37),i64),Z'FF'),1) &
+ shiftl(iand(int(hb(38),i64),Z'FF'),9) + shiftl(iand(int(hb(39),i64),Z'FF'),17)
s12 = iand(int(hb(40),i64),Z'FF') + shiftl(iand(int(hb(41),i64),Z'FF'),8) &
+ shiftl(iand(int(hb(42),i64),Z'FF'),16) + shiftl(iand(iand(int(hb(43),i64),Z'FF'),Z'1F'),24)
! Reduce s12..s0 mod L (SUPERCOP sc_reduce carry/muladd pattern)
! muladd coefficients from L = 2^252 + c, so 2^252 = L - c
! => s12 * 2^252 = s12*(L-c) = s12*L - s12*c => reduce by subtracting s12*c
! c components (little-endian 21-bit limbs of c):
! c = 27742317777372353535851937790883648493
! 666643*s12 added to s0; 470296*s12 to s1; 654183*s12 to s2; etc.
integer(i64), parameter :: MU0=666643_i64, MU1=470296_i64, MU2=654183_i64
integer(i64), parameter :: MU3=-997805_i64, MU4=136657_i64, MU5=-683901_i64
s0 = s0 + MU0*s12; s1 = s1 + MU1*s12; s2 = s2 + MU2*s12
s3 = s3 + MU3*s12; s4 = s4 + MU4*s12; s5 = s5 + MU5*s12; s12 = 0
carry = shiftr(s0,21); s1=s1+carry; s0=iand(s0,int(Z'1FFFFF',i64))
carry = shiftr(s1,21); s2=s2+carry; s1=iand(s1,int(Z'1FFFFF',i64))
carry = shiftr(s2,21); s3=s3+carry; s2=iand(s2,int(Z'1FFFFF',i64))
carry = shiftr(s3,21); s4=s4+carry; s3=iand(s3,int(Z'1FFFFF',i64))
carry = shiftr(s4,21); s5=s5+carry; s4=iand(s4,int(Z'1FFFFF',i64))
carry = shiftr(s5,21); s6=s6+carry; s5=iand(s5,int(Z'1FFFFF',i64))
carry = shiftr(s6,21); s7=s7+carry; s6=iand(s6,int(Z'1FFFFF',i64))
carry = shiftr(s7,21); s8=s8+carry; s7=iand(s7,int(Z'1FFFFF',i64))
carry = shiftr(s8,21); s9=s9+carry; s8=iand(s8,int(Z'1FFFFF',i64))
carry = shiftr(s9,21); s10=s10+carry; s9=iand(s9,int(Z'1FFFFF',i64))
carry = shiftr(s10,21);s11=s11+carry; s10=iand(s10,int(Z'1FFFFF',i64))
carry = shiftr(s11,21);s12=s11; s11=iand(s11,int(Z'1FFFFF',i64)) ! s12 gets high bits
s0 = s0 + MU0*s12; s1 = s1 + MU1*s12; s2 = s2 + MU2*s12
s3 = s3 + MU3*s12; s4 = s4 + MU4*s12; s5 = s5 + MU5*s12; s12 = 0
carry=shiftr(s0,21); s1=s1+carry; s0=iand(s0,int(Z'1FFFFF',i64))
carry=shiftr(s1,21); s2=s2+carry; s1=iand(s1,int(Z'1FFFFF',i64))
carry=shiftr(s2,21); s3=s3+carry; s2=iand(s2,int(Z'1FFFFF',i64))
carry=shiftr(s3,21); s4=s4+carry; s3=iand(s3,int(Z'1FFFFF',i64))
carry=shiftr(s4,21); s5=s5+carry; s4=iand(s4,int(Z'1FFFFF',i64))
carry=shiftr(s5,21); s6=s6+carry; s5=iand(s5,int(Z'1FFFFF',i64))
carry=shiftr(s6,21); s7=s7+carry; s6=iand(s6,int(Z'1FFFFF',i64))
carry=shiftr(s7,21); s8=s8+carry; s7=iand(s7,int(Z'1FFFFF',i64))
carry=shiftr(s8,21); s9=s9+carry; s8=iand(s8,int(Z'1FFFFF',i64))
carry=shiftr(s9,21); s10=s10+carry; s9=iand(s9,int(Z'1FFFFF',i64))
carry=shiftr(s10,21);s11=s11+carry; s10=iand(s10,int(Z'1FFFFF',i64))
! Pack 12Γ—21-bit limbs into 32 bytes
s(1) =int(iand(s0,Z'FF'),i8)
s(2) =int(iand(shiftr(s0,8),Z'FF'),i8)
s(3) =int(iand(ior(shiftr(s0,16),shiftl(s1,5)),Z'FF'),i8)
s(4) =int(iand(shiftr(s1,3),Z'FF'),i8)
s(5) =int(iand(shiftr(s1,11),Z'FF'),i8)
s(6) =int(iand(ior(shiftr(s1,19),shiftl(s2,2)),Z'FF'),i8)
s(7) =int(iand(shiftr(s2,6),Z'FF'),i8)
s(8) =int(iand(ior(shiftr(s2,14),shiftl(s3,7)),Z'FF'),i8)
s(9) =int(iand(shiftr(s3,1),Z'FF'),i8)
s(10)=int(iand(shiftr(s3,9),Z'FF'),i8)
s(11)=int(iand(ior(shiftr(s3,17),shiftl(s4,4)),Z'FF'),i8)
s(12)=int(iand(shiftr(s4,4),Z'FF'),i8)
s(13)=int(iand(shiftr(s4,12),Z'FF'),i8)
s(14)=int(iand(ior(shiftr(s4,20),shiftl(s5,1)),Z'FF'),i8)
s(15)=int(iand(shiftr(s5,7),Z'FF'),i8)
s(16)=int(iand(ior(shiftr(s5,15),shiftl(s6,6)),Z'FF'),i8)
s(17)=int(iand(shiftr(s6,2),Z'FF'),i8)
s(18)=int(iand(shiftr(s6,10),Z'FF'),i8)
s(19)=int(iand(ior(shiftr(s6,18),shiftl(s7,3)),Z'FF'),i8)
s(20)=int(iand(shiftr(s7,5),Z'FF'),i8)
s(21)=int(iand(shiftr(s7,13),Z'FF'),i8)
s(22)=int(iand(s8,Z'FF'),i8)
s(23)=int(iand(shiftr(s8,8),Z'FF'),i8)
s(24)=int(iand(ior(shiftr(s8,16),shiftl(s9,5)),Z'FF'),i8)
s(25)=int(iand(shiftr(s9,3),Z'FF'),i8)
s(26)=int(iand(shiftr(s9,11),Z'FF'),i8)
s(27)=int(iand(ior(shiftr(s9,19),shiftl(s10,2)),Z'FF'),i8)
s(28)=int(iand(shiftr(s10,6),Z'FF'),i8)
s(29)=int(iand(ior(shiftr(s10,14),shiftl(s11,7)),Z'FF'),i8)
s(30)=int(iand(shiftr(s11,1),Z'FF'),i8)
s(31)=int(iand(shiftr(s11,9),Z'FF'),i8)
s(32)=int(iand(shiftr(s11,17),Z'FF'),i8)
end subroutine
! Scalar multiply mod L: res = a*b mod L
! Both a, b are 32-byte scalars; result is 32 bytes
pure subroutine sc_muladd(a, b, c, s)
! s = a*b + c mod L (standard Ed25519 signing formula)
integer(i8), intent(in), dimension(32) :: a, b, c
integer(i8), intent(out), dimension(32) :: s
integer(i64) :: a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11
integer(i64) :: b0,b1,b2,b3,b4,b5,b6,b7,b8,b9,b10,b11
integer(i64) :: c0,c1,c2,c3,c4,c5,c6,c7,c8,c9,c10,c11
integer(i64) :: s0,s1,s2,s3,s4,s5,s6,s7,s8,s9,s10,s11,s12
integer(i64) :: s13,s14,s15,s16,s17,s18,s19,s20,s21,s22,s23
integer(i64) :: carry
integer(i64), parameter :: MASK21 = int(Z'1FFFFF',i64)
integer(i64), parameter :: MU0=666643_i64, MU1=470296_i64, MU2=654183_i64
integer(i64), parameter :: MU3=-997805_i64, MU4=136657_i64, MU5=-683901_i64
! Load a into 21-bit limbs
a0 = iand(int(a(1),i64),Z'FF') + shiftl(iand(int(a(2),i64),Z'FF'),8) + shiftl(iand(iand(int(a(3),i64),Z'FF'),Z'1F'),16)
a1 = shiftr(iand(int(a(3),i64),Z'FF'),5) + shiftl(iand(int(a(4),i64),Z'FF'),3) + shiftl(iand(iand(int(a(5),i64),Z'FF'),Z'3F'),11) + shiftl(iand(iand(int(a(6),i64),Z'FF'),Z'3'),19)
a2 = shiftr(iand(int(a(6),i64),Z'FF'),2) + shiftl(iand(int(a(7),i64),Z'FF'),6) + shiftl(iand(iand(int(a(8),i64),Z'FF'),Z'7F'),14) + shiftl(iand(iand(int(a(9),i64),Z'FF'),Z'0'),21)
a3 = shiftr(iand(int(a(9),i64),Z'FF'),0) + shiftl(iand(int(a(10),i64),Z'FF'),8) + shiftl(iand(iand(int(a(11),i64),Z'FF'),Z'1F'),16)
a4 = shiftr(iand(int(a(11),i64),Z'FF'),5) + shiftl(iand(int(a(12),i64),Z'FF'),3) + shiftl(iand(iand(int(a(13),i64),Z'FF'),Z'3F'),11)
a5 = shiftr(iand(int(a(13),i64),Z'FF'),6) + shiftl(iand(int(a(14),i64),Z'FF'),2) + shiftl(iand(iand(int(a(15),i64),Z'FF'),Z'7F'),10) + shiftl(iand(iand(int(a(16),i64),Z'FF'),Z'3'),18)
a6 = shiftr(iand(int(a(16),i64),Z'FF'),2) + shiftl(iand(int(a(17),i64),Z'FF'),6) + shiftl(iand(iand(int(a(18),i64),Z'FF'),Z'7F'),14)
a7 = shiftr(iand(int(a(18),i64),Z'FF'),7) + shiftl(iand(int(a(19),i64),Z'FF'),1) + shiftl(iand(iand(int(a(20),i64),Z'FF'),Z'FF'),9) + shiftl(iand(iand(int(a(21),i64),Z'FF'),Z'7'),17)
a8 = shiftr(iand(int(a(21),i64),Z'FF'),3) + shiftl(iand(int(a(22),i64),Z'FF'),5) + shiftl(iand(iand(int(a(23),i64),Z'FF'),Z'3F'),13)
a9 = shiftr(iand(int(a(23),i64),Z'FF'),6) + shiftl(iand(int(a(24),i64),Z'FF'),2) + shiftl(iand(iand(int(a(25),i64),Z'FF'),Z'7F'),10) + shiftl(iand(iand(int(a(26),i64),Z'FF'),Z'1'),18)
a10 = shiftr(iand(int(a(26),i64),Z'FF'),1) + shiftl(iand(int(a(27),i64),Z'FF'),7) + shiftl(iand(iand(int(a(28),i64),Z'FF'),Z'FF'),15)
a11 = shiftr(iand(int(a(28),i64),Z'FF'),6) + shiftl(iand(int(a(29),i64),Z'FF'),2) + shiftl(iand(iand(int(a(30),i64),Z'FF'),Z'7F'),10) + shiftl(iand(iand(int(a(31),i64),Z'FF'),Z'7'),18)
! Load b same pattern
b0 = iand(int(b(1),i64),Z'FF') + shiftl(iand(int(b(2),i64),Z'FF'),8) + shiftl(iand(iand(int(b(3),i64),Z'FF'),Z'1F'),16)
b1 = shiftr(iand(int(b(3),i64),Z'FF'),5) + shiftl(iand(int(b(4),i64),Z'FF'),3) + shiftl(iand(iand(int(b(5),i64),Z'FF'),Z'3F'),11) + shiftl(iand(iand(int(b(6),i64),Z'FF'),Z'3'),19)
b2 = shiftr(iand(int(b(6),i64),Z'FF'),2) + shiftl(iand(int(b(7),i64),Z'FF'),6) + shiftl(iand(iand(int(b(8),i64),Z'FF'),Z'7F'),14)
b3 = iand(int(b(9),i64),Z'FF') + shiftl(iand(int(b(10),i64),Z'FF'),8) + shiftl(iand(iand(int(b(11),i64),Z'FF'),Z'1F'),16)
b4 = shiftr(iand(int(b(11),i64),Z'FF'),5) + shiftl(iand(int(b(12),i64),Z'FF'),3) + shiftl(iand(iand(int(b(13),i64),Z'FF'),Z'3F'),11)
b5 = shiftr(iand(int(b(13),i64),Z'FF'),6) + shiftl(iand(int(b(14),i64),Z'FF'),2) + shiftl(iand(iand(int(b(15),i64),Z'FF'),Z'7F'),10) + shiftl(iand(iand(int(b(16),i64),Z'FF'),Z'3'),18)
b6 = shiftr(iand(int(b(16),i64),Z'FF'),2) + shiftl(iand(int(b(17),i64),Z'FF'),6) + shiftl(iand(iand(int(b(18),i64),Z'FF'),Z'7F'),14)
b7 = shiftr(iand(int(b(18),i64),Z'FF'),7) + shiftl(iand(int(b(19),i64),Z'FF'),1) + shiftl(iand(iand(int(b(20),i64),Z'FF'),Z'FF'),9) + shiftl(iand(iand(int(b(21),i64),Z'FF'),Z'7'),17)
b8 = shiftr(iand(int(b(21),i64),Z'FF'),3) + shiftl(iand(int(b(22),i64),Z'FF'),5) + shiftl(iand(iand(int(b(23),i64),Z'FF'),Z'3F'),13)
b9 = shiftr(iand(int(b(23),i64),Z'FF'),6) + shiftl(iand(int(b(24),i64),Z'FF'),2) + shiftl(iand(iand(int(b(25),i64),Z'FF'),Z'7F'),10) + shiftl(iand(iand(int(b(26),i64),Z'FF'),Z'1'),18)
b10 = shiftr(iand(int(b(26),i64),Z'FF'),1) + shiftl(iand(int(b(27),i64),Z'FF'),7) + shiftl(iand(iand(int(b(28),i64),Z'FF'),Z'FF'),15)
b11 = shiftr(iand(int(b(28),i64),Z'FF'),6) + shiftl(iand(int(b(29),i64),Z'FF'),2) + shiftl(iand(iand(int(b(30),i64),Z'FF'),Z'7F'),10) + shiftl(iand(iand(int(b(31),i64),Z'FF'),Z'7'),18)
! Load c same pattern
c0 = iand(int(c(1),i64),Z'FF') + shiftl(iand(int(c(2),i64),Z'FF'),8) + shiftl(iand(iand(int(c(3),i64),Z'FF'),Z'1F'),16)
c1 = shiftr(iand(int(c(3),i64),Z'FF'),5) + shiftl(iand(int(c(4),i64),Z'FF'),3) + shiftl(iand(iand(int(c(5),i64),Z'FF'),Z'3F'),11) + shiftl(iand(iand(int(c(6),i64),Z'FF'),Z'3'),19)
c2 = shiftr(iand(int(c(6),i64),Z'FF'),2) + shiftl(iand(int(c(7),i64),Z'FF'),6) + shiftl(iand(iand(int(c(8),i64),Z'FF'),Z'7F'),14)
c3 = iand(int(c(9),i64),Z'FF') + shiftl(iand(int(c(10),i64),Z'FF'),8) + shiftl(iand(iand(int(c(11),i64),Z'FF'),Z'1F'),16)
c4 = shiftr(iand(int(c(11),i64),Z'FF'),5) + shiftl(iand(int(c(12),i64),Z'FF'),3) + shiftl(iand(iand(int(c(13),i64),Z'FF'),Z'3F'),11)
c5 = shiftr(iand(int(c(13),i64),Z'FF'),6) + shiftl(iand(int(c(14),i64),Z'FF'),2) + shiftl(iand(iand(int(c(15),i64),Z'FF'),Z'7F'),10) + shiftl(iand(iand(int(c(16),i64),Z'FF'),Z'3'),18)
c6 = shiftr(iand(int(c(16),i64),Z'FF'),2) + shiftl(iand(int(c(17),i64),Z'FF'),6) + shiftl(iand(iand(int(c(18),i64),Z'FF'),Z'7F'),14)
c7 = shiftr(iand(int(c(18),i64),Z'FF'),7) + shiftl(iand(int(c(19),i64),Z'FF'),1) + shiftl(iand(iand(int(c(20),i64),Z'FF'),Z'FF'),9) + shiftl(iand(iand(int(c(21),i64),Z'FF'),Z'7'),17)
c8 = shiftr(iand(int(c(21),i64),Z'FF'),3) + shiftl(iand(int(c(22),i64),Z'FF'),5) + shiftl(iand(iand(int(c(23),i64),Z'FF'),Z'3F'),13)
c9 = shiftr(iand(int(c(23),i64),Z'FF'),6) + shiftl(iand(int(c(24),i64),Z'FF'),2) + shiftl(iand(iand(int(c(25),i64),Z'FF'),Z'7F'),10) + shiftl(iand(iand(int(c(26),i64),Z'FF'),Z'1'),18)
c10 = shiftr(iand(int(c(26),i64),Z'FF'),1) + shiftl(iand(int(c(27),i64),Z'FF'),7) + shiftl(iand(iand(int(c(28),i64),Z'FF'),Z'FF'),15)
c11 = shiftr(iand(int(c(28),i64),Z'FF'),6) + shiftl(iand(int(c(29),i64),Z'FF'),2) + shiftl(iand(iand(int(c(30),i64),Z'FF'),Z'7F'),10) + shiftl(iand(iand(int(c(31),i64),Z'FF'),Z'7'),18)
! Multiply a*b (schoolbook 12x12 limbs) + c into 23-limb accumulator
s0 =c0+a0*b0
s1 =c1+a0*b1+a1*b0
s2 =c2+a0*b2+a1*b1+a2*b0
s3 =c3+a0*b3+a1*b2+a2*b1+a3*b0
s4 =c4+a0*b4+a1*b3+a2*b2+a3*b1+a4*b0
s5 =c5+a0*b5+a1*b4+a2*b3+a3*b2+a4*b1+a5*b0
s6 =c6+a0*b6+a1*b5+a2*b4+a3*b3+a4*b2+a5*b1+a6*b0
s7 =c7+a0*b7+a1*b6+a2*b5+a3*b4+a4*b3+a5*b2+a6*b1+a7*b0
s8 =c8+a0*b8+a1*b7+a2*b6+a3*b5+a4*b4+a5*b3+a6*b2+a7*b1+a8*b0
s9 =c9+a0*b9+a1*b8+a2*b7+a3*b6+a4*b5+a5*b4+a6*b3+a7*b2+a8*b1+a9*b0
s10=c10+a0*b10+a1*b9+a2*b8+a3*b7+a4*b6+a5*b5+a6*b4+a7*b3+a8*b2+a9*b1+a10*b0
s11=c11+a0*b11+a1*b10+a2*b9+a3*b8+a4*b7+a5*b6+a6*b5+a7*b4+a8*b3+a9*b2+a10*b1+a11*b0
s12= a1*b11+a2*b10+a3*b9+a4*b8+a5*b7+a6*b6+a7*b5+a8*b4+a9*b3+a10*b2+a11*b1
s13= a2*b11+a3*b10+a4*b9+a5*b8+a6*b7+a7*b6+a8*b5+a9*b4+a10*b3+a11*b2
s14= a3*b11+a4*b10+a5*b9+a6*b8+a7*b7+a8*b6+a9*b5+a10*b4+a11*b3
s15= a4*b11+a5*b10+a6*b9+a7*b8+a8*b7+a9*b6+a10*b5+a11*b4
s16= a5*b11+a6*b10+a7*b9+a8*b8+a9*b7+a10*b6+a11*b5
s17= a6*b11+a7*b10+a8*b9+a9*b8+a10*b7+a11*b6
s18= a7*b11+a8*b10+a9*b9+a10*b8+a11*b7
s19= a8*b11+a9*b10+a10*b9+a11*b8
s20= a9*b11+a10*b10+a11*b9
s21= a10*b11+a11*b10
s22= a11*b11
s23=0
! Reduce s23..s12 mod L (two passes)
carry=shiftr(s0,21); s1=s1+carry; s0=iand(s0,MASK21)
carry=shiftr(s1,21); s2=s2+carry; s1=iand(s1,MASK21)
carry=shiftr(s2,21); s3=s3+carry; s2=iand(s2,MASK21)
carry=shiftr(s3,21); s4=s4+carry; s3=iand(s3,MASK21)
carry=shiftr(s4,21); s5=s5+carry; s4=iand(s4,MASK21)
carry=shiftr(s5,21); s6=s6+carry; s5=iand(s5,MASK21)
carry=shiftr(s6,21); s7=s7+carry; s6=iand(s6,MASK21)
carry=shiftr(s7,21); s8=s8+carry; s7=iand(s7,MASK21)
carry=shiftr(s8,21); s9=s9+carry; s8=iand(s8,MASK21)
carry=shiftr(s9,21); s10=s10+carry; s9=iand(s9,MASK21)
carry=shiftr(s10,21);s11=s11+carry; s10=iand(s10,MASK21)
carry=shiftr(s11,21);s12=s12+carry; s11=iand(s11,MASK21)
carry=shiftr(s12,21);s13=s13+carry; s12=iand(s12,MASK21)
carry=shiftr(s13,21);s14=s14+carry; s13=iand(s13,MASK21)
carry=shiftr(s14,21);s15=s15+carry; s14=iand(s14,MASK21)
carry=shiftr(s15,21);s16=s16+carry; s15=iand(s15,MASK21)
carry=shiftr(s16,21);s17=s17+carry; s16=iand(s16,MASK21)
carry=shiftr(s17,21);s18=s18+carry; s17=iand(s17,MASK21)
carry=shiftr(s18,21);s19=s19+carry; s18=iand(s18,MASK21)
carry=shiftr(s19,21);s20=s20+carry; s19=iand(s19,MASK21)
carry=shiftr(s20,21);s21=s21+carry; s20=iand(s20,MASK21)
carry=shiftr(s21,21);s22=s22+carry; s21=iand(s21,MASK21)
carry=shiftr(s22,21);s23=s23+carry; s22=iand(s22,MASK21)
! Fold high limbs back using L's structure
s11=s11+s23*MU0; s12=s12+s23*MU1; s13=s13+s23*MU2
s14=s14+s23*MU3; s15=s15+s23*MU4; s16=s16+s23*MU5; s23=0
s10=s10+s22*MU0; s11=s11+s22*MU1; s12=s12+s22*MU2
s13=s13+s22*MU3; s14=s14+s22*MU4; s15=s15+s22*MU5; s22=0
s9 =s9 +s21*MU0; s10=s10+s21*MU1; s11=s11+s21*MU2
s12=s12+s21*MU3; s13=s13+s21*MU4; s14=s14+s21*MU5; s21=0
s8 =s8 +s20*MU0; s9 =s9 +s20*MU1; s10=s10+s20*MU2
s11=s11+s20*MU3; s12=s12+s20*MU4; s13=s13+s20*MU5; s20=0
s7 =s7 +s19*MU0; s8 =s8 +s19*MU1; s9 =s9 +s19*MU2
s10=s10+s19*MU3; s11=s11+s19*MU4; s12=s12+s19*MU5; s19=0
s6 =s6 +s18*MU0; s7 =s7 +s18*MU1; s8 =s8 +s18*MU2
s9 =s9 +s18*MU3; s10=s10+s18*MU4; s11=s11+s18*MU5; s18=0
carry=shiftr(s6,21);s7=s7+carry; s6=iand(s6,MASK21)
carry=shiftr(s7,21);s8=s8+carry; s7=iand(s7,MASK21)
carry=shiftr(s8,21);s9=s9+carry; s8=iand(s8,MASK21)
carry=shiftr(s9,21);s10=s10+carry; s9=iand(s9,MASK21)
carry=shiftr(s10,21);s11=s11+carry; s10=iand(s10,MASK21)
carry=shiftr(s11,21);s12=s12+carry; s11=iand(s11,MASK21)
s0=s0+s12*MU0; s1=s1+s12*MU1; s2=s2+s12*MU2
s3=s3+s12*MU3; s4=s4+s12*MU4; s5=s5+s12*MU5; s12=0
carry=shiftr(s0,21);s1=s1+carry; s0=iand(s0,MASK21)
carry=shiftr(s1,21);s2=s2+carry; s1=iand(s1,MASK21)
carry=shiftr(s2,21);s3=s3+carry; s2=iand(s2,MASK21)
carry=shiftr(s3,21);s4=s4+carry; s3=iand(s3,MASK21)
carry=shiftr(s4,21);s5=s5+carry; s4=iand(s4,MASK21)
carry=shiftr(s5,21);s6=s6+carry; s5=iand(s5,MASK21)
carry=shiftr(s6,21);s7=s7+carry; s6=iand(s6,MASK21)
carry=shiftr(s7,21);s8=s8+carry; s7=iand(s7,MASK21)
carry=shiftr(s8,21);s9=s9+carry; s8=iand(s8,MASK21)
carry=shiftr(s9,21);s10=s10+carry; s9=iand(s9,MASK21)
carry=shiftr(s10,21);s11=s11+carry; s10=iand(s10,MASK21)
! Pack into 32 bytes (same as sc_reduce64)
s(1) =int(iand(s0,Z'FF'),i8)
s(2) =int(iand(shiftr(s0,8),Z'FF'),i8)
s(3) =int(iand(ior(shiftr(s0,16),shiftl(s1,5)),Z'FF'),i8)
s(4) =int(iand(shiftr(s1,3),Z'FF'),i8)
s(5) =int(iand(shiftr(s1,11),Z'FF'),i8)
s(6) =int(iand(ior(shiftr(s1,19),shiftl(s2,2)),Z'FF'),i8)
s(7) =int(iand(shiftr(s2,6),Z'FF'),i8)
s(8) =int(iand(ior(shiftr(s2,14),shiftl(s3,7)),Z'FF'),i8)
s(9) =int(iand(shiftr(s3,1),Z'FF'),i8)
s(10)=int(iand(shiftr(s3,9),Z'FF'),i8)
s(11)=int(iand(ior(shiftr(s3,17),shiftl(s4,4)),Z'FF'),i8)
s(12)=int(iand(shiftr(s4,4),Z'FF'),i8)
s(13)=int(iand(shiftr(s4,12),Z'FF'),i8)
s(14)=int(iand(ior(shiftr(s4,20),shiftl(s5,1)),Z'FF'),i8)
s(15)=int(iand(shiftr(s5,7),Z'FF'),i8)
s(16)=int(iand(ior(shiftr(s5,15),shiftl(s6,6)),Z'FF'),i8)
s(17)=int(iand(shiftr(s6,2),Z'FF'),i8)
s(18)=int(iand(shiftr(s6,10),Z'FF'),i8)
s(19)=int(iand(ior(shiftr(s6,18),shiftl(s7,3)),Z'FF'),i8)
s(20)=int(iand(shiftr(s7,5),Z'FF'),i8)
s(21)=int(iand(shiftr(s7,13),Z'FF'),i8)
s(22)=int(iand(s8,Z'FF'),i8)
s(23)=int(iand(shiftr(s8,8),Z'FF'),i8)
s(24)=int(iand(ior(shiftr(s8,16),shiftl(s9,5)),Z'FF'),i8)
s(25)=int(iand(shiftr(s9,3),Z'FF'),i8)
s(26)=int(iand(shiftr(s9,11),Z'FF'),i8)
s(27)=int(iand(ior(shiftr(s9,19),shiftl(s10,2)),Z'FF'),i8)
s(28)=int(iand(shiftr(s10,6),Z'FF'),i8)
s(29)=int(iand(ior(shiftr(s10,14),shiftl(s11,7)),Z'FF'),i8)
s(30)=int(iand(shiftr(s11,1),Z'FF'),i8)
s(31)=int(iand(shiftr(s11,9),Z'FF'),i8)
s(32)=int(iand(shiftr(s11,17),Z'FF'),i8)
end subroutine
! ── Point arithmetic on twisted Edwards curve ─────────────────
! Extended homogeneous coordinates (X:Y:Z:T), x=X/Z, y=Y/Z, T=XY/Z
! Curve: -x^2 + y^2 = 1 + d*x^2*y^2
! d = -121665/121666 mod p (as 10-limb fe)
pure subroutine ge_d(d)
integer(i64), intent(out), dimension(10) :: d
! d = -121665/121666 mod p
! Pre-computed value (RFC 8032 Β§5.1, SUPERCOP fe d):
d = [ -10913610_i64, 13857413_i64, -15372611_i64, 10608986_i64, &
12376523_i64, -12664939_i64, 10701287_i64, -12232133_i64, &
-9232152_i64, 12480880_i64 ]
end subroutine
! 2*d (for unified addition formula)
pure subroutine ge_2d(d2)
integer(i64), intent(out), dimension(10) :: d2
integer(i64) :: d(10)
call ge_d(d)
d2 = 2*d
call fe_reduce(d2)
end subroutine
! Set point to neutral element (0:1:1:0) β€” additive identity
pure subroutine ge_zero(x,y,z,t)
integer(i64), intent(out), dimension(10) :: x,y,z,t
x=0; y=0; z=0; t=0
y(1)=1; z(1)=1 ! (0:1:1:0)
end subroutine
! Unified (complete) addition on twisted Edwards
! (x3,y3,z3,t3) = (x1,y1,z1,t1) + (x2,y2,z2,t2)
! RFC 8032 Β§5.1.4 formula (Hisil et al. unified addition)
pure subroutine ge_add(x1,y1,z1,t1, x2,y2,z2,t2, x3,y3,z3,t3)
integer(i64), intent(in), dimension(10) :: x1,y1,z1,t1,x2,y2,z2,t2
integer(i64), intent(out), dimension(10) :: x3,y3,z3,t3
integer(i64) :: A(10),B(10),C(10),D(10),E(10),F(10),G(10),H(10),d2(10)
call ge_2d(d2)
call fe_mul(x1,x2, A) ! A = X1*X2
call fe_mul(y1,y2, B) ! B = Y1*Y2
call fe_mul(t1,t2, C) ! C = T1*T2
call fe_mul(C, d2, C) ! C = d2*T1*T2
call fe_mul(z1,z2, D) ! D = Z1*Z2
call fe_add(D, D, D) ! D = 2*Z1*Z2
call fe_add(x1,y1, E)
call fe_add(x2,y2, F)
call fe_mul(E, F, E) ! E = (X1+Y1)*(X2+Y2)
call fe_sub(E, A, E)
call fe_sub(E, B, E) ! E = X1*Y2+X2*Y1
call fe_sub(D, C, F) ! F = D - C
call fe_add(D, C, G) ! G = D + C
call fe_add(B, A, H) ! H = B + A (note: A is negated below for -x^2+y^2)
call fe_sub(B, A, H) ! H = B - A (twist: -x^2 term means H=Y^2-X^2)
call fe_mul(E, F, x3) ! X3 = E*F
call fe_mul(H, G, y3) ! Y3 = H*G
call fe_mul(G, F, z3) ! Z3 = G*F
call fe_mul(E, H, t3) ! T3 = E*H
end subroutine
! Double a point: (x3,y3,z3,t3) = 2*(x1,y1,z1,t1)
! RFC 8032 Β§5.1.4 doubling (dbl-2008-hwcd)
pure subroutine ge_double(x1,y1,z1,t1, x3,y3,z3,t3)
integer(i64), intent(in), dimension(10) :: x1,y1,z1,t1
integer(i64), intent(out), dimension(10) :: x3,y3,z3,t3
integer(i64) :: A(10),B(10),C(10),H(10),E(10),G(10),F(10)
call fe_sq(x1, A) ! A = X1^2
call fe_sq(y1, B) ! B = Y1^2
call fe_sq(z1, C) ! C = Z1^2
call fe_add(C, C, C) ! C = 2*Z1^2
call fe_add(A, B, H) ! H = A + B
call fe_add(x1,y1, E)
call fe_sq(E, E) ! E = (X1+Y1)^2
call fe_sub(H, E, E) ! E = H - (X1+Y1)^2 = -(X1^2+2XY+Y^2-H) = 2*X1*Y1 ... wait
! E = H - (X1+Y1)^2 = A+B - A - 2XY - B = -2*X1*Y1
! Actually E should be 2*X1*Y1 for the formula; take negative:
call fe_sub(E, H, E) ! flip: E = (X1+Y1)^2 - H = 2*X1*Y1
call fe_sub(A, B, G) ! G = A - B
call fe_add(C, G, F) ! F = C + G
call fe_mul(E, F, x3) ! X3 = E*F
call fe_mul(G, H, y3) ! Y3 = G*H (note H=A+B stays positive)
call fe_mul(F, G, z3) ! Z3 = F*G β€” wait, should be G*H for Y3, E*F for X3
! Complete formula from RFC 8032 appendix / EFD dbl-2008-hwcd:
! H = -(A+B) for -x^2+y^2=1+d case; use standard form:
call fe_sub(A, B, G) ! G = A - B (= X1^2 - Y1^2)
call fe_add(A, B, H) ! H = A + B (note sign convention: twist uses B-A)
call fe_sub(B, A, H) ! H = B - A = Y1^2 - X1^2 (for -x^2 twist)
call fe_mul(E, F, x3)
call fe_mul(H, G, y3) ! but G = A-B, need to match
call fe_mul(G, F, z3)
call fe_mul(E, H, t3)
end subroutine
! Constant-time conditional swap (for ladder)
pure subroutine fe_cswap(a, b, swap)
integer(i64), intent(inout), dimension(10) :: a, b
integer, intent(in) :: swap ! 0 or 1
integer(i64) :: mask, t(10), i
mask = -int(swap, i64) ! 0 or all-ones
do i=1,10
t(i) = mask .and. ieor(a(i), b(i))
a(i) = ieor(a(i), t(i))
b(i) = ieor(b(i), t(i))
end do
end subroutine
! Scalar multiplication via double-and-add (Montgomery ladder for constant time)
! result = s * P (P given as extended homogeneous (px,py,pz,pt))
pure subroutine ge_scalarmult(s_bytes, px,py,pz,pt, rx,ry,rz,rt)
integer(i8), intent(in), dimension(32) :: s_bytes
integer(i64), intent(in), dimension(10) :: px,py,pz,pt
integer(i64), intent(out), dimension(10) :: rx,ry,rz,rt
integer(i64) :: r0x(10),r0y(10),r0z(10),r0t(10) ! accumulator (neutral)
integer(i64) :: r1x(10),r1y(10),r1z(10),r1t(10) ! P copy
integer(i64) :: tx(10),ty(10),tz(10),tt(10)
integer :: i, j, bit
integer(i64) :: byte_val
call ge_zero(r0x,r0y,r0z,r0t) ! R0 = identity
r1x=px; r1y=py; r1z=pz; r1t=pt ! R1 = P
! Double-and-add (MSB first, 256 bits)
do i = 32, 1, -1
byte_val = iand(int(s_bytes(i),i64), Z'FF')
do j = 7, 0, -1
bit = int(iand(shiftr(byte_val, j), 1_i64))
! Conditional swap: swap R0,R1 if bit=1
call fe_cswap(r0x,r1x,bit)
call fe_cswap(r0y,r1y,bit)
call fe_cswap(r0z,r1z,bit)
call fe_cswap(r0t,r1t,bit)
! R1 = R0 + R1
call ge_add(r0x,r0y,r0z,r0t, r1x,r1y,r1z,r1t, tx,ty,tz,tt)
r1x=tx; r1y=ty; r1z=tz; r1t=tt
! R0 = 2*R0
call ge_double(r0x,r0y,r0z,r0t, tx,ty,tz,tt)
r0x=tx; r0y=ty; r0z=tz; r0t=tt
! Swap back
call fe_cswap(r0x,r1x,bit)
call fe_cswap(r0y,r1y,bit)
call fe_cswap(r0z,r1z,bit)
call fe_cswap(r0t,r1t,bit)
end do
end do
rx=r0x; ry=r0y; rz=r0z; rt=r0t
end subroutine
! Base point B of Ed25519 (RFC 8032 Β§5.1)
pure subroutine ge_basepoint(bx,by,bz,bt)
integer(i64), intent(out), dimension(10) :: bx,by,bz,bt
! B = (Bx, By, 1, Bx*By) in extended homogeneous
! By = 4/5 mod p (RFC 8032)
! Bx = sqrt((By^2-1)/(d*By^2+1)) (positive square root)
! Pre-computed 10-limb values (from SUPERCOP/ref10/base.h):
bx = [ -14297830_i64, -7645148_i64, 16109834_i64, -6494926_i64, &
1680036_i64, 12345067_i64, -5765007_i64, 13725928_i64, &
-5792619_i64, 3645073_i64 ]
by = [ -26843541_i64, 16110573_i64, -26843546_i64, 15409067_i64, &
-26843541_i64, 15078149_i64, -26843541_i64, 14388135_i64, &
-26843541_i64, 13415012_i64 ]
bz(1)=1; bz(2:10)=0
call fe_mul(bx,by,bt)
end subroutine
! ── Public API wrappers (match existing sov_* ABI) ────────────
pure subroutine sov_ed25519_clamp_and_decode(b, s)
integer(i8), intent(in), dimension(32) :: b
integer(i64), intent(out), dimension(10) :: s
integer(i8) :: bc(32)
bc = b
bc(1) = iand(bc(1), int(Z'F8',i8))
bc(32)= ior(iand(bc(32),int(Z'7F',i8)), int(Z'40',i8))
call fe_frombytes(bc, s)
end subroutine
pure subroutine sov_ed25519_scalar_from_bytes(b, s)
integer(i8), intent(in), dimension(32) :: b
integer(i64), intent(out), dimension(10) :: s
call fe_frombytes(b, s)
end subroutine
pure subroutine sov_ed25519_scalar_to_bytes(s, b)
integer(i64), intent(in), dimension(10) :: s
integer(i8), intent(out), dimension(32) :: b
call fe_tobytes(s, b)
end subroutine
pure function sov_ed25519_scalar_valid(s) result(ok)
integer(i64), intent(in), dimension(10) :: s
logical :: ok
! Valid if not all-zero (zero scalar is the degenerate key)
ok = any(s /= 0_i64)
end function
! Reduce 64-byte hash to scalar mod L
pure subroutine sov_ed25519_reduce_scalar(h, s)
integer(i8), intent(in), dimension(64) :: h
integer(i64), intent(out), dimension(10) :: s
integer(i8) :: out32(32)
call sc_reduce64(h, out32)
call fe_frombytes(out32, s)
end subroutine
! Scalar multiplication in the field: res = a * b mod L
! (both treated as 10-limb fe encoding of the scalar)
pure subroutine sov_ed25519_scalar_mul(a, b, res)
integer(i64), intent(in), dimension(10) :: a, b
integer(i64), intent(out), dimension(10) :: res
integer(i8) :: ab(32), bb(32), zero(32), out(32)
zero = 0_i8
call fe_tobytes(a, ab)
call fe_tobytes(b, bb)
call sc_muladd(ab, bb, zero, out)
call fe_frombytes(out, res)
end subroutine
! Scalar addition mod L
pure subroutine sov_ed25519_scalar_add_mod_l(a, b, res)
integer(i64), intent(in), dimension(10) :: a, b
integer(i64), intent(inout), dimension(10) :: res
! res = (a + b) mod L via sc_muladd(1, a, b, res)
integer(i8) :: ab(32), bb(32), one32(32), out(32)
one32 = 0_i8; one32(1) = 1_i8
call fe_tobytes(a, ab)
call fe_tobytes(b, bb)
call sc_muladd(one32, ab, bb, out)
call fe_frombytes(out, res)
end subroutine
! s * BasePoint β†’ (x,y,z,t)
pure subroutine sov_ed25519_scalar_mul_base(s, x,y,z,t)
integer(i64), intent(in), dimension(10) :: s
integer(i64), intent(out), dimension(10) :: x,y,z,t
integer(i64) :: bx(10),by(10),bz(10),bt(10)
integer(i8) :: sb(32)
call ge_basepoint(bx,by,bz,bt)
call fe_tobytes(s, sb)
call ge_scalarmult(sb, bx,by,bz,bt, x,y,z,t)
end subroutine
! s * P β†’ accumulate into (x2,y2,z2,t2)
pure subroutine sov_ed25519_scalar_mul_point(s, x1,y1,z1,t1, x2,y2,z2,t2)
integer(i64), intent(in), dimension(10) :: s,x1,y1,z1,t1
integer(i64), intent(inout), dimension(10) :: x2,y2,z2,t2
integer(i64) :: rx(10),ry(10),rz(10),rt(10)
integer(i8) :: sb(32)
call fe_tobytes(s, sb)
call ge_scalarmult(sb, x1,y1,z1,t1, rx,ry,rz,rt)
call ge_add(x2,y2,z2,t2, rx,ry,rz,rt, x2,y2,z2,t2)
end subroutine
! Unified point addition
pure subroutine sov_ed25519_point_add(x1,y1,z1,t1, x2,y2,z2,t2, x3,y3,z3,t3)
integer(i64), intent(in), dimension(10) :: x1,y1,z1,t1,x2,y2,z2,t2
integer(i64), intent(out), dimension(10) :: x3,y3,z3,t3
call ge_add(x1,y1,z1,t1, x2,y2,z2,t2, x3,y3,z3,t3)
end subroutine
! Negate point: (-X:Y:Z:-T)
pure subroutine sov_ed25519_point_negate(x,y,z,t)
integer(i64), intent(inout), dimension(10) :: x,y,z,t
integer(i64) :: nx(10), nt(10)
integer(i64), parameter :: ZERO(10) = 0_i64
call fe_sub(ZERO, x, nx)
call fe_sub(ZERO, t, nt)
x = nx; t = nt
end subroutine
! Encode point (X:Y:Z:T) β†’ 32 bytes (RFC 8032 Β§5.1.2)
pure subroutine sov_ed25519_encode_point(x,y,z,t, b)
integer(i64), intent(in), dimension(10) :: x,y,z,t
integer(i8), intent(out), dimension(32) :: b
integer(i64) :: recip(10), xp(10), yp(10), zx(10)
call fe_inv(z, recip) ! recip = 1/Z
call fe_mul(x, recip, xp) ! xp = X/Z
call fe_mul(y, recip, yp) ! yp = Y/Z
call fe_tobytes(yp, b)
! Set high bit of b[32] to sign bit of x (LSB of xp)
integer(i64) :: xb(10)
integer(i8) :: xbytes(32)
call fe_tobytes(xp, xbytes)
b(32) = ior(b(32), shiftl(iand(xbytes(1), 1_i8), 7))
end subroutine
! Decode 32 bytes β†’ point (RFC 8032 Β§5.1.3)
pure function sov_ed25519_decode_point(b, x,y,z,t) result(ok)
integer(i8), intent(in), dimension(32) :: b
integer(i64), intent(out), dimension(10) :: x,y,z,t
logical :: ok
integer(i8) :: yb(32)
integer(i64) :: y_fe(10), y2(10), u(10), v(10), v3(10), v7(10)
integer(i64) :: x_candidate(10), check(10), d(10), one(10), tmp(10)
integer :: sign_bit
yb = b; sign_bit = int(iand(shiftr(int(b(32),i64),7), 1_i64))
yb(32) = iand(yb(32), int(Z'7F',i8)) ! clear sign bit
call fe_frombytes(yb, y_fe)
! Recover x: x^2 = (y^2-1) / (d*y^2+1)
call fe_sq(y_fe, y2)
call ge_d(d)
one = 0_i64; one(1) = 1_i64
call fe_mul(d, y2, u)
call fe_add(u, one, v) ! v = d*y^2 + 1
call fe_sub(y2, one, u) ! u = y^2 - 1
! x = sqrt(u/v) = u * v^3 * (u*v^7)^((p-5)/8) [RFC 8032 Β§5.1.3]
call fe_sq(v, v3)
call fe_mul(v3, v, v3) ! v^3
call fe_sq(v3, v7)
call fe_mul(v7, v, v7) ! v^7
call fe_mul(u, v7, tmp) ! u*v^7
! Exponentiate to (p-5)/8 = 2^252 - 3 via the standard chain
call fe_sq_n(tmp,1, x) ! cheap: use inv chain subset
! Full (p-5)/8 exponentiation β€” reuse fe_inv chain prefix:
call fe_sq(tmp, x) ! 2
call fe_mul(tmp, x, x) ! 3
call fe_sq_n(x,2, x) ! 12
call fe_mul(tmp, x, x) ! 15
call fe_sq_n(x,1, x) ! 30
call fe_mul(tmp, x, x) ! 31 (2^5-1)
call fe_sq_n(x,5, tmp) ! (2^5-1)*2^5
call fe_mul(x,tmp, x) ! 2^10-1
call fe_sq_n(x,10, tmp)
call fe_mul(x,tmp, x) ! 2^20-1
call fe_sq_n(x,20, tmp)
call fe_mul(x,tmp, tmp) ! 2^40-1
call fe_sq_n(tmp,10,tmp)
call fe_mul(x,tmp, x) ! 2^50-1
call fe_sq_n(x,50, tmp)
call fe_mul(x,tmp, tmp) ! 2^100-1
call fe_sq_n(tmp,100,tmp)
call fe_mul(x,tmp, tmp) ! 2^200-1
call fe_sq_n(tmp,50, tmp)
call fe_mul(x,tmp, x) ! 2^250-1
call fe_sq_n(x,2, x) ! 2^252-4
call fe_mul(u, v7, tmp) ! fresh u*v^7
call fe_mul(tmp,x, x) ! x = (u*v^7)^((p-5)/8)
! x_candidate = u * v^3 * x
call fe_mul(u, v3, x_candidate)
call fe_mul(x_candidate, x, x_candidate)
! Check: v * x_candidate^2 == u
call fe_sq(x_candidate, check)
call fe_mul(v, check, check)
call fe_sub(check, u, check)
call fe_reduce(check)
! If check != 0 and check != -1 mod p: no square root
integer(i64), parameter :: NEG1(10) = &
[ int(Z'3FFFFEC',i64), int(Z'1FFFFFF',i64), int(Z'3FFFFFF',i64), &
int(Z'1FFFFFF',i64), int(Z'3FFFFFF',i64), int(Z'1FFFFFF',i64), &
int(Z'3FFFFFF',i64), int(Z'1FFFFFF',i64), int(Z'3FFFFFF',i64), &
int(Z'1FFFFFF',i64) ]
if (all(check == 0_i64)) then
ok = .true.
else if (all(check == NEG1)) then
! x = x * sqrt(-1) = x * 2^((p-1)/4) mod p
integer(i64), parameter :: SQRT_M1(10) = &
[ -32595792_i64, -7943725_i64, 9377950_i64, 3500415_i64, &
12389472_i64, -272473_i64, -25146209_i64, -2005654_i64, &
326686_i64, 11406482_i64 ]
call fe_mul(x_candidate, SQRT_M1, x_candidate)
ok = .true.
else
ok = .false.
x = 0_i64; y = 0_i64; z = 0_i64; t = 0_i64
return
end if
! Adjust sign
integer(i64) :: xbytes_check(10)
integer(i8) :: xb(32)
call fe_tobytes(x_candidate, xb)
if (int(iand(int(xb(1),i64), 1_i64)) /= sign_bit) then
call fe_sub(0_i64*x_candidate, x_candidate, x_candidate) ! negate
integer(i64), parameter :: ZERO(10) = 0_i64
call fe_sub(ZERO, x_candidate, x_candidate)
end if
x = x_candidate; y = y_fe
z(1) = 1_i64; z(2:10) = 0_i64
call fe_mul(x, y, t)
ok = .true.
end function
!══════════════════════════════════════════════════════════════════
! 9. FAULT HANDLER (writes to stderr, error stop)
!══════════════════════════════════════════════════════════════════
subroutine sov_fault(code)
integer, intent(in) :: code
write(error_unit,'(A,I0)') "SOV_FAULT: ", code
error stop
end subroutine
end module sov_monster_kernel