Translation of Go-Code to Java but there are problems with ranges of DataTypes

Hello World !,

i tried to translate a piece of code to java but I have problems about the ranges of the datatypes and the bit operations is someone able to help.

Both Code pieces are ready to compile and work but get different results :frowning:

If someone could help I would be really thankful !!!
The Go-Code :

package main

import (
	"encoding/binary"
	"fmt"
	"strconv"
)

func crc32v(idx int) int {
	POLY := 0x04c11db7
	c := idx << 24 & 0xFFFFFFFF
	for i := 0; i < 8; i++ {
		k:= c&0x80000000
		if k > 0 {
			c = c<<1 ^ POLY
		} else {
			c = c << 1 & 0xFFFFFFFF
		}
	}
	return c
}

func adler32(bs []byte) int {
	s1 := 4
	s2 := 0
	for _, x := range bs {
		s1 = (s1 + int(x)) % 65521
		s2 = (s2 + s1) % 65521
	}
	res := (s2 << 16) | s1
	return res
}

func prepareSubs(key []int) [][]int {
	table1 := make([]int, 256)
	for i := range table1 {
		table1[i] = i
	}
	s := 0
	for i := range table1 {
		s += table1[i]
		s += key[i%len(key)]
		s &= 0xFF
		table1[i] = table1[s]
	}

	table2 := make([]int, 256)
	for i, v := range table1 {
		table2[i] = v
	}
	s = 0
	for i := range table2 {
		if i > 0 {
			s = (table2[i] + s) & 0xFF
			table2[i] = table2[s]
		}
	}
	out := [][]int{table1, table2}
	return out
}

func Reverse(s string) (result string) {
	for _, v := range s {
		result = string(v) + result
	}
	return
}

func rbit(x int, width int) int {
	x2 := strconv.FormatInt(int64(x), 2)
	orig := len(x2)
	for i := 0; i < width-orig; i++ {
		x2 = fmt.Sprintf("0%s", x2)
	}
	x3, _ := strconv.ParseInt(Reverse(x2), 2, 32)
	return int(x3)
}


func hashFunction(srcData []byte, timestamp int) string {

	ldsBytes := []byte{0x0D, 0xC0, 0xA0, 0xE1, 0xF0, 0x00, 0x2D, 0xE9, 0xE9, 0x00, 0x40, 0xE2, 0x00, 0x70, 0xA0, 0xE1, 0x01, 0x00, 0xA0, 0xE1, 0x02, 0x10, 0xA0, 0xE1, 0x03, 0x20, 0xA0, 0xE1, 0x78, 0x00, 0x9C, 0xE8}
	stackCrc := crc32v(0x63)
	inpbs := make([]byte, 0)
	for i := 0; i < 4; i++ {
		next := srcData[i*16 : i*16+4]
		for _, b := range next {
			inpbs = append(inpbs, b)
		}
	}


	// Append timestamp bytes to inpbs
	tsBytes := make([]byte, 4)
	binary.BigEndian.PutUint32(tsBytes, uint32(timestamp))

	for _, b := range tsBytes {
		inpbs = append(inpbs, b)
	}
	ldsChecksum := adler32(ldsBytes)
	crcbs := 0
	for i := 0; i < 4; i++ {
		crcbs = (crcbs << 4) + (ldsChecksum & 0xff)
		ldsChecksum >>= 8
	}
	crcbs = (crc32v(crcbs&0xFF) ^ crc32v((crcbs>>8)&0xFF))
	key := []int{
		crcbs & 0xFF,
		0,
		(crcbs >> 8) & 0xFF,
		(stackCrc >> 8) & 0xFF,
		(crcbs >> 16) & 0xFF,
		(crcbs >> 24) & 0xFF,
		0,
		stackCrc & 0xFF,
	}

	// Returns multi-dimensional int array
	sbox := prepareSubs(key)
	result := make([]int, len(inpbs)) // It prepends like 6 bytes before the return

	for r, i := range inpbs {
		result[r] = int(i)
	}

	for i := range result {
		result[i] ^= sbox[0][(sbox[1][i+1]<<1)&0xFF]
	}

	for i := range result {
		x := ((result[i] << 4) & 0xF0) | ((result[i] >> 4) & 0xF)
		x ^= result[(i+1)%len(result)]
		x = rbit(x, 8)
		x ^= 0xFF
		x ^= 0x14
		result[i] = x
	}

	pre := make([]int, len(result)+6)
	pre[0] = 0x03
	pre[1] = 0x6F
	pre[2] = key[7]
	pre[3] = key[3]
	pre[4] = 0
	pre[5] = 0

	for r, v := range result {
		pre[r+6] = v
	}

	str := ""
	for _, p := range pre {
		str = fmt.Sprintf("%s%02x", str, p)
	}

	fmt.Println("As []int: \t", pre)
	fmt.Printf("As hex:\t\t%02x\n", pre)
	fmt.Printf("As hex string:\t%v\n", str)
	return str
}

