Skip to content

Commit 0031339

Browse files
authored
Merge pull request #776 from jeremylong/scratch/selective-hashing
2 parents ca1f222 + 6c105bd commit 0031339

File tree

1 file changed

+93
-18
lines changed

1 file changed

+93
-18
lines changed

src/main/java/org/cyclonedx/util/BomUtils.java

Lines changed: 93 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@
2222
import org.apache.commons.codec.digest.DigestUtils;
2323
import org.cyclonedx.Version;
2424
import org.cyclonedx.model.Hash;
25+
import org.cyclonedx.model.VersionFilter;
26+
2527
import java.io.BufferedInputStream;
2628
import java.io.File;
2729
import java.io.IOException;
@@ -32,6 +34,7 @@
3234
import java.net.URL;
3335
import java.nio.file.Files;
3436
import java.security.MessageDigest;
37+
import java.security.NoSuchAlgorithmException;
3538
import java.util.Arrays;
3639
import java.util.LinkedList;
3740
import java.util.List;
@@ -53,40 +56,112 @@ private BomUtils() {
5356
* @since 1.0.0
5457
*/
5558
public static List<Hash> calculateHashes(final File file, final Version schemaVersion) throws IOException {
59+
final List<Hash.Algorithm> algorithms = new LinkedList<>(Arrays.asList(
60+
Hash.Algorithm.MD5,
61+
Hash.Algorithm.SHA1,
62+
Hash.Algorithm.SHA_256,
63+
Hash.Algorithm.SHA_512,
64+
Hash.Algorithm.SHA3_256,
65+
Hash.Algorithm.SHA3_512
66+
));
67+
if (schemaVersion.getVersion() >= 1.2) {
68+
algorithms.add(Hash.Algorithm.SHA_384);
69+
algorithms.add(Hash.Algorithm.SHA3_384);
70+
}
71+
return calculateHashes(file, schemaVersion, algorithms);
72+
}
73+
74+
/**
75+
* Calculates the hashes of the specified file using only the specified algorithms.
76+
* @param file the File to calculate hashes on
77+
* @param schemaVersion enum denoting the schema version in use. Affects hash algorithm selection.
78+
* @param algorithms the list of algorithms to calculate hashes for
79+
* @return a List of Hash objects
80+
* @throws IOException an IOException
81+
* @throws IllegalArgumentException if an algorithm is not supported by the schema version
82+
* @since 9.2.0
83+
*/
84+
public static List<Hash> calculateHashes(final File file, final Version schemaVersion,
85+
final List<Hash.Algorithm> algorithms) throws IOException {
5686
if (file == null || !file.exists() || !file.canRead() || !file.isFile()) {
5787
return null;
5888
}
59-
long start = System.currentTimeMillis();
89+
if (algorithms == null || algorithms.isEmpty()) {
90+
return new LinkedList<>();
91+
}
92+
6093
final List<Hash> hashes = new LinkedList<>();
61-
final List<MessageDigest> digests = new LinkedList<>(Arrays.asList(DigestUtils.getMd5Digest()
62-
, DigestUtils.getSha1Digest()
63-
, DigestUtils.getSha256Digest() , DigestUtils.getSha512Digest()));
64-
if (schemaVersion.getVersion() >= 1.2) {
65-
digests.add(DigestUtils.getSha384Digest());
66-
try {
67-
digests.add(DigestUtils.getSha3_384Digest());
68-
} catch (Exception | NoSuchMethodError e) { /* Not available in Java 8 and only available in later versions of DigestUtils */ }
94+
final List<MessageDigest> digests = new LinkedList<>();
6995

96+
for (Hash.Algorithm algorithm : algorithms) {
97+
validateAlgorithmForVersion(algorithm, schemaVersion);
98+
MessageDigest digest = getDigestForAlgorithm(algorithm);
99+
if (digest != null) {
100+
digests.add(digest);
101+
}
70102
}
71-
try {
72-
digests.add(DigestUtils.getSha3_256Digest());
73-
} catch (Exception | NoSuchMethodError e) { /* Not available in Java 8 and only available in later versions of DigestUtils */ }
74-
try {
75-
digests.add(DigestUtils.getSha3_512Digest());
76-
} catch (Exception | NoSuchMethodError e) { /* Not available in Java 8 and only available in later versions of DigestUtils */ }
77103

78104
final int bufSize = 8192;
79-
try (InputStream fis = new BufferedInputStream(Files.newInputStream(file.toPath()),bufSize)) {
105+
try (InputStream fis = new BufferedInputStream(Files.newInputStream(file.toPath()), bufSize)) {
80106
final byte[] buf = new byte[bufSize];
81107
while (fis.available() > 0) {
82108
final int read = fis.read(buf);
83-
digests.stream().parallel().forEach(d-> d.update(buf, 0, read));
109+
digests.stream().parallel().forEach(d -> d.update(buf, 0, read));
84110
}
85111
}
86-
digests.stream().map(d->new Hash(toAlgorithm(d), Hex.encodeHexString(d.digest()))).forEach(hashes::add);
112+
digests.stream().map(d -> new Hash(toAlgorithm(d), Hex.encodeHexString(d.digest()))).forEach(hashes::add);
87113
return hashes;
88114
}
89115

116+
private static void validateAlgorithmForVersion(Hash.Algorithm algorithm, Version schemaVersion) {
117+
try {
118+
java.lang.reflect.Field field = Hash.Algorithm.class.getField(algorithm.name());
119+
VersionFilter versionFilter = field.getAnnotation(VersionFilter.class);
120+
if (versionFilter != null) {
121+
Version minVersion = versionFilter.value();
122+
if (schemaVersion.getVersion() < minVersion.getVersion()) {
123+
throw new IllegalArgumentException(
124+
"Algorithm " + algorithm.getSpec() + " is not supported in schema version " +
125+
schemaVersion + ". Minimum required version: " + minVersion);
126+
}
127+
}
128+
} catch (NoSuchFieldException e) {
129+
throw new IllegalArgumentException("Unknown algorithm: " + algorithm);
130+
}
131+
}
132+
133+
private static MessageDigest getDigestForAlgorithm(Hash.Algorithm algorithm) {
134+
try {
135+
switch (algorithm) {
136+
case MD5:
137+
return DigestUtils.getMd5Digest();
138+
case SHA1:
139+
return DigestUtils.getSha1Digest();
140+
case SHA_256:
141+
return DigestUtils.getSha256Digest();
142+
case SHA_384:
143+
return DigestUtils.getSha384Digest();
144+
case SHA_512:
145+
return DigestUtils.getSha512Digest();
146+
case SHA3_256:
147+
return DigestUtils.getSha3_256Digest();
148+
case SHA3_384:
149+
return DigestUtils.getSha3_384Digest();
150+
case SHA3_512:
151+
return DigestUtils.getSha3_512Digest();
152+
case BLAKE2b_256:
153+
case BLAKE2b_384:
154+
case BLAKE2b_512:
155+
case BLAKE3:
156+
return MessageDigest.getInstance(algorithm.getSpec());
157+
default:
158+
throw new IllegalArgumentException("Unsupported algorithm: " + algorithm.getSpec());
159+
}
160+
} catch (NoSuchAlgorithmException | NoSuchMethodError e) {
161+
throw new IllegalArgumentException("Algorithm not available: " + algorithm.getSpec(), e);
162+
}
163+
}
164+
90165
private static Hash.Algorithm toAlgorithm(MessageDigest digest) {
91166
for (Hash.Algorithm value : Hash.Algorithm.values()) {
92167
if (value.getSpec().equals(digest.getAlgorithm())) {

0 commit comments

Comments
 (0)