Skip to content

Commit

Permalink
Security: fix TokenMetaData equals and hashcode (#30347)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jaymode committed May 10, 2018
1 parent 428881a commit f746334
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,19 @@ public static byte randomByte() {
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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.elasticsearch.common.xcontent.XContentBuilder;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

Expand Down Expand Up @@ -74,13 +75,13 @@ public boolean equals(Object o) {
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;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* 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;
Expand Down

0 comments on commit f746334

Please sign in to comment.