Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

chat.dim:Plugins

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

chat.dim:Plugins

Extensions for cryptography and user identity

  • 1.1.0
  • Source
  • Maven
  • Socket score

Version published
Maintainers
1
Source

Decentralized Instant Messaging (Java SDK)

license Version PRs Welcome Platform

Dependencies

build.gradle

allprojects {
    repositories {
        jcenter()
        mavenCentral()
    }
}

dependencies {
    // https://bintray.com/dimchat/common/sdk
    implementation group: 'chat.dim', name: 'SDK', version: '0.2.2'
}

pom.xml

<dependencies>

    <!-- https://mvnrepository.com/artifact/chat.dim/SDK -->
    <dependency>
        <groupId>chat.dim</groupId>
        <artifactId>SDK</artifactId>
        <version>0.2.2</version>
        <type>pom</type>
    </dependency>

</dependencies>

Account

User private key, ID, meta, and profile are generated in client, and broadcast only meta & profile onto DIM station.

Register User Account

Step 1. generate private key (with asymmetric algorithm)

PrivateKey privateKey = PrivateKey.generate(PrivateKey.RSA);

NOTICE: After registered, the client should save the private key in secret storage.

Step 2. generate meta with private key (and meta seed)

String seed = "username";
Meta meta = Meta.generate(MetaType.Default, privateKey, seed);

Step 3. generate ID with meta (and network type)

ID identifier = meta.generateID(NetworkType.Main);

Create and upload User Profile

Step 4. create profile with ID and sign with private key

UserProfile profile = new UserProfile(identifier);
// set nickname and avatar URL
profile.setName("Albert Moky");
profile.setAvatar("https://secure.gravatar.com/avatar/34aa0026f924d017dcc7a771f495c086");
// sign
profile.sign(privateKey);

Step 5. send meta & profile to station

Messenger messenger = Messenger.getInstance();

Command cmd = new ProfileCommand(identifier, meta, profile);
messenger.sendCommand(cmd);

The profile should be sent to station after connected and handshake accepted, details are provided in later chapters

Connect and Handshake

Step 1. connect to DIM station (TCP)

Step 2. prepare for receiving message data package

public void onReceive(byte[] responseData) {
    byte[] response = messenger.onReceivePackage(responseData);
    if (response != null && response.length > 0) {
        // send processing result back to the station
        send(response);
    }
}

Step 3. send first handshake command

(1) create handshake command

// first handshake will have no session key
String sessionKey = null;
Command cmd = new HandshakeCommand(sessionKey);

(2) pack, encrypt and sign

InstantMessage iMsg = new InstantMessage(cmd, userId, stationId);
SecureMessage sMsg = messenger.encryptMessage(iMsg);
ReliableMessage rMsg = messenger.signMessage(sMsg);

(3) Meta protocol

Attaching meta in the first message package is to make sure the station can find it, particularly when it's first time the user connect to this station.

rMsg.setMeta(user.getMeta());

(4) send out serialized message data package

byte[] data = messenger.serializeMessage(rMsg);
send(data);

Step 4. waiting handshake response

The CPU (Command Processing Units) will catch the handshake command response from station, and CPU will process them automatically, so just wait untill handshake success or network error.

Message

Content

  • Text message
Content content = new TextContent("Hey, girl!");
  • Image message
Content content = new ImageContent(imageData, "image.png");
  • Voice message
Content content = new AudioContent(voiceData, "voice.mp3");

NOTICE: file message content (Image, Audio, Video) will be sent out only includes the filename and a URL where the file data (encrypted with the same symmetric key) be stored.

Command

  • Query meta with contact ID
Command cmd = new MetaCommand(identifier);
  • Query profile with contact ID
Command cmd = new ProfileCommand(identifier);

Send command

public class Messenger extends chat.dim.Messenger {

    /**
     *  Pack and send command to station
     *
     * @param cmd - command
     * @return true on success
     */
    public boolean sendCommand(Command cmd) {
        assert server != null;
        return sendContent(cmd, server.identifier);
    }
}

MetaCommand or ProfileCommand with only ID means querying, and the CPUs will catch and process all the response automatically.

Command Processing Units

You can send a customized command (such as search command) and prepare a processor to handle the response.

Search command processor

public class SearchCommandProcessor extends CommandProcessor {

    public static final String SearchUpdated = "SearchUpdated";

    public SearchCommandProcessor(Messenger messenger) {
        super(messenger);
    }

    private void parse(SearchCommand cmd) {
        Map<String, Object> results = cmd.getResults();
        // TODO: processing results
    }

    @Override
    public Content process(Content content, ID sender, InstantMessage iMsg) {
        assert content instanceof SearchCommand;
        parse((SearchCommand) content);

        NotificationCenter nc = NotificationCenter.getInstance();
        nc.postNotification(SearchUpdated, this, content);
        // response nothing to the station
        return null;
    }
}

Handshake command processor

public class HandshakeCommandProcessor extends CommandProcessor {

    public HandshakeCommandProcessor(Messenger messenger) {
        super(messenger);
    }

    private Content success() {
        Log.info("handshake success!");
        String sessionKey = (String) getContext("session_key");
        Server server = (Server) getContext("server");
        server.handshakeAccepted(sessionKey, true);
        return null;
    }

    private Content ask(String sessionKey) {
        Log.info("handshake again, session key: " + sessionKey);
        setContext("session_key", sessionKey);
        return new HandshakeCommand(sessionKey);
    }

    @Override
    public Content process(Content content, ID sender, InstantMessage iMsg) {
        assert content instanceof HandshakeCommand;
        HandshakeCommand cmd = (HandshakeCommand) content;
        String message = cmd.message;
        if ("DIM!".equals(message)) {
            // S -> C
            return success();
        } else if ("DIM?".equals(message)) {
            // S -> C
            return ask(cmd.sessionKey);
        } else {
            // C -> S: Hello world!
            throw new IllegalStateException("handshake command error: " + content);
        }
    }
}

And don't forget to register them.

CommandProcessor.register(Command.HANDSHAKE, HandshakeCommandProcessor.class);
CommandProcessor.register(SearchCommand.SEARCH, SearchCommandProcessor.class);

Save instant message

Override interface saveMessage() in Messenger to store instant message:

public class Messenger extends chat.dim.Messenger {

    @Override
    public boolean saveMessage(InstantMessage msg) {
        Content content = msg.content;
        // TODO: check message type
        //       only save normal message and group commands
        //       ignore 'handshake', 'meta', 'profile', 'search', ...
        //       return true to allow responding

        if (content instanceof HandshakeCommand) {
            // handshake command will be processed by CPUs
            // no need to save handshake command here
            return true;
        }
        if (content instanceof MetaCommand) {
            // meta & profile command will be checked and saved by CPUs
            // no need to save meta & profile command here
            return true;
        }
        if (content instanceof MuteCommand || content instanceof BlockCommand) {
            // TODO: create CPUs for mute & block command
            // no need to save mute & block command here
            return true;
        }
        if (content instanceof SearchCommand) {
            // search result will be parsed by CPUs
            // no need to save search command here
            return true;
        }

        Amanuensis clerk = Amanuensis.getInstance();

        if (content instanceof ReceiptCommand) {
            return clerk.saveReceipt(msg);
        } else {
            return clerk.saveMessage(msg);
        }
    }

}

Copyright © 2019 Albert Moky

FAQs

Package last updated on 28 Aug 2024

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc