Skip to content

Commit

Permalink
Use same request timestamp for dates in signing
Browse files Browse the repository at this point in the history
  • Loading branch information
ennru committed May 9, 2019
1 parent 156e752 commit 0eb3bfc
Show file tree
Hide file tree
Showing 5 changed files with 27 additions and 21 deletions.
6 changes: 3 additions & 3 deletions s3/src/main/scala/akka/stream/alpakka/s3/auth/Signer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,18 @@ import com.amazonaws.auth._
private[alpakka] object Signer {
private val dateFormatter = DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmssX")

def signedRequest(request: HttpRequest, key: SigningKey, date: ZonedDateTime = ZonedDateTime.now(ZoneOffset.UTC))(
def signedRequest(request: HttpRequest, key: SigningKey)(
implicit mat: Materializer
): Future[HttpRequest] = {
import mat.executionContext
val hashedBody = request.entity.dataBytes.runWith(digest()).map(hash => encodeHex(hash.toArray))

hashedBody.map { hb =>
val headersToAdd = Vector(RawHeader("x-amz-date", date.format(dateFormatter)),
val headersToAdd = Vector(RawHeader("x-amz-date", key.requestDate.format(dateFormatter)),
RawHeader("x-amz-content-sha256", hb)) ++ sessionHeader(key.credProvider)
val reqWithHeaders = request.withHeaders(request.headers ++ headersToAdd)
val cr = CanonicalRequest.from(reqWithHeaders)
val authHeader = authorizationHeader("AWS4-HMAC-SHA256", key, date, cr)
val authHeader = authorizationHeader("AWS4-HMAC-SHA256", key, key.requestDate, cr)
reqWithHeaders.withHeaders(reqWithHeaders.headers :+ authHeader)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@

package akka.stream.alpakka.s3.auth

import java.time.LocalDate
import java.time.{LocalDate, ZonedDateTime}
import java.time.format.DateTimeFormatter

import javax.crypto.Mac
import javax.crypto.spec.SecretKeySpec
import com.amazonaws.auth.{AWSCredentials AmzAWSCredentials, AWSCredentialsProvider}
Expand All @@ -16,7 +17,8 @@ private[alpakka] final case class CredentialScope(date: LocalDate, awsRegion: St
def scopeString = s"$formattedDate/$awsRegion/$awsService/aws4_request"
}

private[alpakka] final case class SigningKey(credProvider: AWSCredentialsProvider,
private[alpakka] final case class SigningKey(requestDate: ZonedDateTime,
credProvider: AWSCredentialsProvider,
scope: CredentialScope,
algorithm: String = "HmacSHA256") {

Expand Down
19 changes: 10 additions & 9 deletions s3/src/main/scala/akka/stream/alpakka/s3/impl/S3Stream.scala
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

package akka.stream.alpakka.s3.impl

import java.time.{LocalDate, ZoneOffset}
import java.time.{LocalDate, ZoneOffset, ZonedDateTime}

import scala.collection.immutable.Seq
import scala.concurrent.{ExecutionContext, Future}
Expand Down Expand Up @@ -61,10 +61,14 @@ private[alpakka] final class S3Stream(settings: S3Settings)(implicit system: Act
implicit val conf = settings
val MinChunkSize = 5242880 //in bytes
// def because tokens can expire
def signingKey = SigningKey(
settings.credentialsProvider,
CredentialScope(LocalDate.now(ZoneOffset.UTC), settings.s3RegionProvider.getRegion, "s3")
)
def signingKey = {
val requestDate: ZonedDateTime = ZonedDateTime.now(ZoneOffset.UTC)
SigningKey(
requestDate,
settings.credentialsProvider,
CredentialScope(requestDate.toLocalDate, settings.s3RegionProvider.getRegion, "s3")
)
}

def download(s3Location: S3Location,
range: Option[ByteRange],
Expand Down Expand Up @@ -262,9 +266,6 @@ private[alpakka] final class S3Stream(settings: S3Settings)(implicit system: Act
sse.fold[Seq[HttpHeader]](Seq.empty) { _.headersFor(InitiateMultipartUpload) }
))

// use the same key for all sub-requests (chunks)
val key: SigningKey = signingKey

val headers: S3Headers = S3Headers(sse.fold[Seq[HttpHeader]](Seq.empty) { _.headersFor(UploadPart) })

SplitAfterSize(chunkSize)(Flow.apply[ByteString])
Expand All @@ -277,7 +278,7 @@ private[alpakka] final class S3Stream(settings: S3Settings)(implicit system: Act
uploadPartRequest(uploadInfo, chunkIndex, chunkedPayload.data, chunkedPayload.size, headers)
(partRequest, (uploadInfo, chunkIndex))
}
.mapAsync(parallelism) { case (req, info) => Signer.signedRequest(req, key).zip(Future.successful(info)) }
.mapAsync(parallelism) { case (req, info) => Signer.signedRequest(req, signingKey).zip(Future.successful(info)) }
}

private def getChunkBuffer(chunkSize: Int) = settings.bufferType match {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ class SignerSpec(_system: ActorSystem) extends TestKit(_system) with FlatSpecLik
new BasicAWSCredentials("AKIDEXAMPLE", "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY")
)

def scope(date: LocalDate) = CredentialScope(date, "us-east-1", "iam")
def signingKey(dateTime: ZonedDateTime) = SigningKey(credentials, scope(dateTime.toLocalDate))
def signingKey(dateTime: ZonedDateTime) =
SigningKey(dateTime, credentials, CredentialScope(dateTime.toLocalDate, "us-east-1", "iam"))

val cr = CanonicalRequest(
"GET",
Expand Down Expand Up @@ -58,7 +58,7 @@ class SignerSpec(_system: ActorSystem) extends TestKit(_system) with FlatSpecLik

val date = LocalDateTime.of(2015, 8, 30, 12, 36, 0).atZone(ZoneOffset.UTC)
val srFuture =
Signer.signedRequest(req, signingKey(date), date)
Signer.signedRequest(req, signingKey(date))
whenReady(srFuture) { signedRequest =>
signedRequest should equal(
HttpRequest(HttpMethods.GET)
Expand All @@ -83,7 +83,7 @@ class SignerSpec(_system: ActorSystem) extends TestKit(_system) with FlatSpecLik

val date = LocalDateTime.of(2017, 12, 31, 12, 36, 0).atZone(ZoneOffset.UTC)
val srFuture =
Signer.signedRequest(req, signingKey(date), date)
Signer.signedRequest(req, signingKey(date))

whenReady(srFuture) { signedRequest =>
signedRequest.getHeader("x-amz-date").get.value should equal("20171231T123600Z")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@

package akka.stream.alpakka.s3.auth

import java.time.LocalDate
import java.time.{LocalDate, ZoneId, ZonedDateTime}

import com.amazonaws.auth.{AWSStaticCredentialsProvider, BasicAWSCredentials}
import org.scalatest.{FlatSpec, Matchers}

Expand All @@ -15,8 +16,10 @@ class SigningKeySpec extends FlatSpec with Matchers {
new BasicAWSCredentials("AKIDEXAMPLE", "wJalrXUtnFEMI/K7MDENG+bPxRfiCYEXAMPLEKEY")
)

val scope = CredentialScope(LocalDate.of(2015, 8, 30), "us-east-1", "iam")
val signingKey = SigningKey(credentials, scope)
val signingKey = {
val requestDate = ZonedDateTime.of(2015, 8, 30, 1, 2, 3, 4, ZoneId.of("UTC"))
SigningKey(requestDate, credentials, CredentialScope(requestDate.toLocalDate, "us-east-1", "iam"))
}

it should "produce a signing key" in {
val expected: Array[Byte] = Array(196, 175, 177, 204, 87, 113, 216, 113, 118, 58, 57, 62, 68, 183, 3, 87, 27, 85,
Expand Down

1 comment on commit 0eb3bfc

@2m
Copy link
Member

@2m 2m commented on 0eb3bfc May 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Please sign in to comment.