diff --git a/.gitignore b/.gitignore index 6b0bf13b3c857d41b467d1bacc6634f5cbd386c8..4025da0208de7d028d455a6666c09d094687e1ae 100644 --- a/.gitignore +++ b/.gitignore @@ -88,6 +88,5 @@ fabric.properties # Maven log/ target/ -jams/ testdb/ tomcat*/ diff --git a/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java b/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java new file mode 100644 index 0000000000000000000000000000000000000000..610e4e952e14a37ed899bbaeda2c538208d9f2e9 --- /dev/null +++ b/ad-connector/src/main/java/net/jami/jams/ad/connector/ADConnector.java @@ -0,0 +1,99 @@ +package net.jami.jams.ad.connector; + +import com.imperva.ddc.core.query.Endpoint; +import com.jsoniter.JsonIterator; +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.ad.connector.service.AuthenticationService; +import net.jami.jams.common.authentication.AuthenticationSource; +import net.jami.jams.common.authentication.AuthenticationSourceInfo; +import net.jami.jams.common.authentication.AuthenticationSourceType; +import net.jami.jams.common.authentication.activedirectory.ActiveDirectorySettings; +import net.jami.jams.common.objects.user.User; +import net.jami.jams.common.objects.user.UserProfile; +import net.jami.jams.common.serialization.JsoniterRegistry; + +import java.util.concurrent.ConcurrentLinkedQueue; + +@Slf4j +public class ADConnector implements AuthenticationSource { + + private static final ConcurrentLinkedQueue<Endpoint> endpoints = new ConcurrentLinkedQueue<>(); + private static ActiveDirectorySettings settings; + private AuthenticationService authenticationService = new AuthenticationService(); + + public ADConnector(String settings) { + JsoniterRegistry.initCodecs(); + ADConnector.settings = JsonIterator.deserialize(settings,ActiveDirectorySettings.class); + for(int i=0; i<10;i++){ + Endpoint endpoint = new Endpoint(); + endpoint.setSecuredConnection(ADConnector.settings.getSsl()); + endpoint.setPort(ADConnector.settings.getPort()); + endpoint.setHost(ADConnector.settings.getHost()); + endpoints.add(endpoint); + } + log.info("Started Active Directory Connector!"); + } + + public static Endpoint getConnection(){ + Endpoint endpoint = null; + while(endpoint == null){ + endpoint = endpoints.poll(); + } + endpoint.setUserAccountName(settings.getUsername()); //* You can use the user's Distinguished Name as well + endpoint.setPassword(settings.getPassword()); + return endpoint; + } + + public static Endpoint getConnection(String username, String password){ + Endpoint endpoint = null; + while(endpoint == null){ + endpoint = endpoints.poll(); + } + endpoint.setUserAccountName(ADConnector.settings.getRealm() + "\\" + username); //* You can use the user's Distinguished Name as well + endpoint.setPassword(password); + return endpoint; + } + + public static void returnConnection(Endpoint connection){ + endpoints.add(connection); + } + + + @Override + public boolean createUser(User user) { + return false; + } + + @Override + public UserProfile getUserProfile(String username) { + return null; + } + + + @Override + public boolean authenticate(String username, String password) { + try { + return authenticationService.authenticateUser(username, password); + } + catch (Exception e){ + return false; + } + } + + @Override + public AuthenticationSourceInfo getInfo() { + return new AuthenticationSourceInfo(settings.getRealm(), AuthenticationSourceType.AD); + } + + @Override + public boolean testConfiguration(String configuration) { + return false; + } + + @Override + public boolean updatePassword(User user, String password) { + return false; + } + + +} diff --git a/ad-connector/src/main/java/net/jami/jams/ad/connector/service/AuthenticationService.java b/ad-connector/src/main/java/net/jami/jams/ad/connector/service/AuthenticationService.java new file mode 100644 index 0000000000000000000000000000000000000000..deb25d253ab208af1b9592151e9edad976fc1b7c --- /dev/null +++ b/ad-connector/src/main/java/net/jami/jams/ad/connector/service/AuthenticationService.java @@ -0,0 +1,25 @@ +package net.jami.jams.ad.connector.service; + +import com.imperva.ddc.core.query.ConnectionResponse; +import com.imperva.ddc.core.query.Endpoint; +import com.imperva.ddc.service.DirectoryConnectorService; +import net.jami.jams.ad.connector.ADConnector; + +public class AuthenticationService { + + public boolean authenticateUser(String username, String password) { + Endpoint endpoint = ADConnector.getConnection(username, password); + try { + ConnectionResponse connectionResponse = DirectoryConnectorService.authenticate(endpoint); + return !connectionResponse.isError(); + } + catch (Exception e){ + return false; + } + finally { + ADConnector.returnConnection(endpoint); + } + } + + +} diff --git a/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java b/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java new file mode 100644 index 0000000000000000000000000000000000000000..be611eec61c396467ae7fed577b312e96a8b0074 --- /dev/null +++ b/ad-connector/src/main/java/net/jami/jams/ad/connector/service/UserProfileService.java @@ -0,0 +1,7 @@ +package net.jami.jams.ad.connector.service; + +public class UserProfileService { + + + +} diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/CryptoEngine.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/CryptoEngine.java new file mode 100644 index 0000000000000000000000000000000000000000..13500473cd9bacac2cbdb8b92d9fde5f008258d1 --- /dev/null +++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/CryptoEngine.java @@ -0,0 +1,85 @@ +package net.jami.jams.cryptoengine; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.cryptoengineapi.CertificateAuthority; +import net.jami.jams.common.objects.devices.Device; +import net.jami.jams.common.objects.requests.RevocationRequest; +import net.jami.jams.common.objects.system.SystemAccount; +import net.jami.jams.common.objects.user.User; +import net.jami.jams.cryptoengine.workers.crl.CRLWorker; +import net.jami.jams.cryptoengine.workers.csr.CertificateWorker; +import net.jami.jams.cryptoengine.workers.ocsp.OCSPWorker; +import org.bouncycastle.cert.X509CRLHolder; +import org.bouncycastle.cert.ocsp.OCSPReq; +import org.bouncycastle.cert.ocsp.OCSPResp; +import org.bouncycastle.jce.provider.BouncyCastleProvider; + +import java.security.Security; +import java.util.concurrent.atomic.AtomicReference; + +@Slf4j +public class CryptoEngine implements CertificateAuthority { + + + //These are the workers which are responsible for CRL/OCSP, they have an odd relationship. + private static CRLWorker crlWorker; + private static OCSPWorker ocspWorker; + public static volatile String serverDomain; + public static volatile String signingAlgorithm; + + //Various times + public static long caLifetime = 360_000_000; + public static long crlLifetime = 360_000_000; + public static long userLifetime = 360_000_000; + public static long deviceLifetime = 360_000_000; + + //CA certificate & OCSP Certificates, because they are often used. + public static SystemAccount CA; + public static SystemAccount OCSP; + + static { + Security.addProvider(new BouncyCastleProvider()); + } + + @Override + public void init(String domain, String signingAlgorithm, SystemAccount ca, SystemAccount ocsp) { + serverDomain = domain; + CA = ca; + OCSP = ocsp; + CryptoEngine.signingAlgorithm = signingAlgorithm; + crlWorker = new CRLWorker(CA.getPrivateKey(), CA.getCertificate()); + } + + @Override + public User getSignedCertificate(User user) { + return CertificateWorker.getSignedCertificate(user); + } + + @Override + public Device getSignedCertificate(User user, Device device) { + return CertificateWorker.getSignedCertificate(user, device); + } + + @Override + public SystemAccount getSignedCertificate(SystemAccount systemAccount) { + return CertificateWorker.getSignedCertificate(systemAccount); + } + + @Override + public void revokeCertificate(RevocationRequest revocationRequest) { + crlWorker.getInput().add(revocationRequest); + synchronized (crlWorker.getInput()) { + crlWorker.getInput().notify(); + } + } + + @Override + public AtomicReference<X509CRLHolder> getLatestCRL() { + return crlWorker.getExistingCRL(); + } + + @Override + public OCSPResp getOCSPResponse(OCSPReq ocspRequest) { + return null; + } +} diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/X509Worker.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/X509Worker.java new file mode 100644 index 0000000000000000000000000000000000000000..3746eba42336bcd0c9983670ad67645061185f45 --- /dev/null +++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/X509Worker.java @@ -0,0 +1,24 @@ +package net.jami.jams.cryptoengine.workers; + +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.concurrent.ConcurrentLinkedQueue; + +@Getter +@Setter +@Slf4j +public abstract class X509Worker<T> extends Thread{ + + private ConcurrentLinkedQueue<T> input = new ConcurrentLinkedQueue<>(); + private PrivateKey signingKey; + private X509Certificate certificate; + + public X509Worker(PrivateKey privateKey, X509Certificate certificate) { + this.signingKey = privateKey; + this.certificate = certificate; + } +} diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/crl/CRLWorker.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/crl/CRLWorker.java new file mode 100644 index 0000000000000000000000000000000000000000..3b9c05f7adff67e6cb79115bc4aa113f7319ddf7 --- /dev/null +++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/crl/CRLWorker.java @@ -0,0 +1,78 @@ +package net.jami.jams.cryptoengine.workers.crl; + +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.objects.requests.RevocationRequest; +import net.jami.jams.cryptoengine.CryptoEngine; +import net.jami.jams.cryptoengine.workers.X509Worker; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.CRLReason; +import org.bouncycastle.cert.X509CRLHolder; +import org.bouncycastle.cert.X509v2CRLBuilder; +import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder; + +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.util.Date; +import java.util.concurrent.atomic.AtomicReference; + +@Slf4j +public class CRLWorker extends X509Worker<RevocationRequest> { + + @Getter + private AtomicReference<X509CRLHolder> existingCRL = null; + + public CRLWorker(PrivateKey privateKey, X509Certificate certificate) { + super(privateKey, certificate); + this.setDaemon(true); + this.start(); + log.info("Instantiated & started a CRL Worker..."); + } + + //Basically we just publish it in the CRL. + private void revokeCertificate(RevocationRequest revocationRequest){ + try { + X509v2CRLBuilder crlBuilder = new X509v2CRLBuilder(new X500Name((getCertificate()).getSubjectDN().getName()), new Date()); + if(revocationRequest != null) { + crlBuilder.addCRLEntry(revocationRequest.getIdentifier(), new Date(), CRLReason.privilegeWithdrawn); + } + if (existingCRL != null){ + crlBuilder.addCRL(existingCRL.get()); + } + else existingCRL = new AtomicReference<>(); + existingCRL.set(crlBuilder.build(new JcaContentSignerBuilder("SHA512WITHRSA").setProvider("BC").build(getSigningKey()))); + log.info("Successfully processed request for ID " + revocationRequest.getIdentifier()); + } + catch (Exception e){ + log.error("Failed to sign a CRL with error " + e.toString()); + } + } + + @Override + public void run() { + boolean needsRefresh = false; + while(true){ + try{ + while(getInput().isEmpty()){ + if(needsRefresh){ + revokeCertificate(null); + needsRefresh = false; + } + synchronized (getInput()){ + getInput().wait(CryptoEngine.crlLifetime - 10_000); + needsRefresh = true; + } + } + while(!getInput().isEmpty()) { + RevocationRequest revocationRequest = getInput().poll(); + revokeCertificate(revocationRequest); + needsRefresh = false; + log.info("Successfully revoked the certificated with ID " + revocationRequest.getIdentifier()); + } + } + catch (Exception e){ + log.error("An error has occured in the CRL signing thread: " + e.toString()); + } + } + } +} diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/CertificateWorker.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/CertificateWorker.java new file mode 100644 index 0000000000000000000000000000000000000000..150cb0dbb37b51905f018914301db7df00106cf5 --- /dev/null +++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/CertificateWorker.java @@ -0,0 +1,30 @@ +package net.jami.jams.cryptoengine.workers.csr; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.objects.devices.Device; +import net.jami.jams.common.objects.system.SystemAccount; +import net.jami.jams.common.objects.user.User; +import net.jami.jams.cryptoengine.workers.csr.builders.DeviceBuilder; +import net.jami.jams.cryptoengine.workers.csr.builders.SystemAccountBuilder; +import net.jami.jams.cryptoengine.workers.csr.builders.UserBuilder; + +@Slf4j +public class CertificateWorker { + + //The CSR here is null because we generate a certificate and keypair. + public static SystemAccount getSignedCertificate(SystemAccount systemAccount) { + switch (systemAccount.getSystemAccountType()){ + case CA: return SystemAccountBuilder.generateCA(systemAccount); + case OCSP: return SystemAccountBuilder.generateOCSP(systemAccount); + default: return null; + } + } + + public static User getSignedCertificate(User user){ + return UserBuilder.generateUser(user); + } + + public static Device getSignedCertificate(User user, Device device){ + return DeviceBuilder.generateDevice(user,device); + } +} diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/DeviceBuilder.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/DeviceBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..8c21eda83deaea4eff6ed9597679683026eeb308 --- /dev/null +++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/DeviceBuilder.java @@ -0,0 +1,39 @@ +package net.jami.jams.cryptoengine.workers.csr.builders; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.objects.devices.Device; +import net.jami.jams.common.objects.user.User; +import net.jami.jams.cryptoengine.CryptoEngine; +import net.jami.jams.cryptoengine.workers.csr.utils.CertificateSigner; +import net.jami.jams.cryptoengine.workers.csr.utils.ExtensionLibrary; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; + +import java.math.BigInteger; +import java.security.SecureRandom; +import java.util.Date; + +@Slf4j +public class DeviceBuilder { + + public static Device generateDevice(User user, Device device){ + try { + X509v3CertificateBuilder builder = new X509v3CertificateBuilder( + new JcaX509CertificateHolder(user.getCertificate()).getSubject(), + new BigInteger(256, new SecureRandom()), + new Date(System.currentTimeMillis()), + new Date(System.currentTimeMillis() + CryptoEngine.deviceLifetime), + device.getCertificationRequest().getSubject(), + device.getCertificationRequest().getSubjectPublicKeyInfo() + ); + device.setCertificate(CertificateSigner.signCertificate(user.getPrivateKey(),builder, ExtensionLibrary.deviceExtensions)); + return device; + } + catch (Exception e){ + log.error("Could not generate a user certificate: " + e.toString()); + return null; + } + } + + +} diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/SystemAccountBuilder.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/SystemAccountBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..353ed47c7ea7ad620bfd2cd75396088e6cda3f2a --- /dev/null +++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/SystemAccountBuilder.java @@ -0,0 +1,71 @@ +package net.jami.jams.cryptoengine.workers.csr.builders; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.objects.system.SystemAccount; +import net.jami.jams.cryptoengine.CryptoEngine; +import net.jami.jams.cryptoengine.workers.csr.utils.CertificateSigner; +import net.jami.jams.cryptoengine.workers.csr.utils.ExtensionLibrary; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.util.Date; + +@Slf4j +public class SystemAccountBuilder { + + //Self-signed because it is a CA + public static SystemAccount generateCA(SystemAccount systemAccount){ + try { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(4096); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + X509v3CertificateBuilder builder = new X509v3CertificateBuilder( + new X500Name("CN=" + systemAccount.getX509Fields().getDN()), + new BigInteger(256, new SecureRandom()), + new Date(System.currentTimeMillis()), + new Date(System.currentTimeMillis() + CryptoEngine.caLifetime), + new X500Name("CN="+ systemAccount.getX509Fields().getDN()), + SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()) + ); + systemAccount.setPrivateKey(keyPair.getPrivate()); + systemAccount.setCertificate(CertificateSigner.signCertificate(keyPair.getPrivate(),builder, ExtensionLibrary.caExtensions)); + return systemAccount; + } + catch (Exception e){ + log.error("Could not generate the system's CA: " + e.toString()); + return null; + } + } + + //This is signed by the CA, the OCSP certificate has the same lifetime as the CA by default. + //someday this could change. + public static SystemAccount generateOCSP(SystemAccount systemAccount){ + try { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(4096); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + X509v3CertificateBuilder builder = new X509v3CertificateBuilder( + new JcaX509CertificateHolder(CryptoEngine.CA.getCertificate()).getSubject(), + new BigInteger(256, new SecureRandom()), + new Date(System.currentTimeMillis()), + new Date(System.currentTimeMillis() + CryptoEngine.caLifetime), + new X500Name("CN=" + systemAccount.getX509Fields().getDN()), + SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()) + ); + systemAccount.setPrivateKey(keyPair.getPrivate()); + systemAccount.setCertificate(CertificateSigner.signCertificate(CryptoEngine.CA.getPrivateKey(), builder, ExtensionLibrary.caExtensions)); + return systemAccount; + } + catch (Exception e){ + log.error("Could not generate the system's OCSP: " + e.toString()); + return null; + } + } + +} diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/UserBuilder.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/UserBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..5ef3929fb4ba95219570fc94444333f16b614922 --- /dev/null +++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/builders/UserBuilder.java @@ -0,0 +1,44 @@ +package net.jami.jams.cryptoengine.workers.csr.builders; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.objects.user.User; +import net.jami.jams.cryptoengine.CryptoEngine; +import net.jami.jams.cryptoengine.workers.csr.utils.CertificateSigner; +import net.jami.jams.cryptoengine.workers.csr.utils.ExtensionLibrary; +import org.bouncycastle.asn1.x500.X500Name; +import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder; + +import java.math.BigInteger; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.SecureRandom; +import java.util.Date; + +@Slf4j +public class UserBuilder { + + public static User generateUser(User user){ + try { + KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA"); + keyPairGenerator.initialize(4096); + KeyPair keyPair = keyPairGenerator.generateKeyPair(); + X509v3CertificateBuilder builder = new X509v3CertificateBuilder( + new JcaX509CertificateHolder(CryptoEngine.CA.getCertificate()).getSubject(), + new BigInteger(256, new SecureRandom()), + new Date(System.currentTimeMillis()), + new Date(System.currentTimeMillis() + CryptoEngine.userLifetime), + new X500Name(user.getX509Fields().getDN()), + SubjectPublicKeyInfo.getInstance(keyPair.getPublic().getEncoded()) + ); + user.setPrivateKey(keyPair.getPrivate()); + user.setCertificate(CertificateSigner.signCertificate(CryptoEngine.CA.getPrivateKey(),builder, ExtensionLibrary.userExtensions)); + return user; + } + catch (Exception e){ + log.error("Could not generate a user certificate: " + e.toString()); + return null; + } + } +} diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/CertificateExtendedData.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/CertificateExtendedData.java new file mode 100644 index 0000000000000000000000000000000000000000..ef1fb6b04d220aaaa2b7625ede1cfeb76e598880 --- /dev/null +++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/CertificateExtendedData.java @@ -0,0 +1,15 @@ +package net.jami.jams.cryptoengine.workers.csr.utils; + +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +public class CertificateExtendedData { + private List<Object[]> extensions = new ArrayList<>(); +} + + diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/CertificateSigner.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/CertificateSigner.java new file mode 100644 index 0000000000000000000000000000000000000000..a718c8b1a2040d12e83523781f95236d15b26e7e --- /dev/null +++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/CertificateSigner.java @@ -0,0 +1,48 @@ +package net.jami.jams.cryptoengine.workers.csr.utils; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.cryptoengine.CryptoEngine; +import org.bouncycastle.asn1.ASN1Encodable; +import org.bouncycastle.asn1.ASN1ObjectIdentifier; +import org.bouncycastle.asn1.x509.AlgorithmIdentifier; +import org.bouncycastle.asn1.x509.Certificate; +import org.bouncycastle.cert.X509v3CertificateBuilder; +import org.bouncycastle.crypto.params.AsymmetricKeyParameter; +import org.bouncycastle.crypto.util.PrivateKeyFactory; +import org.bouncycastle.jcajce.provider.asymmetric.x509.CertificateFactory; +import org.bouncycastle.operator.ContentSigner; +import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder; +import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder; +import org.bouncycastle.operator.bc.BcRSAContentSignerBuilder; + +import java.io.ByteArrayInputStream; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +@Slf4j +public class CertificateSigner { + + public static X509Certificate signCertificate(PrivateKey privateKey, X509v3CertificateBuilder certificateBuilder, + CertificateExtendedData certificateExtendedData){ + try { + //Appose the extended data from the template. + for(Object[] extensions : certificateExtendedData.getExtensions()){ + certificateBuilder.addExtension((ASN1ObjectIdentifier) extensions[0],(boolean) extensions[1],(ASN1Encodable) extensions[2]); + } + //Initialize the signing. + AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(CryptoEngine.signingAlgorithm); + AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId); + AsymmetricKeyParameter asymmetricKeyParameter = PrivateKeyFactory.createKey(privateKey.getEncoded()); + //Sign the certificate. + ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(asymmetricKeyParameter); + Certificate eeX509CertificateStructure = certificateBuilder.build(sigGen).toASN1Structure(); + return (X509Certificate) new CertificateFactory().engineGenerateCertificate(new ByteArrayInputStream(eeX509CertificateStructure.getEncoded())); + } + catch (Exception e){ + log.error("Could not sign a certificate with error: " + e.toString()); + return null; + } + } + + +} diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/ExtensionLibrary.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/ExtensionLibrary.java new file mode 100644 index 0000000000000000000000000000000000000000..f47130bae7f33d09a4f650ea6f7bd72150c2a5ca --- /dev/null +++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/csr/utils/ExtensionLibrary.java @@ -0,0 +1,52 @@ +package net.jami.jams.cryptoengine.workers.csr.utils; + +import net.jami.jams.cryptoengine.CryptoEngine; +import org.bouncycastle.asn1.x509.*; + +public class ExtensionLibrary { + + public static CertificateExtendedData caExtensions = new CertificateExtendedData(); + public static CertificateExtendedData userExtensions = new CertificateExtendedData(); + public static CertificateExtendedData ocspExtensions = new CertificateExtendedData(); + public static CertificateExtendedData deviceExtensions = new CertificateExtendedData(); + + private static final int SCHEMA = GeneralName.uniformResourceIdentifier; + + static { + //Pre-Define the CRL Distribution Point + DistributionPoint[] distPoints = new DistributionPoint[1]; + distPoints[0] = new DistributionPoint(new DistributionPointName( + new GeneralNames(new GeneralName(SCHEMA, CryptoEngine.serverDomain + "/api/auth/crl"))) + , null, null + ); + + //Pre-Define the AIA Point + AccessDescription accessDescription = new AccessDescription( + AccessDescription.id_ad_ocsp, + new GeneralName(SCHEMA,CryptoEngine.serverDomain + "/api/auth/ocsp") + ); + + //CA Extensions. + caExtensions.getExtensions().add(new Object[]{Extension.basicConstraints, true, new BasicConstraints(10)}); + caExtensions.getExtensions().add(new Object[]{Extension.keyUsage, false, new KeyUsage(KeyUsage.cRLSign | KeyUsage.keyCertSign)}); + + //OCSP Extensions. + ocspExtensions.getExtensions().add(new Object[]{Extension.basicConstraints, true, new BasicConstraints(false)}); + ocspExtensions.getExtensions().add(new Object[]{Extension.extendedKeyUsage, false, new ExtendedKeyUsage(KeyPurposeId.id_kp_OCSPSigning)}); + ocspExtensions.getExtensions().add(new Object[]{Extension.keyUsage, false, new KeyUsage(KeyUsage.nonRepudiation | KeyUsage.digitalSignature | KeyUsage.keyEncipherment)}); + + //User extensions (the user is a sub-CA) + userExtensions.getExtensions().add(new Object[]{Extension.basicConstraints, true, new BasicConstraints(10)}); + userExtensions.getExtensions().add(new Object[]{Extension.keyUsage, false, new KeyUsage(KeyUsage.cRLSign | KeyUsage.keyCertSign)}); + userExtensions.getExtensions().add(new Object[]{Extension.cRLDistributionPoints, false, new CRLDistPoint(distPoints)}); + userExtensions.getExtensions().add(new Object[]{Extension.authorityInfoAccess, false, new AuthorityInformationAccess(accessDescription)}); + + //Device extensions + deviceExtensions.getExtensions().add(new Object[]{Extension.basicConstraints, true, new BasicConstraints(false)}); + deviceExtensions.getExtensions().add(new Object[]{Extension.keyUsage, false, new KeyUsage(KeyUsage.digitalSignature | KeyUsage.dataEncipherment | KeyUsage.keyAgreement | KeyUsage.nonRepudiation)}); + deviceExtensions.getExtensions().add(new Object[]{Extension.cRLDistributionPoints, false, new CRLDistPoint(distPoints)}); + deviceExtensions.getExtensions().add(new Object[]{Extension.authorityInfoAccess, false, new AuthorityInformationAccess(accessDescription)}); + } + + +} diff --git a/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/ocsp/OCSPWorker.java b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/ocsp/OCSPWorker.java new file mode 100644 index 0000000000000000000000000000000000000000..87763b88febd2b01703b7c3ce1cb23c8d726fb13 --- /dev/null +++ b/cryptoengine/src/main/java/net/jami/jams/cryptoengine/workers/ocsp/OCSPWorker.java @@ -0,0 +1,16 @@ +package net.jami.jams.cryptoengine.workers.ocsp; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.cryptoengine.workers.X509Worker; + +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +@Slf4j +public class OCSPWorker extends X509Worker<String> { + + public OCSPWorker(PrivateKey privateKey, X509Certificate certificate) { + super(privateKey, certificate); + log.info("Instantiated OCSP Worker..."); + } +} diff --git a/cryptoengine/src/test/java/net/jami/jams/cryptoengine/workers/csr/builders/SystemAccountBuilderTest.java b/cryptoengine/src/test/java/net/jami/jams/cryptoengine/workers/csr/builders/SystemAccountBuilderTest.java new file mode 100644 index 0000000000000000000000000000000000000000..37397a0bfa354c51a3a4cb07bfe1cec58eb80aef --- /dev/null +++ b/cryptoengine/src/test/java/net/jami/jams/cryptoengine/workers/csr/builders/SystemAccountBuilderTest.java @@ -0,0 +1,105 @@ +package net.jami.jams.cryptoengine.workers.csr.builders; + +import net.jami.jams.common.authentication.AuthenticationSourceType; +import net.jami.jams.common.objects.devices.Device; +import net.jami.jams.common.objects.requests.RevocationRequest; +import net.jami.jams.common.objects.requests.RevocationType; +import net.jami.jams.common.objects.roots.X509Fields; +import net.jami.jams.common.objects.system.SystemAccount; +import net.jami.jams.common.objects.system.SystemAccountType; +import net.jami.jams.common.objects.user.User; +import net.jami.jams.common.utils.X509Utils; +import net.jami.jams.cryptoengine.CryptoEngine; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.InputStream; +import java.math.BigInteger; + +class SystemAccountBuilderTest { + + private static String strPkcs10Request; + + @BeforeAll + static void setUp() throws Exception{ + CryptoEngine.serverDomain = "https://localhost"; + CryptoEngine.signingAlgorithm = "SHA512WITHRSA"; + InputStream path; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + path = classLoader.getResourceAsStream("pkcs10request.txt"); + strPkcs10Request = new String(path.readAllBytes()); + } + + @Test + void generateFullChain() { + SystemAccount caAccount = new SystemAccount(); + caAccount.setSystemAccountType(SystemAccountType.CA); + caAccount.setX509Fields(new X509Fields()); + caAccount.getX509Fields().setCommonName("Test CA"); + caAccount.getX509Fields().setCountry("FR"); + caAccount = SystemAccountBuilder.generateCA(caAccount); + Assertions.assertNotNull(caAccount.getCertificate(),"CA Certificate was not generated!"); + + CryptoEngine.CA = caAccount; + + //Generate OCSP + SystemAccount ocspAccount = new SystemAccount(); + ocspAccount.setSystemAccountType(SystemAccountType.OCSP); + ocspAccount.setX509Fields(new X509Fields()); + ocspAccount.getX509Fields().setCommonName("OCSP Server"); + ocspAccount = SystemAccountBuilder.generateOCSP(ocspAccount); + Assertions.assertNotNull(ocspAccount.getCertificate(),"OCSP Certificate was not generated!"); + + //Generate User. + User user = new User(); + user.setUserType(AuthenticationSourceType.LOCAL); + user.setX509Fields(new X509Fields()); + user.getX509Fields().setCommonName("Felix's Personal Certificate"); + user = UserBuilder.generateUser(user); + Assertions.assertNotNull(user.getCertificate(),"User Certificate was not generated!"); + + //Generate a device + Device device = new Device(); + device.setOwner("00000"); + device.setCertificationRequest(X509Utils.getCSRFromString(strPkcs10Request)); + device = DeviceBuilder.generateDevice(user,device); + Assertions.assertNotNull(device.getCertificate(),"Device certificate was not generated!"); + } + + @Test + void testCRLGeneration() throws Exception{ + SystemAccount caAccount = new SystemAccount(); + caAccount.setSystemAccountType(SystemAccountType.CA); + caAccount.setX509Fields(new X509Fields()); + caAccount.getX509Fields().setCommonName("Test CA"); + caAccount.getX509Fields().setCountry("FR"); + caAccount = SystemAccountBuilder.generateCA(caAccount); + Assertions.assertNotNull(caAccount.getCertificate(),"CA Certificate was not generated!"); + + CryptoEngine cryptoEngine = new CryptoEngine(); + cryptoEngine.init("http://localhost","SHA512WITHRSA",caAccount,null); + RevocationRequest revocationRequest = new RevocationRequest(); + revocationRequest.setIdentifier(new BigInteger("91828882")); + revocationRequest.setRevocationType(RevocationType.USER); + cryptoEngine.revokeCertificate(revocationRequest); + synchronized (this){ + this.wait(2_000); + } + Assertions.assertNotNull(cryptoEngine.getLatestCRL()); + Assertions.assertEquals(cryptoEngine.getLatestCRL().get().getRevokedCertificates().toArray().length,1,"Expected only 1 certificate!"); + + revocationRequest = new RevocationRequest(); + revocationRequest.setIdentifier(new BigInteger("17262653")); + revocationRequest.setRevocationType(RevocationType.USER); + cryptoEngine.revokeCertificate(revocationRequest); + synchronized (this){ + this.wait(2_000); + } + Assertions.assertNotNull(cryptoEngine.getLatestCRL()); + Assertions.assertEquals(cryptoEngine.getLatestCRL().get().getRevokedCertificates().toArray().length,2,"Expected only 2 certificates!"); + + + + } +} \ No newline at end of file diff --git a/jami-dht/src/main/java/net/jami/jams/dht/DeviceReceiptGenerator.java b/jami-dht/src/main/java/net/jami/jams/dht/DeviceReceiptGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..99f3698e77b5162c30460b988b13493724f2def6 --- /dev/null +++ b/jami-dht/src/main/java/net/jami/jams/dht/DeviceReceiptGenerator.java @@ -0,0 +1,125 @@ +package net.jami.jams.dht; + +/* + * Copyright (c) 2019 Savoir-faire Linux Inc. + * + * Author: Felix Sidokhine <felix.sidokhined@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.codec.binary.Hex; +import org.apache.commons.codec.digest.MessageDigestAlgorithms; +import org.msgpack.core.MessageBufferPacker; +import org.msgpack.core.MessagePack; + +import java.io.IOException; +import java.security.MessageDigest; +import java.security.PrivateKey; +import java.security.PublicKey; +import java.security.Signature; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.stream.Collectors; + +@Slf4j +public class DeviceReceiptGenerator { + + + private static void packToSign(MessageBufferPacker messagePack, PublicKey publicKey, byte[] deviceId) throws IOException { + byte[] owner = publicKey.getEncoded(); + messagePack.packMapHeader(4); + messagePack.packString("seq"); + messagePack.packInt(0); + messagePack.packString("owner"); + messagePack.packBinaryHeader(owner.length); + messagePack.addPayload(owner); + messagePack.packString("type"); + messagePack.packInt(0); + messagePack.packString("data"); + //Inner message building. + MessageBufferPacker innerMessagePack = MessagePack.newDefaultBufferPacker(); + innerMessagePack.packMapHeader(1); + innerMessagePack.packString("dev"); + innerMessagePack.packBinaryHeader(deviceId.length); + innerMessagePack.addPayload(deviceId); + //Append the inner to the messagePack + messagePack.packBinaryHeader(innerMessagePack.toByteArray().length); + messagePack.addPayload(innerMessagePack.toByteArray()); + } + + private static byte[] generateAnnoucement(PrivateKey privateKey, PublicKey publicKey, PublicKey devicePubKey){ + try { + MessageBufferPacker outerMessagePack = MessagePack.newDefaultBufferPacker(); + MessageBufferPacker messagePack = MessagePack.newDefaultBufferPacker(); + MessageDigest digest = MessageDigest.getInstance(MessageDigestAlgorithms.SHA_1); + byte[] deviceId = digest.digest(devicePubKey.getEncoded()); + packToSign(messagePack, publicKey, deviceId); + byte[] owner = publicKey.getEncoded(); + + //Sign the message pack object using SHA-512 digest. + Signature signer = Signature.getInstance("SHA512withRSA"); + signer.initSign(privateKey); + signer.update(messagePack.toByteArray()); + byte[] signature = signer.sign(); + + //Build the outer enveloper. + outerMessagePack.packMapHeader(2); + outerMessagePack.packString("id"); + outerMessagePack.packInt(0); + outerMessagePack.packString("dat"); + outerMessagePack.packMapHeader(2); + outerMessagePack.packString("body"); + packToSign(outerMessagePack, publicKey, deviceId); + outerMessagePack.packString("sig"); + outerMessagePack.packBinaryHeader(signature.length); + outerMessagePack.addPayload(signature); + + return outerMessagePack.toByteArray(); + } + catch (Exception e){ + log.error("Could not build the device receipt!"); + return null; + } + } + + public static String[] generateReceipt(PrivateKey privateKey, PublicKey publicKey, PublicKey devicePubKey, String ethAddress){ + try { + HashMap<String, String> receipt = new LinkedHashMap<String, String>() { + @Override + public String toString() { + return "{" + entrySet().stream().map(x -> "\"" + x.getKey() + "\":\"" + x.getValue() + "\"").collect(Collectors.joining(",")) + "}"; + } + }; + MessageDigest digest = MessageDigest.getInstance(MessageDigestAlgorithms.SHA_1); + receipt.put("id",Hex.encodeHexString(digest.digest(publicKey.getEncoded()))); + receipt.put("dev",Hex.encodeHexString(digest.digest(devicePubKey.getEncoded()))); + receipt.put("eth",ethAddress); + receipt.put("announce", Base64.encodeBase64String(generateAnnoucement(privateKey,publicKey,devicePubKey))); + Signature signer = Signature.getInstance("SHA512withRSA"); + signer.initSign(privateKey); + signer.update(receipt.toString().getBytes()); + byte[] signature = signer.sign(); + return new String[]{receipt.toString(), java.util.Base64.getEncoder().encodeToString(signature)}; + } + catch (Exception e){ + log.error("Could not build generate device receipt!"); + return null; + } + + } + +} diff --git a/jami-dht/src/main/java/net/jami/jams/dht/ETHAddressGenerator.java b/jami-dht/src/main/java/net/jami/jams/dht/ETHAddressGenerator.java new file mode 100644 index 0000000000000000000000000000000000000000..4329865ff41504fad37a920679747e083678ebaa --- /dev/null +++ b/jami-dht/src/main/java/net/jami/jams/dht/ETHAddressGenerator.java @@ -0,0 +1,61 @@ +package net.jami.jams.dht; + +/* + * Copyright (c) 2019 Savoir-faire Linux Inc. + * + * Author: Felix Sidokhine <felix.sidokhined@savoirfairelinux.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +import net.jami.jams.dht.hashutils.HexBin; +import net.jami.jams.dht.hashutils.Keccak256; +import org.apache.commons.codec.binary.Hex; + +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECGenParameterSpec; + +public class ETHAddressGenerator { + + + public static String[] generateAddress(){ + try { + KeyPairGenerator keyGen = KeyPairGenerator.getInstance("EC","SunEC"); + keyGen.initialize(new ECGenParameterSpec("secp256k1")); + //Generate the Public and Private Keys + KeyPair pair = keyGen.generateKeyPair(); + ECPrivateKey ecpriv = (ECPrivateKey) pair.getPrivate(); + ECPublicKey ecpub = (ECPublicKey) pair.getPublic(); + + //Get the hex representations we need. + String hexPubKey = ecpub.getW().getAffineX().toString(16) + ecpub.getW().getAffineY().toString(16); + String hexPrvKey = ecpriv.getS().toString(16); + + //In ethereum the private key is just 0x + private key, so nothing to do here. + Keccak256 keccak256 = new Keccak256(); + byte[] addressData = keccak256.digest(HexBin.decode(hexPubKey)); + String address = Hex.encodeHexString(addressData); + address = "0x" + address.substring(24); + //Return the address and the private key - we just store them for now. + return new String[]{address,hexPrvKey}; + } + catch (Exception e){ + return null; + } + } + + +} diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/Digest.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/Digest.java new file mode 100755 index 0000000000000000000000000000000000000000..5eb3da19e8a2acfc7fafd4b402eeabc17ce9f7e8 --- /dev/null +++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/Digest.java @@ -0,0 +1,165 @@ +// $Id: Digest.java 232 2010-06-17 14:19:24Z tp $ + +package net.jami.jams.dht.hashutils; + +/** + * <p>This interface documents the API for a hash function. This + * interface somewhat mimics the standard {@code + * java.security.MessageDigest} class. We do not extend that class in + * order to provide compatibility with reduced Java implementations such + * as J2ME. Implementing a {@code java.security.Provider} compatible + * with Sun's JCA ought to be easy.</p> + * + * <p>A {@code Digest} object maintains a running state for a hash + * function computation. Data is inserted with {@code update()} calls; + * the result is obtained from a {@code digest()} method (where some + * final data can be inserted as well). When a digest output has been + * produced, the objet is automatically resetted, and can be used + * immediately for another digest operation. The state of a computation + * can be cloned with the {@link #copy} method; this can be used to get + * a partial hash result without interrupting the complete + * computation.</p> + * + * <p>{@code Digest} objects are stateful and hence not thread-safe; + * however, distinct {@code Digest} objects can be accessed concurrently + * without any problem.</p> + * + * <pre> + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * </pre> + * + * @version $Revision: 232 $ + * @author Thomas Pornin <thomas.pornin@cryptolog.com> + */ + +public interface Digest { + + /** + * Insert one more input data byte. + * + * @param in the input byte + */ + public void update(byte in); + + /** + * Insert some more bytes. + * + * @param inbuf the data bytes + */ + public void update(byte[] inbuf); + + /** + * Insert some more bytes. + * + * @param inbuf the data buffer + * @param off the data offset in {@code inbuf} + * @param len the data length (in bytes) + */ + public void update(byte[] inbuf, int off, int len); + + /** + * Finalize the current hash computation and return the hash value + * in a newly-allocated array. The object is resetted. + * + * @return the hash output + */ + public byte[] digest(); + + /** + * Input some bytes, then finalize the current hash computation + * and return the hash value in a newly-allocated array. The object + * is resetted. + * + * @param inbuf the input data + * @return the hash output + */ + public byte[] digest(byte[] inbuf); + + /** + * Finalize the current hash computation and store the hash value + * in the provided output buffer. The {@code len} parameter + * contains the maximum number of bytes that should be written; + * no more bytes than the natural hash function output length will + * be produced. If {@code len} is smaller than the natural + * hash output length, the hash output is truncated to its first + * {@code len} bytes. The object is resetted. + * + * @param outbuf the output buffer + * @param off the output offset within {@code outbuf} + * @param len the requested hash output length (in bytes) + * @return the number of bytes actually written in {@code outbuf} + */ + public int digest(byte[] outbuf, int off, int len); + + /** + * Get the natural hash function output length (in bytes). + * + * @return the digest output length (in bytes) + */ + public int getDigestLength(); + + /** + * Reset the object: this makes it suitable for a new hash + * computation. The current computation, if any, is discarded. + */ + public void reset(); + + /** + * Clone the current state. The returned object evolves independantly + * of this object. + * + * @return the clone + */ + public Digest copy(); + + /** + * <p>Return the "block length" for the hash function. This + * value is naturally defined for iterated hash functions + * (Merkle-Damgard). It is used in HMAC (that's what the + * <a href="http://tools.ietf.org/html/rfc2104">HMAC specification</a> + * names the "{@code B}" parameter).</p> + * + * <p>If the function is "block-less" then this function may + * return {@code -n} where {@code n} is an integer such that the + * block length for HMAC ("{@code B}") will be inferred from the + * key length, by selecting the smallest multiple of {@code n} + * which is no smaller than the key length. For instance, for + * the Fugue-xxx hash functions, this function returns -4: the + * virtual block length B is the HMAC key length, rounded up to + * the next multiple of 4.</p> + * + * @return the internal block length (in bytes), or {@code -n} + */ + public int getBlockLength(); + + /** + * <p>Get the display name for this function (e.g. {@code "SHA-1"} + * for SHA-1).</p> + * + * @see Object + */ + public String toString(); +} diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/DigestEngine.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/DigestEngine.java new file mode 100755 index 0000000000000000000000000000000000000000..bc9667ad704b429dc82bcf8fca99952a9f4c3778 --- /dev/null +++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/DigestEngine.java @@ -0,0 +1,266 @@ +// $Id: DigestEngine.java 229 2010-06-16 20:22:27Z tp $ + +package net.jami.jams.dht.hashutils; + +/** + * <p>This class is a template which can be used to implement hash + * functions. It takes care of some of the API, and also provides an + * internal data buffer whose length is equal to the hash function + * internal block length.</p> + * + * <p>Classes which use this template MUST provide a working {@link + * #getBlockLength} method even before initialization (alternatively, + * they may define a custom {@link #getInternalBlockLength} which does + * not call {@link #getBlockLength}. The {@link #getDigestLength} should + * also be operational from the beginning, but it is acceptable that it + * returns 0 while the {@link #doInit} method has not been called + * yet.</p> + * + * <pre> + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * </pre> + * + * @version $Revision: 229 $ + * @author Thomas Pornin <thomas.pornin@cryptolog.com> + */ + +public abstract class DigestEngine implements Digest { + + /** + * Reset the hash algorithm state. + */ + protected abstract void engineReset(); + + /** + * Process one block of data. + * + * @param data the data block + */ + protected abstract void processBlock(byte[] data); + + /** + * Perform the final padding and store the result in the + * provided buffer. This method shall call {@link #flush} + * and then {@link #update} with the appropriate padding + * data in order to get the full input data. + * + * @param buf the output buffer + * @param off the output offset + */ + protected abstract void doPadding(byte[] buf, int off); + + /** + * This function is called at object creation time; the + * implementation should use it to perform initialization tasks. + * After this method is called, the implementation should be ready + * to process data or meaningfully honour calls such as + * {@link #getDigestLength}</code>. + */ + protected abstract void doInit(); + + private int digestLen, blockLen, inputLen; + private byte[] inputBuf, outputBuf; + private long blockCount; + + /** + * Instantiate the engine. + */ + public DigestEngine() + { + doInit(); + digestLen = getDigestLength(); + blockLen = getInternalBlockLength(); + inputBuf = new byte[blockLen]; + outputBuf = new byte[digestLen]; + inputLen = 0; + blockCount = 0; + } + + private void adjustDigestLen() + { + if (digestLen == 0) { + digestLen = getDigestLength(); + outputBuf = new byte[digestLen]; + } + } + + /** @see Digest */ + public byte[] digest() + { + adjustDigestLen(); + byte[] result = new byte[digestLen]; + digest(result, 0, digestLen); + return result; + } + + /** @see Digest */ + public byte[] digest(byte[] input) + { + update(input, 0, input.length); + return digest(); + } + + /** @see Digest */ + public int digest(byte[] buf, int offset, int len) + { + adjustDigestLen(); + if (len >= digestLen) { + doPadding(buf, offset); + reset(); + return digestLen; + } else { + doPadding(outputBuf, 0); + System.arraycopy(outputBuf, 0, buf, offset, len); + reset(); + return len; + } + } + + /** @see Digest */ + public void reset() + { + engineReset(); + inputLen = 0; + blockCount = 0; + } + + /** @see Digest */ + public void update(byte input) + { + inputBuf[inputLen ++] = (byte)input; + if (inputLen == blockLen) { + processBlock(inputBuf); + blockCount ++; + inputLen = 0; + } + } + + /** @see Digest */ + public void update(byte[] input) + { + update(input, 0, input.length); + } + + /** @see Digest */ + public void update(byte[] input, int offset, int len) + { + while (len > 0) { + int copyLen = blockLen - inputLen; + if (copyLen > len) + copyLen = len; + System.arraycopy(input, offset, inputBuf, inputLen, + copyLen); + offset += copyLen; + inputLen += copyLen; + len -= copyLen; + if (inputLen == blockLen) { + processBlock(inputBuf); + blockCount ++; + inputLen = 0; + } + } + } + + /** + * Get the internal block length. This is the length (in + * bytes) of the array which will be passed as parameter to + * {@link #processBlock}. The default implementation of this + * method calls {@link #getBlockLength} and returns the same + * value. Overriding this method is useful when the advertised + * block length (which is used, for instance, by HMAC) is + * suboptimal with regards to internal buffering needs. + * + * @return the internal block length (in bytes) + */ + protected int getInternalBlockLength() + { + return getBlockLength(); + } + + /** + * Flush internal buffers, so that less than a block of data + * may at most be upheld. + * + * @return the number of bytes still unprocessed after the flush + */ + protected final int flush() + { + return inputLen; + } + + /** + * Get a reference to an internal buffer with the same size + * than a block. The contents of that buffer are defined only + * immediately after a call to {@link #flush()}: if + * {@link #flush()} return the value {@code n}, then the + * first {@code n} bytes of the array returned by this method + * are the {@code n} bytes of input data which are still + * unprocessed. The values of the remaining bytes are + * undefined and may be altered at will. + * + * @return a block-sized internal buffer + */ + protected final byte[] getBlockBuffer() + { + return inputBuf; + } + + /** + * Get the "block count": this is the number of times the + * {@link #processBlock} method has been invoked for the + * current hash operation. That counter is incremented + * <em>after</em> the call to {@link #processBlock}. + * + * @return the block count + */ + protected long getBlockCount() + { + return blockCount; + } + + /** + * This function copies the internal buffering state to some + * other instance of a class extending {@code DigestEngine}. + * It returns a reference to the copy. This method is intended + * to be called by the implementation of the {@link #copy} + * method. + * + * @param dest the copy + * @return the value {@code dest} + */ + protected Digest copyState(DigestEngine dest) + { + dest.inputLen = inputLen; + dest.blockCount = blockCount; + System.arraycopy(inputBuf, 0, dest.inputBuf, 0, + inputBuf.length); + adjustDigestLen(); + dest.adjustDigestLen(); + System.arraycopy(outputBuf, 0, dest.outputBuf, 0, + outputBuf.length); + return dest; + } +} diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/HexBin.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/HexBin.java new file mode 100755 index 0000000000000000000000000000000000000000..5dbd9610abb783d23b7a82a3cd2c167914fd7267 --- /dev/null +++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/HexBin.java @@ -0,0 +1,90 @@ +package net.jami.jams.dht.hashutils; +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + + +/** + * format validation + * + * This class encodes/decodes hexadecimal data + * + * @xerces.internal + * + * @author Jeffrey Rodriguez + * @version $Id: HexBin.java 446747 2006-09-15 21:46:20Z mrglavas $ + */ +public final class HexBin { + static private final int BASELENGTH = 128; + static private final int LOOKUPLENGTH = 16; + static final private byte [] hexNumberTable = new byte[BASELENGTH]; + static final private char [] lookUpHexAlphabet = new char[LOOKUPLENGTH]; + + + static { + for (int i = 0; i < BASELENGTH; i++ ) { + hexNumberTable[i] = -1; + } + for ( int i = '9'; i >= '0'; i--) { + hexNumberTable[i] = (byte) (i-'0'); + } + for ( int i = 'F'; i>= 'A'; i--) { + hexNumberTable[i] = (byte) ( i-'A' + 10 ); + } + for ( int i = 'f'; i>= 'a'; i--) { + hexNumberTable[i] = (byte) ( i-'a' + 10 ); + } + + for(int i = 0; i<10; i++ ) { + lookUpHexAlphabet[i] = (char)('0'+i); + } + for(int i = 10; i<=15; i++ ) { + lookUpHexAlphabet[i] = (char)('A'+i -10); + } + } + + /** + * Decode hex string to a byte array + * + * @param encoded encoded string + * @return return array of byte to encode + */ + static public byte[] decode(String encoded) { + if (encoded == null) + return null; + int lengthData = encoded.length(); + if (lengthData % 2 != 0) + return null; + + char[] binaryData = encoded.toCharArray(); + int lengthDecode = lengthData / 2; + byte[] decodedData = new byte[lengthDecode]; + byte temp1, temp2; + char tempChar; + for( int i = 0; i<lengthDecode; i++ ){ + tempChar = binaryData[i*2]; + temp1 = (tempChar < BASELENGTH) ? hexNumberTable[tempChar] : -1; + if (temp1 == -1) + return null; + tempChar = binaryData[i*2+1]; + temp2 = (tempChar < BASELENGTH) ? hexNumberTable[tempChar] : -1; + if (temp2 == -1) + return null; + decodedData[i] = (byte)((temp1 << 4) | temp2); + } + return decodedData; + } +} \ No newline at end of file diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/Keccak256.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/Keccak256.java new file mode 100755 index 0000000000000000000000000000000000000000..0cc57c2a1e4884cb1e14e5bc8bf383da0f055c03 --- /dev/null +++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/Keccak256.java @@ -0,0 +1,59 @@ +package net.jami.jams.dht.hashutils; + +// $Id: Keccak256.java 189 2010-05-14 21:21:46Z tp $ +/** + * <p>This class implements the Keccak-256 digest algorithm under the + * {@link Digest} API.</p> + * + * <pre> + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * </pre> + * + * @version $Revision: 189 $ + * @author Thomas Pornin <thomas.pornin@cryptolog.com> + */ + +public class Keccak256 extends KeccakCore { + + /** + * Create the engine. + */ + public Keccak256() + { + } + + /** @see Digest */ + public Digest copy() + { + return copyState(new Keccak256()); + } + + /** @see Digest */ + public int getDigestLength() + { + return 32; + } +} \ No newline at end of file diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/KeccakCore.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/KeccakCore.java new file mode 100755 index 0000000000000000000000000000000000000000..0c1473cece63d16dce96f36bcfa671bdfde346b2 --- /dev/null +++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/KeccakCore.java @@ -0,0 +1,585 @@ +// $Id: KeccakCore.java 258 2011-07-15 22:16:50Z tp $ + +package net.jami.jams.dht.hashutils; + +/** + * This class implements the core operations for the Keccak digest + * algorithm. + * + * <pre> + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * </pre> + * + * @version $Revision: 258 $ + * @author Thomas Pornin <thomas.pornin@cryptolog.com> + */ + +abstract class KeccakCore extends DigestEngine { + + KeccakCore() + { + } + + private long[] A; + private byte[] tmpOut; + + private static final long[] RC = { + 0x0000000000000001L, 0x0000000000008082L, + 0x800000000000808AL, 0x8000000080008000L, + 0x000000000000808BL, 0x0000000080000001L, + 0x8000000080008081L, 0x8000000000008009L, + 0x000000000000008AL, 0x0000000000000088L, + 0x0000000080008009L, 0x000000008000000AL, + 0x000000008000808BL, 0x800000000000008BL, + 0x8000000000008089L, 0x8000000000008003L, + 0x8000000000008002L, 0x8000000000000080L, + 0x000000000000800AL, 0x800000008000000AL, + 0x8000000080008081L, 0x8000000000008080L, + 0x0000000080000001L, 0x8000000080008008L + }; + + /** + * Encode the 64-bit word {@code val} into the array + * {@code buf} at offset {@code off}, in little-endian + * convention (least significant byte first). + * + * @param val the value to encode + * @param buf the destination buffer + * @param off the destination offset + */ + private static final void encodeLELong(long val, byte[] buf, int off) + { + buf[off + 0] = (byte)val; + buf[off + 1] = (byte)(val >>> 8); + buf[off + 2] = (byte)(val >>> 16); + buf[off + 3] = (byte)(val >>> 24); + buf[off + 4] = (byte)(val >>> 32); + buf[off + 5] = (byte)(val >>> 40); + buf[off + 6] = (byte)(val >>> 48); + buf[off + 7] = (byte)(val >>> 56); + } + + /** + * Decode a 64-bit little-endian word from the array {@code buf} + * at offset {@code off}. + * + * @param buf the source buffer + * @param off the source offset + * @return the decoded value + */ + private static final long decodeLELong(byte[] buf, int off) + { + return (buf[off + 0] & 0xFFL) + | ((buf[off + 1] & 0xFFL) << 8) + | ((buf[off + 2] & 0xFFL) << 16) + | ((buf[off + 3] & 0xFFL) << 24) + | ((buf[off + 4] & 0xFFL) << 32) + | ((buf[off + 5] & 0xFFL) << 40) + | ((buf[off + 6] & 0xFFL) << 48) + | ((buf[off + 7] & 0xFFL) << 56); + } + + /** @see DigestEngine */ + protected void engineReset() + { + doReset(); + } + + /** @see DigestEngine */ + protected void processBlock(byte[] data) + { + /* Input block */ + for (int i = 0; i < data.length; i += 8) + A[i >>> 3] ^= decodeLELong(data, i); + + long t0, t1, t2, t3, t4; + long tt0, tt1, tt2, tt3, tt4; + long t, kt; + long c0, c1, c2, c3, c4, bnn; + + /* + * Unrolling four rounds kills performance big time + * on Intel x86 Core2, in both 32-bit and 64-bit modes + * (less than 1 MB/s instead of 55 MB/s on x86-64). + * Unrolling two rounds appears to be fine. + */ + for (int j = 0; j < 24; j += 2) { + + tt0 = A[ 1] ^ A[ 6]; + tt1 = A[11] ^ A[16]; + tt0 ^= A[21] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 4] ^ A[ 9]; + tt3 = A[14] ^ A[19]; + tt0 ^= A[24]; + tt2 ^= tt3; + t0 = tt0 ^ tt2; + + tt0 = A[ 2] ^ A[ 7]; + tt1 = A[12] ^ A[17]; + tt0 ^= A[22] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 0] ^ A[ 5]; + tt3 = A[10] ^ A[15]; + tt0 ^= A[20]; + tt2 ^= tt3; + t1 = tt0 ^ tt2; + + tt0 = A[ 3] ^ A[ 8]; + tt1 = A[13] ^ A[18]; + tt0 ^= A[23] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 1] ^ A[ 6]; + tt3 = A[11] ^ A[16]; + tt0 ^= A[21]; + tt2 ^= tt3; + t2 = tt0 ^ tt2; + + tt0 = A[ 4] ^ A[ 9]; + tt1 = A[14] ^ A[19]; + tt0 ^= A[24] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 2] ^ A[ 7]; + tt3 = A[12] ^ A[17]; + tt0 ^= A[22]; + tt2 ^= tt3; + t3 = tt0 ^ tt2; + + tt0 = A[ 0] ^ A[ 5]; + tt1 = A[10] ^ A[15]; + tt0 ^= A[20] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 3] ^ A[ 8]; + tt3 = A[13] ^ A[18]; + tt0 ^= A[23]; + tt2 ^= tt3; + t4 = tt0 ^ tt2; + + A[ 0] = A[ 0] ^ t0; + A[ 5] = A[ 5] ^ t0; + A[10] = A[10] ^ t0; + A[15] = A[15] ^ t0; + A[20] = A[20] ^ t0; + A[ 1] = A[ 1] ^ t1; + A[ 6] = A[ 6] ^ t1; + A[11] = A[11] ^ t1; + A[16] = A[16] ^ t1; + A[21] = A[21] ^ t1; + A[ 2] = A[ 2] ^ t2; + A[ 7] = A[ 7] ^ t2; + A[12] = A[12] ^ t2; + A[17] = A[17] ^ t2; + A[22] = A[22] ^ t2; + A[ 3] = A[ 3] ^ t3; + A[ 8] = A[ 8] ^ t3; + A[13] = A[13] ^ t3; + A[18] = A[18] ^ t3; + A[23] = A[23] ^ t3; + A[ 4] = A[ 4] ^ t4; + A[ 9] = A[ 9] ^ t4; + A[14] = A[14] ^ t4; + A[19] = A[19] ^ t4; + A[24] = A[24] ^ t4; + A[ 5] = (A[ 5] << 36) | (A[ 5] >>> (64 - 36)); + A[10] = (A[10] << 3) | (A[10] >>> (64 - 3)); + A[15] = (A[15] << 41) | (A[15] >>> (64 - 41)); + A[20] = (A[20] << 18) | (A[20] >>> (64 - 18)); + A[ 1] = (A[ 1] << 1) | (A[ 1] >>> (64 - 1)); + A[ 6] = (A[ 6] << 44) | (A[ 6] >>> (64 - 44)); + A[11] = (A[11] << 10) | (A[11] >>> (64 - 10)); + A[16] = (A[16] << 45) | (A[16] >>> (64 - 45)); + A[21] = (A[21] << 2) | (A[21] >>> (64 - 2)); + A[ 2] = (A[ 2] << 62) | (A[ 2] >>> (64 - 62)); + A[ 7] = (A[ 7] << 6) | (A[ 7] >>> (64 - 6)); + A[12] = (A[12] << 43) | (A[12] >>> (64 - 43)); + A[17] = (A[17] << 15) | (A[17] >>> (64 - 15)); + A[22] = (A[22] << 61) | (A[22] >>> (64 - 61)); + A[ 3] = (A[ 3] << 28) | (A[ 3] >>> (64 - 28)); + A[ 8] = (A[ 8] << 55) | (A[ 8] >>> (64 - 55)); + A[13] = (A[13] << 25) | (A[13] >>> (64 - 25)); + A[18] = (A[18] << 21) | (A[18] >>> (64 - 21)); + A[23] = (A[23] << 56) | (A[23] >>> (64 - 56)); + A[ 4] = (A[ 4] << 27) | (A[ 4] >>> (64 - 27)); + A[ 9] = (A[ 9] << 20) | (A[ 9] >>> (64 - 20)); + A[14] = (A[14] << 39) | (A[14] >>> (64 - 39)); + A[19] = (A[19] << 8) | (A[19] >>> (64 - 8)); + A[24] = (A[24] << 14) | (A[24] >>> (64 - 14)); + bnn = ~A[12]; + kt = A[ 6] | A[12]; + c0 = A[ 0] ^ kt; + kt = bnn | A[18]; + c1 = A[ 6] ^ kt; + kt = A[18] & A[24]; + c2 = A[12] ^ kt; + kt = A[24] | A[ 0]; + c3 = A[18] ^ kt; + kt = A[ 0] & A[ 6]; + c4 = A[24] ^ kt; + A[ 0] = c0; + A[ 6] = c1; + A[12] = c2; + A[18] = c3; + A[24] = c4; + bnn = ~A[22]; + kt = A[ 9] | A[10]; + c0 = A[ 3] ^ kt; + kt = A[10] & A[16]; + c1 = A[ 9] ^ kt; + kt = A[16] | bnn; + c2 = A[10] ^ kt; + kt = A[22] | A[ 3]; + c3 = A[16] ^ kt; + kt = A[ 3] & A[ 9]; + c4 = A[22] ^ kt; + A[ 3] = c0; + A[ 9] = c1; + A[10] = c2; + A[16] = c3; + A[22] = c4; + bnn = ~A[19]; + kt = A[ 7] | A[13]; + c0 = A[ 1] ^ kt; + kt = A[13] & A[19]; + c1 = A[ 7] ^ kt; + kt = bnn & A[20]; + c2 = A[13] ^ kt; + kt = A[20] | A[ 1]; + c3 = bnn ^ kt; + kt = A[ 1] & A[ 7]; + c4 = A[20] ^ kt; + A[ 1] = c0; + A[ 7] = c1; + A[13] = c2; + A[19] = c3; + A[20] = c4; + bnn = ~A[17]; + kt = A[ 5] & A[11]; + c0 = A[ 4] ^ kt; + kt = A[11] | A[17]; + c1 = A[ 5] ^ kt; + kt = bnn | A[23]; + c2 = A[11] ^ kt; + kt = A[23] & A[ 4]; + c3 = bnn ^ kt; + kt = A[ 4] | A[ 5]; + c4 = A[23] ^ kt; + A[ 4] = c0; + A[ 5] = c1; + A[11] = c2; + A[17] = c3; + A[23] = c4; + bnn = ~A[ 8]; + kt = bnn & A[14]; + c0 = A[ 2] ^ kt; + kt = A[14] | A[15]; + c1 = bnn ^ kt; + kt = A[15] & A[21]; + c2 = A[14] ^ kt; + kt = A[21] | A[ 2]; + c3 = A[15] ^ kt; + kt = A[ 2] & A[ 8]; + c4 = A[21] ^ kt; + A[ 2] = c0; + A[ 8] = c1; + A[14] = c2; + A[15] = c3; + A[21] = c4; + A[ 0] = A[ 0] ^ RC[j + 0]; + + tt0 = A[ 6] ^ A[ 9]; + tt1 = A[ 7] ^ A[ 5]; + tt0 ^= A[ 8] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[24] ^ A[22]; + tt3 = A[20] ^ A[23]; + tt0 ^= A[21]; + tt2 ^= tt3; + t0 = tt0 ^ tt2; + + tt0 = A[12] ^ A[10]; + tt1 = A[13] ^ A[11]; + tt0 ^= A[14] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 0] ^ A[ 3]; + tt3 = A[ 1] ^ A[ 4]; + tt0 ^= A[ 2]; + tt2 ^= tt3; + t1 = tt0 ^ tt2; + + tt0 = A[18] ^ A[16]; + tt1 = A[19] ^ A[17]; + tt0 ^= A[15] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[ 6] ^ A[ 9]; + tt3 = A[ 7] ^ A[ 5]; + tt0 ^= A[ 8]; + tt2 ^= tt3; + t2 = tt0 ^ tt2; + + tt0 = A[24] ^ A[22]; + tt1 = A[20] ^ A[23]; + tt0 ^= A[21] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[12] ^ A[10]; + tt3 = A[13] ^ A[11]; + tt0 ^= A[14]; + tt2 ^= tt3; + t3 = tt0 ^ tt2; + + tt0 = A[ 0] ^ A[ 3]; + tt1 = A[ 1] ^ A[ 4]; + tt0 ^= A[ 2] ^ tt1; + tt0 = (tt0 << 1) | (tt0 >>> 63); + tt2 = A[18] ^ A[16]; + tt3 = A[19] ^ A[17]; + tt0 ^= A[15]; + tt2 ^= tt3; + t4 = tt0 ^ tt2; + + A[ 0] = A[ 0] ^ t0; + A[ 3] = A[ 3] ^ t0; + A[ 1] = A[ 1] ^ t0; + A[ 4] = A[ 4] ^ t0; + A[ 2] = A[ 2] ^ t0; + A[ 6] = A[ 6] ^ t1; + A[ 9] = A[ 9] ^ t1; + A[ 7] = A[ 7] ^ t1; + A[ 5] = A[ 5] ^ t1; + A[ 8] = A[ 8] ^ t1; + A[12] = A[12] ^ t2; + A[10] = A[10] ^ t2; + A[13] = A[13] ^ t2; + A[11] = A[11] ^ t2; + A[14] = A[14] ^ t2; + A[18] = A[18] ^ t3; + A[16] = A[16] ^ t3; + A[19] = A[19] ^ t3; + A[17] = A[17] ^ t3; + A[15] = A[15] ^ t3; + A[24] = A[24] ^ t4; + A[22] = A[22] ^ t4; + A[20] = A[20] ^ t4; + A[23] = A[23] ^ t4; + A[21] = A[21] ^ t4; + A[ 3] = (A[ 3] << 36) | (A[ 3] >>> (64 - 36)); + A[ 1] = (A[ 1] << 3) | (A[ 1] >>> (64 - 3)); + A[ 4] = (A[ 4] << 41) | (A[ 4] >>> (64 - 41)); + A[ 2] = (A[ 2] << 18) | (A[ 2] >>> (64 - 18)); + A[ 6] = (A[ 6] << 1) | (A[ 6] >>> (64 - 1)); + A[ 9] = (A[ 9] << 44) | (A[ 9] >>> (64 - 44)); + A[ 7] = (A[ 7] << 10) | (A[ 7] >>> (64 - 10)); + A[ 5] = (A[ 5] << 45) | (A[ 5] >>> (64 - 45)); + A[ 8] = (A[ 8] << 2) | (A[ 8] >>> (64 - 2)); + A[12] = (A[12] << 62) | (A[12] >>> (64 - 62)); + A[10] = (A[10] << 6) | (A[10] >>> (64 - 6)); + A[13] = (A[13] << 43) | (A[13] >>> (64 - 43)); + A[11] = (A[11] << 15) | (A[11] >>> (64 - 15)); + A[14] = (A[14] << 61) | (A[14] >>> (64 - 61)); + A[18] = (A[18] << 28) | (A[18] >>> (64 - 28)); + A[16] = (A[16] << 55) | (A[16] >>> (64 - 55)); + A[19] = (A[19] << 25) | (A[19] >>> (64 - 25)); + A[17] = (A[17] << 21) | (A[17] >>> (64 - 21)); + A[15] = (A[15] << 56) | (A[15] >>> (64 - 56)); + A[24] = (A[24] << 27) | (A[24] >>> (64 - 27)); + A[22] = (A[22] << 20) | (A[22] >>> (64 - 20)); + A[20] = (A[20] << 39) | (A[20] >>> (64 - 39)); + A[23] = (A[23] << 8) | (A[23] >>> (64 - 8)); + A[21] = (A[21] << 14) | (A[21] >>> (64 - 14)); + bnn = ~A[13]; + kt = A[ 9] | A[13]; + c0 = A[ 0] ^ kt; + kt = bnn | A[17]; + c1 = A[ 9] ^ kt; + kt = A[17] & A[21]; + c2 = A[13] ^ kt; + kt = A[21] | A[ 0]; + c3 = A[17] ^ kt; + kt = A[ 0] & A[ 9]; + c4 = A[21] ^ kt; + A[ 0] = c0; + A[ 9] = c1; + A[13] = c2; + A[17] = c3; + A[21] = c4; + bnn = ~A[14]; + kt = A[22] | A[ 1]; + c0 = A[18] ^ kt; + kt = A[ 1] & A[ 5]; + c1 = A[22] ^ kt; + kt = A[ 5] | bnn; + c2 = A[ 1] ^ kt; + kt = A[14] | A[18]; + c3 = A[ 5] ^ kt; + kt = A[18] & A[22]; + c4 = A[14] ^ kt; + A[18] = c0; + A[22] = c1; + A[ 1] = c2; + A[ 5] = c3; + A[14] = c4; + bnn = ~A[23]; + kt = A[10] | A[19]; + c0 = A[ 6] ^ kt; + kt = A[19] & A[23]; + c1 = A[10] ^ kt; + kt = bnn & A[ 2]; + c2 = A[19] ^ kt; + kt = A[ 2] | A[ 6]; + c3 = bnn ^ kt; + kt = A[ 6] & A[10]; + c4 = A[ 2] ^ kt; + A[ 6] = c0; + A[10] = c1; + A[19] = c2; + A[23] = c3; + A[ 2] = c4; + bnn = ~A[11]; + kt = A[ 3] & A[ 7]; + c0 = A[24] ^ kt; + kt = A[ 7] | A[11]; + c1 = A[ 3] ^ kt; + kt = bnn | A[15]; + c2 = A[ 7] ^ kt; + kt = A[15] & A[24]; + c3 = bnn ^ kt; + kt = A[24] | A[ 3]; + c4 = A[15] ^ kt; + A[24] = c0; + A[ 3] = c1; + A[ 7] = c2; + A[11] = c3; + A[15] = c4; + bnn = ~A[16]; + kt = bnn & A[20]; + c0 = A[12] ^ kt; + kt = A[20] | A[ 4]; + c1 = bnn ^ kt; + kt = A[ 4] & A[ 8]; + c2 = A[20] ^ kt; + kt = A[ 8] | A[12]; + c3 = A[ 4] ^ kt; + kt = A[12] & A[16]; + c4 = A[ 8] ^ kt; + A[12] = c0; + A[16] = c1; + A[20] = c2; + A[ 4] = c3; + A[ 8] = c4; + A[ 0] = A[ 0] ^ RC[j + 1]; + t = A[ 5]; + A[ 5] = A[18]; + A[18] = A[11]; + A[11] = A[10]; + A[10] = A[ 6]; + A[ 6] = A[22]; + A[22] = A[20]; + A[20] = A[12]; + A[12] = A[19]; + A[19] = A[15]; + A[15] = A[24]; + A[24] = A[ 8]; + A[ 8] = t; + t = A[ 1]; + A[ 1] = A[ 9]; + A[ 9] = A[14]; + A[14] = A[ 2]; + A[ 2] = A[13]; + A[13] = A[23]; + A[23] = A[ 4]; + A[ 4] = A[21]; + A[21] = A[16]; + A[16] = A[ 3]; + A[ 3] = A[17]; + A[17] = A[ 7]; + A[ 7] = t; + } + } + + /** @see DigestEngine */ + protected void doPadding(byte[] out, int off) + { + int ptr = flush(); + byte[] buf = getBlockBuffer(); + if ((ptr + 1) == buf.length) { + buf[ptr] = (byte)0x81; + } else { + buf[ptr] = (byte)0x01; + for (int i = ptr + 1; i < (buf.length - 1); i ++) + buf[i] = 0; + buf[buf.length - 1] = (byte)0x80; + } + processBlock(buf); + A[ 1] = ~A[ 1]; + A[ 2] = ~A[ 2]; + A[ 8] = ~A[ 8]; + A[12] = ~A[12]; + A[17] = ~A[17]; + A[20] = ~A[20]; + int dlen = getDigestLength(); + for (int i = 0; i < dlen; i += 8) + encodeLELong(A[i >>> 3], tmpOut, i); + System.arraycopy(tmpOut, 0, out, off, dlen); + } + + /** @see DigestEngine */ + protected void doInit() + { + A = new long[25]; + tmpOut = new byte[(getDigestLength() + 7) & ~7]; + doReset(); + } + + /** @see Digest */ + public int getBlockLength() + { + return 200 - 2 * getDigestLength(); + } + + private final void doReset() + { + for (int i = 0; i < 25; i ++) + A[i] = 0; + A[ 1] = 0xFFFFFFFFFFFFFFFFL; + A[ 2] = 0xFFFFFFFFFFFFFFFFL; + A[ 8] = 0xFFFFFFFFFFFFFFFFL; + A[12] = 0xFFFFFFFFFFFFFFFFL; + A[17] = 0xFFFFFFFFFFFFFFFFL; + A[20] = 0xFFFFFFFFFFFFFFFFL; + } + + /** @see DigestEngine */ + protected Digest copyState(KeccakCore dst) + { + System.arraycopy(A, 0, dst.A, 0, 25); + return super.copyState(dst); + } + + /** @see Digest */ + public String toString() + { + return "Keccak-" + (getDigestLength() << 3); + } +} \ No newline at end of file diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/MDHelper.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/MDHelper.java new file mode 100755 index 0000000000000000000000000000000000000000..b5d4b83f2626aab035d7e52ed206f11044c65de8 --- /dev/null +++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/MDHelper.java @@ -0,0 +1,154 @@ +// $Id: MDHelper.java 157 2010-04-26 19:03:44Z tp $ + +package net.jami.jams.dht.hashutils; + +/** + * <p>This class implements the padding common to MD4, MD5, the SHA family, + * and RIPEMD-160. This code works as long as the internal block length + * is a power of 2, which is the case for all these algorithms.</p> + * + * <pre> + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * </pre> + * + * @version $Revision: 157 $ + * @author Thomas Pornin <thomas.pornin@cryptolog.com> + */ + +abstract class MDHelper extends DigestEngine { + + /** + * Create the object. Little-endian padding is for MD4, MD5 and + * RIPEMD-160; the SHA family uses big-endian padding. The + * MD padding includes an encoding of the input message bit length, + * which is over 64 bits for some algorithms, 128-bit for others + * (namely SHA-384 and SHA-512). Note that this implementation + * handles only message lengths which fit on 64 bits. + * + * @param littleEndian {@code true} for little-endian padding + * @param lenlen the length encoding length, in bytes (must + * be at least 8) + */ + MDHelper(boolean littleEndian, int lenlen) + { + this(littleEndian, lenlen, (byte)0x80); + } + + /** + * Create the object. Little-endian padding is for MD4, MD5 and + * RIPEMD-160; the SHA family uses big-endian padding. The + * MD padding includes an encoding of the input message bit length, + * which is over 64 bits for some algorithms, 128-bit for others + * (namely SHA-384 and SHA-512). Note that this implementation + * handles only message lengths which fit on 64 bits. The first + * additional byte value is specified; this is normally 0x80, + * except for Tiger (not Tiger2) which uses 0x01. + * + * @param littleEndian {@code true} for little-endian padding + * @param lenlen the length encoding length, in bytes (must + * be at least 8) + * @param fbyte the first padding byte + */ + MDHelper(boolean littleEndian, int lenlen, byte fbyte) + { + this.littleEndian = littleEndian; + countBuf = new byte[lenlen]; + this.fbyte = fbyte; + } + + private boolean littleEndian; + private byte[] countBuf; + private byte fbyte; + + /** + * Compute the padding. The padding data is input into the engine, + * which is flushed. + */ + protected void makeMDPadding() + { + int dataLen = flush(); + int blen = getBlockLength(); + long currentLength = getBlockCount() * (long)blen; + currentLength = (currentLength + (long)dataLen) * 8L; + int lenlen = countBuf.length; + if (littleEndian) { + encodeLEInt((int)currentLength, countBuf, 0); + encodeLEInt((int)(currentLength >>> 32), countBuf, 4); + } else { + encodeBEInt((int)(currentLength >>> 32), + countBuf, lenlen - 8); + encodeBEInt((int)currentLength, + countBuf, lenlen - 4); + } + int endLen = (dataLen + lenlen + blen) & ~(blen - 1); + update(fbyte); + for (int i = dataLen + 1; i < endLen - lenlen; i ++) + update((byte)0); + update(countBuf); + + /* + * This code is used only for debugging purposes. + * + if (flush() != 0) + throw new Error("panic: buffering went astray"); + * + */ + } + + /** + * Encode the 32-bit word {@code val} into the array + * {@code buf} at offset {@code off}, in little-endian + * convention (least significant byte first). + * + * @param val the value to encode + * @param buf the destination buffer + * @param off the destination offset + */ + private static final void encodeLEInt(int val, byte[] buf, int off) + { + buf[off + 0] = (byte)val; + buf[off + 1] = (byte)(val >>> 8); + buf[off + 2] = (byte)(val >>> 16); + buf[off + 3] = (byte)(val >>> 24); + } + + /** + * Encode the 32-bit word {@code val} into the array + * {@code buf} at offset {@code off}, in big-endian + * convention (most significant byte first). + * + * @param val the value to encode + * @param buf the destination buffer + * @param off the destination offset + */ + private static final void encodeBEInt(int val, byte[] buf, int off) + { + buf[off + 0] = (byte)(val >>> 24); + buf[off + 1] = (byte)(val >>> 16); + buf[off + 2] = (byte)(val >>> 8); + buf[off + 3] = (byte)val; + } +} diff --git a/jami-dht/src/main/java/net/jami/jams/dht/hashutils/SHA2Core.java b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/SHA2Core.java new file mode 100755 index 0000000000000000000000000000000000000000..de78d1055c6a9bba67a0ac468990f4b59a3b588c --- /dev/null +++ b/jami-dht/src/main/java/net/jami/jams/dht/hashutils/SHA2Core.java @@ -0,0 +1,631 @@ +// $Id: SHA2Core.java 214 2010-06-03 17:25:08Z tp $ + +package net.jami.jams.dht.hashutils; + +/** + * This class implements SHA-224 and SHA-256, which differ only by the IV + * and the output length. + * + * <pre> + * ==========================(LICENSE BEGIN)============================ + * + * Copyright (c) 2007-2010 Projet RNRT SAPHIR + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * ===========================(LICENSE END)============================= + * </pre> + * + * @version $Revision: 214 $ + * @author Thomas Pornin <thomas.pornin@cryptolog.com> + */ + +abstract class SHA2Core extends MDHelper { + + /** + * Create the object. + */ + SHA2Core() + { + super(false, 8); + } + + /** private special values. */ + private static final int[] K = { + 0x428A2F98, 0x71374491, 0xB5C0FBCF, 0xE9B5DBA5, + 0x3956C25B, 0x59F111F1, 0x923F82A4, 0xAB1C5ED5, + 0xD807AA98, 0x12835B01, 0x243185BE, 0x550C7DC3, + 0x72BE5D74, 0x80DEB1FE, 0x9BDC06A7, 0xC19BF174, + 0xE49B69C1, 0xEFBE4786, 0x0FC19DC6, 0x240CA1CC, + 0x2DE92C6F, 0x4A7484AA, 0x5CB0A9DC, 0x76F988DA, + 0x983E5152, 0xA831C66D, 0xB00327C8, 0xBF597FC7, + 0xC6E00BF3, 0xD5A79147, 0x06CA6351, 0x14292967, + 0x27B70A85, 0x2E1B2138, 0x4D2C6DFC, 0x53380D13, + 0x650A7354, 0x766A0ABB, 0x81C2C92E, 0x92722C85, + 0xA2BFE8A1, 0xA81A664B, 0xC24B8B70, 0xC76C51A3, + 0xD192E819, 0xD6990624, 0xF40E3585, 0x106AA070, + 0x19A4C116, 0x1E376C08, 0x2748774C, 0x34B0BCB5, + 0x391C0CB3, 0x4ED8AA4A, 0x5B9CCA4F, 0x682E6FF3, + 0x748F82EE, 0x78A5636F, 0x84C87814, 0x8CC70208, + 0x90BEFFFA, 0xA4506CEB, 0xBEF9A3F7, 0xC67178F2 + }; + + private int[] currentVal, W; + + /** @see DigestEngine */ + protected Digest copyState(SHA2Core dst) + { + System.arraycopy(currentVal, 0, dst.currentVal, 0, + currentVal.length); + return super.copyState(dst); + } + + /** @see Digest */ + public int getBlockLength() + { + return 64; + } + + /** @see DigestEngine */ + protected void engineReset() + { + System.arraycopy(getInitVal(), 0, currentVal, 0, 8); + } + + /** + * Get the initial value for this algorithm. + * + * @return the initial value (eight 32-bit words) + */ + abstract int[] getInitVal(); + + /** @see DigestEngine */ + protected void doPadding(byte[] output, int outputOffset) + { + makeMDPadding(); + int olen = getDigestLength(); + for (int i = 0, j = 0; j < olen; i ++, j += 4) + encodeBEInt(currentVal[i], output, outputOffset + j); + } + + /** @see DigestEngine */ + protected void doInit() + { + currentVal = new int[8]; + W = new int[64]; + engineReset(); + } + + /** + * Encode the 32-bit word {@code val} into the array + * {@code buf} at offset {@code off}, in big-endian + * convention (most significant byte first). + * + * @param val the value to encode + * @param buf the destination buffer + * @param off the destination offset + */ + private static final void encodeBEInt(int val, byte[] buf, int off) + { + buf[off + 0] = (byte)(val >>> 24); + buf[off + 1] = (byte)(val >>> 16); + buf[off + 2] = (byte)(val >>> 8); + buf[off + 3] = (byte)val; + } + + /** + * Decode a 32-bit big-endian word from the array {@code buf} + * at offset {@code off}. + * + * @param buf the source buffer + * @param off the source offset + * @return the decoded value + */ + private static final int decodeBEInt(byte[] buf, int off) + { + return ((buf[off] & 0xFF) << 24) + | ((buf[off + 1] & 0xFF) << 16) + | ((buf[off + 2] & 0xFF) << 8) + | (buf[off + 3] & 0xFF); + } + + /** + * Perform a circular rotation by {@code n} to the left + * of the 32-bit word {@code x}. The {@code n} parameter + * must lie between 1 and 31 (inclusive). + * + * @param x the value to rotate + * @param n the rotation count (between 1 and 31) + * @return the rotated value + */ + static private int circularLeft(int x, int n) + { + return (x << n) | (x >>> (32 - n)); + } + + /** @see DigestEngine */ + protected void processBlock(byte[] data) + { + int A = currentVal[0]; + int B = currentVal[1]; + int C = currentVal[2]; + int D = currentVal[3]; + int E = currentVal[4]; + int F = currentVal[5]; + int G = currentVal[6]; + int H = currentVal[7]; + + for (int i = 0; i < 16; i ++) + W[i] = decodeBEInt(data, 4 * i); + for (int i = 16; i < 64; i ++) { + W[i] = (circularLeft(W[i - 2], 15) + ^ circularLeft(W[i - 2], 13) + ^ (W[i - 2] >>> 10)) + + W[i - 7] + + (circularLeft(W[i - 15], 25) + ^ circularLeft(W[i - 15], 14) + ^ (W[i - 15] >>> 3)) + + W[i - 16]; + } + for (int i = 0; i < 64; i ++) { + int T1 = H + (circularLeft(E, 26) ^ circularLeft(E, 21) + ^ circularLeft(E, 7)) + ((F & E) ^ (G & ~E)) + + K[i] + W[i]; + int T2 = (circularLeft(A, 30) ^ circularLeft(A, 19) + ^ circularLeft(A, 10)) + + ((A & B) ^ (A & C) ^ (B & C)); + H = G; G = F; F = E; E = D + T1; + D = C; C = B; B = A; A = T1 + T2; + } + currentVal[0] += A; + currentVal[1] += B; + currentVal[2] += C; + currentVal[3] += D; + currentVal[4] += E; + currentVal[5] += F; + currentVal[6] += G; + currentVal[7] += H; + + /* + * The version below unrolls 16 rounds and inlines + * rotations. It should avoid many array accesses + * (W[] is transformed into 16 local variables) and + * data routing (16 is a multiple of 8, so the + * big rotation of the eight words becomes trivial). + * Strangely enough, it yields only a very small + * performance gain (less than 10% on Intel x86 with + * Sun JDK 6, both in 32-bit and 64-bit modes). Since + * it also probably consumes much more L1 cache, the + * simpler version above is preferred. + * + int A = currentVal[0]; + int B = currentVal[1]; + int C = currentVal[2]; + int D = currentVal[3]; + int E = currentVal[4]; + int F = currentVal[5]; + int G = currentVal[6]; + int H = currentVal[7]; + int t1, t2; + int pcount = 0; + int W0 = decodeBEInt(data, 4 * 0x0); + t1 = H + (((E >>> 6) | (E << (32 - 6))) ^ ((E >>> 11) + | (E << (32 - 11))) ^ ((E >>> 25) | (E << (32 - 25)))) + + (((F ^ G) & E) ^ G) + K[pcount + 0x0] + W0; + t2 = (((A >>> 2) | (A << (32 - 2))) ^ ((A >>> 13) + | (A << (32 - 13))) ^ ((A >>> 22) | (A << (32 - 22)))) + + ((B & C) | ((B | C) & A)); + D += t1; + H = t1 + t2; + int W1 = decodeBEInt(data, 4 * 0x1); + t1 = G + (((D >>> 6) | (D << (32 - 6))) ^ ((D >>> 11) + | (D << (32 - 11))) ^ ((D >>> 25) | (D << (32 - 25)))) + + (((E ^ F) & D) ^ F) + K[pcount + 0x1] + W1; + t2 = (((H >>> 2) | (H << (32 - 2))) ^ ((H >>> 13) + | (H << (32 - 13))) ^ ((H >>> 22) | (H << (32 - 22)))) + + ((A & B) | ((A | B) & H)); + C += t1; + G = t1 + t2; + int W2 = decodeBEInt(data, 4 * 0x2); + t1 = F + (((C >>> 6) | (C << (32 - 6))) ^ ((C >>> 11) + | (C << (32 - 11))) ^ ((C >>> 25) | (C << (32 - 25)))) + + (((D ^ E) & C) ^ E) + K[pcount + 0x2] + W2; + t2 = (((G >>> 2) | (G << (32 - 2))) ^ ((G >>> 13) + | (G << (32 - 13))) ^ ((G >>> 22) | (G << (32 - 22)))) + + ((H & A) | ((H | A) & G)); + B += t1; + F = t1 + t2; + int W3 = decodeBEInt(data, 4 * 0x3); + t1 = E + (((B >>> 6) | (B << (32 - 6))) ^ ((B >>> 11) + | (B << (32 - 11))) ^ ((B >>> 25) | (B << (32 - 25)))) + + (((C ^ D) & B) ^ D) + K[pcount + 0x3] + W3; + t2 = (((F >>> 2) | (F << (32 - 2))) ^ ((F >>> 13) + | (F << (32 - 13))) ^ ((F >>> 22) | (F << (32 - 22)))) + + ((G & H) | ((G | H) & F)); + A += t1; + E = t1 + t2; + int W4 = decodeBEInt(data, 4 * 0x4); + t1 = D + (((A >>> 6) | (A << (32 - 6))) ^ ((A >>> 11) + | (A << (32 - 11))) ^ ((A >>> 25) | (A << (32 - 25)))) + + (((B ^ C) & A) ^ C) + K[pcount + 0x4] + W4; + t2 = (((E >>> 2) | (E << (32 - 2))) ^ ((E >>> 13) + | (E << (32 - 13))) ^ ((E >>> 22) | (E << (32 - 22)))) + + ((F & G) | ((F | G) & E)); + H += t1; + D = t1 + t2; + int W5 = decodeBEInt(data, 4 * 0x5); + t1 = C + (((H >>> 6) | (H << (32 - 6))) ^ ((H >>> 11) + | (H << (32 - 11))) ^ ((H >>> 25) | (H << (32 - 25)))) + + (((A ^ B) & H) ^ B) + K[pcount + 0x5] + W5; + t2 = (((D >>> 2) | (D << (32 - 2))) ^ ((D >>> 13) + | (D << (32 - 13))) ^ ((D >>> 22) | (D << (32 - 22)))) + + ((E & F) | ((E | F) & D)); + G += t1; + C = t1 + t2; + int W6 = decodeBEInt(data, 4 * 0x6); + t1 = B + (((G >>> 6) | (G << (32 - 6))) ^ ((G >>> 11) + | (G << (32 - 11))) ^ ((G >>> 25) | (G << (32 - 25)))) + + (((H ^ A) & G) ^ A) + K[pcount + 0x6] + W6; + t2 = (((C >>> 2) | (C << (32 - 2))) ^ ((C >>> 13) + | (C << (32 - 13))) ^ ((C >>> 22) | (C << (32 - 22)))) + + ((D & E) | ((D | E) & C)); + F += t1; + B = t1 + t2; + int W7 = decodeBEInt(data, 4 * 0x7); + t1 = A + (((F >>> 6) | (F << (32 - 6))) ^ ((F >>> 11) + | (F << (32 - 11))) ^ ((F >>> 25) | (F << (32 - 25)))) + + (((G ^ H) & F) ^ H) + K[pcount + 0x7] + W7; + t2 = (((B >>> 2) | (B << (32 - 2))) ^ ((B >>> 13) + | (B << (32 - 13))) ^ ((B >>> 22) | (B << (32 - 22)))) + + ((C & D) | ((C | D) & B)); + E += t1; + A = t1 + t2; + int W8 = decodeBEInt(data, 4 * 0x8); + t1 = H + (((E >>> 6) | (E << (32 - 6))) ^ ((E >>> 11) + | (E << (32 - 11))) ^ ((E >>> 25) | (E << (32 - 25)))) + + (((F ^ G) & E) ^ G) + K[pcount + 0x8] + W8; + t2 = (((A >>> 2) | (A << (32 - 2))) ^ ((A >>> 13) + | (A << (32 - 13))) ^ ((A >>> 22) | (A << (32 - 22)))) + + ((B & C) | ((B | C) & A)); + D += t1; + H = t1 + t2; + int W9 = decodeBEInt(data, 4 * 0x9); + t1 = G + (((D >>> 6) | (D << (32 - 6))) ^ ((D >>> 11) + | (D << (32 - 11))) ^ ((D >>> 25) | (D << (32 - 25)))) + + (((E ^ F) & D) ^ F) + K[pcount + 0x9] + W9; + t2 = (((H >>> 2) | (H << (32 - 2))) ^ ((H >>> 13) + | (H << (32 - 13))) ^ ((H >>> 22) | (H << (32 - 22)))) + + ((A & B) | ((A | B) & H)); + C += t1; + G = t1 + t2; + int WA = decodeBEInt(data, 4 * 0xA); + t1 = F + (((C >>> 6) | (C << (32 - 6))) ^ ((C >>> 11) + | (C << (32 - 11))) ^ ((C >>> 25) | (C << (32 - 25)))) + + (((D ^ E) & C) ^ E) + K[pcount + 0xA] + WA; + t2 = (((G >>> 2) | (G << (32 - 2))) ^ ((G >>> 13) + | (G << (32 - 13))) ^ ((G >>> 22) | (G << (32 - 22)))) + + ((H & A) | ((H | A) & G)); + B += t1; + F = t1 + t2; + int WB = decodeBEInt(data, 4 * 0xB); + t1 = E + (((B >>> 6) | (B << (32 - 6))) ^ ((B >>> 11) + | (B << (32 - 11))) ^ ((B >>> 25) | (B << (32 - 25)))) + + (((C ^ D) & B) ^ D) + K[pcount + 0xB] + WB; + t2 = (((F >>> 2) | (F << (32 - 2))) ^ ((F >>> 13) + | (F << (32 - 13))) ^ ((F >>> 22) | (F << (32 - 22)))) + + ((G & H) | ((G | H) & F)); + A += t1; + E = t1 + t2; + int WC = decodeBEInt(data, 4 * 0xC); + t1 = D + (((A >>> 6) | (A << (32 - 6))) ^ ((A >>> 11) + | (A << (32 - 11))) ^ ((A >>> 25) | (A << (32 - 25)))) + + (((B ^ C) & A) ^ C) + K[pcount + 0xC] + WC; + t2 = (((E >>> 2) | (E << (32 - 2))) ^ ((E >>> 13) + | (E << (32 - 13))) ^ ((E >>> 22) | (E << (32 - 22)))) + + ((F & G) | ((F | G) & E)); + H += t1; + D = t1 + t2; + int WD = decodeBEInt(data, 4 * 0xD); + t1 = C + (((H >>> 6) | (H << (32 - 6))) ^ ((H >>> 11) + | (H << (32 - 11))) ^ ((H >>> 25) | (H << (32 - 25)))) + + (((A ^ B) & H) ^ B) + K[pcount + 0xD] + WD; + t2 = (((D >>> 2) | (D << (32 - 2))) ^ ((D >>> 13) + | (D << (32 - 13))) ^ ((D >>> 22) | (D << (32 - 22)))) + + ((E & F) | ((E | F) & D)); + G += t1; + C = t1 + t2; + int WE = decodeBEInt(data, 4 * 0xE); + t1 = B + (((G >>> 6) | (G << (32 - 6))) ^ ((G >>> 11) + | (G << (32 - 11))) ^ ((G >>> 25) | (G << (32 - 25)))) + + (((H ^ A) & G) ^ A) + K[pcount + 0xE] + WE; + t2 = (((C >>> 2) | (C << (32 - 2))) ^ ((C >>> 13) + | (C << (32 - 13))) ^ ((C >>> 22) | (C << (32 - 22)))) + + ((D & E) | ((D | E) & C)); + F += t1; + B = t1 + t2; + int WF = decodeBEInt(data, 4 * 0xF); + t1 = A + (((F >>> 6) | (F << (32 - 6))) ^ ((F >>> 11) + | (F << (32 - 11))) ^ ((F >>> 25) | (F << (32 - 25)))) + + (((G ^ H) & F) ^ H) + K[pcount + 0xF] + WF; + t2 = (((B >>> 2) | (B << (32 - 2))) ^ ((B >>> 13) + | (B << (32 - 13))) ^ ((B >>> 22) | (B << (32 - 22)))) + + ((C & D) | ((C | D) & B)); + E += t1; + A = t1 + t2; + for (pcount = 16; pcount < 64; pcount += 16) { + W0 += (((WE >>> 17) | (WE << (32 - 17))) ^ ((WE >>> 19) + | (WE << (32 - 19))) ^ (WE >>> 10)) + W9 + + (((W1 >>> 7) | (W1 << (32 - 7))) + ^ ((W1 >>> 18) | (W1 << (32 - 18))) + ^ (W1 >>> 3)); + t1 = H + (((E >>> 6) | (E << (32 - 6))) ^ ((E >>> 11) + | (E << (32 - 11))) ^ ((E >>> 25) + | (E << (32 - 25)))) + (((F ^ G) & E) ^ G) + + K[pcount + 0x0] + W0; + t2 = (((A >>> 2) | (A << (32 - 2))) ^ ((A >>> 13) + | (A << (32 - 13))) ^ ((A >>> 22) + | (A << (32 - 22)))) + + ((B & C) | ((B | C) & A)); + D += t1; + H = t1 + t2; + W1 += (((WF >>> 17) | (WF << (32 - 17))) ^ ((WF >>> 19) + | (WF << (32 - 19))) ^ (WF >>> 10)) + WA + + (((W2 >>> 7) | (W2 << (32 - 7))) + ^ ((W2 >>> 18) | (W2 << (32 - 18))) + ^ (W2 >>> 3)); + t1 = G + (((D >>> 6) | (D << (32 - 6))) ^ ((D >>> 11) + | (D << (32 - 11))) ^ ((D >>> 25) + | (D << (32 - 25)))) + (((E ^ F) & D) ^ F) + + K[pcount + 0x1] + W1; + t2 = (((H >>> 2) | (H << (32 - 2))) ^ ((H >>> 13) + | (H << (32 - 13))) ^ ((H >>> 22) + | (H << (32 - 22)))) + + ((A & B) | ((A | B) & H)); + C += t1; + G = t1 + t2; + W2 += (((W0 >>> 17) | (W0 << (32 - 17))) ^ ((W0 >>> 19) + | (W0 << (32 - 19))) ^ (W0 >>> 10)) + WB + + (((W3 >>> 7) | (W3 << (32 - 7))) + ^ ((W3 >>> 18) | (W3 << (32 - 18))) + ^ (W3 >>> 3)); + t1 = F + (((C >>> 6) | (C << (32 - 6))) ^ ((C >>> 11) + | (C << (32 - 11))) ^ ((C >>> 25) + | (C << (32 - 25)))) + (((D ^ E) & C) ^ E) + + K[pcount + 0x2] + W2; + t2 = (((G >>> 2) | (G << (32 - 2))) ^ ((G >>> 13) + | (G << (32 - 13))) ^ ((G >>> 22) + | (G << (32 - 22)))) + + ((H & A) | ((H | A) & G)); + B += t1; + F = t1 + t2; + W3 += (((W1 >>> 17) | (W1 << (32 - 17))) ^ ((W1 >>> 19) + | (W1 << (32 - 19))) ^ (W1 >>> 10)) + WC + + (((W4 >>> 7) | (W4 << (32 - 7))) + ^ ((W4 >>> 18) | (W4 << (32 - 18))) + ^ (W4 >>> 3)); + t1 = E + (((B >>> 6) | (B << (32 - 6))) ^ ((B >>> 11) + | (B << (32 - 11))) ^ ((B >>> 25) + | (B << (32 - 25)))) + (((C ^ D) & B) ^ D) + + K[pcount + 0x3] + W3; + t2 = (((F >>> 2) | (F << (32 - 2))) ^ ((F >>> 13) + | (F << (32 - 13))) ^ ((F >>> 22) + | (F << (32 - 22)))) + + ((G & H) | ((G | H) & F)); + A += t1; + E = t1 + t2; + W4 += (((W2 >>> 17) | (W2 << (32 - 17))) ^ ((W2 >>> 19) + | (W2 << (32 - 19))) ^ (W2 >>> 10)) + WD + + (((W5 >>> 7) | (W5 << (32 - 7))) + ^ ((W5 >>> 18) | (W5 << (32 - 18))) + ^ (W5 >>> 3)); + t1 = D + (((A >>> 6) | (A << (32 - 6))) ^ ((A >>> 11) + | (A << (32 - 11))) ^ ((A >>> 25) + | (A << (32 - 25)))) + (((B ^ C) & A) ^ C) + + K[pcount + 0x4] + W4; + t2 = (((E >>> 2) | (E << (32 - 2))) ^ ((E >>> 13) + | (E << (32 - 13))) ^ ((E >>> 22) + | (E << (32 - 22)))) + + ((F & G) | ((F | G) & E)); + H += t1; + D = t1 + t2; + W5 += (((W3 >>> 17) | (W3 << (32 - 17))) ^ ((W3 >>> 19) + | (W3 << (32 - 19))) ^ (W3 >>> 10)) + WE + + (((W6 >>> 7) | (W6 << (32 - 7))) + ^ ((W6 >>> 18) | (W6 << (32 - 18))) + ^ (W6 >>> 3)); + t1 = C + (((H >>> 6) | (H << (32 - 6))) ^ ((H >>> 11) + | (H << (32 - 11))) ^ ((H >>> 25) + | (H << (32 - 25)))) + (((A ^ B) & H) ^ B) + + K[pcount + 0x5] + W5; + t2 = (((D >>> 2) | (D << (32 - 2))) ^ ((D >>> 13) + | (D << (32 - 13))) ^ ((D >>> 22) + | (D << (32 - 22)))) + + ((E & F) | ((E | F) & D)); + G += t1; + C = t1 + t2; + W6 += (((W4 >>> 17) | (W4 << (32 - 17))) ^ ((W4 >>> 19) + | (W4 << (32 - 19))) ^ (W4 >>> 10)) + WF + + (((W7 >>> 7) | (W7 << (32 - 7))) + ^ ((W7 >>> 18) | (W7 << (32 - 18))) + ^ (W7 >>> 3)); + t1 = B + (((G >>> 6) | (G << (32 - 6))) ^ ((G >>> 11) + | (G << (32 - 11))) ^ ((G >>> 25) + | (G << (32 - 25)))) + (((H ^ A) & G) ^ A) + + K[pcount + 0x6] + W6; + t2 = (((C >>> 2) | (C << (32 - 2))) ^ ((C >>> 13) + | (C << (32 - 13))) ^ ((C >>> 22) + | (C << (32 - 22)))) + + ((D & E) | ((D | E) & C)); + F += t1; + B = t1 + t2; + W7 += (((W5 >>> 17) | (W5 << (32 - 17))) ^ ((W5 >>> 19) + | (W5 << (32 - 19))) ^ (W5 >>> 10)) + W0 + + (((W8 >>> 7) | (W8 << (32 - 7))) + ^ ((W8 >>> 18) | (W8 << (32 - 18))) + ^ (W8 >>> 3)); + t1 = A + (((F >>> 6) | (F << (32 - 6))) ^ ((F >>> 11) + | (F << (32 - 11))) ^ ((F >>> 25) + | (F << (32 - 25)))) + (((G ^ H) & F) ^ H) + + K[pcount + 0x7] + W7; + t2 = (((B >>> 2) | (B << (32 - 2))) ^ ((B >>> 13) + | (B << (32 - 13))) ^ ((B >>> 22) + | (B << (32 - 22)))) + + ((C & D) | ((C | D) & B)); + E += t1; + A = t1 + t2; + W8 += (((W6 >>> 17) | (W6 << (32 - 17))) ^ ((W6 >>> 19) + | (W6 << (32 - 19))) ^ (W6 >>> 10)) + W1 + + (((W9 >>> 7) | (W9 << (32 - 7))) + ^ ((W9 >>> 18) | (W9 << (32 - 18))) + ^ (W9 >>> 3)); + t1 = H + (((E >>> 6) | (E << (32 - 6))) ^ ((E >>> 11) + | (E << (32 - 11))) ^ ((E >>> 25) + | (E << (32 - 25)))) + (((F ^ G) & E) ^ G) + + K[pcount + 0x8] + W8; + t2 = (((A >>> 2) | (A << (32 - 2))) ^ ((A >>> 13) + | (A << (32 - 13))) ^ ((A >>> 22) + | (A << (32 - 22)))) + + ((B & C) | ((B | C) & A)); + D += t1; + H = t1 + t2; + W9 += (((W7 >>> 17) | (W7 << (32 - 17))) ^ ((W7 >>> 19) + | (W7 << (32 - 19))) ^ (W7 >>> 10)) + W2 + + (((WA >>> 7) | (WA << (32 - 7))) + ^ ((WA >>> 18) | (WA << (32 - 18))) + ^ (WA >>> 3)); + t1 = G + (((D >>> 6) | (D << (32 - 6))) ^ ((D >>> 11) + | (D << (32 - 11))) ^ ((D >>> 25) + | (D << (32 - 25)))) + (((E ^ F) & D) ^ F) + + K[pcount + 0x9] + W9; + t2 = (((H >>> 2) | (H << (32 - 2))) ^ ((H >>> 13) + | (H << (32 - 13))) ^ ((H >>> 22) + | (H << (32 - 22)))) + + ((A & B) | ((A | B) & H)); + C += t1; + G = t1 + t2; + WA += (((W8 >>> 17) | (W8 << (32 - 17))) ^ ((W8 >>> 19) + | (W8 << (32 - 19))) ^ (W8 >>> 10)) + W3 + + (((WB >>> 7) | (WB << (32 - 7))) + ^ ((WB >>> 18) | (WB << (32 - 18))) + ^ (WB >>> 3)); + t1 = F + (((C >>> 6) | (C << (32 - 6))) ^ ((C >>> 11) + | (C << (32 - 11))) ^ ((C >>> 25) + | (C << (32 - 25)))) + (((D ^ E) & C) ^ E) + + K[pcount + 0xA] + WA; + t2 = (((G >>> 2) | (G << (32 - 2))) ^ ((G >>> 13) + | (G << (32 - 13))) ^ ((G >>> 22) + | (G << (32 - 22)))) + + ((H & A) | ((H | A) & G)); + B += t1; + F = t1 + t2; + WB += (((W9 >>> 17) | (W9 << (32 - 17))) ^ ((W9 >>> 19) + | (W9 << (32 - 19))) ^ (W9 >>> 10)) + W4 + + (((WC >>> 7) | (WC << (32 - 7))) + ^ ((WC >>> 18) | (WC << (32 - 18))) + ^ (WC >>> 3)); + t1 = E + (((B >>> 6) | (B << (32 - 6))) ^ ((B >>> 11) + | (B << (32 - 11))) ^ ((B >>> 25) + | (B << (32 - 25)))) + (((C ^ D) & B) ^ D) + + K[pcount + 0xB] + WB; + t2 = (((F >>> 2) | (F << (32 - 2))) ^ ((F >>> 13) + | (F << (32 - 13))) ^ ((F >>> 22) + | (F << (32 - 22)))) + + ((G & H) | ((G | H) & F)); + A += t1; + E = t1 + t2; + WC += (((WA >>> 17) | (WA << (32 - 17))) ^ ((WA >>> 19) + | (WA << (32 - 19))) ^ (WA >>> 10)) + W5 + + (((WD >>> 7) | (WD << (32 - 7))) + ^ ((WD >>> 18) | (WD << (32 - 18))) + ^ (WD >>> 3)); + t1 = D + (((A >>> 6) | (A << (32 - 6))) ^ ((A >>> 11) + | (A << (32 - 11))) ^ ((A >>> 25) + | (A << (32 - 25)))) + (((B ^ C) & A) ^ C) + + K[pcount + 0xC] + WC; + t2 = (((E >>> 2) | (E << (32 - 2))) ^ ((E >>> 13) + | (E << (32 - 13))) ^ ((E >>> 22) + | (E << (32 - 22)))) + + ((F & G) | ((F | G) & E)); + H += t1; + D = t1 + t2; + WD += (((WB >>> 17) | (WB << (32 - 17))) ^ ((WB >>> 19) + | (WB << (32 - 19))) ^ (WB >>> 10)) + W6 + + (((WE >>> 7) | (WE << (32 - 7))) + ^ ((WE >>> 18) | (WE << (32 - 18))) + ^ (WE >>> 3)); + t1 = C + (((H >>> 6) | (H << (32 - 6))) ^ ((H >>> 11) + | (H << (32 - 11))) ^ ((H >>> 25) + | (H << (32 - 25)))) + (((A ^ B) & H) ^ B) + + K[pcount + 0xD] + WD; + t2 = (((D >>> 2) | (D << (32 - 2))) ^ ((D >>> 13) + | (D << (32 - 13))) ^ ((D >>> 22) + | (D << (32 - 22)))) + + ((E & F) | ((E | F) & D)); + G += t1; + C = t1 + t2; + WE += (((WC >>> 17) | (WC << (32 - 17))) ^ ((WC >>> 19) + | (WC << (32 - 19))) ^ (WC >>> 10)) + W7 + + (((WF >>> 7) | (WF << (32 - 7))) + ^ ((WF >>> 18) | (WF << (32 - 18))) + ^ (WF >>> 3)); + t1 = B + (((G >>> 6) | (G << (32 - 6))) ^ ((G >>> 11) + | (G << (32 - 11))) ^ ((G >>> 25) + | (G << (32 - 25)))) + (((H ^ A) & G) ^ A) + + K[pcount + 0xE] + WE; + t2 = (((C >>> 2) | (C << (32 - 2))) ^ ((C >>> 13) + | (C << (32 - 13))) ^ ((C >>> 22) + | (C << (32 - 22)))) + + ((D & E) | ((D | E) & C)); + F += t1; + B = t1 + t2; + WF += (((WD >>> 17) | (WD << (32 - 17))) ^ ((WD >>> 19) + | (WD << (32 - 19))) ^ (WD >>> 10)) + W8 + + (((W0 >>> 7) | (W0 << (32 - 7))) + ^ ((W0 >>> 18) | (W0 << (32 - 18))) + ^ (W0 >>> 3)); + t1 = A + (((F >>> 6) | (F << (32 - 6))) ^ ((F >>> 11) + | (F << (32 - 11))) ^ ((F >>> 25) + | (F << (32 - 25)))) + (((G ^ H) & F) ^ H) + + K[pcount + 0xF] + WF; + t2 = (((B >>> 2) | (B << (32 - 2))) ^ ((B >>> 13) + | (B << (32 - 13))) ^ ((B >>> 22) + | (B << (32 - 22)))) + + ((C & D) | ((C | D) & B)); + E += t1; + A = t1 + t2; + } + + currentVal[0] += A; + currentVal[1] += B; + currentVal[2] += C; + currentVal[3] += D; + currentVal[4] += E; + currentVal[5] += F; + currentVal[6] += G; + currentVal[7] += H; + */ + } + + /** @see Digest */ + public String toString() + { + return "SHA-" + (getDigestLength() << 3); + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java new file mode 100644 index 0000000000000000000000000000000000000000..f00b6d9598e9dea13632e75eef0232bd57e7d9ec --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSource.java @@ -0,0 +1,15 @@ +package net.jami.jams.common.authentication; + +import net.jami.jams.common.objects.user.User; +import net.jami.jams.common.objects.user.UserProfile; + +public interface AuthenticationSource { + + boolean createUser(User user); + UserProfile getUserProfile(String username); + boolean authenticate(String username, String password); + AuthenticationSourceInfo getInfo(); + boolean testConfiguration(String configuration); + boolean updatePassword(User user, String password); + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceInfo.java b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceInfo.java new file mode 100644 index 0000000000000000000000000000000000000000..1f5e494f83600c8e7fd6f364c212b74e5d2595e9 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceInfo.java @@ -0,0 +1,17 @@ +package net.jami.jams.common.authentication; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class AuthenticationSourceInfo { + + private String realm; + private AuthenticationSourceType authenticationSourceType; + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceType.java b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceType.java new file mode 100644 index 0000000000000000000000000000000000000000..329f003b8d5c5ded471bae9d7fc51e1b8053d514 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/authentication/AuthenticationSourceType.java @@ -0,0 +1,7 @@ +package net.jami.jams.common.authentication; + +public enum AuthenticationSourceType { + AD, + LDAP, + LOCAL +} diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/activedirectory/ActiveDirectorySettings.java b/jams-common/src/main/java/net/jami/jams/common/authentication/activedirectory/ActiveDirectorySettings.java new file mode 100644 index 0000000000000000000000000000000000000000..55e262bab892c3efd109c1ed020456e4eb28727c --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/authentication/activedirectory/ActiveDirectorySettings.java @@ -0,0 +1,15 @@ +package net.jami.jams.common.authentication.activedirectory; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ActiveDirectorySettings { + private Boolean ssl; + private int port; + private String realm; + private String host; + private String username; + private String password; +} diff --git a/jams-common/src/main/java/net/jami/jams/common/authentication/ldap/LDAPSettings.java b/jams-common/src/main/java/net/jami/jams/common/authentication/ldap/LDAPSettings.java new file mode 100644 index 0000000000000000000000000000000000000000..f4fd0fc4be7362bc8b5eac67bbea5454497471c7 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/authentication/ldap/LDAPSettings.java @@ -0,0 +1,18 @@ +package net.jami.jams.common.authentication.ldap; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class LDAPSettings { + + private Boolean useStartTLS; + private String realm; + private String baseDN; + private String host; + private String username; + private String password; + private String usernameField; + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/configuration/ServerConfiguration.java b/jams-common/src/main/java/net/jami/jams/common/configuration/ServerConfiguration.java new file mode 100644 index 0000000000000000000000000000000000000000..7cc0d52a62082dd961cba740be623bf69686002b --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/configuration/ServerConfiguration.java @@ -0,0 +1,12 @@ +package net.jami.jams.common.configuration; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class ServerConfiguration { + + private int port; + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/cryptoengineapi/CertificateAuthority.java b/jams-common/src/main/java/net/jami/jams/common/cryptoengineapi/CertificateAuthority.java new file mode 100644 index 0000000000000000000000000000000000000000..29fb239d8b8c7546d2b7dc9921d7cdbe790e7407 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/cryptoengineapi/CertificateAuthority.java @@ -0,0 +1,22 @@ +package net.jami.jams.common.cryptoengineapi; + +import net.jami.jams.common.objects.devices.Device; +import net.jami.jams.common.objects.requests.RevocationRequest; +import net.jami.jams.common.objects.system.SystemAccount; +import net.jami.jams.common.objects.user.User; +import org.bouncycastle.cert.X509CRLHolder; +import org.bouncycastle.cert.ocsp.OCSPReq; +import org.bouncycastle.cert.ocsp.OCSPResp; + +import java.util.concurrent.atomic.AtomicReference; + +public interface CertificateAuthority { + //Return a signed X509 certificate based on various constraints. + void init(String domain, String signingAlgorithm, SystemAccount ca, SystemAccount ocsp); + User getSignedCertificate(User user); + Device getSignedCertificate(User user, Device device); + SystemAccount getSignedCertificate(SystemAccount systemAccount); + void revokeCertificate(RevocationRequest revocationRequest); + AtomicReference<X509CRLHolder> getLatestCRL(); + OCSPResp getOCSPResponse(OCSPReq ocspRequest); +} diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/SelectStatementBuilder.java b/jams-common/src/main/java/net/jami/jams/common/dao/SelectStatementBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..d5d53104dbda41eeb4b04e4ce6aae7d9189cfa9c --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/dao/SelectStatementBuilder.java @@ -0,0 +1,42 @@ +package net.jami.jams.common.dao; + +import net.jami.jams.common.dao.connectivity.SQLConnection; + +import java.sql.PreparedStatement; +import java.util.List; + +public class SelectStatementBuilder { + + public static PreparedStatement buildStatement(String table, List<StatementElement> statementElements, + SQLConnection connection) throws Exception + { + PreparedStatement ps = null; + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("SELECT * FROM ").append(table); + if(statementElements != null) { + stringBuilder.append(" WHERE "); + for (StatementElement statementElement : statementElements) { + stringBuilder + .append(statementElement.getColumn()) + .append(" ") + .append(statementElement.getOperator()) + .append(" ") + .append("?") + .append(" ") + .append(statementElement.getNextStatementRelation()); + } + ps = connection.getConnection().prepareStatement(stringBuilder.toString()); + int i = 1; + for (StatementElement statementElement : statementElements) { + ps.setString(i, statementElement.getValue()); + i++; + } + } + else{ + ps = connection.getConnection().prepareStatement(stringBuilder.toString()); + } + return ps; + } + + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/StatementElement.java b/jams-common/src/main/java/net/jami/jams/common/dao/StatementElement.java new file mode 100644 index 0000000000000000000000000000000000000000..541e48582b5a625c5d6fd9d8c1b6ea8bb58f05df --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/dao/StatementElement.java @@ -0,0 +1,17 @@ +package net.jami.jams.common.dao; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class StatementElement { + private String column; + private String operator; + private String value; + private String nextStatementRelation; +} diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/StatementList.java b/jams-common/src/main/java/net/jami/jams/common/dao/StatementList.java new file mode 100644 index 0000000000000000000000000000000000000000..1b4421dc0af3aa0003480c8042b54bb084d500ee --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/dao/StatementList.java @@ -0,0 +1,19 @@ +package net.jami.jams.common.dao; + +import lombok.Getter; +import lombok.Setter; + +import java.util.ArrayList; +import java.util.List; + +@Getter +@Setter +public class StatementList { + + private List<StatementElement> statements = new ArrayList<>(); + + public void addStatement(StatementElement statementElement){ + statements.add(statementElement); + } + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/UpdateStatementBuilder.java b/jams-common/src/main/java/net/jami/jams/common/dao/UpdateStatementBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..d67273725f6635649bad1b7e382bd1101b74f8da --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/dao/UpdateStatementBuilder.java @@ -0,0 +1,44 @@ +package net.jami.jams.common.dao; + +import net.jami.jams.common.dao.connectivity.SQLConnection; + +import java.sql.PreparedStatement; +import java.util.List; + +public class UpdateStatementBuilder { + public static PreparedStatement buildStatement(String table, List<StatementElement> updateElements, + List<StatementElement> conditionalElements, + SQLConnection connection) throws Exception { + PreparedStatement ps = null; + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("UPDATE ").append(table).append(" SET "); + for (int i = 0; i < updateElements.size(); i++) { + StatementElement statementElement = updateElements.get(i); + stringBuilder + .append(statementElement.getColumn()) + .append(" = ") + .append("?"); + if (i != updateElements.size() - 1) stringBuilder.append(","); + } + stringBuilder.append(" WHERE "); + for (StatementElement statementElement : conditionalElements) { + stringBuilder + .append(statementElement.getColumn()) + .append(" ") + .append(statementElement.getOperator()) + .append(" ") + .append("?") + .append(" ") + .append(statementElement.getNextStatementRelation()); + } + ps = connection.getConnection().prepareStatement(stringBuilder.toString()); + //Now we have to feed this all the elements it hsould have. + updateElements.addAll(conditionalElements); + int i = 1; + for (StatementElement statementElement : updateElements) { + ps.setString(i, statementElement.getValue()); + i++; + } + return ps; + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/connectivity/ConnectionPool.java b/jams-common/src/main/java/net/jami/jams/common/dao/connectivity/ConnectionPool.java new file mode 100644 index 0000000000000000000000000000000000000000..e4ae8891f36fb02f47afcefae15469df5cc47809 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/dao/connectivity/ConnectionPool.java @@ -0,0 +1,60 @@ +package net.jami.jams.common.dao.connectivity; + +import lombok.extern.slf4j.Slf4j; + +import java.sql.DriverManager; +import java.util.concurrent.ConcurrentLinkedQueue; + +@Slf4j +public class ConnectionPool { + + private final ConcurrentLinkedQueue<SQLConnection> connections = new ConcurrentLinkedQueue<>(); + private String connectionURI; + + //Load Derby class. + + public ConnectionPool(String connectionURI) { + try { + Class.forName("org.apache.derby.jdbc.EmbeddedDriver"); + this.connectionURI = connectionURI; + while (connections.size() < 10) { + try { + connections.add(new SQLConnection(DriverManager.getConnection(connectionURI))); + } catch (Exception e) { + log.error("Could not create a link with the database: " + e.toString()); + } + } + } + catch (Exception e){ + log.error("Could not load ApacheDerby class driver: " + e.toString()); + } + } + + public SQLConnection getConnection(){ + try { + SQLConnection sqlConnection = null; + while (sqlConnection == null) { + sqlConnection = connections.poll(); + } + if (sqlConnection.isStale()) sqlConnection = new SQLConnection(DriverManager.getConnection(connectionURI)); + return sqlConnection; + } + catch (Exception e){ + log.info("A connection could not be obtained with error " + e.toString()); + return null; + } + } + + public void returnConnection(SQLConnection sqlConnection){ + try { + if (sqlConnection.isStale()){ + sqlConnection.getConnection().close(); + sqlConnection = new SQLConnection(DriverManager.getConnection(connectionURI)); + } + connections.add(sqlConnection); + } + catch (Exception e){ + log.error("Could not return a connection!"); + } + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/dao/connectivity/SQLConnection.java b/jams-common/src/main/java/net/jami/jams/common/dao/connectivity/SQLConnection.java new file mode 100644 index 0000000000000000000000000000000000000000..5ee1e227cd2e952917d63c88d7e08e691bbe0608 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/dao/connectivity/SQLConnection.java @@ -0,0 +1,25 @@ +package net.jami.jams.common.dao.connectivity; + +import lombok.Getter; +import lombok.Setter; + +import java.sql.Connection; + +@Getter +@Setter +public class SQLConnection { + + private static final long TIMEOUT = 30_000L; + private Connection connection; + private long creationTimestamp; + + public SQLConnection(Connection connection) { + this.connection = connection; + this.creationTimestamp = System.currentTimeMillis(); + } + + public boolean isStale(){ + return (System.currentTimeMillis() - creationTimestamp > TIMEOUT); + } + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/jami/NameRegistrationRequest.java b/jams-common/src/main/java/net/jami/jams/common/jami/NameRegistrationRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..c84f10d349db0282943e8d2852cc1fba027c879d --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/jami/NameRegistrationRequest.java @@ -0,0 +1,13 @@ +package net.jami.jams.common.jami; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class NameRegistrationRequest { + public String addr; + public String owner; + public String signature; + public String publickey; +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/contacts/Contact.java b/jams-common/src/main/java/net/jami/jams/common/objects/contacts/Contact.java new file mode 100644 index 0000000000000000000000000000000000000000..7b8e46406ad79763e1907980620bdd34b884d124 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/contacts/Contact.java @@ -0,0 +1,9 @@ +package net.jami.jams.common.objects.contacts; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class Contact { +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/devices/Device.java b/jams-common/src/main/java/net/jami/jams/common/objects/devices/Device.java new file mode 100644 index 0000000000000000000000000000000000000000..77092714e7e8f0fbab7b0fc60e0eb7991b11640c --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/devices/Device.java @@ -0,0 +1,51 @@ +package net.jami.jams.common.objects.devices; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import net.jami.jams.common.objects.roots.X509Entity; +import net.jami.jams.common.serialization.database.DatabaseObject; +import net.jami.jams.common.utils.X509Utils; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +public class Device extends X509Entity implements DatabaseObject { + + private String deviceId; + private String owner; + private String displayName; + + public Device(ResultSet rs) throws Exception { + this.deviceId = rs.getString("deviceId"); + this.owner = rs.getString("owner"); + this.displayName = rs.getString("displayName"); + this.setCertificate(X509Utils.getCertificateFromPEMString(rs.getString("certificate"))); + this.setPrivateKey(X509Utils.getKeyFromPEMString(rs.getString("privatekey"))); + } + + @Override + public PreparedStatement getInsert(PreparedStatement ps) throws Exception { + ps.setString(1, deviceId); + ps.setString(2, owner); + ps.setString(3,displayName); + ps.setString(4, X509Utils.getPEMStringFromCertificate(this.getCertificate())); + ps.setString(5, X509Utils.getPEMStringFromPrivateKey(this.getPrivateKey())); + return ps; + } + + @Override + public PreparedStatement getDelete(PreparedStatement ps) throws Exception { + return null; + } + + @Override + public PreparedStatement getUpdate(PreparedStatement ps) throws Exception { + return null; + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/requests/EnrollementRequest.java b/jams-common/src/main/java/net/jami/jams/common/objects/requests/EnrollementRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..dda3da98550be76885288c60203fbc0ed45255e8 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/requests/EnrollementRequest.java @@ -0,0 +1,13 @@ +package net.jami.jams.common.objects.requests; + +import lombok.Getter; +import lombok.Setter; +import org.bouncycastle.pkcs.PKCS10CertificationRequest; + +@Getter +@Setter +public class EnrollementRequest { + + private PKCS10CertificationRequest csr; + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationRequest.java b/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationRequest.java new file mode 100644 index 0000000000000000000000000000000000000000..e8cd1d01fbafd5bb61a55fca885b81d6cebcff61 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationRequest.java @@ -0,0 +1,13 @@ +package net.jami.jams.common.objects.requests; + +import lombok.Getter; +import lombok.Setter; + +import java.math.BigInteger; + +@Getter +@Setter +public class RevocationRequest { + private BigInteger identifier; + private RevocationType revocationType; +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationType.java b/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationType.java new file mode 100644 index 0000000000000000000000000000000000000000..713723f1cfa49bfe9040d8036e1e9ba514720138 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/requests/RevocationType.java @@ -0,0 +1,6 @@ +package net.jami.jams.common.objects.requests; + +public enum RevocationType { + USER, + DEVICE +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/roots/BlockchainEntity.java b/jams-common/src/main/java/net/jami/jams/common/objects/roots/BlockchainEntity.java new file mode 100644 index 0000000000000000000000000000000000000000..4c905fc6d4fcefdc77139669e391bba19912d706 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/roots/BlockchainEntity.java @@ -0,0 +1,8 @@ +package net.jami.jams.common.objects.roots; + +public interface BlockchainEntity { + String getAddress(); + void setAddress(String address); + String getKey(); + void setKey(String key); +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/roots/X509Entity.java b/jams-common/src/main/java/net/jami/jams/common/objects/roots/X509Entity.java new file mode 100644 index 0000000000000000000000000000000000000000..c4de1279bf1db6fc943585506d24f1c5e9e46329 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/roots/X509Entity.java @@ -0,0 +1,18 @@ +package net.jami.jams.common.objects.roots; + +import lombok.Getter; +import lombok.Setter; +import org.bouncycastle.pkcs.PKCS10CertificationRequest; + +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +@Getter +@Setter +public class X509Entity { + private X509Certificate certificate; + private PrivateKey privateKey; + //These can be null because they are only used if this is a request. + private X509Fields x509Fields; + private PKCS10CertificationRequest certificationRequest; +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/roots/X509Fields.java b/jams-common/src/main/java/net/jami/jams/common/objects/roots/X509Fields.java new file mode 100644 index 0000000000000000000000000000000000000000..fc635c69b92fe1809b43ae070e420395c1883cae --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/roots/X509Fields.java @@ -0,0 +1,29 @@ +package net.jami.jams.common.objects.roots; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class X509Fields { + + private String commonName; + private String country; + private String state; + private String organization; + private String organizationUnit; + + public String getDN(){ + StringBuilder stringBuilder = new StringBuilder(); + //This makes no sense without a Common Name; + if(commonName != null) { + stringBuilder.append("CN=").append(commonName); + } else return null; + if(country != null) stringBuilder.append(",").append("C=").append(country); + if(state != null) stringBuilder.append(",").append("ST=").append(state); + if(organization != null) stringBuilder.append(",").append("O=").append(organization); + if(organizationUnit != null) stringBuilder.append(",").append("OU=").append(organizationUnit); + return stringBuilder.toString(); + } + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/system/SystemAccount.java b/jams-common/src/main/java/net/jami/jams/common/objects/system/SystemAccount.java new file mode 100644 index 0000000000000000000000000000000000000000..4764ec09a8f3b327da9805e458829ef50eb4a299 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/system/SystemAccount.java @@ -0,0 +1,12 @@ +package net.jami.jams.common.objects.system; + +import lombok.Getter; +import lombok.Setter; +import net.jami.jams.common.objects.roots.X509Entity; + +@Getter +@Setter +public class SystemAccount extends X509Entity { + + private SystemAccountType systemAccountType; +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/system/SystemAccountType.java b/jams-common/src/main/java/net/jami/jams/common/objects/system/SystemAccountType.java new file mode 100644 index 0000000000000000000000000000000000000000..b85bb07f298b66a2c07716d2da5c8b36c4014d05 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/system/SystemAccountType.java @@ -0,0 +1,6 @@ +package net.jami.jams.common.objects.system; + +public enum SystemAccountType { + CA, + OCSP +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/AccessLevel.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/AccessLevel.java new file mode 100644 index 0000000000000000000000000000000000000000..e579388f2cdaa952f248322bc132788fcf89fea3 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/AccessLevel.java @@ -0,0 +1,6 @@ +package net.jami.jams.common.objects.user; + +public enum AccessLevel { + ADMIN, + USER +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/User.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/User.java new file mode 100644 index 0000000000000000000000000000000000000000..09ec4da70e29031fcc9775f8b5c2e787b4d9a6f3 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/User.java @@ -0,0 +1,107 @@ +package net.jami.jams.common.objects.user; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import net.jami.jams.common.authentication.AuthenticationSourceType; +import net.jami.jams.common.objects.roots.BlockchainEntity; +import net.jami.jams.common.objects.roots.X509Entity; +import net.jami.jams.common.serialization.database.DatabaseObject; +import net.jami.jams.common.utils.X509Utils; + +import java.sql.PreparedStatement; +import java.sql.ResultSet; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class User extends X509Entity implements BlockchainEntity, DatabaseObject { + + private String username; + private String password; + private AuthenticationSourceType userType; + private String realm; //sort of the domain. + private AccessLevel accessLevel = AccessLevel.USER; + private Boolean needsPasswordReset = false; + + private String ethAddress; + private String ethKey; + private String jamiId; + + public User(ResultSet rs) throws Exception { + this.username = rs.getString("username"); + this.password = rs.getString("password"); + this.userType = AuthenticationSourceType.valueOf(rs.getString("userType")); + this.accessLevel = AccessLevel.valueOf(rs.getString("accessLevel")); + this.needsPasswordReset = Boolean.parseBoolean(rs.getString("needsPasswordReset")); + this.realm = rs.getString("realm"); + this.ethAddress = rs.getString("ethAddress"); + this.ethKey = rs.getString("ethKey"); + this.jamiId = rs.getString("jamiId"); + if(rs.getString("certificate") != null && !rs.getString("certificate").isBlank()) { + this.setCertificate(X509Utils.getCertificateFromPEMString(rs.getString("certificate"))); + } + if(rs.getString("privatekey") != null && !rs.getString("privatekey").isBlank()) { + this.setPrivateKey(X509Utils.getKeyFromPEMString(rs.getString("privatekey"))); + } + } + + @Override + public String getAddress() { + return this.ethAddress; + } + + @Override + public void setAddress(String address) { + this.ethAddress = address; + } + + @Override + public String getKey() { + return ethKey; + } + + @Override + public void setKey(String key) { + this.ethKey = key; + } + + @Override + public PreparedStatement getInsert(PreparedStatement ps) throws Exception { + ps.setString(1, username); + //We don't store the user's password if he is remote. + if(userType != null && userType.equals(AuthenticationSourceType.LOCAL)) ps.setString(2, password); + else ps.setString(2, null); + if(userType != null) ps.setString(3, userType.toString()); + else ps.setString(3, null); + ps.setString(4, realm); + ps.setString(5, ethAddress); + ps.setString(6, ethKey); + ps.setString(7, jamiId); + if (this.getCertificate() != null) { + ps.setString(8, X509Utils.getPEMStringFromCertificate(this.getCertificate())); + } else { + ps.setString(8, ""); + } + if (this.getPrivateKey() != null) { + ps.setString(9, X509Utils.getPEMStringFromPrivateKey(this.getPrivateKey())); + } else { + ps.setString(9, ""); + } + ps.setString(10,accessLevel.toString()); + ps.setString(11,needsPasswordReset.toString()); + return ps; + } + + @Override + public PreparedStatement getDelete(PreparedStatement ps) throws Exception { + return null; + } + + @Override + public PreparedStatement getUpdate(PreparedStatement ps) throws Exception { + return null; + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java new file mode 100644 index 0000000000000000000000000000000000000000..92ce57987b27a24d981edb5d1ccc8284b8bc8e0f --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/objects/user/UserProfile.java @@ -0,0 +1,14 @@ +package net.jami.jams.common.objects.user; + +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class UserProfile { + private String firstName; + private String lastName; + private String phoneNumber; + private String profilePicture; + private String email; +} diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/JsoniterRegistry.java b/jams-common/src/main/java/net/jami/jams/common/serialization/JsoniterRegistry.java new file mode 100644 index 0000000000000000000000000000000000000000..673c85c1b0e415b86a6d3b2a5c390640f8606ad1 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/serialization/JsoniterRegistry.java @@ -0,0 +1,31 @@ +package net.jami.jams.common.serialization; + +import com.jsoniter.JsonIterator; +import com.jsoniter.output.EncodingMode; +import com.jsoniter.output.JsonStream; +import com.jsoniter.spi.DecodingMode; +import com.jsoniter.spi.JsoniterSpi; +import net.jami.jams.common.serialization.decoders.CSRDecoder; +import net.jami.jams.common.serialization.decoders.PrivateKeyDecoder; +import net.jami.jams.common.serialization.decoders.X509CertificateDecoder; +import net.jami.jams.common.serialization.encoders.PrivateKeyEncoder; +import net.jami.jams.common.serialization.encoders.X509CertificateEncoder; +import org.bouncycastle.pkcs.PKCS10CertificationRequest; + +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +public class JsoniterRegistry { + + public static void initCodecs(){ + JsonStream.setMode(EncodingMode.DYNAMIC_MODE); + JsonIterator.setMode(DecodingMode.DYNAMIC_MODE_AND_MATCH_FIELD_WITH_HASH); + JsoniterSpi.registerTypeDecoder(X509Certificate.class,new X509CertificateDecoder()); + JsoniterSpi.registerTypeDecoder(PrivateKey.class,new PrivateKeyDecoder()); + JsoniterSpi.registerTypeEncoder(X509Certificate.class,new X509CertificateEncoder()); + JsoniterSpi.registerTypeEncoder(PrivateKey.class,new PrivateKeyEncoder()); + JsoniterSpi.registerTypeDecoder(PKCS10CertificationRequest.class,new CSRDecoder()); + } + + +} diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/database/DatabaseObject.java b/jams-common/src/main/java/net/jami/jams/common/serialization/database/DatabaseObject.java new file mode 100644 index 0000000000000000000000000000000000000000..2913b16051880305f6e1ce1696a14dcc01f6e43f --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/serialization/database/DatabaseObject.java @@ -0,0 +1,10 @@ +package net.jami.jams.common.serialization.database; + +import java.sql.PreparedStatement; + +public interface DatabaseObject { + + PreparedStatement getInsert(PreparedStatement ps) throws Exception; + PreparedStatement getDelete(PreparedStatement ps) throws Exception; + PreparedStatement getUpdate(PreparedStatement ps) throws Exception; +} diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/CSRDecoder.java b/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/CSRDecoder.java new file mode 100644 index 0000000000000000000000000000000000000000..fb0da96fca7849da56acd1a492a5b763c58c60b9 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/CSRDecoder.java @@ -0,0 +1,15 @@ +package net.jami.jams.common.serialization.decoders; + +import com.jsoniter.JsonIterator; +import com.jsoniter.spi.Decoder; +import net.jami.jams.common.utils.X509Utils; + +import java.io.IOException; + +public class CSRDecoder implements Decoder { + + @Override + public Object decode(JsonIterator jsonIterator) throws IOException { + return X509Utils.getCSRFromString(jsonIterator.readString()); + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/PrivateKeyDecoder.java b/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/PrivateKeyDecoder.java new file mode 100644 index 0000000000000000000000000000000000000000..dc021598b787fd892c0bd651c82a101e2338d960 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/PrivateKeyDecoder.java @@ -0,0 +1,15 @@ +package net.jami.jams.common.serialization.decoders; + +import com.jsoniter.JsonIterator; +import com.jsoniter.spi.Decoder; +import net.jami.jams.common.utils.X509Utils; + +import java.io.IOException; + +public class PrivateKeyDecoder implements Decoder { + + @Override + public Object decode(JsonIterator jsonIterator) throws IOException { + return X509Utils.getKeyFromPEMString(jsonIterator.readString()); + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/X509CertificateDecoder.java b/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/X509CertificateDecoder.java new file mode 100644 index 0000000000000000000000000000000000000000..d01919ac46df370d0d51fc0be8244485e4a86cfe --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/serialization/decoders/X509CertificateDecoder.java @@ -0,0 +1,15 @@ +package net.jami.jams.common.serialization.decoders; + +import com.jsoniter.JsonIterator; +import com.jsoniter.spi.Decoder; +import net.jami.jams.common.utils.X509Utils; + +import java.io.IOException; + +public class X509CertificateDecoder implements Decoder { + + @Override + public Object decode(JsonIterator jsonIterator) throws IOException { + return X509Utils.getCertificateFromPEMString(jsonIterator.readString()); + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/encoders/PrivateKeyEncoder.java b/jams-common/src/main/java/net/jami/jams/common/serialization/encoders/PrivateKeyEncoder.java new file mode 100644 index 0000000000000000000000000000000000000000..01231a4b844ebf2b70087b66e43afa43898ab881 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/serialization/encoders/PrivateKeyEncoder.java @@ -0,0 +1,16 @@ +package net.jami.jams.common.serialization.encoders; + +import com.jsoniter.output.JsonStream; +import com.jsoniter.spi.Encoder; +import net.jami.jams.common.utils.X509Utils; + +import java.io.IOException; +import java.security.PrivateKey; + +public class PrivateKeyEncoder implements Encoder { + + @Override + public void encode(Object o, JsonStream jsonStream) throws IOException { + jsonStream.write(("\"" + X509Utils.getPEMStringFromPrivateKey((PrivateKey) o) + "\"").getBytes()); + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/serialization/encoders/X509CertificateEncoder.java b/jams-common/src/main/java/net/jami/jams/common/serialization/encoders/X509CertificateEncoder.java new file mode 100644 index 0000000000000000000000000000000000000000..0c39ca54007361dbceb42bf217e68e04465a8a55 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/serialization/encoders/X509CertificateEncoder.java @@ -0,0 +1,16 @@ +package net.jami.jams.common.serialization.encoders; + +import com.jsoniter.output.JsonStream; +import com.jsoniter.spi.Encoder; +import net.jami.jams.common.utils.X509Utils; + +import java.io.IOException; +import java.security.cert.X509Certificate; + +public class X509CertificateEncoder implements Encoder { + + @Override + public void encode(Object o, JsonStream jsonStream) throws IOException { + jsonStream.write(("\"" + X509Utils.getPEMStringFromCertificate((X509Certificate) o) + "\"").getBytes()); + } +} diff --git a/jams-common/src/main/java/net/jami/jams/common/utils/X509Utils.java b/jams-common/src/main/java/net/jami/jams/common/utils/X509Utils.java new file mode 100644 index 0000000000000000000000000000000000000000..3eb5068a61730a30a33b636ff1a49b62fbba5ed5 --- /dev/null +++ b/jams-common/src/main/java/net/jami/jams/common/utils/X509Utils.java @@ -0,0 +1,96 @@ +package net.jami.jams.common.utils; + +import lombok.extern.slf4j.Slf4j; +import org.bouncycastle.asn1.pkcs.PrivateKeyInfo; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; +import org.bouncycastle.pkcs.PKCS10CertificationRequest; + +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.PrivateKey; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; + +@Slf4j +public class X509Utils { + + private static final String PVK_HEADER = "-----BEGIN PRIVATE KEY-----\n"; + private static final String PVK_TAIL = "\n-----END PRIVATE KEY-----"; + private static final String CERT_HEADER = "-----BEGIN CERTIFICATE-----\n"; + private static final String CERT_TAIL = "\n-----END CERTIFICATE-----"; + + public static PrivateKey getKeyFromPEMString(String keyString) { + try { + PEMParser parser = new PEMParser(new StringReader(keyString)); + Object parsedObject = parser.readObject(); + if (parsedObject instanceof PEMKeyPair) { + PEMKeyPair pk = (PEMKeyPair) parsedObject; + PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pk.getPrivateKeyInfo().getEncoded()); + return KeyFactory.getInstance("RSA").generatePrivate(keySpec); + } else { + JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); + return converter.getPrivateKey((PrivateKeyInfo) parsedObject); + } + } catch (Exception e) { + log.error("An error has occured trying to convert the PEM to PrivateKey, stack trace: " + e.toString()); + return null; + } + } + + public static X509Certificate getCertificateFromPEMString(String certificateString) { + try { + CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); + InputStream inputStream = new ByteArrayInputStream(certificateString.getBytes()); + return (X509Certificate) certificateFactory.generateCertificate(inputStream); + } catch (Exception e) { + log.error("An error has occured trying to convert the PEM to X509, stack trace: " + e.toString()); + return null; + } + } + + public static String getPEMStringFromPrivateKey(PrivateKey privateKey) { + StringBuilder stringBuilder = new StringBuilder(); + try { + stringBuilder.append(PVK_HEADER); + stringBuilder.append(Base64.getEncoder().encodeToString(privateKey.getEncoded())); + stringBuilder.append(PVK_TAIL); + return stringBuilder.toString(); + } catch (Exception e) { + log.error("An error has occured trying to convert the Private Key to PEM, stack trace: " + e.toString()); + return null; + } + } + + public static String getPEMStringFromCertificate(X509Certificate certificate) { + StringBuilder stringBuilder = new StringBuilder(); + try { + stringBuilder.append(CERT_HEADER); + stringBuilder.append(Base64.getEncoder().encodeToString(certificate.getEncoded())); + stringBuilder.append(CERT_TAIL); + return stringBuilder.toString(); + } catch (Exception e) { + log.error("An error has occured trying to convert the Private Key to PEM, stack trace: " + e.toString()); + return null; + } + } + + public static PKCS10CertificationRequest getCSRFromString(String pkcs10StringRequest) { + try { + ByteArrayInputStream pemStream = new ByteArrayInputStream(pkcs10StringRequest.getBytes(StandardCharsets.UTF_8)); + PEMParser pemParser = new PEMParser(new BufferedReader(new InputStreamReader(pemStream))); + Object parsedObj = pemParser.readObject(); + if (parsedObj instanceof PKCS10CertificationRequest) return (PKCS10CertificationRequest) parsedObj; + log.error("The request does not seem to be a CSR request!"); + return null; + } catch (Exception e) { + log.error("An error has occured trying to convert a string to a PKCS10 Certification Request, stack trace: " + e.toString()); + return null; + } + } + +} diff --git a/jams-common/src/test/java/net/jami/jams/common/serialization/JsoniterRegistryTest.java b/jams-common/src/test/java/net/jami/jams/common/serialization/JsoniterRegistryTest.java new file mode 100644 index 0000000000000000000000000000000000000000..a11bfb67c72f4eb8deeb91fe1b5e52d2a6a84abf --- /dev/null +++ b/jams-common/src/test/java/net/jami/jams/common/serialization/JsoniterRegistryTest.java @@ -0,0 +1,60 @@ +package net.jami.jams.common.serialization; + +import com.jsoniter.JsonIterator; +import com.jsoniter.output.JsonStream; +import net.jami.jams.common.objects.requests.EnrollementRequest; +import net.jami.jams.common.objects.roots.X509Entity; +import net.jami.jams.common.utils.X509Utils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.InputStream; + +import static org.junit.jupiter.api.Assertions.fail; + +class JsoniterRegistryTest { + + static String strPrivateKey; + static String strCertificate; + static String strPkcs10Request; + + @BeforeAll + public static void loadProps(){ + try { + JsoniterRegistry.initCodecs(); + InputStream path; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + path = classLoader.getResourceAsStream("cakey.txt"); + strPrivateKey = new String(path.readAllBytes()); + path = classLoader.getResourceAsStream("cacert.txt"); + strCertificate = new String(path.readAllBytes()); + path = classLoader.getResourceAsStream("pkcs10request.txt"); + strPkcs10Request = new String(path.readAllBytes()); + } + catch (Exception e){ + fail("Could not set-up resources for test!"); + } + } + + @Test + void X509SerializationAndDeserialization(){ + X509Entity entity = new X509Entity(); + entity.setCertificate(X509Utils.getCertificateFromPEMString(strCertificate)); + entity.setPrivateKey(X509Utils.getKeyFromPEMString(strPrivateKey)); + String x = JsonStream.serialize(entity); + Assertions.assertNotNull(x,"Serialization failed!"); + entity = null; + entity = JsonIterator.deserialize(x,X509Entity.class); + Assertions.assertNotNull(entity.getCertificate(),"Certificate was not parsed!"); + Assertions.assertNotNull(entity.getPrivateKey(),"Private key was not parsed!"); + } + + @Test + void CSRDeserializeTest(){ + String input = "{\"csr\":\"" + strPkcs10Request + "\"}"; + EnrollementRequest request = JsonIterator.deserialize(input,EnrollementRequest.class); + Assertions.assertNotNull(request.getCsr(),"CSR Should not have been null!"); + } + +} \ No newline at end of file diff --git a/jams-common/src/test/java/net/jami/jams/common/utils/X509UtilsTest.java b/jams-common/src/test/java/net/jami/jams/common/utils/X509UtilsTest.java new file mode 100644 index 0000000000000000000000000000000000000000..5e986a52d475e4df946403132eced09c998618db --- /dev/null +++ b/jams-common/src/test/java/net/jami/jams/common/utils/X509UtilsTest.java @@ -0,0 +1,83 @@ +package net.jami.jams.common.utils; + +import org.bouncycastle.pkcs.PKCS10CertificationRequest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.io.InputStream; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; + +import static org.junit.jupiter.api.Assertions.fail; + +class X509UtilsTest { + + static String strPrivateKey; + static String strCertificate; + static String strPkcs10Request; + + @BeforeAll + public static void loadProps(){ + try { + InputStream path; + ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + path = classLoader.getResourceAsStream("cakey.txt"); + strPrivateKey = new String(path.readAllBytes()); + path = classLoader.getResourceAsStream("cacert.txt"); + strCertificate = new String(path.readAllBytes()); + path = classLoader.getResourceAsStream("pkcs10request.txt"); + strPkcs10Request = new String(path.readAllBytes()); + } + catch (Exception e){ + fail("Could not set-up resources for test!"); + } + } + + @Test + void getKeyFromPEMString() { + PrivateKey privateKey; + privateKey = X509Utils.getKeyFromPEMString(strPrivateKey); + Assertions.assertNotNull(privateKey,"Could not decode a correctly formatted private key!"); + privateKey = X509Utils.getKeyFromPEMString(strPrivateKey.substring(10)); + Assertions.assertNull(privateKey,"Decoded a private key from an incorrect string!"); + } + + @Test + void getCertificateFromPEMString() { + X509Certificate x509Certificate; + x509Certificate = X509Utils.getCertificateFromPEMString(strCertificate); + Assertions.assertNotNull(x509Certificate,"Could not decoded a correctly formatted certificate!"); + x509Certificate = X509Utils.getCertificateFromPEMString(strCertificate.substring(25)); + Assertions.assertNull(x509Certificate,"Decoded certificate from an incorrect string!"); + } + + @Test + void getPEMStringFromPrivateKey() { + PrivateKey privateKey; + privateKey = X509Utils.getKeyFromPEMString(strPrivateKey); + Assertions.assertNotNull(privateKey,"Could not decode a correctly formatted private key!"); + String str = X509Utils.getPEMStringFromPrivateKey(privateKey); + PrivateKey privateKey1 = X509Utils.getKeyFromPEMString(str); + Assertions.assertEquals(privateKey,privateKey1,"Keys do not match - although they should!"); + } + + @Test + void getPEMStringFromCertificate() { + X509Certificate x509Certificate; + x509Certificate = X509Utils.getCertificateFromPEMString(strCertificate); + Assertions.assertNotNull(x509Certificate,"Could not decoded a correctly formatted certificate!"); + String str = X509Utils.getPEMStringFromCertificate(x509Certificate); + X509Certificate x509Certificate1 = X509Utils.getCertificateFromPEMString(str); + Assertions.assertEquals(x509Certificate,x509Certificate1,"Certificates do not match - although they should!"); + } + + @Test + void getCSRFromString() { + PKCS10CertificationRequest certificationRequest; + certificationRequest = X509Utils.getCSRFromString(strPkcs10Request); + Assertions.assertNotNull(certificationRequest,"Certification request should have been parsed correctly!"); + certificationRequest = X509Utils.getCSRFromString(strPkcs10Request.substring(23)); + Assertions.assertNull(certificationRequest,"Certification request should not have been parsed!"); + } +} \ No newline at end of file diff --git a/jams-server/src/main/java/net/jami/jams/server/Server.java b/jams-server/src/main/java/net/jami/jams/server/Server.java new file mode 100644 index 0000000000000000000000000000000000000000..ec8b11c87a3efeb112f93324ccb3bfe9967f2f14 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/Server.java @@ -0,0 +1,63 @@ +package net.jami.jams.server; + +import lombok.extern.slf4j.Slf4j; +import net.jami.datastore.main.DataStore; +import net.jami.jams.common.authentication.AuthenticationSource; +import net.jami.jams.common.cryptoengineapi.CertificateAuthority; +import net.jami.jams.common.serialization.JsoniterRegistry; +import net.jami.jams.server.core.TomcatLauncher; +import net.jami.jams.server.core.usermanagement.UserAuthenticationModule; +import net.jami.jams.server.startup.CryptoEngineLoader; +import net.jami.jams.server.startup.LibraryLoader; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; + +@Slf4j +public class Server { + + public static List<String> authModuleClasses = new ArrayList<>(); + public static HashMap<String, AuthenticationSource> authenticationSources = new HashMap<>(); + public static AtomicBoolean isInstalled = new AtomicBoolean(false); + + static { + JsoniterRegistry.initCodecs(); + } + + public static DataStore dataStore; + //This one gets loaded via JAR, to make it more flexible. + public static CertificateAuthority certificateAuthority; + public static UserAuthenticationModule userAuthenticationModule; + + public static void main(String[] args){ + //Pre-load the libraries we should pre-load. + LibraryLoader.loadlibs("libs"); + //Step 1: Create the data store. + dataStore = new DataStore("jdbc:derby:jams;create=true"); + userAuthenticationModule = new UserAuthenticationModule(); + //Test block + //Step 2: if the server is initialized, + certificateAuthority = CryptoEngineLoader.loadCryptoEngine(dataStore); + //nasty injection to test this flow. + + + //Okay this is me cheating again heavily, just for testing purposes. + //try { + // InputStream path; + // ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); + // path = classLoader.getResourceAsStream("ldapconfig.json"); + // userAuthenticationModule.attachLDAPAuthSource(new String(path.readAllBytes())); + //} + //catch (Exception e){ + // log.error("Could not load and inject active directory connector with error: " + e.toString()); + //} + + //Start tomcat. + TomcatLauncher tomcatLauncher = new TomcatLauncher(); + tomcatLauncher.startServer(); + } + + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/core/TomcatConnectorFactory.java b/jams-server/src/main/java/net/jami/jams/server/core/TomcatConnectorFactory.java new file mode 100644 index 0000000000000000000000000000000000000000..578ebead2ead816f6f2579eec8a99d0d3fd47edb --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/core/TomcatConnectorFactory.java @@ -0,0 +1,56 @@ +package net.jami.jams.server.core; + +import lombok.extern.slf4j.Slf4j; +import org.apache.catalina.connector.Connector; + +import java.io.File; + +@Slf4j +public class TomcatConnectorFactory { + + public static Connector getSSLConnectorWithTrustStore(String certificateFile, String keyFile, int port) { + Connector connector = new Connector(); + connector.setPort(port); + connector.setSecure(true); + connector.setScheme("https"); + connector.setAttribute("protocol", "org.apache.coyote.http11.Http11NioProtocol"); + connector.setAttribute("SSLCertificateFile", System.getProperty("user.dir") + File.separator + certificateFile); + connector.setAttribute("SSLCertificateKeyFile", System.getProperty("user.dir") + File.separator + keyFile); + connector.setAttribute("truststoreFile",System.getProperty("user.dir") + File.separator + "keystore.jks"); + connector.setAttribute("clientAuth","optional"); + connector.setAttribute("truststorePassword","changeit"); + connector.setAttribute("protocol", "HTTP/1.1"); + connector.setAttribute("sslProtocol", "TLSv1.3"); + connector.setAttribute("maxThreads", "200"); + connector.setAttribute("SSLEnabled", "true"); + return connector; + } + + public static Connector getSSLConnectorWithoutTrustStore(String certificateFile, String keyFile, int port) { + Connector connector = new Connector(); + connector.setPort(port); + connector.setSecure(true); + connector.setScheme("https"); + connector.setAttribute("protocol", "org.apache.coyote.http11.Http11NioProtocol"); + connector.setAttribute("SSLCertificateFile", System.getProperty("user.dir") + File.separator + certificateFile); + connector.setAttribute("SSLCertificateKeyFile", System.getProperty("user.dir") + File.separator + keyFile); + connector.setAttribute("protocol", "HTTP/1.1"); + connector.setAttribute("sslProtocol", "TLSv1.3"); + connector.setAttribute("maxThreads", "200"); + connector.setAttribute("SSLEnabled", "true"); + return connector; + } + + + public static Connector getNoSSLConnector(int port) { + Connector connector = new Connector(); + connector.setPort(port); + connector.setScheme("http"); + connector.setAttribute("protocol", "org.apache.coyote.http11.Http11NioProtocol"); + connector.setAttribute("protocol", "HTTP/1.1"); + connector.setAttribute("maxThreads", "200"); + return connector; + } + + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java b/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java new file mode 100644 index 0000000000000000000000000000000000000000..5b713d97595b403612041975c0e400000a7d3f1c --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/core/TomcatLauncher.java @@ -0,0 +1,57 @@ +package net.jami.jams.server.core; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.server.Server; +import org.apache.catalina.WebResourceRoot; +import org.apache.catalina.core.StandardContext; +import org.apache.catalina.startup.Tomcat; +import org.apache.catalina.webresources.DirResourceSet; +import org.apache.catalina.webresources.JarResourceSet; +import org.apache.catalina.webresources.StandardRoot; + +import java.io.File; +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; + +//This class boots the tomcat server which provides the subsystem +//for the API calls. +@Slf4j +public class TomcatLauncher { + + private static final Tomcat tomcat = new Tomcat(); + private static StandardContext context; + + public void startServer() { + tomcat.getService().addConnector(TomcatConnectorFactory.getNoSSLConnector(8080)); + String jarName = URLDecoder.decode(new File(Server.class.getProtectionDomain().getCodeSource().getLocation().getPath()).getAbsolutePath(), StandardCharsets.UTF_8); + log.info("JAR Resource File = " + jarName); + context = (StandardContext) tomcat.addWebapp("", new File(System.getProperty("user.dir")).getAbsolutePath()); + WebResourceRoot resources = new StandardRoot(context); + if (jarName.contains(".jar")) { + resources.addPreResources(new JarResourceSet(resources, "/WEB-INF/classes", jarName, "/net/jami/jams/server/servlets")); + resources.addPreResources(new JarResourceSet(resources, "/", jarName, "/webapp")); + } + else { + log.info("WARNING: You are running from your local filesystem, this makes sense only for developers!"); + StringBuilder basePath = new StringBuilder(); + String[] paths = System.getProperty("user.dir").split("/"); + for(int i=0; i < paths.length-1; i++){ + basePath.append("/").append(paths[i]); + } + basePath.append("/jams-server"); + resources.addPreResources(new DirResourceSet(resources, "/WEB-INF/classes", basePath.toString() + "/target/classes/net/jami/jams/server/servlets", "/")); + resources.addPreResources(new DirResourceSet(resources, "/", basePath.toString() + "/target/classes", "/webapp")); + + } + context.setResources(resources); + try { + tomcat.start(); + tomcat.getServer().await(); + } catch + (Exception e) { + log.error("Web-server has failed to start - this is critical!"); + } + } + + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/core/usermanagement/AuthModuleKey.java b/jams-server/src/main/java/net/jami/jams/server/core/usermanagement/AuthModuleKey.java new file mode 100644 index 0000000000000000000000000000000000000000..0a74cbcb2f6b98ff058e510ffe9216f13572b571 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/core/usermanagement/AuthModuleKey.java @@ -0,0 +1,16 @@ +package net.jami.jams.server.core.usermanagement; + +import lombok.*; +import net.jami.jams.common.authentication.AuthenticationSourceType; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@EqualsAndHashCode +public class AuthModuleKey { + + private String realm; + private AuthenticationSourceType type; + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/core/usermanagement/UserAuthenticationModule.java b/jams-server/src/main/java/net/jami/jams/server/core/usermanagement/UserAuthenticationModule.java new file mode 100644 index 0000000000000000000000000000000000000000..5cb1816f54e838e6a99b4c902a1466d385b5588f --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/core/usermanagement/UserAuthenticationModule.java @@ -0,0 +1,83 @@ +package net.jami.jams.server.core.usermanagement; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.authentication.AuthenticationSource; +import net.jami.jams.common.authentication.AuthenticationSourceType; +import net.jami.jams.common.dao.StatementElement; +import net.jami.jams.common.dao.StatementList; +import net.jami.jams.common.objects.user.User; +import net.jami.jams.server.startup.LibraryLoader; + +import java.util.HashMap; + +import static net.jami.jams.server.Server.dataStore; + +@Slf4j +public class UserAuthenticationModule { + //This contains the DOMAIN-SOURCE. + //In general there is at most 2 here. + private final HashMap<AuthModuleKey, AuthenticationSource> authenticationSources = new HashMap<>(); + + //We should initialize the authentication sources here, based on configurations, by default, + //we only attach the basic one. + public UserAuthenticationModule() { + authenticationSources.put(new AuthModuleKey("LOCAL", AuthenticationSourceType.LOCAL), dataStore); + } + + public void attachAuthSource(String realm, AuthenticationSourceType type, AuthenticationSource source){ + authenticationSources.put(new AuthModuleKey(realm,type), source); + } + + public void attachLDAPAuthSource(String settings){ + try { + Class<?> cls = LibraryLoader.classLoader.loadClass("net.jami.jams.ldap.connector.LDAPConnector"); + AuthenticationSource source = (AuthenticationSource) cls.getConstructor(String.class).newInstance(settings); + authenticationSources.put(new AuthModuleKey(source.getInfo().getRealm(),source.getInfo().getAuthenticationSourceType()), source); + } + catch (Exception e){ + log.error("Could not load an ldap directory connector with reason: " + e.toString()); + } + } + + //Repeat the same technique for the LDAP connector. + public void attachADAuthSource(String settings){ + try { + Class<?> cls = LibraryLoader.classLoader.loadClass("net.jami.jams.ad.connector.ADConnector"); + AuthenticationSource source = (AuthenticationSource) cls.getConstructor(String.class).newInstance(settings); + authenticationSources.put(new AuthModuleKey(source.getInfo().getRealm(),source.getInfo().getAuthenticationSourceType()), source); + } + catch (Exception e){ + log.error("Could not load an active directory connector with reason: " + e.toString()); + } + } + + public boolean authenticateUser(String username, String password){ + //The user could already exist, so we pull him up. + if(dataStore.userExists(username)){ + StatementList statementList = new StatementList(); + StatementElement statementElement = new StatementElement("username","=",username,""); + statementList.addStatement(statementElement); + User user = dataStore.getUserDao().getObjects(statementList).get(0); + return authenticationSources.get(new AuthModuleKey(user.getRealm(),user.getUserType())) + .authenticate(username,password); + } + //The second case is much more violent, because we don't know in advance "where" this user comes + //from, so we have to infer (this is only really true for "users", all others are usually pre-marked) + //This is also the case when we store the user into the DAO - because he never existed before. + for(AuthModuleKey key : authenticationSources.keySet()){ + if(authenticationSources.get(key).authenticate(username,password)){ + User user = new User(); + user.setUsername(username); + user.setRealm(key.getRealm()); + user.setUserType(key.getType()); + if(key.getType().equals(AuthenticationSourceType.LOCAL)) user.setPassword(password); + //TODO: We need a function here which generates the user (obvious workflow). + //The user never existed, obviously. + dataStore.getUserDao().storeObject(user); + return true; + } + } + return false; + } + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/core/utils/ResponseBuilder.java b/jams-server/src/main/java/net/jami/jams/server/core/utils/ResponseBuilder.java new file mode 100644 index 0000000000000000000000000000000000000000..498e32cd17692cc31dd24746d578eb5e57479df9 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/core/utils/ResponseBuilder.java @@ -0,0 +1,13 @@ +package net.jami.jams.server.core.utils; + +import jakarta.servlet.http.HttpServletResponse; + +public class ResponseBuilder { + + public void buildResponse(Integer status, String body, HttpServletResponse resp) throws Exception{ + resp.setStatus(status); + resp.getOutputStream().write(body.getBytes()); + } + + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java new file mode 100644 index 0000000000000000000000000000000000000000..13f2518c961884235a31c543167e09cc2c47ddcd --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterDeviceFlow.java @@ -0,0 +1,14 @@ +package net.jami.jams.server.core.workflows; + +public class RegisterDeviceFlow { + + public static void registerDevice(){ + //Build "Device" from the JSON that came in. + //Check if user exists + //if does not exist -> create user -> confirm created + //create device + } + + + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterUserFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterUserFlow.java new file mode 100644 index 0000000000000000000000000000000000000000..03bca2f28df33e5ce7e6dee11001d020efede204 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RegisterUserFlow.java @@ -0,0 +1,24 @@ +package net.jami.jams.server.core.workflows; + +import net.jami.jams.common.objects.user.User; + +import static net.jami.jams.server.Server.certificateAuthority; + +public class RegisterUserFlow { + + //Get the CA, sign, return the Jami ID. + public static User createUser(User user){ + user = certificateAuthority.getSignedCertificate(user); + //while(ethKeyPair == null){ + // ethKeyPair = ETHAddressGenerator.generateAddress(); + //} + //user.setEthAddress(ethKeyPair[0]); + //user.setEthKey(ethKeyPair[1]); + //user.setJamiId(Hex.encodeHexString(MessageDigest.getInstance(MessageDigestAlgorithms.SHA_1).digest(userKeyPair.getPublic().getEncoded()))); + //user.setEthAddress(); + //user.setJamiId(); + return user; + } + + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java new file mode 100644 index 0000000000000000000000000000000000000000..ea433ee5ed2420848a78507ddfa44a0ac6c5733e --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeDeviceFlow.java @@ -0,0 +1,4 @@ +package net.jami.jams.server.core.workflows; + +public class RevokeDeviceFlow { +} diff --git a/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeUserFlow.java b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeUserFlow.java new file mode 100644 index 0000000000000000000000000000000000000000..59bc146717729cd36b853be788145030dad0a9ad --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/core/workflows/RevokeUserFlow.java @@ -0,0 +1,4 @@ +package net.jami.jams.server.core.workflows; + +public class RevokeUserFlow { +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/DeviceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/DeviceServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..d5e15ce4e2ddf7957d3c3b25e158a1eaad4efe68 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/DeviceServlet.java @@ -0,0 +1,32 @@ +package net.jami.jams.server.servlets.api.auth; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +@WebServlet("/api/auth/device") +public class DeviceServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.getOutputStream().print("Hello World!"); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + } + + @Override + protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doPut(req, resp); + } + + @Override + protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doDelete(req, resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/DevicesServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/DevicesServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..fcab7cc56cbbfa5cce6036281ee9ebbe715a1676 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/auth/DevicesServlet.java @@ -0,0 +1,19 @@ +package net.jami.jams.server.servlets.api.auth; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +@WebServlet("/api/auth/devices") +public class DevicesServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doGet(req, resp); + } + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/contacts/ContactServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/contacts/ContactServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..880e92879701bbc97f7e28f9eccdd81156f0ea67 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/contacts/ContactServlet.java @@ -0,0 +1,28 @@ +package net.jami.jams.server.servlets.api.contacts; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +@WebServlet("/api/contacts") +public class ContactServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doGet(req, resp); + } + + @Override + protected void doPut(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doPut(req, resp); + } + + @Override + protected void doDelete(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doDelete(req, resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/api/directory/SearchDirectoryServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/api/directory/SearchDirectoryServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..099a787011307bfe390505fd64351cde5f7040e6 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/api/directory/SearchDirectoryServlet.java @@ -0,0 +1,18 @@ +package net.jami.jams.server.servlets.api.directory; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +@WebServlet("/api/directory/search") +public class SearchDirectoryServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doGet(req, resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/APIFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/APIFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..956495e1edceb91d9f9907c0e4cdfc6c5cf1a25f --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/APIFilter.java @@ -0,0 +1,48 @@ +package net.jami.jams.server.servlets.filters; + +import jakarta.servlet.*; +import jakarta.servlet.annotation.WebFilter; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.objects.user.User; + +import java.io.IOException; +import java.util.concurrent.ConcurrentHashMap; + +import static net.jami.jams.server.Server.userAuthenticationModule; + +@WebFilter(urlPatterns = {"/api/*"}) +@Slf4j +public class APIFilter implements Filter { + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + boolean authsuccess = false; + /* + The API endpoint has only 2 possible states: auth with basic Auth, and auth with SSL. + I left the possibility of working with HTTP sessions if someday, this takes place. + We should keep in mind, that we will NEVER need to create a user if an SSL certificate has + been presented. + */ + //First try with SSL + if(Decoders.decodeSSLAuth(request) != null){ + //From the certificate, we can pull up the deviceId, and find the owner. + authsuccess = true; + } + else if(Decoders.decodeAuthHeader(request) != null){ + String[] authCredentials = Decoders.decodeAuthHeader(request); + if(authCredentials != null && authCredentials.length == 2) { + if(userAuthenticationModule.authenticateUser(authCredentials[0],authCredentials[1])){ + log.info("User " + authCredentials[0] + " has succesfully logged in!"); + authsuccess = true; + } + } + } + if(authsuccess) filterChain.doFilter(servletRequest,servletResponse); + else response.setStatus(403); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/Decoders.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/Decoders.java new file mode 100644 index 0000000000000000000000000000000000000000..302996a39edc5deaeb877e9d864c6abf57e0bbc9 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/Decoders.java @@ -0,0 +1,39 @@ +package net.jami.jams.server.servlets.filters; + +import jakarta.servlet.http.HttpServletRequest; + +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public class Decoders { + + public static String[] decodeAuthHeader(HttpServletRequest request){ + if(request.getHeader("authorization") != null){ + return decodeAuthHeader(request.getHeader("authorization")); + } + else return null; + } + + private static String[] decodeAuthHeader(String authorization){ + if (authorization != null && authorization.toLowerCase().startsWith("basic")) { + // Authorization: Basic base64credentials + String base64Credentials = authorization.substring("Basic".length()).trim(); + byte[] credDecoded = Base64.getDecoder().decode(base64Credentials); + //TODO: change this to char array + String credentials = new String(credDecoded, StandardCharsets.UTF_8); + // credentials = username:password + return credentials.split(":", 2); + } + return null; + } + + //This just return whatever we need in the header. + public static String decodeSSLAuth(HttpServletRequest request){ + if(request.getAttribute("jakarta.servlet.request.X509Certificate") != null){ + return null; + } + return null; + } + + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/filters/WebAppFilter.java b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/WebAppFilter.java new file mode 100644 index 0000000000000000000000000000000000000000..da7300b5f9064484b4141133c6b80c11a68c4443 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/filters/WebAppFilter.java @@ -0,0 +1,37 @@ +package net.jami.jams.server.servlets.filters; + +import jakarta.servlet.*; +import jakarta.servlet.annotation.WebFilter; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import jakarta.servlet.http.HttpSession; +import net.jami.jams.common.objects.user.User; + +import java.io.IOException; +import java.util.concurrent.ConcurrentHashMap; + +/* + + */ +@WebFilter("/web/*") +public class WebAppFilter implements Filter { + + public static final ConcurrentHashMap<HttpSession, User> authenticatedUsers = new ConcurrentHashMap<>(); + + @Override + public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { + HttpServletRequest request = (HttpServletRequest) servletRequest; + HttpServletResponse response = (HttpServletResponse) servletResponse; + boolean authsuccess = authenticatedUsers.containsKey(request.getSession()); + + if(authsuccess){ + filterChain.doFilter(request,response); + } + if(!authsuccess && request.getServletPath().contains("login")){ + filterChain.doFilter(request,response); + } + else{ + response.setStatus(403); + } + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/general/ServerStatusServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/general/ServerStatusServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..ba5f1fa8dda7b27e7cc76b439c2970f97b35d142 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/general/ServerStatusServlet.java @@ -0,0 +1,19 @@ +package net.jami.jams.server.servlets.general; + + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +@WebServlet("/api/info") +public class ServerStatusServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + resp.getOutputStream().print("OK"); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/web/IndexServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/web/IndexServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..ec287893c188cc39192eaf89993972212624efeb --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/web/IndexServlet.java @@ -0,0 +1,24 @@ +package net.jami.jams.server.servlets.web; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.server.Server; + +import java.io.IOException; + +@Slf4j +@WebServlet("/web/index") +public class IndexServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + req.setAttribute("test","AB"); + if(Server.isInstalled.get()) req.getRequestDispatcher("login.jsp").forward(req,resp); + else req.getRequestDispatcher("register.jsp").forward(req,resp); + } + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/web/LoginServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/web/LoginServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..3486b21421547bac6d08a19cf79b61b59e57f25f --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/web/LoginServlet.java @@ -0,0 +1,21 @@ +package net.jami.jams.server.servlets.web; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import net.jami.jams.server.Server; +import net.jami.jams.server.servlets.filters.WebAppFilter; + +import java.io.IOException; + +@WebServlet("/web/login") +public class LoginServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + if(Server.isInstalled.get()) req.getRequestDispatcher("/login.jsp").forward(req,resp); + else req.getRequestDispatcher("/register.jsp").forward(req,resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateAdminAccountServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateAdminAccountServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..c3dbeb6bd71ba9220d7c10caa1a52a226a347721 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateAdminAccountServlet.java @@ -0,0 +1,18 @@ +package net.jami.jams.server.servlets.web.install; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +@WebServlet("/web/install/admin") +public class CreateAdminAccountServlet extends HttpServlet { + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doPost(req, resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateAuthSourceServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateAuthSourceServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..061af70b91e017850924e35f7791d4f3f66472d0 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateAuthSourceServlet.java @@ -0,0 +1,23 @@ +package net.jami.jams.server.servlets.web.install; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +@WebServlet("/web/install/auth") +public class CreateAuthSourceServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doGet(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doPost(req, resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateCAServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateCAServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..54bc13937ca97e4d8adc18c02c9b248e138b9d52 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateCAServlet.java @@ -0,0 +1,23 @@ +package net.jami.jams.server.servlets.web.install; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +@WebServlet("/web/install/ca") +public class CreateCAServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doGet(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doPost(req, resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateGeneralSettingsServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateGeneralSettingsServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..519aa0d25263e3b298aa13a7f9b732feec63ad44 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/web/install/CreateGeneralSettingsServlet.java @@ -0,0 +1,23 @@ +package net.jami.jams.server.servlets.web.install; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +@WebServlet("/web/install/settings") +public class CreateGeneralSettingsServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doGet(req, resp); + } + + @Override + protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + super.doPost(req, resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/x509/CRLServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/x509/CRLServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..92f2f116f86ab4aca86428a277b87df3447074ba --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/x509/CRLServlet.java @@ -0,0 +1,22 @@ +package net.jami.jams.server.servlets.x509; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +import static net.jami.jams.server.Server.certificateAuthority; + + +@WebServlet("/api/auth/crl") +public class CRLServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + certificateAuthority.getLatestCRL(); + super.doGet(req, resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/servlets/x509/OCSPServlet.java b/jams-server/src/main/java/net/jami/jams/server/servlets/x509/OCSPServlet.java new file mode 100644 index 0000000000000000000000000000000000000000..2cfd39a2b963cabfb5bb58157a7d9cfece8d70d8 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/servlets/x509/OCSPServlet.java @@ -0,0 +1,21 @@ +package net.jami.jams.server.servlets.x509; + +import jakarta.servlet.ServletException; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import java.io.IOException; + +import static net.jami.jams.server.Server.certificateAuthority; + +@WebServlet("/api/auth/ocsp") +public class OCSPServlet extends HttpServlet { + + @Override + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + certificateAuthority.getOCSPResponse(null); + super.doGet(req, resp); + } +} diff --git a/jams-server/src/main/java/net/jami/jams/server/startup/CryptoEngineLoader.java b/jams-server/src/main/java/net/jami/jams/server/startup/CryptoEngineLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..ac62227229016efba8666f04f09153316081f546 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/startup/CryptoEngineLoader.java @@ -0,0 +1,46 @@ +package net.jami.jams.server.startup; + +import lombok.extern.slf4j.Slf4j; +import net.jami.datastore.main.DataStore; +import net.jami.jams.common.cryptoengineapi.CertificateAuthority; +import net.jami.jams.common.dao.StatementElement; +import net.jami.jams.common.dao.StatementList; +import net.jami.jams.common.objects.system.SystemAccount; +import net.jami.jams.server.Server; + +import java.util.List; + +@Slf4j +public class CryptoEngineLoader { + + public static CertificateAuthority loadCryptoEngine(DataStore dataStore){ + try { + Class<?> cls = LibraryLoader.classLoader.loadClass("net.jami.jams.cryptoengine.CryptoEngine"); + CertificateAuthority certificateAuthority = (CertificateAuthority) cls.getConstructor().newInstance(); + StatementList statementList = new StatementList(); + statementList.addStatement(new StatementElement("entity","=","CA","")); + List<SystemAccount> accounts = dataStore.getSystemDao().getObjects(statementList); + if(accounts.size() == 0){ + log.info("This is an fresh install, and it has no CA or any system accounts!"); + Server.isInstalled.set(false); + } + else{ + statementList = new StatementList(); + statementList.addStatement(new StatementElement("entity","=","CA","")); + SystemAccount caAccount = dataStore.getSystemDao().getObjects(statementList).get(0); + statementList = new StatementList(); + statementList.addStatement(new StatementElement("entity","=","OCSP","")); + SystemAccount ocspAccount = dataStore.getSystemDao().getObjects(statementList).get(0); + log.info("Injecting OCSP and CA acocunts..."); + certificateAuthority.init("domain.com","SHA512WITHRSA",caAccount,ocspAccount); + } + log.info("Loaded X509 Engine - please make sure it is initialized before using it to sign requests!"); + return certificateAuthority; + } + catch (Exception e){ + log.error("Unable to load X509 Engine!"); + return null; + } + } + +} diff --git a/jams-server/src/main/java/net/jami/jams/server/startup/LibraryLoader.java b/jams-server/src/main/java/net/jami/jams/server/startup/LibraryLoader.java new file mode 100644 index 0000000000000000000000000000000000000000..83338c4187193b8301ace763e13e63331e881291 --- /dev/null +++ b/jams-server/src/main/java/net/jami/jams/server/startup/LibraryLoader.java @@ -0,0 +1,37 @@ +package net.jami.jams.server.startup; + +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.server.Server; +import org.apache.xbean.classloader.JarFileClassLoader; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; + +@Slf4j +public class LibraryLoader { + + public static JarFileClassLoader classLoader; + + public static void loadlibs(String libDir){ + try { + File dependencyDirectory = new File(libDir); + File[] files = dependencyDirectory.listFiles(); + ArrayList<URL> urls = new ArrayList<>(); + assert files != null; + for (int i = 0; i < files.length; i++) { + if (files[i].getName().endsWith(".jar")) { + urls.add(files[i].toURI().toURL()); + log.info("Successfully loaded the library " + files[i]); + } + } + classLoader = new JarFileClassLoader("Scheduler CL" + System.currentTimeMillis(), urls.toArray(new URL[urls.size()]), Server.class.getClassLoader()); + } + catch (Exception e){ + log.error("Errors occured while trying to load libraries..."); + } + } + + + +} diff --git a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java new file mode 100644 index 0000000000000000000000000000000000000000..3e646a51460524fc21374503ce73fbc8790ba2cd --- /dev/null +++ b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/LDAPConnector.java @@ -0,0 +1,68 @@ +package net.jami.jams.ldap.connector; + +import com.jsoniter.JsonIterator; +import lombok.extern.slf4j.Slf4j; +import net.jami.jams.common.authentication.AuthenticationSource; +import net.jami.jams.common.authentication.AuthenticationSourceInfo; +import net.jami.jams.common.authentication.AuthenticationSourceType; +import net.jami.jams.common.authentication.ldap.LDAPSettings; +import net.jami.jams.common.objects.user.User; +import net.jami.jams.common.objects.user.UserProfile; +import net.jami.jams.common.serialization.JsoniterRegistry; +import net.jami.jams.ldap.connector.service.AuthenticationService; +import org.ldaptive.BindConnectionInitializer; +import org.ldaptive.ConnectionConfig; +import org.ldaptive.Credential; +import org.ldaptive.DefaultConnectionFactory; + +@Slf4j +public class LDAPConnector implements AuthenticationSource { + + private AuthenticationService authenticationService; + public static LDAPSettings settings; + + public LDAPConnector(String strSettings) { + JsoniterRegistry.initCodecs(); + LDAPConnector.settings = JsonIterator.deserialize(strSettings,LDAPSettings.class); + BindConnectionInitializer bindConnectionInitializer = new BindConnectionInitializer(); + bindConnectionInitializer.setBindDn(settings.getUsername()); + bindConnectionInitializer.setBindCredential(new Credential(settings.getPassword())); + ConnectionConfig connConfig = ConnectionConfig.builder() + .url(settings.getHost()) + .useStartTLS(settings.getUseStartTLS()) + .connectionInitializers(bindConnectionInitializer) + .build(); + authenticationService = new AuthenticationService(new DefaultConnectionFactory(connConfig)); + log.info("Started LDAP Connector!"); + } + + @Override + public boolean createUser(User user) { + return false; + } + + @Override + public UserProfile getUserProfile(String username) { + return null; + } + + @Override + public boolean authenticate(String username, String password) { + return authenticationService.authenticateUser(username,password); + } + + @Override + public AuthenticationSourceInfo getInfo() { + return new AuthenticationSourceInfo("savoirfairelinux", AuthenticationSourceType.LDAP); + } + + @Override + public boolean testConfiguration(String configuration) { + return false; + } + + @Override + public boolean updatePassword(User user, String password) { + return false; + } +} diff --git a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/AuthenticationService.java b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/AuthenticationService.java new file mode 100644 index 0000000000000000000000000000000000000000..90b07eec2aa19b78f0e525f45a7f998dd3908afe --- /dev/null +++ b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/AuthenticationService.java @@ -0,0 +1,38 @@ +package net.jami.jams.ldap.connector.service; + +import lombok.extern.slf4j.Slf4j; +import org.ldaptive.ConnectionFactory; +import org.ldaptive.Credential; +import org.ldaptive.auth.*; + +import static net.jami.jams.ldap.connector.LDAPConnector.settings; + +@Slf4j +public class AuthenticationService { + + private final ConnectionFactory connectionFactory; + + public AuthenticationService(ConnectionFactory connectionFactory) { + this.connectionFactory = connectionFactory; + } + + public boolean authenticateUser(String username, String password) { + try { + FormatDnResolver dnResolver = new FormatDnResolver(); + dnResolver.setFormat(settings.getUsernameField() + "," + settings.getBaseDN()); + SimpleBindAuthenticationHandler bindAuthenticationHandler = new SimpleBindAuthenticationHandler(connectionFactory); + Authenticator auth = new Authenticator(); + auth.setDnResolver(dnResolver); + auth.setAuthenticationHandler(bindAuthenticationHandler); + AuthenticationResponse resp = auth.authenticate(new AuthenticationRequest(username, new Credential(password))); + log.info("User " + username + " has tried to authenticate with result: " + resp.getAuthenticationResultCode()); + return resp.isSuccess(); + } + catch (Exception e){ + log.info("An exception has occured trying to process and authentication request: " + e.toString()); + return false; + } + } + + +} diff --git a/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java new file mode 100644 index 0000000000000000000000000000000000000000..0fa7b5472464117406ecf75883b3e710a76c1293 --- /dev/null +++ b/ldap-connector/src/main/java/net/jami/jams/ldap/connector/service/UserProfileService.java @@ -0,0 +1,4 @@ +package net.jami.jams.ldap.connector.service; + +public class UserProfileService { +}