All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jvmMain.aws.smithy.kotlin.runtime.auth.awssigning.crt.CrtAwsSigner.kt Maven / Gradle / Ivy

There is a newer version: 1.3.12
Show newest version
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */
package aws.smithy.kotlin.runtime.auth.awssigning.crt

import aws.smithy.kotlin.runtime.auth.awscredentials.Credentials
import aws.smithy.kotlin.runtime.auth.awssigning.*
import aws.smithy.kotlin.runtime.crt.toSignableCrtRequest
import aws.smithy.kotlin.runtime.crt.update
import aws.smithy.kotlin.runtime.http.Headers
import aws.smithy.kotlin.runtime.http.request.HttpRequest
import aws.smithy.kotlin.runtime.http.request.toBuilder
import aws.smithy.kotlin.runtime.telemetry.logging.debug
import aws.smithy.kotlin.runtime.time.epochMilliseconds
import kotlin.coroutines.coroutineContext
import aws.sdk.kotlin.crt.auth.credentials.Credentials as CrtCredentials
import aws.sdk.kotlin.crt.auth.signing.AwsSignatureType as CrtSignatureType
import aws.sdk.kotlin.crt.auth.signing.AwsSignedBodyHeaderType as CrtSignedBodyHeaderType
import aws.sdk.kotlin.crt.auth.signing.AwsSigner as CrtSigner
import aws.sdk.kotlin.crt.auth.signing.AwsSigningAlgorithm as CrtSigningAlgorithm
import aws.sdk.kotlin.crt.auth.signing.AwsSigningConfig as CrtSigningConfig
import aws.sdk.kotlin.crt.http.Headers as CrtHeaders

private const val S3_EXPRESS_HEADER_NAME = "X-Amz-S3session-Token"

public object CrtAwsSigner : AwsSigner {
    override suspend fun sign(request: HttpRequest, config: AwsSigningConfig): AwsSigningResult {
        val isUnsigned = config.hashSpecification is HashSpecification.UnsignedPayload
        val isAwsChunked = request.headers.contains("Content-Encoding", "aws-chunked")
        val isS3Express = request.headers.contains(S3_EXPRESS_HEADER_NAME)

        val requestBuilder = request.toBuilder()

        val crtConfig = config.toCrtSigningConfig().toBuilder()
        if (isS3Express) {
            crtConfig.algorithm = CrtSigningAlgorithm.SIGV4_S3EXPRESS
            crtConfig.omitSessionToken = false
            requestBuilder.headers.remove(S3_EXPRESS_HEADER_NAME) // CRT signer fails if this header is already present
        }

        val crtRequest = requestBuilder.build().toSignableCrtRequest(isUnsigned, isAwsChunked)

        val crtResult = CrtSigner.sign(crtRequest, crtConfig.build())
        coroutineContext.debug { "Calculated signature: ${crtResult.signature.decodeToString()}" }

        val crtSignedResult = checkNotNull(crtResult.signedRequest) { "Signed request unexpectedly null" }

        requestBuilder.update(crtSignedResult)
        return AwsSigningResult(requestBuilder.build(), crtResult.signature)
    }

    override suspend fun signChunk(
        chunkBody: ByteArray,
        prevSignature: ByteArray,
        config: AwsSigningConfig,
    ): AwsSigningResult {
        val crtConfig = config.toCrtSigningConfig()
        val crtResult = CrtSigner.signChunk(chunkBody, prevSignature, crtConfig)
        coroutineContext.debug { "Calculated signature: ${crtResult.signature.decodeToString()}" }

        return AwsSigningResult(Unit, crtResult.signature)
    }

    override suspend fun signChunkTrailer(
        trailingHeaders: Headers,
        prevSignature: ByteArray,
        config: AwsSigningConfig,
    ): AwsSigningResult {
        val crtConfig = config.toCrtSigningConfig()
        val crtTrailingHeaders = trailingHeaders.toCrtHeaders()

        val crtResult = CrtSigner.signChunkTrailer(crtTrailingHeaders, prevSignature, crtConfig)
        return AwsSigningResult(Unit, crtResult.signature)
    }
}

private fun AwsSignatureType.toCrtSignatureType() = when (this) {
    AwsSignatureType.HTTP_REQUEST_CHUNK -> CrtSignatureType.HTTP_REQUEST_CHUNK
    AwsSignatureType.HTTP_REQUEST_EVENT -> CrtSignatureType.HTTP_REQUEST_EVENT
    AwsSignatureType.HTTP_REQUEST_VIA_HEADERS -> CrtSignatureType.HTTP_REQUEST_VIA_HEADERS
    AwsSignatureType.HTTP_REQUEST_VIA_QUERY_PARAMS -> CrtSignatureType.HTTP_REQUEST_VIA_QUERY_PARAMS
    AwsSignatureType.HTTP_REQUEST_TRAILING_HEADERS -> CrtSignatureType.HTTP_REQUEST_TRAILING_HEADERS
}

private fun AwsSignedBodyHeader.toCrtSignedBodyHeaderType() = when (this) {
    AwsSignedBodyHeader.NONE -> CrtSignedBodyHeaderType.NONE
    AwsSignedBodyHeader.X_AMZ_CONTENT_SHA256 -> CrtSignedBodyHeaderType.X_AMZ_CONTENT_SHA256
}

private fun AwsSigningAlgorithm.toCrtSigningAlgorithm() = when (this) {
    AwsSigningAlgorithm.SIGV4 -> CrtSigningAlgorithm.SIGV4
    AwsSigningAlgorithm.SIGV4_ASYMMETRIC -> CrtSigningAlgorithm.SIGV4_ASYMMETRIC
}

private suspend fun AwsSigningConfig.toCrtSigningConfig(): CrtSigningConfig {
    val src = this
    return CrtSigningConfig.build {
        region = src.region
        service = src.service
        date = src.signingDate.epochMilliseconds
        algorithm = src.algorithm.toCrtSigningAlgorithm()
        shouldSignHeader = src.shouldSignHeader
        signatureType = src.signatureType.toCrtSignatureType()
        useDoubleUriEncode = src.useDoubleUriEncode
        normalizeUriPath = src.normalizeUriPath
        omitSessionToken = src.omitSessionToken
        signedBodyValue = src.hashSpecification.toCrtSignedBodyValue()
        signedBodyHeader = src.signedBodyHeader.toCrtSignedBodyHeaderType()
        credentials = src.credentials.toCrtCredentials()
        expirationInSeconds = src.expiresAfter?.inWholeSeconds ?: 0
    }
}

private fun Credentials.toCrtCredentials() = CrtCredentials(accessKeyId, secretAccessKey, sessionToken)

private fun HashSpecification.toCrtSignedBodyValue(): String? = when (this) {
    is HashSpecification.CalculateFromPayload -> null
    is HashSpecification.HashLiteral -> hash
}

private fun Headers.toCrtHeaders(): CrtHeaders {
    val headersBuilder = aws.sdk.kotlin.crt.http.HeadersBuilder()
    forEach(headersBuilder::appendAll)
    return headersBuilder.build()
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy