Skip to content

Commit

Permalink
Add more tests, address comments
Browse files Browse the repository at this point in the history
  • Loading branch information
haydenbaker committed Sep 1, 2023
1 parent c16ef04 commit 3124558
Show file tree
Hide file tree
Showing 17 changed files with 434 additions and 37 deletions.
6 changes: 6 additions & 0 deletions core/http-auth-aws/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@
<artifactId>checksums</artifactId>
<version>${awsjavasdk.version}</version>
</dependency>
<dependency>
<groupId>software.amazon.awssdk.crt</groupId>
<artifactId>aws-crt</artifactId>
<version>${awscrt.version}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>org.mockito</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@

import static software.amazon.awssdk.http.auth.aws.internal.checksums.factory.CrtBasedChecksumProvider.longToByte;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.zip.Checksum;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.crt.checksums.CRC32C;
import software.amazon.awssdk.http.auth.aws.internal.checksums.factory.CrtBasedChecksumProvider;
import software.amazon.awssdk.http.auth.aws.internal.checksums.factory.SdkCrc32C;

Expand Down Expand Up @@ -83,15 +83,10 @@ public void reset() {

private Checksum cloneChecksum(Checksum checksum) {
if (isCrtBasedChecksum) {
try {
Method method = checksum.getClass().getDeclaredMethod("clone");
return (Checksum) method.invoke(checksum);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Could not clone checksum class " + checksum.getClass(), e);
}
} else {
return (Checksum) ((SdkCrc32C) checksum).clone();

return (Checksum) ((CRC32C) checksum).clone();
}

return (Checksum) ((SdkCrc32C) checksum).clone();

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@

import static software.amazon.awssdk.http.auth.aws.internal.checksums.factory.CrtBasedChecksumProvider.longToByte;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.zip.Checksum;
import software.amazon.awssdk.annotations.SdkInternalApi;
import software.amazon.awssdk.crt.checksums.CRC32;
import software.amazon.awssdk.http.auth.aws.internal.checksums.factory.CrtBasedChecksumProvider;
import software.amazon.awssdk.http.auth.aws.internal.checksums.factory.SdkCrc32;

Expand Down Expand Up @@ -81,14 +81,9 @@ public void reset() {

private Checksum cloneChecksum(Checksum checksum) {
if (isCrtBasedChecksum) {
try {
Method method = checksum.getClass().getDeclaredMethod("clone");
return (Checksum) method.invoke(checksum);
} catch (ReflectiveOperationException e) {
throw new IllegalStateException("Could not clone checksum class " + checksum.getClass(), e);
}
} else {
return (Checksum) ((SdkCrc32) checksum).clone();
return (Checksum) ((CRC32) checksum).clone();
}

return (Checksum) ((SdkCrc32) checksum).clone();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@
import java.util.zip.Checksum;
import software.amazon.awssdk.annotations.SdkInternalApi;

/**
* An input-stream that takes a collection of checksums, and updates each checksum when it reads data.
*/
@SdkInternalApi
public class ChecksumInputStream extends FilterInputStream {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
import org.reactivestreams.Subscription;
import software.amazon.awssdk.annotations.SdkInternalApi;

/**
* A subscriber that takes a collection of checksums, and updates each checksum when it receives data.
*/
@SdkInternalApi
public final class ChecksumSubscriber implements Subscriber<ByteBuffer> {
private final CompletableFuture<Void> checksumming = new CompletableFuture<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
import software.amazon.awssdk.utils.BinaryUtils;
import software.amazon.awssdk.utils.Logger;

/**
* The default implementation of a v4-request-signer. It performs each step of the SigV4 signing process, but does not add the
* signature or auth information to the request itself.
* <p>
* All signing information, such as signature, signing key, canonical request, etc. is present in context object that is
* returned. This can be used by the caller to add the auth info to the request, such as adding the signature as a query
* parameter or building an authorization header using the signature and canonical request headers.
*/
@SdkInternalApi
public final class DefaultV4RequestSigner implements V4RequestSigner {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@
import software.amazon.awssdk.http.auth.aws.signer.Checksummer;

/**
* A "flexible" implementation of a checksummer.
* A "flexible" implementation of a checksummer. It takes a map of checksums and their header names, computes them efficiently
* by updating each checksum while reading the payload (once), and adds the computed checksum strings to the request using the
* given header names in the map. This should be used in cases where a (flexible) checksum algorithm is present during signing.
*/
@SdkInternalApi
public final class FlexibleChecksummer implements Checksummer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@

/**
* An implementation of a checksummer that simply passes along a computed value as a checksum. Specifically, this is used in
* the cases where the checksum is a pre-defined value that dictates specific behavior by the signer (such as aws-chunked
* payload signing, unsigned streaming with trailers, etc.).
* the cases where the checksum is a pre-defined value that dictates specific behavior by the signer, and flexible checksums is
* not enabled for the request (such as aws-chunked payload signing without trailers, unsigned streaming without trailers, etc.).
*/
@SdkInternalApi
public final class PrecomputedChecksummer implements Checksummer {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import static software.amazon.awssdk.checksums.DefaultChecksumAlgorithm.CRC32;
import static software.amazon.awssdk.checksums.DefaultChecksumAlgorithm.CRC32C;
import static software.amazon.awssdk.checksums.DefaultChecksumAlgorithm.MD5;
import static software.amazon.awssdk.checksums.DefaultChecksumAlgorithm.SHA1;
import static software.amazon.awssdk.checksums.DefaultChecksumAlgorithm.SHA256;

Expand All @@ -27,6 +28,7 @@
import software.amazon.awssdk.http.auth.aws.internal.checksums.ConstantChecksum;
import software.amazon.awssdk.http.auth.aws.internal.checksums.Crc32CChecksum;
import software.amazon.awssdk.http.auth.aws.internal.checksums.Crc32Checksum;
import software.amazon.awssdk.http.auth.aws.internal.checksums.Md5Checksum;
import software.amazon.awssdk.http.auth.aws.internal.checksums.SdkChecksum;
import software.amazon.awssdk.http.auth.aws.internal.checksums.Sha1Checksum;
import software.amazon.awssdk.http.auth.aws.internal.checksums.Sha256Checksum;
Expand All @@ -51,11 +53,7 @@ public static String checksumHeaderName(ChecksumAlgorithm checksumAlgorithm) {
}

/**
* Gets the SdkChecksum object based on the given ChecksumAlgorithm. Instances for CRC32C, CRC32 Algorithm will be added from
* CRT Java library once they are available in release.
*
* @param checksumAlgorithm Algorithm for calculating the checksum
* @return an SdkChecksum instance.
* Gets the SdkChecksum object based on the given ChecksumAlgorithm.
*/
public static SdkChecksum fromChecksumAlgorithm(ChecksumAlgorithm checksumAlgorithm) {
if (SHA256.algorithmId().equals(checksumAlgorithm.algorithmId())) {
Expand All @@ -74,6 +72,10 @@ public static SdkChecksum fromChecksumAlgorithm(ChecksumAlgorithm checksumAlgori
return new Crc32CChecksum();
}

if (MD5.algorithmId().equals(checksumAlgorithm.algorithmId())) {
return new Md5Checksum();
}

if (CONSTANT_CHECKSUM.equals(checksumAlgorithm.algorithmId())) {
return new ConstantChecksum(((ConstantChecksumAlgorithm) checksumAlgorithm).value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ static Checksummer create() {
}

/**
* Get a flexible checksummer that uses the given checksum-algorithm and the default.
* Get a flexible checksummer that performs two checksums: the given checksum-algorithm and the default (sha256).
*/
static Checksummer create(ChecksumAlgorithm checksumAlgorithm) {
if (checksumAlgorithm != null) {
Expand All @@ -63,14 +63,15 @@ static Checksummer create(ChecksumAlgorithm checksumAlgorithm) {
}

/**
* Get a flexible checksummer that uses the given checksum-algorithm and the default.
* Get a precomputed checksummer that results the given checksum string.
*/
static Checksummer create(String checksum) {
return new PrecomputedChecksummer(() -> checksum);
}

/**
* Get a flexible checksummer that uses the given checksum-algorithm and the default.
* Get a flexible checksummer that performs two checksums: the given checksum-algorithm and a precomputed checksum from the
* given checksum string.
*/
static Checksummer create(String checksum, ChecksumAlgorithm checksumAlgorithm) {
if (checksumAlgorithm != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,23 @@
@SdkProtectedApi
@Immutable
public final class V4Context {
private final String checksum;
private final String contentHash;
private final byte[] signingKey;
private final String signature;
private final V4CanonicalRequest canonicalRequest;
private final SdkHttpRequest.Builder signedRequest;

public V4Context(String contentHash, byte[] signingKey, String signature,
V4CanonicalRequest canonicalRequest, SdkHttpRequest.Builder signedRequest) {
this.checksum = contentHash;
this.contentHash = contentHash;
this.signingKey = signingKey.clone();
this.signature = signature;
this.canonicalRequest = canonicalRequest;
this.signedRequest = signedRequest;
}

public String getContentChecksum() {
return checksum;
public String getContentHash() {
return contentHash;
}

public byte[] getSigningKey() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ public void sign_WithPayloadSigningFalseAndChunkEncodingTrueWithoutTrailer_Throw
}

@Test
public void sign_withFlexibleChecksum_DelegatesToFlexibleChecksummer() {
public void sign_withChecksumAlgorithm_DelegatesToChecksummerWithThatChecksumAlgorithm() {
SyncSignRequest<? extends AwsCredentialsIdentity> request = generateBasicRequest(
AwsCredentialsIdentity.create("access", "secret"),
httpRequest -> {
Expand All @@ -250,15 +250,14 @@ public void sign_withFlexibleChecksum_DelegatesToFlexibleChecksummer() {
}

@Test
public void sign_withFlexibleChecksumAndPayloadSigningFalse_DelegatesToFlexibleChecksummer() {
public void sign_withChecksumAlgorithmAndPayloadSigningFalse_DelegatesToChecksummerWithThatChecksumAlgorithm() {
SyncSignRequest<? extends AwsCredentialsIdentity> request = generateBasicRequest(
AwsCredentialsIdentity.create("access", "secret"),
httpRequest -> {
},
signRequest -> signRequest
.putProperty(CHECKSUM_ALGORITHM, CRC32)
.putProperty(PAYLOAD_SIGNING_ENABLED, false)

);

SyncSignedRequest signedRequest = signer.sign(request);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.http.auth.aws.internal.signer;

import static org.junit.jupiter.api.Assertions.assertEquals;

import io.reactivex.Flowable;
import java.io.ByteArrayInputStream;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import org.junit.jupiter.api.Test;
import org.reactivestreams.Publisher;
import software.amazon.awssdk.http.ContentStreamProvider;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;

public class DefaultChecksummerTest {

ContentStreamProvider payload = () -> new ByteArrayInputStream("foo".getBytes(StandardCharsets.UTF_8));
Publisher<ByteBuffer> payloadAsync = Flowable.just(ByteBuffer.wrap("foo".getBytes(StandardCharsets.UTF_8)));


SdkHttpRequest.Builder request = SdkHttpRequest.builder()
.uri(URI.create("https://localhost"))
.method(SdkHttpMethod.GET);

@Test
public void checksummer_shouldAddSha256ChecksumToAmzContentSha256Header() {
DefaultChecksummer checksummer = new DefaultChecksummer();
SdkHttpRequest expectedRequest = request
.putHeader("x-amz-content-sha256", "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae")
.build();

checksummer.checksum(payload, request);

assertEquals(expectedRequest.toString(), request.build().toString());
}

@Test
public void checksummerAsync_shouldAddSha256ChecksumToAmzContentSha256Header() {
DefaultChecksummer checksummer = new DefaultChecksummer();
SdkHttpRequest expectedRequest = request
.putHeader("x-amz-content-sha256", "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae")
.build();

checksummer.checksum(payloadAsync, request);

assertEquals(expectedRequest.toString(), request.build().toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package software.amazon.awssdk.http.auth.aws.internal.signer;

import static java.time.ZoneOffset.UTC;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static software.amazon.awssdk.utils.BinaryUtils.toHex;

import java.net.URI;
import java.time.Clock;
import java.time.Instant;
import org.junit.jupiter.api.Test;
import software.amazon.awssdk.http.SdkHttpMethod;
import software.amazon.awssdk.http.SdkHttpRequest;
import software.amazon.awssdk.http.auth.aws.signer.CredentialScope;
import software.amazon.awssdk.http.auth.aws.signer.V4Context;
import software.amazon.awssdk.http.auth.aws.signer.V4Properties;
import software.amazon.awssdk.http.auth.aws.signer.V4RequestSigner;
import software.amazon.awssdk.identity.spi.AwsCredentialsIdentity;

public class DefaultRequestSignerTest {

V4Properties v4Properties = V4Properties.builder()
.credentials(AwsCredentialsIdentity.create("foo", "bar"))
.credentialScope(new CredentialScope("baz", "qux", Instant.EPOCH))
.signingClock(Clock.fixed(Instant.EPOCH, UTC))
.doubleUrlEncode(true)
.normalizePath(true)
.build();

V4RequestSigner requestSigner = new DefaultV4RequestSigner(v4Properties);

@Test
public void requestSigner_sign_shouldReturnSignedContext_butNotAddAnyAuthInfoToRequest() {
SdkHttpRequest.Builder request = SdkHttpRequest
.builder()
.uri(URI.create("https://localhost"))
.method(SdkHttpMethod.GET)
.putHeader("x-amz-content-sha256", "quux");
String expectedContentHash = "quux";
String expectedSigningKeyHex = "3d558b7a87b67996abc908071e0771a31b2a7977ab247144e60a6cba3356be1f";
String expectedSignature = "7557839280ea0ef5c4acc66e5670d0857ac4f491884b1b8031d4dea2fc33483c";
String expectedCanonicalRequestString = "GET\n/\n\n"
+ "host:localhost\nx-amz-content-sha256:quux\n\n"
+ "host;x-amz-content-sha256\nquux";
String expectedHost = "localhost";

V4Context v4Context = requestSigner.sign(request);

assertEquals(expectedContentHash, v4Context.getContentHash());
assertEquals(expectedSigningKeyHex, toHex(v4Context.getSigningKey()));
assertEquals(expectedSignature, v4Context.getSignature());
assertEquals(expectedCanonicalRequestString, v4Context.getCanonicalRequest().getCanonicalRequestString());
assertEquals(expectedHost, v4Context.getSignedRequest().firstMatchingHeader("Host").orElse(""));
}
}
Loading

0 comments on commit 3124558

Please sign in to comment.