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.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
31
32 public class GordianBlake3Engine
33 implements StreamCipher, Memoable {
34
35
36
37 private int theIndex;
38
39
40
41
42 private final byte[] theKeyStream;
43
44
45
46
47 private final GordianBlake3Digest theDigest;
48
49
50
51
52 private GordianBlake3Digest theResetState;
53
54
55
56
57 public GordianBlake3Engine() {
58 theDigest = new GordianBlake3Digest();
59 theKeyStream = new byte[theDigest.getDigestSize() << 1];
60 }
61
62
63
64
65
66
67 private GordianBlake3Engine(final GordianBlake3Engine pSource) {
68 this();
69 reset(pSource);
70 }
71
72
73
74
75
76
77
78
79 public void init(final boolean forEncryption,
80 final CipherParameters params) {
81
82
83
84
85
86
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
104 theDigest.init(GordianBlake3Parameters.key(newKey));
105 theDigest.update(newIV, 0, newIV.length);
106
107
108 theResetState = theDigest.copy();
109
110
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
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
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
166
167 private void makeStreamBlock() {
168
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 }