1 Commits
Author SHA1 Message Date
XoséandClaude Opus 5.5 9442afb2bd AssetRoots: keep loaded roots, retry a failed read (from tlskit)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:00:52 +02:00
+18 -6
View File
@@ -4,35 +4,47 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import android.content.Context; import android.content.Context;
/** Mozilla roots shipped as assets/roots/<sha1(subject)>-<n>.der */ /**
* Mozilla roots shipped as assets/roots/<sha1(subject)>-<n>.der
*
* Roots are read from the APK once and kept: an app installed on the SD card loses its APK while the
* card is shared over USB, and a failed read must not leave every server "untrusted" until a restart.
*/
public final class AssetRoots implements CertVerifier.RootStore { public final class AssetRoots implements CertVerifier.RootStore {
private final Context ctx; private final Context ctx;
private Set<String> names; private Set<String> names; // asset file names, once listed
private final Map<String, List<byte[]>> found = new HashMap<String, List<byte[]>>(); // by subject hash; null = none
public AssetRoots(Context c) { ctx = c.getApplicationContext(); } public AssetRoots(Context c) { ctx = c.getApplicationContext(); }
public synchronized List<byte[]> find(byte[] subjectDer) { public synchronized List<byte[]> find(byte[] subjectDer) {
String h = CertVerifier.sha1Hex(subjectDer);
if (found.containsKey(h)) return found.get(h);
try { try {
if (names == null) { if (names == null) {
names = new HashSet<String>();
String[] l = ctx.getAssets().list("roots"); String[] l = ctx.getAssets().list("roots");
if (l != null) for (int i = 0; i < l.length; i++) names.add(l[i]); if (l == null || l.length == 0) return null; // APK unreadable right now: try again next time
Set<String> n = new HashSet<String>();
for (int i = 0; i < l.length; i++) n.add(l[i]);
names = n;
} }
String h = CertVerifier.sha1Hex(subjectDer);
List<byte[]> out = null; List<byte[]> out = null;
for (int i = 0; names.contains(h + "-" + i + ".der"); i++) { for (int i = 0; names.contains(h + "-" + i + ".der"); i++) {
if (out == null) out = new ArrayList<byte[]>(); if (out == null) out = new ArrayList<byte[]>();
out.add(readAll(ctx.getAssets().open("roots/" + h + "-" + i + ".der"))); out.add(readAll(ctx.getAssets().open("roots/" + h + "-" + i + ".der")));
} }
found.put(h, out);
return out; return out;
} catch (IOException e) { } catch (IOException e) {
return null; return null; // not cached: the next handshake reads again
} }
} }