Skip to content

Commit 8d96e43

Browse files
committed
added constraints, fragmented aad support, fragmented data support, key size check and additional tests (exceptions, split data).
1 parent 46d203b commit 8d96e43

3 files changed

Lines changed: 211 additions & 57 deletions

File tree

core/src/main/java/org/bouncycastle/crypto/modes/Grain128AEADCipher.java renamed to core/src/main/java/org/bouncycastle/crypto/engines/Grain128AEADEngine.java

Lines changed: 109 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1-
package org.bouncycastle.crypto.modes;
1+
package org.bouncycastle.crypto.engines;
2+
3+
import java.io.ByteArrayOutputStream;
24

35
import org.bouncycastle.crypto.CipherParameters;
6+
import org.bouncycastle.crypto.CryptoServicesRegistrar;
47
import org.bouncycastle.crypto.DataLengthException;
58
import org.bouncycastle.crypto.InvalidCipherTextException;
69
import org.bouncycastle.crypto.OutputLengthException;
10+
import org.bouncycastle.crypto.constraints.DefaultServiceProperties;
11+
import org.bouncycastle.crypto.modes.AEADCipher;
712
import org.bouncycastle.crypto.params.KeyParameter;
813
import org.bouncycastle.crypto.params.ParametersWithIV;
14+
import org.bouncycastle.util.Arrays;
915

10-
public class Grain128AEADCipher
16+
/**
17+
* Grain-128 AEAD, based on the current round 3 submission, https://grain-128aead.github.io/
18+
*/
19+
public class Grain128AEADEngine
1120
implements AEADCipher
1221
{
1322

@@ -29,6 +38,11 @@ public class Grain128AEADCipher
2938
private int output;
3039

3140
private boolean initialised = false;
41+
private boolean isEven = true; // zero treated as even
42+
private boolean aadFinished = false;
43+
private ErasableOutputStream aadData = new ErasableOutputStream();
44+
45+
private byte[] mac;
3246

3347
public String getAlgorithmName()
3448
{
@@ -53,7 +67,7 @@ public void init(boolean forEncryption, CipherParameters params)
5367
if (!(params instanceof ParametersWithIV))
5468
{
5569
throw new IllegalArgumentException(
56-
"Grain-128AEAD Init parameters must include an IV");
70+
"Grain-128AEAD init parameters must include an IV");
5771
}
5872

5973
ParametersWithIV ivParams = (ParametersWithIV)params;
@@ -63,30 +77,38 @@ public void init(boolean forEncryption, CipherParameters params)
6377
if (iv == null || iv.length != 12)
6478
{
6579
throw new IllegalArgumentException(
66-
"Grain-128AEAD requires exactly 12 bytes of IV");
80+
"Grain-128AEAD requires exactly 12 bytes of IV");
6781
}
6882

6983
if (!(ivParams.getParameters() instanceof KeyParameter))
7084
{
7185
throw new IllegalArgumentException(
72-
"Grain-128AEAD Init parameters must include a key");
86+
"Grain-128AEAD init parameters must include a key");
7387
}
7488

7589
KeyParameter key = (KeyParameter)ivParams.getParameters();
90+
byte[] keyBytes = key.getKey();
91+
if (keyBytes.length != 16)
92+
{
93+
throw new IllegalArgumentException(
94+
"Grain-128AEAD key must be 128 bits long");
95+
}
7696

97+
CryptoServicesRegistrar.checkConstraints(new DefaultServiceProperties(
98+
this.getAlgorithmName(), 128, params, Utils.getPurpose(forEncryption)));
99+
77100
/**
78101
* Initialize variables.
79102
*/
80-
workingIV = new byte[key.getKey().length];
81-
workingKey = new byte[key.getKey().length];
103+
workingIV = new byte[16];
104+
workingKey = new byte[16];
82105
lfsr = new int[STATE_SIZE];
83106
nfsr = new int[STATE_SIZE];
84107
authAcc = new int[2];
85108
authSr = new int[2];
86109

87-
88110
System.arraycopy(iv, 0, workingIV, 0, iv.length);
89-
System.arraycopy(key.getKey(), 0, workingKey, 0, key.getKey().length);
111+
System.arraycopy(keyBytes, 0, workingKey, 0, keyBytes.length);
90112

91113
reset();
92114
}
@@ -280,6 +302,12 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output,
280302
+ " not initialised");
281303
}
282304

