From be59c09b157d06898c5a724f9d16269fca52f17d Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 25 Sep 2020 12:58:27 +0200 Subject: [PATCH] Migrate ikey away from xmlsec --- domain/build.gradle | 3 - .../jivesoftware/smackx/ikey/IkeyManager.java | 32 ++----- .../smackx/ikey/IkeySignatureCreator.java | 17 ++-- .../smackx/ikey/IkeySignatureVerifier.java | 13 +-- .../smackx/ikey/element/IkeyElement.java | 16 ++-- .../smackx/ikey/element/SignedElement.java | 80 ++++++++++++++++ .../ikey/element/SubordinateListElement.java | 17 +++- .../ikey/provider/IkeyElementProvider.java | 22 ++--- .../SubordinateListElementProvider.java | 45 +++++++++ .../ikey/{ => record}/FileBasedIkeyStore.java | 3 +- .../smackx/ikey/{ => record}/IkeyStore.java | 2 +- .../ElementCanonicalizer.java | 26 ------ .../XmlSecElementCanonicalizer.java | 25 ----- .../EncapsulatingSignatureManager.java | 20 ++++ .../signed/xep0285/element/DataElement.java | 58 ++++++++++++ .../signed/xep0285/element/PlainElement.java | 92 +++++++++++++++++++ .../xep0285/element/SignatureElement.java | 56 +++++++++++ .../signed/xep0285/element/SignedElement.java | 45 +++++++++ .../provider/PlainElementProvider.java | 20 ++++ .../provider/SignedElementProvider.java | 45 +++++++++ .../signed/xep0285/signing/OpenPGPSigner.java | 74 +++++++++++++++ .../smackx/signed/xep0285/signing/Signer.java | 12 +++ .../smackx/util/MercurySmackTestSuite.java | 41 +++++++++ .../messenger/core/di/module/IkeyModule.java | 35 ------- .../smackx/ikey/element/IkeyElementTest.java | 46 +--------- .../IkeySignatureCreatorAndVerifierTest.java | 29 ++---- .../XmlSecElementCanonicalizerTest.java | 47 ---------- .../signed/xep0285/PlainElementTest.java | 42 +++++++++ .../signed/xep0285/SignedElementTest.java | 40 ++++++++ libs/Smack | 2 +- 30 files changed, 736 insertions(+), 269 deletions(-) create mode 100644 domain/src/main/java/org/jivesoftware/smackx/ikey/element/SignedElement.java create mode 100644 domain/src/main/java/org/jivesoftware/smackx/ikey/provider/SubordinateListElementProvider.java rename domain/src/main/java/org/jivesoftware/smackx/ikey/{ => record}/FileBasedIkeyStore.java (97%) rename domain/src/main/java/org/jivesoftware/smackx/ikey/{ => record}/IkeyStore.java (87%) delete mode 100644 domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/ElementCanonicalizer.java delete mode 100644 domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/XmlSecElementCanonicalizer.java create mode 100644 domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/EncapsulatingSignatureManager.java create mode 100644 domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/DataElement.java create mode 100644 domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/PlainElement.java create mode 100644 domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/SignatureElement.java create mode 100644 domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/SignedElement.java create mode 100644 domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/provider/PlainElementProvider.java create mode 100644 domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/provider/SignedElementProvider.java create mode 100644 domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/signing/OpenPGPSigner.java create mode 100644 domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/signing/Signer.java delete mode 100644 domain/src/main/java/org/mercury_im/messenger/core/di/module/IkeyModule.java delete mode 100644 domain/src/test/java/org/jivesoftware/smackx/ikey/element/XmlSecElementCanonicalizerTest.java create mode 100644 domain/src/test/java/org/jivesoftware/smackx/signed/xep0285/PlainElementTest.java create mode 100644 domain/src/test/java/org/jivesoftware/smackx/signed/xep0285/SignedElementTest.java diff --git a/domain/build.gradle b/domain/build.gradle index f6e291f..31be99d 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -21,9 +21,6 @@ dependencies { api "org.jxmpp:jxmpp-core:1.0.1" api "org.jxmpp:jxmpp-jid:1.0.1" - api "org.apache.santuario:xmlsec:2.2.0" - - testImplementation "org.igniterealtime.smack:smack-java7:$smackJava7Version" // RxJava2 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 a1f3eef..291ae7d 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java @@ -1,22 +1,16 @@ 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.packet.Message; -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.record.IkeyStore; 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; @@ -41,15 +35,9 @@ 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) { @@ -106,9 +94,9 @@ public final class IkeyManager extends Manager { } private void processIkeyElement(EntityBareJid from, IkeyElement element) - throws XMLParserException, IOException, CanonicalizationException, UnsupportedSignatureAlgorithmException { + throws IOException, UnsupportedSignatureAlgorithmException { if (isFromTheFuture(element)) { - LOGGER.log(Level.WARNING, "Received ikey element appears to be from the future: " + element.getSubordinates().getTimestamp()); + LOGGER.log(Level.WARNING, "Received ikey element appears to be from the future: " + element.getSignedElement().getChildElement().getTimestamp()); return; } @@ -126,9 +114,9 @@ public final class IkeyManager extends Manager { } private boolean verifyIkeyElement(EntityBareJid from, IkeyElement element) - throws XMLParserException, IOException, CanonicalizationException, UnsupportedSignatureAlgorithmException { + throws IOException, UnsupportedSignatureAlgorithmException { IkeySignatureVerificationMechanism verificationMechanism = getSignatureVerificationMechanismFor(element); - IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism, canonicalizer); + IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism); return verifier.verify(element, from); } @@ -146,18 +134,18 @@ public final class IkeyManager extends Manager { } private static boolean isFromTheFuture(IkeyElement element) { - Date elementTimestamp = element.getSubordinates().getTimestamp(); + Date elementTimestamp = element.getSignedElement().getChildElement().getTimestamp(); Date now = new Date(); return elementTimestamp.after(now); } private boolean existsSameOrNewerRecord(IkeyElement ikeyElement) throws IOException { - IkeyElement existingRecord = store.loadIkeyRecord(ikeyElement.getSubordinates().getJid()); + IkeyElement existingRecord = store.loadIkeyRecord(ikeyElement.getSignedElement().getChildElement().getJid()); if (existingRecord == null) { return false; } - Date latestTimestamp = existingRecord.getSubordinates().getTimestamp(); - Date eventTimestamp = ikeyElement.getSubordinates().getTimestamp(); + Date latestTimestamp = existingRecord.getSignedElement().getChildElement().getTimestamp(); + Date eventTimestamp = ikeyElement.getSignedElement().getChildElement().getTimestamp(); return latestTimestamp.equals(eventTimestamp) // same || latestTimestamp.after(eventTimestamp); // newer } @@ -168,7 +156,7 @@ public final class IkeyManager extends Manager { public void onPepEvent(EntityBareJid from, IkeyElement event, String id, Message carrierMessage) { try { processIkeyElement(from, event); - } catch (XMLParserException | CanonicalizationException | IOException | UnsupportedSignatureAlgorithmException e) { + } catch (IOException | UnsupportedSignatureAlgorithmException e) { LOGGER.log(Level.WARNING, "Error:", e); } } 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 fb407a6..952fcb9 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreator.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreator.java @@ -1,29 +1,26 @@ package org.jivesoftware.smackx.ikey; -import org.apache.xml.security.c14n.CanonicalizationException; -import org.apache.xml.security.parser.XMLParserException; -import org.bouncycastle.util.encoders.Base64; +import org.jivesoftware.smack.util.stringencoder.Base64; import org.jivesoftware.smackx.ikey.element.ProofElement; +import org.jivesoftware.smackx.ikey.element.SignedElement; 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; public class IkeySignatureCreator { private final IkeySignatureCreationMechanism signatureCreationMechanism; - private final XmlSecElementCanonicalizer elementCanonicalizer; - public IkeySignatureCreator(IkeySignatureCreationMechanism signingMechanism, XmlSecElementCanonicalizer elementCanonicalizer) { + public IkeySignatureCreator(IkeySignatureCreationMechanism signingMechanism) { this.signatureCreationMechanism = signingMechanism; - this.elementCanonicalizer = elementCanonicalizer; } - public ProofElement createProofFor(SubordinateListElement subordinateListElement) throws XMLParserException, IOException, CanonicalizationException { - byte[] canonicalized = elementCanonicalizer.canonicalize(subordinateListElement); + public ProofElement createProofFor(SubordinateListElement subordinateListElement) + throws IOException { + byte[] canonicalized = new SignedElement(subordinateListElement).getUtf8Bytes(); byte[] signature = signatureCreationMechanism.createSignature(canonicalized); - return new ProofElement(Base64.toBase64String(signature)); + return new ProofElement(Base64.encodeToString(signature)); } } 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 6e65cde..95ed7cb 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerifier.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerifier.java @@ -1,11 +1,8 @@ package org.jivesoftware.smackx.ikey; -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; @@ -13,26 +10,24 @@ import java.io.IOException; public class IkeySignatureVerifier { private final IkeySignatureVerificationMechanism signatureVerificationMechanism; - private final ElementCanonicalizer elementCanonicalizer; - public IkeySignatureVerifier(IkeySignatureVerificationMechanism signatureVerificationMechanism, ElementCanonicalizer elementCanonicalizer) { + public IkeySignatureVerifier(IkeySignatureVerificationMechanism signatureVerificationMechanism) { this.signatureVerificationMechanism = signatureVerificationMechanism; - this.elementCanonicalizer = elementCanonicalizer; } public boolean verify(IkeyElement element, EntityBareJid owner) - throws XMLParserException, IOException, CanonicalizationException { + throws IOException { throwIfMismatchingMechanism(element); throwIfMismatchingOwnerJid(element, owner); - byte[] canonicalizedXml = elementCanonicalizer.canonicalize(element.getSubordinates()); + byte[] canonicalizedXml = element.getSignedElement().getUtf8Bytes(); byte[] signature = Base64.decode(element.getProof().getBase64Signature()); return signatureVerificationMechanism.isSignatureValid(canonicalizedXml, signature); } private static void throwIfMismatchingOwnerJid(IkeyElement element, EntityBareJid owner) { - if (!element.getSubordinates().getJid().equals(owner)) { + if (!element.getSignedElement().getChildElement().getJid().equals(owner)) { throw new IllegalArgumentException("Provided ikey element does not contain jid of " + owner); } } 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 78f5395..994b2b8 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 @@ -21,13 +21,13 @@ public class IkeyElement implements ExtensionElement { private final IkeyType type; private final SuperordinateElement superordinate; - private final SubordinateListElement subordinates; + private final SignedElement signedElement; private final ProofElement proof; - public IkeyElement(IkeyType type, SuperordinateElement superordinate, SubordinateListElement subordinates, ProofElement proof) { + public IkeyElement(IkeyType type, SuperordinateElement superordinate, SignedElement signedElement, ProofElement proof) { this.type = type; this.superordinate = superordinate; - this.subordinates = subordinates; + this.signedElement = signedElement; this.proof = proof; } @@ -39,8 +39,8 @@ public class IkeyElement implements ExtensionElement { return superordinate; } - public SubordinateListElement getSubordinates() { - return subordinates; + public SignedElement getSignedElement() { + return signedElement; } public ProofElement getProof() { @@ -63,7 +63,7 @@ public class IkeyElement implements ExtensionElement { .attribute(ATTR_IKEY_TYPE, getType()) .rightAngleBracket() .append(getSuperordinate()) - .append(getSubordinates()) + .append(getSignedElement()) .append(getProof()) .closeElement(this); } @@ -74,7 +74,7 @@ public class IkeyElement implements ExtensionElement { .append(getElementName()) .append(getType()) .append(getSuperordinate()) - .append(getSubordinates()) + .append(getSignedElement()) .append(getProof()) .build(); } @@ -85,7 +85,7 @@ public class IkeyElement implements ExtensionElement { .append(getElementName(), o.getElementName()) .append(getType(), o.getType()) .append(getSuperordinate(), o.getSuperordinate()) - .append(getSubordinates(), o.getSubordinates()) + .append(getSignedElement(), o.getSignedElement()) .append(getProof(), o.getProof())); } } diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SignedElement.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SignedElement.java new file mode 100644 index 0000000..d38e4ad --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SignedElement.java @@ -0,0 +1,80 @@ +package org.jivesoftware.smackx.ikey.element; + +import org.jivesoftware.smack.packet.NamedElement; +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.util.EqualsUtil; +import org.jivesoftware.smack.util.HashCode; +import org.jivesoftware.smack.util.Objects; +import org.jivesoftware.smack.util.PacketParserUtils; +import org.jivesoftware.smack.util.XmlStringBuilder; +import org.jivesoftware.smack.util.stringencoder.Base64; +import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.ikey.provider.SubordinateListElementProvider; + +import java.io.IOException; + +public class SignedElement implements NamedElement { + + public static final String ELEMENT = "signed"; + + private final String base64Encoded; + private final SubordinateListElement childElement; + private final byte[] utf8Bytes; + + public SignedElement(SubordinateListElement childElement) { + this(childElement, null); + } + + public SignedElement(String base64Encoded) throws IOException, XmlPullParserException, SmackParsingException { + this(SubordinateListElementProvider.INSTANCE.parse( + PacketParserUtils.getParserFor(Base64.decodeToString(base64Encoded))), + base64Encoded); + } + + private SignedElement(SubordinateListElement childElement, String base64Encoded) { + this.childElement = Objects.requireNonNull(childElement); + this.base64Encoded = base64Encoded == null ? childElement.toBase64EncodedString() : base64Encoded; + this.utf8Bytes = Base64.decode(this.base64Encoded); + } + + public String getBase64Encoded() { + return base64Encoded; + } + + public byte[] getUtf8Bytes() { + return utf8Bytes; + } + + public SubordinateListElement getChildElement() { + return childElement; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { + return new XmlStringBuilder(this) + .rightAngleBracket() + .append(getBase64Encoded()) + .closeElement(this); + } + + @Override + public int hashCode() { + return HashCode.builder() + .append(getElementName()) + .append(getBase64Encoded()) + .build(); + } + + @Override + public boolean equals(Object other) { + return EqualsUtil.equals(this, other, (e, o) -> e + .append(getElementName(), o.getElementName()) + .append(getBase64Encoded(), o.getBase64Encoded())); + } +} 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 0a64438..57a4b42 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 @@ -1,23 +1,27 @@ package org.jivesoftware.smackx.ikey.element; +import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.NamedElement; import org.jivesoftware.smack.packet.XmlEnvironment; import org.jivesoftware.smack.util.EqualsUtil; import org.jivesoftware.smack.util.HashCode; import org.jivesoftware.smack.util.Objects; +import org.jivesoftware.smack.util.ParserUtils; import org.jivesoftware.smack.util.XmlStringBuilder; +import org.jivesoftware.smack.util.stringencoder.Base64; import org.jivesoftware.smackx.ikey.util.IkeyConstants; import org.jxmpp.jid.EntityBareJid; +import java.nio.charset.StandardCharsets; import java.util.Date; import java.util.List; -public class SubordinateListElement implements NamedElement { +public class SubordinateListElement implements ExtensionElement { 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"; + public static final String ATTR_STAMP = "stamp"; private final List subordinates; private final EntityBareJid jid; @@ -41,6 +45,10 @@ public class SubordinateListElement implements NamedElement { return subordinates; } + public String toBase64EncodedString() { + return Base64.encodeToString(toXML().toString().getBytes(StandardCharsets.UTF_8)); + } + @Override public String getElementName() { return ELEMENT; @@ -74,4 +82,9 @@ public class SubordinateListElement implements NamedElement { .append(getTimestamp(), o.getTimestamp()) .append(getSubordinates(), o.getSubordinates())); } + + @Override + public String getNamespace() { + return NAMESPACE; + } } 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 index 03fc3e6..babbf95 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/provider/IkeyElementProvider.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/provider/IkeyElementProvider.java @@ -3,10 +3,13 @@ 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.PacketParserUtils; import org.jivesoftware.smack.util.ParserUtils; +import org.jivesoftware.smack.util.stringencoder.Base64; 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.SignedElement; import org.jivesoftware.smackx.ikey.element.ProofElement; import org.jivesoftware.smackx.ikey.element.SubordinateElement; import org.jivesoftware.smackx.ikey.element.SubordinateListElement; @@ -30,9 +33,7 @@ public class IkeyElementProvider extends ExtensionElementProvider { String typeString = ParserUtils.getRequiredAttribute(parser, IkeyElement.ATTR_IKEY_TYPE); IkeyType type = IkeyType.valueOf(typeString); SuperordinateElement superordinate = null; - List subordinates = new ArrayList<>(); - EntityBareJid jid = null; - Date timestamp = null; + SignedElement signedElement = null; ProofElement proofElement = null; do { @@ -44,17 +45,8 @@ public class IkeyElementProvider extends ExtensionElementProvider { 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 SignedElement.ELEMENT: + signedElement = new SignedElement(parser.nextText()); break; case ProofElement.ELEMENT: @@ -66,6 +58,6 @@ public class IkeyElementProvider extends ExtensionElementProvider { break; } } while (parser.getDepth() != initialDepth); - return new IkeyElement(type, superordinate, new SubordinateListElement(jid, timestamp, subordinates), proofElement); + return new IkeyElement(type, superordinate, signedElement, proofElement); } } diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/provider/SubordinateListElementProvider.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/provider/SubordinateListElementProvider.java new file mode 100644 index 0000000..c08e53e --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/provider/SubordinateListElementProvider.java @@ -0,0 +1,45 @@ +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.SubordinateElement; +import org.jivesoftware.smackx.ikey.element.SubordinateListElement; +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 SubordinateListElementProvider extends ExtensionElementProvider { + + public static final SubordinateListElementProvider INSTANCE = new SubordinateListElementProvider(); + + @Override + public SubordinateListElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) + throws XmlPullParserException, IOException, SmackParsingException { + Date timestamp = ParserUtils.getDateFromXep82String(ParserUtils.getRequiredAttribute(parser, SubordinateListElement.ATTR_STAMP)); + EntityBareJid jid = ParserUtils.getBareJidAttribute(parser); + List subordinates = new ArrayList<>(); + do { + switch (parser.nextTag()) { + case START_ELEMENT: + if (SubordinateElement.ELEMENT.equals(parser.getName())) { + 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)); + } + break; + case END_ELEMENT: + break; + } + } while (parser.getDepth() != initialDepth); + return new SubordinateListElement(jid, timestamp, subordinates); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/FileBasedIkeyStore.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/record/FileBasedIkeyStore.java similarity index 97% rename from domain/src/main/java/org/jivesoftware/smackx/ikey/FileBasedIkeyStore.java rename to domain/src/main/java/org/jivesoftware/smackx/ikey/record/FileBasedIkeyStore.java index a561537..aca14ea 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/FileBasedIkeyStore.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/record/FileBasedIkeyStore.java @@ -1,4 +1,4 @@ -package org.jivesoftware.smackx.ikey; +package org.jivesoftware.smackx.ikey.record; import org.jivesoftware.smack.parsing.SmackParsingException; import org.jivesoftware.smack.util.Objects; @@ -7,6 +7,7 @@ import org.jivesoftware.smack.xml.XmlPullParser; import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smackx.ikey.element.IkeyElement; import org.jivesoftware.smackx.ikey.provider.IkeyElementProvider; +import org.jivesoftware.smackx.ikey.record.IkeyStore; import org.jxmpp.jid.EntityBareJid; import java.io.BufferedReader; diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyStore.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/record/IkeyStore.java similarity index 87% rename from domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyStore.java rename to domain/src/main/java/org/jivesoftware/smackx/ikey/record/IkeyStore.java index c5ee92f..8a6519b 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyStore.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/record/IkeyStore.java @@ -1,4 +1,4 @@ -package org.jivesoftware.smackx.ikey; +package org.jivesoftware.smackx.ikey.record; import org.jivesoftware.smackx.ikey.element.IkeyElement; import org.jxmpp.jid.EntityBareJid; diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/ElementCanonicalizer.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/ElementCanonicalizer.java deleted file mode 100644 index 10f171b..0000000 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/ElementCanonicalizer.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.jivesoftware.smackx.ikey.util.canonicalization; - -import org.apache.xml.security.c14n.CanonicalizationException; -import org.apache.xml.security.parser.XMLParserException; -import org.jivesoftware.smack.packet.Element; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -public interface ElementCanonicalizer { - - default byte[] canonicalize(Element e) throws XMLParserException, IOException, CanonicalizationException { - String xml = e.toXML().toString(); - return canonicalize(xml); - } - - default byte[] canonicalize(CharSequence xml) throws XMLParserException, IOException, CanonicalizationException { - return canonicalize(xml.toString().getBytes(StandardCharsets.UTF_8)); - } - - 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/util/canonicalization/XmlSecElementCanonicalizer.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/XmlSecElementCanonicalizer.java deleted file mode 100644 index 6496bfc..0000000 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/util/canonicalization/XmlSecElementCanonicalizer.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.jivesoftware.smackx.ikey.util.canonicalization; - -import org.apache.xml.security.c14n.CanonicalizationException; -import org.apache.xml.security.c14n.Canonicalizer; -import org.apache.xml.security.parser.XMLParserException; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - - -public class XmlSecElementCanonicalizer implements ElementCanonicalizer { - - private final Canonicalizer canonicalizer; - - public XmlSecElementCanonicalizer(Canonicalizer canonicalizer) { - this.canonicalizer = canonicalizer; - } - - @Override - public byte[] canonicalize(byte[] xml) throws XMLParserException, IOException, CanonicalizationException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - canonicalizer.canonicalize(xml, out, true); - return out.toByteArray(); - } -} diff --git a/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/EncapsulatingSignatureManager.java b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/EncapsulatingSignatureManager.java new file mode 100644 index 0000000..4ba2dd8 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/EncapsulatingSignatureManager.java @@ -0,0 +1,20 @@ +package org.jivesoftware.smackx.signed.xep0285; + +import org.bouncycastle.openpgp.PGPSignature; +import org.jivesoftware.smackx.signed.xep0285.element.DataElement; +import org.jivesoftware.smackx.signed.xep0285.element.PlainElement; +import org.jivesoftware.smackx.signed.xep0285.element.SignatureElement; +import org.jivesoftware.smackx.signed.xep0285.element.SignedElement; +import org.jivesoftware.smackx.signed.xep0285.signing.Signer; + +import java.io.IOException; + +public class EncapsulatingSignatureManager { + + public static SignedElement createSignedElement(PlainElement plainElement, Signer signer) throws IOException { + DataElement dataElement = DataElement.fromPlainElement(plainElement); + PGPSignature signature = signer.createSignature(dataElement.getUtf8Bytes()); + SignatureElement signatureElement = SignatureElement.fromBytes(signer.getAlgorithmName(signature), signature.getEncoded()); + return new SignedElement(signatureElement, dataElement); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/DataElement.java b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/DataElement.java new file mode 100644 index 0000000..024d213 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/DataElement.java @@ -0,0 +1,58 @@ +package org.jivesoftware.smackx.signed.xep0285.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; + +import java.nio.charset.StandardCharsets; + +public final class DataElement implements NamedElement { + + public static final String ELEMENT = "data"; + + private final String base64Content; + private final String utf8Content; + + private DataElement(String utf8Content, String base64Content) { + this.utf8Content = utf8Content; + this.base64Content = base64Content; + } + + public static DataElement fromUtf8Content(String utf8Content) { + return new DataElement(utf8Content, Base64.encodeToString(utf8Content.getBytes(StandardCharsets.UTF_8))); + } + + public static DataElement fromBase64Content(String base64Content) { + return new DataElement(Base64.decodeToString(base64Content), base64Content); + } + + public static DataElement fromPlainElement(PlainElement element) { + return fromUtf8Content(element.toXML().toString()); + } + + public String getUtf8Content() { + return utf8Content; + } + + public byte[] getUtf8Bytes() { + return getUtf8Content().getBytes(StandardCharsets.UTF_8); + } + + public String getBase64Content() { + return base64Content; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { + return new XmlStringBuilder(this) + .rightAngleBracket() + .append(getBase64Content()) + .closeElement(this); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/PlainElement.java b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/PlainElement.java new file mode 100644 index 0000000..67e5181 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/PlainElement.java @@ -0,0 +1,92 @@ +package org.jivesoftware.smackx.signed.xep0285.element; + +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.util.EqualsUtil; +import org.jivesoftware.smack.util.HashCode; +import org.jivesoftware.smack.util.XmlStringBuilder; +import org.jivesoftware.smack.util.stringencoder.Base64; + +import java.nio.charset.StandardCharsets; +import java.util.Date; + +public final class PlainElement implements ExtensionElement { + + public static final String ELEMENT = "plain"; + public static final String NAMESPACE = "urn:xmpp:signed:0"; + public static final String ATTR_TIMESTAMP = "timestamp"; + + private final String utf8Content; + private final String base64Content; + private final Date timestamp; + + private PlainElement(String utf8Content, String base64Content, Date timestamp) { + this.utf8Content = utf8Content; + this.base64Content = base64Content; + this.timestamp = timestamp; + } + + public static PlainElement fromExtensionElement(ExtensionElement element) { + return fromUtf8String(element.toXML().toString(), new Date()); + } + + public static PlainElement fromUtf8String(String utf8, Date timestamp) { + return new PlainElement(utf8, Base64.encodeToString(utf8.getBytes(StandardCharsets.UTF_8)), timestamp); + } + + public static PlainElement fromBase64String(String base64, Date timestamp) { + return new PlainElement(Base64.decodeToString(base64), base64, timestamp); + } + + public String getBase64Content() { + return base64Content; + } + + public String getUtf8Content() { + return utf8Content; + } + + public byte[] asUtf8Bytes() { + return toXML().toString().getBytes(StandardCharsets.UTF_8); + } + + public Date getTimestamp() { + return timestamp; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { + return new XmlStringBuilder(this) + .attribute(ATTR_TIMESTAMP, getTimestamp()) + .rightAngleBracket() + .append(getBase64Content()) + .closeElement(this); + } + + @Override + public boolean equals(Object other) { + return EqualsUtil.equals(this, other, (e, o) -> e + .append(getElementName(), o.getElementName()) + .append(getBase64Content(), o.getBase64Content()) + .append(getTimestamp(), o.getTimestamp())); + } + + @Override + public int hashCode() { + return HashCode.builder() + .append(getElementName()) + .append(getBase64Content()) + .append(getTimestamp()) + .build(); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/SignatureElement.java b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/SignatureElement.java new file mode 100644 index 0000000..0cea166 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/SignatureElement.java @@ -0,0 +1,56 @@ +package org.jivesoftware.smackx.signed.xep0285.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 final class SignatureElement implements NamedElement { + + public static final String ELEMENT = "signature"; + public static final String ATTR_ALGORITHM = "algorithm"; + + private final String algorithm; + private final byte[] bytes; + private final String base64Content; + + private SignatureElement(String algorithm, byte[] bytes, String base64Content) { + this.algorithm = algorithm; + this.bytes = bytes; + this.base64Content = base64Content; + } + + public static SignatureElement fromBytes(String algorithm, byte[] bytes) { + return new SignatureElement(algorithm, bytes, Base64.encodeToString(bytes)); + } + + public static SignatureElement fromBase64Content(String algorithm, String base64Content) { + return new SignatureElement(algorithm, Base64.decode(base64Content), base64Content); + } + + public String getAlgorithm() { + return algorithm; + } + + public String getBase64Content() { + return base64Content; + } + + public byte[] getBytes() { + return bytes.clone(); + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { + return new XmlStringBuilder(this) + .attribute(ATTR_ALGORITHM, getAlgorithm()) + .rightAngleBracket() + .append(getBase64Content()) + .closeElement(this); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/SignedElement.java b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/SignedElement.java new file mode 100644 index 0000000..6647035 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/element/SignedElement.java @@ -0,0 +1,45 @@ +package org.jivesoftware.smackx.signed.xep0285.element; + +import org.jivesoftware.smack.packet.ExtensionElement; +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.util.XmlStringBuilder; + +public class SignedElement implements ExtensionElement { + + public static final String ELEMENT = "signed"; + + private final SignatureElement signature; + private final DataElement data; + + public SignedElement(SignatureElement signature, DataElement data) { + this.signature = signature; + this.data = data; + } + + public SignatureElement getSignature() { + return signature; + } + + public DataElement getData() { + return data; + } + + @Override + public String getNamespace() { + return PlainElement.NAMESPACE; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { + return new XmlStringBuilder(this) + .rightAngleBracket() + .append(getSignature()) + .append(getData()) + .closeElement(this); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/provider/PlainElementProvider.java b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/provider/PlainElementProvider.java new file mode 100644 index 0000000..3f8040d --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/provider/PlainElementProvider.java @@ -0,0 +1,20 @@ +package org.jivesoftware.smackx.signed.xep0285.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.signed.xep0285.element.PlainElement; + +import java.io.IOException; + +public class PlainElementProvider extends ExtensionElementProvider { + @Override + public PlainElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) + throws XmlPullParserException, IOException, SmackParsingException { + String timestamp = ParserUtils.getRequiredAttribute(parser, PlainElement.ATTR_TIMESTAMP); + return PlainElement.fromBase64String(parser.nextText(), ParserUtils.getDateFromXep82String(timestamp)); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/provider/SignedElementProvider.java b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/provider/SignedElementProvider.java new file mode 100644 index 0000000..3330e23 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/provider/SignedElementProvider.java @@ -0,0 +1,45 @@ +package org.jivesoftware.smackx.signed.xep0285.provider; + +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.provider.ExtensionElementProvider; +import org.jivesoftware.smack.util.Objects; +import org.jivesoftware.smack.util.ParserUtils; +import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.signed.xep0285.element.DataElement; +import org.jivesoftware.smackx.signed.xep0285.element.SignatureElement; +import org.jivesoftware.smackx.signed.xep0285.element.SignedElement; + +import java.io.IOException; + +public class SignedElementProvider extends ExtensionElementProvider { + + @Override + public SignedElement parse(XmlPullParser parser, int initialDepth, XmlEnvironment xmlEnvironment) + throws XmlPullParserException, IOException { + + SignatureElement signature = null; + DataElement data = null; + + XmlPullParser.TagEvent tag; + String name; + do { + tag = parser.nextTag(); + name = parser.getName(); + if (tag == XmlPullParser.TagEvent.START_ELEMENT) { + switch (name) { + case SignatureElement.ELEMENT: + String algorithm = ParserUtils.getRequiredAttribute(parser, SignatureElement.ATTR_ALGORITHM); + signature = SignatureElement.fromBase64Content(algorithm, parser.nextText()); + break; + + case DataElement.ELEMENT: + data = DataElement.fromBase64Content(parser.nextText()); + break; + } + } + } while (parser.getDepth() != initialDepth); + + return new SignedElement(Objects.requireNonNull(signature), Objects.requireNonNull(data)); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/signing/OpenPGPSigner.java b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/signing/OpenPGPSigner.java new file mode 100644 index 0000000..4f5ecbc --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/signing/OpenPGPSigner.java @@ -0,0 +1,74 @@ +package org.jivesoftware.smackx.signed.xep0285.signing; + +import org.bouncycastle.openpgp.PGPException; +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.bouncycastle.openpgp.PGPSignature; +import org.bouncycastle.util.io.Streams; +import org.pgpainless.PGPainless; +import org.pgpainless.algorithm.HashAlgorithm; +import org.pgpainless.algorithm.PublicKeyAlgorithm; +import org.pgpainless.decryption_verification.OpenPgpMetadata; +import org.pgpainless.encryption_signing.EncryptionStream; +import org.pgpainless.key.protection.SecretKeyRingProtector; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class OpenPGPSigner implements Signer { + + private final SecretKeyRingProtector protector; + private final PGPSecretKeyRing secretKey; + + public OpenPGPSigner(PGPSecretKeyRing secretKey, SecretKeyRingProtector protector) { + this.protector = protector; + this.secretKey = secretKey; + } + + @Override + public PGPSignature createSignature(byte[] data) throws IOException { + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ByteArrayInputStream inputStream = new ByteArrayInputStream(data); + try { + EncryptionStream signer = PGPainless.createEncryptor().onOutputStream(outputStream) + .doNotEncrypt() + .createDetachedSignature() + .signWith(protector, secretKey) + .noArmor(); + Streams.pipeAll(inputStream, signer); + signer.close(); + inputStream.close(); + outputStream.close(); + OpenPgpMetadata metadata = signer.getResult(); + return metadata.getSignatures().iterator().next(); + + } catch (PGPException e) { + throw new IOException(e); + } + } + + @Override + public String getAlgorithmName(PGPSignature signature) { + PublicKeyAlgorithm signAlgo = PublicKeyAlgorithm.fromId(signature.getKeyAlgorithm()); + + String signAlgoName; + switch (signAlgo) { + case RSA_GENERAL: + case RSA_ENCRYPT: + case RSA_SIGN: + signAlgoName = "RSA"; + break; + case DSA: + signAlgoName = "DSA"; + break; + case ECDSA: + signAlgoName = "ECDSA"; + break; + default: + throw new IllegalArgumentException("Signature was signed with unknown signature algorithm: " + signature.getKeyAlgorithm()); + } + + signAlgoName += HashAlgorithm.fromId(signature.getHashAlgorithm()); + return signAlgoName; + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/signing/Signer.java b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/signing/Signer.java new file mode 100644 index 0000000..36bdacc --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/signed/xep0285/signing/Signer.java @@ -0,0 +1,12 @@ +package org.jivesoftware.smackx.signed.xep0285.signing; + +import org.bouncycastle.openpgp.PGPSignature; + +import java.io.IOException; + +public interface Signer { + + PGPSignature createSignature(byte[] data) throws IOException; + + String getAlgorithmName(PGPSignature signature); +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/util/MercurySmackTestSuite.java b/domain/src/main/java/org/jivesoftware/smackx/util/MercurySmackTestSuite.java index 68f034d..72a75b9 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/util/MercurySmackTestSuite.java +++ b/domain/src/main/java/org/jivesoftware/smackx/util/MercurySmackTestSuite.java @@ -3,7 +3,13 @@ package org.jivesoftware.smackx.util; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.jivesoftware.smack.SmackConfiguration; import org.jivesoftware.smack.util.stringencoder.Base64; +import org.jivesoftware.smack.xml.SmackXmlParser; +import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.xml.XmlPullParserException; +import java.io.IOException; +import java.io.Reader; +import java.io.StringReader; import java.security.Security; public class MercurySmackTestSuite { @@ -35,4 +41,39 @@ public class MercurySmackTestSuite { Security.addProvider(new BouncyCastleProvider()); } + + public static XmlPullParser getParser(String string) { + return getParser(string, null); + } + + public static XmlPullParser getParser(String string, String startTag) { + return getParser(new StringReader(string), startTag); + } + + private static XmlPullParser getParser(Reader reader, String startTag) { + XmlPullParser parser; + try { + parser = SmackXmlParser.newXmlParser(reader); + if (startTag == null) { + while (parser.getEventType() != XmlPullParser.Event.START_ELEMENT) { + parser.next(); + } + return parser; + } + boolean found = false; + + while (!found) { + if ((parser.next() == XmlPullParser.Event.START_ELEMENT) && parser.getName().equals(startTag)) + found = true; + } + + if (!found) + throw new IllegalArgumentException("Can not find start tag '" + startTag + "'"); + } catch (XmlPullParserException e) { + throw new RuntimeException(e); + } catch (IOException e) { + throw new RuntimeException(e); + } + return parser; + } } 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 deleted file mode 100644 index 762fde6..0000000 --- a/domain/src/main/java/org/mercury_im/messenger/core/di/module/IkeyModule.java +++ /dev/null @@ -1,35 +0,0 @@ -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 99961ea..0f78aa7 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,8 +1,7 @@ package org.jivesoftware.smackx.ikey.element; import org.jivesoftware.smack.parsing.SmackParsingException; -import org.jivesoftware.smack.xml.SmackXmlParser; -import org.jivesoftware.smack.xml.XmlPullParser; +import org.jivesoftware.smack.util.PacketParserUtils; import org.jivesoftware.smack.xml.XmlPullParserException; import org.jivesoftware.smackx.ikey.mechanism.IkeyType; import org.jivesoftware.smackx.ikey.provider.IkeyElementProvider; @@ -12,8 +11,6 @@ import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.impl.JidCreate; import java.io.IOException; -import java.io.Reader; -import java.io.StringReader; import java.net.URI; import java.net.URISyntaxException; import java.util.Arrays; @@ -44,53 +41,18 @@ public class IkeyElementTest extends MercurySmackTestSuite { ); ProofElement proof = new ProofElement("d2hpbGUgdGhpcyBpcyBub3QgYSB2YWxpZCBwcm9vZiwgaXQgaXMgc3VmZmljaWVudCBmb3IgdGVzdGluZy4="); - IkeyElement ikeyElement = new IkeyElement(type, superordinate, subordinates, proof); + IkeyElement ikeyElement = new IkeyElement(type, superordinate, new SignedElement(subordinates), proof); String xml = ikeyElement.toXML().toString(); System.out.println(xml); - IkeyElement parsed = IkeyElementProvider.INSTANCE.parse(getParser(xml)); + IkeyElement parsed = IkeyElementProvider.INSTANCE.parse(PacketParserUtils.getParserFor(xml)); assertEquals(ikeyElement, parsed); + assertEquals(subordinates, parsed.getSignedElement().getChildElement()); } - - private static SubordinateListElement buildSubListElement(EntityBareJid jid, Date date, SubordinateElement... subordinateElements) { return new SubordinateListElement(jid, date, Arrays.asList(subordinateElements)); } - public static XmlPullParser getParser(String string) { - return getParser(string, null); - } - - public static XmlPullParser getParser(String string, String startTag) { - return getParser(new StringReader(string), startTag); - } - - private static XmlPullParser getParser(Reader reader, String startTag) { - XmlPullParser parser; - try { - parser = SmackXmlParser.newXmlParser(reader); - if (startTag == null) { - while (parser.getEventType() != XmlPullParser.Event.START_ELEMENT) { - parser.next(); - } - return parser; - } - boolean found = false; - - while (!found) { - if ((parser.next() == XmlPullParser.Event.START_ELEMENT) && parser.getName().equals(startTag)) - found = true; - } - - if (!found) - throw new IllegalArgumentException("Can not find start tag '" + startTag + "'"); - } catch (XmlPullParserException e) { - throw new RuntimeException(e); - } catch (IOException e) { - throw new RuntimeException(e); - } - return parser; - } } 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 136fd6b..e24f9d7 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 @@ -1,23 +1,15 @@ package org.jivesoftware.smackx.ikey.element; -import org.apache.xml.security.Init; -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.PGPException; import org.jivesoftware.smack.util.stringencoder.Base64; -import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureCreationMechanism; import org.jivesoftware.smackx.ikey.IkeySignatureCreator; -import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureVerificationMechanism; import org.jivesoftware.smackx.ikey.IkeySignatureVerifier; +import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureCreationMechanism; +import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureVerificationMechanism; import org.jivesoftware.smackx.ikey.mechanism.IkeyType; -import org.jivesoftware.smackx.ikey.util.canonicalization.XmlSecElementCanonicalizer; import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureCreationMechanism; import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureVerificationMechanism; - import org.jivesoftware.smackx.util.MercurySmackTestSuite; -import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.jxmpp.jid.EntityBareJid; import org.jxmpp.jid.impl.JidCreate; @@ -43,15 +35,9 @@ import static org.junit.jupiter.api.Assertions.assertTrue; public class IkeySignatureCreatorAndVerifierTest extends MercurySmackTestSuite { - @BeforeAll - public static void initialize() { - if (!Init.isInitialized()) { - Init.init(); - } - } - @Test - public void createIkeyElementAndVerifySignature() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, URISyntaxException, XMLParserException, IOException, CanonicalizationException, InvalidCanonicalizerException { + public void createIkeyElementAndVerifySignature() + throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, URISyntaxException, IOException { EntityBareJid jid = JidCreate.entityBareFromOrThrowUnchecked("alice@wonderland.lit"); PGPKeyRing keyRing = PGPainless.generateKeyRing() .withMasterKey(KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256)) @@ -60,13 +46,12 @@ public class IkeySignatureCreatorAndVerifierTest extends MercurySmackTestSuite { .withPrimaryUserId("xmpp:" + jid) .withoutPassphrase() .build(); - XmlSecElementCanonicalizer elementCanonicalizer = new XmlSecElementCanonicalizer(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS)); IkeySignatureCreationMechanism signingMechanism = new OxIkeySignatureCreationMechanism( keyRing.getSecretKeys(), new UnprotectedKeysProtector()); - IkeySignatureCreator creator = new IkeySignatureCreator(signingMechanism, elementCanonicalizer); + IkeySignatureCreator creator = new IkeySignatureCreator(signingMechanism); IkeySignatureVerificationMechanism verificationMechanism = new OxIkeySignatureVerificationMechanism(keyRing.getPublicKeys()); - IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism, elementCanonicalizer); + IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism); OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(keyRing.getMasterKey()); SuperordinateElement superordinate = new SuperordinateElement(Base64.encodeToString(keyRing.getMasterKey().getEncoded())); @@ -77,7 +62,7 @@ public class IkeySignatureCreatorAndVerifierTest extends MercurySmackTestSuite { SubordinateListElement subs = new SubordinateListElement(jid, new Date(), subList); ProofElement proofElement = creator.createProofFor(subs); - IkeyElement ikeyElement = new IkeyElement(IkeyType.OX, superordinate, subs, proofElement); + IkeyElement ikeyElement = new IkeyElement(IkeyType.OX, superordinate, new SignedElement(subs), proofElement); System.out.println(ikeyElement.toXML().toString()); 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 deleted file mode 100644 index 1db9546..0000000 --- a/domain/src/test/java/org/jivesoftware/smackx/ikey/element/XmlSecElementCanonicalizerTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package org.jivesoftware.smackx.ikey.element; - -import org.apache.xml.security.Init; -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.util.canonicalization.XmlSecElementCanonicalizer; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; - -import java.io.IOException; -import java.nio.charset.StandardCharsets; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -public class XmlSecElementCanonicalizerTest { - - @BeforeAll - public static void initialize() { - if (!Init.isInitialized()) { - Init.init(); - } - } - - @Test - public void test() throws InvalidCanonicalizerException, XMLParserException, IOException, CanonicalizationException { - XmlSecElementCanonicalizer canonicalizer = new XmlSecElementCanonicalizer(Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N11_OMIT_COMMENTS)); - String element = "" + - "" + - "" + - "iF4EABMIAAYFAl9I3esACgkQNZFg0LCGhWntNAD+LDO/Q+WQ5TrQOt4vBcqnUarCOZ6Ev4Wp4QgsIjs2BHcA/2BOIC6FBqkx80zB8NZsZu4H1fvn+gWgrscXhgf9+f+h" + - ""; - String elementWithInsignificantWhitespace = "" + - "\n" + - " \n" + - " \n" + - " \n" + - " iF4EABMIAAYFAl9I3esACgkQNZFg0LCGhWntNAD+LDO/Q+WQ5TrQOt4vBcqnUarCOZ6Ev4Wp4QgsIjs2BHcA/2BOIC6FBqkx80zB8NZsZu4H1fvn+gWgrscXhgf9+f+h\n" + - ""; - String can1 = canonicalizer.removeInterElementWhitespace(new String(canonicalizer.canonicalize(element), StandardCharsets.UTF_8)); - String can2 = canonicalizer.removeInterElementWhitespace(new String(canonicalizer.canonicalize(elementWithInsignificantWhitespace), StandardCharsets.UTF_8)); - assertEquals(can1, can2); - } -} diff --git a/domain/src/test/java/org/jivesoftware/smackx/signed/xep0285/PlainElementTest.java b/domain/src/test/java/org/jivesoftware/smackx/signed/xep0285/PlainElementTest.java new file mode 100644 index 0000000..29cc44b --- /dev/null +++ b/domain/src/test/java/org/jivesoftware/smackx/signed/xep0285/PlainElementTest.java @@ -0,0 +1,42 @@ +package org.jivesoftware.smackx.signed.xep0285; + +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.util.ParserUtils; +import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.signed.xep0285.element.PlainElement; +import org.jivesoftware.smackx.signed.xep0285.provider.PlainElementProvider; +import org.jivesoftware.smackx.util.MercurySmackTestSuite; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class PlainElementTest extends MercurySmackTestSuite { + + @Test + public void test() throws XmlPullParserException, IOException, SmackParsingException { + String plain = "" + + "c6373824-a307-40dd-8fe0-bad6e7299ad0" + + "Wherefore art thou, Romeo?" + + ""; + String example2 = "" + + "PG1lc3NhZ2UgeG1sbnM9ImphYmJlcjpjbGllbnQiIGZyb209Imp1bGlldEBjYXB" + + "1bGV0Lm5ldC9iYWxjb255IiB0bz0icm9tZW9AbW9udGVndWUubmV0IiB0eXBlPS" + + "JjaGF0Ij48dGhyZWFkPmM2MzczODI0LWEzMDctNDBkZC04ZmUwLWJhZDZlNzI5O" + + "WFkMDwvdGhyZWFkPjxib2R5PldoZXJlZm9yZSBhcnQgdGhvdSwgUm9tZW8/PC9i" + + "b2R5PjwvbWVzc2FnZT4=" + + ""; + + PlainElement parsed = new PlainElementProvider() + .parse(getParser(example2)); + PlainElement fromPlain = PlainElement.fromUtf8String(plain, ParserUtils.getDateFromXep82String("2010-06-29T02:15:21.012+00:00")); + + assertEquals(parsed, fromPlain); + assertEquals(plain, parsed.getUtf8Content()); + } +} diff --git a/domain/src/test/java/org/jivesoftware/smackx/signed/xep0285/SignedElementTest.java b/domain/src/test/java/org/jivesoftware/smackx/signed/xep0285/SignedElementTest.java new file mode 100644 index 0000000..8c75645 --- /dev/null +++ b/domain/src/test/java/org/jivesoftware/smackx/signed/xep0285/SignedElementTest.java @@ -0,0 +1,40 @@ +package org.jivesoftware.smackx.signed.xep0285; + +import org.jivesoftware.smack.parsing.SmackParsingException; +import org.jivesoftware.smack.xml.XmlPullParserException; +import org.jivesoftware.smackx.signed.xep0285.element.SignedElement; +import org.jivesoftware.smackx.signed.xep0285.provider.SignedElementProvider; +import org.jivesoftware.smackx.util.MercurySmackTestSuite; +import org.junit.jupiter.api.Test; + +import java.io.IOException; + +public class SignedElementTest extends MercurySmackTestSuite { + + @Test + public void test() throws XmlPullParserException, IOException, SmackParsingException { + String example3 = + "" + + "" + + "DxbxIziY1C1Ytcxkj0IFLsfmDLMv96JMlMAQZ7jh49IbsOIPsxI2LyLmqhKH/994UXDJKQLHvLJz" + + "gAmw8V2b+zmyZeItJzSmB+HHiLFVXkD2Dd4JfetsafsfIcB7uNWg0gAeiKrTHfFgiyEC/2WxwOj3" + + "JUMRyQ9ykEPIzS0GZ/k=" + + "" + + "" + + "PHBsYWluIHhtbG5zPSJ1cm46eG1wcDpzaWduZWQ6MCIgdGltZXN0YW1wPSIyMDEwLTA2LTI5VDAy" + + "OjE1OjIxLjAxMloiPgogIFBHMWxjM05oWjJVZ2VHMXNibk05SW1waFltSmxjanBqYkdsbGJuUWlJ" + + "R1p5YjIwOUltcDFiR2xsZEVCallYQgogIDFiR1YwTG01bGRDOWlZV3hqYjI1NUlpQjBiejBpY205" + + "dFpXOUFiVzl1ZEdWbmRXVXVibVYwSWlCMGVYQmxQUwogIEpqYUdGMElqNDhkR2h5WldGa1BtTTJN" + + "emN6T0RJMExXRXpNRGN0TkRCa1pDMDRabVV3TFdKaFpEWmxOekk1TwogIFdGa01Ed3ZkR2h5WldG" + + "a1BqeGliMlI1UGxkb1pYSmxabTl5WlNCaGNuUWdkR2h2ZFN3Z1VtOXRaVzgvUEM5aQogIGIyUjVQ" + + "and2YldWemMyRm5aVDQ9CjwvcGxhaW4+Cg==" + + "" + + ""; + + SignedElement signedElement = new SignedElementProvider() + .parse(getParser(example3)); + + System.out.println(example3); + System.out.println(signedElement.toXML()); + } +} diff --git a/libs/Smack b/libs/Smack index 38bfe24..1c822dc 160000 --- a/libs/Smack +++ b/libs/Smack @@ -1 +1 @@ -Subproject commit 38bfe2492149cb095a1376080320e37c71832504 +Subproject commit 1c822dcaa4d4cb92d8b2d048f49bb69885143d56