Esempio n. 1
0
        /// <summary>
        /// Calculates the length in bytes of a TrailingChecksumWrapperStream initialized
        /// with the given trailing headers and optional checksum
        /// </summary>
        /// <param name="trailingHeaders">Dictionary of trailing headers</param>
        /// <param name="checksumAlgorithm">Trailing checksum</param>
        /// <param name="baseStreamLength">Length of the base stream in bytes</param>
        /// <returns>Length of a TrailingChecksumWrapperStream with given parameters, in bytes</returns>
        public static long CalculateLength(IDictionary <string, string> trailingHeaders, CoreChecksumAlgorithm checksumAlgorithm, long baseStreamLength)
        {
            var prefixLength         = baseStreamLength.ToString("X", CultureInfo.InvariantCulture).Length;
            var trailingHeaderLength = 0;

            if (trailingHeaders != null)
            {
                foreach (var key in trailingHeaders.Keys)
                {
                    if (checksumAlgorithm != CoreChecksumAlgorithm.NONE && ChecksumUtils.GetChecksumHeaderKey(checksumAlgorithm) == key)
                    {
                        trailingHeaderLength += key.Length +
                                                CryptoUtilFactory.GetChecksumBase64Length(checksumAlgorithm) + HEADER_ROW_PADDING_LENGTH;
                    }
                    else
                    {
                        trailingHeaderLength += key.Length + trailingHeaders[key].Length + HEADER_ROW_PADDING_LENGTH;
                    }
                }
            }

            return(prefixLength +
                   NEWLINE_LENGTH +
                   baseStreamLength +
                   NEWLINE_LENGTH +
                   EMPTY_CHUNK_LENGTH +
                   trailingHeaderLength +
                   NEWLINE_LENGTH);
        }
Esempio n. 2
0
        /// <summary>
        /// Generates the trailing header content, assuming the rolling checksum is now finalized
        /// </summary>
        /// <returns>Trailing headers as a single string</returns>
        private string GenerateTrailingHeaderChunk()
        {
            var trailer = new StringBuilder();

            // End the data chunk
            trailer.Append(STREAM_NEWLINE);

            // Append a chunk of size 0
            trailer.Append(EMPTY_CHUNK);

            // Append trailing headers, including special handling for the checksum.
            // The order here must match the order of keys sent already in the X-Amz-Trailer header.
            foreach (var kvp in _trailingHeaders.OrderBy(kvp => kvp.Key))
            {
                if (_checksumAlgorithm != CoreChecksumAlgorithm.NONE && ChecksumUtils.GetChecksumHeaderKey(_checksumAlgorithm) == kvp.Key)
                {
                    // Use the calculated checksum, since it likely wasn't set in advance
                    trailer.Append($"{kvp.Key}:{Convert.ToBase64String(_hashAlgorithm.Hash)}{STREAM_NEWLINE}");
                }
                else
                {
                    trailer.Append($"{kvp.Key}:{kvp.Value}{STREAM_NEWLINE}");
                }
            }

            // Append a final trailing CRLF
            trailer.Append(STREAM_NEWLINE);

            return(trailer.ToString());
        }
Esempio n. 3
0
        /// <summary>
        /// Computes the total size of the data payload, including the chunk metadata
        /// and optional trailing headers. Called externally so as to be able to set
        /// the correct Content-Length header value.
        /// </summary>
        /// <param name="originalLength">Length of the wrapped stream</param>
        /// <param name="signatureLength">Length of the signature for each chunk, in bytes</param>
        /// <param name="trailingHeaders">Optional trailing headers</param>
        /// <param name="trailingChecksum">Optional checksum algorithm for a trailing header</param>
        /// <returns>Total size of the wrapped payload, in bytes</returns>
        public static long ComputeChunkedContentLength(long originalLength, int signatureLength, IDictionary <string, string> trailingHeaders, CoreChecksumAlgorithm trailingChecksum)
        {
            if (originalLength < 0)
            {
                throw new ArgumentOutOfRangeException("originalLength", "Expected 0 or greater value for originalLength.");
            }

            int  trailingHeaderLength = 0;
            long chunkedContentLength;

            // Calculate the size of the chunked content, before trailing headers/checksum
            if (originalLength == 0)
            {
                chunkedContentLength = CalculateChunkHeaderLength(0, signatureLength);
            }
            else
            {
                var maxSizeChunks  = originalLength / DefaultChunkSize;
                var remainingBytes = originalLength % DefaultChunkSize;

                chunkedContentLength = maxSizeChunks * CalculateChunkHeaderLength(DefaultChunkSize, signatureLength)
                                       + (remainingBytes > 0 ? CalculateChunkHeaderLength(remainingBytes, signatureLength) : 0)
                                       + CalculateChunkHeaderLength(0, signatureLength);
            }

            if (trailingHeaders?.Count > 0)
            {
                foreach (var key in trailingHeaders.Keys)
                {
                    // If the trailing checksum key is already in dictionary, use the
                    // expected length since the checksum value may not be set yet.
                    if (trailingChecksum != CoreChecksumAlgorithm.NONE && ChecksumUtils.GetChecksumHeaderKey(trailingChecksum) == key)
                    {
                        trailingHeaderLength += key.Length +
                                                CryptoUtilFactory.GetChecksumBase64Length(trailingChecksum) + HEADER_ROW_PADDING_LENGTH;
                    }
                    else
                    {
                        trailingHeaderLength += key.Length + trailingHeaders[key].Length + HEADER_ROW_PADDING_LENGTH;
                    }
                }

                trailingHeaderLength += TRAILING_HEADER_SIGNATURE_KEY.Length + signatureLength + HEADER_ROW_PADDING_LENGTH;
            }

            return(chunkedContentLength + trailingHeaderLength);
        }
