Skip to content

Commit f746334

Browse files
committed
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.
1 parent 428881a commit f746334

File tree

4 files changed

+69
-3
lines changed

4 files changed

+69
-3
lines changed

test/framework/src/main/java/org/elasticsearch/test/ESTestCase.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,19 @@ public static byte randomByte() {
519519
return (byte) random().nextInt();
520520
}
521521

522+
/**
523+
* Helper method to create a byte array of a given length populated with random byte values
524+
*
525+
* @see #randomByte()
526+
*/
527+
public static byte[] randomByteArrayOfLength(int size) {
528+
byte[] bytes = new byte[size];
529+
for (int i = 0; i < size; i++) {
530+
bytes[i] = randomByte();
531+
}
532+
return bytes;
533+
}
534+
522535
public static short randomShort() {
523536
return (short) random().nextInt();
524537
}

x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authc/TokenMetaData.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import org.elasticsearch.common.xcontent.XContentBuilder;
1515

1616
import java.io.IOException;
17+
import java.util.Arrays;
1718
import java.util.Collections;
1819
import java.util.List;
1920

@@ -74,13 +75,13 @@ public boolean equals(Object o) {
7475
if (o == null || getClass() != o.getClass()) return false;
7576

7677
TokenMetaData that = (TokenMetaData)o;
77-
return keys.equals(that.keys) && currentKeyHash.equals(that.currentKeyHash);
78+
return keys.equals(that.keys) && Arrays.equals(currentKeyHash, that.currentKeyHash);
7879
}
7980

8081
@Override
8182
public int hashCode() {
8283
int result = keys.hashCode();
83-
result = 31 * result + currentKeyHash.hashCode();
84+
result = 31 * result + Arrays.hashCode(currentKeyHash);
8485
return result;
8586
}
8687

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License;
4+
* you may not use this file except in compliance with the Elastic License.
5+
*/
6+
package org.elasticsearch.xpack.core.security.authc;
7+
8+
import org.elasticsearch.common.io.stream.BytesStreamOutput;
9+
import org.elasticsearch.common.settings.SecureString;
10+
import org.elasticsearch.test.ESTestCase;
11+
import org.elasticsearch.test.EqualsHashCodeTestUtils;
12+
13+
import java.util.ArrayList;
14+
import java.util.Arrays;
15+
import java.util.List;
16+
17+
public class TokenMetaDataTests extends ESTestCase {
18+
19+
public void testEqualsAndHashCode() {
20+
final int numKeyAndTimestamps = scaledRandomIntBetween(1, 8);
21+
final List<KeyAndTimestamp> keyAndTimestampList = generateKeyAndTimestampListOfSize(numKeyAndTimestamps);
22+
final byte[] currentKeyHash = randomByteArrayOfLength(8);
23+
final TokenMetaData original = new TokenMetaData(keyAndTimestampList, currentKeyHash);
24+
25+
EqualsHashCodeTestUtils.checkEqualsAndHashCode(original, tokenMetaData -> {
26+
final List<KeyAndTimestamp> copiedList = new ArrayList<>(keyAndTimestampList);
27+
final byte[] copyKeyHash = Arrays.copyOf(currentKeyHash, currentKeyHash.length);
28+
return new TokenMetaData(copiedList, copyKeyHash);
29+
}, tokenMetaData -> {
30+
final List<KeyAndTimestamp> modifiedList = generateKeyAndTimestampListOfSize(numKeyAndTimestamps);
31+
return new TokenMetaData(modifiedList, currentKeyHash);
32+
});
33+
34+
EqualsHashCodeTestUtils.checkEqualsAndHashCode(original, tokenMetaData -> {
35+
BytesStreamOutput out = new BytesStreamOutput();
36+
tokenMetaData.writeTo(out);
37+
return new TokenMetaData(out.bytes().streamInput());
38+
}, tokenMetaData -> {
39+
final byte[] modifiedKeyHash = randomByteArrayOfLength(8);
40+
return new TokenMetaData(keyAndTimestampList, modifiedKeyHash);
41+
});
42+
}
43+
44+
private List<KeyAndTimestamp> generateKeyAndTimestampListOfSize(int size) {
45+
final List<KeyAndTimestamp> keyAndTimestampList = new ArrayList<>(size);
46+
for (int i = 0; i < size; i++) {
47+
keyAndTimestampList.add(
48+
new KeyAndTimestamp(new SecureString(randomAlphaOfLengthBetween(1, 12).toCharArray()), randomNonNegativeLong()));
49+
}
50+
return keyAndTimestampList;
51+
}
52+
}

x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/BytesKey.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* Simple wrapper around bytes so that it can be used as a cache key. The hashCode is computed
1515
* once upon creation and cached.
1616
*/
17-
public class BytesKey {
17+
public final class BytesKey {
1818

1919
final byte[] bytes;
2020
private final int hashCode;

0 commit comments

Comments
 (0)