Compare commits
1 Commits
v1.3.1
...
v1.4.0-alp
Author | SHA1 | Date | |
---|---|---|---|
|
acd8c7d7c5 |
@@ -101,7 +101,7 @@ dependencies {
|
||||
compile 'com.j256.ormlite:ormlite-jdbc:5.0'
|
||||
|
||||
// ed25519 handling
|
||||
compile 'net.i2p.crypto:eddsa:0.1.0'
|
||||
compile 'net.i2p.crypto:eddsa:0.3.0'
|
||||
|
||||
// LDAP connector
|
||||
compile 'org.apache.directory.api:api-all:1.0.0'
|
||||
|
@@ -82,7 +82,7 @@ public class HttpMxisd {
|
||||
// Identity endpoints
|
||||
.get(HelloHandler.Path, helloHandler)
|
||||
.get(HelloHandler.Path + "/", helloHandler) // Be lax with possibly trailing slash
|
||||
.get(SingleLookupHandler.Path, SaneHandler.around(new SingleLookupHandler(m.getIdentity(), m.getSign())))
|
||||
.get(SingleLookupHandler.Path, SaneHandler.around(new SingleLookupHandler(m.getConfig(), m.getIdentity(), m.getSign())))
|
||||
.post(BulkLookupHandler.Path, SaneHandler.around(new BulkLookupHandler(m.getIdentity())))
|
||||
.post(StoreInviteHandler.Path, storeInvHandler)
|
||||
.post(SessionStartHandler.Path, SaneHandler.around(new SessionStartHandler(m.getSession())))
|
||||
|
@@ -20,8 +20,6 @@
|
||||
|
||||
package io.kamax.mxisd;
|
||||
|
||||
import io.kamax.matrix.crypto.KeyManager;
|
||||
import io.kamax.matrix.crypto.SignatureManager;
|
||||
import io.kamax.mxisd.as.AppSvcManager;
|
||||
import io.kamax.mxisd.auth.AuthManager;
|
||||
import io.kamax.mxisd.auth.AuthProviders;
|
||||
@@ -48,6 +46,9 @@ import io.kamax.mxisd.profile.ProfileManager;
|
||||
import io.kamax.mxisd.profile.ProfileProviders;
|
||||
import io.kamax.mxisd.session.SessionManager;
|
||||
import io.kamax.mxisd.storage.IStorage;
|
||||
import io.kamax.mxisd.storage.crypto.Ed25519KeyManager;
|
||||
import io.kamax.mxisd.storage.crypto.KeyManager;
|
||||
import io.kamax.mxisd.storage.crypto.SignatureManager;
|
||||
import io.kamax.mxisd.storage.ormlite.OrmLiteSqlStorage;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
@@ -63,7 +64,7 @@ public class Mxisd {
|
||||
|
||||
private IStorage store;
|
||||
|
||||
private KeyManager keyMgr;
|
||||
private Ed25519KeyManager keyMgr;
|
||||
private SignatureManager signMgr;
|
||||
|
||||
// Features
|
||||
@@ -92,7 +93,7 @@ public class Mxisd {
|
||||
|
||||
store = new OrmLiteSqlStorage(cfg);
|
||||
keyMgr = CryptoFactory.getKeyManager(cfg.getKey());
|
||||
signMgr = CryptoFactory.getSignatureManager(keyMgr, cfg.getServer());
|
||||
signMgr = CryptoFactory.getSignatureManager(keyMgr);
|
||||
ClientDnsOverwrite clientDns = new ClientDnsOverwrite(cfg.getDns().getOverwrite());
|
||||
FederationDnsOverwrite fedDns = new FederationDnsOverwrite(cfg.getDns().getOverwrite());
|
||||
Synapse synapse = new Synapse(cfg.getSynapseSql());
|
||||
@@ -105,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.getInvite(), store, idStrategy, signMgr, fedDns, notifMgr);
|
||||
invMgr = new InvitationManager(cfg, store, idStrategy, 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);
|
||||
|
@@ -20,9 +20,8 @@
|
||||
|
||||
package io.kamax.mxisd.crypto;
|
||||
|
||||
import io.kamax.matrix.crypto.*;
|
||||
import io.kamax.mxisd.config.KeyConfig;
|
||||
import io.kamax.mxisd.config.ServerConfig;
|
||||
import io.kamax.mxisd.storage.crypto.*;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
@@ -31,10 +30,10 @@ import java.io.IOException;
|
||||
|
||||
public class CryptoFactory {
|
||||
|
||||
public static KeyManager getKeyManager(KeyConfig keyCfg) {
|
||||
_KeyStore store;
|
||||
public static Ed25519KeyManager getKeyManager(KeyConfig keyCfg) {
|
||||
KeyStore store;
|
||||
if (StringUtils.equals(":memory:", keyCfg.getPath())) {
|
||||
store = new KeyMemoryStore();
|
||||
store = new MemoryKeyStore();
|
||||
} else {
|
||||
File keyStore = new File(keyCfg.getPath());
|
||||
if (!keyStore.exists()) {
|
||||
@@ -45,14 +44,14 @@ public class CryptoFactory {
|
||||
}
|
||||
}
|
||||
|
||||
store = new KeyFileStore(keyCfg.getPath());
|
||||
store = new FileKeyStore(keyCfg.getPath());
|
||||
}
|
||||
|
||||
return new KeyManager(store);
|
||||
return new Ed25519KeyManager(store);
|
||||
}
|
||||
|
||||
public static SignatureManager getSignatureManager(KeyManager keyMgr, ServerConfig cfg) {
|
||||
return new SignatureManager(keyMgr, cfg.getName());
|
||||
public static SignatureManager getSignatureManager(Ed25519KeyManager keyMgr) {
|
||||
return new Ed25519SignatureManager(keyMgr);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -21,10 +21,11 @@
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.matrix.crypto.KeyManager;
|
||||
import io.kamax.mxisd.exception.BadRequestException;
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.undertow.handler.BasicHttpHandler;
|
||||
import io.kamax.mxisd.storage.crypto.GenericKeyIdentifier;
|
||||
import io.kamax.mxisd.storage.crypto.KeyManager;
|
||||
import io.kamax.mxisd.storage.crypto.KeyType;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -46,16 +47,12 @@ public class KeyGetHandler extends BasicHttpHandler {
|
||||
public void handleRequest(HttpServerExchange exchange) {
|
||||
String key = getQueryParameter(exchange, Key);
|
||||
String[] v = key.split(":", 2);
|
||||
String keyType = v[0];
|
||||
int keyId = Integer.parseInt(v[1]);
|
||||
String keyAlgo = v[0];
|
||||
String keyId = v[1];
|
||||
|
||||
if (!"ed25519".contentEquals(keyType)) {
|
||||
throw new BadRequestException("Invalid algorithm: " + keyType);
|
||||
}
|
||||
|
||||
log.info("Key {}:{} was requested", keyType, keyId);
|
||||
log.info("Key {}:{} was requested", keyAlgo, keyId);
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty("public_key", mgr.getPublicKeyBase64(keyId));
|
||||
obj.addProperty("public_key", mgr.getPublicKeyBase64(new GenericKeyIdentifier(KeyType.Regular, keyAlgo, keyId)));
|
||||
respond(exchange, obj);
|
||||
}
|
||||
|
||||
|
@@ -20,10 +20,10 @@
|
||||
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
|
||||
import io.kamax.matrix.crypto.KeyManager;
|
||||
import io.kamax.mxisd.http.IsAPIv1;
|
||||
import io.kamax.mxisd.storage.crypto.KeyManager;
|
||||
import io.kamax.mxisd.storage.crypto.KeyType;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -44,9 +44,7 @@ public class RegularKeyIsValidHandler extends KeyIsValidHandler {
|
||||
String pubKey = getQueryParameter(exchange, "public_key");
|
||||
log.info("Validating public key {}", pubKey);
|
||||
|
||||
// TODO do in manager
|
||||
boolean valid = StringUtils.equals(pubKey, mgr.getPublicKeyBase64(mgr.getCurrentIndex()));
|
||||
respondJson(exchange, valid ? validKey : invalidKey);
|
||||
respondJson(exchange, mgr.isValid(KeyType.Regular, pubKey) ? validKey : invalidKey);
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -21,15 +21,17 @@
|
||||
package io.kamax.mxisd.http.undertow.handler.identity.v1;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.matrix.crypto.SignatureManager;
|
||||
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.http.IsAPIv1;
|
||||
import io.kamax.mxisd.http.io.identity.SingeLookupReplyJson;
|
||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||
import io.kamax.mxisd.lookup.SingleLookupRequest;
|
||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||
import io.kamax.mxisd.storage.crypto.SignatureManager;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -42,10 +44,12 @@ public class SingleLookupHandler extends LookupHandler {
|
||||
|
||||
private transient final Logger log = LoggerFactory.getLogger(SingleLookupHandler.class);
|
||||
|
||||
private ServerConfig cfg;
|
||||
private LookupStrategy strategy;
|
||||
private SignatureManager signMgr;
|
||||
|
||||
public SingleLookupHandler(LookupStrategy strategy, SignatureManager signMgr) {
|
||||
public SingleLookupHandler(MxisdConfig cfg, LookupStrategy strategy, SignatureManager signMgr) {
|
||||
this.cfg = cfg.getServer();
|
||||
this.strategy = strategy;
|
||||
this.signMgr = signMgr;
|
||||
}
|
||||
@@ -72,7 +76,7 @@ public class SingleLookupHandler extends LookupHandler {
|
||||
|
||||
// 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(MatrixJson.encodeCanonical(obj)));
|
||||
obj.add(EventKey.Signatures.get(), signMgr.signMessageGson(cfg.getName(), MatrixJson.encodeCanonical(obj)));
|
||||
|
||||
respondJson(exchange, obj);
|
||||
}
|
||||
|
@@ -24,7 +24,6 @@ import com.google.gson.JsonObject;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import io.kamax.matrix.MatrixID;
|
||||
import io.kamax.matrix._MatrixID;
|
||||
import io.kamax.matrix.crypto.KeyManager;
|
||||
import io.kamax.matrix.json.GsonUtil;
|
||||
import io.kamax.mxisd.config.ServerConfig;
|
||||
import io.kamax.mxisd.exception.BadRequestException;
|
||||
@@ -36,6 +35,7 @@ import io.kamax.mxisd.invitation.IThreePidInvite;
|
||||
import io.kamax.mxisd.invitation.IThreePidInviteReply;
|
||||
import io.kamax.mxisd.invitation.InvitationManager;
|
||||
import io.kamax.mxisd.invitation.ThreePidInvite;
|
||||
import io.kamax.mxisd.storage.crypto.KeyManager;
|
||||
import io.undertow.server.HttpServerExchange;
|
||||
import io.undertow.util.QueryParameterUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -96,7 +96,8 @@ public class StoreInviteHandler extends BasicHttpHandler {
|
||||
IThreePidInvite invite = new ThreePidInvite(sender, inv.getMedium(), inv.getAddress(), inv.getRoomId(), parameters);
|
||||
IThreePidInviteReply reply = invMgr.storeInvite(invite);
|
||||
|
||||
respondJson(exchange, new ThreePidInviteReplyIO(reply, keyMgr.getPublicKeyBase64(keyMgr.getCurrentIndex()), cfg.getPublicUrl()));
|
||||
// FIXME the key info must be set by the invitation manager in the reply object!
|
||||
respondJson(exchange, new ThreePidInviteReplyIO(reply, keyMgr.getPublicKeyBase64(keyMgr.getServerSigningKey().getId()), cfg.getPublicUrl()));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -23,9 +23,10 @@ package io.kamax.mxisd.invitation;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.matrix.MatrixID;
|
||||
import io.kamax.matrix.crypto.SignatureManager;
|
||||
import io.kamax.matrix.json.GsonUtil;
|
||||
import io.kamax.mxisd.config.InvitationConfig;
|
||||
import io.kamax.mxisd.config.MxisdConfig;
|
||||
import io.kamax.mxisd.config.ServerConfig;
|
||||
import io.kamax.mxisd.dns.FederationDnsOverwrite;
|
||||
import io.kamax.mxisd.exception.BadRequestException;
|
||||
import io.kamax.mxisd.exception.MappingAlreadyExistsException;
|
||||
@@ -34,6 +35,7 @@ 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.ormlite.dao.ThreePidInviteIO;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.commons.lang.RandomStringUtils;
|
||||
@@ -67,6 +69,7 @@ public class InvitationManager {
|
||||
private transient final Logger log = LoggerFactory.getLogger(InvitationManager.class);
|
||||
|
||||
private InvitationConfig cfg;
|
||||
private ServerConfig srvCfg;
|
||||
private IStorage storage;
|
||||
private LookupStrategy lookupMgr;
|
||||
private SignatureManager signMgr;
|
||||
@@ -79,14 +82,15 @@ public class InvitationManager {
|
||||
private Map<String, IThreePidInviteReply> invitations = new ConcurrentHashMap<>();
|
||||
|
||||
public InvitationManager(
|
||||
InvitationConfig cfg,
|
||||
MxisdConfig mxisdCfg,
|
||||
IStorage storage,
|
||||
LookupStrategy lookupMgr,
|
||||
SignatureManager signMgr,
|
||||
FederationDnsOverwrite dns,
|
||||
NotificationManager notifMgr
|
||||
) {
|
||||
this.cfg = cfg;
|
||||
this.cfg = mxisdCfg.getInvite();
|
||||
this.srvCfg = mxisdCfg.getServer();
|
||||
this.storage = storage;
|
||||
this.lookupMgr = lookupMgr;
|
||||
this.signMgr = signMgr;
|
||||
@@ -280,7 +284,7 @@ public class InvitationManager {
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.addProperty("mxid", mxid);
|
||||
obj.addProperty("token", reply.getToken());
|
||||
obj.add("signatures", signMgr.signMessageGson(obj.toString()));
|
||||
obj.add("signatures", signMgr.signMessageGson(srvCfg.getName(), obj.toString()));
|
||||
|
||||
JsonObject objUp = new JsonObject();
|
||||
objUp.addProperty("mxid", mxid);
|
||||
@@ -298,7 +302,7 @@ public class InvitationManager {
|
||||
content.addProperty("address", address);
|
||||
content.addProperty("mxid", mxid);
|
||||
|
||||
content.add("signatures", signMgr.signMessageGson(content.toString()));
|
||||
content.add("signatures", signMgr.signMessageGson(srvCfg.getName(), content.toString()));
|
||||
|
||||
StringEntity entity = new StringEntity(content.toString(), StandardCharsets.UTF_8);
|
||||
entity.setContentType("application/json");
|
||||
|
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
public class Ed2219RegularKeyIdentifier extends RegularKeyIdentifier {
|
||||
|
||||
public Ed2219RegularKeyIdentifier(String serial) {
|
||||
super(KeyAlgorithm.Ed25519, serial);
|
||||
}
|
||||
|
||||
}
|
53
src/main/java/io/kamax/mxisd/storage/crypto/Ed25519Key.java
Normal file
53
src/main/java/io/kamax/mxisd/storage/crypto/Ed25519Key.java
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
public class Ed25519Key implements Key {
|
||||
|
||||
private KeyIdentifier id;
|
||||
private String privKey;
|
||||
|
||||
public Ed25519Key(KeyIdentifier id, String privKey) {
|
||||
if (!KeyAlgorithm.Ed25519.equals(id.getAlgorithm())) {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
|
||||
this.id = new GenericKeyIdentifier(id);
|
||||
this.privKey = privKey;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public KeyIdentifier getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrivateKeyBase64() {
|
||||
return privKey;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
import io.kamax.matrix.codec.MxBase64;
|
||||
import net.i2p.crypto.eddsa.EdDSAPrivateKey;
|
||||
import net.i2p.crypto.eddsa.EdDSAPublicKey;
|
||||
import net.i2p.crypto.eddsa.KeyPairGenerator;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSANamedCurveTable;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAParameterSpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPrivateKeySpec;
|
||||
import net.i2p.crypto.eddsa.spec.EdDSAPublicKeySpec;
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.apache.commons.lang3.RandomStringUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.KeyPair;
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
|
||||
public class Ed25519KeyManager implements KeyManager {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Ed25519KeyManager.class);
|
||||
|
||||
private final EdDSAParameterSpec keySpecs;
|
||||
private final KeyStore store;
|
||||
|
||||
public Ed25519KeyManager(KeyStore store) {
|
||||
this.keySpecs = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.ED_25519);
|
||||
this.store = store;
|
||||
|
||||
if (!store.getCurrentKey().isPresent()) {
|
||||
List<KeyIdentifier> keys = store.list(KeyType.Regular);
|
||||
if (keys.isEmpty()) {
|
||||
keys.add(generateKey(KeyType.Regular));
|
||||
}
|
||||
|
||||
store.setCurrentKey(keys.get(0));
|
||||
}
|
||||
}
|
||||
|
||||
protected String generateId() {
|
||||
ByteBuffer buffer = ByteBuffer.allocate(Long.BYTES);
|
||||
buffer.putLong(Instant.now().toEpochMilli() - 1546297200000L); // TS since 2019-01-01T00:00:00Z to keep IDs short
|
||||
return Base64.encodeBase64URLSafeString(buffer.array()) + RandomStringUtils.randomAlphanumeric(1);
|
||||
}
|
||||
|
||||
protected String getPrivateKeyBase64(EdDSAPrivateKey key) {
|
||||
return MxBase64.encode(key.getSeed());
|
||||
}
|
||||
|
||||
public EdDSAParameterSpec getKeySpecs() {
|
||||
return keySpecs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyIdentifier generateKey(KeyType type) {
|
||||
KeyIdentifier id;
|
||||
do {
|
||||
id = new GenericKeyIdentifier(type, KeyAlgorithm.Ed25519, generateId());
|
||||
} while (store.has(id));
|
||||
|
||||
KeyPair pair = (new KeyPairGenerator()).generateKeyPair();
|
||||
String keyEncoded = getPrivateKeyBase64((EdDSAPrivateKey) pair.getPrivate());
|
||||
|
||||
Key key = new GenericKey(id, true, keyEncoded);
|
||||
store.add(key);
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyIdentifier> getKeys(KeyType type) {
|
||||
return store.list(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key getServerSigningKey() {
|
||||
return store.get(store.getCurrentKey().orElseThrow(IllegalStateException::new));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key getKey(KeyIdentifier id) {
|
||||
return store.get(id);
|
||||
}
|
||||
|
||||
public EdDSAPrivateKeySpec getPrivateKeySpecs(KeyIdentifier id) {
|
||||
return new EdDSAPrivateKeySpec(java.util.Base64.getDecoder().decode(getKey(id).getPrivateKeyBase64()), keySpecs);
|
||||
}
|
||||
|
||||
public EdDSAPrivateKey getPrivateKey(KeyIdentifier id) {
|
||||
return new EdDSAPrivateKey(getPrivateKeySpecs(id));
|
||||
}
|
||||
|
||||
public EdDSAPublicKey getPublicKey(KeyIdentifier id) {
|
||||
EdDSAPrivateKeySpec privKeySpec = getPrivateKeySpecs(id);
|
||||
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKeySpec.getA(), keySpecs);
|
||||
return new EdDSAPublicKey(pubKeySpec);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableKey(KeyIdentifier id) {
|
||||
Key key = store.get(id);
|
||||
key = new GenericKey(id, false, key.getPrivateKeyBase64());
|
||||
store.update(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPublicKeyBase64(KeyIdentifier id) {
|
||||
return MxBase64.encode(getPublicKey(id).getAbyte());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(KeyType type, String publicKeyBase64) {
|
||||
// TODO caching?
|
||||
return getKeys(type).stream().anyMatch(id -> StringUtils.equals(getPublicKeyBase64(id), publicKeyBase64));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.matrix.codec.MxBase64;
|
||||
import io.kamax.matrix.json.MatrixJson;
|
||||
import net.i2p.crypto.eddsa.EdDSAEngine;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SignatureException;
|
||||
|
||||
public class Ed25519SignatureManager implements SignatureManager {
|
||||
|
||||
private final Ed25519KeyManager keyMgr;
|
||||
|
||||
public Ed25519SignatureManager(Ed25519KeyManager keyMgr) {
|
||||
this.keyMgr = keyMgr;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonObject signMessageGson(String domain, String message) {
|
||||
Signature sign = sign(message);
|
||||
|
||||
JsonObject keySignature = new JsonObject();
|
||||
// FIXME should create a signing key object what would give this ed and index values
|
||||
keySignature.addProperty(sign.getKey().getAlgorithm() + ":" + sign.getKey().getSerial(), sign.getSignature());
|
||||
JsonObject signature = new JsonObject();
|
||||
signature.add(domain, keySignature);
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Signature sign(JsonObject obj) {
|
||||
|
||||
return sign(MatrixJson.encodeCanonical(obj));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Signature sign(byte[] data) {
|
||||
try {
|
||||
KeyIdentifier signingKeyId = keyMgr.getServerSigningKey().getId();
|
||||
EdDSAEngine signEngine = new EdDSAEngine(MessageDigest.getInstance(keyMgr.getKeySpecs().getHashAlgorithm()));
|
||||
signEngine.initSign(keyMgr.getPrivateKey(signingKeyId));
|
||||
byte[] signRaw = signEngine.signOneShot(data);
|
||||
String sign = MxBase64.encode(signRaw);
|
||||
|
||||
return new Signature() {
|
||||
@Override
|
||||
public KeyIdentifier getKey() {
|
||||
return signingKeyId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSignature() {
|
||||
return sign;
|
||||
}
|
||||
};
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException | SignatureException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
import io.kamax.mxisd.exception.ObjectNotFoundException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FileKeyStore implements KeyStore {
|
||||
|
||||
public FileKeyStore(String path) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(KeyIdentifier id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyIdentifier> list() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyIdentifier> list(KeyType type) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key get(KeyIdentifier id) throws ObjectNotFoundException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Key key) throws IllegalStateException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Key key) throws ObjectNotFoundException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(KeyIdentifier id) throws ObjectNotFoundException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentKey(KeyIdentifier id) throws IllegalArgumentException {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<KeyIdentifier> getCurrentKey() {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
}
|
51
src/main/java/io/kamax/mxisd/storage/crypto/GenericKey.java
Normal file
51
src/main/java/io/kamax/mxisd/storage/crypto/GenericKey.java
Normal file
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
public class GenericKey implements Key {
|
||||
|
||||
private final KeyIdentifier id;
|
||||
private final boolean isValid;
|
||||
private final String privKey;
|
||||
|
||||
public GenericKey(KeyIdentifier id, boolean isValid, String privKey) {
|
||||
this.id = new GenericKeyIdentifier(id);
|
||||
this.isValid = isValid;
|
||||
this.privKey = privKey;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public KeyIdentifier getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid() {
|
||||
return isValid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrivateKeyBase64() {
|
||||
return privKey;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
public class GenericKeyIdentifier implements KeyIdentifier {
|
||||
|
||||
private final KeyType type;
|
||||
private final String algo;
|
||||
private final String serial;
|
||||
|
||||
public GenericKeyIdentifier(KeyIdentifier id) {
|
||||
this(id.getType(), id.getAlgorithm(), id.getSerial());
|
||||
}
|
||||
|
||||
public GenericKeyIdentifier(KeyType type, String algo, String serial) {
|
||||
this.type = type;
|
||||
this.algo = algo;
|
||||
this.serial = serial;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyType getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return algo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSerial() {
|
||||
return serial;
|
||||
}
|
||||
|
||||
}
|
44
src/main/java/io/kamax/mxisd/storage/crypto/Key.java
Normal file
44
src/main/java/io/kamax/mxisd/storage/crypto/Key.java
Normal file
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
/**
|
||||
* A signing key
|
||||
*/
|
||||
public interface Key {
|
||||
|
||||
KeyIdentifier getId();
|
||||
|
||||
/**
|
||||
* If the key is currently valid
|
||||
*
|
||||
* @return true if the key is valid, false if not
|
||||
*/
|
||||
boolean isValid();
|
||||
|
||||
/**
|
||||
* Get the private key
|
||||
*
|
||||
* @return the private key encoded as Base64
|
||||
*/
|
||||
String getPrivateKeyBase64();
|
||||
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
public interface KeyAlgorithm {
|
||||
|
||||
String Ed25519 = "ed25519";
|
||||
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
/**
|
||||
* Identifying data for a given Key.
|
||||
*/
|
||||
public interface KeyIdentifier {
|
||||
|
||||
/**
|
||||
* Type of key.
|
||||
*
|
||||
* @return The type of the key
|
||||
*/
|
||||
KeyType getType();
|
||||
|
||||
/**
|
||||
* Algorithm of the key. Typically <code>ed25519</code>.
|
||||
*
|
||||
* @return The algorithm of the key
|
||||
*/
|
||||
String getAlgorithm();
|
||||
|
||||
/**
|
||||
* Serial of the key, unique for the algorithm.
|
||||
* It is typically made of random alphanumerical characters.
|
||||
*
|
||||
* @return The serial of the key
|
||||
*/
|
||||
String getSerial();
|
||||
|
||||
}
|
41
src/main/java/io/kamax/mxisd/storage/crypto/KeyManager.java
Normal file
41
src/main/java/io/kamax/mxisd/storage/crypto/KeyManager.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface KeyManager {
|
||||
|
||||
KeyIdentifier generateKey(KeyType type);
|
||||
|
||||
List<KeyIdentifier> getKeys(KeyType type);
|
||||
|
||||
Key getServerSigningKey();
|
||||
|
||||
Key getKey(KeyIdentifier id);
|
||||
|
||||
void disableKey(KeyIdentifier id);
|
||||
|
||||
String getPublicKeyBase64(KeyIdentifier id);
|
||||
|
||||
boolean isValid(KeyType type, String publicKeyBase64);
|
||||
|
||||
}
|
98
src/main/java/io/kamax/mxisd/storage/crypto/KeyStore.java
Normal file
98
src/main/java/io/kamax/mxisd/storage/crypto/KeyStore.java
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
import io.kamax.mxisd.exception.ObjectNotFoundException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Store to persist signing keys and the identifier for the current long-term signing key
|
||||
*/
|
||||
public interface KeyStore {
|
||||
|
||||
/**
|
||||
* If a given key is currently stored
|
||||
*
|
||||
* @param id The Identifier elements for the key
|
||||
* @return true if the key is stored, false if not
|
||||
*/
|
||||
boolean has(KeyIdentifier id);
|
||||
|
||||
/**
|
||||
* List all keys within the store
|
||||
*
|
||||
* @return The list of key identifiers
|
||||
*/
|
||||
List<KeyIdentifier> list();
|
||||
|
||||
/**
|
||||
* List all keys of a given type within the store
|
||||
*
|
||||
* @param type The type to filter on
|
||||
* @return The list of keys identifiers matching the given type
|
||||
*/
|
||||
List<KeyIdentifier> list(KeyType type);
|
||||
|
||||
/**
|
||||
* Get the key that relates to the given identifier
|
||||
*
|
||||
* @param id The identifier of the key to get
|
||||
* @return The key
|
||||
* @throws ObjectNotFoundException If no key is found for that identifier
|
||||
*/
|
||||
Key get(KeyIdentifier id) throws ObjectNotFoundException;
|
||||
|
||||
/**
|
||||
* Add a key to the store
|
||||
*
|
||||
* @param key The key to store
|
||||
* @throws IllegalStateException If a key already exist for the given identifier data
|
||||
*/
|
||||
void add(Key key) throws IllegalStateException;
|
||||
|
||||
void update(Key key) throws ObjectNotFoundException;
|
||||
|
||||
/**
|
||||
* Delete a key from the store
|
||||
*
|
||||
* @param id The key identifier of the key to delete
|
||||
* @throws ObjectNotFoundException If no key is found for that identifier
|
||||
*/
|
||||
void delete(KeyIdentifier id) throws ObjectNotFoundException;
|
||||
|
||||
/**
|
||||
* Store the information of which key is the current signing key
|
||||
*
|
||||
* @param id The key identifier
|
||||
* @throws ObjectNotFoundException If the key is not known to the store
|
||||
*/
|
||||
void setCurrentKey(KeyIdentifier id) throws ObjectNotFoundException;
|
||||
|
||||
/**
|
||||
* Retrieve the previously stored information of which key is the current signing key, if any
|
||||
*
|
||||
* @return The optional key identifier that was previously stored
|
||||
*/
|
||||
Optional<KeyIdentifier> getCurrentKey();
|
||||
|
||||
}
|
39
src/main/java/io/kamax/mxisd/storage/crypto/KeyType.java
Normal file
39
src/main/java/io/kamax/mxisd/storage/crypto/KeyType.java
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
/**
|
||||
* Types of keys used by an Identity server.
|
||||
* See https://matrix.org/docs/spec/identity_service/r0.1.0.html#key-management
|
||||
*/
|
||||
public enum KeyType {
|
||||
|
||||
/**
|
||||
* Ephemeral keys are related to 3PID invites and are only valid while the invite is pending.
|
||||
*/
|
||||
Ephemeral,
|
||||
|
||||
/**
|
||||
* Regular keys are used by the Identity Server itself to sign requests/responses
|
||||
*/
|
||||
Regular
|
||||
|
||||
}
|
109
src/main/java/io/kamax/mxisd/storage/crypto/MemoryKeyStore.java
Normal file
109
src/main/java/io/kamax/mxisd/storage/crypto/MemoryKeyStore.java
Normal file
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
import io.kamax.mxisd.exception.ObjectNotFoundException;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class MemoryKeyStore implements KeyStore {
|
||||
|
||||
private Map<KeyType, Map<String, Map<String, String>>> keys = new ConcurrentHashMap<>();
|
||||
private KeyIdentifier current;
|
||||
|
||||
private Map<String, String> getMap(KeyType type, String algo) {
|
||||
return keys.computeIfAbsent(type, k -> new ConcurrentHashMap<>()).computeIfAbsent(algo, k -> new ConcurrentHashMap<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(KeyIdentifier id) {
|
||||
return getMap(id.getType(), id.getAlgorithm()).containsKey(id.getSerial());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyIdentifier> list() {
|
||||
List<KeyIdentifier> keyIds = new ArrayList<>();
|
||||
keys.forEach((key, value) -> value.forEach((key1, value1) -> value1.forEach((key2, value2) -> keyIds.add(new GenericKeyIdentifier(key, key1, key2)))));
|
||||
return keyIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<KeyIdentifier> list(KeyType type) {
|
||||
List<KeyIdentifier> keyIds = new ArrayList<>();
|
||||
keys.computeIfAbsent(type, t -> new ConcurrentHashMap<>()).forEach((key, value) -> value.forEach((key1, value1) -> keyIds.add(new GenericKeyIdentifier(type, key, key1))));
|
||||
return keyIds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Key get(KeyIdentifier id) throws ObjectNotFoundException {
|
||||
String data = getMap(id.getType(), id.getAlgorithm()).get(id.getSerial());
|
||||
if (Objects.isNull(data)) {
|
||||
throw new ObjectNotFoundException("Key", id.getType() + ":" + id.getAlgorithm() + ":" + id.getSerial());
|
||||
}
|
||||
|
||||
return new GenericKey(new GenericKeyIdentifier(id), StringUtils.isEmpty(data), data);
|
||||
}
|
||||
|
||||
private void set(Key key) {
|
||||
String data = key.isValid() ? key.getPrivateKeyBase64() : "";
|
||||
getMap(key.getId().getType(), key.getId().getAlgorithm()).put(key.getId().getSerial(), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(Key key) throws IllegalStateException {
|
||||
if (has(key.getId())) {
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
set(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Key key) throws ObjectNotFoundException {
|
||||
if (!has(key.getId())) {
|
||||
throw new ObjectNotFoundException("Key", key.getId().getType() + ":" + key.getId().getAlgorithm() + ":" + key.getId().getSerial());
|
||||
}
|
||||
|
||||
set(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(KeyIdentifier id) throws ObjectNotFoundException {
|
||||
keys.computeIfAbsent(id.getType(), k -> new ConcurrentHashMap<>()).computeIfAbsent(id.getAlgorithm(), k -> new ConcurrentHashMap<>()).remove(id.getSerial());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCurrentKey(KeyIdentifier id) throws ObjectNotFoundException {
|
||||
if (!has(id)) {
|
||||
throw new ObjectNotFoundException("Key", id.getType() + ":" + id.getAlgorithm() + ":" + id.getSerial());
|
||||
}
|
||||
|
||||
current = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<KeyIdentifier> getCurrentKey() {
|
||||
return Optional.ofNullable(current);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
public class RegularKeyIdentifier extends GenericKeyIdentifier {
|
||||
|
||||
public RegularKeyIdentifier(String algo, String serial) {
|
||||
super(KeyType.Regular, algo, serial);
|
||||
}
|
||||
|
||||
}
|
29
src/main/java/io/kamax/mxisd/storage/crypto/Signature.java
Normal file
29
src/main/java/io/kamax/mxisd/storage/crypto/Signature.java
Normal file
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
public interface Signature {
|
||||
|
||||
KeyIdentifier getKey();
|
||||
|
||||
String getSignature();
|
||||
|
||||
}
|
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.storage.crypto;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public interface SignatureManager {
|
||||
|
||||
JsonObject signMessageGson(String domain, String message);
|
||||
|
||||
/**
|
||||
* Sign the canonical form of a JSON object
|
||||
*
|
||||
* @param obj The JSON object to canonicalize and sign
|
||||
* @return The signature
|
||||
*/
|
||||
Signature sign(JsonObject obj);
|
||||
|
||||
/**
|
||||
* Sign the message, using UTF-8 as decoding character set
|
||||
*
|
||||
* @param message The UTF-8 encoded message
|
||||
* @return
|
||||
*/
|
||||
default Signature sign(String message) {
|
||||
return sign(message.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sign the data
|
||||
*
|
||||
* @param data The data to sign
|
||||
* @return The signature
|
||||
*/
|
||||
Signature sign(byte[] data);
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.test.storage.crypto;
|
||||
|
||||
public class KeyTest {
|
||||
|
||||
// As per https://matrix.org/docs/spec/appendices.html#signing-key
|
||||
public static final String Private = "YJDBA9Xnr2sVqXD9Vj7XVUnmFZcZrlw8Md7kMW+3XA1";
|
||||
|
||||
// The corresponding public key, not being documented in the spec
|
||||
public static final String Public = "XGX0JRS2Af3be3knz2fBiRbApjm2Dh61gXDJA8kcJNI";
|
||||
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2019 Kamax Sàrl
|
||||
*
|
||||
* 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.test.storage.crypto;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.matrix.json.GsonUtil;
|
||||
import io.kamax.matrix.json.MatrixJson;
|
||||
import io.kamax.mxisd.storage.crypto.*;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.core.Is.is;
|
||||
import static org.hamcrest.core.IsEqual.equalTo;
|
||||
import static org.junit.Assert.assertThat;
|
||||
|
||||
public class SignatureManagerTest {
|
||||
|
||||
private static SignatureManager signMgr;
|
||||
|
||||
private static SignatureManager build(String keySeed) {
|
||||
Ed25519Key key = new Ed25519Key(new Ed2219RegularKeyIdentifier("0"), keySeed);
|
||||
KeyStore store = new MemoryKeyStore();
|
||||
store.add(key);
|
||||
|
||||
return new Ed25519SignatureManager(new Ed25519KeyManager(store));
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
signMgr = build(KeyTest.Private);
|
||||
}
|
||||
|
||||
private void testSign(String value, String sign) {
|
||||
assertThat(signMgr.sign(value).getSignature(), is(equalTo(sign)));
|
||||
}
|
||||
|
||||
// As per https://matrix.org/docs/spec/appendices.html#json-signing
|
||||
@Test
|
||||
public void onEmptyObject() {
|
||||
String value = "{}";
|
||||
String sign = "K8280/U9SSy9IVtjBuVeLr+HpOB4BQFWbg+UZaADMtTdGYI7Geitb76LTrr5QV/7Xg4ahLwYGYZzuHGZKM5ZAQ";
|
||||
|
||||
testSign(value, sign);
|
||||
}
|
||||
|
||||
// As per https://matrix.org/docs/spec/appendices.html#json-signing
|
||||
@Test
|
||||
public void onSimpleObject() {
|
||||
JsonObject data = new JsonObject();
|
||||
data.addProperty("one", 1);
|
||||
data.addProperty("two", "Two");
|
||||
|
||||
String value = GsonUtil.get().toJson(data);
|
||||
String sign = "KqmLSbO39/Bzb0QIYE82zqLwsA+PDzYIpIRA2sRQ4sL53+sN6/fpNSoqE7BP7vBZhG6kYdD13EIMJpvhJI+6Bw";
|
||||
|
||||
testSign(value, sign);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onFederationHeader() {
|
||||
SignatureManager mgr = build("1QblgjFeL3IxoY4DKOR7p5mL5sQTC0ChmeMJlqb4d5M");
|
||||
|
||||
JsonObject o = new JsonObject();
|
||||
o.addProperty("method", "GET");
|
||||
o.addProperty("uri", "/_matrix/federation/v1/query/directory?room_alias=%23a%3Amxhsd.local.kamax.io%3A8447");
|
||||
o.addProperty("origin", "synapse.local.kamax.io");
|
||||
o.addProperty("destination", "mxhsd.local.kamax.io:8447");
|
||||
|
||||
String signExpected = "SEMGSOJEsoalrBfHqPO2QrSlbLaUYLHLk4e3q4IJ2JbgvCynT1onp7QF1U4Sl3G3NzybrgdnVvpqcaEgV0WPCw";
|
||||
Signature signProduced = mgr.sign(o);
|
||||
assertThat(signProduced.getSignature(), is(equalTo(signExpected)));
|
||||
}
|
||||
|
||||
@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 sign = "ObKA4PNQh2g6c7Yo2QcTcuDgIwhknG7ZfqmNYzbhrbLBOqZomU22xX9raufN2Y3ke1FXsDqsGs7WBDodmzZJCg";
|
||||
testSign(value, sign);
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user