diff --git a/data/src/main/java/org/mercury_im/messenger/data/di/MappingModule.java b/data/src/main/java/org/mercury_im/messenger/data/di/MappingModule.java index 28085ff..d45216a 100644 --- a/data/src/main/java/org/mercury_im/messenger/data/di/MappingModule.java +++ b/data/src/main/java/org/mercury_im/messenger/data/di/MappingModule.java @@ -23,20 +23,20 @@ public class MappingModule { @Provides @Singleton - static PeerMapping providePeerMapping() { - return new PeerMapping(provideAccountMapping()); + static PeerMapping providePeerMapping(AccountMapping accountMapping) { + return new PeerMapping(accountMapping); } @Provides @Singleton - static DirectChatMapping provideDirectChatMapping() { - return new DirectChatMapping(providePeerMapping()); + static DirectChatMapping provideDirectChatMapping(PeerMapping peerMapping) { + return new DirectChatMapping(peerMapping); } @Provides @Singleton - static GroupChatMapping provideGroupChatMapping() { - return new GroupChatMapping(provideAccountMapping()); + static GroupChatMapping provideGroupChatMapping(AccountMapping accountMapping) { + return new GroupChatMapping(accountMapping); } @Provides 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 3786294..9522cdd 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java @@ -1,10 +1,166 @@ package org.jivesoftware.smackx.ikey; +import org.apache.xml.security.c14n.CanonicalizationException; +import org.apache.xml.security.c14n.Canonicalizer; +import org.apache.xml.security.c14n.InvalidCanonicalizerException; +import org.apache.xml.security.parser.XMLParserException; +import org.bouncycastle.openpgp.PGPPublicKeyRing; +import org.jivesoftware.smack.Manager; +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.util.Async; +import org.jivesoftware.smackx.ikey.element.IkeyElement; +import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureVerificationMechanism; +import org.jivesoftware.smackx.ikey.util.IkeyConstants; +import org.jivesoftware.smackx.ikey.util.UnsupportedSignatureAlgorithmException; +import org.jivesoftware.smackx.ikey.util.canonicalization.ElementCanonicalizer; +import org.jivesoftware.smackx.ikey.util.canonicalization.XmlSecElementCanonicalizer; +import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureVerificationMechanism; +import org.jivesoftware.smackx.pep.PepEventListener; +import org.jivesoftware.smackx.pep.PepManager; +import org.jivesoftware.smackx.pubsub.LeafNode; +import org.jivesoftware.smackx.pubsub.PayloadItem; +import org.jivesoftware.smackx.pubsub.PubSubException; +import org.jivesoftware.smackx.pubsub.PubSubManager; import org.jxmpp.jid.EntityBareJid; +import org.pgpainless.PGPainless; -public class IkeyManager { +import java.io.IOException; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; - private IkeySignatureVerificationMechanism getSignatureMechanismFor(EntityBareJid contactJid) { - return null; +public final class IkeyManager extends Manager { + + private static final Map INSTANCES = new WeakHashMap<>(); + + private IkeyStore store; + private final ElementCanonicalizer canonicalizer; + + private IkeyManager(XMPPConnection connection) { + super(connection); + try { + this.canonicalizer = new XmlSecElementCanonicalizer(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS)); + } catch (InvalidCanonicalizerException e) { + throw new AssertionError(e); + } } + + public static synchronized IkeyManager getInstanceFor(XMPPConnection connection) { + IkeyManager manager = INSTANCES.get(connection); + if (manager == null) { + manager = new IkeyManager(connection); + INSTANCES.put(connection, manager); + } + return manager; + } + + public void startListeners() { + PepManager.getInstanceFor(connection()) + .addPepEventListener(IkeyConstants.IKEY_NODE, IkeyElement.class, pepEventListener); + } + + public void stopListeners() { + PepManager.getInstanceFor(connection()) + .removePepEventListener(pepEventListener); + } + + public void publishIkeyElement(IkeyElement ikeyElement) + throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, + SmackException.NotConnectedException, SmackException.NoResponseException { + PepManager.getInstanceFor(connection()) + .publish(IkeyConstants.IKEY_NODE, new PayloadItem<>(ikeyElement)); + } + + public IkeyElement fetchIkeyElementOf(EntityBareJid jid) + throws InterruptedException, PubSubException.NotALeafNodeException, SmackException.NoResponseException, + SmackException.NotConnectedException, XMPPException.XMPPErrorException, PubSubException.NotAPubSubNodeException { + PubSubManager pubSubManager = PubSubManager.getInstanceFor(connection(), jid); + return fetchIkeyElementFrom(pubSubManager); + } + + public IkeyElement fetchOwnIkeyElement() + throws InterruptedException, PubSubException.NotALeafNodeException, SmackException.NoResponseException, + SmackException.NotConnectedException, XMPPException.XMPPErrorException, PubSubException.NotAPubSubNodeException { + PubSubManager pubSubManager = PubSubManager.getInstanceFor(connection()); + return fetchIkeyElementFrom(pubSubManager); + } + + 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()) { + return null; + } else { + return items.get(0).getPayload(); + } + } + + private void processIkeyElement(EntityBareJid from, IkeyElement element) + throws XMLParserException, IOException, CanonicalizationException, UnsupportedSignatureAlgorithmException { + if (isFromTheFuture(element) || existsSameOrNewerRecord(element)) { + return; + } + + if (!verifyIkeyElement(from, element)) { + return; + } + + //store.setLatestTimestamp(from, element.getSubordinates().getTimestamp()); + //store.setIkeySubkeys(from, element.getSubordinates().getSubordinates()); + } + + private boolean verifyIkeyElement(EntityBareJid from, IkeyElement element) + throws XMLParserException, IOException, CanonicalizationException, UnsupportedSignatureAlgorithmException { + IkeySignatureVerificationMechanism verificationMechanism = getSignatureVerificationMechanismFor(element); + IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism, canonicalizer); + return verifier.verify(element, from); + } + + private IkeySignatureVerificationMechanism getSignatureVerificationMechanismFor(IkeyElement ikeyElement) + throws IOException, UnsupportedSignatureAlgorithmException { + switch (ikeyElement.getType()) { + case OX: + PGPPublicKeyRing ikey = PGPainless.readKeyRing().publicKeyRing(ikeyElement.getSuperordinate().getPubKeyBytes()); + return new OxIkeySignatureVerificationMechanism(ikey); + + case X509: + throw new UnsupportedSignatureAlgorithmException("X.509"); + } + throw new AssertionError("Unknown verification algorithm encountered: " + ikeyElement.getType()); + } + + private static boolean isFromTheFuture(IkeyElement element) { + Date elementTimestamp = element.getSubordinates().getTimestamp(); + Date now = new Date(); + return elementTimestamp.after(now); + } + + 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. + return false; + } + return latestTimestamp.equals(eventTimestamp) // same + || latestTimestamp.after(eventTimestamp); // newer + } + + private final PepEventListener pepEventListener = + (from, event, id, carrierMessage) -> Async.go(() -> { + try { + processIkeyElement(from, event); + } catch (XMLParserException 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/IkeySignatureCreator.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreator.java index a718450..fb407a6 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreator.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreator.java @@ -5,6 +5,8 @@ import org.apache.xml.security.parser.XMLParserException; import org.bouncycastle.util.encoders.Base64; import org.jivesoftware.smackx.ikey.element.ProofElement; import org.jivesoftware.smackx.ikey.element.SubordinateListElement; +import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureCreationMechanism; +import org.jivesoftware.smackx.ikey.util.canonicalization.XmlSecElementCanonicalizer; import java.io.IOException; diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerifier.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerifier.java index bb73411..5d194c6 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerifier.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerifier.java @@ -4,6 +4,8 @@ import org.apache.xml.security.c14n.CanonicalizationException; import org.apache.xml.security.parser.XMLParserException; import org.bouncycastle.util.encoders.Base64; import org.jivesoftware.smackx.ikey.element.IkeyElement; +import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureVerificationMechanism; +import org.jivesoftware.smackx.ikey.util.canonicalization.ElementCanonicalizer; import org.jxmpp.jid.EntityBareJid; import java.io.IOException; @@ -11,9 +13,9 @@ import java.io.IOException; public class IkeySignatureVerifier { private final IkeySignatureVerificationMechanism signatureVerificationMechanism; - private final XmlSecElementCanonicalizer elementCanonicalizer; + private final ElementCanonicalizer elementCanonicalizer; - public IkeySignatureVerifier(IkeySignatureVerificationMechanism signatureVerificationMechanism, XmlSecElementCanonicalizer elementCanonicalizer) { + public IkeySignatureVerifier(IkeySignatureVerificationMechanism signatureVerificationMechanism, ElementCanonicalizer elementCanonicalizer) { this.signatureVerificationMechanism = signatureVerificationMechanism; this.elementCanonicalizer = elementCanonicalizer; } diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyStore.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyStore.java new file mode 100644 index 0000000..62af681 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyStore.java @@ -0,0 +1,12 @@ +package org.jivesoftware.smackx.ikey; + +import org.jxmpp.jid.EntityBareJid; + +import java.util.Date; + +public interface IkeyStore { + + Date getLatestTimestamp(EntityBareJid jid); + + +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyType.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyType.java deleted file mode 100644 index 3833c75..0000000 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyType.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.jivesoftware.smackx.ikey; - -public enum IkeyType { - OX, - X509 -} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/element/IkeyElement.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/IkeyElement.java index 96e784e..477b555 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/element/IkeyElement.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/IkeyElement.java @@ -3,8 +3,8 @@ package org.jivesoftware.smackx.ikey.element; import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.util.XmlStringBuilder; -import org.jivesoftware.smackx.ikey.IkeyConstants; -import org.jivesoftware.smackx.ikey.IkeyType; +import org.jivesoftware.smackx.ikey.util.IkeyConstants; +import org.jivesoftware.smackx.ikey.mechanism.IkeyType; import javax.xml.namespace.QName; @@ -17,11 +17,13 @@ public class IkeyElement implements ExtensionElement { private static final QName QNAME = new QName(IkeyConstants.NAMESPACE, ELEMENT); private final IkeyType type; + private final SuperordinateElement superordinate; private final SubordinateListElement subordinates; private final ProofElement proof; - public IkeyElement(IkeyType type, SubordinateListElement subordinates, ProofElement proof) { + public IkeyElement(IkeyType type, SuperordinateElement superordinate, SubordinateListElement subordinates, ProofElement proof) { this.type = type; + this.superordinate = superordinate; this.subordinates = subordinates; this.proof = proof; } @@ -30,6 +32,10 @@ public class IkeyElement implements ExtensionElement { return type; } + public SuperordinateElement getSuperordinate() { + return superordinate; + } + public SubordinateListElement getSubordinates() { return subordinates; } @@ -53,6 +59,7 @@ public class IkeyElement implements ExtensionElement { return new XmlStringBuilder(this, xmlEnvironment) .attribute(ATTR_IKEY_TYPE, getType()) .rightAngleBracket() + .append(getSuperordinate()) .append(getSubordinates()) .append(getProof()) .closeElement(this); diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SubordinateListElement.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SubordinateListElement.java index 392b2ea..750417d 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SubordinateListElement.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SubordinateListElement.java @@ -4,22 +4,26 @@ import org.jivesoftware.smack.packet.NamedElement; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.XmlStringBuilder; -import org.jivesoftware.smackx.ikey.IkeyConstants; +import org.jivesoftware.smackx.ikey.util.IkeyConstants; import org.jxmpp.jid.EntityBareJid; +import java.util.Date; import java.util.List; public class SubordinateListElement implements NamedElement { public static final String NAMESPACE = IkeyConstants.NAMESPACE; public static final String ELEMENT = "subordinates"; + public static final String ATTR_STAMP = "stamp"; public static final String ATTR_JID = "jid"; private final List subordinates; private final EntityBareJid jid; + private final Date timestamp; - public SubordinateListElement(EntityBareJid jid, List subordinates) { + public SubordinateListElement(EntityBareJid jid, Date timestamp, List subordinates) { this.jid = jid; + this.timestamp = timestamp; this.subordinates = Objects.requireNonNullNorEmpty(subordinates, "List of subordinates MUST NOT be null nor empty."); } @@ -27,6 +31,10 @@ public class SubordinateListElement implements NamedElement { return jid; } + public Date getTimestamp() { + return timestamp; + } + public List getSubordinates() { return subordinates; } @@ -40,6 +48,7 @@ public class SubordinateListElement implements NamedElement { public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { return new XmlStringBuilder(this) .attribute(ATTR_JID, getJid()) + .attribute(ATTR_STAMP, getTimestamp()) .rightAngleBracket() .append(getSubordinates()) .closeElement(this); diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SuperordinateElement.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SuperordinateElement.java new file mode 100644 index 0000000..5f87fe3 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SuperordinateElement.java @@ -0,0 +1,42 @@ +package org.jivesoftware.smackx.ikey.element; + +import org.jivesoftware.smack.packet.NamedElement; +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.util.XmlStringBuilder; +import org.jivesoftware.smack.util.stringencoder.Base64; + +public class SuperordinateElement implements NamedElement { + + public static final String ELEMENT = "superordinate"; + + private final byte[] pubKeyBytes; + + public SuperordinateElement(byte[] bytes) { + this.pubKeyBytes = bytes; + } + + public SuperordinateElement(String base64) { + this.pubKeyBytes = Base64.decode(base64); + } + + @Override + public String getElementName() { + return ELEMENT; + } + + public byte[] getPubKeyBytes() { + return pubKeyBytes.clone(); + } + + public String getBase64PubKey() { + return Base64.encodeToString(getPubKeyBytes()); + } + + @Override + public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { + return new XmlStringBuilder(this) + .rightAngleBracket() + .append(getBase64PubKey()) + .closeElement(this); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreationMechanism.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/mechanism/IkeySignatureCreationMechanism.java similarity index 77% rename from domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreationMechanism.java rename to domain/src/main/java/org/jivesoftware/smackx/ikey/mechanism/IkeySignatureCreationMechanism.java index 444aa58..5b7765a 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreationMechanism.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/mechanism/IkeySignatureCreationMechanism.java @@ -1,4 +1,4 @@ -package org.jivesoftware.smackx.ikey; +package org.jivesoftware.smackx.ikey.mechanism; import java.io.IOException; diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerificationMechanism.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/mechanism/IkeySignatureVerificationMechanism.java similarity index 80% rename from domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerificationMechanism.java rename to domain/src/main/java/org/jivesoftware/smackx/ikey/mechanism/IkeySignatureVerificationMechanism.java index fd995ce..9b8a011 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerificationMechanism.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/mechanism/IkeySignatureVerificationMechanism.java @@ -1,4 +1,4 @@ -package org.jivesoftware.smackx.ikey; +package org.jivesoftware.smackx.ikey.mechanism; import java.io.IOException; diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/mechanism/IkeyType.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/mechanism/IkeyType.java new file mode 100644 index 0000000..699140c --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/mechanism/IkeyType.java @@ -0,0 +1,6 @@ +package org.jivesoftware.smackx.ikey.mechanism; + +public enum IkeyType { + OX, + X509 +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyConstants.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/util/IkeyConstants.java similarity index 83% rename from domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyConstants.java rename to domain/src/main/java/org/jivesoftware/smackx/ikey/util/IkeyConstants.java index 94cf000..07453b2 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyConstants.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/util/IkeyConstants.java @@ -1,4 +1,4 @@ -package org.jivesoftware.smackx.ikey; +package org.jivesoftware.smackx.ikey.util; public class IkeyConstants { diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/util/UnsupportedSignatureAlgorithmException.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/util/UnsupportedSignatureAlgorithmException.java new file mode 100644 index 0000000..4201ea4 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/util/UnsupportedSignatureAlgorithmException.java @@ -0,0 +1,17 @@ +package org.jivesoftware.smackx.ikey.util; + +import org.jivesoftware.smack.util.StringUtils; + +public class UnsupportedSignatureAlgorithmException extends Exception { + + private static final long serialVersionUID = 1L; + private final String algorithmName; + + public UnsupportedSignatureAlgorithmException(String algorithmName) { + this.algorithmName = StringUtils.requireNotNullNorEmpty(algorithmName, "Algorithm name MUST NOT be null NOR empty."); + } + + public String getAlgorithmName() { + return algorithmName; + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/ElementCanonicalizer.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/ElementCanonicalizer.java similarity index 80% rename from domain/src/main/java/org/jivesoftware/smackx/ikey/ElementCanonicalizer.java rename to domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/ElementCanonicalizer.java index 1baf6e9..19f02ae 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/ElementCanonicalizer.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/ElementCanonicalizer.java @@ -1,4 +1,4 @@ -package org.jivesoftware.smackx.ikey; +package org.jivesoftware.smackx.ikey.util.canonicalization; import org.apache.xml.security.c14n.CanonicalizationException; import org.apache.xml.security.parser.XMLParserException; @@ -18,4 +18,8 @@ public interface ElementCanonicalizer { } byte[] canonicalize(byte[] xml) throws XMLParserException, IOException, CanonicalizationException; + + default String removeInterElementWhitespace(String xml) { + return xml.replaceAll(">\\s*<", "><"); + } } diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/XmlSecElementCanonicalizer.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/XmlSecElementCanonicalizer.java similarity index 93% rename from domain/src/main/java/org/jivesoftware/smackx/ikey/XmlSecElementCanonicalizer.java rename to domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/XmlSecElementCanonicalizer.java index b9b81ce..3d94239 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/XmlSecElementCanonicalizer.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/XmlSecElementCanonicalizer.java @@ -1,4 +1,4 @@ -package org.jivesoftware.smackx.ikey; +package org.jivesoftware.smackx.ikey.util.canonicalization; import org.apache.xml.security.c14n.CanonicalizationException; import org.apache.xml.security.c14n.Canonicalizer; diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeySignatureCreationMechanism.java b/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeySignatureCreationMechanism.java index 9b0e685..8ccb4fa 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeySignatureCreationMechanism.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeySignatureCreationMechanism.java @@ -1,12 +1,11 @@ package org.jivesoftware.smackx.ikey_ox; -import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.util.io.Streams; -import org.jivesoftware.smackx.ikey.IkeySignatureCreationMechanism; -import org.jivesoftware.smackx.ikey.IkeyType; +import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureCreationMechanism; +import org.jivesoftware.smackx.ikey.mechanism.IkeyType; import org.pgpainless.PGPainless; import org.pgpainless.encryption_signing.EncryptionStream; import org.pgpainless.key.protection.SecretKeyRingProtector; diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeySignatureVerificationMechanism.java b/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeySignatureVerificationMechanism.java index 5402f35..8f405ad 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeySignatureVerificationMechanism.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeySignatureVerificationMechanism.java @@ -3,8 +3,8 @@ package org.jivesoftware.smackx.ikey_ox; import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.util.io.Streams; -import org.jivesoftware.smackx.ikey.IkeySignatureVerificationMechanism; -import org.jivesoftware.smackx.ikey.IkeyType; +import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureVerificationMechanism; +import org.jivesoftware.smackx.ikey.mechanism.IkeyType; import org.pgpainless.PGPainless; import org.pgpainless.decryption_verification.DecryptionStream; import org.pgpainless.decryption_verification.OpenPgpMetadata; diff --git a/domain/src/main/java/org/mercury_im/messenger/core/crypto/ikey/IkeySignatureProvider.java b/domain/src/main/java/org/mercury_im/messenger/core/crypto/ikey/IkeySignatureProvider.java new file mode 100644 index 0000000..be4bb6c --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/core/crypto/ikey/IkeySignatureProvider.java @@ -0,0 +1,5 @@ +package org.mercury_im.messenger.core.crypto.ikey; + +public class IkeySignatureProvider { + +} diff --git a/domain/src/main/java/org/mercury_im/messenger/core/di/module/IkeyModule.java b/domain/src/main/java/org/mercury_im/messenger/core/di/module/IkeyModule.java new file mode 100644 index 0000000..762fde6 --- /dev/null +++ b/domain/src/main/java/org/mercury_im/messenger/core/di/module/IkeyModule.java @@ -0,0 +1,35 @@ +package org.mercury_im.messenger.core.di.module; + +import org.apache.xml.security.Init; +import org.apache.xml.security.c14n.Canonicalizer; +import org.apache.xml.security.c14n.InvalidCanonicalizerException; +import org.jivesoftware.smackx.ikey.util.canonicalization.ElementCanonicalizer; +import org.jivesoftware.smackx.ikey.util.canonicalization.XmlSecElementCanonicalizer; + +import javax.inject.Singleton; + +import dagger.Module; +import dagger.Provides; + +@Module +public class IkeyModule { + + @Singleton + @Provides + static Canonicalizer provideCanonicalizer() { + if (!Init.isInitialized()) { + Init.init(); + } + try { + return Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS); + } catch (InvalidCanonicalizerException e) { + throw new AssertionError(e); + } + } + + @Singleton + @Provides + static ElementCanonicalizer provideElementCanonicalizer(Canonicalizer canonicalizer) { + return new XmlSecElementCanonicalizer(canonicalizer); + } +} 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 b11b247..2ff62e1 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,6 +1,6 @@ package org.jivesoftware.smackx.ikey.element; -import org.jivesoftware.smackx.ikey.IkeyType; +import org.jivesoftware.smackx.ikey.mechanism.IkeyType; import org.junit.Test; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.impl.JidCreate; @@ -8,14 +8,21 @@ import org.jxmpp.jid.impl.JidCreate; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; +import java.util.Date; public class IkeyElementTest { @Test public void elementTest() throws URISyntaxException { IkeyType type = IkeyType.OX; + Date date = new Date(); + SuperordinateElement superordinate = new SuperordinateElement("" + + "VGhpcyBpcyBvbmx5IGEgdGVzdCwgbm90IGEgcmVhbCBrZXkuIFdoeSB3b3VsZCB5b3UgdGhpbmsg" + + "dGhpcyBpcyBhIHJlYWwga2V5PyBCZWNhdXNlIGl0IGlzICdiYXNlNjQtZW5jcnlwdGVkJz8gRHVt" + + "bXkgZHVtYiBkdW1iLg=="); SubordinateListElement subordinates = buildSubListElement( JidCreate.entityBareFromOrThrowUnchecked("hamlet@denmark.lit"), + date, new SubordinateElement( new URI("xmpp:hamlet@denmark.lit?;node=urn:xmpp:openpgp:0:public-keys:1357B01865B2503C18453D208CAC2A9678548E35;item=2020-01-21T10:46:21Z"), "1357B01865B2503C18453D208CAC2A9678548E35"), @@ -25,12 +32,12 @@ public class IkeyElementTest { ); ProofElement proof = new ProofElement("d2hpbGUgdGhpcyBpcyBub3QgYSB2YWxpZCBwcm9vZiwgaXQgaXMgc3VmZmljaWVudCBmb3IgdGVzdGluZy4="); - IkeyElement ikeyElement = new IkeyElement(type, subordinates, proof); + IkeyElement ikeyElement = new IkeyElement(type, superordinate, subordinates, proof); System.out.println(ikeyElement.toXML().toString()); } - private SubordinateListElement buildSubListElement(EntityBareJid jid, SubordinateElement... subordinateElements) { - return new SubordinateListElement(jid, Arrays.asList(subordinateElements)); + private SubordinateListElement buildSubListElement(EntityBareJid jid, Date date, SubordinateElement... subordinateElements) { + return new SubordinateListElement(jid, date, Arrays.asList(subordinateElements)); } } diff --git a/domain/src/test/java/org/jivesoftware/smackx/ikey/element/IkeySignatureCreatorAndVerifierTest.java b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/IkeySignatureCreatorAndVerifierTest.java index 7f9c6ff..6b34b44 100644 --- a/domain/src/test/java/org/jivesoftware/smackx/ikey/element/IkeySignatureCreatorAndVerifierTest.java +++ b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/IkeySignatureCreatorAndVerifierTest.java @@ -6,12 +6,13 @@ import org.apache.xml.security.c14n.Canonicalizer; import org.apache.xml.security.c14n.InvalidCanonicalizerException; import org.apache.xml.security.parser.XMLParserException; import org.bouncycastle.openpgp.PGPException; -import org.jivesoftware.smackx.ikey.XmlSecElementCanonicalizer; -import org.jivesoftware.smackx.ikey.IkeySignatureCreationMechanism; +import org.jivesoftware.smack.util.stringencoder.Base64; +import org.jivesoftware.smackx.ikey.util.canonicalization.XmlSecElementCanonicalizer; +import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureCreationMechanism; import org.jivesoftware.smackx.ikey.IkeySignatureCreator; -import org.jivesoftware.smackx.ikey.IkeySignatureVerificationMechanism; +import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureVerificationMechanism; import org.jivesoftware.smackx.ikey.IkeySignatureVerifier; -import org.jivesoftware.smackx.ikey.IkeyType; +import org.jivesoftware.smackx.ikey.mechanism.IkeyType; import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureCreationMechanism; import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureVerificationMechanism; import org.junit.BeforeClass; @@ -19,8 +20,12 @@ import org.junit.Test; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.impl.JidCreate; import org.pgpainless.PGPainless; +import org.pgpainless.algorithm.KeyFlag; import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.collection.PGPKeyRing; +import org.pgpainless.key.generation.KeySpec; +import org.pgpainless.key.generation.type.ECDSA; +import org.pgpainless.key.generation.type.curve.EllipticCurve; import org.pgpainless.key.protection.UnprotectedKeysProtector; import java.io.IOException; @@ -29,11 +34,12 @@ import java.net.URISyntaxException; import java.security.InvalidAlgorithmParameterException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; +import java.util.Date; import java.util.List; import static junit.framework.TestCase.assertTrue; -public class IkeySignatureCreatorAndVerifierTest { +public class IkeySignatureCreatorAndVerifierTest extends MercurySmackTestSuite { @BeforeClass public static void initialize() { @@ -45,7 +51,13 @@ public class IkeySignatureCreatorAndVerifierTest { @Test public void createIkeyElementAndVerifySignature() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, URISyntaxException, XMLParserException, IOException, CanonicalizationException, InvalidCanonicalizerException { EntityBareJid jid = JidCreate.entityBareFromOrThrowUnchecked("alice@wonderland.lit"); - PGPKeyRing keyRing = PGPainless.generateKeyRing().simpleEcKeyRing("xmpp:" + jid); + PGPKeyRing keyRing = PGPainless.generateKeyRing() + .withMasterKey(KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256)) + .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) + .withDefaultAlgorithms()) + .withPrimaryUserId("xmpp:" + jid) + .withoutPassphrase() + .build(); XmlSecElementCanonicalizer elementCanonicalizer = new XmlSecElementCanonicalizer(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS)); IkeySignatureCreationMechanism signingMechanism = new OxIkeySignatureCreationMechanism( @@ -55,14 +67,15 @@ public class IkeySignatureCreatorAndVerifierTest { IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism, elementCanonicalizer); OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(keyRing.getMasterKey()); + SuperordinateElement superordinate = new SuperordinateElement(Base64.encodeToString(keyRing.getMasterKey().getEncoded())); List subList = new ArrayList<>(); subList.add(new SubordinateElement( new URI("xmpp:" + jid + "?;node=urn:xmpp:openpgp:0:public-keys:" + fingerprint + ";item=2020-01-21T10:46:21Z"), fingerprint.toString())); - SubordinateListElement subs = new SubordinateListElement(jid, subList); + SubordinateListElement subs = new SubordinateListElement(jid, new Date(), subList); ProofElement proofElement = creator.createProofFor(subs); - IkeyElement ikeyElement = new IkeyElement(IkeyType.OX, subs, proofElement); + IkeyElement ikeyElement = new IkeyElement(IkeyType.OX, superordinate, subs, proofElement); System.out.println(ikeyElement.toXML().toString()); diff --git a/domain/src/test/java/org/jivesoftware/smackx/ikey/element/MercurySmackTestSuite.java b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/MercurySmackTestSuite.java new file mode 100644 index 0000000..c903117 --- /dev/null +++ b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/MercurySmackTestSuite.java @@ -0,0 +1,38 @@ +package org.jivesoftware.smackx.ikey.element; + +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.jivesoftware.smack.SmackConfiguration; +import org.jivesoftware.smack.util.stringencoder.Base64; + +import java.security.Security; + +public class MercurySmackTestSuite { + static { + SmackConfiguration.getVersion(); + org.jivesoftware.smack.util.stringencoder.Base64.setEncoder(new Base64.Encoder() { + + @Override + public byte[] decode(String string) { + return java.util.Base64.getDecoder().decode(string); + } + + @Override + public String encodeToString(byte[] input) { + return java.util.Base64.getEncoder().encodeToString(input); + } + + @Override + public String encodeToStringWithoutPadding(byte[] input) { + return java.util.Base64.getEncoder().withoutPadding().encodeToString(input); + } + + @Override + public byte[] encode(byte[] input) { + return java.util.Base64.getEncoder().encode(input); + } + + }); + + Security.addProvider(new BouncyCastleProvider()); + } +} diff --git a/domain/src/test/java/org/jivesoftware/smackx/ikey/element/XmlSecElementCanonicalizerTest.java b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/XmlSecElementCanonicalizerTest.java index a99ea72..e2002d0 100644 --- a/domain/src/test/java/org/jivesoftware/smackx/ikey/element/XmlSecElementCanonicalizerTest.java +++ b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/XmlSecElementCanonicalizerTest.java @@ -5,9 +5,8 @@ import org.apache.xml.security.c14n.CanonicalizationException; import org.apache.xml.security.c14n.Canonicalizer; import org.apache.xml.security.c14n.InvalidCanonicalizerException; import org.apache.xml.security.parser.XMLParserException; -import org.jivesoftware.smackx.ikey.XmlSecElementCanonicalizer; +import org.jivesoftware.smackx.ikey.util.canonicalization.XmlSecElementCanonicalizer; import org.junit.BeforeClass; -import org.junit.Ignore; import org.junit.Test; import java.io.IOException; @@ -24,7 +23,6 @@ public class XmlSecElementCanonicalizerTest { } @Test - @Ignore public void test() throws InvalidCanonicalizerException, XMLParserException, IOException, CanonicalizationException { XmlSecElementCanonicalizer canonicalizer = new XmlSecElementCanonicalizer(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS)); String element = "" + @@ -33,18 +31,16 @@ public class XmlSecElementCanonicalizerTest { "iF4EABMIAAYFAl9I3esACgkQNZFg0LCGhWntNAD+LDO/Q+WQ5TrQOt4vBcqnUarCOZ6Ev4Wp4QgsIjs2BHcA/2BOIC6FBqkx80zB8NZsZu4H1fvn+gWgrscXhgf9+f+h" + ""; String elementWithInsignificantWhitespace = "" + - "" + - " " + + "\n" + + " \n" + " " + - " " + - " " + - " iF4EABMIAAYFAl9I3esACgkQNZFg0LCGhWntNAD+LDO/Q+WQ5TrQOt4vBcqnUarCOZ6Ev4Wp4QgsIjs2BHcA/2BOIC6FBqkx80zB8NZsZu4H1fvn+gWgrscXhgf9+f+h" + - " " + + " fpr='34669E842617A0D38A96016B359160D0B0868569'/>\n" + + " \n" + + " iF4EABMIAAYFAl9I3esACgkQNZFg0LCGhWntNAD+LDO/Q+WQ5TrQOt4vBcqnUarCOZ6Ev4Wp4QgsIjs2BHcA/2BOIC6FBqkx80zB8NZsZu4H1fvn+gWgrscXhgf9+f+h\n" + ""; - String can1 = new String(canonicalizer.canonicalize(element)); - String can2 = new String(canonicalizer.canonicalize(elementWithInsignificantWhitespace)); + String can1 = canonicalizer.removeInterElementWhitespace(new String(canonicalizer.canonicalize(element))); + String can2 = canonicalizer.removeInterElementWhitespace(new String(canonicalizer.canonicalize(elementWithInsignificantWhitespace))); assertEquals(can1, can2); } }