github.com/cloudflare/circl@v1.5.0/sign/dilithium/mode3/internal/rounding.go (about) 1 package internal 2 3 import ( 4 common "github.com/cloudflare/circl/sign/internal/dilithium" 5 ) 6 7 // Splits 0 ≤ a < q into a₀ and a₁ with a = a₁*α + a₀ with -α/2 < a₀ ≤ α/2, 8 // except for when we would have a₁ = (q-1)/α in which case a₁=0 is taken 9 // and -α/2 ≤ a₀ < 0. Returns a₀ + q. Note 0 ≤ a₁ < (q-1)/α. 10 // Recall α = 2γ₂. 11 func decompose(a uint32) (a0plusQ, a1 uint32) { 12 // a₁ = ⌈a / 128⌉ 13 a1 = (a + 127) >> 7 14 15 if Alpha == 523776 { 16 // 1025/2²² is close enough to 1/4092 so that a₁ 17 // becomes a/α rounded down. 18 a1 = ((a1*1025 + (1 << 21)) >> 22) 19 20 // For the corner-case a₁ = (q-1)/α = 16, we have to set a₁=0. 21 a1 &= 15 22 } else if Alpha == 190464 { 23 // 1488/2²⁴ is close enough to 1/1488 so that a₁ 24 // becomes a/α rounded down. 25 a1 = ((a1 * 11275) + (1 << 23)) >> 24 26 27 // For the corner-case a₁ = (q-1)/α = 44, we have to set a₁=0. 28 a1 ^= uint32(int32(43-a1)>>31) & a1 29 } else { 30 panic("unsupported α") 31 } 32 33 a0plusQ = a - a1*Alpha 34 35 // In the corner-case, when we set a₁=0, we will incorrectly 36 // have a₀ > (q-1)/2 and we'll need to subtract q. As we 37 // return a₀ + q, that comes down to adding q if a₀ < (q-1)/2. 38 a0plusQ += uint32(int32(a0plusQ-(common.Q-1)/2)>>31) & common.Q 39 40 return 41 } 42 43 // Assume 0 ≤ r, f < Q with ‖f‖_∞ ≤ α/2. Decompose r as r = r1*α + r0 as 44 // computed by decompose(). Write r' := r - f (mod Q). Now, decompose 45 // r'=r-f again as r' = r'1*α + r'0 using decompose(). As f is small, we 46 // have r'1 = r1 + h, where h ∈ {-1, 0, 1}. makeHint() computes |h| 47 // given z0 := r0 - f (mod Q) and r1. With |h|, which is called the hint, 48 // we can reconstruct r1 using only r' = r - f, which is done by useHint(). 49 // To wit: 50 // 51 // useHint( r - f, makeHint( r0 - f, r1 ) ) = r1. 52 // 53 // Assumes 0 ≤ z0 < Q. 54 func makeHint(z0, r1 uint32) uint32 { 55 // If -α/2 < r0 - f ≤ α/2, then r1*α + r0 - f is a valid decomposition of r' 56 // with the restrictions of decompose() and so r'1 = r1. So the hint 57 // should be 0. This is covered by the first two inequalities. 58 // There is one other case: if r0 - f = -α/2, then r1*α + r0 - f is also 59 // a valid decomposition if r1 = 0. In the other cases a one is carried 60 // and the hint should be 1. 61 if z0 <= Gamma2 || z0 > common.Q-Gamma2 || (z0 == common.Q-Gamma2 && r1 == 0) { 62 return 0 63 } 64 return 1 65 } 66 67 // Uses the hint created by makeHint() to reconstruct r1 from r'=r-f; see 68 // documentation of makeHint() for context. 69 // Assumes 0 ≤ r' < Q. 70 func useHint(rp uint32, hint uint32) uint32 { 71 rp0plusQ, rp1 := decompose(rp) 72 if hint == 0 { 73 return rp1 74 } 75 if rp0plusQ > common.Q { 76 return (rp1 + 1) & 15 77 } 78 return (rp1 - 1) & 15 79 } 80 81 // Sets p to the hint polynomial for p0 the modified low bits and p1 82 // the unmodified high bits --- see makeHint(). 83 // 84 // Returns the number of ones in the hint polynomial. 85 func PolyMakeHint(p, p0, p1 *common.Poly) (pop uint32) { 86 for i := 0; i < common.N; i++ { 87 h := makeHint(p0[i], p1[i]) 88 pop += h 89 p[i] = h 90 } 91 return 92 } 93 94 // Computes corrections to the high bits of the polynomial q according 95 // to the hints in h and sets p to the corrected high bits. Returns p. 96 func PolyUseHint(p, q, hint *common.Poly) { 97 var q0PlusQ common.Poly 98 99 // See useHint() and makeHint() for an explanation. We reimplement it 100 // here so that we can call Poly.Decompose(), which might be way faster 101 // than calling decompose() in a loop (for instance when having AVX2.) 102 103 PolyDecompose(q, &q0PlusQ, p) 104 105 for i := 0; i < common.N; i++ { 106 if hint[i] == 0 { 107 continue 108 } 109 if Gamma2 == 261888 { 110 if q0PlusQ[i] > common.Q { 111 p[i] = (p[i] + 1) & 15 112 } else { 113 p[i] = (p[i] - 1) & 15 114 } 115 } else if Gamma2 == 95232 { 116 if q0PlusQ[i] > common.Q { 117 if p[i] == 43 { 118 p[i] = 0 119 } else { 120 p[i]++ 121 } 122 } else { 123 if p[i] == 0 { 124 p[i] = 43 125 } else { 126 p[i]-- 127 } 128 } 129 } else { 130 panic("unsupported γ₂") 131 } 132 } 133 } 134 135 // Splits each of the coefficients of p using decompose. 136 func PolyDecompose(p, p0PlusQ, p1 *common.Poly) { 137 for i := 0; i < common.N; i++ { 138 p0PlusQ[i], p1[i] = decompose(p[i]) 139 } 140 }