Skip to content

Commit 0d55ad5

Browse files
committed
added SavableDigest support to SHA-3
1 parent 1119427 commit 0d55ad5

File tree

2 files changed

+122
-5
lines changed

2 files changed

+122
-5
lines changed

core/src/main/java/org/bouncycastle/crypto/digests/SHA3Digest.java

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package org.bouncycastle.crypto.digests;
22

3-
43
import org.bouncycastle.crypto.CryptoServicePurpose;
4+
import org.bouncycastle.crypto.CryptoServicesRegistrar;
5+
import org.bouncycastle.crypto.SavableDigest;
6+
import org.bouncycastle.util.Memoable;
7+
import org.bouncycastle.util.Pack;
58

69
/**
710
* implementation of SHA-3 based on following KeccakNISTInterface.c from https://keccak.noekeon.org/
@@ -10,6 +13,7 @@
1013
*/
1114
public class SHA3Digest
1215
extends KeccakDigest
16+
implements SavableDigest
1317
{
1418
private static int checkBitLength(int bitLength)
1519
{
@@ -45,11 +49,51 @@ public SHA3Digest(int bitLength, CryptoServicePurpose purpose)
4549
super(checkBitLength(bitLength), purpose);
4650
}
4751

52+
public SHA3Digest(byte[] encodedState)
53+
{
54+
super(getCryptoServicePurpose(encodedState[encodedState.length - 1]));
55+
56+
Pack.bigEndianToLong(encodedState, 0, state, 0, state.length);
57+
int encOff = state.length * 8;
58+
System.arraycopy(encodedState, encOff, dataQueue, 0, dataQueue.length);
59+
encOff += dataQueue.length;
60+
rate = Pack.bigEndianToInt(encodedState, encOff);
61+
encOff += 4;
62+
bitsInQueue = Pack.bigEndianToInt(encodedState, encOff);
63+
encOff += 4;
64+
fixedOutputLength = Pack.bigEndianToInt(encodedState, encOff);
65+
encOff += 4;
66+
squeezing = encodedState[encOff] != 0;
67+
}
68+
69+
private static CryptoServicePurpose getCryptoServicePurpose(byte b)
70+
{
71+
CryptoServicePurpose[] values = CryptoServicePurpose.values();
72+
return values[b];
73+
}
74+
4875
public SHA3Digest(SHA3Digest source)
4976
{
5077
super(source);
5178
}
5279

80+
private void copyIn(SHA3Digest source)
81+
{
82+
if (this.purpose != source.purpose)
83+
{
84+
throw new IllegalArgumentException("attempt to copy digest of different purpose");
85+
}
86+
87+
System.arraycopy(source.state, 0, this.state, 0, source.state.length);
88+
System.arraycopy(source.dataQueue, 0, this.dataQueue, 0, source.dataQueue.length);
89+
this.rate = source.rate;
90+
this.bitsInQueue = source.bitsInQueue;
91+
this.fixedOutputLength = source.fixedOutputLength;
92+
this.squeezing = source.squeezing;
93+
94+
CryptoServicesRegistrar.checkConstraints(cryptoServiceProperties());
95+
}
96+
5397
public String getAlgorithmName()
5498
{
5599
return "SHA3-" + fixedOutputLength;
@@ -84,4 +128,41 @@ protected int doFinal(byte[] out, int outOff, byte partialByte, int partialBits)
84128

85129
return super.doFinal(out, outOff, (byte)finalInput, finalBits);
86130
}
131+
132+
public byte[] getEncodedState()
133+
{
134+
byte[] encState = new byte[state.length * 8 + dataQueue.length + 12 + 2];
135+
136+
for (int i = 0; i != state.length; i++)
137+
{
138+
Pack.longToBigEndian(state[i], encState, i * 8);
139+
}
140+
141+
int sOff = state.length * 8;
142+
System.arraycopy(dataQueue, 0, encState, sOff, dataQueue.length);
143+
144+
sOff += dataQueue.length;
145+
Pack.intToBigEndian(rate, encState, sOff);
146+
sOff += 4;
147+
Pack.intToBigEndian(bitsInQueue, encState, sOff);
148+
sOff += 4;
149+
Pack.intToBigEndian(fixedOutputLength, encState, sOff);
150+
sOff += 4;
151+
encState[sOff++] = squeezing ? (byte)1 : (byte)0;
152+
encState[sOff] = (byte)purpose.ordinal();
153+
154+
return encState;
155+
}
156+
157+
public Memoable copy()
158+
{
159+
return new SHA3Digest(this);
160+
}
161+
162+
public void reset(Memoable other)
163+
{
164+
SHA3Digest d = (SHA3Digest)other;
165+
166+
copyIn(d);
167+
}
87168
}

core/src/test/java/org/bouncycastle/crypto/test/SHA3DigestTest.java

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,34 @@
77
import java.util.ArrayList;
88
import java.util.List;
99

10+
import org.bouncycastle.crypto.Digest;
1011
import org.bouncycastle.crypto.digests.SHA3Digest;
1112
import org.bouncycastle.test.TestResourceFinder;
1213
import org.bouncycastle.util.Arrays;
1314
import org.bouncycastle.util.encoders.Hex;
14-
import org.bouncycastle.util.test.SimpleTest;
1515

1616
/**
1717
* SHA3 Digest Test
1818
*/
1919
public class SHA3DigestTest
20-
extends SimpleTest
20+
extends DigestTest
2121
{
22+
private static String[] messages =
23+
{
24+
"",
25+
"a",
26+
"abc",
27+
"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"
28+
};
29+
30+
private static String[] digests =
31+
{
32+
"a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a",
33+
"80084bf2fba02475726feb2cab2d8215eab14bc6bdd8bfb2c8151257032ecd8b",
34+
"3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532",
35+
"41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376"
36+
};
37+
2238
static class MySHA3Digest extends SHA3Digest
2339
{
2440
MySHA3Digest(int bitLength)
@@ -34,16 +50,36 @@ int myDoFinal(byte[] out, int outOff, byte partialByte, int partialBits)
3450

3551
SHA3DigestTest()
3652
{
53+
super(new SHA3Digest(), messages, digests);
3754
}
3855

3956
public String getName()
4057
{
4158
return "SHA-3";
4259
}
4360

44-
public void performTest() throws Exception
61+
public void performTest()
62+
{
63+
super.performTest();
64+
65+
try
66+
{
67+
testVectors();
68+
}
69+
catch (Exception e)
70+
{
71+
throw new IllegalStateException(e.toString(), e);
72+
}
73+
}
74+
75+
protected Digest cloneDigest(Digest digest)
76+
{
77+
return new SHA3Digest((SHA3Digest)digest);
78+
}
79+
80+
protected Digest cloneDigest(byte[] encodedState)
4581
{
46-
testVectors();
82+
return new SHA3Digest(encodedState);
4783
}
4884

4985
public void testVectors() throws Exception

0 commit comments

Comments
 (0)