Private
Public Access
Minamp 1.4: faster connecting
- Remember the connection that worked and try it alone first next start - Prefer plain HTTP on the home network (each TLS handshake costs seconds on a 600 MHz phone); new setting "Encrypt at home too" to keep HTTPS - Try secure addresses one at a time instead of in parallel, so they don't all time out together - TLS 1.2 with session resumption: reconnects skip the full handshake - Cache certificate chains already verified; changing the trusted roots clears that cache and all TLS sessions - README: updated connection order; test: session resumption check and HandshakeProfile Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
58d21b6d68
commit
6fd17bc9c5
9 files changed
+284
-67
No files matched your search
@@ -45,14 +45,39 @@ public final class CertVerifier {
|
||||
}
|
||||
|
||||
private static RootStore roots;
|
||||
/** Chains already proven to lead to a trusted root (keyed by a hash of the chain), so a
|
||||
* reconnect only re-checks the host name and dates, not every signature again. */
|
||||
private static final java.util.Set<String> verifiedChains = new java.util.HashSet<String>();
|
||||
|
||||
public static void setRootStore(RootStore store) { roots = store; }
|
||||
/** Changing what's trusted invalidates everything trusted so far: cached chains and TLS sessions. */
|
||||
public static void setRootStore(RootStore store) {
|
||||
roots = store;
|
||||
synchronized (verifiedChains) { verifiedChains.clear(); }
|
||||
Http.forgetSessions();
|
||||
}
|
||||
|
||||
private CertVerifier() {}
|
||||
|
||||
|
||||
public static void verify(List<byte[]> chainDer, String host) throws IOException {
|
||||
if (chainDer == null || chainDer.isEmpty()) throw new IOException("Server sent no certificate");
|
||||
if (roots == null) throw new IOException("No trust store configured");
|
||||
java.io.ByteArrayOutputStream all = new java.io.ByteArrayOutputStream();
|
||||
for (int i = 0; i < chainDer.size(); i++) all.write(chainDer.get(i), 0, chainDer.get(i).length);
|
||||
String chainKey = sha1Hex(all.toByteArray());
|
||||
boolean known;
|
||||
synchronized (verifiedChains) { known = verifiedChains.contains(chainKey); }
|
||||
if (known) {
|
||||
Certificate leaf = Certificate.getInstance(chainDer.get(0));
|
||||
if (!hostMatches(leaf, host)) throw new IOException("Certificate does not match " + host);
|
||||
if (new Date().getTime() > 1704067200000L) checkDates(leaf, new Date());
|
||||
return;
|
||||
}
|
||||
verifyFully(chainDer, host);
|
||||
synchronized (verifiedChains) { verifiedChains.add(chainKey); }
|
||||
}
|
||||
|
||||
private static void verifyFully(List<byte[]> chainDer, String host) throws IOException {
|
||||
List<Certificate> chain = new ArrayList<Certificate>();
|
||||
for (int i = 0; i < chainDer.size(); i++) chain.add(Certificate.getInstance(chainDer.get(i)));
|
||||
|
||||
|
||||
@@ -133,6 +133,12 @@ public final class Http {
|
||||
}
|
||||
}
|
||||
|
||||
/** Forget resumable TLS sessions (next connection to each host does a full, verified handshake). */
|
||||
static void forgetSessions() {
|
||||
synchronized (sessions) { sessions.clear(); }
|
||||
evictAll();
|
||||
}
|
||||
|
||||
/** Drop all pooled connections (e.g. after the network changed). */
|
||||
public static void evictAll() {
|
||||
final List<Conn> doomed = new ArrayList<Conn>();
|
||||
@@ -191,6 +197,8 @@ public final class Http {
|
||||
private final String host;
|
||||
private final String sessionKey;
|
||||
|
||||
private final long started = System.currentTimeMillis();
|
||||
|
||||
Client(TlsCrypto crypto, String host, String sessionKey) {
|
||||
super(crypto);
|
||||
this.host = host;
|
||||
@@ -209,6 +217,16 @@ public final class Http {
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* TLS 1.2 only: this TLS stack can resume 1.2 sessions but not 1.3 ones, and on a 600 MHz
|
||||
* phone a resumed handshake (no key exchange, no signature checks) is the difference
|
||||
* between ~10 s and well under a second for every new connection.
|
||||
*/
|
||||
@Override
|
||||
protected com.minamp.bc.tls.ProtocolVersion[] getSupportedVersions() {
|
||||
return com.minamp.bc.tls.ProtocolVersion.TLSv12.only();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TlsSession getSessionToResume() {
|
||||
synchronized (sessions) { return sessions.get(sessionKey); }
|
||||
@@ -217,6 +235,9 @@ public final class Http {
|
||||
@Override
|
||||
public void notifyHandshakeComplete() throws IOException {
|
||||
super.notifyHandshakeComplete();
|
||||
boolean resumed = context.getSecurityParametersConnection().isResumedSession();
|
||||
DiagLog.log("TLS " + (resumed ? "resumed" : "full handshake") + " with " + host
|
||||
+ " in " + (System.currentTimeMillis() - started) + "ms");
|
||||
TlsSession s = context.getSession();
|
||||
if (s != null && s.isResumable()) {
|
||||
synchronized (sessions) { sessions.put(sessionKey, s); }
|
||||
|
||||
Reference in new issue
Block a user