func main() {

	var favicon = []byte{
		0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00,
		0x10, 0x00, 0x00, 0x00, 0x0f, 0x04, 0x03, 0x00, 0x00, 0x00, 0x1f, 0x5d, 0x52, 0x1c, 0x00, 0x00, 0x00, 0x0f, 0x50,
		0x4c, 0x54, 0x45, 0x7a, 0xdf, 0xfd, 0xfd, 0xff, 0xfc, 0x39, 0x4d, 0x52, 0x19, 0x16, 0x15, 0xc3, 0x8d, 0x76, 0xc7,
		0x36, 0x2c, 0xf5, 0x00, 0x00, 0x00, 0x40, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x95, 0xc9, 0xd1, 0x0d, 0xc0, 0x20,
		0x0c, 0x03, 0xd1, 0x23, 0x5d, 0xa0, 0x49, 0x17, 0x20, 0x4c, 0xc0, 0x10, 0xec, 0x3f, 0x53, 0x8d, 0xc2, 0x02, 0x9c,
		0xfc, 0xf1, 0x24, 0xe3, 0x31, 0x54, 0x3a, 0xd1, 0x51, 0x96, 0x74, 0x1c, 0xcd, 0x18, 0xed, 0x9b, 0x9a, 0x11, 0x85,
		0x24, 0xea, 0xda, 0xe0, 0x99, 0x14, 0xd6, 0x3a, 0x68, 0x6f, 0x41, 0xdd, 0xe2, 0x07, 0xdb, 0xb5, 0x05, 0xca, 0xdb,
		0xb2, 0x9a, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
	}
	hashFunction(favicon,1)
}

The Java Code :

import java.nio.ByteBuffer;
import java.util.Arrays;

public class Lev {

    public static long crc32v(long idx) {
        int POLY = 0x04c11db7;
        long c = (idx << 24) & 0xFFFFFFFF;
        long k;
        for (int i = 0; i < 8; i++) {
            k = c&0x80000000;
            if (k > 0) {
                c = (c << 1) ^ POLY;
            } else {
                c = (c << 1) & 0xFFFFFFFF;
            }
        }
        return c;
    }

    public static long adler32(int[] bs) {
        long s1 = 4;
        long s2 = 0;
        for (int i = 0; i < bs.length; i++) {
            s1 = (s1 + (int) bs[i]) % 65521;
            s2 = (s2 + s1) % 65521;
        }
        long res = (s2 << 16) | s1;
        return res;
    }

    public static long[][] prepareSubs(long[] key) {
        long[] table1 = new long[256];
        for (int i = 0; i < table1.length; i++) {
            table1[i] = i;
        }
        int s = 0;
        for (int i = 0; i < table1.length; i++) {
            s += table1[i];
            s += key[i % key.length];
            s &= 0xFF;
            table1[i] = table1[s];
        }

        long[] table2 = new long[256];

        for (int i = 0; i < table1.length; i++) {
            table2[i] = table1[i];
        }
        s = 0;
        for (int i = 0; i < table2.length; i++) {
            if (i > 0) {
                s = (int) ((table2[i] + s) & 0xFF);
                table2[i] = table2[s];
            }
        }
        long[][] out = new long[][]{
                table1, table2
        };
        return out;
    }

    // Reverse a string
    static String Reverse(String s) {
        StringBuilder sb = new StringBuilder(s);
        return sb.reverse().toString();
    }

    public static int rbit(int x, int width) {
        String x2 = Long.toString((long) x, 2);
        int orig = x2.length();
        for (int i = 0; i < width - orig; i++) {
            x2 = String.format("0%s", x2);
        }
        int x3 = Integer.parseInt(Reverse(x2), 2);
        return x3;
    }

    public static String hashFunction(int[] srcData, int timestamp) {

        int[] ldsBytes = new int[]{
                0x0D, 0xC0, 0xA0, 0xE1, 0xF0, 0x00, 0x2D, 0xE9, 0xE9, 0x00, 0x40, 0xE2, 0x00, 0x70, 0xA0, 0xE1, 0x01, 0x00, 0xA0, 0xE1, 0x02, 0x10, 0xA0, 0xE1, 0x03, 0x20, 0xA0, 0xE1, 0x78, 0x00, 0x9C, 0xE8
        };
        long stackCrc = crc32v(0x63);
        byte[] inpbs = new byte[0];
        for (int i = 0; i < 4; i++) {
            int[] next = Arrays.copyOfRange(srcData, i * 16, i * 16 + 4);

            for (int f = 0; f < next.length; f++) {
                inpbs = append(inpbs, next[f]);
            }
        }


        // Append timestamp bytes to inpbs
        byte[] tsBytes = new byte[4];
        tsBytes = ByteBuffer.allocate(4).putInt(timestamp).array();



        //binary.BigEndian.PutUint32(tsBytes, uint32(timestamp))

        for (int i = 0; i < tsBytes.length; i++) {
            inpbs = append(inpbs, tsBytes[i]);
        }

        long ldsChecksum = adler32(ldsBytes);
        long crcbs = 0;
        for (int i = 0; i < 4; i++) {
            crcbs = (crcbs << 4) + (ldsChecksum & 0xff);
            ldsChecksum >>= 8;
        }
        crcbs = (crc32v(crcbs & 0xFF) ^ crc32v((crcbs >> 8) & 0xFF));
        long[] key = new long[]{
                crcbs & 0xFF,
                0,
                (crcbs >> 8) & 0xFF,
                (stackCrc >> 8) & 0xFF,
                (crcbs >> 16) & 0xFF,
                (crcbs >> 24) & 0xFF,
                0,
                stackCrc & 0xFF,
        };

        // Returns multi-dimensional int array
        long[][] sbox = prepareSubs(key);
        int[] result = new int[inpbs.length]; // It prepends like 6 bytes before the return

        for (int i = 0; i < inpbs.length; i++) {
            result[i] = inpbs[i];
        }

        for(int i = 0; i < result.length;i++){
            result[i] ^= sbox[0][(int) ((sbox[1][i + 1] << 1) & 0xFF)];
        }

        for(int i = 0; i < result.length;i++){
            int x =((result[i] << 4) & 0xF0) | ((result[i] >> 4) & 0xF);
            x ^= result[(i + 1) % result.length];
            x = rbit(x, 8);
            x ^= 0xFF;
            x ^= 0x14;
            result[i] = x;
        }

        long[] pre =new long[result.length + 6];
        pre[0] = 0x03;
        pre[1] = 0x6F;
        pre[2] = key[7];
        pre[3] = key[3];
        pre[4] = 0;
        pre[5] = 0;

        for (int i = 0; i < result.length; i++) {
            pre[i + 6] = result[i];
        }

        String str="";
        for(int i = 0; i < pre.length; i++){
            str = str.format("%s%02x", str, pre[i]);
        }


        System.out.println(str);
        return str;
    }

