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  }