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)) {
96              final ParametersWithIV ivParams = (ParametersWithIV) myParams;
97              newIV = ivParams.getIV();
98              myParams = ivParams.getParameters();
99          }
100         if (myParams instanceof KeyParameter) {
101             final KeyParameter keyParam = (KeyParameter) myParams;
102             newKey = keyParam.getKey();
103         }
104         if (newKey == null || newIV == null) {
105             throw new IllegalArgumentException("A key and IV must be provided");
106         }
107 
108         /* Initialise engine and mark as initialised */
109         final GordianSkeinParametersBuilder myBuilder = new GordianSkeinParametersBuilder()
110                 .setKey(newKey)
111                 .setNonce(newIV)
112                 .setMaxOutputLen(-1);
113         theSkeinXof.init(myBuilder.build());
114 
115         /* Save reset state */
116         theResetState = theSkeinXof.copy();
117 
118         /* Initialise the stream block */
119         theIndex = 0;
120         makeStreamBlock();
121     }
122 
123     @Override
124     public String getAlgorithmName() {
125         return theSkeinXof.getAlgorithmName();
126     }
127 
128     @Override
129     public int processBytes(final byte[] in,
130                             final int inOff,
131                             final int len,
132                             final byte[] out,
133                             final int outOff) {
134         /* Check for errors */
135         if (theResetState == null) {
136             throw new IllegalStateException(getAlgorithmName() + " not initialised");
137         }
138         if ((inOff + len) > in.length) {
139             throw new DataLengthException("input buffer too short");
140         }
141         if ((outOff + len) > out.length) {
142             throw new OutputLengthException("output buffer too short");
143         }
144 
145         /* Loop through the input bytes */
146         for (int i = 0; i < len; i++) {
147             out[i + outOff] = returnByte(in[i + inOff]);
148         }
149         return len;
150     }
151 
152     @Override
153     public void reset() {
154         if (theResetState != null) {
155             theSkeinXof.reset(theResetState);
156             theIndex = 0;
157             makeStreamBlock();
158         }
159     }
160 
161     @Override
162     public byte returnByte(final byte in) {
163         final byte out = (byte) (theKeyStream[theIndex] ^ in);
164         theIndex = (theIndex + 1) % theKeyStream.length;
165 
166         if (theIndex == 0) {
167             makeStreamBlock();
168         }
169         return out;
170     }
171 
172     /**
173      * Generate keystream.
174      */
175     private void makeStreamBlock() {
176         /* Generate next output block */
177         theSkeinXof.doOutput(theKeyStream, 0, theKeyStream.length);
178     }
179 
180     @Override
181     public GordianSkeinXofEngine copy() {
182         return new GordianSkeinXofEngine(this);
183     }
184 
185     @Override
186     public void reset(final Memoable pState) {
187         final GordianSkeinXofEngine e = (GordianSkeinXofEngine) pState;
188         if (theKeyStream.length != e.theKeyStream.length) {
189             throw new IllegalArgumentException();
190         }
191         theSkeinXof.reset(e.theSkeinXof);
192         System.arraycopy(e.theKeyStream, 0, theKeyStream, 0, theKeyStream.length);
193         theIndex = e.theIndex;
194         theResetState = e.theResetState;
195     }
196 }
197