1 package org.bouncycastle.crypto.digests;
2 
3 import org.bouncycastle.util.Arrays;
4 
5 /**
6  * Customizable SHAKE function.
7  */
8 public class CSHAKEDigest
9     extends SHAKEDigest
10 {
11     private static final byte[] padding = new byte[100];
12     private final byte[] diff;
13 
14     /**
15      * Base constructor.
16      *
17      * @param bitLength bit length of the underlying SHAKE function, 128 or 256.
18      * @param N         the function name string, note this is reserved for use by NIST. Avoid using it if not required.
19      * @param S         the customization string - available for local use.
20      */
CSHAKEDigest(int bitLength, byte[] N, byte[] S)21     public CSHAKEDigest(int bitLength, byte[] N, byte[] S)
22     {
23         super(bitLength);
24 
25         if ((N == null || N.length == 0) && (S == null || S.length == 0))
26         {
27             diff = null;
28         }
29         else
30         {
31             diff = Arrays.concatenate(XofUtils.leftEncode(rate / 8), encodeString(N), encodeString(S));
32             diffPadAndAbsorb();
33         }
34     }
35 
CSHAKEDigest(CSHAKEDigest source)36     CSHAKEDigest(CSHAKEDigest source)
37     {
38         super(source);
39 
40         this.diff = Arrays.clone(source.diff);
41     }
42 
43     // bytepad in SP 800-185
diffPadAndAbsorb()44     private void diffPadAndAbsorb()
45     {
46         int blockSize = rate / 8;
47         absorb(diff, 0, diff.length);
48 
49         int delta = diff.length % blockSize;
50 
51         // only add padding if needed
52         if (delta != 0)
53         {
54             int required = blockSize - delta;
55 
56             while (required > padding.length)
57             {
58                 absorb(padding, 0, padding.length);
59                 required -= padding.length;
60             }
61 
62             absorb(padding, 0, required);
63         }
64     }
65 
encodeString(byte[] str)66     private byte[] encodeString(byte[] str)
67     {
68         if (str == null || str.length == 0)
69         {
70             return XofUtils.leftEncode(0);
71         }
72 
73         return Arrays.concatenate(XofUtils.leftEncode(str.length * 8L), str);
74     }
75 
getAlgorithmName()76     public String getAlgorithmName()
77     {
78         return "CSHAKE" + fixedOutputLength;
79     }
80 
doOutput(byte[] out, int outOff, int outLen)81     public int doOutput(byte[] out, int outOff, int outLen)
82     {
83         if (diff != null)
84         {
85             if (!squeezing)
86             {
87                 absorbBits(0x00, 2);
88             }
89 
90             squeeze(out, outOff, ((long)outLen) * 8);
91 
92             return outLen;
93         }
94         else
95         {
96             return super.doOutput(out, outOff, outLen);
97         }
98     }
99 
reset()100     public void reset()
101     {
102         super.reset();
103 
104         if (diff != null)
105         {
106             diffPadAndAbsorb();
107         }
108     }
109 }
110