github.com/cloudflare/circl@v1.5.0/sign/dilithium/mode3/internal/pack.go (about)

     1  package internal
     2  
     3  import (
     4  	common "github.com/cloudflare/circl/sign/internal/dilithium"
     5  )
     6  
     7  // Writes p with norm less than or equal η into buf, which must be of
     8  // size PolyLeqEtaSize.
     9  //
    10  // Assumes coefficients of p are not normalized, but in [q-η,q+η].
    11  func PolyPackLeqEta(p *common.Poly, buf []byte) {
    12  	if DoubleEtaBits == 4 { // compiler eliminates branch
    13  		j := 0
    14  		for i := 0; i < PolyLeqEtaSize; i++ {
    15  			buf[i] = (byte(common.Q+Eta-p[j]) |
    16  				byte(common.Q+Eta-p[j+1])<<4)
    17  			j += 2
    18  		}
    19  	} else if DoubleEtaBits == 3 {
    20  		j := 0
    21  		for i := 0; i < PolyLeqEtaSize; i += 3 {
    22  			buf[i] = (byte(common.Q+Eta-p[j]) |
    23  				(byte(common.Q+Eta-p[j+1]) << 3) |
    24  				(byte(common.Q+Eta-p[j+2]) << 6))
    25  			buf[i+1] = ((byte(common.Q+Eta-p[j+2]) >> 2) |
    26  				(byte(common.Q+Eta-p[j+3]) << 1) |
    27  				(byte(common.Q+Eta-p[j+4]) << 4) |
    28  				(byte(common.Q+Eta-p[j+5]) << 7))
    29  			buf[i+2] = ((byte(common.Q+Eta-p[j+5]) >> 1) |
    30  				(byte(common.Q+Eta-p[j+6]) << 2) |
    31  				(byte(common.Q+Eta-p[j+7]) << 5))
    32  			j += 8
    33  		}
    34  	} else {
    35  		panic("eta not supported")
    36  	}
    37  }
    38  
    39  // Sets p to the polynomial of norm less than or equal η encoded in the
    40  // given buffer of size PolyLeqEtaSize.
    41  //
    42  // Output coefficients of p are not normalized, but in [q-η,q+η] provided
    43  // buf was created using PackLeqEta.
    44  //
    45  // Beware, for arbitrary buf the coefficients of p might end up in
    46  // the interval [q-2^b,q+2^b] where b is the least b with η≤2^b.
    47  func PolyUnpackLeqEta(p *common.Poly, buf []byte) {
    48  	if DoubleEtaBits == 4 { // compiler eliminates branch
    49  		j := 0
    50  		for i := 0; i < PolyLeqEtaSize; i++ {
    51  			p[j] = common.Q + Eta - uint32(buf[i]&15)
    52  			p[j+1] = common.Q + Eta - uint32(buf[i]>>4)
    53  			j += 2
    54  		}
    55  	} else if DoubleEtaBits == 3 {
    56  		j := 0
    57  		for i := 0; i < PolyLeqEtaSize; i += 3 {
    58  			p[j] = common.Q + Eta - uint32(buf[i]&7)
    59  			p[j+1] = common.Q + Eta - uint32((buf[i]>>3)&7)
    60  			p[j+2] = common.Q + Eta - uint32((buf[i]>>6)|((buf[i+1]<<2)&7))
    61  			p[j+3] = common.Q + Eta - uint32((buf[i+1]>>1)&7)
    62  			p[j+4] = common.Q + Eta - uint32((buf[i+1]>>4)&7)
    63  			p[j+5] = common.Q + Eta - uint32((buf[i+1]>>7)|((buf[i+2]<<1)&7))
    64  			p[j+6] = common.Q + Eta - uint32((buf[i+2]>>2)&7)
    65  			p[j+7] = common.Q + Eta - uint32((buf[i+2]>>5)&7)
    66  			j += 8
    67  		}
    68  	} else {
    69  		panic("eta not supported")
    70  	}
    71  }
    72  
    73  // Writes v with coefficients in {0, 1} of which at most ω non-zero
    74  // to buf, which must have length ω+k.
    75  func (v *VecK) PackHint(buf []byte) {
    76  	// The packed hint starts with the indices of the non-zero coefficients
    77  	// For instance:
    78  	//
    79  	//    (x⁵⁶ + x¹⁰⁰, x²⁵⁵, 0, x² + x²³, x¹)
    80  	//
    81  	// Yields
    82  	//
    83  	//  56, 100, 255, 2, 23, 1
    84  	//
    85  	// Then we pad with zeroes until we have a list of ω items:
    86  	// //  56, 100, 255, 2, 23, 1, 0, 0, ..., 0
    87  	//
    88  	// Then we finish with a list of the switch-over-indices in this
    89  	// list between polynomials, so:
    90  	//
    91  	//  56, 100, 255, 2, 23, 1, 0, 0, ..., 0, 2, 3, 3, 5, 6
    92  
    93  	off := uint8(0)
    94  	for i := 0; i < K; i++ {
    95  		for j := uint16(0); j < common.N; j++ {
    96  			if v[i][j] != 0 {
    97  				buf[off] = uint8(j)
    98  				off++
    99  			}
   100  		}
   101  		buf[Omega+i] = off
   102  	}
   103  	for ; off < Omega; off++ {
   104  		buf[off] = 0
   105  	}
   106  }
   107  
   108  // Sets v to the vector encoded using VecK.PackHint()
   109  //
   110  // Returns whether unpacking was successful.
   111  func (v *VecK) UnpackHint(buf []byte) bool {
   112  	// A priori, there would be several reasonable ways to encode the same
   113  	// hint vector.  We take care to only allow only one encoding, to ensure
   114  	// "strong unforgeability".
   115  	//
   116  	// See PackHint() source for description of the encoding.
   117  	*v = VecK{}         // zero v
   118  	prevSOP := uint8(0) // previous switch-over-point
   119  	for i := 0; i < K; i++ {
   120  		SOP := buf[Omega+i]
   121  		if SOP < prevSOP || SOP > Omega {
   122  			return false // ensures switch-over-points are increasing
   123  		}
   124  		for j := prevSOP; j < SOP; j++ {
   125  			if j > prevSOP && buf[j] <= buf[j-1] {
   126  				return false // ensures indices are increasing (within a poly)
   127  			}
   128  			v[i][buf[j]] = 1
   129  		}
   130  		prevSOP = SOP
   131  	}
   132  	for j := prevSOP; j < Omega; j++ {
   133  		if buf[j] != 0 {
   134  			return false // ensures padding indices are zero
   135  		}
   136  	}
   137  
   138  	return true
   139  }
   140  
   141  // Sets p to the polynomial packed into buf by PolyPackLeGamma1.
   142  //
   143  // p will be normalized.
   144  func PolyUnpackLeGamma1(p *common.Poly, buf []byte) {
   145  	if Gamma1Bits == 17 {
   146  		j := 0
   147  		for i := 0; i < PolyLeGamma1Size; i += 9 {
   148  			p0 := uint32(buf[i]) | (uint32(buf[i+1]) << 8) |
   149  				(uint32(buf[i+2]&0x3) << 16)
   150  			p1 := uint32(buf[i+2]>>2) | (uint32(buf[i+3]) << 6) |
   151  				(uint32(buf[i+4]&0xf) << 14)
   152  			p2 := uint32(buf[i+4]>>4) | (uint32(buf[i+5]) << 4) |
   153  				(uint32(buf[i+6]&0x3f) << 12)
   154  			p3 := uint32(buf[i+6]>>6) | (uint32(buf[i+7]) << 2) |
   155  				(uint32(buf[i+8]) << 10)
   156  
   157  			// coefficients in [0,…,2γ₁)
   158  			p0 = Gamma1 - p0 // (-γ₁,…,γ₁]
   159  			p1 = Gamma1 - p1
   160  			p2 = Gamma1 - p2
   161  			p3 = Gamma1 - p3
   162  
   163  			p0 += uint32(int32(p0)>>31) & common.Q // normalize
   164  			p1 += uint32(int32(p1)>>31) & common.Q
   165  			p2 += uint32(int32(p2)>>31) & common.Q
   166  			p3 += uint32(int32(p3)>>31) & common.Q
   167  
   168  			p[j] = p0
   169  			p[j+1] = p1
   170  			p[j+2] = p2
   171  			p[j+3] = p3
   172  
   173  			j += 4
   174  		}
   175  	} else if Gamma1Bits == 19 {
   176  		j := 0
   177  		for i := 0; i < PolyLeGamma1Size; i += 5 {
   178  			p0 := uint32(buf[i]) | (uint32(buf[i+1]) << 8) |
   179  				(uint32(buf[i+2]&0xf) << 16)
   180  			p1 := uint32(buf[i+2]>>4) | (uint32(buf[i+3]) << 4) |
   181  				(uint32(buf[i+4]) << 12)
   182  
   183  			p0 = Gamma1 - p0
   184  			p1 = Gamma1 - p1
   185  
   186  			p0 += uint32(int32(p0)>>31) & common.Q
   187  			p1 += uint32(int32(p1)>>31) & common.Q
   188  
   189  			p[j] = p0
   190  			p[j+1] = p1
   191  
   192  			j += 2
   193  		}
   194  	} else {
   195  		panic("γ₁ not supported")
   196  	}
   197  }
   198  
   199  // Writes p whose coefficients are in (-γ₁,γ₁] into buf
   200  // which has to be of length PolyLeGamma1Size.
   201  //
   202  // Assumes p is normalized.
   203  func PolyPackLeGamma1(p *common.Poly, buf []byte) {
   204  	if Gamma1Bits == 17 {
   205  		j := 0
   206  		// coefficients in [0,…,γ₁] ∪ (q-γ₁,…,q)
   207  		for i := 0; i < PolyLeGamma1Size; i += 9 {
   208  			p0 := Gamma1 - p[j]                    // [0,…,γ₁] ∪ (γ₁-q,…,2γ₁-q)
   209  			p0 += uint32(int32(p0)>>31) & common.Q // [0,…,2γ₁)
   210  			p1 := Gamma1 - p[j+1]
   211  			p1 += uint32(int32(p1)>>31) & common.Q
   212  			p2 := Gamma1 - p[j+2]
   213  			p2 += uint32(int32(p2)>>31) & common.Q
   214  			p3 := Gamma1 - p[j+3]
   215  			p3 += uint32(int32(p3)>>31) & common.Q
   216  
   217  			buf[i+0] = byte(p0)
   218  			buf[i+1] = byte(p0 >> 8)
   219  			buf[i+2] = byte(p0>>16) | byte(p1<<2)
   220  			buf[i+3] = byte(p1 >> 6)
   221  			buf[i+4] = byte(p1>>14) | byte(p2<<4)
   222  			buf[i+5] = byte(p2 >> 4)
   223  			buf[i+6] = byte(p2>>12) | byte(p3<<6)
   224  			buf[i+7] = byte(p3 >> 2)
   225  			buf[i+8] = byte(p3 >> 10)
   226  
   227  			j += 4
   228  		}
   229  	} else if Gamma1Bits == 19 {
   230  		j := 0
   231  		for i := 0; i < PolyLeGamma1Size; i += 5 {
   232  			// Coefficients are in [0, γ₁] ∪ (Q-γ₁, Q)
   233  			p0 := Gamma1 - p[j]
   234  			p0 += uint32(int32(p0)>>31) & common.Q
   235  			p1 := Gamma1 - p[j+1]
   236  			p1 += uint32(int32(p1)>>31) & common.Q
   237  
   238  			buf[i+0] = byte(p0)
   239  			buf[i+1] = byte(p0 >> 8)
   240  			buf[i+2] = byte(p0>>16) | byte(p1<<4)
   241  			buf[i+3] = byte(p1 >> 4)
   242  			buf[i+4] = byte(p1 >> 12)
   243  
   244  			j += 2
   245  		}
   246  	} else {
   247  		panic("γ₁ not supported")
   248  	}
   249  }
   250  
   251  // Pack w₁ into buf, which must be of length PolyW1Size.
   252  //
   253  // Assumes w₁ is normalized.
   254  func PolyPackW1(p *common.Poly, buf []byte) {
   255  	if Gamma1Bits == 19 {
   256  		p.PackLe16(buf)
   257  	} else if Gamma1Bits == 17 {
   258  		j := 0
   259  		for i := 0; i < PolyW1Size; i += 3 {
   260  			buf[i] = byte(p[j]) | byte(p[j+1]<<6)
   261  			buf[i+1] = byte(p[j+1]>>2) | byte(p[j+2]<<4)
   262  			buf[i+2] = byte(p[j+2]>>4) | byte(p[j+3]<<2)
   263  			j += 4
   264  		}
   265  	} else {
   266  		panic("unsupported γ₁")
   267  	}
   268  }