Research
Security News
Malicious npm Package Targets Solana Developers and Hijacks Funds
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
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>
User private key, ID, meta, and profile are generated in client,
and broadcast only meta
& profile
onto DIM station.
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);
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
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.
Content content = new TextContent("Hey, girl!");
Content content = new ImageContent(imageData, "image.png");
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 cmd = new MetaCommand(identifier);
Command cmd = new ProfileCommand(identifier);
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.
You can send a customized command (such as search command) and prepare a processor to handle the response.
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;
}
}
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);
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
Decentralized Instant Messaging (Java SDK)
We found that chat.dim:SDK demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
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.
Research
Security News
A malicious npm package targets Solana developers, rerouting funds in 2% of transactions to a hardcoded address.
Security News
Research
Socket researchers have discovered malicious npm packages targeting crypto developers, stealing credentials and wallet data using spyware delivered through typosquats of popular cryptographic libraries.
Security News
Socket's package search now displays weekly downloads for npm packages, helping developers quickly assess popularity and make more informed decisions.