305+
if (!aadFinished)
306+
{
307+
doProcessAADBytes(aadData.getBuf(), 0, aadData.size());
308+
aadFinished = true;
309+
}
310+
283311
if ((inOff + len) > input.length)
284312
{
285313
throw new DataLengthException("input buffer too short");
@@ -295,6 +323,10 @@ public int processBytes(byte[] input, int inOff, int len, byte[] output,
295323

296324
public void reset()
297325
{
326+
this.isEven = true;
327+
this.mac = null;
328+
this.aadData.reset();
329+
this.aadFinished = false;
298330
setKey(workingKey, workingIV);
299331
initGrain();
300332
}
@@ -316,65 +348,49 @@ private byte[] getKeyStream(byte[] input, int inOff, int len, byte[] ciphertext,
316348
output = getOutput();
317349
nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1);
318350
lfsr = shift(lfsr, (getOutputLFSR()) & 1);
319-
if ((j & 1) == 0)
351+
if (isEven)
320352
{
321353
cc |= (((plaintext[mCnt >> 3] >>> (7 - (mCnt & 7))) & 1) ^ output) << (cCnt & 7);
322354
mCnt++;
323355
cCnt++;
356+
isEven = false;
324357
}
325358
else
326359
{
327-
328360
if ((plaintext[acCnt >> 3] & (1 << (7 - (acCnt & 7)))) != 0)
329361
{
330362
accumulate();
331363
}
332364
authShift(output);
333365
acCnt++;
366+
isEven = true;
334367
}
335368
}
336369
ciphertext[outOff + i] = cc;
337370
}
338-
output = getOutput();
339-
nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1);
340-
lfsr = shift(lfsr, (getOutputLFSR()) & 1);
341-
accumulate();
342-
cCnt = len + outOff;//acc_idx
343-
for (int i = 0; i < 2; ++i)
344-
{
345-
for (int j = 0; j < 4; ++j)
346-
{
347-
ciphertext[cCnt] = (byte)((authAcc[i] >>> (j << 3)) & 0xff);
348-
cCnt++;
349-
}
350-
}
351371

352372
return ciphertext;
353373
}
354374

355-
356-
public byte returnByte(byte input)
375+
public void processAADByte(byte in)
357376
{
358-
if (!initialised)
377+
if (aadFinished)
359378
{
360-
throw new IllegalStateException(getAlgorithmName()
361-
+ " not initialised");
379+
throw new IllegalStateException("associated data must be added before plaintext/ciphertext");
362380
}
363-
byte[] plaintext = new byte[1];
364-
plaintext[0] = input;
365-
byte[] ciphertext = new byte[1];
366-
return getKeyStream(plaintext, 0, 1, ciphertext, 0)[0];
381+
aadData.write(in);
367382
}
368383

369-
370-
@Override
371-
public void processAADByte(byte in)
384+
public void processAADBytes(byte[] input, int inOff, int len)
372385
{
373-
386+
if (aadFinished)
387+
{
388+
throw new IllegalStateException("associated data must be added before plaintext/ciphertext");
389+
}
390+
aadData.write(input, inOff, len);
374391
}
375392

376-
@Override
377-
public void processAADBytes(byte[] input, int inOff, int len)
393+
private void doProcessAADBytes(byte[] input, int inOff, int len)
378394
{
379395
byte[] ader;
380396
int aderlen;
@@ -423,7 +439,6 @@ public void processAADBytes(byte[] input, int inOff, int len)
423439
}
424440
}
425441
}
426-
427442
}
428443

429444
private void accumulate()
@@ -438,36 +453,62 @@ private void authShift(int val)
438453
authSr[1] = (authSr[1] >>> 1) | (val << 31);
439454
}
440455

441-
@Override
442456
public int processByte(byte input, byte[] output, int outOff)
443457
throws DataLengthException
444458
{
445459
return processBytes(new byte[]{input}, 0, 1, output, outOff);
446460
}
447461

448-
@Override
449462
public int doFinal(byte[] out, int outOff)
450463
throws IllegalStateException, InvalidCipherTextException
451464
{
452-
return 0;
465+
if (!aadFinished)
466+
{
467+
doProcessAADBytes(aadData.getBuf(), 0, aadData.size());
468+
aadFinished = true;
469+
}
470+
471+
this.mac = new byte[8];
472+
473+
output = getOutput();
474+
nfsr = shift(nfsr, (getOutputNFSR() ^ lfsr[0]) & 1);
475+
lfsr = shift(lfsr, (getOutputLFSR()) & 1);
476+
accumulate();
477+
478+
int cCnt = 0;
479+
for (int i = 0; i < 2; ++i)
480+
{
481+
for (int j = 0; j < 4; ++j)
482+
{
483+
mac[cCnt++] = (byte)((authAcc[i] >>> (j << 3)) & 0xff);
484+
}
485+
}
486+
487+
System.arraycopy(mac, 0, out, outOff, mac.length);
488+
489+
try
490+
{
491+
return mac.length;
492+
}
493+
finally
494+
{
495+
reset();
496+
}
453497
}
454498

455-
@Override
456499
public byte[] getMac()
457500
{
458-
return new byte[0];
501+
return mac;
459502
}
460503

461-
@Override
462504
public int getUpdateOutputSize(int len)
463505
{
464-
return 0;
506+
return len;
465507
}
466508

467-
@Override
468509
public int getOutputSize(int len)
469510
{
470-
//the last 8 bits are from AD
511+
//the last 8 bytes are from AD
471512
return len + 8;
472513
}
473514

@@ -478,4 +519,23 @@ private int reverseByte(int x)
478519
x = (((x & 0x0f) << 4) | ((x & (~0x0f)) >>> 4)) & 0xFF;
479520
return x;
480521
}
522+
523+
private static final class ErasableOutputStream
524+
extends ByteArrayOutputStream
525+
{
526+
public ErasableOutputStream()
527+
{
528+
}
529+
530+
public byte[] getBuf()
531+
{
532+
return buf;
533+
}
534+
535+
public void erase()
536+
{
537+
Arrays.fill(this.buf, (byte)0);
538+
reset();
539+
}
540+
}
481541
}

0 commit comments

Comments
 (0)