github.com/cloudflare/circl@v1.5.0/pke/kyber/internal/common/sample.go (about) 1 package common 2 3 import ( 4 "encoding/binary" 5 6 "github.com/cloudflare/circl/internal/sha3" 7 "github.com/cloudflare/circl/simd/keccakf1600" 8 ) 9 10 // DeriveX4Available indicates whether the system supports the quick fourway 11 // sampling variants like PolyDeriveUniformX4. 12 var DeriveX4Available = keccakf1600.IsEnabledX4() 13 14 // Samples p from a centered binomial distribution with given η. 15 // 16 // Essentially CBD_η(PRF(seed, nonce)) from the specification. 17 func (p *Poly) DeriveNoise(seed []byte, nonce uint8, eta int) { 18 switch eta { 19 case 2: 20 p.DeriveNoise2(seed, nonce) 21 case 3: 22 p.DeriveNoise3(seed, nonce) 23 default: 24 panic("unsupported eta") 25 } 26 } 27 28 // Sample p from a centered binomial distribution with n=6 and p=½ - that is: 29 // coefficients are in {-3, -2, -1, 0, 1, 2, 3} with probabilities {1/64, 3/32, 30 // 15/64, 5/16, 16/64, 3/32, 1/64}. 31 func (p *Poly) DeriveNoise3(seed []byte, nonce uint8) { 32 keySuffix := [1]byte{nonce} 33 h := sha3.NewShake256() 34 _, _ = h.Write(seed[:]) 35 _, _ = h.Write(keySuffix[:]) 36 37 // The distribution at hand is exactly the same as that 38 // of (a₁ + a₂ + a₃) - (b₁ + b₂+b₃) where a_i,b_i~U(1). Thus we need 39 // 6 bits per coefficients, thus 192 bytes of input entropy. 40 41 // We add two extra zero bytes in the buffer to be able to read 8 bytes 42 // at the same time (while using only 6.) 43 var buf [192 + 2]byte 44 _, _ = h.Read(buf[:192]) 45 46 for i := 0; i < 32; i++ { 47 // t is interpreted as a₁ + 2a₂ + 4a₃ + 8b₁ + 16b₂ + …. 48 t := binary.LittleEndian.Uint64(buf[6*i:]) 49 50 d := t & 0x249249249249 // a₁ + 8b₁ + … 51 d += (t >> 1) & 0x249249249249 // a₁ + a₂ + 8(b₁ + b₂) + … 52 d += (t >> 2) & 0x249249249249 // a₁ + a₂ + a₃ + 4(b₁ + b₂ + b₃) + … 53 54 for j := 0; j < 8; j++ { 55 a := int16(d) & 0x7 // a₁ + a₂ + a₃ 56 d >>= 3 57 b := int16(d) & 0x7 // b₁ + b₂ + b₃ 58 d >>= 3 59 p[8*i+j] = a - b 60 } 61 } 62 } 63 64 // Sample p from a centered binomial distribution with n=4 and p=½ - that is: 65 // coefficients are in {-2, -1, 0, 1, 2} with probabilities {1/16, 1/4, 66 // 3/8, 1/4, 1/16}. 67 func (p *Poly) DeriveNoise2(seed []byte, nonce uint8) { 68 keySuffix := [1]byte{nonce} 69 h := sha3.NewShake256() 70 _, _ = h.Write(seed[:]) 71 _, _ = h.Write(keySuffix[:]) 72 73 // The distribution at hand is exactly the same as that 74 // of (a + a') - (b + b') where a,a',b,b'~U(1). Thus we need 4 bits per 75 // coefficients, thus 128 bytes of input entropy. 76 77 var buf [128]byte 78 _, _ = h.Read(buf[:]) 79 80 for i := 0; i < 16; i++ { 81 // t is interpreted as a + 2a' + 4b + 8b' + …. 82 t := binary.LittleEndian.Uint64(buf[8*i:]) 83 84 d := t & 0x5555555555555555 // a + 4b + … 85 d += (t >> 1) & 0x5555555555555555 // a+a' + 4(b + b') + … 86 87 for j := 0; j < 16; j++ { 88 a := int16(d) & 0x3 89 d >>= 2 90 b := int16(d) & 0x3 91 d >>= 2 92 p[16*i+j] = a - b 93 } 94 } 95 } 96 97 // For each i, sample ps[i] uniformly from the given seed for coordinates 98 // xs[i] and ys[i]. ps[i] may be nil and is ignored in that case. 99 // 100 // Can only be called when DeriveX4Available is true. 101 func PolyDeriveUniformX4(ps [4]*Poly, seed *[32]byte, xs, ys [4]uint8) { 102 var perm keccakf1600.StateX4 103 state := perm.Initialize(false) 104 105 // Absorb the seed in the four states 106 for i := 0; i < 4; i++ { 107 v := binary.LittleEndian.Uint64(seed[8*i : 8*(i+1)]) 108 for j := 0; j < 4; j++ { 109 state[i*4+j] = v 110 } 111 } 112 113 // Absorb the coordinates, the SHAKE128 domain separator (0b1111), the 114 // start of the padding (0b…001) and the end of the padding 0b100…. 115 // Recall that the rate of SHAKE128 is 168; ie. 21 uint64s. 116 for j := 0; j < 4; j++ { 117 state[4*4+j] = uint64(xs[j]) | (uint64(ys[j]) << 8) | (0x1f << 16) 118 state[20*4+j] = 0x80 << 56 119 } 120 121 var idx [4]int // indices into ps 122 for j := 0; j < 4; j++ { 123 if ps[j] == nil { 124 idx[j] = N // mark nil polynomials as completed 125 } 126 } 127 128 done := false 129 for !done { 130 // Applies KeccaK-f[1600] to state to get the next 21 uint64s of each of 131 // the four SHAKE128 streams. 132 perm.Permute() 133 134 done = true 135 136 PolyLoop: 137 for j := 0; j < 4; j++ { 138 if idx[j] == N { 139 continue 140 } 141 for i := 0; i < 7; i++ { 142 var t [16]uint16 143 144 v1 := state[i*3*4+j] 145 v2 := state[(i*3+1)*4+j] 146 v3 := state[(i*3+2)*4+j] 147 148 t[0] = uint16(v1) & 0xfff 149 t[1] = uint16(v1>>12) & 0xfff 150 t[2] = uint16(v1>>24) & 0xfff 151 t[3] = uint16(v1>>36) & 0xfff 152 t[4] = uint16(v1>>48) & 0xfff 153 t[5] = uint16((v1>>60)|(v2<<4)) & 0xfff 154 155 t[6] = uint16(v2>>8) & 0xfff 156 t[7] = uint16(v2>>20) & 0xfff 157 t[8] = uint16(v2>>32) & 0xfff 158 t[9] = uint16(v2>>44) & 0xfff 159 t[10] = uint16((v2>>56)|(v3<<8)) & 0xfff 160 161 t[11] = uint16(v3>>4) & 0xfff 162 t[12] = uint16(v3>>16) & 0xfff 163 t[13] = uint16(v3>>28) & 0xfff 164 t[14] = uint16(v3>>40) & 0xfff 165 t[15] = uint16(v3>>52) & 0xfff 166 167 for k := 0; k < 16; k++ { 168 if t[k] < uint16(Q) { 169 ps[j][idx[j]] = int16(t[k]) 170 idx[j]++ 171 if idx[j] == N { 172 continue PolyLoop 173 } 174 } 175 } 176 } 177 178 done = false 179 } 180 } 181 182 for i := 0; i < 4; i++ { 183 if ps[i] != nil { 184 ps[i].Tangle() 185 } 186 } 187 } 188 189 // Sample p uniformly from the given seed and x and y coordinates. 190 // 191 // Coefficients are reduced and will be in "tangled" order. See Tangle(). 192 func (p *Poly) DeriveUniform(seed *[32]byte, x, y uint8) { 193 var seedSuffix [2]byte 194 var buf [168]byte // rate of SHAKE-128 195 196 seedSuffix[0] = x 197 seedSuffix[1] = y 198 199 h := sha3.NewShake128() 200 _, _ = h.Write(seed[:]) 201 _, _ = h.Write(seedSuffix[:]) 202 203 i := 0 204 for { 205 _, _ = h.Read(buf[:]) 206 207 for j := 0; j < 168; j += 3 { 208 t1 := (uint16(buf[j]) | (uint16(buf[j+1]) << 8)) & 0xfff 209 t2 := (uint16(buf[j+1]>>4) | (uint16(buf[j+2]) << 4)) & 0xfff 210 211 if t1 < uint16(Q) { 212 p[i] = int16(t1) 213 i++ 214 215 if i == N { 216 break 217 } 218 } 219 220 if t2 < uint16(Q) { 221 p[i] = int16(t2) 222 i++ 223 224 if i == N { 225 break 226 } 227 } 228 } 229 230 if i == N { 231 break 232 } 233 } 234 235 p.Tangle() 236 }