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