-
-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathManifestVerifier.java
More file actions
91 lines (81 loc) · 3.32 KB
/
ManifestVerifier.java
File metadata and controls
91 lines (81 loc) · 3.32 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
package cpw.mods.jarhandling.impl;
import java.security.CodeSigner;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
final class ManifestVerifier {
private ManifestVerifier() {}
private static final boolean DEBUG = Boolean.parseBoolean(System.getProperty("securejarhandler.debugVerifier", "false"));
private static final Base64.Decoder BASE64D = Base64.getDecoder();
private static final Map<String, MessageDigest> HASHERS = new HashMap<>();
private static MessageDigest getHasher(String name) {
return HASHERS.computeIfAbsent(name.toLowerCase(Locale.ENGLISH), k -> {
try {
return MessageDigest.getInstance(k);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
});
}
private static void log(String line) {
System.out.println(line);
}
/**
* This is Dumb API, but it's a package private class so la-de-da!
* return:
* null - Something went wrong, digests were not verified.
* Optional.empty() - No signatures to verify, missing *-Digest entry in manifest, or nobody signed that particular entry
* Optional.isPresent() - code signers!
*/
static Optional<CodeSigner[]> verify(final Manifest manifest, final Map<String, CodeSigner[]> pending,
final Map<String, CodeSigner[]> verified, final String name, final byte[] data) {
if (DEBUG)
log("[SJH] Verifying: " + name);
Attributes attr = manifest.getAttributes(name);
if (attr == null) {
if (DEBUG)
log("[SJH] No Manifest Entry");
return Optional.empty();
}
record Expected(MessageDigest hash, byte[] value){};
var expected = new ArrayList<Expected>();
attr.forEach((k,v) -> {
var key = k.toString();
if (key.toLowerCase(Locale.ENGLISH).endsWith("-digest")) {
var algo = key.substring(0, key.length() - 7);
var hash = BASE64D.decode((String)v);
expected.add(new Expected(getHasher(algo), hash));
}
});
if (expected.isEmpty()) {
if (DEBUG)
log("[SJH] No Manifest Hashes");
return Optional.empty();
}
for (var exp : expected) {
synchronized (exp.hash()) {
exp.hash().reset();
byte[] actual = exp.hash().digest(data);
if (DEBUG) {
log("[SJH] " + exp.hash().getAlgorithm() + " Expected: " + SecureJarVerifier.toHexString(exp.value()));
log("[SJH] " + exp.hash().getAlgorithm() + " Actual: " + SecureJarVerifier.toHexString(actual));
}
if (!Arrays.equals(exp.value(), actual)) {
if (DEBUG)
log("[SJH] Failed: Invalid hashes");
return null;
}
}
}
var signers = pending.remove(name);
if (signers != null)
verified.put(name, signers);
return Optional.ofNullable(signers);
}
}