Skip to content

Commit 6ca5a08

Browse files
committed
Add support for SHAKE128-256 and SHAKE256-512 to DigestUtils and
`MessageDigestAlgorithms` on Java 25 and up
1 parent c23a872 commit 6ca5a08

File tree

9 files changed

+782
-104
lines changed

9 files changed

+782
-104
lines changed

pom.xml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -298,10 +298,27 @@ limitations under the License.
298298
</properties>
299299
</profile>
300300
<profile>
301-
<!-- Java 9 and up -->
302-
<id>java-9-up</id>
301+
<!-- Java 9 through 24 -->
302+
<id>java-9-24</id>
303303
<activation>
304-
<jdk>[9,)</jdk>
304+
<jdk>[9,24]</jdk>
305+
</activation>
306+
<properties>
307+
<!-- JaCoCo: Don't make code coverage worse than: -->
308+
<commons.jacoco.haltOnFailure>true</commons.jacoco.haltOnFailure>
309+
<commons.jacoco.classRatio>0.96</commons.jacoco.classRatio>
310+
<commons.jacoco.instructionRatio>0.97</commons.jacoco.instructionRatio>
311+
<commons.jacoco.methodRatio>0.92</commons.jacoco.methodRatio>
312+
<commons.jacoco.branchRatio>0.92</commons.jacoco.branchRatio>
313+
<commons.jacoco.complexityRatio>0.89</commons.jacoco.complexityRatio>
314+
<commons.jacoco.lineRatio>0.95</commons.jacoco.lineRatio>
315+
</properties>
316+
</profile>
317+
<profile>
318+
<!-- Java 25 and up -->
319+
<id>java-25-up</id>
320+
<activation>
321+
<jdk>[25,)</jdk>
305322
</activation>
306323
<properties>
307324
<!-- JaCoCo: Don't make code coverage worse than: -->

src/changes/changes.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ The <action> type attribute can be add,update,fix,remove.
5252
<!-- ADD -->
5353
<action type="add" dev="ggregory" due-to="Fredrik Kjellberg, Gary Gregory">Add org.apache.commons.codec.digest.CRC16.</action>
5454
<action type="add" dev="ggregory" due-to="Gary Gregory">Add builders to org.apache.commons.codec.digest streams and deprecate some old constructors.</action>
55-
<action type="add" dev="ggregory" due-to="Gary Gregory">Add DecoderException.DecoderException(String, Object...).</action>
55+
<action type="add" dev="ggregory" due-to="Gary Gregory">Add support for SHAKE128-256 and SHAKE256-512 to `DigestUtils` and `MessageDigestAlgorithms` on Java 25 and up.</action>
5656
<!-- UPDATE -->
5757
<action type="update" dev="ggregory" due-to="Gary Gregory, Dependabot">Bump org.apache.commons:commons-parent from 85 to 88.</action>
5858
<action type="update" dev="ggregory" due-to="Gary Gregory">[test] Bump org.apache.commons:commons-lang3 from 3.18.0 to 3.19.0.</action>

