/*******************************************************************************
* 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.
* *****************************************************************************
* __ _ _ ___
* ( )( \/\/ )/ __)
* /__\ \ / \__ \
* (_)(_) \/\/ (___/
*
* AWS SDK for .NET
*
*/
using System.IO;
using Amazon.Runtime.Internal.Util;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.IO;
namespace Amazon.Extensions.S3.Encryption.Util
{
///
/// A wrapper stream that decrypts the base stream using AES GCM algorithm as it
/// is being read.
///
public class AesGcmDecryptStream : DecryptStream
{
///
/// Constructor for initializing decryption stream
///
/// Original data stream
/// Key to be used for decryption
/// Nonce to be used for decryption
/// Tag size for the tag appended in the end of the stream
/// Additional associated data
public AesGcmDecryptStream(Stream baseStream, byte[] key, byte[] nonce, int tagSize, byte[] associatedText = null)
: base(new CipherStream(baseStream, AesGcmUtils.CreateCipher(false, key, tagSize, nonce, associatedText), null))
{
}
///
/// Reads a sequence of encrypted bytes from the current stream and advances the position
/// within the stream by the number of bytes read.
///
///
/// An array of bytes. When this method returns, the buffer contains the specified
/// byte array with the values between offset and (offset + count - 1) replaced
/// by the bytes read from the current source.
///
///
/// The zero-based byte offset in buffer at which to begin storing the data read
/// from the current stream.
///
///
/// The maximum number of bytes to be read from the current stream.
///
///
/// The total number of bytes read into the buffer. This can be less than the
/// number of bytes requested if that many bytes are not currently available,
/// or zero (0) if the end of the stream has been reached.
///
///
/// Underlying crypto exception wrapped in Amazon exception
///
public override int Read(byte[] buffer, int offset, int count)
{
try
{
return BaseStream.Read(buffer, offset, count);
}
catch (CryptoException cryptoException)
{
throw new AmazonCryptoException($"Failed to decrypt: {cryptoException.Message}", cryptoException);
}
}
#if AWS_ASYNC_API
///
/// Asynchronously reads a sequence of decrypted bytes from the current stream, advances
/// the position within the stream by the number of bytes read, and monitors
/// cancellation requests.
///
///
/// An array of bytes. When this method returns, the buffer contains the specified
/// byte array with the values between offset and (offset + count - 1) replaced
/// by the bytes read from the current source.
///
///
/// The zero-based byte offset in buffer at which to begin storing the data read
/// from the current stream.
///
///
/// The maximum number of bytes to be read from the current stream.
///
///
/// The token to monitor for cancellation requests. The default value is
/// System.Threading.CancellationToken.None.
///
///
/// A task that represents the asynchronous read operation. The value of the TResult
/// parameter contains the total number of bytes read into the buffer. This can be
/// less than the number of bytes requested if that many bytes are not currently
/// available, or zero (0) if the end of the stream has been reached.
///
///
/// Underlying crypto exception wrapped in Amazon exception
///
public override async System.Threading.Tasks.Task ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken)
{
try
{
var readBytes = await BaseStream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false);
return readBytes;
}
catch (CryptoException cryptoException)
{
throw new AmazonCryptoException($"Failed to decrypt: {cryptoException.Message}", cryptoException);
}
}
#endif
}
}