     public static void main(String[] args) {
        int[] favicon = new int[]{
                0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d, 0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00,
                0x10, 0x00, 0x00, 0x00, 0x0f, 0x04, 0x03, 0x00, 0x00, 0x00, 0x1f, 0x5d, 0x52, 0x1c, 0x00, 0x00, 0x00, 0x0f, 0x50,
                0x4c, 0x54, 0x45, 0x7a, 0xdf, 0xfd, 0xfd, 0xff, 0xfc, 0x39, 0x4d, 0x52, 0x19, 0x16, 0x15, 0xc3, 0x8d, 0x76, 0xc7,
                0x36, 0x2c, 0xf5, 0x00, 0x00, 0x00, 0x40, 0x49, 0x44, 0x41, 0x54, 0x08, 0xd7, 0x95, 0xc9, 0xd1, 0x0d, 0xc0, 0x20,
                0x0c, 0x03, 0xd1, 0x23, 0x5d, 0xa0, 0x49, 0x17, 0x20, 0x4c, 0xc0, 0x10, 0xec, 0x3f, 0x53, 0x8d, 0xc2, 0x02, 0x9c,
                0xfc, 0xf1, 0x24, 0xe3, 0x31, 0x54, 0x3a, 0xd1, 0x51, 0x96, 0x74, 0x1c, 0xcd, 0x18, 0xed, 0x9b, 0x9a, 0x11, 0x85,
                0x24, 0xea, 0xda, 0xe0, 0x99, 0x14, 0xd6, 0x3a, 0x68, 0x6f, 0x41, 0xdd, 0xe2, 0x07, 0xdb, 0xb5, 0x05, 0xca, 0xdb,
                0xb2, 0x9a, 0xdd, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82,
        };
        hashFunction(favicon, 1);

    }

    public static byte[] append(byte[] arr, byte b){
        byte[] re = new byte[arr.length+1];
        for(int i = 0; i < arr.length; i ++){
            re[i] = arr[i];
        }
        re[arr.length] = b;
        return re;
    }
    public static byte[] append(byte[] arr, int b){
        byte[] re = new byte[arr.length+1];
        for(int i = 0; i < arr.length; i ++){
            re[i] = arr[i];
        }
        re[arr.length] = (byte) b;
        return re;
    }
    public static int[] append(int[] arr, byte b){
        int[] re = new int[arr.length+1];
        for(int i = 0; i < arr.length; i ++){
            re[i] = arr[i];
        }
        re[arr.length] = b;
        return re;
    }
}

As you mentioned ranges, then just looking at adler32 you have byte in the Go version (range 0 to 255) and int in the Java version (32-bit integer) for the input array.

You might also want to specify the size of your int datatypes, e.g. int64, int32 to correspond directly to Java’s long and int.

My Problem is I am not able to change the go code just the java code to prevent changes in result. Could you tell me what i have to change in java ?

Best regards
Joost

The problem was not the ranges, but that Java needs you to specify if a number is long. The discrepancies arise in crc32v, and the only changes needed are to add an L to 0x80000000 and 0xFFFFFFFF.

    public static long crc32v(long idx) {
        int POLY = 0x04c11db7;
        long c = (idx << 24) & 0xFFFFFFFFL;
        long k;
        for (int i = 0; i < 8; i++) {
            k = c&0x80000000L;
            if (k > 0) {
                c = (c << 1) ^ POLY;
            } else {
                c = (c << 1) & 0xFFFFFFFFL;
            }
        }
        return c;
    }

without the L, Java treats the number as a 32-bit int, and sign extends it to 64-bits.

1 Like

Thank you so much !!!