GordianXChaCha20Engine.java
/*
* GordianKnot: Security Suite
* Copyright 2012-2026. Tony Washer
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package io.github.tonywasher.joceanus.gordianknot.impl.ext.engines;
import org.bouncycastle.crypto.engines.ChaChaEngine;
import org.bouncycastle.crypto.engines.Salsa20Engine;
import org.bouncycastle.util.Pack;
/**
* Modelled on XSalsa20Engine.
*/
@SuppressWarnings("checkstyle:MagicNumber")
public class GordianXChaCha20Engine
extends ChaChaEngine {
@Override
public String getAlgorithmName() {
return "XChaCha20";
}
@Override
protected int getNonceSize() {
return 24;
}
/**
* XChaCha key generation: process 256 bit input key and 128 bits of the input nonce
* using a core ChaCha20 function without input addition to produce 256 bit working key
* and use that with the remaining 64 bits of nonce to initialize a standard ChaCha20 engine state.
*/
@Override
protected void setKey(final byte[] keyBytes,
final byte[] ivBytes) {
if (keyBytes == null) {
throw new IllegalArgumentException(getAlgorithmName() + " doesn't support re-init with null key");
}
if (keyBytes.length != 32) {
throw new IllegalArgumentException(getAlgorithmName() + " requires a 256 bit key");
}
/* Set key for ChaCha20 */
super.setKey(keyBytes, ivBytes);
/* Pack first 128 bits of IV into engine state */
Pack.littleEndianToInt(ivBytes, 0, engineState, 12, 4);
/* Process engine state to generate ChaCha20 key */
final int[] hChaCha20Out = new int[engineState.length];
chachaCore(Salsa20Engine.DEFAULT_ROUNDS, engineState, hChaCha20Out);
/* Set new key, removing addition in last round of chachaCore */
engineState[4] = hChaCha20Out[0] - engineState[0];
engineState[5] = hChaCha20Out[1] - engineState[1];
engineState[6] = hChaCha20Out[2] - engineState[2];
engineState[7] = hChaCha20Out[3] - engineState[3];
engineState[8] = hChaCha20Out[12] - engineState[12];
engineState[9] = hChaCha20Out[13] - engineState[13];
engineState[10] = hChaCha20Out[14] - engineState[14];
engineState[11] = hChaCha20Out[15] - engineState[15];
/* Last 64 bits of input IV */
Pack.littleEndianToInt(ivBytes, 16, engineState, 14, 2);
}
}