Skip to content

Commit c608efe

Browse files
feeblefakiejnmt
andauthored
Backport to branch(3.9) : Fix NullPointerException when a client is misconfigured with digital signature for HMAC (#317)
Co-authored-by: Jun Nemoto <35618893+jnmt@users.noreply.github.com>
1 parent 7dac7bd commit c608efe

File tree

8 files changed

+78
-16
lines changed

8 files changed

+78
-16
lines changed

common/src/main/java/com/scalar/dl/ledger/service/Constants.java

Lines changed: 0 additions & 5 deletions
This file was deleted.

common/src/main/java/com/scalar/dl/ledger/service/StatusCode.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ public enum StatusCode {
123123
/** StatusCode: 413. This indicates that the given secret is already registered. */
124124
SECRET_ALREADY_REGISTERED(413),
125125

126+
/** StatusCode: 414. This indicates that the argument is invalid. */
127+
INVALID_ARGUMENT(414),
128+
129+
/** StatusCode: 415. This indicates that the given secret is not found. */
130+
SECRET_NOT_FOUND(415),
131+
126132
/**
127133
* StatusCode: 500. This indicates that the system encountered a database error such as IO error.
128134
*/

ledger/src/integration-test/java/com/scalar/dl/ledger/service/LedgerServiceEndToEndTest.java

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@
8989
import com.scalar.dl.ledger.exception.ContractContextException;
9090
import com.scalar.dl.ledger.exception.LedgerException;
9191
import com.scalar.dl.ledger.exception.MissingContractException;
92+
import com.scalar.dl.ledger.exception.MissingSecretException;
9293
import com.scalar.dl.ledger.exception.SignatureException;
9394
import com.scalar.dl.ledger.model.CertificateRegistrationRequest;
9495
import com.scalar.dl.ledger.model.ContractExecutionRequest;
@@ -2384,7 +2385,7 @@ public void execute_HmacConfiguredAndValidHmacSignatureGiven_ShouldExecuteProper
23842385
}
23852386

23862387
@Test
2387-
public void execute_HmacConfiguredAndInvalidHmacSignatureGiven_ShouldExecuteProperly() {
2388+
public void execute_HmacConfiguredAndInvalidHmacSignatureGiven_ShouldThrowSignatureException() {
23882389
// Arrange
23892390
Properties props2 = createProperties();
23902391
props2.put(LedgerConfig.AUTHENTICATION_METHOD, AuthenticationMethod.HMAC.getMethod());
@@ -2419,6 +2420,43 @@ public void execute_HmacConfiguredAndInvalidHmacSignatureGiven_ShouldExecuteProp
24192420
assertThat(thrown).isExactlyInstanceOf(SignatureException.class);
24202421
}
24212422

2423+
@Test
2424+
public void
2425+
execute_HmacConfiguredAndValidDigitalSignatureGiven_ShouldThrowMissingSecretException() {
2426+
// Arrange
2427+
Properties props2 = createProperties();
2428+
props2.put(LedgerConfig.AUTHENTICATION_METHOD, AuthenticationMethod.HMAC.getMethod());
2429+
props2.put(LedgerConfig.AUTHENTICATION_HMAC_CIPHER_KEY, SOME_CIPHER_KEY);
2430+
createServices(new LedgerConfig(props2));
2431+
String nonce = UUID.randomUUID().toString();
2432+
JsonNode contractArgument =
2433+
mapper
2434+
.createObjectNode()
2435+
.put(ASSET_ATTRIBUTE_NAME, SOME_ASSET_ID_1)
2436+
.put(AMOUNT_ATTRIBUTE_NAME, SOME_AMOUNT_1);
2437+
String argument = Argument.format(contractArgument, nonce, Collections.emptyList());
2438+
2439+
byte[] serialized =
2440+
ContractExecutionRequest.serialize(CREATE_CONTRACT_ID1, argument, ENTITY_ID_A, KEY_VERSION);
2441+
ContractExecutionRequest request =
2442+
new ContractExecutionRequest(
2443+
nonce,
2444+
ENTITY_ID_A,
2445+
KEY_VERSION,
2446+
CREATE_CONTRACT_ID1,
2447+
argument,
2448+
Collections.emptyList(),
2449+
null,
2450+
dsSigner1.sign(serialized),
2451+
null);
2452+
2453+
// Act
2454+
Throwable thrown = catchThrowable(() -> ledgerService.execute(request));
2455+
2456+
// Assert
2457+
assertThat(thrown).isExactlyInstanceOf(MissingSecretException.class);
2458+
}
2459+
24222460
@Test
24232461
public void execute_AuditorEnabledAndValidAuditorHmacSignatureGiven_ShouldExecuteProperly() {
24242462
// Arrange

ledger/src/main/java/com/scalar/dl/ledger/crypto/SecretManager.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import com.google.inject.Inject;
99
import com.scalar.dl.ledger.database.SecretRegistry;
1010
import com.scalar.dl.ledger.exception.DatabaseException;
11+
import com.scalar.dl.ledger.exception.MissingSecretException;
1112
import com.scalar.dl.ledger.service.StatusCode;
1213
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
1314
import javax.annotation.concurrent.Immutable;
@@ -56,12 +57,13 @@ public SecretManager(
5657
* @param entry a {@code SecretEntry}
5758
*/
5859
public void register(SecretEntry entry) {
59-
SecretEntry existing = registry.lookup(entry.getKey());
60-
if (existing != null) {
60+
try {
61+
registry.lookup(entry.getKey());
6162
throw new DatabaseException(
6263
"The specified secret is already registered", StatusCode.SECRET_ALREADY_REGISTERED);
64+
} catch (MissingSecretException e) {
65+
registry.bind(entry);
6366
}
64-
registry.bind(entry);
6567
}
6668

6769
/**

ledger/src/main/java/com/scalar/dl/ledger/database/scalardb/ScalarSecretRegistry.java

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
import com.scalar.dl.ledger.crypto.SecretEntry;
1717
import com.scalar.dl.ledger.database.SecretRegistry;
1818
import com.scalar.dl.ledger.exception.DatabaseException;
19+
import com.scalar.dl.ledger.exception.MissingSecretException;
1920
import com.scalar.dl.ledger.exception.UnexpectedValueException;
2021
import com.scalar.dl.ledger.service.StatusCode;
2122
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
2223
import java.nio.charset.StandardCharsets;
23-
import javax.annotation.Nullable;
2424
import javax.annotation.concurrent.Immutable;
2525

2626
@Immutable
@@ -72,7 +72,6 @@ public void unbind(SecretEntry.Key key) {
7272
}
7373

7474
@Override
75-
@Nullable
7675
public SecretEntry lookup(SecretEntry.Key key) {
7776
Get get =
7877
new Get(
@@ -81,12 +80,18 @@ public SecretEntry lookup(SecretEntry.Key key) {
8180
.withConsistency(Consistency.SEQUENTIAL)
8281
.forTable(TABLE);
8382

83+
Result result;
8484
try {
85-
return storage.get(get).map(this::toSecretEntry).orElse(null);
85+
result =
86+
storage
87+
.get(get)
88+
.orElseThrow(() -> new MissingSecretException("the specified secret is not found"));
8689
} catch (ExecutionException e) {
8790
throw new DatabaseException(
8891
"can't get the secret key from storage", e, StatusCode.DATABASE_ERROR);
8992
}
93+
94+
return toSecretEntry(result);
9095
}
9196

9297
private SecretEntry toSecretEntry(Result result) {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.scalar.dl.ledger.exception;
2+
3+
import com.scalar.dl.ledger.service.StatusCode;
4+
5+
public class MissingSecretException extends DatabaseException {
6+
7+
public MissingSecretException(String message) {
8+
super(message, StatusCode.SECRET_NOT_FOUND);
9+
}
10+
11+
public MissingSecretException(String message, Throwable cause) {
12+
super(message, cause, StatusCode.SECRET_NOT_FOUND);
13+
}
14+
}

ledger/src/test/java/com/scalar/dl/ledger/crypto/SecretManagerTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import com.google.common.cache.CacheBuilder;
1414
import com.scalar.dl.ledger.database.SecretRegistry;
1515
import com.scalar.dl.ledger.exception.DatabaseException;
16+
import com.scalar.dl.ledger.exception.MissingSecretException;
1617
import org.junit.jupiter.api.BeforeEach;
1718
import org.junit.jupiter.api.Test;
1819
import org.mockito.Mock;
@@ -43,7 +44,8 @@ public void setUp() {
4344
@Test
4445
public void register_ProperSecretEntryGiven_ShouldCallBind() {
4546
// Arrange
46-
when(registry.lookup(entry.getKey())).thenReturn(null);
47+
MissingSecretException toThrow = mock(MissingSecretException.class);
48+
when(registry.lookup(entry.getKey())).thenThrow(toThrow);
4749

4850
// Act
4951
manager.register(entry);

ledger/src/test/java/com/scalar/dl/ledger/database/scalardb/ScalarSecretRegistryTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import com.scalar.dl.ledger.crypto.Cipher;
2323
import com.scalar.dl.ledger.crypto.SecretEntry;
2424
import com.scalar.dl.ledger.exception.DatabaseException;
25+
import com.scalar.dl.ledger.exception.MissingSecretException;
2526
import java.nio.charset.StandardCharsets;
2627
import java.util.Optional;
2728
import org.junit.jupiter.api.BeforeEach;
@@ -153,18 +154,17 @@ public void lookup_ValidArgumentGiven_ShouldLookupProperly() throws ExecutionExc
153154
}
154155

155156
@Test
156-
public void lookup_ValidArgumentGivenButEmptyResultReturned_ShouldReturnNull()
157+
public void lookup_ValidArgumentGivenButEmptyResultReturned_ShouldThrowMissingSecretException()
157158
throws ExecutionException {
158159
// Arrange
159160
SecretEntry.Key key = new SecretEntry.Key(SOME_ENTITY_ID, SOME_KEY_VERSION);
160161
when(storage.get(any(Get.class))).thenReturn(Optional.empty());
161162

162163
// Act Assert
163-
SecretEntry actual = registry.lookup(key);
164+
assertThatThrownBy(() -> registry.lookup(key)).isInstanceOf(MissingSecretException.class);
164165

165166
// Assert
166167
verify(storage).get(any(Get.class));
167-
assertThat(actual).isNull();
168168
}
169169

170170
@Test

0 commit comments

Comments
 (0)