Download src/sov_monster_kernel.f90.bak from Snapkitty/sov-kernel-monster: direct link, hf CLI and curl.
- Browser
- Download file 79.1 kB
-
https://huggingface.co/Snapkitty/sov-kernel-monster/resolve/main/src/sov_monster_kernel.f90.bak
- Command line
-
hf download hf://Snapkitty/sov-kernel-monster/src/sov_monster_kernel.f90.bak
-
curl -L -o sov_monster_kernel.f90.bak https://huggingface.co/Snapkitty/sov-kernel-monster/resolve/main/src/sov_monster_kernel.f90.bak
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 | |