All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.refcodes.security.alt.chaos.ChaosKey Maven / Gradle / Ivy

There is a newer version: 3.3.9
Show newest version
// /////////////////////////////////////////////////////////////////////////////
// REFCODES.ORG
// /////////////////////////////////////////////////////////////////////////////
// This code is copyright (c) by Siegfried Steiner, Munich, Germany, distributed
// on an "AS IS" BASIS WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, and licen-
// sed under the following (see "http://en.wikipedia.org/wiki/Multi-licensing")
// licenses:
// -----------------------------------------------------------------------------
// GNU General Public License, v3.0 ("http://www.gnu.org/licenses/gpl-3.0.html")
// -----------------------------------------------------------------------------
// Apache License, v2.0 ("http://www.apache.org/licenses/TEXT-2.0")
// -----------------------------------------------------------------------------
// Please contact the copyright holding author(s) of the software artifacts in
// question for licensing issues not being covered by the above listed licenses,
// also regarding commercial licensing models or regarding the compatibility
// with other open source licenses.
// /////////////////////////////////////////////////////////////////////////////

package org.refcodes.security.alt.chaos;

import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.crypto.SecretKey;
import javax.security.auth.DestroyFailedException;

import org.refcodes.codec.BaseDecoderInputStream;
import org.refcodes.codec.BaseEncoderOutputStream;
import org.refcodes.codec.BaseMetricsConfig;
import org.refcodes.data.Numbers;
import org.refcodes.exception.BugException;
import org.refcodes.io.LineBreakOutputStream;
import org.refcodes.mixin.ChildAccessor;
import org.refcodes.mixin.Disposable;
import org.refcodes.mixin.EncodedAccessor;
import org.refcodes.numerical.Endianess;
import org.refcodes.numerical.NumericalUtility;

/**
 * The key holding the parameters for the chaos function. Thanks Christian
 * Pontesegger for the very good example on "Writing your own JCA extensions - a
 * full cipher" at:
 * "http://codeandme.blogspot.de/2013/07/writing-your-own-jca-extensions-full.html"
 * and for the very good example on "Writing your own JCA extensions - a simple
 * digest " at:
 * "http://codeandme.blogspot.de/2013/06/writing-your-own-jca-extensions-simple.html"
 */
public class ChaosKey implements SecretKey, EncodedAccessor, ChildAccessor, Disposable {

	private static final long serialVersionUID = 1L;

	// /////////////////////////////////////////////////////////////////////////
	// STATICS:
	// /////////////////////////////////////////////////////////////////////////

	private static final Logger LOGGER = Logger.getLogger( ChaosKey.class.getName() );

	// /////////////////////////////////////////////////////////////////////////
	// CONSTANTS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * The name of the security provider.
	 */
	public static final String PROVIDER_NAME = "CHAOS";

	/**
	 * The number of bytes required to store A
	 */
	public static final int A_BYTES = Double.BYTES;

	/**
	 * The maximum valid A value.
	 */
	public static final double A_MAX = 4;

	/**
	 * The minimum valid A value.
	 */
	public static final double A_MIN = 3.57;

	/**
	 * The number of bytes required to store S
	 */
	public static final int S_BYTES = Numbers.SAFE_INTEGER_BYTES;

	/**
	 * The maximum valid S value (2^53-1 = MAX_SAFE_INTEGER when floating point
	 * is involved).
	 */
	public static final long S_MAX = Numbers.MAX_SAFE_INTEGER;

	/**
	 * The minimum valid S value (-2^53 = MIN_SAFE_INTEGER when floating point
	 * is involved).
	 */
	public static final long S_MIN = Numbers.MIN_SAFE_INTEGER;

	/**
	 * The maximum negative S value ({@link #S_NEGATIVE_MAX} x
	 * {@link #S_POSITIVE_MIN} = 256 possibilities, which is the range of values
	 * one byte can represent, which in turn is he atomic data to be encrypted /
	 * decrypted).
	 */
	public static final long S_NEGATIVE_MAX = -16;

	/**
	 * The minimum positive S value ({@link #S_NEGATIVE_MAX} x
	 * {@link #S_POSITIVE_MIN} = 256 possibilities, which is the range of values
	 * one byte can represent, which in turn is he atomic data to be encrypted /
	 * decrypted).
	 */
	public static final long S_POSITIVE_MIN = 16;

	/**
	 * The number of bytes required to store X
	 */
	public static final int X_BYTES = Double.BYTES; // The number of bytes required to store X

	/**
	 * The maximum valid X value.
	 */
	public static final double X_MAX = 1;

	/**
	 * The minimum valid X value.
	 */
	public static final double X_MIN = 0;

	/**
	 * The overall encoded length in bytes of the {@link ChaosKey} including the
	 * {@link ChaosOptions}.
	 */
	public static final int ENCODED_LENGTH = X_BYTES + A_BYTES + S_BYTES + ChaosOptions.ENCODED_LENGTH;

	// -------------------------------------------------------------------------

	private static final long S_MAX_RANGE = S_MAX - S_POSITIVE_MIN;
	private static final long S_MIN_RANGE = S_MIN - S_NEGATIVE_MAX;

	// -------------------------------------------------------------------------

	private static final int CERTIFICATE_LINE_WIDTH = 80;
	private static final String CERTIFICATE_BEGIN_TAG_PREFIX = "-----BEGIN";
	private static final String CERTIFICATE_BEGIN_TAG_SUFFIX = "CHAOS-KEY-CHAIN-----";
	private static final String CERTIFICATE_ENCRYPTED = "ENCRYPTED";
	private static final String CERTIFICATE_SALTED = "SALTED";
	private static final String CERTIFICATE_MUTATE = "MUTATE";
	private static final String CERTIFICATE_XOR = "XOR";
	private static final String CERTIFICATE_END_TAG_PREFIX = "-----END";
	private static final String CERTIFICATE_END_TAG_SUFFIX = "CHAOS-KEY-CHAIN-----";
	private static final String PLAIN_CERTIFICATE_BEGIN = CERTIFICATE_BEGIN_TAG_PREFIX + " " + CERTIFICATE_BEGIN_TAG_SUFFIX;
	private static final String PLAIN_CERTIFICATE_END = CERTIFICATE_END_TAG_PREFIX + " " + CERTIFICATE_END_TAG_SUFFIX;

	// /////////////////////////////////////////////////////////////////////////
	// VARIABLES:
	// /////////////////////////////////////////////////////////////////////////

	private double _x0;
	private double _a;
	private long _s;
	private ChaosOptions _chaosOptions;
	private ChaosKey _childKey = null;

	// /////////////////////////////////////////////////////////////////////////
	// CONSTRUCTORS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Instantiates the {@link ChaosKey} by reversing the result of the
	 * {@link #getEncoded()} operation where the byte array contains the values
	 * x0, a and s (excluding the {@link ChaosOptions}). The values use a big
	 * endian representation. The byte array being passed is to be of the size
	 * as returned by {@link #getEncodedLength()}. The number of bytes required
	 * by each value in the byte array are defined in the constants
	 * {@value #X_BYTES}, {@link #A_BYTES} and {@link #S_BYTES} respectively and
	 * in that order.
	 * 
	 * @param aEncoded The encoded representation of the chaos key.
	 */
	public ChaosKey( byte[] aEncoded ) {
		this( aEncoded, null );
	}

	/**
	 * Instantiates a new {@link ChaosKey} directly from the provided double
	 * values. Make sure your provided arguments are within the allowed bounds;
	 *
	 * @param x0 the x0: (0 <= x0 <= 1 )
	 * @param a the a: (a <= 3.57 <= 4 )
	 * @param s the s: ({@link Integer#MAX_VALUE} >= s
	 *        >={@link Integer#MAX_VALUE} AND 16 <= s <= -16)
	 */
	public ChaosKey( double x0, double a, long s ) {
		this( x0, a, s, ChaosMode.NONE, null );
	}

	/**
	 * Instantiates a new {@link ChaosKey} directly from the provided double
	 * values. Make sure your provided arguments are within the allowed bounds;
	 *
	 * @param x0 the x0: (0 <= x0 <= 1 )
	 * @param a the a: (a <= 3.57 <= 4 )
	 * @param s the s: ({@link Integer#MAX_VALUE} >= s
	 *        >={@link Integer#MAX_VALUE} AND 16 <= s <= -16)
	 * @param aChaosOptions The {@link ChaosMode} ({@link ChaosOptions}) to use
	 *        to enhance Chaos-based encryption by enabling various additional
	 *        processing steps.
	 */
	public ChaosKey( double x0, double a, long s, ChaosOptions aChaosOptions ) {
		this( x0, a, s, aChaosOptions, null );
	}

	/**
	 * Instantiates a new {@link ChaosKey} from the provided integer values. The
	 * integers are converted to the valid ranges of double values for invoking
	 * the constructor {@link #ChaosKey(double, double, long)}. You can use
	 * values in the range of {@link Integer#MIN_VALUE} and
	 * {@link Integer#MAX_VALUE}.
	 *
	 * @param x0 The value from which to calculate the valid x0 double.
	 * @param a The value from which to calculate the valid a double.
	 * @param s The value from which to calculate the valid s double.
	 */
	public ChaosKey( int x0, int a, int s ) {
		this( x0, a, s, ChaosMode.NONE, null );
	}

	/**
	 * Instantiates a new {@link ChaosKey} from the provided integer values. The
	 * integers are converted to the valid ranges of double values for invoking
	 * the constructor {@link #ChaosKey(double, double, long)}. You can use
	 * values in the range of {@link Integer#MIN_VALUE} and
	 * {@link Integer#MAX_VALUE}.
	 *
	 * @param x0 The value from which to calculate the valid x0 double.
	 * @param a The value from which to calculate the valid a double.
	 * @param s The value from which to calculate the valid s double.
	 * @param aChaosOptions The {@link ChaosMode} ({@link ChaosOptions}) to use
	 *        to enhance Chaos-based encryption by enabling various additional
	 *        processing steps.
	 */
	public ChaosKey( int x0, int a, int s, ChaosOptions aChaosOptions ) {
		this( x0, a, s, aChaosOptions, null );
	}

	/**
	 * Instantiates a new {@link ChaosKey} by calculating x0, a and s from the
	 * provided char array.
	 *
	 * @param aSecret The char array (in contrast to a {@link String}, a char
	 *        array can be invalidated after use by overwriting its array
	 *        elements with random values) from which to calculate x0, a and s.
	 */
	public ChaosKey( char[] aSecret ) {
		this( aSecret, ChaosMode.NONE, null );
	}

	/**
	 * Instantiates a new {@link ChaosKey} by calculating x0, a and s from the
	 * provided {@link String}.
	 *
	 * @param aSecret The {@link String} from which to calculate x0, a and s.
	 */
	public ChaosKey( String aSecret ) {
		this( aSecret, ChaosMode.NONE, null );
	}

	/**
	 * Instantiates a new {@link ChaosKey} by calculating x0, a and s from the
	 * provided char array.
	 *
	 * @param aSecret The char array (in contrast to a {@link String}, a char
	 *        array can be invalidated after use by overwriting its array
	 *        elements with random values) from which to calculate x0, a and s.
	 * 
	 * @param aChaosOptions The {@link ChaosMode} ({@link ChaosOptions}) to use
	 *        to enhance Chaos-based encryption.
	 */
	public ChaosKey( char[] aSecret, ChaosOptions aChaosOptions ) {
		this( aSecret, aChaosOptions, null );
	}

	/**
	 * Instantiates a new {@link ChaosKey} by calculating x0, a and s from the
	 * provided {@link String}.
	 *
	 * @param aSecret The {@link String} from which to calculate x0, a and s.
	 * 
	 * @param aChaosOptions The {@link ChaosMode} ({@link ChaosOptions}) to use
	 *        to enhance Chaos-based encryption.
	 */
	public ChaosKey( String aSecret, ChaosOptions aChaosOptions ) {
		this( aSecret, aChaosOptions, null );
	}

	// -------------------------------------------------------------------------

	/**
	 * Instantiates a new {@link ChaosKey} directly from the provided double
	 * values. Make sure your provided arguments are within the allowed bounds;
	 *
	 * @param x0 the x0: (0 <= x0 <= 1 )
	 * @param a the a: (a <= 3.57 <= 4 )
	 * @param s the s: ({@link Integer#MAX_VALUE} >= s
	 *        >={@link Integer#MAX_VALUE} AND 16 <= s <= -16)
	 * @param aChildKey The child {@link ChaosKey} being the successor of this
	 *        {@link ChaosKey} to encrypt and the predecessor of this
	 *        {@link ChaosKey} to decrypt.
	 */
	public ChaosKey( double x0, double a, long s, ChaosKey aChildKey ) {
		this( x0, a, s, ChaosMode.NONE, aChildKey );
	}

	/**
	 * Instantiates a new {@link ChaosKey} from the provided integer values. The
	 * integers are converted to the valid ranges of double values for invoking
	 * the constructor {@link #ChaosKey(double, double, long)}. You can use
	 * values in the range of {@link Integer#MIN_VALUE} and
	 * {@link Integer#MAX_VALUE}.
	 *
	 * @param x0 The value from which to calculate the valid x0 double.
	 * @param a The value from which to calculate the valid a double.
	 * @param s The value from which to calculate the valid s double.
	 * @param aChildKey The child {@link ChaosKey} being the successor of this
	 *        {@link ChaosKey} to encrypt and the predecessor of this
	 *        {@link ChaosKey} to decrypt.
	 */
	public ChaosKey( int x0, int a, int s, ChaosKey aChildKey ) {
		this( x0, a, s, ChaosMode.NONE, aChildKey );
	}

	/**
	 * Instantiates a new {@link ChaosKey} from the provided integer values. The
	 * integers are converted to the valid ranges of double values for invoking
	 * the constructor {@link #ChaosKey(double, double, long)}. You can use
	 * values in the range of {@link Integer#MIN_VALUE} and
	 * {@link Integer#MAX_VALUE}.
	 *
	 * @param x0 The value from which to calculate the valid x0 double.
	 * @param a The value from which to calculate the valid a double.
	 * @param s The value from which to calculate the valid s double.
	 * @param aChaosOptions The {@link ChaosMode} ({@link ChaosOptions}) to use
	 *        to enhance Chaos-based encryption by enabling various additional
	 *        processing steps.
	 * @param aChildKey The child {@link ChaosKey} being the successor of this
	 *        {@link ChaosKey} to encrypt and the predecessor of this
	 *        {@link ChaosKey} to decrypt.
	 */
	public ChaosKey( int x0, int a, int s, ChaosOptions aChaosOptions, ChaosKey aChildKey ) {
		this( toX0( x0 ), toA( a ), toS( s ), aChaosOptions, aChildKey );
	}

	/**
	 * Instantiates a new {@link ChaosKey} by calculating x0, a and s from the
	 * provided char array.
	 *
	 * @param aSecret The char array (in contrast to a {@link String}, a char
	 *        array can be invalidated after use by overwriting its array
	 *        elements with random values) from which to calculate x0, a and s.
	 * @param aChildKey The child {@link ChaosKey} being the successor of this
	 *        {@link ChaosKey} to encrypt and the predecessor of this
	 *        {@link ChaosKey} to decrypt.
	 */
	public ChaosKey( char[] aSecret, ChaosKey aChildKey ) {
		this( aSecret, ChaosMode.NONE, aChildKey );
	}

	/**
	 * Instantiates a new {@link ChaosKey} by calculating x0, a and s from the
	 * provided {@link String}.
	 *
	 * @param aSecret The {@link String} from which to calculate x0, a and s.
	 * @param aChildKey The child {@link ChaosKey} being the successor of this
	 *        {@link ChaosKey} to encrypt and the predecessor of this
	 *        {@link ChaosKey} to decrypt.
	 */
	public ChaosKey( String aSecret, ChaosKey aChildKey ) {
		this( aSecret, ChaosMode.NONE, aChildKey );
	}

	/**
	 * Instantiates the {@link ChaosKey} by reversing the result of the
	 * {@link #getEncoded()} operation where the byte array contains the values
	 * x0, a and s (including the {@link ChaosOptions#getEncoded()}). The values
	 * use a big endian representation. The byte array being passed is to be of
	 * the size as returned by {@link #getEncodedLength()}. The number of bytes
	 * required by each value in the byte array are defined in the constants
	 * {@value #X_BYTES}, {@link #A_BYTES} and {@link #S_BYTES} respectively and
	 * in that order.
	 * 
	 * @param aEncoded The encoded representation of the chaos key.
	 * @param aChildKey The child {@link ChaosKey} being the successor of this
	 *        {@link ChaosKey} to encrypt and the predecessor of this
	 *        {@link ChaosKey} to decrypt.
	 */
	public ChaosKey( byte[] aEncoded, ChaosKey aChildKey ) {
		final byte[] theX0 = new byte[X_BYTES];
		final byte[] theA = new byte[A_BYTES];
		final byte[] theS = new byte[S_BYTES];
		final byte[] theOptions = new byte[ChaosOptions.getEncodedLength()];
		System.arraycopy( aEncoded, 0, theX0, 0, X_BYTES );
		System.arraycopy( aEncoded, X_BYTES, theA, 0, A_BYTES );
		System.arraycopy( aEncoded, X_BYTES + A_BYTES, theS, 0, S_BYTES );
		System.arraycopy( aEncoded, X_BYTES + A_BYTES + S_BYTES, theOptions, 0, ChaosOptions.getEncodedLength() );
		final ChaosOptions theChaosOptions = new ChaosOptionsImpl( theOptions );
		init( Endianess.BIG.toDouble( theX0 ), Endianess.BIG.toDouble( theA ), Endianess.BIG.toLong( theS ), theChaosOptions, aChildKey );
	}

	/**
	 * Instantiates a new {@link ChaosKey} directly from the provided double
	 * values. Make sure your provided arguments are within the allowed bounds;
	 *
	 * @param x0 the x0: (0 <= x0 <= 1 )
	 * @param a the a: (a <= 3.57 <= 4 )
	 * @param s the s: ({@link Integer#MAX_VALUE} >= s
	 *        >={@link Integer#MAX_VALUE} AND 16 <= s <= -16)
	 * @param aChaosOptions The {@link ChaosMode} ({@link ChaosOptions}) to use
	 *        to enhance Chaos-based encryption by enabling various additional
	 *        processing steps.
	 * @param aChildKey The child {@link ChaosKey} being the successor of this
	 *        {@link ChaosKey} to encrypt and the predecessor of this
	 *        {@link ChaosKey} to decrypt.
	 */
	public ChaosKey( double x0, double a, long s, ChaosOptions aChaosOptions, ChaosKey aChildKey ) {
		init( x0, a, s, aChaosOptions, aChildKey );
	}

	/**
	 * Instantiates a new {@link ChaosKey} by calculating x0, a and s from the
	 * provided char array.
	 *
	 * @param aSecret The char array (in contrast to a {@link String}, a char
	 *        array can be invalidated after use by overwriting its array
	 *        elements with random values) from which to calculate x0, a and s.
	 * 
	 * @param aChaosOptions The {@link ChaosMode} ({@link ChaosOptions}) to use
	 *        to enhance Chaos-based encryption.
	 * @param aChildKey The child {@link ChaosKey} being the successor of this
	 *        {@link ChaosKey} to encrypt and the predecessor of this
	 *        {@link ChaosKey} to decrypt.
	 */
	public ChaosKey( char[] aSecret, ChaosOptions aChaosOptions, ChaosKey aChildKey ) {
		final int[] theIds = NumericalUtility.toHashCodes( aSecret, 3 );
		init( toX0( theIds[0] ), toA( theIds[1] ), toS( theIds[2] ), aChaosOptions, aChildKey );
	}

	/**
	 * Instantiates a new {@link ChaosKey} by calculating x0, a and s from the
	 * provided {@link String}.
	 *
	 * @param aSecret The {@link String} from which to calculate x0, a and s.
	 * 
	 * @param aChaosOptions The {@link ChaosMode} ({@link ChaosOptions}) to use
	 *        to enhance Chaos-based encryption.
	 * @param aChildKey The child {@link ChaosKey} being the successor of this
	 *        {@link ChaosKey} to encrypt and the predecessor of this
	 *        {@link ChaosKey} to decrypt.
	 */
	public ChaosKey( String aSecret, ChaosOptions aChaosOptions, ChaosKey aChildKey ) {
		final int[] theIds = NumericalUtility.toHashCodes( aSecret, 3 );
		init( toX0( theIds[0] ), toA( theIds[1] ), toS( theIds[2] ), aChaosOptions, aChildKey );
	}

	// -------------------------------------------------------------------------

	private void init( double x0, double a, long s, ChaosOptions aChaosOptions, ChaosKey aChildKey ) {
		validate( x0, a, s );
		_x0 = x0;
		_a = a;
		_s = s;
		_chaosOptions = aChaosOptions;
		_childKey = aChildKey;
		if ( x0 == X_MIN ) {
			LOGGER.log( Level.WARNING, "The provided  value <" + X_MIN + "> causes degenerated security ( = )!" );
		}
	}

	// /////////////////////////////////////////////////////////////////////////
	// METHODS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void destroy() throws DestroyFailedException {
		dispose();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void dispose() {
		_a = -1;
		_x0 = -1;
		_s = -1;
		_chaosOptions = null;
		if ( _childKey != null ) {
			_childKey.dispose();
			_childKey = null;
		}
	}

	/**
	 * Gets the a.
	 *
	 * @return the a
	 */
	public double getA() {
		return _a;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getAlgorithm() {
		return PROVIDER_NAME;
	}

	/**
	 * Returns the {@link ChaosOptions} being used to enhance Chaos-based
	 * encryption by enabling various additional processing steps. Defaults to
	 * {@link ChaosMode#NONE}.
	 *
	 * @return the chaos metrics
	 */
	public ChaosOptions getOptions() {
		return _chaosOptions;
	}

	/**
	 * Returns the key in its primary encoding format. Returns the bytes in a
	 * defined order representing the x0, a and s values (including the
	 * {@link ChaosOptions#getEncoded()}). The values use a big endian
	 * representation. The byte array being returned is of the size as returned
	 * by {@link #getEncodedLength()}. The number of bytes required by each
	 * value in the byte array are defined in the constants {@value #X_BYTES},
	 * {@link #A_BYTES} and {@link #S_BYTES} respectively and in that order.
	 * 
	 * @return The encoded key being the according bytes representing the
	 *         {@link ChaosKey} x0, a and s values.
	 */
	@Override
	public byte[] getEncoded() {
		final byte[] theX0 = Endianess.BIG.toBytes( _x0 );
		final byte[] theA = Endianess.BIG.toBytes( _a );
		final byte[] theS = Endianess.BIG.toBytes( _s, S_BYTES );
		final byte[] theOptions = _chaosOptions.getEncoded();
		final byte[] theBytes = new byte[getEncodedLength()];
		System.arraycopy( theX0, 0, theBytes, 0, X_BYTES );
		System.arraycopy( theA, 0, theBytes, X_BYTES, A_BYTES );
		System.arraycopy( theS, 0, theBytes, X_BYTES + A_BYTES, S_BYTES );
		System.arraycopy( theOptions, 0, theBytes, X_BYTES + A_BYTES + S_BYTES, ChaosOptions.getEncodedLength() );
		return theBytes;
	}

	/**
	 * Returns all encoded {@link ChaosKey} representations including this
	 * {@link ChaosKey} as well as all the nested {@link ChaosKey} children
	 * being their concatenated {@link ChaosKey#getEncoded()} representation,
	 * the this being first and the bottom most child the last.
	 * 
	 * Invoking the {@link ChaosKey#createKeyChain(byte[])} method with the
	 * result of this method reconstructs the {@link ChaosKey} chain's
	 * instances.
	 * 
	 * @return The bytes representation of this {@link ChaosKey} and its nested
	 *         {@link ChaosKey} children as of their
	 *         {@link ChaosKey#getEncoded()} representation.
	 */
	public byte[] toEncodedChain() {
		if ( _childKey != null ) {
			final byte[] theChildrenChain = _childKey.toEncodedChain();
			final byte[] theChain = new byte[theChildrenChain.length + getEncodedLength()];
			System.arraycopy( getEncoded(), 0, theChain, 0, getEncodedLength() );
			System.arraycopy( theChildrenChain, 0, theChain, getEncodedLength(), theChildrenChain.length );
			return theChain;
		}
		else {
			return getEncoded();
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getFormat() {
		return getAlgorithm() + " format";
	}

	/**
	 * Returns the next outer {@link ChaosKey}. It is the successor to encrypt
	 * (the last child {@link ChaosKey} is the last one to encrypt) and the
	 * predecessor to decrypt (the last child {@link ChaosKey} is the first one
	 * to decrypt).
	 * 
	 * @return the child {@link ChaosKey} being the successor of this
	 *         {@link ChaosKey} to encrypt and the predecessor of this
	 *         {@link ChaosKey} to decrypt.
	 */
	@Override
	public ChaosKey getChild() {
		return _childKey;
	}

	/**
	 * Gets the s.
	 *
	 * @return the s
	 */
	public long getS() {
		return _s;
	}

	/**
	 * Gets the x0.
	 *
	 * @return the x0
	 */
	public double getX0() {
		return _x0;
	}

	/**
	 * Creates a certificate for encryption and decryption from this
	 * {@link ChaosKey}. The certificate is not password protected!
	 * 
	 * @return The {@link String} certificate representation of this
	 *         {@link ChaosKey}.
	 */
	public String toCertificate() {
		return toCertificate( CERTIFICATE_LINE_WIDTH );
	}

	/**
	 * Creates a certificate for encryption and decryption from this
	 * {@link ChaosKey}. The certificate is not password protected!
	 * 
	 * @param aLineWidth The line width to use for the encoded certificate data
	 *        (excluding the head and the tail).
	 * 
	 * @return The {@link String} certificate representation of this
	 *         {@link ChaosKey}.
	 */
	public String toCertificate( int aLineWidth ) {
		final StringBuilder theBuffer = new StringBuilder( toCertificateHead() );
		try ( ByteArrayOutputStream theBytesOut = new ByteArrayOutputStream() ) {
			try ( LineBreakOutputStream theLineOut = new LineBreakOutputStream( theBytesOut, aLineWidth ); BaseEncoderOutputStream theBaseOut = new BaseEncoderOutputStream( theLineOut, BaseMetricsConfig.BASE64 ) ) {
				theBaseOut.write( toEncodedChain() );
				theBaseOut.flush();
			} // Auto closing "theBaseOut.close()" adds a line break if required!
			theBuffer.append( theBytesOut.toString( StandardCharsets.UTF_8 ) );
		}
		catch ( IOException e ) {
			throw new BugException( "Encountered an I/O related bug which cannot occur as we merely use streams in memory!", e );
		}
		theBuffer.append( toCertificateTail() );
		return theBuffer.toString();
	}

	/**
	 * Creates a password protected certificate for encryption and decryption
	 * from this {@link ChaosKey}.
	 * 
	 * @param aPassword The password with which to protect the key.
	 * 
	 * @return The {@link String} certificate representation of this
	 *         {@link ChaosKey}.
	 */
	public String toCertificate( String aPassword ) {
		return toCertificate( aPassword, ChaosMode.NONE, CERTIFICATE_LINE_WIDTH );
	}

	/**
	 * Creates a password protected certificate for encryption and decryption
	 * from this {@link ChaosKey}.
	 * 
	 * @param aPassword The password with which to protect the key.
	 * 
	 * @param aLineWidth The line width to use for the encoded certificate data
	 *        (excluding the head and the tail).
	 * 
	 * @return The {@link String} certificate representation of this
	 *         {@link ChaosKey}.
	 */
	public String toCertificate( String aPassword, int aLineWidth ) {
		return toCertificate( aPassword, ChaosMode.NONE, aLineWidth );
	}

	/**
	 * Creates password protected a certificate for encryption and decryption
	 * from this {@link ChaosKey}.
	 * 
	 * @param aPassword The password with which to protect the key.
	 * @param aChaosOptions The {@link ChaosOptions} to use for the certificate
	 *        encryption.
	 * 
	 * @return The {@link String} certificate representation of this
	 *         {@link ChaosKey}.
	 */
	public String toCertificate( String aPassword, ChaosOptions aChaosOptions ) {
		return toCertificate( aPassword, aChaosOptions, CERTIFICATE_LINE_WIDTH );
	}

	/**
	 * Creates password protected a certificate for encryption and decryption
	 * from this {@link ChaosKey}.
	 * 
	 * @param aPassword The password with which to protect the key.
	 * @param aChaosOptions The {@link ChaosOptions} to use for the certificate
	 *        encryption.
	 * @param aLineWidth The line width to use for the encoded certificate data
	 *        (excluding the head and the tail).
	 * 
	 * @return The {@link String} certificate representation of this
	 *         {@link ChaosKey}.
	 */
	public String toCertificate( String aPassword, ChaosOptions aChaosOptions, int aLineWidth ) {
		if ( aPassword == null || aPassword.isEmpty() ) {
			throw new IllegalArgumentException( "The password must not be null or empty!" );
		}
		final StringBuilder theBuffer = new StringBuilder( toCertificateHead( aChaosOptions ) );
		try ( ByteArrayOutputStream theBytesOut = new ByteArrayOutputStream() ) {
			try ( LineBreakOutputStream theLineOut = new LineBreakOutputStream( theBytesOut, CERTIFICATE_LINE_WIDTH ); BaseEncoderOutputStream theBaseOut = new BaseEncoderOutputStream( theLineOut, BaseMetricsConfig.BASE64 ); ChaosEncryptionOutputStream theChaosOut = new ChaosEncryptionOutputStream( theBaseOut, new ChaosKey( aPassword, aChaosOptions ) ) ) {
				theChaosOut.write( toEncodedChain() );
				theChaosOut.flush();
			} // Auto closing "theChaosOut.close()" adds a line break if required!
			theBuffer.append( theBytesOut.toString( StandardCharsets.UTF_8.name() ) );
		}
		catch ( IOException e ) {
			throw new BugException( "Encountered an I/O related bug which cannot occur as we merely use streams in memory!", e );
		}
		theBuffer.append( toCertificateTail( aChaosOptions ) );
		return theBuffer.toString();
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		long temp;
		temp = Double.doubleToLongBits( _a );
		result = prime * result + (int) ( temp ^ ( temp >>> 32 ) );
		result = prime * result + ( ( _chaosOptions == null ) ? 0 : _chaosOptions.hashCode() );
		result = prime * result + ( ( _childKey == null ) ? 0 : _childKey.hashCode() );
		result = prime * result + (int) ( _s ^ ( _s >>> 32 ) );
		temp = Double.doubleToLongBits( _x0 );
		result = prime * result + (int) ( temp ^ ( temp >>> 32 ) );
		return result;
	}

	/**
	 * Returns true if all the passed object is of type {@link ChaosKey} and all
	 * the attributes from the {@link ChaosKey} are equal to this instance's
	 * attributes (as of {@link #getX0()}, {@link #getA()} and {@link #getS()}
	 * as well as {@link #getOptions()} and {@link #getChild()}).
	 *
	 * @param obj the obj
	 * 
	 * @return True if the objects are considered semantically equal.
	 */
	@Override
	public boolean equals( Object obj ) {
		if ( this == obj ) {
			return true;
		}
		if ( obj == null ) {
			return false;
		}
		if ( getClass() != obj.getClass() ) {
			return false;
		}
		final ChaosKey other = (ChaosKey) obj;
		if ( Double.doubleToLongBits( _a ) != Double.doubleToLongBits( other._a ) ) {
			return false;
		}
		if ( _chaosOptions == null ) {
			if ( other._chaosOptions != null ) {
				return false;
			}
		}
		else if ( !_chaosOptions.equals( other._chaosOptions ) ) {
			return false;
		}
		if ( _childKey == null ) {
			if ( other._childKey != null ) {
				return false;
			}
		}
		else if ( !_childKey.equals( other._childKey ) ) {
			return false;
		}
		if ( _s != other._s ) {
			return false;
		}
		if ( Double.doubleToLongBits( _x0 ) != Double.doubleToLongBits( other._x0 ) ) {
			return false;
		}
		return true;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String toString() {
		return getClass().getSimpleName() + " [x0=" + _x0 + ", a=" + _a + ", s=" + _s + ", chaosMetrics=" + _chaosOptions + ", childKey=" + ( _childKey != null ? _childKey.toString() : null ) + "]";
	}

	// /////////////////////////////////////////////////////////////////////////
	// CREATION:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Creates a {@link ChaosKey} (chain) from the password protected
	 * certificate retrieved from the given {@link File}.
	 * 
	 * @param aCertFile The {@link File} pointing to the certificate
	 *        representing the {@link ChaosKey}.
	 * 
	 * @param aPassword The password protecting the certificate.
	 * 
	 * @return The {@link ChaosKey} (chain) represented by the certificate.
	 * 
	 * @throws IOException thrown in case an I/O related problem occurred while
	 *         accessing the {@link File}.
	 */
	public static ChaosKey createFromCertificate( File aCertFile, String aPassword ) throws IOException {
		return createFromCertificate( new BufferedInputStream( new FileInputStream( aCertFile ) ), aPassword );
	}

	/**
	 * Creates a {@link ChaosKey} (chain) from the password protected
	 * certificate retrieved from the given {@link InputStream}.
	 * 
	 * @param aInputStream The {@link InputStream} providing the certificate
	 *        representing the {@link ChaosKey}.
	 * 
	 * @param aPassword The password protecting the certificate.
	 * 
	 * @return The {@link ChaosKey} (chain) represented by the certificate.
	 * 
	 * @throws IOException thrown in case an I/O related problem occurred while
	 *         accessing the {@link InputStream}.
	 */
	public static ChaosKey createFromCertificate( InputStream aInputStream, String aPassword ) throws IOException {
		final ByteArrayOutputStream theByteOut = new ByteArrayOutputStream();
		aInputStream.transferTo( theByteOut );
		return createFromCertificate( theByteOut.toString( StandardCharsets.UTF_8.name() ), aPassword );
	}

	/**
	 * Creates a {@link ChaosKey} (chain) from the password protected
	 * certificate.
	 * 
	 * @param aCertificate The certificate representing the {@link ChaosKey}.
	 * 
	 * @param aPassword The password protecting the certificate.
	 * 
	 * @return The {@link ChaosKey} (chain) represented by the certificate.
	 */
	public static ChaosKey createFromCertificate( String aCertificate, String aPassword ) {
		final String[] theLines = aCertificate.split( "\n" );
		final ChaosOptions theOptions = fromCertificateHead( theLines[0] );
		final ChaosOptions theTailOptions = fromCertificateTail( theLines[theLines.length - 1] );
		if ( !theOptions.equals( theTailOptions ) ) {
			throw new IllegalArgumentException( "The head options <" + theOptions.toString() + "> do not match the tail options <" + theTailOptions.toString() + "> !" );
		}
		final ChaosKey theKey = new ChaosKey( aPassword, theOptions );

		final byte[] theBytes;
		try ( ByteArrayOutputStream theBytesOut = new ByteArrayOutputStream() ) {
			for ( int i = 1; i < theLines.length - 1; i++ ) {
				theBytesOut.write( theLines[i].getBytes( StandardCharsets.UTF_8 ) );
			}
			theBytesOut.flush();
			theBytes = theBytesOut.toByteArray();
		}
		catch ( IOException e ) {
			throw new BugException( "Encountered an I/O related bug which cannot occur as we merely use streams in memory!", e );
		}

		try ( ByteArrayInputStream theBytesIn = new ByteArrayInputStream( theBytes ); BaseDecoderInputStream theBaseIn = new BaseDecoderInputStream( theBytesIn, BaseMetricsConfig.BASE64 ); ChaosDecryptionInputStream theChaosIn = new ChaosDecryptionInputStream( theBaseIn, theKey ); ByteArrayOutputStream theBytesOut = new ByteArrayOutputStream() ) {
			theChaosIn.transferTo( theBytesOut );
			return ChaosKey.createKeyChain( theBytesOut.toByteArray() );
		}
		catch ( IOException e ) {
			throw new BugException( "Encountered an I/O related bug which cannot occur as we merely use streams in memory!", e );
		}
	}

	/**
	 * Creates a {@link ChaosKey} (chain) from the unsecured certificate
	 * retrieved from the given {@link File}.
	 * 
	 * @param aCertFile The {@link File} pointing to the certificate
	 *        representing the {@link ChaosKey}.
	 * 
	 * @return The {@link ChaosKey} (chain) represented by the certificate.
	 * 
	 * @throws IOException thrown in case an I/O related problem occurred while
	 *         accessing the {@link File}.
	 */
	public static ChaosKey createFromCertificate( File aCertFile ) throws IOException {
		return createFromCertificate( new BufferedInputStream( new FileInputStream( aCertFile ) ) );
	}

	/**
	 * Creates a {@link ChaosKey} (chain) from the unsecured certificate
	 * retrieved from the given {@link InputStream}.
	 * 
	 * @param aInputStream The {@link InputStream} providing the certificate
	 *        representing the {@link ChaosKey}.
	 * 
	 * @return The {@link ChaosKey} (chain) represented by the certificate.
	 * 
	 * @throws IOException thrown in case an I/O related problem occurred while
	 *         accessing the {@link InputStream}.
	 */
	public static ChaosKey createFromCertificate( InputStream aInputStream ) throws IOException {
		final ByteArrayOutputStream theByteOut = new ByteArrayOutputStream();
		aInputStream.transferTo( theByteOut );
		return createFromCertificate( theByteOut.toString( StandardCharsets.UTF_8.name() ) );
	}

	/**
	 * Creates a {@link ChaosKey} (chain) from the unsecured certificate.
	 * 
	 * @param aCertificate The certificate representing the {@link ChaosKey}.
	 * 
	 * @return The {@link ChaosKey} (chain) represented by the certificate.
	 */
	public static ChaosKey createFromCertificate( String aCertificate ) {
		final String[] theLines = aCertificate.split( "\n" );
		if ( !theLines[0].equals( PLAIN_CERTIFICATE_BEGIN ) ) {
			throw new IllegalArgumentException( "The head (\"" + theLines[0] + "\") contains superflous declarations contrasting an expected plain (unsecured) certificate (expexting \"" + PLAIN_CERTIFICATE_BEGIN + "\") !" );
		}
		if ( !theLines[theLines.length - 1].equals( PLAIN_CERTIFICATE_END ) ) {
			throw new IllegalArgumentException( "The tail (\"" + theLines[theLines.length - 1] + "\") contains superflous declarations contrasting an expected plain (unsecured) certificate (expexting \"" + PLAIN_CERTIFICATE_END + "\") !" );
		}

		final byte[] theBytes;
		try ( ByteArrayOutputStream theBytesOut = new ByteArrayOutputStream() ) {
			for ( int i = 1; i < theLines.length - 1; i++ ) {
				theBytesOut.write( theLines[i].getBytes( StandardCharsets.UTF_8 ) );
			}
			theBytesOut.flush();
			theBytes = theBytesOut.toByteArray();
		}
		catch ( IOException e ) {
			throw new BugException( "Encountered an I/O related bug which cannot occur as we merely use streams in memory!", e );
		}

		try ( ByteArrayInputStream theBytesIn = new ByteArrayInputStream( theBytes ); BaseDecoderInputStream theBaseIn = new BaseDecoderInputStream( theBytesIn, BaseMetricsConfig.BASE64 ); ByteArrayOutputStream theBytesOut = new ByteArrayOutputStream() ) {
			theBaseIn.transferTo( theBytesOut );
			return ChaosKey.createKeyChain( theBytesOut.toByteArray() );
		}
		catch ( IOException e ) {
			throw new BugException( "Encountered an I/O related bug which cannot occur as we merely use streams in memory!", e );
		}
	}

	/**
	 * Creates a {@link ChaosKey} from the provided encoded chain
	 * representations (as of {@link ChaosKey#toEncodedChain()}) including all
	 * the nested {@link ChaosKey} children.
	 * 
	 * @param aEncodedChain The byte array representing the {@link ChaosKey}
	 *        chain's encoding (as of {@link ChaosKey#toEncodedChain()}).
	 * 
	 * @return The reconstructed {@link ChaosKey} chain from the provided
	 *         encoded chain representation.
	 */
	public static ChaosKey createKeyChain( byte[] aEncodedChain ) {
		if ( aEncodedChain.length == 0 || aEncodedChain.length % getEncodedLength() != 0 ) {
			throw new IllegalArgumentException( "The provided encoded chain size <" + aEncodedChain.length + "> must be a multiple of <" + getEncodedLength() + ">!" );
		}
		ChaosKey eKey = null;
		final byte[] eEncoded = new byte[getEncodedLength()];
		final int theKeyCount = aEncodedChain.length / getEncodedLength();
		for ( int i = theKeyCount - 1; i >= 0; i-- ) {
			System.arraycopy( aEncodedChain, i * getEncodedLength(), eEncoded, 0, getEncodedLength() );
			eKey = new ChaosKey( eEncoded, eKey );
		}
		return eKey;
	}

	/**
	 * Creates a random {@link ChaosKey} chain, where each {@link ChaosKey}
	 * references a child {@link ChaosKey} (as of {@link ChaosKey#getChild()})
	 * as defined by the provided chain length and each {@link ChaosKey} in the
	 * chain is initialized by its unique random values.
	 * 
	 * @param aChainLength The number of {@link ChaosKey} instances chained
	 *        behind each other as of {@link ChaosKey#getChild()}
	 *
	 * @return the {@link ChaosKey} with the according chain length being
	 *         initialized with random values.
	 */
	public static ChaosKey createRndKeyChain( int aChainLength ) {
		return createRndKeyChain( aChainLength, ChaosMode.NONE );
	}

	/**
	 * Creates a random {@link ChaosKey} chain, where each {@link ChaosKey}
	 * references a child {@link ChaosKey} (as of {@link ChaosKey#getChild()})
	 * as defined by the provided chain length and each {@link ChaosKey} in the
	 * chain is initialized by its unique random values.
	 * 
	 * @param aChainLength The number of {@link ChaosKey} instances chained
	 *        behind each other as of {@link ChaosKey#getChild()}
	 * @param aChaosOptions The {@link ChaosOptions} to use for each
	 *        {@link ChaosKey}, the first one is used for the top most parent
	 *        {@link ChaosKey}, the others for the succeeding {@link ChaosKey}
	 *        children. The last {@link ChaosOptions} element is used for any
	 *        {@link ChaosKey} to be created exceeding the length of the
	 *        {@link ChaosOptions} elements.
	 *
	 * @return the {@link ChaosKey} with the according chain length being
	 *         initialized with random values.
	 */
	public static ChaosKey createRndKeyChain( int aChainLength, ChaosOptions... aChaosOptions ) {
		if ( aChainLength < 1 ) {
			throw new IllegalArgumentException( "The key chain length <" + aChainLength + "> must be > 0 !" );
		}
		aChaosOptions = aChaosOptions != null && aChaosOptions.length != 0 ? aChaosOptions : new ChaosOptions[] { ChaosMode.NONE };
		ChaosKey eKey = null;
		ChaosKey eChildKey = null;
		for ( int i = aChainLength - 1; i >= 0; i-- ) {
			eKey = createRndKey( i > aChaosOptions.length - 1 ? aChaosOptions[aChaosOptions.length - 1] : aChaosOptions[i], eChildKey );
			eChildKey = eKey;
		}
		return eKey;
	}

	/**
	 * Creates a random {@link ChaosKey}. Use {@link #getX0()}, {@link #getA()}
	 * and {@link #getS()} or {@link #getEncoded()} to retrieve the parameters
	 * of the created {@link ChaosKey}.
	 *
	 * @return the {@link ChaosKey} being initialized with random values.
	 */
	public static ChaosKey createRndKey() {
		return createRndKey( ChaosMode.NONE, null );
	}

	/**
	 * Creates a random {@link ChaosKey}. Use {@link #getX0()}, {@link #getA()}
	 * and {@link #getS()} or {@link #getEncoded()} to retrieve the parameters
	 * of the created {@link ChaosKey}.
	 *
	 * @param aChaosOptions The {@link ChaosMode} ({@link ChaosOptions}) to use
	 *        to enhance Chaos-based encryption.
	 * 
	 * @return the {@link ChaosKey} being initialized with random values.
	 */
	public static ChaosKey createRndKey( ChaosOptions aChaosOptions ) {
		return createRndKey( aChaosOptions, null );
	}

	/**
	 * Creates a random {@link ChaosKey}. Use {@link #getX0()}, {@link #getA()}
	 * and {@link #getS()} or {@link #getEncoded()} to retrieve the parameters
	 * of the created {@link ChaosKey}.
	 *
	 * @param aChildKey The child {@link ChaosKey} being the successor of this
	 *        {@link ChaosKey} to encrypt and the predecessor of this
	 *        {@link ChaosKey} to decrypt.
	 * 
	 * @return the {@link ChaosKey} being initialized with random values.
	 */
	public static ChaosKey createRndKey( ChaosKey aChildKey ) {
		return createRndKey( ChaosMode.NONE, aChildKey );
	}

	/**
	 * Creates a random {@link ChaosKey}. Use {@link #getX0()}, {@link #getA()}
	 * and {@link #getS()} or {@link #getEncoded()} to retrieve the parameters
	 * of the created {@link ChaosKey}.
	 *
	 * @param aChaosOptions The {@link ChaosMode} ({@link ChaosOptions}) to use
	 *        to enhance Chaos-based encryption.
	 * 
	 * @param aChildKey The child {@link ChaosKey} being the successor of this
	 *        {@link ChaosKey} to encrypt and the predecessor of this
	 *        {@link ChaosKey} to decrypt.
	 * 
	 * @return the {@link ChaosKey} being initialized with random values.
	 */
	public static ChaosKey createRndKey( ChaosOptions aChaosOptions, ChaosKey aChildKey ) {
		SecureRandom rnd;
		try {
			rnd = SecureRandom.getInstanceStrong();
		}
		catch ( NoSuchAlgorithmException ignore ) {
			rnd = new SecureRandom();
		}

		RuntimeException theFirstException = null;
		for ( int i = 0; i < 10; i++ ) { // <--| Should not be needed, just to make sure
			try {
				return new ChaosKey( ChaosKey.toX0( rnd.nextLong() ), ChaosKey.toA( rnd.nextLong() ), ChaosKey.toS( rnd.nextLong() ), aChaosOptions, aChildKey ); // <--| We should get here in the first (i=0) loop!
			}
			catch ( RuntimeException e ) {
				if ( theFirstException == null ) {
					theFirstException = e;
				}
			}
		}
		throw theFirstException; // <--| Should never happen, just to make sure
	}

	/**
	 * Creates a {@link ChaosOptions} of the (secured) certificate itself (and
	 * not the therein contained key chain) retrieved from the given
	 * {@link File}.
	 * 
	 * @param aCertFile The {@link File} pointing to the certificate
	 *        representing which's {@link ChaosOptions} are to be determined.
	 * 
	 * @return The {@link ChaosOptions} of the certificate itself or null if it
	 *         is an unsecured certificate.
	 * 
	 * @throws IOException thrown in case an I/O related problem occurred while
	 *         accessing the {@link File}.
	 * 
	 * @throws IllegalArgumentException thrown in case the provided certificate
	 *         does not match a {@link ChaosKey} certificate notation.
	 * 
	 */
	public static ChaosOptions asCertificateOptions( File aCertFile ) throws IOException {
		return asCertificateOptions( new BufferedInputStream( new FileInputStream( aCertFile ) ) );
	}

	/**
	 * Creates a {@link ChaosOptions} of the (secured) certificate itself (and
	 * not the therein contained key chain) retrieved from the given
	 * {@link InputStream}.
	 * 
	 * @param aInputStream The {@link InputStream} pointing to the certificate
	 *        representing which's {@link ChaosOptions} are to be determined.
	 * 
	 * @return The {@link ChaosOptions} of the certificate itself or null if it
	 *         is an unsecured certificate
	 * 
	 * @throws IOException thrown in case an I/O related problem occurred while
	 *         accessing the {@link InputStream}.
	 * 
	 * @throws IllegalArgumentException thrown in case the provided certificate
	 *         does not match a {@link ChaosKey} certificate notation.
	 * 
	 */
	public static ChaosOptions asCertificateOptions( InputStream aInputStream ) throws IOException {
		final ByteArrayOutputStream theByteOut = new ByteArrayOutputStream();
		aInputStream.transferTo( theByteOut );
		return asCertificateOptions( theByteOut.toString( StandardCharsets.UTF_8.name() ) );
	}

	/**
	 * Creates a {@link ChaosOptions} of the (secured) certificate itself (and
	 * not the therein contained key chain) retrieved from the given
	 * {@link String}.
	 *
	 * @param aCertificate The {@link String} representing the
	 *        certificatewhich's {@link ChaosOptions} are to be determined.
	 * 
	 * @return The {@link ChaosOptions} of the certificate itself or null if it
	 *         is an unsecured certificate
	 * 
	 * @throws IllegalArgumentException thrown in case the provided certificate
	 *         does not match a {@link ChaosKey} certificate notation.
	 */
	public static ChaosOptions asCertificateOptions( String aCertificate ) {
		ChaosOptions theOptions = null;
		final String[] theLines = aCertificate.split( "\n" );
		try {
			if ( !theLines[0].equals( PLAIN_CERTIFICATE_BEGIN ) ) {
				throw new IllegalArgumentException( "The head (\"" + theLines[0] + "\") contains superflous declarations contrasting an expected plain (unsecured) certificate (expexting \"" + PLAIN_CERTIFICATE_BEGIN + "\") !" );
			}
			if ( !theLines[theLines.length - 1].equals( PLAIN_CERTIFICATE_END ) ) {
				throw new IllegalArgumentException( "The tail (\"" + theLines[theLines.length - 1] + "\") contains superflous declarations contrasting an expected plain (unsecured) certificate (expexting \"" + PLAIN_CERTIFICATE_END + "\") !" );
			}
		}
		catch ( IllegalArgumentException e ) {
			theOptions = fromCertificateHead( theLines[0] );
			final ChaosOptions theTailOptions = fromCertificateTail( theLines[theLines.length - 1] );
			if ( !theOptions.equals( theTailOptions ) ) {
				throw new IllegalArgumentException( "The head options <" + theOptions.toString() + "> do not match the tail options <" + theTailOptions.toString() + "> !" );
			}
		}
		return theOptions;
	}

	/**
	 * Returns the length of this {@link ChaosKey} when represented as bytes (as
	 * of {@link ChaosKey#getEncoded()}) (excluding the {@link ChaosOptions}).
	 * 
	 * @return The number of bytes required to represent a {@link ChaosKey} x0,
	 *         a and s values.
	 */
	public static int getEncodedLength() {
		return ENCODED_LENGTH;
	}

	// /////////////////////////////////////////////////////////////////////////
	// HOOKS:
	// /////////////////////////////////////////////////////////////////////////

	/**
	 * Determines the number of {@link ChaosKey} children in a row which when
	 * applied produce output of the same length as the input was. The
	 * {@link ChaosKey} child after that determined depth is either null or a
	 * variable length {@link ChaosKey} child (as of
	 * {@link #nextVariableLengthChild()}).
	 * 
	 * @return The number of {@link ChaosKey} children in a row which when
	 *         applied produce output of the same length as the input was.
	 */
	protected int fixedLengthChildDepth() {
		int depth = 0;
		ChaosKey eKey = getChild();
		while ( eKey != null ) {
			if ( !eKey.getOptions().isFixedLength() ) {
				return depth;
			}
			eKey = eKey.getChild();
			depth++;
		}
		return depth;
	}

	/**
	 * Determines the next {@link ChaosKey} child (excluding this) which does
	 * not(!) produce output of the same length as the input was. This is the
	 * {@link ChaosKey} child after the determined depth of {@link ChaosKey}
	 * children in a row which when applied produce output of the same length as
	 * the input was (as of {@link #fixedLengthChildDepth()}) or null if there
	 * is no such next {@link ChaosKey}.
	 * 
	 * @return The next {@link ChaosKey} child which does not(!) produce output
	 *         of the same length as the input was or null if there is no such
	 *         next {@link ChaosKey}.
	 */
	protected ChaosKey nextVariableLengthChild() {
		ChaosKey eKey = getChild();
		while ( eKey != null ) {
			if ( !eKey.getOptions().isFixedLength() ) {
				return eKey;
			}
			eKey = eKey.getChild();
		}
		return null;
	}

	/**
	 * Adds a value to s so that a result exceeding {@link #S_MAX} or
	 * {{@link #S_MIN} is wrapped around accordingly and any result inbetween
	 * {@link #S_POSITIVE_MIN} and {@link #S_NEGATIVE_MAX} is moved past
	 * {@link #S_POSITIVE_MIN} or {@link #S_NEGATIVE_MAX} accordingly.
	 * 
	 * @param s The value to which to add an summand.
	 * @param aAddend The summand to be added to s.
	 * 
	 * @return The accordingly wrapped new s value.
	 */
	protected static long addToS( long s, long aAddend ) {
		if ( aAddend == 0 ) {
			return s;
		}
		long theS = s + aAddend;
		if ( theS > S_MAX ) {
			theS = S_MIN + ( theS - S_MAX );
		}
		else if ( theS < S_MIN ) {
			theS = S_MAX + ( theS - S_MIN );
		}
		if ( theS > S_NEGATIVE_MAX && theS < S_POSITIVE_MIN ) {
			if ( s > 0 ) {
				theS = S_NEGATIVE_MAX - ( S_POSITIVE_MIN - theS );
			}
			else {
				theS = S_POSITIVE_MIN - ( S_NEGATIVE_MAX - theS );
			}
		}
		return theS;
	}

	/**
	 * Creates a valid a double from the provided integer (values from
	 * {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE} are allowed.
	 * 
	 * @param a The integer as source for a
	 * 
	 * @return The a inside the valid scope.
	 */
	protected static double toA( int a ) {
		return NumericalUtility.toDouble( a, A_MIN, A_MAX );
	}

	/**
	 * Creates a valid a double from the provided long (values from
	 * {@link Long#MIN_VALUE} to {@link Long#MAX_VALUE} are allowed).
	 * 
	 * @param a The long as source for a
	 * 
	 * @return The a inside the valid scope.
	 */
	protected static double toA( long a ) {
		return NumericalUtility.toDouble( a, A_MIN, A_MAX );
	}

	/**
	 * Creates a valid s double from the provided integer (values from
	 * {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE} are allowed).
	 * 
	 * @param s The integer as source for s
	 * 
	 * @return The s inside the valid scope.
	 */
	protected static long toS( int s ) {
		long theS = s;

		if ( theS > 0 ) {
			theS = theS * ( S_MAX_RANGE / Integer.MAX_VALUE ) + S_POSITIVE_MIN;
		}
		else {
			theS = theS * ( S_MIN_RANGE / Integer.MIN_VALUE ) + S_NEGATIVE_MAX;
		}

		if ( theS > S_NEGATIVE_MAX && theS < S_POSITIVE_MIN ) {
			throw new IllegalArgumentException( "The value S was <" + theS + ">, though must not be between (excluding) <" + S_NEGATIVE_MAX + "> and  <" + S_POSITIVE_MIN + ">!" );
		}
		if ( theS > S_MAX ) {
			throw new IllegalArgumentException( "The value S was <" + theS + ">, though must not be greater than <" + S_MAX + ">!" );
		}
		if ( theS < S_MIN ) {
			throw new IllegalArgumentException( "The value S was <" + theS + ">, though must not be less than <" + S_MIN + ">!" );
		}

		return theS;
	}

	/**
	 * Creates a valid s long from the provided long (values from
	 * {@link Long#MIN_VALUE} to {@link Long#MAX_VALUE} are allowed).
	 * 
	 * @param s The long as source for s
	 * 
	 * @return The s inside the valid scope.
	 */
	protected static long toS( long s ) {
		if ( s > S_NEGATIVE_MAX && s < S_POSITIVE_MIN ) {
			s += s > 0 ? S_POSITIVE_MIN : S_NEGATIVE_MAX;
		}
		if ( s > S_MAX ) {
			s = ( s % S_MAX_RANGE ) + S_POSITIVE_MIN;
		}
		if ( s < S_MIN ) {
			s = ( s % S_MIN_RANGE ) + S_NEGATIVE_MAX;
		}

		if ( s > S_NEGATIVE_MAX && s < S_POSITIVE_MIN ) {
			throw new IllegalArgumentException( "The value S was <" + s + ">, though must not be between (excluding) <" + S_NEGATIVE_MAX + "> and  <" + S_POSITIVE_MIN + ">!" );
		}
		if ( s > S_MAX ) {
			throw new IllegalArgumentException( "The value S was <" + s + ">, though must not be greater than <" + S_MAX + ">!" );
		}
		if ( s < S_MIN ) {
			throw new IllegalArgumentException( "The value S was <" + s + ">, though must not be less than <" + S_MIN + ">!" );
		}
		return s;
	}

	/**
	 * Creates a valid x0 double from the provided integer (values from
	 * {@link Integer#MIN_VALUE} to {@link Integer#MAX_VALUE} are allowed.
	 * 
	 * @param x0 The integer as source for x0
	 * 
	 * @return The x0 inside the valid scope.
	 */
	protected static double toX0( int x0 ) {
		return NumericalUtility.toDouble( x0, X_MIN, X_MAX );
	}

	/**
	 * Creates a valid x0 double from the provided long (values from
	 * {@link Long#MIN_VALUE} to {@link Long#MAX_VALUE} are allowed).
	 * 
	 * @param x0 The long as source for x0
	 * 
	 * @return The x0 inside the valid scope.
	 */
	protected static double toX0( long x0 ) {
		return NumericalUtility.toDouble( x0, X_MIN, X_MAX );
	}

	/**
	 * Validates the given values whether them are in scope.
	 * 
	 * @param x0 The x0 value to validate.
	 * @param a The a value to validate.
	 * @param s The s value to validate.
	 */
	protected static void validate( double x0, double a, double s ) {
		if ( x0 < X_MIN || x0 > X_MAX ) {
			throw new IllegalArgumentException( "The value x(0) of the chaos key was to be <" + x0 + ">, though must be between <" + X_MIN + "> and <" + X_MAX + "> (" + X_MIN + " <= x0 <= " + X_MAX + ")" );
		}
		if ( a < A_MIN || a > A_MAX ) {
			throw new IllegalArgumentException( "The value A of the chaos key was to be <" + a + ">, though must be between <" + A_MIN + "> and <" + A_MAX + "> (" + A_MIN + " <= A <= " + A_MAX + ")" );
		}
		if ( s < S_MIN || s > S_MAX ) {
			throw new IllegalArgumentException( "The value S of the chaos key was to be <" + s + ">, though must be between <" + S_MIN + "> and  <" + S_MAX + ">" );
		}
		if ( s > S_NEGATIVE_MAX && s < S_POSITIVE_MIN ) {
			throw new IllegalArgumentException( "The value S of the chaos key was to be <" + s + ">, though must not be between (excluding) <" + S_NEGATIVE_MAX + "> and  <" + S_POSITIVE_MIN + ">" );
		}
	}

	/**
	 * Extracts a {@link ChaosOptions} instance from the provided certificate
	 * head.
	 * 
	 * @param aCertificateHead The head (first line) of the certificate in
	 *        question.
	 * 
	 * @return The according {@link ChaosOptions} instance.
	 * 
	 * @throws IllegalArgumentException in case the certificate head is not
	 *         valid for creating a {@link ChaosOptions} instance.
	 */
	protected static ChaosOptions fromCertificateHead( String aCertificateHead ) {
		String theCertificateHead = aCertificateHead;
		theCertificateHead = theCertificateHead.trim();
		if ( !theCertificateHead.startsWith( CERTIFICATE_BEGIN_TAG_PREFIX ) ) {
			throw new IllegalArgumentException( "The provided certificate head (\"" + theCertificateHead + "\") does not(!) start with \"" + CERTIFICATE_BEGIN_TAG_PREFIX + "\" !" );
		}
		theCertificateHead = theCertificateHead.substring( CERTIFICATE_BEGIN_TAG_PREFIX.length() );
		theCertificateHead = trim( theCertificateHead );
		if ( !theCertificateHead.endsWith( CERTIFICATE_BEGIN_TAG_SUFFIX ) ) {
			throw new IllegalArgumentException( "The provided certificate head (\"" + theCertificateHead + "\") does not(!) end with \"" + CERTIFICATE_BEGIN_TAG_SUFFIX + "\" !" );
		}
		theCertificateHead = theCertificateHead.substring( 0, theCertificateHead.length() - CERTIFICATE_BEGIN_TAG_SUFFIX.length() );
		final boolean isEncrypted = theCertificateHead.contains( CERTIFICATE_ENCRYPTED );
		if ( isEncrypted ) {
			theCertificateHead = theCertificateHead.replaceFirst( CERTIFICATE_ENCRYPTED, "" );
		}
		else {
			throw new IllegalArgumentException( "The provided certificate head (\"" + aCertificateHead + "\") is expected to declare an encrypted certificate but does not declared to be \"" + CERTIFICATE_ENCRYPTED + "\" !" );
		}
		final boolean isMutateS = theCertificateHead.contains( CERTIFICATE_MUTATE );
		if ( isMutateS ) {
			theCertificateHead = theCertificateHead.replaceFirst( CERTIFICATE_MUTATE, "" );
		}
		final boolean isXorNext = theCertificateHead.contains( CERTIFICATE_XOR );
		if ( isXorNext ) {
			theCertificateHead = theCertificateHead.replaceFirst( CERTIFICATE_XOR, "" );
		}
		final boolean isSalted = theCertificateHead.contains( CERTIFICATE_SALTED );
		if ( isSalted ) {
			theCertificateHead = theCertificateHead.replaceFirst( CERTIFICATE_SALTED, "" );
		}
		if ( trim( theCertificateHead ).length() != 0 ) {
			throw new IllegalArgumentException( "The provided certificate head (\"" + aCertificateHead + "\") has superflous elements \"" + theCertificateHead + "\" !" );
		}
		return new ChaosOptionsImpl( isMutateS, isXorNext, isSalted );
	}

	/**
	 * Extracts a {@link ChaosOptions} instance from the provided certificate
	 * tail.
	 * 
	 * @param aCertificateTail The tail (last line) of the certificate in
	 *        question.
	 * 
	 * @return The according {@link ChaosOptions} instance.
	 * 
	 * @throws IllegalArgumentException in case the certificate tail is not
	 *         valid for creating a {@link ChaosOptions} instance.
	 */
	protected static ChaosOptions fromCertificateTail( String aCertificateTail ) {
		String theCertificateTail = aCertificateTail;
		theCertificateTail = theCertificateTail.trim();
		if ( !theCertificateTail.startsWith( CERTIFICATE_END_TAG_PREFIX ) ) {
			throw new IllegalArgumentException( "The provided certificate tail (\"" + theCertificateTail + "\") does not(!) start with \"" + CERTIFICATE_END_TAG_PREFIX + "\" !" );
		}
		theCertificateTail = theCertificateTail.substring( CERTIFICATE_END_TAG_PREFIX.length() );
		theCertificateTail = trim( theCertificateTail );
		if ( !theCertificateTail.endsWith( CERTIFICATE_END_TAG_SUFFIX ) ) {
			throw new IllegalArgumentException( "The provided certificate tail (\"" + theCertificateTail + "\") does not(!) end with \"" + CERTIFICATE_END_TAG_SUFFIX + "\" !" );
		}
		theCertificateTail = theCertificateTail.substring( 0, theCertificateTail.length() - CERTIFICATE_END_TAG_SUFFIX.length() );
		final boolean isEncrypted = theCertificateTail.contains( CERTIFICATE_ENCRYPTED );
		if ( isEncrypted ) {
			theCertificateTail = theCertificateTail.replaceFirst( CERTIFICATE_ENCRYPTED, "" );
		}
		else {
			throw new IllegalArgumentException( "The provided certificate tail (\"" + aCertificateTail + "\") is expected to declare an encrypted certificate but does not declared to be \"" + CERTIFICATE_ENCRYPTED + "\" !" );
		}
		final boolean isMutateS = theCertificateTail.contains( CERTIFICATE_MUTATE );
		if ( isMutateS ) {
			theCertificateTail = theCertificateTail.replaceFirst( CERTIFICATE_MUTATE, "" );
		}
		final boolean isXorNext = theCertificateTail.contains( CERTIFICATE_XOR );
		if ( isXorNext ) {
			theCertificateTail = theCertificateTail.replaceFirst( CERTIFICATE_XOR, "" );
		}
		final boolean isSalted = theCertificateTail.contains( CERTIFICATE_SALTED );
		if ( isSalted ) {
			theCertificateTail = theCertificateTail.replaceFirst( CERTIFICATE_SALTED, "" );
		}
		if ( trim( theCertificateTail ).length() != 0 ) {
			throw new IllegalArgumentException( "The provided certificate tail (\"" + aCertificateTail + "\") has superflous elements \"" + theCertificateTail + "\" !" );
		}
		return new ChaosOptionsImpl( isMutateS, isXorNext, isSalted );
	}

	/**
	 * Creates a certificate head for an unsecured certificate.
	 * 
	 * @return The accordingly created certificate head.
	 */
	protected static String toCertificateHead() {
		return toCertificateHead( null );
	}

	/**
	 * Creates a certificate head from the provided {@link ChaosOptions}
	 * instance for a password protected certificate.
	 * 
	 * @param aChaosOptions The {@link ChaosOptions} from which to create the
	 *        certificate head.
	 * 
	 * @return The accordingly created certificate head.
	 */
	protected static String toCertificateHead( ChaosOptions aChaosOptions ) {
		final StringBuilder theBuffer = new StringBuilder();
		theBuffer.append( CERTIFICATE_BEGIN_TAG_PREFIX ).append( ' ' );
		if ( aChaosOptions != null ) {
			theBuffer.append( CERTIFICATE_ENCRYPTED ).append( ' ' );
			if ( aChaosOptions.isSalted() ) {
				theBuffer.append( CERTIFICATE_SALTED ).append( ' ' );
			}
			if ( aChaosOptions.isMutateS() ) {
				theBuffer.append( CERTIFICATE_MUTATE ).append( ' ' );
			}
			if ( aChaosOptions.isXorNext() ) {
				theBuffer.append( CERTIFICATE_XOR ).append( ' ' );
			}
		}
		theBuffer.append( CERTIFICATE_BEGIN_TAG_SUFFIX ).append( '\n' );
		return theBuffer.toString();
	}

	/**
	 * Creates a certificate tail for an unsecured certificate.
	 * 
	 * @return The accordingly created certificate tail.
	 */
	protected static String toCertificateTail() {
		return toCertificateTail( null );
	}

	/**
	 * Creates a certificate tail from the provided {@link ChaosOptions}
	 * instance for a password protected certificate.
	 * 
	 * @param aChaosOptions The {@link ChaosOptions} from which to create the
	 *        certificate tail.
	 * 
	 * @return The accordingly created certificate tail.
	 */
	protected static String toCertificateTail( ChaosOptions aChaosOptions ) {
		final StringBuilder theBuffer = new StringBuilder();
		theBuffer.append( CERTIFICATE_END_TAG_PREFIX ).append( ' ' );
		if ( aChaosOptions != null ) {
			theBuffer.append( CERTIFICATE_ENCRYPTED ).append( ' ' );
			if ( aChaosOptions.isSalted() ) {
				theBuffer.append( CERTIFICATE_SALTED ).append( ' ' );
			}
			if ( aChaosOptions.isMutateS() ) {
				theBuffer.append( CERTIFICATE_MUTATE ).append( ' ' );
			}
			if ( aChaosOptions.isXorNext() ) {
				theBuffer.append( CERTIFICATE_XOR ).append( ' ' );
			}
		}
		theBuffer.append( CERTIFICATE_END_TAG_SUFFIX );
		return theBuffer.toString();
	}

	// /////////////////////////////////////////////////////////////////////////
	// HELPER:
	// /////////////////////////////////////////////////////////////////////////

	private static String trim( String aString ) {
		while ( aString.startsWith( " " ) ) {
			aString = aString.substring( 1 );
		}
		while ( aString.endsWith( " " ) ) {
			aString = aString.substring( 0, aString.length() - 1 );
		}
		return aString;
	}
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy