diff --git a/cli/src/main/java/org/mercury_im/messenger/cli/di/component/CliComponent.java b/cli/src/main/java/org/mercury_im/messenger/cli/di/component/CliComponent.java index 5517a58..c9a81f7 100644 --- a/cli/src/main/java/org/mercury_im/messenger/cli/di/component/CliComponent.java +++ b/cli/src/main/java/org/mercury_im/messenger/cli/di/component/CliComponent.java @@ -4,8 +4,10 @@ import org.mercury_im.messenger.cli.MercuryCli; import org.mercury_im.messenger.cli.di.module.CliDatabaseModule; import org.mercury_im.messenger.cli.di.module.CliSchedulersModule; import org.mercury_im.messenger.cli.di.module.X509WorkaroundConnectionFactoryModule; +import org.mercury_im.messenger.core.di.module.OpenPgpModule; import org.mercury_im.messenger.core.di.module.RxMercuryMessageStoreFactoryModule; import org.mercury_im.messenger.core.di.module.RxMercuryRosterStoreFactoryModule; +import org.mercury_im.messenger.core.di.module.StanzaIdSourceFactoryModule; import org.mercury_im.messenger.core.di.module.ViewModelModule; import org.mercury_im.messenger.data.di.RepositoryModule; @@ -22,7 +24,9 @@ import dagger.Component; ViewModelModule.class, X509WorkaroundConnectionFactoryModule.class, RxMercuryMessageStoreFactoryModule.class, - RxMercuryRosterStoreFactoryModule.class + RxMercuryRosterStoreFactoryModule.class, + OpenPgpModule.class, + StanzaIdSourceFactoryModule.class }) public interface CliComponent { diff --git a/domain/build.gradle b/domain/build.gradle index 52fd626..d1bdc50 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -29,6 +29,8 @@ dependencies { annotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion" testAnnotationProcessor "com.google.dagger:dagger-compiler:$daggerVersion" + api "org.apache.santuario:xmlsec:2.2.0" + compileOnly "org.projectlombok:lombok:$lombokVersion" annotationProcessor "org.projectlombok:lombok:$lombokVersion" diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/ElementCanonicalizer.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/ElementCanonicalizer.java new file mode 100644 index 0000000..1baf6e9 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/ElementCanonicalizer.java @@ -0,0 +1,21 @@ +package org.jivesoftware.smackx.ikey; + +import org.apache.xml.security.c14n.CanonicalizationException; +import org.apache.xml.security.parser.XMLParserException; +import org.jivesoftware.smack.packet.Element; + +import java.io.IOException; + +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()); + } + + byte[] canonicalize(byte[] xml) throws XMLParserException, IOException, CanonicalizationException; +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java new file mode 100644 index 0000000..3786294 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java @@ -0,0 +1,10 @@ +package org.jivesoftware.smackx.ikey; + +import org.jxmpp.jid.EntityBareJid; + +public class IkeyManager { + + private IkeySignatureVerificationMechanism getSignatureMechanismFor(EntityBareJid contactJid) { + return null; + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreationMechanism.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreationMechanism.java new file mode 100644 index 0000000..444aa58 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreationMechanism.java @@ -0,0 +1,11 @@ +package org.jivesoftware.smackx.ikey; + +import java.io.IOException; + +public interface IkeySignatureCreationMechanism { + + byte[] createSignature(byte[] data) throws IOException; + + IkeyType getType(); + +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreator.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreator.java new file mode 100644 index 0000000..a718450 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureCreator.java @@ -0,0 +1,27 @@ +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.ProofElement; +import org.jivesoftware.smackx.ikey.element.SubordinateListElement; + +import java.io.IOException; + +public class IkeySignatureCreator { + + private final IkeySignatureCreationMechanism signatureCreationMechanism; + private final XmlSecElementCanonicalizer elementCanonicalizer; + + public IkeySignatureCreator(IkeySignatureCreationMechanism signingMechanism, XmlSecElementCanonicalizer elementCanonicalizer) { + this.signatureCreationMechanism = signingMechanism; + this.elementCanonicalizer = elementCanonicalizer; + } + + public ProofElement createProofFor(SubordinateListElement subordinateListElement) throws XMLParserException, IOException, CanonicalizationException { + byte[] canonicalized = elementCanonicalizer.canonicalize(subordinateListElement); + byte[] signature = signatureCreationMechanism.createSignature(canonicalized); + + return new ProofElement(Base64.toBase64String(signature)); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerificationMechanism.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerificationMechanism.java new file mode 100644 index 0000000..fd995ce --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerificationMechanism.java @@ -0,0 +1,10 @@ +package org.jivesoftware.smackx.ikey; + +import java.io.IOException; + +public interface IkeySignatureVerificationMechanism { + + boolean isSignatureValid(byte[] data, byte[] signature) throws IOException; + + IkeyType getType(); +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerifier.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerifier.java new file mode 100644 index 0000000..bb73411 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeySignatureVerifier.java @@ -0,0 +1,44 @@ +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.jxmpp.jid.EntityBareJid; + +import java.io.IOException; + +public class IkeySignatureVerifier { + + private final IkeySignatureVerificationMechanism signatureVerificationMechanism; + private final XmlSecElementCanonicalizer elementCanonicalizer; + + public IkeySignatureVerifier(IkeySignatureVerificationMechanism signatureVerificationMechanism, XmlSecElementCanonicalizer elementCanonicalizer) { + this.signatureVerificationMechanism = signatureVerificationMechanism; + this.elementCanonicalizer = elementCanonicalizer; + } + + public boolean verify(IkeyElement element, EntityBareJid owner) + throws XMLParserException, IOException, CanonicalizationException { + throwIfMismatchingMechanism(element); + throwIfMismatchingOwnerJid(element, owner); + + byte[] canonicalizedXml = elementCanonicalizer.canonicalize(element.getSubordinates()); + byte[] signature = Base64.decode(element.getProof().getBase64Signature()); + + return signatureVerificationMechanism.isSignatureValid(canonicalizedXml, signature); + } + + private void throwIfMismatchingOwnerJid(IkeyElement element, EntityBareJid owner) { + if (!element.getSubordinates().getJid().equals(owner)) { + throw new IllegalArgumentException("Provided ikey element does not contain jid of " + owner); + } + } + + private void throwIfMismatchingMechanism(IkeyElement element) { + if (element.getType() != signatureVerificationMechanism.getType()) { + throw new IllegalArgumentException("Element was created using mechanism " + element.getType() + + " but this is a verifier for " + signatureVerificationMechanism.getType() + " ikey elements."); + } + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/XmlSecElementCanonicalizer.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/XmlSecElementCanonicalizer.java new file mode 100644 index 0000000..b9b81ce --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/XmlSecElementCanonicalizer.java @@ -0,0 +1,27 @@ +package org.jivesoftware.smackx.ikey; + +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; + +import javax.inject.Inject; + +public class XmlSecElementCanonicalizer implements ElementCanonicalizer { + + private final Canonicalizer canonicalizer; + + @Inject + 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/ikey/element/IkeyElement.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/IkeyElement.java new file mode 100644 index 0000000..96e784e --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/IkeyElement.java @@ -0,0 +1,60 @@ +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 javax.xml.namespace.QName; + +public class IkeyElement implements ExtensionElement { + + public static final String NAMESPACE = IkeyConstants.NAMESPACE; + public static final String ELEMENT = "ikey"; + public static final String ATTR_IKEY_TYPE = "type"; + + private static final QName QNAME = new QName(IkeyConstants.NAMESPACE, ELEMENT); + + private final IkeyType type; + private final SubordinateListElement subordinates; + private final ProofElement proof; + + public IkeyElement(IkeyType type, SubordinateListElement subordinates, ProofElement proof) { + this.type = type; + this.subordinates = subordinates; + this.proof = proof; + } + + public IkeyType getType() { + return type; + } + + public SubordinateListElement getSubordinates() { + return subordinates; + } + + public ProofElement getProof() { + return proof; + } + + @Override + public String getNamespace() { + return NAMESPACE; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { + return new XmlStringBuilder(this, xmlEnvironment) + .attribute(ATTR_IKEY_TYPE, getType()) + .rightAngleBracket() + .append(getSubordinates()) + .append(getProof()) + .closeElement(this); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/element/PlainIKeyElement.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/PlainIKeyElement.java deleted file mode 100644 index 00a8339..0000000 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/element/PlainIKeyElement.java +++ /dev/null @@ -1,40 +0,0 @@ -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 javax.xml.namespace.QName; - -public class PlainIKeyElement implements ExtensionElement { - - public static final String NAMESPACE = IkeyConstants.NAMESPACE; - public static final String ELEMENT = "ikey"; - public static final String ATTR_IKEY_TYPE = "type"; - - private final IkeyType type; - - private final QName QNAME = new QName(IkeyConstants.NAMESPACE, ELEMENT); - - public PlainIKeyElement() { - this.type = IkeyType.OX; - } - - @Override - public String getNamespace() { - return NAMESPACE; - } - - @Override - public String getElementName() { - return ELEMENT; - } - - @Override - public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { - XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment); - return xml; - } -} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/element/ProofElement.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/ProofElement.java new file mode 100644 index 0000000..1db0a1d --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/ProofElement.java @@ -0,0 +1,32 @@ +package org.jivesoftware.smackx.ikey.element; + +import org.jivesoftware.smack.packet.NamedElement; +import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.util.XmlStringBuilder; + +public class ProofElement implements NamedElement { + + public static final String ELEMENT = "proof"; + + private final String base64Sig; + + public ProofElement(String base64Sig) { + this.base64Sig = base64Sig; + } + + public String getBase64Signature() { + return base64Sig; + } + + @Override + public String getElementName() { + return ELEMENT; + } + + @Override + public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { + return new XmlStringBuilder(this).rightAngleBracket() + .append(getBase64Signature()) + .closeElement(this); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SubordinateElement.java b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SubordinateElement.java index 19c092f..c83ea7f 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SubordinateElement.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/SubordinateElement.java @@ -2,24 +2,27 @@ package org.jivesoftware.smackx.ikey.element; import org.jivesoftware.smack.packet.NamedElement; import org.jivesoftware.smack.packet.XmlEnvironment; +import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.XmlStringBuilder; +import java.net.URI; + public class SubordinateElement implements NamedElement { public static final String ELEMENT = "sub"; public static final String ATTR_SUB_URI = "uri"; - public static final String ATTR_SUB_FINGERPRINT = "fingerprint"; + public static final String ATTR_SUB_FINGERPRINT = "fpr"; - private final String subUri; + private final URI subUri; private final String subFingerprint; - public SubordinateElement(String subKeyItemUri, String subKeyFingerprint) { - this.subUri = StringUtils.requireNotNullNorEmpty(subKeyItemUri, "uri MUST NOT be null nor empty."); - this.subFingerprint = StringUtils.requireNotNullNorEmpty(subKeyFingerprint, "fingerprint MUST NOT be null nor empty."); + public SubordinateElement(URI subKeyItemUri, String subKeyFingerprint) { + this.subUri = Objects.requireNonNull(subKeyItemUri, "uri MUST NOT be null nor empty."); + this.subFingerprint = StringUtils.requireNotNullNorEmpty(subKeyFingerprint, "fpr MUST NOT be null nor empty."); } - public String getUri() { + public URI getUri() { return subUri; } @@ -35,7 +38,7 @@ public class SubordinateElement implements NamedElement { @Override public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { XmlStringBuilder xml = new XmlStringBuilder(this) - .attribute(ATTR_SUB_URI, getUri()) + .attribute(ATTR_SUB_URI, getUri().toString()) .attribute(ATTR_SUB_FINGERPRINT, getFingerprint()) .closeEmptyElement(); return xml; 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 ef934a5..392b2ea 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,27 +1,34 @@ 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.Objects; import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smackx.ikey.IkeyConstants; +import org.jxmpp.jid.EntityBareJid; import java.util.List; -public class SubordinateListElement implements ExtensionElement { +public class SubordinateListElement implements NamedElement { public static final String NAMESPACE = IkeyConstants.NAMESPACE; public static final String ELEMENT = "subordinates"; + public static final String ATTR_JID = "jid"; private final List subordinates; + private final EntityBareJid jid; - public SubordinateListElement(List subordinates) { + public SubordinateListElement(EntityBareJid jid, List subordinates) { + this.jid = jid; this.subordinates = Objects.requireNonNullNorEmpty(subordinates, "List of subordinates MUST NOT be null nor empty."); } - @Override - public String getNamespace() { - return NAMESPACE; + public EntityBareJid getJid() { + return jid; + } + + public List getSubordinates() { + return subordinates; } @Override @@ -31,10 +38,10 @@ public class SubordinateListElement implements ExtensionElement { @Override public XmlStringBuilder toXML(XmlEnvironment xmlEnvironment) { - XmlStringBuilder xml = new XmlStringBuilder(this, xmlEnvironment) + return new XmlStringBuilder(this) + .attribute(ATTR_JID, getJid()) .rightAngleBracket() - .append(subordinates) + .append(getSubordinates()) .closeElement(this); - return xml; } } 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 new file mode 100644 index 0000000..9b0e685 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeySignatureCreationMechanism.java @@ -0,0 +1,57 @@ +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.pgpainless.PGPainless; +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 OxIkeySignatureCreationMechanism implements IkeySignatureCreationMechanism { + + private final PGPSecretKeyRing ourIdentityKey; + private final SecretKeyRingProtector secretKeyRingProtector; + + public OxIkeySignatureCreationMechanism(PGPSecretKeyRing ourIdentityKey, + SecretKeyRingProtector secretKeyRingProtector) { + this.ourIdentityKey = ourIdentityKey; + this.secretKeyRingProtector = secretKeyRingProtector; + } + + @Override + public byte[] createSignature(byte[] data) throws IOException { + try { + ByteArrayOutputStream dummyOutputStream = new ByteArrayOutputStream(); + EncryptionStream encryptionStream = PGPainless.createEncryptor() + .onOutputStream(dummyOutputStream) + .doNotEncrypt() + .createDetachedSignature() + .signWith(secretKeyRingProtector, ourIdentityKey) + .noArmor(); + ByteArrayInputStream dataIn = new ByteArrayInputStream(data); + Streams.pipeAll(dataIn, encryptionStream); + + encryptionStream.close(); + dataIn.close(); + + PGPSignature signature = encryptionStream.getResult().getSignatures() + .iterator().next(); + return signature.getEncoded(); + } catch (PGPException e) { + throw new IOException(e); + } + } + + @Override + public IkeyType getType() { + return IkeyType.OX; + } +} 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 new file mode 100644 index 0000000..5402f35 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeySignatureVerificationMechanism.java @@ -0,0 +1,53 @@ +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.pgpainless.PGPainless; +import org.pgpainless.decryption_verification.DecryptionStream; +import org.pgpainless.decryption_verification.OpenPgpMetadata; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collections; + +public class OxIkeySignatureVerificationMechanism implements IkeySignatureVerificationMechanism { + + private PGPPublicKeyRing contactIdentityKey; + + public OxIkeySignatureVerificationMechanism(PGPPublicKeyRing contactIdentityKey) { + this.contactIdentityKey = contactIdentityKey; + } + + @Override + public boolean isSignatureValid(byte[] data, byte[] signature) throws IOException { + try { + DecryptionStream decryptionInputStream = PGPainless.createDecryptor() + .onInputStream(new ByteArrayInputStream(data)) + .doNotDecrypt() + .verifyDetachedSignature(new ByteArrayInputStream(signature)) + .verifyWith(Collections.singleton(contactIdentityKey)) + .ignoreMissingPublicKeys() + .build(); + ByteArrayOutputStream decryptionOutputStream = new ByteArrayOutputStream(data.length); + Streams.pipeAll(decryptionInputStream, decryptionOutputStream); + + decryptionInputStream.close(); + decryptionOutputStream.close(); + + OpenPgpMetadata metadata = decryptionInputStream.getResult(); + return !metadata.getVerifiedSignatures().isEmpty(); + } catch (PGPException e) { + throw new IOException(e); + } + } + + @Override + public IkeyType getType() { + return IkeyType.OX; + } + +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/pubsub/PubSubUri.java b/domain/src/main/java/org/jivesoftware/smackx/pubsub/PubSubUri.java index 95ecbed..b43eeb6 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/pubsub/PubSubUri.java +++ b/domain/src/main/java/org/jivesoftware/smackx/pubsub/PubSubUri.java @@ -32,6 +32,15 @@ public class PubSubUri { this.action = action; } + public PubSubUri(URI uri) throws XmppStringprepException { + throwIfNoXmppUri(uri); + String ssp = uri.getSchemeSpecificPart(); + service = getService(ssp); + node = getNodeOrNull(ssp); + item = getItemOrNull(ssp); + action = getActionOrNull(ssp); + } + /** * Parse a {@link PubSubUri}. * This method ignores unknown parameters. @@ -43,14 +52,7 @@ public class PubSubUri { public static PubSubUri from(String s) throws XmppStringprepException { URI uri = URI.create(s); - throwIfNoXmppUri(uri); - String ssp = uri.getSchemeSpecificPart(); - BareJid service = getService(ssp); - String node = getNodeOrNull(ssp); - String item = getItemOrNull(ssp); - Action action = getActionOrNull(ssp); - - return new PubSubUri(service, node, item, action); + return new PubSubUri(uri); } /** 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 new file mode 100644 index 0000000..b11b247 --- /dev/null +++ b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/IkeyElementTest.java @@ -0,0 +1,36 @@ +package org.jivesoftware.smackx.ikey.element; + +import org.jivesoftware.smackx.ikey.IkeyType; +import org.junit.Test; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; + +public class IkeyElementTest { + + @Test + public void elementTest() throws URISyntaxException { + IkeyType type = IkeyType.OX; + SubordinateListElement subordinates = buildSubListElement( + JidCreate.entityBareFromOrThrowUnchecked("hamlet@denmark.lit"), + new SubordinateElement( + new URI("xmpp:hamlet@denmark.lit?;node=urn:xmpp:openpgp:0:public-keys:1357B01865B2503C18453D208CAC2A9678548E35;item=2020-01-21T10:46:21Z"), + "1357B01865B2503C18453D208CAC2A9678548E35"), + new SubordinateElement( + new URI("xmpp:hamlet@denmark.lit?;node=urn:xmpp:omemo:1:bundles;item=123456"), + "e64dc9166dd34db64c9247bd502c5969e365a98f3aa41c87247d120487fdd32f") + ); + ProofElement proof = new ProofElement("d2hpbGUgdGhpcyBpcyBub3QgYSB2YWxpZCBwcm9vZiwgaXQgaXMgc3VmZmljaWVudCBmb3IgdGVzdGluZy4="); + + IkeyElement ikeyElement = new IkeyElement(type, subordinates, proof); + + System.out.println(ikeyElement.toXML().toString()); + } + + private SubordinateListElement buildSubListElement(EntityBareJid jid, SubordinateElement... subordinateElements) { + return new SubordinateListElement(jid, 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 new file mode 100644 index 0000000..7f9c6ff --- /dev/null +++ b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/IkeySignatureCreatorAndVerifierTest.java @@ -0,0 +1,71 @@ +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.smackx.ikey.XmlSecElementCanonicalizer; +import org.jivesoftware.smackx.ikey.IkeySignatureCreationMechanism; +import org.jivesoftware.smackx.ikey.IkeySignatureCreator; +import org.jivesoftware.smackx.ikey.IkeySignatureVerificationMechanism; +import org.jivesoftware.smackx.ikey.IkeySignatureVerifier; +import org.jivesoftware.smackx.ikey.IkeyType; +import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureCreationMechanism; +import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureVerificationMechanism; +import org.junit.BeforeClass; +import org.junit.Test; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; +import org.pgpainless.PGPainless; +import org.pgpainless.key.OpenPgpV4Fingerprint; +import org.pgpainless.key.collection.PGPKeyRing; +import org.pgpainless.key.protection.UnprotectedKeysProtector; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.List; + +import static junit.framework.TestCase.assertTrue; + +public class IkeySignatureCreatorAndVerifierTest { + + @BeforeClass + public static void initialize() { + if (!Init.isInitialized()) { + Init.init(); + } + } + + @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); + 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); + IkeySignatureVerificationMechanism verificationMechanism = new OxIkeySignatureVerificationMechanism(keyRing.getPublicKeys()); + IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism, elementCanonicalizer); + + OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint(keyRing.getMasterKey()); + 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); + + ProofElement proofElement = creator.createProofFor(subs); + IkeyElement ikeyElement = new IkeyElement(IkeyType.OX, subs, proofElement); + + System.out.println(ikeyElement.toXML().toString()); + + assertTrue(verifier.verify(ikeyElement, jid)); + } +} diff --git a/domain/src/test/java/org/jivesoftware/smackx/ikey/element/SubordinateListElementTest.java b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/SubordinateListElementTest.java index 041f311..6650fdf 100644 --- a/domain/src/test/java/org/jivesoftware/smackx/ikey/element/SubordinateListElementTest.java +++ b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/SubordinateListElementTest.java @@ -1,24 +1,14 @@ package org.jivesoftware.smackx.ikey.element; import org.junit.Test; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; +import java.net.URI; +import java.net.URISyntaxException; import java.util.Arrays; public class SubordinateListElementTest { - @Test - public void test() { - SubordinateListElement listElement = buildElement( - new SubordinateElement("xmpp:hamlet@denmark.lit?;node=urn:xmpp:openpgp:0:public-keys:1357B01865B2503C18453D208CAC2A9678548E35" + - ";item=2020-01-21T10:46:21Z", "1357B01865B2503C18453D208CAC2A9678548E35"), - new SubordinateElement("xmpp:hamlet@denmark.lit?;node=urn:xmpp:omemo:1:bundles" + - ";item=123456", "e64dc9166dd34db64c9247bd502c5969e365a98f3aa41c87247d120487fdd32f") - ); - System.out.println(listElement.toXML()); - } - - private SubordinateListElement buildElement(SubordinateElement... subordinateElements) { - return new SubordinateListElement(Arrays.asList(subordinateElements)); - } } 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 new file mode 100644 index 0000000..a99ea72 --- /dev/null +++ b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/XmlSecElementCanonicalizerTest.java @@ -0,0 +1,50 @@ +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.XmlSecElementCanonicalizer; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Test; + +import java.io.IOException; + +import static junit.framework.TestCase.assertEquals; + +public class XmlSecElementCanonicalizerTest { + + @BeforeClass + public static void initialize() { + if (!Init.isInitialized()) { + Init.init(); + } + } + + @Test + @Ignore + 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 = "" + + "" + + " " + + " " + + " " + + " " + + " iF4EABMIAAYFAl9I3esACgkQNZFg0LCGhWntNAD+LDO/Q+WQ5TrQOt4vBcqnUarCOZ6Ev4Wp4QgsIjs2BHcA/2BOIC6FBqkx80zB8NZsZu4H1fvn+gWgrscXhgf9+f+h" + + " " + + ""; + String can1 = new String(canonicalizer.canonicalize(element)); + String can2 = new String(canonicalizer.canonicalize(elementWithInsignificantWhitespace)); + assertEquals(can1, can2); + } +} diff --git a/domain/src/test/java/org/mercury_im/messenger/learning_tests/rx/BehaviourSubjectSubscriptionTest.java b/domain/src/test/java/org/mercury_im/messenger/learning_tests/rx/BehaviourSubjectSubscriptionTest.java index 702101b..7274333 100644 --- a/domain/src/test/java/org/mercury_im/messenger/learning_tests/rx/BehaviourSubjectSubscriptionTest.java +++ b/domain/src/test/java/org/mercury_im/messenger/learning_tests/rx/BehaviourSubjectSubscriptionTest.java @@ -3,6 +3,7 @@ package org.mercury_im.messenger.learning_tests.rx; import org.junit.Test; import io.reactivex.Observable; +import io.reactivex.schedulers.Schedulers; import io.reactivex.subjects.BehaviorSubject; import io.reactivex.subjects.Subject; @@ -15,8 +16,12 @@ public class BehaviourSubjectSubscriptionTest { Observable observable = Observable.just("One", "Two", "Three"); BehaviorSubject behaviorSubject = BehaviorSubject.create(); - observable.subscribe(behaviorSubject); - behaviorSubject.subscribe(System.out::println); + observable + .subscribeOn(Schedulers.io()) + .subscribe(behaviorSubject); + behaviorSubject + //.subscribeOn(Schedulers.io()) + .subscribe(System.out::println); Thread.sleep(100);