View Javadoc
1   /*
2    * GordianKnot: Security Suite
3    * Copyright 2012-2026. Tony Washer
4    *
5    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
6    * use this file except in compliance with the License.  You may obtain a copy
7    * of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
14   * License for the specific language governing permissions and limitations under
15   * the License.
16   */
17  package io.github.tonywasher.joceanus.gordianknot.impl.ext.engines;
18  
19  import org.bouncycastle.crypto.engines.ChaChaEngine;
20  import org.bouncycastle.crypto.engines.Salsa20Engine;
21  import org.bouncycastle.util.Pack;
22  
23  /**
24   * Modelled on XSalsa20Engine.
25   */
26  @SuppressWarnings("checkstyle:MagicNumber")
27  public class GordianXChaCha20Engine
28          extends ChaChaEngine {
29  
30      @Override
31      public String getAlgorithmName() {
32          return "XChaCha20";
33      }
34  
35      @Override
36      protected int getNonceSize() {
37          return 24;
38      }
39  
40      /**
41       * XChaCha key generation: process 256 bit input key and 128 bits of the input nonce
42       * using a core ChaCha20 function without input addition to produce 256 bit working key
43       * and use that with the remaining 64 bits of nonce to initialize a standard ChaCha20 engine state.
44       */
45      @Override
46      protected void setKey(final byte[] keyBytes,
47                            final byte[] ivBytes) {
48          if (keyBytes == null) {
49              throw new IllegalArgumentException(getAlgorithmName() + " doesn't support re-init with null key");
50          }
51  
52          if (keyBytes.length != 32) {
53              throw new IllegalArgumentException(getAlgorithmName() + " requires a 256 bit key");
54          }
55  
56          /* Set key for ChaCha20 */
57          super.setKey(keyBytes, ivBytes);
58  
59          /* Pack first 128 bits of IV into engine state */
60          Pack.littleEndianToInt(ivBytes, 0, engineState, 12, 4);
61  
62          /* Process engine state to generate ChaCha20 key */
63          final int[] hChaCha20Out = new int[engineState.length];
64          chachaCore(Salsa20Engine.DEFAULT_ROUNDS, engineState, hChaCha20Out);
65  
66          /* Set new key, removing addition in last round of chachaCore */
67          engineState[4] = hChaCha20Out[0] - engineState[0];
68          engineState[5] = hChaCha20Out[1] - engineState[1];
69          engineState[6] = hChaCha20Out[2] - engineState[2];
70          engineState[7] = hChaCha20Out[3] - engineState[3];
71  
72          engineState[8] = hChaCha20Out[12] - engineState[12];
73          engineState[9] = hChaCha20Out[13] - engineState[13];
74          engineState[10] = hChaCha20Out[14] - engineState[14];
75          engineState[11] = hChaCha20Out[15] - engineState[15];
76  
77          /* Last 64 bits of input IV */
78          Pack.littleEndianToInt(ivBytes, 16, engineState, 14, 2);
79      }
80  }