package crypto import ( "git.mleku.dev/mleku/dendrite/pkg/epoch" "git.mleku.dev/mleku/dendrite/pkg/permutation" "git.mleku.dev/mleku/dendrite/pkg/state" ) // PhasePerms returns the S_3 permutation pair (binPerm, decPerm) for a // given token index within an epoch's phase schedule. // // binPerm is selected by |Phase.Num| mod 6 -- the binary clock's // fractional position determines the inner trigram rotation. // decPerm is selected by Phase.Denom mod 6 -- the decimal clock's // normalization factor determines the outer trigram rotation. // // At epoch boundaries (Phase = 0/1), both permutations are Identity // because 0 mod 6 = 0 and 1 mod 6 = 1 (Identity and Swap01). // This is intentional: epoch boundaries are synchronization points // where the shadow channel collapses to zero. func PhasePerms(ep epoch.Epoch, index int) (binPerm, decPerm permutation.Perm) { phase := ep.Phase(int64(index + 1)) num := phase.Num if num < 0 { num = -num } binPerm = permutation.Perm(num % 6) decPerm = permutation.Perm(phase.Denom % 6) return } // PhaseProjection returns the projection vertex and key derived from // the epoch phase at a given token index. // // vertex = Phase.Denom mod 8 (3-bit cube corner from decimal clock) // key = |Phase.Num| mod 8 (3-bit projection direction from binary clock) func PhaseProjection(ep epoch.Epoch, index int) (vertex, key uint8) { phase := ep.Phase(int64(index + 1)) num := phase.Num if num < 0 { num = -num } vertex = uint8(phase.Denom % 8) key = uint8(num % 8) return } // ShadowDecompose applies the phase-derived permutation pair to a // hexagram, rotating inner and outer trigrams independently. // This is the forward (encryption) direction. // // The inner trigram is permuted by binPerm (binary clock component). // The outer trigram is permuted by decPerm (decimal clock component). // Neither projection alone determines the original hexagram. func ShadowDecompose(h state.Hexagram, binP, decP permutation.Perm) state.Hexagram { inner := binP.ApplyTrigram(h.Inner()) outer := decP.ApplyTrigram(h.Outer()) return state.Hex(inner, outer) } // ShadowRecompose applies the inverse permutation pair to recover the // original hexagram from a shadow-decomposed one. // This is the reverse (decryption) direction. func ShadowRecompose(h state.Hexagram, binP, decP permutation.Perm) state.Hexagram { inner := binP.Inverse().ApplyTrigram(h.Inner()) outer := decP.Inverse().ApplyTrigram(h.Outer()) return state.Hex(inner, outer) } // PhasePairIndex returns the canonical index (0-35) of an S_3×S_3 // permutation pair. The home pair (determined by the epoch phase) // always maps to index 0. Other pairs are numbered 1-35. // // This supports the shadow compression algorithm: the deviation // from the home pair encodes auxiliary data bits. func PhasePairIndex(binP, decP, homeBin, homeDec permutation.Perm) uint8 { if binP == homeBin && decP == homeDec { return 0 } // Canonical ordering: (binP * 6 + decP), with home pair removed. raw := uint8(binP)*6 + uint8(decP) home := uint8(homeBin)*6 + uint8(homeDec) if raw < home { return raw + 1 } return raw // home was removed, so indices above home shift down by 1 } // PairFromIndex recovers the (binPerm, decPerm) pair from a canonical // index, given the home pair. func PairFromIndex(idx uint8, homeBin, homeDec permutation.Perm) (permutation.Perm, permutation.Perm) { if idx == 0 { return homeBin, homeDec } home := uint8(homeBin)*6 + uint8(homeDec) raw := idx if raw <= home { raw = raw - 1 } // raw is now the absolute index in the 6×6 grid return permutation.Perm(raw / 6), permutation.Perm(raw % 6) }