diff --git a/src/Core/Tools/Models/Data/SendAuthenticationTypes.cs b/src/Core/Tools/Models/Data/SendAuthenticationTypes.cs
index c90dba43a8..769e9df713 100644
--- a/src/Core/Tools/Models/Data/SendAuthenticationTypes.cs
+++ b/src/Core/Tools/Models/Data/SendAuthenticationTypes.cs
@@ -44,7 +44,7 @@ public record ResourcePassword(string Hash) : SendAuthenticationMethod;
///
/// Create a send claim by requesting a one time password (OTP) confirmation code.
///
-///
+///
/// The list of email address **hashes** permitted access to the send.
///
-public record EmailOtp(string[] Emails) : SendAuthenticationMethod;
+public record EmailOtp(string[] EmailHashes) : SendAuthenticationMethod;
diff --git a/src/Identity/IdentityServer/RequestValidators/SendAccess/SendEmailOtpRequestValidator.cs b/src/Identity/IdentityServer/RequestValidators/SendAccess/SendEmailOtpRequestValidator.cs
index 34a7a6f6e7..f20fdb6f07 100644
--- a/src/Identity/IdentityServer/RequestValidators/SendAccess/SendEmailOtpRequestValidator.cs
+++ b/src/Identity/IdentityServer/RequestValidators/SendAccess/SendEmailOtpRequestValidator.cs
@@ -1,4 +1,6 @@
using System.Security.Claims;
+using System.Security.Cryptography;
+using System.Text;
using Bit.Core;
using Bit.Core.Auth.Identity;
using Bit.Core.Auth.Identity.TokenProviders;
@@ -40,8 +42,10 @@ public class SendEmailOtpRequestValidator(
return BuildErrorResult(SendAccessConstants.EmailOtpValidatorResults.EmailRequired);
}
- // email must be in the list of emails in the EmailOtp array
- if (!authMethod.Emails.Contains(email))
+ // email hash must be in the list of email hashes in the EmailOtp array
+ byte[] hashBytes = SHA256.HashData(Encoding.UTF8.GetBytes(email));
+ string hashEmailHex = Convert.ToHexString(hashBytes).ToUpperInvariant();
+ if (!authMethod.EmailHashes.Contains(hashEmailHex))
{
return BuildErrorResult(SendAccessConstants.EmailOtpValidatorResults.EmailInvalid);
}
diff --git a/test/Common/Helpers/CryptographyHelper.cs b/test/Common/Helpers/CryptographyHelper.cs
new file mode 100644
index 0000000000..30dfb1a679
--- /dev/null
+++ b/test/Common/Helpers/CryptographyHelper.cs
@@ -0,0 +1,17 @@
+using System.Security.Cryptography;
+using System.Text;
+
+namespace Bit.Test.Common.Helpers;
+
+public class CryptographyHelper
+{
+ ///
+ /// Returns a hex-encoded, SHA256 hash for the given string
+ ///
+ public static string HashAndEncode(string text)
+ {
+ var hashBytes = SHA256.HashData(Encoding.UTF8.GetBytes(text));
+ var hashEncoded = Convert.ToHexString(hashBytes).ToUpperInvariant();
+ return hashEncoded;
+ }
+}
diff --git a/test/Core.Test/Tools/Services/SendAuthenticationQueryTests.cs b/test/Core.Test/Tools/Services/SendAuthenticationQueryTests.cs
index 56b0f306cb..b4b1ecbc79 100644
--- a/test/Core.Test/Tools/Services/SendAuthenticationQueryTests.cs
+++ b/test/Core.Test/Tools/Services/SendAuthenticationQueryTests.cs
@@ -56,7 +56,7 @@ public class SendAuthenticationQueryTests
// Assert
var emailOtp = Assert.IsType(result);
- Assert.Equal(expectedEmailHashes, emailOtp.Emails);
+ Assert.Equal(expectedEmailHashes, emailOtp.EmailHashes);
}
[Fact]
diff --git a/test/Identity.IntegrationTest/RequestValidation/SendAccess/SendEmailOtpReqestValidatorIntegrationTests.cs b/test/Identity.IntegrationTest/RequestValidation/SendAccess/SendEmailOtpReqestValidatorIntegrationTests.cs
index 3c4657653b..1c740cd448 100644
--- a/test/Identity.IntegrationTest/RequestValidation/SendAccess/SendEmailOtpReqestValidatorIntegrationTests.cs
+++ b/test/Identity.IntegrationTest/RequestValidation/SendAccess/SendEmailOtpReqestValidatorIntegrationTests.cs
@@ -3,6 +3,7 @@ using Bit.Core.Services;
using Bit.Core.Tools.Models.Data;
using Bit.Core.Tools.SendFeatures.Queries.Interfaces;
using Bit.IntegrationTestCommon.Factories;
+using Bit.Test.Common.Helpers;
using Duende.IdentityModel;
using NSubstitute;
using Xunit;
@@ -60,7 +61,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
var sendAuthQuery = Substitute.For();
sendAuthQuery.GetAuthenticationMethod(sendId)
- .Returns(new EmailOtp([email]));
+ .Returns(new EmailOtp([CryptographyHelper.HashAndEncode(email)]));
services.AddSingleton(sendAuthQuery);
// Mock OTP token provider
@@ -75,6 +76,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
});
}).CreateClient();
+
var requestBody = SendAccessTestUtilities.CreateTokenRequestBody(sendId, email: email); // Email but no OTP
// Act
@@ -104,7 +106,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
var sendAuthQuery = Substitute.For();
sendAuthQuery.GetAuthenticationMethod(sendId)
- .Returns(new EmailOtp(new[] { email }));
+ .Returns(new EmailOtp(new[] { CryptographyHelper.HashAndEncode(email) }));
services.AddSingleton(sendAuthQuery);
// Mock OTP token provider to validate successfully
@@ -148,7 +150,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
var sendAuthQuery = Substitute.For();
sendAuthQuery.GetAuthenticationMethod(sendId)
- .Returns(new EmailOtp(new[] { email }));
+ .Returns(new EmailOtp(new[] { CryptographyHelper.HashAndEncode(email) }));
services.AddSingleton(sendAuthQuery);
// Mock OTP token provider to validate as false
@@ -190,7 +192,7 @@ public class SendEmailOtpRequestValidatorIntegrationTests(IdentityApplicationFac
var sendAuthQuery = Substitute.For();
sendAuthQuery.GetAuthenticationMethod(sendId)
- .Returns(new EmailOtp(new[] { email }));
+ .Returns(new EmailOtp(new[] { CryptographyHelper.HashAndEncode(email) }));
services.AddSingleton(sendAuthQuery);
// Mock OTP token provider to fail generation
diff --git a/test/Identity.Test/IdentityServer/SendAccess/SendEmailOtpRequestValidatorTests.cs b/test/Identity.Test/IdentityServer/SendAccess/SendEmailOtpRequestValidatorTests.cs
index 7fdfacf428..1815b9207d 100644
--- a/test/Identity.Test/IdentityServer/SendAccess/SendEmailOtpRequestValidatorTests.cs
+++ b/test/Identity.Test/IdentityServer/SendAccess/SendEmailOtpRequestValidatorTests.cs
@@ -5,6 +5,7 @@ using Bit.Core.Tools.Models.Data;
using Bit.Identity.IdentityServer.RequestValidators.SendAccess;
using Bit.Test.Common.AutoFixture;
using Bit.Test.Common.AutoFixture.Attributes;
+using Bit.Test.Common.Helpers;
using Duende.IdentityModel;
using Duende.IdentityServer.Validation;
using NSubstitute;
@@ -105,7 +106,8 @@ public class SendEmailOtpRequestValidatorTests
expectedUniqueId)
.Returns(generatedToken);
- emailOtp = emailOtp with { Emails = [email] };
+ var emailHash = CryptographyHelper.HashAndEncode(email);
+ emailOtp = emailOtp with { EmailHashes = [emailHash] };
// Act
var result = await sutProvider.Sut.ValidateRequestAsync(context, emailOtp, sendId);
@@ -144,7 +146,8 @@ public class SendEmailOtpRequestValidatorTests
Request = tokenRequest
};
- emailOtp = emailOtp with { Emails = [email] };
+ var emailHash = CryptographyHelper.HashAndEncode(email);
+ emailOtp = emailOtp with { EmailHashes = [emailHash] };
sutProvider.GetDependency>()
.GenerateTokenAsync(Arg.Any(), Arg.Any(), Arg.Any())
@@ -179,7 +182,8 @@ public class SendEmailOtpRequestValidatorTests
Request = tokenRequest
};
- emailOtp = emailOtp with { Emails = [email] };
+ var emailHash = CryptographyHelper.HashAndEncode(email);
+ emailOtp = emailOtp with { EmailHashes = [emailHash] };
var expectedUniqueId = string.Format(SendAccessConstants.OtpToken.TokenUniqueIdentifier, sendId, email);
@@ -231,7 +235,8 @@ public class SendEmailOtpRequestValidatorTests
Request = tokenRequest
};
- emailOtp = emailOtp with { Emails = [email] };
+ var emailHash = CryptographyHelper.HashAndEncode(email);
+ emailOtp = emailOtp with { EmailHashes = [emailHash] };
var expectedUniqueId = string.Format(SendAccessConstants.OtpToken.TokenUniqueIdentifier, sendId, email);