Security: fix TokenMetaData equals and hashcode (#30347)

The TokenMetaData equals method compared byte arrays using `.equals` on
the arrays themselves, which is the equivalent of an `==` check. This
means that a seperate byte[] with the same contents would not be
considered equivalent to the existing one, even though it should be.

The method has been updated to use `Array#equals` and similarly the
hashcode method has been updated to call `Arrays#hashCode` instead of
calling hashcode on the array itself.
This commit is contained in:
Jay Modi 2018-05-10 13:12:11 -06:00 committed by GitHub
parent 66ef5550ce
commit f733de8e67
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 69 additions and 3 deletions

View File

@ -519,6 +519,19 @@ public abstract class ESTestCase extends LuceneTestCase {
return (byte) random().nextInt();
}
/**
* Helper method to create a byte array of a given length populated with random byte values
*
* @see #randomByte()
*/
public static byte[] randomByteArrayOfLength(int size) {
byte[] bytes = new byte[size];
for (int i = 0; i < size; i++) {
bytes[i] = randomByte();
}
return bytes;
}
public static short randomShort() {
return (short) random().nextInt();
}

View File

@ -14,6 +14,7 @@ import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.xcontent.XContentBuilder;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@ -74,13 +75,13 @@ public final class TokenMetaData extends AbstractNamedDiffable<ClusterState.Cust
if (o == null || getClass() != o.getClass()) return false;
TokenMetaData that = (TokenMetaData)o;
return keys.equals(that.keys) && currentKeyHash.equals(that.currentKeyHash);
return keys.equals(that.keys) && Arrays.equals(currentKeyHash, that.currentKeyHash);
}
@Override
public int hashCode() {
int result = keys.hashCode();
result = 31 * result + currentKeyHash.hashCode();
result = 31 * result + Arrays.hashCode(currentKeyHash);
return result;
}

View File

@ -0,0 +1,52 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
package org.elasticsearch.xpack.core.security.authc;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.settings.SecureString;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.EqualsHashCodeTestUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TokenMetaDataTests extends ESTestCase {
public void testEqualsAndHashCode() {
final int numKeyAndTimestamps = scaledRandomIntBetween(1, 8);
final List<KeyAndTimestamp> keyAndTimestampList = generateKeyAndTimestampListOfSize(numKeyAndTimestamps);
final byte[] currentKeyHash = randomByteArrayOfLength(8);
final TokenMetaData original = new TokenMetaData(keyAndTimestampList, currentKeyHash);
EqualsHashCodeTestUtils.checkEqualsAndHashCode(original, tokenMetaData -> {
final List<KeyAndTimestamp> copiedList = new ArrayList<>(keyAndTimestampList);
final byte[] copyKeyHash = Arrays.copyOf(currentKeyHash, currentKeyHash.length);
return new TokenMetaData(copiedList, copyKeyHash);
}, tokenMetaData -> {
final List<KeyAndTimestamp> modifiedList = generateKeyAndTimestampListOfSize(numKeyAndTimestamps);
return new TokenMetaData(modifiedList, currentKeyHash);
});
EqualsHashCodeTestUtils.checkEqualsAndHashCode(original, tokenMetaData -> {
BytesStreamOutput out = new BytesStreamOutput();
tokenMetaData.writeTo(out);
return new TokenMetaData(out.bytes().streamInput());
}, tokenMetaData -> {
final byte[] modifiedKeyHash = randomByteArrayOfLength(8);
return new TokenMetaData(keyAndTimestampList, modifiedKeyHash);
});
}
private List<KeyAndTimestamp> generateKeyAndTimestampListOfSize(int size) {
final List<KeyAndTimestamp> keyAndTimestampList = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
keyAndTimestampList.add(
new KeyAndTimestamp(new SecureString(randomAlphaOfLengthBetween(1, 12).toCharArray()), randomNonNegativeLong()));
}
return keyAndTimestampList;
}
}

View File

@ -14,7 +14,7 @@ import java.util.Arrays;
* Simple wrapper around bytes so that it can be used as a cache key. The hashCode is computed
* once upon creation and cached.
*/
public class BytesKey {
public final class BytesKey {
final byte[] bytes;
private final int hashCode;