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