Compare commits

...
1 Commits
Author SHA1 Message Date
XoséandClaude Opus 5.5 c5c3973878 AssetRoots: keep loaded roots, retry a failed read
An app on the SD card loses its APK while the card is shared over USB; the old code
cached an empty list and every server stayed untrusted until a restart.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 14:00:51 +02:00
+18 -6
View File
@@ -4,35 +4,47 @@ import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
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 {
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 synchronized List<byte[]> find(byte[] subjectDer) {
String h = CertVerifier.sha1Hex(subjectDer);
if (found.containsKey(h)) return found.get(h);
try {
if (names == null) {
names = new HashSet<String>();
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;
for (int i = 0; names.contains(h + "-" + i + ".der"); i++) {
if (out == null) out = new ArrayList<byte[]>();
out.add(readAll(ctx.getAssets().open("roots/" + h + "-" + i + ".der")));
}
found.put(h, out);
return out;
} catch (IOException e) {
return null;
return null; // not cached: the next handshake reads again
}
}