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 io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianSkeinBase;
20  import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianSkeinXof;
21  import io.github.tonywasher.joceanus.gordianknot.impl.ext.params.GordianSkeinParameters.GordianSkeinParametersBuilder;
22  import org.bouncycastle.crypto.CipherParameters;
23  import org.bouncycastle.crypto.DataLengthException;
24  import org.bouncycastle.crypto.OutputLengthException;
25  import org.bouncycastle.crypto.StreamCipher;
26  import org.bouncycastle.crypto.params.KeyParameter;
27  import org.bouncycastle.crypto.params.ParametersWithIV;
28  import org.bouncycastle.util.Memoable;
29  
30  /**
31   * SkeinXof used as a stream Cipher.
32   */
33  public class GordianSkeinXofEngine
34          implements StreamCipher, Memoable {
35      /**
36       * index of next byte in keyStream.
37       */
38      private int theIndex;
39  
40      /**
41       * Advanced stream.
42       */
43      private final byte[] theKeyStream;
44  
45      /**
46       * Underlying digest.
47       */
48      private final GordianSkeinXof theSkeinXof;
49  
50      /**
51       * Reset state.
52       */
53      private GordianSkeinXof theResetState;
54  
55      /**
56       * Constructor.
57       *
58       * @param pLength the underlying stateLength
59       */
60      public GordianSkeinXofEngine(final int pLength) {
61          final GordianSkeinBase myBase = new GordianSkeinBase(pLength, pLength);
62          theSkeinXof = new GordianSkeinXof(myBase);
63          theKeyStream = new byte[myBase.getBlockSize()];
64      }
65  
66      /**
67       * Constructor.
68       *
69       * @param pSource the source engine
70       */
71      private GordianSkeinXofEngine(final GordianSkeinXofEngine pSource) {
72          theSkeinXof = new GordianSkeinXof(pSource.theSkeinXof);
73          theKeyStream = new byte[theSkeinXof.getByteLength()];
74          reset(pSource);
75      }
76  
77      /**
78       * initialise a SkeinXof cipher.
79       *
80       * @param forEncryption whether or not we are for encryption.
81       * @param params        the parameters required to set up the cipher.
82       * @throws IllegalArgumentException if the params argument is inappropriate.
83       */
84      public void init(final boolean forEncryption,
85                       final CipherParameters params) {
86          /*
87           * SkeinXof encryption and decryption is completely symmetrical, so the 'forEncryption' is
88           * irrelevant. (Like 90% of stream ciphers)
89           */
90  
91          /* Determine parameters */
92          CipherParameters myParams = params;
93          byte[] newKey = null;
94          byte[] newIV = null;
95          if ((myParams instanceof ParametersWithIV ivParams)) {
96              newIV = ivParams.getIV();
97              myParams = ivParams.getParameters();
98          }
99          if (myParams instanceof KeyParameter keyParam) {
100             newKey = keyParam.getKey();
101         }
102         if (newKey == null || newIV == null) {
103             throw new IllegalArgumentException("A key and IV must be provided");
104         }
105 
106         /* Initialise engine and mark as initialised */
107         final GordianSkeinParametersBuilder myBuilder = new GordianSkeinParametersBuilder()
108                 .setKey(newKey)
109                 .setNonce(newIV)
110                 .setMaxOutputLen(-1);
111         theSkeinXof.init(myBuilder.build());
112 
113         /* Save reset state */
114         theResetState = theSkeinXof.copy();
115 
116         /* Initialise the stream block */
117         theIndex = 0;
118         makeStreamBlock();
119     }
120 
121     @Override
122     public String getAlgorithmName() {
123         return theSkeinXof.getAlgorithmName();
124     }
125 
126     @Override
127     public int processBytes(final byte[] in,
128                             final int inOff,
129                             final int len,
130                             final byte[] out,
131                             final int outOff) {
132         /* Check for errors */
133         if (theResetState == null) {
134             throw new IllegalStateException(getAlgorithmName() + " not initialised");
135         }
136         if ((inOff + len) > in.length) {
137             throw new DataLengthException("input buffer too short");
138         }
139         if ((outOff + len) > out.length) {
140             throw new OutputLengthException("output buffer too short");
141         }
142 
143         /* Loop through the input bytes */
144         for (int i = 0; i < len; i++) {
145             out[i + outOff] = returnByte(in[i + inOff]);
146         }
147         return len;
148     }
149 
150     @Override
151     public void reset() {
152         if (theResetState != null) {
153             theSkeinXof.reset(theResetState);
154             theIndex = 0;
155             makeStreamBlock();
156         }
157     }
158 
159     @Override
160     public byte returnByte(final byte in) {
161         final byte out = (byte) (theKeyStream[theIndex] ^ in);
162         theIndex = (theIndex + 1) % theKeyStream.length;
163 
164         if (theIndex == 0) {
165             makeStreamBlock();
166         }
167         return out;
168     }
169 
170     /**
171      * Generate keystream.
172      */
173     private void makeStreamBlock() {
174         /* Generate next output block */
175         theSkeinXof.doOutput(theKeyStream, 0, theKeyStream.length);
176     }
177 
178     @Override
179     public GordianSkeinXofEngine copy() {
180         return new GordianSkeinXofEngine(this);
181     }
182 
183     @Override
184     public void reset(final Memoable pState) {
185         final GordianSkeinXofEngine e = (GordianSkeinXofEngine) pState;
186         if (theKeyStream.length != e.theKeyStream.length) {
187             throw new IllegalArgumentException();
188         }
189         theSkeinXof.reset(e.theSkeinXof);
190         System.arraycopy(e.theKeyStream, 0, theKeyStream, 0, theKeyStream.length);
191         theIndex = e.theIndex;
192         theResetState = e.theResetState;
193     }
194 }
195