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  }