Store ephemeral key in invite and add support for /sign-ed25519

This commit is contained in:
Max Dor
2019-02-13 04:14:30 +01:00
parent 91e5e08e70
commit f3b528d1ba
7 changed files with 142 additions and 9 deletions

View File

@@ -91,6 +91,7 @@ public class HttpMxisd {
.get(SessionTpidGetValidatedHandler.Path, SaneHandler.around(new SessionTpidGetValidatedHandler(m.getSession())))
.post(SessionTpidBindHandler.Path, SaneHandler.around(new SessionTpidBindHandler(m.getSession(), m.getInvitationManager())))
.post(SessionTpidUnbindHandler.Path, SaneHandler.around(new SessionTpidUnbindHandler(m.getSession())))
.post(SignEd25519Handler.Path, SaneHandler.around(new SignEd25519Handler(m.getConfig(), m.getInvitationManager(), m.getSign())))
// Profile endpoints
.get(ProfileHandler.Path, SaneHandler.around(new ProfileHandler(m.getProfile())))

View File

@@ -106,7 +106,7 @@ public class Mxisd {
pMgr = new ProfileManager(ProfileProviders.get(), clientDns, httpClient);
notifMgr = new NotificationManager(cfg.getNotification(), NotificationHandlers.get());
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);
dirMgr = new DirectoryManager(cfg.getDirectory(), clientDns, httpClient, DirectoryProviders.get());
asHander = new AppSvcManager(cfg, store, pMgr, notifMgr, synapse);

View File

@@ -22,8 +22,12 @@ package io.kamax.mxisd.exception;
public class ObjectNotFoundException extends RuntimeException {
public ObjectNotFoundException(String message) {
super(message);
}
public ObjectNotFoundException(String type, String id) {
super(type + " with ID " + id + " does not exist");
this(type + " with ID " + id + " does not exist");
}
}

View File

@@ -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);
}
}

View File

@@ -20,6 +20,8 @@
package io.kamax.mxisd.invitation;
import java.util.List;
public interface IThreePidInviteReply {
String getId();
@@ -30,4 +32,6 @@ public interface IThreePidInviteReply {
String getDisplayName();
List<String> getPublicKeys();
}

View File

@@ -30,16 +30,17 @@ import io.kamax.mxisd.config.ServerConfig;
import io.kamax.mxisd.dns.FederationDnsOverwrite;
import io.kamax.mxisd.exception.BadRequestException;
import io.kamax.mxisd.exception.MappingAlreadyExistsException;
import io.kamax.mxisd.exception.ObjectNotFoundException;
import io.kamax.mxisd.lookup.SingleLookupReply;
import io.kamax.mxisd.lookup.ThreePidMapping;
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
import io.kamax.mxisd.notification.NotificationManager;
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 org.apache.commons.io.IOUtils;
import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
@@ -72,6 +73,7 @@ public class InvitationManager {
private ServerConfig srvCfg;
private IStorage storage;
private LookupStrategy lookupMgr;
private KeyManager keyMgr;
private SignatureManager signMgr;
private FederationDnsOverwrite dns;
private NotificationManager notifMgr;
@@ -85,6 +87,7 @@ public class InvitationManager {
MxisdConfig mxisdCfg,
IStorage storage,
LookupStrategy lookupMgr,
KeyManager keyMgr,
SignatureManager signMgr,
FederationDnsOverwrite dns,
NotificationManager notifMgr
@@ -93,6 +96,7 @@ public class InvitationManager {
this.srvCfg = mxisdCfg.getServer();
this.storage = storage;
this.lookupMgr = lookupMgr;
this.keyMgr = keyMgr;
this.signMgr = signMgr;
this.dns = dns;
this.notifMgr = notifMgr;
@@ -109,7 +113,7 @@ public class InvitationManager {
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);
});
@@ -221,7 +225,7 @@ public class InvitationManager {
log.info("Invite is already pending for {}:{}, returning data", invitation.getMedium(), invitation.getAddress());
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());
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 {
// FIXME we should check attempt and send if bigger
}
@@ -236,8 +240,20 @@ public class InvitationManager {
String token = RandomStringUtils.randomAlphanumeric(64);
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());
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) {
String medium = reply.getInvite().getMedium();
String address = reply.getInvite().getAddress();

View File

@@ -20,18 +20,24 @@
package io.kamax.mxisd.invitation;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class ThreePidInviteReply implements IThreePidInviteReply {
private String id;
private IThreePidInvite invite;
private String token;
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.invite = invite;
this.token = token;
this.displayName = displayName;
this.publicKeys = Collections.unmodifiableList(new ArrayList<>(publicKeys));
}
@Override
@@ -54,4 +60,9 @@ public class ThreePidInviteReply implements IThreePidInviteReply {
return displayName;
}
@Override
public List<String> getPublicKeys() {
return publicKeys;
}
}