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