diff --git a/src/main/java/io/kamax/mxisd/crypto/SignatureManager.java b/src/main/java/io/kamax/mxisd/crypto/SignatureManager.java index bcfdcfa..99d0470 100644 --- a/src/main/java/io/kamax/mxisd/crypto/SignatureManager.java +++ b/src/main/java/io/kamax/mxisd/crypto/SignatureManager.java @@ -20,12 +20,45 @@ package io.kamax.mxisd.crypto; +import com.google.gson.JsonElement; import com.google.gson.JsonObject; +import io.kamax.matrix.event.EventKey; +import io.kamax.matrix.json.MatrixJson; import java.nio.charset.StandardCharsets; +import java.util.Objects; public interface SignatureManager { + /** + * Sign the message and add the signature to the signatures key. + *

+ * If the key does not exist yet, it is created. If the key exist, the produced signature will be merged with any + * existing ones. + * + * @param domain The domain under which the signature should be added + * @param message The message to sign and add the produced signature to + * @return The provided message with the new signature + * @throws IllegalArgumentException If the signatures value is not a JSON object + */ + default JsonObject signMessageGson(String domain, JsonObject message) throws IllegalArgumentException { + JsonElement signEl = message.remove(EventKey.Signatures.get()); + JsonObject oldSigns = new JsonObject(); + if (!Objects.isNull(signEl)) { + if (!signEl.isJsonObject()) { + throw new IllegalArgumentException("Message contains a signatures key that is not a JSON object value"); + } + + oldSigns = signEl.getAsJsonObject(); + } + + JsonObject newSigns = signMessageGson(domain, MatrixJson.encodeCanonical(message)); + oldSigns.entrySet().forEach(entry -> newSigns.add(entry.getKey(), entry.getValue())); + message.add(EventKey.Signatures.get(), newSigns); + + return message; + } + /** * Sign the message and produce a signatures object that can directly be added to the object being signed. * diff --git a/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SingleLookupHandler.java b/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SingleLookupHandler.java index d81d38c..87cf6f1 100644 --- a/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SingleLookupHandler.java +++ b/src/main/java/io/kamax/mxisd/http/undertow/handler/identity/v1/SingleLookupHandler.java @@ -21,9 +21,7 @@ package io.kamax.mxisd.http.undertow.handler.identity.v1; import com.google.gson.JsonObject; -import io.kamax.matrix.event.EventKey; import io.kamax.matrix.json.GsonUtil; -import io.kamax.matrix.json.MatrixJson; import io.kamax.mxisd.config.MxisdConfig; import io.kamax.mxisd.config.ServerConfig; import io.kamax.mxisd.crypto.SignatureManager; @@ -73,11 +71,8 @@ public class SingleLookupHandler extends LookupHandler { respondJson(exchange, "{}"); } else { SingleLookupReply lookup = lookupOpt.get(); - - // FIXME signing should be done in the business model, not in the controller JsonObject obj = GsonUtil.makeObj(new SingeLookupReplyJson(lookup)); - obj.add(EventKey.Signatures.get(), signMgr.signMessageGson(cfg.getName(), MatrixJson.encodeCanonical(obj))); - + signMgr.signMessageGson(cfg.getName(), obj); respondJson(exchange, obj); } } diff --git a/src/test/java/io/kamax/mxisd/test/crypto/SignatureManagerTest.java b/src/test/java/io/kamax/mxisd/test/crypto/SignatureManagerTest.java index 06d2fed..b48a723 100644 --- a/src/test/java/io/kamax/mxisd/test/crypto/SignatureManagerTest.java +++ b/src/test/java/io/kamax/mxisd/test/crypto/SignatureManagerTest.java @@ -21,6 +21,7 @@ package io.kamax.mxisd.test.crypto; import com.google.gson.JsonObject; +import io.kamax.matrix.event.EventKey; import io.kamax.matrix.json.GsonUtil; import io.kamax.matrix.json.MatrixJson; import io.kamax.mxisd.crypto.Signature; @@ -36,10 +37,14 @@ import org.junit.Test; import static org.hamcrest.core.Is.is; import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertThat; public class SignatureManagerTest { + private static final String lookupData = "{\n" + " \"not_before\": 0,\n" + " \"address\": \"mxisd-federation-test@kamax.io\",\n" + + " \"medium\": \"email\",\n" + " \"mxid\": \"@mxisd-lookup-test:kamax.io\",\n" + + " \"not_after\": 253402300799000,\n" + " \"ts\": 1523482030147\n" + "}"; private static SignatureManager signMgr; private static SignatureManager build(String keySeed) { @@ -98,12 +103,19 @@ public class SignatureManagerTest { @Test public void onIdentityLookup() { - String value = MatrixJson.encodeCanonical("{\n" + " \"address\": \"mxisd-federation-test@kamax.io\",\n" - + " \"medium\": \"email\",\n" + " \"mxid\": \"@mxisd-lookup-test:kamax.io\",\n" - + " \"not_after\": 253402300799000,\n" + " \"not_before\": 0,\n" + " \"ts\": 1523482030147\n" + "}"); - + String value = MatrixJson.encodeCanonical(lookupData); String sign = "ObKA4PNQh2g6c7Yo2QcTcuDgIwhknG7ZfqmNYzbhrbLBOqZomU22xX9raufN2Y3ke1FXsDqsGs7WBDodmzZJCg"; testSign(value, sign); } + @Test + public void onIdentityLookupFull() { + JsonObject data = GsonUtil.parseObj(lookupData); + signMgr.signMessageGson("localhost", data); + JsonObject signatures = EventKey.Signatures.getObj(data); + JsonObject domainSign = GsonUtil.getObj(signatures, "localhost"); + String sign = GsonUtil.getStringOrThrow(domainSign, "ed25519:0"); + assertEquals(sign, "ObKA4PNQh2g6c7Yo2QcTcuDgIwhknG7ZfqmNYzbhrbLBOqZomU22xX9raufN2Y3ke1FXsDqsGs7WBDodmzZJCg"); + } + }