gitlab.com/yawning/chacha20.git@v0.0.0-20230427033715-7877545b1b37/internal/ref/impl.go (about) 1 // Copryright (C) 2019 Yawning Angel 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 // Package ref provides the portable ChaCha20 implementation. 17 package ref 18 19 import ( 20 "encoding/binary" 21 "math/bits" 22 23 "gitlab.com/yawning/chacha20.git/internal/api" 24 ) 25 26 const rounds = 20 27 28 // Impl is the reference implementation (exposed for testing). 29 var Impl = &implRef{} 30 31 type implRef struct{} 32 33 func (impl *implRef) Name() string { 34 return "ref" 35 } 36 37 func (impl *implRef) Blocks(x *[api.StateSize]uint32, dst, src []byte, nrBlocks int) { 38 for n := 0; n < nrBlocks; n++ { 39 x0, x1, x2, x3 := api.Sigma0, api.Sigma1, api.Sigma2, api.Sigma3 40 x4, x5, x6, x7, x8, x9, x10, x11, x12, x13, x14, x15 := x[4], x[5], x[6], x[7], x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15] 41 42 for i := rounds; i > 0; i -= 2 { 43 // quarterround(x, 0, 4, 8, 12) 44 x0 += x4 45 x12 ^= x0 46 x12 = bits.RotateLeft32(x12, 16) 47 x8 += x12 48 x4 ^= x8 49 x4 = bits.RotateLeft32(x4, 12) 50 x0 += x4 51 x12 ^= x0 52 x12 = bits.RotateLeft32(x12, 8) 53 x8 += x12 54 x4 ^= x8 55 x4 = bits.RotateLeft32(x4, 7) 56 57 // quarterround(x, 1, 5, 9, 13) 58 x1 += x5 59 x13 ^= x1 60 x13 = bits.RotateLeft32(x13, 16) 61 x9 += x13 62 x5 ^= x9 63 x5 = bits.RotateLeft32(x5, 12) 64 x1 += x5 65 x13 ^= x1 66 x13 = bits.RotateLeft32(x13, 8) 67 x9 += x13 68 x5 ^= x9 69 x5 = bits.RotateLeft32(x5, 7) 70 71 // quarterround(x, 2, 6, 10, 14) 72 x2 += x6 73 x14 ^= x2 74 x14 = bits.RotateLeft32(x14, 16) 75 x10 += x14 76 x6 ^= x10 77 x6 = bits.RotateLeft32(x6, 12) 78 x2 += x6 79 x14 ^= x2 80 x14 = bits.RotateLeft32(x14, 8) 81 x10 += x14 82 x6 ^= x10 83 x6 = bits.RotateLeft32(x6, 7) 84 85 // quarterround(x, 3, 7, 11, 15) 86 x3 += x7 87 x15 ^= x3 88 x15 = bits.RotateLeft32(x15, 16) 89 x11 += x15 90 x7 ^= x11 91 x7 = bits.RotateLeft32(x7, 12) 92 x3 += x7 93 x15 ^= x3 94 x15 = bits.RotateLeft32(x15, 8) 95 x11 += x15 96 x7 ^= x11 97 x7 = bits.RotateLeft32(x7, 7) 98 99 // quarterround(x, 0, 5, 10, 15) 100 x0 += x5 101 x15 ^= x0 102 x15 = bits.RotateLeft32(x15, 16) 103 x10 += x15 104 x5 ^= x10 105 x5 = bits.RotateLeft32(x5, 12) 106 x0 += x5 107 x15 ^= x0 108 x15 = bits.RotateLeft32(x15, 8) 109 x10 += x15 110 x5 ^= x10 111 x5 = bits.RotateLeft32(x5, 7) 112 113 // quarterround(x, 1, 6, 11, 12) 114 x1 += x6 115 x12 ^= x1 116 x12 = bits.RotateLeft32(x12, 16) 117 x11 += x12 118 x6 ^= x11 119 x6 = bits.RotateLeft32(x6, 12) 120 x1 += x6 121 x12 ^= x1 122 x12 = bits.RotateLeft32(x12, 8) 123 x11 += x12 124 x6 ^= x11 125 x6 = bits.RotateLeft32(x6, 7) 126 127 // quarterround(x, 2, 7, 8, 13) 128 x2 += x7 129 x13 ^= x2 130 x13 = bits.RotateLeft32(x13, 16) 131 x8 += x13 132 x7 ^= x8 133 x7 = bits.RotateLeft32(x7, 12) 134 x2 += x7 135 x13 ^= x2 136 x13 = bits.RotateLeft32(x13, 8) 137 x8 += x13 138 x7 ^= x8 139 x7 = bits.RotateLeft32(x7, 7) 140 141 // quarterround(x, 3, 4, 9, 14) 142 x3 += x4 143 x14 ^= x3 144 x14 = bits.RotateLeft32(x14, 16) 145 x9 += x14 146 x4 ^= x9 147 x4 = bits.RotateLeft32(x4, 12) 148 x3 += x4 149 x14 ^= x3 150 x14 = bits.RotateLeft32(x14, 8) 151 x9 += x14 152 x4 ^= x9 153 x4 = bits.RotateLeft32(x4, 7) 154 } 155 156 x0 += api.Sigma0 157 x1 += api.Sigma1 158 x2 += api.Sigma2 159 x3 += api.Sigma3 160 x4 += x[4] 161 x5 += x[5] 162 x6 += x[6] 163 x7 += x[7] 164 x8 += x[8] 165 x9 += x[9] 166 x10 += x[10] 167 x11 += x[11] 168 x12 += x[12] 169 x13 += x[13] 170 x14 += x[14] 171 x15 += x[15] 172 173 _ = dst[api.BlockSize-1] // Force bounds check elimination. 174 175 if src != nil { 176 _ = src[api.BlockSize-1] // Force bounds check elimination. 177 binary.LittleEndian.PutUint32(dst[0:4], binary.LittleEndian.Uint32(src[0:4])^x0) 178 binary.LittleEndian.PutUint32(dst[4:8], binary.LittleEndian.Uint32(src[4:8])^x1) 179 binary.LittleEndian.PutUint32(dst[8:12], binary.LittleEndian.Uint32(src[8:12])^x2) 180 binary.LittleEndian.PutUint32(dst[12:16], binary.LittleEndian.Uint32(src[12:16])^x3) 181 binary.LittleEndian.PutUint32(dst[16:20], binary.LittleEndian.Uint32(src[16:20])^x4) 182 binary.LittleEndian.PutUint32(dst[20:24], binary.LittleEndian.Uint32(src[20:24])^x5) 183 binary.LittleEndian.PutUint32(dst[24:28], binary.LittleEndian.Uint32(src[24:28])^x6) 184 binary.LittleEndian.PutUint32(dst[28:32], binary.LittleEndian.Uint32(src[28:32])^x7) 185 binary.LittleEndian.PutUint32(dst[32:36], binary.LittleEndian.Uint32(src[32:36])^x8) 186 binary.LittleEndian.PutUint32(dst[36:40], binary.LittleEndian.Uint32(src[36:40])^x9) 187 binary.LittleEndian.PutUint32(dst[40:44], binary.LittleEndian.Uint32(src[40:44])^x10) 188 binary.LittleEndian.PutUint32(dst[44:48], binary.LittleEndian.Uint32(src[44:48])^x11) 189 binary.LittleEndian.PutUint32(dst[48:52], binary.LittleEndian.Uint32(src[48:52])^x12) 190 binary.LittleEndian.PutUint32(dst[52:56], binary.LittleEndian.Uint32(src[52:56])^x13) 191 binary.LittleEndian.PutUint32(dst[56:60], binary.LittleEndian.Uint32(src[56:60])^x14) 192 binary.LittleEndian.PutUint32(dst[60:64], binary.LittleEndian.Uint32(src[60:64])^x15) 193 src = src[api.BlockSize:] 194 } else { 195 binary.LittleEndian.PutUint32(dst[0:4], x0) 196 binary.LittleEndian.PutUint32(dst[4:8], x1) 197 binary.LittleEndian.PutUint32(dst[8:12], x2) 198 binary.LittleEndian.PutUint32(dst[12:16], x3) 199 binary.LittleEndian.PutUint32(dst[16:20], x4) 200 binary.LittleEndian.PutUint32(dst[20:24], x5) 201 binary.LittleEndian.PutUint32(dst[24:28], x6) 202 binary.LittleEndian.PutUint32(dst[28:32], x7) 203 binary.LittleEndian.PutUint32(dst[32:36], x8) 204 binary.LittleEndian.PutUint32(dst[36:40], x9) 205 binary.LittleEndian.PutUint32(dst[40:44], x10) 206 binary.LittleEndian.PutUint32(dst[44:48], x11) 207 binary.LittleEndian.PutUint32(dst[48:52], x12) 208 binary.LittleEndian.PutUint32(dst[52:56], x13) 209 binary.LittleEndian.PutUint32(dst[56:60], x14) 210 binary.LittleEndian.PutUint32(dst[60:64], x15) 211 } 212 dst = dst[api.BlockSize:] 213 214 // Stoping at 2^70 bytes per nonce is the user's responsibility. 215 ctr := uint64(x[13])<<32 | uint64(x[12]) 216 ctr++ 217 x[12] = uint32(ctr) 218 x[13] = uint32(ctr >> 32) 219 } 220 } 221 222 func (impl *implRef) HChaCha(key, nonce []byte, dst []byte) { 223 // Force bounds check elimination. 224 _ = key[31] 225 _ = nonce[api.HNonceSize-1] 226 227 x0, x1, x2, x3 := api.Sigma0, api.Sigma1, api.Sigma2, api.Sigma3 228 x4 := binary.LittleEndian.Uint32(key[0:4]) 229 x5 := binary.LittleEndian.Uint32(key[4:8]) 230 x6 := binary.LittleEndian.Uint32(key[8:12]) 231 x7 := binary.LittleEndian.Uint32(key[12:16]) 232 x8 := binary.LittleEndian.Uint32(key[16:20]) 233 x9 := binary.LittleEndian.Uint32(key[20:24]) 234 x10 := binary.LittleEndian.Uint32(key[24:28]) 235 x11 := binary.LittleEndian.Uint32(key[28:32]) 236 x12 := binary.LittleEndian.Uint32(nonce[0:4]) 237 x13 := binary.LittleEndian.Uint32(nonce[4:8]) 238 x14 := binary.LittleEndian.Uint32(nonce[8:12]) 239 x15 := binary.LittleEndian.Uint32(nonce[12:16]) 240 241 // Yes, this could be carved out into a function for code reuse (TM) 242 // however the go inliner won't inline it. 243 for i := rounds; i > 0; i -= 2 { 244 // quarterround(x, 0, 4, 8, 12) 245 x0 += x4 246 x12 ^= x0 247 x12 = bits.RotateLeft32(x12, 16) 248 x8 += x12 249 x4 ^= x8 250 x4 = bits.RotateLeft32(x4, 12) 251 x0 += x4 252 x12 ^= x0 253 x12 = bits.RotateLeft32(x12, 8) 254 x8 += x12 255 x4 ^= x8 256 x4 = bits.RotateLeft32(x4, 7) 257 258 // quarterround(x, 1, 5, 9, 13) 259 x1 += x5 260 x13 ^= x1 261 x13 = bits.RotateLeft32(x13, 16) 262 x9 += x13 263 x5 ^= x9 264 x5 = bits.RotateLeft32(x5, 12) 265 x1 += x5 266 x13 ^= x1 267 x13 = bits.RotateLeft32(x13, 8) 268 x9 += x13 269 x5 ^= x9 270 x5 = bits.RotateLeft32(x5, 7) 271 272 // quarterround(x, 2, 6, 10, 14) 273 x2 += x6 274 x14 ^= x2 275 x14 = bits.RotateLeft32(x14, 16) 276 x10 += x14 277 x6 ^= x10 278 x6 = bits.RotateLeft32(x6, 12) 279 x2 += x6 280 x14 ^= x2 281 x14 = bits.RotateLeft32(x14, 8) 282 x10 += x14 283 x6 ^= x10 284 x6 = bits.RotateLeft32(x6, 7) 285 286 // quarterround(x, 3, 7, 11, 15) 287 x3 += x7 288 x15 ^= x3 289 x15 = bits.RotateLeft32(x15, 16) 290 x11 += x15 291 x7 ^= x11 292 x7 = bits.RotateLeft32(x7, 12) 293 x3 += x7 294 x15 ^= x3 295 x15 = bits.RotateLeft32(x15, 8) 296 x11 += x15 297 x7 ^= x11 298 x7 = bits.RotateLeft32(x7, 7) 299 300 // quarterround(x, 0, 5, 10, 15) 301 x0 += x5 302 x15 ^= x0 303 x15 = bits.RotateLeft32(x15, 16) 304 x10 += x15 305 x5 ^= x10 306 x5 = bits.RotateLeft32(x5, 12) 307 x0 += x5 308 x15 ^= x0 309 x15 = bits.RotateLeft32(x15, 8) 310 x10 += x15 311 x5 ^= x10 312 x5 = bits.RotateLeft32(x5, 7) 313 314 // quarterround(x, 1, 6, 11, 12) 315 x1 += x6 316 x12 ^= x1 317 x12 = bits.RotateLeft32(x12, 16) 318 x11 += x12 319 x6 ^= x11 320 x6 = bits.RotateLeft32(x6, 12) 321 x1 += x6 322 x12 ^= x1 323 x12 = bits.RotateLeft32(x12, 8) 324 x11 += x12 325 x6 ^= x11 326 x6 = bits.RotateLeft32(x6, 7) 327 328 // quarterround(x, 2, 7, 8, 13) 329 x2 += x7 330 x13 ^= x2 331 x13 = bits.RotateLeft32(x13, 16) 332 x8 += x13 333 x7 ^= x8 334 x7 = bits.RotateLeft32(x7, 12) 335 x2 += x7 336 x13 ^= x2 337 x13 = bits.RotateLeft32(x13, 8) 338 x8 += x13 339 x7 ^= x8 340 x7 = bits.RotateLeft32(x7, 7) 341 342 // quarterround(x, 3, 4, 9, 14) 343 x3 += x4 344 x14 ^= x3 345 x14 = bits.RotateLeft32(x14, 16) 346 x9 += x14 347 x4 ^= x9 348 x4 = bits.RotateLeft32(x4, 12) 349 x3 += x4 350 x14 ^= x3 351 x14 = bits.RotateLeft32(x14, 8) 352 x9 += x14 353 x4 ^= x9 354 x4 = bits.RotateLeft32(x4, 7) 355 } 356 357 // HChaCha returns x0...x3 | x12...x15, which corresponds to the 358 // indexes of the ChaCha constant and the indexes of the IV. 359 _ = dst[api.HashSize-1] // Force bounds check elimination. 360 binary.LittleEndian.PutUint32(dst[0:4], x0) 361 binary.LittleEndian.PutUint32(dst[4:8], x1) 362 binary.LittleEndian.PutUint32(dst[8:12], x2) 363 binary.LittleEndian.PutUint32(dst[12:16], x3) 364 binary.LittleEndian.PutUint32(dst[16:20], x12) 365 binary.LittleEndian.PutUint32(dst[20:24], x13) 366 binary.LittleEndian.PutUint32(dst[24:28], x14) 367 binary.LittleEndian.PutUint32(dst[28:32], x15) 368 } 369 370 // Register appends the implementation to the provided slice, and returns the 371 // new slice. 372 func Register(impls []api.Implementation) []api.Implementation { 373 return append(impls, Impl) 374 }