From 49eb137acba2f1d65479d9db01e679b7ef702728 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Fri, 25 Sep 2020 22:18:42 +0200 Subject: [PATCH] Add OxIkeyManager --- domain/build.gradle | 2 + .../jivesoftware/smackx/ikey/IkeyManager.java | 13 ++++ .../smackx/ikey/element/ProofElement.java | 5 ++ .../smackx/ikey_ox/OxIkeyManager.java | 49 +++++++++++++ .../IkeySubordinateElementCreator.java | 37 ++++++++++ .../smackx/ikey/element/IkeyElementTest.java | 1 + .../smackx/ikey/element/XepTest.java | 73 +++++++++++++++++++ .../IkeySubordinateElementCreatorTest.java | 56 ++++++++++++++ 8 files changed, 236 insertions(+) create mode 100644 domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeyManager.java create mode 100644 domain/src/main/java/org/jivesoftware/smackx/ikey_utils/IkeySubordinateElementCreator.java create mode 100644 domain/src/test/java/org/jivesoftware/smackx/ikey/element/XepTest.java create mode 100644 domain/src/test/java/org/jivesoftware/smackx/ikey_utils/IkeySubordinateElementCreatorTest.java diff --git a/domain/build.gradle b/domain/build.gradle index 31be99d..ae3773c 100644 --- a/domain/build.gradle +++ b/domain/build.gradle @@ -17,6 +17,8 @@ dependencies { api "org.igniterealtime.smack:smack-im:$smackImVersion" api "org.igniterealtime.smack:smack-tcp:$smackTcpVersion" api "org.igniterealtime.smack:smack-openpgp:$smackOpenpgpVersion" + api "org.igniterealtime.smack:smack-omemo:$smackOmemoVersion" + api "org.igniterealtime.smack:smack-omemo-signal:$smackOmemoSignalVersion" api "org.jxmpp:jxmpp-core:1.0.1" api "org.jxmpp:jxmpp-jid:1.0.1" 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 a856861..265aa0d 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/IkeyManager.java @@ -8,7 +8,11 @@ import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.provider.ProviderManager; import org.jivesoftware.smackx.ikey.element.IkeyElement; +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.element.SuperordinateElement; +import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureCreationMechanism; import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureVerificationMechanism; import org.jivesoftware.smackx.ikey.provider.IkeyElementProvider; import org.jivesoftware.smackx.ikey.provider.SubordinateListElementProvider; @@ -69,6 +73,15 @@ public final class IkeyManager extends Manager { .removePepEventListener(pepEventListener); } + public IkeyElement createIkeyElement(IkeySignatureCreationMechanism mechanism, + SuperordinateElement superordinateElement, + SubordinateListElement subordinateListElement) + throws IOException { + SignedElement signedElement = new SignedElement(subordinateListElement); + ProofElement proofElement = new ProofElement(mechanism.createSignature(signedElement.getUtf8Bytes())); + return new IkeyElement(mechanism.getType(), superordinateElement, signedElement, proofElement); + } + public void publishIkeyElement(IkeyElement ikeyElement) throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException { 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 index 421598a..f467214 100644 --- a/domain/src/main/java/org/jivesoftware/smackx/ikey/element/ProofElement.java +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey/element/ProofElement.java @@ -5,6 +5,7 @@ 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; public class ProofElement implements NamedElement { @@ -16,6 +17,10 @@ public class ProofElement implements NamedElement { this.base64Sig = base64Sig; } + public ProofElement(byte[] plainBytes) { + this(Base64.encodeToString(plainBytes)); + } + public String getBase64Signature() { return base64Sig; } diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeyManager.java b/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeyManager.java new file mode 100644 index 0000000..76c78a9 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey_ox/OxIkeyManager.java @@ -0,0 +1,49 @@ +package org.jivesoftware.smackx.ikey_ox; + +import org.bouncycastle.openpgp.PGPSecretKeyRing; +import org.jivesoftware.smack.Manager; +import org.jivesoftware.smack.XMPPConnection; +import org.jivesoftware.smackx.ikey.IkeyManager; +import org.jivesoftware.smackx.ikey.element.IkeyElement; +import org.jivesoftware.smackx.ikey.element.SubordinateElement; +import org.jivesoftware.smackx.ikey.element.SubordinateListElement; +import org.jivesoftware.smackx.ikey.element.SuperordinateElement; +import org.jivesoftware.smackx.ikey.mechanism.IkeySignatureCreationMechanism; +import org.pgpainless.key.protection.SecretKeyRingProtector; + +import java.io.IOException; +import java.util.Arrays; +import java.util.Date; +import java.util.Map; +import java.util.WeakHashMap; + +public final class OxIkeyManager extends Manager { + + private static final Map INSTANCES = new WeakHashMap<>(); + + private final IkeyManager ikeyManager; + + private OxIkeyManager(XMPPConnection connection) { + super(connection); + this.ikeyManager = IkeyManager.getInstanceFor(connection); + } + + public static OxIkeyManager getInstanceFor(XMPPConnection connection) { + OxIkeyManager manager = INSTANCES.get(connection); + if (manager == null) { + manager = new OxIkeyManager(connection); + INSTANCES.put(connection, manager); + } + return manager; + } + + public IkeyElement createOxIkeyElement(PGPSecretKeyRing secretKeys, + SecretKeyRingProtector keyRingProtector, + SubordinateElement... subordinateElements) throws IOException { + IkeySignatureCreationMechanism mechanism = new OxIkeySignatureCreationMechanism(secretKeys, keyRingProtector); + SuperordinateElement superordinateElement = new SuperordinateElement(secretKeys.getPublicKey().getEncoded()); + SubordinateListElement subordinateListElement = new SubordinateListElement(connection().getUser().asEntityBareJid(), + new Date(), Arrays.asList(subordinateElements)); + return ikeyManager.createIkeyElement(mechanism, superordinateElement, subordinateListElement); + } +} diff --git a/domain/src/main/java/org/jivesoftware/smackx/ikey_utils/IkeySubordinateElementCreator.java b/domain/src/main/java/org/jivesoftware/smackx/ikey_utils/IkeySubordinateElementCreator.java new file mode 100644 index 0000000..3c261c9 --- /dev/null +++ b/domain/src/main/java/org/jivesoftware/smackx/ikey_utils/IkeySubordinateElementCreator.java @@ -0,0 +1,37 @@ +package org.jivesoftware.smackx.ikey_utils; + +import org.jivesoftware.smackx.ikey.element.SubordinateElement; +import org.jivesoftware.smackx.omemo.internal.OmemoDevice; +import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint; +import org.jivesoftware.smackx.ox.util.OpenPgpPubSubUtil; +import org.jivesoftware.smackx.pubsub.PubSubUri; +import org.jxmpp.jid.BareJid; +import org.pgpainless.key.OpenPgpV4Fingerprint; + +import java.net.URI; +import java.net.URISyntaxException; + +public class IkeySubordinateElementCreator { + + public static SubordinateElement createOxSubordinateElement(BareJid pubSubService, OpenPgpV4Fingerprint fingerprint, String itemId) + throws URISyntaxException { + String node = OpenPgpPubSubUtil.PEP_NODE_PUBLIC_KEY(fingerprint); + PubSubUri pubSubUri = new PubSubUri(pubSubService, node, itemId, null); + URI uri = new URI(pubSubUri.toString()); + return new SubordinateElement(uri, fingerprint.toString()); + } + + public static SubordinateElement createOmemoSubordinateElement(BareJid pubSubService, OmemoDevice device, OmemoFingerprint fingerprint) + throws URISyntaxException { + PubSubUri pubSubUri = new PubSubUri(pubSubService, "urn:xmpp:omemo:1:bundles", Integer.toString(device.getDeviceId()), null); + URI uri = new URI(pubSubUri.toString()); + return new SubordinateElement(uri, fingerprint.toString()); + } + + public static SubordinateElement createSiacsOmemoSubordinateElement(BareJid pubSubService, OmemoDevice device, OmemoFingerprint fingerprint) + throws URISyntaxException { + PubSubUri pubSubUri = new PubSubUri(pubSubService, device.getBundleNodeName(), null, null); + URI uri = new URI(pubSubUri.toString()); + return new SubordinateElement(uri, fingerprint.toString()); + } +} 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 0f78aa7..689cd36 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 @@ -44,6 +44,7 @@ public class IkeyElementTest extends MercurySmackTestSuite { IkeyElement ikeyElement = new IkeyElement(type, superordinate, new SignedElement(subordinates), proof); String xml = ikeyElement.toXML().toString(); System.out.println(xml); + System.out.println(subordinates.toXML().toString()); IkeyElement parsed = IkeyElementProvider.INSTANCE.parse(PacketParserUtils.getParserFor(xml)); diff --git a/domain/src/test/java/org/jivesoftware/smackx/ikey/element/XepTest.java b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/XepTest.java new file mode 100644 index 0000000..e41bac3 --- /dev/null +++ b/domain/src/test/java/org/jivesoftware/smackx/ikey/element/XepTest.java @@ -0,0 +1,73 @@ +package org.jivesoftware.smackx.ikey.element; + +import org.bouncycastle.openpgp.PGPException; +import org.jivesoftware.smack.util.stringencoder.Base64; +import org.jivesoftware.smackx.ikey.IkeySignatureCreator; +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_ox.OxIkeySignatureCreationMechanism; +import org.jivesoftware.smackx.ikey_ox.OxIkeySignatureVerificationMechanism; +import org.jivesoftware.smackx.util.MercurySmackTestSuite; +import org.junit.jupiter.api.Test; +import org.jxmpp.jid.EntityBareJid; +import org.jxmpp.jid.impl.JidCreate; +import org.pgpainless.PGPainless; +import org.pgpainless.algorithm.KeyFlag; +import org.pgpainless.key.collection.PGPKeyRing; +import org.pgpainless.key.generation.KeySpec; +import org.pgpainless.key.generation.type.ECDSA; +import org.pgpainless.key.generation.type.curve.EllipticCurve; +import org.pgpainless.key.protection.UnprotectedKeysProtector; + +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class XepTest extends MercurySmackTestSuite { + + @Test + public void test() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException, URISyntaxException { + EntityBareJid jid = JidCreate.entityBareFromOrThrowUnchecked("juliet@capulet.lit"); + PGPKeyRing keyRing = PGPainless.generateKeyRing() + .withMasterKey(KeySpec.getBuilder(ECDSA.fromCurve(EllipticCurve._P256)) + .withKeyFlags(KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA) + .withDefaultAlgorithms()) + .withPrimaryUserId("xmpp:" + jid) + .withoutPassphrase() + .build(); + + IkeySignatureCreationMechanism signingMechanism = new OxIkeySignatureCreationMechanism( + keyRing.getSecretKeys(), new UnprotectedKeysProtector()); + IkeySignatureCreator creator = new IkeySignatureCreator(signingMechanism); + IkeySignatureVerificationMechanism verificationMechanism = new OxIkeySignatureVerificationMechanism(keyRing.getPublicKeys()); + IkeySignatureVerifier verifier = new IkeySignatureVerifier(verificationMechanism); + + SuperordinateElement superordinate = new SuperordinateElement(Base64.encodeToString(keyRing.getMasterKey().getEncoded())); + + List subList = new ArrayList<>(); + subList.add(new SubordinateElement( + new URI("xmpp:" + jid + "?;node=urn:xmpp:openpgp:0:public-keys:1357B01865B2503C18453D208CAC2A9678548E35;item=2020-01-21T10:46:21Z"), + "1357B01865B2503C18453D208CAC2A9678548E35")); + subList.add(new SubordinateElement(new URI("xmpp:" + jid + "?;node=urn:xmpp:omemo:1:bundles;item=31415"), "e64dc9166dd34db64c9247bd502c5969e365a98f3aa41c87247d120487fdd32f")); + subList.add(new SubordinateElement(new URI("xmpp:" + jid + "?;node=eu.siacs.conversations.axolotl.bundles:31415"), "e64dc9166dd34db64c9247bd502c5969e365a98f3aa41c87247d120487fdd32f")); + + SubordinateListElement subs = new SubordinateListElement(jid, new Date(), subList); + + ProofElement proofElement = creator.createProofFor(subs); + IkeyElement ikeyElement = new IkeyElement(IkeyType.OX, superordinate, new SignedElement(subs), proofElement); + + System.out.println(subs.toXML().toString()); + System.out.println(ikeyElement.toXML().toString()); + + assertTrue(verifier.verify(ikeyElement, jid)); + } +} diff --git a/domain/src/test/java/org/jivesoftware/smackx/ikey_utils/IkeySubordinateElementCreatorTest.java b/domain/src/test/java/org/jivesoftware/smackx/ikey_utils/IkeySubordinateElementCreatorTest.java new file mode 100644 index 0000000..ab035cd --- /dev/null +++ b/domain/src/test/java/org/jivesoftware/smackx/ikey_utils/IkeySubordinateElementCreatorTest.java @@ -0,0 +1,56 @@ +package org.jivesoftware.smackx.ikey_utils; + +import org.jivesoftware.smackx.ikey.element.SubordinateElement; +import org.jivesoftware.smackx.omemo.internal.OmemoDevice; +import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint; +import org.junit.jupiter.api.Test; +import org.jxmpp.jid.BareJid; +import org.jxmpp.jid.impl.JidCreate; +import org.pgpainless.key.OpenPgpV4Fingerprint; + +import java.net.URISyntaxException; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class IkeySubordinateElementCreatorTest { + + @Test + public void testOxSubordinateElementCreation() throws URISyntaxException { + String xml = ""; + + BareJid pubSubService = JidCreate.bareFromOrThrowUnchecked("hamlet@denmark.lit"); + OpenPgpV4Fingerprint fingerprint = new OpenPgpV4Fingerprint("1357B01865B2503C18453D208CAC2A9678548E35"); + String itemId = "2020-01-21T10:46:21Z"; + + SubordinateElement element = IkeySubordinateElementCreator.createOxSubordinateElement(pubSubService, fingerprint, itemId); + + assertEquals(xml, element.toXML().toString()); + } + + @Test + public void testOmemoSubordinateElementCreation() throws URISyntaxException { + String xml = ""; + + BareJid pubSubService = JidCreate.bareFromOrThrowUnchecked("hamlet@denmark.lit"); + OmemoFingerprint fingerprint = new OmemoFingerprint("e64dc9166dd34db64c9247bd502c5969e365a98f3aa41c87247d120487fdd32f"); + + SubordinateElement element = IkeySubordinateElementCreator.createOmemoSubordinateElement(pubSubService, new OmemoDevice(pubSubService, 31415), fingerprint); + + assertEquals(xml, element.toXML().toString()); + } + + @Test + public void testSiacsOmemoSubordinateElementCreation() throws URISyntaxException { + String xml = ""; + + BareJid pubSubService = JidCreate.bareFromOrThrowUnchecked("hamlet@denmark.lit"); + OmemoFingerprint fingerprint = new OmemoFingerprint("e64dc9166dd34db64c9247bd502c5969e365a98f3aa41c87247d120487fdd32f"); + + SubordinateElement element = IkeySubordinateElementCreator.createSiacsOmemoSubordinateElement(pubSubService, new OmemoDevice(pubSubService, 31415), fingerprint); + + assertEquals(xml, element.toXML().toString()); + } +}