1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.github.tonywasher.joceanus.gordianknot.impl.ext.engines;
18
19 import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianBlake2Base;
20 import io.github.tonywasher.joceanus.gordianknot.impl.ext.digests.GordianBlake2Xof;
21 import io.github.tonywasher.joceanus.gordianknot.impl.ext.params.GordianBlake2Parameters.GordianBlake2ParametersBuilder;
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
32
33 public class GordianBlake2XEngine
34 implements StreamCipher, Memoable {
35
36
37
38 private int theIndex;
39
40
41
42
43 private final byte[] theKeyStream;
44
45
46
47
48 private final GordianBlake2Xof theBlake2X;
49
50
51
52
53 private GordianBlake2Xof theResetState;
54
55
56
57
58
59
60 public GordianBlake2XEngine(final GordianBlake2Base pDigest) {
61 theBlake2X = new GordianBlake2Xof(pDigest);
62 theKeyStream = new byte[pDigest.getDigestSize()];
63 }
64
65
66
67
68
69
70 private GordianBlake2XEngine(final GordianBlake2XEngine pSource) {
71 theBlake2X = new GordianBlake2Xof(pSource.theBlake2X);
72 theKeyStream = new byte[theBlake2X.getByteLength() >> 1];
73 reset(pSource);
74 }
75
76
77
78
79
80
81
82
83 public void init(final boolean forEncryption,
84 final CipherParameters params) {
85
86
87
88
89
90
91 CipherParameters myParams = params;
92 byte[] newKey = null;
93 byte[] newIV = null;
94 if ((myParams instanceof ParametersWithIV ivParams)) {
95 newIV = ivParams.getIV();
96 myParams = ivParams.getParameters();
97 }
98 if (myParams instanceof KeyParameter keyParam) {
99 newKey = keyParam.getKey();
100 }
101 if (newKey == null || newIV == null) {
102 throw new IllegalArgumentException("A key and IV must be provided");
103 }
104
105
106 final GordianBlake2ParametersBuilder myBuilder = new GordianBlake2ParametersBuilder()
107 .setKey(newKey)
108 .setSalt(newIV)
109 .setMaxOutputLen(-1);
110 theBlake2X.init(myBuilder.build());
111
112
113 theResetState = theBlake2X.copy();
114
115
116 theIndex = 0;
117 makeStreamBlock();
118 }
119
120 @Override
121 public String getAlgorithmName() {
122 return theBlake2X.getAlgorithmName();
123 }
124
125 @Override
126 public int processBytes(final byte[] in,
127 final int inOff,
128 final int len,
129 final byte[] out,
130 final int outOff) {
131
132 if (theResetState == null) {
133 throw new IllegalStateException(getAlgorithmName() + " not initialised");
134 }
135 if ((inOff + len) > in.length) {
136 throw new DataLengthException("input buffer too short");
137 }
138 if ((outOff + len) > out.length) {
139 throw new OutputLengthException("output buffer too short");
140 }
141
142
143 for (int i = 0; i < len; i++) {
144 out[i + outOff] = returnByte(in[i + inOff]);
145 }
146 return len;
147 }
148
149 @Override
150 public void reset() {
151 if (theResetState != null) {
152 theBlake2X.reset(theResetState);
153 theIndex = 0;
154 makeStreamBlock();
155 }
156 }
157
158 @Override
159 public byte returnByte(final byte in) {
160 final byte out = (byte) (theKeyStream[theIndex] ^ in);
161 theIndex = (theIndex + 1) % theKeyStream.length;
162
163 if (theIndex == 0) {
164 makeStreamBlock();
165 }
166 return out;
167 }
168
169
170
171
172 private void makeStreamBlock() {
173
174 theBlake2X.doOutput(theKeyStream, 0, theKeyStream.length);
175 }
176
177 @Override
178 public GordianBlake2XEngine copy() {
179 return new GordianBlake2XEngine(this);
180 }
181
182 @Override
183 public void reset(final Memoable pState) {
184 final GordianBlake2XEngine e = (GordianBlake2XEngine) pState;
185 if (theKeyStream.length != e.theKeyStream.length) {
186 throw new IllegalArgumentException();
187 }
188 theBlake2X.reset(e.theBlake2X);
189 System.arraycopy(e.theKeyStream, 0, theKeyStream, 0, theKeyStream.length);
190 theIndex = e.theIndex;
191 theResetState = e.theResetState;
192 }
193 }
194