Java vs Golang for HOTP (rfc-4226)

I am trying to implement HOTP (rfc-4226) in the Golang, and I am struggling to create a live HOTP. I can create it in java, but for some reason my implementation in Golang is different. Here are some examples:

public static String constructOTP(final Long counter, final String key) throws NoSuchAlgorithmException, DecoderException, InvalidKeyException { final Mac mac = Mac.getInstance("HmacSHA512"); final byte[] binaryKey = Hex.decodeHex(key.toCharArray()); mac.init(new SecretKeySpec(binaryKey, "HmacSHA512")); final byte[] b = ByteBuffer.allocate(8).putLong(counter).array(); byte[] computedOtp = mac.doFinal(b); return new String(Hex.encodeHex(computedOtp)); } 

and in Go:

 func getOTP(counter uint64, key string) string { str, err := hex.DecodeString(key) if err != nil { panic(err) } h := hmac.New(sha512.New, str) bs := make([]byte, 8) binary.BigEndian.PutUint64(bs, counter) h.Write(bs) return base64.StdEncoding.EncodeToString(h.Sum(nil)) } 

I believe the problem is that the Java string is: ByteBuffer.allocate(8).putLong(counter).array(); creates a different byte array than the string Go: binary.BigEndian.PutUint64(bs, counter) .

The following byte array is created in Java: 83 -116 -9 -98 115 -126 -3 -48 and in Go: 83 140 247 158 115 130 253 207 .

Does anyone know the difference in two lines and how can I wrap a java line?

+5
source share
1 answer

The byte type in Java is signed, it has a range of -128..127 , and in Go byte is an alias of uint8 and has a range of 0..255 . Therefore, if you want to compare the results, you need to shift the negative Java values ​​by 256 (add 256 ).

Tip. To display the Java byte value in unsigned form, use: byteValue & 0xff , which converts it to int , using 8 byte bits as the lower 8 bits in int . Or better: display both results in hexadecimal so that you don't have to worry about the sign ...

Adding 256 to your negative Java byte values, the result is almost identical to Go: the last byte is turned off by 1:

 javabytes := []int{83, -116, -9, -98, 115, -126, -3, -48} for i, b := range javabytes { if b < 0 { javabytes[i] += 256 } } fmt.Println(javabytes) 

Output:

 [83 140 247 158 115 130 253 208] 

So, the last byte of your Java array is 208 , and Go is 207 . I assume that your counter incremented once in another place in your code that you have not yet submitted.

What makes Java different is that you return a hexadecimal result, while in Go you return a Base64 encoded result (these are two different encodings giving completely different results). As you have confirmed, in Go returning hex.EncodeToString(h.Sum(nil)) , the results are the same.

Tip # 2: To display Go bytes in signed form, just convert them to int8 (which is signed) as follows:

 gobytes := []byte{83, 140, 247, 158, 115, 130, 253, 207} for _, b := range gobytes { fmt.Print(int8(b), " ") } 

It is output:

 83 -116 -9 -98 115 -126 -3 -49 
+10
source

Source: https://habr.com/ru/post/1274045/


All Articles