1- package org .bouncycastle .crypto .modes ;
1+ package org .bouncycastle .crypto .engines ;
2+
3+ import java .io .ByteArrayOutputStream ;
24
35import org .bouncycastle .crypto .CipherParameters ;
6+ import org .bouncycastle .crypto .CryptoServicesRegistrar ;
47import org .bouncycastle .crypto .DataLengthException ;
58import org .bouncycastle .crypto .InvalidCipherTextException ;
69import org .bouncycastle .crypto .OutputLengthException ;
10+ import org .bouncycastle .crypto .constraints .DefaultServiceProperties ;
11+ import org .bouncycastle .crypto .modes .AEADCipher ;
712import org .bouncycastle .crypto .params .KeyParameter ;
813import 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