GordianStreamDefinition.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.core.stream;
import io.github.tonywasher.joceanus.gordianknot.api.base.GordianException;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianCipherParameters;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianStreamCipher;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianStreamCipherSpec;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianStreamKeySpec;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianSymCipher;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianSymCipherSpec;
import io.github.tonywasher.joceanus.gordianknot.api.cipher.GordianSymKeySpec;
import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigest;
import io.github.tonywasher.joceanus.gordianknot.api.digest.GordianDigestSpec;
import io.github.tonywasher.joceanus.gordianknot.api.key.GordianKey;
import io.github.tonywasher.joceanus.gordianknot.api.mac.GordianMac;
import io.github.tonywasher.joceanus.gordianknot.api.mac.GordianMacParameters;
import io.github.tonywasher.joceanus.gordianknot.api.mac.GordianMacSpec;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianBaseFactory;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianCoreKnuthObfuscater;
import io.github.tonywasher.joceanus.gordianknot.impl.core.base.GordianDataConverter;
import io.github.tonywasher.joceanus.gordianknot.impl.core.cipher.GordianCoreCipher;
import io.github.tonywasher.joceanus.gordianknot.impl.core.cipher.GordianCoreCipherFactory;
import io.github.tonywasher.joceanus.gordianknot.impl.core.digest.GordianCoreDigestFactory;
import io.github.tonywasher.joceanus.gordianknot.impl.core.exc.GordianDataException;
import io.github.tonywasher.joceanus.gordianknot.impl.core.keyset.GordianCoreKeySet;
import io.github.tonywasher.joceanus.gordianknot.impl.core.mac.GordianCoreMacFactory;
import java.io.InputStream;
import java.util.Arrays;
/**
* Stream definition.
*/
public final class GordianStreamDefinition {
/**
* Stream Type.
*/
private final GordianStreamType theType;
/**
* TypeId.
*/
private final int theTypeId;
/**
* TypeDefinition.
*/
private final byte[] theTypeDefinition;
/**
* InitVector.
*/
private final byte[] theInitVector;
/**
* Value.
*/
private final byte[] theValue;
/**
* The length.
*/
private final Long theLength;
/**
* Constructor.
*
* @param pExternalId the externalId
* @param pTypeDef the type definition
* @param pInitVector the IV
* @param pValue the value
* @throws GordianException on error
*/
public GordianStreamDefinition(final long pExternalId,
final byte[] pTypeDef,
final byte[] pInitVector,
final byte[] pValue) throws GordianException {
final int myStreamId = (int) (pExternalId & GordianDataConverter.NYBBLE_MASK);
theType = GordianStreamType.fromId(myStreamId);
theTypeId = (int) (pExternalId >> GordianDataConverter.NYBBLE_SHIFT);
theTypeDefinition = pTypeDef == null
? null
: Arrays.copyOf(pTypeDef, pTypeDef.length);
theInitVector = pInitVector == null
? null
: Arrays.copyOf(pInitVector, pInitVector.length);
theValue = pValue == null
? null
: Arrays.copyOf(pValue, pValue.length);
theLength = null;
}
/**
* Constructor.
*
* @param pKeySet the KeySet
* @param pStream the DigestOutputStream
* @throws GordianException on error
*/
GordianStreamDefinition(final GordianCoreKeySet pKeySet,
final GordianDigestOutputStream pStream) throws GordianException {
/* Access factory */
final GordianBaseFactory myFactory = pKeySet.getFactory();
final GordianCoreKnuthObfuscater myKnuth = (GordianCoreKnuthObfuscater) myFactory.getObfuscater();
/* Access DigestType */
theType = GordianStreamType.DIGEST;
final GordianDigest myDigest = pStream.getDigest();
theTypeId = myKnuth.deriveExternalIdFromType(myDigest.getDigestSpec());
/* Build details */
theTypeDefinition = null;
theInitVector = null;
theValue = pStream.getResult();
theLength = pStream.getDataLen();
}
/**
* Constructor.
*
* @param pKeySet the KeySet
* @param pStream the MacOutputStream
* @throws GordianException on error
*/
GordianStreamDefinition(final GordianCoreKeySet pKeySet,
final GordianMacOutputStream pStream) throws GordianException {
/* Access factory */
final GordianBaseFactory myFactory = pKeySet.getFactory();
final GordianCoreKnuthObfuscater myKnuth = (GordianCoreKnuthObfuscater) myFactory.getObfuscater();
/* Access MacType */
theType = GordianStreamType.MAC;
final GordianMac myMac = pStream.getMac();
theTypeId = myKnuth.deriveExternalIdFromType(myMac.getMacSpec());
/* Build details */
theTypeDefinition = pKeySet.secureKey(myMac.getKey());
theInitVector = myMac.getInitVector();
theValue = pStream.getResult();
theLength = pStream.getDataLen();
}
/**
* Constructor.
*
* @param pKeySet the KeySet
* @param pStream the CipherOutputStream
* @throws GordianException on error
*/
GordianStreamDefinition(final GordianCoreKeySet pKeySet,
final GordianCipherOutputStream<?> pStream) throws GordianException {
/* Access factory */
final GordianBaseFactory myFactory = pKeySet.getFactory();
final GordianCoreKnuthObfuscater myKnuth = (GordianCoreKnuthObfuscater) myFactory.getObfuscater();
/* Access CipherType */
final GordianCoreCipher<?> myCipher = pStream.getCipher();
theType = pStream.isSymKeyStream()
? GordianStreamType.SYMMETRIC
: GordianStreamType.STREAM;
theTypeId = myKnuth.deriveExternalIdFromType(myCipher.getCipherSpec());
/* Build details */
theTypeDefinition = pKeySet.secureKey(myCipher.getKey());
theInitVector = myCipher.getInitVector();
theValue = null;
theLength = null;
}
/**
* Constructor.
*
* @param pStreamType the StreamType
*/
GordianStreamDefinition(final GordianStreamType pStreamType) {
theType = GordianStreamType.LZMA;
theTypeId = 0;
theTypeDefinition = null;
theInitVector = null;
theValue = null;
theLength = null;
}
/**
* Obtain Encoded stream type.
*
* @return the encoded value
*/
public long getExternalId() {
return ((long) theTypeId << GordianDataConverter.NYBBLE_SHIFT)
+ theType.getId();
}
/**
* Obtain Type.
*
* @return the type
*/
public GordianStreamType getType() {
return theType;
}
/**
* Obtain TypeId.
*
* @return the typeId
*/
public long getTypeId() {
return theTypeId;
}
/**
* Obtain TypeDefinition.
*
* @return the type definition
*/
public byte[] getTypeDefinition() {
return theTypeDefinition == null
? null
: Arrays.copyOf(theTypeDefinition, theTypeDefinition.length);
}
/**
* Obtain InitVector.
*
* @return the initVector
*/
public byte[] getInitVector() {
return theInitVector == null
? null
: Arrays.copyOf(theInitVector, theInitVector.length);
}
/**
* Obtain Value.
*
* @return the value
*/
public byte[] getValue() {
return theValue == null
? null
: Arrays.copyOf(theValue, theValue.length);
}
/**
* Obtain dataLength.
*
* @return the length
*/
public Long getDataLength() {
return theLength;
}
/**
* Build input Stream.
*
* @param pKeySet the keySet
* @param pCurrent the current stream
* @param pMacStream the Mac input stream
* @return the new input stream
* @throws GordianException on error
*/
InputStream buildInputStream(final GordianCoreKeySet pKeySet,
final InputStream pCurrent,
final GordianMacInputStream pMacStream) throws GordianException {
switch (theType) {
case DIGEST:
return buildDigestInputStream(pKeySet, pCurrent, pMacStream);
case MAC:
return buildMacInputStream(pKeySet, pCurrent);
case SYMMETRIC:
return buildSymKeyInputStream(pKeySet, pCurrent);
case STREAM:
return buildStreamKeyInputStream(pKeySet, pCurrent);
case LZMA:
default:
return buildLZMAInputStream(pCurrent);
}
}
/**
* Build digest input Stream.
*
* @param pKeySet the keySet
* @param pCurrent the current stream
* @param pMacStream the Mac input stream
* @return the new input stream
* @throws GordianException on error
*/
private InputStream buildDigestInputStream(final GordianCoreKeySet pKeySet,
final InputStream pCurrent,
final GordianMacInputStream pMacStream) throws GordianException {
/* Access factory */
final GordianBaseFactory myFactory = pKeySet.getFactory();
final GordianCoreKnuthObfuscater myKnuth = (GordianCoreKnuthObfuscater) myFactory.getObfuscater();
/* Parse the TypeId */
final GordianDigestSpec mySpec = (GordianDigestSpec) myKnuth.deriveTypeFromExternalId(theTypeId);
/* Generate the Digest */
final GordianCoreDigestFactory myDigests = (GordianCoreDigestFactory) myFactory.getDigestFactory();
final GordianDigest myDigest = myDigests.createDigest(mySpec);
/* Create the stream */
return new GordianDigestInputStream(myDigest, theValue, pCurrent, pMacStream);
}
/**
* Build MAC input Stream.
*
* @param pKeySet the keySet
* @param pCurrent the current stream
* @return the new input stream
* @throws GordianException on error
*/
private InputStream buildMacInputStream(final GordianCoreKeySet pKeySet,
final InputStream pCurrent) throws GordianException {
/* Access factory */
final GordianBaseFactory myFactory = pKeySet.getFactory();
final GordianCoreKnuthObfuscater myKnuth = (GordianCoreKnuthObfuscater) myFactory.getObfuscater();
/* Parse the TypeId */
final GordianMacSpec mySpec = (GordianMacSpec) myKnuth.deriveTypeFromExternalId(theTypeId);
/* Generate the MAC */
final GordianCoreMacFactory myMacs = (GordianCoreMacFactory) myFactory.getMacFactory();
final GordianMac myMac = myMacs.createMac(mySpec);
final GordianKey<GordianMacSpec> myKey = pKeySet.deriveKey(theTypeDefinition, mySpec);
myMac.init(GordianMacParameters.keyAndNonce(myKey, theInitVector));
/* Create the stream */
return new GordianMacInputStream(myMac, theValue, pCurrent);
}
/**
* Build SymKey input Stream.
*
* @param pKeySet the keySet
* @param pCurrent the current stream
* @return the new input stream
* @throws GordianException on error
*/
private InputStream buildSymKeyInputStream(final GordianCoreKeySet pKeySet,
final InputStream pCurrent) throws GordianException {
/* Access factory */
final GordianBaseFactory myFactory = pKeySet.getFactory();
final GordianCoreKnuthObfuscater myKnuth = (GordianCoreKnuthObfuscater) myFactory.getObfuscater();
/* Parse the TypeId */
final GordianSymCipherSpec mySpec = (GordianSymCipherSpec) myKnuth.deriveTypeFromExternalId(theTypeId);
final GordianSymKeySpec myKeySpec = mySpec.getKeyType();
/* Generate the Cipher */
final GordianCoreCipherFactory myCiphers = (GordianCoreCipherFactory) myFactory.getCipherFactory();
final GordianSymCipher myCipher = myCiphers.createSymKeyCipher(mySpec);
final GordianKey<GordianSymKeySpec> myKey = pKeySet.deriveKey(theTypeDefinition, myKeySpec);
myCipher.initForDecrypt(GordianCipherParameters.keyAndNonce(myKey, theInitVector));
/* Create the stream */
return new GordianCipherInputStream<>(myCipher, pCurrent);
}
/**
* Build StreamKey input Stream.
*
* @param pKeySet the keySet
* @param pCurrent the current stream
* @return the new input stream
* @throws GordianException on error
*/
private InputStream buildStreamKeyInputStream(final GordianCoreKeySet pKeySet,
final InputStream pCurrent) throws GordianException {
/* Access factory */
final GordianBaseFactory myFactory = pKeySet.getFactory();
final GordianCoreKnuthObfuscater myKnuth = (GordianCoreKnuthObfuscater) myFactory.getObfuscater();
/* Parse the TypeId */
final GordianStreamCipherSpec mySpec = (GordianStreamCipherSpec) myKnuth.deriveTypeFromExternalId(theTypeId);
final GordianStreamKeySpec myType = mySpec.getKeyType();
/* Generate the Cipher */
final GordianCoreCipherFactory myCiphers = (GordianCoreCipherFactory) myFactory.getCipherFactory();
final GordianStreamCipher myCipher = myCiphers.createStreamKeyCipher(mySpec);
final GordianKey<GordianStreamKeySpec> myKey = pKeySet.deriveKey(theTypeDefinition, myType);
myCipher.initForDecrypt(GordianCipherParameters.keyAndNonce(myKey, theInitVector));
/* Create the stream */
return new GordianCipherInputStream<>(myCipher, pCurrent);
}
/**
* Build LZMA input Stream.
*
* @param pCurrent the current stream
* @return the new input stream
*/
private static InputStream buildLZMAInputStream(final InputStream pCurrent) {
/* Create the stream */
return new GordianLZMAInputStream(pCurrent);
}
/**
* Stream Type.
*/
public enum GordianStreamType {
/**
* Symmetric.
*/
SYMMETRIC(1),
/**
* Stream.
*/
STREAM(2),
/**
* Digest.
*/
DIGEST(3),
/**
* MAC.
*/
MAC(4),
/**
* LZMA.
*/
LZMA(5);
/**
* The external Id of the stream type.
*/
private final int theId;
/**
* Constructor.
*
* @param id the id
*/
GordianStreamType(final int id) {
theId = id;
}
/**
* Obtain the external Id.
*
* @return the external Id
*/
public int getId() {
return theId;
}
/**
* get value from id.
*
* @param id the id value
* @return the corresponding enumeration object
* @throws GordianException on error
*/
public static GordianStreamType fromId(final int id) throws GordianException {
for (final GordianStreamType myType : values()) {
if (myType.getId() == id) {
return myType;
}
}
throw new GordianDataException("Invalid StreamType: "
+ id);
}
}
}