@@ -938,6 +938,21 @@ namespace Aws
938938 handle->SetContentType (getObjectOutcome.GetResult ().GetContentType ());
939939 handle->ChangePartToCompleted (partState, getObjectOutcome.GetResult ().GetETag ());
940940 getObjectOutcome.GetResult ().GetBody ().flush ();
941+
942+ if (!ValidateDownloadChecksum (handle)) {
943+ Aws::Client::AWSError<Aws::S3::S3Errors> checksumError (
944+ Aws::S3::S3Errors::INTERNAL_FAILURE,
945+ " ChecksumMismatch" ,
946+ " Downloaded file checksum does not match expected checksum" ,
947+ false );
948+ handle->ChangePartToFailed (partState);
949+ handle->UpdateStatus (TransferStatus::FAILED);
950+ handle->SetError (checksumError);
951+ TriggerErrorCallback (handle, checksumError);
952+ TriggerTransferStatusUpdatedCallback (handle);
953+ return ;
954+ }
955+
941956 handle->UpdateStatus (TransferStatus::COMPLETED);
942957 }
943958 else
@@ -1000,6 +1015,21 @@ namespace Aws
10001015 handle->SetContentType (headObjectOutcome.GetResult ().GetContentType ());
10011016 handle->SetMetadata (headObjectOutcome.GetResult ().GetMetadata ());
10021017 handle->SetEtag (headObjectOutcome.GetResult ().GetETag ());
1018+
1019+ if (headObjectOutcome.GetResult ().GetChecksumType () == S3::Model::ChecksumType::FULL_OBJECT) {
1020+ if (!headObjectOutcome.GetResult ().GetChecksumCRC32 ().empty ()) {
1021+ handle->SetChecksum (headObjectOutcome.GetResult ().GetChecksumCRC32 ());
1022+ } else if (!headObjectOutcome.GetResult ().GetChecksumCRC32C ().empty ()) {
1023+ handle->SetChecksum (headObjectOutcome.GetResult ().GetChecksumCRC32C ());
1024+ } else if (!headObjectOutcome.GetResult ().GetChecksumSHA256 ().empty ()) {
1025+ handle->SetChecksum (headObjectOutcome.GetResult ().GetChecksumSHA256 ());
1026+ } else if (!headObjectOutcome.GetResult ().GetChecksumSHA1 ().empty ()) {
1027+ handle->SetChecksum (headObjectOutcome.GetResult ().GetChecksumSHA1 ());
1028+ } else if (!headObjectOutcome.GetResult ().GetChecksumCRC64NVME ().empty ()) {
1029+ handle->SetChecksum (headObjectOutcome.GetResult ().GetChecksumCRC64NVME ());
1030+ }
1031+ }
1032+
10031033 /* When bucket versioning is suspended, head object will return "null" for unversioned object.
10041034 * Send following GetObject with "null" as versionId will result in 403 access denied error if your IAM role or policy
10051035 * doesn't have GetObjectVersion permission.
@@ -1240,6 +1270,22 @@ namespace Aws
12401270 if (failedParts.size () == 0 && handle->GetBytesTransferred () == handle->GetBytesTotalSize ())
12411271 {
12421272 outcome.GetResult ().GetBody ().flush ();
1273+
1274+ // Validate checksum if available
1275+ if (!ValidateDownloadChecksum (handle)) {
1276+ Aws::Client::AWSError<Aws::S3::S3Errors> checksumError (
1277+ Aws::S3::S3Errors::INTERNAL_FAILURE,
1278+ " ChecksumMismatch" ,
1279+ " Downloaded file checksum does not match expected checksum" ,
1280+ false );
1281+ handle->UpdateStatus (TransferStatus::FAILED);
1282+ handle->SetError (checksumError);
1283+ TriggerErrorCallback (handle, checksumError);
1284+ TriggerTransferStatusUpdatedCallback (handle);
1285+ partState->SetDownloadPartStream (nullptr );
1286+ return ;
1287+ }
1288+
12431289 handle->UpdateStatus (TransferStatus::COMPLETED);
12441290 }
12451291 else
@@ -1594,5 +1640,56 @@ namespace Aws
15941640 partFunc->second (part, state->GetChecksum ());
15951641 }
15961642 }
1643+
1644+ bool TransferManager::ValidateDownloadChecksum (const std::shared_ptr<TransferHandle>& handle) {
1645+ if (handle->GetChecksum ().empty () || handle->GetTargetFilePath ().empty ()) {
1646+ return true ;
1647+ }
1648+
1649+ std::shared_ptr<Aws::Utils::Crypto::Hash> hashCalculator;
1650+ if (m_transferConfig.checksumAlgorithm == S3::Model::ChecksumAlgorithm::CRC32) {
1651+ hashCalculator = Aws::MakeShared<Aws::Utils::Crypto::CRC32>(" TransferManager" );
1652+ } else if (m_transferConfig.checksumAlgorithm == S3::Model::ChecksumAlgorithm::CRC32C) {
1653+ hashCalculator = Aws::MakeShared<Aws::Utils::Crypto::CRC32C>(" TransferManager" );
1654+ } else if (m_transferConfig.checksumAlgorithm == S3::Model::ChecksumAlgorithm::SHA1) {
1655+ hashCalculator = Aws::MakeShared<Aws::Utils::Crypto::Sha1>(" TransferManager" );
1656+ } else if (m_transferConfig.checksumAlgorithm == S3::Model::ChecksumAlgorithm::SHA256) {
1657+ hashCalculator = Aws::MakeShared<Aws::Utils::Crypto::Sha256>(" TransferManager" );
1658+ } else {
1659+ hashCalculator = Aws::MakeShared<Aws::Utils::Crypto::CRC64>(" TransferManager" );
1660+ }
1661+
1662+ auto fileStream = Aws::MakeShared<Aws::FStream>(" TransferManager" ,
1663+ handle->GetTargetFilePath ().c_str (),
1664+ std::ios_base::in | std::ios_base::binary);
1665+
1666+ if (!fileStream->good ()) {
1667+ AWS_LOGSTREAM_ERROR (CLASS_TAG, " Transfer handle [" << handle->GetId ()
1668+ << " ] Failed to open downloaded file for checksum validation: " << handle->GetTargetFilePath ());
1669+ return false ;
1670+ }
1671+
1672+ unsigned char buffer[8192 ];
1673+ while (fileStream->read (reinterpret_cast <char *>(buffer), sizeof (buffer)) || fileStream->gcount () > 0 ) {
1674+ hashCalculator->Update (buffer, static_cast <size_t >(fileStream->gcount ()));
1675+ }
1676+
1677+ auto hashResult = hashCalculator->GetHash ();
1678+ if (!hashResult.IsSuccess ()) {
1679+ AWS_LOGSTREAM_ERROR (CLASS_TAG, " Transfer handle [" << handle->GetId ()
1680+ << " ] Failed to calculate checksum for downloaded file" );
1681+ return false ;
1682+ }
1683+
1684+ Aws::String calculatedChecksum = Aws::Utils::HashingUtils::Base64Encode (hashResult.GetResult ());
1685+ if (calculatedChecksum != handle->GetChecksum ()) {
1686+ AWS_LOGSTREAM_ERROR (CLASS_TAG, " Transfer handle [" << handle->GetId ()
1687+ << " ] Checksum validation failed. Expected: " << handle->GetChecksum ()
1688+ << " , Calculated: " << calculatedChecksum);
1689+ return false ;
1690+ }
1691+
1692+ return true ;
1693+ }
15971694 }
15981695}
0 commit comments