src/main/java/org/apache/commons/codec/DecoderException.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ public DecoderException(final String message) {
5858
* @param args the format arguments to use.
5959
*
6060
* @see String#format(String, Object...)
61-
* @since 1.20
61+
* @since 1.20.0
6262
*/
6363
public DecoderException(final String message, Object... args) {
6464
super(String.format(message, args));

src/main/java/org/apache/commons/codec/binary/Hex.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,8 @@ public static byte[] decodeHex(final char[] data) throws DecoderException {
9090
*/
9191
public static int decodeHex(final char[] data, final byte[] out, final int outOffset) throws DecoderException {
9292
final int len = data.length;
93-
if ((len & 0x01) != 0) {
94-
throw new DecoderException("Odd number of characters.");
93+
if ((len & 1) != 0) {
94+
throw new DecoderException("Odd number of characters %,d.", len);
9595
}
9696
final int outLen = len >> 1;
9797
if (out.length - outOffset < outLen) {
@@ -365,7 +365,7 @@ private static byte[] toByteArray(final ByteBuffer byteBuffer) {
365365
protected static int toDigit(final char ch, final int index) throws DecoderException {
366366
final int digit = Character.digit(ch, 16);
367367
if (digit == -1) {
368-
throw new DecoderException("Illegal hexadecimal character " + ch + " at index " + index);
368+
throw new DecoderException("Illegal hexadecimal character 0x%02X at index %,d.", ch & 0xFF, index);
369369
}
370370
return digit;
371371
}

src/main/java/org/apache/commons/codec/digest/DigestUtils.java

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,38 @@ public static MessageDigest getShaDigest() {
341341
return getSha1Digest();
342342
}
343343

344+
/**
345+
* Gets an SHAKE128_256 digest.
346+
*
347+
* @return An SHAKE128_256 digest instance.
348+
* @throws IllegalArgumentException when a {@link NoSuchAlgorithmException} is caught, which should not happen on Oracle Java 25 and greater.
349+
* @see MessageDigestAlgorithms#SHAKE128_256
350+
* @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/security/standard-names.html#messagedigest-algorithms"> Java 25 Cryptography
351+
* Architecture Standard Algorithm Name Documentation</a>
352+
* @see <a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf">FIPS PUB 202 - SHA-3 Standard: Permutation-Based Hash and Extendable-Output
353+
* Functions</a>
354+
* @since 1.20.0
355+
*/
356+
public static MessageDigest getShake128_256Digest() {
357+
return getDigest(MessageDigestAlgorithms.SHAKE128_256);
358+
}
359+
360+
/**
361+
* Gets an SHAKE128_512 digest.
362+
*
363+
* @return An SHAKE128_512 digest instance.
364+
* @throws IllegalArgumentException when a {@link NoSuchAlgorithmException} is caught, which should not happen on Oracle Java 25 and greater.
365+
* @see MessageDigestAlgorithms#SHAKE256_512
366+
* @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/security/standard-names.html#messagedigest-algorithms"> Java 25 Cryptography
367+
* Architecture Standard Algorithm Name Documentation</a>
368+
* @see <a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf">FIPS PUB 202 - SHA-3 Standard: Permutation-Based Hash and Extendable-Output
369+
* Functions</a>
370+
* @since 1.20.0
371+
*/
372+
public static MessageDigest getShake256_512Digest() {
373+
return getDigest(MessageDigestAlgorithms.SHAKE256_512);
374+
}
375+
344376
/**
345377
* Test whether the algorithm is supported.
346378
*
@@ -1239,6 +1271,145 @@ public static String shaHex(final String data) {
12391271
return sha1Hex(data);
12401272
}
12411273

1274+
/**
1275+
* Calculates the SHAKE128-256 digest and returns the value as a {@code byte[]}.
1276+
* <p>
1277+
* SHAKE128-256 produces a 256 bit digest.
1278+
* </p>
1279+
*
1280+
* @param data Data to digest.
1281+
* @return A 256 bit SHAKE128-256 digest.
1282+
* @since 1.20.0
1283+
*/
1284+
public static byte[] shake128_256(final byte[] data) {
1285+
return getShake128_256Digest().digest(data);
1286+
}
1287+
1288+
/**
1289+
* Calculates the SHAKE128-256 digest and returns the value as a {@code byte[]}.
1290+
*
1291+
* @param data Data to digest.
1292+
* @return A 256 bit SHAKE128-256 digest.
1293+
* @throws IOException On error reading from the stream.
1294+
* @since 1.20.0
1295+
*/
1296+
public static byte[] shake128_256(final InputStream data) throws IOException {
1297+
return digest(getShake128_256Digest(), data);
1298+
}
1299+
1300+
/**
1301+
* Calculates the SHAKE128-256 digest and returns the value as a {@code byte[]}.
1302+
*
1303+
* @param data Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)}
1304+
* @return SHAKE128-256 digest
1305+
* @since 1.20.0
1306+
*/
1307+
public static byte[] shake128_256(final String data) {
1308+
return shake128_256(StringUtils.getBytesUtf8(data));
1309+
}
1310+
1311+
/**
1312+
* Calculates the SHAKE128-256 digest and returns the value as a hexadecimal string.
1313+
*
1314+
* @param data Data to digest
1315+
* @return SHAKE128-256 digest as a hexadecimal string
1316+
* @since 1.20.0
1317+
*/
1318+
public static String shake128_256Hex(final byte[] data) {
1319+
return Hex.encodeHexString(shake128_256(data));
1320+
}
1321+
1322+
/**
1323+
* Calculates the SHAKE128-256 digest and returns the value as a hexadecimal string.
1324+
*
1325+
* @param data Data to digest
1326+
* @return SHAKE128-256 digest as a hexadecimal string
1327+
* @throws IOException On error reading from the stream
1328+
* @since 1.20.0
1329+
*/
1330+
public static String shake128_256Hex(final InputStream data) throws IOException {
1331+
return Hex.encodeHexString(shake128_256(data));
1332+
}
1333+
1334+
/**
1335+
* Calculates the SHAKE128-256 digest and returns the value as a hexadecimal string.
1336+
*
1337+
* @param data Data to digest
1338+
* @return SHAKE128-256 digest as a hexadecimal string
1339+
* @since 1.20.0
1340+
*/
1341+
public static String shake128_256Hex(final String data) {
1342+
return Hex.encodeHexString(shake128_256(data));
1343+
}
1344+
1345+
/**
1346+
* Calculates the SHAKE256-512 digest and returns the value as a {@code byte[]}.
1347+
*
1348+
* @param data Data to digest
1349+
* @return SHAKE256-512 digest
1350+
* @since 1.20.0
1351+
*/
1352+
public static byte[] shake256_512(final byte[] data) {
1353+
return getShake256_512Digest().digest(data);
1354+
}
1355+
1356+
/**
1357+
* Calculates the SHAKE256-512 digest and returns the value as a {@code byte[]}.
1358+
*
1359+
* @param data Data to digest
1360+
* @return SHAKE256-512 digest
1361+
* @throws IOException On error reading from the stream
1362+
* @since 1.20.0
1363+
*/
1364+
public static byte[] shake256_512(final InputStream data) throws IOException {
1365+
return digest(getShake256_512Digest(), data);
1366+
}
1367+
1368+
/**
1369+
* Calculates the SHAKE256-512 digest and returns the value as a {@code byte[]}.
1370+
*
1371+
* @param data Data to digest; converted to bytes using {@link StringUtils#getBytesUtf8(String)}
1372+
* @return SHAKE256-512 digest
1373+
* @since 1.20.0
1374+
*/
1375+
public static byte[] shake256_512(final String data) {
1376+
return shake256_512(StringUtils.getBytesUtf8(data));
1377+
}
1378+
1379+
/**
1380+
* Calculates the SHAKE256-512 digest and returns the value as a hexadecimal string.
1381+
*
1382+
* @param data Data to digest
1383+
* @return SHAKE256-512 digest as a hexadecimal string
1384+
* @since 1.20.0
1385+
*/
1386+
public static String shake256_512Hex(final byte[] data) {
1387+
return Hex.encodeHexString(shake256_512(data));
1388+
}
1389+
1390+
/**
1391+
* Calculates the SHAKE256-512 digest and returns the value as a hexadecimal string.
1392+
*
1393+
* @param data Data to digest
1394+
* @return SHAKE256-512 digest as a hexadecimal string
1395+
* @throws IOException On error reading from the stream
1396+
* @since 1.20.0
1397+
*/
1398+
public static String shake256_512Hex(final InputStream data) throws IOException {
1399+
return Hex.encodeHexString(shake256_512(data));
1400+
}
1401+
1402+
/**
1403+
* Calculates the SHAKE256-512 digest and returns the value as a hexadecimal string.
1404+
*
1405+
* @param data Data to digest
1406+
* @return SHAKE256-512 digest as a hexadecimal string
1407+
* @since 1.20.0
1408+
*/
1409+
public static String shake256_512Hex(final String data) {
1410+
return Hex.encodeHexString(shake256_512(data));
1411+
}
1412+
12421413
/**
12431414
* Updates the given {@link MessageDigest}.
12441415
*

src/main/java/org/apache/commons/codec/digest/MessageDigestAlgorithms.java

Lines changed: 37 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,10 @@
3636
* Java 11 Cryptography Architecture Standard Algorithm Name Documentation</a>
3737
* @see <a href="https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html#messagedigest-algorithms">
3838
* Java 17 Cryptography Architecture Standard Algorithm Name Documentation</a>
39-
* @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/security/standard-names.html#messagedigest-algorithms">
39+
* @see <a href="https://docs.oracle.com/en/java/javase/21/docs/specs/security/standard-names.html#messagedigest-algorithms">
4040
* Java 21 Cryptography Architecture Standard Algorithm Name Documentation</a>
41+
* @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/security/standard-names.html#messagedigest-algorithms">
42+
* Java 25 Cryptography Architecture Standard Algorithm Name Documentation</a>
4143
*
4244
* @see <a href="https://dx.doi.org/10.6028/NIST.FIPS.180-4">FIPS PUB 180-4</a>
4345
* @see <a href="https://dx.doi.org/10.6028/NIST.FIPS.202">FIPS PUB 202</a>
@@ -106,7 +108,7 @@ public class MessageDigestAlgorithms {
106108
public static final String SHA_512_256 = "SHA-512/256";
107109

108110
/**
109-
* The SHA3-224 hash algorithm defined in the FIPS PUB 202.
111+
* The SHA3-224 hash algorithm defined in the <a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf">FIPS PUB 202</a>.
110112
* <p>
111113
* Included starting in Oracle Java 9.
112114
* </p>
@@ -116,7 +118,7 @@ public class MessageDigestAlgorithms {
116118
public static final String SHA3_224 = "SHA3-224";
117119

118120
/**
119-
* The SHA3-256 hash algorithm defined in the FIPS PUB 202.
121+
* The SHA3-256 hash algorithm defined in the <a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf">FIPS PUB 202</a>.
120122
* <p>
121123
* Included starting in Oracle Java 9.
122124
* </p>
@@ -126,7 +128,7 @@ public class MessageDigestAlgorithms {
126128
public static final String SHA3_256 = "SHA3-256";
127129

128130
/**
129-
* The SHA3-384 hash algorithm defined in the FIPS PUB 202.
131+
* The SHA3-384 hash algorithm defined in the <a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf">FIPS PUB 202</a>.
130132
* <p>
131133
* Included starting in Oracle Java 9.
132134
* </p>
@@ -136,7 +138,7 @@ public class MessageDigestAlgorithms {
136138
public static final String SHA3_384 = "SHA3-384";
137139

138140
/**
139-
* The SHA3-512 hash algorithm defined in the FIPS PUB 202.
141+
* The SHA3-512 hash algorithm defined in the <a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf">FIPS PUB 202</a>.
140142
* <p>
141143
* Included starting in Oracle Java 9.
142144
* </p>
@@ -145,6 +147,34 @@ public class MessageDigestAlgorithms {
145147
*/
146148
public static final String SHA3_512 = "SHA3-512";
147149

150+
/**
151+
* The SHAKE128-256 hash algorithm defined in the <a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf">FIPS PUB 202</a>.
152+
* <p>
153+
* Included starting in Oracle Java 25.
154+
* </p>
155+
*
156+
* @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/security/standard-names.html#messagedigest-algorithms"> Java 25 Cryptography
157+
* Architecture Standard Algorithm Name Documentation</a>
158+
* @see <a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf">FIPS PUB 202 - SHA-3 Standard: Permutation-Based Hash and Extendable-Output
159+
* Functions</a>
160+
* @since 1.20.0
161+
*/
162+
public static final String SHAKE128_256 = "SHAKE128-256";
163+
164+
/**
165+
* The SHAKE128-512 hash algorithm defined in the <a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf">FIPS PUB 202</a>.
166+
* <p>
167+
* Included starting in Oracle Java 25.
168+
* </p>
169+
*
170+
* @see <a href="https://docs.oracle.com/en/java/javase/25/docs/specs/security/standard-names.html#messagedigest-algorithms"> Java 25 Cryptography
171+
* Architecture Standard Algorithm Name Documentation</a>
172+
* @see <a href="https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf">FIPS PUB 202 - SHA-3 Standard: Permutation-Based Hash and Extendable-Output
173+
* Functions</a>
174+
* @since 1.20.0
175+
*/
176+
public static final String SHAKE256_512 = "SHAKE256-512";
177+
148178
/**
149179
* Gets all constant values defined in this class.
150180
*
@@ -153,10 +183,8 @@ public class MessageDigestAlgorithms {
153183
*/
154184
public static String[] values() {
155185
// Do not use a constant array here as that can be changed externally by accident or design
156-
return new String[] {
157-
MD2, MD5, SHA_1, SHA_224, SHA_256, SHA_384,
158-
SHA_512, SHA_512_224, SHA_512_256, SHA3_224, SHA3_256, SHA3_384, SHA3_512
159-
};
186+
return new String[] { MD2, MD5, SHA_1, SHA_224, SHA_256, SHA_384, SHA_512, SHA_512_224, SHA_512_256, SHA3_224, SHA3_256, SHA3_384, SHA3_512,
187+
SHAKE128_256, SHAKE256_512 };
160188
}
161189

162190
private MessageDigestAlgorithms() {
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.commons.codec.digest;
19+
20+
class Binary {
21+
22+
private static final int RADIX = 2;
23+
24+
/**
25+
* Converts a string of 0s and 1s to a byte array.
26+
*
27+
* @param binary a string of 0s and 1s to a byte array.
28+
* @return a byte array
29+
*/
30+
static byte[] toByteArray(final String binary) {
31+
// Ensure the binary string length is a multiple of 8
32+
final int inLen = binary.length();
33+
if (inLen % Byte.SIZE != 0) {
34+
throw new IllegalArgumentException(String.format("Binary string length must be a multiple of %,d.", Byte.SIZE));
35+
}
36+
final byte[] byteArray = new byte[inLen / Byte.SIZE];
37+
for (int i = 0; i < byteArray.length; i += Byte.SIZE) {
38+
byteArray[i] = (byte) Integer.parseInt(binary.substring(i, i + Byte.SIZE), RADIX);
39+
}
40+
return byteArray;
41+
}
42+
}

0 commit comments

Comments
 (0)