GordianCoreStreamKeySpec.java
/*
* GordianKnot: Security Suite
* Copyright 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.core.spec.cipher;
import io.github.tonywasher.joceanus.gordianknot.api.base.GordianLength;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianStreamKeySpec;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianStreamKeySubType;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianStreamKeySubType.GordianBlakeXofKey;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianStreamKeySubType.GordianChaCha20Key;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianStreamKeySubType.GordianElephantKey;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianStreamKeySubType.GordianISAPKey;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianStreamKeySubType.GordianRomulusKey;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianStreamKeySubType.GordianSalsa20Key;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianStreamKeySubType.GordianSkeinXofKey;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianStreamKeySubType.GordianSparkleKey;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianStreamKeySubType.GordianVMPCKey;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.spec.GordianStreamKeyType;
import io.github.tonywasher.joceanus.gordianknot.impl.core.spec.base.GordianSpecConstants;
import java.util.Objects;
/**
* GordianKnot StreamKeySpec.
*/
public class GordianCoreStreamKeySpec
implements GordianStreamKeySpec {
/**
* The StreamKey Type.
*/
private final GordianCoreStreamKeyType theType;
/**
* The Key Length.
*/
private final GordianLength theKeyLength;
/**
* SubKeyType.
*/
private final GordianStreamKeySubType theSubKeyType;
/**
* The Validity.
*/
private final boolean isValid;
/**
* The String name.
*/
private String theName;
/**
* Constructor.
*
* @param pType the streamKeyType
* @param pSubKeyType the subKeyTypeType
* @param pKeyLength the keyLength
*/
GordianCoreStreamKeySpec(final GordianStreamKeyType pType,
final GordianStreamKeySubType pSubKeyType,
final GordianLength pKeyLength) {
theType = GordianCoreStreamKeyType.mapCoreType(pType);
theSubKeyType = pSubKeyType;
theKeyLength = pKeyLength;
isValid = checkValidity();
}
/**
* Obtain the Core streamKeyType.
*
* @return the keyType
*/
public GordianCoreStreamKeyType getCoreStreamKeyType() {
return theType;
}
@Override
public GordianStreamKeyType getStreamKeyType() {
return theType.getType();
}
@Override
public GordianStreamKeySubType getSubKeyType() {
return theSubKeyType;
}
@Override
public GordianLength getKeyLength() {
return theKeyLength;
}
@Override
public boolean isValid() {
return isValid;
}
/**
* Does this cipher need an IV?
*
* @return true/false
*/
public boolean needsIV() {
return getIVLength() > 0;
}
/**
* Check spec validity.
*
* @return valid true/false
*/
private boolean checkValidity() {
/* Stream KeyType and Key length must be non-null */
if (theType == null
|| theKeyLength == null) {
return false;
}
/* Check subKeyTypes */
return switch (theType.getType()) {
case SALSA20 -> checkSalsaValidity();
case CHACHA20 -> checkChaChaValidity();
case VMPC -> checkVMPCValidity();
case SKEINXOF -> checkSkeinValidity();
case BLAKE2XOF -> checkBlake2Validity();
case ELEPHANT -> theSubKeyType instanceof GordianElephantKey
&& theType.validForKeyLength(theKeyLength);
case ISAP -> theSubKeyType instanceof GordianISAPKey
&& theType.validForKeyLength(theKeyLength);
case ROMULUS -> theSubKeyType instanceof GordianRomulusKey
&& theType.validForKeyLength(theKeyLength);
case SPARKLE -> checkSparkleValidity();
default -> theSubKeyType == null
&& theType.validForKeyLength(theKeyLength);
};
}
/**
* Check salsa spec validity.
*
* @return valid true/false
*/
private boolean checkSalsaValidity() {
/* SubKeyType must be a SalsaKey */
if (!(theSubKeyType instanceof GordianSalsa20Key)) {
return false;
}
/* Check keyLength validity */
return theSubKeyType != GordianSalsa20Key.STD
? theKeyLength == GordianLength.LEN_256
: theType.validForKeyLength(theKeyLength);
}
/**
* Check chacha spec validity.
*
* @return valid true/false
*/
private boolean checkChaChaValidity() {
/* SubKeyType must be a ChaChaKey */
if (!(theSubKeyType instanceof GordianChaCha20Key)) {
return false;
}
/* Check keyLength validity */
return theSubKeyType != GordianChaCha20Key.STD
? theKeyLength == GordianLength.LEN_256
: theType.validForKeyLength(theKeyLength);
}
/**
* Check vmpc spec validity.
*
* @return valid true/false
*/
private boolean checkVMPCValidity() {
/* SubKeyType must be a GordianVMPCKey */
if (!(theSubKeyType instanceof GordianVMPCKey)) {
return false;
}
/* Check keyLength validity */
return theType.validForKeyLength(theKeyLength);
}
/**
* Check skein spec validity.
*
* @return valid true/false
*/
private boolean checkSkeinValidity() {
/* SubKeyType must be a GordianSkeinXofKey */
if (!(theSubKeyType instanceof GordianSkeinXofKey)) {
return false;
}
/* Check keyLength validity */
return theType.validForKeyLength(theKeyLength);
}
/**
* Check blake2 spec validity.
*
* @return valid true/false
*/
private boolean checkBlake2Validity() {
/* SubKeyType must be a GordianBlakeXofKey */
if (!(theSubKeyType instanceof GordianBlakeXofKey myType)) {
return false;
}
/* Check keyLength validity */
return theType.validForKeyLength(theKeyLength)
&& (myType != GordianBlakeXofKey.BLAKE2XS
|| theKeyLength != GordianLength.LEN_512);
}
/**
* Check sparkle spec validity.
*
* @return valid true/false
*/
private boolean checkSparkleValidity() {
/* SubKeyType must be a GordianSparkleKey */
if (!(theSubKeyType instanceof GordianSparkleKey)) {
return false;
}
/* Check keyLength validity */
return theKeyLength == GordianCoreStreamKeySubType.requiredSparkleKeyLength((GordianSparkleKey) theSubKeyType);
}
@Override
public String toString() {
/* If we have not yet loaded the name */
if (theName == null) {
/* If the keySpec is valid */
if (isValid) {
/* Load the name */
theName = getName();
theName += GordianSpecConstants.SEP + theKeyLength;
} else {
/* Report invalid spec */
theName = "InvalidStreamKeySpec: " + theType + ":" + theKeyLength;
}
}
/* return the name */
return theName;
}
/**
* Determine the name for the KeySpec.
*
* @return the name
*/
private String getName() {
return switch (theType.getType()) {
case VMPC -> theSubKeyType == GordianVMPCKey.KSA ? theType + "KSA3" : theType.toString();
case SALSA20 -> theSubKeyType == GordianSalsa20Key.XSALSA ? "X" + theType : theType.toString();
case CHACHA20 -> switch ((GordianChaCha20Key) theSubKeyType) {
case XCHACHA -> "X" + theType;
case ISO7539 -> "ChaCha7539";
default -> theType.toString();
};
case SKEINXOF -> theType + GordianSpecConstants.SEP
+ GordianCoreStreamKeySubType.getLengthForSkeinXofKey((GordianSkeinXofKey) theSubKeyType);
case BLAKE2XOF, ELEPHANT, ISAP, ROMULUS, SPARKLE ->
GordianCoreStreamKeySubType.toSubTypeString(theType.getType(), theSubKeyType);
default -> theType.toString();
};
}
/**
* Obtain the IV Length.
*
* @return the IV length.
*/
public int getIVLength() {
return switch (theType.getType()) {
case RABBIT -> GordianLength.LEN_64.getByteLength();
case GRAIN, ELEPHANT -> GordianLength.LEN_96.getByteLength();
case SOSEMANUK, SNOW3G, BLAKE3XOF, SKEINXOF, ASCON, ISAP, PHOTONBEETLE, ROMULUS, XOODYAK ->
GordianLength.LEN_128.getByteLength();
case HC -> GordianLength.LEN_128 == theKeyLength
? GordianLength.LEN_128.getByteLength()
: GordianLength.LEN_256.getByteLength();
case ZUC -> GordianLength.LEN_128 == theKeyLength
? GordianLength.LEN_128.getByteLength()
: GordianLength.LEN_200.getByteLength();
case VMPC -> theKeyLength.getByteLength();
case BLAKE2XOF ->
GordianCoreStreamKeySubType.requiredBlakeIVLength((GordianBlakeXofKey) theSubKeyType).getByteLength();
case CHACHA20 ->
GordianCoreStreamKeySubType.requiredChaChaIVLength((GordianChaCha20Key) theSubKeyType).getByteLength();
case SALSA20 ->
GordianCoreStreamKeySubType.requiredSalsaIVLength((GordianSalsa20Key) theSubKeyType).getByteLength();
case SPARKLE ->
GordianCoreStreamKeySubType.requiredSparkleIVLength((GordianSparkleKey) theSubKeyType).getByteLength();
default -> 0;
};
}
/**
* Does this keySpec optionally support AEAD?
*
* @return true/false
*/
public boolean supportsAEAD() {
return GordianStreamKeyType.CHACHA20.equals(theType.getType())
&& theSubKeyType != GordianChaCha20Key.STD;
}
/**
* Is this keySpec an AEAD keySpec?
*
* @return true/false
*/
public boolean isAEAD() {
return switch (theType.getType()) {
case ASCON, ELEPHANT, ISAP, PHOTONBEETLE, ROMULUS, SPARKLE, XOODYAK -> true;
default -> false;
};
}
@Override
public boolean equals(final Object pThat) {
/* Handle the trivial cases */
if (this == pThat) {
return true;
}
if (pThat == null) {
return false;
}
/* Check subFields */
return pThat instanceof GordianCoreStreamKeySpec myThat
&& Objects.equals(theType, myThat.getCoreStreamKeyType())
&& theKeyLength == myThat.getKeyLength()
&& theSubKeyType == myThat.getSubKeyType();
}
@Override
public int hashCode() {
return Objects.hash(theType, theKeyLength, theSubKeyType);
}
}