8

I'm trying to Hash a BigInteger/BigNum and I'm getting different results in Android/iOS. I need to get the same Hash result so that both the apps work as per the SRP protocol. On closer inspection it is working fine for positive numbers but not working for negative numbers (first nibble greater than 7). Not sure which one is correct and which one is to be adjusted to match with the other.

Android:

    void hashBigInteger(String s) {
    try {
        BigInteger a = new BigInteger(s, 16);
        MessageDigest sha = MessageDigest.getInstance("SHA-256");
        byte[] b = a.toByteArray();
        sha.update(b, 0, b.length);
        byte[] digest = sha.digest();
        BigInteger d = new BigInteger(digest);
        Log.d("HASH", "H = " + d.toString(16));
    } catch (NoSuchAlgorithmException e) {
        throw new UnsupportedOperationException(e);
    }
}

iOS:

void hashBigNum(unsigned char *c) {
    BIGNUM *n = BN_new();
    BN_hex2bn(&n, c);
    unsigned char   buff[ SHA256_DIGEST_LENGTH ];
    int             len  = BN_num_bytes(n);
    unsigned char * bin    = (unsigned char *) malloc( len );
    BN_bn2bin(n, bin);
    hash( SRP_SHA256, bin, len, buff ); 
    fprintf(stderr, "H: ");
    for (int z = 0; z < SHA256_DIGEST_LENGTH; z++)
        fprintf(stderr, "%2x", buff[z]);
    fprintf(stderr, "\n");
    free(bin);
}

Results:

Source String = "6F"
Android Hash = 65c74c15a686187bb6bbf9958f494fc6b80068034a659a9ad44991b08c58f2d2
iOS     Hash = 65c74c15a686187bb6bbf9958f494fc6b80068034a659a9ad44991b08c58f2d2

Source String = "FF"
Android Hash = 06eb7d6a69ee19e5fbdf749018d3d2abfa04bcbd1365db312eb86dc7169389b8
iOS     Hash = a8100ae6aa1940d0b663bb31cd466142ebbdbd5187131b92d93818987832eb89
mehrdad khosravi
  • 2,228
  • 9
  • 29
  • 34
AndroidDev
  • 5,193
  • 5
  • 37
  • 68
  • If you want to implement SRP, it is not enough to have the hash match between Android and iOS. The hash needs to match what the spec says they should be. I suggest using someone else's implementation and seeing what hashes they get. – brian beuning Aug 28 '16 at 17:53
  • @brianbeuning, we have implemented all of the protocol except this bit. Once I fixed the JAVA code for the leading 0s problem now it is working fine cross platform. – AndroidDev Aug 30 '16 at 07:33
  • For example, OpenSSL supports SRP. If the way you compute SRP hashes does not match the way OpenSSL computes SRP hashes, your code will not be able to talk to OpenSSL. – brian beuning Aug 30 '16 at 12:07
  • Yes, we got this working with OpenSSL Hash. – AndroidDev Aug 30 '16 at 12:18

2 Answers2

4

The problem is in the JAVA code. new BigInteger(s, 16).toByteArray() is not safe for leading zeros. See poster comment at Convert a string representation of a hex dump to a byte array using Java?

The bit representation of FF with Android is 00000000 11111111 whereas in iOS is 11111111. The leading zeros is the reason because the SHA256 hashing is different.

Just change the Hex to byte converter using one method of the linked post to get the same byte array (without zeros). For example

public static byte[] hexStringToByteArray(String s) {
    int len = s.length();
    byte[] data = new byte[len / 2];
    for (int i = 0; i < len; i += 2) {
        data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                             + Character.digit(s.charAt(i+1), 16));
    }
    return data;
}

void hashBigInteger(String s){
    try{
        MessageDigest sha = MessageDigest.getInstance("SHA-256");
        byte b[] = hexStringToByteArray(s);
        sha.update(b,0,b.length);
        byte digest[] = sha.digest();
        BigInteger d = new BigInteger(1,digest);

        System.out.println("H "+d.toString(16));
    }catch (NoSuchAlgorithmException e){
        throw new UnsupportedOperationException(e);
    }
}

To proper HEX printing, change also BigInteger d = new BigInteger(digest); with

BigInteger d = new BigInteger(1,digest); 
Community
  • 1
  • 1
pedrofb
  • 37,271
  • 5
  • 94
  • 142
  • 2
    In other words, this is the reverse of what I did in your question about the two's complement. Convert the BigInteger to a byte array and *remove* the leading zero byte, and create a new BigInteger with the modified array. Java will interpret that as -1, but try it. – Rudy Velthuis Aug 21 '16 at 16:19
0

One way is to convert your big numbers to strings and get hash of them.

Anton Malyshev
  • 8,686
  • 2
  • 27
  • 45
  • The BigInteger/BigNum are created from Strings only so would it make a difference if we convert them back to Strings? Also in iOS the Hash uses OpenSSL libraries which are low level C APIs, not sure how we can use a String for the Hash API. – AndroidDev Aug 18 '16 at 14:15
  • 1
    BigInteger and BigNum may have different implementations, so their conversions to byte arrays may produce different results. – Anton Malyshev Aug 18 '16 at 14:17
  • But the SRP protocol (https://en.wikipedia.org/wiki/Secure_Remote_Password_protocol) calculations need BigInteger/BigNum and hence I want to get the same Hash results. Its working for +ve numbers so want to know if one platform treats -ve numbers as signed and the other treats as +ve – AndroidDev Aug 18 '16 at 14:20
  • @Harish: yes, it seems so. See *pedrofb's answer*. On iOS the string is simply seen as one unsigned byte, while in Java it is seen as 255, which needs a leading 0 byte to keep it positive. The extra 0 is probably the difference. – Rudy Velthuis Aug 21 '16 at 16:22