Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
1e413af019 |
@@ -66,6 +66,7 @@ buildscript {
|
||||
|
||||
repositories {
|
||||
maven { url "https://kamax.io/maven/releases/" }
|
||||
maven { url "https://kamax.io/maven/snapshots/" }
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
@@ -80,7 +81,7 @@ dependencies {
|
||||
compile "org.springframework.boot:spring-boot-starter-thymeleaf:1.5.10.RELEASE"
|
||||
|
||||
// Matrix Java SDK
|
||||
compile 'io.kamax:matrix-java-sdk:0.0.8'
|
||||
compile 'io.kamax:matrix-java-sdk:0.0.11'
|
||||
|
||||
// ed25519 handling
|
||||
compile 'net.i2p.crypto:eddsa:0.1.0'
|
||||
@@ -94,9 +95,6 @@ dependencies {
|
||||
// HTTP connections
|
||||
compile 'org.apache.httpcomponents:httpclient:4.5.3'
|
||||
|
||||
// JSON
|
||||
compile 'com.google.code.gson:gson:2.8.1'
|
||||
|
||||
// Phone numbers validation
|
||||
compile 'com.googlecode.libphonenumber:libphonenumber:8.7.1'
|
||||
|
||||
|
@@ -22,13 +22,13 @@ package io.kamax.mxisd.controller.identity.v1;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import io.kamax.matrix.MatrixID;
|
||||
import io.kamax.matrix.crypto.KeyManager;
|
||||
import io.kamax.mxisd.config.ServerConfig;
|
||||
import io.kamax.mxisd.controller.identity.v1.io.ThreePidInviteReplyIO;
|
||||
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.key.KeyManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
|
@@ -22,9 +22,9 @@ package io.kamax.mxisd.controller.identity.v1;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.matrix.crypto.KeyManager;
|
||||
import io.kamax.mxisd.controller.identity.v1.io.KeyValidityJson;
|
||||
import io.kamax.mxisd.exception.BadRequestException;
|
||||
import io.kamax.mxisd.key.KeyManager;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@@ -64,7 +64,7 @@ public class KeyController {
|
||||
|
||||
@RequestMapping(value = "/pubkey/ephemeral/isvalid", method = GET)
|
||||
public String checkEphemeralKeyValidity(HttpServletRequest request) {
|
||||
log.warn("Ephemeral key was request but no ephemeral key are generated, replying not valid");
|
||||
log.warn("Ephemeral key was requested but no ephemeral key are generated, replying not valid");
|
||||
|
||||
return invalidKey;
|
||||
}
|
||||
|
@@ -22,11 +22,14 @@ package io.kamax.mxisd.controller.identity.v1;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.matrix.crypto.SignatureManager;
|
||||
import io.kamax.matrix.event.EventKey;
|
||||
import io.kamax.matrix.json.MatrixJson;
|
||||
import io.kamax.mxisd.config.MatrixConfig;
|
||||
import io.kamax.mxisd.controller.identity.v1.io.SingeLookupReplyJson;
|
||||
import io.kamax.mxisd.exception.InternalServerError;
|
||||
import io.kamax.mxisd.lookup.*;
|
||||
import io.kamax.mxisd.lookup.strategy.LookupStrategy;
|
||||
import io.kamax.mxisd.signature.SignatureManager;
|
||||
import io.kamax.mxisd.util.GsonParser;
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
@@ -57,6 +60,9 @@ public class MappingController {
|
||||
private Gson gson = new Gson();
|
||||
private GsonParser parser = new GsonParser(gson);
|
||||
|
||||
@Autowired
|
||||
private MatrixConfig mxCfg;
|
||||
|
||||
@Autowired
|
||||
private LookupStrategy strategy;
|
||||
|
||||
@@ -92,17 +98,13 @@ public class MappingController {
|
||||
}
|
||||
|
||||
SingleLookupReply lookup = lookupOpt.get();
|
||||
if (lookup.isSigned()) {
|
||||
log.info("Lookup is already signed, sending as-is");
|
||||
return lookup.getBody();
|
||||
} else {
|
||||
log.info("Lookup is not signed, signing");
|
||||
|
||||
// FIXME signing should be done in the business model, not in the controller
|
||||
JsonObject obj = gson.toJsonTree(new SingeLookupReplyJson(lookup)).getAsJsonObject();
|
||||
obj.add("signatures", signMgr.signMessageGson(gson.toJson(obj)));
|
||||
obj.add(EventKey.Signatures.get(), signMgr.signMessageGson(MatrixJson.encodeCanonical(obj)));
|
||||
|
||||
return gson.toJson(obj);
|
||||
}
|
||||
}
|
||||
|
||||
@RequestMapping(value = "/bulk_lookup", method = POST)
|
||||
String bulkLookup(HttpServletRequest request) {
|
||||
|
@@ -22,9 +22,6 @@ package io.kamax.mxisd.controller.identity.v1.io;
|
||||
|
||||
import io.kamax.mxisd.lookup.SingleLookupReply;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class SingeLookupReplyJson {
|
||||
|
||||
private String address;
|
||||
@@ -33,7 +30,6 @@ public class SingeLookupReplyJson {
|
||||
private long not_after;
|
||||
private long not_before;
|
||||
private long ts;
|
||||
private Map<String, Map<String, String>> signatures = new HashMap<>();
|
||||
|
||||
public SingeLookupReplyJson(SingleLookupReply reply) {
|
||||
this.address = reply.getRequest().getThreePid();
|
||||
@@ -68,8 +64,4 @@ public class SingeLookupReplyJson {
|
||||
return ts;
|
||||
}
|
||||
|
||||
public boolean isSigned() {
|
||||
return signatures != null && !signatures.isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -24,6 +24,7 @@ import com.google.gson.Gson;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.matrix.MatrixID;
|
||||
import io.kamax.matrix.crypto.SignatureManager;
|
||||
import io.kamax.mxisd.config.InvitationConfig;
|
||||
import io.kamax.mxisd.dns.FederationDnsOverwrite;
|
||||
import io.kamax.mxisd.exception.BadRequestException;
|
||||
@@ -32,7 +33,6 @@ 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.signature.SignatureManager;
|
||||
import io.kamax.mxisd.storage.IStorage;
|
||||
import io.kamax.mxisd.storage.ormlite.ThreePidInviteIO;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2017 Maxime Dor
|
||||
*
|
||||
* https://max.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.key;
|
||||
|
||||
import io.kamax.mxisd.config.KeyConfig;
|
||||
import net.i2p.crypto.eddsa.EdDSAEngine;
|
||||
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.io.FileUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyPair;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
public class KeyManager {
|
||||
|
||||
@Autowired
|
||||
private KeyConfig keyCfg;
|
||||
|
||||
private EdDSAParameterSpec keySpecs;
|
||||
private EdDSAEngine signEngine;
|
||||
private List<KeyPair> keys;
|
||||
|
||||
@PostConstruct
|
||||
public void build() {
|
||||
try {
|
||||
keySpecs = EdDSANamedCurveTable.getByName(EdDSANamedCurveTable.CURVE_ED25519_SHA512);
|
||||
signEngine = new EdDSAEngine(MessageDigest.getInstance(keySpecs.getHashAlgorithm()));
|
||||
keys = new ArrayList<>();
|
||||
|
||||
Path privKey = Paths.get(keyCfg.getPath());
|
||||
|
||||
if (!Files.exists(privKey)) {
|
||||
KeyPair pair = (new KeyPairGenerator()).generateKeyPair();
|
||||
String keyEncoded = Base64.getEncoder().encodeToString(pair.getPrivate().getEncoded());
|
||||
FileUtils.writeStringToFile(privKey.toFile(), keyEncoded, StandardCharsets.ISO_8859_1);
|
||||
keys.add(pair);
|
||||
} else {
|
||||
if (Files.isDirectory(privKey)) {
|
||||
throw new RuntimeException("Invalid path for private key: " + privKey.toString());
|
||||
}
|
||||
|
||||
if (Files.isReadable(privKey)) {
|
||||
byte[] seed = Base64.getDecoder().decode(FileUtils.readFileToString(privKey.toFile(), StandardCharsets.ISO_8859_1));
|
||||
EdDSAPrivateKeySpec privKeySpec = new EdDSAPrivateKeySpec(seed, keySpecs);
|
||||
EdDSAPublicKeySpec pubKeySpec = new EdDSAPublicKeySpec(privKeySpec.getA(), keySpecs);
|
||||
keys.add(new KeyPair(new EdDSAPublicKey(pubKeySpec), new EdDSAPrivateKey(privKeySpec)));
|
||||
}
|
||||
}
|
||||
} catch (NoSuchAlgorithmException | IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getCurrentIndex() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public KeyPair getKeys(int index) {
|
||||
return keys.get(index);
|
||||
}
|
||||
|
||||
public PrivateKey getPrivateKey(int index) {
|
||||
return getKeys(index).getPrivate();
|
||||
}
|
||||
|
||||
public EdDSAPublicKey getPublicKey(int index) {
|
||||
return (EdDSAPublicKey) getKeys(index).getPublic();
|
||||
}
|
||||
|
||||
public EdDSAParameterSpec getSpecs() {
|
||||
return keySpecs;
|
||||
}
|
||||
|
||||
public String getPublicKeyBase64(int index) {
|
||||
return Base64.getEncoder().encodeToString(getPublicKey(index).getAbyte());
|
||||
}
|
||||
|
||||
}
|
@@ -33,7 +33,6 @@ public class SingleLookupReply {
|
||||
private static Gson gson = new Gson();
|
||||
|
||||
private boolean isRecursive;
|
||||
private boolean isSigned;
|
||||
private String body;
|
||||
private SingleLookupRequest request;
|
||||
private _MatrixID mxid;
|
||||
@@ -53,7 +52,6 @@ public class SingleLookupReply {
|
||||
reply.notAfter = Instant.ofEpochMilli(json.getNot_after());
|
||||
reply.notBefore = Instant.ofEpochMilli(json.getNot_before());
|
||||
reply.timestamp = Instant.ofEpochMilli(json.getTs());
|
||||
reply.isSigned = json.isSigned();
|
||||
} catch (JsonSyntaxException e) {
|
||||
// stub - we only want to try, nothing more
|
||||
}
|
||||
@@ -85,10 +83,6 @@ public class SingleLookupReply {
|
||||
return isRecursive;
|
||||
}
|
||||
|
||||
public boolean isSigned() {
|
||||
return isSigned;
|
||||
}
|
||||
|
||||
public String getBody() {
|
||||
return body;
|
||||
}
|
||||
|
@@ -1,79 +0,0 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2017 Maxime Dor
|
||||
*
|
||||
* https://max.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.signature;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import io.kamax.mxisd.config.ServerConfig;
|
||||
import io.kamax.mxisd.exception.InternalServerError;
|
||||
import io.kamax.mxisd.key.KeyManager;
|
||||
import net.i2p.crypto.eddsa.EdDSAEngine;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Base64;
|
||||
|
||||
@Component
|
||||
public class SignatureManager {
|
||||
|
||||
@Autowired
|
||||
private KeyManager keyMgr;
|
||||
|
||||
@Autowired
|
||||
private ServerConfig srvCfg;
|
||||
|
||||
private EdDSAEngine signEngine;
|
||||
|
||||
private String sign(String message) {
|
||||
try {
|
||||
byte[] signRaw = signEngine.signOneShot(message.getBytes());
|
||||
return Base64.getEncoder().encodeToString(signRaw);
|
||||
} catch (SignatureException e) {
|
||||
throw new InternalServerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public JsonObject signMessageGson(String message) {
|
||||
String sign = sign(message);
|
||||
|
||||
JsonObject keySignature = new JsonObject();
|
||||
keySignature.addProperty("ed25519:" + keyMgr.getCurrentIndex(), sign);
|
||||
JsonObject signature = new JsonObject();
|
||||
signature.add(srvCfg.getName(), keySignature);
|
||||
|
||||
return signature;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void build() {
|
||||
try {
|
||||
signEngine = new EdDSAEngine(MessageDigest.getInstance(keyMgr.getSpecs().getHashAlgorithm()));
|
||||
signEngine.initSign(keyMgr.getPrivateKey(keyMgr.getCurrentIndex()));
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
57
src/main/java/io/kamax/mxisd/spring/CryptoFactory.java
Normal file
57
src/main/java/io/kamax/mxisd/spring/CryptoFactory.java
Normal file
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* mxisd - Matrix Identity Server Daemon
|
||||
* Copyright (C) 2018 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.spring;
|
||||
|
||||
import io.kamax.matrix.crypto.KeyFileStore;
|
||||
import io.kamax.matrix.crypto.KeyManager;
|
||||
import io.kamax.matrix.crypto.SignatureManager;
|
||||
import io.kamax.mxisd.config.KeyConfig;
|
||||
import io.kamax.mxisd.config.MatrixConfig;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
@Configuration
|
||||
public class CryptoFactory {
|
||||
|
||||
@Bean
|
||||
public KeyManager getKeyManager(KeyConfig keyCfg) {
|
||||
File keyStore = new File(keyCfg.getPath());
|
||||
if (!keyStore.exists()) {
|
||||
try {
|
||||
FileUtils.touch(keyStore);
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
return new KeyManager(new KeyFileStore(keyCfg.getPath()));
|
||||
}
|
||||
|
||||
@Bean
|
||||
public SignatureManager getSignatureManager(KeyManager keyMgr, MatrixConfig mxCfg) {
|
||||
return new SignatureManager(keyMgr, mxCfg.getDomain());
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user