From 42c48a8e3774fb68e70363256253a86ad093752b Mon Sep 17 00:00:00 2001 From: Zlatin Balevsky Date: Mon, 9 Dec 2019 14:26:39 +0000 Subject: [PATCH] certificate backend --- .../muwire/core/filecert/Certificate.groovy | 18 ++- .../core/filecert/CertificateClient.groovy | 13 +- .../filecert/CertificateFetchEvent.groovy | 4 + .../filecert/CertificateFetchedEvent.groovy | 4 + .../com/muwire/webui/CertificateManager.java | 101 ++++++++++++++ .../com/muwire/webui/CertificateServlet.java | 127 ++++++++++++++++++ .../java/com/muwire/webui/MuWireClient.java | 7 + 7 files changed, 267 insertions(+), 7 deletions(-) create mode 100644 webui/src/main/java/com/muwire/webui/CertificateManager.java create mode 100644 webui/src/main/java/com/muwire/webui/CertificateServlet.java diff --git a/core/src/main/groovy/com/muwire/core/filecert/Certificate.groovy b/core/src/main/groovy/com/muwire/core/filecert/Certificate.groovy index bcae8549..29b4e6e6 100644 --- a/core/src/main/groovy/com/muwire/core/filecert/Certificate.groovy +++ b/core/src/main/groovy/com/muwire/core/filecert/Certificate.groovy @@ -10,17 +10,20 @@ import net.i2p.crypto.DSAEngine import net.i2p.data.Signature import net.i2p.data.SigningPrivateKey import net.i2p.data.SigningPublicKey +import net.i2p.data.Base64 class Certificate { private final byte version private final InfoHash infoHash - private final Name name, comment - private final long timestamp - private final Persona issuer + final Name name, comment + final long timestamp + final Persona issuer private final byte[] sig private volatile byte [] payload + private String base64; + Certificate(InputStream is) { version = (byte) (is.read() & 0xFF) if (version > Constants.FILE_CERT_VERSION) @@ -131,6 +134,15 @@ class Certificate { os.write(payload) } + public String toBase64() { + if (base64 == null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream() + write(baos) + base64 = Base64.encode(baos.toByteArray()) + } + return base64; + } + @Override public int hashCode() { version.hashCode() ^ infoHash.hashCode() ^ timestamp.hashCode() ^ name.hashCode() ^ issuer.hashCode() ^ Objects.hashCode(comment) diff --git a/core/src/main/groovy/com/muwire/core/filecert/CertificateClient.groovy b/core/src/main/groovy/com/muwire/core/filecert/CertificateClient.groovy index abc2312c..31716118 100644 --- a/core/src/main/groovy/com/muwire/core/filecert/CertificateClient.groovy +++ b/core/src/main/groovy/com/muwire/core/filecert/CertificateClient.groovy @@ -32,7 +32,8 @@ class CertificateClient { fetcherThread.execute({ Endpoint endpoint = null try { - eventBus.publish(new CertificateFetchEvent(status : CertificateFetchStatus.CONNECTING)) + eventBus.publish(new CertificateFetchEvent(status : CertificateFetchStatus.CONNECTING, + user : e.host, infoHash : e.infoHash)) endpoint = connector.connect(e.host.destination) String infoHashString = Base64.encode(e.infoHash.getRoot()) @@ -62,7 +63,8 @@ class CertificateClient { int count = Integer.parseInt(headers['Count']) // start pulling the certs - eventBus.publish(new CertificateFetchEvent(status : CertificateFetchStatus.FETCHING, count : count)) + eventBus.publish(new CertificateFetchEvent(status : CertificateFetchStatus.FETCHING, count : count, + user : e.host, infoHash : e.infoHash)) DataInputStream dis = new DataInputStream(is) for (int i = 0; i < count; i++) { @@ -77,11 +79,14 @@ class CertificateClient { continue } if (cert.infoHash == e.infoHash) - eventBus.publish(new CertificateFetchedEvent(certificate : cert)) + eventBus.publish(new CertificateFetchedEvent(certificate : cert, user : e.host, infoHash : e.infoHash)) } + eventBus.publish(new CertificateFetchEvent(status : CertificateFetchStatus.DONE, count : count, + user : e.host, infoHash : e.infoHash)) } catch (Exception bad) { log.log(Level.WARNING,"Fetching certificates failed", bad) - eventBus.publish(new CertificateFetchEvent(status : CertificateFetchStatus.FAILED)) + eventBus.publish(new CertificateFetchEvent(status : CertificateFetchStatus.FAILED, + user : e.host, infoHash : e.infoHash)) } finally { endpoint?.close() } diff --git a/core/src/main/groovy/com/muwire/core/filecert/CertificateFetchEvent.groovy b/core/src/main/groovy/com/muwire/core/filecert/CertificateFetchEvent.groovy index 29e93391..9b7c4cc1 100644 --- a/core/src/main/groovy/com/muwire/core/filecert/CertificateFetchEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/filecert/CertificateFetchEvent.groovy @@ -1,8 +1,12 @@ package com.muwire.core.filecert import com.muwire.core.Event +import com.muwire.core.InfoHash +import com.muwire.core.Persona class CertificateFetchEvent extends Event { CertificateFetchStatus status int count + Persona user + InfoHash infoHash } diff --git a/core/src/main/groovy/com/muwire/core/filecert/CertificateFetchedEvent.groovy b/core/src/main/groovy/com/muwire/core/filecert/CertificateFetchedEvent.groovy index 869673cc..639d90e4 100644 --- a/core/src/main/groovy/com/muwire/core/filecert/CertificateFetchedEvent.groovy +++ b/core/src/main/groovy/com/muwire/core/filecert/CertificateFetchedEvent.groovy @@ -1,7 +1,11 @@ package com.muwire.core.filecert import com.muwire.core.Event +import com.muwire.core.InfoHash +import com.muwire.core.Persona class CertificateFetchedEvent extends Event { Certificate certificate + Persona user + InfoHash infoHash } diff --git a/webui/src/main/java/com/muwire/webui/CertificateManager.java b/webui/src/main/java/com/muwire/webui/CertificateManager.java new file mode 100644 index 00000000..80e252d5 --- /dev/null +++ b/webui/src/main/java/com/muwire/webui/CertificateManager.java @@ -0,0 +1,101 @@ +package com.muwire.webui; + +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import com.muwire.core.Core; +import com.muwire.core.InfoHash; +import com.muwire.core.Persona; +import com.muwire.core.filecert.Certificate; +import com.muwire.core.filecert.CertificateFetchEvent; +import com.muwire.core.filecert.CertificateFetchStatus; +import com.muwire.core.filecert.CertificateFetchedEvent; +import com.muwire.core.filecert.UIFetchCertificatesEvent; +import com.muwire.core.filecert.UIImportCertificateEvent; + +public class CertificateManager { + private final Core core; + + + private final Map> requests = new ConcurrentHashMap<>(); + + public CertificateManager(Core core) { + this.core = core; + } + + public void onCertificateFetchEvent(CertificateFetchEvent e) { + Map map = requests.get(e.getUser()); + if (map == null) + return; + CertificateRequest request = map.get(e.getInfoHash()); + if (request == null) + return; + request.status = e.getStatus(); + if (request.status == CertificateFetchStatus.FETCHING) + request.totalCertificates = e.getCount(); + } + + public void onCertificateFetchedEvent(CertificateFetchedEvent e) { + Map map = requests.get(e.getUser()); + if (map == null) + return; + CertificateRequest request = map.get(e.getInfoHash()); + if (request == null) + return; + request.certificates.add(e.getCertificate()); + } + + void request(Persona user, InfoHash infoHash) { + CertificateRequest request = new CertificateRequest(user, infoHash); + Map requestsFromUser = requests.get(user); + if (requestsFromUser == null) { + requestsFromUser = new ConcurrentHashMap<>(); + requests.put(user, requestsFromUser); + } + requestsFromUser.put(infoHash, request); + + UIFetchCertificatesEvent event = new UIFetchCertificatesEvent(); + event.setHost(user); + event.setInfoHash(infoHash); + core.getEventBus().publish(event); + } + + CertificateRequest get(Persona user, InfoHash infoHash) { + Map map = requests.get(user); + if (map == null) + return null; + return map.get(infoHash); + } + + void importCertificate(Certificate certificate) { + UIImportCertificateEvent event = new UIImportCertificateEvent(); + event.setCertificate(certificate); + core.getEventBus().publish(event); + } + + static class CertificateRequest { + private final Persona user; + private final InfoHash infoHash; + private volatile CertificateFetchStatus status; + private volatile int totalCertificates; + private Set certificates; + + CertificateRequest(Persona user, InfoHash infoHash) { + this.user = user; + this.infoHash = infoHash; + } + + CertificateFetchStatus getStatus() { + return status; + } + + int totalCertificates() { + return totalCertificates; + } + + Set getCertificates() { + return certificates; + } + } +} diff --git a/webui/src/main/java/com/muwire/webui/CertificateServlet.java b/webui/src/main/java/com/muwire/webui/CertificateServlet.java new file mode 100644 index 00000000..3834e8ef --- /dev/null +++ b/webui/src/main/java/com/muwire/webui/CertificateServlet.java @@ -0,0 +1,127 @@ +package com.muwire.webui; + +import java.io.ByteArrayInputStream; +import java.io.IOException; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.muwire.core.InfoHash; +import com.muwire.core.Persona; +import com.muwire.core.filecert.Certificate; +import com.muwire.webui.CertificateManager.CertificateRequest; + +import net.i2p.data.Base64; +import net.i2p.data.DataHelper; + +public class CertificateServlet extends HttpServlet { + + private CertificateManager certificateManager; + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String userB64 = req.getParameter("user"); + Persona user; + try { + user = new Persona(new ByteArrayInputStream(Base64.decode(userB64))); + } catch (Exception bad) { + resp.sendError(403, "Bad param"); + return; + } + + String infoHashB64 = req.getParameter("infoHash"); + InfoHash infoHash; + try { + infoHash = new InfoHash(Base64.decode(infoHashB64)); + } catch (Exception bad) { + resp.sendError(403, "Bad param"); + return; + } + + CertificateRequest request = certificateManager.get(user, infoHash); + if (request == null) { + resp.sendError(404,"Not found"); + return; + } + + StringBuilder sb = new StringBuilder(); + sb.append(""); + sb.append(""); + sb.append("").append(request.getStatus().toString()).append(""); + sb.append("").append(request.totalCertificates()).append(""); + sb.append(""); + for (Certificate certificate : request.getCertificates()) { + sb.append(""); + sb.append("").append(certificate.getIssuer().getHumanReadableName()).append(""); + sb.append("").append(Util.escapeHTMLinXML(certificate.getName().getName())).append(""); + if (certificate.getComment() != null) + sb.append("").append(Util.escapeHTMLinXML(certificate.getComment().getName())).append(""); + sb.append("").append(DataHelper.formatTime(certificate.getTimestamp())).append(""); + sb.append("").append(certificate.toBase64()).append(""); + sb.append(""); + } + sb.append(""); + sb.append(""); + + resp.setContentType("text/xml"); + resp.setCharacterEncoding("UTF-8"); + resp.setDateHeader("Expires", 0); + resp.setHeader("Pragma", "no-cache"); + resp.setHeader("Cache-Control", "no-store, max-age=0, no-cache, must-revalidate"); + byte[] out = sb.toString().getBytes("UTF-8"); + resp.setContentLength(out.length); + resp.getOutputStream().write(out); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + String action = req.getParameter("action"); + if (action == null) { + resp.sendError(403, "Bad param"); + return; + } + + if (action.equals("fetch")) { + String userB64 = req.getParameter("user"); + Persona user; + try { + user = new Persona(new ByteArrayInputStream(Base64.decode(userB64))); + } catch (Exception bad) { + resp.sendError(403, "Bad param"); + return; + } + + String infoHashB64 = req.getParameter("infoHash"); + InfoHash infoHash; + try { + infoHash = new InfoHash(Base64.decode(infoHashB64)); + } catch (Exception bad) { + resp.sendError(403, "Bad param"); + return; + } + + certificateManager.request(user, infoHash); + } else if (action.equals("import")) { + String certB64 = req.getParameter("base64"); + Certificate certificate; + try { + certificate = new Certificate(new ByteArrayInputStream(Base64.decode(certB64))); + } catch (Exception bad) { + resp.sendError(403, "Bad param"); + return; + } + certificateManager.importCertificate(certificate); + } + } + + @Override + public void init(ServletConfig config) throws ServletException { + certificateManager = (CertificateManager) config.getServletContext().getAttribute("certificateManager"); + } + + + +} diff --git a/webui/src/main/java/com/muwire/webui/MuWireClient.java b/webui/src/main/java/com/muwire/webui/MuWireClient.java index 151efb06..3242e9f4 100644 --- a/webui/src/main/java/com/muwire/webui/MuWireClient.java +++ b/webui/src/main/java/com/muwire/webui/MuWireClient.java @@ -22,6 +22,8 @@ import com.muwire.core.UILoadedEvent; import com.muwire.core.connection.ConnectionEvent; import com.muwire.core.connection.DisconnectionEvent; import com.muwire.core.download.DownloadStartedEvent; +import com.muwire.core.filecert.CertificateFetchEvent; +import com.muwire.core.filecert.CertificateFetchedEvent; import com.muwire.core.files.AllFilesLoadedEvent; import com.muwire.core.files.FileDownloadedEvent; import com.muwire.core.files.FileHashedEvent; @@ -148,12 +150,17 @@ public class MuWireClient { TrustManager trustManager = new TrustManager(); core.getEventBus().register(TrustEvent.class, trustManager); + CertificateManager certificateManager = new CertificateManager(core); + core.getEventBus().register(CertificateFetchedEvent.class, certificateManager); + core.getEventBus().register(CertificateFetchEvent.class, certificateManager); + servletContext.setAttribute("searchManager", searchManager); servletContext.setAttribute("downloadManager", downloadManager); servletContext.setAttribute("connectionCounter", connectionCounter); servletContext.setAttribute("fileManager", fileManager); servletContext.setAttribute("browseManager", browseManager); servletContext.setAttribute("trustManager", trustManager); + servletContext.setAttribute("certificateManager", certificateManager); core.getEventBus().publish(new UILoadedEvent()); }