Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java native messaging with chrome extension - cannot correctly write length

I'm currently writing a Java program that communicates with a Chrome extension. I need to implement the Chrome native messaging protocol in order to communicate. The Google Chrome docs say:

... each message is serialized using JSON, UTF-8 encoded and is preceded with 32-bit message length in native byte order. (Source)

I tried to implement this in Java, but I have problems when my messages have a certain length, even though my implementation should be correct. Here is my current implementation, based on earlier SO-answers & questions(here for example):

// read the message size from Chrome. This part works correctly.
public static int getInt(char[] bytes) {
    return  (bytes[3]<<24) & 0xff000000|
            (bytes[2]<<16) & 0x00ff0000|
            (bytes[1]<< 8) & 0x0000ff00|
            (bytes[0]<< 0) & 0x000000ff;
}

// transform the length into the 32-bit message length. 
// This part works for small numbers, but does not work for length 2269 for example.
public static String getBytes(int length) {
    return String.format("%c%c%c%c", 
            (char) ( length      & 0xFF),
            (char) ((length>>8)  & 0xFF),
            (char) ((length>>16) & 0xFF),
            (char) ((length>>24) & 0xFF));
}

It seems the problem lies in the way java implements chars. I'd expect normal chars, like in C. In practice, it seems Java sometimes transforms these chars into unicode-chars (or at least, that's my suspicion so far). This is reflected in the following output (piped to xxd to show the actual bytes) from the java-program for length 2269:

0000000: c39d 0800 00                             .....

The expected output however (with python):

import struct
struct.pack('I', 2269)
# outputs in interactive mode: '\xdd\x08\x00\x00'

What exactly is happening here? Why does Java convert my "0xDD" to "0xC39D" and how can I get my getBytes function to represent the expected input for Chrome Native Messaging? Using another language is not an option.

like image 586
Snicksie Avatar asked Sep 30 '22 23:09

Snicksie


1 Answers

Chars in Java are automatically transformed into unicode. The correct type for this use case is byte, which doesn't automatically transform and keeps the correct value. A correct implementation of the Chrome Native Messaging protocol is thus as follows:

    public static byte[] getBytes(int length) {
        byte[] bytes = new byte[4];
        bytes[0] = (byte) ( length      & 0xFF);
        bytes[1] = (byte) ((length>>8)  & 0xFF);
        bytes[2] = (byte) ((length>>16) & 0xFF);
        bytes[3] = (byte) ((length>>24) & 0xFF);
        return bytes;
    }

Besides this method, care needs to be used not to use a String anywhere between the calculation of the length-bytes and the output. Output to System.out can be done as following:

    try {
        System.out.write(getBytes(message.length()));
    } catch (IOException ex) {
        ex.printStackTrace();
    }
like image 139
Snicksie Avatar answered Oct 20 '22 09:10

Snicksie