Store ephemeral key in invite and add support for /sign-ed25519
This commit is contained in:
@@ -91,6 +91,7 @@ public class HttpMxisd {
|
|||||||
.get(SessionTpidGetValidatedHandler.Path, SaneHandler.around(new SessionTpidGetValidatedHandler(m.getSession())))
|
.get(SessionTpidGetValidatedHandler.Path, SaneHandler.around(new SessionTpidGetValidatedHandler(m.getSession())))
|
||||||
.post(SessionTpidBindHandler.Path, SaneHandler.around(new SessionTpidBindHandler(m.getSession(), m.getInvitationManager())))
|
.post(SessionTpidBindHandler.Path, SaneHandler.around(new SessionTpidBindHandler(m.getSession(), m.getInvitationManager())))
|
||||||
.post(SessionTpidUnbindHandler.Path, SaneHandler.around(new SessionTpidUnbindHandler(m.getSession())))
|
.post(SessionTpidUnbindHandler.Path, SaneHandler.around(new SessionTpidUnbindHandler(m.getSession())))
|
||||||
|
.post(SignEd25519Handler.Path, SaneHandler.around(new SignEd25519Handler(m.getConfig(), m.getInvitationManager(), m.getSign())))
|
||||||
|
|
||||||
// Profile endpoints
|
// Profile endpoints
|
||||||
.get(ProfileHandler.Path, SaneHandler.around(new ProfileHandler(m.getProfile())))
|
.get(ProfileHandler.Path, SaneHandler.around(new ProfileHandler(m.getProfile())))
|
||||||
|
@@ -106,7 +106,7 @@ public class Mxisd {
|
|||||||
pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient);
|
pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient);
|
||||||
notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get());
|
notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get());
|
||||||
sessMgr = new SessionManager(cfg.getSession(), cfg.getMatrix(), store, notifMgr, idStrategy, httpClient);
|
sessMgr = new SessionManager(cfg.getSession(), cfg.getMatrix(), store, notifMgr, idStrategy, httpClient);
|
||||||
invMgr = new InvitationManager(cfg, store, idStrategy, signMgr, fedDns, notifMgr);
|
invMgr = new InvitationManager(cfg, store, idStrategy, keyMgr, signMgr, fedDns, notifMgr);
|
||||||
authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient);
|
authMgr = new AuthManager(cfg, AuthProviders.get(), idStrategy, invMgr, clientDns, httpClient);
|
||||||
dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get());
|
dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get());
|
||||||
asHander = new AppSvcManager(cfg, store, pMgr, notifMgr, synapse);
|
asHander = new AppSvcManager(cfg, store, pMgr, notifMgr, synapse);
|
||||||
|
@@ -22,8 +22,12 @@ package io.kamax.mxisd.exception;
|
|||||||
|
|
||||||
public class ObjectNotFoundException extends RuntimeException {
|
public class ObjectNotFoundException extends RuntimeException {
|
||||||
|
|
||||||
|
public ObjectNotFoundException(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
public ObjectNotFoundException(String type, String id) {
|
public ObjectNotFoundException(String type, String id) {
|
||||||
super(type + " with ID " + id + " does not exist");
|
this(type + " with ID " + id + " does not exist");
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -0,0 +1,75 @@
|
|||||||
|
/*
|
||||||
|
* mxisd - Matrix Identity Server Daemon
|
||||||
|
* Copyright (C) 2019 Kamax Sarl
|
||||||
|
*
|
||||||
|
* https://www.kamax.io/
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as
|
||||||
|
* published by the Free Software Foundation, either version 3 of the
|
||||||
|
* License, or (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||||
|
|
||||||
|
import com.google.gson.JsonObject;
|
||||||
|
import io.kamax.matrix.MatrixID;
|
||||||
|
import io.kamax.matrix._MatrixID;
|
||||||
|
import io.kamax.matrix.json.GsonUtil;
|
||||||
|
import io.kamax.matrix.json.MatrixJson;
|
||||||
|
import io.kamax.mxisd.config.MxisdConfig;
|
||||||
|
import io.kamax.mxisd.http.IsAPIv1;
|
||||||
|
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||||
|
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||||
|
import io.kamax.mxisd.invitation.InvitationManager;
|
||||||
|
import io.kamax.mxisd.storage.crypto.SignatureManager;
|
||||||
|
import io.undertow.server.HttpServerExchange;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class SignEd25519Handler extends BasicHttpHandler {
|
||||||
|
|
||||||
|
public static final String Path = IsAPIv1.Base + "/sign-ed25519";
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(SignEd25519Handler.class);
|
||||||
|
|
||||||
|
private final MxisdConfig cfg;
|
||||||
|
private final InvitationManager invMgr;
|
||||||
|
private final SignatureManager signMgr;
|
||||||
|
|
||||||
|
public SignEd25519Handler(MxisdConfig cfg, InvitationManager invMgr, SignatureManager signMgr) {
|
||||||
|
this.cfg = cfg;
|
||||||
|
this.invMgr = invMgr;
|
||||||
|
this.signMgr = signMgr;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleRequest(HttpServerExchange exchange) {
|
||||||
|
JsonObject body = parseJsonObject(exchange);
|
||||||
|
|
||||||
|
_MatrixID mxid = MatrixID.asAcceptable(GsonUtil.getStringOrThrow(body, "mxid"));
|
||||||
|
String token = GsonUtil.getStringOrThrow(body, "token");
|
||||||
|
String privKey = GsonUtil.getStringOrThrow(body, "private_key");
|
||||||
|
|
||||||
|
IThreePidInviteReply reply = invMgr.getInvite(token, privKey);
|
||||||
|
_MatrixID sender = reply.getInvite().getSender();
|
||||||
|
|
||||||
|
JsonObject res = new JsonObject();
|
||||||
|
res.addProperty("token", token);
|
||||||
|
res.addProperty("sender", sender.getId());
|
||||||
|
res.addProperty("mxid", mxid.getId());
|
||||||
|
res.add("signatures", signMgr.signMessageGson(cfg.getServer().getName(), MatrixJson.encodeCanonical(res)));
|
||||||
|
|
||||||
|
log.info("Signed data for invite using token {}", token);
|
||||||
|
respondJson(exchange, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -20,6 +20,8 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.invitation;
|
package io.kamax.mxisd.invitation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface IThreePidInviteReply {
|
public interface IThreePidInviteReply {
|
||||||
|
|
||||||
String getId();
|
String getId();
|
||||||
@@ -30,4 +32,6 @@ public interface IThreePidInviteReply {
|
|||||||
|
|
||||||
String getDisplayName();
|
String getDisplayName();
|
||||||
|
|
||||||
|
List<String> getPublicKeys();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@@ -30,16 +30,17 @@ import io.kamax.mxisd.config.ServerConfig;
|
|||||||
import io.kamax.mxisd.dns.FederationDnsOverwrite;
|
import io.kamax.mxisd.dns.FederationDnsOverwrite;
|
||||||
import io.kamax.mxisd.exception.BadRequestException;
|
import io.kamax.mxisd.exception.BadRequestException;
|
||||||
import io.kamax.mxisd.exception.MappingAlreadyExistsException;
|
import io.kamax.mxisd.exception.MappingAlreadyExistsException;
|
||||||
|
import io.kamax.mxisd.exception.ObjectNotFoundException;
|
||||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||||
import io.kamax.mxisd.lookup.ThreePidMapping;
|
import io.kamax.mxisd.lookup.ThreePidMapping;
|
||||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||||
import io.kamax.mxisd.notification.NotificationManager;
|
import io.kamax.mxisd.notification.NotificationManager;
|
||||||
import io.kamax.mxisd.storage.IStorage;
|
import io.kamax.mxisd.storage.IStorage;
|
||||||
import io.kamax.mxisd.storage.crypto.SignatureManager;
|
import io.kamax.mxisd.storage.crypto.*;
|
||||||
import io.kamax.mxisd.storage.ormlite.dao.ThreePidInviteIO;
|
import io.kamax.mxisd.storage.ormlite.dao.ThreePidInviteIO;
|
||||||
import org.apache.commons.io.IOUtils;
|
import org.apache.commons.io.IOUtils;
|
||||||
import org.apache.commons.lang.RandomStringUtils;
|
import org.apache.commons.lang3.RandomStringUtils;
|
||||||
import org.apache.commons.lang.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||||
import org.apache.http.client.methods.HttpPost;
|
import org.apache.http.client.methods.HttpPost;
|
||||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||||
@@ -72,6 +73,7 @@ public class InvitationManager {
|
|||||||
private ServerConfig srvCfg;
|
private ServerConfig srvCfg;
|
||||||
private IStorage storage;
|
private IStorage storage;
|
||||||
private LookupStrategy lookupMgr;
|
private LookupStrategy lookupMgr;
|
||||||
|
private KeyManager keyMgr;
|
||||||
private SignatureManager signMgr;
|
private SignatureManager signMgr;
|
||||||
private FederationDnsOverwrite dns;
|
private FederationDnsOverwrite dns;
|
||||||
private NotificationManager notifMgr;
|
private NotificationManager notifMgr;
|
||||||
@@ -85,6 +87,7 @@ public class InvitationManager {
|
|||||||
MxisdConfig mxisdCfg,
|
MxisdConfig mxisdCfg,
|
||||||
IStorage storage,
|
IStorage storage,
|
||||||
LookupStrategy lookupMgr,
|
LookupStrategy lookupMgr,
|
||||||
|
KeyManager keyMgr,
|
||||||
SignatureManager signMgr,
|
SignatureManager signMgr,
|
||||||
FederationDnsOverwrite dns,
|
FederationDnsOverwrite dns,
|
||||||
NotificationManager notifMgr
|
NotificationManager notifMgr
|
||||||
@@ -93,6 +96,7 @@ public class InvitationManager {
|
|||||||
this.srvCfg = mxisdCfg.getServer();
|
this.srvCfg = mxisdCfg.getServer();
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
this.lookupMgr = lookupMgr;
|
this.lookupMgr = lookupMgr;
|
||||||
|
this.keyMgr = keyMgr;
|
||||||
this.signMgr = signMgr;
|
this.signMgr = signMgr;
|
||||||
this.dns = dns;
|
this.dns = dns;
|
||||||
this.notifMgr = notifMgr;
|
this.notifMgr = notifMgr;
|
||||||
@@ -109,7 +113,7 @@ public class InvitationManager {
|
|||||||
io.getProperties()
|
io.getProperties()
|
||||||
);
|
);
|
||||||
|
|
||||||
ThreePidInviteReply reply = new ThreePidInviteReply(getId(invite), invite, io.getToken(), "");
|
ThreePidInviteReply reply = new ThreePidInviteReply(getId(invite), invite, io.getToken(), "", Collections.emptyList());
|
||||||
invitations.put(reply.getId(), reply);
|
invitations.put(reply.getId(), reply);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -221,7 +225,7 @@ public class InvitationManager {
|
|||||||
log.info("Invite is already pending for {}:{}, returning data", invitation.getMedium(), invitation.getAddress());
|
log.info("Invite is already pending for {}:{}, returning data", invitation.getMedium(), invitation.getAddress());
|
||||||
if (!StringUtils.equals(invitation.getRoomId(), reply.getInvite().getRoomId())) {
|
if (!StringUtils.equals(invitation.getRoomId(), reply.getInvite().getRoomId())) {
|
||||||
log.info("Sending new notification as new invite room {} is different from the original {}", invitation.getRoomId(), reply.getInvite().getRoomId());
|
log.info("Sending new notification as new invite room {} is different from the original {}", invitation.getRoomId(), reply.getInvite().getRoomId());
|
||||||
notifMgr.sendForReply(new ThreePidInviteReply(reply.getId(), invitation, reply.getToken(), reply.getDisplayName()));
|
notifMgr.sendForReply(new ThreePidInviteReply(reply.getId(), invitation, reply.getToken(), reply.getDisplayName(), reply.getPublicKeys()));
|
||||||
} else {
|
} else {
|
||||||
// FIXME we should check attempt and send if bigger
|
// FIXME we should check attempt and send if bigger
|
||||||
}
|
}
|
||||||
@@ -236,8 +240,20 @@ public class InvitationManager {
|
|||||||
|
|
||||||
String token = RandomStringUtils.randomAlphanumeric(64);
|
String token = RandomStringUtils.randomAlphanumeric(64);
|
||||||
String displayName = invitation.getAddress().substring(0, 3) + "...";
|
String displayName = invitation.getAddress().substring(0, 3) + "...";
|
||||||
|
KeyIdentifier pKeyId = keyMgr.getServerSigningKey().getId();
|
||||||
|
KeyIdentifier eKeyId = keyMgr.generateKey(KeyType.Ephemeral);
|
||||||
|
|
||||||
reply = new ThreePidInviteReply(invId, invitation, token, displayName);
|
String pPubKey = keyMgr.getPublicKeyBase64(pKeyId);
|
||||||
|
String ePubKey = keyMgr.getPublicKeyBase64(eKeyId);
|
||||||
|
|
||||||
|
invitation.getProperties().put("p_key_algo", pKeyId.getAlgorithm());
|
||||||
|
invitation.getProperties().put("p_key_serial", pKeyId.getSerial());
|
||||||
|
invitation.getProperties().put("p_key_public", pPubKey);
|
||||||
|
invitation.getProperties().put("e_key_algo", eKeyId.getAlgorithm());
|
||||||
|
invitation.getProperties().put("e_key_serial", eKeyId.getSerial());
|
||||||
|
invitation.getProperties().put("e_key_public", ePubKey);
|
||||||
|
|
||||||
|
reply = new ThreePidInviteReply(invId, invitation, token, displayName, Arrays.asList(pPubKey, ePubKey));
|
||||||
|
|
||||||
log.info("Performing invite to {}:{}", invitation.getMedium(), invitation.getAddress());
|
log.info("Performing invite to {}:{}", invitation.getMedium(), invitation.getAddress());
|
||||||
notifMgr.sendForReply(reply);
|
notifMgr.sendForReply(reply);
|
||||||
@@ -270,6 +286,28 @@ public class InvitationManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IThreePidInviteReply getInvite(String token, String privKey) {
|
||||||
|
for (IThreePidInviteReply reply : invitations.values()) {
|
||||||
|
if (StringUtils.equals(reply.getToken(), token)) {
|
||||||
|
String algo = reply.getInvite().getProperties().get("e_key_algo");
|
||||||
|
String serial = reply.getInvite().getProperties().get("e_key_serial");
|
||||||
|
|
||||||
|
if (StringUtils.isAnyBlank(algo, serial)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String storedPrivKey = keyMgr.getKey(new GenericKeyIdentifier(KeyType.Ephemeral, algo, serial)).getPrivateKeyBase64();
|
||||||
|
if (!StringUtils.equals(storedPrivKey, privKey)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reply;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ObjectNotFoundException("No invite with such token and/or private key");
|
||||||
|
}
|
||||||
|
|
||||||
private void publishMapping(IThreePidInviteReply reply, String mxid) {
|
private void publishMapping(IThreePidInviteReply reply, String mxid) {
|
||||||
String medium = reply.getInvite().getMedium();
|
String medium = reply.getInvite().getMedium();
|
||||||
String address = reply.getInvite().getAddress();
|
String address = reply.getInvite().getAddress();
|
||||||
|
@@ -20,18 +20,24 @@
|
|||||||
|
|
||||||
package io.kamax.mxisd.invitation;
|
package io.kamax.mxisd.invitation;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class ThreePidInviteReply implements IThreePidInviteReply {
|
public class ThreePidInviteReply implements IThreePidInviteReply {
|
||||||
|
|
||||||
private String id;
|
private String id;
|
||||||
private IThreePidInvite invite;
|
private IThreePidInvite invite;
|
||||||
private String token;
|
private String token;
|
||||||
private String displayName;
|
private String displayName;
|
||||||
|
private List<String> publicKeys;
|
||||||
|
|
||||||
public ThreePidInviteReply(String id, IThreePidInvite invite, String token, String displayName) {
|
public ThreePidInviteReply(String id, IThreePidInvite invite, String token, String displayName, List<String> publicKeys) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.invite = invite;
|
this.invite = invite;
|
||||||
this.token = token;
|
this.token = token;
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
|
this.publicKeys = Collections.unmodifiableList(new ArrayList<>(publicKeys));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -54,4 +60,9 @@ public class ThreePidInviteReply implements IThreePidInviteReply {
|
|||||||
return displayName;
|
return displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> getPublicKeys() {
|
||||||
|
return publicKeys;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user