gaussian.go raw
1 package crypto
2
3 import (
4 "git.mleku.dev/mleku/dendrite/pkg/axiom"
5 "git.mleku.dev/mleku/dendrite/pkg/dissolve"
6 "git.mleku.dev/mleku/dendrite/pkg/lattice"
7 "git.mleku.dev/mleku/dendrite/pkg/ratio"
8 )
9
10 // SiteMark records one lattice site's state in a ciphertext or sample.
11 type SiteMark struct {
12 Index uint64 // position in the lattice
13 Occupied bool // element bonded here?
14 TypeTag string // element type tag (if occupied)
15 ValueHash Hamadryad // Hamadryad hash of element value (if occupied)
16 Projection uint8 // 8-bit: [age(2)|key(3)|vertex(3)]
17 ProjPath uint16 // rendering path index (token stream position)
18 Age uint8 // 2-bit ADSR phase (0=Attack, 1=Decay, 2=Sustain, 3=Release)
19 Perm uint8 // S_3 permutation index
20 LockIn ratio.Ratio // bond strength
21 }
22
23 // NoiseSample records a dissolution event during encryption.
24 type NoiseSample struct {
25 Index uint64 // which site dissolved
26 TypeTag string // what was there
27 LockIn ratio.Ratio // how strong the bond was before dissolution
28 }
29
30 // GaussianSampler produces discrete Gaussian samples using the
31 // lattice's own dissolution dynamics. Elements that survive
32 // dissolution are samples: their probability of survival at
33 // a site is governed by ContextualLockIn relative to the threshold.
34 type GaussianSampler struct {
35 Lattice *lattice.Lattice
36 Width ratio.Ratio // sigma parameter (controls spread)
37 Threshold ratio.Ratio // dissolution cutoff = sampling boundary
38 }
39
40 // NewSampler creates a sampler from an existing lattice.
41 // The threshold determines the dissolution cutoff: elements with
42 // ContextualLockIn below threshold are dissolved (become noise).
43 func NewSampler(l *lattice.Lattice, threshold ratio.Ratio) *GaussianSampler {
44 return &GaussianSampler{
45 Lattice: l,
46 Width: threshold,
47 Threshold: threshold,
48 }
49 }
50
51 // Sample runs one dissolution scan and returns the surviving
52 // bonding pattern as a sample from the discrete Gaussian.
53 func (gs *GaussianSampler) Sample() []SiteMark {
54 // Run dissolution — elements below threshold are removed.
55 dissolved := make(chan axiom.Element, gs.Lattice.Size())
56 events := make(chan dissolve.Event, gs.Lattice.Size())
57
58 cfg := dissolve.Config{
59 Threshold: gs.Threshold,
60 }
61 dissolve.ScanOnce(gs.Lattice, cfg, dissolved, events)
62
63 // Drain channels.
64 close(dissolved)
65 close(events)
66 for range dissolved {
67 }
68 for range events {
69 }
70
71 // Collect surviving pattern.
72 return snapshot(gs.Lattice)
73 }
74
75 // NoiseVector produces a noise vector for LWE encryption by
76 // running dissolution and recording which sites dissolved.
77 // Length = lattice dimension. Values are lock-in depths of dissolved sites.
78 func (gs *GaussianSampler) NoiseVector() ([]ratio.Ratio, []NoiseSample) {
79 dissolved := make(chan axiom.Element, gs.Lattice.Size())
80 events := make(chan dissolve.Event, gs.Lattice.Size())
81
82 cfg := dissolve.Config{
83 Threshold: gs.Threshold,
84 }
85 dissolve.ScanOnce(gs.Lattice, cfg, dissolved, events)
86
87 close(dissolved)
88 close(events)
89
90 // Drain dissolved elements.
91 for range dissolved {
92 }
93
94 // Collect noise samples from dissolution events.
95 var noise []NoiseSample
96 noiseVec := make([]ratio.Ratio, gs.Lattice.Size())
97 for ev := range events {
98 idx := uint64(ev.NodeID)
99 tag := ""
100 if ev.Element != nil {
101 tag = ev.Element.Type()
102 }
103 noise = append(noise, NoiseSample{
104 Index: idx,
105 TypeTag: tag,
106 LockIn: ev.LockIn,
107 })
108 if int(idx) < len(noiseVec) {
109 noiseVec[idx] = ev.LockIn
110 }
111 }
112
113 return noiseVec, noise
114 }
115
116 // snapshot captures the current bonding pattern of the lattice.
117 func snapshot(l *lattice.Lattice) []SiteMark {
118 nodes := l.Nodes()
119 marks := make([]SiteMark, len(nodes))
120 for i, n := range nodes {
121 marks[i] = SiteMark{
122 Index: uint64(n.ID()),
123 Occupied: n.Occupied(),
124 Projection: n.ProjectionByte(), // [age(2)|key(3)|vertex(3)]
125 ProjPath: n.ProjectionPath(),
126 Age: n.Age(),
127 Perm: n.Permutation(),
128 LockIn: n.LockIn(),
129 }
130 if n.Occupied() {
131 occ := n.Occupant()
132 marks[i].TypeTag = occ.Type()
133 marks[i].ValueHash = hashValue(occ.Value())
134 }
135 }
136 return marks
137 }
138