From 466eb6f228a76838bbea1581f60786689fee4b2d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sun, 6 Sep 2020 21:41:38 +0200 Subject: [PATCH] Prototype ikey store implementation --- .../smackx/ikey/FileBasedIkeyStore.java | 70 +++++++++++++++++++ .../jivesoftware/smackx/ikey/IkeyManager.java | 32 ++++++--- .../jivesoftware/smackx/ikey/IkeyStore.java | 7 +- .../ikey/provider/IkeyElementProvider.java | 68 ++++++++++++++++++ .../smackx/ikey/element/IkeyElementTest.java | 16 ++++- libs/Smack | 2 +- 6 files changed, 177 insertions(+), 18 deletions(-) create mode 100644 domain/src/main/java/org/jivesoftware/smackx/ikey/FileBasedIkeyStore.java create mode 100644 domain/src/main/java/org/jivesoftware/smackx/ikey/provider/IkeyElementProvider.java diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/FileBasedIkeyStore.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/FileBasedIkeyStore.java new file mode 100644 index 0000000..75c9566 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/FileBasedIkeyStore.java @@ -0,0 +1,70 @@ +package org.jivesoftware.smackx.ikey; + +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.test.util.TestUtils; +import org.jivesoftware.smack.util.Objects; +import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.ikey.element.IkeyElement; +import org.jivesoftware.smackx.ikey.provider.IkeyElementProvider; +import org.jxmpp.jid.EntityBareJid; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStreamWriter; +import java.nio.charset.StandardCharsets; + +public class FileBasedIkeyStore implements IkeyStore { + + private final File baseFile; + + public FileBasedIkeyStore(File file) { + this.baseFile = Objects.requireNonNull(file); + } + + @Override + public IkeyElement loadIkeyRecord(EntityBareJid jid) throws IOException { + File file = new File(baseFile, jid.asUrlEncodedString()); + if (file.exists() && !file.isDirectory()) { + return null; + } + try { + String content = getFileContent(new FileInputStream(file)); + return IkeyElementProvider.INSTANCE.parse(TestUtils.getParser(content)); + } catch (XmlPullParserException | SmackParsingException e) { + throw new IOException(e); + } + } + + @Override + public void storeIkeyRecord(EntityBareJid jid, IkeyElement record) throws IOException { + File file = new File(baseFile, jid.asUrlEncodedString()); + if (!file.exists()) { + file.createNewFile(); + } + writeToFile(new FileOutputStream(file), record.toXML().toString()); + } + + private static String getFileContent(FileInputStream fis) throws IOException { + try (BufferedReader br = new BufferedReader(new InputStreamReader(fis, StandardCharsets.UTF_8))) { + StringBuilder sb = new StringBuilder(); + String line; + while((line = br.readLine()) != null) { + sb.append(line); + sb.append('\n'); + } + return sb.toString(); + } + } + + private static void writeToFile(FileOutputStream fos, String content) throws IOException { + try (BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8))) { + bw.write(content); + } + } + +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java index 9522cdd..df425f6 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java @@ -31,9 +31,12 @@ import java.util.Date; import java.util.List; import java.util.Map; import java.util.WeakHashMap; +import java.util.logging.Level; +import java.util.logging.Logger; public final class IkeyManager extends Manager { + private static final Logger LOGGER = Logger.getLogger(IkeyManager.class.getName()); private static final Map INSTANCES = new WeakHashMap<>(); private IkeyStore store; @@ -88,7 +91,10 @@ public final class IkeyManager extends Manager { return fetchIkeyElementFrom(pubSubManager); } - private IkeyElement fetchIkeyElementFrom(PubSubManager pubSubManager) throws PubSubException.NotALeafNodeException, SmackException.NoResponseException, SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, PubSubException.NotAPubSubNodeException { + private IkeyElement fetchIkeyElementFrom(PubSubManager pubSubManager) + throws PubSubException.NotALeafNodeException, SmackException.NoResponseException, + SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, + PubSubException.NotAPubSubNodeException { LeafNode node = pubSubManager.getLeafNode(IkeyConstants.IKEY_NODE); List> items = node.getItems(1); if (items.isEmpty()) { @@ -100,16 +106,22 @@ public final class IkeyManager extends Manager { private void processIkeyElement(EntityBareJid from, IkeyElement element) throws XMLParserException, IOException, CanonicalizationException, UnsupportedSignatureAlgorithmException { - if (isFromTheFuture(element) || existsSameOrNewerRecord(element)) { + if (isFromTheFuture(element)) { + LOGGER.log(Level.WARNING, "Received ikey element appears to be from the future: " + element.getSubordinates().getTimestamp()); + return; + } + + if (existsSameOrNewerRecord(element)) { + LOGGER.log(Level.WARNING, "There exists this exact, or a newer ikey record in the database for " + from); return; } if (!verifyIkeyElement(from, element)) { + LOGGER.log(Level.WARNING, "Invalid signature on ikey element of " + from); return; } - //store.setLatestTimestamp(from, element.getSubordinates().getTimestamp()); - //store.setIkeySubkeys(from, element.getSubordinates().getSubordinates()); + store.storeIkeyRecord(from, element); } private boolean verifyIkeyElement(EntityBareJid from, IkeyElement element) @@ -139,12 +151,12 @@ public final class IkeyManager extends Manager { } private boolean existsSameOrNewerRecord(IkeyElement ikeyElement) { - Date latestTimestamp = store.getLatestTimestamp(ikeyElement.getSubordinates().getJid()); - Date eventTimestamp = ikeyElement.getSubordinates().getTimestamp(); - if (latestTimestamp == null) { - // No record at all found. + IkeyElement existingRecord = store.loadIkeyRecord(ikeyElement.getSubordinates().getJid()); + if (existingRecord == null) { return false; } + Date latestTimestamp = existingRecord.getSubordinates().getTimestamp(); + Date eventTimestamp = ikeyElement.getSubordinates().getTimestamp(); return latestTimestamp.equals(eventTimestamp) // same || latestTimestamp.after(eventTimestamp); // newer } @@ -153,12 +165,10 @@ public final class IkeyManager extends Manager { (from, event, id, carrierMessage) -> Async.go(() -> { try { processIkeyElement(from, event); - } catch (XMLParserException e) { + } catch (XMLParserException | CanonicalizationException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); - } catch (CanonicalizationException e) { - e.printStackTrace(); } catch (UnsupportedSignatureAlgorithmException e) { e.printStackTrace(); } diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyStore.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyStore.java index 62af681..c5ee92f 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyStore.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyStore.java @@ -1,12 +1,13 @@ package org.jivesoftware.smackx.ikey; +import org.jivesoftware.smackx.ikey.element.IkeyElement; import org.jxmpp.jid.EntityBareJid; -import java.util.Date; +import java.io.IOException; public interface IkeyStore { - Date getLatestTimestamp(EntityBareJid jid); - + IkeyElement loadIkeyRecord(EntityBareJid jid) throws IOException; + void storeIkeyRecord(EntityBareJid jid, IkeyElement record) throws IOException; } diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/provider/IkeyElementProvider.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/provider/IkeyElementProvider.java new file mode 100644 index 0000000..d3c6548 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/provider/IkeyElementProvider.java @@ -0,0 +1,68 @@ +package org.jivesoftware.smackx.ikey.provider; + +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.provider.ExtensionElementProvider; +import org.jivesoftware.smack.util.ParserUtils; +import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.ikey.element.IkeyElement; +import org.jivesoftware.smackx.ikey.element.ProofElement; +import org.jivesoftware.smackx.ikey.element.SubordinateElement; +import org.jivesoftware.smackx.ikey.element.SubordinateListElement; +import org.jivesoftware.smackx.ikey.element.SuperordinateElement; +import org.jivesoftware.smackx.ikey.mechanism.IkeyType; +import org.jxmpp.jid.EntityBareJid; + +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class IkeyElementProvider extends ExtensionElementProvider { + + public static final IkeyElementProvider INSTANCE = new IkeyElementProvider(); + + @Override + public IkeyElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) + throws XmlPullParserException, IOException, SmackParsingException { + String typeString = ParserUtils.getRequiredAttribute(parser, IkeyElement.ELEMENT); + IkeyType type = IkeyType.valueOf(typeString); + SuperordinateElement superordinate = null; + List subordinates = new ArrayList<>(); + EntityBareJid jid = null; + Date timestamp = null; + ProofElement proofElement = null; + + while (parser.getDepth() != initialDepth) { + switch (parser.nextTag()) { + case START_ELEMENT: + switch (parser.getName()) { + + case SuperordinateElement.ELEMENT: + superordinate = new SuperordinateElement(parser.nextText()); + break; + + case SubordinateListElement.ELEMENT: + jid = ParserUtils.getBareJidAttribute(parser); + timestamp = ParserUtils.getDateFromXep82String( + ParserUtils.getRequiredAttribute(parser, SubordinateListElement.ATTR_STAMP)); + break; + + case SubordinateElement.ELEMENT: + String uriString = ParserUtils.getRequiredAttribute(parser, SubordinateElement.ATTR_SUB_URI); + URI uri = URI.create(uriString); + String fingerprint = ParserUtils.getRequiredAttribute(parser, SubordinateElement.ATTR_SUB_FINGERPRINT); + subordinates.add(new SubordinateElement(uri, fingerprint)); + + case ProofElement.ELEMENT: + proofElement = new ProofElement(parser.nextText()); + break; + } + case END_ELEMENT: + } + } + return new IkeyElement(type, superordinate, new SubordinateListElement(jid, timestamp, subordinates), proofElement); + } +} diff --git a/domain/src/test/java/org/jivesoftware/smackx/ikey/element/IkeyElementTest.java b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/IkeyElementTest.java index 2ff62e1..3f91944 100644 --- a/domain/src/test/java/org/jivesoftware/smackx/ikey/element/IkeyElementTest.java +++ b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/IkeyElementTest.java @@ -1,19 +1,26 @@ package org.jivesoftware.smackx.ikey.element; +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.test.util.TestUtils; +import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smackx.ikey.mechanism.IkeyType; +import org.jivesoftware.smackx.ikey.provider.IkeyElementProvider; import org.junit.Test; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.impl.JidCreate; +import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; import java.util.Date; -public class IkeyElementTest { +import static junit.framework.TestCase.assertEquals; + +public class IkeyElementTest extends MercurySmackTestSuite { @Test - public void elementTest() throws URISyntaxException { + public void elementTest() throws URISyntaxException, XmlPullParserException, IOException, SmackParsingException { IkeyType type = IkeyType.OX; Date date = new Date(); SuperordinateElement superordinate = new SuperordinateElement("" + @@ -33,8 +40,11 @@ public class IkeyElementTest { ProofElement proof = new ProofElement("d2hpbGUgdGhpcyBpcyBub3QgYSB2YWxpZCBwcm9vZiwgaXQgaXMgc3VmZmljaWVudCBmb3IgdGVzdGluZy4="); IkeyElement ikeyElement = new IkeyElement(type, superordinate, subordinates, proof); + String xml = ikeyElement.toXML().toString(); - System.out.println(ikeyElement.toXML().toString()); + IkeyElement parsed = IkeyElementProvider.INSTANCE.parse(TestUtils.getParser(xml)); + + assertEquals(ikeyElement, parsed); } private SubordinateListElement buildSubListElement(EntityBareJid jid, Date date, SubordinateElement... subordinateElements) { diff --git a/libs/Smack b/libs/Smack index d86d20e..1c822dc 160000 --- a/libs/Smack +++ b/libs/Smack @@ -1 +1 @@ -Subproject commit d86d20e82a5a490b1ffd31384cb79d0a974b04d5 +Subproject commit 1c822dcaa4d4cb92d8b2d048f49bb69885143d56