/// <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()); }
/// <summary> /// Computes the derived signature for a chunk of data of given length in the input buffer, /// placing a formatted chunk with headers, signature and data into the output buffer /// ready for streaming back to the consumer. /// </summary> /// <param name="dataLen"></param> private void ConstructOutputBufferChunk(int dataLen) { // if the input wasn't sufficient to fill the buffer, size it // down to make the subseqent hashing/computations easier since // they don't take any length arguments if (dataLen > 0 && dataLen < _inputBuffer.Length) { var temp = new byte[dataLen]; Buffer.BlockCopy(_inputBuffer, 0, temp, 0, dataLen); _inputBuffer = temp; } var chunkHeader = new StringBuilder(); // variable-length size of the embedded chunk data in hex chunkHeader.Append(dataLen.ToString("X", CultureInfo.InvariantCulture)); string chunkSignature = ""; if (HeaderSigningResult is AWS4aSigningResult v4aHeaderSigningResult) { if (dataLen == 0) // _inputBuffer still contains previous chunk, but this is the final 0 content chunk so sign null { chunkSignature = Sigv4aSigner.SignChunk(null, PreviousChunkSignature, v4aHeaderSigningResult); } else { chunkSignature = Sigv4aSigner.SignChunk(new MemoryStream(_inputBuffer), PreviousChunkSignature, v4aHeaderSigningResult); } } else if (HeaderSigningResult is AWS4SigningResult v4HeaderSingingResult) // SigV4 { var chunkStringToSign = BuildChunkedStringToSign(CHUNK_STRING_TO_SIGN_PREFIX, v4HeaderSingingResult.ISO8601DateTime, v4HeaderSingingResult.Scope, PreviousChunkSignature, dataLen, _inputBuffer); chunkSignature = AWSSDKUtils.ToHex(AWS4Signer.SignBlob(v4HeaderSingingResult.GetSigningKey(), chunkStringToSign), true); } // For Sigv4a the chunk signature must be padded when being appended to the chunk metadata, // but not when being used as the input for the next chunk PreviousChunkSignature = chunkSignature; if (HeaderSigningResult is AWS4aSigningResult) { chunkHeader.Append(CHUNK_SIGNATURE_HEADER + chunkSignature.PadRight(V4A_SIGNATURE_LENGTH, '*')); } else // SigV4 { chunkHeader.Append(CHUNK_SIGNATURE_HEADER + chunkSignature); } chunkHeader.Append(CLRF); try { var header = Encoding.UTF8.GetBytes(chunkHeader.ToString()); var trailer = Encoding.UTF8.GetBytes(CLRF); var writePos = 0; Buffer.BlockCopy(header, 0, _outputBuffer, writePos, header.Length); writePos += header.Length; if (dataLen > 0) { Buffer.BlockCopy(_inputBuffer, 0, _outputBuffer, writePos, dataLen); writePos += dataLen; } Buffer.BlockCopy(trailer, 0, _outputBuffer, writePos, trailer.Length); _outputBufferPos = 0; _outputBufferDataLen = header.Length + dataLen + trailer.Length; } catch (Exception e) { throw new AmazonClientException("Unable to sign the chunked data. " + e.Message, e); } }