Skip to content

Commit e6fe7a7

Browse files
feeblefakiejnmt
andauthored
Backport to branch(3.10) : Fix NullPointerException when a client is misconfigured with digital signature for HMAC (#316)
Co-authored-by: Jun Nemoto <[email protected]>
1 parent 6f4ace9 commit e6fe7a7

File tree

9 files changed

+86
-16
lines changed

9 files changed

+86
-16
lines changed

common/src/main/java/com/scalar/dl/ledger/error/CommonError.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,12 @@ public enum CommonError implements ScalarDlError {
214214
"",
215215
""),
216216

217+
//
218+
// Errors for SECRET_NOT_FOUND(415)
219+
//
220+
SECRET_NOT_FOUND(
221+
StatusCode.SECRET_NOT_FOUND, "001", "The specified secret is not found.", "", ""),
222+
217223
//
218224
// Errors for DATABASE_ERROR(500)
219225
//

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: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ public enum StatusCode {
126126
/** StatusCode: 414. This indicates that the argument is invalid. */
127127
INVALID_ARGUMENT(414),
128128

129+
/** StatusCode: 415. This indicates that the given secret is not found. */
130+
SECRET_NOT_FOUND(415),
131+
129132
/**
130133
* StatusCode: 500. This indicates that the system encountered a database error such as IO error.
131134
*/

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
@@ -91,6 +91,7 @@
9191
import com.scalar.dl.ledger.exception.ContractContextException;
9292
import com.scalar.dl.ledger.exception.LedgerException;
9393
import com.scalar.dl.ledger.exception.MissingContractException;
94+
import com.scalar.dl.ledger.exception.MissingSecretException;
9495
import com.scalar.dl.ledger.exception.SignatureException;
9596
import com.scalar.dl.ledger.model.CertificateRegistrationRequest;
9697
import com.scalar.dl.ledger.model.ContractExecutionRequest;
@@ -2472,7 +2473,7 @@ public void execute_HmacConfiguredAndValidHmacSignatureGiven_ShouldExecuteProper
24722473
}
24732474

24742475
@Test
2475-
public void execute_HmacConfiguredAndInvalidHmacSignatureGiven_ShouldExecuteProperly() {
2476+
public void execute_HmacConfiguredAndInvalidHmacSignatureGiven_ShouldThrowSignatureException() {
24762477
// Arrange
24772478
Properties props2 = createProperties();
24782479
props2.put(LedgerConfig.AUTHENTICATION_METHOD, AuthenticationMethod.HMAC.getMethod());
@@ -2507,6 +2508,43 @@ public void execute_HmacConfiguredAndInvalidHmacSignatureGiven_ShouldExecuteProp
25072508
assertThat(thrown).isExactlyInstanceOf(SignatureException.class);
25082509
}
25092510

2511+
@Test
2512+
public void
2513+
execute_HmacConfiguredAndValidDigitalSignatureGiven_ShouldThrowMissingSecretException() {
2514+
// Arrange
2515+
Properties props2 = createProperties();
2516+
props2.put(LedgerConfig.AUTHENTICATION_METHOD, AuthenticationMethod.HMAC.getMethod());
2517+
props2.put(LedgerConfig.AUTHENTICATION_HMAC_CIPHER_KEY, SOME_CIPHER_KEY);
2518+
createServices(new LedgerConfig(props2));
2519+
String nonce = UUID.randomUUID().toString();
2520+
JsonNode contractArgument =
2521+
mapper
2522+
.createObjectNode()
2523+
.put(ASSET_ATTRIBUTE_NAME, SOME_ASSET_ID_1)
2524+
.put(AMOUNT_ATTRIBUTE_NAME, SOME_AMOUNT_1);
2525+
String argument = Argument.format(contractArgument, nonce, Collections.emptyList());
2526+
2527+
byte[] serialized =
2528+
ContractExecutionRequest.serialize(CREATE_CONTRACT_ID1, argument, ENTITY_ID_A, KEY_VERSION);
2529+
ContractExecutionRequest request =
2530+
new ContractExecutionRequest(
2531+
nonce,
2532+
ENTITY_ID_A,
2533+
KEY_VERSION,
2534+
CREATE_CONTRACT_ID1,
2535+
argument,
2536+
Collections.emptyList(),
2537+
null,
2538+
dsSigner1.sign(serialized),
2539+
null);
2540+
2541+
// Act
2542+
Throwable thrown = catchThrowable(() -> ledgerService.execute(request));
2543+
2544+
// Assert
2545+
assertThat(thrown).isExactlyInstanceOf(MissingSecretException.class);
2546+
}
2547+
25102548
@Test
25112549
public void execute_AuditorEnabledAndValidAuditorHmacSignatureGiven_ShouldExecuteProperly() {
25122550
// 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
@@ -9,6 +9,7 @@
99
import com.scalar.dl.ledger.database.SecretRegistry;
1010
import com.scalar.dl.ledger.error.CommonError;
1111
import com.scalar.dl.ledger.exception.DatabaseException;
12+
import com.scalar.dl.ledger.exception.MissingSecretException;
1213
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
1314
import javax.annotation.concurrent.Immutable;
1415

@@ -56,11 +57,12 @@ 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(CommonError.SECRET_ALREADY_REGISTERED);
63+
} catch (MissingSecretException e) {
64+
registry.bind(entry);
6265
}
63-
registry.bind(entry);
6466
}
6567

6668
/**

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
@@ -17,10 +17,10 @@
1717
import com.scalar.dl.ledger.database.SecretRegistry;
1818
import com.scalar.dl.ledger.error.CommonError;
1919
import com.scalar.dl.ledger.exception.DatabaseException;
20+
import com.scalar.dl.ledger.exception.MissingSecretException;
2021
import com.scalar.dl.ledger.exception.UnexpectedValueException;
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,11 +80,17 @@ 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(CommonError.SECRET_NOT_FOUND));
8689
} catch (ExecutionException e) {
8790
throw new DatabaseException(CommonError.GETTING_SECRET_KEY_FAILED, e, e.getMessage());
8891
}
92+
93+
return toSecretEntry(result);
8994
}
9095

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

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)