Esempio n. 4
0
        /// <summary>
        /// Attempts to select and then calculate the checksum for a request
        /// </summary>
        /// <param name="request">Request to calculate the checksum for</param>
        /// <param name="checksumAlgorithm">Checksum algorithm to use, specified on the request using a service-specific enum</param>
        /// <param name="fallbackToMD5">If checksumAlgorithm is <see cref="CoreChecksumAlgorithm.NONE"/>,
        /// this flag controls whether or not to fallback to using a MD5 to generate a checksum.
        /// </param>
        public static void SetRequestChecksum(IRequest request, string checksumAlgorithm, bool fallbackToMD5 = true)
        {
            var coreChecksumAlgoritm = ChecksumUtils.ConvertToCoreChecksumAlgorithm(checksumAlgorithm);

            if (coreChecksumAlgoritm == CoreChecksumAlgorithm.NONE)
            {
                if (fallbackToMD5)
                {
                    SetRequestChecksumMD5(request);
                }
                return;
            }

            var checksumHeaderKey = GetChecksumHeaderKey(coreChecksumAlgoritm);

            // If the user provided a precalculated header for this checksum, don't recalculate it
            if (request.Headers.TryGetValue(checksumHeaderKey, out var checksumHeaderValue))
            {
                if (!string.IsNullOrEmpty(checksumHeaderValue))
                {
                    return;
                }
            }

            if (request.UseChunkEncoding || (request.DisablePayloadSigning ?? false))
            {
                // Add the checksum key to the trailing headers, but not the value
                // because we won't know it until the wrapper stream is fully read,
                // and we only want to read the wrapper stream once.
                //
                // The header key is required upfront for calculating the total length of
                // the wrapper stream, which we need to send as the Content-Length header
                // before the wrapper stream is transmitted.
                request.TrailingHeaders.Add(checksumHeaderKey, string.Empty);
                request.SelectedChecksum = coreChecksumAlgoritm;
            }
            else // calculate and set the checksum in the request headers
            {
                request.Headers[checksumHeaderKey] = CalculateChecksumForRequest(CryptoUtilFactory.GetChecksumInstance(coreChecksumAlgoritm), request);
            }
        }
Esempio n. 5
0
        /// <summary>
        /// Constructs the signed trailing headers, optionally including
        /// the selected checksum for this stream's data. For example:
        /// trailing-header-A:value CRLF
        /// trailing-header-B:value CRLF
        /// x-amz-trailer-signature:signature_value CRLF
        /// CRLF
        /// </summary>
        /// <returns>Stream chunk containing the trailing headers and their signature</returns>
        private string ConstructSignedTrailersChunk()
        {
            // If the trailing headers included a trailing checksum, set the hash value
            if (_hashAlgorithm != null)
            {
                _hashAlgorithm.TransformFinalBlock(new byte[0], 0, 0);
                _trailingHeaders[ChecksumUtils.GetChecksumHeaderKey(_trailingChecksum)] = Convert.ToBase64String(_hashAlgorithm.Hash);
            }

            string chunkSignature;

            if (HeaderSigningResult is AWS4SigningResult)
            {
                var sortedTrailingHeaders        = AWS4Signer.SortAndPruneHeaders(_trailingHeaders);
                var canonicalizedTrailingHeaders = AWS4Signer.CanonicalizeHeaders(sortedTrailingHeaders);

                var chunkStringToSign =
                    TRAILING_HEADER_STRING_TO_SIGN_PREFIX + "\n" +
                    HeaderSigningResult.ISO8601DateTime + "\n" +
                    HeaderSigningResult.Scope + "\n" +
                    PreviousChunkSignature + "\n" +
                    AWSSDKUtils.ToHex(AWS4Signer.ComputeHash(canonicalizedTrailingHeaders), true);

                chunkSignature = AWSSDKUtils.ToHex(AWS4Signer.SignBlob(((AWS4SigningResult)HeaderSigningResult).GetSigningKey(), chunkStringToSign), true);
            }
            else // SigV4a
            {
                chunkSignature = Sigv4aSigner.SignTrailingHeaderChunk(_trailingHeaders, PreviousChunkSignature, (AWS4aSigningResult)HeaderSigningResult).PadRight(V4A_SIGNATURE_LENGTH, '*');
            }

            var chunk = new StringBuilder();

            // The order here must match the order of keys sent already in the X-Amz-Trailer header.
            foreach (var kvp in _trailingHeaders.OrderBy(kvp => kvp.Key))
            {
                chunk.Append($"{kvp.Key}:{kvp.Value}{STREAM_NEWLINE}");
            }
            chunk.Append($"{TRAILING_HEADER_SIGNATURE_KEY}:{chunkSignature}{STREAM_NEWLINE}");
            chunk.Append(STREAM_NEWLINE);
            return(chunk.ToString());
        }